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