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