41eebf06ce090399d221253abb01572beee7cd5d
[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
1524         Xstrdup_a(tmp_mailto, mailto, return -1);
1525
1526         if (!strncmp(tmp_mailto, "mailto:", 7))
1527                 tmp_mailto += 7;
1528
1529         p = strchr(tmp_mailto, '?');
1530         if (p) {
1531                 *p = '\0';
1532                 p++;
1533         }
1534
1535         if (to && !*to)
1536                 *to = decode_uri_gdup(tmp_mailto);
1537
1538         while (p) {
1539                 gchar *field, *value;
1540
1541                 field = p;
1542
1543                 p = strchr(p, '=');
1544                 if (!p) break;
1545                 *p = '\0';
1546                 p++;
1547
1548                 value = p;
1549
1550                 p = strchr(p, '&');
1551                 if (p) {
1552                         *p = '\0';
1553                         p++;
1554                 }
1555
1556                 if (*value == '\0') continue;
1557
1558                 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1559                         *cc = decode_uri_gdup(value);
1560                 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1561                         *bcc = decode_uri_gdup(value);
1562                 } else if (subject && !*subject &&
1563                            !g_ascii_strcasecmp(field, "subject")) {
1564                         *subject = decode_uri_gdup(value);
1565                 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1566                         *body = decode_uri_gdup(value);
1567                 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1568                         int i = 0;
1569                         *attach = decode_uri_gdup(value);
1570                         for (; forbidden_uris[i]; i++) {
1571                                 if (strstr(*attach, forbidden_uris[i])) {
1572                                         g_print("Refusing to attach '%s', potential private data leak\n",
1573                                                         *attach);
1574                                         g_free(*attach);
1575                                         *attach = NULL;
1576                                         break;
1577                                 }
1578                         }
1579                 }
1580         }
1581
1582         return 0;
1583 }
1584
1585
1586 #ifdef G_OS_WIN32
1587 #include <windows.h>
1588 #ifndef CSIDL_APPDATA
1589 #define CSIDL_APPDATA 0x001a
1590 #endif
1591 #ifndef CSIDL_LOCAL_APPDATA
1592 #define CSIDL_LOCAL_APPDATA 0x001c
1593 #endif
1594 #ifndef CSIDL_FLAG_CREATE
1595 #define CSIDL_FLAG_CREATE 0x8000
1596 #endif
1597 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
1598
1599 #define RTLD_LAZY 0
1600 const char *
1601 w32_strerror (int w32_errno)
1602 {
1603   static char strerr[256];
1604   int ec = (int)GetLastError ();
1605
1606   if (w32_errno == 0)
1607     w32_errno = ec;
1608   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1609                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1610                  strerr, DIM (strerr)-1, NULL);
1611   return strerr;
1612 }
1613
1614 static __inline__ void *
1615 dlopen (const char * name, int flag)
1616 {
1617   void * hd = LoadLibrary (name);
1618   return hd;
1619 }
1620
1621 static __inline__ void *
1622 dlsym (void * hd, const char * sym)
1623 {
1624   if (hd && sym)
1625     {
1626       void * fnc = GetProcAddress (hd, sym);
1627       if (!fnc)
1628         return NULL;
1629       return fnc;
1630     }
1631   return NULL;
1632 }
1633
1634
1635 static __inline__ const char *
1636 dlerror (void)
1637 {
1638   return w32_strerror (0);
1639 }
1640
1641
1642 static __inline__ int
1643 dlclose (void * hd)
1644 {
1645   if (hd)
1646     {
1647       FreeLibrary (hd);
1648       return 0;
1649     }
1650   return -1;
1651 }
1652
1653 static HRESULT
1654 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1655 {
1656   static int initialized;
1657   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1658
1659   if (!initialized)
1660     {
1661       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1662       void *handle;
1663       int i;
1664
1665       initialized = 1;
1666
1667       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1668         {
1669           handle = dlopen (dllnames[i], RTLD_LAZY);
1670           if (handle)
1671             {
1672               func = dlsym (handle, "SHGetFolderPathA");
1673               if (!func)
1674                 {
1675                   dlclose (handle);
1676                   handle = NULL;
1677                 }
1678             }
1679         }
1680     }
1681
1682   if (func)
1683     return func (a,b,c,d,e);
1684   else
1685     return -1;
1686 }
1687
1688 /* Returns a static string with the directroy from which the module
1689    has been loaded.  Returns an empty string on error. */
1690 static char *w32_get_module_dir(void)
1691 {
1692         static char *moddir;
1693
1694         if (!moddir) {
1695                 char name[MAX_PATH+10];
1696                 char *p;
1697
1698                 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1699                         *name = 0;
1700                 else {
1701                         p = strrchr (name, '\\');
1702                         if (p)
1703                                 *p = 0;
1704                         else
1705                                 *name = 0;
1706                 }
1707                 moddir = g_strdup (name);
1708         }
1709         return moddir;
1710 }
1711 #endif /* G_OS_WIN32 */
1712
1713 /* Return a static string with the locale dir. */
1714 const gchar *get_locale_dir(void)
1715 {
1716         static gchar *loc_dir;
1717
1718 #ifdef G_OS_WIN32
1719         if (!loc_dir)
1720                 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1721                                       "\\share\\locale", NULL);
1722 #endif
1723         if (!loc_dir)
1724                 loc_dir = LOCALEDIR;
1725         
1726         return loc_dir;
1727 }
1728
1729
1730 const gchar *get_home_dir(void)
1731 {
1732 #ifdef G_OS_WIN32
1733         static char home_dir[MAX_PATH] = "";
1734
1735         if (home_dir[0] == '\0') {
1736                 if (w32_shgetfolderpath
1737                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1738                              NULL, 0, home_dir) < 0)
1739                                 strcpy (home_dir, "C:\\Sylpheed");
1740         }
1741         return home_dir;
1742 #else
1743         static const gchar *homeenv = NULL;
1744
1745         if (homeenv)
1746                 return homeenv;
1747
1748         if (!homeenv && g_getenv("HOME") != NULL)
1749                 homeenv = g_strdup(g_getenv("HOME"));
1750         if (!homeenv)
1751                 homeenv = g_get_home_dir();
1752
1753         return homeenv;
1754 #endif
1755 }
1756
1757 static gchar *claws_rc_dir = NULL;
1758 static gboolean rc_dir_alt = FALSE;
1759 const gchar *get_rc_dir(void)
1760 {
1761
1762         if (!claws_rc_dir)
1763                 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1764                                      RC_DIR, NULL);
1765
1766         return claws_rc_dir;
1767 }
1768
1769 void set_rc_dir(const gchar *dir)
1770 {
1771         if (claws_rc_dir != NULL) {
1772                 g_print("Error: rc_dir already set\n");
1773         } else {
1774                 rc_dir_alt = TRUE;
1775                 if (g_path_is_absolute(dir))
1776                         claws_rc_dir = g_strdup(dir);
1777                 else {
1778                         claws_rc_dir = g_strconcat(g_get_current_dir(),
1779                                 G_DIR_SEPARATOR_S, dir, NULL);
1780                 }
1781                 debug_print("set rc_dir to %s\n", claws_rc_dir);
1782                 if (!is_dir_exist(claws_rc_dir)) {
1783                         if (make_dir_hier(claws_rc_dir) != 0) {
1784                                 g_print("Error: can't create %s\n",
1785                                 claws_rc_dir);
1786                         }
1787                 }
1788         }
1789 }
1790
1791 gboolean rc_dir_is_alt(void) {
1792         return rc_dir_alt;
1793 }
1794
1795 const gchar *get_mail_base_dir(void)
1796 {
1797 #ifdef G_OS_WIN32
1798         static gchar *mail_base_dir = NULL;
1799
1800         if (!mail_base_dir)
1801                 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1802                                             "Mailboxes", NULL);
1803
1804         return mail_base_dir;
1805 #else
1806         return get_home_dir();
1807 #endif
1808 }
1809
1810 #ifdef MAEMO
1811 const gchar *prefs_common_get_data_root(void);
1812 gchar *last_data_root = NULL;
1813 #endif
1814
1815 const gchar *get_news_cache_dir(void)
1816 {
1817         static gchar *news_cache_dir = NULL;
1818 #ifdef MAEMO
1819         const gchar *data_root = prefs_common_get_data_root();
1820         if (strcmp2(data_root, last_data_root)) {
1821                 g_free(news_cache_dir);
1822                 news_cache_dir = NULL;
1823         }
1824 #endif
1825         if (!news_cache_dir)
1826 #ifndef MAEMO
1827                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1828                                              NEWS_CACHE_DIR, NULL);
1829 #else
1830         {
1831                 if (data_root) {
1832                         news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1833                                              "Claws", G_DIR_SEPARATOR_S, 
1834                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1835                                              NEWS_CACHE_DIR, NULL);
1836                         g_free(last_data_root);
1837                         last_data_root = g_strdup(last_data_root);
1838                 } else {
1839                         news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1840                                              NEWS_CACHE_DIR, NULL);
1841                         g_free(last_data_root);
1842                         last_data_root = NULL;
1843                 }
1844         }
1845 #endif
1846         return news_cache_dir;
1847 }
1848
1849 const gchar *get_imap_cache_dir(void)
1850 {
1851         static gchar *imap_cache_dir = NULL;
1852 #ifdef MAEMO
1853         const gchar *data_root = prefs_common_get_data_root();
1854         if (strcmp2(data_root, last_data_root)) {
1855                 g_free(imap_cache_dir);
1856                 imap_cache_dir = NULL;
1857         }
1858 #endif
1859
1860         if (!imap_cache_dir)
1861 #ifndef MAEMO
1862                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1863                                              IMAP_CACHE_DIR, NULL);
1864 #else
1865         {
1866                 if (data_root) {
1867                         imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1868                                              "Claws", G_DIR_SEPARATOR_S, 
1869                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1870                                              IMAP_CACHE_DIR, NULL);
1871                         g_free(last_data_root);
1872                         last_data_root = g_strdup(last_data_root);
1873                 } else {
1874                         imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1875                                              IMAP_CACHE_DIR, NULL);
1876                         g_free(last_data_root);
1877                         last_data_root = NULL;
1878                 }
1879         }
1880 #endif
1881
1882         return imap_cache_dir;
1883 }
1884
1885 const gchar *get_mime_tmp_dir(void)
1886 {
1887         static gchar *mime_tmp_dir = NULL;
1888
1889         if (!mime_tmp_dir)
1890                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1891                                            MIME_TMP_DIR, NULL);
1892
1893         return mime_tmp_dir;
1894 }
1895
1896 const gchar *get_template_dir(void)
1897 {
1898         static gchar *template_dir = NULL;
1899
1900         if (!template_dir)
1901                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1902                                            TEMPLATE_DIR, NULL);
1903
1904         return template_dir;
1905 }
1906
1907 /* Return the default directory for Plugins. */
1908 const gchar *get_plugin_dir(void)
1909 {
1910 #ifdef G_OS_WIN32
1911         static gchar *plugin_dir = NULL;
1912
1913         if (!plugin_dir)
1914                 plugin_dir = g_strconcat(w32_get_module_dir(),
1915                                          "\\lib\\claws-mail\\plugins\\",
1916                                          NULL);
1917         return plugin_dir;
1918 #else
1919         if (is_dir_exist(PLUGINDIR))
1920                 return PLUGINDIR;
1921         else {
1922                 static gchar *plugin_dir = NULL;
1923                 if (!plugin_dir)
1924                         plugin_dir = g_strconcat(get_rc_dir(), 
1925                                 G_DIR_SEPARATOR_S, "plugins", 
1926                                 G_DIR_SEPARATOR_S, NULL);
1927                 return plugin_dir;                      
1928         }
1929 #endif
1930 }
1931
1932 const gchar *get_tmp_dir(void)
1933 {
1934         static gchar *tmp_dir = NULL;
1935
1936         if (!tmp_dir)
1937                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1938                                       TMP_DIR, NULL);
1939
1940         return tmp_dir;
1941 }
1942
1943 gchar *get_tmp_file(void)
1944 {
1945         gchar *tmp_file;
1946         static guint32 id = 0;
1947
1948         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1949                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
1950
1951         return tmp_file;
1952 }
1953
1954 const gchar *get_domain_name(void)
1955 {
1956 #ifdef G_OS_UNIX
1957         static gchar *domain_name = NULL;
1958
1959         if (!domain_name) {
1960                 struct hostent *hp;
1961                 char hostname[256];
1962
1963                 if (gethostname(hostname, sizeof(hostname)) != 0) {
1964                         perror("gethostname");
1965                         domain_name = "unknown";
1966                 } else {
1967                         hostname[sizeof(hostname) - 1] = '\0';
1968                         if ((hp = my_gethostbyname(hostname)) == NULL) {
1969                                 perror("gethostbyname");
1970                                 domain_name = g_strdup(hostname);
1971                         } else {
1972                                 domain_name = g_strdup(hp->h_name);
1973                         }
1974                 }
1975                 debug_print("domain name = %s\n", domain_name);
1976         }
1977
1978         return domain_name;
1979 #else
1980         return "unknown";
1981 #endif
1982 }
1983
1984 off_t get_file_size(const gchar *file)
1985 {
1986         struct stat s;
1987
1988         if (g_stat(file, &s) < 0) {
1989                 FILE_OP_ERROR(file, "stat");
1990                 return -1;
1991         }
1992
1993         return s.st_size;
1994 }
1995
1996 time_t get_file_mtime(const gchar *file)
1997 {
1998         struct stat s;
1999
2000         if (g_stat(file, &s) < 0) {
2001                 FILE_OP_ERROR(file, "stat");
2002                 return -1;
2003         }
2004
2005         return s.st_mtime;
2006 }
2007
2008 off_t get_file_size_as_crlf(const gchar *file)
2009 {
2010         FILE *fp;
2011         off_t size = 0;
2012         gchar buf[BUFFSIZE];
2013
2014         if ((fp = g_fopen(file, "rb")) == NULL) {
2015                 FILE_OP_ERROR(file, "fopen");
2016                 return -1;
2017         }
2018
2019         while (fgets(buf, sizeof(buf), fp) != NULL) {
2020                 strretchomp(buf);
2021                 size += strlen(buf) + 2;
2022         }
2023
2024         if (ferror(fp)) {
2025                 FILE_OP_ERROR(file, "fgets");
2026                 size = -1;
2027         }
2028
2029         fclose(fp);
2030
2031         return size;
2032 }
2033
2034 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2035 {
2036         struct stat s;
2037
2038         if (file == NULL)
2039                 return FALSE;
2040
2041         if (g_stat(file, &s) < 0) {
2042                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2043                 return FALSE;
2044         }
2045
2046         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2047                 return TRUE;
2048
2049         return FALSE;
2050 }
2051
2052
2053 /* Test on whether FILE is a relative file name. This is
2054  * straightforward for Unix but more complex for Windows. */
2055 gboolean is_relative_filename(const gchar *file)
2056 {
2057         if (!file)
2058                 return TRUE;
2059 #ifdef G_OS_WIN32
2060         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2061                 return FALSE; /* Prefixed with a hostname - this can't
2062                                * be a relative name. */
2063
2064         if ( ((*file >= 'a' && *file <= 'z')
2065               || (*file >= 'A' && *file <= 'Z'))
2066              && file[1] == ':')
2067                 file += 2;  /* Skip drive letter. */
2068
2069         return !(*file == '\\' || *file == '/');
2070 #else
2071         return !(*file == G_DIR_SEPARATOR);
2072 #endif
2073 }
2074
2075
2076 gboolean is_dir_exist(const gchar *dir)
2077 {
2078         if (dir == NULL)
2079                 return FALSE;
2080
2081         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2082 }
2083
2084 gboolean is_file_entry_exist(const gchar *file)
2085 {
2086         if (file == NULL)
2087                 return FALSE;
2088
2089         return g_file_test(file, G_FILE_TEST_EXISTS);
2090 }
2091
2092 gboolean dirent_is_regular_file(struct dirent *d)
2093 {
2094 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2095         if (d->d_type == DT_REG)
2096                 return TRUE;
2097         else if (d->d_type != DT_UNKNOWN)
2098                 return FALSE;
2099 #endif
2100
2101         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2102 }
2103
2104 gint change_dir(const gchar *dir)
2105 {
2106         gchar *prevdir = NULL;
2107
2108         if (debug_mode)
2109                 prevdir = g_get_current_dir();
2110
2111         if (g_chdir(dir) < 0) {
2112                 FILE_OP_ERROR(dir, "chdir");
2113                 if (debug_mode) g_free(prevdir);
2114                 return -1;
2115         } else if (debug_mode) {
2116                 gchar *cwd;
2117
2118                 cwd = g_get_current_dir();
2119                 if (strcmp(prevdir, cwd) != 0)
2120                         g_print("current dir: %s\n", cwd);
2121                 g_free(cwd);
2122                 g_free(prevdir);
2123         }
2124
2125         return 0;
2126 }
2127
2128 gint make_dir(const gchar *dir)
2129 {
2130         if (g_mkdir(dir, S_IRWXU) < 0) {
2131                 FILE_OP_ERROR(dir, "mkdir");
2132                 return -1;
2133         }
2134         if (g_chmod(dir, S_IRWXU) < 0)
2135                 FILE_OP_ERROR(dir, "chmod");
2136
2137         return 0;
2138 }
2139
2140 gint make_dir_hier(const gchar *dir)
2141 {
2142         gchar *parent_dir;
2143         const gchar *p;
2144
2145         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2146                 parent_dir = g_strndup(dir, p - dir);
2147                 if (*parent_dir != '\0') {
2148                         if (!is_dir_exist(parent_dir)) {
2149                                 if (make_dir(parent_dir) < 0) {
2150                                         g_free(parent_dir);
2151                                         return -1;
2152                                 }
2153                         }
2154                 }
2155                 g_free(parent_dir);
2156         }
2157
2158         if (!is_dir_exist(dir)) {
2159                 if (make_dir(dir) < 0)
2160                         return -1;
2161         }
2162
2163         return 0;
2164 }
2165
2166 gint remove_all_files(const gchar *dir)
2167 {
2168         GDir *dp;
2169         const gchar *dir_name;
2170         gchar *prev_dir;
2171
2172         prev_dir = g_get_current_dir();
2173
2174         if (g_chdir(dir) < 0) {
2175                 FILE_OP_ERROR(dir, "chdir");
2176                 g_free(prev_dir);
2177                 return -1;
2178         }
2179
2180         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2181                 g_warning("failed to open directory: %s\n", dir);
2182                 g_free(prev_dir);
2183                 return -1;
2184         }
2185
2186         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2187                 if (g_unlink(dir_name) < 0)
2188                         FILE_OP_ERROR(dir_name, "unlink");
2189         }
2190
2191         g_dir_close(dp);
2192
2193         if (g_chdir(prev_dir) < 0) {
2194                 FILE_OP_ERROR(prev_dir, "chdir");
2195                 g_free(prev_dir);
2196                 return -1;
2197         }
2198
2199         g_free(prev_dir);
2200
2201         return 0;
2202 }
2203
2204 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2205 {
2206         GDir *dp;
2207         const gchar *dir_name;
2208         gchar *prev_dir;
2209         gint file_no;
2210
2211         prev_dir = g_get_current_dir();
2212
2213         if (g_chdir(dir) < 0) {
2214                 FILE_OP_ERROR(dir, "chdir");
2215                 g_free(prev_dir);
2216                 return -1;
2217         }
2218
2219         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2220                 g_warning("failed to open directory: %s\n", dir);
2221                 g_free(prev_dir);
2222                 return -1;
2223         }
2224
2225         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2226                 file_no = to_number(dir_name);
2227                 if (file_no > 0 && first <= file_no && file_no <= last) {
2228                         if (is_dir_exist(dir_name))
2229                                 continue;
2230                         if (g_unlink(dir_name) < 0)
2231                                 FILE_OP_ERROR(dir_name, "unlink");
2232                 }
2233         }
2234
2235         g_dir_close(dp);
2236
2237         if (g_chdir(prev_dir) < 0) {
2238                 FILE_OP_ERROR(prev_dir, "chdir");
2239                 g_free(prev_dir);
2240                 return -1;
2241         }
2242
2243         g_free(prev_dir);
2244
2245         return 0;
2246 }
2247
2248 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2249 {
2250         GDir *dp;
2251         const gchar *dir_name;
2252         gchar *prev_dir;
2253         gint file_no;
2254
2255         prev_dir = g_get_current_dir();
2256
2257         if (g_chdir(dir) < 0) {
2258                 FILE_OP_ERROR(dir, "chdir");
2259                 g_free(prev_dir);
2260                 return -1;
2261         }
2262
2263         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2264                 FILE_OP_ERROR(dir, "opendir");
2265                 g_free(prev_dir);
2266                 return -1;
2267         }
2268
2269         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2270                 file_no = to_number(dir_name);
2271                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2272                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2273                         if (is_dir_exist(dir_name))
2274                                 continue;
2275                         if (g_unlink(dir_name) < 0)
2276                                 FILE_OP_ERROR(dir_name, "unlink");
2277                 }
2278         }
2279
2280         g_dir_close(dp);
2281
2282         if (g_chdir(prev_dir) < 0) {
2283                 FILE_OP_ERROR(prev_dir, "chdir");
2284                 g_free(prev_dir);
2285                 return -1;
2286         }
2287
2288         g_free(prev_dir);
2289
2290         return 0;
2291 }
2292
2293 gint remove_all_numbered_files(const gchar *dir)
2294 {
2295         return remove_numbered_files(dir, 0, UINT_MAX);
2296 }
2297
2298 gint remove_dir_recursive(const gchar *dir)
2299 {
2300         struct stat s;
2301         GDir *dp;
2302         const gchar *dir_name;
2303         gchar *prev_dir;
2304
2305         if (g_stat(dir, &s) < 0) {
2306                 FILE_OP_ERROR(dir, "stat");
2307                 if (ENOENT == errno) return 0;
2308                 return -1;
2309         }
2310
2311         if (!S_ISDIR(s.st_mode)) {
2312                 if (g_unlink(dir) < 0) {
2313                         FILE_OP_ERROR(dir, "unlink");
2314                         return -1;
2315                 }
2316
2317                 return 0;
2318         }
2319
2320         prev_dir = g_get_current_dir();
2321         /* g_print("prev_dir = %s\n", prev_dir); */
2322
2323         if (!path_cmp(prev_dir, dir)) {
2324                 g_free(prev_dir);
2325                 if (g_chdir("..") < 0) {
2326                         FILE_OP_ERROR(dir, "chdir");
2327                         return -1;
2328                 }
2329                 prev_dir = g_get_current_dir();
2330         }
2331
2332         if (g_chdir(dir) < 0) {
2333                 FILE_OP_ERROR(dir, "chdir");
2334                 g_free(prev_dir);
2335                 return -1;
2336         }
2337
2338         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2339                 g_warning("failed to open directory: %s\n", dir);
2340                 g_chdir(prev_dir);
2341                 g_free(prev_dir);
2342                 return -1;
2343         }
2344
2345         /* remove all files in the directory */
2346         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2347                 /* g_print("removing %s\n", dir_name); */
2348
2349                 if (is_dir_exist(dir_name)) {
2350                         if (remove_dir_recursive(dir_name) < 0) {
2351                                 g_warning("can't remove directory\n");
2352                                 return -1;
2353                         }
2354                 } else {
2355                         if (g_unlink(dir_name) < 0)
2356                                 FILE_OP_ERROR(dir_name, "unlink");
2357                 }
2358         }
2359
2360         g_dir_close(dp);
2361
2362         if (g_chdir(prev_dir) < 0) {
2363                 FILE_OP_ERROR(prev_dir, "chdir");
2364                 g_free(prev_dir);
2365                 return -1;
2366         }
2367
2368         g_free(prev_dir);
2369
2370         if (g_rmdir(dir) < 0) {
2371                 FILE_OP_ERROR(dir, "rmdir");
2372                 return -1;
2373         }
2374
2375         return 0;
2376 }
2377
2378 gint rename_force(const gchar *oldpath, const gchar *newpath)
2379 {
2380 #ifndef G_OS_UNIX
2381         if (!is_file_entry_exist(oldpath)) {
2382                 errno = ENOENT;
2383                 return -1;
2384         }
2385         if (is_file_exist(newpath)) {
2386                 if (g_unlink(newpath) < 0)
2387                         FILE_OP_ERROR(newpath, "unlink");
2388         }
2389 #endif
2390         return g_rename(oldpath, newpath);
2391 }
2392
2393 /*
2394  * Append src file body to the tail of dest file.
2395  * Now keep_backup has no effects.
2396  */
2397 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2398 {
2399         FILE *src_fp, *dest_fp;
2400         gint n_read;
2401         gchar buf[BUFSIZ];
2402
2403         gboolean err = FALSE;
2404
2405         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2406                 FILE_OP_ERROR(src, "fopen");
2407                 return -1;
2408         }
2409
2410         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2411                 FILE_OP_ERROR(dest, "fopen");
2412                 fclose(src_fp);
2413                 return -1;
2414         }
2415
2416         if (change_file_mode_rw(dest_fp, dest) < 0) {
2417                 FILE_OP_ERROR(dest, "chmod");
2418                 g_warning("can't change file mode\n");
2419         }
2420
2421         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2422                 if (n_read < sizeof(buf) && ferror(src_fp))
2423                         break;
2424                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2425                         g_warning("writing to %s failed.\n", dest);
2426                         fclose(dest_fp);
2427                         fclose(src_fp);
2428                         g_unlink(dest);
2429                         return -1;
2430                 }
2431         }
2432
2433         if (ferror(src_fp)) {
2434                 FILE_OP_ERROR(src, "fread");
2435                 err = TRUE;
2436         }
2437         fclose(src_fp);
2438         if (fclose(dest_fp) == EOF) {
2439                 FILE_OP_ERROR(dest, "fclose");
2440                 err = TRUE;
2441         }
2442
2443         if (err) {
2444                 g_unlink(dest);
2445                 return -1;
2446         }
2447
2448         return 0;
2449 }
2450
2451 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2452 {
2453         FILE *src_fp, *dest_fp;
2454         gint n_read;
2455         gchar buf[BUFSIZ];
2456         gchar *dest_bak = NULL;
2457         gboolean err = FALSE;
2458
2459         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2460                 FILE_OP_ERROR(src, "fopen");
2461                 return -1;
2462         }
2463         if (is_file_exist(dest)) {
2464                 dest_bak = g_strconcat(dest, ".bak", NULL);
2465                 if (rename_force(dest, dest_bak) < 0) {
2466                         FILE_OP_ERROR(dest, "rename");
2467                         fclose(src_fp);
2468                         g_free(dest_bak);
2469                         return -1;
2470                 }
2471         }
2472
2473         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2474                 FILE_OP_ERROR(dest, "fopen");
2475                 fclose(src_fp);
2476                 if (dest_bak) {
2477                         if (rename_force(dest_bak, dest) < 0)
2478                                 FILE_OP_ERROR(dest_bak, "rename");
2479                         g_free(dest_bak);
2480                 }
2481                 return -1;
2482         }
2483
2484         if (change_file_mode_rw(dest_fp, dest) < 0) {
2485                 FILE_OP_ERROR(dest, "chmod");
2486                 g_warning("can't change file mode\n");
2487         }
2488
2489         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2490                 if (n_read < sizeof(buf) && ferror(src_fp))
2491                         break;
2492                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2493                         g_warning("writing to %s failed.\n", dest);
2494                         fclose(dest_fp);
2495                         fclose(src_fp);
2496                         g_unlink(dest);
2497                         if (dest_bak) {
2498                                 if (rename_force(dest_bak, dest) < 0)
2499                                         FILE_OP_ERROR(dest_bak, "rename");
2500                                 g_free(dest_bak);
2501                         }
2502                         return -1;
2503                 }
2504         }
2505
2506         if (ferror(src_fp)) {
2507                 FILE_OP_ERROR(src, "fread");
2508                 err = TRUE;
2509         }
2510         fclose(src_fp);
2511         if (fclose(dest_fp) == EOF) {
2512                 FILE_OP_ERROR(dest, "fclose");
2513                 err = TRUE;
2514         }
2515
2516         if (err) {
2517                 g_unlink(dest);
2518                 if (dest_bak) {
2519                         if (rename_force(dest_bak, dest) < 0)
2520                                 FILE_OP_ERROR(dest_bak, "rename");
2521                         g_free(dest_bak);
2522                 }
2523                 return -1;
2524         }
2525
2526         if (keep_backup == FALSE && dest_bak)
2527                 g_unlink(dest_bak);
2528
2529         g_free(dest_bak);
2530
2531         return 0;
2532 }
2533
2534 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2535 {
2536         if (overwrite == FALSE && is_file_exist(dest)) {
2537                 g_warning("move_file(): file %s already exists.", dest);
2538                 return -1;
2539         }
2540
2541         if (rename_force(src, dest) == 0) return 0;
2542
2543         if (EXDEV != errno) {
2544                 FILE_OP_ERROR(src, "rename");
2545                 return -1;
2546         }
2547
2548         if (copy_file(src, dest, FALSE) < 0) return -1;
2549
2550         g_unlink(src);
2551
2552         return 0;
2553 }
2554
2555 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2556 {
2557         gint n_read;
2558         gint bytes_left, to_read;
2559         gchar buf[BUFSIZ];
2560
2561         if (fseek(fp, offset, SEEK_SET) < 0) {
2562                 perror("fseek");
2563                 return -1;
2564         }
2565
2566         bytes_left = length;
2567         to_read = MIN(bytes_left, sizeof(buf));
2568
2569         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2570                 if (n_read < to_read && ferror(fp))
2571                         break;
2572                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2573                         return -1;
2574                 }
2575                 bytes_left -= n_read;
2576                 if (bytes_left == 0)
2577                         break;
2578                 to_read = MIN(bytes_left, sizeof(buf));
2579         }
2580
2581         if (ferror(fp)) {
2582                 perror("fread");
2583                 return -1;
2584         }
2585
2586         return 0;
2587 }
2588
2589 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2590 {
2591         FILE *dest_fp;
2592         gboolean err = FALSE;
2593
2594         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2595                 FILE_OP_ERROR(dest, "fopen");
2596                 return -1;
2597         }
2598
2599         if (change_file_mode_rw(dest_fp, dest) < 0) {
2600                 FILE_OP_ERROR(dest, "chmod");
2601                 g_warning("can't change file mode\n");
2602         }
2603
2604         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2605                 err = TRUE;
2606
2607         if (!err && fclose(dest_fp) == EOF) {
2608                 FILE_OP_ERROR(dest, "fclose");
2609                 err = TRUE;
2610         }
2611
2612         if (err) {
2613                 g_warning("writing to %s failed.\n", dest);
2614                 g_unlink(dest);
2615                 return -1;
2616         }
2617
2618         return 0;
2619 }
2620
2621 /* convert line endings into CRLF. If the last line doesn't end with
2622  * linebreak, add it.
2623  */
2624 gchar *canonicalize_str(const gchar *str)
2625 {
2626         const gchar *p;
2627         guint new_len = 0;
2628         gchar *out, *outp;
2629
2630         for (p = str; *p != '\0'; ++p) {
2631                 if (*p != '\r') {
2632                         ++new_len;
2633                         if (*p == '\n')
2634                                 ++new_len;
2635                 }
2636         }
2637         if (p == str || *(p - 1) != '\n')
2638                 new_len += 2;
2639
2640         out = outp = g_malloc(new_len + 1);
2641         for (p = str; *p != '\0'; ++p) {
2642                 if (*p != '\r') {
2643                         if (*p == '\n')
2644                                 *outp++ = '\r';
2645                         *outp++ = *p;
2646                 }
2647         }
2648         if (p == str || *(p - 1) != '\n') {
2649                 *outp++ = '\r';
2650                 *outp++ = '\n';
2651         }
2652         *outp = '\0';
2653
2654         return out;
2655 }
2656
2657 gint canonicalize_file(const gchar *src, const gchar *dest)
2658 {
2659         FILE *src_fp, *dest_fp;
2660         gchar buf[BUFFSIZE];
2661         gint len;
2662         gboolean err = FALSE;
2663         gboolean last_linebreak = FALSE;
2664
2665         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2666                 FILE_OP_ERROR(src, "fopen");
2667                 return -1;
2668         }
2669
2670         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2671                 FILE_OP_ERROR(dest, "fopen");
2672                 fclose(src_fp);
2673                 return -1;
2674         }
2675
2676         if (change_file_mode_rw(dest_fp, dest) < 0) {
2677                 FILE_OP_ERROR(dest, "chmod");
2678                 g_warning("can't change file mode\n");
2679         }
2680
2681         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2682                 gint r = 0;
2683
2684                 len = strlen(buf);
2685                 if (len == 0) break;
2686                 last_linebreak = FALSE;
2687
2688                 if (buf[len - 1] != '\n') {
2689                         last_linebreak = TRUE;
2690                         r = fputs(buf, dest_fp);
2691                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2692                         r = fputs(buf, dest_fp);
2693                 } else {
2694                         if (len > 1) {
2695                                 r = fwrite(buf, 1, len - 1, dest_fp);
2696                                 if (r != (len -1))
2697                                         r = EOF;
2698                         }
2699                         if (r != EOF)
2700                                 r = fputs("\r\n", dest_fp);
2701                 }
2702
2703                 if (r == EOF) {
2704                         g_warning("writing to %s failed.\n", dest);
2705                         fclose(dest_fp);
2706                         fclose(src_fp);
2707                         g_unlink(dest);
2708                         return -1;
2709                 }
2710         }
2711
2712         if (last_linebreak == TRUE) {
2713                 if (fputs("\r\n", dest_fp) == EOF)
2714                         err = TRUE;
2715         }
2716
2717         if (ferror(src_fp)) {
2718                 FILE_OP_ERROR(src, "fgets");
2719                 err = TRUE;
2720         }
2721         fclose(src_fp);
2722         if (fclose(dest_fp) == EOF) {
2723                 FILE_OP_ERROR(dest, "fclose");
2724                 err = TRUE;
2725         }
2726
2727         if (err) {
2728                 g_unlink(dest);
2729                 return -1;
2730         }
2731
2732         return 0;
2733 }
2734
2735 gint canonicalize_file_replace(const gchar *file)
2736 {
2737         gchar *tmp_file;
2738
2739         tmp_file = get_tmp_file();
2740
2741         if (canonicalize_file(file, tmp_file) < 0) {
2742                 g_free(tmp_file);
2743                 return -1;
2744         }
2745
2746         if (move_file(tmp_file, file, TRUE) < 0) {
2747                 g_warning("can't replace %s .\n", file);
2748                 g_unlink(tmp_file);
2749                 g_free(tmp_file);
2750                 return -1;
2751         }
2752
2753         g_free(tmp_file);
2754         return 0;
2755 }
2756
2757 gchar *normalize_newlines(const gchar *str)
2758 {
2759         const gchar *p = str;
2760         gchar *out, *outp;
2761
2762         out = outp = g_malloc(strlen(str) + 1);
2763         for (p = str; *p != '\0'; ++p) {
2764                 if (*p == '\r') {
2765                         if (*(p + 1) != '\n')
2766                                 *outp++ = '\n';
2767                 } else
2768                         *outp++ = *p;
2769         }
2770
2771         *outp = '\0';
2772
2773         return out;
2774 }
2775
2776 gchar *get_outgoing_rfc2822_str(FILE *fp)
2777 {
2778         gchar buf[BUFFSIZE];
2779         GString *str;
2780         gchar *ret;
2781
2782         str = g_string_new(NULL);
2783
2784         /* output header part */
2785         while (fgets(buf, sizeof(buf), fp) != NULL) {
2786                 strretchomp(buf);
2787                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2788                         gint next;
2789
2790                         for (;;) {
2791                                 next = fgetc(fp);
2792                                 if (next == EOF)
2793                                         break;
2794                                 else if (next != ' ' && next != '\t') {
2795                                         ungetc(next, fp);
2796                                         break;
2797                                 }
2798                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2799                                         break;
2800                         }
2801                 } else {
2802                         g_string_append(str, buf);
2803                         g_string_append(str, "\r\n");
2804                         if (buf[0] == '\0')
2805                                 break;
2806                 }
2807         }
2808
2809         /* output body part */
2810         while (fgets(buf, sizeof(buf), fp) != NULL) {
2811                 strretchomp(buf);
2812                 if (buf[0] == '.')
2813                         g_string_append_c(str, '.');
2814                 g_string_append(str, buf);
2815                 g_string_append(str, "\r\n");
2816         }
2817
2818         ret = str->str;
2819         g_string_free(str, FALSE);
2820
2821         return ret;
2822 }
2823
2824 /*
2825  * Create a new boundary in a way that it is very unlikely that this
2826  * will occur in the following text.  It would be easy to ensure
2827  * uniqueness if everything is either quoted-printable or base64
2828  * encoded (note that conversion is allowed), but because MIME bodies
2829  * may be nested, it may happen that the same boundary has already
2830  * been used.
2831  *
2832  *   boundary := 0*69<bchars> bcharsnospace
2833  *   bchars := bcharsnospace / " "
2834  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2835  *                  "+" / "_" / "," / "-" / "." /
2836  *                  "/" / ":" / "=" / "?"
2837  *
2838  * some special characters removed because of buggy MTAs
2839  */
2840
2841 gchar *generate_mime_boundary(const gchar *prefix)
2842 {
2843         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2844                              "abcdefghijklmnopqrstuvwxyz"
2845                              "1234567890+_./=";
2846         gchar buf_uniq[24];
2847         gint i;
2848
2849         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2850                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2851         buf_uniq[i] = '\0';
2852
2853         return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2854                                buf_uniq);
2855 }
2856
2857 gint change_file_mode_rw(FILE *fp, const gchar *file)
2858 {
2859 #if HAVE_FCHMOD
2860         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2861 #else
2862         return g_chmod(file, S_IRUSR|S_IWUSR);
2863 #endif
2864 }
2865
2866 FILE *my_tmpfile(void)
2867 {
2868 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2869         const gchar suffix[] = ".XXXXXX";
2870         const gchar *tmpdir;
2871         guint tmplen;
2872         const gchar *progname;
2873         guint proglen;
2874         gchar *fname;
2875         gint fd;
2876         FILE *fp;
2877         gchar buf[2]="\0";
2878
2879         tmpdir = get_tmp_dir();
2880         tmplen = strlen(tmpdir);
2881         progname = g_get_prgname();
2882         if (progname == NULL)
2883                 progname = "claws-mail";
2884         proglen = strlen(progname);
2885         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2886                 return tmpfile());
2887
2888         memcpy(fname, tmpdir, tmplen);
2889         fname[tmplen] = G_DIR_SEPARATOR;
2890         memcpy(fname + tmplen + 1, progname, proglen);
2891         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2892
2893         fd = mkstemp(fname);
2894         if (fd < 0)
2895                 return tmpfile();
2896
2897 #ifndef G_OS_WIN32
2898         g_unlink(fname);
2899         
2900         /* verify that we can write in the file after unlinking */
2901         if (write(fd, buf, 1) < 0) {
2902                 close(fd);
2903                 return tmpfile();
2904         }
2905         
2906 #endif
2907
2908         fp = fdopen(fd, "w+b");
2909         if (!fp)
2910                 close(fd);
2911         else {
2912                 rewind(fp);
2913                 return fp;
2914         }
2915
2916 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2917
2918         return tmpfile();
2919 }
2920
2921 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2922 {
2923         int fd;
2924 #ifdef G_OS_WIN32
2925         char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2926                                           dir, G_DIR_SEPARATOR);
2927         fd = mkstemp_name(template, filename);
2928         g_free(template);
2929 #else
2930         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2931         fd = mkstemp(*filename);
2932 #endif
2933         return fdopen(fd, "w+");
2934 }
2935
2936 FILE *str_open_as_stream(const gchar *str)
2937 {
2938         FILE *fp;
2939         size_t len;
2940
2941         g_return_val_if_fail(str != NULL, NULL);
2942
2943         fp = my_tmpfile();
2944         if (!fp) {
2945                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2946                 return NULL;
2947         }
2948
2949         len = strlen(str);
2950         if (len == 0) return fp;
2951
2952         if (fwrite(str, 1, len, fp) != len) {
2953                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2954                 fclose(fp);
2955                 return NULL;
2956         }
2957
2958         rewind(fp);
2959         return fp;
2960 }
2961
2962 gint str_write_to_file(const gchar *str, const gchar *file)
2963 {
2964         FILE *fp;
2965         size_t len;
2966
2967         g_return_val_if_fail(str != NULL, -1);
2968         g_return_val_if_fail(file != NULL, -1);
2969
2970         if ((fp = g_fopen(file, "wb")) == NULL) {
2971                 FILE_OP_ERROR(file, "fopen");
2972                 return -1;
2973         }
2974
2975         len = strlen(str);
2976         if (len == 0) {
2977                 fclose(fp);
2978                 return 0;
2979         }
2980
2981         if (fwrite(str, 1, len, fp) != len) {
2982                 FILE_OP_ERROR(file, "fwrite");
2983                 fclose(fp);
2984                 g_unlink(file);
2985                 return -1;
2986         }
2987
2988         if (fclose(fp) == EOF) {
2989                 FILE_OP_ERROR(file, "fclose");
2990                 g_unlink(file);
2991                 return -1;
2992         }
2993
2994         return 0;
2995 }
2996
2997 gchar *file_read_to_str(const gchar *file)
2998 {
2999         FILE *fp;
3000         gchar *str;
3001
3002         g_return_val_if_fail(file != NULL, NULL);
3003
3004         if ((fp = g_fopen(file, "rb")) == NULL) {
3005                 FILE_OP_ERROR(file, "fopen");
3006                 return NULL;
3007         }
3008
3009         str = file_read_stream_to_str(fp);
3010
3011         fclose(fp);
3012
3013         return str;
3014 }
3015
3016 gchar *file_read_stream_to_str(FILE *fp)
3017 {
3018         GByteArray *array;
3019         guchar buf[BUFSIZ];
3020         gint n_read;
3021         gchar *str;
3022
3023         g_return_val_if_fail(fp != NULL, NULL);
3024
3025         array = g_byte_array_new();
3026
3027         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3028                 if (n_read < sizeof(buf) && ferror(fp))
3029                         break;
3030                 g_byte_array_append(array, buf, n_read);
3031         }
3032
3033         if (ferror(fp)) {
3034                 FILE_OP_ERROR("file stream", "fread");
3035                 g_byte_array_free(array, TRUE);
3036                 return NULL;
3037         }
3038
3039         buf[0] = '\0';
3040         g_byte_array_append(array, buf, 1);
3041         str = (gchar *)array->data;
3042         g_byte_array_free(array, FALSE);
3043
3044         if (!g_utf8_validate(str, -1, NULL)) {
3045                 const gchar *src_codeset, *dest_codeset;
3046                 gchar *tmp = NULL;
3047                 src_codeset = conv_get_locale_charset_str();
3048                 dest_codeset = CS_UTF_8;
3049                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3050                 g_free(str);
3051                 str = tmp;
3052         }
3053
3054         return str;
3055 }
3056
3057
3058 char *fgets_crlf(char *buf, int size, FILE *stream)
3059 {
3060         gboolean is_cr = FALSE;
3061         gboolean last_was_cr = FALSE;
3062         int c = 0;
3063         char *cs;
3064
3065         cs = buf;
3066         while (--size > 0 && (c = getc(stream)) != EOF)
3067         {
3068                 *cs++ = c;
3069                 is_cr = (c == '\r');
3070                 if (c == '\n') {
3071                         break;
3072                 }
3073                 if (last_was_cr) {
3074                         *(--cs) = '\n';
3075                         cs++;
3076                         ungetc(c, stream);
3077                         break;
3078                 }
3079                 last_was_cr = is_cr;
3080         }
3081         if (c == EOF && cs == buf)
3082                 return NULL;
3083
3084         *cs = '\0';
3085
3086         return buf;     
3087 }
3088
3089 static gint execute_async(gchar *const argv[])
3090 {
3091         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3092
3093         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3094                           NULL, NULL, NULL, FALSE) == FALSE) {
3095                 g_warning("Couldn't execute command: %s\n", argv[0]);
3096                 return -1;
3097         }
3098
3099         return 0;
3100 }
3101
3102 static gint execute_sync(gchar *const argv[])
3103 {
3104         gint status;
3105
3106         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3107
3108         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3109                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3110                 g_warning("Couldn't execute command: %s\n", argv[0]);
3111                 return -1;
3112         }
3113
3114 #ifdef G_OS_UNIX
3115         if (WIFEXITED(status))
3116                 return WEXITSTATUS(status);
3117         else
3118                 return -1;
3119 #else
3120         return status;
3121 #endif
3122 }
3123
3124 gint execute_command_line(const gchar *cmdline, gboolean async)
3125 {
3126         gchar **argv;
3127         gint ret;
3128
3129         debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3130
3131         argv = strsplit_with_quote(cmdline, " ", 0);
3132
3133         if (async)
3134                 ret = execute_async(argv);
3135         else
3136                 ret = execute_sync(argv);
3137
3138         g_strfreev(argv);
3139
3140         return ret;
3141 }
3142
3143 gchar *get_command_output(const gchar *cmdline)
3144 {
3145         gchar *child_stdout;
3146         gint status;
3147
3148         g_return_val_if_fail(cmdline != NULL, NULL);
3149
3150         debug_print("get_command_output(): executing: %s\n", cmdline);
3151
3152         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3153                                       NULL) == FALSE) {
3154                 g_warning("Couldn't execute command: %s\n", cmdline);
3155                 return NULL;
3156         }
3157
3158         return child_stdout;
3159 }
3160 #ifndef MAEMO
3161 static gint is_unchanged_uri_char(char c)
3162 {
3163         switch (c) {
3164                 case '(':
3165                 case ')':
3166                         return 0;
3167                 default:
3168                         return 1;
3169         }
3170 }
3171
3172 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3173 {
3174         int i;
3175         int k;
3176
3177         k = 0;
3178         for(i = 0; i < strlen(uri) ; i++) {
3179                 if (is_unchanged_uri_char(uri[i])) {
3180                         if (k + 2 >= bufsize)
3181                                 break;
3182                         encoded_uri[k++] = uri[i];
3183                 }
3184                 else {
3185                         char * hexa = "0123456789ABCDEF";
3186
3187                         if (k + 4 >= bufsize)
3188                                 break;
3189                         encoded_uri[k++] = '%';
3190                         encoded_uri[k++] = hexa[uri[i] / 16];
3191                         encoded_uri[k++] = hexa[uri[i] % 16];
3192                 }
3193         }
3194         encoded_uri[k] = 0;
3195 }
3196 #endif
3197 gint open_uri(const gchar *uri, const gchar *cmdline)
3198 {
3199 #ifndef MAEMO
3200         gchar buf[BUFFSIZE];
3201         gchar *p;
3202         gchar encoded_uri[BUFFSIZE];
3203         g_return_val_if_fail(uri != NULL, -1);
3204
3205         /* an option to choose whether to use encode_uri or not ? */
3206         encode_uri(encoded_uri, BUFFSIZE, uri);
3207
3208         if (cmdline &&
3209             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3210             !strchr(p + 2, '%'))
3211                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3212         else {
3213                 if (cmdline)
3214                         g_warning("Open URI command line is invalid "
3215                                   "(there must be only one '%%s'): %s",
3216                                   cmdline);
3217                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3218         }
3219
3220         execute_command_line(buf, TRUE);
3221 #else
3222         extern osso_context_t *get_osso_context(void);
3223         osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3224                                         OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL, 
3225                                         DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3226 #endif
3227         return 0;
3228 }
3229
3230 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3231 {
3232         gchar buf[BUFFSIZE];
3233         gchar *p;
3234
3235         g_return_val_if_fail(filepath != NULL, -1);
3236
3237         if (cmdline &&
3238             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3239             !strchr(p + 2, '%'))
3240                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3241         else {
3242                 if (cmdline)
3243                         g_warning("Open Text Editor command line is invalid "
3244                                   "(there must be only one '%%s'): %s",
3245                                   cmdline);
3246                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3247         }
3248
3249         execute_command_line(buf, TRUE);
3250
3251         return 0;
3252 }
3253
3254 time_t remote_tzoffset_sec(const gchar *zone)
3255 {
3256         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3257         gchar zone3[4];
3258         gchar *p;
3259         gchar c;
3260         gint iustz;
3261         gint offset;
3262         time_t remoteoffset;
3263
3264         strncpy(zone3, zone, 3);
3265         zone3[3] = '\0';
3266         remoteoffset = 0;
3267
3268         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3269             (c == '+' || c == '-')) {
3270                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3271                 if (c == '-')
3272                         remoteoffset = -remoteoffset;
3273         } else if (!strncmp(zone, "UT" , 2) ||
3274                    !strncmp(zone, "GMT", 2)) {
3275                 remoteoffset = 0;
3276         } else if (strlen(zone3) == 3) {
3277                 for (p = ustzstr; *p != '\0'; p += 3) {
3278                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3279                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3280                                 remoteoffset = iustz * 3600;
3281                                 break;
3282                         }
3283                 }
3284                 if (*p == '\0')
3285                         return -1;
3286         } else if (strlen(zone3) == 1) {
3287                 switch (zone[0]) {
3288                 case 'Z': remoteoffset =   0; break;
3289                 case 'A': remoteoffset =  -1; break;
3290                 case 'B': remoteoffset =  -2; break;
3291                 case 'C': remoteoffset =  -3; break;
3292                 case 'D': remoteoffset =  -4; break;
3293                 case 'E': remoteoffset =  -5; break;
3294                 case 'F': remoteoffset =  -6; break;
3295                 case 'G': remoteoffset =  -7; break;
3296                 case 'H': remoteoffset =  -8; break;
3297                 case 'I': remoteoffset =  -9; break;
3298                 case 'K': remoteoffset = -10; break; /* J is not used */
3299                 case 'L': remoteoffset = -11; break;
3300                 case 'M': remoteoffset = -12; break;
3301                 case 'N': remoteoffset =   1; break;
3302                 case 'O': remoteoffset =   2; break;
3303                 case 'P': remoteoffset =   3; break;
3304                 case 'Q': remoteoffset =   4; break;
3305                 case 'R': remoteoffset =   5; break;
3306                 case 'S': remoteoffset =   6; break;
3307                 case 'T': remoteoffset =   7; break;
3308                 case 'U': remoteoffset =   8; break;
3309                 case 'V': remoteoffset =   9; break;
3310                 case 'W': remoteoffset =  10; break;
3311                 case 'X': remoteoffset =  11; break;
3312                 case 'Y': remoteoffset =  12; break;
3313                 default:  remoteoffset =   0; break;
3314                 }
3315                 remoteoffset = remoteoffset * 3600;
3316         } else
3317                 return -1;
3318
3319         return remoteoffset;
3320 }
3321
3322 time_t tzoffset_sec(time_t *now)
3323 {
3324         struct tm gmt, *lt;
3325         gint off;
3326         struct tm buf1, buf2;
3327         
3328         gmt = *gmtime_r(now, &buf1);
3329         lt = localtime_r(now, &buf2);
3330
3331         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3332
3333         if (lt->tm_year < gmt.tm_year)
3334                 off -= 24 * 60;
3335         else if (lt->tm_year > gmt.tm_year)
3336                 off += 24 * 60;
3337         else if (lt->tm_yday < gmt.tm_yday)
3338                 off -= 24 * 60;
3339         else if (lt->tm_yday > gmt.tm_yday)
3340                 off += 24 * 60;
3341
3342         if (off >= 24 * 60)             /* should be impossible */
3343                 off = 23 * 60 + 59;     /* if not, insert silly value */
3344         if (off <= -24 * 60)
3345                 off = -(23 * 60 + 59);
3346
3347         return off * 60;
3348 }
3349
3350 /* calculate timezone offset */
3351 gchar *tzoffset(time_t *now)
3352 {
3353         static gchar offset_string[6];
3354         struct tm gmt, *lt;
3355         gint off;
3356         gchar sign = '+';
3357         struct tm buf1, buf2;
3358
3359         gmt = *gmtime_r(now, &buf1);
3360         lt = localtime_r(now, &buf2);
3361
3362         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3363
3364         if (lt->tm_year < gmt.tm_year)
3365                 off -= 24 * 60;
3366         else if (lt->tm_year > gmt.tm_year)
3367                 off += 24 * 60;
3368         else if (lt->tm_yday < gmt.tm_yday)
3369                 off -= 24 * 60;
3370         else if (lt->tm_yday > gmt.tm_yday)
3371                 off += 24 * 60;
3372
3373         if (off < 0) {
3374                 sign = '-';
3375                 off = -off;
3376         }
3377
3378         if (off >= 24 * 60)             /* should be impossible */
3379                 off = 23 * 60 + 59;     /* if not, insert silly value */
3380
3381         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3382
3383         return offset_string;
3384 }
3385
3386 void get_rfc822_date(gchar *buf, gint len)
3387 {
3388         struct tm *lt;
3389         time_t t;
3390         gchar day[4], mon[4];
3391         gint dd, hh, mm, ss, yyyy;
3392         struct tm buf1;
3393         gchar buf2[BUFFSIZE];
3394
3395         t = time(NULL);
3396         lt = localtime_r(&t, &buf1);
3397
3398         sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3399                day, mon, &dd, &hh, &mm, &ss, &yyyy);
3400
3401         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3402                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3403 }
3404
3405 void debug_set_mode(gboolean mode)
3406 {
3407         debug_mode = mode;
3408 }
3409
3410 gboolean debug_get_mode(void)
3411 {
3412         return debug_mode;
3413 }
3414
3415 void debug_print_real(const gchar *format, ...)
3416 {
3417         va_list args;
3418         gchar buf[BUFFSIZE];
3419
3420         if (!debug_mode) return;
3421
3422         va_start(args, format);
3423         g_vsnprintf(buf, sizeof(buf), format, args);
3424         va_end(args);
3425
3426         g_print("%s", buf);
3427 }
3428
3429
3430 const char * debug_srcname(const char *file)
3431 {
3432         const char *s = strrchr (file, '/');
3433         return s? s+1:file;
3434 }
3435
3436
3437 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3438 {
3439         if (subject == NULL)
3440                 subject = "";
3441         else
3442                 subject += subject_get_prefix_length(subject);
3443
3444         return g_hash_table_lookup(subject_table, subject);
3445 }
3446
3447 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3448                           void * data)
3449 {
3450         if (subject == NULL || *subject == 0)
3451                 return;
3452         subject += subject_get_prefix_length(subject);
3453         g_hash_table_insert(subject_table, subject, data);
3454 }
3455
3456 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3457 {
3458         if (subject == NULL)
3459                 return;
3460
3461         subject += subject_get_prefix_length(subject);
3462         g_hash_table_remove(subject_table, subject);
3463 }
3464
3465 /*!
3466  *\brief        Check if a string is prefixed with known (combinations)
3467  *              of prefixes. The function assumes that each prefix
3468  *              is terminated by zero or exactly _one_ space.
3469  *
3470  *\param        str String to check for a prefixes
3471  *
3472  *\return       int Number of chars in the prefix that should be skipped
3473  *              for a "clean" subject line. If no prefix was found, 0
3474  *              is returned.
3475  */
3476 int subject_get_prefix_length(const gchar *subject)
3477 {
3478         /*!< Array with allowable reply prefixes regexps. */
3479         static const gchar * const prefixes[] = {
3480                 "Re\\:",                        /* "Re:" */
3481                 "Re\\[[1-9][0-9]*\\]\\:",       /* "Re[XXX]:" (non-conforming news mail clients) */
3482                 "Antw\\:",                      /* "Antw:" (Dutch / German Outlook) */
3483                 "Aw\\:",                        /* "Aw:"   (German) */
3484                 "Antwort\\:",                   /* "Antwort:" (German Lotus Notes) */
3485                 "Res\\:",                       /* "Res:" (Brazilian Outlook) */
3486                 "Fw\\:",                        /* "Fw:" Forward */
3487                 "Enc\\:",                       /* "Enc:" Forward (Brazilian Outlook) */
3488                 "Odp\\:",                       /* "Odp:" Re (Polish Outlook) */
3489                 "Rif\\:",                       /* "Rif:" (Italian Outlook) */
3490                 "Sv\\:",                        /* "Sv" (Norwegian) */
3491                 "Vs\\:",                        /* "Vs" (Norwegian) */
3492                 "Ad\\:",                        /* "Ad" (Norwegian) */
3493                 "\347\255\224\345\244\215\\:"   /* "Re" (Chinese, UTF-8) */
3494                 /* add more */
3495         };
3496         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3497         int n;
3498         regmatch_t pos;
3499         static regex_t regex;
3500         static gboolean init_;
3501
3502         if (!subject) return 0;
3503         if (!*subject) return 0;
3504
3505         if (!init_) {
3506                 GString *s = g_string_new("");
3507
3508                 for (n = 0; n < PREFIXES; n++)
3509                         /* Terminate each prefix regexpression by a
3510                          * "\ ?" (zero or ONE space), and OR them */
3511                         g_string_append_printf(s, "(%s\\ ?)%s",
3512                                           prefixes[n],
3513                                           n < PREFIXES - 1 ?
3514                                           "|" : "");
3515
3516                 g_string_prepend(s, "(");
3517                 g_string_append(s, ")+");       /* match at least once */
3518                 g_string_prepend(s, "^\\ *");   /* from beginning of line */
3519
3520
3521                 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3522                  * TODO: Should this be       "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3523                 if (regcomp(&regex, s->str, REG_EXTENDED | REG_ICASE)) {
3524                         debug_print("Error compiling regexp %s\n", s->str);
3525                         g_string_free(s, TRUE);
3526                         return 0;
3527                 } else {
3528                         init_ = TRUE;
3529                         g_string_free(s, TRUE);
3530                 }
3531         }
3532
3533         if (!regexec(&regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3534                 return pos.rm_eo;
3535         else
3536                 return 0;
3537 }
3538
3539 static guint g_stricase_hash(gconstpointer gptr)
3540 {
3541         guint hash_result = 0;
3542         const char *str;
3543
3544         for (str = gptr; str && *str; str++) {
3545                 hash_result += toupper(*str);
3546         }
3547
3548         return hash_result;
3549 }
3550
3551 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3552 {
3553         const char *str1 = gptr1;
3554         const char *str2 = gptr2;
3555
3556         return !strcasecmp(str1, str2);
3557 }
3558
3559 gint g_int_compare(gconstpointer a, gconstpointer b)
3560 {
3561         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3562 }
3563
3564 gchar *generate_msgid(gchar *buf, gint len)
3565 {
3566         struct tm *lt;
3567         time_t t;
3568         gchar *addr;
3569         struct tm buft;
3570
3571         t = time(NULL);
3572         lt = localtime_r(&t, &buft);
3573
3574         if (strcmp(buf, "") == 0) {
3575                 addr = g_strconcat("@", get_domain_name(), NULL);
3576         }
3577         else {
3578                 addr = g_strconcat("@", buf, NULL);
3579         }
3580
3581         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3582                    lt->tm_year + 1900, lt->tm_mon + 1,
3583                    lt->tm_mday, lt->tm_hour,
3584                    lt->tm_min, lt->tm_sec,
3585                    (guint) rand(), addr);
3586
3587         g_free(addr);
3588         return buf;
3589 }
3590
3591 /*
3592    quote_cmd_argument()
3593
3594    return a quoted string safely usable in argument of a command.
3595
3596    code is extracted and adapted from etPan! project -- DINH V. Hoà.
3597 */
3598
3599 gint quote_cmd_argument(gchar * result, guint size,
3600                         const gchar * path)
3601 {
3602         const gchar * p;
3603         gchar * result_p;
3604         guint remaining;
3605
3606         result_p = result;
3607         remaining = size;
3608
3609         for(p = path ; * p != '\0' ; p ++) {
3610
3611                 if (isalnum((guchar)*p) || (* p == '/')) {
3612                         if (remaining > 0) {
3613                                 * result_p = * p;
3614                                 result_p ++;
3615                                 remaining --;
3616                         }
3617                         else {
3618                                 result[size - 1] = '\0';
3619                                 return -1;
3620                         }
3621                 }
3622                 else {
3623                         if (remaining >= 2) {
3624                                 * result_p = '\\';
3625                                 result_p ++;
3626                                 * result_p = * p;
3627                                 result_p ++;
3628                                 remaining -= 2;
3629                         }
3630                         else {
3631                                 result[size - 1] = '\0';
3632                                 return -1;
3633                         }
3634                 }
3635         }
3636         if (remaining > 0) {
3637                 * result_p = '\0';
3638         }
3639         else {
3640                 result[size - 1] = '\0';
3641                 return -1;
3642         }
3643
3644         return 0;
3645 }
3646
3647 typedef struct
3648 {
3649         GNode           *parent;
3650         GNodeMapFunc     func;
3651         gpointer         data;
3652 } GNodeMapData;
3653
3654 static void g_node_map_recursive(GNode *node, gpointer data)
3655 {
3656         GNodeMapData *mapdata = (GNodeMapData *) data;
3657         GNode *newnode;
3658         GNodeMapData newmapdata;
3659         gpointer newdata;
3660
3661         newdata = mapdata->func(node->data, mapdata->data);
3662         if (newdata != NULL) {
3663                 newnode = g_node_new(newdata);
3664                 g_node_append(mapdata->parent, newnode);
3665
3666                 newmapdata.parent = newnode;
3667                 newmapdata.func = mapdata->func;
3668                 newmapdata.data = mapdata->data;
3669
3670                 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3671         }
3672 }
3673
3674 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3675 {
3676         GNode *root;
3677         GNodeMapData mapdata;
3678
3679         g_return_val_if_fail(node != NULL, NULL);
3680         g_return_val_if_fail(func != NULL, NULL);
3681
3682         root = g_node_new(func(node->data, data));
3683
3684         mapdata.parent = root;
3685         mapdata.func = func;
3686         mapdata.data = data;
3687
3688         g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3689
3690         return root;
3691 }
3692
3693 #define HEX_TO_INT(val, hex)                    \
3694 {                                               \
3695         gchar c = hex;                          \
3696                                                 \
3697         if ('0' <= c && c <= '9') {             \
3698                 val = c - '0';                  \
3699         } else if ('a' <= c && c <= 'f') {      \
3700                 val = c - 'a' + 10;             \
3701         } else if ('A' <= c && c <= 'F') {      \
3702                 val = c - 'A' + 10;             \
3703         } else {                                \
3704                 val = -1;                       \
3705         }                                       \
3706 }
3707
3708 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3709 {
3710         gint hi, lo;
3711
3712         HEX_TO_INT(hi, c1);
3713         HEX_TO_INT(lo, c2);
3714
3715         if (hi == -1 || lo == -1)
3716                 return FALSE;
3717
3718         *out = (hi << 4) + lo;
3719         return TRUE;
3720 }
3721
3722 #define INT_TO_HEX(hex, val)            \
3723 {                                       \
3724         if ((val) < 10)                 \
3725                 hex = '0' + (val);      \
3726         else                            \
3727                 hex = 'A' + (val) - 10; \
3728 }
3729
3730 void get_hex_str(gchar *out, guchar ch)
3731 {
3732         gchar hex;
3733
3734         INT_TO_HEX(hex, ch >> 4);
3735         *out++ = hex;
3736         INT_TO_HEX(hex, ch & 0x0f);
3737         *out++ = hex;
3738 }
3739
3740 #undef REF_DEBUG
3741 #ifndef REF_DEBUG
3742 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3743 #else
3744 #define G_PRINT_REF g_print
3745 #endif
3746
3747 /*!
3748  *\brief        Register ref counted pointer. It is based on GBoxed, so should
3749  *              work with anything that uses the GType system. The semantics
3750  *              are similar to a C++ auto pointer, with the exception that
3751  *              C doesn't have automatic closure (calling destructors) when
3752  *              exiting a block scope.
3753  *              Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3754  *              function directly.
3755  *
3756  *\return       GType A GType type.
3757  */
3758 GType g_auto_pointer_register(void)
3759 {
3760         static GType auto_pointer_type;
3761         if (!auto_pointer_type)
3762                 auto_pointer_type =
3763                         g_boxed_type_register_static
3764                                 ("G_TYPE_AUTO_POINTER",
3765                                  (GBoxedCopyFunc) g_auto_pointer_copy,
3766                                  (GBoxedFreeFunc) g_auto_pointer_free);
3767         return auto_pointer_type;
3768 }
3769
3770 /*!
3771  *\brief        Structure with g_new() allocated pointer guarded by the
3772  *              auto pointer
3773  */
3774 typedef struct AutoPointerRef {
3775         void          (*free) (gpointer);
3776         gpointer        pointer;
3777         glong           cnt;
3778 } AutoPointerRef;
3779
3780 /*!
3781  *\brief        The auto pointer opaque structure that references the
3782  *              pointer guard block.
3783  */
3784 typedef struct AutoPointer {
3785         AutoPointerRef *ref;
3786         gpointer        ptr; /*!< access to protected pointer */
3787 } AutoPointer;
3788
3789 /*!
3790  *\brief        Creates an auto pointer for a g_new()ed pointer. Example:
3791  *
3792  *\code
3793  *
3794  *              ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3795  *              ... when assigning, copying and freeing storage elements
3796  *
3797  *              gtk_list_store_new(N_S_COLUMNS,
3798  *                                 G_TYPE_AUTO_POINTER,
3799  *                                 -1);
3800  *
3801  *
3802  *              Template *precious_data = g_new0(Template, 1);
3803  *              g_pointer protect = g_auto_pointer_new(precious_data);
3804  *
3805  *              gtk_list_store_set(container, &iter,
3806  *                                 S_DATA, protect,
3807  *                                 -1);
3808  *
3809  *              ... the gtk_list_store has copied the pointer and
3810  *              ... incremented its reference count, we should free
3811  *              ... the auto pointer (in C++ a destructor would do
3812  *              ... this for us when leaving block scope)
3813  *
3814  *              g_auto_pointer_free(protect);
3815  *
3816  *              ... gtk_list_store_set() now manages the data. When
3817  *              ... *explicitly* requesting a pointer from the list
3818  *              ... store, don't forget you get a copy that should be
3819  *              ... freed with g_auto_pointer_free() eventually.
3820  *
3821  *\endcode
3822  *
3823  *\param        pointer Pointer to be guarded.
3824  *
3825  *\return       GAuto * Pointer that should be used in containers with
3826  *              GType support.
3827  */
3828 GAuto *g_auto_pointer_new(gpointer p)
3829 {
3830         AutoPointerRef *ref;
3831         AutoPointer    *ptr;
3832
3833         if (p == NULL)
3834                 return NULL;
3835
3836         ref = g_new0(AutoPointerRef, 1);
3837         ptr = g_new0(AutoPointer, 1);
3838
3839         ref->pointer = p;
3840         ref->free = g_free;
3841         ref->cnt = 1;
3842
3843         ptr->ref = ref;
3844         ptr->ptr = p;
3845
3846 #ifdef REF_DEBUG
3847         G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3848 #endif
3849         return ptr;
3850 }
3851
3852 /*!
3853  *\brief        Allocate an autopointer using the passed \a free function to
3854  *              free the guarded pointer
3855  */
3856 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3857 {
3858         AutoPointer *aptr;
3859
3860         if (p == NULL)
3861                 return NULL;
3862
3863         aptr = g_auto_pointer_new(p);
3864         aptr->ref->free = free_;
3865         return aptr;
3866 }
3867
3868 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3869 {
3870         if (auto_ptr == NULL)
3871                 return NULL;
3872         return ((AutoPointer *) auto_ptr)->ptr;
3873 }
3874
3875 /*!
3876  *\brief        Copies an auto pointer by. It's mostly not necessary
3877  *              to call this function directly, unless you copy/assign
3878  *              the guarded pointer.
3879  *
3880  *\param        auto_ptr Auto pointer returned by previous call to
3881  *              g_auto_pointer_new_XXX()
3882  *
3883  *\return       gpointer An auto pointer
3884  */
3885 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3886 {
3887         AutoPointer     *ptr;
3888         AutoPointerRef  *ref;
3889         AutoPointer     *newp;
3890
3891         if (auto_ptr == NULL)
3892                 return NULL;
3893
3894         ptr = auto_ptr;
3895         ref = ptr->ref;
3896         newp = g_new0(AutoPointer, 1);
3897
3898         newp->ref = ref;
3899         newp->ptr = ref->pointer;
3900         ++(ref->cnt);
3901
3902 #ifdef REF_DEBUG
3903         G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3904 #endif
3905         return newp;
3906 }
3907
3908 /*!
3909  *\brief        Free an auto pointer
3910  */
3911 void g_auto_pointer_free(GAuto *auto_ptr)
3912 {
3913         AutoPointer     *ptr;
3914         AutoPointerRef  *ref;
3915
3916         if (auto_ptr == NULL)
3917                 return;
3918
3919         ptr = auto_ptr;
3920         ref = ptr->ref;
3921
3922         if (--(ref->cnt) == 0) {
3923 #ifdef REF_DEBUG
3924                 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3925 #endif
3926                 ref->free(ref->pointer);
3927                 g_free(ref);
3928         }
3929 #ifdef REF_DEBUG
3930         else
3931                 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3932 #endif
3933         g_free(ptr);
3934 }
3935
3936 void replace_returns(gchar *str)
3937 {
3938         if (!str)
3939                 return;
3940
3941         while (strstr(str, "\n")) {
3942                 *strstr(str, "\n") = ' ';
3943         }
3944         while (strstr(str, "\r")) {
3945                 *strstr(str, "\r") = ' ';
3946         }
3947 }
3948
3949 /* get_uri_part() - retrieves a URI starting from scanpos.
3950                     Returns TRUE if succesful */
3951 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
3952                              const gchar **bp, const gchar **ep, gboolean hdr)
3953 {
3954         const gchar *ep_;
3955         gint parenthese_cnt = 0;
3956
3957         g_return_val_if_fail(start != NULL, FALSE);
3958         g_return_val_if_fail(scanpos != NULL, FALSE);
3959         g_return_val_if_fail(bp != NULL, FALSE);
3960         g_return_val_if_fail(ep != NULL, FALSE);
3961
3962         *bp = scanpos;
3963
3964         /* find end point of URI */
3965         for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
3966                 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
3967                     !IS_ASCII(*(const guchar *)ep_) ||
3968                     strchr("[]{}<>\"", *ep_)) {
3969                         break;
3970                 } else if (strchr("(", *ep_)) {
3971                         parenthese_cnt++;
3972                 } else if (strchr(")", *ep_)) {
3973                         if (parenthese_cnt > 0)
3974                                 parenthese_cnt--;
3975                         else
3976                                 break;
3977                 }
3978         }
3979
3980         /* no punctuation at end of string */
3981
3982         /* FIXME: this stripping of trailing punctuations may bite with other URIs.
3983          * should pass some URI type to this function and decide on that whether
3984          * to perform punctuation stripping */
3985
3986 #define IS_REAL_PUNCT(ch)       (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
3987
3988         for (; ep_ - 1 > scanpos + 1 &&
3989                IS_REAL_PUNCT(*(ep_ - 1));
3990              ep_--)
3991                 ;
3992
3993 #undef IS_REAL_PUNCT
3994
3995         *ep = ep_;
3996
3997         return TRUE;
3998 }
3999
4000 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4001 {
4002         while (bp && *bp && g_ascii_isspace(*bp))
4003                 bp++;
4004         return g_strndup(bp, ep - bp);
4005 }
4006
4007 /* valid mail address characters */
4008 #define IS_RFC822_CHAR(ch) \
4009         (IS_ASCII(ch) && \
4010          (ch) > 32   && \
4011          (ch) != 127 && \
4012          !g_ascii_isspace(ch) && \
4013          !strchr("(),;<>\"", (ch)))
4014
4015 /* alphabet and number within 7bit ASCII */
4016 #define IS_ASCII_ALNUM(ch)      (IS_ASCII(ch) && g_ascii_isalnum(ch))
4017 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4018
4019 static GHashTable *create_domain_tab(void)
4020 {
4021         static const gchar *toplvl_domains [] = {
4022             "museum", "aero",
4023             "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4024             "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4025             "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4026             "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4027             "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4028             "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4029             "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4030             "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4031             "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4032             "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4033             "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4034             "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4035             "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4036             "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4037             "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4038             "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4039             "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4040             "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4041             "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4042             "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4043             "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4044             "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4045             "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4046             "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4047             "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4048             "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4049         };
4050         gint n;
4051         GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4052
4053         g_return_val_if_fail(htab, NULL);
4054         for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4055                 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4056         return htab;
4057 }
4058
4059 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4060 {
4061         const gint MAX_LVL_DOM_NAME_LEN = 6;
4062         gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4063         const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4064         register gchar *p;
4065
4066         if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4067                 return FALSE;
4068
4069         for (p = buf; p < m &&  first < last; *p++ = *first++)
4070                 ;
4071         *p = 0;
4072
4073         return g_hash_table_lookup(tab, buf) != NULL;
4074 }
4075
4076 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4077 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4078                                const gchar **bp, const gchar **ep, gboolean hdr)
4079 {
4080         /* more complex than the uri part because we need to scan back and forward starting from
4081          * the scan position. */
4082         gboolean result = FALSE;
4083         const gchar *bp_ = NULL;
4084         const gchar *ep_ = NULL;
4085         static GHashTable *dom_tab;
4086         const gchar *last_dot = NULL;
4087         const gchar *prelast_dot = NULL;
4088         const gchar *last_tld_char = NULL;
4089
4090         /* the informative part of the email address (describing the name
4091          * of the email address owner) may contain quoted parts. the
4092          * closure stack stores the last encountered quotes. */
4093         gchar closure_stack[128];
4094         gchar *ptr = closure_stack;
4095
4096         g_return_val_if_fail(start != NULL, FALSE);
4097         g_return_val_if_fail(scanpos != NULL, FALSE);
4098         g_return_val_if_fail(bp != NULL, FALSE);
4099         g_return_val_if_fail(ep != NULL, FALSE);
4100
4101         if (hdr) {
4102                 const gchar *start_quote = NULL;
4103                 const gchar *end_quote = NULL;
4104 search_again:
4105                 /* go to the real start */
4106                 if (start[0] == ',')
4107                         start++;
4108                 if (start[0] == ';')
4109                         start++;
4110                 while (start[0] == '\n' || start[0] == '\r')
4111                         start++;
4112                 while (start[0] == ' ' || start[0] == '\t')
4113                         start++;
4114
4115                 *bp = start;
4116                 
4117                 /* check if there are quotes (to skip , in them) */
4118                 if (*start == '"') {
4119                         start_quote = start;
4120                         start++;
4121                         end_quote = strstr(start, "\"");
4122                 } else {
4123                         start_quote = NULL;
4124                         end_quote = NULL;
4125                 }
4126                 
4127                 /* skip anything between quotes */
4128                 if (start_quote && end_quote) {
4129                         start = end_quote;
4130                         
4131                 } 
4132
4133                 /* find end (either , or ; or end of line) */
4134                 if (strstr(start, ",") && strstr(start, ";"))
4135                         *ep = strstr(start,",") < strstr(start, ";")
4136                                 ? strstr(start, ",") : strstr(start, ";");
4137                 else if (strstr(start, ","))
4138                         *ep = strstr(start, ",");
4139                 else if (strstr(start, ";"))
4140                         *ep = strstr(start, ";");
4141                 else
4142                         *ep = start+strlen(start);
4143
4144                 /* go back to real start */
4145                 if (start_quote && end_quote) {
4146                         start = start_quote;
4147                 }
4148
4149                 /* check there's still an @ in that, or search
4150                  * further if possible */
4151                 if (strstr(start, "@") && strstr(start, "@") < *ep)
4152                         return TRUE;
4153                 else if (*ep < start+strlen(start)) {
4154                         start = *ep;
4155                         goto search_again;
4156                 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4157                         *bp = start_quote;
4158                         return TRUE;
4159                 } else
4160                         return FALSE;
4161         }
4162
4163         if (!dom_tab)
4164                 dom_tab = create_domain_tab();
4165         g_return_val_if_fail(dom_tab, FALSE);
4166
4167         /* scan start of address */
4168         for (bp_ = scanpos - 1;
4169              bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4170                 ;
4171
4172         /* TODO: should start with an alnum? */
4173         bp_++;
4174         for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4175                 ;
4176
4177         if (bp_ != scanpos) {
4178                 /* scan end of address */
4179                 for (ep_ = scanpos + 1;
4180                      *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4181                         if (*ep_ == '.') {
4182                                 prelast_dot = last_dot;
4183                                 last_dot = ep_;
4184                                 if (*(last_dot + 1) == '.') {
4185                                         if (prelast_dot == NULL)
4186                                                 return FALSE;
4187                                         last_dot = prelast_dot;
4188                                         break;
4189                                 }
4190                         }
4191
4192                 /* TODO: really should terminate with an alnum? */
4193                 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4194                      --ep_)
4195                         ;
4196                 ep_++;
4197
4198                 if (last_dot == NULL)
4199                         return FALSE;
4200                 if (last_dot >= ep_)
4201                         last_dot = prelast_dot;
4202                 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4203                         return FALSE;
4204                 last_dot++;
4205
4206                 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4207                         if (*last_tld_char == '?')
4208                                 break;
4209
4210                 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4211                         result = TRUE;
4212
4213                 *ep = ep_;
4214                 *bp = bp_;
4215         }
4216
4217         if (!result) return FALSE;
4218
4219         if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4220         && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4221         && IS_RFC822_CHAR(*(ep_ + 3))) {
4222                 /* this informative part with an @ in it is
4223                  * followed by the email address */
4224                 ep_ += 3;
4225
4226                 /* go to matching '>' (or next non-rfc822 char, like \n) */
4227                 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4228                         ;
4229
4230                 /* include the bracket */
4231                 if (*ep_ == '>') ep_++;
4232
4233                 /* include the leading quote */
4234                 bp_--;
4235
4236                 *ep = ep_;
4237                 *bp = bp_;
4238                 return TRUE;
4239         }
4240
4241         /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4242         if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4243                 return FALSE;
4244
4245         /* see if this is <bracketed>; in this case we also scan for the informative part. */
4246         if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4247                 return TRUE;
4248
4249 #define FULL_STACK()    ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4250 #define IN_STACK()      (ptr > closure_stack)
4251 /* has underrun check */
4252 #define POP_STACK()     if(IN_STACK()) --ptr
4253 /* has overrun check */
4254 #define PUSH_STACK(c)   if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4255 /* has underrun check */
4256 #define PEEK_STACK()    (IN_STACK() ? *(ptr - 1) : 0)
4257
4258         ep_++;
4259
4260         /* scan for the informative part. */
4261         for (bp_ -= 2; bp_ >= start; bp_--) {
4262                 /* if closure on the stack keep scanning */
4263                 if (PEEK_STACK() == *bp_) {
4264                         POP_STACK();
4265                         continue;
4266                 }
4267                 if (*bp_ == '\'' || *bp_ == '"') {
4268                         PUSH_STACK(*bp_);
4269                         continue;
4270                 }
4271
4272                 /* if nothing in the closure stack, do the special conditions
4273                  * the following if..else expression simply checks whether
4274                  * a token is acceptable. if not acceptable, the clause
4275                  * should terminate the loop with a 'break' */
4276                 if (!PEEK_STACK()) {
4277                         if (*bp_ == '-'
4278                         && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4279                         && (((bp_ + 1) < ep_)    && isalnum(*(bp_ + 1)))) {
4280                                 /* hyphens are allowed, but only in
4281                                    between alnums */
4282                         } else if (strchr(" \"'", *bp_)) {
4283                                 /* but anything not being a punctiation
4284                                    is ok */
4285                         } else {
4286                                 break; /* anything else is rejected */
4287                         }
4288                 }
4289         }
4290
4291         bp_++;
4292
4293         /* scan forward (should start with an alnum) */
4294         for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4295                 ;
4296 #undef PEEK_STACK
4297 #undef PUSH_STACK
4298 #undef POP_STACK
4299 #undef IN_STACK
4300 #undef FULL_STACK
4301
4302
4303         *bp = bp_;
4304         *ep = ep_;
4305
4306         return result;
4307 }
4308
4309 #undef IS_QUOTE
4310 #undef IS_ASCII_ALNUM
4311 #undef IS_RFC822_CHAR
4312
4313 gchar *make_email_string(const gchar *bp, const gchar *ep)
4314 {
4315         /* returns a mailto: URI; mailto: is also used to detect the
4316          * uri type later on in the button_pressed signal handler */
4317         gchar *tmp;
4318         gchar *result;
4319
4320         tmp = g_strndup(bp, ep - bp);
4321         result = g_strconcat("mailto:", tmp, NULL);
4322         g_free(tmp);
4323
4324         return result;
4325 }
4326
4327 gchar *make_http_string(const gchar *bp, const gchar *ep)
4328 {
4329         /* returns an http: URI; */
4330         gchar *tmp;
4331         gchar *result;
4332
4333         while (bp && *bp && g_ascii_isspace(*bp))
4334                 bp++;
4335         tmp = g_strndup(bp, ep - bp);
4336         result = g_strconcat("http://", tmp, NULL);
4337         g_free(tmp);
4338
4339         return result;
4340 }
4341
4342 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4343 {
4344         FILE *fp = fopen(path, "rb");
4345         gchar buf[BUFFSIZE];
4346         gchar *result = NULL;
4347         if (!fp)
4348                 return NULL;
4349         while (fgets(buf, sizeof (buf), fp) != NULL) {
4350                 gchar **parts = g_strsplit(buf, ";", 3);
4351                 gchar *trimmed = parts[0];
4352                 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4353                         trimmed++;
4354                 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4355                         trimmed[strlen(trimmed)-1] = '\0';
4356
4357                 if (!strcmp(trimmed, type)) {
4358                         gboolean needsterminal = FALSE;
4359                         if (parts[2] && strstr(parts[2], "needsterminal")) {
4360                                 needsterminal = TRUE;
4361                         }
4362                         if (parts[2] && strstr(parts[2], "test=")) {
4363                                 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4364                                 gchar *testcmd = orig_testcmd;
4365                                 if (strstr(testcmd,";"))
4366                                         *(strstr(testcmd,";")) = '\0';
4367                                 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4368                                         testcmd++;
4369                                 while (testcmd[strlen(testcmd)-1] == '\n')
4370                                         testcmd[strlen(testcmd)-1] = '\0';
4371                                 while (testcmd[strlen(testcmd)-1] == '\r')
4372                                         testcmd[strlen(testcmd)-1] = '\0';
4373                                 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4374                                         testcmd[strlen(testcmd)-1] = '\0';
4375                                         
4376                                 if (strstr(testcmd, "%s")) {
4377                                         gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4378                                         gint res = system(tmp);
4379                                         g_free(tmp);
4380                                         g_free(orig_testcmd);
4381                                         
4382                                         if (res != 0) {
4383                                                 g_strfreev(parts);
4384                                                 continue;
4385                                         }
4386                                 } else {
4387                                         gint res = system(testcmd);
4388                                         g_free(orig_testcmd);
4389                                         
4390                                         if (res != 0) {
4391                                                 g_strfreev(parts);
4392                                                 continue;
4393                                         }
4394                                 }
4395                         }
4396                         
4397                         trimmed = parts[1];
4398                         while (trimmed[0] == ' ' || trimmed[0] == '\t')
4399                                 trimmed++;
4400                         while (trimmed[strlen(trimmed)-1] == '\n')
4401                                 trimmed[strlen(trimmed)-1] = '\0';
4402                         while (trimmed[strlen(trimmed)-1] == '\r')
4403                                 trimmed[strlen(trimmed)-1] = '\0';
4404                         while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4405                                 trimmed[strlen(trimmed)-1] = '\0';
4406                         result = g_strdup(trimmed);
4407                         g_strfreev(parts);
4408                         fclose(fp);
4409                         /* if there are no single quotes around %s, add them.
4410                          * '.*%s.*' is ok, as in display 'png:%s'
4411                          */
4412                         if (strstr(result, "%s") 
4413                         && !(strstr(result, "'") < strstr(result,"%s") &&
4414                              strstr(strstr(result,"%s"), "'"))) {
4415                                 gchar *start = g_strdup(result);
4416                                 gchar *end = g_strdup(strstr(result, "%s")+2);
4417                                 gchar *tmp;
4418                                 *strstr(start, "%s") = '\0';
4419                                 tmp = g_strconcat(start,"'%s'",end, NULL);
4420                                 g_free(start);
4421                                 g_free(end);
4422                                 g_free(result);
4423                                 result = tmp;
4424                         }
4425                         if (needsterminal) {
4426                                 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4427                                 g_free(result);
4428                                 result = tmp;
4429                         }
4430                         return result;
4431                 }
4432                 g_strfreev(parts);
4433         }
4434         fclose(fp);
4435         return NULL;
4436 }
4437 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4438 {
4439         gchar *result = NULL;
4440         gchar *path = NULL;
4441         if (type == NULL)
4442                 return NULL;
4443         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4444         result = mailcap_get_command_in_file(path, type, file_to_open);
4445         g_free(path);
4446         if (result)
4447                 return result;
4448         result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4449         return result;
4450 }
4451
4452 void mailcap_update_default(const gchar *type, const gchar *command)
4453 {
4454         gchar *path = NULL, *outpath = NULL;
4455         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4456         outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4457         FILE *fp = fopen(path, "rb");
4458         FILE *outfp = fopen(outpath, "wb");
4459         gchar buf[BUFFSIZE];
4460         gboolean err = FALSE;
4461
4462         if (!outfp) {
4463                 g_free(path);
4464                 g_free(outpath);
4465                 fclose(fp);
4466                 return;
4467         }
4468         while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4469                 gchar **parts = g_strsplit(buf, ";", 3);
4470                 gchar *trimmed = parts[0];
4471                 while (trimmed[0] == ' ')
4472                         trimmed++;
4473                 while (trimmed[strlen(trimmed)-1] == ' ')
4474                         trimmed[strlen(trimmed)-1] = '\0';
4475
4476                 if (!strcmp(trimmed, type)) {
4477                         g_strfreev(parts);
4478                         continue;
4479                 }
4480                 else {
4481                         if(fputs(buf, outfp) == EOF) {
4482                                 err = TRUE;
4483                                 break;
4484                         }
4485                 }
4486                 g_strfreev(parts);
4487         }
4488         if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4489                 err = TRUE;
4490
4491         if (fp)
4492                 fclose(fp);
4493
4494         if (fclose(outfp) == EOF)
4495                 err = TRUE;
4496                 
4497         if (!err)
4498                 g_rename(outpath, path);
4499
4500         g_free(path);
4501         g_free(outpath);
4502 }
4503
4504 gint copy_dir(const gchar *src, const gchar *dst)
4505 {
4506         GDir *dir;
4507         const gchar *name;
4508
4509         if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4510                 g_warning("failed to open directory: %s\n", src);
4511                 return -1;
4512         }
4513
4514         if (make_dir(dst) < 0)
4515                 return -1;
4516
4517         while ((name = g_dir_read_name(dir)) != NULL) {
4518                 gchar *old_file, *new_file;
4519                 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4520                 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4521                 debug_print("copying: %s -> %s\n", old_file, new_file);
4522                 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4523                         gint r = copy_file(old_file, new_file, TRUE);
4524                         if (r < 0) {
4525                                 g_dir_close(dir);
4526                                 return r;
4527                         }
4528                 }
4529 #ifndef G_OS_WIN32
4530                 /* Windows has no symlinks.  Or well, Vista seems to
4531                    have something like this but the semantics might be
4532                    different.  Thus we don't use it under Windows. */
4533                  else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4534                         GError *error;
4535                         gint r = 0;
4536                         gchar *target = g_file_read_link(old_file, &error);
4537                         if (target)
4538                                 r = symlink(target, new_file);
4539                         g_free(target);
4540                         if (r < 0) {
4541                                 g_dir_close(dir);
4542                                 return r;
4543                         }
4544                  }
4545 #endif /*G_OS_WIN32*/
4546                 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4547                         gint r = copy_dir(old_file, new_file);
4548                         if (r < 0) {
4549                                 g_dir_close(dir);
4550                                 return r;
4551                         }
4552                 }
4553         }
4554         g_dir_close(dir);
4555         return 0;
4556 }
4557
4558 /* crude test to see if a file is an email. */
4559 gboolean file_is_email (const gchar *filename)
4560 {
4561         FILE *fp = NULL;
4562         gchar buffer[2048];
4563         gint i = 0;
4564         gint score = 0;
4565         if (filename == NULL)
4566                 return FALSE;
4567         if ((fp = g_fopen(filename, "rb")) == NULL)
4568                 return FALSE;
4569         while (i < 60 && score < 3
4570                && fgets(buffer, sizeof (buffer), fp) > 0) {
4571                 if (!strncmp(buffer, "From:", strlen("From:")))
4572                         score++;
4573                 if (!strncmp(buffer, "To:", strlen("To:")))
4574                         score++;
4575                 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4576                         score++;
4577                 i++;
4578         }
4579         fclose(fp);
4580         return (score >= 3);
4581 }
4582
4583 gboolean sc_g_list_bigger(GList *list, gint max)
4584 {
4585         GList *cur = list;
4586         int i = 0;
4587         while (cur && i <= max+1) {
4588                 i++;
4589                 cur = cur->next;
4590         }
4591         return (i > max);
4592 }
4593
4594 gboolean sc_g_slist_bigger(GSList *list, gint max)
4595 {
4596         GSList *cur = list;
4597         int i = 0;
4598         while (cur && i <= max+1) {
4599                 i++;
4600                 cur = cur->next;
4601         }
4602         return (i > max);
4603 }
4604
4605 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4606 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
4607                              NULL, NULL, NULL, NULL, NULL, NULL};
4608 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4609 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
4610                              NULL, NULL, NULL, NULL, NULL, NULL};
4611 const gchar *s_am_up = NULL;
4612 const gchar *s_pm_up = NULL;
4613 const gchar *s_am_low = NULL;
4614 const gchar *s_pm_low = NULL;
4615 const gchar *def_loc_format = NULL;
4616 const gchar *date_loc_format = NULL;
4617 const gchar *time_loc_format = NULL;
4618 const gchar *time_am_pm = NULL;
4619
4620 static gboolean time_names_init_done = FALSE;
4621
4622 static void init_time_names(void)
4623 {
4624         daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4625         daynames[1] = Q_("Complete day name for use by strftime|Monday");
4626         daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4627         daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4628         daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4629         daynames[5] = Q_("Complete day name for use by strftime|Friday");
4630         daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4631         
4632         monthnames[0] = Q_("Complete month name for use by strftime|January");
4633         monthnames[1] = Q_("Complete month name for use by strftime|February");
4634         monthnames[2] = Q_("Complete month name for use by strftime|March");
4635         monthnames[3] = Q_("Complete month name for use by strftime|April");
4636         monthnames[4] = Q_("Complete month name for use by strftime|May");
4637         monthnames[5] = Q_("Complete month name for use by strftime|June");
4638         monthnames[6] = Q_("Complete month name for use by strftime|July");
4639         monthnames[7] = Q_("Complete month name for use by strftime|August");
4640         monthnames[8] = Q_("Complete month name for use by strftime|September");
4641         monthnames[9] = Q_("Complete month name for use by strftime|October");
4642         monthnames[10] = Q_("Complete month name for use by strftime|November");
4643         monthnames[11] = Q_("Complete month name for use by strftime|December");
4644
4645         s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4646         s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4647         s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4648         s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4649         s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4650         s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4651         s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4652         
4653         s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4654         s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4655         s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4656         s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4657         s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4658         s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4659         s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4660         s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4661         s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4662         s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4663         s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4664         s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4665
4666         s_am_up = Q_("For use by strftime (morning)|AM");
4667         s_pm_up = Q_("For use by strftime (afternoon)|PM");
4668         s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4669         s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4670         
4671         def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4672         date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4673         time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4674
4675         time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4676
4677         time_names_init_done = TRUE;
4678 }
4679
4680 #define CHECK_SIZE() {                  \
4681         total_done += len;              \
4682         if (total_done >= buflen) {     \
4683                 buf[buflen-1] = '\0';   \
4684                 return 0;               \
4685         }                               \
4686 }
4687
4688 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4689 {
4690         gchar *curpos = buf;
4691         gint total_done = 0;
4692         gchar subbuf[64], subfmt[64];
4693         static time_t last_tzset = (time_t)0;
4694         
4695         if (!time_names_init_done)
4696                 init_time_names();
4697         
4698         if (format == NULL || lt == NULL)
4699                 return 0;
4700                 
4701         if (last_tzset != time(NULL)) {
4702                 tzset();
4703                 last_tzset = time(NULL);
4704         }
4705         while(*format) {
4706                 if (*format == '%') {
4707                         gint len = 0, tmp = 0;
4708                         format++;
4709                         switch(*format) {
4710                         case '%':
4711                                 len = 1; CHECK_SIZE();
4712                                 *curpos = '%';
4713                                 break;
4714                         case 'a':
4715                                 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4716                                 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4717                                 break;
4718                         case 'A':
4719                                 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4720                                 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4721                                 break;
4722                         case 'b':
4723                         case 'h':
4724                                 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4725                                 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4726                                 break;
4727                         case 'B':
4728                                 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4729                                 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4730                                 break;
4731                         case 'c':
4732                                 fast_strftime(subbuf, 64, def_loc_format, lt);
4733                                 len = strlen(subbuf); CHECK_SIZE();
4734                                 strncpy2(curpos, subbuf, buflen - total_done);
4735                                 break;
4736                         case 'C':
4737                                 total_done += 2; CHECK_SIZE();
4738                                 tmp = (lt->tm_year + 1900)/100;
4739                                 *curpos++ = '0'+(tmp / 10);
4740                                 *curpos++ = '0'+(tmp % 10);
4741                                 break;
4742                         case 'd':
4743                                 total_done += 2; CHECK_SIZE();
4744                                 *curpos++ = '0'+(lt->tm_mday / 10);
4745                                 *curpos++ = '0'+(lt->tm_mday % 10);
4746                                 break;
4747                         case 'D':
4748                                 total_done += 8; CHECK_SIZE();
4749                                 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4750                                 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4751                                 *curpos++ = '/';
4752                                 *curpos++ = '0'+(lt->tm_mday / 10);
4753                                 *curpos++ = '0'+(lt->tm_mday % 10);
4754                                 *curpos++ = '/';
4755                                 tmp = lt->tm_year%100;
4756                                 *curpos++ = '0'+(tmp / 10);
4757                                 *curpos++ = '0'+(tmp % 10);
4758                                 break;
4759                         case 'e':
4760                                 len = 2; CHECK_SIZE();
4761                                 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4762                                 break;
4763                         case 'F':
4764                                 len = 10; CHECK_SIZE();
4765                                 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d", 
4766                                         lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4767                                 break;
4768                         case 'H':
4769                                 total_done += 2; CHECK_SIZE();
4770                                 *curpos++ = '0'+(lt->tm_hour / 10);
4771                                 *curpos++ = '0'+(lt->tm_hour % 10);
4772                                 break;
4773                         case 'I':
4774                                 total_done += 2; CHECK_SIZE();
4775                                 tmp = lt->tm_hour;
4776                                 if (tmp > 12)
4777                                         tmp -= 12;
4778                                 else if (tmp == 0)
4779                                         tmp = 12;
4780                                 *curpos++ = '0'+(tmp / 10);
4781                                 *curpos++ = '0'+(tmp % 10);
4782                                 break;
4783                         case 'j':
4784                                 len = 3; CHECK_SIZE();
4785                                 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4786                                 break;
4787                         case 'k':
4788                                 len = 2; CHECK_SIZE();
4789                                 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4790                                 break;
4791                         case 'l':
4792                                 len = 2; CHECK_SIZE();
4793                                 tmp = lt->tm_hour;
4794                                 if (tmp > 12)
4795                                         tmp -= 12;
4796                                 else if (tmp == 0)
4797                                         tmp = 12;
4798                                 snprintf(curpos, buflen - total_done, "%2d", tmp);
4799                                 break;
4800                         case 'm':
4801                                 total_done += 2; CHECK_SIZE();
4802                                 tmp = lt->tm_mon + 1;
4803                                 *curpos++ = '0'+(tmp / 10);
4804                                 *curpos++ = '0'+(tmp % 10);
4805                                 break;
4806                         case 'M':
4807                                 total_done += 2; CHECK_SIZE();
4808                                 *curpos++ = '0'+(lt->tm_min / 10);
4809                                 *curpos++ = '0'+(lt->tm_min % 10);
4810                                 break;
4811                         case 'n':
4812                                 len = 1; CHECK_SIZE();
4813                                 *curpos = '\n';
4814                                 break;
4815                         case 'p':
4816                                 if (lt->tm_hour >= 12) {
4817                                         len = strlen(s_pm_up); CHECK_SIZE();
4818                                         snprintf(curpos, buflen-total_done, s_pm_up);
4819                                 } else {
4820                                         len = strlen(s_am_up); CHECK_SIZE();
4821                                         snprintf(curpos, buflen-total_done, s_am_up);
4822                                 }
4823                                 break;
4824                         case 'P':
4825                                 if (lt->tm_hour >= 12) {
4826                                         len = strlen(s_pm_low); CHECK_SIZE();
4827                                         snprintf(curpos, buflen-total_done, s_pm_low);
4828                                 } else {
4829                                         len = strlen(s_am_low); CHECK_SIZE();
4830                                         snprintf(curpos, buflen-total_done, s_am_low);
4831                                 }
4832                                 break;
4833                         case 'r':
4834                                 fast_strftime(subbuf, 64, time_am_pm, lt);
4835                                 len = strlen(subbuf); CHECK_SIZE();
4836                                 strncpy2(curpos, subbuf, buflen - total_done);
4837                                 break;
4838                         case 'R':
4839                                 total_done += 5; CHECK_SIZE();
4840                                 *curpos++ = '0'+(lt->tm_hour / 10);
4841                                 *curpos++ = '0'+(lt->tm_hour % 10);
4842                                 *curpos++ = ':';
4843                                 *curpos++ = '0'+(lt->tm_min / 10);
4844                                 *curpos++ = '0'+(lt->tm_min % 10);
4845                                 break;
4846                         case 's':
4847                                 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4848                                 len = strlen(subbuf); CHECK_SIZE();
4849                                 strncpy2(curpos, subbuf, buflen - total_done);
4850                                 break;
4851                         case 'S':
4852                                 total_done += 2; CHECK_SIZE();
4853                                 *curpos++ = '0'+(lt->tm_sec / 10);
4854                                 *curpos++ = '0'+(lt->tm_sec % 10);
4855                                 break;
4856                         case 't':
4857                                 len = 1; CHECK_SIZE();
4858                                 *curpos = '\t';
4859                                 break;
4860                         case 'T':
4861                                 total_done += 8; CHECK_SIZE();
4862                                 *curpos++ = '0'+(lt->tm_hour / 10);
4863                                 *curpos++ = '0'+(lt->tm_hour % 10);
4864                                 *curpos++ = ':';
4865                                 *curpos++ = '0'+(lt->tm_min / 10);
4866                                 *curpos++ = '0'+(lt->tm_min % 10);
4867                                 *curpos++ = ':';
4868                                 *curpos++ = '0'+(lt->tm_sec / 10);
4869                                 *curpos++ = '0'+(lt->tm_sec % 10);
4870                                 break;
4871                         case 'u':
4872                                 len = 1; CHECK_SIZE();
4873                                 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4874                                 break;
4875                         case 'w':
4876                                 len = 1; CHECK_SIZE();
4877                                 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4878                                 break;
4879                         case 'x':
4880                                 fast_strftime(subbuf, 64, date_loc_format, lt);
4881                                 len = strlen(subbuf); CHECK_SIZE();
4882                                 strncpy2(curpos, subbuf, buflen - total_done);
4883                                 break;
4884                         case 'X':
4885                                 fast_strftime(subbuf, 64, time_loc_format, lt);
4886                                 len = strlen(subbuf); CHECK_SIZE();
4887                                 strncpy2(curpos, subbuf, buflen - total_done);
4888                                 break;
4889                         case 'y':
4890                                 total_done += 2; CHECK_SIZE();
4891                                 tmp = lt->tm_year%100;
4892                                 *curpos++ = '0'+(tmp / 10);
4893                                 *curpos++ = '0'+(tmp % 10);
4894                                 break;
4895                         case 'Y':
4896                                 len = 4; CHECK_SIZE();
4897                                 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4898                                 break;
4899                         case 'G':
4900                         case 'g':
4901                         case 'U':
4902                         case 'V':
4903                         case 'W':
4904                         case 'z':
4905                         case 'Z':
4906                         case '+':
4907                                 /* let these complicated ones be done with the libc */
4908                                 snprintf(subfmt, 64, "%%%c", *format);
4909                                 strftime(subbuf, 64, subfmt, lt);
4910                                 len = strlen(subbuf); CHECK_SIZE();
4911                                 strncpy2(curpos, subbuf, buflen - total_done);
4912                                 break;
4913                         case 'E':
4914                         case 'O':
4915                                 /* let these complicated modifiers be done with the libc */
4916                                 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4917                                 strftime(subbuf, 64, subfmt, lt);
4918                                 len = strlen(subbuf); CHECK_SIZE();
4919                                 strncpy2(curpos, subbuf, buflen - total_done);
4920                                 format++;
4921                                 break;
4922                         default:
4923                                 g_warning("format error (%c)", *format);
4924                                 *curpos = '\0';
4925                                 return total_done;
4926                         }
4927                         curpos += len;
4928                         format++;
4929                 } else {
4930                         int len = 1; CHECK_SIZE();
4931                         *curpos++ = *format++; 
4932                 }
4933         }
4934         *curpos++ = '\0';
4935         return total_done;
4936 }