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)) {
3777 if (subject[len] == ' ')
3786 static guint g_stricase_hash(gconstpointer gptr)
3788 guint hash_result = 0;
3791 for (str = gptr; str && *str; str++) {
3792 hash_result += toupper(*str);
3798 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3800 const char *str1 = gptr1;
3801 const char *str2 = gptr2;
3803 return !strcasecmp(str1, str2);
3806 gint g_int_compare(gconstpointer a, gconstpointer b)
3808 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3811 gchar *generate_msgid(gchar *buf, gint len)
3821 lt = localtime_r(&t, &buft);
3823 if (strcmp(buf, "") == 0) {
3824 addr = g_strconcat("@", get_domain_name(), NULL);
3827 addr = g_strconcat("@", buf, NULL);
3830 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3831 lt->tm_year + 1900, lt->tm_mon + 1,
3832 lt->tm_mday, lt->tm_hour,
3833 lt->tm_min, lt->tm_sec,
3834 (guint) rand(), addr);
3841 quote_cmd_argument()
3843 return a quoted string safely usable in argument of a command.
3845 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3848 gint quote_cmd_argument(gchar * result, guint size,
3858 for(p = path ; * p != '\0' ; p ++) {
3860 if (isalnum((guchar)*p) || (* p == '/')) {
3861 if (remaining > 0) {
3867 result[size - 1] = '\0';
3872 if (remaining >= 2) {
3880 result[size - 1] = '\0';
3885 if (remaining > 0) {
3889 result[size - 1] = '\0';
3903 static void g_node_map_recursive(GNode *node, gpointer data)
3905 GNodeMapData *mapdata = (GNodeMapData *) data;
3907 GNodeMapData newmapdata;
3910 newdata = mapdata->func(node->data, mapdata->data);
3911 if (newdata != NULL) {
3912 newnode = g_node_new(newdata);
3913 g_node_append(mapdata->parent, newnode);
3915 newmapdata.parent = newnode;
3916 newmapdata.func = mapdata->func;
3917 newmapdata.data = mapdata->data;
3919 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3923 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3926 GNodeMapData mapdata;
3928 cm_return_val_if_fail(node != NULL, NULL);
3929 cm_return_val_if_fail(func != NULL, NULL);
3931 root = g_node_new(func(node->data, data));
3933 mapdata.parent = root;
3934 mapdata.func = func;
3935 mapdata.data = data;
3937 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3942 #define HEX_TO_INT(val, hex) \
3946 if ('0' <= c && c <= '9') { \
3948 } else if ('a' <= c && c <= 'f') { \
3949 val = c - 'a' + 10; \
3950 } else if ('A' <= c && c <= 'F') { \
3951 val = c - 'A' + 10; \
3957 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3964 if (hi == -1 || lo == -1)
3967 *out = (hi << 4) + lo;
3971 #define INT_TO_HEX(hex, val) \
3974 hex = '0' + (val); \
3976 hex = 'A' + (val) - 10; \
3979 void get_hex_str(gchar *out, guchar ch)
3983 INT_TO_HEX(hex, ch >> 4);
3985 INT_TO_HEX(hex, ch & 0x0f);
3991 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3993 #define G_PRINT_REF g_print
3997 *\brief Register ref counted pointer. It is based on GBoxed, so should
3998 * work with anything that uses the GType system. The semantics
3999 * are similar to a C++ auto pointer, with the exception that
4000 * C doesn't have automatic closure (calling destructors) when
4001 * exiting a block scope.
4002 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4003 * function directly.
4005 *\return GType A GType type.
4007 GType g_auto_pointer_register(void)
4009 static GType auto_pointer_type;
4010 if (!auto_pointer_type)
4012 g_boxed_type_register_static
4013 ("G_TYPE_AUTO_POINTER",
4014 (GBoxedCopyFunc) g_auto_pointer_copy,
4015 (GBoxedFreeFunc) g_auto_pointer_free);
4016 return auto_pointer_type;
4020 *\brief Structure with g_new() allocated pointer guarded by the
4023 typedef struct AutoPointerRef {
4024 void (*free) (gpointer);
4030 *\brief The auto pointer opaque structure that references the
4031 * pointer guard block.
4033 typedef struct AutoPointer {
4034 AutoPointerRef *ref;
4035 gpointer ptr; /*!< access to protected pointer */
4039 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4043 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4044 * ... when assigning, copying and freeing storage elements
4046 * gtk_list_store_new(N_S_COLUMNS,
4047 * G_TYPE_AUTO_POINTER,
4051 * Template *precious_data = g_new0(Template, 1);
4052 * g_pointer protect = g_auto_pointer_new(precious_data);
4054 * gtk_list_store_set(container, &iter,
4058 * ... the gtk_list_store has copied the pointer and
4059 * ... incremented its reference count, we should free
4060 * ... the auto pointer (in C++ a destructor would do
4061 * ... this for us when leaving block scope)
4063 * g_auto_pointer_free(protect);
4065 * ... gtk_list_store_set() now manages the data. When
4066 * ... *explicitly* requesting a pointer from the list
4067 * ... store, don't forget you get a copy that should be
4068 * ... freed with g_auto_pointer_free() eventually.
4072 *\param pointer Pointer to be guarded.
4074 *\return GAuto * Pointer that should be used in containers with
4077 GAuto *g_auto_pointer_new(gpointer p)
4079 AutoPointerRef *ref;
4085 ref = g_new0(AutoPointerRef, 1);
4086 ptr = g_new0(AutoPointer, 1);
4096 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4102 *\brief Allocate an autopointer using the passed \a free function to
4103 * free the guarded pointer
4105 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4112 aptr = g_auto_pointer_new(p);
4113 aptr->ref->free = free_;
4117 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4119 if (auto_ptr == NULL)
4121 return ((AutoPointer *) auto_ptr)->ptr;
4125 *\brief Copies an auto pointer by. It's mostly not necessary
4126 * to call this function directly, unless you copy/assign
4127 * the guarded pointer.
4129 *\param auto_ptr Auto pointer returned by previous call to
4130 * g_auto_pointer_new_XXX()
4132 *\return gpointer An auto pointer
4134 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4137 AutoPointerRef *ref;
4140 if (auto_ptr == NULL)
4145 newp = g_new0(AutoPointer, 1);
4148 newp->ptr = ref->pointer;
4152 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4158 *\brief Free an auto pointer
4160 void g_auto_pointer_free(GAuto *auto_ptr)
4163 AutoPointerRef *ref;
4165 if (auto_ptr == NULL)
4171 if (--(ref->cnt) == 0) {
4173 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4175 ref->free(ref->pointer);
4180 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4185 void replace_returns(gchar *str)
4190 while (strstr(str, "\n")) {
4191 *strstr(str, "\n") = ' ';
4193 while (strstr(str, "\r")) {
4194 *strstr(str, "\r") = ' ';
4198 /* get_uri_part() - retrieves a URI starting from scanpos.
4199 Returns TRUE if succesful */
4200 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4201 const gchar **bp, const gchar **ep, gboolean hdr)
4204 gint parenthese_cnt = 0;
4206 cm_return_val_if_fail(start != NULL, FALSE);
4207 cm_return_val_if_fail(scanpos != NULL, FALSE);
4208 cm_return_val_if_fail(bp != NULL, FALSE);
4209 cm_return_val_if_fail(ep != NULL, FALSE);
4213 /* find end point of URI */
4214 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4215 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4216 !IS_ASCII(*(const guchar *)ep_) ||
4217 strchr("[]{}<>\"", *ep_)) {
4219 } else if (strchr("(", *ep_)) {
4221 } else if (strchr(")", *ep_)) {
4222 if (parenthese_cnt > 0)
4229 /* no punctuation at end of string */
4231 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4232 * should pass some URI type to this function and decide on that whether
4233 * to perform punctuation stripping */
4235 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4237 for (; ep_ - 1 > scanpos + 1 &&
4238 IS_REAL_PUNCT(*(ep_ - 1));
4242 #undef IS_REAL_PUNCT
4249 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4251 while (bp && *bp && g_ascii_isspace(*bp))
4253 return g_strndup(bp, ep - bp);
4256 /* valid mail address characters */
4257 #define IS_RFC822_CHAR(ch) \
4261 !g_ascii_isspace(ch) && \
4262 !strchr("(),;<>\"", (ch)))
4264 /* alphabet and number within 7bit ASCII */
4265 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4266 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4268 static GHashTable *create_domain_tab(void)
4270 static const gchar *toplvl_domains [] = {
4272 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4273 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4274 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4275 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4276 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4277 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4278 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4279 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4280 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4281 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4282 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4283 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4284 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4285 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4286 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4287 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4288 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4289 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4290 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4291 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4292 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4293 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4294 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4295 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4296 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4297 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4300 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4302 cm_return_val_if_fail(htab, NULL);
4303 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4304 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4308 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4310 const gint MAX_LVL_DOM_NAME_LEN = 6;
4311 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4312 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4315 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4318 for (p = buf; p < m && first < last; *p++ = *first++)
4322 return g_hash_table_lookup(tab, buf) != NULL;
4325 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4326 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4327 const gchar **bp, const gchar **ep, gboolean hdr)
4329 /* more complex than the uri part because we need to scan back and forward starting from
4330 * the scan position. */
4331 gboolean result = FALSE;
4332 const gchar *bp_ = NULL;
4333 const gchar *ep_ = NULL;
4334 static GHashTable *dom_tab;
4335 const gchar *last_dot = NULL;
4336 const gchar *prelast_dot = NULL;
4337 const gchar *last_tld_char = NULL;
4339 /* the informative part of the email address (describing the name
4340 * of the email address owner) may contain quoted parts. the
4341 * closure stack stores the last encountered quotes. */
4342 gchar closure_stack[128];
4343 gchar *ptr = closure_stack;
4345 cm_return_val_if_fail(start != NULL, FALSE);
4346 cm_return_val_if_fail(scanpos != NULL, FALSE);
4347 cm_return_val_if_fail(bp != NULL, FALSE);
4348 cm_return_val_if_fail(ep != NULL, FALSE);
4351 const gchar *start_quote = NULL;
4352 const gchar *end_quote = NULL;
4354 /* go to the real start */
4355 if (start[0] == ',')
4357 if (start[0] == ';')
4359 while (start[0] == '\n' || start[0] == '\r')
4361 while (start[0] == ' ' || start[0] == '\t')
4366 /* check if there are quotes (to skip , in them) */
4367 if (*start == '"') {
4368 start_quote = start;
4370 end_quote = strstr(start, "\"");
4376 /* skip anything between quotes */
4377 if (start_quote && end_quote) {
4382 /* find end (either , or ; or end of line) */
4383 if (strstr(start, ",") && strstr(start, ";"))
4384 *ep = strstr(start,",") < strstr(start, ";")
4385 ? strstr(start, ",") : strstr(start, ";");
4386 else if (strstr(start, ","))
4387 *ep = strstr(start, ",");
4388 else if (strstr(start, ";"))
4389 *ep = strstr(start, ";");
4391 *ep = start+strlen(start);
4393 /* go back to real start */
4394 if (start_quote && end_quote) {
4395 start = start_quote;
4398 /* check there's still an @ in that, or search
4399 * further if possible */
4400 if (strstr(start, "@") && strstr(start, "@") < *ep)
4402 else if (*ep < start+strlen(start)) {
4405 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4413 dom_tab = create_domain_tab();
4414 cm_return_val_if_fail(dom_tab, FALSE);
4416 /* scan start of address */
4417 for (bp_ = scanpos - 1;
4418 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4421 /* TODO: should start with an alnum? */
4423 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4426 if (bp_ != scanpos) {
4427 /* scan end of address */
4428 for (ep_ = scanpos + 1;
4429 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4431 prelast_dot = last_dot;
4433 if (*(last_dot + 1) == '.') {
4434 if (prelast_dot == NULL)
4436 last_dot = prelast_dot;
4441 /* TODO: really should terminate with an alnum? */
4442 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4447 if (last_dot == NULL)
4449 if (last_dot >= ep_)
4450 last_dot = prelast_dot;
4451 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4455 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4456 if (*last_tld_char == '?')
4459 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4466 if (!result) return FALSE;
4468 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4469 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4470 && IS_RFC822_CHAR(*(ep_ + 3))) {
4471 /* this informative part with an @ in it is
4472 * followed by the email address */
4475 /* go to matching '>' (or next non-rfc822 char, like \n) */
4476 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4479 /* include the bracket */
4480 if (*ep_ == '>') ep_++;
4482 /* include the leading quote */
4490 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4491 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4494 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4495 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4498 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4499 #define IN_STACK() (ptr > closure_stack)
4500 /* has underrun check */
4501 #define POP_STACK() if(IN_STACK()) --ptr
4502 /* has overrun check */
4503 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4504 /* has underrun check */
4505 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4509 /* scan for the informative part. */
4510 for (bp_ -= 2; bp_ >= start; bp_--) {
4511 /* if closure on the stack keep scanning */
4512 if (PEEK_STACK() == *bp_) {
4516 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4521 /* if nothing in the closure stack, do the special conditions
4522 * the following if..else expression simply checks whether
4523 * a token is acceptable. if not acceptable, the clause
4524 * should terminate the loop with a 'break' */
4525 if (!PEEK_STACK()) {
4527 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4528 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4529 /* hyphens are allowed, but only in
4531 } else if (strchr(" \"'", *bp_)) {
4532 /* but anything not being a punctiation
4535 break; /* anything else is rejected */
4542 /* scan forward (should start with an alnum) */
4543 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4559 #undef IS_ASCII_ALNUM
4560 #undef IS_RFC822_CHAR
4562 gchar *make_email_string(const gchar *bp, const gchar *ep)
4564 /* returns a mailto: URI; mailto: is also used to detect the
4565 * uri type later on in the button_pressed signal handler */
4569 tmp = g_strndup(bp, ep - bp);
4570 result = g_strconcat("mailto:", tmp, NULL);
4576 gchar *make_http_string(const gchar *bp, const gchar *ep)
4578 /* returns an http: URI; */
4582 while (bp && *bp && g_ascii_isspace(*bp))
4584 tmp = g_strndup(bp, ep - bp);
4585 result = g_strconcat("http://", tmp, NULL);
4591 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4593 FILE *fp = g_fopen(path, "rb");
4594 gchar buf[BUFFSIZE];
4595 gchar *result = NULL;
4598 while (fgets(buf, sizeof (buf), fp) != NULL) {
4599 gchar **parts = g_strsplit(buf, ";", 3);
4600 gchar *trimmed = parts[0];
4601 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4603 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4604 trimmed[strlen(trimmed)-1] = '\0';
4606 if (!strcmp(trimmed, type)) {
4607 gboolean needsterminal = FALSE;
4608 if (parts[2] && strstr(parts[2], "needsterminal")) {
4609 needsterminal = TRUE;
4611 if (parts[2] && strstr(parts[2], "test=")) {
4612 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4613 gchar *testcmd = orig_testcmd;
4614 if (strstr(testcmd,";"))
4615 *(strstr(testcmd,";")) = '\0';
4616 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4618 while (testcmd[strlen(testcmd)-1] == '\n')
4619 testcmd[strlen(testcmd)-1] = '\0';
4620 while (testcmd[strlen(testcmd)-1] == '\r')
4621 testcmd[strlen(testcmd)-1] = '\0';
4622 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4623 testcmd[strlen(testcmd)-1] = '\0';
4625 if (strstr(testcmd, "%s")) {
4626 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4627 gint res = system(tmp);
4629 g_free(orig_testcmd);
4636 gint res = system(testcmd);
4637 g_free(orig_testcmd);
4647 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4649 while (trimmed[strlen(trimmed)-1] == '\n')
4650 trimmed[strlen(trimmed)-1] = '\0';
4651 while (trimmed[strlen(trimmed)-1] == '\r')
4652 trimmed[strlen(trimmed)-1] = '\0';
4653 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4654 trimmed[strlen(trimmed)-1] = '\0';
4655 result = g_strdup(trimmed);
4658 /* if there are no single quotes around %s, add them.
4659 * '.*%s.*' is ok, as in display 'png:%s'
4661 if (strstr(result, "%s")
4662 && !(strstr(result, "'") < strstr(result,"%s") &&
4663 strstr(strstr(result,"%s"), "'"))) {
4664 gchar *start = g_strdup(result);
4665 gchar *end = g_strdup(strstr(result, "%s")+2);
4667 *strstr(start, "%s") = '\0';
4668 tmp = g_strconcat(start,"'%s'",end, NULL);
4674 if (needsterminal) {
4675 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4686 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4688 gchar *result = NULL;
4692 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4693 result = mailcap_get_command_in_file(path, type, file_to_open);
4697 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4701 void mailcap_update_default(const gchar *type, const gchar *command)
4703 gchar *path = NULL, *outpath = NULL;
4704 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4705 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4706 FILE *fp = g_fopen(path, "rb");
4707 FILE *outfp = g_fopen(outpath, "wb");
4708 gchar buf[BUFFSIZE];
4709 gboolean err = FALSE;
4717 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4718 gchar **parts = g_strsplit(buf, ";", 3);
4719 gchar *trimmed = parts[0];
4720 while (trimmed[0] == ' ')
4722 while (trimmed[strlen(trimmed)-1] == ' ')
4723 trimmed[strlen(trimmed)-1] = '\0';
4725 if (!strcmp(trimmed, type)) {
4730 if(fputs(buf, outfp) == EOF) {
4737 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4743 if (fclose(outfp) == EOF)
4747 g_rename(outpath, path);
4753 gint copy_dir(const gchar *src, const gchar *dst)
4758 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4759 g_warning("failed to open directory: %s\n", src);
4763 if (make_dir(dst) < 0)
4766 while ((name = g_dir_read_name(dir)) != NULL) {
4767 gchar *old_file, *new_file;
4768 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4769 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4770 debug_print("copying: %s -> %s\n", old_file, new_file);
4771 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4772 gint r = copy_file(old_file, new_file, TRUE);
4779 /* Windows has no symlinks. Or well, Vista seems to
4780 have something like this but the semantics might be
4781 different. Thus we don't use it under Windows. */
4782 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4785 gchar *target = g_file_read_link(old_file, &error);
4787 r = symlink(target, new_file);
4794 #endif /*G_OS_WIN32*/
4795 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4796 gint r = copy_dir(old_file, new_file);
4807 /* crude test to see if a file is an email. */
4808 gboolean file_is_email (const gchar *filename)
4814 if (filename == NULL)
4816 if ((fp = g_fopen(filename, "rb")) == NULL)
4818 while (i < 60 && score < 3
4819 && fgets(buffer, sizeof (buffer), fp) > 0) {
4820 if (!strncmp(buffer, "From:", strlen("From:")))
4822 if (!strncmp(buffer, "To:", strlen("To:")))
4824 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4829 return (score >= 3);
4832 gboolean sc_g_list_bigger(GList *list, gint max)
4836 while (cur && i <= max+1) {
4843 gboolean sc_g_slist_bigger(GSList *list, gint max)
4847 while (cur && i <= max+1) {
4854 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4855 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4856 NULL, NULL, NULL, NULL, NULL, NULL};
4857 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4858 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4859 NULL, NULL, NULL, NULL, NULL, NULL};
4861 gint daynames_len[] = {0,0,0,0,0,0,0};
4862 gint monthnames_len[] = {0,0,0,0,0,0,
4864 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4865 gint s_monthnames_len[] = {0,0,0,0,0,0,
4867 const gchar *s_am_up = NULL;
4868 const gchar *s_pm_up = NULL;
4869 const gchar *s_am_low = NULL;
4870 const gchar *s_pm_low = NULL;
4872 gint s_am_up_len = 0;
4873 gint s_pm_up_len = 0;
4874 gint s_am_low_len = 0;
4875 gint s_pm_low_len = 0;
4877 const gchar *def_loc_format = NULL;
4878 const gchar *date_loc_format = NULL;
4879 const gchar *time_loc_format = NULL;
4880 const gchar *time_am_pm = NULL;
4882 static gboolean time_names_init_done = FALSE;
4884 static void init_time_names(void)
4888 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4889 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4890 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4891 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4892 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4893 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4894 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4896 monthnames[0] = Q_("Complete month name for use by strftime|January");
4897 monthnames[1] = Q_("Complete month name for use by strftime|February");
4898 monthnames[2] = Q_("Complete month name for use by strftime|March");
4899 monthnames[3] = Q_("Complete month name for use by strftime|April");
4900 monthnames[4] = Q_("Complete month name for use by strftime|May");
4901 monthnames[5] = Q_("Complete month name for use by strftime|June");
4902 monthnames[6] = Q_("Complete month name for use by strftime|July");
4903 monthnames[7] = Q_("Complete month name for use by strftime|August");
4904 monthnames[8] = Q_("Complete month name for use by strftime|September");
4905 monthnames[9] = Q_("Complete month name for use by strftime|October");
4906 monthnames[10] = Q_("Complete month name for use by strftime|November");
4907 monthnames[11] = Q_("Complete month name for use by strftime|December");
4909 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4910 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4911 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4912 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4913 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4914 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4915 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4917 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4918 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4919 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4920 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4921 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4922 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4923 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4924 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4925 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4926 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4927 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4928 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4930 for (i = 0; i < 7; i++) {
4931 daynames_len[i] = strlen(daynames[i]);
4932 s_daynames_len[i] = strlen(s_daynames[i]);
4934 for (i = 0; i < 12; i++) {
4935 monthnames_len[i] = strlen(monthnames[i]);
4936 s_monthnames_len[i] = strlen(s_monthnames[i]);
4939 s_am_up = Q_("For use by strftime (morning)|AM");
4940 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4941 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4942 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4944 s_am_up_len = strlen(s_am_up);
4945 s_pm_up_len = strlen(s_pm_up);
4946 s_am_low_len = strlen(s_am_low);
4947 s_pm_low_len = strlen(s_pm_low);
4949 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4950 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4951 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4953 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4955 time_names_init_done = TRUE;
4958 #define CHECK_SIZE() { \
4959 total_done += len; \
4960 if (total_done >= buflen) { \
4961 buf[buflen-1] = '\0'; \
4966 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4968 gchar *curpos = buf;
4969 gint total_done = 0;
4970 gchar subbuf[64], subfmt[64];
4971 static time_t last_tzset = (time_t)0;
4973 if (!time_names_init_done)
4976 if (format == NULL || lt == NULL)
4979 if (last_tzset != time(NULL)) {
4981 last_tzset = time(NULL);
4984 if (*format == '%') {
4985 gint len = 0, tmp = 0;
4989 len = 1; CHECK_SIZE();
4993 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4994 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4997 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4998 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5002 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5003 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5006 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5007 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5010 fast_strftime(subbuf, 64, def_loc_format, lt);
5011 len = strlen(subbuf); CHECK_SIZE();
5012 strncpy2(curpos, subbuf, buflen - total_done);
5015 total_done += 2; CHECK_SIZE();
5016 tmp = (lt->tm_year + 1900)/100;
5017 *curpos++ = '0'+(tmp / 10);
5018 *curpos++ = '0'+(tmp % 10);
5021 total_done += 2; CHECK_SIZE();
5022 *curpos++ = '0'+(lt->tm_mday / 10);
5023 *curpos++ = '0'+(lt->tm_mday % 10);
5026 total_done += 8; CHECK_SIZE();
5027 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5028 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5030 *curpos++ = '0'+(lt->tm_mday / 10);
5031 *curpos++ = '0'+(lt->tm_mday % 10);
5033 tmp = lt->tm_year%100;
5034 *curpos++ = '0'+(tmp / 10);
5035 *curpos++ = '0'+(tmp % 10);
5038 len = 2; CHECK_SIZE();
5039 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5042 len = 10; CHECK_SIZE();
5043 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5044 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5047 total_done += 2; CHECK_SIZE();
5048 *curpos++ = '0'+(lt->tm_hour / 10);
5049 *curpos++ = '0'+(lt->tm_hour % 10);
5052 total_done += 2; CHECK_SIZE();
5058 *curpos++ = '0'+(tmp / 10);
5059 *curpos++ = '0'+(tmp % 10);
5062 len = 3; CHECK_SIZE();
5063 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5066 len = 2; CHECK_SIZE();
5067 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5070 len = 2; CHECK_SIZE();
5076 snprintf(curpos, buflen - total_done, "%2d", tmp);
5079 total_done += 2; CHECK_SIZE();
5080 tmp = lt->tm_mon + 1;
5081 *curpos++ = '0'+(tmp / 10);
5082 *curpos++ = '0'+(tmp % 10);
5085 total_done += 2; CHECK_SIZE();
5086 *curpos++ = '0'+(lt->tm_min / 10);
5087 *curpos++ = '0'+(lt->tm_min % 10);
5090 len = 1; CHECK_SIZE();
5094 if (lt->tm_hour >= 12) {
5095 len = s_pm_up_len; CHECK_SIZE();
5096 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5098 len = s_am_up_len; CHECK_SIZE();
5099 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5103 if (lt->tm_hour >= 12) {
5104 len = s_pm_low_len; CHECK_SIZE();
5105 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5107 len = s_am_low_len; CHECK_SIZE();
5108 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5112 fast_strftime(subbuf, 64, time_am_pm, lt);
5113 len = strlen(subbuf); CHECK_SIZE();
5114 strncpy2(curpos, subbuf, buflen - total_done);
5117 total_done += 5; CHECK_SIZE();
5118 *curpos++ = '0'+(lt->tm_hour / 10);
5119 *curpos++ = '0'+(lt->tm_hour % 10);
5121 *curpos++ = '0'+(lt->tm_min / 10);
5122 *curpos++ = '0'+(lt->tm_min % 10);
5125 snprintf(subbuf, 64, "%ld", mktime(lt));
5126 len = strlen(subbuf); CHECK_SIZE();
5127 strncpy2(curpos, subbuf, buflen - total_done);
5130 total_done += 2; CHECK_SIZE();
5131 *curpos++ = '0'+(lt->tm_sec / 10);
5132 *curpos++ = '0'+(lt->tm_sec % 10);
5135 len = 1; CHECK_SIZE();
5139 total_done += 8; CHECK_SIZE();
5140 *curpos++ = '0'+(lt->tm_hour / 10);
5141 *curpos++ = '0'+(lt->tm_hour % 10);
5143 *curpos++ = '0'+(lt->tm_min / 10);
5144 *curpos++ = '0'+(lt->tm_min % 10);
5146 *curpos++ = '0'+(lt->tm_sec / 10);
5147 *curpos++ = '0'+(lt->tm_sec % 10);
5150 len = 1; CHECK_SIZE();
5151 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5154 len = 1; CHECK_SIZE();
5155 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5158 fast_strftime(subbuf, 64, date_loc_format, lt);
5159 len = strlen(subbuf); CHECK_SIZE();
5160 strncpy2(curpos, subbuf, buflen - total_done);
5163 fast_strftime(subbuf, 64, time_loc_format, lt);
5164 len = strlen(subbuf); CHECK_SIZE();
5165 strncpy2(curpos, subbuf, buflen - total_done);
5168 total_done += 2; CHECK_SIZE();
5169 tmp = lt->tm_year%100;
5170 *curpos++ = '0'+(tmp / 10);
5171 *curpos++ = '0'+(tmp % 10);
5174 len = 4; CHECK_SIZE();
5175 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5185 /* let these complicated ones be done with the libc */
5186 snprintf(subfmt, 64, "%%%c", *format);
5187 strftime(subbuf, 64, subfmt, lt);
5188 len = strlen(subbuf); CHECK_SIZE();
5189 strncpy2(curpos, subbuf, buflen - total_done);
5193 /* let these complicated modifiers be done with the libc */
5194 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5195 strftime(subbuf, 64, subfmt, lt);
5196 len = strlen(subbuf); CHECK_SIZE();
5197 strncpy2(curpos, subbuf, buflen - total_done);
5201 if (format && *format)
5202 g_warning("format error (%c)", *format);
5209 int len = 1; CHECK_SIZE();
5210 *curpos++ = *format++;
5217 gboolean prefs_common_get_use_shred(void);
5221 #define WEXITSTATUS(x) (x)
5224 int claws_unlink(const gchar *filename)
5227 static int found_shred = -1;
5228 static const gchar *args[4];
5230 if (filename == NULL)
5233 if (prefs_common_get_use_shred()) {
5234 if (found_shred == -1) {
5236 args[0] = g_find_program_in_path("shred");
5237 debug_print("found shred: %s\n", args[0]);
5238 found_shred = (args[0] != NULL) ? 1:0;
5242 if (found_shred == 1) {
5243 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5244 if (s.st_nlink == 1) {
5247 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5248 NULL, NULL, NULL, NULL, &status, NULL);
5249 debug_print("%s %s exited with status %d\n",
5250 args[0], filename, WEXITSTATUS(status));
5251 if (truncate(filename, 0) < 0)
5252 g_warning("couln't truncate");
5257 return g_unlink(filename);