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);
3289 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3290 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3291 g_warning("Couldn't execute command: %s\n", argv[0]);
3296 if (WIFEXITED(status))
3297 return WEXITSTATUS(status);
3305 gint execute_command_line(const gchar *cmdline, gboolean async)
3310 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3312 argv = strsplit_with_quote(cmdline, " ", 0);
3315 ret = execute_async(argv);
3317 ret = execute_sync(argv);
3324 gchar *get_command_output(const gchar *cmdline)
3326 gchar *child_stdout;
3329 cm_return_val_if_fail(cmdline != NULL, NULL);
3331 debug_print("get_command_output(): executing: %s\n", cmdline);
3333 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3335 g_warning("Couldn't execute command: %s\n", cmdline);
3339 return child_stdout;
3342 static gint is_unchanged_uri_char(char c)
3353 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3359 for(i = 0; i < strlen(uri) ; i++) {
3360 if (is_unchanged_uri_char(uri[i])) {
3361 if (k + 2 >= bufsize)
3363 encoded_uri[k++] = uri[i];
3366 char * hexa = "0123456789ABCDEF";
3368 if (k + 4 >= bufsize)
3370 encoded_uri[k++] = '%';
3371 encoded_uri[k++] = hexa[uri[i] / 16];
3372 encoded_uri[k++] = hexa[uri[i] % 16];
3378 gint open_uri(const gchar *uri, const gchar *cmdline)
3382 gchar buf[BUFFSIZE];
3384 gchar encoded_uri[BUFFSIZE];
3385 cm_return_val_if_fail(uri != NULL, -1);
3387 /* an option to choose whether to use encode_uri or not ? */
3388 encode_uri(encoded_uri, BUFFSIZE, uri);
3391 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3392 !strchr(p + 2, '%'))
3393 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3396 g_warning("Open URI command-line is invalid "
3397 "(there must be only one '%%s'): %s",
3399 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3402 execute_command_line(buf, TRUE);
3404 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3407 extern osso_context_t *get_osso_context(void);
3408 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3409 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3410 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3415 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3417 gchar buf[BUFFSIZE];
3420 cm_return_val_if_fail(filepath != NULL, -1);
3423 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3424 !strchr(p + 2, '%'))
3425 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3428 g_warning("Open Text Editor command-line is invalid "
3429 "(there must be only one '%%s'): %s",
3431 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3434 execute_command_line(buf, TRUE);
3439 time_t remote_tzoffset_sec(const gchar *zone)
3441 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3447 time_t remoteoffset;
3449 strncpy(zone3, zone, 3);
3453 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3454 (c == '+' || c == '-')) {
3455 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3457 remoteoffset = -remoteoffset;
3458 } else if (!strncmp(zone, "UT" , 2) ||
3459 !strncmp(zone, "GMT", 2)) {
3461 } else if (strlen(zone3) == 3) {
3462 for (p = ustzstr; *p != '\0'; p += 3) {
3463 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3464 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3465 remoteoffset = iustz * 3600;
3471 } else if (strlen(zone3) == 1) {
3473 case 'Z': remoteoffset = 0; break;
3474 case 'A': remoteoffset = -1; break;
3475 case 'B': remoteoffset = -2; break;
3476 case 'C': remoteoffset = -3; break;
3477 case 'D': remoteoffset = -4; break;
3478 case 'E': remoteoffset = -5; break;
3479 case 'F': remoteoffset = -6; break;
3480 case 'G': remoteoffset = -7; break;
3481 case 'H': remoteoffset = -8; break;
3482 case 'I': remoteoffset = -9; break;
3483 case 'K': remoteoffset = -10; break; /* J is not used */
3484 case 'L': remoteoffset = -11; break;
3485 case 'M': remoteoffset = -12; break;
3486 case 'N': remoteoffset = 1; break;
3487 case 'O': remoteoffset = 2; break;
3488 case 'P': remoteoffset = 3; break;
3489 case 'Q': remoteoffset = 4; break;
3490 case 'R': remoteoffset = 5; break;
3491 case 'S': remoteoffset = 6; break;
3492 case 'T': remoteoffset = 7; break;
3493 case 'U': remoteoffset = 8; break;
3494 case 'V': remoteoffset = 9; break;
3495 case 'W': remoteoffset = 10; break;
3496 case 'X': remoteoffset = 11; break;
3497 case 'Y': remoteoffset = 12; break;
3498 default: remoteoffset = 0; break;
3500 remoteoffset = remoteoffset * 3600;
3504 return remoteoffset;
3507 time_t tzoffset_sec(time_t *now)
3512 struct tm buf1, buf2;
3515 if (now && *now < 0)
3518 gmt = *gmtime_r(now, &buf1);
3519 lt = localtime_r(now, &buf2);
3521 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3523 if (lt->tm_year < gmt.tm_year)
3525 else if (lt->tm_year > gmt.tm_year)
3527 else if (lt->tm_yday < gmt.tm_yday)
3529 else if (lt->tm_yday > gmt.tm_yday)
3532 if (off >= 24 * 60) /* should be impossible */
3533 off = 23 * 60 + 59; /* if not, insert silly value */
3534 if (off <= -24 * 60)
3535 off = -(23 * 60 + 59);
3540 /* calculate timezone offset */
3541 gchar *tzoffset(time_t *now)
3543 static gchar offset_string[6];
3548 struct tm buf1, buf2;
3551 if (now && *now < 0)
3554 gmt = *gmtime_r(now, &buf1);
3555 lt = localtime_r(now, &buf2);
3557 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3559 if (lt->tm_year < gmt.tm_year)
3561 else if (lt->tm_year > gmt.tm_year)
3563 else if (lt->tm_yday < gmt.tm_yday)
3565 else if (lt->tm_yday > gmt.tm_yday)
3573 if (off >= 24 * 60) /* should be impossible */
3574 off = 23 * 60 + 59; /* if not, insert silly value */
3576 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3578 return offset_string;
3581 void get_rfc822_date(gchar *buf, gint len)
3585 gchar day[4], mon[4];
3586 gint dd, hh, mm, ss, yyyy;
3589 gchar buf2[BUFFSIZE];
3593 lt = localtime_r(&t, &buf1);
3595 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3596 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3598 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3599 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3602 void debug_set_mode(gboolean mode)
3607 gboolean debug_get_mode(void)
3612 void debug_print_real(const gchar *format, ...)
3615 gchar buf[BUFFSIZE];
3617 if (!debug_mode) return;
3619 va_start(args, format);
3620 g_vsnprintf(buf, sizeof(buf), format, args);
3627 const char * debug_srcname(const char *file)
3629 const char *s = strrchr (file, '/');
3634 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3636 if (subject == NULL)
3639 subject += subject_get_prefix_length(subject);
3641 return g_hash_table_lookup(subject_table, subject);
3644 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3647 if (subject == NULL || *subject == 0)
3649 subject += subject_get_prefix_length(subject);
3650 g_hash_table_insert(subject_table, subject, data);
3653 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3655 if (subject == NULL)
3658 subject += subject_get_prefix_length(subject);
3659 g_hash_table_remove(subject_table, subject);
3663 *\brief Check if a string is prefixed with known (combinations)
3664 * of prefixes. The function assumes that each prefix
3665 * is terminated by zero or exactly _one_ space.
3667 *\param str String to check for a prefixes
3669 *\return int Number of chars in the prefix that should be skipped
3670 * for a "clean" subject line. If no prefix was found, 0
3675 static regex_t u_regex;
3676 static gboolean u_init_;
3679 void utils_free_regex(void)
3689 int subject_get_prefix_length(const gchar *subject)
3692 /*!< Array with allowable reply prefixes regexps. */
3693 static const gchar * const prefixes[] = {
3694 "Re\\:", /* "Re:" */
3695 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3696 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3697 "Aw\\:", /* "Aw:" (German) */
3698 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3699 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3700 "Fw\\:", /* "Fw:" Forward */
3701 "Fwd\\:", /* "Fwd:" Forward */
3702 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3703 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3704 "Rif\\:", /* "Rif:" (Italian Outlook) */
3705 "Sv\\:", /* "Sv" (Norwegian) */
3706 "Vs\\:", /* "Vs" (Norwegian) */
3707 "Ad\\:", /* "Ad" (Norwegian) */
3708 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3711 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3715 if (!subject) return 0;
3716 if (!*subject) return 0;
3719 GString *s = g_string_new("");
3721 for (n = 0; n < PREFIXES; n++)
3722 /* Terminate each prefix regexpression by a
3723 * "\ ?" (zero or ONE space), and OR them */
3724 g_string_append_printf(s, "(%s\\ ?)%s",
3729 g_string_prepend(s, "(");
3730 g_string_append(s, ")+"); /* match at least once */
3731 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3734 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3735 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3736 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3737 debug_print("Error compiling regexp %s\n", s->str);
3738 g_string_free(s, TRUE);
3742 g_string_free(s, TRUE);
3746 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3751 /*!< Array with allowable reply prefixes regexps. */
3752 static const gchar * const prefixes[] = {
3754 "antw:", /* "Antw:" (Dutch / German Outlook) */
3755 "aw:", /* "Aw:" (German) */
3756 "antwort:", /* "Antwort:" (German Lotus Notes) */
3757 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3758 "fw:", /* "Fw:" Forward */
3759 "fwd:", /* "Fwd:" Forward */
3760 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3761 "odp:", /* "Odp:" Re (Polish Outlook) */
3762 "rif:", /* "Rif:" (Italian Outlook) */
3763 "sv:", /* "Sv" (Norwegian) */
3764 "vs:", /* "Vs" (Norwegian) */
3765 "ad:", /* "Ad" (Norwegian) */
3768 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3771 if (!subject) return 0;
3772 if (!*subject) return 0;
3774 for (n = 0; n < PREFIXES; n++) {
3775 int len = strlen(prefixes[n]);
3776 if (!strncasecmp(subject, prefixes[n], len))
3782 static guint g_stricase_hash(gconstpointer gptr)
3784 guint hash_result = 0;
3787 for (str = gptr; str && *str; str++) {
3788 hash_result += toupper(*str);
3794 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3796 const char *str1 = gptr1;
3797 const char *str2 = gptr2;
3799 return !strcasecmp(str1, str2);
3802 gint g_int_compare(gconstpointer a, gconstpointer b)
3804 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3807 gchar *generate_msgid(gchar *buf, gint len)
3817 lt = localtime_r(&t, &buft);
3819 if (strcmp(buf, "") == 0) {
3820 addr = g_strconcat("@", get_domain_name(), NULL);
3823 addr = g_strconcat("@", buf, NULL);
3826 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3827 lt->tm_year + 1900, lt->tm_mon + 1,
3828 lt->tm_mday, lt->tm_hour,
3829 lt->tm_min, lt->tm_sec,
3830 (guint) rand(), addr);
3837 quote_cmd_argument()
3839 return a quoted string safely usable in argument of a command.
3841 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3844 gint quote_cmd_argument(gchar * result, guint size,
3854 for(p = path ; * p != '\0' ; p ++) {
3856 if (isalnum((guchar)*p) || (* p == '/')) {
3857 if (remaining > 0) {
3863 result[size - 1] = '\0';
3868 if (remaining >= 2) {
3876 result[size - 1] = '\0';
3881 if (remaining > 0) {
3885 result[size - 1] = '\0';
3899 static void g_node_map_recursive(GNode *node, gpointer data)
3901 GNodeMapData *mapdata = (GNodeMapData *) data;
3903 GNodeMapData newmapdata;
3906 newdata = mapdata->func(node->data, mapdata->data);
3907 if (newdata != NULL) {
3908 newnode = g_node_new(newdata);
3909 g_node_append(mapdata->parent, newnode);
3911 newmapdata.parent = newnode;
3912 newmapdata.func = mapdata->func;
3913 newmapdata.data = mapdata->data;
3915 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3919 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3922 GNodeMapData mapdata;
3924 cm_return_val_if_fail(node != NULL, NULL);
3925 cm_return_val_if_fail(func != NULL, NULL);
3927 root = g_node_new(func(node->data, data));
3929 mapdata.parent = root;
3930 mapdata.func = func;
3931 mapdata.data = data;
3933 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3938 #define HEX_TO_INT(val, hex) \
3942 if ('0' <= c && c <= '9') { \
3944 } else if ('a' <= c && c <= 'f') { \
3945 val = c - 'a' + 10; \
3946 } else if ('A' <= c && c <= 'F') { \
3947 val = c - 'A' + 10; \
3953 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3960 if (hi == -1 || lo == -1)
3963 *out = (hi << 4) + lo;
3967 #define INT_TO_HEX(hex, val) \
3970 hex = '0' + (val); \
3972 hex = 'A' + (val) - 10; \
3975 void get_hex_str(gchar *out, guchar ch)
3979 INT_TO_HEX(hex, ch >> 4);
3981 INT_TO_HEX(hex, ch & 0x0f);
3987 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3989 #define G_PRINT_REF g_print
3993 *\brief Register ref counted pointer. It is based on GBoxed, so should
3994 * work with anything that uses the GType system. The semantics
3995 * are similar to a C++ auto pointer, with the exception that
3996 * C doesn't have automatic closure (calling destructors) when
3997 * exiting a block scope.
3998 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3999 * function directly.
4001 *\return GType A GType type.
4003 GType g_auto_pointer_register(void)
4005 static GType auto_pointer_type;
4006 if (!auto_pointer_type)
4008 g_boxed_type_register_static
4009 ("G_TYPE_AUTO_POINTER",
4010 (GBoxedCopyFunc) g_auto_pointer_copy,
4011 (GBoxedFreeFunc) g_auto_pointer_free);
4012 return auto_pointer_type;
4016 *\brief Structure with g_new() allocated pointer guarded by the
4019 typedef struct AutoPointerRef {
4020 void (*free) (gpointer);
4026 *\brief The auto pointer opaque structure that references the
4027 * pointer guard block.
4029 typedef struct AutoPointer {
4030 AutoPointerRef *ref;
4031 gpointer ptr; /*!< access to protected pointer */
4035 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4039 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4040 * ... when assigning, copying and freeing storage elements
4042 * gtk_list_store_new(N_S_COLUMNS,
4043 * G_TYPE_AUTO_POINTER,
4047 * Template *precious_data = g_new0(Template, 1);
4048 * g_pointer protect = g_auto_pointer_new(precious_data);
4050 * gtk_list_store_set(container, &iter,
4054 * ... the gtk_list_store has copied the pointer and
4055 * ... incremented its reference count, we should free
4056 * ... the auto pointer (in C++ a destructor would do
4057 * ... this for us when leaving block scope)
4059 * g_auto_pointer_free(protect);
4061 * ... gtk_list_store_set() now manages the data. When
4062 * ... *explicitly* requesting a pointer from the list
4063 * ... store, don't forget you get a copy that should be
4064 * ... freed with g_auto_pointer_free() eventually.
4068 *\param pointer Pointer to be guarded.
4070 *\return GAuto * Pointer that should be used in containers with
4073 GAuto *g_auto_pointer_new(gpointer p)
4075 AutoPointerRef *ref;
4081 ref = g_new0(AutoPointerRef, 1);
4082 ptr = g_new0(AutoPointer, 1);
4092 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4098 *\brief Allocate an autopointer using the passed \a free function to
4099 * free the guarded pointer
4101 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4108 aptr = g_auto_pointer_new(p);
4109 aptr->ref->free = free_;
4113 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4115 if (auto_ptr == NULL)
4117 return ((AutoPointer *) auto_ptr)->ptr;
4121 *\brief Copies an auto pointer by. It's mostly not necessary
4122 * to call this function directly, unless you copy/assign
4123 * the guarded pointer.
4125 *\param auto_ptr Auto pointer returned by previous call to
4126 * g_auto_pointer_new_XXX()
4128 *\return gpointer An auto pointer
4130 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4133 AutoPointerRef *ref;
4136 if (auto_ptr == NULL)
4141 newp = g_new0(AutoPointer, 1);
4144 newp->ptr = ref->pointer;
4148 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4154 *\brief Free an auto pointer
4156 void g_auto_pointer_free(GAuto *auto_ptr)
4159 AutoPointerRef *ref;
4161 if (auto_ptr == NULL)
4167 if (--(ref->cnt) == 0) {
4169 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4171 ref->free(ref->pointer);
4176 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4181 void replace_returns(gchar *str)
4186 while (strstr(str, "\n")) {
4187 *strstr(str, "\n") = ' ';
4189 while (strstr(str, "\r")) {
4190 *strstr(str, "\r") = ' ';
4194 /* get_uri_part() - retrieves a URI starting from scanpos.
4195 Returns TRUE if succesful */
4196 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4197 const gchar **bp, const gchar **ep, gboolean hdr)
4200 gint parenthese_cnt = 0;
4202 cm_return_val_if_fail(start != NULL, FALSE);
4203 cm_return_val_if_fail(scanpos != NULL, FALSE);
4204 cm_return_val_if_fail(bp != NULL, FALSE);
4205 cm_return_val_if_fail(ep != NULL, FALSE);
4209 /* find end point of URI */
4210 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4211 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4212 !IS_ASCII(*(const guchar *)ep_) ||
4213 strchr("[]{}<>\"", *ep_)) {
4215 } else if (strchr("(", *ep_)) {
4217 } else if (strchr(")", *ep_)) {
4218 if (parenthese_cnt > 0)
4225 /* no punctuation at end of string */
4227 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4228 * should pass some URI type to this function and decide on that whether
4229 * to perform punctuation stripping */
4231 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4233 for (; ep_ - 1 > scanpos + 1 &&
4234 IS_REAL_PUNCT(*(ep_ - 1));
4238 #undef IS_REAL_PUNCT
4245 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4247 while (bp && *bp && g_ascii_isspace(*bp))
4249 return g_strndup(bp, ep - bp);
4252 /* valid mail address characters */
4253 #define IS_RFC822_CHAR(ch) \
4257 !g_ascii_isspace(ch) && \
4258 !strchr("(),;<>\"", (ch)))
4260 /* alphabet and number within 7bit ASCII */
4261 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4262 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4264 static GHashTable *create_domain_tab(void)
4266 static const gchar *toplvl_domains [] = {
4268 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4269 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4270 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4271 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4272 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4273 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4274 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4275 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4276 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4277 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4278 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4279 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4280 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4281 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4282 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4283 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4284 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4285 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4286 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4287 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4288 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4289 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4290 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4291 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4292 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4293 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4296 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4298 cm_return_val_if_fail(htab, NULL);
4299 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4300 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4304 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4306 const gint MAX_LVL_DOM_NAME_LEN = 6;
4307 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4308 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4311 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4314 for (p = buf; p < m && first < last; *p++ = *first++)
4318 return g_hash_table_lookup(tab, buf) != NULL;
4321 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4322 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4323 const gchar **bp, const gchar **ep, gboolean hdr)
4325 /* more complex than the uri part because we need to scan back and forward starting from
4326 * the scan position. */
4327 gboolean result = FALSE;
4328 const gchar *bp_ = NULL;
4329 const gchar *ep_ = NULL;
4330 static GHashTable *dom_tab;
4331 const gchar *last_dot = NULL;
4332 const gchar *prelast_dot = NULL;
4333 const gchar *last_tld_char = NULL;
4335 /* the informative part of the email address (describing the name
4336 * of the email address owner) may contain quoted parts. the
4337 * closure stack stores the last encountered quotes. */
4338 gchar closure_stack[128];
4339 gchar *ptr = closure_stack;
4341 cm_return_val_if_fail(start != NULL, FALSE);
4342 cm_return_val_if_fail(scanpos != NULL, FALSE);
4343 cm_return_val_if_fail(bp != NULL, FALSE);
4344 cm_return_val_if_fail(ep != NULL, FALSE);
4347 const gchar *start_quote = NULL;
4348 const gchar *end_quote = NULL;
4350 /* go to the real start */
4351 if (start[0] == ',')
4353 if (start[0] == ';')
4355 while (start[0] == '\n' || start[0] == '\r')
4357 while (start[0] == ' ' || start[0] == '\t')
4362 /* check if there are quotes (to skip , in them) */
4363 if (*start == '"') {
4364 start_quote = start;
4366 end_quote = strstr(start, "\"");
4372 /* skip anything between quotes */
4373 if (start_quote && end_quote) {
4378 /* find end (either , or ; or end of line) */
4379 if (strstr(start, ",") && strstr(start, ";"))
4380 *ep = strstr(start,",") < strstr(start, ";")
4381 ? strstr(start, ",") : strstr(start, ";");
4382 else if (strstr(start, ","))
4383 *ep = strstr(start, ",");
4384 else if (strstr(start, ";"))
4385 *ep = strstr(start, ";");
4387 *ep = start+strlen(start);
4389 /* go back to real start */
4390 if (start_quote && end_quote) {
4391 start = start_quote;
4394 /* check there's still an @ in that, or search
4395 * further if possible */
4396 if (strstr(start, "@") && strstr(start, "@") < *ep)
4398 else if (*ep < start+strlen(start)) {
4401 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4409 dom_tab = create_domain_tab();
4410 cm_return_val_if_fail(dom_tab, FALSE);
4412 /* scan start of address */
4413 for (bp_ = scanpos - 1;
4414 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4417 /* TODO: should start with an alnum? */
4419 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4422 if (bp_ != scanpos) {
4423 /* scan end of address */
4424 for (ep_ = scanpos + 1;
4425 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4427 prelast_dot = last_dot;
4429 if (*(last_dot + 1) == '.') {
4430 if (prelast_dot == NULL)
4432 last_dot = prelast_dot;
4437 /* TODO: really should terminate with an alnum? */
4438 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4443 if (last_dot == NULL)
4445 if (last_dot >= ep_)
4446 last_dot = prelast_dot;
4447 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4451 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4452 if (*last_tld_char == '?')
4455 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4462 if (!result) return FALSE;
4464 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4465 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4466 && IS_RFC822_CHAR(*(ep_ + 3))) {
4467 /* this informative part with an @ in it is
4468 * followed by the email address */
4471 /* go to matching '>' (or next non-rfc822 char, like \n) */
4472 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4475 /* include the bracket */
4476 if (*ep_ == '>') ep_++;
4478 /* include the leading quote */
4486 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4487 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4490 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4491 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4494 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4495 #define IN_STACK() (ptr > closure_stack)
4496 /* has underrun check */
4497 #define POP_STACK() if(IN_STACK()) --ptr
4498 /* has overrun check */
4499 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4500 /* has underrun check */
4501 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4505 /* scan for the informative part. */
4506 for (bp_ -= 2; bp_ >= start; bp_--) {
4507 /* if closure on the stack keep scanning */
4508 if (PEEK_STACK() == *bp_) {
4512 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4517 /* if nothing in the closure stack, do the special conditions
4518 * the following if..else expression simply checks whether
4519 * a token is acceptable. if not acceptable, the clause
4520 * should terminate the loop with a 'break' */
4521 if (!PEEK_STACK()) {
4523 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4524 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4525 /* hyphens are allowed, but only in
4527 } else if (strchr(" \"'", *bp_)) {
4528 /* but anything not being a punctiation
4531 break; /* anything else is rejected */
4538 /* scan forward (should start with an alnum) */
4539 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4555 #undef IS_ASCII_ALNUM
4556 #undef IS_RFC822_CHAR
4558 gchar *make_email_string(const gchar *bp, const gchar *ep)
4560 /* returns a mailto: URI; mailto: is also used to detect the
4561 * uri type later on in the button_pressed signal handler */
4565 tmp = g_strndup(bp, ep - bp);
4566 result = g_strconcat("mailto:", tmp, NULL);
4572 gchar *make_http_string(const gchar *bp, const gchar *ep)
4574 /* returns an http: URI; */
4578 while (bp && *bp && g_ascii_isspace(*bp))
4580 tmp = g_strndup(bp, ep - bp);
4581 result = g_strconcat("http://", tmp, NULL);
4587 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4589 FILE *fp = g_fopen(path, "rb");
4590 gchar buf[BUFFSIZE];
4591 gchar *result = NULL;
4594 while (fgets(buf, sizeof (buf), fp) != NULL) {
4595 gchar **parts = g_strsplit(buf, ";", 3);
4596 gchar *trimmed = parts[0];
4597 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4599 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4600 trimmed[strlen(trimmed)-1] = '\0';
4602 if (!strcmp(trimmed, type)) {
4603 gboolean needsterminal = FALSE;
4604 if (parts[2] && strstr(parts[2], "needsterminal")) {
4605 needsterminal = TRUE;
4607 if (parts[2] && strstr(parts[2], "test=")) {
4608 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4609 gchar *testcmd = orig_testcmd;
4610 if (strstr(testcmd,";"))
4611 *(strstr(testcmd,";")) = '\0';
4612 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4614 while (testcmd[strlen(testcmd)-1] == '\n')
4615 testcmd[strlen(testcmd)-1] = '\0';
4616 while (testcmd[strlen(testcmd)-1] == '\r')
4617 testcmd[strlen(testcmd)-1] = '\0';
4618 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4619 testcmd[strlen(testcmd)-1] = '\0';
4621 if (strstr(testcmd, "%s")) {
4622 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4623 gint res = system(tmp);
4625 g_free(orig_testcmd);
4632 gint res = system(testcmd);
4633 g_free(orig_testcmd);
4643 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4645 while (trimmed[strlen(trimmed)-1] == '\n')
4646 trimmed[strlen(trimmed)-1] = '\0';
4647 while (trimmed[strlen(trimmed)-1] == '\r')
4648 trimmed[strlen(trimmed)-1] = '\0';
4649 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4650 trimmed[strlen(trimmed)-1] = '\0';
4651 result = g_strdup(trimmed);
4654 /* if there are no single quotes around %s, add them.
4655 * '.*%s.*' is ok, as in display 'png:%s'
4657 if (strstr(result, "%s")
4658 && !(strstr(result, "'") < strstr(result,"%s") &&
4659 strstr(strstr(result,"%s"), "'"))) {
4660 gchar *start = g_strdup(result);
4661 gchar *end = g_strdup(strstr(result, "%s")+2);
4663 *strstr(start, "%s") = '\0';
4664 tmp = g_strconcat(start,"'%s'",end, NULL);
4670 if (needsterminal) {
4671 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4682 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4684 gchar *result = NULL;
4688 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4689 result = mailcap_get_command_in_file(path, type, file_to_open);
4693 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4697 void mailcap_update_default(const gchar *type, const gchar *command)
4699 gchar *path = NULL, *outpath = NULL;
4700 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4701 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4702 FILE *fp = g_fopen(path, "rb");
4703 FILE *outfp = g_fopen(outpath, "wb");
4704 gchar buf[BUFFSIZE];
4705 gboolean err = FALSE;
4713 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4714 gchar **parts = g_strsplit(buf, ";", 3);
4715 gchar *trimmed = parts[0];
4716 while (trimmed[0] == ' ')
4718 while (trimmed[strlen(trimmed)-1] == ' ')
4719 trimmed[strlen(trimmed)-1] = '\0';
4721 if (!strcmp(trimmed, type)) {
4726 if(fputs(buf, outfp) == EOF) {
4733 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4739 if (fclose(outfp) == EOF)
4743 g_rename(outpath, path);
4749 gint copy_dir(const gchar *src, const gchar *dst)
4754 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4755 g_warning("failed to open directory: %s\n", src);
4759 if (make_dir(dst) < 0)
4762 while ((name = g_dir_read_name(dir)) != NULL) {
4763 gchar *old_file, *new_file;
4764 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4765 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4766 debug_print("copying: %s -> %s\n", old_file, new_file);
4767 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4768 gint r = copy_file(old_file, new_file, TRUE);
4775 /* Windows has no symlinks. Or well, Vista seems to
4776 have something like this but the semantics might be
4777 different. Thus we don't use it under Windows. */
4778 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4781 gchar *target = g_file_read_link(old_file, &error);
4783 r = symlink(target, new_file);
4790 #endif /*G_OS_WIN32*/
4791 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4792 gint r = copy_dir(old_file, new_file);
4803 /* crude test to see if a file is an email. */
4804 gboolean file_is_email (const gchar *filename)
4810 if (filename == NULL)
4812 if ((fp = g_fopen(filename, "rb")) == NULL)
4814 while (i < 60 && score < 3
4815 && fgets(buffer, sizeof (buffer), fp) > 0) {
4816 if (!strncmp(buffer, "From:", strlen("From:")))
4818 if (!strncmp(buffer, "To:", strlen("To:")))
4820 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4825 return (score >= 3);
4828 gboolean sc_g_list_bigger(GList *list, gint max)
4832 while (cur && i <= max+1) {
4839 gboolean sc_g_slist_bigger(GSList *list, gint max)
4843 while (cur && i <= max+1) {
4850 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4851 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4852 NULL, NULL, NULL, NULL, NULL, NULL};
4853 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4854 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4855 NULL, NULL, NULL, NULL, NULL, NULL};
4857 gint daynames_len[] = {0,0,0,0,0,0,0};
4858 gint monthnames_len[] = {0,0,0,0,0,0,
4860 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4861 gint s_monthnames_len[] = {0,0,0,0,0,0,
4863 const gchar *s_am_up = NULL;
4864 const gchar *s_pm_up = NULL;
4865 const gchar *s_am_low = NULL;
4866 const gchar *s_pm_low = NULL;
4868 gint s_am_up_len = 0;
4869 gint s_pm_up_len = 0;
4870 gint s_am_low_len = 0;
4871 gint s_pm_low_len = 0;
4873 const gchar *def_loc_format = NULL;
4874 const gchar *date_loc_format = NULL;
4875 const gchar *time_loc_format = NULL;
4876 const gchar *time_am_pm = NULL;
4878 static gboolean time_names_init_done = FALSE;
4880 static void init_time_names(void)
4884 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4885 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4886 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4887 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4888 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4889 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4890 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4892 monthnames[0] = Q_("Complete month name for use by strftime|January");
4893 monthnames[1] = Q_("Complete month name for use by strftime|February");
4894 monthnames[2] = Q_("Complete month name for use by strftime|March");
4895 monthnames[3] = Q_("Complete month name for use by strftime|April");
4896 monthnames[4] = Q_("Complete month name for use by strftime|May");
4897 monthnames[5] = Q_("Complete month name for use by strftime|June");
4898 monthnames[6] = Q_("Complete month name for use by strftime|July");
4899 monthnames[7] = Q_("Complete month name for use by strftime|August");
4900 monthnames[8] = Q_("Complete month name for use by strftime|September");
4901 monthnames[9] = Q_("Complete month name for use by strftime|October");
4902 monthnames[10] = Q_("Complete month name for use by strftime|November");
4903 monthnames[11] = Q_("Complete month name for use by strftime|December");
4905 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4906 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4907 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4908 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4909 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4910 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4911 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4913 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4914 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4915 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4916 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4917 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4918 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4919 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4920 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4921 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4922 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4923 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4924 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4926 for (i = 0; i < 7; i++) {
4927 daynames_len[i] = strlen(daynames[i]);
4928 s_daynames_len[i] = strlen(s_daynames[i]);
4930 for (i = 0; i < 12; i++) {
4931 monthnames_len[i] = strlen(monthnames[i]);
4932 s_monthnames_len[i] = strlen(s_monthnames[i]);
4935 s_am_up = Q_("For use by strftime (morning)|AM");
4936 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4937 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4938 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4940 s_am_up_len = strlen(s_am_up);
4941 s_pm_up_len = strlen(s_pm_up);
4942 s_am_low_len = strlen(s_am_low);
4943 s_pm_low_len = strlen(s_pm_low);
4945 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4946 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4947 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4949 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4951 time_names_init_done = TRUE;
4954 #define CHECK_SIZE() { \
4955 total_done += len; \
4956 if (total_done >= buflen) { \
4957 buf[buflen-1] = '\0'; \
4962 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4964 gchar *curpos = buf;
4965 gint total_done = 0;
4966 gchar subbuf[64], subfmt[64];
4967 static time_t last_tzset = (time_t)0;
4969 if (!time_names_init_done)
4972 if (format == NULL || lt == NULL)
4975 if (last_tzset != time(NULL)) {
4977 last_tzset = time(NULL);
4980 if (*format == '%') {
4981 gint len = 0, tmp = 0;
4985 len = 1; CHECK_SIZE();
4989 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4990 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4993 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4994 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4998 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4999 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5002 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5003 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5006 fast_strftime(subbuf, 64, def_loc_format, lt);
5007 len = strlen(subbuf); CHECK_SIZE();
5008 strncpy2(curpos, subbuf, buflen - total_done);
5011 total_done += 2; CHECK_SIZE();
5012 tmp = (lt->tm_year + 1900)/100;
5013 *curpos++ = '0'+(tmp / 10);
5014 *curpos++ = '0'+(tmp % 10);
5017 total_done += 2; CHECK_SIZE();
5018 *curpos++ = '0'+(lt->tm_mday / 10);
5019 *curpos++ = '0'+(lt->tm_mday % 10);
5022 total_done += 8; CHECK_SIZE();
5023 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5024 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5026 *curpos++ = '0'+(lt->tm_mday / 10);
5027 *curpos++ = '0'+(lt->tm_mday % 10);
5029 tmp = lt->tm_year%100;
5030 *curpos++ = '0'+(tmp / 10);
5031 *curpos++ = '0'+(tmp % 10);
5034 len = 2; CHECK_SIZE();
5035 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5038 len = 10; CHECK_SIZE();
5039 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5040 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5043 total_done += 2; CHECK_SIZE();
5044 *curpos++ = '0'+(lt->tm_hour / 10);
5045 *curpos++ = '0'+(lt->tm_hour % 10);
5048 total_done += 2; CHECK_SIZE();
5054 *curpos++ = '0'+(tmp / 10);
5055 *curpos++ = '0'+(tmp % 10);
5058 len = 3; CHECK_SIZE();
5059 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5062 len = 2; CHECK_SIZE();
5063 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5066 len = 2; CHECK_SIZE();
5072 snprintf(curpos, buflen - total_done, "%2d", tmp);
5075 total_done += 2; CHECK_SIZE();
5076 tmp = lt->tm_mon + 1;
5077 *curpos++ = '0'+(tmp / 10);
5078 *curpos++ = '0'+(tmp % 10);
5081 total_done += 2; CHECK_SIZE();
5082 *curpos++ = '0'+(lt->tm_min / 10);
5083 *curpos++ = '0'+(lt->tm_min % 10);
5086 len = 1; CHECK_SIZE();
5090 if (lt->tm_hour >= 12) {
5091 len = s_pm_up_len; CHECK_SIZE();
5092 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5094 len = s_am_up_len; CHECK_SIZE();
5095 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5099 if (lt->tm_hour >= 12) {
5100 len = s_pm_low_len; CHECK_SIZE();
5101 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5103 len = s_am_low_len; CHECK_SIZE();
5104 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5108 fast_strftime(subbuf, 64, time_am_pm, lt);
5109 len = strlen(subbuf); CHECK_SIZE();
5110 strncpy2(curpos, subbuf, buflen - total_done);
5113 total_done += 5; CHECK_SIZE();
5114 *curpos++ = '0'+(lt->tm_hour / 10);
5115 *curpos++ = '0'+(lt->tm_hour % 10);
5117 *curpos++ = '0'+(lt->tm_min / 10);
5118 *curpos++ = '0'+(lt->tm_min % 10);
5121 snprintf(subbuf, 64, "%ld", mktime(lt));
5122 len = strlen(subbuf); CHECK_SIZE();
5123 strncpy2(curpos, subbuf, buflen - total_done);
5126 total_done += 2; CHECK_SIZE();
5127 *curpos++ = '0'+(lt->tm_sec / 10);
5128 *curpos++ = '0'+(lt->tm_sec % 10);
5131 len = 1; CHECK_SIZE();
5135 total_done += 8; CHECK_SIZE();
5136 *curpos++ = '0'+(lt->tm_hour / 10);
5137 *curpos++ = '0'+(lt->tm_hour % 10);
5139 *curpos++ = '0'+(lt->tm_min / 10);
5140 *curpos++ = '0'+(lt->tm_min % 10);
5142 *curpos++ = '0'+(lt->tm_sec / 10);
5143 *curpos++ = '0'+(lt->tm_sec % 10);
5146 len = 1; CHECK_SIZE();
5147 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5150 len = 1; CHECK_SIZE();
5151 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5154 fast_strftime(subbuf, 64, date_loc_format, lt);
5155 len = strlen(subbuf); CHECK_SIZE();
5156 strncpy2(curpos, subbuf, buflen - total_done);
5159 fast_strftime(subbuf, 64, time_loc_format, lt);
5160 len = strlen(subbuf); CHECK_SIZE();
5161 strncpy2(curpos, subbuf, buflen - total_done);
5164 total_done += 2; CHECK_SIZE();
5165 tmp = lt->tm_year%100;
5166 *curpos++ = '0'+(tmp / 10);
5167 *curpos++ = '0'+(tmp % 10);
5170 len = 4; CHECK_SIZE();
5171 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5181 /* let these complicated ones be done with the libc */
5182 snprintf(subfmt, 64, "%%%c", *format);
5183 strftime(subbuf, 64, subfmt, lt);
5184 len = strlen(subbuf); CHECK_SIZE();
5185 strncpy2(curpos, subbuf, buflen - total_done);
5189 /* let these complicated modifiers be done with the libc */
5190 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5191 strftime(subbuf, 64, subfmt, lt);
5192 len = strlen(subbuf); CHECK_SIZE();
5193 strncpy2(curpos, subbuf, buflen - total_done);
5197 if (format && *format)
5198 g_warning("format error (%c)", *format);
5205 int len = 1; CHECK_SIZE();
5206 *curpos++ = *format++;
5213 gboolean prefs_common_get_use_shred(void);
5217 #define WEXITSTATUS(x) (x)
5220 int claws_unlink(const gchar *filename)
5223 static int found_shred = -1;
5224 static const gchar *args[4];
5226 if (filename == NULL)
5229 if (prefs_common_get_use_shred()) {
5230 if (found_shred == -1) {
5232 args[0] = g_find_program_in_path("shred");
5233 debug_print("found shred: %s\n", args[0]);
5234 found_shred = (args[0] != NULL) ? 1:0;
5238 if (found_shred == 1) {
5239 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5240 if (s.st_nlink == 1) {
5243 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5244 NULL, NULL, NULL, NULL, &status, NULL);
5245 debug_print("%s %s exited with status %d\n",
5246 args[0], filename, WEXITSTATUS(status));
5247 if (truncate(filename, 0) < 0)
5248 g_warning("couln't truncate");
5253 return g_unlink(filename);