2009-10-21 [wwp] 3.7.3cvs7
[claws.git] / src / common / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2009 Hiroyuki Yamamoto & The Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27
28 #include <glib/gi18n.h>
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/param.h>
35
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
37 #  include <wchar.h>
38 #  include <wctype.h>
39 #endif
40 #include <stdlib.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <stdarg.h>
44 #include <sys/types.h>
45 #if HAVE_SYS_WAIT_H
46 #  include <sys/wait.h>
47 #endif
48 #include <dirent.h>
49 #include <time.h>
50 #include <regex.h>
51
52 #ifdef G_OS_UNIX
53 #include <sys/utsname.h>
54 #endif
55
56 #include <fcntl.h>
57
58 #ifdef G_OS_WIN32
59 #  include <direct.h>
60 #  include <io.h>
61 #  include <w32lib.h>
62 #endif
63
64 #ifdef MAEMO
65 #include <libosso.h>
66 #ifdef CHINOOK
67 # include <tablet-browser-interface.h>
68 #else
69 # include <osso-browser-interface.h>
70 #endif
71 #endif
72
73 #include "utils.h"
74 #include "socket.h"
75 #include "../codeconv.h"
76
77 #define BUFFSIZE        8192
78
79 static gboolean debug_mode = FALSE;
80 #ifdef G_OS_WIN32
81 static GSList *tempfiles=NULL;
82 #endif
83
84 /* Return true if we are running as root.  This function should beused
85    instead of getuid () == 0.  */
86 gboolean superuser_p (void)
87 {
88 #ifdef G_OS_WIN32
89   return w32_is_administrator ();
90 #else
91   return !getuid();
92 #endif  
93 }
94
95
96
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
99 {
100 #ifdef G_OS_WIN32
101         if (G_WIN32_HAVE_WIDECHAR_API()) {
102                 wchar_t *wpath;
103                 gint retval;
104                 gint save_errno;
105
106                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
107                 if (wpath == NULL) {
108                         errno = EINVAL;
109                         return -1;
110                 }
111
112                 retval = _wchdir(wpath);
113                 save_errno = errno;
114
115                 g_free(wpath);
116
117                 errno = save_errno;
118                 return retval;
119         } else {
120                 gchar *cp_path;
121                 gint retval;
122                 gint save_errno;
123
124                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125                 if (cp_path == NULL) {
126                         errno = EINVAL;
127                         return -1;
128                 }
129
130                 retval = chdir(cp_path);
131                 save_errno = errno;
132
133                 g_free(cp_path);
134
135                 errno = save_errno;
136                 return retval;
137         }
138 #else
139         return chdir(path);
140 #endif
141 }
142
143 gint g_chmod(const gchar *path, gint mode)
144 {
145 #ifdef G_OS_WIN32
146         if (G_WIN32_HAVE_WIDECHAR_API()) {
147                 wchar_t *wpath;
148                 gint retval;
149                 gint save_errno;
150
151                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
152                 if (wpath == NULL) {
153                         errno = EINVAL;
154                         return -1;
155                 }
156
157                 retval = _wchmod(wpath, mode);
158                 save_errno = errno;
159
160                 g_free(wpath);
161
162                 errno = save_errno;
163                 return retval;
164         } else {
165                 gchar *cp_path;
166                 gint retval;
167                 gint save_errno;
168
169                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170                 if (cp_path == NULL) {
171                         errno = EINVAL;
172                         return -1;
173                 }
174
175                 retval = chmod(cp_path, mode);
176                 save_errno = errno;
177
178                 g_free(cp_path);
179
180                 errno = save_errno;
181                 return retval;
182         }
183 #else
184         return chmod(path, mode);
185 #endif
186 }
187
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
189 {
190 #ifdef G_OS_WIN32
191         char *name = g_win32_locale_filename_from_utf8(filename);
192         FILE* fp = fopen(name, mode);
193         g_free(name);
194         return fp;
195 #else
196         return fopen(filename, mode);
197 #endif
198 }
199 int g_open(const gchar *filename, int flags, int mode)
200 {
201 #ifdef G_OS_WIN32
202         char *name = g_win32_locale_filename_from_utf8(filename);
203         int fd = open(name, flags, mode);
204         g_free(name);
205         return fp;
206 #else
207         return open(filename, flags, mode);
208 #endif
209 }
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
211
212
213 #ifdef G_OS_WIN32
214 gint mkstemp_name(gchar *template, gchar **name_used)
215 {
216         static gulong count=0; /* W32-_mktemp only supports up to 27
217                                   tempfiles... */
218         int tmpfd;
219
220         *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221         tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222                                     (S_IRUSR | S_IWUSR));
223
224         tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
225         if (tmpfd<0) {
226                 perror(g_strdup_printf("cant create %s",*name_used));
227                 return -1;
228         }
229         else
230                 return tmpfd;
231 }
232 #endif /* G_OS_WIN32 */
233
234 #ifdef G_OS_WIN32
235 gint mkstemp(gchar *template)
236 {
237         gchar *dummyname;
238         gint res = mkstemp_name(template, &dummyname);
239         g_free(dummyname);
240         return res;
241 }
242 #endif /* G_OS_WIN32 */
243
244 void list_free_strings(GList *list)
245 {
246         list = g_list_first(list);
247
248         while (list != NULL) {
249                 g_free(list->data);
250                 list = list->next;
251         }
252 }
253
254 void slist_free_strings(GSList *list)
255 {
256         while (list != NULL) {
257                 g_free(list->data);
258                 list = list->next;
259         }
260 }
261
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
263 {
264         g_free(key);
265 }
266
267 void hash_free_strings(GHashTable *table)
268 {
269         g_hash_table_foreach(table, hash_free_strings_func, NULL);
270 }
271
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
273 {
274         return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
275 }
276
277 guint str_case_hash(gconstpointer key)
278 {
279         const gchar *p = key;
280         guint h = *p;
281
282         if (h) {
283                 h = g_ascii_tolower(h);
284                 for (p += 1; *p != '\0'; p++)
285                         h = (h << 5) - h + g_ascii_tolower(*p);
286         }
287
288         return h;
289 }
290
291 void ptr_array_free_strings(GPtrArray *array)
292 {
293         gint i;
294         gchar *str;
295
296         cm_return_if_fail(array != NULL);
297
298         for (i = 0; i < array->len; i++) {
299                 str = g_ptr_array_index(array, i);
300                 g_free(str);
301         }
302 }
303
304 gboolean str_find(const gchar *haystack, const gchar *needle)
305 {
306         return strstr(haystack, needle) != NULL ? TRUE : FALSE;
307 }
308
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
310 {
311         return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
312 }
313
314 gint to_number(const gchar *nstr)
315 {
316         register const gchar *p;
317
318         if (*nstr == '\0') return -1;
319
320         for (p = nstr; *p != '\0'; p++)
321                 if (!g_ascii_isdigit(*p)) return -1;
322
323         return atoi(nstr);
324 }
325
326 /* convert integer into string,
327    nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
329 {
330         g_snprintf(nstr, 11, "%d", n);
331         return nstr;
332 }
333
334 /* convert integer into string */
335 gchar *itos(gint n)
336 {
337         static gchar nstr[11];
338
339         return itos_buf(nstr, n);
340 }
341
342 #define divide(num,divisor,i,d)         \
343 {                                       \
344         i = num >> divisor;             \
345         d = num & ((1<<divisor)-1);     \
346         d = (d*100) >> divisor;         \
347 }
348
349
350 /*!
351  * \brief Convert a given size in bytes in a human-readable string
352  *
353  * \param size  The size expressed in bytes to convert in string
354  * \return      The string that respresents the size in an human-readable way
355  */
356 gchar *to_human_readable(goffset size)
357 {
358         static gchar str[14];
359         static gchar *b_format = NULL, *kb_format = NULL, 
360                      *mb_format = NULL, *gb_format = NULL;
361         register int t = 0, r = 0;
362         if (b_format == NULL) {
363                 b_format  = _("%dB");
364                 kb_format = _("%d.%02dKB");
365                 mb_format = _("%d.%02dMB");
366                 gb_format = _("%.2fGB");
367         }
368         
369         if (size < (goffset)1024) {
370                 g_snprintf(str, sizeof(str), b_format, (gint)size);
371                 return str;
372         } else if (size >> 10 < (goffset)1024) {
373                 divide(size, 10, t, r);
374                 g_snprintf(str, sizeof(str), kb_format, t, r);
375                 return str;
376         } else if (size >> 20 < (goffset)1024) {
377                 divide(size, 20, t, r);
378                 g_snprintf(str, sizeof(str), mb_format, t, r);
379                 return str;
380         } else {
381                 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
382                 return str;
383         }
384 }
385
386 /* strcmp with NULL-checking */
387 gint strcmp2(const gchar *s1, const gchar *s2)
388 {
389         if (s1 == NULL || s2 == NULL)
390                 return -1;
391         else
392                 return strcmp(s1, s2);
393 }
394 /* strstr with NULL-checking */
395 gchar *strstr2(const gchar *s1, const gchar *s2)
396 {
397         if (s1 == NULL || s2 == NULL)
398                 return NULL;
399         else
400                 return strstr(s1, s2);
401 }
402 /* compare paths */
403 gint path_cmp(const gchar *s1, const gchar *s2)
404 {
405         gint len1, len2;
406         int rc;
407 #ifdef G_OS_WIN32
408         gchar *s1buf, *s2buf;
409 #endif
410
411         if (s1 == NULL || s2 == NULL) return -1;
412         if (*s1 == '\0' || *s2 == '\0') return -1;
413
414 #ifdef G_OS_WIN32
415         s1buf = g_strdup (s1);
416         s2buf = g_strdup (s2);
417         subst_char (s1buf, '/', G_DIR_SEPARATOR);
418         subst_char (s2buf, '/', G_DIR_SEPARATOR);
419         s1 = s1buf;
420         s2 = s2buf;
421 #endif /* !G_OS_WIN32 */
422
423         len1 = strlen(s1);
424         len2 = strlen(s2);
425
426         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
427         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
428
429         rc = strncmp(s1, s2, MAX(len1, len2));
430 #ifdef G_OS_WIN32
431         g_free (s1buf);
432         g_free (s2buf);
433 #endif /* !G_OS_WIN32 */
434         return rc;
435 }
436
437 /* remove trailing return code */
438 gchar *strretchomp(gchar *str)
439 {
440         register gchar *s;
441
442         if (!*str) return str;
443
444         for (s = str + strlen(str) - 1;
445              s >= str && (*s == '\n' || *s == '\r');
446              s--)
447                 *s = '\0';
448
449         return str;
450 }
451
452 /* remove trailing character */
453 gchar *strtailchomp(gchar *str, gchar tail_char)
454 {
455         register gchar *s;
456
457         if (!*str) return str;
458         if (tail_char == '\0') return str;
459
460         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
461                 *s = '\0';
462
463         return str;
464 }
465
466 /* remove CR (carriage return) */
467 gchar *strcrchomp(gchar *str)
468 {
469         register gchar *s;
470
471         if (!*str) return str;
472
473         s = str + strlen(str) - 1;
474         if (*s == '\n' && s > str && *(s - 1) == '\r') {
475                 *(s - 1) = '\n';
476                 *s = '\0';
477         }
478
479         return str;
480 }
481
482 gint file_strip_crs(const gchar *file)
483 {
484         FILE *fp = NULL, *outfp = NULL;
485         gchar buf[4096];
486         gchar *out = get_tmp_file();
487         if (file == NULL)
488                 goto freeout;
489
490         fp = g_fopen(file, "rb");
491         if (!fp)
492                 goto freeout;
493
494         outfp = g_fopen(out, "wb");
495         if (!outfp) {
496                 fclose(fp);
497                 goto freeout;
498         }
499
500         while (fgets(buf, sizeof (buf), fp) != NULL) {
501                 strcrchomp(buf);
502                 if (fputs(buf, outfp) == EOF) {
503                         fclose(fp);
504                         fclose(outfp);
505                         goto unlinkout;
506                 }
507         }
508
509         fclose(fp);
510         if (fclose(outfp) == EOF) {
511                 goto unlinkout;
512         }
513         
514         if (move_file(out, file, TRUE) < 0)
515                 goto unlinkout;
516         
517         g_free(out);
518         return 0;
519 unlinkout:
520         claws_unlink(out);
521 freeout:
522         g_free(out);
523         return -1;
524 }
525
526 /* Similar to `strstr' but this function ignores the case of both strings.  */
527 gchar *strcasestr(const gchar *haystack, const gchar *needle)
528 {
529         register size_t haystack_len, needle_len;
530
531         haystack_len = strlen(haystack);
532         needle_len   = strlen(needle);
533
534         if (haystack_len < needle_len || needle_len == 0)
535                 return NULL;
536
537         while (haystack_len >= needle_len) {
538                 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
539                         return (gchar *)haystack;
540                 else {
541                         haystack++;
542                         haystack_len--;
543                 }
544         }
545
546         return NULL;
547 }
548
549 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
550                    gconstpointer needle, size_t needlelen)
551 {
552         const gchar *haystack_ = (const gchar *)haystack;
553         const gchar *needle_ = (const gchar *)needle;
554         const gchar *haystack_cur = (const gchar *)haystack;
555         size_t haystack_left = haystacklen;
556
557         if (needlelen == 1)
558                 return memchr(haystack_, *needle_, haystacklen);
559
560         while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
561                != NULL) {
562                 if (haystacklen - (haystack_cur - haystack_) < needlelen)
563                         break;
564                 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
565                         return (gpointer)haystack_cur;
566                 else{
567                         haystack_cur++;
568                         haystack_left = haystacklen - (haystack_cur - haystack_);
569                 }
570         }
571
572         return NULL;
573 }
574
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
577 {
578         register const gchar *s = src;
579         register gchar *d = dest;
580
581         while (--n && *s)
582                 *d++ = *s++;
583         *d = '\0';
584
585         return dest;
586 }
587
588
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
591 {
592         const gchar *p;
593
594         /* skip head space */
595         for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
596                 ;
597         for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598                 if (*(guchar *)p > 127 || *(guchar *)p < 32)
599                         return TRUE;
600         }
601
602         return FALSE;
603 }
604
605 gint get_next_word_len(const gchar *s)
606 {
607         gint len = 0;
608
609         for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
610                 ;
611
612         return len;
613 }
614
615 static void trim_subject_for_compare(gchar *str)
616 {
617         gchar *srcp;
618
619         eliminate_parenthesis(str, '[', ']');
620         eliminate_parenthesis(str, '(', ')');
621         g_strstrip(str);
622
623         srcp = str + subject_get_prefix_length(str);
624         if (srcp != str)
625                 memmove(str, srcp, strlen(srcp) + 1);
626 }
627
628 static void trim_subject_for_sort(gchar *str)
629 {
630         gchar *srcp;
631
632         g_strstrip(str);
633
634         srcp = str + subject_get_prefix_length(str);
635         if (srcp != str)
636                 memmove(str, srcp, strlen(srcp) + 1);
637 }
638
639 /* compare subjects */
640 gint subject_compare(const gchar *s1, const gchar *s2)
641 {
642         gchar *str1, *str2;
643
644         if (!s1 || !s2) return -1;
645         if (!*s1 || !*s2) return -1;
646
647         Xstrdup_a(str1, s1, return -1);
648         Xstrdup_a(str2, s2, return -1);
649
650         trim_subject_for_compare(str1);
651         trim_subject_for_compare(str2);
652
653         if (!*str1 || !*str2) return -1;
654
655         return strcmp(str1, str2);
656 }
657
658 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
659 {
660         gchar *str1, *str2;
661
662         if (!s1 || !s2) return -1;
663
664         Xstrdup_a(str1, s1, return -1);
665         Xstrdup_a(str2, s2, return -1);
666
667         trim_subject_for_sort(str1);
668         trim_subject_for_sort(str2);
669
670         return g_utf8_collate(str1, str2);
671 }
672
673 void trim_subject(gchar *str)
674 {
675         register gchar *srcp;
676         gchar op, cl;
677         gint in_brace;
678
679         g_strstrip(str);
680
681         srcp = str + subject_get_prefix_length(str);
682
683         if (*srcp == '[') {
684                 op = '[';
685                 cl = ']';
686         } else if (*srcp == '(') {
687                 op = '(';
688                 cl = ')';
689         } else
690                 op = 0;
691
692         if (op) {
693                 ++srcp;
694                 in_brace = 1;
695                 while (*srcp) {
696                         if (*srcp == op)
697                                 in_brace++;
698                         else if (*srcp == cl)
699                                 in_brace--;
700                         srcp++;
701                         if (in_brace == 0)
702                                 break;
703                 }
704         }
705         while (g_ascii_isspace(*srcp)) srcp++;
706         memmove(str, srcp, strlen(srcp) + 1);
707 }
708
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
710 {
711         register gchar *srcp, *destp;
712         gint in_brace;
713
714         srcp = destp = str;
715
716         while ((destp = strchr(destp, op))) {
717                 in_brace = 1;
718                 srcp = destp + 1;
719                 while (*srcp) {
720                         if (*srcp == op)
721                                 in_brace++;
722                         else if (*srcp == cl)
723                                 in_brace--;
724                         srcp++;
725                         if (in_brace == 0)
726                                 break;
727                 }
728                 while (g_ascii_isspace(*srcp)) srcp++;
729                 memmove(destp, srcp, strlen(srcp) + 1);
730         }
731 }
732
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
734 {
735         register gchar *srcp, *destp;
736         gint in_brace;
737
738         srcp = destp = str;
739
740         while ((srcp = strchr(destp, op))) {
741                 if (destp > str)
742                         *destp++ = ' ';
743                 memmove(destp, srcp + 1, strlen(srcp));
744                 in_brace = 1;
745                 while(*destp) {
746                         if (*destp == op)
747                                 in_brace++;
748                         else if (*destp == cl)
749                                 in_brace--;
750
751                         if (in_brace == 0)
752                                 break;
753
754                         destp++;
755                 }
756         }
757         *destp = '\0';
758 }
759
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
761                                          gchar op, gchar cl)
762 {
763         register gchar *srcp, *destp;
764         gint in_brace;
765         gboolean in_quote = FALSE;
766
767         srcp = destp = str;
768
769         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
770                 if (destp > str)
771                         *destp++ = ' ';
772                 memmove(destp, srcp + 1, strlen(srcp));
773                 in_brace = 1;
774                 while(*destp) {
775                         if (*destp == op && !in_quote)
776                                 in_brace++;
777                         else if (*destp == cl && !in_quote)
778                                 in_brace--;
779                         else if (*destp == quote_chr)
780                                 in_quote ^= TRUE;
781
782                         if (in_brace == 0)
783                                 break;
784
785                         destp++;
786                 }
787         }
788         *destp = '\0';
789 }
790
791 void extract_quote(gchar *str, gchar quote_chr)
792 {
793         register gchar *p;
794
795         if ((str = strchr(str, quote_chr))) {
796                 p = str;
797                 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798                         memmove(p - 1, p, strlen(p) + 1);
799                         p--;
800                 }
801                 if(p) {
802                         *p = '\0';
803                         memmove(str, str + 1, p - str);
804                 }
805         }
806 }
807
808 void eliminate_address_comment(gchar *str)
809 {
810         register gchar *srcp, *destp;
811         gint in_brace;
812
813         srcp = destp = str;
814
815         while ((destp = strchr(destp, '"'))) {
816                 if ((srcp = strchr(destp + 1, '"'))) {
817                         srcp++;
818                         if (*srcp == '@') {
819                                 destp = srcp + 1;
820                         } else {
821                                 while (g_ascii_isspace(*srcp)) srcp++;
822                                 memmove(destp, srcp, strlen(srcp) + 1);
823                         }
824                 } else {
825                         *destp = '\0';
826                         break;
827                 }
828         }
829
830         srcp = destp = str;
831
832         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
833                 in_brace = 1;
834                 srcp = destp + 1;
835                 while (*srcp) {
836                         if (*srcp == '(')
837                                 in_brace++;
838                         else if (*srcp == ')')
839                                 in_brace--;
840                         srcp++;
841                         if (in_brace == 0)
842                                 break;
843                 }
844                 while (g_ascii_isspace(*srcp)) srcp++;
845                 memmove(destp, srcp, strlen(srcp) + 1);
846         }
847 }
848
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
850 {
851         gboolean in_quote = FALSE;
852
853         while (*str) {
854                 if (*str == c && !in_quote)
855                         return (gchar *)str;
856                 if (*str == quote_chr)
857                         in_quote ^= TRUE;
858                 str++;
859         }
860
861         return NULL;
862 }
863
864 void extract_address(gchar *str)
865 {
866         eliminate_address_comment(str);
867         if (strchr_with_skip_quote(str, '"', '<'))
868                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
869         g_strstrip(str);
870 }
871
872 void extract_list_id_str(gchar *str)
873 {
874         if (strchr_with_skip_quote(str, '"', '<'))
875                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
876         g_strstrip(str);
877 }
878
879 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
880 {
881         gchar *work;
882         gchar *workp;
883
884         if (!str) return addr_list;
885
886         Xstrdup_a(work, str, return addr_list);
887
888         if (removecomments)
889                 eliminate_address_comment(work);
890         workp = work;
891
892         while (workp && *workp) {
893                 gchar *p, *next;
894
895                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
896                         *p = '\0';
897                         next = p + 1;
898                 } else
899                         next = NULL;
900
901                 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
902                         extract_parenthesis_with_skip_quote
903                                 (workp, '"', '<', '>');
904
905                 g_strstrip(workp);
906                 if (*workp)
907                         addr_list = g_slist_append(addr_list, g_strdup(workp));
908
909                 workp = next;
910         }
911
912         return addr_list;
913 }
914
915 GSList *address_list_append(GSList *addr_list, const gchar *str)
916 {
917         return address_list_append_real(addr_list, str, TRUE);
918 }
919
920 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
921 {
922         return address_list_append_real(addr_list, str, FALSE);
923 }
924
925 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
926 {
927         const gchar *strp;
928
929         if (!str) return msgid_list;
930         strp = str;
931
932         while (strp && *strp) {
933                 const gchar *start, *end;
934                 gchar *msgid;
935
936                 if ((start = strchr(strp, '<')) != NULL) {
937                         end = strchr(start + 1, '>');
938                         if (!end) break;
939                 } else
940                         break;
941
942                 msgid = g_strndup(start + 1, end - start - 1);
943                 g_strstrip(msgid);
944                 if (*msgid)
945                         msgid_list = g_slist_prepend(msgid_list, msgid);
946                 else
947                         g_free(msgid);
948
949                 strp = end + 1;
950         }
951
952         return msgid_list;
953 }
954
955 GSList *references_list_append(GSList *msgid_list, const gchar *str)
956 {
957         GSList *list;
958
959         list = references_list_prepend(NULL, str);
960         list = g_slist_reverse(list);
961         msgid_list = g_slist_concat(msgid_list, list);
962
963         return msgid_list;
964 }
965
966 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
967 {
968         gchar *work;
969         gchar *workp;
970
971         if (!str) return group_list;
972
973         Xstrdup_a(work, str, return group_list);
974
975         workp = work;
976
977         while (workp && *workp) {
978                 gchar *p, *next;
979
980                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
981                         *p = '\0';
982                         next = p + 1;
983                 } else
984                         next = NULL;
985
986                 g_strstrip(workp);
987                 if (*workp)
988                         group_list = g_slist_append(group_list,
989                                                     g_strdup(workp));
990
991                 workp = next;
992         }
993
994         return group_list;
995 }
996
997 GList *add_history(GList *list, const gchar *str)
998 {
999         GList *old;
1000
1001         cm_return_val_if_fail(str != NULL, list);
1002
1003         old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1004         if (old) {
1005                 g_free(old->data);
1006                 list = g_list_remove(list, old->data);
1007         } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1008                 GList *last;
1009
1010                 last = g_list_last(list);
1011                 if (last) {
1012                         g_free(last->data);
1013                         list = g_list_remove(list, last->data);
1014                 }
1015         }
1016
1017         list = g_list_prepend(list, g_strdup(str));
1018
1019         return list;
1020 }
1021
1022 void remove_return(gchar *str)
1023 {
1024         register gchar *p = str;
1025
1026         while (*p) {
1027                 if (*p == '\n' || *p == '\r')
1028                         memmove(p, p + 1, strlen(p));
1029                 else
1030                         p++;
1031         }
1032 }
1033
1034 void remove_space(gchar *str)
1035 {
1036         register gchar *p = str;
1037         register gint spc;
1038
1039         while (*p) {
1040                 spc = 0;
1041                 while (g_ascii_isspace(*(p + spc)))
1042                         spc++;
1043                 if (spc)
1044                         memmove(p, p + spc, strlen(p + spc) + 1);
1045                 else
1046                         p++;
1047         }
1048 }
1049
1050 void unfold_line(gchar *str)
1051 {
1052         register gchar *p = str;
1053         register gint spc;
1054
1055         while (*p) {
1056                 if (*p == '\n' || *p == '\r') {
1057                         *p++ = ' ';
1058                         spc = 0;
1059                         while (g_ascii_isspace(*(p + spc)))
1060                                 spc++;
1061                         if (spc)
1062                                 memmove(p, p + spc, strlen(p + spc) + 1);
1063                 } else
1064                         p++;
1065         }
1066 }
1067
1068 void subst_char(gchar *str, gchar orig, gchar subst)
1069 {
1070         register gchar *p = str;
1071
1072         while (*p) {
1073                 if (*p == orig)
1074                         *p = subst;
1075                 p++;
1076         }
1077 }
1078
1079 void subst_chars(gchar *str, gchar *orig, gchar subst)
1080 {
1081         register gchar *p = str;
1082
1083         while (*p) {
1084                 if (strchr(orig, *p) != NULL)
1085                         *p = subst;
1086                 p++;
1087         }
1088 }
1089
1090 void subst_for_filename(gchar *str)
1091 {
1092         if (!str)
1093                 return;
1094 #ifdef G_OS_WIN32
1095         subst_chars(str, "\t\r\n\\/*:", '_');
1096 #else
1097         subst_chars(str, "\t\r\n\\/*", '_');
1098 #endif
1099 }
1100
1101 void subst_for_shellsafe_filename(gchar *str)
1102 {
1103         if (!str)
1104                 return;
1105         subst_for_filename(str);
1106         subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1107 }
1108
1109 gboolean is_ascii_str(const gchar *str)
1110 {
1111         const guchar *p = (const guchar *)str;
1112
1113         while (*p != '\0') {
1114                 if (*p != '\t' && *p != ' ' &&
1115                     *p != '\r' && *p != '\n' &&
1116                     (*p < 32 || *p >= 127))
1117                         return FALSE;
1118                 p++;
1119         }
1120
1121         return TRUE;
1122 }
1123
1124 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1125 {
1126         gchar * position = NULL;
1127         gchar * tmp_pos = NULL;
1128         int i;
1129
1130         if (quote_chars == NULL)
1131                 return FALSE;
1132
1133         for (i = 0; i < strlen(quote_chars); i++) {
1134                 tmp_pos = strrchr (str, quote_chars[i]);
1135                 if(position == NULL
1136                    || (tmp_pos != NULL && position <= tmp_pos) )
1137                         position = tmp_pos;
1138         }
1139         return position;
1140 }
1141
1142 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1143 {
1144         const gchar *first_pos;
1145         const gchar *last_pos;
1146         const gchar *p = str;
1147         gint quote_level = -1;
1148
1149         /* speed up line processing by only searching to the last '>' */
1150         if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1151                 /* skip a line if it contains a '<' before the initial '>' */
1152                 if (memchr(str, '<', first_pos - str) != NULL)
1153                         return -1;
1154                 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1155         } else
1156                 return -1;
1157
1158         while (p <= last_pos) {
1159                 while (p < last_pos) {
1160                         if (g_ascii_isspace(*p))
1161                                 p++;
1162                         else
1163                                 break;
1164                 }
1165
1166                 if (strchr(quote_chars, *p))
1167                         quote_level++;
1168                 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1169                         /* any characters are allowed except '-' and space */
1170                         while (*p != '-'
1171                                && !strchr(quote_chars, *p)
1172                                && !g_ascii_isspace(*p)
1173                                && p < last_pos)
1174                                 p++;
1175                         if (strchr(quote_chars, *p))
1176                                 quote_level++;
1177                         else
1178                                 break;
1179                 }
1180
1181                 p++;
1182         }
1183
1184         return quote_level;
1185 }
1186
1187 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1188 {
1189         const gchar *p = str, *q;
1190         gint cur_line = 0, len;
1191
1192         while ((q = strchr(p, '\n')) != NULL) {
1193                 len = q - p + 1;
1194                 if (len > max_chars) {
1195                         if (line)
1196                                 *line = cur_line;
1197                         return -1;
1198                 }
1199                 p = q + 1;
1200                 ++cur_line;
1201         }
1202
1203         len = strlen(p);
1204         if (len > max_chars) {
1205                 if (line)
1206                         *line = cur_line;
1207                 return -1;
1208         }
1209
1210         return 0;
1211 }
1212
1213 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1214 {
1215         gchar * position = NULL;
1216         gchar * tmp_pos = NULL;
1217         int i;
1218
1219         if (quote_chars == NULL)
1220                 return FALSE;
1221
1222         for (i = 0; i < strlen(quote_chars); i++) {
1223                 tmp_pos = strchr (str,  quote_chars[i]);
1224                 if(position == NULL
1225                    || (tmp_pos != NULL && position >= tmp_pos) )
1226                         position = tmp_pos;
1227         }
1228         return position;
1229 }
1230
1231 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1232 {
1233         register guint haystack_len, needle_len;
1234         gboolean in_squote = FALSE, in_dquote = FALSE;
1235
1236         haystack_len = strlen(haystack);
1237         needle_len   = strlen(needle);
1238
1239         if (haystack_len < needle_len || needle_len == 0)
1240                 return NULL;
1241
1242         while (haystack_len >= needle_len) {
1243                 if (!in_squote && !in_dquote &&
1244                     !strncmp(haystack, needle, needle_len))
1245                         return (gchar *)haystack;
1246
1247                 /* 'foo"bar"' -> foo"bar"
1248                    "foo'bar'" -> foo'bar' */
1249                 if (*haystack == '\'') {
1250                         if (in_squote)
1251                                 in_squote = FALSE;
1252                         else if (!in_dquote)
1253                                 in_squote = TRUE;
1254                 } else if (*haystack == '\"') {
1255                         if (in_dquote)
1256                                 in_dquote = FALSE;
1257                         else if (!in_squote)
1258                                 in_dquote = TRUE;
1259                 }
1260
1261                 haystack++;
1262                 haystack_len--;
1263         }
1264
1265         return NULL;
1266 }
1267
1268 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1269                             gint max_tokens)
1270 {
1271         GSList *string_list = NULL, *slist;
1272         gchar **str_array, *s, *new_str;
1273         guint i, n = 1, len;
1274
1275         cm_return_val_if_fail(str != NULL, NULL);
1276         cm_return_val_if_fail(delim != NULL, NULL);
1277
1278         if (max_tokens < 1)
1279                 max_tokens = G_MAXINT;
1280
1281         s = strstr_with_skip_quote(str, delim);
1282         if (s) {
1283                 guint delimiter_len = strlen(delim);
1284
1285                 do {
1286                         len = s - str;
1287                         new_str = g_strndup(str, len);
1288
1289                         if (new_str[0] == '\'' || new_str[0] == '\"') {
1290                                 if (new_str[len - 1] == new_str[0]) {
1291                                         new_str[len - 1] = '\0';
1292                                         memmove(new_str, new_str + 1, len - 1);
1293                                 }
1294                         }
1295                         string_list = g_slist_prepend(string_list, new_str);
1296                         n++;
1297                         str = s + delimiter_len;
1298                         s = strstr_with_skip_quote(str, delim);
1299                 } while (--max_tokens && s);
1300         }
1301
1302         if (*str) {
1303                 new_str = g_strdup(str);
1304                 if (new_str[0] == '\'' || new_str[0] == '\"') {
1305                         len = strlen(str);
1306                         if (new_str[len - 1] == new_str[0]) {
1307                                 new_str[len - 1] = '\0';
1308                                 memmove(new_str, new_str + 1, len - 1);
1309                         }
1310                 }
1311                 string_list = g_slist_prepend(string_list, new_str);
1312                 n++;
1313         }
1314
1315         str_array = g_new(gchar*, n);
1316
1317         i = n - 1;
1318
1319         str_array[i--] = NULL;
1320         for (slist = string_list; slist; slist = slist->next)
1321                 str_array[i--] = slist->data;
1322
1323         g_slist_free(string_list);
1324
1325         return str_array;
1326 }
1327
1328 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1329 {
1330         gchar *abbrev_group;
1331         gchar *ap;
1332         const gchar *p = group;
1333         const gchar *last;
1334
1335         cm_return_val_if_fail(group != NULL, NULL);
1336
1337         last = group + strlen(group);
1338         abbrev_group = ap = g_malloc(strlen(group) + 1);
1339
1340         while (*p) {
1341                 while (*p == '.')
1342                         *ap++ = *p++;
1343                 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1344                         *ap++ = *p++;
1345                         while (*p != '.') p++;
1346                 } else {
1347                         strcpy(ap, p);
1348                         return abbrev_group;
1349                 }
1350         }
1351
1352         *ap = '\0';
1353         return abbrev_group;
1354 }
1355
1356 gchar *trim_string(const gchar *str, gint len)
1357 {
1358         const gchar *p = str;
1359         gint mb_len;
1360         gchar *new_str;
1361         gint new_len = 0;
1362
1363         if (!str) return NULL;
1364         if (strlen(str) <= len)
1365                 return g_strdup(str);
1366         if (g_utf8_validate(str, -1, NULL) == FALSE)
1367                 return g_strdup(str);
1368
1369         while (*p != '\0') {
1370                 mb_len = g_utf8_skip[*(guchar *)p];
1371                 if (mb_len == 0)
1372                         break;
1373                 else if (new_len + mb_len > len)
1374                         break;
1375
1376                 new_len += mb_len;
1377                 p += mb_len;
1378         }
1379
1380         Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1381         return g_strconcat(new_str, "...", NULL);
1382 }
1383
1384 GList *uri_list_extract_filenames(const gchar *uri_list)
1385 {
1386         GList *result = NULL;
1387         const gchar *p, *q;
1388         gchar *escaped_utf8uri;
1389
1390         p = uri_list;
1391
1392         while (p) {
1393                 if (*p != '#') {
1394                         while (g_ascii_isspace(*p)) p++;
1395                         if (!strncmp(p, "file:", 5)) {
1396                                 q = p;
1397                                 q += 5;
1398                                 while (*q && *q != '\n' && *q != '\r') q++;
1399
1400                                 if (q > p) {
1401                                         gchar *file, *locale_file = NULL;
1402                                         q--;
1403                                         while (q > p && g_ascii_isspace(*q))
1404                                                 q--;
1405                                         Xalloca(escaped_utf8uri, q - p + 2,
1406                                                 return result);
1407                                         Xalloca(file, q - p + 2,
1408                                                 return result);
1409                                         *file = '\0';
1410                                         strncpy(escaped_utf8uri, p, q - p + 1);
1411                                         escaped_utf8uri[q - p + 1] = '\0';
1412                                         decode_uri(file, escaped_utf8uri);
1413                     /*
1414                      * g_filename_from_uri() rejects escaped/locale encoded uri
1415                      * string which come from Nautilus.
1416                      */
1417 #ifndef G_OS_WIN32
1418                                         if (g_utf8_validate(file, -1, NULL))
1419                                                 locale_file
1420                                                         = conv_codeset_strdup(
1421                                                                 file + 5,
1422                                                                 CS_UTF_8,
1423                                                                 conv_get_locale_charset_str());
1424                                         if (!locale_file)
1425                                                 locale_file = g_strdup(file + 5);
1426 #else
1427                                         locale_file = g_filename_from_uri(file, NULL, NULL);
1428 #endif
1429                                         result = g_list_append(result, locale_file);
1430                                 }
1431                         }
1432                 }
1433                 p = strchr(p, '\n');
1434                 if (p) p++;
1435         }
1436
1437         return result;
1438 }
1439
1440 /* Converts two-digit hexadecimal to decimal.  Used for unescaping escaped
1441  * characters
1442  */
1443 static gint axtoi(const gchar *hexstr)
1444 {
1445         gint hi, lo, result;
1446
1447         hi = hexstr[0];
1448         if ('0' <= hi && hi <= '9') {
1449                 hi -= '0';
1450         } else
1451                 if ('a' <= hi && hi <= 'f') {
1452                         hi -= ('a' - 10);
1453                 } else
1454                         if ('A' <= hi && hi <= 'F') {
1455                                 hi -= ('A' - 10);
1456                         }
1457
1458         lo = hexstr[1];
1459         if ('0' <= lo && lo <= '9') {
1460                 lo -= '0';
1461         } else
1462                 if ('a' <= lo && lo <= 'f') {
1463                         lo -= ('a'-10);
1464                 } else
1465                         if ('A' <= lo && lo <= 'F') {
1466                                 lo -= ('A' - 10);
1467                         }
1468         result = lo + (16 * hi);
1469         return result;
1470 }
1471
1472 gboolean is_uri_string(const gchar *str)
1473 {
1474         while (str && *str && g_ascii_isspace(*str))
1475                 str++;
1476         return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1477                 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1478                 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1479                 g_ascii_strncasecmp(str, "www.", 4) == 0);
1480 }
1481
1482 gchar *get_uri_path(const gchar *uri)
1483 {
1484         while (uri && *uri && g_ascii_isspace(*uri))
1485                 uri++;
1486         if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1487                 return (gchar *)(uri + 7);
1488         else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1489                 return (gchar *)(uri + 8);
1490         else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1491                 return (gchar *)(uri + 6);
1492         else
1493                 return (gchar *)uri;
1494 }
1495
1496 gint get_uri_len(const gchar *str)
1497 {
1498         const gchar *p;
1499
1500         if (is_uri_string(str)) {
1501                 for (p = str; *p != '\0'; p++) {
1502                         if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1503                                 break;
1504                 }
1505                 return p - str;
1506         }
1507
1508         return 0;
1509 }
1510
1511 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1512  * plusses, and escape characters are used)
1513  */
1514 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1515 {
1516         gchar *dec = decoded_uri;
1517         const gchar *enc = encoded_uri;
1518
1519         while (*enc) {
1520                 if (*enc == '%') {
1521                         enc++;
1522                         if (isxdigit((guchar)enc[0]) &&
1523                             isxdigit((guchar)enc[1])) {
1524                                 *dec = axtoi(enc);
1525                                 dec++;
1526                                 enc += 2;
1527                         }
1528                 } else {
1529                         if (with_plus && *enc == '+')
1530                                 *dec = ' ';
1531                         else
1532                                 *dec = *enc;
1533                         dec++;
1534                         enc++;
1535                 }
1536         }
1537
1538         *dec = '\0';
1539 }
1540
1541 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1542 {
1543         decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1544 }
1545
1546 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1547 {
1548     gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1549     decode_uri_with_plus(buffer, encoded_uri, FALSE);
1550     return buffer;
1551 }
1552
1553 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1554                      gchar **subject, gchar **body, gchar ***attach)
1555 {
1556         gchar *tmp_mailto;
1557         gchar *p;
1558         const gchar *forbidden_uris[] = { ".gnupg/",
1559                                           "/etc/passwd",
1560                                           "/etc/shadow",
1561                                           ".ssh/",
1562                                           "../",
1563                                           NULL };
1564         gint num_attach = 0;
1565         gchar **my_att = NULL;
1566
1567         Xstrdup_a(tmp_mailto, mailto, return -1);
1568
1569         if (!strncmp(tmp_mailto, "mailto:", 7))
1570                 tmp_mailto += 7;
1571
1572         p = strchr(tmp_mailto, '?');
1573         if (p) {
1574                 *p = '\0';
1575                 p++;
1576         }
1577
1578         if (to && !*to)
1579                 *to = decode_uri_gdup(tmp_mailto);
1580
1581         my_att = g_malloc(sizeof(char *));
1582         my_att[0] = NULL;
1583
1584         while (p) {
1585                 gchar *field, *value;
1586
1587                 field = p;
1588
1589                 p = strchr(p, '=');
1590                 if (!p) break;
1591                 *p = '\0';
1592                 p++;
1593
1594                 value = p;
1595
1596                 p = strchr(p, '&');
1597                 if (p) {
1598                         *p = '\0';
1599                         p++;
1600                 }
1601
1602                 if (*value == '\0') continue;
1603
1604                 if (from && !g_ascii_strcasecmp(field, "from")) {
1605                         if (!*from) {
1606                                 *from = decode_uri_gdup(value);
1607                         } else {
1608                                 gchar *tmp = decode_uri_gdup(value);
1609                                 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1610                                 g_free(*from);
1611                                 *from = new_from;
1612                         }
1613                 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1614                         if (!*cc) {
1615                                 *cc = decode_uri_gdup(value);
1616                         } else {
1617                                 gchar *tmp = decode_uri_gdup(value);
1618                                 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1619                                 g_free(*cc);
1620                                 *cc = new_cc;
1621                         }
1622                 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1623                         if (!*bcc) {
1624                                 *bcc = decode_uri_gdup(value);
1625                         } else {
1626                                 gchar *tmp = decode_uri_gdup(value);
1627                                 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1628                                 g_free(*bcc);
1629                                 *bcc = new_bcc;
1630                         }
1631                 } else if (subject && !*subject &&
1632                            !g_ascii_strcasecmp(field, "subject")) {
1633                         *subject = decode_uri_gdup(value);
1634                 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1635                         *body = decode_uri_gdup(value);
1636                 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1637                         gchar *tmp = decode_uri_gdup(value);
1638 <<<<<<< utils.c
1639                         if (!g_file_get_contents(value, body, NULL, NULL)) {
1640                                 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1641 =======
1642                         if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1643                                 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1644 >>>>>>> 1.36.2.176
1645                         }
1646                         g_free(tmp);
1647                         tmp = NULL;
1648                 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1649                         int i = 0;
1650                         gchar *tmp = decode_uri_gdup(value);
1651                         for (; forbidden_uris[i]; i++) {
1652                                 if (strstr(tmp, forbidden_uris[i])) {
1653                                         g_print("Refusing to attach '%s', potential private data leak\n",
1654                                                         tmp);
1655                                         g_free(tmp);
1656                                         tmp = NULL;
1657                                         break;
1658                                 }
1659                         }
1660                         if (tmp) {
1661                                 /* attach is correct */
1662                                 num_attach++;
1663                                 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1664                                 my_att[num_attach-1] = tmp;
1665                                 my_att[num_attach] = NULL;
1666                         }
1667                 }
1668         }
1669
1670         if (attach)
1671                 *attach = my_att;
1672         return 0;
1673 }
1674
1675
1676 #ifdef G_OS_WIN32
1677 #include <windows.h>
1678 #ifndef CSIDL_APPDATA
1679 #define CSIDL_APPDATA 0x001a
1680 #endif
1681 #ifndef CSIDL_LOCAL_APPDATA
1682 #define CSIDL_LOCAL_APPDATA 0x001c
1683 #endif
1684 #ifndef CSIDL_FLAG_CREATE
1685 #define CSIDL_FLAG_CREATE 0x8000
1686 #endif
1687 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
1688
1689 #define RTLD_LAZY 0
1690 const char *
1691 w32_strerror (int w32_errno)
1692 {
1693   static char strerr[256];
1694   int ec = (int)GetLastError ();
1695
1696   if (w32_errno == 0)
1697     w32_errno = ec;
1698   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1699                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1700                  strerr, DIM (strerr)-1, NULL);
1701   return strerr;
1702 }
1703
1704 static __inline__ void *
1705 dlopen (const char * name, int flag)
1706 {
1707   void * hd = LoadLibrary (name);
1708   return hd;
1709 }
1710
1711 static __inline__ void *
1712 dlsym (void * hd, const char * sym)
1713 {
1714   if (hd && sym)
1715     {
1716       void * fnc = GetProcAddress (hd, sym);
1717       if (!fnc)
1718         return NULL;
1719       return fnc;
1720     }
1721   return NULL;
1722 }
1723
1724
1725 static __inline__ const char *
1726 dlerror (void)
1727 {
1728   return w32_strerror (0);
1729 }
1730
1731
1732 static __inline__ int
1733 dlclose (void * hd)
1734 {
1735   if (hd)
1736     {
1737       FreeLibrary (hd);
1738       return 0;
1739     }
1740   return -1;
1741 }
1742
1743 static HRESULT
1744 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1745 {
1746   static int initialized;
1747   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1748
1749   if (!initialized)
1750     {
1751       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1752       void *handle;
1753       int i;
1754
1755       initialized = 1;
1756
1757       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1758         {
1759           handle = dlopen (dllnames[i], RTLD_LAZY);
1760           if (handle)
1761             {
1762               func = dlsym (handle, "SHGetFolderPathW");
1763               if (!func)
1764                 {
1765                   dlclose (handle);
1766                   handle = NULL;
1767                 }
1768             }
1769         }
1770     }
1771
1772   if (func)
1773     return func (a,b,c,d,e);
1774   else
1775     return -1;
1776 }
1777
1778 /* Returns a static string with the directroy from which the module
1779    has been loaded.  Returns an empty string on error. */
1780 static char *w32_get_module_dir(void)
1781 {
1782         static char *moddir;
1783
1784         if (!moddir) {
1785                 char name[MAX_PATH+10];
1786                 char *p;
1787
1788                 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1789                         *name = 0;
1790                 else {
1791                         p = strrchr (name, '\\');
1792                         if (p)
1793                                 *p = 0;
1794                         else
1795                                 *name = 0;
1796                 }
1797                 moddir = g_strdup (name);
1798         }
1799         return moddir;
1800 }
1801 #endif /* G_OS_WIN32 */
1802
1803 /* Return a static string with the locale dir. */
1804 const gchar *get_locale_dir(void)
1805 {
1806         static gchar *loc_dir;
1807
1808 #ifdef G_OS_WIN32
1809         if (!loc_dir)
1810                 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1811                                       "\\share\\locale", NULL);
1812 #endif
1813         if (!loc_dir)
1814                 loc_dir = LOCALEDIR;
1815         
1816         return loc_dir;
1817 }
1818
1819
1820 const gchar *get_home_dir(void)
1821 {
1822 #ifdef G_OS_WIN32
1823         static char home_dir_utf16[MAX_PATH] = "";
1824         static gchar *home_dir_utf8 = NULL;
1825         if (home_dir_utf16[0] == '\0') {
1826                 if (w32_shgetfolderpath
1827                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1828                              NULL, 0, home_dir_utf16) < 0)
1829                                 strcpy (home_dir_utf16, "C:\\Sylpheed");
1830                 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1831         }
1832         return home_dir_utf8;
1833 #else
1834         static const gchar *homeenv = NULL;
1835
1836         if (homeenv)
1837                 return homeenv;
1838
1839         if (!homeenv && g_getenv("HOME") != NULL)
1840                 homeenv = g_strdup(g_getenv("HOME"));
1841         if (!homeenv)
1842                 homeenv = g_get_home_dir();
1843
1844         return homeenv;
1845 #endif
1846 }
1847
1848 static gchar *claws_rc_dir = NULL;
1849 static gboolean rc_dir_alt = FALSE;
1850 const gchar *get_rc_dir(void)
1851 {
1852
1853         if (!claws_rc_dir)
1854                 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1855                                      RC_DIR, NULL);
1856
1857         return claws_rc_dir;
1858 }
1859
1860 void set_rc_dir(const gchar *dir)
1861 {
1862         if (claws_rc_dir != NULL) {
1863                 g_print("Error: rc_dir already set\n");
1864         } else {
1865                 rc_dir_alt = TRUE;
1866                 if (g_path_is_absolute(dir))
1867                         claws_rc_dir = g_strdup(dir);
1868                 else {
1869                         claws_rc_dir = g_strconcat(g_get_current_dir(),
1870                                 G_DIR_SEPARATOR_S, dir, NULL);
1871                 }
1872                 debug_print("set rc_dir to %s\n", claws_rc_dir);
1873                 if (!is_dir_exist(claws_rc_dir)) {
1874                         if (make_dir_hier(claws_rc_dir) != 0) {
1875                                 g_print("Error: can't create %s\n",
1876                                 claws_rc_dir);
1877                         }
1878                 }
1879         }
1880 }
1881
1882 gboolean rc_dir_is_alt(void) {
1883         return rc_dir_alt;
1884 }
1885
1886 const gchar *get_mail_base_dir(void)
1887 {
1888         return get_home_dir();
1889 }
1890
1891 #ifdef MAEMO
1892 const gchar *prefs_common_get_data_root(void);
1893 gchar *last_data_root = NULL;
1894 #endif
1895
1896 const gchar *get_news_cache_dir(void)
1897 {
1898         static gchar *news_cache_dir = NULL;
1899 #ifdef MAEMO
1900         const gchar *data_root = prefs_common_get_data_root();
1901         if (strcmp2(data_root, last_data_root)) {
1902                 g_free(news_cache_dir);
1903                 news_cache_dir = NULL;
1904         }
1905 #endif
1906         if (!news_cache_dir)
1907 #ifndef MAEMO
1908                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1909                                              NEWS_CACHE_DIR, NULL);
1910 #else
1911         {
1912                 if (data_root) {
1913                         news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1914                                              "Claws", G_DIR_SEPARATOR_S, 
1915                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1916                                              NEWS_CACHE_DIR, NULL);
1917                         g_free(last_data_root);
1918                         last_data_root = g_strdup(last_data_root);
1919                 } else {
1920                         news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1921                                              NEWS_CACHE_DIR, NULL);
1922                         g_free(last_data_root);
1923                         last_data_root = NULL;
1924                 }
1925         }
1926 #endif
1927         return news_cache_dir;
1928 }
1929
1930 const gchar *get_imap_cache_dir(void)
1931 {
1932         static gchar *imap_cache_dir = NULL;
1933 #ifdef MAEMO
1934         const gchar *data_root = prefs_common_get_data_root();
1935         if (strcmp2(data_root, last_data_root)) {
1936                 g_free(imap_cache_dir);
1937                 imap_cache_dir = NULL;
1938         }
1939 #endif
1940
1941         if (!imap_cache_dir)
1942 #ifndef MAEMO
1943                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1944                                              IMAP_CACHE_DIR, NULL);
1945 #else
1946         {
1947                 if (data_root) {
1948                         imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1949                                              "Claws", G_DIR_SEPARATOR_S, 
1950                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1951                                              IMAP_CACHE_DIR, NULL);
1952                         g_free(last_data_root);
1953                         last_data_root = g_strdup(last_data_root);
1954                 } else {
1955                         imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1956                                              IMAP_CACHE_DIR, NULL);
1957                         g_free(last_data_root);
1958                         last_data_root = NULL;
1959                 }
1960         }
1961 #endif
1962
1963         return imap_cache_dir;
1964 }
1965
1966 const gchar *get_mime_tmp_dir(void)
1967 {
1968         static gchar *mime_tmp_dir = NULL;
1969
1970         if (!mime_tmp_dir)
1971                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1972                                            MIME_TMP_DIR, NULL);
1973
1974         return mime_tmp_dir;
1975 }
1976
1977 const gchar *get_template_dir(void)
1978 {
1979         static gchar *template_dir = NULL;
1980
1981         if (!template_dir)
1982                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1983                                            TEMPLATE_DIR, NULL);
1984
1985         return template_dir;
1986 }
1987
1988 #ifdef G_OS_WIN32
1989 const gchar *get_cert_file(void)
1990 {
1991         const gchar *cert_file = NULL;
1992         if (!cert_file)
1993                 cert_file = g_strconcat(w32_get_module_dir(),
1994                                  "\\share\\claws-mail\\",
1995                                 "ca-certificates.crt",
1996                                 NULL);  
1997         return cert_file;
1998 }
1999 #endif
2000
2001 /* Return the default directory for Plugins. */
2002 const gchar *get_plugin_dir(void)
2003 {
2004 #ifdef G_OS_WIN32
2005         static gchar *plugin_dir = NULL;
2006
2007         if (!plugin_dir)
2008                 plugin_dir = g_strconcat(w32_get_module_dir(),
2009                                          "\\lib\\claws-mail\\plugins\\",
2010                                          NULL);
2011         return plugin_dir;
2012 #else
2013         if (is_dir_exist(PLUGINDIR))
2014                 return PLUGINDIR;
2015         else {
2016                 static gchar *plugin_dir = NULL;
2017                 if (!plugin_dir)
2018                         plugin_dir = g_strconcat(get_rc_dir(), 
2019                                 G_DIR_SEPARATOR_S, "plugins", 
2020                                 G_DIR_SEPARATOR_S, NULL);
2021                 return plugin_dir;                      
2022         }
2023 #endif
2024 }
2025
2026
2027 #ifdef G_OS_WIN32
2028 /* Return the default directory for Themes. */
2029 const gchar *get_themes_dir(void)
2030 {
2031         static gchar *themes_dir = NULL;
2032
2033         if (!themes_dir)
2034                 themes_dir = g_strconcat(w32_get_module_dir(),
2035                                          "\\share\\claws-mail\\themes",
2036                                          NULL);
2037         return themes_dir;
2038 }
2039 #endif
2040
2041 const gchar *get_tmp_dir(void)
2042 {
2043         static gchar *tmp_dir = NULL;
2044
2045         if (!tmp_dir)
2046                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2047                                       TMP_DIR, NULL);
2048
2049         return tmp_dir;
2050 }
2051
2052 gchar *get_tmp_file(void)
2053 {
2054         gchar *tmp_file;
2055         static guint32 id = 0;
2056
2057         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2058                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
2059
2060         return tmp_file;
2061 }
2062
2063 const gchar *get_domain_name(void)
2064 {
2065 #ifdef G_OS_UNIX
2066         static gchar *domain_name = NULL;
2067
2068         if (!domain_name) {
2069                 struct hostent *hp;
2070                 char hostname[256];
2071
2072                 if (gethostname(hostname, sizeof(hostname)) != 0) {
2073                         perror("gethostname");
2074                         domain_name = "unknown";
2075                 } else {
2076                         hostname[sizeof(hostname) - 1] = '\0';
2077                         if ((hp = my_gethostbyname(hostname)) == NULL) {
2078                                 perror("gethostbyname");
2079                                 domain_name = g_strdup(hostname);
2080                         } else {
2081                                 domain_name = g_strdup(hp->h_name);
2082                         }
2083                 }
2084                 debug_print("domain name = %s\n", domain_name);
2085         }
2086
2087         return domain_name;
2088 #else
2089         return "unknown";
2090 #endif
2091 }
2092
2093 off_t get_file_size(const gchar *file)
2094 {
2095         struct stat s;
2096
2097         if (g_stat(file, &s) < 0) {
2098                 FILE_OP_ERROR(file, "stat");
2099                 return -1;
2100         }
2101
2102         return s.st_size;
2103 }
2104
2105 time_t get_file_mtime(const gchar *file)
2106 {
2107         struct stat s;
2108
2109         if (g_stat(file, &s) < 0) {
2110                 FILE_OP_ERROR(file, "stat");
2111                 return -1;
2112         }
2113
2114         return s.st_mtime;
2115 }
2116
2117 off_t get_file_size_as_crlf(const gchar *file)
2118 {
2119         FILE *fp;
2120         off_t size = 0;
2121         gchar buf[BUFFSIZE];
2122
2123         if ((fp = g_fopen(file, "rb")) == NULL) {
2124                 FILE_OP_ERROR(file, "g_fopen");
2125                 return -1;
2126         }
2127
2128         while (fgets(buf, sizeof(buf), fp) != NULL) {
2129                 strretchomp(buf);
2130                 size += strlen(buf) + 2;
2131         }
2132
2133         if (ferror(fp)) {
2134                 FILE_OP_ERROR(file, "fgets");
2135                 size = -1;
2136         }
2137
2138         fclose(fp);
2139
2140         return size;
2141 }
2142
2143 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2144 {
2145         struct stat s;
2146
2147         if (file == NULL)
2148                 return FALSE;
2149
2150         if (g_stat(file, &s) < 0) {
2151                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2152                 return FALSE;
2153         }
2154
2155         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2156                 return TRUE;
2157
2158         return FALSE;
2159 }
2160
2161
2162 /* Test on whether FILE is a relative file name. This is
2163  * straightforward for Unix but more complex for Windows. */
2164 gboolean is_relative_filename(const gchar *file)
2165 {
2166         if (!file)
2167                 return TRUE;
2168 #ifdef G_OS_WIN32
2169         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2170                 return FALSE; /* Prefixed with a hostname - this can't
2171                                * be a relative name. */
2172
2173         if ( ((*file >= 'a' && *file <= 'z')
2174               || (*file >= 'A' && *file <= 'Z'))
2175              && file[1] == ':')
2176                 file += 2;  /* Skip drive letter. */
2177
2178         return !(*file == '\\' || *file == '/');
2179 #else
2180         return !(*file == G_DIR_SEPARATOR);
2181 #endif
2182 }
2183
2184
2185 gboolean is_dir_exist(const gchar *dir)
2186 {
2187         if (dir == NULL)
2188                 return FALSE;
2189
2190         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2191 }
2192
2193 gboolean is_file_entry_exist(const gchar *file)
2194 {
2195         if (file == NULL)
2196                 return FALSE;
2197
2198         return g_file_test(file, G_FILE_TEST_EXISTS);
2199 }
2200
2201 gboolean dirent_is_regular_file(struct dirent *d)
2202 {
2203 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2204         if (d->d_type == DT_REG)
2205                 return TRUE;
2206         else if (d->d_type != DT_UNKNOWN)
2207                 return FALSE;
2208 #endif
2209
2210         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2211 }
2212
2213 gint change_dir(const gchar *dir)
2214 {
2215         gchar *prevdir = NULL;
2216
2217         if (debug_mode)
2218                 prevdir = g_get_current_dir();
2219
2220         if (g_chdir(dir) < 0) {
2221                 FILE_OP_ERROR(dir, "chdir");
2222                 if (debug_mode) g_free(prevdir);
2223                 return -1;
2224         } else if (debug_mode) {
2225                 gchar *cwd;
2226
2227                 cwd = g_get_current_dir();
2228                 if (strcmp(prevdir, cwd) != 0)
2229                         g_print("current dir: %s\n", cwd);
2230                 g_free(cwd);
2231                 g_free(prevdir);
2232         }
2233
2234         return 0;
2235 }
2236
2237 gint make_dir(const gchar *dir)
2238 {
2239         if (g_mkdir(dir, S_IRWXU) < 0) {
2240                 FILE_OP_ERROR(dir, "mkdir");
2241                 return -1;
2242         }
2243         if (g_chmod(dir, S_IRWXU) < 0)
2244                 FILE_OP_ERROR(dir, "chmod");
2245
2246         return 0;
2247 }
2248
2249 gint make_dir_hier(const gchar *dir)
2250 {
2251         gchar *parent_dir;
2252         const gchar *p;
2253
2254         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2255                 parent_dir = g_strndup(dir, p - dir);
2256                 if (*parent_dir != '\0') {
2257                         if (!is_dir_exist(parent_dir)) {
2258                                 if (make_dir(parent_dir) < 0) {
2259                                         g_free(parent_dir);
2260                                         return -1;
2261                                 }
2262                         }
2263                 }
2264                 g_free(parent_dir);
2265         }
2266
2267         if (!is_dir_exist(dir)) {
2268                 if (make_dir(dir) < 0)
2269                         return -1;
2270         }
2271
2272         return 0;
2273 }
2274
2275 gint remove_all_files(const gchar *dir)
2276 {
2277         GDir *dp;
2278         const gchar *dir_name;
2279         gchar *prev_dir;
2280
2281         prev_dir = g_get_current_dir();
2282
2283         if (g_chdir(dir) < 0) {
2284                 FILE_OP_ERROR(dir, "chdir");
2285                 g_free(prev_dir);
2286                 return -1;
2287         }
2288
2289         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2290                 g_warning("failed to open directory: %s\n", dir);
2291                 g_free(prev_dir);
2292                 return -1;
2293         }
2294
2295         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2296                 if (claws_unlink(dir_name) < 0)
2297                         FILE_OP_ERROR(dir_name, "unlink");
2298         }
2299
2300         g_dir_close(dp);
2301
2302         if (g_chdir(prev_dir) < 0) {
2303                 FILE_OP_ERROR(prev_dir, "chdir");
2304                 g_free(prev_dir);
2305                 return -1;
2306         }
2307
2308         g_free(prev_dir);
2309
2310         return 0;
2311 }
2312
2313 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2314 {
2315         GDir *dp;
2316         const gchar *dir_name;
2317         gchar *prev_dir;
2318         gint file_no;
2319
2320         prev_dir = g_get_current_dir();
2321
2322         if (g_chdir(dir) < 0) {
2323                 FILE_OP_ERROR(dir, "chdir");
2324                 g_free(prev_dir);
2325                 return -1;
2326         }
2327
2328         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2329                 g_warning("failed to open directory: %s\n", dir);
2330                 g_free(prev_dir);
2331                 return -1;
2332         }
2333
2334         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2335                 file_no = to_number(dir_name);
2336                 if (file_no > 0 && first <= file_no && file_no <= last) {
2337                         if (is_dir_exist(dir_name))
2338                                 continue;
2339                         if (claws_unlink(dir_name) < 0)
2340                                 FILE_OP_ERROR(dir_name, "unlink");
2341                 }
2342         }
2343
2344         g_dir_close(dp);
2345
2346         if (g_chdir(prev_dir) < 0) {
2347                 FILE_OP_ERROR(prev_dir, "chdir");
2348                 g_free(prev_dir);
2349                 return -1;
2350         }
2351
2352         g_free(prev_dir);
2353
2354         return 0;
2355 }
2356
2357 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2358 {
2359         GDir *dp;
2360         const gchar *dir_name;
2361         gchar *prev_dir;
2362         gint file_no;
2363
2364         prev_dir = g_get_current_dir();
2365
2366         if (g_chdir(dir) < 0) {
2367                 FILE_OP_ERROR(dir, "chdir");
2368                 g_free(prev_dir);
2369                 return -1;
2370         }
2371
2372         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2373                 FILE_OP_ERROR(dir, "opendir");
2374                 g_free(prev_dir);
2375                 return -1;
2376         }
2377
2378         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2379                 file_no = to_number(dir_name);
2380                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2381                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2382                         if (is_dir_exist(dir_name))
2383                                 continue;
2384                         if (claws_unlink(dir_name) < 0)
2385                                 FILE_OP_ERROR(dir_name, "unlink");
2386                 }
2387         }
2388
2389         g_dir_close(dp);
2390
2391         if (g_chdir(prev_dir) < 0) {
2392                 FILE_OP_ERROR(prev_dir, "chdir");
2393                 g_free(prev_dir);
2394                 return -1;
2395         }
2396
2397         g_free(prev_dir);
2398
2399         return 0;
2400 }
2401
2402 gint remove_all_numbered_files(const gchar *dir)
2403 {
2404         return remove_numbered_files(dir, 0, UINT_MAX);
2405 }
2406
2407 gint remove_dir_recursive(const gchar *dir)
2408 {
2409         struct stat s;
2410         GDir *dp;
2411         const gchar *dir_name;
2412         gchar *prev_dir;
2413
2414         if (g_stat(dir, &s) < 0) {
2415                 FILE_OP_ERROR(dir, "stat");
2416                 if (ENOENT == errno) return 0;
2417                 return -1;
2418         }
2419
2420         if (!S_ISDIR(s.st_mode)) {
2421                 if (claws_unlink(dir) < 0) {
2422                         FILE_OP_ERROR(dir, "unlink");
2423                         return -1;
2424                 }
2425
2426                 return 0;
2427         }
2428
2429         prev_dir = g_get_current_dir();
2430         /* g_print("prev_dir = %s\n", prev_dir); */
2431
2432         if (!path_cmp(prev_dir, dir)) {
2433                 g_free(prev_dir);
2434                 if (g_chdir("..") < 0) {
2435                         FILE_OP_ERROR(dir, "chdir");
2436                         return -1;
2437                 }
2438                 prev_dir = g_get_current_dir();
2439         }
2440
2441         if (g_chdir(dir) < 0) {
2442                 FILE_OP_ERROR(dir, "chdir");
2443                 g_free(prev_dir);
2444                 return -1;
2445         }
2446
2447         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2448                 g_warning("failed to open directory: %s\n", dir);
2449                 g_chdir(prev_dir);
2450                 g_free(prev_dir);
2451                 return -1;
2452         }
2453
2454         /* remove all files in the directory */
2455         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2456                 /* g_print("removing %s\n", dir_name); */
2457
2458                 if (is_dir_exist(dir_name)) {
2459                         if (remove_dir_recursive(dir_name) < 0) {
2460                                 g_warning("can't remove directory\n");
2461                                 return -1;
2462                         }
2463                 } else {
2464                         if (claws_unlink(dir_name) < 0)
2465                                 FILE_OP_ERROR(dir_name, "unlink");
2466                 }
2467         }
2468
2469         g_dir_close(dp);
2470
2471         if (g_chdir(prev_dir) < 0) {
2472                 FILE_OP_ERROR(prev_dir, "chdir");
2473                 g_free(prev_dir);
2474                 return -1;
2475         }
2476
2477         g_free(prev_dir);
2478
2479         if (g_rmdir(dir) < 0) {
2480                 FILE_OP_ERROR(dir, "rmdir");
2481                 return -1;
2482         }
2483
2484         return 0;
2485 }
2486
2487 gint rename_force(const gchar *oldpath, const gchar *newpath)
2488 {
2489 #ifndef G_OS_UNIX
2490         if (!is_file_entry_exist(oldpath)) {
2491                 errno = ENOENT;
2492                 return -1;
2493         }
2494         if (is_file_exist(newpath)) {
2495                 if (claws_unlink(newpath) < 0)
2496                         FILE_OP_ERROR(newpath, "unlink");
2497         }
2498 #endif
2499         return g_rename(oldpath, newpath);
2500 }
2501
2502 /*
2503  * Append src file body to the tail of dest file.
2504  * Now keep_backup has no effects.
2505  */
2506 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2507 {
2508         FILE *src_fp, *dest_fp;
2509         gint n_read;
2510         gchar buf[BUFSIZ];
2511
2512         gboolean err = FALSE;
2513
2514         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2515                 FILE_OP_ERROR(src, "g_fopen");
2516                 return -1;
2517         }
2518
2519         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2520                 FILE_OP_ERROR(dest, "g_fopen");
2521                 fclose(src_fp);
2522                 return -1;
2523         }
2524
2525         if (change_file_mode_rw(dest_fp, dest) < 0) {
2526                 FILE_OP_ERROR(dest, "chmod");
2527                 g_warning("can't change file mode\n");
2528         }
2529
2530         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2531                 if (n_read < sizeof(buf) && ferror(src_fp))
2532                         break;
2533                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2534                         g_warning("writing to %s failed.\n", dest);
2535                         fclose(dest_fp);
2536                         fclose(src_fp);
2537                         claws_unlink(dest);
2538                         return -1;
2539                 }
2540         }
2541
2542         if (ferror(src_fp)) {
2543                 FILE_OP_ERROR(src, "fread");
2544                 err = TRUE;
2545         }
2546         fclose(src_fp);
2547         if (fclose(dest_fp) == EOF) {
2548                 FILE_OP_ERROR(dest, "fclose");
2549                 err = TRUE;
2550         }
2551
2552         if (err) {
2553                 claws_unlink(dest);
2554                 return -1;
2555         }
2556
2557         return 0;
2558 }
2559
2560 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2561 {
2562         FILE *src_fp, *dest_fp;
2563         gint n_read;
2564         gchar buf[BUFSIZ];
2565         gchar *dest_bak = NULL;
2566         gboolean err = FALSE;
2567
2568         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2569                 FILE_OP_ERROR(src, "g_fopen");
2570                 return -1;
2571         }
2572         if (is_file_exist(dest)) {
2573                 dest_bak = g_strconcat(dest, ".bak", NULL);
2574                 if (rename_force(dest, dest_bak) < 0) {
2575                         FILE_OP_ERROR(dest, "rename");
2576                         fclose(src_fp);
2577                         g_free(dest_bak);
2578                         return -1;
2579                 }
2580         }
2581
2582         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2583                 FILE_OP_ERROR(dest, "g_fopen");
2584                 fclose(src_fp);
2585                 if (dest_bak) {
2586                         if (rename_force(dest_bak, dest) < 0)
2587                                 FILE_OP_ERROR(dest_bak, "rename");
2588                         g_free(dest_bak);
2589                 }
2590                 return -1;
2591         }
2592
2593         if (change_file_mode_rw(dest_fp, dest) < 0) {
2594                 FILE_OP_ERROR(dest, "chmod");
2595                 g_warning("can't change file mode\n");
2596         }
2597
2598         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2599                 if (n_read < sizeof(buf) && ferror(src_fp))
2600                         break;
2601                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2602                         g_warning("writing to %s failed.\n", dest);
2603                         fclose(dest_fp);
2604                         fclose(src_fp);
2605                         claws_unlink(dest);
2606                         if (dest_bak) {
2607                                 if (rename_force(dest_bak, dest) < 0)
2608                                         FILE_OP_ERROR(dest_bak, "rename");
2609                                 g_free(dest_bak);
2610                         }
2611                         return -1;
2612                 }
2613         }
2614
2615         if (ferror(src_fp)) {
2616                 FILE_OP_ERROR(src, "fread");
2617                 err = TRUE;
2618         }
2619         fclose(src_fp);
2620         if (fclose(dest_fp) == EOF) {
2621                 FILE_OP_ERROR(dest, "fclose");
2622                 err = TRUE;
2623         }
2624
2625         if (err) {
2626                 claws_unlink(dest);
2627                 if (dest_bak) {
2628                         if (rename_force(dest_bak, dest) < 0)
2629                                 FILE_OP_ERROR(dest_bak, "rename");
2630                         g_free(dest_bak);
2631                 }
2632                 return -1;
2633         }
2634
2635         if (keep_backup == FALSE && dest_bak)
2636                 claws_unlink(dest_bak);
2637
2638         g_free(dest_bak);
2639
2640         return 0;
2641 }
2642
2643 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2644 {
2645         if (overwrite == FALSE && is_file_exist(dest)) {
2646                 g_warning("move_file(): file %s already exists.", dest);
2647                 return -1;
2648         }
2649
2650         if (rename_force(src, dest) == 0) return 0;
2651
2652         if (EXDEV != errno) {
2653                 FILE_OP_ERROR(src, "rename");
2654                 return -1;
2655         }
2656
2657         if (copy_file(src, dest, FALSE) < 0) return -1;
2658
2659         claws_unlink(src);
2660
2661         return 0;
2662 }
2663
2664 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2665 {
2666         gint n_read;
2667         gint bytes_left, to_read;
2668         gchar buf[BUFSIZ];
2669
2670         if (fseek(fp, offset, SEEK_SET) < 0) {
2671                 perror("fseek");
2672                 return -1;
2673         }
2674
2675         bytes_left = length;
2676         to_read = MIN(bytes_left, sizeof(buf));
2677
2678         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2679                 if (n_read < to_read && ferror(fp))
2680                         break;
2681                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2682                         return -1;
2683                 }
2684                 bytes_left -= n_read;
2685                 if (bytes_left == 0)
2686                         break;
2687                 to_read = MIN(bytes_left, sizeof(buf));
2688         }
2689
2690         if (ferror(fp)) {
2691                 perror("fread");
2692                 return -1;
2693         }
2694
2695         return 0;
2696 }
2697
2698 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2699 {
2700         FILE *dest_fp;
2701         gboolean err = FALSE;
2702
2703         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2704                 FILE_OP_ERROR(dest, "g_fopen");
2705                 return -1;
2706         }
2707
2708         if (change_file_mode_rw(dest_fp, dest) < 0) {
2709                 FILE_OP_ERROR(dest, "chmod");
2710                 g_warning("can't change file mode\n");
2711         }
2712
2713         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2714                 err = TRUE;
2715
2716         if (!err && fclose(dest_fp) == EOF) {
2717                 FILE_OP_ERROR(dest, "fclose");
2718                 err = TRUE;
2719         }
2720
2721         if (err) {
2722                 g_warning("writing to %s failed.\n", dest);
2723                 claws_unlink(dest);
2724                 return -1;
2725         }
2726
2727         return 0;
2728 }
2729
2730 /* convert line endings into CRLF. If the last line doesn't end with
2731  * linebreak, add it.
2732  */
2733 gchar *canonicalize_str(const gchar *str)
2734 {
2735         const gchar *p;
2736         guint new_len = 0;
2737         gchar *out, *outp;
2738
2739         for (p = str; *p != '\0'; ++p) {
2740                 if (*p != '\r') {
2741                         ++new_len;
2742                         if (*p == '\n')
2743                                 ++new_len;
2744                 }
2745         }
2746         if (p == str || *(p - 1) != '\n')
2747                 new_len += 2;
2748
2749         out = outp = g_malloc(new_len + 1);
2750         for (p = str; *p != '\0'; ++p) {
2751                 if (*p != '\r') {
2752                         if (*p == '\n')
2753                                 *outp++ = '\r';
2754                         *outp++ = *p;
2755                 }
2756         }
2757         if (p == str || *(p - 1) != '\n') {
2758                 *outp++ = '\r';
2759                 *outp++ = '\n';
2760         }
2761         *outp = '\0';
2762
2763         return out;
2764 }
2765
2766 gint canonicalize_file(const gchar *src, const gchar *dest)
2767 {
2768         FILE *src_fp, *dest_fp;
2769         gchar buf[BUFFSIZE];
2770         gint len;
2771         gboolean err = FALSE;
2772         gboolean last_linebreak = FALSE;
2773
2774         if (src == NULL || dest == NULL)
2775                 return -1;
2776
2777         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2778                 FILE_OP_ERROR(src, "g_fopen");
2779                 return -1;
2780         }
2781
2782         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2783                 FILE_OP_ERROR(dest, "g_fopen");
2784                 fclose(src_fp);
2785                 return -1;
2786         }
2787
2788         if (change_file_mode_rw(dest_fp, dest) < 0) {
2789                 FILE_OP_ERROR(dest, "chmod");
2790                 g_warning("can't change file mode\n");
2791         }
2792
2793         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2794                 gint r = 0;
2795
2796                 len = strlen(buf);
2797                 if (len == 0) break;
2798                 last_linebreak = FALSE;
2799
2800                 if (buf[len - 1] != '\n') {
2801                         last_linebreak = TRUE;
2802                         r = fputs(buf, dest_fp);
2803                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2804                         r = fputs(buf, dest_fp);
2805                 } else {
2806                         if (len > 1) {
2807                                 r = fwrite(buf, 1, len - 1, dest_fp);
2808                                 if (r != (len -1))
2809                                         r = EOF;
2810                         }
2811                         if (r != EOF)
2812                                 r = fputs("\r\n", dest_fp);
2813                 }
2814
2815                 if (r == EOF) {
2816                         g_warning("writing to %s failed.\n", dest);
2817                         fclose(dest_fp);
2818                         fclose(src_fp);
2819                         claws_unlink(dest);
2820                         return -1;
2821                 }
2822         }
2823
2824         if (last_linebreak == TRUE) {
2825                 if (fputs("\r\n", dest_fp) == EOF)
2826                         err = TRUE;
2827         }
2828
2829         if (ferror(src_fp)) {
2830                 FILE_OP_ERROR(src, "fgets");
2831                 err = TRUE;
2832         }
2833         fclose(src_fp);
2834         if (fclose(dest_fp) == EOF) {
2835                 FILE_OP_ERROR(dest, "fclose");
2836                 err = TRUE;
2837         }
2838
2839         if (err) {
2840                 claws_unlink(dest);
2841                 return -1;
2842         }
2843
2844         return 0;
2845 }
2846
2847 gint canonicalize_file_replace(const gchar *file)
2848 {
2849         gchar *tmp_file;
2850
2851         tmp_file = get_tmp_file();
2852
2853         if (canonicalize_file(file, tmp_file) < 0) {
2854                 g_free(tmp_file);
2855                 return -1;
2856         }
2857
2858         if (move_file(tmp_file, file, TRUE) < 0) {
2859                 g_warning("can't replace %s .\n", file);
2860                 claws_unlink(tmp_file);
2861                 g_free(tmp_file);
2862                 return -1;
2863         }
2864
2865         g_free(tmp_file);
2866         return 0;
2867 }
2868
2869 gchar *normalize_newlines(const gchar *str)
2870 {
2871         const gchar *p = str;
2872         gchar *out, *outp;
2873
2874         out = outp = g_malloc(strlen(str) + 1);
2875         for (p = str; *p != '\0'; ++p) {
2876                 if (*p == '\r') {
2877                         if (*(p + 1) != '\n')
2878                                 *outp++ = '\n';
2879                 } else
2880                         *outp++ = *p;
2881         }
2882
2883         *outp = '\0';
2884
2885         return out;
2886 }
2887
2888 gchar *get_outgoing_rfc2822_str(FILE *fp)
2889 {
2890         gchar buf[BUFFSIZE];
2891         GString *str;
2892         gchar *ret;
2893
2894         str = g_string_new(NULL);
2895
2896         /* output header part */
2897         while (fgets(buf, sizeof(buf), fp) != NULL) {
2898                 strretchomp(buf);
2899                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2900                         gint next;
2901
2902                         for (;;) {
2903                                 next = fgetc(fp);
2904                                 if (next == EOF)
2905                                         break;
2906                                 else if (next != ' ' && next != '\t') {
2907                                         ungetc(next, fp);
2908                                         break;
2909                                 }
2910                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2911                                         break;
2912                         }
2913                 } else {
2914                         g_string_append(str, buf);
2915                         g_string_append(str, "\r\n");
2916                         if (buf[0] == '\0')
2917                                 break;
2918                 }
2919         }
2920
2921         /* output body part */
2922         while (fgets(buf, sizeof(buf), fp) != NULL) {
2923                 strretchomp(buf);
2924                 if (buf[0] == '.')
2925                         g_string_append_c(str, '.');
2926                 g_string_append(str, buf);
2927                 g_string_append(str, "\r\n");
2928         }
2929
2930         ret = str->str;
2931         g_string_free(str, FALSE);
2932
2933         return ret;
2934 }
2935
2936 /*
2937  * Create a new boundary in a way that it is very unlikely that this
2938  * will occur in the following text.  It would be easy to ensure
2939  * uniqueness if everything is either quoted-printable or base64
2940  * encoded (note that conversion is allowed), but because MIME bodies
2941  * may be nested, it may happen that the same boundary has already
2942  * been used.
2943  *
2944  *   boundary := 0*69<bchars> bcharsnospace
2945  *   bchars := bcharsnospace / " "
2946  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2947  *                  "+" / "_" / "," / "-" / "." /
2948  *                  "/" / ":" / "=" / "?"
2949  *
2950  * some special characters removed because of buggy MTAs
2951  */
2952
2953 gchar *generate_mime_boundary(const gchar *prefix)
2954 {
2955         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2956                              "abcdefghijklmnopqrstuvwxyz"
2957                              "1234567890+_./=";
2958         gchar buf_uniq[24];
2959         gint i;
2960
2961         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2962                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2963         buf_uniq[i] = '\0';
2964
2965         return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2966                                buf_uniq);
2967 }
2968
2969 gint change_file_mode_rw(FILE *fp, const gchar *file)
2970 {
2971 #if HAVE_FCHMOD
2972         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2973 #else
2974         return g_chmod(file, S_IRUSR|S_IWUSR);
2975 #endif
2976 }
2977
2978 FILE *my_tmpfile(void)
2979 {
2980 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2981         const gchar suffix[] = ".XXXXXX";
2982         const gchar *tmpdir;
2983         guint tmplen;
2984         const gchar *progname;
2985         guint proglen;
2986         gchar *fname;
2987         gint fd;
2988         FILE *fp;
2989 #ifndef G_OS_WIN32
2990         gchar buf[2]="\0";
2991 #endif
2992
2993         tmpdir = get_tmp_dir();
2994         tmplen = strlen(tmpdir);
2995         progname = g_get_prgname();
2996         if (progname == NULL)
2997                 progname = "claws-mail";
2998         proglen = strlen(progname);
2999         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3000                 return tmpfile());
3001
3002         memcpy(fname, tmpdir, tmplen);
3003         fname[tmplen] = G_DIR_SEPARATOR;
3004         memcpy(fname + tmplen + 1, progname, proglen);
3005         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3006
3007         fd = mkstemp(fname);
3008         if (fd < 0)
3009                 return tmpfile();
3010
3011 #ifndef G_OS_WIN32
3012         claws_unlink(fname);
3013         
3014         /* verify that we can write in the file after unlinking */
3015         if (write(fd, buf, 1) < 0) {
3016                 close(fd);
3017                 return tmpfile();
3018         }
3019         
3020 #endif
3021
3022         fp = fdopen(fd, "w+b");
3023         if (!fp)
3024                 close(fd);
3025         else {
3026                 rewind(fp);
3027                 return fp;
3028         }
3029
3030 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3031
3032         return tmpfile();
3033 }
3034
3035 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3036 {
3037         int fd;
3038 #ifdef G_OS_WIN32
3039         char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3040                                           dir, G_DIR_SEPARATOR);
3041         fd = mkstemp_name(template, filename);
3042         g_free(template);
3043 #else
3044         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3045         fd = mkstemp(*filename);
3046 #endif
3047         return fdopen(fd, "w+");
3048 }
3049
3050 FILE *str_open_as_stream(const gchar *str)
3051 {
3052         FILE *fp;
3053         size_t len;
3054
3055         cm_return_val_if_fail(str != NULL, NULL);
3056
3057         fp = my_tmpfile();
3058         if (!fp) {
3059                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3060                 return NULL;
3061         }
3062
3063         len = strlen(str);
3064         if (len == 0) return fp;
3065
3066         if (fwrite(str, 1, len, fp) != len) {
3067                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3068                 fclose(fp);
3069                 return NULL;
3070         }
3071
3072         rewind(fp);
3073         return fp;
3074 }
3075
3076 gint str_write_to_file(const gchar *str, const gchar *file)
3077 {
3078         FILE *fp;
3079         size_t len;
3080
3081         cm_return_val_if_fail(str != NULL, -1);
3082         cm_return_val_if_fail(file != NULL, -1);
3083
3084         if ((fp = g_fopen(file, "wb")) == NULL) {
3085                 FILE_OP_ERROR(file, "g_fopen");
3086                 return -1;
3087         }
3088
3089         len = strlen(str);
3090         if (len == 0) {
3091                 fclose(fp);
3092                 return 0;
3093         }
3094
3095         if (fwrite(str, 1, len, fp) != len) {
3096                 FILE_OP_ERROR(file, "fwrite");
3097                 fclose(fp);
3098                 claws_unlink(file);
3099                 return -1;
3100         }
3101
3102         if (fclose(fp) == EOF) {
3103                 FILE_OP_ERROR(file, "fclose");
3104                 claws_unlink(file);
3105                 return -1;
3106         }
3107
3108         return 0;
3109 }
3110
3111 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3112 {
3113         GByteArray *array;
3114         guchar buf[BUFSIZ];
3115         gint n_read;
3116         gchar *str;
3117
3118         cm_return_val_if_fail(fp != NULL, NULL);
3119
3120         array = g_byte_array_new();
3121
3122         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3123                 if (n_read < sizeof(buf) && ferror(fp))
3124                         break;
3125                 g_byte_array_append(array, buf, n_read);
3126         }
3127
3128         if (ferror(fp)) {
3129                 FILE_OP_ERROR("file stream", "fread");
3130                 g_byte_array_free(array, TRUE);
3131                 return NULL;
3132         }
3133
3134         buf[0] = '\0';
3135         g_byte_array_append(array, buf, 1);
3136         str = (gchar *)array->data;
3137         g_byte_array_free(array, FALSE);
3138
3139         if (recode && !g_utf8_validate(str, -1, NULL)) {
3140                 const gchar *src_codeset, *dest_codeset;
3141                 gchar *tmp = NULL;
3142                 src_codeset = conv_get_locale_charset_str();
3143                 dest_codeset = CS_UTF_8;
3144                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3145                 g_free(str);
3146                 str = tmp;
3147         }
3148
3149         return str;
3150 }
3151
3152 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3153 {
3154         FILE *fp;
3155         gchar *str;
3156         struct stat s;
3157 #ifndef G_OS_WIN32
3158         gint fd, err;
3159         struct timeval timeout = {1, 0};
3160         fd_set fds;
3161         int fflags = 0;
3162 #endif
3163
3164         cm_return_val_if_fail(file != NULL, NULL);
3165
3166         if (g_stat(file, &s) != 0) {
3167                 FILE_OP_ERROR(file, "stat");
3168                 return NULL;
3169         }
3170         if (S_ISDIR(s.st_mode)) {
3171                 g_warning("%s: is a directory\n", file);
3172                 return NULL;
3173         }
3174
3175 #ifdef G_OS_WIN32
3176         fp = g_fopen (file, "rb");
3177         if (fp == NULL) {
3178                 FILE_OP_ERROR(file, "open");
3179                 return NULL;
3180         }
3181 #else     
3182         /* test whether the file is readable without blocking */
3183         fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3184         if (fd == -1) {
3185                 FILE_OP_ERROR(file, "open");
3186                 return NULL;
3187         }
3188
3189         FD_ZERO(&fds);
3190         FD_SET(fd, &fds);
3191
3192         /* allow for one second */
3193         err = select(fd+1, &fds, NULL, NULL, &timeout);
3194         if (err <= 0 || !FD_ISSET(fd, &fds)) {
3195                 if (err < 0) {
3196                         FILE_OP_ERROR(file, "select");
3197                 } else {
3198                         g_warning("%s: doesn't seem readable\n", file);
3199                 }
3200                 close(fd);
3201                 return NULL;
3202         }
3203         
3204         /* Now clear O_NONBLOCK */
3205         if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3206                 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3207                 close(fd);
3208                 return NULL;
3209         }
3210         if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3211                 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3212                 close(fd);
3213                 return NULL;
3214         }
3215         
3216         /* get the FILE pointer */
3217         fp = fdopen(fd, "rb");
3218
3219         if (fp == NULL) {
3220                 FILE_OP_ERROR(file, "fdopen");
3221                 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3222                 return NULL;
3223         }
3224 #endif
3225
3226         str = file_read_stream_to_str_full(fp, recode);
3227
3228         fclose(fp);
3229
3230         return str;
3231 }
3232
3233 gchar *file_read_to_str(const gchar *file)
3234 {
3235         return file_read_to_str_full(file, TRUE);
3236 }
3237 gchar *file_read_stream_to_str(FILE *fp)
3238 {
3239         return file_read_stream_to_str_full(fp, TRUE);
3240 }
3241
3242 gchar *file_read_to_str_no_recode(const gchar *file)
3243 {
3244         return file_read_to_str_full(file, FALSE);
3245 }
3246 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3247 {
3248         return file_read_stream_to_str_full(fp, FALSE);
3249 }
3250
3251 char *fgets_crlf(char *buf, int size, FILE *stream)
3252 {
3253         gboolean is_cr = FALSE;
3254         gboolean last_was_cr = FALSE;
3255         int c = 0;
3256         char *cs;
3257
3258         cs = buf;
3259         while (--size > 0 && (c = getc(stream)) != EOF)
3260         {
3261                 *cs++ = c;
3262                 is_cr = (c == '\r');
3263                 if (c == '\n') {
3264                         break;
3265                 }
3266                 if (last_was_cr) {
3267                         *(--cs) = '\n';
3268                         cs++;
3269                         ungetc(c, stream);
3270                         break;
3271                 }
3272                 last_was_cr = is_cr;
3273         }
3274         if (c == EOF && cs == buf)
3275                 return NULL;
3276
3277         *cs = '\0';
3278
3279         return buf;     
3280 }
3281
3282 static gint execute_async(gchar *const argv[])
3283 {
3284         cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3285
3286         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3287                           NULL, NULL, NULL, FALSE) == FALSE) {
3288                 g_warning("Couldn't execute command: %s\n", argv[0]);
3289                 return -1;
3290         }
3291
3292         return 0;
3293 }
3294
3295 static gint execute_sync(gchar *const argv[])
3296 {
3297         gint status;
3298
3299         cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3300
3301 #ifdef G_OS_UNIX
3302         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3303                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3304                 g_warning("Couldn't execute command: %s\n", argv[0]);
3305                 return -1;
3306         }
3307
3308         if (WIFEXITED(status))
3309                 return WEXITSTATUS(status);
3310         else
3311                 return -1;
3312 #else
3313         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH| 
3314                          G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3315                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3316                 g_warning("Couldn't execute command: %s\n", argv[0]);
3317                 return -1;
3318         }
3319
3320         return status;
3321 #endif
3322 }
3323
3324 gint execute_command_line(const gchar *cmdline, gboolean async)
3325 {
3326         gchar **argv;
3327         gint ret;
3328
3329         debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3330
3331         argv = strsplit_with_quote(cmdline, " ", 0);
3332
3333         if (async)
3334                 ret = execute_async(argv);
3335         else
3336                 ret = execute_sync(argv);
3337
3338         g_strfreev(argv);
3339
3340         return ret;
3341 }
3342
3343 gchar *get_command_output(const gchar *cmdline)
3344 {
3345         gchar *child_stdout;
3346         gint status;
3347
3348         cm_return_val_if_fail(cmdline != NULL, NULL);
3349
3350         debug_print("get_command_output(): executing: %s\n", cmdline);
3351
3352         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3353                                       NULL) == FALSE) {
3354                 g_warning("Couldn't execute command: %s\n", cmdline);
3355                 return NULL;
3356         }
3357
3358         return child_stdout;
3359 }
3360 #ifndef MAEMO
3361 static gint is_unchanged_uri_char(char c)
3362 {
3363         switch (c) {
3364                 case '(':
3365                 case ')':
3366                         return 0;
3367                 default:
3368                         return 1;
3369         }
3370 }
3371
3372 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3373 {
3374         int i;
3375         int k;
3376
3377         k = 0;
3378         for(i = 0; i < strlen(uri) ; i++) {
3379                 if (is_unchanged_uri_char(uri[i])) {
3380                         if (k + 2 >= bufsize)
3381                                 break;
3382                         encoded_uri[k++] = uri[i];
3383                 }
3384                 else {
3385                         char * hexa = "0123456789ABCDEF";
3386
3387                         if (k + 4 >= bufsize)
3388                                 break;
3389                         encoded_uri[k++] = '%';
3390                         encoded_uri[k++] = hexa[uri[i] / 16];
3391                         encoded_uri[k++] = hexa[uri[i] % 16];
3392                 }
3393         }
3394         encoded_uri[k] = 0;
3395 }
3396 #endif
3397 gint open_uri(const gchar *uri, const gchar *cmdline)
3398 {
3399 #ifndef MAEMO
3400 #ifndef G_OS_WIN32
3401         gchar buf[BUFFSIZE];
3402         gchar *p;
3403         gchar encoded_uri[BUFFSIZE];
3404         cm_return_val_if_fail(uri != NULL, -1);
3405
3406         /* an option to choose whether to use encode_uri or not ? */
3407         encode_uri(encoded_uri, BUFFSIZE, uri);
3408
3409         if (cmdline &&
3410             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3411             !strchr(p + 2, '%'))
3412                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3413         else {
3414                 if (cmdline)
3415                         g_warning("Open URI command-line is invalid "
3416                                   "(there must be only one '%%s'): %s",
3417                                   cmdline);
3418                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3419         }
3420
3421         execute_command_line(buf, TRUE);
3422 #else
3423         ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3424 #endif
3425 #else
3426         extern osso_context_t *get_osso_context(void);
3427         osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3428                                         OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL, 
3429                                         DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3430 #endif
3431         return 0;
3432 }
3433
3434 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3435 {
3436         gchar buf[BUFFSIZE];
3437         gchar *p;
3438
3439         cm_return_val_if_fail(filepath != NULL, -1);
3440
3441         if (cmdline &&
3442             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3443             !strchr(p + 2, '%'))
3444                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3445         else {
3446                 if (cmdline)
3447                         g_warning("Open Text Editor command-line is invalid "
3448                                   "(there must be only one '%%s'): %s",
3449                                   cmdline);
3450                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3451         }
3452
3453         execute_command_line(buf, TRUE);
3454
3455         return 0;
3456 }
3457
3458 time_t remote_tzoffset_sec(const gchar *zone)
3459 {
3460         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3461         gchar zone3[4];
3462         gchar *p;
3463         gchar c;
3464         gint iustz;
3465         gint offset;
3466         time_t remoteoffset;
3467
3468         strncpy(zone3, zone, 3);
3469         zone3[3] = '\0';
3470         remoteoffset = 0;
3471
3472         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3473             (c == '+' || c == '-')) {
3474                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3475                 if (c == '-')
3476                         remoteoffset = -remoteoffset;
3477         } else if (!strncmp(zone, "UT" , 2) ||
3478                    !strncmp(zone, "GMT", 2)) {
3479                 remoteoffset = 0;
3480         } else if (strlen(zone3) == 3) {
3481                 for (p = ustzstr; *p != '\0'; p += 3) {
3482                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3483                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3484                                 remoteoffset = iustz * 3600;
3485                                 break;
3486                         }
3487                 }
3488                 if (*p == '\0')
3489                         return -1;
3490         } else if (strlen(zone3) == 1) {
3491                 switch (zone[0]) {
3492                 case 'Z': remoteoffset =   0; break;
3493                 case 'A': remoteoffset =  -1; break;
3494                 case 'B': remoteoffset =  -2; break;
3495                 case 'C': remoteoffset =  -3; break;
3496                 case 'D': remoteoffset =  -4; break;
3497                 case 'E': remoteoffset =  -5; break;
3498                 case 'F': remoteoffset =  -6; break;
3499                 case 'G': remoteoffset =  -7; break;
3500                 case 'H': remoteoffset =  -8; break;
3501                 case 'I': remoteoffset =  -9; break;
3502                 case 'K': remoteoffset = -10; break; /* J is not used */
3503                 case 'L': remoteoffset = -11; break;
3504                 case 'M': remoteoffset = -12; break;
3505                 case 'N': remoteoffset =   1; break;
3506                 case 'O': remoteoffset =   2; break;
3507                 case 'P': remoteoffset =   3; break;
3508                 case 'Q': remoteoffset =   4; break;
3509                 case 'R': remoteoffset =   5; break;
3510                 case 'S': remoteoffset =   6; break;
3511                 case 'T': remoteoffset =   7; break;
3512                 case 'U': remoteoffset =   8; break;
3513                 case 'V': remoteoffset =   9; break;
3514                 case 'W': remoteoffset =  10; break;
3515                 case 'X': remoteoffset =  11; break;
3516                 case 'Y': remoteoffset =  12; break;
3517                 default:  remoteoffset =   0; break;
3518                 }
3519                 remoteoffset = remoteoffset * 3600;
3520         } else
3521                 return -1;
3522
3523         return remoteoffset;
3524 }
3525
3526 time_t tzoffset_sec(time_t *now)
3527 {
3528         struct tm gmt, *lt;
3529         gint off;
3530 #ifndef G_OS_WIN32
3531         struct tm buf1, buf2;
3532 #endif
3533 #ifdef G_OS_WIN32
3534         if (now && *now < 0)
3535                 return 0;
3536 #endif  
3537         gmt = *gmtime_r(now, &buf1);
3538         lt = localtime_r(now, &buf2);
3539
3540         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3541
3542         if (lt->tm_year < gmt.tm_year)
3543                 off -= 24 * 60;
3544         else if (lt->tm_year > gmt.tm_year)
3545                 off += 24 * 60;
3546         else if (lt->tm_yday < gmt.tm_yday)
3547                 off -= 24 * 60;
3548         else if (lt->tm_yday > gmt.tm_yday)
3549                 off += 24 * 60;
3550
3551         if (off >= 24 * 60)             /* should be impossible */
3552                 off = 23 * 60 + 59;     /* if not, insert silly value */
3553         if (off <= -24 * 60)
3554                 off = -(23 * 60 + 59);
3555
3556         return off * 60;
3557 }
3558
3559 /* calculate timezone offset */
3560 gchar *tzoffset(time_t *now)
3561 {
3562         static gchar offset_string[6];
3563         struct tm gmt, *lt;
3564         gint off;
3565         gchar sign = '+';
3566 #ifndef G_OS_WIN32
3567         struct tm buf1, buf2;
3568 #endif
3569 #ifdef G_OS_WIN32
3570         if (now && *now < 0)
3571                 return 0;
3572 #endif
3573         gmt = *gmtime_r(now, &buf1);
3574         lt = localtime_r(now, &buf2);
3575
3576         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3577
3578         if (lt->tm_year < gmt.tm_year)
3579                 off -= 24 * 60;
3580         else if (lt->tm_year > gmt.tm_year)
3581                 off += 24 * 60;
3582         else if (lt->tm_yday < gmt.tm_yday)
3583                 off -= 24 * 60;
3584         else if (lt->tm_yday > gmt.tm_yday)
3585                 off += 24 * 60;
3586
3587         if (off < 0) {
3588                 sign = '-';
3589                 off = -off;
3590         }
3591
3592         if (off >= 24 * 60)             /* should be impossible */
3593                 off = 23 * 60 + 59;     /* if not, insert silly value */
3594
3595         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3596
3597         return offset_string;
3598 }
3599
3600 void get_rfc822_date(gchar *buf, gint len)
3601 {
3602         struct tm *lt;
3603         time_t t;
3604         gchar day[4], mon[4];
3605         gint dd, hh, mm, ss, yyyy;
3606 #ifndef G_OS_WIN32
3607         struct tm buf1;
3608         gchar buf2[BUFFSIZE];
3609 #endif
3610
3611         t = time(NULL);