2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto & The Claws Mail Team
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/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
67 # include <tablet-browser-interface.h>
69 # include <osso-browser-interface.h>
75 #include "../codeconv.h"
79 static gboolean debug_mode = FALSE;
81 static GSList *tempfiles=NULL;
84 /* Return true if we are running as root. This function should beused
85 instead of getuid () == 0. */
86 gboolean superuser_p (void)
89 return w32_is_administrator ();
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
101 if (G_WIN32_HAVE_WIDECHAR_API()) {
106 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
112 retval = _wchdir(wpath);
124 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125 if (cp_path == NULL) {
130 retval = chdir(cp_path);
143 gint g_chmod(const gchar *path, gint mode)
146 if (G_WIN32_HAVE_WIDECHAR_API()) {
151 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
157 retval = _wchmod(wpath, mode);
169 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170 if (cp_path == NULL) {
175 retval = chmod(cp_path, mode);
184 return chmod(path, mode);
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
191 char *name = g_win32_locale_filename_from_utf8(filename);
192 FILE* fp = fopen(name, mode);
196 return fopen(filename, mode);
199 int g_open(const gchar *filename, int flags, int mode)
202 char *name = g_win32_locale_filename_from_utf8(filename);
203 int fd = open(name, flags, mode);
207 return open(filename, flags, mode);
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
214 gint mkstemp_name(gchar *template, gchar **name_used)
216 static gulong count=0; /* W32-_mktemp only supports up to 27
220 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222 (S_IRUSR | S_IWUSR));
224 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
226 perror(g_strdup_printf("cant create %s",*name_used));
232 #endif /* G_OS_WIN32 */
235 gint mkstemp(gchar *template)
238 gint res = mkstemp_name(template, &dummyname);
242 #endif /* G_OS_WIN32 */
244 void list_free_strings(GList *list)
246 list = g_list_first(list);
248 while (list != NULL) {
254 void slist_free_strings(GSList *list)
256 while (list != NULL) {
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
267 void hash_free_strings(GHashTable *table)
269 g_hash_table_foreach(table, hash_free_strings_func, NULL);
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
274 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
277 guint str_case_hash(gconstpointer key)
279 const gchar *p = key;
283 h = g_ascii_tolower(h);
284 for (p += 1; *p != '\0'; p++)
285 h = (h << 5) - h + g_ascii_tolower(*p);
291 void ptr_array_free_strings(GPtrArray *array)
296 cm_return_if_fail(array != NULL);
298 for (i = 0; i < array->len; i++) {
299 str = g_ptr_array_index(array, i);
304 gboolean str_find(const gchar *haystack, const gchar *needle)
306 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
311 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
314 gint to_number(const gchar *nstr)
316 register const gchar *p;
318 if (*nstr == '\0') return -1;
320 for (p = nstr; *p != '\0'; p++)
321 if (!g_ascii_isdigit(*p)) return -1;
326 /* convert integer into string,
327 nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
330 g_snprintf(nstr, 11, "%d", n);
334 /* convert integer into string */
337 static gchar nstr[11];
339 return itos_buf(nstr, n);
342 #define divide(num,divisor,i,d) \
344 i = num >> divisor; \
345 d = num & ((1<<divisor)-1); \
346 d = (d*100) >> divisor; \
349 gchar *to_human_readable(goffset size)
351 static gchar str[14];
352 static gchar *b_format = NULL, *kb_format = NULL,
353 *mb_format = NULL, *gb_format = NULL;
354 register int t = 0, r = 0;
355 if (b_format == NULL) {
357 kb_format = _("%d.%02dKB");
358 mb_format = _("%d.%02dMB");
359 gb_format = _("%.2fGB");
362 if (size < (goffset)1024) {
363 g_snprintf(str, sizeof(str), b_format, (gint)size);
365 } else if (size >> 10 < (goffset)1024) {
366 divide(size, 10, t, r);
367 g_snprintf(str, sizeof(str), kb_format, t, r);
369 } else if (size >> 20 < (goffset)1024) {
370 divide(size, 20, t, r);
371 g_snprintf(str, sizeof(str), mb_format, t, r);
374 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
379 /* strcmp with NULL-checking */
380 gint strcmp2(const gchar *s1, const gchar *s2)
382 if (s1 == NULL || s2 == NULL)
385 return strcmp(s1, s2);
387 /* strstr with NULL-checking */
388 gchar *strstr2(const gchar *s1, const gchar *s2)
390 if (s1 == NULL || s2 == NULL)
393 return strstr(s1, s2);
396 gint path_cmp(const gchar *s1, const gchar *s2)
401 gchar *s1buf, *s2buf;
404 if (s1 == NULL || s2 == NULL) return -1;
405 if (*s1 == '\0' || *s2 == '\0') return -1;
408 s1buf = g_strdup (s1);
409 s2buf = g_strdup (s2);
410 subst_char (s1buf, '/', G_DIR_SEPARATOR);
411 subst_char (s2buf, '/', G_DIR_SEPARATOR);
414 #endif /* !G_OS_WIN32 */
419 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
420 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
422 rc = strncmp(s1, s2, MAX(len1, len2));
426 #endif /* !G_OS_WIN32 */
430 /* remove trailing return code */
431 gchar *strretchomp(gchar *str)
435 if (!*str) return str;
437 for (s = str + strlen(str) - 1;
438 s >= str && (*s == '\n' || *s == '\r');
445 /* remove trailing character */
446 gchar *strtailchomp(gchar *str, gchar tail_char)
450 if (!*str) return str;
451 if (tail_char == '\0') return str;
453 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
459 /* remove CR (carriage return) */
460 gchar *strcrchomp(gchar *str)
464 if (!*str) return str;
466 s = str + strlen(str) - 1;
467 if (*s == '\n' && s > str && *(s - 1) == '\r') {
475 gint file_strip_crs(const gchar *file)
477 FILE *fp = NULL, *outfp = NULL;
479 gchar *out = get_tmp_file();
483 fp = g_fopen(file, "rb");
487 outfp = g_fopen(out, "wb");
493 while (fgets(buf, sizeof (buf), fp) != NULL) {
495 if (fputs(buf, outfp) == EOF) {
503 if (fclose(outfp) == EOF) {
507 if (rename_force(out, file) < 0)
519 /* Similar to `strstr' but this function ignores the case of both strings. */
520 gchar *strcasestr(const gchar *haystack, const gchar *needle)
522 register size_t haystack_len, needle_len;
524 haystack_len = strlen(haystack);
525 needle_len = strlen(needle);
527 if (haystack_len < needle_len || needle_len == 0)
530 while (haystack_len >= needle_len) {
531 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
532 return (gchar *)haystack;
542 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
543 gconstpointer needle, size_t needlelen)
545 const gchar *haystack_ = (const gchar *)haystack;
546 const gchar *needle_ = (const gchar *)needle;
547 const gchar *haystack_cur = (const gchar *)haystack;
548 size_t haystack_left = haystacklen;
551 return memchr(haystack_, *needle_, haystacklen);
553 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
555 if (haystacklen - (haystack_cur - haystack_) < needlelen)
557 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
558 return (gpointer)haystack_cur;
561 haystack_left = haystacklen - (haystack_cur - haystack_);
568 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
569 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
571 register const gchar *s = src;
572 register gchar *d = dest;
582 /* Examine if next block is non-ASCII string */
583 gboolean is_next_nonascii(const gchar *s)
587 /* skip head space */
588 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
590 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
591 if (*(guchar *)p > 127 || *(guchar *)p < 32)
598 gint get_next_word_len(const gchar *s)
602 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
608 static void trim_subject_for_compare(gchar *str)
612 eliminate_parenthesis(str, '[', ']');
613 eliminate_parenthesis(str, '(', ')');
616 srcp = str + subject_get_prefix_length(str);
618 memmove(str, srcp, strlen(srcp) + 1);
621 static void trim_subject_for_sort(gchar *str)
627 srcp = str + subject_get_prefix_length(str);
629 memmove(str, srcp, strlen(srcp) + 1);
632 /* compare subjects */
633 gint subject_compare(const gchar *s1, const gchar *s2)
637 if (!s1 || !s2) return -1;
638 if (!*s1 || !*s2) return -1;
640 Xstrdup_a(str1, s1, return -1);
641 Xstrdup_a(str2, s2, return -1);
643 trim_subject_for_compare(str1);
644 trim_subject_for_compare(str2);
646 if (!*str1 || !*str2) return -1;
648 return strcmp(str1, str2);
651 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
655 if (!s1 || !s2) return -1;
657 Xstrdup_a(str1, s1, return -1);
658 Xstrdup_a(str2, s2, return -1);
660 trim_subject_for_sort(str1);
661 trim_subject_for_sort(str2);
663 return g_utf8_collate(str1, str2);
666 void trim_subject(gchar *str)
668 register gchar *srcp;
674 srcp = str + subject_get_prefix_length(str);
679 } else if (*srcp == '(') {
691 else if (*srcp == cl)
698 while (g_ascii_isspace(*srcp)) srcp++;
699 memmove(str, srcp, strlen(srcp) + 1);
702 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
704 register gchar *srcp, *destp;
709 while ((destp = strchr(destp, op))) {
715 else if (*srcp == cl)
721 while (g_ascii_isspace(*srcp)) srcp++;
722 memmove(destp, srcp, strlen(srcp) + 1);
726 void extract_parenthesis(gchar *str, gchar op, gchar cl)
728 register gchar *srcp, *destp;
733 while ((srcp = strchr(destp, op))) {
736 memmove(destp, srcp + 1, strlen(srcp));
741 else if (*destp == cl)
753 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
756 register gchar *srcp, *destp;
758 gboolean in_quote = FALSE;
762 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
765 memmove(destp, srcp + 1, strlen(srcp));
768 if (*destp == op && !in_quote)
770 else if (*destp == cl && !in_quote)
772 else if (*destp == quote_chr)
784 void extract_quote(gchar *str, gchar quote_chr)
788 if ((str = strchr(str, quote_chr))) {
790 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
791 memmove(p - 1, p, strlen(p) + 1);
796 memmove(str, str + 1, p - str);
801 void eliminate_address_comment(gchar *str)
803 register gchar *srcp, *destp;
808 while ((destp = strchr(destp, '"'))) {
809 if ((srcp = strchr(destp + 1, '"'))) {
814 while (g_ascii_isspace(*srcp)) srcp++;
815 memmove(destp, srcp, strlen(srcp) + 1);
825 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
831 else if (*srcp == ')')
837 while (g_ascii_isspace(*srcp)) srcp++;
838 memmove(destp, srcp, strlen(srcp) + 1);
842 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
844 gboolean in_quote = FALSE;
847 if (*str == c && !in_quote)
849 if (*str == quote_chr)
857 void extract_address(gchar *str)
859 eliminate_address_comment(str);
860 if (strchr_with_skip_quote(str, '"', '<'))
861 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
865 void extract_list_id_str(gchar *str)
867 if (strchr_with_skip_quote(str, '"', '<'))
868 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
872 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
877 if (!str) return addr_list;
879 Xstrdup_a(work, str, return addr_list);
882 eliminate_address_comment(work);
885 while (workp && *workp) {
888 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
894 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
895 extract_parenthesis_with_skip_quote
896 (workp, '"', '<', '>');
900 addr_list = g_slist_append(addr_list, g_strdup(workp));
908 GSList *address_list_append(GSList *addr_list, const gchar *str)
910 return address_list_append_real(addr_list, str, TRUE);
913 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
915 return address_list_append_real(addr_list, str, FALSE);
918 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
922 if (!str) return msgid_list;
925 while (strp && *strp) {
926 const gchar *start, *end;
929 if ((start = strchr(strp, '<')) != NULL) {
930 end = strchr(start + 1, '>');
935 msgid = g_strndup(start + 1, end - start - 1);
938 msgid_list = g_slist_prepend(msgid_list, msgid);
948 GSList *references_list_append(GSList *msgid_list, const gchar *str)
952 list = references_list_prepend(NULL, str);
953 list = g_slist_reverse(list);
954 msgid_list = g_slist_concat(msgid_list, list);
959 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
964 if (!str) return group_list;
966 Xstrdup_a(work, str, return group_list);
970 while (workp && *workp) {
973 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
981 group_list = g_slist_append(group_list,
990 GList *add_history(GList *list, const gchar *str)
994 cm_return_val_if_fail(str != NULL, list);
996 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
999 list = g_list_remove(list, old->data);
1000 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1003 last = g_list_last(list);
1006 list = g_list_remove(list, last->data);
1010 list = g_list_prepend(list, g_strdup(str));
1015 void remove_return(gchar *str)
1017 register gchar *p = str;
1020 if (*p == '\n' || *p == '\r')
1021 memmove(p, p + 1, strlen(p));
1027 void remove_space(gchar *str)
1029 register gchar *p = str;
1034 while (g_ascii_isspace(*(p + spc)))
1037 memmove(p, p + spc, strlen(p + spc) + 1);
1043 void unfold_line(gchar *str)
1045 register gchar *p = str;
1049 if (*p == '\n' || *p == '\r') {
1052 while (g_ascii_isspace(*(p + spc)))
1055 memmove(p, p + spc, strlen(p + spc) + 1);
1061 void subst_char(gchar *str, gchar orig, gchar subst)
1063 register gchar *p = str;
1072 void subst_chars(gchar *str, gchar *orig, gchar subst)
1074 register gchar *p = str;
1077 if (strchr(orig, *p) != NULL)
1083 void subst_for_filename(gchar *str)
1088 subst_chars(str, "\t\r\n\\/*:", '_');
1090 subst_chars(str, "\t\r\n\\/*", '_');
1094 void subst_for_shellsafe_filename(gchar *str)
1098 subst_for_filename(str);
1099 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1102 gboolean is_ascii_str(const gchar *str)
1104 const guchar *p = (const guchar *)str;
1106 while (*p != '\0') {
1107 if (*p != '\t' && *p != ' ' &&
1108 *p != '\r' && *p != '\n' &&
1109 (*p < 32 || *p >= 127))
1117 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1119 gchar * position = NULL;
1120 gchar * tmp_pos = NULL;
1123 if (quote_chars == NULL)
1126 for (i = 0; i < strlen(quote_chars); i++) {
1127 tmp_pos = strrchr (str, quote_chars[i]);
1129 || (tmp_pos != NULL && position <= tmp_pos) )
1135 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1137 const gchar *first_pos;
1138 const gchar *last_pos;
1139 const gchar *p = str;
1140 gint quote_level = -1;
1142 /* speed up line processing by only searching to the last '>' */
1143 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1144 /* skip a line if it contains a '<' before the initial '>' */
1145 if (memchr(str, '<', first_pos - str) != NULL)
1147 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1151 while (p <= last_pos) {
1152 while (p < last_pos) {
1153 if (g_ascii_isspace(*p))
1159 if (strchr(quote_chars, *p))
1161 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1162 /* any characters are allowed except '-' and space */
1164 && !strchr(quote_chars, *p)
1165 && !g_ascii_isspace(*p)
1168 if (strchr(quote_chars, *p))
1180 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1182 const gchar *p = str, *q;
1183 gint cur_line = 0, len;
1185 while ((q = strchr(p, '\n')) != NULL) {
1187 if (len > max_chars) {
1197 if (len > max_chars) {
1206 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1208 gchar * position = NULL;
1209 gchar * tmp_pos = NULL;
1212 if (quote_chars == NULL)
1215 for (i = 0; i < strlen(quote_chars); i++) {
1216 tmp_pos = strchr (str, quote_chars[i]);
1218 || (tmp_pos != NULL && position >= tmp_pos) )
1224 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1226 register guint haystack_len, needle_len;
1227 gboolean in_squote = FALSE, in_dquote = FALSE;
1229 haystack_len = strlen(haystack);
1230 needle_len = strlen(needle);
1232 if (haystack_len < needle_len || needle_len == 0)
1235 while (haystack_len >= needle_len) {
1236 if (!in_squote && !in_dquote &&
1237 !strncmp(haystack, needle, needle_len))
1238 return (gchar *)haystack;
1240 /* 'foo"bar"' -> foo"bar"
1241 "foo'bar'" -> foo'bar' */
1242 if (*haystack == '\'') {
1245 else if (!in_dquote)
1247 } else if (*haystack == '\"') {
1250 else if (!in_squote)
1261 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1264 GSList *string_list = NULL, *slist;
1265 gchar **str_array, *s, *new_str;
1266 guint i, n = 1, len;
1268 cm_return_val_if_fail(str != NULL, NULL);
1269 cm_return_val_if_fail(delim != NULL, NULL);
1272 max_tokens = G_MAXINT;
1274 s = strstr_with_skip_quote(str, delim);
1276 guint delimiter_len = strlen(delim);
1280 new_str = g_strndup(str, len);
1282 if (new_str[0] == '\'' || new_str[0] == '\"') {
1283 if (new_str[len - 1] == new_str[0]) {
1284 new_str[len - 1] = '\0';
1285 memmove(new_str, new_str + 1, len - 1);
1288 string_list = g_slist_prepend(string_list, new_str);
1290 str = s + delimiter_len;
1291 s = strstr_with_skip_quote(str, delim);
1292 } while (--max_tokens && s);
1296 new_str = g_strdup(str);
1297 if (new_str[0] == '\'' || new_str[0] == '\"') {
1299 if (new_str[len - 1] == new_str[0]) {
1300 new_str[len - 1] = '\0';
1301 memmove(new_str, new_str + 1, len - 1);
1304 string_list = g_slist_prepend(string_list, new_str);
1308 str_array = g_new(gchar*, n);
1312 str_array[i--] = NULL;
1313 for (slist = string_list; slist; slist = slist->next)
1314 str_array[i--] = slist->data;
1316 g_slist_free(string_list);
1321 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1323 gchar *abbrev_group;
1325 const gchar *p = group;
1328 cm_return_val_if_fail(group != NULL, NULL);
1330 last = group + strlen(group);
1331 abbrev_group = ap = g_malloc(strlen(group) + 1);
1336 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1338 while (*p != '.') p++;
1341 return abbrev_group;
1346 return abbrev_group;
1349 gchar *trim_string(const gchar *str, gint len)
1351 const gchar *p = str;
1356 if (!str) return NULL;
1357 if (strlen(str) <= len)
1358 return g_strdup(str);
1359 if (g_utf8_validate(str, -1, NULL) == FALSE)
1360 return g_strdup(str);
1362 while (*p != '\0') {
1363 mb_len = g_utf8_skip[*(guchar *)p];
1366 else if (new_len + mb_len > len)
1373 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1374 return g_strconcat(new_str, "...", NULL);
1377 GList *uri_list_extract_filenames(const gchar *uri_list)
1379 GList *result = NULL;
1381 gchar *escaped_utf8uri;
1387 while (g_ascii_isspace(*p)) p++;
1388 if (!strncmp(p, "file:", 5)) {
1391 while (*q && *q != '\n' && *q != '\r') q++;
1394 gchar *file, *locale_file = NULL;
1396 while (q > p && g_ascii_isspace(*q))
1398 Xalloca(escaped_utf8uri, q - p + 2,
1400 Xalloca(file, q - p + 2,
1403 strncpy(escaped_utf8uri, p, q - p + 1);
1404 escaped_utf8uri[q - p + 1] = '\0';
1405 decode_uri(file, escaped_utf8uri);
1407 * g_filename_from_uri() rejects escaped/locale encoded uri
1408 * string which come from Nautilus.
1411 if (g_utf8_validate(file, -1, NULL))
1413 = conv_codeset_strdup(
1416 conv_get_locale_charset_str());
1418 locale_file = g_strdup(file + 5);
1420 locale_file = g_filename_from_uri(file, NULL, NULL);
1422 result = g_list_append(result, locale_file);
1426 p = strchr(p, '\n');
1433 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1436 static gint axtoi(const gchar *hexstr)
1438 gint hi, lo, result;
1441 if ('0' <= hi && hi <= '9') {
1444 if ('a' <= hi && hi <= 'f') {
1447 if ('A' <= hi && hi <= 'F') {
1452 if ('0' <= lo && lo <= '9') {
1455 if ('a' <= lo && lo <= 'f') {
1458 if ('A' <= lo && lo <= 'F') {
1461 result = lo + (16 * hi);
1465 gboolean is_uri_string(const gchar *str)
1467 while (str && *str && g_ascii_isspace(*str))
1469 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1470 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1471 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1472 g_ascii_strncasecmp(str, "www.", 4) == 0);
1475 gchar *get_uri_path(const gchar *uri)
1477 while (uri && *uri && g_ascii_isspace(*uri))
1479 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1480 return (gchar *)(uri + 7);
1481 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1482 return (gchar *)(uri + 8);
1483 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1484 return (gchar *)(uri + 6);
1486 return (gchar *)uri;
1489 gint get_uri_len(const gchar *str)
1493 if (is_uri_string(str)) {
1494 for (p = str; *p != '\0'; p++) {
1495 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1504 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1505 * plusses, and escape characters are used)
1507 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1509 gchar *dec = decoded_uri;
1510 const gchar *enc = encoded_uri;
1515 if (isxdigit((guchar)enc[0]) &&
1516 isxdigit((guchar)enc[1])) {
1522 if (with_plus && *enc == '+')
1534 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1536 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1539 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1541 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1542 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1546 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1547 gchar **subject, gchar **body, gchar ***attach)
1551 const gchar *forbidden_uris[] = { ".gnupg/",
1557 gint num_attach = 0;
1558 gchar **my_att = NULL;
1560 Xstrdup_a(tmp_mailto, mailto, return -1);
1562 if (!strncmp(tmp_mailto, "mailto:", 7))
1565 p = strchr(tmp_mailto, '?');
1572 *to = decode_uri_gdup(tmp_mailto);
1574 my_att = g_malloc(sizeof(char *));
1578 gchar *field, *value;
1595 if (*value == '\0') continue;
1597 if (from && !g_ascii_strcasecmp(field, "from")) {
1599 *from = decode_uri_gdup(value);
1601 gchar *tmp = decode_uri_gdup(value);
1602 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1606 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1608 *cc = decode_uri_gdup(value);
1610 gchar *tmp = decode_uri_gdup(value);
1611 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1615 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1617 *bcc = decode_uri_gdup(value);
1619 gchar *tmp = decode_uri_gdup(value);
1620 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1624 } else if (subject && !*subject &&
1625 !g_ascii_strcasecmp(field, "subject")) {
1626 *subject = decode_uri_gdup(value);
1627 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1628 *body = decode_uri_gdup(value);
1629 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1630 gchar *tmp = decode_uri_gdup(value);
1631 if (!g_file_get_contents(value, body, NULL, NULL)) {
1632 g_error("Error: couldn't set insert file '%s' in body\n", value);
1636 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1638 gchar *tmp = decode_uri_gdup(value);
1639 for (; forbidden_uris[i]; i++) {
1640 if (strstr(tmp, forbidden_uris[i])) {
1641 g_print("Refusing to attach '%s', potential private data leak\n",
1649 /* attach is correct */
1651 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1652 my_att[num_attach-1] = tmp;
1653 my_att[num_attach] = NULL;
1665 #include <windows.h>
1666 #ifndef CSIDL_APPDATA
1667 #define CSIDL_APPDATA 0x001a
1669 #ifndef CSIDL_LOCAL_APPDATA
1670 #define CSIDL_LOCAL_APPDATA 0x001c
1672 #ifndef CSIDL_FLAG_CREATE
1673 #define CSIDL_FLAG_CREATE 0x8000
1675 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1679 w32_strerror (int w32_errno)
1681 static char strerr[256];
1682 int ec = (int)GetLastError ();
1686 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1687 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1688 strerr, DIM (strerr)-1, NULL);
1692 static __inline__ void *
1693 dlopen (const char * name, int flag)
1695 void * hd = LoadLibrary (name);
1699 static __inline__ void *
1700 dlsym (void * hd, const char * sym)
1704 void * fnc = GetProcAddress (hd, sym);
1713 static __inline__ const char *
1716 return w32_strerror (0);
1720 static __inline__ int
1732 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1734 static int initialized;
1735 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1739 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1745 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1747 handle = dlopen (dllnames[i], RTLD_LAZY);
1750 func = dlsym (handle, "SHGetFolderPathW");
1761 return func (a,b,c,d,e);
1766 /* Returns a static string with the directroy from which the module
1767 has been loaded. Returns an empty string on error. */
1768 static char *w32_get_module_dir(void)
1770 static char *moddir;
1773 char name[MAX_PATH+10];
1776 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1779 p = strrchr (name, '\\');
1785 moddir = g_strdup (name);
1789 #endif /* G_OS_WIN32 */
1791 /* Return a static string with the locale dir. */
1792 const gchar *get_locale_dir(void)
1794 static gchar *loc_dir;
1798 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1799 "\\share\\locale", NULL);
1802 loc_dir = LOCALEDIR;
1808 const gchar *get_home_dir(void)
1811 static char home_dir_utf16[MAX_PATH] = "";
1812 static gchar *home_dir_utf8 = NULL;
1813 if (home_dir_utf16[0] == '\0') {
1814 if (w32_shgetfolderpath
1815 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1816 NULL, 0, home_dir_utf16) < 0)
1817 strcpy (home_dir_utf16, "C:\\Sylpheed");
1818 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1820 return home_dir_utf8;
1822 static const gchar *homeenv = NULL;
1827 if (!homeenv && g_getenv("HOME") != NULL)
1828 homeenv = g_strdup(g_getenv("HOME"));
1830 homeenv = g_get_home_dir();
1836 static gchar *claws_rc_dir = NULL;
1837 static gboolean rc_dir_alt = FALSE;
1838 const gchar *get_rc_dir(void)
1842 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1845 return claws_rc_dir;
1848 void set_rc_dir(const gchar *dir)
1850 if (claws_rc_dir != NULL) {
1851 g_print("Error: rc_dir already set\n");
1854 if (g_path_is_absolute(dir))
1855 claws_rc_dir = g_strdup(dir);
1857 claws_rc_dir = g_strconcat(g_get_current_dir(),
1858 G_DIR_SEPARATOR_S, dir, NULL);
1860 debug_print("set rc_dir to %s\n", claws_rc_dir);
1861 if (!is_dir_exist(claws_rc_dir)) {
1862 if (make_dir_hier(claws_rc_dir) != 0) {
1863 g_print("Error: can't create %s\n",
1870 gboolean rc_dir_is_alt(void) {
1874 const gchar *get_mail_base_dir(void)
1876 return get_home_dir();
1880 const gchar *prefs_common_get_data_root(void);
1881 gchar *last_data_root = NULL;
1884 const gchar *get_news_cache_dir(void)
1886 static gchar *news_cache_dir = NULL;
1888 const gchar *data_root = prefs_common_get_data_root();
1889 if (strcmp2(data_root, last_data_root)) {
1890 g_free(news_cache_dir);
1891 news_cache_dir = NULL;
1894 if (!news_cache_dir)
1896 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1897 NEWS_CACHE_DIR, NULL);
1901 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1902 "Claws", G_DIR_SEPARATOR_S,
1903 g_get_user_name(), G_DIR_SEPARATOR_S,
1904 NEWS_CACHE_DIR, NULL);
1905 g_free(last_data_root);
1906 last_data_root = g_strdup(last_data_root);
1908 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1909 NEWS_CACHE_DIR, NULL);
1910 g_free(last_data_root);
1911 last_data_root = NULL;
1915 return news_cache_dir;
1918 const gchar *get_imap_cache_dir(void)
1920 static gchar *imap_cache_dir = NULL;
1922 const gchar *data_root = prefs_common_get_data_root();
1923 if (strcmp2(data_root, last_data_root)) {
1924 g_free(imap_cache_dir);
1925 imap_cache_dir = NULL;
1929 if (!imap_cache_dir)
1931 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1932 IMAP_CACHE_DIR, NULL);
1936 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1937 "Claws", G_DIR_SEPARATOR_S,
1938 g_get_user_name(), G_DIR_SEPARATOR_S,
1939 IMAP_CACHE_DIR, NULL);
1940 g_free(last_data_root);
1941 last_data_root = g_strdup(last_data_root);
1943 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1944 IMAP_CACHE_DIR, NULL);
1945 g_free(last_data_root);
1946 last_data_root = NULL;
1951 return imap_cache_dir;
1954 const gchar *get_mime_tmp_dir(void)
1956 static gchar *mime_tmp_dir = NULL;
1959 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1960 MIME_TMP_DIR, NULL);
1962 return mime_tmp_dir;
1965 const gchar *get_template_dir(void)
1967 static gchar *template_dir = NULL;
1970 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1971 TEMPLATE_DIR, NULL);
1973 return template_dir;
1977 const gchar *get_cert_file(void)
1979 const gchar *cert_file = NULL;
1981 cert_file = g_strconcat(w32_get_module_dir(),
1982 "\\share\\claws-mail\\",
1983 "ca-certificates.crt",
1989 /* Return the default directory for Plugins. */
1990 const gchar *get_plugin_dir(void)
1993 static gchar *plugin_dir = NULL;
1996 plugin_dir = g_strconcat(w32_get_module_dir(),
1997 "\\lib\\claws-mail\\plugins\\",
2001 if (is_dir_exist(PLUGINDIR))
2004 static gchar *plugin_dir = NULL;
2006 plugin_dir = g_strconcat(get_rc_dir(),
2007 G_DIR_SEPARATOR_S, "plugins",
2008 G_DIR_SEPARATOR_S, NULL);
2016 /* Return the default directory for Themes. */
2017 const gchar *get_themes_dir(void)
2019 static gchar *themes_dir = NULL;
2022 themes_dir = g_strconcat(w32_get_module_dir(),
2023 "\\share\\claws-mail\\themes",
2029 const gchar *get_tmp_dir(void)
2031 static gchar *tmp_dir = NULL;
2034 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2040 gchar *get_tmp_file(void)
2043 static guint32 id = 0;
2045 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2046 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2051 const gchar *get_domain_name(void)
2054 static gchar *domain_name = NULL;
2060 if (gethostname(hostname, sizeof(hostname)) != 0) {
2061 perror("gethostname");
2062 domain_name = "unknown";
2064 hostname[sizeof(hostname) - 1] = '\0';
2065 if ((hp = my_gethostbyname(hostname)) == NULL) {
2066 perror("gethostbyname");
2067 domain_name = g_strdup(hostname);
2069 domain_name = g_strdup(hp->h_name);
2072 debug_print("domain name = %s\n", domain_name);
2081 off_t get_file_size(const gchar *file)
2085 if (g_stat(file, &s) < 0) {
2086 FILE_OP_ERROR(file, "stat");
2093 time_t get_file_mtime(const gchar *file)
2097 if (g_stat(file, &s) < 0) {
2098 FILE_OP_ERROR(file, "stat");
2105 off_t get_file_size_as_crlf(const gchar *file)
2109 gchar buf[BUFFSIZE];
2111 if ((fp = g_fopen(file, "rb")) == NULL) {
2112 FILE_OP_ERROR(file, "g_fopen");
2116 while (fgets(buf, sizeof(buf), fp) != NULL) {
2118 size += strlen(buf) + 2;
2122 FILE_OP_ERROR(file, "fgets");
2131 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2138 if (g_stat(file, &s) < 0) {
2139 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2143 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2150 /* Test on whether FILE is a relative file name. This is
2151 * straightforward for Unix but more complex for Windows. */
2152 gboolean is_relative_filename(const gchar *file)
2157 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2158 return FALSE; /* Prefixed with a hostname - this can't
2159 * be a relative name. */
2161 if ( ((*file >= 'a' && *file <= 'z')
2162 || (*file >= 'A' && *file <= 'Z'))
2164 file += 2; /* Skip drive letter. */
2166 return !(*file == '\\' || *file == '/');
2168 return !(*file == G_DIR_SEPARATOR);
2173 gboolean is_dir_exist(const gchar *dir)
2178 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2181 gboolean is_file_entry_exist(const gchar *file)
2186 return g_file_test(file, G_FILE_TEST_EXISTS);
2189 gboolean dirent_is_regular_file(struct dirent *d)
2191 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2192 if (d->d_type == DT_REG)
2194 else if (d->d_type != DT_UNKNOWN)
2198 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2201 gint change_dir(const gchar *dir)
2203 gchar *prevdir = NULL;
2206 prevdir = g_get_current_dir();
2208 if (g_chdir(dir) < 0) {
2209 FILE_OP_ERROR(dir, "chdir");
2210 if (debug_mode) g_free(prevdir);
2212 } else if (debug_mode) {
2215 cwd = g_get_current_dir();
2216 if (strcmp(prevdir, cwd) != 0)
2217 g_print("current dir: %s\n", cwd);
2225 gint make_dir(const gchar *dir)
2227 if (g_mkdir(dir, S_IRWXU) < 0) {
2228 FILE_OP_ERROR(dir, "mkdir");
2231 if (g_chmod(dir, S_IRWXU) < 0)
2232 FILE_OP_ERROR(dir, "chmod");
2237 gint make_dir_hier(const gchar *dir)
2242 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2243 parent_dir = g_strndup(dir, p - dir);
2244 if (*parent_dir != '\0') {
2245 if (!is_dir_exist(parent_dir)) {
2246 if (make_dir(parent_dir) < 0) {
2255 if (!is_dir_exist(dir)) {
2256 if (make_dir(dir) < 0)
2263 gint remove_all_files(const gchar *dir)
2266 const gchar *dir_name;
2269 prev_dir = g_get_current_dir();
2271 if (g_chdir(dir) < 0) {
2272 FILE_OP_ERROR(dir, "chdir");
2277 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2278 g_warning("failed to open directory: %s\n", dir);
2283 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2284 if (claws_unlink(dir_name) < 0)
2285 FILE_OP_ERROR(dir_name, "unlink");
2290 if (g_chdir(prev_dir) < 0) {
2291 FILE_OP_ERROR(prev_dir, "chdir");
2301 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2304 const gchar *dir_name;
2308 prev_dir = g_get_current_dir();
2310 if (g_chdir(dir) < 0) {
2311 FILE_OP_ERROR(dir, "chdir");
2316 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2317 g_warning("failed to open directory: %s\n", dir);
2322 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2323 file_no = to_number(dir_name);
2324 if (file_no > 0 && first <= file_no && file_no <= last) {
2325 if (is_dir_exist(dir_name))
2327 if (claws_unlink(dir_name) < 0)
2328 FILE_OP_ERROR(dir_name, "unlink");
2334 if (g_chdir(prev_dir) < 0) {
2335 FILE_OP_ERROR(prev_dir, "chdir");
2345 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2348 const gchar *dir_name;
2352 prev_dir = g_get_current_dir();
2354 if (g_chdir(dir) < 0) {
2355 FILE_OP_ERROR(dir, "chdir");
2360 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2361 FILE_OP_ERROR(dir, "opendir");
2366 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2367 file_no = to_number(dir_name);
2368 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2369 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2370 if (is_dir_exist(dir_name))
2372 if (claws_unlink(dir_name) < 0)
2373 FILE_OP_ERROR(dir_name, "unlink");
2379 if (g_chdir(prev_dir) < 0) {
2380 FILE_OP_ERROR(prev_dir, "chdir");
2390 gint remove_all_numbered_files(const gchar *dir)
2392 return remove_numbered_files(dir, 0, UINT_MAX);
2395 gint remove_dir_recursive(const gchar *dir)
2399 const gchar *dir_name;
2402 if (g_stat(dir, &s) < 0) {
2403 FILE_OP_ERROR(dir, "stat");
2404 if (ENOENT == errno) return 0;
2408 if (!S_ISDIR(s.st_mode)) {
2409 if (claws_unlink(dir) < 0) {
2410 FILE_OP_ERROR(dir, "unlink");
2417 prev_dir = g_get_current_dir();
2418 /* g_print("prev_dir = %s\n", prev_dir); */
2420 if (!path_cmp(prev_dir, dir)) {
2422 if (g_chdir("..") < 0) {
2423 FILE_OP_ERROR(dir, "chdir");
2426 prev_dir = g_get_current_dir();
2429 if (g_chdir(dir) < 0) {
2430 FILE_OP_ERROR(dir, "chdir");
2435 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2436 g_warning("failed to open directory: %s\n", dir);
2442 /* remove all files in the directory */
2443 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2444 /* g_print("removing %s\n", dir_name); */
2446 if (is_dir_exist(dir_name)) {
2447 if (remove_dir_recursive(dir_name) < 0) {
2448 g_warning("can't remove directory\n");
2452 if (claws_unlink(dir_name) < 0)
2453 FILE_OP_ERROR(dir_name, "unlink");
2459 if (g_chdir(prev_dir) < 0) {
2460 FILE_OP_ERROR(prev_dir, "chdir");
2467 if (g_rmdir(dir) < 0) {
2468 FILE_OP_ERROR(dir, "rmdir");
2475 gint rename_force(const gchar *oldpath, const gchar *newpath)
2478 if (!is_file_entry_exist(oldpath)) {
2482 if (is_file_exist(newpath)) {
2483 if (claws_unlink(newpath) < 0)
2484 FILE_OP_ERROR(newpath, "unlink");
2487 return g_rename(oldpath, newpath);
2491 * Append src file body to the tail of dest file.
2492 * Now keep_backup has no effects.
2494 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2496 FILE *src_fp, *dest_fp;
2500 gboolean err = FALSE;
2502 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2503 FILE_OP_ERROR(src, "g_fopen");
2507 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2508 FILE_OP_ERROR(dest, "g_fopen");
2513 if (change_file_mode_rw(dest_fp, dest) < 0) {
2514 FILE_OP_ERROR(dest, "chmod");
2515 g_warning("can't change file mode\n");
2518 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2519 if (n_read < sizeof(buf) && ferror(src_fp))
2521 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2522 g_warning("writing to %s failed.\n", dest);
2530 if (ferror(src_fp)) {
2531 FILE_OP_ERROR(src, "fread");
2535 if (fclose(dest_fp) == EOF) {
2536 FILE_OP_ERROR(dest, "fclose");
2548 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2550 FILE *src_fp, *dest_fp;
2553 gchar *dest_bak = NULL;
2554 gboolean err = FALSE;
2556 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2557 FILE_OP_ERROR(src, "g_fopen");
2560 if (is_file_exist(dest)) {
2561 dest_bak = g_strconcat(dest, ".bak", NULL);
2562 if (rename_force(dest, dest_bak) < 0) {
2563 FILE_OP_ERROR(dest, "rename");
2570 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2571 FILE_OP_ERROR(dest, "g_fopen");
2574 if (rename_force(dest_bak, dest) < 0)
2575 FILE_OP_ERROR(dest_bak, "rename");
2581 if (change_file_mode_rw(dest_fp, dest) < 0) {
2582 FILE_OP_ERROR(dest, "chmod");
2583 g_warning("can't change file mode\n");
2586 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2587 if (n_read < sizeof(buf) && ferror(src_fp))
2589 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2590 g_warning("writing to %s failed.\n", dest);
2595 if (rename_force(dest_bak, dest) < 0)
2596 FILE_OP_ERROR(dest_bak, "rename");
2603 if (ferror(src_fp)) {
2604 FILE_OP_ERROR(src, "fread");
2608 if (fclose(dest_fp) == EOF) {
2609 FILE_OP_ERROR(dest, "fclose");
2616 if (rename_force(dest_bak, dest) < 0)
2617 FILE_OP_ERROR(dest_bak, "rename");
2623 if (keep_backup == FALSE && dest_bak)
2624 claws_unlink(dest_bak);
2631 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2633 if (overwrite == FALSE && is_file_exist(dest)) {
2634 g_warning("move_file(): file %s already exists.", dest);
2638 if (rename_force(src, dest) == 0) return 0;
2640 if (EXDEV != errno) {
2641 FILE_OP_ERROR(src, "rename");
2645 if (copy_file(src, dest, FALSE) < 0) return -1;
2652 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2655 gint bytes_left, to_read;
2658 if (fseek(fp, offset, SEEK_SET) < 0) {
2663 bytes_left = length;
2664 to_read = MIN(bytes_left, sizeof(buf));
2666 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2667 if (n_read < to_read && ferror(fp))
2669 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2672 bytes_left -= n_read;
2673 if (bytes_left == 0)
2675 to_read = MIN(bytes_left, sizeof(buf));
2686 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2689 gboolean err = FALSE;
2691 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2692 FILE_OP_ERROR(dest, "g_fopen");
2696 if (change_file_mode_rw(dest_fp, dest) < 0) {
2697 FILE_OP_ERROR(dest, "chmod");
2698 g_warning("can't change file mode\n");
2701 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2704 if (!err && fclose(dest_fp) == EOF) {
2705 FILE_OP_ERROR(dest, "fclose");
2710 g_warning("writing to %s failed.\n", dest);
2718 /* convert line endings into CRLF. If the last line doesn't end with
2719 * linebreak, add it.
2721 gchar *canonicalize_str(const gchar *str)
2727 for (p = str; *p != '\0'; ++p) {
2734 if (p == str || *(p - 1) != '\n')
2737 out = outp = g_malloc(new_len + 1);
2738 for (p = str; *p != '\0'; ++p) {
2745 if (p == str || *(p - 1) != '\n') {
2754 gint canonicalize_file(const gchar *src, const gchar *dest)
2756 FILE *src_fp, *dest_fp;
2757 gchar buf[BUFFSIZE];
2759 gboolean err = FALSE;
2760 gboolean last_linebreak = FALSE;
2762 if (src == NULL || dest == NULL)
2765 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2766 FILE_OP_ERROR(src, "g_fopen");
2770 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2771 FILE_OP_ERROR(dest, "g_fopen");
2776 if (change_file_mode_rw(dest_fp, dest) < 0) {
2777 FILE_OP_ERROR(dest, "chmod");
2778 g_warning("can't change file mode\n");
2781 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2785 if (len == 0) break;
2786 last_linebreak = FALSE;
2788 if (buf[len - 1] != '\n') {
2789 last_linebreak = TRUE;
2790 r = fputs(buf, dest_fp);
2791 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2792 r = fputs(buf, dest_fp);
2795 r = fwrite(buf, 1, len - 1, dest_fp);
2800 r = fputs("\r\n", dest_fp);
2804 g_warning("writing to %s failed.\n", dest);
2812 if (last_linebreak == TRUE) {
2813 if (fputs("\r\n", dest_fp) == EOF)
2817 if (ferror(src_fp)) {
2818 FILE_OP_ERROR(src, "fgets");
2822 if (fclose(dest_fp) == EOF) {
2823 FILE_OP_ERROR(dest, "fclose");
2835 gint canonicalize_file_replace(const gchar *file)
2839 tmp_file = get_tmp_file();
2841 if (canonicalize_file(file, tmp_file) < 0) {
2846 if (move_file(tmp_file, file, TRUE) < 0) {
2847 g_warning("can't replace %s .\n", file);
2848 claws_unlink(tmp_file);
2857 gchar *normalize_newlines(const gchar *str)
2859 const gchar *p = str;
2862 out = outp = g_malloc(strlen(str) + 1);
2863 for (p = str; *p != '\0'; ++p) {
2865 if (*(p + 1) != '\n')
2876 gchar *get_outgoing_rfc2822_str(FILE *fp)
2878 gchar buf[BUFFSIZE];
2882 str = g_string_new(NULL);
2884 /* output header part */
2885 while (fgets(buf, sizeof(buf), fp) != NULL) {
2887 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2894 else if (next != ' ' && next != '\t') {
2898 if (fgets(buf, sizeof(buf), fp) == NULL)
2902 g_string_append(str, buf);
2903 g_string_append(str, "\r\n");
2909 /* output body part */
2910 while (fgets(buf, sizeof(buf), fp) != NULL) {
2913 g_string_append_c(str, '.');
2914 g_string_append(str, buf);
2915 g_string_append(str, "\r\n");
2919 g_string_free(str, FALSE);
2925 * Create a new boundary in a way that it is very unlikely that this
2926 * will occur in the following text. It would be easy to ensure
2927 * uniqueness if everything is either quoted-printable or base64
2928 * encoded (note that conversion is allowed), but because MIME bodies
2929 * may be nested, it may happen that the same boundary has already
2932 * boundary := 0*69<bchars> bcharsnospace
2933 * bchars := bcharsnospace / " "
2934 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2935 * "+" / "_" / "," / "-" / "." /
2936 * "/" / ":" / "=" / "?"
2938 * some special characters removed because of buggy MTAs
2941 gchar *generate_mime_boundary(const gchar *prefix)
2943 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2944 "abcdefghijklmnopqrstuvwxyz"
2949 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2950 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2953 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2957 gint change_file_mode_rw(FILE *fp, const gchar *file)
2960 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2962 return g_chmod(file, S_IRUSR|S_IWUSR);
2966 FILE *my_tmpfile(void)
2968 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2969 const gchar suffix[] = ".XXXXXX";
2970 const gchar *tmpdir;
2972 const gchar *progname;
2981 tmpdir = get_tmp_dir();
2982 tmplen = strlen(tmpdir);
2983 progname = g_get_prgname();
2984 if (progname == NULL)
2985 progname = "claws-mail";
2986 proglen = strlen(progname);
2987 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2990 memcpy(fname, tmpdir, tmplen);
2991 fname[tmplen] = G_DIR_SEPARATOR;
2992 memcpy(fname + tmplen + 1, progname, proglen);
2993 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2995 fd = mkstemp(fname);
3000 claws_unlink(fname);
3002 /* verify that we can write in the file after unlinking */
3003 if (write(fd, buf, 1) < 0) {
3010 fp = fdopen(fd, "w+b");
3018 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3023 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3027 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3028 dir, G_DIR_SEPARATOR);
3029 fd = mkstemp_name(template, filename);
3032 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3033 fd = mkstemp(*filename);
3035 return fdopen(fd, "w+");
3038 FILE *str_open_as_stream(const gchar *str)
3043 cm_return_val_if_fail(str != NULL, NULL);
3047 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3052 if (len == 0) return fp;
3054 if (fwrite(str, 1, len, fp) != len) {
3055 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3064 gint str_write_to_file(const gchar *str, const gchar *file)
3069 cm_return_val_if_fail(str != NULL, -1);
3070 cm_return_val_if_fail(file != NULL, -1);
3072 if ((fp = g_fopen(file, "wb")) == NULL) {
3073 FILE_OP_ERROR(file, "g_fopen");
3083 if (fwrite(str, 1, len, fp) != len) {
3084 FILE_OP_ERROR(file, "fwrite");
3090 if (fclose(fp) == EOF) {
3091 FILE_OP_ERROR(file, "fclose");
3099 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3106 cm_return_val_if_fail(fp != NULL, NULL);
3108 array = g_byte_array_new();
3110 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3111 if (n_read < sizeof(buf) && ferror(fp))
3113 g_byte_array_append(array, buf, n_read);
3117 FILE_OP_ERROR("file stream", "fread");
3118 g_byte_array_free(array, TRUE);
3123 g_byte_array_append(array, buf, 1);
3124 str = (gchar *)array->data;
3125 g_byte_array_free(array, FALSE);
3127 if (recode && !g_utf8_validate(str, -1, NULL)) {
3128 const gchar *src_codeset, *dest_codeset;
3130 src_codeset = conv_get_locale_charset_str();
3131 dest_codeset = CS_UTF_8;
3132 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3140 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3147 struct timeval timeout = {1, 0};
3152 cm_return_val_if_fail(file != NULL, NULL);
3154 if (g_stat(file, &s) != 0) {
3155 FILE_OP_ERROR(file, "stat");
3158 if (S_ISDIR(s.st_mode)) {
3159 g_warning("%s: is a directory\n", file);
3164 fp = g_fopen (file, "rb");
3166 FILE_OP_ERROR(file, "open");
3170 /* test whether the file is readable without blocking */
3171 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3173 FILE_OP_ERROR(file, "open");
3180 /* allow for one second */
3181 err = select(fd+1, &fds, NULL, NULL, &timeout);
3182 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3184 FILE_OP_ERROR(file, "select");
3186 g_warning("%s: doesn't seem readable\n", file);
3192 /* Now clear O_NONBLOCK */
3193 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3194 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3198 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3199 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3204 /* get the FILE pointer */
3205 fp = fdopen(fd, "rb");
3208 FILE_OP_ERROR(file, "fdopen");
3209 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3214 str = file_read_stream_to_str_full(fp, recode);
3221 gchar *file_read_to_str(const gchar *file)
3223 return file_read_to_str_full(file, TRUE);
3225 gchar *file_read_stream_to_str(FILE *fp)
3227 return file_read_stream_to_str_full(fp, TRUE);
3230 gchar *file_read_to_str_no_recode(const gchar *file)
3232 return file_read_to_str_full(file, FALSE);
3234 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3236 return file_read_stream_to_str_full(fp, FALSE);
3239 char *fgets_crlf(char *buf, int size, FILE *stream)
3241 gboolean is_cr = FALSE;
3242 gboolean last_was_cr = FALSE;
3247 while (--size > 0 && (c = getc(stream)) != EOF)
3250 is_cr = (c == '\r');
3260 last_was_cr = is_cr;
3262 if (c == EOF && cs == buf)
3270 static gint execute_async(gchar *const argv[])
3272 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3274 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3275 NULL, NULL, NULL, FALSE) == FALSE) {
3276 g_warning("Couldn't execute command: %s\n", argv[0]);
3283 static gint execute_sync(gchar *const argv[])
3287 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3290 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3291 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3292 g_warning("Couldn't execute command: %s\n", argv[0]);
3296 if (WIFEXITED(status))
3297 return WEXITSTATUS(status);
3301 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3302 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3303 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3304 g_warning("Couldn't execute command: %s\n", argv[0]);
3312 gint execute_command_line(const gchar *cmdline, gboolean async)
3317 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3319 argv = strsplit_with_quote(cmdline, " ", 0);
3322 ret = execute_async(argv);
3324 ret = execute_sync(argv);
3331 gchar *get_command_output(const gchar *cmdline)
3333 gchar *child_stdout;
3336 cm_return_val_if_fail(cmdline != NULL, NULL);
3338 debug_print("get_command_output(): executing: %s\n", cmdline);
3340 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3342 g_warning("Couldn't execute command: %s\n", cmdline);
3346 return child_stdout;
3349 static gint is_unchanged_uri_char(char c)
3360 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3366 for(i = 0; i < strlen(uri) ; i++) {
3367 if (is_unchanged_uri_char(uri[i])) {
3368 if (k + 2 >= bufsize)
3370 encoded_uri[k++] = uri[i];
3373 char * hexa = "0123456789ABCDEF";
3375 if (k + 4 >= bufsize)
3377 encoded_uri[k++] = '%';
3378 encoded_uri[k++] = hexa[uri[i] / 16];
3379 encoded_uri[k++] = hexa[uri[i] % 16];
3385 gint open_uri(const gchar *uri, const gchar *cmdline)
3389 gchar buf[BUFFSIZE];
3391 gchar encoded_uri[BUFFSIZE];
3392 cm_return_val_if_fail(uri != NULL, -1);
3394 /* an option to choose whether to use encode_uri or not ? */
3395 encode_uri(encoded_uri, BUFFSIZE, uri);
3398 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3399 !strchr(p + 2, '%'))
3400 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3403 g_warning("Open URI command-line is invalid "
3404 "(there must be only one '%%s'): %s",
3406 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3409 execute_command_line(buf, TRUE);
3411 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3414 extern osso_context_t *get_osso_context(void);
3415 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3416 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3417 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3422 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3424 gchar buf[BUFFSIZE];
3427 cm_return_val_if_fail(filepath != NULL, -1);
3430 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3431 !strchr(p + 2, '%'))
3432 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3435 g_warning("Open Text Editor command-line is invalid "
3436 "(there must be only one '%%s'): %s",
3438 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3441 execute_command_line(buf, TRUE);
3446 time_t remote_tzoffset_sec(const gchar *zone)
3448 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3454 time_t remoteoffset;
3456 strncpy(zone3, zone, 3);
3460 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3461 (c == '+' || c == '-')) {
3462 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3464 remoteoffset = -remoteoffset;
3465 } else if (!strncmp(zone, "UT" , 2) ||
3466 !strncmp(zone, "GMT", 2)) {
3468 } else if (strlen(zone3) == 3) {
3469 for (p = ustzstr; *p != '\0'; p += 3) {
3470 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3471 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3472 remoteoffset = iustz * 3600;
3478 } else if (strlen(zone3) == 1) {
3480 case 'Z': remoteoffset = 0; break;
3481 case 'A': remoteoffset = -1; break;
3482 case 'B': remoteoffset = -2; break;
3483 case 'C': remoteoffset = -3; break;
3484 case 'D': remoteoffset = -4; break;
3485 case 'E': remoteoffset = -5; break;
3486 case 'F': remoteoffset = -6; break;
3487 case 'G': remoteoffset = -7; break;
3488 case 'H': remoteoffset = -8; break;
3489 case 'I': remoteoffset = -9; break;
3490 case 'K': remoteoffset = -10; break; /* J is not used */
3491 case 'L': remoteoffset = -11; break;
3492 case 'M': remoteoffset = -12; break;
3493 case 'N': remoteoffset = 1; break;
3494 case 'O': remoteoffset = 2; break;
3495 case 'P': remoteoffset = 3; break;
3496 case 'Q': remoteoffset = 4; break;
3497 case 'R': remoteoffset = 5; break;
3498 case 'S': remoteoffset = 6; break;
3499 case 'T': remoteoffset = 7; break;
3500 case 'U': remoteoffset = 8; break;
3501 case 'V': remoteoffset = 9; break;
3502 case 'W': remoteoffset = 10; break;
3503 case 'X': remoteoffset = 11; break;
3504 case 'Y': remoteoffset = 12; break;
3505 default: remoteoffset = 0; break;
3507 remoteoffset = remoteoffset * 3600;
3511 return remoteoffset;
3514 time_t tzoffset_sec(time_t *now)
3519 struct tm buf1, buf2;
3522 if (now && *now < 0)
3525 gmt = *gmtime_r(now, &buf1);
3526 lt = localtime_r(now, &buf2);
3528 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3530 if (lt->tm_year < gmt.tm_year)
3532 else if (lt->tm_year > gmt.tm_year)
3534 else if (lt->tm_yday < gmt.tm_yday)
3536 else if (lt->tm_yday > gmt.tm_yday)
3539 if (off >= 24 * 60) /* should be impossible */
3540 off = 23 * 60 + 59; /* if not, insert silly value */
3541 if (off <= -24 * 60)
3542 off = -(23 * 60 + 59);
3547 /* calculate timezone offset */
3548 gchar *tzoffset(time_t *now)
3550 static gchar offset_string[6];
3555 struct tm buf1, buf2;
3558 if (now && *now < 0)
3561 gmt = *gmtime_r(now, &buf1);
3562 lt = localtime_r(now, &buf2);
3564 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3566 if (lt->tm_year < gmt.tm_year)
3568 else if (lt->tm_year > gmt.tm_year)
3570 else if (lt->tm_yday < gmt.tm_yday)
3572 else if (lt->tm_yday > gmt.tm_yday)
3580 if (off >= 24 * 60) /* should be impossible */
3581 off = 23 * 60 + 59; /* if not, insert silly value */
3583 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3585 return offset_string;
3588 void get_rfc822_date(gchar *buf, gint len)
3592 gchar day[4], mon[4];
3593 gint dd, hh, mm, ss, yyyy;
3596 gchar buf2[BUFFSIZE];
3600 lt = localtime_r(&t, &buf1);
3602 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3603 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3605 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3606 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3609 void debug_set_mode(gboolean mode)
3614 gboolean debug_get_mode(void)
3619 void debug_print_real(const gchar *format, ...)
3622 gchar buf[BUFFSIZE];
3624 if (!debug_mode) return;
3626 va_start(args, format);
3627 g_vsnprintf(buf, sizeof(buf), format, args);
3634 const char * debug_srcname(const char *file)
3636 const char *s = strrchr (file, '/');
3641 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3643 if (subject == NULL)
3646 subject += subject_get_prefix_length(subject);
3648 return g_hash_table_lookup(subject_table, subject);
3651 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3654 if (subject == NULL || *subject == 0)
3656 subject += subject_get_prefix_length(subject);
3657 g_hash_table_insert(subject_table, subject, data);
3660 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3662 if (subject == NULL)
3665 subject += subject_get_prefix_length(subject);
3666 g_hash_table_remove(subject_table, subject);
3670 *\brief Check if a string is prefixed with known (combinations)
3671 * of prefixes. The function assumes that each prefix
3672 * is terminated by zero or exactly _one_ space.
3674 *\param str String to check for a prefixes
3676 *\return int Number of chars in the prefix that should be skipped
3677 * for a "clean" subject line. If no prefix was found, 0
3682 static regex_t u_regex;
3683 static gboolean u_init_;
3686 void utils_free_regex(void)
3696 int subject_get_prefix_length(const gchar *subject)
3699 /*!< Array with allowable reply prefixes regexps. */
3700 static const gchar * const prefixes[] = {
3701 "Re\\:", /* "Re:" */
3702 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3703 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3704 "Aw\\:", /* "Aw:" (German) */
3705 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3706 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3707 "Fw\\:", /* "Fw:" Forward */
3708 "Fwd\\:", /* "Fwd:" Forward */
3709 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3710 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3711 "Rif\\:", /* "Rif:" (Italian Outlook) */
3712 "Sv\\:", /* "Sv" (Norwegian) */
3713 "Vs\\:", /* "Vs" (Norwegian) */
3714 "Ad\\:", /* "Ad" (Norwegian) */
3715 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3718 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3722 if (!subject) return 0;
3723 if (!*subject) return 0;
3726 GString *s = g_string_new("");
3728 for (n = 0; n < PREFIXES; n++)
3729 /* Terminate each prefix regexpression by a
3730 * "\ ?" (zero or ONE space), and OR them */
3731 g_string_append_printf(s, "(%s\\ ?)%s",
3736 g_string_prepend(s, "(");
3737 g_string_append(s, ")+"); /* match at least once */
3738 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3741 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3742 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3743 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3744 debug_print("Error compiling regexp %s\n", s->str);
3745 g_string_free(s, TRUE);
3749 g_string_free(s, TRUE);
3753 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3758 /*!< Array with allowable reply prefixes regexps. */
3759 static const gchar * const prefixes[] = {
3761 "antw:", /* "Antw:" (Dutch / German Outlook) */
3762 "aw:", /* "Aw:" (German) */
3763 "antwort:", /* "Antwort:" (German Lotus Notes) */
3764 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3765 "fw:", /* "Fw:" Forward */
3766 "fwd:", /* "Fwd:" Forward */
3767 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3768 "odp:", /* "Odp:" Re (Polish Outlook) */
3769 "rif:", /* "Rif:" (Italian Outlook) */
3770 "sv:", /* "Sv" (Norwegian) */
3771 "vs:", /* "Vs" (Norwegian) */
3772 "ad:", /* "Ad" (Norwegian) */
3775 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3778 if (!subject) return 0;
3779 if (!*subject) return 0;
3781 for (n = 0; n < PREFIXES; n++) {
3782 int len = strlen(prefixes[n]);
3783 if (!strncasecmp(subject, prefixes[n], len)) {
3784 if (subject[len] == ' ')
3793 static guint g_stricase_hash(gconstpointer gptr)
3795 guint hash_result = 0;
3798 for (str = gptr; str && *str; str++) {
3799 hash_result += toupper(*str);
3805 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3807 const char *str1 = gptr1;
3808 const char *str2 = gptr2;
3810 return !strcasecmp(str1, str2);
3813 gint g_int_compare(gconstpointer a, gconstpointer b)
3815 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3818 gchar *generate_msgid(gchar *buf, gint len)
3828 lt = localtime_r(&t, &buft);
3830 if (strcmp(buf, "") == 0) {
3831 addr = g_strconcat("@", get_domain_name(), NULL);
3834 addr = g_strconcat("@", buf, NULL);
3837 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3838 lt->tm_year + 1900, lt->tm_mon + 1,
3839 lt->tm_mday, lt->tm_hour,
3840 lt->tm_min, lt->tm_sec,
3841 (guint) rand(), addr);
3848 quote_cmd_argument()
3850 return a quoted string safely usable in argument of a command.
3852 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3855 gint quote_cmd_argument(gchar * result, guint size,
3865 for(p = path ; * p != '\0' ; p ++) {
3867 if (isalnum((guchar)*p) || (* p == '/')) {
3868 if (remaining > 0) {
3874 result[size - 1] = '\0';
3879 if (remaining >= 2) {
3887 result[size - 1] = '\0';
3892 if (remaining > 0) {
3896 result[size - 1] = '\0';
3910 static void g_node_map_recursive(GNode *node, gpointer data)
3912 GNodeMapData *mapdata = (GNodeMapData *) data;
3914 GNodeMapData newmapdata;
3917 newdata = mapdata->func(node->data, mapdata->data);
3918 if (newdata != NULL) {
3919 newnode = g_node_new(newdata);
3920 g_node_append(mapdata->parent, newnode);
3922 newmapdata.parent = newnode;
3923 newmapdata.func = mapdata->func;
3924 newmapdata.data = mapdata->data;
3926 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3930 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3933 GNodeMapData mapdata;
3935 cm_return_val_if_fail(node != NULL, NULL);
3936 cm_return_val_if_fail(func != NULL, NULL);
3938 root = g_node_new(func(node->data, data));
3940 mapdata.parent = root;
3941 mapdata.func = func;
3942 mapdata.data = data;
3944 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3949 #define HEX_TO_INT(val, hex) \
3953 if ('0' <= c && c <= '9') { \
3955 } else if ('a' <= c && c <= 'f') { \
3956 val = c - 'a' + 10; \
3957 } else if ('A' <= c && c <= 'F') { \
3958 val = c - 'A' + 10; \
3964 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3971 if (hi == -1 || lo == -1)
3974 *out = (hi << 4) + lo;
3978 #define INT_TO_HEX(hex, val) \
3981 hex = '0' + (val); \
3983 hex = 'A' + (val) - 10; \
3986 void get_hex_str(gchar *out, guchar ch)
3990 INT_TO_HEX(hex, ch >> 4);
3992 INT_TO_HEX(hex, ch & 0x0f);
3998 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4000 #define G_PRINT_REF g_print
4004 *\brief Register ref counted pointer. It is based on GBoxed, so should
4005 * work with anything that uses the GType system. The semantics
4006 * are similar to a C++ auto pointer, with the exception that
4007 * C doesn't have automatic closure (calling destructors) when
4008 * exiting a block scope.
4009 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4010 * function directly.
4012 *\return GType A GType type.
4014 GType g_auto_pointer_register(void)
4016 static GType auto_pointer_type;
4017 if (!auto_pointer_type)
4019 g_boxed_type_register_static
4020 ("G_TYPE_AUTO_POINTER",
4021 (GBoxedCopyFunc) g_auto_pointer_copy,
4022 (GBoxedFreeFunc) g_auto_pointer_free);
4023 return auto_pointer_type;
4027 *\brief Structure with g_new() allocated pointer guarded by the
4030 typedef struct AutoPointerRef {
4031 void (*free) (gpointer);
4037 *\brief The auto pointer opaque structure that references the
4038 * pointer guard block.
4040 typedef struct AutoPointer {
4041 AutoPointerRef *ref;
4042 gpointer ptr; /*!< access to protected pointer */
4046 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4050 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4051 * ... when assigning, copying and freeing storage elements
4053 * gtk_list_store_new(N_S_COLUMNS,
4054 * G_TYPE_AUTO_POINTER,
4058 * Template *precious_data = g_new0(Template, 1);
4059 * g_pointer protect = g_auto_pointer_new(precious_data);
4061 * gtk_list_store_set(container, &iter,
4065 * ... the gtk_list_store has copied the pointer and
4066 * ... incremented its reference count, we should free
4067 * ... the auto pointer (in C++ a destructor would do
4068 * ... this for us when leaving block scope)
4070 * g_auto_pointer_free(protect);
4072 * ... gtk_list_store_set() now manages the data. When
4073 * ... *explicitly* requesting a pointer from the list
4074 * ... store, don't forget you get a copy that should be
4075 * ... freed with g_auto_pointer_free() eventually.
4079 *\param pointer Pointer to be guarded.
4081 *\return GAuto * Pointer that should be used in containers with
4084 GAuto *g_auto_pointer_new(gpointer p)
4086 AutoPointerRef *ref;
4092 ref = g_new0(AutoPointerRef, 1);
4093 ptr = g_new0(AutoPointer, 1);
4103 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4109 *\brief Allocate an autopointer using the passed \a free function to
4110 * free the guarded pointer
4112 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4119 aptr = g_auto_pointer_new(p);
4120 aptr->ref->free = free_;
4124 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4126 if (auto_ptr == NULL)
4128 return ((AutoPointer *) auto_ptr)->ptr;
4132 *\brief Copies an auto pointer by. It's mostly not necessary
4133 * to call this function directly, unless you copy/assign
4134 * the guarded pointer.
4136 *\param auto_ptr Auto pointer returned by previous call to
4137 * g_auto_pointer_new_XXX()
4139 *\return gpointer An auto pointer
4141 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4144 AutoPointerRef *ref;
4147 if (auto_ptr == NULL)
4152 newp = g_new0(AutoPointer, 1);
4155 newp->ptr = ref->pointer;
4159 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4165 *\brief Free an auto pointer
4167 void g_auto_pointer_free(GAuto *auto_ptr)
4170 AutoPointerRef *ref;
4172 if (auto_ptr == NULL)
4178 if (--(ref->cnt) == 0) {
4180 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4182 ref->free(ref->pointer);
4187 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4192 void replace_returns(gchar *str)
4197 while (strstr(str, "\n")) {
4198 *strstr(str, "\n") = ' ';
4200 while (strstr(str, "\r")) {
4201 *strstr(str, "\r") = ' ';
4205 /* get_uri_part() - retrieves a URI starting from scanpos.
4206 Returns TRUE if succesful */
4207 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4208 const gchar **bp, const gchar **ep, gboolean hdr)
4211 gint parenthese_cnt = 0;
4213 cm_return_val_if_fail(start != NULL, FALSE);
4214 cm_return_val_if_fail(scanpos != NULL, FALSE);
4215 cm_return_val_if_fail(bp != NULL, FALSE);
4216 cm_return_val_if_fail(ep != NULL, FALSE);
4220 /* find end point of URI */
4221 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4222 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4223 !IS_ASCII(*(const guchar *)ep_) ||
4224 strchr("[]{}<>\"", *ep_)) {
4226 } else if (strchr("(", *ep_)) {
4228 } else if (strchr(")", *ep_)) {
4229 if (parenthese_cnt > 0)
4236 /* no punctuation at end of string */
4238 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4239 * should pass some URI type to this function and decide on that whether
4240 * to perform punctuation stripping */
4242 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4244 for (; ep_ - 1 > scanpos + 1 &&
4245 IS_REAL_PUNCT(*(ep_ - 1));
4249 #undef IS_REAL_PUNCT
4256 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4258 while (bp && *bp && g_ascii_isspace(*bp))
4260 return g_strndup(bp, ep - bp);
4263 /* valid mail address characters */
4264 #define IS_RFC822_CHAR(ch) \
4268 !g_ascii_isspace(ch) && \
4269 !strchr("(),;<>\"", (ch)))
4271 /* alphabet and number within 7bit ASCII */
4272 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4273 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4275 static GHashTable *create_domain_tab(void)
4277 static const gchar *toplvl_domains [] = {
4279 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4280 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4281 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4282 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4283 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4284 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4285 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4286 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4287 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4288 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4289 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4290 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4291 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4292 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4293 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4294 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4295 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4296 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4297 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4298 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4299 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4300 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4301 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4302 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4303 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4304 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4307 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4309 cm_return_val_if_fail(htab, NULL);
4310 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4311 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4315 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4317 const gint MAX_LVL_DOM_NAME_LEN = 6;
4318 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4319 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4322 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4325 for (p = buf; p < m && first < last; *p++ = *first++)
4329 return g_hash_table_lookup(tab, buf) != NULL;
4332 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4333 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4334 const gchar **bp, const gchar **ep, gboolean hdr)
4336 /* more complex than the uri part because we need to scan back and forward starting from
4337 * the scan position. */
4338 gboolean result = FALSE;
4339 const gchar *bp_ = NULL;
4340 const gchar *ep_ = NULL;
4341 static GHashTable *dom_tab;
4342 const gchar *last_dot = NULL;
4343 const gchar *prelast_dot = NULL;
4344 const gchar *last_tld_char = NULL;
4346 /* the informative part of the email address (describing the name
4347 * of the email address owner) may contain quoted parts. the
4348 * closure stack stores the last encountered quotes. */
4349 gchar closure_stack[128];
4350 gchar *ptr = closure_stack;
4352 cm_return_val_if_fail(start != NULL, FALSE);
4353 cm_return_val_if_fail(scanpos != NULL, FALSE);
4354 cm_return_val_if_fail(bp != NULL, FALSE);
4355 cm_return_val_if_fail(ep != NULL, FALSE);
4358 const gchar *start_quote = NULL;
4359 const gchar *end_quote = NULL;
4361 /* go to the real start */
4362 if (start[0] == ',')
4364 if (start[0] == ';')
4366 while (start[0] == '\n' || start[0] == '\r')
4368 while (start[0] == ' ' || start[0] == '\t')
4373 /* check if there are quotes (to skip , in them) */
4374 if (*start == '"') {
4375 start_quote = start;
4377 end_quote = strstr(start, "\"");
4383 /* skip anything between quotes */
4384 if (start_quote && end_quote) {
4389 /* find end (either , or ; or end of line) */
4390 if (strstr(start, ",") && strstr(start, ";"))
4391 *ep = strstr(start,",") < strstr(start, ";")
4392 ? strstr(start, ",") : strstr(start, ";");
4393 else if (strstr(start, ","))
4394 *ep = strstr(start, ",");
4395 else if (strstr(start, ";"))
4396 *ep = strstr(start, ";");
4398 *ep = start+strlen(start);
4400 /* go back to real start */
4401 if (start_quote && end_quote) {
4402 start = start_quote;
4405 /* check there's still an @ in that, or search
4406 * further if possible */
4407 if (strstr(start, "@") && strstr(start, "@") < *ep)
4409 else if (*ep < start+strlen(start)) {
4412 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4420 dom_tab = create_domain_tab();
4421 cm_return_val_if_fail(dom_tab, FALSE);
4423 /* scan start of address */
4424 for (bp_ = scanpos - 1;
4425 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4428 /* TODO: should start with an alnum? */
4430 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4433 if (bp_ != scanpos) {
4434 /* scan end of address */
4435 for (ep_ = scanpos + 1;
4436 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4438 prelast_dot = last_dot;
4440 if (*(last_dot + 1) == '.') {
4441 if (prelast_dot == NULL)
4443 last_dot = prelast_dot;
4448 /* TODO: really should terminate with an alnum? */
4449 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4454 if (last_dot == NULL)
4456 if (last_dot >= ep_)
4457 last_dot = prelast_dot;
4458 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4462 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4463 if (*last_tld_char == '?')
4466 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4473 if (!result) return FALSE;
4475 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4476 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4477 && IS_RFC822_CHAR(*(ep_ + 3))) {
4478 /* this informative part with an @ in it is
4479 * followed by the email address */
4482 /* go to matching '>' (or next non-rfc822 char, like \n) */
4483 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4486 /* include the bracket */
4487 if (*ep_ == '>') ep_++;
4489 /* include the leading quote */
4497 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4498 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4501 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4502 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4505 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4506 #define IN_STACK() (ptr > closure_stack)
4507 /* has underrun check */
4508 #define POP_STACK() if(IN_STACK()) --ptr
4509 /* has overrun check */
4510 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4511 /* has underrun check */
4512 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4516 /* scan for the informative part. */
4517 for (bp_ -= 2; bp_ >= start; bp_--) {
4518 /* if closure on the stack keep scanning */
4519 if (PEEK_STACK() == *bp_) {
4523 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4528 /* if nothing in the closure stack, do the special conditions
4529 * the following if..else expression simply checks whether
4530 * a token is acceptable. if not acceptable, the clause
4531 * should terminate the loop with a 'break' */
4532 if (!PEEK_STACK()) {
4534 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4535 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4536 /* hyphens are allowed, but only in
4538 } else if (strchr(" \"'", *bp_)) {
4539 /* but anything not being a punctiation
4542 break; /* anything else is rejected */
4549 /* scan forward (should start with an alnum) */
4550 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4566 #undef IS_ASCII_ALNUM
4567 #undef IS_RFC822_CHAR
4569 gchar *make_email_string(const gchar *bp, const gchar *ep)
4571 /* returns a mailto: URI; mailto: is also used to detect the
4572 * uri type later on in the button_pressed signal handler */
4576 tmp = g_strndup(bp, ep - bp);
4577 result = g_strconcat("mailto:", tmp, NULL);
4583 gchar *make_http_string(const gchar *bp, const gchar *ep)
4585 /* returns an http: URI; */
4589 while (bp && *bp && g_ascii_isspace(*bp))
4591 tmp = g_strndup(bp, ep - bp);
4592 result = g_strconcat("http://", tmp, NULL);
4598 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4600 FILE *fp = g_fopen(path, "rb");
4601 gchar buf[BUFFSIZE];
4602 gchar *result = NULL;
4605 while (fgets(buf, sizeof (buf), fp) != NULL) {
4606 gchar **parts = g_strsplit(buf, ";", 3);
4607 gchar *trimmed = parts[0];
4608 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4610 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4611 trimmed[strlen(trimmed)-1] = '\0';
4613 if (!strcmp(trimmed, type)) {
4614 gboolean needsterminal = FALSE;
4615 if (parts[2] && strstr(parts[2], "needsterminal")) {
4616 needsterminal = TRUE;
4618 if (parts[2] && strstr(parts[2], "test=")) {
4619 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4620 gchar *testcmd = orig_testcmd;
4621 if (strstr(testcmd,";"))
4622 *(strstr(testcmd,";")) = '\0';
4623 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4625 while (testcmd[strlen(testcmd)-1] == '\n')
4626 testcmd[strlen(testcmd)-1] = '\0';
4627 while (testcmd[strlen(testcmd)-1] == '\r')
4628 testcmd[strlen(testcmd)-1] = '\0';
4629 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4630 testcmd[strlen(testcmd)-1] = '\0';
4632 if (strstr(testcmd, "%s")) {
4633 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4634 gint res = system(tmp);
4636 g_free(orig_testcmd);
4643 gint res = system(testcmd);
4644 g_free(orig_testcmd);
4654 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4656 while (trimmed[strlen(trimmed)-1] == '\n')
4657 trimmed[strlen(trimmed)-1] = '\0';
4658 while (trimmed[strlen(trimmed)-1] == '\r')
4659 trimmed[strlen(trimmed)-1] = '\0';
4660 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4661 trimmed[strlen(trimmed)-1] = '\0';
4662 result = g_strdup(trimmed);
4665 /* if there are no single quotes around %s, add them.
4666 * '.*%s.*' is ok, as in display 'png:%s'
4668 if (strstr(result, "%s")
4669 && !(strstr(result, "'") < strstr(result,"%s") &&
4670 strstr(strstr(result,"%s"), "'"))) {
4671 gchar *start = g_strdup(result);
4672 gchar *end = g_strdup(strstr(result, "%s")+2);
4674 *strstr(start, "%s") = '\0';
4675 tmp = g_strconcat(start,"'%s'",end, NULL);
4681 if (needsterminal) {
4682 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4693 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4695 gchar *result = NULL;
4699 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4700 result = mailcap_get_command_in_file(path, type, file_to_open);
4704 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4708 void mailcap_update_default(const gchar *type, const gchar *command)
4710 gchar *path = NULL, *outpath = NULL;
4711 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4712 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4713 FILE *fp = g_fopen(path, "rb");
4714 FILE *outfp = g_fopen(outpath, "wb");
4715 gchar buf[BUFFSIZE];
4716 gboolean err = FALSE;
4724 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4725 gchar **parts = g_strsplit(buf, ";", 3);
4726 gchar *trimmed = parts[0];
4727 while (trimmed[0] == ' ')
4729 while (trimmed[strlen(trimmed)-1] == ' ')
4730 trimmed[strlen(trimmed)-1] = '\0';
4732 if (!strcmp(trimmed, type)) {
4737 if(fputs(buf, outfp) == EOF) {
4744 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4750 if (fclose(outfp) == EOF)
4754 g_rename(outpath, path);
4760 gint copy_dir(const gchar *src, const gchar *dst)
4765 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4766 g_warning("failed to open directory: %s\n", src);
4770 if (make_dir(dst) < 0)
4773 while ((name = g_dir_read_name(dir)) != NULL) {
4774 gchar *old_file, *new_file;
4775 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4776 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4777 debug_print("copying: %s -> %s\n", old_file, new_file);
4778 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4779 gint r = copy_file(old_file, new_file, TRUE);
4786 /* Windows has no symlinks. Or well, Vista seems to
4787 have something like this but the semantics might be
4788 different. Thus we don't use it under Windows. */
4789 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4792 gchar *target = g_file_read_link(old_file, &error);
4794 r = symlink(target, new_file);
4801 #endif /*G_OS_WIN32*/
4802 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4803 gint r = copy_dir(old_file, new_file);
4814 /* crude test to see if a file is an email. */
4815 gboolean file_is_email (const gchar *filename)
4821 if (filename == NULL)
4823 if ((fp = g_fopen(filename, "rb")) == NULL)
4825 while (i < 60 && score < 3
4826 && fgets(buffer, sizeof (buffer), fp) > 0) {
4827 if (!strncmp(buffer, "From:", strlen("From:")))
4829 if (!strncmp(buffer, "To:", strlen("To:")))
4831 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4836 return (score >= 3);
4839 gboolean sc_g_list_bigger(GList *list, gint max)
4843 while (cur && i <= max+1) {
4850 gboolean sc_g_slist_bigger(GSList *list, gint max)
4854 while (cur && i <= max+1) {
4861 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4862 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4863 NULL, NULL, NULL, NULL, NULL, NULL};
4864 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4865 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4866 NULL, NULL, NULL, NULL, NULL, NULL};
4868 gint daynames_len[] = {0,0,0,0,0,0,0};
4869 gint monthnames_len[] = {0,0,0,0,0,0,
4871 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4872 gint s_monthnames_len[] = {0,0,0,0,0,0,
4874 const gchar *s_am_up = NULL;
4875 const gchar *s_pm_up = NULL;
4876 const gchar *s_am_low = NULL;
4877 const gchar *s_pm_low = NULL;
4879 gint s_am_up_len = 0;
4880 gint s_pm_up_len = 0;
4881 gint s_am_low_len = 0;
4882 gint s_pm_low_len = 0;
4884 const gchar *def_loc_format = NULL;
4885 const gchar *date_loc_format = NULL;
4886 const gchar *time_loc_format = NULL;
4887 const gchar *time_am_pm = NULL;
4889 static gboolean time_names_init_done = FALSE;
4891 static void init_time_names(void)
4895 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4896 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4897 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4898 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4899 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4900 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4901 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4903 monthnames[0] = Q_("Complete month name for use by strftime|January");
4904 monthnames[1] = Q_("Complete month name for use by strftime|February");
4905 monthnames[2] = Q_("Complete month name for use by strftime|March");
4906 monthnames[3] = Q_("Complete month name for use by strftime|April");
4907 monthnames[4] = Q_("Complete month name for use by strftime|May");
4908 monthnames[5] = Q_("Complete month name for use by strftime|June");
4909 monthnames[6] = Q_("Complete month name for use by strftime|July");
4910 monthnames[7] = Q_("Complete month name for use by strftime|August");
4911 monthnames[8] = Q_("Complete month name for use by strftime|September");
4912 monthnames[9] = Q_("Complete month name for use by strftime|October");
4913 monthnames[10] = Q_("Complete month name for use by strftime|November");
4914 monthnames[11] = Q_("Complete month name for use by strftime|December");
4916 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4917 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4918 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4919 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4920 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4921 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4922 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4924 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4925 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4926 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4927 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4928 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4929 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4930 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4931 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4932 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4933 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4934 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4935 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4937 for (i = 0; i < 7; i++) {
4938 daynames_len[i] = strlen(daynames[i]);
4939 s_daynames_len[i] = strlen(s_daynames[i]);
4941 for (i = 0; i < 12; i++) {
4942 monthnames_len[i] = strlen(monthnames[i]);
4943 s_monthnames_len[i] = strlen(s_monthnames[i]);
4946 s_am_up = Q_("For use by strftime (morning)|AM");
4947 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4948 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4949 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4951 s_am_up_len = strlen(s_am_up);
4952 s_pm_up_len = strlen(s_pm_up);
4953 s_am_low_len = strlen(s_am_low);
4954 s_pm_low_len = strlen(s_pm_low);
4956 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4957 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4958 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4960 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4962 time_names_init_done = TRUE;
4965 #define CHECK_SIZE() { \
4966 total_done += len; \
4967 if (total_done >= buflen) { \
4968 buf[buflen-1] = '\0'; \
4973 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4975 gchar *curpos = buf;
4976 gint total_done = 0;
4977 gchar subbuf[64], subfmt[64];
4978 static time_t last_tzset = (time_t)0;
4980 if (!time_names_init_done)
4983 if (format == NULL || lt == NULL)
4986 if (last_tzset != time(NULL)) {
4988 last_tzset = time(NULL);
4991 if (*format == '%') {
4992 gint len = 0, tmp = 0;
4996 len = 1; CHECK_SIZE();
5000 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5001 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5004 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5005 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5009 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5010 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5013 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5014 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5017 fast_strftime(subbuf, 64, def_loc_format, lt);
5018 len = strlen(subbuf); CHECK_SIZE();
5019 strncpy2(curpos, subbuf, buflen - total_done);
5022 total_done += 2; CHECK_SIZE();
5023 tmp = (lt->tm_year + 1900)/100;
5024 *curpos++ = '0'+(tmp / 10);
5025 *curpos++ = '0'+(tmp % 10);
5028 total_done += 2; CHECK_SIZE();
5029 *curpos++ = '0'+(lt->tm_mday / 10);
5030 *curpos++ = '0'+(lt->tm_mday % 10);
5033 total_done += 8; CHECK_SIZE();
5034 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5035 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5037 *curpos++ = '0'+(lt->tm_mday / 10);
5038 *curpos++ = '0'+(lt->tm_mday % 10);
5040 tmp = lt->tm_year%100;
5041 *curpos++ = '0'+(tmp / 10);
5042 *curpos++ = '0'+(tmp % 10);
5045 len = 2; CHECK_SIZE();
5046 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5049 len = 10; CHECK_SIZE();
5050 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5051 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5054 total_done += 2; CHECK_SIZE();
5055 *curpos++ = '0'+(lt->tm_hour / 10);
5056 *curpos++ = '0'+(lt->tm_hour % 10);
5059 total_done += 2; CHECK_SIZE();
5065 *curpos++ = '0'+(tmp / 10);
5066 *curpos++ = '0'+(tmp % 10);
5069 len = 3; CHECK_SIZE();
5070 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5073 len = 2; CHECK_SIZE();
5074 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5077 len = 2; CHECK_SIZE();
5083 snprintf(curpos, buflen - total_done, "%2d", tmp);
5086 total_done += 2; CHECK_SIZE();
5087 tmp = lt->tm_mon + 1;
5088 *curpos++ = '0'+(tmp / 10);
5089 *curpos++ = '0'+(tmp % 10);
5092 total_done += 2; CHECK_SIZE();
5093 *curpos++ = '0'+(lt->tm_min / 10);
5094 *curpos++ = '0'+(lt->tm_min % 10);
5097 len = 1; CHECK_SIZE();
5101 if (lt->tm_hour >= 12) {
5102 len = s_pm_up_len; CHECK_SIZE();
5103 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5105 len = s_am_up_len; CHECK_SIZE();
5106 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5110 if (lt->tm_hour >= 12) {
5111 len = s_pm_low_len; CHECK_SIZE();
5112 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5114 len = s_am_low_len; CHECK_SIZE();
5115 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5119 fast_strftime(subbuf, 64, time_am_pm, lt);
5120 len = strlen(subbuf); CHECK_SIZE();
5121 strncpy2(curpos, subbuf, buflen - total_done);
5124 total_done += 5; CHECK_SIZE();
5125 *curpos++ = '0'+(lt->tm_hour / 10);
5126 *curpos++ = '0'+(lt->tm_hour % 10);
5128 *curpos++ = '0'+(lt->tm_min / 10);
5129 *curpos++ = '0'+(lt->tm_min % 10);
5132 snprintf(subbuf, 64, "%ld", mktime(lt));
5133 len = strlen(subbuf); CHECK_SIZE();
5134 strncpy2(curpos, subbuf, buflen - total_done);
5137 total_done += 2; CHECK_SIZE();
5138 *curpos++ = '0'+(lt->tm_sec / 10);
5139 *curpos++ = '0'+(lt->tm_sec % 10);
5142 len = 1; CHECK_SIZE();
5146 total_done += 8; CHECK_SIZE();
5147 *curpos++ = '0'+(lt->tm_hour / 10);
5148 *curpos++ = '0'+(lt->tm_hour % 10);
5150 *curpos++ = '0'+(lt->tm_min / 10);
5151 *curpos++ = '0'+(lt->tm_min % 10);
5153 *curpos++ = '0'+(lt->tm_sec / 10);
5154 *curpos++ = '0'+(lt->tm_sec % 10);
5157 len = 1; CHECK_SIZE();
5158 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5161 len = 1; CHECK_SIZE();
5162 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5165 fast_strftime(subbuf, 64, date_loc_format, lt);
5166 len = strlen(subbuf); CHECK_SIZE();
5167 strncpy2(curpos, subbuf, buflen - total_done);
5170 fast_strftime(subbuf, 64, time_loc_format, lt);
5171 len = strlen(subbuf); CHECK_SIZE();
5172 strncpy2(curpos, subbuf, buflen - total_done);
5175 total_done += 2; CHECK_SIZE();
5176 tmp = lt->tm_year%100;
5177 *curpos++ = '0'+(tmp / 10);
5178 *curpos++ = '0'+(tmp % 10);
5181 len = 4; CHECK_SIZE();
5182 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5192 /* let these complicated ones be done with the libc */
5193 snprintf(subfmt, 64, "%%%c", *format);
5194 strftime(subbuf, 64, subfmt, lt);
5195 len = strlen(subbuf); CHECK_SIZE();
5196 strncpy2(curpos, subbuf, buflen - total_done);
5200 /* let these complicated modifiers be done with the libc */
5201 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5202 strftime(subbuf, 64, subfmt, lt);
5203 len = strlen(subbuf); CHECK_SIZE();
5204 strncpy2(curpos, subbuf, buflen - total_done);
5208 if (format && *format)
5209 g_warning("format error (%c)", *format);
5216 int len = 1; CHECK_SIZE();
5217 *curpos++ = *format++;
5224 gboolean prefs_common_get_use_shred(void);
5228 #define WEXITSTATUS(x) (x)
5231 int claws_unlink(const gchar *filename)
5234 static int found_shred = -1;
5235 static const gchar *args[4];
5237 if (filename == NULL)
5240 if (prefs_common_get_use_shred()) {
5241 if (found_shred == -1) {
5243 args[0] = g_find_program_in_path("shred");
5244 debug_print("found shred: %s\n", args[0]);
5245 found_shred = (args[0] != NULL) ? 1:0;
5249 if (found_shred == 1) {
5250 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5251 if (s.st_nlink == 1) {
5254 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5255 NULL, NULL, NULL, NULL, &status, NULL);
5256 debug_print("%s %s exited with status %d\n",
5257 args[0], filename, WEXITSTATUS(status));
5258 if (truncate(filename, 0) < 0)
5259 g_warning("couln't truncate");
5264 return g_unlink(filename);