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