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