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