2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2020 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>
91 #include "file-utils.h"
95 static gboolean debug_mode
= FALSE
;
97 GSList
*slist_copy_deep(GSList
*list
, GCopyFunc func
)
99 #if GLIB_CHECK_VERSION(2, 34, 0)
100 return g_slist_copy_deep(list
, func
, NULL
);
102 GSList
*res
= g_slist_copy(list
);
105 walk
->data
= func(walk
->data
, NULL
);
112 void list_free_strings_full(GList
*list
)
114 g_list_free_full(list
, (GDestroyNotify
)g_free
);
117 void slist_free_strings_full(GSList
*list
)
119 g_slist_free_full(list
, (GDestroyNotify
)g_free
);
122 static void hash_free_strings_func(gpointer key
, gpointer value
, gpointer data
)
127 void hash_free_strings(GHashTable
*table
)
129 g_hash_table_foreach(table
, hash_free_strings_func
, NULL
);
132 gint
str_case_equal(gconstpointer v
, gconstpointer v2
)
134 return g_ascii_strcasecmp((const gchar
*)v
, (const gchar
*)v2
) == 0;
137 guint
str_case_hash(gconstpointer key
)
139 const gchar
*p
= key
;
143 h
= g_ascii_tolower(h
);
144 for (p
+= 1; *p
!= '\0'; p
++)
145 h
= (h
<< 5) - h
+ g_ascii_tolower(*p
);
151 gint
to_number(const gchar
*nstr
)
153 register const gchar
*p
;
155 if (*nstr
== '\0') return -1;
157 for (p
= nstr
; *p
!= '\0'; p
++)
158 if (!g_ascii_isdigit(*p
)) return -1;
163 /* convert integer into string,
164 nstr must be not lower than 11 characters length */
165 gchar
*itos_buf(gchar
*nstr
, gint n
)
167 g_snprintf(nstr
, 11, "%d", n
);
171 /* convert integer into string */
174 static gchar nstr
[11];
176 return itos_buf(nstr
, n
);
179 #define divide(num,divisor,i,d) \
181 i = num >> divisor; \
182 d = num & ((1<<divisor)-1); \
183 d = (d*100) >> divisor; \
188 * \brief Convert a given size in bytes in a human-readable string
190 * \param size The size expressed in bytes to convert in string
191 * \return The string that respresents the size in an human-readable way
193 gchar
*to_human_readable(goffset size
)
195 static gchar str
[14];
196 static gchar
*b_format
= NULL
, *kb_format
= NULL
,
197 *mb_format
= NULL
, *gb_format
= NULL
;
198 register int t
= 0, r
= 0;
199 if (b_format
== NULL
) {
201 kb_format
= _("%d.%02dKB");
202 mb_format
= _("%d.%02dMB");
203 gb_format
= _("%.2fGB");
206 if (size
< (goffset
)1024) {
207 g_snprintf(str
, sizeof(str
), b_format
, (gint
)size
);
209 } else if (size
>> 10 < (goffset
)1024) {
210 divide(size
, 10, t
, r
);
211 g_snprintf(str
, sizeof(str
), kb_format
, t
, r
);
213 } else if (size
>> 20 < (goffset
)1024) {
214 divide(size
, 20, t
, r
);
215 g_snprintf(str
, sizeof(str
), mb_format
, t
, r
);
218 g_snprintf(str
, sizeof(str
), gb_format
, (gfloat
)(size
>> 30));
224 gint
path_cmp(const gchar
*s1
, const gchar
*s2
)
229 gchar
*s1buf
, *s2buf
;
232 if (s1
== NULL
|| s2
== NULL
) return -1;
233 if (*s1
== '\0' || *s2
== '\0') return -1;
236 s1buf
= g_strdup (s1
);
237 s2buf
= g_strdup (s2
);
238 subst_char (s1buf
, '/', G_DIR_SEPARATOR
);
239 subst_char (s2buf
, '/', G_DIR_SEPARATOR
);
242 #endif /* !G_OS_WIN32 */
247 if (s1
[len1
- 1] == G_DIR_SEPARATOR
) len1
--;
248 if (s2
[len2
- 1] == G_DIR_SEPARATOR
) len2
--;
250 rc
= strncmp(s1
, s2
, MAX(len1
, len2
));
254 #endif /* !G_OS_WIN32 */
258 /* remove trailing return code */
259 gchar
*strretchomp(gchar
*str
)
263 if (!*str
) return str
;
265 for (s
= str
+ strlen(str
) - 1;
266 s
>= str
&& (*s
== '\n' || *s
== '\r');
273 /* remove trailing character */
274 gchar
*strtailchomp(gchar
*str
, gchar tail_char
)
278 if (!*str
) return str
;
279 if (tail_char
== '\0') return str
;
281 for (s
= str
+ strlen(str
) - 1; s
>= str
&& *s
== tail_char
; s
--)
287 /* remove CR (carriage return) */
288 gchar
*strcrchomp(gchar
*str
)
292 if (!*str
) return str
;
294 s
= str
+ strlen(str
) - 1;
295 if (*s
== '\n' && s
> str
&& *(s
- 1) == '\r') {
303 #ifndef HAVE_STRCASESTR
304 /* Similar to `strstr' but this function ignores the case of both strings. */
305 gchar
*strcasestr(const gchar
*haystack
, const gchar
*needle
)
307 size_t haystack_len
= strlen(haystack
);
309 return strncasestr(haystack
, haystack_len
, needle
);
311 #endif /* HAVE_STRCASESTR */
313 gchar
*strncasestr(const gchar
*haystack
, gint haystack_len
, const gchar
*needle
)
315 register size_t needle_len
;
317 needle_len
= strlen(needle
);
319 if (haystack_len
< needle_len
|| needle_len
== 0)
322 while (haystack_len
>= needle_len
) {
323 if (!g_ascii_strncasecmp(haystack
, needle
, needle_len
))
324 return (gchar
*)haystack
;
334 gpointer
my_memmem(gconstpointer haystack
, size_t haystacklen
,
335 gconstpointer needle
, size_t needlelen
)
337 const gchar
*haystack_
= (const gchar
*)haystack
;
338 const gchar
*needle_
= (const gchar
*)needle
;
339 const gchar
*haystack_cur
= (const gchar
*)haystack
;
340 size_t haystack_left
= haystacklen
;
343 return memchr(haystack_
, *needle_
, haystacklen
);
345 while ((haystack_cur
= memchr(haystack_cur
, *needle_
, haystack_left
))
347 if (haystacklen
- (haystack_cur
- haystack_
) < needlelen
)
349 if (memcmp(haystack_cur
+ 1, needle_
+ 1, needlelen
- 1) == 0)
350 return (gpointer
)haystack_cur
;
353 haystack_left
= haystacklen
- (haystack_cur
- haystack_
);
360 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
361 gchar
*strncpy2(gchar
*dest
, const gchar
*src
, size_t n
)
363 register const gchar
*s
= src
;
364 register gchar
*d
= dest
;
374 /* Examine if next block is non-ASCII string */
375 gboolean
is_next_nonascii(const gchar
*s
)
379 /* skip head space */
380 for (p
= s
; *p
!= '\0' && g_ascii_isspace(*p
); p
++)
382 for (; *p
!= '\0' && !g_ascii_isspace(*p
); p
++) {
383 if (*(guchar
*)p
> 127 || *(guchar
*)p
< 32)
390 gint
get_next_word_len(const gchar
*s
)
394 for (; *s
!= '\0' && !g_ascii_isspace(*s
); s
++, len
++)
400 static void trim_subject_for_compare(gchar
*str
)
404 eliminate_parenthesis(str
, '[', ']');
405 eliminate_parenthesis(str
, '(', ')');
408 srcp
= str
+ subject_get_prefix_length(str
);
410 memmove(str
, srcp
, strlen(srcp
) + 1);
413 static void trim_subject_for_sort(gchar
*str
)
419 srcp
= str
+ subject_get_prefix_length(str
);
421 memmove(str
, srcp
, strlen(srcp
) + 1);
424 /* compare subjects */
425 gint
subject_compare(const gchar
*s1
, const gchar
*s2
)
429 if (!s1
|| !s2
) return -1;
430 if (!*s1
|| !*s2
) return -1;
432 Xstrdup_a(str1
, s1
, return -1);
433 Xstrdup_a(str2
, s2
, return -1);
435 trim_subject_for_compare(str1
);
436 trim_subject_for_compare(str2
);
438 if (!*str1
|| !*str2
) return -1;
440 return strcmp(str1
, str2
);
443 gint
subject_compare_for_sort(const gchar
*s1
, const gchar
*s2
)
447 if (!s1
|| !s2
) return -1;
449 Xstrdup_a(str1
, s1
, return -1);
450 Xstrdup_a(str2
, s2
, return -1);
452 trim_subject_for_sort(str1
);
453 trim_subject_for_sort(str2
);
455 return g_utf8_collate(str1
, str2
);
458 void trim_subject(gchar
*str
)
460 register gchar
*srcp
;
466 srcp
= str
+ subject_get_prefix_length(str
);
471 } else if (*srcp
== '(') {
483 else if (*srcp
== cl
)
490 while (g_ascii_isspace(*srcp
)) srcp
++;
491 memmove(str
, srcp
, strlen(srcp
) + 1);
494 void eliminate_parenthesis(gchar
*str
, gchar op
, gchar cl
)
496 register gchar
*srcp
, *destp
;
501 while ((destp
= strchr(destp
, op
))) {
507 else if (*srcp
== cl
)
513 while (g_ascii_isspace(*srcp
)) srcp
++;
514 memmove(destp
, srcp
, strlen(srcp
) + 1);
518 void extract_parenthesis(gchar
*str
, gchar op
, gchar cl
)
520 register gchar
*srcp
, *destp
;
525 while ((srcp
= strchr(destp
, op
))) {
528 memmove(destp
, srcp
+ 1, strlen(srcp
));
533 else if (*destp
== cl
)
545 static void extract_parenthesis_with_skip_quote(gchar
*str
, gchar quote_chr
,
548 register gchar
*srcp
, *destp
;
550 gboolean in_quote
= FALSE
;
554 while ((srcp
= strchr_with_skip_quote(destp
, quote_chr
, op
))) {
557 memmove(destp
, srcp
+ 1, strlen(srcp
));
560 if (*destp
== op
&& !in_quote
)
562 else if (*destp
== cl
&& !in_quote
)
564 else if (*destp
== quote_chr
)
576 void extract_quote(gchar
*str
, gchar quote_chr
)
580 if ((str
= strchr(str
, quote_chr
))) {
582 while ((p
= strchr(p
+ 1, quote_chr
)) && (p
[-1] == '\\')) {
583 memmove(p
- 1, p
, strlen(p
) + 1);
588 memmove(str
, str
+ 1, p
- str
);
593 /* Returns a newly allocated string with all quote_chr not at the beginning
594 or the end of str escaped with '\' or the given str if not required. */
595 gchar
*escape_internal_quotes(gchar
*str
, gchar quote_chr
)
597 register gchar
*p
, *q
;
601 if (str
== NULL
|| *str
== '\0')
604 /* search for unescaped quote_chr */
609 if (*p
== quote_chr
&& *(p
- 1) != '\\' && *(p
+ 1) != '\0')
613 if (!k
) /* nothing to escape */
616 /* unescaped quote_chr found */
617 qstr
= g_malloc(l
+ k
+ 1);
620 if (*p
== quote_chr
) {
625 if (*p
== quote_chr
&& *(p
- 1) != '\\' && *(p
+ 1) != '\0')
634 void eliminate_address_comment(gchar
*str
)
636 register gchar
*srcp
, *destp
;
641 while ((destp
= strchr(destp
, '"'))) {
642 if ((srcp
= strchr(destp
+ 1, '"'))) {
647 while (g_ascii_isspace(*srcp
)) srcp
++;
648 memmove(destp
, srcp
, strlen(srcp
) + 1);
658 while ((destp
= strchr_with_skip_quote(destp
, '"', '('))) {
664 else if (*srcp
== ')')
670 while (g_ascii_isspace(*srcp
)) srcp
++;
671 memmove(destp
, srcp
, strlen(srcp
) + 1);
675 gchar
*strchr_with_skip_quote(const gchar
*str
, gint quote_chr
, gint c
)
677 gboolean in_quote
= FALSE
;
680 if (*str
== c
&& !in_quote
)
682 if (*str
== quote_chr
)
690 void extract_address(gchar
*str
)
692 cm_return_if_fail(str
!= NULL
);
693 eliminate_address_comment(str
);
694 if (strchr_with_skip_quote(str
, '"', '<'))
695 extract_parenthesis_with_skip_quote(str
, '"', '<', '>');
699 void extract_list_id_str(gchar
*str
)
701 if (strchr_with_skip_quote(str
, '"', '<'))
702 extract_parenthesis_with_skip_quote(str
, '"', '<', '>');
706 static GSList
*address_list_append_real(GSList
*addr_list
, const gchar
*str
, gboolean removecomments
)
711 if (!str
) return addr_list
;
713 Xstrdup_a(work
, str
, return addr_list
);
716 eliminate_address_comment(work
);
719 while (workp
&& *workp
) {
722 if ((p
= strchr_with_skip_quote(workp
, '"', ','))) {
728 if (removecomments
&& strchr_with_skip_quote(workp
, '"', '<'))
729 extract_parenthesis_with_skip_quote
730 (workp
, '"', '<', '>');
734 addr_list
= g_slist_append(addr_list
, g_strdup(workp
));
742 GSList
*address_list_append(GSList
*addr_list
, const gchar
*str
)
744 return address_list_append_real(addr_list
, str
, TRUE
);
747 GSList
*address_list_append_with_comments(GSList
*addr_list
, const gchar
*str
)
749 return address_list_append_real(addr_list
, str
, FALSE
);
752 GSList
*references_list_prepend(GSList
*msgid_list
, const gchar
*str
)
756 if (!str
) return msgid_list
;
759 while (strp
&& *strp
) {
760 const gchar
*start
, *end
;
763 if ((start
= strchr(strp
, '<')) != NULL
) {
764 end
= strchr(start
+ 1, '>');
769 msgid
= g_strndup(start
+ 1, end
- start
- 1);
772 msgid_list
= g_slist_prepend(msgid_list
, msgid
);
782 GSList
*references_list_append(GSList
*msgid_list
, const gchar
*str
)
786 list
= references_list_prepend(NULL
, str
);
787 list
= g_slist_reverse(list
);
788 msgid_list
= g_slist_concat(msgid_list
, list
);
793 GSList
*newsgroup_list_append(GSList
*group_list
, const gchar
*str
)
798 if (!str
) return group_list
;
800 Xstrdup_a(work
, str
, return group_list
);
804 while (workp
&& *workp
) {
807 if ((p
= strchr_with_skip_quote(workp
, '"', ','))) {
815 group_list
= g_slist_append(group_list
,
824 GList
*add_history(GList
*list
, const gchar
*str
)
829 cm_return_val_if_fail(str
!= NULL
, list
);
831 old
= g_list_find_custom(list
, (gpointer
)str
, (GCompareFunc
)g_strcmp0
);
834 list
= g_list_remove(list
, old
->data
);
836 } else if (g_list_length(list
) >= MAX_HISTORY_SIZE
) {
839 last
= g_list_last(list
);
842 list
= g_list_remove(list
, last
->data
);
847 list
= g_list_prepend(list
, g_strdup(str
));
852 void remove_return(gchar
*str
)
854 register gchar
*p
= str
;
857 if (*p
== '\n' || *p
== '\r')
858 memmove(p
, p
+ 1, strlen(p
));
864 void remove_space(gchar
*str
)
866 register gchar
*p
= str
;
871 while (g_ascii_isspace(*(p
+ spc
)))
874 memmove(p
, p
+ spc
, strlen(p
+ spc
) + 1);
880 void unfold_line(gchar
*str
)
886 ch
= str
; /* iterator for source string */
889 c
= g_utf8_get_char_validated(ch
, -1);
891 if (c
== (gunichar
)-1 || c
== (gunichar
)-2) {
892 /* non-unicode byte, move past it */
897 len
= g_unichar_to_utf8(c
, NULL
);
899 if (!g_unichar_isdefined(c
) || !g_unichar_isprint(c
) ||
900 g_unichar_isspace(c
)) {
901 /* replace anything bad or whitespacey with a single space */
905 /* move rest of the string forwards, since we just replaced
906 * a multi-byte sequence with one byte */
907 memmove(ch
, ch
+ len
-1, strlen(ch
+ len
-1) + 1);
910 /* A valid unicode character, copy it. */
916 void subst_char(gchar
*str
, gchar orig
, gchar subst
)
918 register gchar
*p
= str
;
927 void subst_chars(gchar
*str
, gchar
*orig
, gchar subst
)
929 register gchar
*p
= str
;
932 if (strchr(orig
, *p
) != NULL
)
938 void subst_for_filename(gchar
*str
)
943 subst_chars(str
, "\t\r\n\\/*?:", '_');
945 subst_chars(str
, "\t\r\n\\/*", '_');
949 void subst_for_shellsafe_filename(gchar
*str
)
953 subst_for_filename(str
);
954 subst_chars(str
, " \"'|&;()<>'!{}[]",'_');
957 gboolean
is_ascii_str(const gchar
*str
)
959 const guchar
*p
= (const guchar
*)str
;
962 if (*p
!= '\t' && *p
!= ' ' &&
963 *p
!= '\r' && *p
!= '\n' &&
964 (*p
< 32 || *p
>= 127))
972 static const gchar
* line_has_quote_char_last(const gchar
* str
, const gchar
*quote_chars
)
974 gchar
* position
= NULL
;
975 gchar
* tmp_pos
= NULL
;
978 if (str
== NULL
|| quote_chars
== NULL
)
981 for (i
= 0; i
< strlen(quote_chars
); i
++) {
982 tmp_pos
= strrchr (str
, quote_chars
[i
]);
984 || (tmp_pos
!= NULL
&& position
<= tmp_pos
) )
990 gint
get_quote_level(const gchar
*str
, const gchar
*quote_chars
)
992 const gchar
*first_pos
;
993 const gchar
*last_pos
;
994 const gchar
*p
= str
;
995 gint quote_level
= -1;
997 /* speed up line processing by only searching to the last '>' */
998 if ((first_pos
= line_has_quote_char(str
, quote_chars
)) != NULL
) {
999 /* skip a line if it contains a '<' before the initial '>' */
1000 if (memchr(str
, '<', first_pos
- str
) != NULL
)
1002 last_pos
= line_has_quote_char_last(first_pos
, quote_chars
);
1006 while (p
<= last_pos
) {
1007 while (p
< last_pos
) {
1008 if (g_ascii_isspace(*p
))
1014 if (strchr(quote_chars
, *p
))
1016 else if (*p
!= '-' && !g_ascii_isspace(*p
) && p
<= last_pos
) {
1017 /* any characters are allowed except '-','<' and space */
1018 while (*p
!= '-' && *p
!= '<'
1019 && !strchr(quote_chars
, *p
)
1020 && !g_ascii_isspace(*p
)
1023 if (strchr(quote_chars
, *p
))
1035 gint
check_line_length(const gchar
*str
, gint max_chars
, gint
*line
)
1037 const gchar
*p
= str
, *q
;
1038 gint cur_line
= 0, len
;
1040 while ((q
= strchr(p
, '\n')) != NULL
) {
1042 if (len
> max_chars
) {
1052 if (len
> max_chars
) {
1061 const gchar
* line_has_quote_char(const gchar
* str
, const gchar
*quote_chars
)
1063 gchar
* position
= NULL
;
1064 gchar
* tmp_pos
= NULL
;
1067 if (str
== NULL
|| quote_chars
== NULL
)
1070 for (i
= 0; i
< strlen(quote_chars
); i
++) {
1071 tmp_pos
= strchr (str
, quote_chars
[i
]);
1073 || (tmp_pos
!= NULL
&& position
>= tmp_pos
) )
1079 static gchar
*strstr_with_skip_quote(const gchar
*haystack
, const gchar
*needle
)
1081 register guint haystack_len
, needle_len
;
1082 gboolean in_squote
= FALSE
, in_dquote
= FALSE
;
1084 haystack_len
= strlen(haystack
);
1085 needle_len
= strlen(needle
);
1087 if (haystack_len
< needle_len
|| needle_len
== 0)
1090 while (haystack_len
>= needle_len
) {
1091 if (!in_squote
&& !in_dquote
&&
1092 !strncmp(haystack
, needle
, needle_len
))
1093 return (gchar
*)haystack
;
1095 /* 'foo"bar"' -> foo"bar"
1096 "foo'bar'" -> foo'bar' */
1097 if (*haystack
== '\'') {
1100 else if (!in_dquote
)
1102 } else if (*haystack
== '\"') {
1105 else if (!in_squote
)
1107 } else if (*haystack
== '\\') {
1119 gchar
**strsplit_with_quote(const gchar
*str
, const gchar
*delim
,
1122 GSList
*string_list
= NULL
, *slist
;
1123 gchar
**str_array
, *s
, *new_str
;
1124 guint i
, n
= 1, len
;
1126 cm_return_val_if_fail(str
!= NULL
, NULL
);
1127 cm_return_val_if_fail(delim
!= NULL
, NULL
);
1130 max_tokens
= G_MAXINT
;
1132 s
= strstr_with_skip_quote(str
, delim
);
1134 guint delimiter_len
= strlen(delim
);
1138 new_str
= g_strndup(str
, len
);
1140 if (new_str
[0] == '\'' || new_str
[0] == '\"') {
1141 if (new_str
[len
- 1] == new_str
[0]) {
1142 new_str
[len
- 1] = '\0';
1143 memmove(new_str
, new_str
+ 1, len
- 1);
1146 string_list
= g_slist_prepend(string_list
, new_str
);
1148 str
= s
+ delimiter_len
;
1149 s
= strstr_with_skip_quote(str
, delim
);
1150 } while (--max_tokens
&& s
);
1154 new_str
= g_strdup(str
);
1155 if (new_str
[0] == '\'' || new_str
[0] == '\"') {
1157 if (new_str
[len
- 1] == new_str
[0]) {
1158 new_str
[len
- 1] = '\0';
1159 memmove(new_str
, new_str
+ 1, len
- 1);
1162 string_list
= g_slist_prepend(string_list
, new_str
);
1166 str_array
= g_new(gchar
*, n
);
1170 str_array
[i
--] = NULL
;
1171 for (slist
= string_list
; slist
; slist
= slist
->next
)
1172 str_array
[i
--] = slist
->data
;
1174 g_slist_free(string_list
);
1179 gchar
*get_abbrev_newsgroup_name(const gchar
*group
, gint len
)
1181 gchar
*abbrev_group
;
1183 const gchar
*p
= group
;
1186 cm_return_val_if_fail(group
!= NULL
, NULL
);
1188 last
= group
+ strlen(group
);
1189 abbrev_group
= ap
= g_malloc(strlen(group
) + 1);
1194 if ((ap
- abbrev_group
) + (last
- p
) > len
&& strchr(p
, '.')) {
1196 while (*p
!= '.') p
++;
1199 return abbrev_group
;
1204 return abbrev_group
;
1207 gchar
*trim_string(const gchar
*str
, gint len
)
1209 const gchar
*p
= str
;
1214 if (!str
) return NULL
;
1215 if (strlen(str
) <= len
)
1216 return g_strdup(str
);
1217 if (g_utf8_validate(str
, -1, NULL
) == FALSE
)
1218 return g_strdup(str
);
1220 while (*p
!= '\0') {
1221 mb_len
= g_utf8_skip
[*(guchar
*)p
];
1224 else if (new_len
+ mb_len
> len
)
1231 Xstrndup_a(new_str
, str
, new_len
, return g_strdup(str
));
1232 return g_strconcat(new_str
, "...", NULL
);
1235 GList
*uri_list_extract_filenames(const gchar
*uri_list
)
1237 GList
*result
= NULL
;
1239 gchar
*escaped_utf8uri
;
1245 while (g_ascii_isspace(*p
)) p
++;
1246 if (!strncmp(p
, "file:", 5)) {
1249 while (*q
&& *q
!= '\n' && *q
!= '\r') q
++;
1252 gchar
*file
, *locale_file
= NULL
;
1254 while (q
> p
&& g_ascii_isspace(*q
))
1256 Xalloca(escaped_utf8uri
, q
- p
+ 2,
1258 Xalloca(file
, q
- p
+ 2,
1261 strncpy(escaped_utf8uri
, p
, q
- p
+ 1);
1262 escaped_utf8uri
[q
- p
+ 1] = '\0';
1263 decode_uri_with_plus(file
, escaped_utf8uri
, FALSE
);
1265 * g_filename_from_uri() rejects escaped/locale encoded uri
1266 * string which come from Nautilus.
1269 if (g_utf8_validate(file
, -1, NULL
))
1271 = conv_codeset_strdup(
1274 conv_get_locale_charset_str());
1276 locale_file
= g_strdup(file
+ 5);
1278 locale_file
= g_filename_from_uri(escaped_utf8uri
, NULL
, NULL
);
1280 result
= g_list_append(result
, locale_file
);
1284 p
= strchr(p
, '\n');
1291 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1294 static gint
axtoi(const gchar
*hexstr
)
1296 gint hi
, lo
, result
;
1299 if ('0' <= hi
&& hi
<= '9') {
1302 if ('a' <= hi
&& hi
<= 'f') {
1305 if ('A' <= hi
&& hi
<= 'F') {
1310 if ('0' <= lo
&& lo
<= '9') {
1313 if ('a' <= lo
&& lo
<= 'f') {
1316 if ('A' <= lo
&& lo
<= 'F') {
1319 result
= lo
+ (16 * hi
);
1323 gboolean
is_uri_string(const gchar
*str
)
1325 while (str
&& *str
&& g_ascii_isspace(*str
))
1327 return (g_ascii_strncasecmp(str
, "http://", 7) == 0 ||
1328 g_ascii_strncasecmp(str
, "https://", 8) == 0 ||
1329 g_ascii_strncasecmp(str
, "ftp://", 6) == 0 ||
1330 g_ascii_strncasecmp(str
, "www.", 4) == 0);
1333 gchar
*get_uri_path(const gchar
*uri
)
1335 while (uri
&& *uri
&& g_ascii_isspace(*uri
))
1337 if (g_ascii_strncasecmp(uri
, "http://", 7) == 0)
1338 return (gchar
*)(uri
+ 7);
1339 else if (g_ascii_strncasecmp(uri
, "https://", 8) == 0)
1340 return (gchar
*)(uri
+ 8);
1341 else if (g_ascii_strncasecmp(uri
, "ftp://", 6) == 0)
1342 return (gchar
*)(uri
+ 6);
1344 return (gchar
*)uri
;
1347 gint
get_uri_len(const gchar
*str
)
1351 if (is_uri_string(str
)) {
1352 for (p
= str
; *p
!= '\0'; p
++) {
1353 if (!g_ascii_isgraph(*p
) || strchr("()<>\"", *p
))
1362 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1363 * plusses, and escape characters are used)
1365 void decode_uri_with_plus(gchar
*decoded_uri
, const gchar
*encoded_uri
, gboolean with_plus
)
1367 gchar
*dec
= decoded_uri
;
1368 const gchar
*enc
= encoded_uri
;
1373 if (isxdigit((guchar
)enc
[0]) &&
1374 isxdigit((guchar
)enc
[1])) {
1380 if (with_plus
&& *enc
== '+')
1392 void decode_uri(gchar
*decoded_uri
, const gchar
*encoded_uri
)
1394 decode_uri_with_plus(decoded_uri
, encoded_uri
, TRUE
);
1397 static gchar
*decode_uri_gdup(const gchar
*encoded_uri
)
1399 gchar
*buffer
= g_malloc(strlen(encoded_uri
)+1);
1400 decode_uri_with_plus(buffer
, encoded_uri
, FALSE
);
1404 gint
scan_mailto_url(const gchar
*mailto
, gchar
**from
, gchar
**to
, gchar
**cc
, gchar
**bcc
,
1405 gchar
**subject
, gchar
**body
, gchar
***attach
, gchar
**inreplyto
)
1409 const gchar
*forbidden_uris
[] = { ".gnupg/",
1415 gint num_attach
= 0;
1417 cm_return_val_if_fail(mailto
!= NULL
, -1);
1419 Xstrdup_a(tmp_mailto
, mailto
, return -1);
1421 if (!strncmp(tmp_mailto
, "mailto:", 7))
1424 p
= strchr(tmp_mailto
, '?');
1431 *to
= decode_uri_gdup(tmp_mailto
);
1434 gchar
*field
, *value
;
1451 if (*value
== '\0') continue;
1453 if (from
&& !g_ascii_strcasecmp(field
, "from")) {
1455 *from
= decode_uri_gdup(value
);
1457 gchar
*tmp
= decode_uri_gdup(value
);
1458 gchar
*new_from
= g_strdup_printf("%s, %s", *from
, tmp
);
1463 } else if (cc
&& !g_ascii_strcasecmp(field
, "cc")) {
1465 *cc
= decode_uri_gdup(value
);
1467 gchar
*tmp
= decode_uri_gdup(value
);
1468 gchar
*new_cc
= g_strdup_printf("%s, %s", *cc
, tmp
);
1473 } else if (bcc
&& !g_ascii_strcasecmp(field
, "bcc")) {
1475 *bcc
= decode_uri_gdup(value
);
1477 gchar
*tmp
= decode_uri_gdup(value
);
1478 gchar
*new_bcc
= g_strdup_printf("%s, %s", *bcc
, tmp
);
1483 } else if (subject
&& !*subject
&&
1484 !g_ascii_strcasecmp(field
, "subject")) {
1485 *subject
= decode_uri_gdup(value
);
1486 } else if (body
&& !*body
&& !g_ascii_strcasecmp(field
, "body")) {
1487 *body
= decode_uri_gdup(value
);
1488 } else if (body
&& !*body
&& !g_ascii_strcasecmp(field
, "insert")) {
1490 gchar
*tmp
= decode_uri_gdup(value
);
1492 for (; forbidden_uris
[i
]; i
++) {
1493 if (strstr(tmp
, forbidden_uris
[i
])) {
1494 g_print("Refusing to insert '%s', potential private data leak\n",
1503 if (!is_file_entry_regular(tmp
)) {
1504 g_warning("Refusing to insert '%s', not a regular file\n", tmp
);
1505 } else if (!g_file_get_contents(tmp
, body
, NULL
, NULL
)) {
1506 g_warning("couldn't set insert file '%s' in body", value
);
1511 } else if (attach
&& !g_ascii_strcasecmp(field
, "attach")) {
1513 gchar
*tmp
= decode_uri_gdup(value
);
1514 gchar
**my_att
= g_malloc(sizeof(char *));
1518 for (; forbidden_uris
[i
]; i
++) {
1519 if (strstr(tmp
, forbidden_uris
[i
])) {
1520 g_print("Refusing to attach '%s', potential private data leak\n",
1529 /* attach is correct */
1531 my_att
= g_realloc(my_att
, (sizeof(char *))*(num_attach
+1));
1532 my_att
[num_attach
-1] = tmp
;
1533 my_att
[num_attach
] = NULL
;
1536 } else if (inreplyto
&& !*inreplyto
&&
1537 !g_ascii_strcasecmp(field
, "in-reply-to")) {
1538 *inreplyto
= decode_uri_gdup(value
);
1547 #include <windows.h>
1548 #ifndef CSIDL_APPDATA
1549 #define CSIDL_APPDATA 0x001a
1551 #ifndef CSIDL_LOCAL_APPDATA
1552 #define CSIDL_LOCAL_APPDATA 0x001c
1554 #ifndef CSIDL_FLAG_CREATE
1555 #define CSIDL_FLAG_CREATE 0x8000
1557 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1561 w32_strerror (int w32_errno
)
1563 static char strerr
[256];
1564 int ec
= (int)GetLastError ();
1568 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, w32_errno
,
1569 MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
1570 strerr
, DIM (strerr
)-1, NULL
);
1574 static __inline__
void *
1575 dlopen (const char * name
, int flag
)
1577 void * hd
= LoadLibrary (name
);
1581 static __inline__
void *
1582 dlsym (void * hd
, const char * sym
)
1586 void * fnc
= GetProcAddress (hd
, sym
);
1595 static __inline__
const char *
1598 return w32_strerror (0);
1602 static __inline__
int
1614 w32_shgetfolderpath (HWND a
, int b
, HANDLE c
, DWORD d
, LPSTR e
)
1616 static int initialized
;
1617 static HRESULT (WINAPI
* func
)(HWND
,int,HANDLE
,DWORD
,LPSTR
);
1621 static char *dllnames
[] = { "shell32.dll", "shfolder.dll", NULL
};
1627 for (i
=0, handle
= NULL
; !handle
&& dllnames
[i
]; i
++)
1629 handle
= dlopen (dllnames
[i
], RTLD_LAZY
);
1632 func
= dlsym (handle
, "SHGetFolderPathW");
1643 return func (a
,b
,c
,d
,e
);
1648 /* Returns a static string with the directroy from which the module
1649 has been loaded. Returns an empty string on error. */
1650 static char *w32_get_module_dir(void)
1652 static char *moddir
;
1655 char name
[MAX_PATH
+10];
1658 if ( !GetModuleFileNameA (0, name
, sizeof (name
)-10) )
1661 p
= strrchr (name
, '\\');
1667 moddir
= g_strdup (name
);
1671 #endif /* G_OS_WIN32 */
1673 /* Return a static string with the locale dir. */
1674 const gchar
*get_locale_dir(void)
1676 static gchar
*loc_dir
;
1680 loc_dir
= g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S
,
1681 "\\share\\locale", NULL
);
1684 loc_dir
= LOCALEDIR
;
1690 const gchar
*get_home_dir(void)
1693 static char home_dir_utf16
[MAX_PATH
] = "";
1694 static gchar
*home_dir_utf8
= NULL
;
1695 if (home_dir_utf16
[0] == '\0') {
1696 if (w32_shgetfolderpath
1697 (NULL
, CSIDL_APPDATA
|CSIDL_FLAG_CREATE
,
1698 NULL
, 0, home_dir_utf16
) < 0)
1699 strcpy (home_dir_utf16
, "C:\\Claws Mail");
1700 home_dir_utf8
= g_utf16_to_utf8 ((const gunichar2
*)home_dir_utf16
, -1, NULL
, NULL
, NULL
);
1702 return home_dir_utf8
;
1704 static const gchar
*homeenv
= NULL
;
1709 if (!homeenv
&& g_getenv("HOME") != NULL
)
1710 homeenv
= g_strdup(g_getenv("HOME"));
1712 homeenv
= g_get_home_dir();
1718 static gchar
*claws_rc_dir
= NULL
;
1719 static gboolean rc_dir_alt
= FALSE
;
1720 const gchar
*get_rc_dir(void)
1723 if (!claws_rc_dir
) {
1724 claws_rc_dir
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
,
1726 debug_print("using default rc_dir %s\n", claws_rc_dir
);
1728 return claws_rc_dir
;
1731 void set_rc_dir(const gchar
*dir
)
1733 gchar
*canonical_dir
;
1734 if (claws_rc_dir
!= NULL
) {
1735 g_print("Error: rc_dir already set\n");
1737 int err
= cm_canonicalize_filename(dir
, &canonical_dir
);
1741 g_print("Error looking for %s: %d(%s)\n",
1742 dir
, -err
, g_strerror(-err
));
1747 claws_rc_dir
= canonical_dir
;
1749 len
= strlen(claws_rc_dir
);
1750 if (claws_rc_dir
[len
- 1] == G_DIR_SEPARATOR
)
1751 claws_rc_dir
[len
- 1] = '\0';
1753 debug_print("set rc_dir to %s\n", claws_rc_dir
);
1754 if (!is_dir_exist(claws_rc_dir
)) {
1755 if (make_dir_hier(claws_rc_dir
) != 0) {
1756 g_print("Error: can't create %s\n",
1764 gboolean
rc_dir_is_alt(void) {
1768 const gchar
*get_mail_base_dir(void)
1770 return get_home_dir();
1773 const gchar
*get_news_cache_dir(void)
1775 static gchar
*news_cache_dir
= NULL
;
1776 if (!news_cache_dir
)
1777 news_cache_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1778 NEWS_CACHE_DIR
, NULL
);
1780 return news_cache_dir
;
1783 const gchar
*get_imap_cache_dir(void)
1785 static gchar
*imap_cache_dir
= NULL
;
1787 if (!imap_cache_dir
)
1788 imap_cache_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1789 IMAP_CACHE_DIR
, NULL
);
1791 return imap_cache_dir
;
1794 const gchar
*get_mime_tmp_dir(void)
1796 static gchar
*mime_tmp_dir
= NULL
;
1799 mime_tmp_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1800 MIME_TMP_DIR
, NULL
);
1802 return mime_tmp_dir
;
1805 const gchar
*get_template_dir(void)
1807 static gchar
*template_dir
= NULL
;
1810 template_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1811 TEMPLATE_DIR
, NULL
);
1813 return template_dir
;
1817 const gchar
*w32_get_cert_file(void)
1819 const gchar
*cert_file
= NULL
;
1821 cert_file
= g_strconcat(w32_get_module_dir(),
1822 "\\share\\claws-mail\\",
1823 "ca-certificates.crt",
1829 /* Return the filepath of the claws-mail.desktop file */
1830 const gchar
*get_desktop_file(void)
1832 #ifdef DESKTOPFILEPATH
1833 return DESKTOPFILEPATH
;
1839 /* Return the default directory for Plugins. */
1840 const gchar
*get_plugin_dir(void)
1843 static gchar
*plugin_dir
= NULL
;
1846 plugin_dir
= g_strconcat(w32_get_module_dir(),
1847 "\\lib\\claws-mail\\plugins\\",
1851 if (is_dir_exist(PLUGINDIR
))
1854 static gchar
*plugin_dir
= NULL
;
1856 plugin_dir
= g_strconcat(get_rc_dir(),
1857 G_DIR_SEPARATOR_S
, "plugins",
1858 G_DIR_SEPARATOR_S
, NULL
);
1866 /* Return the default directory for Themes. */
1867 const gchar
*w32_get_themes_dir(void)
1869 static gchar
*themes_dir
= NULL
;
1872 themes_dir
= g_strconcat(w32_get_module_dir(),
1873 "\\share\\claws-mail\\themes",
1879 const gchar
*get_tmp_dir(void)
1881 static gchar
*tmp_dir
= NULL
;
1884 tmp_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1890 gchar
*get_tmp_file(void)
1893 static guint32 id
= 0;
1895 tmp_file
= g_strdup_printf("%s%ctmpfile.%08x",
1896 get_tmp_dir(), G_DIR_SEPARATOR
, id
++);
1901 const gchar
*get_domain_name(void)
1904 static gchar
*domain_name
= NULL
;
1905 struct addrinfo hints
, *res
;
1910 if (gethostname(hostname
, sizeof(hostname
)) != 0) {
1911 perror("gethostname");
1912 domain_name
= "localhost";
1914 memset(&hints
, 0, sizeof(struct addrinfo
));
1915 hints
.ai_family
= AF_UNSPEC
;
1916 hints
.ai_socktype
= 0;
1917 hints
.ai_flags
= AI_CANONNAME
;
1918 hints
.ai_protocol
= 0;
1920 s
= getaddrinfo(hostname
, NULL
, &hints
, &res
);
1922 fprintf(stderr
, "getaddrinfo: %s\n", gai_strerror(s
));
1923 domain_name
= g_strdup(hostname
);
1925 domain_name
= g_strdup(res
->ai_canonname
);
1929 debug_print("domain name = %s\n", domain_name
);
1938 /* Tells whether the given host address string is a valid representation of a
1939 * numerical IP (v4 or, if supported, v6) address.
1941 gboolean
is_numeric_host_address(const gchar
*hostaddress
)
1943 struct addrinfo hints
, *res
;
1946 /* See what getaddrinfo makes of the string when told that it is a
1947 * numeric IP address representation. */
1948 memset(&hints
, 0, sizeof(struct addrinfo
));
1949 hints
.ai_family
= AF_UNSPEC
;
1950 hints
.ai_socktype
= 0;
1951 hints
.ai_flags
= AI_NUMERICHOST
;
1952 hints
.ai_protocol
= 0;
1954 err
= getaddrinfo(hostaddress
, NULL
, &hints
, &res
);
1961 off_t
get_file_size(const gchar
*file
)
1966 GError
*error
= NULL
;
1969 f
= g_file_new_for_path(file
);
1970 fi
= g_file_query_info(f
, "standard::size",
1971 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
1972 if (error
!= NULL
) {
1973 debug_print("get_file_size error: %s\n", error
->message
);
1974 g_error_free(error
);
1978 size
= g_file_info_get_size(fi
);
1986 if (g_stat(file
, &s
) < 0) {
1987 FILE_OP_ERROR(file
, "stat");
1995 time_t get_file_mtime(const gchar
*file
)
1999 if (g_stat(file
, &s
) < 0) {
2000 FILE_OP_ERROR(file
, "stat");
2007 gboolean
file_exist(const gchar
*file
, gboolean allow_fifo
)
2014 if (g_stat(file
, &s
) < 0) {
2015 if (ENOENT
!= errno
) FILE_OP_ERROR(file
, "stat");
2019 if (S_ISREG(s
.st_mode
) || (allow_fifo
&& S_ISFIFO(s
.st_mode
)))
2026 /* Test on whether FILE is a relative file name. This is
2027 * straightforward for Unix but more complex for Windows. */
2028 gboolean
is_relative_filename(const gchar
*file
)
2033 if ( *file
== '\\' && file
[1] == '\\' && strchr (file
+2, '\\') )
2034 return FALSE
; /* Prefixed with a hostname - this can't
2035 * be a relative name. */
2037 if ( ((*file
>= 'a' && *file
<= 'z')
2038 || (*file
>= 'A' && *file
<= 'Z'))
2040 file
+= 2; /* Skip drive letter. */
2042 return !(*file
== '\\' || *file
== '/');
2044 return !(*file
== G_DIR_SEPARATOR
);
2049 gboolean
is_dir_exist(const gchar
*dir
)
2054 return g_file_test(dir
, G_FILE_TEST_IS_DIR
);
2057 gboolean
is_file_entry_exist(const gchar
*file
)
2062 return g_file_test(file
, G_FILE_TEST_EXISTS
);
2065 gboolean
is_file_entry_regular(const gchar
*file
)
2070 return g_file_test(file
, G_FILE_TEST_IS_REGULAR
);
2073 gboolean
dirent_is_regular_file(struct dirent
*d
)
2075 #if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE)
2076 if (d
->d_type
== DT_REG
)
2078 else if (d
->d_type
!= DT_UNKNOWN
)
2082 return g_file_test(d
->d_name
, G_FILE_TEST_IS_REGULAR
);
2085 gint
change_dir(const gchar
*dir
)
2087 gchar
*prevdir
= NULL
;
2090 prevdir
= g_get_current_dir();
2092 if (g_chdir(dir
) < 0) {
2093 FILE_OP_ERROR(dir
, "chdir");
2094 if (debug_mode
) g_free(prevdir
);
2096 } else if (debug_mode
) {
2099 cwd
= g_get_current_dir();
2100 if (strcmp(prevdir
, cwd
) != 0)
2101 g_print("current dir: %s\n", cwd
);
2109 gint
make_dir(const gchar
*dir
)
2111 if (g_mkdir(dir
, S_IRWXU
) < 0) {
2112 FILE_OP_ERROR(dir
, "mkdir");
2115 if (g_chmod(dir
, S_IRWXU
) < 0)
2116 FILE_OP_ERROR(dir
, "chmod");
2121 gint
make_dir_hier(const gchar
*dir
)
2126 for (p
= dir
; (p
= strchr(p
, G_DIR_SEPARATOR
)) != NULL
; p
++) {
2127 parent_dir
= g_strndup(dir
, p
- dir
);
2128 if (*parent_dir
!= '\0') {
2129 if (!is_dir_exist(parent_dir
)) {
2130 if (make_dir(parent_dir
) < 0) {
2139 if (!is_dir_exist(dir
)) {
2140 if (make_dir(dir
) < 0)
2147 gint
remove_all_files(const gchar
*dir
)
2150 const gchar
*file_name
;
2153 if ((dp
= g_dir_open(dir
, 0, NULL
)) == NULL
) {
2154 g_warning("failed to open directory: %s", dir
);
2158 while ((file_name
= g_dir_read_name(dp
)) != NULL
) {
2159 tmp
= g_strconcat(dir
, G_DIR_SEPARATOR_S
, file_name
, NULL
);
2160 if (claws_unlink(tmp
) < 0)
2161 FILE_OP_ERROR(tmp
, "unlink");
2170 gint
remove_numbered_files(const gchar
*dir
, guint first
, guint last
)
2173 const gchar
*dir_name
;
2177 if (first
== last
) {
2178 /* Skip all the dir reading part. */
2179 gchar
*filename
= g_strdup_printf("%s%s%u", dir
, G_DIR_SEPARATOR_S
, first
);
2180 if (is_dir_exist(filename
)) {
2181 /* a numbered directory with this name exists,
2182 * remove the dot-file instead */
2184 filename
= g_strdup_printf("%s%s.%u", dir
, G_DIR_SEPARATOR_S
, first
);
2186 if (claws_unlink(filename
) < 0) {
2187 FILE_OP_ERROR(filename
, "unlink");
2195 prev_dir
= g_get_current_dir();
2197 if (g_chdir(dir
) < 0) {
2198 FILE_OP_ERROR(dir
, "chdir");
2203 if ((dp
= g_dir_open(".", 0, NULL
)) == NULL
) {
2204 g_warning("failed to open directory: %s", dir
);
2209 while ((dir_name
= g_dir_read_name(dp
)) != NULL
) {
2210 file_no
= to_number(dir_name
);
2211 if (file_no
> 0 && first
<= file_no
&& file_no
<= last
) {
2212 if (is_dir_exist(dir_name
)) {
2213 gchar
*dot_file
= g_strdup_printf(".%s", dir_name
);
2214 if (is_file_exist(dot_file
) && claws_unlink(dot_file
) < 0) {
2215 FILE_OP_ERROR(dot_file
, "unlink");
2220 if (claws_unlink(dir_name
) < 0)
2221 FILE_OP_ERROR(dir_name
, "unlink");
2227 if (g_chdir(prev_dir
) < 0) {
2228 FILE_OP_ERROR(prev_dir
, "chdir");
2238 gint
remove_numbered_files_not_in_list(const gchar
*dir
, GSList
*numberlist
)
2241 const gchar
*dir_name
;
2244 GHashTable
*wanted_files
;
2246 GError
*error
= NULL
;
2248 if (numberlist
== NULL
)
2251 prev_dir
= g_get_current_dir();
2253 if (g_chdir(dir
) < 0) {
2254 FILE_OP_ERROR(dir
, "chdir");
2259 if ((dp
= g_dir_open(".", 0, &error
)) == NULL
) {
2260 g_message("Couldn't open current directory: %s (%d).\n",
2261 error
->message
, error
->code
);
2262 g_error_free(error
);
2267 wanted_files
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
2268 for (cur
= numberlist
; cur
!= NULL
; cur
= cur
->next
) {
2269 /* numberlist->data is expected to be GINT_TO_POINTER */
2270 g_hash_table_insert(wanted_files
, cur
->data
, GINT_TO_POINTER(1));
2273 while ((dir_name
= g_dir_read_name(dp
)) != NULL
) {
2274 file_no
= to_number(dir_name
);
2275 if (is_dir_exist(dir_name
))
2277 if (file_no
> 0 && g_hash_table_lookup(wanted_files
, GINT_TO_POINTER(file_no
)) == NULL
) {
2278 debug_print("removing unwanted file %d from %s\n", file_no
, dir
);
2279 if (is_dir_exist(dir_name
)) {
2280 gchar
*dot_file
= g_strdup_printf(".%s", dir_name
);
2281 if (is_file_exist(dot_file
) && claws_unlink(dot_file
) < 0) {
2282 FILE_OP_ERROR(dot_file
, "unlink");
2287 if (claws_unlink(dir_name
) < 0)
2288 FILE_OP_ERROR(dir_name
, "unlink");
2293 g_hash_table_destroy(wanted_files
);
2295 if (g_chdir(prev_dir
) < 0) {
2296 FILE_OP_ERROR(prev_dir
, "chdir");
2306 gint
remove_all_numbered_files(const gchar
*dir
)
2308 return remove_numbered_files(dir
, 0, UINT_MAX
);
2311 gint
remove_dir_recursive(const gchar
*dir
)
2315 const gchar
*dir_name
;
2318 if (g_stat(dir
, &s
) < 0) {
2319 FILE_OP_ERROR(dir
, "stat");
2320 if (ENOENT
== errno
) return 0;
2324 if (!S_ISDIR(s
.st_mode
)) {
2325 if (claws_unlink(dir
) < 0) {
2326 FILE_OP_ERROR(dir
, "unlink");
2333 prev_dir
= g_get_current_dir();
2334 /* g_print("prev_dir = %s\n", prev_dir); */
2336 if (!path_cmp(prev_dir
, dir
)) {
2338 if (g_chdir("..") < 0) {
2339 FILE_OP_ERROR(dir
, "chdir");
2342 prev_dir
= g_get_current_dir();
2345 if (g_chdir(dir
) < 0) {
2346 FILE_OP_ERROR(dir
, "chdir");
2351 if ((dp
= g_dir_open(".", 0, NULL
)) == NULL
) {
2352 g_warning("failed to open directory: %s", dir
);
2358 /* remove all files in the directory */
2359 while ((dir_name
= g_dir_read_name(dp
)) != NULL
) {
2360 /* g_print("removing %s\n", dir_name); */
2362 if (is_dir_exist(dir_name
)) {
2365 if ((ret
= remove_dir_recursive(dir_name
)) < 0) {
2366 g_warning("can't remove directory: %s", dir_name
);
2370 if (claws_unlink(dir_name
) < 0)
2371 FILE_OP_ERROR(dir_name
, "unlink");
2377 if (g_chdir(prev_dir
) < 0) {
2378 FILE_OP_ERROR(prev_dir
, "chdir");
2385 if (g_rmdir(dir
) < 0) {
2386 FILE_OP_ERROR(dir
, "rmdir");
2393 /* convert line endings into CRLF. If the last line doesn't end with
2394 * linebreak, add it.
2396 gchar
*canonicalize_str(const gchar
*str
)
2402 for (p
= str
; *p
!= '\0'; ++p
) {
2409 if (p
== str
|| *(p
- 1) != '\n')
2412 out
= outp
= g_malloc(new_len
+ 1);
2413 for (p
= str
; *p
!= '\0'; ++p
) {
2420 if (p
== str
|| *(p
- 1) != '\n') {
2429 gchar
*normalize_newlines(const gchar
*str
)
2434 out
= outp
= g_malloc(strlen(str
) + 1);
2435 for (p
= str
; *p
!= '\0'; ++p
) {
2437 if (*(p
+ 1) != '\n')
2448 gchar
*get_outgoing_rfc2822_str(FILE *fp
)
2450 gchar buf
[BUFFSIZE
];
2454 str
= g_string_new(NULL
);
2456 /* output header part */
2457 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2459 if (!g_ascii_strncasecmp(buf
, "Bcc:", 4)) {
2466 else if (next
!= ' ' && next
!= '\t') {
2470 if (claws_fgets(buf
, sizeof(buf
), fp
) == NULL
)
2474 g_string_append(str
, buf
);
2475 g_string_append(str
, "\r\n");
2481 /* output body part */
2482 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2485 g_string_append_c(str
, '.');
2486 g_string_append(str
, buf
);
2487 g_string_append(str
, "\r\n");
2491 g_string_free(str
, FALSE
);
2497 * Create a new boundary in a way that it is very unlikely that this
2498 * will occur in the following text. It would be easy to ensure
2499 * uniqueness if everything is either quoted-printable or base64
2500 * encoded (note that conversion is allowed), but because MIME bodies
2501 * may be nested, it may happen that the same boundary has already
2504 * boundary := 0*69<bchars> bcharsnospace
2505 * bchars := bcharsnospace / " "
2506 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2507 * "+" / "_" / "," / "-" / "." /
2508 * "/" / ":" / "=" / "?"
2510 * some special characters removed because of buggy MTAs
2513 gchar
*generate_mime_boundary(const gchar
*prefix
)
2515 static gchar tbl
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2516 "abcdefghijklmnopqrstuvwxyz"
2521 for (i
= 0; i
< sizeof(buf_uniq
) - 1; i
++)
2522 buf_uniq
[i
] = tbl
[g_random_int_range(0, sizeof(tbl
) - 1)];
2525 return g_strdup_printf("%s_/%s", prefix
? prefix
: "MP",
2529 char *fgets_crlf(char *buf
, int size
, FILE *stream
)
2531 gboolean is_cr
= FALSE
;
2532 gboolean last_was_cr
= FALSE
;
2537 while (--size
> 0 && (c
= getc(stream
)) != EOF
)
2540 is_cr
= (c
== '\r');
2550 last_was_cr
= is_cr
;
2552 if (c
== EOF
&& cs
== buf
)
2560 static gint
execute_async(gchar
*const argv
[], const gchar
*working_directory
)
2562 cm_return_val_if_fail(argv
!= NULL
&& argv
[0] != NULL
, -1);
2564 if (g_spawn_async(working_directory
, (gchar
**)argv
, NULL
, G_SPAWN_SEARCH_PATH
,
2565 NULL
, NULL
, NULL
, FALSE
) == FALSE
) {
2566 g_warning("couldn't execute command: %s", argv
[0]);
2573 static gint
execute_sync(gchar
*const argv
[], const gchar
*working_directory
)
2577 cm_return_val_if_fail(argv
!= NULL
&& argv
[0] != NULL
, -1);
2580 if (g_spawn_sync(working_directory
, (gchar
**)argv
, NULL
, G_SPAWN_SEARCH_PATH
,
2581 NULL
, NULL
, NULL
, NULL
, &status
, NULL
) == FALSE
) {
2582 g_warning("couldn't execute command: %s", argv
[0]);
2586 if (WIFEXITED(status
))
2587 return WEXITSTATUS(status
);
2591 if (g_spawn_sync(working_directory
, (gchar
**)argv
, NULL
,
2592 G_SPAWN_SEARCH_PATH
|
2593 G_SPAWN_CHILD_INHERITS_STDIN
|
2594 G_SPAWN_LEAVE_DESCRIPTORS_OPEN
,
2595 NULL
, NULL
, NULL
, NULL
, &status
, NULL
) == FALSE
) {
2596 g_warning("couldn't execute command: %s", argv
[0]);
2604 gint
execute_command_line(const gchar
*cmdline
, gboolean async
,
2605 const gchar
*working_directory
)
2610 debug_print("execute_command_line(): executing: %s\n", cmdline
?cmdline
:"(null)");
2612 argv
= strsplit_with_quote(cmdline
, " ", 0);
2615 ret
= execute_async(argv
, working_directory
);
2617 ret
= execute_sync(argv
, working_directory
);
2624 gchar
*get_command_output(const gchar
*cmdline
)
2626 gchar
*child_stdout
;
2629 cm_return_val_if_fail(cmdline
!= NULL
, NULL
);
2631 debug_print("get_command_output(): executing: %s\n", cmdline
);
2633 if (g_spawn_command_line_sync(cmdline
, &child_stdout
, NULL
, &status
,
2635 g_warning("couldn't execute command: %s", cmdline
);
2639 return child_stdout
;
2642 static gint
is_unchanged_uri_char(char c
)
2653 static void encode_uri(gchar
*encoded_uri
, gint bufsize
, const gchar
*uri
)
2659 for(i
= 0; i
< strlen(uri
) ; i
++) {
2660 if (is_unchanged_uri_char(uri
[i
])) {
2661 if (k
+ 2 >= bufsize
)
2663 encoded_uri
[k
++] = uri
[i
];
2666 char * hexa
= "0123456789ABCDEF";
2668 if (k
+ 4 >= bufsize
)
2670 encoded_uri
[k
++] = '%';
2671 encoded_uri
[k
++] = hexa
[uri
[i
] / 16];
2672 encoded_uri
[k
++] = hexa
[uri
[i
] % 16];
2678 gint
open_uri(const gchar
*uri
, const gchar
*cmdline
)
2682 gchar buf
[BUFFSIZE
];
2684 gchar encoded_uri
[BUFFSIZE
];
2685 cm_return_val_if_fail(uri
!= NULL
, -1);
2687 /* an option to choose whether to use encode_uri or not ? */
2688 encode_uri(encoded_uri
, BUFFSIZE
, uri
);
2691 (p
= strchr(cmdline
, '%')) && *(p
+ 1) == 's' &&
2692 !strchr(p
+ 2, '%'))
2693 g_snprintf(buf
, sizeof(buf
), cmdline
, encoded_uri
);
2696 g_warning("Open URI command-line is invalid "
2697 "(there must be only one '%%s'): %s",
2699 g_snprintf(buf
, sizeof(buf
), DEFAULT_BROWSER_CMD
, encoded_uri
);
2702 execute_command_line(buf
, TRUE
, NULL
);
2704 ShellExecute(NULL
, "open", uri
, NULL
, NULL
, SW_SHOW
);
2709 gint
open_txt_editor(const gchar
*filepath
, const gchar
*cmdline
)
2711 gchar buf
[BUFFSIZE
];
2714 cm_return_val_if_fail(filepath
!= NULL
, -1);
2717 (p
= strchr(cmdline
, '%')) && *(p
+ 1) == 's' &&
2718 !strchr(p
+ 2, '%'))
2719 g_snprintf(buf
, sizeof(buf
), cmdline
, filepath
);
2722 g_warning("Open Text Editor command-line is invalid "
2723 "(there must be only one '%%s'): %s",
2725 g_snprintf(buf
, sizeof(buf
), DEFAULT_EDITOR_CMD
, filepath
);
2728 execute_command_line(buf
, TRUE
, NULL
);
2733 time_t remote_tzoffset_sec(const gchar
*zone
)
2735 static gchar ustzstr
[] = "PSTPDTMSTMDTCSTCDTESTEDT";
2741 time_t remoteoffset
;
2743 strncpy(zone3
, zone
, 3);
2747 if (sscanf(zone
, "%c%d", &c
, &offset
) == 2 &&
2748 (c
== '+' || c
== '-')) {
2749 remoteoffset
= ((offset
/ 100) * 60 + (offset
% 100)) * 60;
2751 remoteoffset
= -remoteoffset
;
2752 } else if (!strncmp(zone
, "UT" , 2) ||
2753 !strncmp(zone
, "GMT", 3)) {
2755 } else if (strlen(zone3
) == 3) {
2756 for (p
= ustzstr
; *p
!= '\0'; p
+= 3) {
2757 if (!g_ascii_strncasecmp(p
, zone3
, 3)) {
2758 iustz
= ((gint
)(p
- ustzstr
) / 3 + 1) / 2 - 8;
2759 remoteoffset
= iustz
* 3600;
2765 } else if (strlen(zone3
) == 1) {
2767 case 'Z': remoteoffset
= 0; break;
2768 case 'A': remoteoffset
= -1; break;
2769 case 'B': remoteoffset
= -2; break;
2770 case 'C': remoteoffset
= -3; break;
2771 case 'D': remoteoffset
= -4; break;
2772 case 'E': remoteoffset
= -5; break;
2773 case 'F': remoteoffset
= -6; break;
2774 case 'G': remoteoffset
= -7; break;
2775 case 'H': remoteoffset
= -8; break;
2776 case 'I': remoteoffset
= -9; break;
2777 case 'K': remoteoffset
= -10; break; /* J is not used */
2778 case 'L': remoteoffset
= -11; break;
2779 case 'M': remoteoffset
= -12; break;
2780 case 'N': remoteoffset
= 1; break;
2781 case 'O': remoteoffset
= 2; break;
2782 case 'P': remoteoffset
= 3; break;
2783 case 'Q': remoteoffset
= 4; break;
2784 case 'R': remoteoffset
= 5; break;
2785 case 'S': remoteoffset
= 6; break;
2786 case 'T': remoteoffset
= 7; break;
2787 case 'U': remoteoffset
= 8; break;
2788 case 'V': remoteoffset
= 9; break;
2789 case 'W': remoteoffset
= 10; break;
2790 case 'X': remoteoffset
= 11; break;
2791 case 'Y': remoteoffset
= 12; break;
2792 default: remoteoffset
= 0; break;
2794 remoteoffset
= remoteoffset
* 3600;
2798 return remoteoffset
;
2801 time_t tzoffset_sec(time_t *now
)
2805 struct tm buf1
, buf2
;
2807 if (now
&& *now
< 0)
2810 gmt
= *gmtime_r(now
, &buf1
);
2811 lt
= localtime_r(now
, &buf2
);
2813 off
= (lt
->tm_hour
- gmt
.tm_hour
) * 60 + lt
->tm_min
- gmt
.tm_min
;
2815 if (lt
->tm_year
< gmt
.tm_year
)
2817 else if (lt
->tm_year
> gmt
.tm_year
)
2819 else if (lt
->tm_yday
< gmt
.tm_yday
)
2821 else if (lt
->tm_yday
> gmt
.tm_yday
)
2824 if (off
>= 24 * 60) /* should be impossible */
2825 off
= 23 * 60 + 59; /* if not, insert silly value */
2826 if (off
<= -24 * 60)
2827 off
= -(23 * 60 + 59);
2832 /* calculate timezone offset */
2833 gchar
*tzoffset(time_t *now
)
2835 static gchar offset_string
[6];
2839 struct tm buf1
, buf2
;
2841 if (now
&& *now
< 0)
2844 gmt
= *gmtime_r(now
, &buf1
);
2845 lt
= localtime_r(now
, &buf2
);
2847 off
= (lt
->tm_hour
- gmt
.tm_hour
) * 60 + lt
->tm_min
- gmt
.tm_min
;
2849 if (lt
->tm_year
< gmt
.tm_year
)
2851 else if (lt
->tm_year
> gmt
.tm_year
)
2853 else if (lt
->tm_yday
< gmt
.tm_yday
)
2855 else if (lt
->tm_yday
> gmt
.tm_yday
)
2863 if (off
>= 24 * 60) /* should be impossible */
2864 off
= 23 * 60 + 59; /* if not, insert silly value */
2866 sprintf(offset_string
, "%c%02d%02d", sign
, off
/ 60, off
% 60);
2868 return offset_string
;
2871 static void _get_rfc822_date(gchar
*buf
, gint len
, gboolean hidetz
)
2875 gchar day
[4], mon
[4];
2876 gint dd
, hh
, mm
, ss
, yyyy
;
2878 gchar buf2
[RFC822_DATE_BUFFSIZE
];
2882 lt
= gmtime_r(&t
, &buf1
);
2884 lt
= localtime_r(&t
, &buf1
);
2886 if (sscanf(asctime_r(lt
, buf2
), "%3s %3s %d %d:%d:%d %d\n",
2887 day
, mon
, &dd
, &hh
, &mm
, &ss
, &yyyy
) != 7)
2888 g_warning("failed reading date/time");
2890 g_snprintf(buf
, len
, "%s, %d %s %d %02d:%02d:%02d %s",
2891 day
, dd
, mon
, yyyy
, hh
, mm
, ss
, (hidetz
? "-0000": tzoffset(&t
)));
2894 void get_rfc822_date(gchar
*buf
, gint len
)
2896 _get_rfc822_date(buf
, len
, FALSE
);
2899 void get_rfc822_date_hide_tz(gchar
*buf
, gint len
)
2901 _get_rfc822_date(buf
, len
, TRUE
);
2904 void debug_set_mode(gboolean mode
)
2909 gboolean
debug_get_mode(void)
2914 void debug_print_real(const gchar
*format
, ...)
2917 gchar buf
[BUFFSIZE
];
2919 if (!debug_mode
) return;
2921 va_start(args
, format
);
2922 g_vsnprintf(buf
, sizeof(buf
), format
, args
);
2929 const char * debug_srcname(const char *file
)
2931 const char *s
= strrchr (file
, '/');
2936 void * subject_table_lookup(GHashTable
*subject_table
, gchar
* subject
)
2938 if (subject
== NULL
)
2941 subject
+= subject_get_prefix_length(subject
);
2943 return g_hash_table_lookup(subject_table
, subject
);
2946 void subject_table_insert(GHashTable
*subject_table
, gchar
* subject
,
2949 if (subject
== NULL
|| *subject
== 0)
2951 subject
+= subject_get_prefix_length(subject
);
2952 g_hash_table_insert(subject_table
, subject
, data
);
2955 void subject_table_remove(GHashTable
*subject_table
, gchar
* subject
)
2957 if (subject
== NULL
)
2960 subject
+= subject_get_prefix_length(subject
);
2961 g_hash_table_remove(subject_table
, subject
);
2964 static regex_t u_regex
;
2965 static gboolean u_init_
;
2967 void utils_free_regex(void)
2976 *\brief Check if a string is prefixed with known (combinations)
2977 * of prefixes. The function assumes that each prefix
2978 * is terminated by zero or exactly _one_ space.
2980 *\param str String to check for a prefixes
2982 *\return int Number of chars in the prefix that should be skipped
2983 * for a "clean" subject line. If no prefix was found, 0
2986 int subject_get_prefix_length(const gchar
*subject
)
2988 /*!< Array with allowable reply prefixes regexps. */
2989 static const gchar
* const prefixes
[] = {
2990 "Re\\:", /* "Re:" */
2991 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
2992 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
2993 "Aw\\:", /* "Aw:" (German) */
2994 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
2995 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
2996 "Fw\\:", /* "Fw:" Forward */
2997 "Fwd\\:", /* "Fwd:" Forward */
2998 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
2999 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3000 "Rif\\:", /* "Rif:" (Italian Outlook) */
3001 "Sv\\:", /* "Sv" (Norwegian) */
3002 "Vs\\:", /* "Vs" (Norwegian) */
3003 "Ad\\:", /* "Ad" (Norwegian) */
3004 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3005 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3006 "Re \\:", /* "Re :" (French Yahoo Mail) */
3009 const int PREFIXES
= sizeof prefixes
/ sizeof prefixes
[0];
3013 if (!subject
) return 0;
3014 if (!*subject
) return 0;
3017 GString
*s
= g_string_new("");
3019 for (n
= 0; n
< PREFIXES
; n
++)
3020 /* Terminate each prefix regexpression by a
3021 * "\ ?" (zero or ONE space), and OR them */
3022 g_string_append_printf(s
, "(%s\\ ?)%s",
3027 g_string_prepend(s
, "(");
3028 g_string_append(s
, ")+"); /* match at least once */
3029 g_string_prepend(s
, "^\\ *"); /* from beginning of line */
3032 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3033 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3034 if (regcomp(&u_regex
, s
->str
, REG_EXTENDED
| REG_ICASE
)) {
3035 debug_print("Error compiling regexp %s\n", s
->str
);
3036 g_string_free(s
, TRUE
);
3040 g_string_free(s
, TRUE
);
3044 if (!regexec(&u_regex
, subject
, 1, &pos
, 0) && pos
.rm_so
!= -1)
3050 static guint
g_stricase_hash(gconstpointer gptr
)
3052 guint hash_result
= 0;
3055 for (str
= gptr
; str
&& *str
; str
++) {
3056 hash_result
+= toupper(*str
);
3062 static gint
g_stricase_equal(gconstpointer gptr1
, gconstpointer gptr2
)
3064 const char *str1
= gptr1
;
3065 const char *str2
= gptr2
;
3067 return !strcasecmp(str1
, str2
);
3070 gint
g_int_compare(gconstpointer a
, gconstpointer b
)
3072 return GPOINTER_TO_INT(a
) - GPOINTER_TO_INT(b
);
3076 quote_cmd_argument()
3078 return a quoted string safely usable in argument of a command.
3080 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3083 gint
quote_cmd_argument(gchar
* result
, guint size
,
3093 for(p
= path
; * p
!= '\0' ; p
++) {
3095 if (isalnum((guchar
)*p
) || (* p
== '/')) {
3096 if (remaining
> 0) {
3102 result
[size
- 1] = '\0';
3107 if (remaining
>= 2) {
3115 result
[size
- 1] = '\0';
3120 if (remaining
> 0) {
3124 result
[size
- 1] = '\0';
3138 static void g_node_map_recursive(GNode
*node
, gpointer data
)
3140 GNodeMapData
*mapdata
= (GNodeMapData
*) data
;
3142 GNodeMapData newmapdata
;
3145 newdata
= mapdata
->func(node
->data
, mapdata
->data
);
3146 if (newdata
!= NULL
) {
3147 newnode
= g_node_new(newdata
);
3148 g_node_append(mapdata
->parent
, newnode
);
3150 newmapdata
.parent
= newnode
;
3151 newmapdata
.func
= mapdata
->func
;
3152 newmapdata
.data
= mapdata
->data
;
3154 g_node_children_foreach(node
, G_TRAVERSE_ALL
, g_node_map_recursive
, &newmapdata
);
3158 GNode
*g_node_map(GNode
*node
, GNodeMapFunc func
, gpointer data
)
3161 GNodeMapData mapdata
;
3163 cm_return_val_if_fail(node
!= NULL
, NULL
);
3164 cm_return_val_if_fail(func
!= NULL
, NULL
);
3166 root
= g_node_new(func(node
->data
, data
));
3168 mapdata
.parent
= root
;
3169 mapdata
.func
= func
;
3170 mapdata
.data
= data
;
3172 g_node_children_foreach(node
, G_TRAVERSE_ALL
, g_node_map_recursive
, &mapdata
);
3177 #define HEX_TO_INT(val, hex) \
3181 if ('0' <= c && c <= '9') { \
3183 } else if ('a' <= c && c <= 'f') { \
3184 val = c - 'a' + 10; \
3185 } else if ('A' <= c && c <= 'F') { \
3186 val = c - 'A' + 10; \
3192 gboolean
get_hex_value(guchar
*out
, gchar c1
, gchar c2
)
3199 if (hi
== -1 || lo
== -1)
3202 *out
= (hi
<< 4) + lo
;
3206 #define INT_TO_HEX(hex, val) \
3209 hex = '0' + (val); \
3211 hex = 'A' + (val) - 10; \
3214 void get_hex_str(gchar
*out
, guchar ch
)
3218 INT_TO_HEX(hex
, ch
>> 4);
3220 INT_TO_HEX(hex
, ch
& 0x0f);
3226 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3228 #define G_PRINT_REF g_print
3232 *\brief Register ref counted pointer. It is based on GBoxed, so should
3233 * work with anything that uses the GType system. The semantics
3234 * are similar to a C++ auto pointer, with the exception that
3235 * C doesn't have automatic closure (calling destructors) when
3236 * exiting a block scope.
3237 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3238 * function directly.
3240 *\return GType A GType type.
3242 GType
g_auto_pointer_register(void)
3244 static GType auto_pointer_type
;
3245 if (!auto_pointer_type
)
3247 g_boxed_type_register_static
3248 ("G_TYPE_AUTO_POINTER",
3249 (GBoxedCopyFunc
) g_auto_pointer_copy
,
3250 (GBoxedFreeFunc
) g_auto_pointer_free
);
3251 return auto_pointer_type
;
3255 *\brief Structure with g_new() allocated pointer guarded by the
3258 typedef struct AutoPointerRef
{
3259 void (*free
) (gpointer
);
3265 *\brief The auto pointer opaque structure that references the
3266 * pointer guard block.
3268 typedef struct AutoPointer
{
3269 AutoPointerRef
*ref
;
3270 gpointer ptr
; /*!< access to protected pointer */
3274 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3278 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3279 * ... when assigning, copying and freeing storage elements
3281 * gtk_list_store_new(N_S_COLUMNS,
3282 * G_TYPE_AUTO_POINTER,
3286 * Template *precious_data = g_new0(Template, 1);
3287 * g_pointer protect = g_auto_pointer_new(precious_data);
3289 * gtk_list_store_set(container, &iter,
3293 * ... the gtk_list_store has copied the pointer and
3294 * ... incremented its reference count, we should free
3295 * ... the auto pointer (in C++ a destructor would do
3296 * ... this for us when leaving block scope)
3298 * g_auto_pointer_free(protect);
3300 * ... gtk_list_store_set() now manages the data. When
3301 * ... *explicitly* requesting a pointer from the list
3302 * ... store, don't forget you get a copy that should be
3303 * ... freed with g_auto_pointer_free() eventually.
3307 *\param pointer Pointer to be guarded.
3309 *\return GAuto * Pointer that should be used in containers with
3312 GAuto
*g_auto_pointer_new(gpointer p
)
3314 AutoPointerRef
*ref
;
3320 ref
= g_new0(AutoPointerRef
, 1);
3321 ptr
= g_new0(AutoPointer
, 1);
3331 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p
);
3337 *\brief Allocate an autopointer using the passed \a free function to
3338 * free the guarded pointer
3340 GAuto
*g_auto_pointer_new_with_free(gpointer p
, GFreeFunc free_
)
3347 aptr
= g_auto_pointer_new(p
);
3348 aptr
->ref
->free
= free_
;
3352 gpointer
g_auto_pointer_get_ptr(GAuto
*auto_ptr
)
3354 if (auto_ptr
== NULL
)
3356 return ((AutoPointer
*) auto_ptr
)->ptr
;
3360 *\brief Copies an auto pointer by. It's mostly not necessary
3361 * to call this function directly, unless you copy/assign
3362 * the guarded pointer.
3364 *\param auto_ptr Auto pointer returned by previous call to
3365 * g_auto_pointer_new_XXX()
3367 *\return gpointer An auto pointer
3369 GAuto
*g_auto_pointer_copy(GAuto
*auto_ptr
)
3372 AutoPointerRef
*ref
;
3375 if (auto_ptr
== NULL
)
3380 newp
= g_new0(AutoPointer
, 1);
3383 newp
->ptr
= ref
->pointer
;
3387 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref
->pointer
, ref
->cnt
);
3393 *\brief Free an auto pointer
3395 void g_auto_pointer_free(GAuto
*auto_ptr
)
3398 AutoPointerRef
*ref
;
3400 if (auto_ptr
== NULL
)
3406 if (--(ref
->cnt
) == 0) {
3408 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref
->pointer
, ref
->cnt
);
3410 ref
->free(ref
->pointer
);
3415 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref
->pointer
, ref
->cnt
);
3420 void replace_returns(gchar
*str
)
3425 while (strstr(str
, "\n")) {
3426 *strstr(str
, "\n") = ' ';
3428 while (strstr(str
, "\r")) {
3429 *strstr(str
, "\r") = ' ';
3433 /* get_uri_part() - retrieves a URI starting from scanpos.
3434 Returns TRUE if successful */
3435 gboolean
get_uri_part(const gchar
*start
, const gchar
*scanpos
,
3436 const gchar
**bp
, const gchar
**ep
, gboolean hdr
)
3439 gint parenthese_cnt
= 0;
3441 cm_return_val_if_fail(start
!= NULL
, FALSE
);
3442 cm_return_val_if_fail(scanpos
!= NULL
, FALSE
);
3443 cm_return_val_if_fail(bp
!= NULL
, FALSE
);
3444 cm_return_val_if_fail(ep
!= NULL
, FALSE
);
3448 /* find end point of URI */
3449 for (ep_
= scanpos
; *ep_
!= '\0'; ep_
= g_utf8_next_char(ep_
)) {
3450 gunichar u
= g_utf8_get_char_validated(ep_
, -1);
3451 if (!g_unichar_isgraph(u
) ||
3452 u
== (gunichar
)-1 ||
3453 strchr("[]{}<>\"", *ep_
)) {
3455 } else if (strchr("(", *ep_
)) {
3457 } else if (strchr(")", *ep_
)) {
3458 if (parenthese_cnt
> 0)
3465 /* no punctuation at end of string */
3467 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
3468 * should pass some URI type to this function and decide on that whether
3469 * to perform punctuation stripping */
3471 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_~)", ch))
3473 for (; ep_
- 1 > scanpos
+ 1 &&
3474 IS_REAL_PUNCT(*(ep_
- 1));
3478 #undef IS_REAL_PUNCT
3485 gchar
*make_uri_string(const gchar
*bp
, const gchar
*ep
)
3487 while (bp
&& *bp
&& g_ascii_isspace(*bp
))
3489 return g_strndup(bp
, ep
- bp
);
3492 /* valid mail address characters */
3493 #define IS_RFC822_CHAR(ch) \
3497 !g_ascii_isspace(ch) && \
3498 !strchr("(),;<>\"", (ch)))
3500 /* alphabet and number within 7bit ASCII */
3501 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
3502 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
3504 static GHashTable
*create_domain_tab(void)
3507 GHashTable
*htab
= g_hash_table_new(g_stricase_hash
, g_stricase_equal
);
3509 cm_return_val_if_fail(htab
, NULL
);
3510 for (n
= 0; n
< sizeof toplvl_domains
/ sizeof toplvl_domains
[0]; n
++)
3511 g_hash_table_insert(htab
, (gpointer
) toplvl_domains
[n
], (gpointer
) toplvl_domains
[n
]);
3515 static gboolean
is_toplvl_domain(GHashTable
*tab
, const gchar
*first
, const gchar
*last
)
3517 gchar buf
[BUFFSIZE
+ 1];
3518 const gchar
*m
= buf
+ BUFFSIZE
+ 1;
3521 if (last
- first
> BUFFSIZE
|| first
> last
)
3524 for (p
= buf
; p
< m
&& first
< last
; *p
++ = *first
++)
3528 return g_hash_table_lookup(tab
, buf
) != NULL
;
3531 /* get_email_part() - retrieves an email address. Returns TRUE if successful */
3532 gboolean
get_email_part(const gchar
*start
, const gchar
*scanpos
,
3533 const gchar
**bp
, const gchar
**ep
, gboolean hdr
)
3535 /* more complex than the uri part because we need to scan back and forward starting from
3536 * the scan position. */
3537 gboolean result
= FALSE
;
3538 const gchar
*bp_
= NULL
;
3539 const gchar
*ep_
= NULL
;
3540 static GHashTable
*dom_tab
;
3541 const gchar
*last_dot
= NULL
;
3542 const gchar
*prelast_dot
= NULL
;
3543 const gchar
*last_tld_char
= NULL
;
3545 /* the informative part of the email address (describing the name
3546 * of the email address owner) may contain quoted parts. the
3547 * closure stack stores the last encountered quotes. */
3548 gchar closure_stack
[128];
3549 gchar
*ptr
= closure_stack
;
3551 cm_return_val_if_fail(start
!= NULL
, FALSE
);
3552 cm_return_val_if_fail(scanpos
!= NULL
, FALSE
);
3553 cm_return_val_if_fail(bp
!= NULL
, FALSE
);
3554 cm_return_val_if_fail(ep
!= NULL
, FALSE
);
3557 const gchar
*start_quote
= NULL
;
3558 const gchar
*end_quote
= NULL
;
3560 /* go to the real start */
3561 if (start
[0] == ',')
3563 if (start
[0] == ';')
3565 while (start
[0] == '\n' || start
[0] == '\r')
3567 while (start
[0] == ' ' || start
[0] == '\t')
3572 /* check if there are quotes (to skip , in them) */
3573 if (*start
== '"') {
3574 start_quote
= start
;
3576 end_quote
= strstr(start
, "\"");
3582 /* skip anything between quotes */
3583 if (start_quote
&& end_quote
) {
3588 /* find end (either , or ; or end of line) */
3589 if (strstr(start
, ",") && strstr(start
, ";"))
3590 *ep
= strstr(start
,",") < strstr(start
, ";")
3591 ? strstr(start
, ",") : strstr(start
, ";");
3592 else if (strstr(start
, ","))
3593 *ep
= strstr(start
, ",");
3594 else if (strstr(start
, ";"))
3595 *ep
= strstr(start
, ";");
3597 *ep
= start
+strlen(start
);
3599 /* go back to real start */
3600 if (start_quote
&& end_quote
) {
3601 start
= start_quote
;
3604 /* check there's still an @ in that, or search
3605 * further if possible */
3606 if (strstr(start
, "@") && strstr(start
, "@") < *ep
)
3608 else if (*ep
< start
+strlen(start
)) {
3611 } else if (start_quote
&& strstr(start
, "\"") && strstr(start
, "\"") < *ep
) {
3619 dom_tab
= create_domain_tab();
3620 cm_return_val_if_fail(dom_tab
, FALSE
);
3622 /* scan start of address */
3623 for (bp_
= scanpos
- 1;
3624 bp_
>= start
&& IS_RFC822_CHAR(*(const guchar
*)bp_
); bp_
--)
3627 /* TODO: should start with an alnum? */
3629 for (; bp_
< scanpos
&& !IS_ASCII_ALNUM(*(const guchar
*)bp_
); bp_
++)
3632 if (bp_
!= scanpos
) {
3633 /* scan end of address */
3634 for (ep_
= scanpos
+ 1;
3635 *ep_
&& IS_RFC822_CHAR(*(const guchar
*)ep_
); ep_
++)
3637 prelast_dot
= last_dot
;
3639 if (*(last_dot
+ 1) == '.') {
3640 if (prelast_dot
== NULL
)
3642 last_dot
= prelast_dot
;
3647 /* TODO: really should terminate with an alnum? */
3648 for (; ep_
> scanpos
&& !IS_ASCII_ALNUM(*(const guchar
*)ep_
);
3653 if (last_dot
== NULL
)
3655 if (last_dot
>= ep_
)
3656 last_dot
= prelast_dot
;
3657 if (last_dot
== NULL
|| (scanpos
+ 1 >= last_dot
))
3661 for (last_tld_char
= last_dot
; last_tld_char
< ep_
; last_tld_char
++)
3662 if (*last_tld_char
== '?')
3665 if (is_toplvl_domain(dom_tab
, last_dot
, last_tld_char
))
3672 if (!result
) return FALSE
;
3674 if (*ep_
&& bp_
!= start
&& *(bp_
- 1) == '"' && *(ep_
) == '"'
3675 && *(ep_
+ 1) == ' ' && *(ep_
+ 2) == '<'
3676 && IS_RFC822_CHAR(*(ep_
+ 3))) {
3677 /* this informative part with an @ in it is
3678 * followed by the email address */
3681 /* go to matching '>' (or next non-rfc822 char, like \n) */
3682 for (; *ep_
!= '>' && *ep_
!= '\0' && IS_RFC822_CHAR(*ep_
); ep_
++)
3685 /* include the bracket */
3686 if (*ep_
== '>') ep_
++;
3688 /* include the leading quote */
3696 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
3697 if (bp_
- 1 > start
&& IS_QUOTE(*(bp_
- 1)) && IS_QUOTE(*ep_
))
3700 /* see if this is <bracketed>; in this case we also scan for the informative part. */
3701 if (bp_
- 1 <= start
|| *(bp_
- 1) != '<' || *ep_
!= '>')
3704 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
3705 #define IN_STACK() (ptr > closure_stack)
3706 /* has underrun check */
3707 #define POP_STACK() if(IN_STACK()) --ptr
3708 /* has overrun check */
3709 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
3710 /* has underrun check */
3711 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
3715 /* scan for the informative part. */
3716 for (bp_
-= 2; bp_
>= start
; bp_
--) {
3717 /* if closure on the stack keep scanning */
3718 if (PEEK_STACK() == *bp_
) {
3722 if (!IN_STACK() && (*bp_
== '\'' || *bp_
== '"')) {
3727 /* if nothing in the closure stack, do the special conditions
3728 * the following if..else expression simply checks whether
3729 * a token is acceptable. if not acceptable, the clause
3730 * should terminate the loop with a 'break' */
3731 if (!PEEK_STACK()) {
3733 && (((bp_
- 1) >= start
) && isalnum(*(bp_
- 1)))
3734 && (((bp_
+ 1) < ep_
) && isalnum(*(bp_
+ 1)))) {
3735 /* hyphens are allowed, but only in
3737 } else if (strchr(" \"'", *bp_
)) {
3738 /* but anything not being a punctiation
3741 break; /* anything else is rejected */
3748 /* scan forward (should start with an alnum) */
3749 for (; *bp_
!= '<' && isspace(*bp_
) && *bp_
!= '"'; bp_
++)
3765 #undef IS_ASCII_ALNUM
3766 #undef IS_RFC822_CHAR
3768 gchar
*make_email_string(const gchar
*bp
, const gchar
*ep
)
3770 /* returns a mailto: URI; mailto: is also used to detect the
3771 * uri type later on in the button_pressed signal handler */
3776 tmp
= g_strndup(bp
, ep
- bp
);
3778 /* If there is a colon in the username part of the address,
3779 * we're dealing with an URI for some other protocol - do
3780 * not prefix with mailto: in such case. */
3781 colon
= strchr(tmp
, ':');
3782 at
= strchr(tmp
, '@');
3783 if (colon
!= NULL
&& at
!= NULL
&& colon
< at
) {
3786 result
= g_strconcat("mailto:", tmp
, NULL
);
3793 gchar
*make_http_string(const gchar
*bp
, const gchar
*ep
)
3795 /* returns an http: URI; */
3799 while (bp
&& *bp
&& g_ascii_isspace(*bp
))
3801 tmp
= g_strndup(bp
, ep
- bp
);
3802 result
= g_strconcat("http://", tmp
, NULL
);
3808 static gchar
*mailcap_get_command_in_file(const gchar
*path
, const gchar
*type
, const gchar
*file_to_open
)
3810 FILE *fp
= claws_fopen(path
, "rb");
3811 gchar buf
[BUFFSIZE
];
3812 gchar
*result
= NULL
;
3815 while (claws_fgets(buf
, sizeof (buf
), fp
) != NULL
) {
3816 gchar
**parts
= g_strsplit(buf
, ";", 3);
3817 gchar
*trimmed
= parts
[0];
3818 while (trimmed
[0] == ' ' || trimmed
[0] == '\t')
3820 while (trimmed
[strlen(trimmed
)-1] == ' ' || trimmed
[strlen(trimmed
)-1] == '\t')
3821 trimmed
[strlen(trimmed
)-1] = '\0';
3823 if (!strcmp(trimmed
, type
)) {
3824 gboolean needsterminal
= FALSE
;
3825 if (parts
[2] && strstr(parts
[2], "needsterminal")) {
3826 needsterminal
= TRUE
;
3828 if (parts
[2] && strstr(parts
[2], "test=")) {
3829 gchar
*orig_testcmd
= g_strdup(strstr(parts
[2], "test=")+5);
3830 gchar
*testcmd
= orig_testcmd
;
3831 if (strstr(testcmd
,";"))
3832 *(strstr(testcmd
,";")) = '\0';
3833 while (testcmd
[0] == ' ' || testcmd
[0] == '\t')
3835 while (testcmd
[strlen(testcmd
)-1] == '\n')
3836 testcmd
[strlen(testcmd
)-1] = '\0';
3837 while (testcmd
[strlen(testcmd
)-1] == '\r')
3838 testcmd
[strlen(testcmd
)-1] = '\0';
3839 while (testcmd
[strlen(testcmd
)-1] == ' ' || testcmd
[strlen(testcmd
)-1] == '\t')
3840 testcmd
[strlen(testcmd
)-1] = '\0';
3842 if (strstr(testcmd
, "%s")) {
3843 gchar
*tmp
= g_strdup_printf(testcmd
, file_to_open
);
3844 gint res
= system(tmp
);
3846 g_free(orig_testcmd
);
3853 gint res
= system(testcmd
);
3854 g_free(orig_testcmd
);
3864 while (trimmed
[0] == ' ' || trimmed
[0] == '\t')
3866 while (trimmed
[strlen(trimmed
)-1] == '\n')
3867 trimmed
[strlen(trimmed
)-1] = '\0';
3868 while (trimmed
[strlen(trimmed
)-1] == '\r')
3869 trimmed
[strlen(trimmed
)-1] = '\0';
3870 while (trimmed
[strlen(trimmed
)-1] == ' ' || trimmed
[strlen(trimmed
)-1] == '\t')
3871 trimmed
[strlen(trimmed
)-1] = '\0';
3872 result
= g_strdup(trimmed
);
3875 if (needsterminal
) {
3876 gchar
*tmp
= g_strdup_printf("xterm -e %s", result
);
3887 gchar
*mailcap_get_command_for_type(const gchar
*type
, const gchar
*file_to_open
)
3889 gchar
*result
= NULL
;
3893 path
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
, ".mailcap", NULL
);
3894 result
= mailcap_get_command_in_file(path
, type
, file_to_open
);
3898 result
= mailcap_get_command_in_file("/etc/mailcap", type
, file_to_open
);
3902 void mailcap_update_default(const gchar
*type
, const gchar
*command
)
3904 gchar
*path
= NULL
, *outpath
= NULL
;
3905 path
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
, ".mailcap", NULL
);
3906 outpath
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
, ".mailcap.new", NULL
);
3907 FILE *fp
= claws_fopen(path
, "rb");
3909 gchar buf
[BUFFSIZE
];
3910 gboolean err
= FALSE
;
3913 fp
= claws_fopen(path
, "a");
3915 g_warning("failed to create file %s", path
);
3920 fp
= g_freopen(path
, "rb", fp
);
3922 g_warning("failed to reopen file %s", path
);
3929 outfp
= claws_fopen(outpath
, "wb");
3931 g_warning("failed to create file %s", outpath
);
3937 while (fp
&& claws_fgets(buf
, sizeof (buf
), fp
) != NULL
) {
3938 gchar
**parts
= g_strsplit(buf
, ";", 3);
3939 gchar
*trimmed
= parts
[0];
3940 while (trimmed
[0] == ' ')
3942 while (trimmed
[strlen(trimmed
)-1] == ' ')
3943 trimmed
[strlen(trimmed
)-1] = '\0';
3945 if (!strcmp(trimmed
, type
)) {
3950 if(claws_fputs(buf
, outfp
) == EOF
) {
3957 if (fprintf(outfp
, "%s; %s\n", type
, command
) < 0)
3963 if (claws_safe_fclose(outfp
) == EOF
)
3967 g_rename(outpath
, path
);
3973 /* crude test to see if a file is an email. */
3974 gboolean
file_is_email (const gchar
*filename
)
3980 if (filename
== NULL
)
3982 if ((fp
= claws_fopen(filename
, "rb")) == NULL
)
3984 while (i
< 60 && score
< 3
3985 && claws_fgets(buffer
, sizeof (buffer
), fp
) != NULL
) {
3986 if (!strncmp(buffer
, "From:", strlen("From:")))
3988 else if (!strncmp(buffer
, "Date:", strlen("Date:")))
3990 else if (!strncmp(buffer
, "Message-ID:", strlen("Message-ID:")))
3992 else if (!strncmp(buffer
, "Subject:", strlen("Subject:")))
3997 return (score
>= 3);
4000 gboolean
sc_g_list_bigger(GList
*list
, gint max
)
4004 while (cur
&& i
<= max
+1) {
4011 gboolean
sc_g_slist_bigger(GSList
*list
, gint max
)
4015 while (cur
&& i
<= max
+1) {
4022 const gchar
*daynames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4023 const gchar
*monthnames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
4024 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4025 const gchar
*s_daynames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4026 const gchar
*s_monthnames
[] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
4027 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
4029 gint daynames_len
[] = {0,0,0,0,0,0,0};
4030 gint monthnames_len
[] = {0,0,0,0,0,0,
4032 gint s_daynames_len
[] = {0,0,0,0,0,0,0};
4033 gint s_monthnames_len
[] = {0,0,0,0,0,0,
4035 const gchar
*s_am_up
= NULL
;
4036 const gchar
*s_pm_up
= NULL
;
4037 const gchar
*s_am_low
= NULL
;
4038 const gchar
*s_pm_low
= NULL
;
4040 gint s_am_up_len
= 0;
4041 gint s_pm_up_len
= 0;
4042 gint s_am_low_len
= 0;
4043 gint s_pm_low_len
= 0;
4045 static gboolean time_names_init_done
= FALSE
;
4047 static void init_time_names(void)
4051 daynames
[0] = C_("Complete day name for use by strftime", "Sunday");
4052 daynames
[1] = C_("Complete day name for use by strftime", "Monday");
4053 daynames
[2] = C_("Complete day name for use by strftime", "Tuesday");
4054 daynames
[3] = C_("Complete day name for use by strftime", "Wednesday");
4055 daynames
[4] = C_("Complete day name for use by strftime", "Thursday");
4056 daynames
[5] = C_("Complete day name for use by strftime", "Friday");
4057 daynames
[6] = C_("Complete day name for use by strftime", "Saturday");
4059 monthnames
[0] = C_("Complete month name for use by strftime", "January");
4060 monthnames
[1] = C_("Complete month name for use by strftime", "February");
4061 monthnames
[2] = C_("Complete month name for use by strftime", "March");
4062 monthnames
[3] = C_("Complete month name for use by strftime", "April");
4063 monthnames
[4] = C_("Complete month name for use by strftime", "May");
4064 monthnames
[5] = C_("Complete month name for use by strftime", "June");
4065 monthnames
[6] = C_("Complete month name for use by strftime", "July");
4066 monthnames
[7] = C_("Complete month name for use by strftime", "August");
4067 monthnames
[8] = C_("Complete month name for use by strftime", "September");
4068 monthnames
[9] = C_("Complete month name for use by strftime", "October");
4069 monthnames
[10] = C_("Complete month name for use by strftime", "November");
4070 monthnames
[11] = C_("Complete month name for use by strftime", "December");
4072 s_daynames
[0] = C_("Abbr. day name for use by strftime", "Sun");
4073 s_daynames
[1] = C_("Abbr. day name for use by strftime", "Mon");
4074 s_daynames
[2] = C_("Abbr. day name for use by strftime", "Tue");
4075 s_daynames
[3] = C_("Abbr. day name for use by strftime", "Wed");
4076 s_daynames
[4] = C_("Abbr. day name for use by strftime", "Thu");
4077 s_daynames
[5] = C_("Abbr. day name for use by strftime", "Fri");
4078 s_daynames
[6] = C_("Abbr. day name for use by strftime", "Sat");
4080 s_monthnames
[0] = C_("Abbr. month name for use by strftime", "Jan");
4081 s_monthnames
[1] = C_("Abbr. month name for use by strftime", "Feb");
4082 s_monthnames
[2] = C_("Abbr. month name for use by strftime", "Mar");
4083 s_monthnames
[3] = C_("Abbr. month name for use by strftime", "Apr");
4084 s_monthnames
[4] = C_("Abbr. month name for use by strftime", "May");
4085 s_monthnames
[5] = C_("Abbr. month name for use by strftime", "Jun");
4086 s_monthnames
[6] = C_("Abbr. month name for use by strftime", "Jul");
4087 s_monthnames
[7] = C_("Abbr. month name for use by strftime", "Aug");
4088 s_monthnames
[8] = C_("Abbr. month name for use by strftime", "Sep");
4089 s_monthnames
[9] = C_("Abbr. month name for use by strftime", "Oct");
4090 s_monthnames
[10] = C_("Abbr. month name for use by strftime", "Nov");
4091 s_monthnames
[11] = C_("Abbr. month name for use by strftime", "Dec");
4093 for (i
= 0; i
< 7; i
++) {
4094 daynames_len
[i
] = strlen(daynames
[i
]);
4095 s_daynames_len
[i
] = strlen(s_daynames
[i
]);
4097 for (i
= 0; i
< 12; i
++) {
4098 monthnames_len
[i
] = strlen(monthnames
[i
]);
4099 s_monthnames_len
[i
] = strlen(s_monthnames
[i
]);
4102 s_am_up
= C_("For use by strftime (morning)", "AM");
4103 s_pm_up
= C_("For use by strftime (afternoon)", "PM");
4104 s_am_low
= C_("For use by strftime (morning, lowercase)", "am");
4105 s_pm_low
= C_("For use by strftime (afternoon, lowercase)", "pm");
4107 s_am_up_len
= strlen(s_am_up
);
4108 s_pm_up_len
= strlen(s_pm_up
);
4109 s_am_low_len
= strlen(s_am_low
);
4110 s_pm_low_len
= strlen(s_pm_low
);
4112 time_names_init_done
= TRUE
;
4115 #define CHECK_SIZE() { \
4116 total_done += len; \
4117 if (total_done >= buflen) { \
4118 buf[buflen-1] = '\0'; \
4123 size_t fast_strftime(gchar
*buf
, gint buflen
, const gchar
*format
, struct tm
*lt
)
4125 gchar
*curpos
= buf
;
4126 gint total_done
= 0;
4127 gchar subbuf
[64], subfmt
[64];
4128 static time_t last_tzset
= (time_t)0;
4130 if (!time_names_init_done
)
4133 if (format
== NULL
|| lt
== NULL
)
4136 if (last_tzset
!= time(NULL
)) {
4138 last_tzset
= time(NULL
);
4141 if (*format
== '%') {
4142 gint len
= 0, tmp
= 0;
4146 len
= 1; CHECK_SIZE();
4150 len
= s_daynames_len
[lt
->tm_wday
]; CHECK_SIZE();
4151 strncpy2(curpos
, s_daynames
[lt
->tm_wday
], buflen
- total_done
);
4154 len
= daynames_len
[lt
->tm_wday
]; CHECK_SIZE();
4155 strncpy2(curpos
, daynames
[lt
->tm_wday
], buflen
- total_done
);
4159 len
= s_monthnames_len
[lt
->tm_mon
]; CHECK_SIZE();
4160 strncpy2(curpos
, s_monthnames
[lt
->tm_mon
], buflen
- total_done
);
4163 len
= monthnames_len
[lt
->tm_mon
]; CHECK_SIZE();
4164 strncpy2(curpos
, monthnames
[lt
->tm_mon
], buflen
- total_done
);
4167 strftime(subbuf
, 64, "%c", lt
);
4168 len
= strlen(subbuf
); CHECK_SIZE();
4169 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4172 total_done
+= 2; CHECK_SIZE();
4173 tmp
= (lt
->tm_year
+ 1900)/100;
4174 *curpos
++ = '0'+(tmp
/ 10);
4175 *curpos
++ = '0'+(tmp
% 10);
4178 total_done
+= 2; CHECK_SIZE();
4179 *curpos
++ = '0'+(lt
->tm_mday
/ 10);
4180 *curpos
++ = '0'+(lt
->tm_mday
% 10);
4183 total_done
+= 8; CHECK_SIZE();
4184 *curpos
++ = '0'+((lt
->tm_mon
+1) / 10);
4185 *curpos
++ = '0'+((lt
->tm_mon
+1) % 10);
4187 *curpos
++ = '0'+(lt
->tm_mday
/ 10);
4188 *curpos
++ = '0'+(lt
->tm_mday
% 10);
4190 tmp
= lt
->tm_year
%100;
4191 *curpos
++ = '0'+(tmp
/ 10);
4192 *curpos
++ = '0'+(tmp
% 10);
4195 len
= 2; CHECK_SIZE();
4196 snprintf(curpos
, buflen
- total_done
, "%2d", lt
->tm_mday
);
4199 len
= 10; CHECK_SIZE();
4200 snprintf(curpos
, buflen
- total_done
, "%4d-%02d-%02d",
4201 lt
->tm_year
+ 1900, lt
->tm_mon
+1, lt
->tm_mday
);
4204 total_done
+= 2; CHECK_SIZE();
4205 *curpos
++ = '0'+(lt
->tm_hour
/ 10);
4206 *curpos
++ = '0'+(lt
->tm_hour
% 10);
4209 total_done
+= 2; CHECK_SIZE();
4215 *curpos
++ = '0'+(tmp
/ 10);
4216 *curpos
++ = '0'+(tmp
% 10);
4219 len
= 3; CHECK_SIZE();
4220 snprintf(curpos
, buflen
- total_done
, "%03d", lt
->tm_yday
+1);
4223 len
= 2; CHECK_SIZE();
4224 snprintf(curpos
, buflen
- total_done
, "%2d", lt
->tm_hour
);
4227 len
= 2; CHECK_SIZE();
4233 snprintf(curpos
, buflen
- total_done
, "%2d", tmp
);
4236 total_done
+= 2; CHECK_SIZE();
4237 tmp
= lt
->tm_mon
+ 1;
4238 *curpos
++ = '0'+(tmp
/ 10);
4239 *curpos
++ = '0'+(tmp
% 10);
4242 total_done
+= 2; CHECK_SIZE();
4243 *curpos
++ = '0'+(lt
->tm_min
/ 10);
4244 *curpos
++ = '0'+(lt
->tm_min
% 10);
4247 len
= 1; CHECK_SIZE();
4251 if (lt
->tm_hour
>= 12) {
4252 len
= s_pm_up_len
; CHECK_SIZE();
4253 snprintf(curpos
, buflen
-total_done
, "%s", s_pm_up
);
4255 len
= s_am_up_len
; CHECK_SIZE();
4256 snprintf(curpos
, buflen
-total_done
, "%s", s_am_up
);
4260 if (lt
->tm_hour
>= 12) {
4261 len
= s_pm_low_len
; CHECK_SIZE();
4262 snprintf(curpos
, buflen
-total_done
, "%s", s_pm_low
);
4264 len
= s_am_low_len
; CHECK_SIZE();
4265 snprintf(curpos
, buflen
-total_done
, "%s", s_am_low
);
4270 strftime(subbuf
, 64, "%I:%M:%S %p", lt
);
4272 strftime(subbuf
, 64, "%r", lt
);
4274 len
= strlen(subbuf
); CHECK_SIZE();
4275 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4278 total_done
+= 5; CHECK_SIZE();
4279 *curpos
++ = '0'+(lt
->tm_hour
/ 10);
4280 *curpos
++ = '0'+(lt
->tm_hour
% 10);
4282 *curpos
++ = '0'+(lt
->tm_min
/ 10);
4283 *curpos
++ = '0'+(lt
->tm_min
% 10);
4286 snprintf(subbuf
, 64, "%lld", (long long)mktime(lt
));
4287 len
= strlen(subbuf
); CHECK_SIZE();
4288 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4291 total_done
+= 2; CHECK_SIZE();
4292 *curpos
++ = '0'+(lt
->tm_sec
/ 10);
4293 *curpos
++ = '0'+(lt
->tm_sec
% 10);
4296 len
= 1; CHECK_SIZE();
4300 total_done
+= 8; CHECK_SIZE();
4301 *curpos
++ = '0'+(lt
->tm_hour
/ 10);
4302 *curpos
++ = '0'+(lt
->tm_hour
% 10);
4304 *curpos
++ = '0'+(lt
->tm_min
/ 10);
4305 *curpos
++ = '0'+(lt
->tm_min
% 10);
4307 *curpos
++ = '0'+(lt
->tm_sec
/ 10);
4308 *curpos
++ = '0'+(lt
->tm_sec
% 10);
4311 len
= 1; CHECK_SIZE();
4312 snprintf(curpos
, buflen
- total_done
, "%d", lt
->tm_wday
== 0 ? 7: lt
->tm_wday
);
4315 len
= 1; CHECK_SIZE();
4316 snprintf(curpos
, buflen
- total_done
, "%d", lt
->tm_wday
);
4319 strftime(subbuf
, 64, "%x", lt
);
4320 len
= strlen(subbuf
); CHECK_SIZE();
4321 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4324 strftime(subbuf
, 64, "%X", lt
);
4325 len
= strlen(subbuf
); CHECK_SIZE();
4326 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4329 total_done
+= 2; CHECK_SIZE();
4330 tmp
= lt
->tm_year
%100;
4331 *curpos
++ = '0'+(tmp
/ 10);
4332 *curpos
++ = '0'+(tmp
% 10);
4335 len
= 4; CHECK_SIZE();
4336 snprintf(curpos
, buflen
- total_done
, "%4d", lt
->tm_year
+ 1900);
4346 /* let these complicated ones be done with the libc */
4347 snprintf(subfmt
, 64, "%%%c", *format
);
4348 strftime(subbuf
, 64, subfmt
, lt
);
4349 len
= strlen(subbuf
); CHECK_SIZE();
4350 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4354 /* let these complicated modifiers be done with the libc */
4355 snprintf(subfmt
, 64, "%%%c%c", *format
, *(format
+1));
4356 strftime(subbuf
, 64, subfmt
, lt
);
4357 len
= strlen(subbuf
); CHECK_SIZE();
4358 strncpy2(curpos
, subbuf
, buflen
- total_done
);
4362 g_warning("format error (%c)", *format
);
4369 int len
= 1; CHECK_SIZE();
4370 *curpos
++ = *format
++;
4378 #define WEXITSTATUS(x) (x)
4381 GMutex
*cm_mutex_new(void) {
4382 #if GLIB_CHECK_VERSION(2,32,0)
4383 GMutex
*m
= g_new0(GMutex
, 1);
4387 return g_mutex_new();
4391 void cm_mutex_free(GMutex
*mutex
) {
4392 #if GLIB_CHECK_VERSION(2,32,0)
4393 g_mutex_clear(mutex
);
4396 g_mutex_free(mutex
);
4400 static gchar
*canonical_list_to_file(GSList
*list
)
4402 GString
*result
= g_string_new(NULL
);
4403 GSList
*pathlist
= g_slist_reverse(g_slist_copy(list
));
4408 result
= g_string_append(result
, G_DIR_SEPARATOR_S
);
4410 if (pathlist
->data
) {
4411 const gchar
*root
= (gchar
*)pathlist
->data
;
4412 if (root
[0] != '\0' && g_ascii_isalpha(root
[0]) &&
4414 /* drive - don't prepend dir separator */
4416 result
= g_string_append(result
, G_DIR_SEPARATOR_S
);
4421 for (cur
= pathlist
; cur
; cur
= cur
->next
) {
4422 result
= g_string_append(result
, (gchar
*)cur
->data
);
4424 result
= g_string_append(result
, G_DIR_SEPARATOR_S
);
4426 g_slist_free(pathlist
);
4429 g_string_free(result
, FALSE
);
4434 static GSList
*cm_split_path(const gchar
*filename
, int depth
)
4437 GSList
*canonical_parts
= NULL
;
4441 gboolean follow_symlinks
= TRUE
;
4448 errno
= EINVAL
; /* can't happen, no symlink handling */
4453 if (!g_path_is_absolute(filename
)) {
4458 path_parts
= g_strsplit(filename
, G_DIR_SEPARATOR_S
, -1);
4460 for (i
= 0; path_parts
[i
] != NULL
; i
++) {
4461 if (!strcmp(path_parts
[i
], ""))
4463 if (!strcmp(path_parts
[i
], "."))
4465 else if (!strcmp(path_parts
[i
], "..")) {
4470 else /* Remove the last inserted element */
4472 g_slist_delete_link(canonical_parts
,
4477 canonical_parts
= g_slist_prepend(canonical_parts
,
4478 g_strdup(path_parts
[i
]));
4480 tmp_path
= canonical_list_to_file(canonical_parts
);
4482 if(g_stat(tmp_path
, &st
) < 0) {
4483 if (errno
== ENOENT
) {
4486 follow_symlinks
= FALSE
;
4491 slist_free_strings_full(canonical_parts
);
4492 g_strfreev(path_parts
);
4498 if (follow_symlinks
&& g_file_test(tmp_path
, G_FILE_TEST_IS_SYMLINK
)) {
4499 GError
*error
= NULL
;
4500 gchar
*target
= g_file_read_link(tmp_path
, &error
);
4502 if (!g_path_is_absolute(target
)) {
4503 /* remove the last inserted element */
4505 g_slist_delete_link(canonical_parts
,
4507 /* add the target */
4508 canonical_parts
= g_slist_prepend(canonical_parts
,
4512 /* and get the new target */
4513 target
= canonical_list_to_file(canonical_parts
);
4516 /* restart from absolute target */
4517 slist_free_strings_full(canonical_parts
);
4518 canonical_parts
= NULL
;
4520 canonical_parts
= cm_split_path(target
, depth
+ 1);
4522 g_error_free(error
);
4523 if (canonical_parts
== NULL
) {
4525 g_strfreev(path_parts
);
4534 g_strfreev(path_parts
);
4535 return canonical_parts
;
4539 * Canonicalize a filename, resolving symlinks along the way.
4540 * Returns a negative errno in case of error.
4542 int cm_canonicalize_filename(const gchar
*filename
, gchar
**canonical_name
) {
4543 GSList
*canonical_parts
;
4544 gboolean is_absolute
;
4546 if (filename
== NULL
)
4548 if (canonical_name
== NULL
)
4550 *canonical_name
= NULL
;
4552 is_absolute
= g_path_is_absolute(filename
);
4554 /* Always work on absolute filenames. */
4555 gchar
*cur
= g_get_current_dir();
4556 gchar
*absolute_filename
= g_strconcat(cur
, G_DIR_SEPARATOR_S
,
4559 canonical_parts
= cm_split_path(absolute_filename
, 0);
4560 g_free(absolute_filename
);
4563 canonical_parts
= cm_split_path(filename
, 0);
4565 if (canonical_parts
== NULL
)
4568 *canonical_name
= canonical_list_to_file(canonical_parts
);
4569 slist_free_strings_full(canonical_parts
);
4573 /* Returns a decoded base64 string, guaranteed to be null-terminated. */
4574 guchar
*g_base64_decode_zero(const gchar
*text
, gsize
*out_len
)
4576 gchar
*tmp
= g_base64_decode(text
, out_len
);
4577 gchar
*out
= g_strndup(tmp
, *out_len
);
4581 if (strlen(out
) != *out_len
) {
4582 g_warning ("strlen(out) %"G_GSIZE_FORMAT
" != *out_len %"G_GSIZE_FORMAT
, strlen(out
), *out_len
);
4588 #if !GLIB_CHECK_VERSION(2, 30, 0)
4591 * @str: a UTF-8 encoded string
4592 * @start_pos: a character offset within @str
4593 * @end_pos: another character offset within @str
4595 * Copies a substring out of a UTF-8 encoded string.
4596 * The substring will contain @end_pos - @start_pos
4599 * Returns: a newly allocated copy of the requested
4600 * substring. Free with g_free() when no longer needed.
4605 g_utf8_substring (const gchar
*str
,
4609 gchar
*start
, *end
, *out
;
4611 start
= g_utf8_offset_to_pointer (str
, start_pos
);
4612 end
= g_utf8_offset_to_pointer (start
, end_pos
- start_pos
);
4614 out
= g_malloc (end
- start
+ 1);
4615 memcpy (out
, start
, end
- start
);
4616 out
[end
- start
] = 0;
4622 /* Attempts to read count bytes from a PRNG into memory area starting at buf.
4623 * It is up to the caller to make sure there is at least count bytes
4624 * available at buf. */
4626 get_random_bytes(void *buf
, size_t count
)
4628 /* Open our prng source. */
4629 #if defined G_OS_WIN32
4632 if (!CryptAcquireContext(&rnd
, NULL
, NULL
, PROV_RSA_FULL
, 0) &&
4633 !CryptAcquireContext(&rnd
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
)) {
4634 debug_print("Could not acquire a CSP handle.\n");
4641 rnd
= open("/dev/urandom", O_RDONLY
);
4643 FILE_OP_ERROR("/dev/urandom", "open");
4644 debug_print("Could not open /dev/urandom.\n");
4649 /* Read data from the source into buf. */
4650 #if defined G_OS_WIN32
4651 if (!CryptGenRandom(rnd
, count
, buf
)) {
4652 debug_print("Could not read %"G_GSIZE_FORMAT
" random bytes.\n", count
);
4653 CryptReleaseContext(rnd
, 0);
4657 ret
= read(rnd
, buf
, count
);
4659 FILE_OP_ERROR("/dev/urandom", "read");
4660 debug_print("Could not read enough data from /dev/urandom, read only %ld of %lu bytes.\n", ret
, count
);
4666 /* Close the prng source. */
4667 #if defined G_OS_WIN32
4668 CryptReleaseContext(rnd
, 0);
4676 /* returns FALSE if parsing failed, otherwise returns TRUE and sets *server, *port
4677 and eventually *fp from filename (if not NULL, they must be free'd by caller after
4679 filenames we expect: 'host.name.port.cert' or 'host.name.port.f:i:n:g:e:r:p:r:i:n:t.cert' */
4680 gboolean
get_serverportfp_from_filename(const gchar
*str
, gchar
**server
, gchar
**port
, gchar
**fp
)
4682 const gchar
*pos
, *dotport_pos
= NULL
, *dotcert_pos
= NULL
, *dotfp_pos
= NULL
;
4684 g_return_val_if_fail(str
!= NULL
, FALSE
);
4686 pos
= str
+ strlen(str
) - 1;
4687 while ((pos
> str
) && !dotport_pos
) {
4690 /* match the .cert suffix */
4691 if (strcmp(pos
, ".cert") == 0) {
4696 /* match an eventual fingerprint */
4697 /* or the port number */
4698 if (strncmp(pos
+ 3, ":", 1) == 0) {
4704 /* match the port number */
4711 if (!dotport_pos
|| !dotcert_pos
) {
4712 g_warning("could not parse filename %s", str
);
4717 *server
= g_strndup(str
, dotport_pos
- str
);
4720 *port
= g_strndup(dotport_pos
+ 1, dotfp_pos
- dotport_pos
- 1);
4722 *fp
= g_strndup(dotfp_pos
+ 1, dotcert_pos
- dotfp_pos
- 1);
4725 *port
= g_strndup(dotport_pos
+ 1, dotcert_pos
- dotport_pos
- 1);
4730 debug_print("filename='%s' => server='%s' port='%s' fp='%s'\n",
4732 (server
? *server
: "(n/a)"),
4733 (port
? *port
: "(n/a)"),
4734 (fp
? *fp
: "(n/a)"));
4736 if (!(server
&& *server
) || !(port
&& *port
))
4743 gchar
*win32_debug_log_path(void)
4745 return g_strconcat(g_get_tmp_dir(), G_DIR_SEPARATOR_S
,
4746 "claws-win32.log", NULL
);