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