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