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