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