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 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
203 gint mkstemp_name(gchar *template, gchar **name_used)
205 static gulong count=0; /* W32-_mktemp only supports up to 27
209 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
210 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
211 (S_IRUSR | S_IWUSR));
213 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
215 perror(g_strdup_printf("cant create %s",*name_used));
221 #endif /* G_OS_WIN32 */
224 gint mkstemp(gchar *template)
227 gint res = mkstemp_name(template, &dummyname);
231 #endif /* G_OS_WIN32 */
233 void list_free_strings(GList *list)
235 list = g_list_first(list);
237 while (list != NULL) {
243 void slist_free_strings(GSList *list)
245 while (list != NULL) {
251 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
256 void hash_free_strings(GHashTable *table)
258 g_hash_table_foreach(table, hash_free_strings_func, NULL);
261 gint str_case_equal(gconstpointer v, gconstpointer v2)
263 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
266 guint str_case_hash(gconstpointer key)
268 const gchar *p = key;
272 h = g_ascii_tolower(h);
273 for (p += 1; *p != '\0'; p++)
274 h = (h << 5) - h + g_ascii_tolower(*p);
280 void ptr_array_free_strings(GPtrArray *array)
285 g_return_if_fail(array != NULL);
287 for (i = 0; i < array->len; i++) {
288 str = g_ptr_array_index(array, i);
293 gboolean str_find(const gchar *haystack, const gchar *needle)
295 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
298 gboolean str_case_find(const gchar *haystack, const gchar *needle)
300 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
303 gint to_number(const gchar *nstr)
305 register const gchar *p;
307 if (*nstr == '\0') return -1;
309 for (p = nstr; *p != '\0'; p++)
310 if (!g_ascii_isdigit(*p)) return -1;
315 /* convert integer into string,
316 nstr must be not lower than 11 characters length */
317 gchar *itos_buf(gchar *nstr, gint n)
319 g_snprintf(nstr, 11, "%d", n);
323 /* convert integer into string */
326 static gchar nstr[11];
328 return itos_buf(nstr, n);
331 #define divide(num,divisor,i,d) \
333 i = num >> divisor; \
334 d = num & ((1<<divisor)-1); \
335 d = (d*100) >> divisor; \
338 gchar *to_human_readable(goffset size)
340 static gchar str[14];
341 static gchar *b_format = NULL, *kb_format = NULL,
342 *mb_format = NULL, *gb_format = NULL;
343 register int t = 0, r = 0;
344 if (b_format == NULL) {
346 kb_format = _("%d.%02dKB");
347 mb_format = _("%d.%02dMB");
348 gb_format = _("%.2fGB");
351 if (size < (goffset)1024) {
352 g_snprintf(str, sizeof(str), b_format, (gint)size);
354 } else if (size >> 10 < (goffset)1024) {
355 divide(size, 10, t, r);
356 g_snprintf(str, sizeof(str), kb_format, t, r);
358 } else if (size >> 20 < (goffset)1024) {
359 divide(size, 20, t, r);
360 g_snprintf(str, sizeof(str), mb_format, t, r);
363 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
368 /* strcmp with NULL-checking */
369 gint strcmp2(const gchar *s1, const gchar *s2)
371 if (s1 == NULL || s2 == NULL)
374 return strcmp(s1, s2);
376 /* strstr with NULL-checking */
377 gchar *strstr2(const gchar *s1, const gchar *s2)
379 if (s1 == NULL || s2 == NULL)
382 return strstr(s1, s2);
385 gint path_cmp(const gchar *s1, const gchar *s2)
390 gchar *s1buf, *s2buf;
393 if (s1 == NULL || s2 == NULL) return -1;
394 if (*s1 == '\0' || *s2 == '\0') return -1;
397 s1buf = g_strdup (s1);
398 s2buf = g_strdup (s2);
399 subst_char (s1buf, '/', G_DIR_SEPARATOR);
400 subst_char (s2buf, '/', G_DIR_SEPARATOR);
403 #endif /* !G_OS_WIN32 */
408 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
409 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
411 rc = strncmp(s1, s2, MAX(len1, len2));
415 #endif /* !G_OS_WIN32 */
419 /* remove trailing return code */
420 gchar *strretchomp(gchar *str)
424 if (!*str) return str;
426 for (s = str + strlen(str) - 1;
427 s >= str && (*s == '\n' || *s == '\r');
434 /* remove trailing character */
435 gchar *strtailchomp(gchar *str, gchar tail_char)
439 if (!*str) return str;
440 if (tail_char == '\0') return str;
442 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
448 /* remove CR (carriage return) */
449 gchar *strcrchomp(gchar *str)
453 if (!*str) return str;
455 s = str + strlen(str) - 1;
456 if (*s == '\n' && s > str && *(s - 1) == '\r') {
464 gint file_strip_crs(const gchar *file)
466 FILE *fp = NULL, *outfp = NULL;
468 gchar *out = get_tmp_file();
472 fp = fopen(file, "rb");
476 outfp = fopen(out, "wb");
482 while (fgets(buf, sizeof (buf), fp) != NULL) {
484 if (fputs(buf, outfp) == EOF) {
492 if (fclose(outfp) == EOF) {
496 if (rename_force(out, file) < 0)
508 /* Similar to `strstr' but this function ignores the case of both strings. */
509 gchar *strcasestr(const gchar *haystack, const gchar *needle)
511 register size_t haystack_len, needle_len;
513 haystack_len = strlen(haystack);
514 needle_len = strlen(needle);
516 if (haystack_len < needle_len || needle_len == 0)
519 while (haystack_len >= needle_len) {
520 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
521 return (gchar *)haystack;
531 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
532 gconstpointer needle, size_t needlelen)
534 const gchar *haystack_ = (const gchar *)haystack;
535 const gchar *needle_ = (const gchar *)needle;
536 const gchar *haystack_cur = (const gchar *)haystack;
537 size_t haystack_left = haystacklen;
540 return memchr(haystack_, *needle_, haystacklen);
542 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
544 if (haystacklen - (haystack_cur - haystack_) < needlelen)
546 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
547 return (gpointer)haystack_cur;
550 haystack_left = haystacklen - (haystack_cur - haystack_);
557 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
558 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
560 register const gchar *s = src;
561 register gchar *d = dest;
571 /* Examine if next block is non-ASCII string */
572 gboolean is_next_nonascii(const gchar *s)
576 /* skip head space */
577 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
579 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
580 if (*(guchar *)p > 127 || *(guchar *)p < 32)
587 gint get_next_word_len(const gchar *s)
591 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
597 static void trim_subject_for_compare(gchar *str)
601 eliminate_parenthesis(str, '[', ']');
602 eliminate_parenthesis(str, '(', ')');
605 srcp = str + subject_get_prefix_length(str);
607 memmove(str, srcp, strlen(srcp) + 1);
610 static void trim_subject_for_sort(gchar *str)
616 srcp = str + subject_get_prefix_length(str);
618 memmove(str, srcp, strlen(srcp) + 1);
621 /* compare subjects */
622 gint subject_compare(const gchar *s1, const gchar *s2)
626 if (!s1 || !s2) return -1;
627 if (!*s1 || !*s2) return -1;
629 Xstrdup_a(str1, s1, return -1);
630 Xstrdup_a(str2, s2, return -1);
632 trim_subject_for_compare(str1);
633 trim_subject_for_compare(str2);
635 if (!*str1 || !*str2) return -1;
637 return strcmp(str1, str2);
640 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
644 if (!s1 || !s2) return -1;
646 Xstrdup_a(str1, s1, return -1);
647 Xstrdup_a(str2, s2, return -1);
649 trim_subject_for_sort(str1);
650 trim_subject_for_sort(str2);
652 return g_utf8_collate(str1, str2);
655 void trim_subject(gchar *str)
657 register gchar *srcp;
663 srcp = str + subject_get_prefix_length(str);
668 } else if (*srcp == '(') {
680 else if (*srcp == cl)
687 while (g_ascii_isspace(*srcp)) srcp++;
688 memmove(str, srcp, strlen(srcp) + 1);
691 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
693 register gchar *srcp, *destp;
698 while ((destp = strchr(destp, op))) {
704 else if (*srcp == cl)
710 while (g_ascii_isspace(*srcp)) srcp++;
711 memmove(destp, srcp, strlen(srcp) + 1);
715 void extract_parenthesis(gchar *str, gchar op, gchar cl)
717 register gchar *srcp, *destp;
722 while ((srcp = strchr(destp, op))) {
725 memmove(destp, srcp + 1, strlen(srcp));
730 else if (*destp == cl)
742 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
745 register gchar *srcp, *destp;
747 gboolean in_quote = FALSE;
751 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
754 memmove(destp, srcp + 1, strlen(srcp));
757 if (*destp == op && !in_quote)
759 else if (*destp == cl && !in_quote)
761 else if (*destp == quote_chr)
773 void extract_quote(gchar *str, gchar quote_chr)
777 if ((str = strchr(str, quote_chr))) {
779 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
780 memmove(p - 1, p, strlen(p) + 1);
785 memmove(str, str + 1, p - str);
790 void eliminate_address_comment(gchar *str)
792 register gchar *srcp, *destp;
797 while ((destp = strchr(destp, '"'))) {
798 if ((srcp = strchr(destp + 1, '"'))) {
803 while (g_ascii_isspace(*srcp)) srcp++;
804 memmove(destp, srcp, strlen(srcp) + 1);
814 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
820 else if (*srcp == ')')
826 while (g_ascii_isspace(*srcp)) srcp++;
827 memmove(destp, srcp, strlen(srcp) + 1);
831 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
833 gboolean in_quote = FALSE;
836 if (*str == c && !in_quote)
838 if (*str == quote_chr)
846 void extract_address(gchar *str)
848 eliminate_address_comment(str);
849 if (strchr_with_skip_quote(str, '"', '<'))
850 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
854 void extract_list_id_str(gchar *str)
856 if (strchr_with_skip_quote(str, '"', '<'))
857 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
861 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
866 if (!str) return addr_list;
868 Xstrdup_a(work, str, return addr_list);
871 eliminate_address_comment(work);
874 while (workp && *workp) {
877 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
883 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
884 extract_parenthesis_with_skip_quote
885 (workp, '"', '<', '>');
889 addr_list = g_slist_append(addr_list, g_strdup(workp));
897 GSList *address_list_append(GSList *addr_list, const gchar *str)
899 return address_list_append_real(addr_list, str, TRUE);
902 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
904 return address_list_append_real(addr_list, str, FALSE);
907 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
911 if (!str) return msgid_list;
914 while (strp && *strp) {
915 const gchar *start, *end;
918 if ((start = strchr(strp, '<')) != NULL) {
919 end = strchr(start + 1, '>');
924 msgid = g_strndup(start + 1, end - start - 1);
927 msgid_list = g_slist_prepend(msgid_list, msgid);
937 GSList *references_list_append(GSList *msgid_list, const gchar *str)
941 list = references_list_prepend(NULL, str);
942 list = g_slist_reverse(list);
943 msgid_list = g_slist_concat(msgid_list, list);
948 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
953 if (!str) return group_list;
955 Xstrdup_a(work, str, return group_list);
959 while (workp && *workp) {
962 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
970 group_list = g_slist_append(group_list,
979 GList *add_history(GList *list, const gchar *str)
983 g_return_val_if_fail(str != NULL, list);
985 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
988 list = g_list_remove(list, old->data);
989 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
992 last = g_list_last(list);
995 list = g_list_remove(list, last->data);
999 list = g_list_prepend(list, g_strdup(str));
1004 void remove_return(gchar *str)
1006 register gchar *p = str;
1009 if (*p == '\n' || *p == '\r')
1010 memmove(p, p + 1, strlen(p));
1016 void remove_space(gchar *str)
1018 register gchar *p = str;
1023 while (g_ascii_isspace(*(p + spc)))
1026 memmove(p, p + spc, strlen(p + spc) + 1);
1032 void unfold_line(gchar *str)
1034 register gchar *p = str;
1038 if (*p == '\n' || *p == '\r') {
1041 while (g_ascii_isspace(*(p + spc)))
1044 memmove(p, p + spc, strlen(p + spc) + 1);
1050 void subst_char(gchar *str, gchar orig, gchar subst)
1052 register gchar *p = str;
1061 void subst_chars(gchar *str, gchar *orig, gchar subst)
1063 register gchar *p = str;
1066 if (strchr(orig, *p) != NULL)
1072 void subst_for_filename(gchar *str)
1077 subst_chars(str, "\t\r\n\\/*:", '_');
1079 subst_chars(str, "\t\r\n\\/*", '_');
1083 void subst_for_shellsafe_filename(gchar *str)
1087 subst_for_filename(str);
1088 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1091 gboolean is_ascii_str(const gchar *str)
1093 const guchar *p = (const guchar *)str;
1095 while (*p != '\0') {
1096 if (*p != '\t' && *p != ' ' &&
1097 *p != '\r' && *p != '\n' &&
1098 (*p < 32 || *p >= 127))
1106 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1108 gchar * position = NULL;
1109 gchar * tmp_pos = NULL;
1112 if (quote_chars == NULL)
1115 for (i = 0; i < strlen(quote_chars); i++) {
1116 tmp_pos = strrchr (str, quote_chars[i]);
1118 || (tmp_pos != NULL && position <= tmp_pos) )
1124 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1126 const gchar *first_pos;
1127 const gchar *last_pos;
1128 const gchar *p = str;
1129 gint quote_level = -1;
1131 /* speed up line processing by only searching to the last '>' */
1132 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1133 /* skip a line if it contains a '<' before the initial '>' */
1134 if (memchr(str, '<', first_pos - str) != NULL)
1136 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1140 while (p <= last_pos) {
1141 while (p < last_pos) {
1142 if (g_ascii_isspace(*p))
1148 if (strchr(quote_chars, *p))
1150 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1151 /* any characters are allowed except '-' and space */
1153 && !strchr(quote_chars, *p)
1154 && !g_ascii_isspace(*p)
1157 if (strchr(quote_chars, *p))
1169 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1171 const gchar *p = str, *q;
1172 gint cur_line = 0, len;
1174 while ((q = strchr(p, '\n')) != NULL) {
1176 if (len > max_chars) {
1186 if (len > max_chars) {
1195 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1197 gchar * position = NULL;
1198 gchar * tmp_pos = NULL;
1201 if (quote_chars == NULL)
1204 for (i = 0; i < strlen(quote_chars); i++) {
1205 tmp_pos = strchr (str, quote_chars[i]);
1207 || (tmp_pos != NULL && position >= tmp_pos) )
1213 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1215 register guint haystack_len, needle_len;
1216 gboolean in_squote = FALSE, in_dquote = FALSE;
1218 haystack_len = strlen(haystack);
1219 needle_len = strlen(needle);
1221 if (haystack_len < needle_len || needle_len == 0)
1224 while (haystack_len >= needle_len) {
1225 if (!in_squote && !in_dquote &&
1226 !strncmp(haystack, needle, needle_len))
1227 return (gchar *)haystack;
1229 /* 'foo"bar"' -> foo"bar"
1230 "foo'bar'" -> foo'bar' */
1231 if (*haystack == '\'') {
1234 else if (!in_dquote)
1236 } else if (*haystack == '\"') {
1239 else if (!in_squote)
1250 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1253 GSList *string_list = NULL, *slist;
1254 gchar **str_array, *s, *new_str;
1255 guint i, n = 1, len;
1257 g_return_val_if_fail(str != NULL, NULL);
1258 g_return_val_if_fail(delim != NULL, NULL);
1261 max_tokens = G_MAXINT;
1263 s = strstr_with_skip_quote(str, delim);
1265 guint delimiter_len = strlen(delim);
1269 new_str = g_strndup(str, len);
1271 if (new_str[0] == '\'' || new_str[0] == '\"') {
1272 if (new_str[len - 1] == new_str[0]) {
1273 new_str[len - 1] = '\0';
1274 memmove(new_str, new_str + 1, len - 1);
1277 string_list = g_slist_prepend(string_list, new_str);
1279 str = s + delimiter_len;
1280 s = strstr_with_skip_quote(str, delim);
1281 } while (--max_tokens && s);
1285 new_str = g_strdup(str);
1286 if (new_str[0] == '\'' || new_str[0] == '\"') {
1288 if (new_str[len - 1] == new_str[0]) {
1289 new_str[len - 1] = '\0';
1290 memmove(new_str, new_str + 1, len - 1);
1293 string_list = g_slist_prepend(string_list, new_str);
1297 str_array = g_new(gchar*, n);
1301 str_array[i--] = NULL;
1302 for (slist = string_list; slist; slist = slist->next)
1303 str_array[i--] = slist->data;
1305 g_slist_free(string_list);
1310 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1312 gchar *abbrev_group;
1314 const gchar *p = group;
1317 g_return_val_if_fail(group != NULL, NULL);
1319 last = group + strlen(group);
1320 abbrev_group = ap = g_malloc(strlen(group) + 1);
1325 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1327 while (*p != '.') p++;
1330 return abbrev_group;
1335 return abbrev_group;
1338 gchar *trim_string(const gchar *str, gint len)
1340 const gchar *p = str;
1345 if (!str) return NULL;
1346 if (strlen(str) <= len)
1347 return g_strdup(str);
1348 if (g_utf8_validate(str, -1, NULL) == FALSE)
1349 return g_strdup(str);
1351 while (*p != '\0') {
1352 mb_len = g_utf8_skip[*(guchar *)p];
1355 else if (new_len + mb_len > len)
1362 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1363 return g_strconcat(new_str, "...", NULL);
1366 GList *uri_list_extract_filenames(const gchar *uri_list)
1368 GList *result = NULL;
1370 gchar *escaped_utf8uri;
1376 while (g_ascii_isspace(*p)) p++;
1377 if (!strncmp(p, "file:", 5)) {
1380 while (*q && *q != '\n' && *q != '\r') q++;
1383 gchar *file, *locale_file = NULL;
1385 while (q > p && g_ascii_isspace(*q))
1387 Xalloca(escaped_utf8uri, q - p + 2,
1389 Xalloca(file, q - p + 2,
1392 strncpy(escaped_utf8uri, p, q - p + 1);
1393 escaped_utf8uri[q - p + 1] = '\0';
1394 decode_uri(file, escaped_utf8uri);
1396 * g_filename_from_uri() rejects escaped/locale encoded uri
1397 * string which come from Nautilus.
1399 if (g_utf8_validate(file, -1, NULL))
1401 = conv_codeset_strdup(
1404 conv_get_locale_charset_str());
1406 locale_file = g_strdup(file + 5);
1407 result = g_list_append(result, locale_file);
1411 p = strchr(p, '\n');
1418 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1421 static gint axtoi(const gchar *hexstr)
1423 gint hi, lo, result;
1426 if ('0' <= hi && hi <= '9') {
1429 if ('a' <= hi && hi <= 'f') {
1432 if ('A' <= hi && hi <= 'F') {
1437 if ('0' <= lo && lo <= '9') {
1440 if ('a' <= lo && lo <= 'f') {
1443 if ('A' <= lo && lo <= 'F') {
1446 result = lo + (16 * hi);
1450 gboolean is_uri_string(const gchar *str)
1452 while (str && *str && g_ascii_isspace(*str))
1454 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1455 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1456 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1457 g_ascii_strncasecmp(str, "www.", 4) == 0);
1460 gchar *get_uri_path(const gchar *uri)
1462 while (uri && *uri && g_ascii_isspace(*uri))
1464 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1465 return (gchar *)(uri + 7);
1466 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1467 return (gchar *)(uri + 8);
1468 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1469 return (gchar *)(uri + 6);
1471 return (gchar *)uri;
1474 gint get_uri_len(const gchar *str)
1478 if (is_uri_string(str)) {
1479 for (p = str; *p != '\0'; p++) {
1480 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1489 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1490 * plusses, and escape characters are used)
1492 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1494 gchar *dec = decoded_uri;
1495 const gchar *enc = encoded_uri;
1500 if (isxdigit((guchar)enc[0]) &&
1501 isxdigit((guchar)enc[1])) {
1507 if (with_plus && *enc == '+')
1519 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1521 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1524 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1526 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1527 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1531 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1532 gchar **subject, gchar **body, gchar ***attach)
1536 const gchar *forbidden_uris[] = { ".gnupg/",
1542 gint num_attach = 0;
1543 gchar **my_att = NULL;
1545 Xstrdup_a(tmp_mailto, mailto, return -1);
1547 if (!strncmp(tmp_mailto, "mailto:", 7))
1550 p = strchr(tmp_mailto, '?');
1557 *to = decode_uri_gdup(tmp_mailto);
1559 my_att = g_malloc(sizeof(char *));
1563 gchar *field, *value;
1580 if (*value == '\0') continue;
1582 if (from && !g_ascii_strcasecmp(field, "from")) {
1584 *from = decode_uri_gdup(value);
1586 gchar *tmp = decode_uri_gdup(value);
1587 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1591 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1593 *cc = decode_uri_gdup(value);
1595 gchar *tmp = decode_uri_gdup(value);
1596 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1600 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1602 *bcc = decode_uri_gdup(value);
1604 gchar *tmp = decode_uri_gdup(value);
1605 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1609 } else if (subject && !*subject &&
1610 !g_ascii_strcasecmp(field, "subject")) {
1611 *subject = decode_uri_gdup(value);
1612 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1613 *body = decode_uri_gdup(value);
1614 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1615 gchar *tmp = decode_uri_gdup(value);
1616 if (!g_file_get_contents(value, body, NULL, NULL)) {
1617 g_error("Error: couldn't set insert file '%s' in body\n", value);
1621 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1623 gchar *tmp = decode_uri_gdup(value);
1624 for (; forbidden_uris[i]; i++) {
1625 if (strstr(tmp, forbidden_uris[i])) {
1626 g_print("Refusing to attach '%s', potential private data leak\n",
1634 /* attach is correct */
1636 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1637 my_att[num_attach-1] = tmp;
1638 my_att[num_attach] = NULL;
1650 #include <windows.h>
1651 #ifndef CSIDL_APPDATA
1652 #define CSIDL_APPDATA 0x001a
1654 #ifndef CSIDL_LOCAL_APPDATA
1655 #define CSIDL_LOCAL_APPDATA 0x001c
1657 #ifndef CSIDL_FLAG_CREATE
1658 #define CSIDL_FLAG_CREATE 0x8000
1660 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1664 w32_strerror (int w32_errno)
1666 static char strerr[256];
1667 int ec = (int)GetLastError ();
1671 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1672 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1673 strerr, DIM (strerr)-1, NULL);
1677 static __inline__ void *
1678 dlopen (const char * name, int flag)
1680 void * hd = LoadLibrary (name);
1684 static __inline__ void *
1685 dlsym (void * hd, const char * sym)
1689 void * fnc = GetProcAddress (hd, sym);
1698 static __inline__ const char *
1701 return w32_strerror (0);
1705 static __inline__ int
1717 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1719 static int initialized;
1720 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1724 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1730 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1732 handle = dlopen (dllnames[i], RTLD_LAZY);
1735 func = dlsym (handle, "SHGetFolderPathW");
1746 return func (a,b,c,d,e);
1751 /* Returns a static string with the directroy from which the module
1752 has been loaded. Returns an empty string on error. */
1753 static char *w32_get_module_dir(void)
1755 static char *moddir;
1758 char name[MAX_PATH+10];
1761 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1764 p = strrchr (name, '\\');
1770 moddir = g_strdup (name);
1774 #endif /* G_OS_WIN32 */
1776 /* Return a static string with the locale dir. */
1777 const gchar *get_locale_dir(void)
1779 static gchar *loc_dir;
1783 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1784 "\\share\\locale", NULL);
1787 loc_dir = LOCALEDIR;
1793 const gchar *get_home_dir(void)
1796 static char home_dir_utf16[MAX_PATH] = "";
1797 static gchar *home_dir_utf8 = NULL;
1798 if (home_dir_utf16[0] == '\0') {
1799 if (w32_shgetfolderpath
1800 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1801 NULL, 0, home_dir_utf16) < 0)
1802 strcpy (home_dir_utf16, "C:\\Sylpheed");
1803 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1805 return home_dir_utf8;
1807 static const gchar *homeenv = NULL;
1812 if (!homeenv && g_getenv("HOME") != NULL)
1813 homeenv = g_strdup(g_getenv("HOME"));
1815 homeenv = g_get_home_dir();
1821 static gchar *claws_rc_dir = NULL;
1822 static gboolean rc_dir_alt = FALSE;
1823 const gchar *get_rc_dir(void)
1827 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1830 return claws_rc_dir;
1833 void set_rc_dir(const gchar *dir)
1835 if (claws_rc_dir != NULL) {
1836 g_print("Error: rc_dir already set\n");
1839 if (g_path_is_absolute(dir))
1840 claws_rc_dir = g_strdup(dir);
1842 claws_rc_dir = g_strconcat(g_get_current_dir(),
1843 G_DIR_SEPARATOR_S, dir, NULL);
1845 debug_print("set rc_dir to %s\n", claws_rc_dir);
1846 if (!is_dir_exist(claws_rc_dir)) {
1847 if (make_dir_hier(claws_rc_dir) != 0) {
1848 g_print("Error: can't create %s\n",
1855 gboolean rc_dir_is_alt(void) {
1859 const gchar *get_mail_base_dir(void)
1861 return get_home_dir();
1865 const gchar *prefs_common_get_data_root(void);
1866 gchar *last_data_root = NULL;
1869 const gchar *get_news_cache_dir(void)
1871 static gchar *news_cache_dir = NULL;
1873 const gchar *data_root = prefs_common_get_data_root();
1874 if (strcmp2(data_root, last_data_root)) {
1875 g_free(news_cache_dir);
1876 news_cache_dir = NULL;
1879 if (!news_cache_dir)
1881 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1882 NEWS_CACHE_DIR, NULL);
1886 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1887 "Claws", G_DIR_SEPARATOR_S,
1888 g_get_user_name(), G_DIR_SEPARATOR_S,
1889 NEWS_CACHE_DIR, NULL);
1890 g_free(last_data_root);
1891 last_data_root = g_strdup(last_data_root);
1893 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1894 NEWS_CACHE_DIR, NULL);
1895 g_free(last_data_root);
1896 last_data_root = NULL;
1900 return news_cache_dir;
1903 const gchar *get_imap_cache_dir(void)
1905 static gchar *imap_cache_dir = NULL;
1907 const gchar *data_root = prefs_common_get_data_root();
1908 if (strcmp2(data_root, last_data_root)) {
1909 g_free(imap_cache_dir);
1910 imap_cache_dir = NULL;
1914 if (!imap_cache_dir)
1916 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1917 IMAP_CACHE_DIR, NULL);
1921 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1922 "Claws", G_DIR_SEPARATOR_S,
1923 g_get_user_name(), G_DIR_SEPARATOR_S,
1924 IMAP_CACHE_DIR, NULL);
1925 g_free(last_data_root);
1926 last_data_root = g_strdup(last_data_root);
1928 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1929 IMAP_CACHE_DIR, NULL);
1930 g_free(last_data_root);
1931 last_data_root = NULL;
1936 return imap_cache_dir;
1939 const gchar *get_mime_tmp_dir(void)
1941 static gchar *mime_tmp_dir = NULL;
1944 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1945 MIME_TMP_DIR, NULL);
1947 return mime_tmp_dir;
1950 const gchar *get_template_dir(void)
1952 static gchar *template_dir = NULL;
1955 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1956 TEMPLATE_DIR, NULL);
1958 return template_dir;
1962 const gchar *get_cert_file(void)
1964 const gchar *cert_file = NULL;
1966 cert_file = g_strconcat(w32_get_module_dir(),
1967 "\\share\\claws-mail\\",
1968 "ca-certificates.crt",
1974 /* Return the default directory for Plugins. */
1975 const gchar *get_plugin_dir(void)
1978 static gchar *plugin_dir = NULL;
1981 plugin_dir = g_strconcat(w32_get_module_dir(),
1982 "\\lib\\claws-mail\\plugins\\",
1986 if (is_dir_exist(PLUGINDIR))
1989 static gchar *plugin_dir = NULL;
1991 plugin_dir = g_strconcat(get_rc_dir(),
1992 G_DIR_SEPARATOR_S, "plugins",
1993 G_DIR_SEPARATOR_S, NULL);
2001 /* Return the default directory for Themes. */
2002 const gchar *get_themes_dir(void)
2004 static gchar *themes_dir = NULL;
2007 themes_dir = g_strconcat(w32_get_module_dir(),
2008 "\\share\\claws-mail\\themes",
2014 const gchar *get_tmp_dir(void)
2016 static gchar *tmp_dir = NULL;
2019 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2025 gchar *get_tmp_file(void)
2028 static guint32 id = 0;
2030 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2031 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2036 const gchar *get_domain_name(void)
2039 static gchar *domain_name = NULL;
2045 if (gethostname(hostname, sizeof(hostname)) != 0) {
2046 perror("gethostname");
2047 domain_name = "unknown";
2049 hostname[sizeof(hostname) - 1] = '\0';
2050 if ((hp = my_gethostbyname(hostname)) == NULL) {
2051 perror("gethostbyname");
2052 domain_name = g_strdup(hostname);
2054 domain_name = g_strdup(hp->h_name);
2057 debug_print("domain name = %s\n", domain_name);
2066 off_t get_file_size(const gchar *file)
2070 if (g_stat(file, &s) < 0) {
2071 FILE_OP_ERROR(file, "stat");
2078 time_t get_file_mtime(const gchar *file)
2082 if (g_stat(file, &s) < 0) {
2083 FILE_OP_ERROR(file, "stat");
2090 off_t get_file_size_as_crlf(const gchar *file)
2094 gchar buf[BUFFSIZE];
2096 if ((fp = g_fopen(file, "rb")) == NULL) {
2097 FILE_OP_ERROR(file, "fopen");
2101 while (fgets(buf, sizeof(buf), fp) != NULL) {
2103 size += strlen(buf) + 2;
2107 FILE_OP_ERROR(file, "fgets");
2116 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2123 if (g_stat(file, &s) < 0) {
2124 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2128 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2135 /* Test on whether FILE is a relative file name. This is
2136 * straightforward for Unix but more complex for Windows. */
2137 gboolean is_relative_filename(const gchar *file)
2142 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2143 return FALSE; /* Prefixed with a hostname - this can't
2144 * be a relative name. */
2146 if ( ((*file >= 'a' && *file <= 'z')
2147 || (*file >= 'A' && *file <= 'Z'))
2149 file += 2; /* Skip drive letter. */
2151 return !(*file == '\\' || *file == '/');
2153 return !(*file == G_DIR_SEPARATOR);
2158 gboolean is_dir_exist(const gchar *dir)
2163 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2166 gboolean is_file_entry_exist(const gchar *file)
2171 return g_file_test(file, G_FILE_TEST_EXISTS);
2174 gboolean dirent_is_regular_file(struct dirent *d)
2176 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2177 if (d->d_type == DT_REG)
2179 else if (d->d_type != DT_UNKNOWN)
2183 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2186 gint change_dir(const gchar *dir)
2188 gchar *prevdir = NULL;
2191 prevdir = g_get_current_dir();
2193 if (g_chdir(dir) < 0) {
2194 FILE_OP_ERROR(dir, "chdir");
2195 if (debug_mode) g_free(prevdir);
2197 } else if (debug_mode) {
2200 cwd = g_get_current_dir();
2201 if (strcmp(prevdir, cwd) != 0)
2202 g_print("current dir: %s\n", cwd);
2210 gint make_dir(const gchar *dir)
2212 if (g_mkdir(dir, S_IRWXU) < 0) {
2213 FILE_OP_ERROR(dir, "mkdir");
2216 if (g_chmod(dir, S_IRWXU) < 0)
2217 FILE_OP_ERROR(dir, "chmod");
2222 gint make_dir_hier(const gchar *dir)
2227 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2228 parent_dir = g_strndup(dir, p - dir);
2229 if (*parent_dir != '\0') {
2230 if (!is_dir_exist(parent_dir)) {
2231 if (make_dir(parent_dir) < 0) {
2240 if (!is_dir_exist(dir)) {
2241 if (make_dir(dir) < 0)
2248 gint remove_all_files(const gchar *dir)
2251 const gchar *dir_name;
2254 prev_dir = g_get_current_dir();
2256 if (g_chdir(dir) < 0) {
2257 FILE_OP_ERROR(dir, "chdir");
2262 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2263 g_warning("failed to open directory: %s\n", dir);
2268 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2269 if (claws_unlink(dir_name) < 0)
2270 FILE_OP_ERROR(dir_name, "unlink");
2275 if (g_chdir(prev_dir) < 0) {
2276 FILE_OP_ERROR(prev_dir, "chdir");
2286 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2289 const gchar *dir_name;
2293 prev_dir = g_get_current_dir();
2295 if (g_chdir(dir) < 0) {
2296 FILE_OP_ERROR(dir, "chdir");
2301 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2302 g_warning("failed to open directory: %s\n", dir);
2307 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2308 file_no = to_number(dir_name);
2309 if (file_no > 0 && first <= file_no && file_no <= last) {
2310 if (is_dir_exist(dir_name))
2312 if (claws_unlink(dir_name) < 0)
2313 FILE_OP_ERROR(dir_name, "unlink");
2319 if (g_chdir(prev_dir) < 0) {
2320 FILE_OP_ERROR(prev_dir, "chdir");
2330 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2333 const gchar *dir_name;
2337 prev_dir = g_get_current_dir();
2339 if (g_chdir(dir) < 0) {
2340 FILE_OP_ERROR(dir, "chdir");
2345 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2346 FILE_OP_ERROR(dir, "opendir");
2351 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2352 file_no = to_number(dir_name);
2353 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2354 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2355 if (is_dir_exist(dir_name))
2357 if (claws_unlink(dir_name) < 0)
2358 FILE_OP_ERROR(dir_name, "unlink");
2364 if (g_chdir(prev_dir) < 0) {
2365 FILE_OP_ERROR(prev_dir, "chdir");
2375 gint remove_all_numbered_files(const gchar *dir)
2377 return remove_numbered_files(dir, 0, UINT_MAX);
2380 gint remove_dir_recursive(const gchar *dir)
2384 const gchar *dir_name;
2387 if (g_stat(dir, &s) < 0) {
2388 FILE_OP_ERROR(dir, "stat");
2389 if (ENOENT == errno) return 0;
2393 if (!S_ISDIR(s.st_mode)) {
2394 if (claws_unlink(dir) < 0) {
2395 FILE_OP_ERROR(dir, "unlink");
2402 prev_dir = g_get_current_dir();
2403 /* g_print("prev_dir = %s\n", prev_dir); */
2405 if (!path_cmp(prev_dir, dir)) {
2407 if (g_chdir("..") < 0) {
2408 FILE_OP_ERROR(dir, "chdir");
2411 prev_dir = g_get_current_dir();
2414 if (g_chdir(dir) < 0) {
2415 FILE_OP_ERROR(dir, "chdir");
2420 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2421 g_warning("failed to open directory: %s\n", dir);
2427 /* remove all files in the directory */
2428 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2429 /* g_print("removing %s\n", dir_name); */
2431 if (is_dir_exist(dir_name)) {
2432 if (remove_dir_recursive(dir_name) < 0) {
2433 g_warning("can't remove directory\n");
2437 if (claws_unlink(dir_name) < 0)
2438 FILE_OP_ERROR(dir_name, "unlink");
2444 if (g_chdir(prev_dir) < 0) {
2445 FILE_OP_ERROR(prev_dir, "chdir");
2452 if (g_rmdir(dir) < 0) {
2453 FILE_OP_ERROR(dir, "rmdir");
2460 gint rename_force(const gchar *oldpath, const gchar *newpath)
2463 if (!is_file_entry_exist(oldpath)) {
2467 if (is_file_exist(newpath)) {
2468 if (claws_unlink(newpath) < 0)
2469 FILE_OP_ERROR(newpath, "unlink");
2472 return g_rename(oldpath, newpath);
2476 * Append src file body to the tail of dest file.
2477 * Now keep_backup has no effects.
2479 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2481 FILE *src_fp, *dest_fp;
2485 gboolean err = FALSE;
2487 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2488 FILE_OP_ERROR(src, "fopen");
2492 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2493 FILE_OP_ERROR(dest, "fopen");
2498 if (change_file_mode_rw(dest_fp, dest) < 0) {
2499 FILE_OP_ERROR(dest, "chmod");
2500 g_warning("can't change file mode\n");
2503 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2504 if (n_read < sizeof(buf) && ferror(src_fp))
2506 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2507 g_warning("writing to %s failed.\n", dest);
2515 if (ferror(src_fp)) {
2516 FILE_OP_ERROR(src, "fread");
2520 if (fclose(dest_fp) == EOF) {
2521 FILE_OP_ERROR(dest, "fclose");
2533 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2535 FILE *src_fp, *dest_fp;
2538 gchar *dest_bak = NULL;
2539 gboolean err = FALSE;
2541 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2542 FILE_OP_ERROR(src, "fopen");
2545 if (is_file_exist(dest)) {
2546 dest_bak = g_strconcat(dest, ".bak", NULL);
2547 if (rename_force(dest, dest_bak) < 0) {
2548 FILE_OP_ERROR(dest, "rename");
2555 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2556 FILE_OP_ERROR(dest, "fopen");
2559 if (rename_force(dest_bak, dest) < 0)
2560 FILE_OP_ERROR(dest_bak, "rename");
2566 if (change_file_mode_rw(dest_fp, dest) < 0) {
2567 FILE_OP_ERROR(dest, "chmod");
2568 g_warning("can't change file mode\n");
2571 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2572 if (n_read < sizeof(buf) && ferror(src_fp))
2574 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2575 g_warning("writing to %s failed.\n", dest);
2580 if (rename_force(dest_bak, dest) < 0)
2581 FILE_OP_ERROR(dest_bak, "rename");
2588 if (ferror(src_fp)) {
2589 FILE_OP_ERROR(src, "fread");
2593 if (fclose(dest_fp) == EOF) {
2594 FILE_OP_ERROR(dest, "fclose");
2601 if (rename_force(dest_bak, dest) < 0)
2602 FILE_OP_ERROR(dest_bak, "rename");
2608 if (keep_backup == FALSE && dest_bak)
2609 claws_unlink(dest_bak);
2616 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2618 if (overwrite == FALSE && is_file_exist(dest)) {
2619 g_warning("move_file(): file %s already exists.", dest);
2623 if (rename_force(src, dest) == 0) return 0;
2625 if (EXDEV != errno) {
2626 FILE_OP_ERROR(src, "rename");
2630 if (copy_file(src, dest, FALSE) < 0) return -1;
2637 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2640 gint bytes_left, to_read;
2643 if (fseek(fp, offset, SEEK_SET) < 0) {
2648 bytes_left = length;
2649 to_read = MIN(bytes_left, sizeof(buf));
2651 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2652 if (n_read < to_read && ferror(fp))
2654 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2657 bytes_left -= n_read;
2658 if (bytes_left == 0)
2660 to_read = MIN(bytes_left, sizeof(buf));
2671 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2674 gboolean err = FALSE;
2676 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2677 FILE_OP_ERROR(dest, "fopen");
2681 if (change_file_mode_rw(dest_fp, dest) < 0) {
2682 FILE_OP_ERROR(dest, "chmod");
2683 g_warning("can't change file mode\n");
2686 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2689 if (!err && fclose(dest_fp) == EOF) {
2690 FILE_OP_ERROR(dest, "fclose");
2695 g_warning("writing to %s failed.\n", dest);
2703 /* convert line endings into CRLF. If the last line doesn't end with
2704 * linebreak, add it.
2706 gchar *canonicalize_str(const gchar *str)
2712 for (p = str; *p != '\0'; ++p) {
2719 if (p == str || *(p - 1) != '\n')
2722 out = outp = g_malloc(new_len + 1);
2723 for (p = str; *p != '\0'; ++p) {
2730 if (p == str || *(p - 1) != '\n') {
2739 gint canonicalize_file(const gchar *src, const gchar *dest)
2741 FILE *src_fp, *dest_fp;
2742 gchar buf[BUFFSIZE];
2744 gboolean err = FALSE;
2745 gboolean last_linebreak = FALSE;
2747 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2748 FILE_OP_ERROR(src, "fopen");
2752 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2753 FILE_OP_ERROR(dest, "fopen");
2758 if (change_file_mode_rw(dest_fp, dest) < 0) {
2759 FILE_OP_ERROR(dest, "chmod");
2760 g_warning("can't change file mode\n");
2763 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2767 if (len == 0) break;
2768 last_linebreak = FALSE;
2770 if (buf[len - 1] != '\n') {
2771 last_linebreak = TRUE;
2772 r = fputs(buf, dest_fp);
2773 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2774 r = fputs(buf, dest_fp);
2777 r = fwrite(buf, 1, len - 1, dest_fp);
2782 r = fputs("\r\n", dest_fp);
2786 g_warning("writing to %s failed.\n", dest);
2794 if (last_linebreak == TRUE) {
2795 if (fputs("\r\n", dest_fp) == EOF)
2799 if (ferror(src_fp)) {
2800 FILE_OP_ERROR(src, "fgets");
2804 if (fclose(dest_fp) == EOF) {
2805 FILE_OP_ERROR(dest, "fclose");
2817 gint canonicalize_file_replace(const gchar *file)
2821 tmp_file = get_tmp_file();
2823 if (canonicalize_file(file, tmp_file) < 0) {
2828 if (move_file(tmp_file, file, TRUE) < 0) {
2829 g_warning("can't replace %s .\n", file);
2830 claws_unlink(tmp_file);
2839 gchar *normalize_newlines(const gchar *str)
2841 const gchar *p = str;
2844 out = outp = g_malloc(strlen(str) + 1);
2845 for (p = str; *p != '\0'; ++p) {
2847 if (*(p + 1) != '\n')
2858 gchar *get_outgoing_rfc2822_str(FILE *fp)
2860 gchar buf[BUFFSIZE];
2864 str = g_string_new(NULL);
2866 /* output header part */
2867 while (fgets(buf, sizeof(buf), fp) != NULL) {
2869 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2876 else if (next != ' ' && next != '\t') {
2880 if (fgets(buf, sizeof(buf), fp) == NULL)
2884 g_string_append(str, buf);
2885 g_string_append(str, "\r\n");
2891 /* output body part */
2892 while (fgets(buf, sizeof(buf), fp) != NULL) {
2895 g_string_append_c(str, '.');
2896 g_string_append(str, buf);
2897 g_string_append(str, "\r\n");
2901 g_string_free(str, FALSE);
2907 * Create a new boundary in a way that it is very unlikely that this
2908 * will occur in the following text. It would be easy to ensure
2909 * uniqueness if everything is either quoted-printable or base64
2910 * encoded (note that conversion is allowed), but because MIME bodies
2911 * may be nested, it may happen that the same boundary has already
2914 * boundary := 0*69<bchars> bcharsnospace
2915 * bchars := bcharsnospace / " "
2916 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2917 * "+" / "_" / "," / "-" / "." /
2918 * "/" / ":" / "=" / "?"
2920 * some special characters removed because of buggy MTAs
2923 gchar *generate_mime_boundary(const gchar *prefix)
2925 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2926 "abcdefghijklmnopqrstuvwxyz"
2931 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2932 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2935 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2939 gint change_file_mode_rw(FILE *fp, const gchar *file)
2942 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2944 return g_chmod(file, S_IRUSR|S_IWUSR);
2948 FILE *my_tmpfile(void)
2950 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2951 const gchar suffix[] = ".XXXXXX";
2952 const gchar *tmpdir;
2954 const gchar *progname;
2963 tmpdir = get_tmp_dir();
2964 tmplen = strlen(tmpdir);
2965 progname = g_get_prgname();
2966 if (progname == NULL)
2967 progname = "claws-mail";
2968 proglen = strlen(progname);
2969 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2972 memcpy(fname, tmpdir, tmplen);
2973 fname[tmplen] = G_DIR_SEPARATOR;
2974 memcpy(fname + tmplen + 1, progname, proglen);
2975 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2977 fd = mkstemp(fname);
2982 claws_unlink(fname);
2984 /* verify that we can write in the file after unlinking */
2985 if (write(fd, buf, 1) < 0) {
2992 fp = fdopen(fd, "w+b");
3000 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3005 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3009 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3010 dir, G_DIR_SEPARATOR);
3011 fd = mkstemp_name(template, filename);
3014 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3015 fd = mkstemp(*filename);
3017 return fdopen(fd, "w+");
3020 FILE *str_open_as_stream(const gchar *str)
3025 g_return_val_if_fail(str != NULL, NULL);
3029 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3034 if (len == 0) return fp;
3036 if (fwrite(str, 1, len, fp) != len) {
3037 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3046 gint str_write_to_file(const gchar *str, const gchar *file)
3051 g_return_val_if_fail(str != NULL, -1);
3052 g_return_val_if_fail(file != NULL, -1);
3054 if ((fp = g_fopen(file, "wb")) == NULL) {
3055 FILE_OP_ERROR(file, "fopen");
3065 if (fwrite(str, 1, len, fp) != len) {
3066 FILE_OP_ERROR(file, "fwrite");
3072 if (fclose(fp) == EOF) {
3073 FILE_OP_ERROR(file, "fclose");
3081 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3088 g_return_val_if_fail(fp != NULL, NULL);
3090 array = g_byte_array_new();
3092 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3093 if (n_read < sizeof(buf) && ferror(fp))
3095 g_byte_array_append(array, buf, n_read);
3099 FILE_OP_ERROR("file stream", "fread");
3100 g_byte_array_free(array, TRUE);
3105 g_byte_array_append(array, buf, 1);
3106 str = (gchar *)array->data;
3107 g_byte_array_free(array, FALSE);
3109 if (recode && !g_utf8_validate(str, -1, NULL)) {
3110 const gchar *src_codeset, *dest_codeset;
3112 src_codeset = conv_get_locale_charset_str();
3113 dest_codeset = CS_UTF_8;
3114 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3122 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3129 struct timeval timeout = {1, 0};
3134 g_return_val_if_fail(file != NULL, NULL);
3136 if (g_stat(file, &s) != 0) {
3137 FILE_OP_ERROR(file, "stat");
3140 if (S_ISDIR(s.st_mode)) {
3141 g_warning("%s: is a directory\n", file);
3146 fp = fopen (file, "rb");
3148 FILE_OP_ERROR(file, "open");
3152 /* test whether the file is readable without blocking */
3153 fd = open(file, O_RDONLY | O_NONBLOCK);
3155 FILE_OP_ERROR(file, "open");
3162 /* allow for one second */
3163 err = select(fd+1, &fds, NULL, NULL, &timeout);
3164 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3166 FILE_OP_ERROR(file, "select");
3168 g_warning("%s: doesn't seem readable\n", file);
3174 /* Now clear O_NONBLOCK */
3175 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3176 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3180 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3181 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3186 /* get the FILE pointer */
3187 fp = fdopen(fd, "rb");
3190 FILE_OP_ERROR(file, "fdopen");
3191 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3196 str = file_read_stream_to_str_full(fp, recode);
3203 gchar *file_read_to_str(const gchar *file)
3205 return file_read_to_str_full(file, TRUE);
3207 gchar *file_read_stream_to_str(FILE *fp)
3209 return file_read_stream_to_str_full(fp, TRUE);
3212 gchar *file_read_to_str_no_recode(const gchar *file)
3214 return file_read_to_str_full(file, FALSE);
3216 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3218 return file_read_stream_to_str_full(fp, FALSE);
3221 char *fgets_crlf(char *buf, int size, FILE *stream)
3223 gboolean is_cr = FALSE;
3224 gboolean last_was_cr = FALSE;
3229 while (--size > 0 && (c = getc(stream)) != EOF)
3232 is_cr = (c == '\r');
3242 last_was_cr = is_cr;
3244 if (c == EOF && cs == buf)
3252 static gint execute_async(gchar *const argv[])
3254 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3256 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3257 NULL, NULL, NULL, FALSE) == FALSE) {
3258 g_warning("Couldn't execute command: %s\n", argv[0]);
3265 static gint execute_sync(gchar *const argv[])
3269 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3271 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3272 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3273 g_warning("Couldn't execute command: %s\n", argv[0]);
3278 if (WIFEXITED(status))
3279 return WEXITSTATUS(status);
3287 gint execute_command_line(const gchar *cmdline, gboolean async)
3292 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3294 argv = strsplit_with_quote(cmdline, " ", 0);
3297 ret = execute_async(argv);
3299 ret = execute_sync(argv);
3306 gchar *get_command_output(const gchar *cmdline)
3308 gchar *child_stdout;
3311 g_return_val_if_fail(cmdline != NULL, NULL);
3313 debug_print("get_command_output(): executing: %s\n", cmdline);
3315 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3317 g_warning("Couldn't execute command: %s\n", cmdline);
3321 return child_stdout;
3324 static gint is_unchanged_uri_char(char c)
3335 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3341 for(i = 0; i < strlen(uri) ; i++) {
3342 if (is_unchanged_uri_char(uri[i])) {
3343 if (k + 2 >= bufsize)
3345 encoded_uri[k++] = uri[i];
3348 char * hexa = "0123456789ABCDEF";
3350 if (k + 4 >= bufsize)
3352 encoded_uri[k++] = '%';
3353 encoded_uri[k++] = hexa[uri[i] / 16];
3354 encoded_uri[k++] = hexa[uri[i] % 16];
3360 gint open_uri(const gchar *uri, const gchar *cmdline)
3364 gchar buf[BUFFSIZE];
3366 gchar encoded_uri[BUFFSIZE];
3367 g_return_val_if_fail(uri != NULL, -1);
3369 /* an option to choose whether to use encode_uri or not ? */
3370 encode_uri(encoded_uri, BUFFSIZE, uri);
3373 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3374 !strchr(p + 2, '%'))
3375 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3378 g_warning("Open URI command-line is invalid "
3379 "(there must be only one '%%s'): %s",
3381 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3384 execute_command_line(buf, TRUE);
3386 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3389 extern osso_context_t *get_osso_context(void);
3390 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3391 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3392 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3397 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3399 gchar buf[BUFFSIZE];
3402 g_return_val_if_fail(filepath != NULL, -1);
3405 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3406 !strchr(p + 2, '%'))
3407 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3410 g_warning("Open Text Editor command-line is invalid "
3411 "(there must be only one '%%s'): %s",
3413 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3416 execute_command_line(buf, TRUE);
3421 time_t remote_tzoffset_sec(const gchar *zone)
3423 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3429 time_t remoteoffset;
3431 strncpy(zone3, zone, 3);
3435 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3436 (c == '+' || c == '-')) {
3437 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3439 remoteoffset = -remoteoffset;
3440 } else if (!strncmp(zone, "UT" , 2) ||
3441 !strncmp(zone, "GMT", 2)) {
3443 } else if (strlen(zone3) == 3) {
3444 for (p = ustzstr; *p != '\0'; p += 3) {
3445 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3446 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3447 remoteoffset = iustz * 3600;
3453 } else if (strlen(zone3) == 1) {
3455 case 'Z': remoteoffset = 0; break;
3456 case 'A': remoteoffset = -1; break;
3457 case 'B': remoteoffset = -2; break;
3458 case 'C': remoteoffset = -3; break;
3459 case 'D': remoteoffset = -4; break;
3460 case 'E': remoteoffset = -5; break;
3461 case 'F': remoteoffset = -6; break;
3462 case 'G': remoteoffset = -7; break;
3463 case 'H': remoteoffset = -8; break;
3464 case 'I': remoteoffset = -9; break;
3465 case 'K': remoteoffset = -10; break; /* J is not used */
3466 case 'L': remoteoffset = -11; break;
3467 case 'M': remoteoffset = -12; break;
3468 case 'N': remoteoffset = 1; break;
3469 case 'O': remoteoffset = 2; break;
3470 case 'P': remoteoffset = 3; break;
3471 case 'Q': remoteoffset = 4; break;
3472 case 'R': remoteoffset = 5; break;
3473 case 'S': remoteoffset = 6; break;
3474 case 'T': remoteoffset = 7; break;
3475 case 'U': remoteoffset = 8; break;
3476 case 'V': remoteoffset = 9; break;
3477 case 'W': remoteoffset = 10; break;
3478 case 'X': remoteoffset = 11; break;
3479 case 'Y': remoteoffset = 12; break;
3480 default: remoteoffset = 0; break;
3482 remoteoffset = remoteoffset * 3600;
3486 return remoteoffset;
3489 time_t tzoffset_sec(time_t *now)
3494 struct tm buf1, buf2;
3497 if (now && *now < 0)
3500 gmt = *gmtime_r(now, &buf1);
3501 lt = localtime_r(now, &buf2);
3503 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3505 if (lt->tm_year < gmt.tm_year)
3507 else if (lt->tm_year > gmt.tm_year)
3509 else if (lt->tm_yday < gmt.tm_yday)
3511 else if (lt->tm_yday > gmt.tm_yday)
3514 if (off >= 24 * 60) /* should be impossible */
3515 off = 23 * 60 + 59; /* if not, insert silly value */
3516 if (off <= -24 * 60)
3517 off = -(23 * 60 + 59);
3522 /* calculate timezone offset */
3523 gchar *tzoffset(time_t *now)
3525 static gchar offset_string[6];
3530 struct tm buf1, buf2;
3533 if (now && *now < 0)
3536 gmt = *gmtime_r(now, &buf1);
3537 lt = localtime_r(now, &buf2);
3539 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3541 if (lt->tm_year < gmt.tm_year)
3543 else if (lt->tm_year > gmt.tm_year)
3545 else if (lt->tm_yday < gmt.tm_yday)
3547 else if (lt->tm_yday > gmt.tm_yday)
3555 if (off >= 24 * 60) /* should be impossible */
3556 off = 23 * 60 + 59; /* if not, insert silly value */
3558 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3560 return offset_string;
3563 void get_rfc822_date(gchar *buf, gint len)
3567 gchar day[4], mon[4];
3568 gint dd, hh, mm, ss, yyyy;
3571 gchar buf2[BUFFSIZE];
3575 lt = localtime_r(&t, &buf1);
3577 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3578 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3580 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3581 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3584 void debug_set_mode(gboolean mode)
3589 gboolean debug_get_mode(void)
3594 void debug_print_real(const gchar *format, ...)
3597 gchar buf[BUFFSIZE];
3599 if (!debug_mode) return;
3601 va_start(args, format);
3602 g_vsnprintf(buf, sizeof(buf), format, args);
3609 const char * debug_srcname(const char *file)
3611 const char *s = strrchr (file, '/');
3616 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3618 if (subject == NULL)
3621 subject += subject_get_prefix_length(subject);
3623 return g_hash_table_lookup(subject_table, subject);
3626 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3629 if (subject == NULL || *subject == 0)
3631 subject += subject_get_prefix_length(subject);
3632 g_hash_table_insert(subject_table, subject, data);
3635 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3637 if (subject == NULL)
3640 subject += subject_get_prefix_length(subject);
3641 g_hash_table_remove(subject_table, subject);
3645 *\brief Check if a string is prefixed with known (combinations)
3646 * of prefixes. The function assumes that each prefix
3647 * is terminated by zero or exactly _one_ space.
3649 *\param str String to check for a prefixes
3651 *\return int Number of chars in the prefix that should be skipped
3652 * for a "clean" subject line. If no prefix was found, 0
3657 static regex_t u_regex;
3658 static gboolean u_init_;
3661 void utils_free_regex(void)
3671 int subject_get_prefix_length(const gchar *subject)
3674 /*!< Array with allowable reply prefixes regexps. */
3675 static const gchar * const prefixes[] = {
3676 "Re\\:", /* "Re:" */
3677 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3678 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3679 "Aw\\:", /* "Aw:" (German) */
3680 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3681 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3682 "Fw\\:", /* "Fw:" Forward */
3683 "Fwd\\:", /* "Fwd:" Forward */
3684 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3685 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3686 "Rif\\:", /* "Rif:" (Italian Outlook) */
3687 "Sv\\:", /* "Sv" (Norwegian) */
3688 "Vs\\:", /* "Vs" (Norwegian) */
3689 "Ad\\:", /* "Ad" (Norwegian) */
3690 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3693 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3697 if (!subject) return 0;
3698 if (!*subject) return 0;
3701 GString *s = g_string_new("");
3703 for (n = 0; n < PREFIXES; n++)
3704 /* Terminate each prefix regexpression by a
3705 * "\ ?" (zero or ONE space), and OR them */
3706 g_string_append_printf(s, "(%s\\ ?)%s",
3711 g_string_prepend(s, "(");
3712 g_string_append(s, ")+"); /* match at least once */
3713 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3716 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3717 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3718 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3719 debug_print("Error compiling regexp %s\n", s->str);
3720 g_string_free(s, TRUE);
3724 g_string_free(s, TRUE);
3728 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3733 /*!< Array with allowable reply prefixes regexps. */
3734 static const gchar * const prefixes[] = {
3736 "antw:", /* "Antw:" (Dutch / German Outlook) */
3737 "aw:", /* "Aw:" (German) */
3738 "antwort:", /* "Antwort:" (German Lotus Notes) */
3739 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3740 "fw:", /* "Fw:" Forward */
3741 "fwd:", /* "Fwd:" Forward */
3742 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3743 "odp:", /* "Odp:" Re (Polish Outlook) */
3744 "rif:", /* "Rif:" (Italian Outlook) */
3745 "sv:", /* "Sv" (Norwegian) */
3746 "vs:", /* "Vs" (Norwegian) */
3747 "ad:", /* "Ad" (Norwegian) */
3750 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3753 if (!subject) return 0;
3754 if (!*subject) return 0;
3756 for (n = 0; n < PREFIXES; n++) {
3757 int len = strlen(prefixes[n]);
3758 if (!strncasecmp(subject, prefixes[n], len))
3764 static guint g_stricase_hash(gconstpointer gptr)
3766 guint hash_result = 0;
3769 for (str = gptr; str && *str; str++) {
3770 hash_result += toupper(*str);
3776 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3778 const char *str1 = gptr1;
3779 const char *str2 = gptr2;
3781 return !strcasecmp(str1, str2);
3784 gint g_int_compare(gconstpointer a, gconstpointer b)
3786 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3789 gchar *generate_msgid(gchar *buf, gint len)
3799 lt = localtime_r(&t, &buft);
3801 if (strcmp(buf, "") == 0) {
3802 addr = g_strconcat("@", get_domain_name(), NULL);
3805 addr = g_strconcat("@", buf, NULL);
3808 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3809 lt->tm_year + 1900, lt->tm_mon + 1,
3810 lt->tm_mday, lt->tm_hour,
3811 lt->tm_min, lt->tm_sec,
3812 (guint) rand(), addr);
3819 quote_cmd_argument()
3821 return a quoted string safely usable in argument of a command.
3823 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3826 gint quote_cmd_argument(gchar * result, guint size,
3836 for(p = path ; * p != '\0' ; p ++) {
3838 if (isalnum((guchar)*p) || (* p == '/')) {
3839 if (remaining > 0) {
3845 result[size - 1] = '\0';
3850 if (remaining >= 2) {
3858 result[size - 1] = '\0';
3863 if (remaining > 0) {
3867 result[size - 1] = '\0';
3881 static void g_node_map_recursive(GNode *node, gpointer data)
3883 GNodeMapData *mapdata = (GNodeMapData *) data;
3885 GNodeMapData newmapdata;
3888 newdata = mapdata->func(node->data, mapdata->data);
3889 if (newdata != NULL) {
3890 newnode = g_node_new(newdata);
3891 g_node_append(mapdata->parent, newnode);
3893 newmapdata.parent = newnode;
3894 newmapdata.func = mapdata->func;
3895 newmapdata.data = mapdata->data;
3897 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3901 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3904 GNodeMapData mapdata;
3906 g_return_val_if_fail(node != NULL, NULL);
3907 g_return_val_if_fail(func != NULL, NULL);
3909 root = g_node_new(func(node->data, data));
3911 mapdata.parent = root;
3912 mapdata.func = func;
3913 mapdata.data = data;
3915 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3920 #define HEX_TO_INT(val, hex) \
3924 if ('0' <= c && c <= '9') { \
3926 } else if ('a' <= c && c <= 'f') { \
3927 val = c - 'a' + 10; \
3928 } else if ('A' <= c && c <= 'F') { \
3929 val = c - 'A' + 10; \
3935 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3942 if (hi == -1 || lo == -1)
3945 *out = (hi << 4) + lo;
3949 #define INT_TO_HEX(hex, val) \
3952 hex = '0' + (val); \
3954 hex = 'A' + (val) - 10; \
3957 void get_hex_str(gchar *out, guchar ch)
3961 INT_TO_HEX(hex, ch >> 4);
3963 INT_TO_HEX(hex, ch & 0x0f);
3969 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3971 #define G_PRINT_REF g_print
3975 *\brief Register ref counted pointer. It is based on GBoxed, so should
3976 * work with anything that uses the GType system. The semantics
3977 * are similar to a C++ auto pointer, with the exception that
3978 * C doesn't have automatic closure (calling destructors) when
3979 * exiting a block scope.
3980 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3981 * function directly.
3983 *\return GType A GType type.
3985 GType g_auto_pointer_register(void)
3987 static GType auto_pointer_type;
3988 if (!auto_pointer_type)
3990 g_boxed_type_register_static
3991 ("G_TYPE_AUTO_POINTER",
3992 (GBoxedCopyFunc) g_auto_pointer_copy,
3993 (GBoxedFreeFunc) g_auto_pointer_free);
3994 return auto_pointer_type;
3998 *\brief Structure with g_new() allocated pointer guarded by the
4001 typedef struct AutoPointerRef {
4002 void (*free) (gpointer);
4008 *\brief The auto pointer opaque structure that references the
4009 * pointer guard block.
4011 typedef struct AutoPointer {
4012 AutoPointerRef *ref;
4013 gpointer ptr; /*!< access to protected pointer */
4017 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4021 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4022 * ... when assigning, copying and freeing storage elements
4024 * gtk_list_store_new(N_S_COLUMNS,
4025 * G_TYPE_AUTO_POINTER,
4029 * Template *precious_data = g_new0(Template, 1);
4030 * g_pointer protect = g_auto_pointer_new(precious_data);
4032 * gtk_list_store_set(container, &iter,
4036 * ... the gtk_list_store has copied the pointer and
4037 * ... incremented its reference count, we should free
4038 * ... the auto pointer (in C++ a destructor would do
4039 * ... this for us when leaving block scope)
4041 * g_auto_pointer_free(protect);
4043 * ... gtk_list_store_set() now manages the data. When
4044 * ... *explicitly* requesting a pointer from the list
4045 * ... store, don't forget you get a copy that should be
4046 * ... freed with g_auto_pointer_free() eventually.
4050 *\param pointer Pointer to be guarded.
4052 *\return GAuto * Pointer that should be used in containers with
4055 GAuto *g_auto_pointer_new(gpointer p)
4057 AutoPointerRef *ref;
4063 ref = g_new0(AutoPointerRef, 1);
4064 ptr = g_new0(AutoPointer, 1);
4074 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4080 *\brief Allocate an autopointer using the passed \a free function to
4081 * free the guarded pointer
4083 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4090 aptr = g_auto_pointer_new(p);
4091 aptr->ref->free = free_;
4095 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4097 if (auto_ptr == NULL)
4099 return ((AutoPointer *) auto_ptr)->ptr;
4103 *\brief Copies an auto pointer by. It's mostly not necessary
4104 * to call this function directly, unless you copy/assign
4105 * the guarded pointer.
4107 *\param auto_ptr Auto pointer returned by previous call to
4108 * g_auto_pointer_new_XXX()
4110 *\return gpointer An auto pointer
4112 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4115 AutoPointerRef *ref;
4118 if (auto_ptr == NULL)
4123 newp = g_new0(AutoPointer, 1);
4126 newp->ptr = ref->pointer;
4130 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4136 *\brief Free an auto pointer
4138 void g_auto_pointer_free(GAuto *auto_ptr)
4141 AutoPointerRef *ref;
4143 if (auto_ptr == NULL)
4149 if (--(ref->cnt) == 0) {
4151 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4153 ref->free(ref->pointer);
4158 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4163 void replace_returns(gchar *str)
4168 while (strstr(str, "\n")) {
4169 *strstr(str, "\n") = ' ';
4171 while (strstr(str, "\r")) {
4172 *strstr(str, "\r") = ' ';
4176 /* get_uri_part() - retrieves a URI starting from scanpos.
4177 Returns TRUE if succesful */
4178 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4179 const gchar **bp, const gchar **ep, gboolean hdr)
4182 gint parenthese_cnt = 0;
4184 g_return_val_if_fail(start != NULL, FALSE);
4185 g_return_val_if_fail(scanpos != NULL, FALSE);
4186 g_return_val_if_fail(bp != NULL, FALSE);
4187 g_return_val_if_fail(ep != NULL, FALSE);
4191 /* find end point of URI */
4192 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4193 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4194 !IS_ASCII(*(const guchar *)ep_) ||
4195 strchr("[]{}<>\"", *ep_)) {
4197 } else if (strchr("(", *ep_)) {
4199 } else if (strchr(")", *ep_)) {
4200 if (parenthese_cnt > 0)
4207 /* no punctuation at end of string */
4209 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4210 * should pass some URI type to this function and decide on that whether
4211 * to perform punctuation stripping */
4213 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4215 for (; ep_ - 1 > scanpos + 1 &&
4216 IS_REAL_PUNCT(*(ep_ - 1));
4220 #undef IS_REAL_PUNCT
4227 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4229 while (bp && *bp && g_ascii_isspace(*bp))
4231 return g_strndup(bp, ep - bp);
4234 /* valid mail address characters */
4235 #define IS_RFC822_CHAR(ch) \
4239 !g_ascii_isspace(ch) && \
4240 !strchr("(),;<>\"", (ch)))
4242 /* alphabet and number within 7bit ASCII */
4243 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4244 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4246 static GHashTable *create_domain_tab(void)
4248 static const gchar *toplvl_domains [] = {
4250 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4251 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4252 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4253 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4254 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4255 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4256 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4257 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4258 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4259 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4260 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4261 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4262 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4263 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4264 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4265 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4266 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4267 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4268 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4269 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4270 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4271 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4272 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4273 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4274 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4275 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4278 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4280 g_return_val_if_fail(htab, NULL);
4281 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4282 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4286 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4288 const gint MAX_LVL_DOM_NAME_LEN = 6;
4289 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4290 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4293 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4296 for (p = buf; p < m && first < last; *p++ = *first++)
4300 return g_hash_table_lookup(tab, buf) != NULL;
4303 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4304 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4305 const gchar **bp, const gchar **ep, gboolean hdr)
4307 /* more complex than the uri part because we need to scan back and forward starting from
4308 * the scan position. */
4309 gboolean result = FALSE;
4310 const gchar *bp_ = NULL;
4311 const gchar *ep_ = NULL;
4312 static GHashTable *dom_tab;
4313 const gchar *last_dot = NULL;
4314 const gchar *prelast_dot = NULL;
4315 const gchar *last_tld_char = NULL;
4317 /* the informative part of the email address (describing the name
4318 * of the email address owner) may contain quoted parts. the
4319 * closure stack stores the last encountered quotes. */
4320 gchar closure_stack[128];
4321 gchar *ptr = closure_stack;
4323 g_return_val_if_fail(start != NULL, FALSE);
4324 g_return_val_if_fail(scanpos != NULL, FALSE);
4325 g_return_val_if_fail(bp != NULL, FALSE);
4326 g_return_val_if_fail(ep != NULL, FALSE);
4329 const gchar *start_quote = NULL;
4330 const gchar *end_quote = NULL;
4332 /* go to the real start */
4333 if (start[0] == ',')
4335 if (start[0] == ';')
4337 while (start[0] == '\n' || start[0] == '\r')
4339 while (start[0] == ' ' || start[0] == '\t')
4344 /* check if there are quotes (to skip , in them) */
4345 if (*start == '"') {
4346 start_quote = start;
4348 end_quote = strstr(start, "\"");
4354 /* skip anything between quotes */
4355 if (start_quote && end_quote) {
4360 /* find end (either , or ; or end of line) */
4361 if (strstr(start, ",") && strstr(start, ";"))
4362 *ep = strstr(start,",") < strstr(start, ";")
4363 ? strstr(start, ",") : strstr(start, ";");
4364 else if (strstr(start, ","))
4365 *ep = strstr(start, ",");
4366 else if (strstr(start, ";"))
4367 *ep = strstr(start, ";");
4369 *ep = start+strlen(start);
4371 /* go back to real start */
4372 if (start_quote && end_quote) {
4373 start = start_quote;
4376 /* check there's still an @ in that, or search
4377 * further if possible */
4378 if (strstr(start, "@") && strstr(start, "@") < *ep)
4380 else if (*ep < start+strlen(start)) {
4383 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4391 dom_tab = create_domain_tab();
4392 g_return_val_if_fail(dom_tab, FALSE);
4394 /* scan start of address */
4395 for (bp_ = scanpos - 1;
4396 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4399 /* TODO: should start with an alnum? */
4401 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4404 if (bp_ != scanpos) {
4405 /* scan end of address */
4406 for (ep_ = scanpos + 1;
4407 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4409 prelast_dot = last_dot;
4411 if (*(last_dot + 1) == '.') {
4412 if (prelast_dot == NULL)
4414 last_dot = prelast_dot;
4419 /* TODO: really should terminate with an alnum? */
4420 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4425 if (last_dot == NULL)
4427 if (last_dot >= ep_)
4428 last_dot = prelast_dot;
4429 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4433 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4434 if (*last_tld_char == '?')
4437 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4444 if (!result) return FALSE;
4446 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4447 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4448 && IS_RFC822_CHAR(*(ep_ + 3))) {
4449 /* this informative part with an @ in it is
4450 * followed by the email address */
4453 /* go to matching '>' (or next non-rfc822 char, like \n) */
4454 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4457 /* include the bracket */
4458 if (*ep_ == '>') ep_++;
4460 /* include the leading quote */
4468 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4469 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4472 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4473 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4476 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4477 #define IN_STACK() (ptr > closure_stack)
4478 /* has underrun check */
4479 #define POP_STACK() if(IN_STACK()) --ptr
4480 /* has overrun check */
4481 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4482 /* has underrun check */
4483 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4487 /* scan for the informative part. */
4488 for (bp_ -= 2; bp_ >= start; bp_--) {
4489 /* if closure on the stack keep scanning */
4490 if (PEEK_STACK() == *bp_) {
4494 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4499 /* if nothing in the closure stack, do the special conditions
4500 * the following if..else expression simply checks whether
4501 * a token is acceptable. if not acceptable, the clause
4502 * should terminate the loop with a 'break' */
4503 if (!PEEK_STACK()) {
4505 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4506 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4507 /* hyphens are allowed, but only in
4509 } else if (strchr(" \"'", *bp_)) {
4510 /* but anything not being a punctiation
4513 break; /* anything else is rejected */
4520 /* scan forward (should start with an alnum) */
4521 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4537 #undef IS_ASCII_ALNUM
4538 #undef IS_RFC822_CHAR
4540 gchar *make_email_string(const gchar *bp, const gchar *ep)
4542 /* returns a mailto: URI; mailto: is also used to detect the
4543 * uri type later on in the button_pressed signal handler */
4547 tmp = g_strndup(bp, ep - bp);
4548 result = g_strconcat("mailto:", tmp, NULL);
4554 gchar *make_http_string(const gchar *bp, const gchar *ep)
4556 /* returns an http: URI; */
4560 while (bp && *bp && g_ascii_isspace(*bp))
4562 tmp = g_strndup(bp, ep - bp);
4563 result = g_strconcat("http://", tmp, NULL);
4569 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4571 FILE *fp = fopen(path, "rb");
4572 gchar buf[BUFFSIZE];
4573 gchar *result = NULL;
4576 while (fgets(buf, sizeof (buf), fp) != NULL) {
4577 gchar **parts = g_strsplit(buf, ";", 3);
4578 gchar *trimmed = parts[0];
4579 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4581 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4582 trimmed[strlen(trimmed)-1] = '\0';
4584 if (!strcmp(trimmed, type)) {
4585 gboolean needsterminal = FALSE;
4586 if (parts[2] && strstr(parts[2], "needsterminal")) {
4587 needsterminal = TRUE;
4589 if (parts[2] && strstr(parts[2], "test=")) {
4590 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4591 gchar *testcmd = orig_testcmd;
4592 if (strstr(testcmd,";"))
4593 *(strstr(testcmd,";")) = '\0';
4594 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4596 while (testcmd[strlen(testcmd)-1] == '\n')
4597 testcmd[strlen(testcmd)-1] = '\0';
4598 while (testcmd[strlen(testcmd)-1] == '\r')
4599 testcmd[strlen(testcmd)-1] = '\0';
4600 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4601 testcmd[strlen(testcmd)-1] = '\0';
4603 if (strstr(testcmd, "%s")) {
4604 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4605 gint res = system(tmp);
4607 g_free(orig_testcmd);
4614 gint res = system(testcmd);
4615 g_free(orig_testcmd);
4625 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4627 while (trimmed[strlen(trimmed)-1] == '\n')
4628 trimmed[strlen(trimmed)-1] = '\0';
4629 while (trimmed[strlen(trimmed)-1] == '\r')
4630 trimmed[strlen(trimmed)-1] = '\0';
4631 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4632 trimmed[strlen(trimmed)-1] = '\0';
4633 result = g_strdup(trimmed);
4636 /* if there are no single quotes around %s, add them.
4637 * '.*%s.*' is ok, as in display 'png:%s'
4639 if (strstr(result, "%s")
4640 && !(strstr(result, "'") < strstr(result,"%s") &&
4641 strstr(strstr(result,"%s"), "'"))) {
4642 gchar *start = g_strdup(result);
4643 gchar *end = g_strdup(strstr(result, "%s")+2);
4645 *strstr(start, "%s") = '\0';
4646 tmp = g_strconcat(start,"'%s'",end, NULL);
4652 if (needsterminal) {
4653 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4664 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4666 gchar *result = NULL;
4670 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4671 result = mailcap_get_command_in_file(path, type, file_to_open);
4675 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4679 void mailcap_update_default(const gchar *type, const gchar *command)
4681 gchar *path = NULL, *outpath = NULL;
4682 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4683 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4684 FILE *fp = fopen(path, "rb");
4685 FILE *outfp = fopen(outpath, "wb");
4686 gchar buf[BUFFSIZE];
4687 gboolean err = FALSE;
4695 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4696 gchar **parts = g_strsplit(buf, ";", 3);
4697 gchar *trimmed = parts[0];
4698 while (trimmed[0] == ' ')
4700 while (trimmed[strlen(trimmed)-1] == ' ')
4701 trimmed[strlen(trimmed)-1] = '\0';
4703 if (!strcmp(trimmed, type)) {
4708 if(fputs(buf, outfp) == EOF) {
4715 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4721 if (fclose(outfp) == EOF)
4725 g_rename(outpath, path);
4731 gint copy_dir(const gchar *src, const gchar *dst)
4736 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4737 g_warning("failed to open directory: %s\n", src);
4741 if (make_dir(dst) < 0)
4744 while ((name = g_dir_read_name(dir)) != NULL) {
4745 gchar *old_file, *new_file;
4746 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4747 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4748 debug_print("copying: %s -> %s\n", old_file, new_file);
4749 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4750 gint r = copy_file(old_file, new_file, TRUE);
4757 /* Windows has no symlinks. Or well, Vista seems to
4758 have something like this but the semantics might be
4759 different. Thus we don't use it under Windows. */
4760 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4763 gchar *target = g_file_read_link(old_file, &error);
4765 r = symlink(target, new_file);
4772 #endif /*G_OS_WIN32*/
4773 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4774 gint r = copy_dir(old_file, new_file);
4785 /* crude test to see if a file is an email. */
4786 gboolean file_is_email (const gchar *filename)
4792 if (filename == NULL)
4794 if ((fp = g_fopen(filename, "rb")) == NULL)
4796 while (i < 60 && score < 3
4797 && fgets(buffer, sizeof (buffer), fp) > 0) {
4798 if (!strncmp(buffer, "From:", strlen("From:")))
4800 if (!strncmp(buffer, "To:", strlen("To:")))
4802 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4807 return (score >= 3);
4810 gboolean sc_g_list_bigger(GList *list, gint max)
4814 while (cur && i <= max+1) {
4821 gboolean sc_g_slist_bigger(GSList *list, gint max)
4825 while (cur && i <= max+1) {
4832 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4833 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4834 NULL, NULL, NULL, NULL, NULL, NULL};
4835 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4836 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4837 NULL, NULL, NULL, NULL, NULL, NULL};
4839 gint daynames_len[] = {0,0,0,0,0,0,0};
4840 gint monthnames_len[] = {0,0,0,0,0,0,
4842 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4843 gint s_monthnames_len[] = {0,0,0,0,0,0,
4845 const gchar *s_am_up = NULL;
4846 const gchar *s_pm_up = NULL;
4847 const gchar *s_am_low = NULL;
4848 const gchar *s_pm_low = NULL;
4850 gint s_am_up_len = 0;
4851 gint s_pm_up_len = 0;
4852 gint s_am_low_len = 0;
4853 gint s_pm_low_len = 0;
4855 const gchar *def_loc_format = NULL;
4856 const gchar *date_loc_format = NULL;
4857 const gchar *time_loc_format = NULL;
4858 const gchar *time_am_pm = NULL;
4860 static gboolean time_names_init_done = FALSE;
4862 static void init_time_names(void)
4866 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4867 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4868 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4869 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4870 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4871 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4872 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4874 monthnames[0] = Q_("Complete month name for use by strftime|January");
4875 monthnames[1] = Q_("Complete month name for use by strftime|February");
4876 monthnames[2] = Q_("Complete month name for use by strftime|March");
4877 monthnames[3] = Q_("Complete month name for use by strftime|April");
4878 monthnames[4] = Q_("Complete month name for use by strftime|May");
4879 monthnames[5] = Q_("Complete month name for use by strftime|June");
4880 monthnames[6] = Q_("Complete month name for use by strftime|July");
4881 monthnames[7] = Q_("Complete month name for use by strftime|August");
4882 monthnames[8] = Q_("Complete month name for use by strftime|September");
4883 monthnames[9] = Q_("Complete month name for use by strftime|October");
4884 monthnames[10] = Q_("Complete month name for use by strftime|November");
4885 monthnames[11] = Q_("Complete month name for use by strftime|December");
4887 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4888 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4889 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4890 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4891 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4892 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4893 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4895 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4896 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4897 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4898 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4899 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4900 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4901 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4902 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4903 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4904 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4905 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4906 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4908 for (i = 0; i < 7; i++) {
4909 daynames_len[i] = strlen(daynames[i]);
4910 s_daynames_len[i] = strlen(s_daynames[i]);
4912 for (i = 0; i < 12; i++) {
4913 monthnames_len[i] = strlen(monthnames[i]);
4914 s_monthnames_len[i] = strlen(s_monthnames[i]);
4917 s_am_up = Q_("For use by strftime (morning)|AM");
4918 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4919 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4920 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4922 s_am_up_len = strlen(s_am_up);
4923 s_pm_up_len = strlen(s_pm_up);
4924 s_am_low_len = strlen(s_am_low);
4925 s_pm_low_len = strlen(s_pm_low);
4927 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4928 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4929 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4931 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4933 time_names_init_done = TRUE;
4936 #define CHECK_SIZE() { \
4937 total_done += len; \
4938 if (total_done >= buflen) { \
4939 buf[buflen-1] = '\0'; \
4944 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4946 gchar *curpos = buf;
4947 gint total_done = 0;
4948 gchar subbuf[64], subfmt[64];
4949 static time_t last_tzset = (time_t)0;
4951 if (!time_names_init_done)
4954 if (format == NULL || lt == NULL)
4957 if (last_tzset != time(NULL)) {
4959 last_tzset = time(NULL);
4962 if (*format == '%') {
4963 gint len = 0, tmp = 0;
4967 len = 1; CHECK_SIZE();
4971 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4972 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4975 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4976 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4980 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4981 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4984 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4985 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4988 fast_strftime(subbuf, 64, def_loc_format, lt);
4989 len = strlen(subbuf); CHECK_SIZE();
4990 strncpy2(curpos, subbuf, buflen - total_done);
4993 total_done += 2; CHECK_SIZE();
4994 tmp = (lt->tm_year + 1900)/100;
4995 *curpos++ = '0'+(tmp / 10);
4996 *curpos++ = '0'+(tmp % 10);
4999 total_done += 2; CHECK_SIZE();
5000 *curpos++ = '0'+(lt->tm_mday / 10);
5001 *curpos++ = '0'+(lt->tm_mday % 10);
5004 total_done += 8; CHECK_SIZE();
5005 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5006 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5008 *curpos++ = '0'+(lt->tm_mday / 10);
5009 *curpos++ = '0'+(lt->tm_mday % 10);
5011 tmp = lt->tm_year%100;
5012 *curpos++ = '0'+(tmp / 10);
5013 *curpos++ = '0'+(tmp % 10);
5016 len = 2; CHECK_SIZE();
5017 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5020 len = 10; CHECK_SIZE();
5021 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5022 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5025 total_done += 2; CHECK_SIZE();
5026 *curpos++ = '0'+(lt->tm_hour / 10);
5027 *curpos++ = '0'+(lt->tm_hour % 10);
5030 total_done += 2; CHECK_SIZE();
5036 *curpos++ = '0'+(tmp / 10);
5037 *curpos++ = '0'+(tmp % 10);
5040 len = 3; CHECK_SIZE();
5041 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5044 len = 2; CHECK_SIZE();
5045 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5048 len = 2; CHECK_SIZE();
5054 snprintf(curpos, buflen - total_done, "%2d", tmp);
5057 total_done += 2; CHECK_SIZE();
5058 tmp = lt->tm_mon + 1;
5059 *curpos++ = '0'+(tmp / 10);
5060 *curpos++ = '0'+(tmp % 10);
5063 total_done += 2; CHECK_SIZE();
5064 *curpos++ = '0'+(lt->tm_min / 10);
5065 *curpos++ = '0'+(lt->tm_min % 10);
5068 len = 1; CHECK_SIZE();
5072 if (lt->tm_hour >= 12) {
5073 len = s_pm_up_len; CHECK_SIZE();
5074 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5076 len = s_am_up_len; CHECK_SIZE();
5077 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5081 if (lt->tm_hour >= 12) {
5082 len = s_pm_low_len; CHECK_SIZE();
5083 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5085 len = s_am_low_len; CHECK_SIZE();
5086 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5090 fast_strftime(subbuf, 64, time_am_pm, lt);
5091 len = strlen(subbuf); CHECK_SIZE();
5092 strncpy2(curpos, subbuf, buflen - total_done);
5095 total_done += 5; CHECK_SIZE();
5096 *curpos++ = '0'+(lt->tm_hour / 10);
5097 *curpos++ = '0'+(lt->tm_hour % 10);
5099 *curpos++ = '0'+(lt->tm_min / 10);
5100 *curpos++ = '0'+(lt->tm_min % 10);
5103 snprintf(subbuf, 64, "%ld", mktime(lt));
5104 len = strlen(subbuf); CHECK_SIZE();
5105 strncpy2(curpos, subbuf, buflen - total_done);
5108 total_done += 2; CHECK_SIZE();
5109 *curpos++ = '0'+(lt->tm_sec / 10);
5110 *curpos++ = '0'+(lt->tm_sec % 10);
5113 len = 1; CHECK_SIZE();
5117 total_done += 8; 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);
5124 *curpos++ = '0'+(lt->tm_sec / 10);
5125 *curpos++ = '0'+(lt->tm_sec % 10);
5128 len = 1; CHECK_SIZE();
5129 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5132 len = 1; CHECK_SIZE();
5133 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5136 fast_strftime(subbuf, 64, date_loc_format, lt);
5137 len = strlen(subbuf); CHECK_SIZE();
5138 strncpy2(curpos, subbuf, buflen - total_done);
5141 fast_strftime(subbuf, 64, time_loc_format, lt);
5142 len = strlen(subbuf); CHECK_SIZE();
5143 strncpy2(curpos, subbuf, buflen - total_done);
5146 total_done += 2; CHECK_SIZE();
5147 tmp = lt->tm_year%100;
5148 *curpos++ = '0'+(tmp / 10);
5149 *curpos++ = '0'+(tmp % 10);
5152 len = 4; CHECK_SIZE();
5153 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5163 /* let these complicated ones be done with the libc */
5164 snprintf(subfmt, 64, "%%%c", *format);
5165 strftime(subbuf, 64, subfmt, lt);
5166 len = strlen(subbuf); CHECK_SIZE();
5167 strncpy2(curpos, subbuf, buflen - total_done);
5171 /* let these complicated modifiers be done with the libc */
5172 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5173 strftime(subbuf, 64, subfmt, lt);
5174 len = strlen(subbuf); CHECK_SIZE();
5175 strncpy2(curpos, subbuf, buflen - total_done);
5179 if (format && *format)
5180 g_warning("format error (%c)", *format);
5187 int len = 1; CHECK_SIZE();
5188 *curpos++ = *format++;
5195 gboolean prefs_common_get_use_shred(void);
5199 #define WEXITSTATUS(x) (x)
5202 int claws_unlink(const gchar *filename)
5205 static int found_shred = -1;
5206 static const gchar *args[4];
5208 if (filename == NULL)
5211 if (prefs_common_get_use_shred()) {
5212 if (found_shred == -1) {
5214 args[0] = g_find_program_in_path("shred");
5215 debug_print("found shred: %s\n", args[0]);
5216 found_shred = (args[0] != NULL) ? 1:0;
5220 if (found_shred == 1) {
5221 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5222 if (s.st_nlink == 1) {
5225 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5226 NULL, NULL, NULL, NULL, &status, NULL);
5227 debug_print("%s %s exited with status %d\n",
5228 args[0], filename, WEXITSTATUS(status));
5229 if (truncate(filename, 0) < 0)
5230 g_warning("couln't truncate");
5235 return g_unlink(filename);