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