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