2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2021 The Claws Mail Team and Hiroyuki Yamamoto
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.
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.
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/>.
18 * The code of the g_utf8_substring function below is owned by
19 * Matthias Clasen <matthiasc@src.gnome.org>/<mclasen@redhat.com>
20 * and is got from GLIB 2.30: https://git.gnome.org/browse/glib/commit/
21 * ?h=glib-2-30&id=9eb65dd3ed5e1a9638595cbe10699c7606376511
23 * GLib 2.30 is licensed under GPL v2 or later and:
24 * Copyright (C) 1999 Tom Tromey
25 * Copyright (C) 2000 Red Hat, Inc.
27 * https://git.gnome.org/browse/glib/tree/glib/gutf8.c
28 * ?h=glib-2-30&id=9eb65dd3ed5e1a9638595cbe10699c7606376511
33 #include "claws-features.h"
41 #include <glib/gi18n.h>
51 #include <sys/param.h>
53 # include <ws2tcpip.h>
55 # include <sys/socket.h>
58 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
66 #include <sys/types.h>
68 # include <sys/wait.h>
75 #include <sys/utsname.h>
90 #include "file-utils.h"
94 static gboolean debug_mode
= FALSE
;
96 void list_free_strings_full(GList
*list
)
98 g_list_free_full(list
, (GDestroyNotify
)g_free
);
101 void slist_free_strings_full(GSList
*list
)
103 g_slist_free_full(list
, (GDestroyNotify
)g_free
);
106 static void hash_free_strings_func(gpointer key
, gpointer value
, gpointer data
)
111 void hash_free_strings(GHashTable
*table
)
113 g_hash_table_foreach(table
, hash_free_strings_func
, NULL
);
116 gint
str_case_equal(gconstpointer v
, gconstpointer v2
)
118 return g_ascii_strcasecmp((const gchar
*)v
, (const gchar
*)v2
) == 0;
121 guint
str_case_hash(gconstpointer key
)
123 const gchar
*p
= key
;
127 h
= g_ascii_tolower(h
);
128 for (p
+= 1; *p
!= '\0'; p
++)
129 h
= (h
<< 5) - h
+ g_ascii_tolower(*p
);
135 gint
to_number(const gchar
*nstr
)
137 register const gchar
*p
;
139 if (*nstr
== '\0') return -1;
141 for (p
= nstr
; *p
!= '\0'; p
++)
142 if (!g_ascii_isdigit(*p
)) return -1;
147 /* convert integer into string,
148 nstr must be not lower than 11 characters length */
149 gchar
*itos_buf(gchar
*nstr
, gint n
)
151 g_snprintf(nstr
, 11, "%d", n
);
155 /* convert integer into string */
158 static gchar nstr
[11];
160 return itos_buf(nstr
, n
);
163 #define divide(num,divisor,i,d) \
165 i = num >> divisor; \
166 d = num & ((1<<divisor)-1); \
167 d = (d*100) >> divisor; \
172 * \brief Convert a given size in bytes in a human-readable string
174 * \param size The size expressed in bytes to convert in string
175 * \return The string that respresents the size in an human-readable way
177 gchar
*to_human_readable(goffset size
)
179 static gchar str
[14];
180 static gchar
*b_format
= NULL
, *kb_format
= NULL
,
181 *mb_format
= NULL
, *gb_format
= NULL
;
182 register int t
= 0, r
= 0;
183 if (b_format
== NULL
) {
185 kb_format
= _("%d.%02dKiB");
186 mb_format
= _("%d.%02dMiB");
187 gb_format
= _("%.2fGiB");
190 if (size
< (goffset
)1024) {
191 g_snprintf(str
, sizeof(str
), b_format
, (gint
)size
);
193 } else if (size
>> 10 < (goffset
)1024) {
194 divide(size
, 10, t
, r
);
195 g_snprintf(str
, sizeof(str
), kb_format
, t
, r
);
197 } else if (size
>> 20 < (goffset
)1024) {
198 divide(size
, 20, t
, r
);
199 g_snprintf(str
, sizeof(str
), mb_format
, t
, r
);
202 g_snprintf(str
, sizeof(str
), gb_format
, (gfloat
)(size
>> 30));
208 gint
path_cmp(const gchar
*s1
, const gchar
*s2
)
213 gchar
*s1buf
, *s2buf
;
216 if (s1
== NULL
|| s2
== NULL
) return -1;
217 if (*s1
== '\0' || *s2
== '\0') return -1;
220 s1buf
= g_strdup (s1
);
221 s2buf
= g_strdup (s2
);
222 subst_char (s1buf
, '/', G_DIR_SEPARATOR
);
223 subst_char (s2buf
, '/', G_DIR_SEPARATOR
);
226 #endif /* !G_OS_WIN32 */
231 if (s1
[len1
- 1] == G_DIR_SEPARATOR
) len1
--;
232 if (s2
[len2
- 1] == G_DIR_SEPARATOR
) len2
--;
234 rc
= strncmp(s1
, s2
, MAX(len1
, len2
));
238 #endif /* !G_OS_WIN32 */
242 /* remove trailing return code */
243 gchar
*strretchomp(gchar
*str
)
247 if (!*str
) return str
;
249 for (s
= str
+ strlen(str
) - 1;
250 s
>= str
&& (*s
== '\n' || *s
== '\r');
257 /* remove trailing character */
258 gchar
*strtailchomp(gchar
*str
, gchar tail_char
)
262 if (!*str
) return str
;
263 if (tail_char
== '\0') return str
;
265 for (s
= str
+ strlen(str
) - 1; s
>= str
&& *s
== tail_char
; s
--)
271 /* remove CR (carriage return) */
272 gchar
*strcrchomp(gchar
*str
)
276 if (!*str
) return str
;
278 s
= str
+ strlen(str
) - 1;
279 if (*s
== '\n' && s
> str
&& *(s
- 1) == '\r') {
287 /* truncates string at first CR (carriage return) or LF (line feed) */
288 gchar
*strcrlftrunc(gchar
*str
)
292 if ((str
== NULL
) || (!*str
)) return str
;
294 if ((p
= strstr(str
, "\r")) != NULL
)
296 if ((p
= strstr(str
, "\n")) != NULL
)
302 #ifndef HAVE_STRCASESTR
303 /* Similar to `strstr' but this function ignores the case of both strings. */
304 gchar
*strcasestr(const gchar
*haystack
, const gchar
*needle
)
306 size_t haystack_len
= strlen(haystack
);
308 return strncasestr(haystack
, haystack_len
, needle
);
310 #endif /* HAVE_STRCASESTR */
312 gchar
*strncasestr(const gchar
*haystack
, gint haystack_len
, const gchar
*needle
)
314 register size_t needle_len
;
316 needle_len
= strlen(needle
);
318 if (haystack_len
< needle_len
|| needle_len
== 0)
321 while (haystack_len
>= needle_len
) {
322 if (!g_ascii_strncasecmp(haystack
, needle
, needle_len
))
323 return (gchar
*)haystack
;
333 gpointer
my_memmem(gconstpointer haystack
, size_t haystacklen
,
334 gconstpointer needle
, size_t needlelen
)
336 const gchar
*haystack_
= (const gchar
*)haystack
;
337 const gchar
*needle_
= (const gchar
*)needle
;
338 const gchar
*haystack_cur
= (const gchar
*)haystack
;
339 size_t haystack_left
= haystacklen
;
342 return memchr(haystack_
, *needle_
, haystacklen
);
344 while ((haystack_cur
= memchr(haystack_cur
, *needle_
, haystack_left
))
346 if (haystacklen
- (haystack_cur
- haystack_
) < needlelen
)
348 if (memcmp(haystack_cur
+ 1, needle_
+ 1, needlelen
- 1) == 0)
349 return (gpointer
)haystack_cur
;
352 haystack_left
= haystacklen
- (haystack_cur
- haystack_
);
359 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
360 gchar
*strncpy2(gchar
*dest
, const gchar
*src
, size_t n
)
362 register const gchar
*s
= src
;
363 register gchar
*d
= dest
;
373 /* Examine if next block is non-ASCII string */
374 gboolean
is_next_nonascii(const gchar
*s
)
378 /* skip head space */
379 for (p
= s
; *p
!= '\0' && g_ascii_isspace(*p
); p
++)
381 for (; *p
!= '\0' && !g_ascii_isspace(*p
); p
++) {
382 if (*(guchar
*)p
> 127 || *(guchar
*)p
< 32)
389 gint
get_next_word_len(const gchar
*s
)
393 for (; *s
!= '\0' && !g_ascii_isspace(*s
); s
++, len
++)
399 static void trim_subject_for_compare(gchar
*str
)
403 eliminate_parenthesis(str
, '[', ']');
404 eliminate_parenthesis(str
, '(', ')');
407 srcp
= str
+ subject_get_prefix_length(str
);
409 memmove(str
, srcp
, strlen(srcp
) + 1);
412 static void trim_subject_for_sort(gchar
*str
)
418 srcp
= str
+ subject_get_prefix_length(str
);
420 memmove(str
, srcp
, strlen(srcp
) + 1);
423 /* compare subjects */
424 gint
subject_compare(const gchar
*s1
, const gchar
*s2
)
428 if (!s1
|| !s2
) return -1;
429 if (!*s1
|| !*s2
) return -1;
431 Xstrdup_a(str1
, s1
, return -1);
432 Xstrdup_a(str2
, s2
, return -1);
434 trim_subject_for_compare(str1
);
435 trim_subject_for_compare(str2
);
437 if (!*str1
|| !*str2
) return -1;
439 return strcmp(str1
, str2
);
442 gint
subject_compare_for_sort(const gchar
*s1
, const gchar
*s2
)
446 if (!s1
|| !s2
) return -1;
448 Xstrdup_a(str1
, s1
, return -1);
449 Xstrdup_a(str2
, s2
, return -1);
451 trim_subject_for_sort(str1
);
452 trim_subject_for_sort(str2
);
454 if (!g_utf8_validate(str1
, -1, NULL
)) {
455 g_warning("message subject \"%s\" failed UTF-8 validation", str1
);
457 } else if (!g_utf8_validate(str2
, -1, NULL
)) {
458 g_warning("message subject \"%s\" failed UTF-8 validation", str2
);
462 return g_utf8_collate(str1
, str2
);
465 void trim_subject(gchar
*str
)
467 register gchar
*srcp
;
473 srcp
= str
+ subject_get_prefix_length(str
);
478 } else if (*srcp
== '(') {
490 else if (*srcp
== cl
)
497 while (g_ascii_isspace(*srcp
)) srcp
++;
498 memmove(str
, srcp
, strlen(srcp
) + 1);
501 void eliminate_parenthesis(gchar
*str
, gchar op
, gchar cl
)
503 register gchar
*srcp
, *destp
;
508 while ((destp
= strchr(destp
, op
))) {
514 else if (*srcp
== cl
)
520 while (g_ascii_isspace(*srcp
)) srcp
++;
521 memmove(destp
, srcp
, strlen(srcp
) + 1);
525 void extract_parenthesis(gchar
*str
, gchar op
, gchar cl
)
527 register gchar
*srcp
, *destp
;
532 while ((srcp
= strchr(destp
, op
))) {
535 memmove(destp
, srcp
+ 1, strlen(srcp
));
540 else if (*destp
== cl
)
552 static void extract_parenthesis_with_skip_quote(gchar
*str
, gchar quote_chr
,
555 register gchar
*srcp
, *destp
;
557 gboolean in_quote
= FALSE
;
561 while ((srcp
= strchr_with_skip_quote(destp
, quote_chr
, op
))) {
564 memmove(destp
, srcp
+ 1, strlen(srcp
));
567 if (*destp
== op
&& !in_quote
)
569 else if (*destp
== cl
&& !in_quote
)
571 else if (*destp
== quote_chr
)
583 void extract_quote(gchar
*str
, gchar quote_chr
)
587 if ((str
= strchr(str
, quote_chr
))) {
589 while ((p
= strchr(p
+ 1, quote_chr
)) && (p
[-1] == '\\')) {
590 memmove(p
- 1, p
, strlen(p
) + 1);
595 memmove(str
, str
+ 1, p
- str
);
600 /* Returns a newly allocated string with all quote_chr not at the beginning
601 or the end of str escaped with '\' or the given str if not required. */
602 gchar
*escape_internal_quotes(gchar
*str
, gchar quote_chr
)
604 register gchar
*p
, *q
;
608 if (str
== NULL
|| *str
== '\0')
614 /* search for unescaped quote_chr */
619 if (*p
== quote_chr
&& *(p
- 1) != '\\' && *(p
+ 1) != '\0')
623 if (!k
) /* nothing to escape */
626 /* unescaped quote_chr found */
627 qstr
= g_malloc(l
+ k
+ 1);
630 if (*p
== quote_chr
) {
635 if (*p
== quote_chr
&& *(p
- 1) != '\\' && *(p
+ 1) != '\0')
644 void eliminate_address_comment(gchar
*str
)
646 register gchar
*srcp
, *destp
;
651 while ((destp
= strchr(destp
, '"'))) {
652 if ((srcp
= strchr(destp
+ 1, '"'))) {
657 while (g_ascii_isspace(*srcp
)) srcp
++;
658 memmove(destp
, srcp
, strlen(srcp
) + 1);
668 while ((destp
= strchr_with_skip_quote(destp
, '"', '('))) {
674 else if (*srcp
== ')')
680 while (g_ascii_isspace(*srcp
)) srcp
++;
681 memmove(destp
, srcp
, strlen(srcp
) + 1);
685 gchar
*strchr_with_skip_quote(const gchar
*str
, gint quote_chr
, gint c
)
687 gboolean in_quote
= FALSE
;
690 if (*str
== c
&& !in_quote
)
692 if (*str
== quote_chr
)
700 void extract_address(gchar
*str
)
702 cm_return_if_fail(str
!= NULL
);
703 eliminate_address_comment(str
);
704 if (strchr_with_skip_quote(str
, '"', '<'))
705 extract_parenthesis_with_skip_quote(str
, '"', '<', '>');
709 void extract_list_id_str(gchar
*str
)
711 if (strchr_with_skip_quote(str
, '"', '<'))
712 extract_parenthesis_with_skip_quote(str
, '"', '<', '>');
716 static GSList
*address_list_append_real(GSList
*addr_list
, const gchar
*str
, gboolean removecomments
)
721 if (!str
) return addr_list
;
723 Xstrdup_a(work
, str
, return addr_list
);
726 eliminate_address_comment(work
);
729 while (workp
&& *workp
) {
732 if ((p
= strchr_with_skip_quote(workp
, '"', ','))) {
738 if (removecomments
&& strchr_with_skip_quote(workp
, '"', '<'))
739 extract_parenthesis_with_skip_quote
740 (workp
, '"', '<', '>');
744 addr_list
= g_slist_append(addr_list
, g_strdup(workp
));
752 GSList
*address_list_append(GSList
*addr_list
, const gchar
*str
)
754 return address_list_append_real(addr_list
, str
, TRUE
);
757 GSList
*address_list_append_with_comments(GSList
*addr_list
, const gchar
*str
)
759 return address_list_append_real(addr_list
, str
, FALSE
);
762 GSList
*references_list_prepend(GSList
*msgid_list
, const gchar
*str
)
766 if (!str
) return msgid_list
;
769 while (strp
&& *strp
) {
770 const gchar
*start
, *end
;
773 if ((start
= strchr(strp
, '<')) != NULL
) {
774 end
= strchr(start
+ 1, '>');
779 msgid
= g_strndup(start
+ 1, end
- start
- 1);
782 msgid_list
= g_slist_prepend(msgid_list
, msgid
);
792 GSList
*references_list_append(GSList
*msgid_list
, const gchar
*str
)
796 list
= references_list_prepend(NULL
, str
);
797 list
= g_slist_reverse(list
);
798 msgid_list
= g_slist_concat(msgid_list
, list
);
803 GSList
*newsgroup_list_append(GSList
*group_list
, const gchar
*str
)
808 if (!str
) return group_list
;
810 Xstrdup_a(work
, str
, return group_list
);
814 while (workp
&& *workp
) {
817 if ((p
= strchr_with_skip_quote(workp
, '"', ','))) {
825 group_list
= g_slist_append(group_list
,
834 GList
*add_history(GList
*list
, const gchar
*str
)
839 cm_return_val_if_fail(str
!= NULL
, list
);
841 old
= g_list_find_custom(list
, (gpointer
)str
, (GCompareFunc
)g_strcmp0
);
844 list
= g_list_remove(list
, old
->data
);
846 } else if (g_list_length(list
) >= MAX_HISTORY_SIZE
) {
849 last
= g_list_last(list
);
852 list
= g_list_remove(list
, last
->data
);
857 list
= g_list_prepend(list
, g_strdup(str
));
862 void remove_return(gchar
*str
)
864 register gchar
*p
= str
;
867 if (*p
== '\n' || *p
== '\r')
868 memmove(p
, p
+ 1, strlen(p
));
874 void remove_space(gchar
*str
)
876 register gchar
*p
= str
;
881 while (g_ascii_isspace(*(p
+ spc
)))
884 memmove(p
, p
+ spc
, strlen(p
+ spc
) + 1);
890 void unfold_line(gchar
*str
)
896 ch
= str
; /* iterator for source string */
899 c
= g_utf8_get_char_validated(ch
, -1);
901 if (c
== (gunichar
)-1 || c
== (gunichar
)-2) {
902 /* non-unicode byte, move past it */
907 len
= g_unichar_to_utf8(c
, NULL
);
909 if ((!g_unichar_isdefined(c
) || !g_unichar_isprint(c
) ||
910 g_unichar_isspace(c
)) && c
!= 173) {
911 /* replace anything bad or whitespacey with a single space */
915 /* move rest of the string forwards, since we just replaced
916 * a multi-byte sequence with one byte */
917 memmove(ch
, ch
+ len
-1, strlen(ch
+ len
-1) + 1);
920 /* A valid unicode character, copy it. */
926 void subst_char(gchar
*str
, gchar orig
, gchar subst
)
928 register gchar
*p
= str
;
937 void subst_chars(gchar
*str
, gchar
*orig
, gchar subst
)
939 register gchar
*p
= str
;
942 if (strchr(orig
, *p
) != NULL
)
948 void subst_for_filename(gchar
*str
)
953 subst_chars(str
, "\t\r\n\\/*?:", '_');
955 subst_chars(str
, "\t\r\n\\/*", '_');
959 void subst_for_shellsafe_filename(gchar
*str
)
963 subst_for_filename(str
);
964 subst_chars(str
, " \"'|&;()<>'!{}[]",'_');
967 gboolean
is_ascii_str(const gchar
*str
)
969 const guchar
*p
= (const guchar
*)str
;
972 if (*p
!= '\t' && *p
!= ' ' &&
973 *p
!= '\r' && *p
!= '\n' &&
974 (*p
< 32 || *p
>= 127))
982 static const gchar
* line_has_quote_char_last(const gchar
* str
, const gchar
*quote_chars
)
984 gchar
* position
= NULL
;
985 gchar
* tmp_pos
= NULL
;
988 if (str
== NULL
|| quote_chars
== NULL
)
991 for (i
= 0; i
< strlen(quote_chars
); i
++) {
992 tmp_pos
= strrchr (str
, quote_chars
[i
]);
994 || (tmp_pos
!= NULL
&& position
<= tmp_pos
) )
1000 gint
get_quote_level(const gchar
*str
, const gchar
*quote_chars
)
1002 const gchar
*first_pos
;
1003 const gchar
*last_pos
;
1004 const gchar
*p
= str
;
1005 gint quote_level
= -1;
1007 /* speed up line processing by only searching to the last '>' */
1008 if ((first_pos
= line_has_quote_char(str
, quote_chars
)) != NULL
) {
1009 /* skip a line if it contains a '<' before the initial '>' */
1010 if (memchr(str
, '<', first_pos
- str
) != NULL
)
1012 last_pos
= line_has_quote_char_last(first_pos
, quote_chars
);
1016 while (p
<= last_pos
) {
1017 while (p
< last_pos
) {
1018 if (g_ascii_isspace(*p
))
1024 if (strchr(quote_chars
, *p
))
1026 else if (*p
!= '-' && !g_ascii_isspace(*p
) && p
<= last_pos
) {
1027 /* any characters are allowed except '-','<' and space */
1028 while (*p
!= '-' && *p
!= '<'
1029 && !strchr(quote_chars
, *p
)
1030 && !g_ascii_isspace(*p
)
1033 if (strchr(quote_chars
, *p
))
1045 gint
check_line_length(const gchar
*str
, gint max_chars
, gint
*line
)
1047 const gchar
*p
= str
, *q
;
1048 gint cur_line
= 0, len
;
1050 while ((q
= strchr(p
, '\n')) != NULL
) {
1052 if (len
> max_chars
) {
1062 if (len
> max_chars
) {
1071 const gchar
* line_has_quote_char(const gchar
* str
, const gchar
*quote_chars
)
1073 gchar
* position
= NULL
;
1074 gchar
* tmp_pos
= NULL
;
1077 if (str
== NULL
|| quote_chars
== NULL
)
1080 for (i
= 0; i
< strlen(quote_chars
); i
++) {
1081 tmp_pos
= strchr (str
, quote_chars
[i
]);
1083 || (tmp_pos
!= NULL
&& position
>= tmp_pos
) )
1089 static gchar
*strstr_with_skip_quote(const gchar
*haystack
, const gchar
*needle
)
1091 register guint haystack_len
, needle_len
;
1092 gboolean in_squote
= FALSE
, in_dquote
= FALSE
;
1094 haystack_len
= strlen(haystack
);
1095 needle_len
= strlen(needle
);
1097 if (haystack_len
< needle_len
|| needle_len
== 0)
1100 while (haystack_len
>= needle_len
) {
1101 if (!in_squote
&& !in_dquote
&&
1102 !strncmp(haystack
, needle
, needle_len
))
1103 return (gchar
*)haystack
;
1105 /* 'foo"bar"' -> foo"bar"
1106 "foo'bar'" -> foo'bar' */
1107 if (*haystack
== '\'') {
1110 else if (!in_dquote
)
1112 } else if (*haystack
== '\"') {
1115 else if (!in_squote
)
1117 } else if (*haystack
== '\\') {
1129 gchar
**strsplit_with_quote(const gchar
*str
, const gchar
*delim
,
1132 GSList
*string_list
= NULL
, *slist
;
1133 gchar
**str_array
, *s
, *new_str
;
1134 guint i
, n
= 1, len
;
1136 cm_return_val_if_fail(str
!= NULL
, NULL
);
1137 cm_return_val_if_fail(delim
!= NULL
, NULL
);
1140 max_tokens
= G_MAXINT
;
1142 s
= strstr_with_skip_quote(str
, delim
);
1144 guint delimiter_len
= strlen(delim
);
1148 new_str
= g_strndup(str
, len
);
1150 if (new_str
[0] == '\'' || new_str
[0] == '\"') {
1151 if (new_str
[len
- 1] == new_str
[0]) {
1152 new_str
[len
- 1] = '\0';
1153 memmove(new_str
, new_str
+ 1, len
- 1);
1156 string_list
= g_slist_prepend(string_list
, new_str
);
1158 str
= s
+ delimiter_len
;
1159 s
= strstr_with_skip_quote(str
, delim
);
1160 } while (--max_tokens
&& s
);
1164 new_str
= g_strdup(str
);
1165 if (new_str
[0] == '\'' || new_str
[0] == '\"') {
1167 if (new_str
[len
- 1] == new_str
[0]) {
1168 new_str
[len
- 1] = '\0';
1169 memmove(new_str
, new_str
+ 1, len
- 1);
1172 string_list
= g_slist_prepend(string_list
, new_str
);
1176 str_array
= g_new(gchar
*, n
);
1180 str_array
[i
--] = NULL
;
1181 for (slist
= string_list
; slist
; slist
= slist
->next
)
1182 str_array
[i
--] = slist
->data
;
1184 g_slist_free(string_list
);
1189 gchar
*get_abbrev_newsgroup_name(const gchar
*group
, gint len
)
1191 gchar
*abbrev_group
;
1193 const gchar
*p
= group
;
1196 cm_return_val_if_fail(group
!= NULL
, NULL
);
1198 last
= group
+ strlen(group
);
1199 abbrev_group
= ap
= g_malloc(strlen(group
) + 1);
1204 if ((ap
- abbrev_group
) + (last
- p
) > len
&& strchr(p
, '.')) {
1206 while (*p
!= '.') p
++;
1209 return abbrev_group
;
1214 return abbrev_group
;
1217 gchar
*trim_string(const gchar
*str
, gint len
)
1219 const gchar
*p
= str
;
1224 if (!str
) return NULL
;
1225 if (strlen(str
) <= len
)
1226 return g_strdup(str
);
1227 if (g_utf8_validate(str
, -1, NULL
) == FALSE
)
1228 return g_strdup(str
);
1230 while (*p
!= '\0') {
1231 mb_len
= g_utf8_skip
[*(guchar
*)p
];
1234 else if (new_len
+ mb_len
> len
)
1241 Xstrndup_a(new_str
, str
, new_len
, return g_strdup(str
));
1242 return g_strconcat(new_str
, "...", NULL
);
1245 GList
*uri_list_extract_filenames(const gchar
*uri_list
)
1247 GList
*result
= NULL
;
1249 gchar
*escaped_utf8uri
;
1255 while (g_ascii_isspace(*p
)) p
++;
1256 if (!strncmp(p
, "file:", 5)) {
1259 while (*q
&& *q
!= '\n' && *q
!= '\r') q
++;
1262 gchar
*file
, *locale_file
= NULL
;
1264 while (q
> p
&& g_ascii_isspace(*q
))
1266 Xalloca(escaped_utf8uri
, q
- p
+ 2,
1268 Xalloca(file
, q
- p
+ 2,
1271 strncpy(escaped_utf8uri
, p
, q
- p
+ 1);
1272 escaped_utf8uri
[q
- p
+ 1] = '\0';
1273 decode_uri_with_plus(file
, escaped_utf8uri
, FALSE
);
1275 * g_filename_from_uri() rejects escaped/locale encoded uri
1276 * string which come from Nautilus.
1279 if (g_utf8_validate(file
, -1, NULL
))
1281 = conv_codeset_strdup(
1284 conv_get_locale_charset_str());
1286 locale_file
= g_strdup(file
+ 5);
1288 locale_file
= g_filename_from_uri(escaped_utf8uri
, NULL
, NULL
);
1290 result
= g_list_append(result
, locale_file
);
1294 p
= strchr(p
, '\n');
1301 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1304 static gint
axtoi(const gchar
*hexstr
)
1306 gint hi
, lo
, result
;
1309 if ('0' <= hi
&& hi
<= '9') {
1312 if ('a' <= hi
&& hi
<= 'f') {
1315 if ('A' <= hi
&& hi
<= 'F') {
1320 if ('0' <= lo
&& lo
<= '9') {
1323 if ('a' <= lo
&& lo
<= 'f') {
1326 if ('A' <= lo
&& lo
<= 'F') {
1329 result
= lo
+ (16 * hi
);
1333 gboolean
is_uri_string(const gchar
*str
)
1335 while (str
&& *str
&& g_ascii_isspace(*str
))
1337 return (g_ascii_strncasecmp(str
, "http://", 7) == 0 ||
1338 g_ascii_strncasecmp(str
, "https://", 8) == 0 ||
1339 g_ascii_strncasecmp(str
, "ftp://", 6) == 0 ||
1340 g_ascii_strncasecmp(str
, "ftps://", 7) == 0 ||
1341 g_ascii_strncasecmp(str
, "sftp://", 7) == 0 ||
1342 g_ascii_strncasecmp(str
, "ftp.", 4) == 0 ||
1343 g_ascii_strncasecmp(str
, "webcal://", 9) == 0 ||
1344 g_ascii_strncasecmp(str
, "webcals://", 10) == 0 ||
1345 g_ascii_strncasecmp(str
, "www.", 4) == 0);
1348 gchar
*get_uri_path(const gchar
*uri
)
1350 while (uri
&& *uri
&& g_ascii_isspace(*uri
))
1352 if (g_ascii_strncasecmp(uri
, "http://", 7) == 0)
1353 return (gchar
*)(uri
+ 7);
1354 else if (g_ascii_strncasecmp(uri
, "https://", 8) == 0)
1355 return (gchar
*)(uri
+ 8);
1356 else if (g_ascii_strncasecmp(uri
, "ftp://", 6) == 0)
1357 return (gchar
*)(uri
+ 6);
1358 else if (g_ascii_strncasecmp(uri
, "ftps://", 7) == 0)
1359 return (gchar
*)(uri
+ 7);
1360 else if (g_ascii_strncasecmp(uri
, "sftp://", 7) == 0)
1361 return (gchar
*)(uri
+ 7);
1362 else if (g_ascii_strncasecmp(uri
, "webcal://", 9) == 0)
1363 return (gchar
*)(uri
+ 7);
1364 else if (g_ascii_strncasecmp(uri
, "webcals://", 10) == 0)
1365 return (gchar
*)(uri
+ 7);
1367 return (gchar
*)uri
;
1370 gint
get_uri_len(const gchar
*str
)
1374 if (is_uri_string(str
)) {
1375 for (p
= str
; *p
!= '\0'; p
++) {
1376 if (!g_ascii_isgraph(*p
) || strchr("<>\"", *p
))
1385 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1386 * plusses, and escape characters are used)
1388 void decode_uri_with_plus(gchar
*decoded_uri
, const gchar
*encoded_uri
, gboolean with_plus
)
1390 gchar
*dec
= decoded_uri
;
1391 const gchar
*enc
= encoded_uri
;
1396 if (isxdigit((guchar
)enc
[0]) &&
1397 isxdigit((guchar
)enc
[1])) {
1403 if (with_plus
&& *enc
== '+')
1415 void decode_uri(gchar
*decoded_uri
, const gchar
*encoded_uri
)
1417 decode_uri_with_plus(decoded_uri
, encoded_uri
, TRUE
);
1420 static gchar
*decode_uri_gdup(const gchar
*encoded_uri
)
1422 gchar
*buffer
= g_malloc(strlen(encoded_uri
)+1);
1423 decode_uri_with_plus(buffer
, encoded_uri
, FALSE
);
1427 gint
scan_mailto_url(const gchar
*mailto
, gchar
**from
, gchar
**to
, gchar
**cc
, gchar
**bcc
,
1428 gchar
**subject
, gchar
**body
, gchar
***attach
, gchar
**inreplyto
)
1432 const gchar
*forbidden_uris
[] = { ".gnupg/",
1438 gint num_attach
= 0;
1440 cm_return_val_if_fail(mailto
!= NULL
, -1);
1442 Xstrdup_a(tmp_mailto
, mailto
, return -1);
1444 if (!strncmp(tmp_mailto
, "mailto:", 7))
1447 p
= strchr(tmp_mailto
, '?');
1454 *to
= decode_uri_gdup(tmp_mailto
);
1457 gchar
*field
, *value
;
1474 if (*value
== '\0') continue;
1476 if (from
&& !g_ascii_strcasecmp(field
, "from")) {
1478 *from
= decode_uri_gdup(value
);
1480 gchar
*tmp
= decode_uri_gdup(value
);
1481 gchar
*new_from
= g_strdup_printf("%s, %s", *from
, tmp
);
1486 } else if (cc
&& !g_ascii_strcasecmp(field
, "cc")) {
1488 *cc
= decode_uri_gdup(value
);
1490 gchar
*tmp
= decode_uri_gdup(value
);
1491 gchar
*new_cc
= g_strdup_printf("%s, %s", *cc
, tmp
);
1496 } else if (bcc
&& !g_ascii_strcasecmp(field
, "bcc")) {
1498 *bcc
= decode_uri_gdup(value
);
1500 gchar
*tmp
= decode_uri_gdup(value
);
1501 gchar
*new_bcc
= g_strdup_printf("%s, %s", *bcc
, tmp
);
1506 } else if (subject
&& !*subject
&&
1507 !g_ascii_strcasecmp(field
, "subject")) {
1508 *subject
= decode_uri_gdup(value
);
1509 } else if (body
&& !*body
&& !g_ascii_strcasecmp(field
, "body")) {
1510 *body
= decode_uri_gdup(value
);
1511 } else if (body
&& !*body
&& !g_ascii_strcasecmp(field
, "insert")) {
1513 gchar
*tmp
= decode_uri_gdup(value
);
1515 for (; forbidden_uris
[i
]; i
++) {
1516 if (strstr(tmp
, forbidden_uris
[i
])) {
1517 g_print("Refusing to insert '%s', potential private data leak\n",
1526 if (!is_file_entry_regular(tmp
)) {
1527 g_warning("refusing to insert '%s', not a regular file", tmp
);
1528 } else if (!g_file_get_contents(tmp
, body
, NULL
, NULL
)) {
1529 g_warning("couldn't set insert file '%s' in body", value
);
1534 } else if (attach
&& !g_ascii_strcasecmp(field
, "attach")) {
1536 gchar
*tmp
= decode_uri_gdup(value
);
1537 gchar
**my_att
= g_malloc(sizeof(char *));
1541 for (; forbidden_uris
[i
]; i
++) {
1542 if (strstr(tmp
, forbidden_uris
[i
])) {
1543 g_print("Refusing to attach '%s', potential private data leak\n",
1551 /* attach is correct */
1553 my_att
= g_realloc(my_att
, (sizeof(char *))*(num_attach
+1));
1554 my_att
[num_attach
-1] = tmp
;
1555 my_att
[num_attach
] = NULL
;
1560 } else if (inreplyto
&& !*inreplyto
&&
1561 !g_ascii_strcasecmp(field
, "in-reply-to")) {
1562 *inreplyto
= decode_uri_gdup(value
);
1571 #include <windows.h>
1572 #ifndef CSIDL_APPDATA
1573 #define CSIDL_APPDATA 0x001a
1575 #ifndef CSIDL_LOCAL_APPDATA
1576 #define CSIDL_LOCAL_APPDATA 0x001c
1578 #ifndef CSIDL_FLAG_CREATE
1579 #define CSIDL_FLAG_CREATE 0x8000
1581 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1585 w32_strerror (int w32_errno
)
1587 static char strerr
[256];
1588 int ec
= (int)GetLastError ();
1592 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, w32_errno
,
1593 MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
1594 strerr
, DIM (strerr
)-1, NULL
);
1598 static __inline__
void *
1599 dlopen (const char * name
, int flag
)
1601 void * hd
= LoadLibrary (name
);
1605 static __inline__
void *
1606 dlsym (void * hd
, const char * sym
)
1610 void * fnc
= GetProcAddress (hd
, sym
);
1619 static __inline__
const char *
1622 return w32_strerror (0);
1626 static __inline__
int
1638 w32_shgetfolderpath (HWND a
, int b
, HANDLE c
, DWORD d
, LPSTR e
)
1640 static int initialized
;
1641 static HRESULT (WINAPI
* func
)(HWND
,int,HANDLE
,DWORD
,LPSTR
);
1645 static char *dllnames
[] = { "shell32.dll", "shfolder.dll", NULL
};
1651 for (i
=0, handle
= NULL
; !handle
&& dllnames
[i
]; i
++)
1653 handle
= dlopen (dllnames
[i
], RTLD_LAZY
);
1656 func
= dlsym (handle
, "SHGetFolderPathW");
1667 return func (a
,b
,c
,d
,e
);
1672 /* Returns a static string with the directroy from which the module
1673 has been loaded. Returns an empty string on error. */
1674 static char *w32_get_module_dir(void)
1676 static char *moddir
;
1679 char name
[MAX_PATH
+10];
1682 if ( !GetModuleFileNameA (0, name
, sizeof (name
)-10) )
1685 p
= strrchr (name
, '\\');
1691 moddir
= g_strdup (name
);
1695 #endif /* G_OS_WIN32 */
1697 /* Return a static string with the locale dir. */
1698 const gchar
*get_locale_dir(void)
1700 static gchar
*loc_dir
;
1704 loc_dir
= g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S
,
1705 "\\share\\locale", NULL
);
1708 loc_dir
= LOCALEDIR
;
1714 const gchar
*get_home_dir(void)
1717 static char home_dir_utf16
[MAX_PATH
] = "";
1718 static gchar
*home_dir_utf8
= NULL
;
1719 if (home_dir_utf16
[0] == '\0') {
1720 if (w32_shgetfolderpath
1721 (NULL
, CSIDL_APPDATA
|CSIDL_FLAG_CREATE
,
1722 NULL
, 0, home_dir_utf16
) < 0)
1723 strcpy (home_dir_utf16
, "C:\\Claws Mail");
1724 home_dir_utf8
= g_utf16_to_utf8 ((const gunichar2
*)home_dir_utf16
, -1, NULL
, NULL
, NULL
);
1726 return home_dir_utf8
;
1728 static const gchar
*homeenv
= NULL
;
1733 if (!homeenv
&& g_getenv("HOME") != NULL
)
1734 homeenv
= g_strdup(g_getenv("HOME"));
1736 homeenv
= g_get_home_dir();
1742 static gchar
*claws_rc_dir
= NULL
;
1743 static gboolean rc_dir_alt
= FALSE
;
1744 const gchar
*get_rc_dir(void)
1747 if (!claws_rc_dir
) {
1748 claws_rc_dir
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
,
1750 debug_print("using default rc_dir %s\n", claws_rc_dir
);
1752 return claws_rc_dir
;
1755 void set_rc_dir(const gchar
*dir
)
1757 gchar
*canonical_dir
;
1758 if (claws_rc_dir
!= NULL
) {
1759 g_print("Error: rc_dir already set\n");
1761 int err
= cm_canonicalize_filename(dir
, &canonical_dir
);
1765 g_print("Error looking for %s: %d(%s)\n",
1766 dir
, -err
, g_strerror(-err
));
1771 claws_rc_dir
= canonical_dir
;
1773 len
= strlen(claws_rc_dir
);
1774 if (claws_rc_dir
[len
- 1] == G_DIR_SEPARATOR
)
1775 claws_rc_dir
[len
- 1] = '\0';
1777 debug_print("set rc_dir to %s\n", claws_rc_dir
);
1778 if (!is_dir_exist(claws_rc_dir
)) {
1779 if (make_dir_hier(claws_rc_dir
) != 0) {
1780 g_print("Error: can't create %s\n",
1788 gboolean
rc_dir_is_alt(void) {
1792 const gchar
*get_mail_base_dir(void)
1794 return get_home_dir();
1797 const gchar
*get_news_cache_dir(void)
1799 static gchar
*news_cache_dir
= NULL
;
1800 if (!news_cache_dir
)
1801 news_cache_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1802 NEWS_CACHE_DIR
, NULL
);
1804 return news_cache_dir
;
1807 const gchar
*get_imap_cache_dir(void)
1809 static gchar
*imap_cache_dir
= NULL
;
1811 if (!imap_cache_dir
)
1812 imap_cache_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1813 IMAP_CACHE_DIR
, NULL
);
1815 return imap_cache_dir
;
1818 const gchar
*get_mime_tmp_dir(void)
1820 static gchar
*mime_tmp_dir
= NULL
;
1823 mime_tmp_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1824 MIME_TMP_DIR
, NULL
);
1826 return mime_tmp_dir
;
1829 const gchar
*get_template_dir(void)
1831 static gchar
*template_dir
= NULL
;
1834 template_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1835 TEMPLATE_DIR
, NULL
);
1837 return template_dir
;
1841 const gchar
*w32_get_cert_file(void)
1843 const gchar
*cert_file
= NULL
;
1845 cert_file
= g_strconcat(w32_get_module_dir(),
1846 "\\share\\claws-mail\\",
1847 "ca-certificates.crt",
1853 /* Return the filepath of the claws-mail.desktop file */
1854 const gchar
*get_desktop_file(void)
1856 #ifdef DESKTOPFILEPATH
1857 return DESKTOPFILEPATH
;
1863 /* Return the default directory for Plugins. */
1864 const gchar
*get_plugin_dir(void)
1867 static gchar
*plugin_dir
= NULL
;
1870 plugin_dir
= g_strconcat(w32_get_module_dir(),
1871 "\\lib\\claws-mail\\plugins\\",
1875 if (is_dir_exist(PLUGINDIR
))
1878 static gchar
*plugin_dir
= NULL
;
1880 plugin_dir
= g_strconcat(get_rc_dir(),
1881 G_DIR_SEPARATOR_S
, "plugins",
1882 G_DIR_SEPARATOR_S
, NULL
);
1890 /* Return the default directory for Themes. */
1891 const gchar
*w32_get_themes_dir(void)
1893 static gchar
*themes_dir
= NULL
;
1896 themes_dir
= g_strconcat(w32_get_module_dir(),
1897 "\\share\\claws-mail\\themes",
1903 const gchar
*get_tmp_dir(void)
1905 static gchar
*tmp_dir
= NULL
;
1908 tmp_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1914 gchar
*get_tmp_file(void)
1917 static guint32 id
= 0;
1919 tmp_file
= g_strdup_printf("%s%ctmpfile.%08x",
1920 get_tmp_dir(), G_DIR_SEPARATOR
, id
++);
1925 const gchar
*get_domain_name(void)
1928 static gchar
*domain_name
= NULL
;
1929 struct addrinfo hints
, *res
;
1934 if (gethostname(hostname
, sizeof(hostname
)) != 0) {
1935 perror("gethostname");
1936 domain_name
= "localhost";
1938 memset(&hints
, 0, sizeof(struct addrinfo
));
1939 hints
.ai_family
= AF_UNSPEC
;
1940 hints
.ai_socktype
= 0;
1941 hints
.ai_flags
= AI_CANONNAME
;
1942 hints
.ai_protocol
= 0;
1944 s
= getaddrinfo(hostname
, NULL
, &hints
, &res
);
1946 fprintf(stderr
, "getaddrinfo: %s\n", gai_strerror(s
));
1947 domain_name
= g_strdup(hostname
);
1949 domain_name
= g_strdup(res
->ai_canonname
);
1953 debug_print("domain name = %s\n", domain_name
);
1962 /* Tells whether the given host address string is a valid representation of a
1963 * numerical IP (v4 or, if supported, v6) address.
1965 gboolean
is_numeric_host_address(const gchar
*hostaddress
)
1967 struct addrinfo hints
, *res
;
1970 /* See what getaddrinfo makes of the string when told that it is a
1971 * numeric IP address representation. */
1972 memset(&hints
, 0, sizeof(struct addrinfo
));
1973 hints
.ai_family
= AF_UNSPEC
;
1974 hints
.ai_socktype
= 0;
1975 hints
.ai_flags
= AI_NUMERICHOST
;
1976 hints
.ai_protocol
= 0;
1978 err
= getaddrinfo(hostaddress
, NULL
, &hints
, &res
);
1985 off_t
get_file_size(const gchar
*file
)
1990 GError
*error
= NULL
;
1993 f
= g_file_new_for_path(file
);
1994 fi
= g_file_query_info(f
, "standard::size",
1995 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
1996 if (error
!= NULL
) {
1997 debug_print("get_file_size error: %s\n", error
->message
);
1998 g_error_free(error
);
2002 size
= g_file_info_get_size(fi
);
2010 if (g_stat(file
, &s
) < 0) {
2011 FILE_OP_ERROR(file
, "stat");
2019 time_t get_file_mtime(const gchar
*file
)
2023 if (g_stat(file
, &s
) < 0) {
2024 FILE_OP_ERROR(file
, "stat");
2031 gboolean
file_exist(const gchar
*file
, gboolean allow_fifo
)
2038 if (g_stat(file
, &s
) < 0) {
2039 if (ENOENT
!= errno
) FILE_OP_ERROR(file
, "stat");
2043 if (S_ISREG(s
.st_mode
) || (allow_fifo
&& S_ISFIFO(s
.st_mode
)))
2050 /* Test on whether FILE is a relative file name. This is
2051 * straightforward for Unix but more complex for Windows. */
2052 gboolean
is_relative_filename(const gchar
*file
)
2057 if ( *file
== '\\' && file
[1] == '\\' && strchr (file
+2, '\\') )
2058 return FALSE
; /* Prefixed with a hostname - this can't
2059 * be a relative name. */
2061 if ( ((*file
>= 'a' && *file
<= 'z')
2062 || (*file
>= 'A' && *file
<= 'Z'))
2064 file
+= 2; /* Skip drive letter. */
2066 return !(*file
== '\\' || *file
== '/');
2068 return !(*file
== G_DIR_SEPARATOR
);
2073 gboolean
is_dir_exist(const gchar
*dir
)
2078 return g_file_test(dir
, G_FILE_TEST_IS_DIR
);
2081 gboolean
is_file_entry_exist(const gchar
*file
)
2086 return g_file_test(file
, G_FILE_TEST_EXISTS
);
2089 gboolean
is_file_entry_regular(const gchar
*file
)
2094 return g_file_test(file
, G_FILE_TEST_IS_REGULAR
);
2097 gboolean
dirent_is_regular_file(struct dirent
*d
)
2099 #if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE)
2100 if (d
->d_type
== DT_REG
)
2102 else if (d
->d_type
!= DT_UNKNOWN
)
2106 return g_file_test(d
->d_name
, G_FILE_TEST_IS_REGULAR
);
2109 gint
change_dir(const gchar
*dir
)
2111 gchar
*prevdir
= NULL
;
2114 prevdir
= g_get_current_dir();
2116 if (g_chdir(dir
) < 0) {
2117 FILE_OP_ERROR(dir
, "chdir");
2118 if (debug_mode
) g_free(prevdir
);
2120 } else if (debug_mode
) {
2123 cwd
= g_get_current_dir();
2124 if (strcmp(prevdir
, cwd
) != 0)
2125 g_print("current dir: %s\n", cwd
);
2133 gint
make_dir(const gchar
*dir
)
2135 if (g_mkdir(dir
, S_IRWXU
) < 0) {
2136 FILE_OP_ERROR(dir
, "mkdir");
2139 if (g_chmod(dir
, S_IRWXU
) < 0)
2140 FILE_OP_ERROR(dir
, "chmod");
2145 gint
make_dir_hier(const gchar
*dir
)
2150 for (p
= dir
; (p
= strchr(p
, G_DIR_SEPARATOR
)) != NULL
; p
++) {
2151 parent_dir
= g_strndup(dir
, p
- dir
);
2152 if (*parent_dir
!= '\0') {
2153 if (!is_dir_exist(parent_dir
)) {
2154 if (make_dir(parent_dir
) < 0) {
2163 if (!is_dir_exist(dir
)) {
2164 if (make_dir(dir
) < 0)
2171 gint
remove_all_files(const gchar
*dir
)
2174 const gchar
*file_name
;
2177 if ((dp
= g_dir_open(dir
, 0, NULL
)) == NULL
) {
2178 g_warning("failed to open directory: %s", dir
);
2182 while ((file_name
= g_dir_read_name(dp
)) != NULL
) {
2183 tmp
= g_strconcat(dir
, G_DIR_SEPARATOR_S
, file_name
, NULL
);
2184 if (claws_unlink(tmp
) < 0)
2185 FILE_OP_ERROR(tmp
, "unlink");
2194 gint
remove_numbered_files(const gchar
*dir
, guint first
, guint last
)
2197 const gchar
*dir_name
;
2201 if (first
== last
) {
2202 /* Skip all the dir reading part. */
2203 gchar
*filename
= g_strdup_printf("%s%s%u", dir
, G_DIR_SEPARATOR_S
, first
);
2204 if (is_dir_exist(filename
)) {
2205 /* a numbered directory with this name exists,
2206 * remove the dot-file instead */
2208 filename
= g_strdup_printf("%s%s.%u", dir
, G_DIR_SEPARATOR_S
, first
);
2210 if (claws_unlink(filename
) < 0) {
2211 FILE_OP_ERROR(filename
, "unlink");
2219 prev_dir
= g_get_current_dir();
2221 if (g_chdir(dir
) < 0) {
2222 FILE_OP_ERROR(dir
, "chdir");
2227 if ((dp
= g_dir_open(".", 0, NULL
)) == NULL
) {
2228 g_warning("failed to open directory: %s", dir
);
2233 while ((dir_name
= g_dir_read_name(dp
)) != NULL
) {
2234 file_no
= to_number(dir_name
);
2235 if (file_no
> 0 && first
<= file_no
&& file_no
<= last
) {
2236 if (is_dir_exist(dir_name
)) {
2237 gchar
*dot_file
= g_strdup_printf(".%s", dir_name
);
2238 if (is_file_exist(dot_file
) && claws_unlink(dot_file
) < 0) {
2239 FILE_OP_ERROR(dot_file
, "unlink");
2244 if (claws_unlink(dir_name
) < 0)
2245 FILE_OP_ERROR(dir_name
, "unlink");
2251 if (g_chdir(prev_dir
) < 0) {
2252 FILE_OP_ERROR(prev_dir
, "chdir");
2262 gint
remove_numbered_files_not_in_list(const gchar
*dir
, GSList
*numberlist
)
2265 const gchar
*dir_name
;
2268 GHashTable
*wanted_files
;
2270 GError
*error
= NULL
;
2272 if (numberlist
== NULL
)
2275 prev_dir
= g_get_current_dir();
2277 if (g_chdir(dir
) < 0) {
2278 FILE_OP_ERROR(dir
, "chdir");
2283 if ((dp
= g_dir_open(".", 0, &error
)) == NULL
) {
2284 g_message("Couldn't open current directory: %s (%d).\n",
2285 error
->message
, error
->code
);
2286 g_error_free(error
);
2291 wanted_files
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
2292 for (cur
= numberlist
; cur
!= NULL
; cur
= cur
->next
) {
2293 /* numberlist->data is expected to be GINT_TO_POINTER */
2294 g_hash_table_insert(wanted_files
, cur
->data
, GINT_TO_POINTER(1));
2297 while ((dir_name
= g_dir_read_name(dp
)) != NULL
) {
2298 file_no
= to_number(dir_name
);
2299 if (is_dir_exist(dir_name
))
2301 if (file_no
> 0 && g_hash_table_lookup(wanted_files
, GINT_TO_POINTER(file_no
)) == NULL
) {
2302 debug_print("removing unwanted file %d from %s\n", file_no
, dir
);
2303 if (is_dir_exist(dir_name
)) {
2304 gchar
*dot_file
= g_strdup_printf(".%s", dir_name
);
2305 if (is_file_exist(dot_file
) && claws_unlink(dot_file
) < 0) {
2306 FILE_OP_ERROR(dot_file
, "unlink");
2311 if (claws_unlink(dir_name
) < 0)
2312 FILE_OP_ERROR(dir_name
, "unlink");
2317 g_hash_table_destroy(wanted_files
);
2319 if (g_chdir(prev_dir
) < 0) {
2320 FILE_OP_ERROR(prev_dir
, "chdir");
2330 gint
remove_all_numbered_files(const gchar
*dir
)
2332 return remove_numbered_files(dir
, 0, UINT_MAX
);
2335 gint
remove_dir_recursive(const gchar
*dir
)
2339 const gchar
*dir_name
;
2342 if (g_stat(dir
, &s
) < 0) {
2343 FILE_OP_ERROR(dir
, "stat");
2344 if (ENOENT
== errno
) return 0;
2348 if (!S_ISDIR(s
.st_mode
)) {
2349 if (claws_unlink(dir
) < 0) {
2350 FILE_OP_ERROR(dir
, "unlink");
2357 prev_dir
= g_get_current_dir();
2358 /* g_print("prev_dir = %s\n", prev_dir); */
2360 if (!path_cmp(prev_dir
, dir
)) {
2362 if (g_chdir("..") < 0) {
2363 FILE_OP_ERROR(dir
, "chdir");
2366 prev_dir
= g_get_current_dir();
2369 if (g_chdir(dir
) < 0) {
2370 FILE_OP_ERROR(dir
, "chdir");
2375 if ((dp
= g_dir_open(".", 0, NULL
)) == NULL
) {
2376 g_warning("failed to open directory: %s", dir
);
2382 /* remove all files in the directory */
2383 while ((dir_name
= g_dir_read_name(dp
)) != NULL
) {
2384 /* g_print("removing %s\n", dir_name); */
2386 if (is_dir_exist(dir_name
)) {
2389 if ((ret
= remove_dir_recursive(dir_name
)) < 0) {
2390 g_warning("can't remove directory: %s", dir_name
);
2395 if (claws_unlink(dir_name
) < 0)
2396 FILE_OP_ERROR(dir_name
, "unlink");
2402 if (g_chdir(prev_dir
) < 0) {
2403 FILE_OP_ERROR(prev_dir
, "chdir");
2410 if (g_rmdir(dir
) < 0) {
2411 FILE_OP_ERROR(dir
, "rmdir");
2418 /* convert line endings into CRLF. If the last line doesn't end with
2419 * linebreak, add it.
2421 gchar
*canonicalize_str(const gchar
*str
)
2427 for (p
= str
; *p
!= '\0'; ++p
) {
2434 if (p
== str
|| *(p
- 1) != '\n')
2437 out
= outp
= g_malloc(new_len
+ 1);
2438 for (p
= str
; *p
!= '\0'; ++p
) {
2445 if (p
== str
|| *(p
- 1) != '\n') {
2454 gchar
*normalize_newlines(const gchar
*str
)
2459 out
= outp
= g_malloc(strlen(str
) + 1);
2460 for (p
= str
; *p
!= '\0'; ++p
) {
2462 if (*(p
+ 1) != '\n')
2473 gchar
*get_outgoing_rfc2822_str(FILE *fp
)
2475 gchar buf
[BUFFSIZE
];
2479 str
= g_string_new(NULL
);
2481 /* output header part */
2482 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2484 if (!g_ascii_strncasecmp(buf
, "Bcc:", 4)) {
2491 else if (next
!= ' ' && next
!= '\t') {
2495 if (claws_fgets(buf
, sizeof(buf
), fp
) == NULL
)
2499 g_string_append(str
, buf
);
2500 g_string_append(str
, "\r\n");
2506 /* output body part */
2507 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2510 g_string_append_c(str
, '.');
2511 g_string_append(str
, buf
);
2512 g_string_append(str
, "\r\n");
2516 g_string_free(str
, FALSE
);
2522 * Create a new boundary in a way that it is very unlikely that this
2523 * will occur in the following text. It would be easy to ensure
2524 * uniqueness if everything is either quoted-printable or base64
2525 * encoded (note that conversion is allowed), but because MIME bodies
2526 * may be nested, it may happen that the same boundary has already
2529 * boundary := 0*69<bchars> bcharsnospace
2530 * bchars := bcharsnospace / " "
2531 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2532 * "+" / "_" / "," / "-" / "." /
2533 * "/" / ":" / "=" / "?"
2535 * some special characters removed because of buggy MTAs
2538 gchar
*generate_mime_boundary(const gchar
*prefix
)
2540 static gchar tbl
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2541 "abcdefghijklmnopqrstuvwxyz"
2546 for (i
= 0; i
< sizeof(buf_uniq
) - 1; i
++)
2547 buf_uniq
[i
] = tbl
[g_random_int_range(0, sizeof(tbl
) - 1)];
2550 return g_strdup_printf("%s_/%s", prefix
? prefix
: "MP",
2554 char *fgets_crlf(char *buf
, int size
, FILE *stream
)
2556 gboolean is_cr
= FALSE
;
2557 gboolean last_was_cr
= FALSE
;
2562 while (--size
> 0 && (c
= getc(stream
)) != EOF
)
2565 is_cr
= (c
== '\r');
2575 last_was_cr
= is_cr
;
2577 if (c
== EOF
&& cs
== buf
)
2585 static gint
execute_async(gchar
*const argv
[], const gchar
*working_directory
)
2587 cm_return_val_if_fail(argv
!= NULL
&& argv
[0] != NULL
, -1);
2589 if (g_spawn_async(working_directory
, (gchar
**)argv
, NULL
, G_SPAWN_SEARCH_PATH
,
2590 NULL
, NULL
, NULL
, FALSE
) == FALSE
) {
2591 g_warning("couldn't execute command: %s", argv
[0]);
2598 static gint
execute_sync(gchar
*const argv
[], const gchar
*working_directory
)
2602 cm_return_val_if_fail(argv
!= NULL
&& argv
[0] != NULL
, -1);
2605 if (g_spawn_sync(working_directory
, (gchar
**)argv
, NULL
, G_SPAWN_SEARCH_PATH
,
2606 NULL
, NULL
, NULL
, NULL
, &status
, NULL
) == FALSE
) {
2607 g_warning("couldn't execute command: %s", argv
[0]);
2611 if (WIFEXITED(status
))
2612 return WEXITSTATUS(status
);
2616 if (g_spawn_sync(working_directory
, (gchar
**)argv
, NULL
,
2617 G_SPAWN_SEARCH_PATH
|
2618 G_SPAWN_CHILD_INHERITS_STDIN
|
2619 G_SPAWN_LEAVE_DESCRIPTORS_OPEN
,
2620 NULL
, NULL
, NULL
, NULL
, &status
, NULL
) == FALSE
) {
2621 g_warning("couldn't execute command: %s", argv
[0]);
2629 gint
execute_command_line(const gchar
*cmdline
, gboolean async
,
2630 const gchar
*working_directory
)
2635 cm_return_val_if_fail(cmdline
!= NULL
, -1);
2637 debug_print("execute_command_line(): executing: %s\n", cmdline
);
2639 argv
= strsplit_with_quote(cmdline
, " ", 0);
2642 ret
= execute_async(argv
, working_directory
);
2644 ret
= execute_sync(argv
, working_directory
);
2651 gchar
*get_command_output(const gchar
*cmdline
)
2653 gchar
*child_stdout
;
2656 cm_return_val_if_fail(cmdline
!= NULL
, NULL
);
2658 debug_print("get_command_output(): executing: %s\n", cmdline
);
2660 if (g_spawn_command_line_sync(cmdline
, &child_stdout
, NULL
, &status
,
2662 g_warning("couldn't execute command: %s", cmdline
);
2666 return child_stdout
;
2669 FILE *get_command_output_stream(const char* cmdline
)
2673 gchar
**argv
= NULL
;
2676 cm_return_val_if_fail(cmdline
!= NULL
, NULL
);
2678 debug_print("get_command_output_stream(): executing: %s\n", cmdline
);
2680 /* turn the command-line string into an array */
2681 if (!g_shell_parse_argv(cmdline
, NULL
, &argv
, &err
)) {
2682 g_warning("could not parse command line from '%s': %s", cmdline
, err
->message
);
2687 if (!g_spawn_async_with_pipes(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
,
2688 NULL
, NULL
, &pid
, NULL
, &fd
, NULL
, &err
)
2691 g_warning("could not spawn '%s': %s", cmdline
, err
->message
);
2698 return fdopen(fd
, "r");
2702 static gint
is_unchanged_uri_char(char c
)
2713 static void encode_uri(gchar
*encoded_uri
, gint bufsize
, const gchar
*uri
)
2719 for(i
= 0; i
< strlen(uri
) ; i
++) {
2720 if (is_unchanged_uri_char(uri
[i
])) {
2721 if (k
+ 2 >= bufsize
)
2723 encoded_uri
[k
++] = uri
[i
];
2726 char * hexa
= "0123456789ABCDEF";
2728 if (k
+ 4 >= bufsize
)
2730 encoded_uri
[k
++] = '%';
2731 encoded_uri
[k
++] = hexa
[uri
[i
] / 16];
2732 encoded_uri
[k
++] = hexa
[uri
[i
] % 16];
2739 gint
open_uri(const gchar
*uri
, const gchar
*cmdline
)
2743 gchar buf
[BUFFSIZE
];
2745 gchar encoded_uri
[BUFFSIZE
];
2746 cm_return_val_if_fail(uri
!= NULL
, -1);
2748 /* an option to choose whether to use encode_uri or not ? */
2749 encode_uri(encoded_uri
, BUFFSIZE
, uri
);
2752 (p
= strchr(cmdline
, '%')) && *(p
+ 1) == 's' &&
2753 !strchr(p
+ 2, '%'))
2754 g_snprintf(buf
, sizeof(buf
), cmdline
, encoded_uri
);
2757 g_warning("Open URI command-line is invalid "
2758 "(there must be only one '%%s'): %s",
2760 g_snprintf(buf
, sizeof(buf
), DEFAULT_BROWSER_CMD
, encoded_uri
);
2763 execute_command_line(buf
, TRUE
, NULL
);
2765 ShellExecute(NULL
, "open", uri
, NULL
, NULL
, SW_SHOW
);
2770 gint
open_txt_editor(const gchar
*filepath
, const gchar
*cmdline
)
2772 gchar buf
[BUFFSIZE
];
2775 cm_return_val_if_fail(filepath
!= NULL
, -1);
2778 (p
= strchr(cmdline
, '%')) && *(p
+ 1) == 's' &&
2779 !strchr(p
+ 2, '%'))
2780 g_snprintf(buf
, sizeof(buf
), cmdline
, filepath
);
2783 g_warning("Open Text Editor command-line is invalid "
2784 "(there must be only one '%%s'): %s",
2786 g_snprintf(buf
, sizeof(buf
), DEFAULT_EDITOR_CMD
, filepath
);
2789 execute_command_line(buf
, TRUE
, NULL
);
2794 time_t remote_tzoffset_sec(const gchar
*zone
)
2796 static gchar ustzstr
[] = "PSTPDTMSTMDTCSTCDTESTEDT";
2802 time_t remoteoffset
;
2804 strncpy(zone3
, zone
, 3);
2808 if (sscanf(zone
, "%c%d", &c
, &offset
) == 2 &&
2809 (c
== '+' || c
== '-')) {
2810 remoteoffset
= ((offset
/ 100) * 60 + (offset
% 100)) * 60;
2812 remoteoffset
= -remoteoffset
;
2813 } else if (!strncmp(zone
, "UT" , 2) ||
2814 !strncmp(zone
, "GMT", 3)) {
2816 } else if (strlen(zone3
) == 3) {
2817 for (p
= ustzstr
; *p
!= '\0'; p
+= 3) {
2818 if (!g_ascii_strncasecmp(p
, zone3
, 3)) {
2819 iustz
= ((gint
)(p
- ustzstr
) / 3 + 1) / 2 - 8;
2820 remoteoffset
= iustz
* 3600;
2826 } else if (strlen(zone3
) == 1) {
2828 case 'Z': remoteoffset
= 0; break;
2829 case 'A': remoteoffset
= -1; break;
2830 case 'B': remoteoffset
= -2; break;
2831 case 'C': remoteoffset
= -3; break;
2832 case 'D': remoteoffset
= -4; break;
2833 case 'E': remoteoffset
= -5; break;
2834 case 'F': remoteoffset
= -6; break;
2835 case 'G': remoteoffset
= -7; break;
2836 case 'H': remoteoffset
= -8; break;
2837 case 'I': remoteoffset
= -9; break;
2838 case 'K': remoteoffset
= -10; break; /* J is not used */
2839 case 'L': remoteoffset
= -11; break;
2840 case 'M': remoteoffset
= -12; break;
2841 case 'N': remoteoffset
= 1; break;
2842 case 'O': remoteoffset
= 2; break;
2843 case 'P': remoteoffset
= 3; break;
2844 case 'Q': remoteoffset
= 4; break;
2845 case 'R': remoteoffset
= 5; break;
2846 case 'S': remoteoffset
= 6; break;
2847 case 'T': remoteoffset
= 7; break;
2848 case 'U': remoteoffset
= 8; break;
2849 case 'V': remoteoffset
= 9; break;
2850 case 'W': remoteoffset
= 10; break;
2851 case 'X': remoteoffset
= 11; break;
2852 case 'Y': remoteoffset
= 12; break;
2853 default: remoteoffset
= 0; break;
2855 remoteoffset
= remoteoffset
* 3600;
2859 return remoteoffset
;
2862 time_t tzoffset_sec(time_t *now
)
2866 struct tm buf1
, buf2
;
2868 if (now
&& *now
< 0)
2871 gmt
= *gmtime_r(now
, &buf1
);
2872 lt
= localtime_r(now
, &buf2
);
2874 off
= (lt
->tm_hour
- gmt
.tm_hour
) * 60 + lt
->tm_min
- gmt
.tm_min
;
2876 if (lt
->tm_year
< gmt
.tm_year
)
2878 else if (lt
->tm_year
> gmt
.tm_year
)
2880 else if (lt
->tm_yday
< gmt
.tm_yday
)
2882 else if (lt
->tm_yday
> gmt
.tm_yday
)
2885 if (off
>= 24 * 60) /* should be impossible */
2886 off
= 23 * 60 + 59; /* if not, insert silly value */
2887 if (off
<= -24 * 60)
2888 off
= -(23 * 60 + 59);
2893 /* calculate timezone offset */
2894 gchar
*tzoffset(time_t *now
)
2896 static gchar offset_string
[6];
2900 struct tm buf1
, buf2
;
2902 if (now
&& *now
< 0)
2905 gmt
= *gmtime_r(now
, &buf1
);
2906 lt
= localtime_r(now
, &buf2
);
2908 off
= (lt
->tm_hour
- gmt
.tm_hour
) * 60 + lt
->tm_min
- gmt
.tm_min
;
2910 if (lt
->tm_year
< gmt
.tm_year
)
2912 else if (lt
->tm_year
> gmt
.tm_year
)
2914 else if (lt
->tm_yday
< gmt
.tm_yday
)
2916 else if (lt
->tm_yday
> gmt
.tm_yday
)
2924 if (off
>= 24 * 60) /* should be impossible */
2925 off
= 23 * 60 + 59; /* if not, insert silly value */
2927 sprintf(offset_string
, "%c%02d%02d", sign
, off
/ 60, off
% 60);
2929 return offset_string
;
2932 static void _get_rfc822_date(gchar
*buf
, gint len
, gboolean hidetz
)
2936 gchar day
[4], mon
[4];
2937 gint dd
, hh
, mm
, ss
, yyyy
;
2939 gchar buf2
[RFC822_DATE_BUFFSIZE
];
2943 lt
= gmtime_r(&t
, &buf1
);
2945 lt
= localtime_r(&t
, &buf1
);
2947 if (sscanf(asctime_r(lt
, buf2
), "%3s %3s %d %d:%d:%d %d\n",
2948 day
, mon
, &dd
, &hh
, &mm
, &ss
, &yyyy
) != 7)
2949 g_warning("failed reading date/time");
2951 g_snprintf(buf
, len
, "%s, %d %s %d %02d:%02d:%02d %s",
2952 day
, dd
, mon
, yyyy
, hh
, mm
, ss
, (hidetz
? "-0000": tzoffset(&t
)));
2955 void get_rfc822_date(gchar
*buf
, gint len
)
2957 _get_rfc822_date(buf
, len
, FALSE
);
2960 void get_rfc822_date_hide_tz(gchar
*buf
, gint len
)
2962 _get_rfc822_date(buf
, len
, TRUE
);
2965 void debug_set_mode(gboolean mode
)
2970 gboolean
debug_get_mode(void)
2976 void debug_print_real(const char *file
, int line
, const gchar
*format
, ...)
2979 gchar buf
[BUFFSIZE
];
2982 if (!debug_mode
) return;
2984 prefix_len
= g_snprintf(buf
, sizeof(buf
), "%s:%d:", debug_srcname(file
), line
);
2986 va_start(args
, format
);
2987 g_vsnprintf(buf
+ prefix_len
, sizeof(buf
) - prefix_len
, format
, args
);
2993 void debug_print_real(const gchar
*format
, ...)
2996 gchar buf
[BUFFSIZE
];
2998 if (!debug_mode
) return;
3000 va_start(args
, format
);
3001 g_vsnprintf(buf
, sizeof(buf
), format
, args
);
3009 const char * debug_srcname(const char *file
)
3011 const char *s
= strrchr (file
, '/');
3016 void * subject_table_lookup(GHashTable
*subject_table
, gchar
* subject
)
3018 if (subject
== NULL
)
3021 subject
+= subject_get_prefix_length(subject
);
3023 return g_hash_table_lookup(subject_table
, subject
);
3026 void subject_table_insert(GHashTable
*subject_table
, gchar
* subject
,
3029 if (subject
== NULL
|| *subject
== 0)
3031 subject
+= subject_get_prefix_length(subject
);
3032 g_hash_table_insert(subject_table
, subject
, data
);
3035 void subject_table_remove(GHashTable
*subject_table
, gchar
* subject
)
3037 if (subject
== NULL
)
3040 subject
+= subject_get_prefix_length(subject
);
3041 g_hash_table_remove(subject_table
, subject
);
3044 static regex_t u_regex
;
3045 static gboolean u_init_
;
3047 void utils_free_regex(void)
3056 *\brief Check if a string is prefixed with known (combinations)
3057 * of prefixes. The function assumes that each prefix
3058 * is terminated by zero or exactly _one_ space.
3060 *\param str String to check for a prefixes
3062 *\return int Number of chars in the prefix that should be skipped
3063 * for a "clean" subject line. If no prefix was found, 0
3066 int subject_get_prefix_length(const gchar
*subject
)
3068 /*!< Array with allowable reply prefixes regexps. */
3069 static const gchar
* const prefixes
[] = {
3070 "Re\\:", /* "Re:" */
3071 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3072 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3073 "Aw\\:", /* "Aw:" (German) */
3074 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3075 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3076 "Fw\\:", /* "Fw:" Forward */
3077 "Fwd\\:", /* "Fwd:" Forward */
3078 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3079 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3080 "Rif\\:", /* "Rif:" (Italian Outlook) */
3081 "Sv\\:", /* "Sv" (Norwegian) */
3082 "Vs\\:", /* "Vs" (Norwegian) */
3083 "Ad\\:", /* "Ad" (Norwegian) */
3084 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3085 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3086 "Re \\:", /* "Re :" (French Yahoo Mail) */
3089 const int PREFIXES
= sizeof prefixes
/ sizeof prefixes
[0];
3093 if (!subject
) return 0;
3094 if (!*subject
) return 0;
3097 GString
*s
= g_string_new("");
3099 for (n
= 0; n
< PREFIXES
; n
++)
3100 /* Terminate each prefix regexpression by a
3101 * "\ ?" (zero or ONE space), and OR them */
3102 g_string_append_printf(s
, "(%s\\ ?)%s",
3107 g_string_prepend(s
, "(");
3108 g_string_append(s
, ")+"); /* match at least once */
3109 g_string_prepend(s
, "^\\ *"); /* from beginning of line */
3112 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3113 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3114 if (regcomp(&u_regex
, s
->str
, REG_EXTENDED
| REG_ICASE
)) {
3115 debug_print("Error compiling regexp %s\n", s
->str
);
3116 g_string_free(s
, TRUE
);
3120 g_string_free(s
, TRUE
);
3124 if (!regexec(&u_regex
, subject
, 1, &pos
, 0) && pos
.rm_so
!= -1)
3130 static guint
g_stricase_hash(gconstpointer gptr
)
3132 guint hash_result
= 0;
3135 for (str
= gptr
; str
&& *str
; str
++) {
3136 hash_result
+= toupper(*str
);
3142 static gint
g_stricase_equal(gconstpointer gptr1
, gconstpointer gptr2
)
3144 const char *str1
= gptr1
;
3145 const char *str2
= gptr2
;
3147 return !strcasecmp(str1
, str2
);
3150 gint
g_int_compare(gconstpointer a
, gconstpointer b
)
3152 return GPOINTER_TO_INT(a
) - GPOINTER_TO_INT(b
);
3156 quote_cmd_argument()
3158 return a quoted string safely usable in argument of a command.
3160 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3163 gint
quote_cmd_argument(gchar
* result
, guint size
,
3173 for(p
= path
; * p
!= '\0' ; p
++) {
3175 if (isalnum((guchar
)*p
) || (* p
== '/')) {
3176 if (remaining
> 0) {
3182 result
[size
- 1] = '\0';
3187 if (remaining
>= 2) {
3195 result
[size
- 1] = '\0';
3200 if (remaining
> 0) {
3204 result
[size
- 1] = '\0';
3218 static void g_node_map_recursive(GNode
*node
, gpointer data
)
3220 GNodeMapData
*mapdata
= (GNodeMapData
*) data
;
3222 GNodeMapData newmapdata
;
3225 newdata
= mapdata
->func(node
->data
, mapdata
->data
);
3226 if (newdata
!= NULL
) {
3227 newnode
= g_node_new(newdata
);
3228 g_node_append(mapdata
->parent
, newnode
);
3230 newmapdata
.parent
= newnode
;
3231 newmapdata
.func
= mapdata
->func
;
3232 newmapdata
.data
= mapdata
->data
;
3234 g_node_children_foreach(node
, G_TRAVERSE_ALL
, g_node_map_recursive
, &newmapdata
);
3238 GNode
*g_node_map(GNode
*node
, GNodeMapFunc func
, gpointer data
)
3241 GNodeMapData mapdata
;
3243 cm_return_val_if_fail(node
!= NULL
, NULL
);
3244 cm_return_val_if_fail(func
!= NULL
, NULL
);
3246 root
= g_node_new(func(node
->data
, data
));
3248 mapdata
.parent
= root
;
3249 mapdata
.func
= func
;
3250 mapdata
.data
= data
;
3252 g_node_children_foreach(node
, G_TRAVERSE_ALL
, g_node_map_recursive
, &mapdata
);
3257 #define HEX_TO_INT(val, hex) \
3261 if ('0' <= c && c <= '9') { \
3263 } else if ('a' <= c && c <= 'f') { \
3264 val = c - 'a' + 10; \
3265 } else if ('A' <= c && c <= 'F') { \
3266 val = c - 'A' + 10; \
3272 gboolean
get_hex_value(guchar
*out
, gchar c1
, gchar c2
)
3279 if (hi
== -1 || lo
== -1)
3282 *out
= (hi
<< 4) + lo
;
3286 #define INT_TO_HEX(hex, val) \
3289 hex = '0' + (val); \
3291 hex = 'A' + (val) - 10; \
3294 void get_hex_str(gchar
*out
, guchar ch
)
3298 INT_TO_HEX(hex
, ch
>> 4);
3300 INT_TO_HEX(hex
, ch
& 0x0f);
3306 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3308 #define G_PRINT_REF g_print
3312 *\brief Register ref counted pointer. It is based on GBoxed, so should
3313 * work with anything that uses the GType system. The semantics
3314 * are similar to a C++ auto pointer, with the exception that
3315 * C doesn't have automatic closure (calling destructors) when
3316 * exiting a block scope.
3317 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3318 * function directly.
3320 *\return GType A GType type.
3322 GType
g_auto_pointer_register(void)
3324 static GType auto_pointer_type
;
3325 if (!auto_pointer_type
)
3327 g_boxed_type_register_static
3328 ("G_TYPE_AUTO_POINTER",
3329 (GBoxedCopyFunc
) g_auto_pointer_copy
,
3330 (GBoxedFreeFunc
) g_auto_pointer_free
);
3331 return auto_pointer_type
;
3335 *\brief Structure with g_new() allocated pointer guarded by the
3338 typedef struct AutoPointerRef
{
3339 void (*free
) (gpointer
);
3345 *\brief The auto pointer opaque structure that references the
3346 * pointer guard block.
3348 typedef struct AutoPointer
{
3349 AutoPointerRef
*ref
;
3350 gpointer ptr
; /*!< access to protected pointer */
3354 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3358 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3359 * ... when assigning, copying and freeing storage elements
3361 * gtk_list_store_new(N_S_COLUMNS,
3362 * G_TYPE_AUTO_POINTER,
3366 * Template *precious_data = g_new0(Template, 1);
3367 * g_pointer protect = g_auto_pointer_new(precious_data);
3369 * gtk_list_store_set(container, &iter,
3373 * ... the gtk_list_store has copied the pointer and
3374 * ... incremented its reference count, we should free
3375 * ... the auto pointer (in C++ a destructor would do
3376 * ... this for us when leaving block scope)
3378 * g_auto_pointer_free(protect);
3380 * ... gtk_list_store_set() now manages the data. When
3381 * ... *explicitly* requesting a pointer from the list
3382 * ... store, don't forget you get a copy that should be
3383 * ... freed with g_auto_pointer_free() eventually.
3387 *\param pointer Pointer to be guarded.
3389 *\return GAuto * Pointer that should be used in containers with
3392 GAuto
*g_auto_pointer_new(gpointer p
)
3394 AutoPointerRef
*ref
;
3400 ref
= g_new0(AutoPointerRef
, 1);
3401 ptr
= g_new0(AutoPointer
, 1);
3411 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p
);
3417 *\brief Allocate an autopointer using the passed \a free function to
3418 * free the guarded pointer
3420 GAuto
*g_auto_pointer_new_with_free(gpointer p
, GFreeFunc free_
)
3427 aptr
= g_auto_pointer_new(p
);
3428 aptr
->ref
->free
= free_
;
3432 gpointer
g_auto_pointer_get_ptr(GAuto
*auto_ptr
)
3434 if (auto_ptr
== NULL
)
3436 return ((AutoPointer
*) auto_ptr
)->ptr
;
3440 *\brief Copies an auto pointer by. It's mostly not necessary
3441 * to call this function directly, unless you copy/assign
3442 * the guarded pointer.
3444 *\param auto_ptr Auto pointer returned by previous call to
3445 * g_auto_pointer_new_XXX()
3447 *\return gpointer An auto pointer
3449 GAuto
*g_auto_pointer_copy(GAuto
*auto_ptr
)
3452 AutoPointerRef
*ref
;
3455 if (auto_ptr
== NULL
)
3460 newp
= g_new0(AutoPointer
, 1);
3463 newp
->ptr
= ref
->pointer
;
3467 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref
->pointer
, ref
->cnt
);
3473 *\brief Free an auto pointer
3475 void g_auto_pointer_free(GAuto
*auto_ptr
)
3478 AutoPointerRef
*ref
;
3480 if (auto_ptr
== NULL
)
3486 if (--(ref
->cnt
) == 0) {
3488 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref
->pointer
, ref
->cnt
);
3490 ref
->free(ref
->pointer
);
3495 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref
->pointer
, ref
->cnt
);
3500 /* get_uri_part() - retrieves a URI starting from scanpos.
3501 Returns TRUE if successful */
3502 gboolean
get_uri_part(const gchar
*start
, const gchar
*scanpos
,
3503 const gchar
**bp
, const gchar
**ep
, gboolean hdr
)
3506 gint parenthese_cnt
= 0;
3508 cm_return_val_if_fail(start
!= NULL
, FALSE
);
3509 cm_return_val_if_fail(scanpos
!= NULL
, FALSE
);
3510 cm_return_val_if_fail(bp
!= NULL
, FALSE
);
3511 cm_return_val_if_fail(ep
!= NULL
, FALSE
);
3515 /* find end point of URI */
3516 for (ep_
= scanpos
; *ep_
!= '\0'; ep_
= g_utf8_next_char(ep_
)) {
3517 gunichar u
= g_utf8_get_char_validated(ep_
, -1);
3518 if (!g_unichar_isgraph(u
) ||
3519 u
== (gunichar
)-1 ||
3520 strchr("[]{}<>\"", *ep_
)) {
3522 } else if (strchr("(", *ep_
)) {
3524 } else if (strchr(")", *ep_
)) {
3525 if (parenthese_cnt
> 0)
3532 /* no punctuation at end of string */
3534 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
3535 * should pass some URI type to this function and decide on that whether
3536 * to perform punctuation stripping */
3538 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_~)", ch))
3540 for (; ep_
- 1 > scanpos
+ 1 &&
3541 IS_REAL_PUNCT(*(ep_
- 1));
3545 #undef IS_REAL_PUNCT
3552 gchar
*make_uri_string(const gchar
*bp
, const gchar
*ep
)
3554 while (bp
&& *bp
&& g_ascii_isspace(*bp
))
3556 return g_strndup(bp
, ep
- bp
);
3559 /* valid mail address characters */
3560 #define IS_RFC822_CHAR(ch) \
3564 !g_ascii_isspace(ch) && \
3565 !strchr("(),;<>\"", (ch)))
3567 /* alphabet and number within 7bit ASCII */
3568 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
3569 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
3571 static GHashTable
*create_domain_tab(void)
3574 GHashTable
*htab
= g_hash_table_new(g_stricase_hash
, g_stricase_equal
);
3576 cm_return_val_if_fail(htab
, NULL
);
3577 for (n
= 0; n
< sizeof toplvl_domains
/ sizeof toplvl_domains
[0]; n
++)
3578 g_hash_table_insert(htab
, (gpointer
) toplvl_domains
[n
], (gpointer
) toplvl_domains
[n
]);
3582 static gboolean
is_toplvl_domain(GHashTable
*tab
, const gchar
*first
, const gchar
*last
)
3584 gchar buf
[BUFFSIZE
+ 1];
3585 const gchar
*m
= buf
+ BUFFSIZE
+ 1;
3588 if (last
- first
> BUFFSIZE
|| first
> last
)
3591 for (p
= buf
; p
< m
&& first
< last
; *p
++ = *first
++)
3595 return g_hash_table_lookup(tab
, buf
) != NULL
;
3598 /* get_email_part() - retrieves an email address. Returns TRUE if successful */
3599 gboolean
get_email_part(const gchar
*start
, const gchar
*scanpos
,
3600 const gchar
**bp
, const gchar
**ep
, gboolean hdr
)
3602 /* more complex than the uri part because we need to scan back and forward starting from
3603 * the scan position. */
3604 gboolean result
= FALSE
;
3605 const gchar
*bp_
= NULL
;
3606 const gchar
*ep_
= NULL
;
3607 static GHashTable
*dom_tab
;
3608 const gchar
*last_dot
= NULL
;
3609 const gchar
*prelast_dot
= NULL
;
3610 const gchar
*last_tld_char
= NULL
;
3612 /* the informative part of the email address (describing the name
3613 * of the email address owner) may contain quoted parts. the
3614 * closure stack stores the last encountered quotes. */
3615 gchar closure_stack
[128];
3616 gchar
*ptr
= closure_stack
;
3618 cm_return_val_if_fail(start
!= NULL
, FALSE
);
3619 cm_return_val_if_fail(scanpos
!= NULL
, FALSE
);
3620 cm_return_val_if_fail(bp
!= NULL
, FALSE
);
3621 cm_return_val_if_fail(ep
!= NULL
, FALSE
);
3624 const gchar
*start_quote
= NULL
;
3625 const gchar
*end_quote
= NULL
;
3627 /* go to the real start */
3628 if (start
[0] == ',')
3630 if (start
[0] == ';')
3632 while (start
[0] == '\n' || start
[0] == '\r')
3634 while (start
[0] == ' ' || start
[0] == '\t')
3639 /* check if there are quotes (to skip , in them) */
3640 if (*start
== '"') {
3641 start_quote
= start
;
3643 end_quote
= strstr(start
, "\"");
3649 /* skip anything between quotes */
3650 if (start_quote
&& end_quote
) {
3655 /* find end (either , or ; or end of line) */
3656 if (strstr(start
, ",") && strstr(start
, ";"))
3657 *ep
= strstr(start
,",") < strstr(start
, ";")
3658 ? strstr(start
, ",") : strstr(start
, ";");
3659 else if (strstr(start
, ","))
3660 *ep
= strstr(start
, ",");
3661 else if (strstr(start
, ";"))
3662 *ep
= strstr(start
, ";");
3664 *ep
= start
+strlen(start
);
3666 /* go back to real start */
3667 if (start_quote
&& end_quote
) {
3668 start
= start_quote
;
3671 /* check there's still an @ in that, or search
3672 * further if possible */
3673 if (strstr(start
, "@") && strstr(start
, "@") < *ep
)
3675 else if (*ep
< start
+strlen(start
)) {
3678 } else if (start_quote
&& strstr(start
, "\"") && strstr(start
, "\"") < *ep
) {
3686 dom_tab
= create_domain_tab();
3687 cm_return_val_if_fail(dom_tab
, FALSE
);
3689 /* scan start of address */
3690 for (bp_
= scanpos
- 1;
3691 bp_
>= start
&& IS_RFC822_CHAR(*(const guchar
*)bp_
); bp_
--)
3694 /* TODO: should start with an alnum? */
3696 for (; bp_
< scanpos
&& !IS_ASCII_ALNUM(*(const guchar
*)bp_
); bp_
++)
3699 if (bp_
!= scanpos
) {
3700 /* scan end of address */
3701 for (ep_
= scanpos
+ 1;
3702 *ep_
&& IS_RFC822_CHAR(*(const guchar
*)ep_
); ep_
++)
3704 prelast_dot
= last_dot
;
3706 if (*(last_dot
+ 1) == '.') {
3707 if (prelast_dot
== NULL
)
3709 last_dot
= prelast_dot
;
3714 /* TODO: really should terminate with an alnum? */
3715 for (; ep_
> scanpos
&& !IS_ASCII_ALNUM(*(const guchar
*)ep_
);
3720 if (last_dot
== NULL
)
3722 if (last_dot
>= ep_
)
3723 last_dot
= prelast_dot
;
3724 if (last_dot
== NULL
|| (scanpos
+ 1 >= last_dot
))
3728 for (last_tld_char
= last_dot
; last_tld_char
< ep_
; last_tld_char
++)
3729 if (*last_tld_char
== '?')
3732 if (is_toplvl_domain(dom_tab
, last_dot
, last_tld_char
))
3739 if (!result
) return FALSE
;
3741 if (*ep_
&& bp_
!= start
&& *(bp_
- 1) == '"' && *(ep_
) == '"'
3742 && *(ep_
+ 1) == ' ' && *(ep_
+ 2) == '<'
3743 && IS_RFC822_CHAR(*(ep_
+ 3))) {
3744 /* this informative part with an @ in it is
3745 * followed by the email address */
3748 /* go to matching '>' (or next non-rfc822 char, like \n) */
3749 for (; *ep_
!= '>' && *ep_
!= '\0' && IS_RFC822_CHAR(*ep_
); ep_
++)
3752 /* include the bracket */
3753 if (*ep_
== '>') ep_
++;
3755 /* include the leading quote */
3763 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
3764 if (bp_
- 1 > start
&& IS_QUOTE(*(bp_
- 1)) && IS_QUOTE(*ep_
))
3767 /* see if this is <bracketed>; in this case we also scan for the informative part. */
3768 if (bp_
- 1 <= start
|| *(bp_
- 1) != '<' || *ep_
!= '>')
3771 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
3772 #define IN_STACK() (ptr > closure_stack)
3773 /* has underrun check */
3774 #define POP_STACK() if(IN_STACK()) --ptr
3775 /* has overrun check */
3776 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
3777 /* has underrun check */
3778 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
3782 /* scan for the informative part. */
3783 for (bp_
-= 2; bp_
>= start
; bp_
--) {
3784 /* if closure on the stack keep scanning */
3785 if (PEEK_STACK() == *bp_
) {
3789 if (!IN_STACK() && (*bp_
== '\'' || *bp_
== '"')) {
3794 /* if nothing in the closure stack, do the special conditions
3795 * the following if..else expression simply checks whether
3796 * a token is acceptable. if not acceptable, the clause
3797 * should terminate the loop with a 'break' */
3798 if (!PEEK_STACK()) {
3800 && (((bp_
- 1) >= start
) && isalnum(*(bp_
- 1)))
3801 && (((bp_
+ 1) < ep_
) && isalnum(*(bp_
+ 1)))) {
3802 /* hyphens are allowed, but only in
3804 } else if (strchr(" \"'", *bp_
)) {
3805 /* but anything not being a punctiation
3808 break; /* anything else is rejected */
3815 /* scan forward (should start with an alnum) */
3816 for (; *bp_
!= '<' && isspace(*bp_
) && *bp_
!= '"'; bp_
++)
3832 #undef IS_ASCII_ALNUM
3833 #undef IS_RFC822_CHAR
3835 gchar
*make_email_string(const gchar
*bp
, const gchar
*ep
)
3837 /* returns a mailto: URI; mailto: is also used to detect the
3838 * uri type later on in the button_pressed signal handler */
3843 tmp
= g_strndup(bp
, ep
- bp
);
3845 /* If there is a colon in the username part of the address,
3846 * we're dealing with an URI for some other protocol - do
3847 * not prefix with mailto: in such case. */
3848 colon
= strchr(tmp
, ':');
3849 at
= strchr(tmp
, '@');
3850 if (colon
!= NULL
&& at
!= NULL
&& colon
< at
) {
3853 result
= g_strconcat("mailto:", tmp
, NULL
);
3860 gchar
*make_http_string(const gchar
*bp
, const gchar
*ep
)
3862 /* returns an http: URI; */
3866 while (bp
&& *bp
&& g_ascii_isspace(*bp
))
3868 tmp
= g_strndup(bp
, ep
- bp
);
3869 result
= g_strconcat("http://", tmp
, NULL
);
3875 static gchar
*mailcap_get_command_in_file(const gchar
*path
, const gchar
*type
, const gchar
*file_to_open
)
3877 FILE *fp
= claws_fopen(path
, "rb");
3878 gchar buf
[BUFFSIZE
];
3879 gchar
*result
= NULL
;
3882 while (claws_fgets(buf
, sizeof (buf
), fp
) != NULL
) {
3883 gchar
**parts
= g_strsplit(buf
, ";", 3);
3884 gchar
*trimmed
= parts
[0];
3885 while (trimmed
[0] == ' ' || trimmed
[0] == '\t')
3887 while (trimmed
[strlen(trimmed
)-1] == ' ' || trimmed
[strlen(trimmed
)-1] == '\t')
3888 trimmed
[strlen(trimmed
)-1] = '\0';
3890 if (!strcmp(trimmed
, type
)) {
3891 gboolean needsterminal
= FALSE
;
3892 if (parts
[2] && strstr(parts
[2], "needsterminal")) {
3893 needsterminal
= TRUE
;
3895 if (parts
[2] && strstr(parts
[2], "test=")) {
3896 gchar
*orig_testcmd
= g_strdup(strstr(parts
[2], "test=")+5);
3897 gchar
*testcmd
= orig_testcmd
;
3898 if (strstr(testcmd
,";"))
3899 *(strstr(testcmd
,";")) = '\0';
3900 while (testcmd
[0] == ' ' || testcmd
[0] == '\t')
3902 while (testcmd
[strlen(testcmd
)-1] == '\n')
3903 testcmd
[strlen(testcmd
)-1] = '\0';
3904 while (testcmd
[strlen(testcmd
)-1] == '\r')
3905 testcmd
[strlen(testcmd
)-1] = '\0';
3906 while (testcmd
[strlen(testcmd
)-1] == ' ' || testcmd
[strlen(testcmd
)-1] == '\t')
3907 testcmd
[strlen(testcmd
)-1] = '\0';
3909 if (strstr(testcmd
, "%s")) {
3910 gchar
*tmp
= g_strdup_printf(testcmd
, file_to_open
);
3911 gint res
= system(tmp
);
3913 g_free(orig_testcmd
);
3920 gint res
= system(testcmd
);
3921 g_free(orig_testcmd
);
3931 while (trimmed
[0] == ' ' || trimmed
[0] == '\t')
3933 while (trimmed
[strlen(trimmed
)-1] == '\n')
3934 trimmed
[strlen(trimmed
)-1] = '\0';
3935 while (trimmed
[strlen(trimmed
)-1] == '\r')
3936 trimmed
[strlen(trimmed
)-1] = '\0';
3937 while (trimmed
[strlen(trimmed
)-1] == ' ' || trimmed
[strlen(trimmed
)-1] == '\t')
3938 trimmed
[strlen(trimmed
)-1] = '\0';
3939 result
= g_strdup(trimmed
);
3942 if (needsterminal
) {
3943 gchar
*tmp
= g_strdup_printf("xterm -e %s", result
);
3954 gchar
*mailcap_get_command_for_type(const gchar
*type
, const gchar
*file_to_open
)
3956 gchar
*result
= NULL
;
3960 path
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
, ".mailcap", NULL
);
3961 result
= mailcap_get_command_in_file(path
, type
, file_to_open
);
3965 result
= mailcap_get_command_in_file("/etc/mailcap", type
, file_to_open
);
3969 void mailcap_update_default(const gchar
*type
, const gchar
*command
)
3971 gchar
*path
= NULL
, *outpath
= NULL
;
3972 path
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
, ".mailcap", NULL
);
3973 outpath
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
, ".mailcap.new", NULL
);
3974 FILE *fp
= claws_fopen(path
, "rb");
3976 gchar buf
[BUFFSIZE
];
3977 gboolean err
= FALSE
;
3980 fp
= claws_fopen(path
, "a");
3982 g_warning("failed to create file %s", path
);
3987 fp
= g_freopen(path
, "rb", fp
);
3989 g_warning("failed to reopen file %s", path
);
3996 outfp
= claws_fopen(outpath
, "wb");
3998 g_warning("failed to create file %s", outpath
);
4004 while (fp
&& claws_fgets(buf
, sizeof (buf
), fp
) != NULL
) {
4005 gchar
**parts
= g_strsplit(buf
, ";", 3);
4006 gchar
*trimmed
= parts
[0];
4007 while (trimmed
[0] == ' ')
4009 while (trimmed
[strlen(trimmed
)-1] == ' ')
4010 trimmed
[strlen(trimmed
)-1] = '\0';
4012 if (!strcmp(trimmed
, type
)) {
4017 if(claws_fputs(buf
, outfp
) == EOF
) {
4025 if (fprintf(outfp
, "%s; %s\n", type
, command
) < 0)
4031 if (claws_safe_fclose(outfp
) == EOF
)
4035 g_rename(outpath
, path
);
4041 /* crude test to see if a file is an email. */
4042 gboolean
file_is_email (const gchar
*filename
)
4047 if (filename
== NULL
)
4049 if ((fp
= claws_fopen(filename
, "rb")) == NULL
)
4052 && claws_fgets(buffer
, sizeof (buffer
), fp
) != NULL
) {
4053 if (!strncmp(buffer
, "From:", strlen("From:")))
4055 else if (!strncmp(buffer
, "Date:", strlen("Date:")))
4057 else if (!strncmp(buffer
, "Message-ID:", strlen("Message-ID:")))
4059 else if (!strncmp(buffer
, "Subject:", strlen("Subject:")))
4061 else if (!strcmp(buffer
, "\r\n")) {
4062 debug_print("End of headers\n");
4067 return (score
>= 3);
4070 gboolean
sc_g_list_bigger(GList
*list
, gint max
)
4074 while (cur
&& i
<= max
+1) {
4081 gboolean
sc_g_slist_bigger(GSList
*list
, gint max
)
4085 while (cur
&& i
<= max
+1) {
4092 const gchar
*daynames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4093 const gchar
*monthnames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
4094 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4095 const gchar
*s_daynames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4096 const gchar
*s_monthnames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
4097 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4099 gint daynames_len
[] = {0,0,0,0,0,0,0};
4100 gint monthnames_len
[] = {0,0,0,0,0,0,
4102 gint s_daynames_len
[] = {0,0,0,0,0,0,0};
4103 gint s_monthnames_len
[] = {0,0,0,0,0,0,
4105 const gchar
*s_am_up
= NULL
;
4106 const gchar
*s_pm_up
= NULL
;
4107 const gchar
*s_am_low
= NULL
;
4108 const gchar
*s_pm_low
= NULL
;
4110 gint s_am_up_len
= 0;
4111 gint s_pm_up_len
= 0;
4112 gint s_am_low_len
= 0;
4113 gint s_pm_low_len
= 0;
4115 static gboolean time_names_init_done
= FALSE
;
4117 static void init_time_names(void)
4121 daynames
[0] = C_("Complete day name for use by strftime", "Sunday");
4122 daynames
[1] = C_("Complete day name for use by strftime", "Monday");
4123 daynames
[2] = C_("Complete day name for use by strftime", "Tuesday");
4124 daynames
[3] = C_("Complete day name for use by strftime", "Wednesday");
4125 daynames
[4] = C_("Complete day name for use by strftime", "Thursday");
4126 daynames
[5] = C_("Complete day name for use by strftime", "Friday");
4127 daynames
[6] = C_("Complete day name for use by strftime", "Saturday");
4129 monthnames
[0] = C_("Complete month name for use by strftime", "January");
4130 monthnames
[1] = C_("Complete month name for use by strftime", "February");
4131 monthnames
[2] = C_("Complete month name for use by strftime", "March");
4132 monthnames
[3] = C_("Complete month name for use by strftime", "April");
4133 monthnames
[4] = C_("Complete month name for use by strftime", "May");
4134 monthnames
[5] = C_("Complete month name for use by strftime", "June");
4135 monthnames
[6] = C_("Complete month name for use by strftime", "July");
4136 monthnames
[7] = C_("Complete month name for use by strftime", "August");
4137 monthnames
[8] = C_("Complete month name for use by strftime", "September");
4138 monthnames
[9] = C_("Complete month name for use by strftime", "October");
4139 monthnames
[10] = C_("Complete month name for use by strftime", "November");
4140 monthnames
[11] = C_("Complete month name for use by strftime", "December");
4142 s_daynames
[0] = C_("Abbr. day name for use by strftime", "Sun");
4143 s_daynames
[1] = C_("Abbr. day name for use by strftime", "Mon");
4144 s_daynames
[2] = C_("Abbr. day name for use by strftime", "Tue");
4145 s_daynames
[3] = C_("Abbr. day name for use by strftime", "Wed");
4146 s_daynames
[4] = C_("Abbr. day name for use by strftime", "Thu");
4147 s_daynames
[5] = C_("Abbr. day name for use by strftime", "Fri");
4148 s_daynames
[6] = C_("Abbr. day name for use by strftime", "Sat");
4150 s_monthnames
[0] = C_("Abbr. month name for use by strftime", "Jan");
4151 s_monthnames
[1] = C_("Abbr. month name for use by strftime", "Feb");
4152 s_monthnames
[2] = C_("Abbr. month name for use by strftime", "Mar");
4153 s_monthnames
[3] = C_("Abbr. month name for use by strftime", "Apr");
4154 s_monthnames
[4] = C_("Abbr. month name for use by strftime", "May");
4155 s_monthnames
[5] = C_("Abbr. month name for use by strftime", "Jun");
4156 s_monthnames
[6] = C_("Abbr. month name for use by strftime", "Jul");
4157 s_monthnames
[7] = C_("Abbr. month name for use by strftime", "Aug");
4158 s_monthnames
[8] = C_("Abbr. month name for use by strftime", "Sep");
4159 s_monthnames
[9] = C_("Abbr. month name for use by strftime", "Oct");
4160 s_monthnames
[10] = C_("Abbr. month name for use by strftime", "Nov");
4161 s_monthnames
[11] = C_("Abbr. month name for use by strftime", "Dec");
4163 for (i
= 0; i
< 7; i
++) {
4164 daynames_len
[i
] = strlen(daynames
[i
]);
4165 s_daynames_len
[i
] = strlen(s_daynames
[i
]);
4167 for (i
= 0; i
< 12; i
++) {
4168 monthnames_len
[i
] = strlen(monthnames
[i
]);
4169 s_monthnames_len
[i
] = strlen(s_monthnames
[i
]);
4172 s_am_up
= C_("For use by strftime (morning)", "AM");
4173 s_pm_up
= C_("For use by strftime (afternoon)", "PM");
4174 s_am_low
= C_("For use by strftime (morning, lowercase)", "am");
4175 s_pm_low
= C_("For use by strftime (afternoon, lowercase)", "pm");
4177 s_am_up_len
= strlen(s_am_up
);
4178 s_pm_up_len
= strlen(s_pm_up
);
4179 s_am_low_len
= strlen(s_am_low
);
4180 s_pm_low_len
= strlen(s_pm_low
);
4182 time_names_init_done
= TRUE
;
4185 #define CHECK_SIZE() { \
4186 total_done += len; \
4187 if (total_done >= buflen) { \
4188 buf[buflen-1] = '\0'; \
4193 size_t fast_strftime(gchar
*buf
, gint buflen
, const gchar
*format
, struct tm
*lt
)
4195 gchar
*curpos
= buf
;
4196 gint total_done
= 0;
4197 gchar subbuf
[64], subfmt
[64];
4198 static time_t last_tzset
= (time_t)0;
4200 if (!time_names_init_done
)
4203 if (format
== NULL
|| lt
== NULL
)
4206 if (last_tzset
!= time(NULL
)) {
4208 last_tzset
= time(NULL
);
4211 if (*format
== '%') {
4212 gint len
= 0, tmp
= 0;
4216 len
= 1; CHECK_SIZE();
4220 len
= s_daynames_len
[lt
->tm_wday
]; CHECK_SIZE();
4221 strncpy2(curpos
, s_daynames
[lt
->tm_wday
], buflen
- total_done
);
4224 len
= daynames_len
[lt
->tm_wday
]; CHECK_SIZE();
4225 strncpy2(curpos
, daynames
[lt
->tm_wday
], buflen
- total_done
);
4229 len
= s_monthnames_len
[lt
->tm_mon
]; CHECK_SIZE();
4230 strncpy2(curpos
, s_monthnames
[lt
->tm_mon
], buflen
- total_done
);
4233 len
= monthnames_len
[lt
->tm_mon
]; CHECK_SIZE();
4234 strncpy2(curpos
, monthnames
[lt
->tm_mon
], buflen
- total_done
);
4237 strftime(subbuf
, 64, "%c", lt
);
4238 len
= strlen(subbuf
); CHECK_SIZE();
4239 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4242 total_done
+= 2; CHECK_SIZE();
4243 tmp
= (lt
->tm_year
+ 1900)/100;
4244 *curpos
++ = '0'+(tmp
/ 10);
4245 *curpos
++ = '0'+(tmp
% 10);
4248 total_done
+= 2; CHECK_SIZE();
4249 *curpos
++ = '0'+(lt
->tm_mday
/ 10);
4250 *curpos
++ = '0'+(lt
->tm_mday
% 10);
4253 total_done
+= 8; CHECK_SIZE();
4254 *curpos
++ = '0'+((lt
->tm_mon
+1) / 10);
4255 *curpos
++ = '0'+((lt
->tm_mon
+1) % 10);
4257 *curpos
++ = '0'+(lt
->tm_mday
/ 10);
4258 *curpos
++ = '0'+(lt
->tm_mday
% 10);
4260 tmp
= lt
->tm_year
%100;
4261 *curpos
++ = '0'+(tmp
/ 10);
4262 *curpos
++ = '0'+(tmp
% 10);
4265 len
= 2; CHECK_SIZE();
4266 snprintf(curpos
, buflen
- total_done
, "%2d", lt
->tm_mday
);
4269 len
= 10; CHECK_SIZE();
4270 snprintf(curpos
, buflen
- total_done
, "%4d-%02d-%02d",
4271 lt
->tm_year
+ 1900, lt
->tm_mon
+1, lt
->tm_mday
);
4274 total_done
+= 2; CHECK_SIZE();
4275 *curpos
++ = '0'+(lt
->tm_hour
/ 10);
4276 *curpos
++ = '0'+(lt
->tm_hour
% 10);
4279 total_done
+= 2; CHECK_SIZE();
4285 *curpos
++ = '0'+(tmp
/ 10);
4286 *curpos
++ = '0'+(tmp
% 10);
4289 len
= 3; CHECK_SIZE();
4290 snprintf(curpos
, buflen
- total_done
, "%03d", lt
->tm_yday
+1);
4293 len
= 2; CHECK_SIZE();
4294 snprintf(curpos
, buflen
- total_done
, "%2d", lt
->tm_hour
);
4297 len
= 2; CHECK_SIZE();
4303 snprintf(curpos
, buflen
- total_done
, "%2d", tmp
);
4306 total_done
+= 2; CHECK_SIZE();
4307 tmp
= lt
->tm_mon
+ 1;
4308 *curpos
++ = '0'+(tmp
/ 10);
4309 *curpos
++ = '0'+(tmp
% 10);
4312 total_done
+= 2; CHECK_SIZE();
4313 *curpos
++ = '0'+(lt
->tm_min
/ 10);
4314 *curpos
++ = '0'+(lt
->tm_min
% 10);
4317 len
= 1; CHECK_SIZE();
4321 if (lt
->tm_hour
>= 12) {
4322 len
= s_pm_up_len
; CHECK_SIZE();
4323 snprintf(curpos
, buflen
-total_done
, "%s", s_pm_up
);
4325 len
= s_am_up_len
; CHECK_SIZE();
4326 snprintf(curpos
, buflen
-total_done
, "%s", s_am_up
);
4330 if (lt
->tm_hour
>= 12) {
4331 len
= s_pm_low_len
; CHECK_SIZE();
4332 snprintf(curpos
, buflen
-total_done
, "%s", s_pm_low
);
4334 len
= s_am_low_len
; CHECK_SIZE();
4335 snprintf(curpos
, buflen
-total_done
, "%s", s_am_low
);
4340 strftime(subbuf
, 64, "%I:%M:%S %p", lt
);
4342 strftime(subbuf
, 64, "%r", lt
);
4344 len
= strlen(subbuf
); CHECK_SIZE();
4345 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4348 total_done
+= 5; CHECK_SIZE();
4349 *curpos
++ = '0'+(lt
->tm_hour
/ 10);
4350 *curpos
++ = '0'+(lt
->tm_hour
% 10);
4352 *curpos
++ = '0'+(lt
->tm_min
/ 10);
4353 *curpos
++ = '0'+(lt
->tm_min
% 10);
4356 snprintf(subbuf
, 64, "%" CM_TIME_FORMAT
, mktime(lt
));
4357 len
= strlen(subbuf
); CHECK_SIZE();
4358 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4361 total_done
+= 2; CHECK_SIZE();
4362 *curpos
++ = '0'+(lt
->tm_sec
/ 10);
4363 *curpos
++ = '0'+(lt
->tm_sec
% 10);
4366 len
= 1; CHECK_SIZE();
4370 total_done
+= 8; CHECK_SIZE();
4371 *curpos
++ = '0'+(lt
->tm_hour
/ 10);
4372 *curpos
++ = '0'+(lt
->tm_hour
% 10);
4374 *curpos
++ = '0'+(lt
->tm_min
/ 10);
4375 *curpos
++ = '0'+(lt
->tm_min
% 10);
4377 *curpos
++ = '0'+(lt
->tm_sec
/ 10);
4378 *curpos
++ = '0'+(lt
->tm_sec
% 10);
4381 len
= 1; CHECK_SIZE();
4382 snprintf(curpos
, buflen
- total_done
, "%d", lt
->tm_wday
== 0 ? 7: lt
->tm_wday
);
4385 len
= 1; CHECK_SIZE();
4386 snprintf(curpos
, buflen
- total_done
, "%d", lt
->tm_wday
);
4389 strftime(subbuf
, 64, "%x", lt
);
4390 len
= strlen(subbuf
); CHECK_SIZE();
4391 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4394 strftime(subbuf
, 64, "%X", lt
);
4395 len
= strlen(subbuf
); CHECK_SIZE();
4396 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4399 total_done
+= 2; CHECK_SIZE();
4400 tmp
= lt
->tm_year
%100;
4401 *curpos
++ = '0'+(tmp
/ 10);
4402 *curpos
++ = '0'+(tmp
% 10);
4405 len
= 4; CHECK_SIZE();
4406 snprintf(curpos
, buflen
- total_done
, "%4d", lt
->tm_year
+ 1900);
4416 /* let these complicated ones be done with the libc */
4417 snprintf(subfmt
, 64, "%%%c", *format
);
4418 strftime(subbuf
, 64, subfmt
, lt
);
4419 len
= strlen(subbuf
); CHECK_SIZE();
4420 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4424 /* let these complicated modifiers be done with the libc */
4425 snprintf(subfmt
, 64, "%%%c%c", *format
, *(format
+1));
4426 strftime(subbuf
, 64, subfmt
, lt
);
4427 len
= strlen(subbuf
); CHECK_SIZE();
4428 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4432 g_warning("format error (%c)", *format
);
4439 int len
= 1; CHECK_SIZE();
4440 *curpos
++ = *format
++;
4448 #define WEXITSTATUS(x) (x)
4451 static gchar
*canonical_list_to_file(GSList
*list
)
4453 GString
*result
= g_string_new(NULL
);
4454 GSList
*pathlist
= g_slist_reverse(g_slist_copy(list
));
4459 result
= g_string_append(result
, G_DIR_SEPARATOR_S
);
4461 if (pathlist
->data
) {
4462 const gchar
*root
= (gchar
*)pathlist
->data
;
4463 if (root
[0] != '\0' && g_ascii_isalpha(root
[0]) &&
4465 /* drive - don't prepend dir separator */
4467 result
= g_string_append(result
, G_DIR_SEPARATOR_S
);
4472 for (cur
= pathlist
; cur
; cur
= cur
->next
) {
4473 result
= g_string_append(result
, (gchar
*)cur
->data
);
4475 result
= g_string_append(result
, G_DIR_SEPARATOR_S
);
4477 g_slist_free(pathlist
);
4480 g_string_free(result
, FALSE
);
4485 static GSList
*cm_split_path(const gchar
*filename
, int depth
)
4488 GSList
*canonical_parts
= NULL
;
4492 gboolean follow_symlinks
= TRUE
;
4499 errno
= EINVAL
; /* can't happen, no symlink handling */
4504 if (!g_path_is_absolute(filename
)) {
4509 path_parts
= g_strsplit(filename
, G_DIR_SEPARATOR_S
, -1);
4511 for (i
= 0; path_parts
[i
] != NULL
; i
++) {
4512 if (!strcmp(path_parts
[i
], ""))
4514 if (!strcmp(path_parts
[i
], "."))
4516 else if (!strcmp(path_parts
[i
], "..")) {
4519 g_strfreev(path_parts
);
4522 else /* Remove the last inserted element */
4524 g_slist_delete_link(canonical_parts
,
4529 canonical_parts
= g_slist_prepend(canonical_parts
,
4530 g_strdup(path_parts
[i
]));
4532 tmp_path
= canonical_list_to_file(canonical_parts
);
4534 if(g_stat(tmp_path
, &st
) < 0) {
4535 if (errno
== ENOENT
) {
4538 follow_symlinks
= FALSE
;
4543 slist_free_strings_full(canonical_parts
);
4544 g_strfreev(path_parts
);
4550 if (follow_symlinks
&& g_file_test(tmp_path
, G_FILE_TEST_IS_SYMLINK
)) {
4551 GError
*error
= NULL
;
4552 gchar
*target
= g_file_read_link(tmp_path
, &error
);
4554 if (!g_path_is_absolute(target
)) {
4555 /* remove the last inserted element */
4557 g_slist_delete_link(canonical_parts
,
4559 /* add the target */
4560 canonical_parts
= g_slist_prepend(canonical_parts
,
4564 /* and get the new target */
4565 target
= canonical_list_to_file(canonical_parts
);
4568 /* restart from absolute target */
4569 slist_free_strings_full(canonical_parts
);
4570 canonical_parts
= NULL
;
4572 canonical_parts
= cm_split_path(target
, depth
+ 1);
4574 g_error_free(error
);
4575 if (canonical_parts
== NULL
) {
4577 g_strfreev(path_parts
);
4586 g_strfreev(path_parts
);
4587 return canonical_parts
;
4591 * Canonicalize a filename, resolving symlinks along the way.
4592 * Returns a negative errno in case of error.
4594 int cm_canonicalize_filename(const gchar
*filename
, gchar
**canonical_name
) {
4595 GSList
*canonical_parts
;
4596 gboolean is_absolute
;
4598 if (filename
== NULL
)
4600 if (canonical_name
== NULL
)
4602 *canonical_name
= NULL
;
4604 is_absolute
= g_path_is_absolute(filename
);
4606 /* Always work on absolute filenames. */
4607 gchar
*cur
= g_get_current_dir();
4608 gchar
*absolute_filename
= g_strconcat(cur
, G_DIR_SEPARATOR_S
,
4611 canonical_parts
= cm_split_path(absolute_filename
, 0);
4612 g_free(absolute_filename
);
4615 canonical_parts
= cm_split_path(filename
, 0);
4617 if (canonical_parts
== NULL
)
4620 *canonical_name
= canonical_list_to_file(canonical_parts
);
4621 slist_free_strings_full(canonical_parts
);
4625 /* Returns a decoded base64 string, guaranteed to be null-terminated. */
4626 guchar
*g_base64_decode_zero(const gchar
*text
, gsize
*out_len
)
4628 gchar
*tmp
= g_base64_decode(text
, out_len
);
4629 gchar
*out
= g_strndup(tmp
, *out_len
);
4633 if (strlen(out
) != *out_len
) {
4634 g_warning("strlen(out) %"G_GSIZE_FORMAT
" != *out_len %"G_GSIZE_FORMAT
, strlen(out
), *out_len
);
4640 /* Attempts to read count bytes from a PRNG into memory area starting at buf.
4641 * It is up to the caller to make sure there is at least count bytes
4642 * available at buf. */
4644 get_random_bytes(void *buf
, size_t count
)
4646 /* Open our prng source. */
4647 #if defined G_OS_WIN32
4650 if (!CryptAcquireContext(&rnd
, NULL
, NULL
, PROV_RSA_FULL
, 0) &&
4651 !CryptAcquireContext(&rnd
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
)) {
4652 debug_print("Could not acquire a CSP handle.\n");
4659 rnd
= open("/dev/urandom", O_RDONLY
);
4661 FILE_OP_ERROR("/dev/urandom", "open");
4662 debug_print("Could not open /dev/urandom.\n");
4667 /* Read data from the source into buf. */
4668 #if defined G_OS_WIN32
4669 if (!CryptGenRandom(rnd
, count
, buf
)) {
4670 debug_print("Could not read %"G_GSIZE_FORMAT
" random bytes.\n", count
);
4671 CryptReleaseContext(rnd
, 0);
4675 ret
= read(rnd
, buf
, count
);
4677 FILE_OP_ERROR("/dev/urandom", "read");
4678 debug_print("Could not read enough data from /dev/urandom, read only %ld of %lu bytes.\n", ret
, count
);
4684 /* Close the prng source. */
4685 #if defined G_OS_WIN32
4686 CryptReleaseContext(rnd
, 0);
4694 /* returns FALSE if parsing failed, otherwise returns TRUE and sets *server, *port
4695 and eventually *fp from filename (if not NULL, they must be free'd by caller after
4697 filenames we expect: 'host.name.port.cert' or 'host.name.port.f:i:n:g:e:r:p:r:i:n:t.cert' */
4698 gboolean
get_serverportfp_from_filename(const gchar
*str
, gchar
**server
, gchar
**port
, gchar
**fp
)
4700 const gchar
*pos
, *dotport_pos
= NULL
, *dotcert_pos
= NULL
, *dotfp_pos
= NULL
;
4702 g_return_val_if_fail(str
!= NULL
, FALSE
);
4704 pos
= str
+ strlen(str
) - 1;
4705 while ((pos
> str
) && !dotport_pos
) {
4708 /* match the .cert suffix */
4709 if (strcmp(pos
, ".cert") == 0) {
4714 /* match an eventual fingerprint */
4715 /* or the port number */
4716 if (strncmp(pos
+ 3, ":", 1) == 0) {
4722 /* match the port number */
4729 if (!dotport_pos
|| !dotcert_pos
) {
4730 g_warning("could not parse filename %s", str
);
4735 *server
= g_strndup(str
, dotport_pos
- str
);
4738 *port
= g_strndup(dotport_pos
+ 1, dotfp_pos
- dotport_pos
- 1);
4740 *fp
= g_strndup(dotfp_pos
+ 1, dotcert_pos
- dotfp_pos
- 1);
4743 *port
= g_strndup(dotport_pos
+ 1, dotcert_pos
- dotport_pos
- 1);
4748 debug_print("filename='%s' => server='%s' port='%s' fp='%s'\n",
4750 (server
? *server
: "(n/a)"),
4751 (port
? *port
: "(n/a)"),
4752 (fp
? *fp
: "(n/a)"));
4754 if (!(server
&& *server
) || !(port
&& *port
))
4761 gchar
*win32_debug_log_path(void)
4763 return g_strconcat(g_get_tmp_dir(), G_DIR_SEPARATOR_S
,
4764 "claws-win32.log", NULL
);