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