2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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);
1999 const gchar *get_tmp_dir(void)
2001 static gchar *tmp_dir = NULL;
2004 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2010 gchar *get_tmp_file(void)
2013 static guint32 id = 0;
2015 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2016 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2021 const gchar *get_domain_name(void)
2024 static gchar *domain_name = NULL;
2030 if (gethostname(hostname, sizeof(hostname)) != 0) {
2031 perror("gethostname");
2032 domain_name = "unknown";
2034 hostname[sizeof(hostname) - 1] = '\0';
2035 if ((hp = my_gethostbyname(hostname)) == NULL) {
2036 perror("gethostbyname");
2037 domain_name = g_strdup(hostname);
2039 domain_name = g_strdup(hp->h_name);
2042 debug_print("domain name = %s\n", domain_name);
2051 off_t get_file_size(const gchar *file)
2055 if (g_stat(file, &s) < 0) {
2056 FILE_OP_ERROR(file, "stat");
2063 time_t get_file_mtime(const gchar *file)
2067 if (g_stat(file, &s) < 0) {
2068 FILE_OP_ERROR(file, "stat");
2075 off_t get_file_size_as_crlf(const gchar *file)
2079 gchar buf[BUFFSIZE];
2081 if ((fp = g_fopen(file, "rb")) == NULL) {
2082 FILE_OP_ERROR(file, "fopen");
2086 while (fgets(buf, sizeof(buf), fp) != NULL) {
2088 size += strlen(buf) + 2;
2092 FILE_OP_ERROR(file, "fgets");
2101 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2108 if (g_stat(file, &s) < 0) {
2109 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2113 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2120 /* Test on whether FILE is a relative file name. This is
2121 * straightforward for Unix but more complex for Windows. */
2122 gboolean is_relative_filename(const gchar *file)
2127 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2128 return FALSE; /* Prefixed with a hostname - this can't
2129 * be a relative name. */
2131 if ( ((*file >= 'a' && *file <= 'z')
2132 || (*file >= 'A' && *file <= 'Z'))
2134 file += 2; /* Skip drive letter. */
2136 return !(*file == '\\' || *file == '/');
2138 return !(*file == G_DIR_SEPARATOR);
2143 gboolean is_dir_exist(const gchar *dir)
2148 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2151 gboolean is_file_entry_exist(const gchar *file)
2156 return g_file_test(file, G_FILE_TEST_EXISTS);
2159 gboolean dirent_is_regular_file(struct dirent *d)
2161 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2162 if (d->d_type == DT_REG)
2164 else if (d->d_type != DT_UNKNOWN)
2168 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2171 gint change_dir(const gchar *dir)
2173 gchar *prevdir = NULL;
2176 prevdir = g_get_current_dir();
2178 if (g_chdir(dir) < 0) {
2179 FILE_OP_ERROR(dir, "chdir");
2180 if (debug_mode) g_free(prevdir);
2182 } else if (debug_mode) {
2185 cwd = g_get_current_dir();
2186 if (strcmp(prevdir, cwd) != 0)
2187 g_print("current dir: %s\n", cwd);
2195 gint make_dir(const gchar *dir)
2197 if (g_mkdir(dir, S_IRWXU) < 0) {
2198 FILE_OP_ERROR(dir, "mkdir");
2201 if (g_chmod(dir, S_IRWXU) < 0)
2202 FILE_OP_ERROR(dir, "chmod");
2207 gint make_dir_hier(const gchar *dir)
2212 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2213 parent_dir = g_strndup(dir, p - dir);
2214 if (*parent_dir != '\0') {
2215 if (!is_dir_exist(parent_dir)) {
2216 if (make_dir(parent_dir) < 0) {
2225 if (!is_dir_exist(dir)) {
2226 if (make_dir(dir) < 0)
2233 gint remove_all_files(const gchar *dir)
2236 const gchar *dir_name;
2239 prev_dir = g_get_current_dir();
2241 if (g_chdir(dir) < 0) {
2242 FILE_OP_ERROR(dir, "chdir");
2247 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2248 g_warning("failed to open directory: %s\n", dir);
2253 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2254 if (claws_unlink(dir_name) < 0)
2255 FILE_OP_ERROR(dir_name, "unlink");
2260 if (g_chdir(prev_dir) < 0) {
2261 FILE_OP_ERROR(prev_dir, "chdir");
2271 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2274 const gchar *dir_name;
2278 prev_dir = g_get_current_dir();
2280 if (g_chdir(dir) < 0) {
2281 FILE_OP_ERROR(dir, "chdir");
2286 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2287 g_warning("failed to open directory: %s\n", dir);
2292 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2293 file_no = to_number(dir_name);
2294 if (file_no > 0 && first <= file_no && file_no <= last) {
2295 if (is_dir_exist(dir_name))
2297 if (claws_unlink(dir_name) < 0)
2298 FILE_OP_ERROR(dir_name, "unlink");
2304 if (g_chdir(prev_dir) < 0) {
2305 FILE_OP_ERROR(prev_dir, "chdir");
2315 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2318 const gchar *dir_name;
2322 prev_dir = g_get_current_dir();
2324 if (g_chdir(dir) < 0) {
2325 FILE_OP_ERROR(dir, "chdir");
2330 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2331 FILE_OP_ERROR(dir, "opendir");
2336 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2337 file_no = to_number(dir_name);
2338 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2339 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2340 if (is_dir_exist(dir_name))
2342 if (claws_unlink(dir_name) < 0)
2343 FILE_OP_ERROR(dir_name, "unlink");
2349 if (g_chdir(prev_dir) < 0) {
2350 FILE_OP_ERROR(prev_dir, "chdir");
2360 gint remove_all_numbered_files(const gchar *dir)
2362 return remove_numbered_files(dir, 0, UINT_MAX);
2365 gint remove_dir_recursive(const gchar *dir)
2369 const gchar *dir_name;
2372 if (g_stat(dir, &s) < 0) {
2373 FILE_OP_ERROR(dir, "stat");
2374 if (ENOENT == errno) return 0;
2378 if (!S_ISDIR(s.st_mode)) {
2379 if (claws_unlink(dir) < 0) {
2380 FILE_OP_ERROR(dir, "unlink");
2387 prev_dir = g_get_current_dir();
2388 /* g_print("prev_dir = %s\n", prev_dir); */
2390 if (!path_cmp(prev_dir, dir)) {
2392 if (g_chdir("..") < 0) {
2393 FILE_OP_ERROR(dir, "chdir");
2396 prev_dir = g_get_current_dir();
2399 if (g_chdir(dir) < 0) {
2400 FILE_OP_ERROR(dir, "chdir");
2405 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2406 g_warning("failed to open directory: %s\n", dir);
2412 /* remove all files in the directory */
2413 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2414 /* g_print("removing %s\n", dir_name); */
2416 if (is_dir_exist(dir_name)) {
2417 if (remove_dir_recursive(dir_name) < 0) {
2418 g_warning("can't remove directory\n");
2422 if (claws_unlink(dir_name) < 0)
2423 FILE_OP_ERROR(dir_name, "unlink");
2429 if (g_chdir(prev_dir) < 0) {
2430 FILE_OP_ERROR(prev_dir, "chdir");
2437 if (g_rmdir(dir) < 0) {
2438 FILE_OP_ERROR(dir, "rmdir");
2445 gint rename_force(const gchar *oldpath, const gchar *newpath)
2448 if (!is_file_entry_exist(oldpath)) {
2452 if (is_file_exist(newpath)) {
2453 if (claws_unlink(newpath) < 0)
2454 FILE_OP_ERROR(newpath, "unlink");
2457 return g_rename(oldpath, newpath);
2461 * Append src file body to the tail of dest file.
2462 * Now keep_backup has no effects.
2464 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2466 FILE *src_fp, *dest_fp;
2470 gboolean err = FALSE;
2472 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2473 FILE_OP_ERROR(src, "fopen");
2477 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2478 FILE_OP_ERROR(dest, "fopen");
2483 if (change_file_mode_rw(dest_fp, dest) < 0) {
2484 FILE_OP_ERROR(dest, "chmod");
2485 g_warning("can't change file mode\n");
2488 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2489 if (n_read < sizeof(buf) && ferror(src_fp))
2491 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2492 g_warning("writing to %s failed.\n", dest);
2500 if (ferror(src_fp)) {
2501 FILE_OP_ERROR(src, "fread");
2505 if (fclose(dest_fp) == EOF) {
2506 FILE_OP_ERROR(dest, "fclose");
2518 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2520 FILE *src_fp, *dest_fp;
2523 gchar *dest_bak = NULL;
2524 gboolean err = FALSE;
2526 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2527 FILE_OP_ERROR(src, "fopen");
2530 if (is_file_exist(dest)) {
2531 dest_bak = g_strconcat(dest, ".bak", NULL);
2532 if (rename_force(dest, dest_bak) < 0) {
2533 FILE_OP_ERROR(dest, "rename");
2540 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2541 FILE_OP_ERROR(dest, "fopen");
2544 if (rename_force(dest_bak, dest) < 0)
2545 FILE_OP_ERROR(dest_bak, "rename");
2551 if (change_file_mode_rw(dest_fp, dest) < 0) {
2552 FILE_OP_ERROR(dest, "chmod");
2553 g_warning("can't change file mode\n");
2556 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2557 if (n_read < sizeof(buf) && ferror(src_fp))
2559 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2560 g_warning("writing to %s failed.\n", dest);
2565 if (rename_force(dest_bak, dest) < 0)
2566 FILE_OP_ERROR(dest_bak, "rename");
2573 if (ferror(src_fp)) {
2574 FILE_OP_ERROR(src, "fread");
2578 if (fclose(dest_fp) == EOF) {
2579 FILE_OP_ERROR(dest, "fclose");
2586 if (rename_force(dest_bak, dest) < 0)
2587 FILE_OP_ERROR(dest_bak, "rename");
2593 if (keep_backup == FALSE && dest_bak)
2594 claws_unlink(dest_bak);
2601 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2603 if (overwrite == FALSE && is_file_exist(dest)) {
2604 g_warning("move_file(): file %s already exists.", dest);
2608 if (rename_force(src, dest) == 0) return 0;
2610 if (EXDEV != errno) {
2611 FILE_OP_ERROR(src, "rename");
2615 if (copy_file(src, dest, FALSE) < 0) return -1;
2622 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2625 gint bytes_left, to_read;
2628 if (fseek(fp, offset, SEEK_SET) < 0) {
2633 bytes_left = length;
2634 to_read = MIN(bytes_left, sizeof(buf));
2636 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2637 if (n_read < to_read && ferror(fp))
2639 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2642 bytes_left -= n_read;
2643 if (bytes_left == 0)
2645 to_read = MIN(bytes_left, sizeof(buf));
2656 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2659 gboolean err = FALSE;
2661 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2662 FILE_OP_ERROR(dest, "fopen");
2666 if (change_file_mode_rw(dest_fp, dest) < 0) {
2667 FILE_OP_ERROR(dest, "chmod");
2668 g_warning("can't change file mode\n");
2671 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2674 if (!err && fclose(dest_fp) == EOF) {
2675 FILE_OP_ERROR(dest, "fclose");
2680 g_warning("writing to %s failed.\n", dest);
2688 /* convert line endings into CRLF. If the last line doesn't end with
2689 * linebreak, add it.
2691 gchar *canonicalize_str(const gchar *str)
2697 for (p = str; *p != '\0'; ++p) {
2704 if (p == str || *(p - 1) != '\n')
2707 out = outp = g_malloc(new_len + 1);
2708 for (p = str; *p != '\0'; ++p) {
2715 if (p == str || *(p - 1) != '\n') {
2724 gint canonicalize_file(const gchar *src, const gchar *dest)
2726 FILE *src_fp, *dest_fp;
2727 gchar buf[BUFFSIZE];
2729 gboolean err = FALSE;
2730 gboolean last_linebreak = FALSE;
2732 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2733 FILE_OP_ERROR(src, "fopen");
2737 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2738 FILE_OP_ERROR(dest, "fopen");
2743 if (change_file_mode_rw(dest_fp, dest) < 0) {
2744 FILE_OP_ERROR(dest, "chmod");
2745 g_warning("can't change file mode\n");
2748 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2752 if (len == 0) break;
2753 last_linebreak = FALSE;
2755 if (buf[len - 1] != '\n') {
2756 last_linebreak = TRUE;
2757 r = fputs(buf, dest_fp);
2758 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2759 r = fputs(buf, dest_fp);
2762 r = fwrite(buf, 1, len - 1, dest_fp);
2767 r = fputs("\r\n", dest_fp);
2771 g_warning("writing to %s failed.\n", dest);
2779 if (last_linebreak == TRUE) {
2780 if (fputs("\r\n", dest_fp) == EOF)
2784 if (ferror(src_fp)) {
2785 FILE_OP_ERROR(src, "fgets");
2789 if (fclose(dest_fp) == EOF) {
2790 FILE_OP_ERROR(dest, "fclose");
2802 gint canonicalize_file_replace(const gchar *file)
2806 tmp_file = get_tmp_file();
2808 if (canonicalize_file(file, tmp_file) < 0) {
2813 if (move_file(tmp_file, file, TRUE) < 0) {
2814 g_warning("can't replace %s .\n", file);
2815 claws_unlink(tmp_file);
2824 gchar *normalize_newlines(const gchar *str)
2826 const gchar *p = str;
2829 out = outp = g_malloc(strlen(str) + 1);
2830 for (p = str; *p != '\0'; ++p) {
2832 if (*(p + 1) != '\n')
2843 gchar *get_outgoing_rfc2822_str(FILE *fp)
2845 gchar buf[BUFFSIZE];
2849 str = g_string_new(NULL);
2851 /* output header part */
2852 while (fgets(buf, sizeof(buf), fp) != NULL) {
2854 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2861 else if (next != ' ' && next != '\t') {
2865 if (fgets(buf, sizeof(buf), fp) == NULL)
2869 g_string_append(str, buf);
2870 g_string_append(str, "\r\n");
2876 /* output body part */
2877 while (fgets(buf, sizeof(buf), fp) != NULL) {
2880 g_string_append_c(str, '.');
2881 g_string_append(str, buf);
2882 g_string_append(str, "\r\n");
2886 g_string_free(str, FALSE);
2892 * Create a new boundary in a way that it is very unlikely that this
2893 * will occur in the following text. It would be easy to ensure
2894 * uniqueness if everything is either quoted-printable or base64
2895 * encoded (note that conversion is allowed), but because MIME bodies
2896 * may be nested, it may happen that the same boundary has already
2899 * boundary := 0*69<bchars> bcharsnospace
2900 * bchars := bcharsnospace / " "
2901 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2902 * "+" / "_" / "," / "-" / "." /
2903 * "/" / ":" / "=" / "?"
2905 * some special characters removed because of buggy MTAs
2908 gchar *generate_mime_boundary(const gchar *prefix)
2910 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2911 "abcdefghijklmnopqrstuvwxyz"
2916 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2917 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2920 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2924 gint change_file_mode_rw(FILE *fp, const gchar *file)
2927 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2929 return g_chmod(file, S_IRUSR|S_IWUSR);
2933 FILE *my_tmpfile(void)
2935 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2936 const gchar suffix[] = ".XXXXXX";
2937 const gchar *tmpdir;
2939 const gchar *progname;
2948 tmpdir = get_tmp_dir();
2949 tmplen = strlen(tmpdir);
2950 progname = g_get_prgname();
2951 if (progname == NULL)
2952 progname = "claws-mail";
2953 proglen = strlen(progname);
2954 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2957 memcpy(fname, tmpdir, tmplen);
2958 fname[tmplen] = G_DIR_SEPARATOR;
2959 memcpy(fname + tmplen + 1, progname, proglen);
2960 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2962 fd = mkstemp(fname);
2967 claws_unlink(fname);
2969 /* verify that we can write in the file after unlinking */
2970 if (write(fd, buf, 1) < 0) {
2977 fp = fdopen(fd, "w+b");
2985 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2990 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2994 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2995 dir, G_DIR_SEPARATOR);
2996 fd = mkstemp_name(template, filename);
2999 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3000 fd = mkstemp(*filename);
3002 return fdopen(fd, "w+");
3005 FILE *str_open_as_stream(const gchar *str)
3010 g_return_val_if_fail(str != NULL, NULL);
3014 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3019 if (len == 0) return fp;
3021 if (fwrite(str, 1, len, fp) != len) {
3022 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3031 gint str_write_to_file(const gchar *str, const gchar *file)
3036 g_return_val_if_fail(str != NULL, -1);
3037 g_return_val_if_fail(file != NULL, -1);
3039 if ((fp = g_fopen(file, "wb")) == NULL) {
3040 FILE_OP_ERROR(file, "fopen");
3050 if (fwrite(str, 1, len, fp) != len) {
3051 FILE_OP_ERROR(file, "fwrite");
3057 if (fclose(fp) == EOF) {
3058 FILE_OP_ERROR(file, "fclose");
3066 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3073 g_return_val_if_fail(fp != NULL, NULL);
3075 array = g_byte_array_new();
3077 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3078 if (n_read < sizeof(buf) && ferror(fp))
3080 g_byte_array_append(array, buf, n_read);
3084 FILE_OP_ERROR("file stream", "fread");
3085 g_byte_array_free(array, TRUE);
3090 g_byte_array_append(array, buf, 1);
3091 str = (gchar *)array->data;
3092 g_byte_array_free(array, FALSE);
3094 if (recode && !g_utf8_validate(str, -1, NULL)) {
3095 const gchar *src_codeset, *dest_codeset;
3097 src_codeset = conv_get_locale_charset_str();
3098 dest_codeset = CS_UTF_8;
3099 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3107 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3114 struct timeval timeout = {1, 0};
3119 g_return_val_if_fail(file != NULL, NULL);
3121 if (g_stat(file, &s) != 0) {
3122 FILE_OP_ERROR(file, "stat");
3125 if (S_ISDIR(s.st_mode)) {
3126 g_warning("%s: is a directory\n", file);
3131 fp = fopen (file, "rb");
3133 FILE_OP_ERROR(file, "open");
3137 /* test whether the file is readable without blocking */
3138 fd = open(file, O_RDONLY | O_NONBLOCK);
3140 FILE_OP_ERROR(file, "open");
3147 /* allow for one second */
3148 err = select(fd+1, &fds, NULL, NULL, &timeout);
3149 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3151 FILE_OP_ERROR(file, "select");
3153 g_warning("%s: doesn't seem readable\n", file);
3159 /* Now clear O_NONBLOCK */
3160 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3161 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3165 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3166 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3171 /* get the FILE pointer */
3172 fp = fdopen(fd, "rb");
3175 FILE_OP_ERROR(file, "fdopen");
3176 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3181 str = file_read_stream_to_str_full(fp, recode);
3188 gchar *file_read_to_str(const gchar *file)
3190 return file_read_to_str_full(file, TRUE);
3192 gchar *file_read_stream_to_str(FILE *fp)
3194 return file_read_stream_to_str_full(fp, TRUE);
3197 gchar *file_read_to_str_no_recode(const gchar *file)
3199 return file_read_to_str_full(file, FALSE);
3201 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3203 return file_read_stream_to_str_full(fp, FALSE);
3206 char *fgets_crlf(char *buf, int size, FILE *stream)
3208 gboolean is_cr = FALSE;
3209 gboolean last_was_cr = FALSE;
3214 while (--size > 0 && (c = getc(stream)) != EOF)
3217 is_cr = (c == '\r');
3227 last_was_cr = is_cr;
3229 if (c == EOF && cs == buf)
3237 static gint execute_async(gchar *const argv[])
3239 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3241 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3242 NULL, NULL, NULL, FALSE) == FALSE) {
3243 g_warning("Couldn't execute command: %s\n", argv[0]);
3250 static gint execute_sync(gchar *const argv[])
3254 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3256 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3257 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3258 g_warning("Couldn't execute command: %s\n", argv[0]);
3263 if (WIFEXITED(status))
3264 return WEXITSTATUS(status);
3272 gint execute_command_line(const gchar *cmdline, gboolean async)
3277 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3279 argv = strsplit_with_quote(cmdline, " ", 0);
3282 ret = execute_async(argv);
3284 ret = execute_sync(argv);
3291 gchar *get_command_output(const gchar *cmdline)
3293 gchar *child_stdout;
3296 g_return_val_if_fail(cmdline != NULL, NULL);
3298 debug_print("get_command_output(): executing: %s\n", cmdline);
3300 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3302 g_warning("Couldn't execute command: %s\n", cmdline);
3306 return child_stdout;
3309 static gint is_unchanged_uri_char(char c)
3320 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3326 for(i = 0; i < strlen(uri) ; i++) {
3327 if (is_unchanged_uri_char(uri[i])) {
3328 if (k + 2 >= bufsize)
3330 encoded_uri[k++] = uri[i];
3333 char * hexa = "0123456789ABCDEF";
3335 if (k + 4 >= bufsize)
3337 encoded_uri[k++] = '%';
3338 encoded_uri[k++] = hexa[uri[i] / 16];
3339 encoded_uri[k++] = hexa[uri[i] % 16];
3345 gint open_uri(const gchar *uri, const gchar *cmdline)
3349 gchar buf[BUFFSIZE];
3351 gchar encoded_uri[BUFFSIZE];
3352 g_return_val_if_fail(uri != NULL, -1);
3354 /* an option to choose whether to use encode_uri or not ? */
3355 encode_uri(encoded_uri, BUFFSIZE, uri);
3358 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3359 !strchr(p + 2, '%'))
3360 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3363 g_warning("Open URI command-line is invalid "
3364 "(there must be only one '%%s'): %s",
3366 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3369 execute_command_line(buf, TRUE);
3371 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3374 extern osso_context_t *get_osso_context(void);
3375 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3376 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3377 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3382 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3384 gchar buf[BUFFSIZE];
3387 g_return_val_if_fail(filepath != NULL, -1);
3390 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3391 !strchr(p + 2, '%'))
3392 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3395 g_warning("Open Text Editor command-line is invalid "
3396 "(there must be only one '%%s'): %s",
3398 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3401 execute_command_line(buf, TRUE);
3406 time_t remote_tzoffset_sec(const gchar *zone)
3408 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3414 time_t remoteoffset;
3416 strncpy(zone3, zone, 3);
3420 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3421 (c == '+' || c == '-')) {
3422 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3424 remoteoffset = -remoteoffset;
3425 } else if (!strncmp(zone, "UT" , 2) ||
3426 !strncmp(zone, "GMT", 2)) {
3428 } else if (strlen(zone3) == 3) {
3429 for (p = ustzstr; *p != '\0'; p += 3) {
3430 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3431 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3432 remoteoffset = iustz * 3600;
3438 } else if (strlen(zone3) == 1) {
3440 case 'Z': remoteoffset = 0; break;
3441 case 'A': remoteoffset = -1; break;
3442 case 'B': remoteoffset = -2; break;
3443 case 'C': remoteoffset = -3; break;
3444 case 'D': remoteoffset = -4; break;
3445 case 'E': remoteoffset = -5; break;
3446 case 'F': remoteoffset = -6; break;
3447 case 'G': remoteoffset = -7; break;
3448 case 'H': remoteoffset = -8; break;
3449 case 'I': remoteoffset = -9; break;
3450 case 'K': remoteoffset = -10; break; /* J is not used */
3451 case 'L': remoteoffset = -11; break;
3452 case 'M': remoteoffset = -12; break;
3453 case 'N': remoteoffset = 1; break;
3454 case 'O': remoteoffset = 2; break;
3455 case 'P': remoteoffset = 3; break;
3456 case 'Q': remoteoffset = 4; break;
3457 case 'R': remoteoffset = 5; break;
3458 case 'S': remoteoffset = 6; break;
3459 case 'T': remoteoffset = 7; break;
3460 case 'U': remoteoffset = 8; break;
3461 case 'V': remoteoffset = 9; break;
3462 case 'W': remoteoffset = 10; break;
3463 case 'X': remoteoffset = 11; break;
3464 case 'Y': remoteoffset = 12; break;
3465 default: remoteoffset = 0; break;
3467 remoteoffset = remoteoffset * 3600;
3471 return remoteoffset;
3474 time_t tzoffset_sec(time_t *now)
3479 struct tm buf1, buf2;
3482 if (now && *now < 0)
3485 gmt = *gmtime_r(now, &buf1);
3486 lt = localtime_r(now, &buf2);
3488 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3490 if (lt->tm_year < gmt.tm_year)
3492 else if (lt->tm_year > gmt.tm_year)
3494 else if (lt->tm_yday < gmt.tm_yday)
3496 else if (lt->tm_yday > gmt.tm_yday)
3499 if (off >= 24 * 60) /* should be impossible */
3500 off = 23 * 60 + 59; /* if not, insert silly value */
3501 if (off <= -24 * 60)
3502 off = -(23 * 60 + 59);
3507 /* calculate timezone offset */
3508 gchar *tzoffset(time_t *now)
3510 static gchar offset_string[6];
3515 struct tm buf1, buf2;
3518 if (now && *now < 0)
3521 gmt = *gmtime_r(now, &buf1);
3522 lt = localtime_r(now, &buf2);
3524 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3526 if (lt->tm_year < gmt.tm_year)
3528 else if (lt->tm_year > gmt.tm_year)
3530 else if (lt->tm_yday < gmt.tm_yday)
3532 else if (lt->tm_yday > gmt.tm_yday)
3540 if (off >= 24 * 60) /* should be impossible */
3541 off = 23 * 60 + 59; /* if not, insert silly value */
3543 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3545 return offset_string;
3548 void get_rfc822_date(gchar *buf, gint len)
3552 gchar day[4], mon[4];
3553 gint dd, hh, mm, ss, yyyy;
3556 gchar buf2[BUFFSIZE];
3560 lt = localtime_r(&t, &buf1);
3562 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3563 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3565 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3566 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3569 void debug_set_mode(gboolean mode)
3574 gboolean debug_get_mode(void)
3579 void debug_print_real(const gchar *format, ...)
3582 gchar buf[BUFFSIZE];
3584 if (!debug_mode) return;
3586 va_start(args, format);
3587 g_vsnprintf(buf, sizeof(buf), format, args);
3594 const char * debug_srcname(const char *file)
3596 const char *s = strrchr (file, '/');
3601 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3603 if (subject == NULL)
3606 subject += subject_get_prefix_length(subject);
3608 return g_hash_table_lookup(subject_table, subject);
3611 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3614 if (subject == NULL || *subject == 0)
3616 subject += subject_get_prefix_length(subject);
3617 g_hash_table_insert(subject_table, subject, data);
3620 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3622 if (subject == NULL)
3625 subject += subject_get_prefix_length(subject);
3626 g_hash_table_remove(subject_table, subject);
3630 *\brief Check if a string is prefixed with known (combinations)
3631 * of prefixes. The function assumes that each prefix
3632 * is terminated by zero or exactly _one_ space.
3634 *\param str String to check for a prefixes
3636 *\return int Number of chars in the prefix that should be skipped
3637 * for a "clean" subject line. If no prefix was found, 0
3642 static regex_t u_regex;
3643 static gboolean u_init_;
3646 void utils_free_regex(void)
3656 int subject_get_prefix_length(const gchar *subject)
3659 /*!< Array with allowable reply prefixes regexps. */
3660 static const gchar * const prefixes[] = {
3661 "Re\\:", /* "Re:" */
3662 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3663 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3664 "Aw\\:", /* "Aw:" (German) */
3665 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3666 "Res\\:", /* "Res:" (Brazilian Outlook) */
3667 "Fw\\:", /* "Fw:" Forward */
3668 "Fwd\\:", /* "Fwd:" Forward */
3669 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3670 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3671 "Rif\\:", /* "Rif:" (Italian Outlook) */
3672 "Sv\\:", /* "Sv" (Norwegian) */
3673 "Vs\\:", /* "Vs" (Norwegian) */
3674 "Ad\\:", /* "Ad" (Norwegian) */
3675 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3678 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3682 if (!subject) return 0;
3683 if (!*subject) return 0;
3686 GString *s = g_string_new("");
3688 for (n = 0; n < PREFIXES; n++)
3689 /* Terminate each prefix regexpression by a
3690 * "\ ?" (zero or ONE space), and OR them */
3691 g_string_append_printf(s, "(%s\\ ?)%s",
3696 g_string_prepend(s, "(");
3697 g_string_append(s, ")+"); /* match at least once */
3698 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3701 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3702 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3703 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3704 debug_print("Error compiling regexp %s\n", s->str);
3705 g_string_free(s, TRUE);
3709 g_string_free(s, TRUE);
3713 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3718 /*!< Array with allowable reply prefixes regexps. */
3719 static const gchar * const prefixes[] = {
3721 "antw:", /* "Antw:" (Dutch / German Outlook) */
3722 "aw:", /* "Aw:" (German) */
3723 "antwort:", /* "Antwort:" (German Lotus Notes) */
3724 "res:", /* "Res:" (Brazilian Outlook) */
3725 "fw:", /* "Fw:" Forward */
3726 "fwd:", /* "Fwd:" Forward */
3727 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3728 "odp:", /* "Odp:" Re (Polish Outlook) */
3729 "rif:", /* "Rif:" (Italian Outlook) */
3730 "sv:", /* "Sv" (Norwegian) */
3731 "vs:", /* "Vs" (Norwegian) */
3732 "ad:", /* "Ad" (Norwegian) */
3735 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3738 if (!subject) return 0;
3739 if (!*subject) return 0;
3741 for (n = 0; n < PREFIXES; n++) {
3742 int len = strlen(prefixes[n]);
3743 if (!strncasecmp(subject, prefixes[n], len))
3749 static guint g_stricase_hash(gconstpointer gptr)
3751 guint hash_result = 0;
3754 for (str = gptr; str && *str; str++) {
3755 hash_result += toupper(*str);
3761 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3763 const char *str1 = gptr1;
3764 const char *str2 = gptr2;
3766 return !strcasecmp(str1, str2);
3769 gint g_int_compare(gconstpointer a, gconstpointer b)
3771 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3774 gchar *generate_msgid(gchar *buf, gint len)
3784 lt = localtime_r(&t, &buft);
3786 if (strcmp(buf, "") == 0) {
3787 addr = g_strconcat("@", get_domain_name(), NULL);
3790 addr = g_strconcat("@", buf, NULL);
3793 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3794 lt->tm_year + 1900, lt->tm_mon + 1,
3795 lt->tm_mday, lt->tm_hour,
3796 lt->tm_min, lt->tm_sec,
3797 (guint) rand(), addr);
3804 quote_cmd_argument()
3806 return a quoted string safely usable in argument of a command.
3808 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3811 gint quote_cmd_argument(gchar * result, guint size,
3821 for(p = path ; * p != '\0' ; p ++) {
3823 if (isalnum((guchar)*p) || (* p == '/')) {
3824 if (remaining > 0) {
3830 result[size - 1] = '\0';
3835 if (remaining >= 2) {
3843 result[size - 1] = '\0';
3848 if (remaining > 0) {
3852 result[size - 1] = '\0';
3866 static void g_node_map_recursive(GNode *node, gpointer data)
3868 GNodeMapData *mapdata = (GNodeMapData *) data;
3870 GNodeMapData newmapdata;
3873 newdata = mapdata->func(node->data, mapdata->data);
3874 if (newdata != NULL) {
3875 newnode = g_node_new(newdata);
3876 g_node_append(mapdata->parent, newnode);
3878 newmapdata.parent = newnode;
3879 newmapdata.func = mapdata->func;
3880 newmapdata.data = mapdata->data;
3882 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3886 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3889 GNodeMapData mapdata;
3891 g_return_val_if_fail(node != NULL, NULL);
3892 g_return_val_if_fail(func != NULL, NULL);
3894 root = g_node_new(func(node->data, data));
3896 mapdata.parent = root;
3897 mapdata.func = func;
3898 mapdata.data = data;
3900 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3905 #define HEX_TO_INT(val, hex) \
3909 if ('0' <= c && c <= '9') { \
3911 } else if ('a' <= c && c <= 'f') { \
3912 val = c - 'a' + 10; \
3913 } else if ('A' <= c && c <= 'F') { \
3914 val = c - 'A' + 10; \
3920 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3927 if (hi == -1 || lo == -1)
3930 *out = (hi << 4) + lo;
3934 #define INT_TO_HEX(hex, val) \
3937 hex = '0' + (val); \
3939 hex = 'A' + (val) - 10; \
3942 void get_hex_str(gchar *out, guchar ch)
3946 INT_TO_HEX(hex, ch >> 4);
3948 INT_TO_HEX(hex, ch & 0x0f);
3954 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3956 #define G_PRINT_REF g_print
3960 *\brief Register ref counted pointer. It is based on GBoxed, so should
3961 * work with anything that uses the GType system. The semantics
3962 * are similar to a C++ auto pointer, with the exception that
3963 * C doesn't have automatic closure (calling destructors) when
3964 * exiting a block scope.
3965 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3966 * function directly.
3968 *\return GType A GType type.
3970 GType g_auto_pointer_register(void)
3972 static GType auto_pointer_type;
3973 if (!auto_pointer_type)
3975 g_boxed_type_register_static
3976 ("G_TYPE_AUTO_POINTER",
3977 (GBoxedCopyFunc) g_auto_pointer_copy,
3978 (GBoxedFreeFunc) g_auto_pointer_free);
3979 return auto_pointer_type;
3983 *\brief Structure with g_new() allocated pointer guarded by the
3986 typedef struct AutoPointerRef {
3987 void (*free) (gpointer);
3993 *\brief The auto pointer opaque structure that references the
3994 * pointer guard block.
3996 typedef struct AutoPointer {
3997 AutoPointerRef *ref;
3998 gpointer ptr; /*!< access to protected pointer */
4002 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4006 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4007 * ... when assigning, copying and freeing storage elements
4009 * gtk_list_store_new(N_S_COLUMNS,
4010 * G_TYPE_AUTO_POINTER,
4014 * Template *precious_data = g_new0(Template, 1);
4015 * g_pointer protect = g_auto_pointer_new(precious_data);
4017 * gtk_list_store_set(container, &iter,
4021 * ... the gtk_list_store has copied the pointer and
4022 * ... incremented its reference count, we should free
4023 * ... the auto pointer (in C++ a destructor would do
4024 * ... this for us when leaving block scope)
4026 * g_auto_pointer_free(protect);
4028 * ... gtk_list_store_set() now manages the data. When
4029 * ... *explicitly* requesting a pointer from the list
4030 * ... store, don't forget you get a copy that should be
4031 * ... freed with g_auto_pointer_free() eventually.
4035 *\param pointer Pointer to be guarded.
4037 *\return GAuto * Pointer that should be used in containers with
4040 GAuto *g_auto_pointer_new(gpointer p)
4042 AutoPointerRef *ref;
4048 ref = g_new0(AutoPointerRef, 1);
4049 ptr = g_new0(AutoPointer, 1);
4059 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4065 *\brief Allocate an autopointer using the passed \a free function to
4066 * free the guarded pointer
4068 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4075 aptr = g_auto_pointer_new(p);
4076 aptr->ref->free = free_;
4080 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4082 if (auto_ptr == NULL)
4084 return ((AutoPointer *) auto_ptr)->ptr;
4088 *\brief Copies an auto pointer by. It's mostly not necessary
4089 * to call this function directly, unless you copy/assign
4090 * the guarded pointer.
4092 *\param auto_ptr Auto pointer returned by previous call to
4093 * g_auto_pointer_new_XXX()
4095 *\return gpointer An auto pointer
4097 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4100 AutoPointerRef *ref;
4103 if (auto_ptr == NULL)
4108 newp = g_new0(AutoPointer, 1);
4111 newp->ptr = ref->pointer;
4115 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4121 *\brief Free an auto pointer
4123 void g_auto_pointer_free(GAuto *auto_ptr)
4126 AutoPointerRef *ref;
4128 if (auto_ptr == NULL)
4134 if (--(ref->cnt) == 0) {
4136 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4138 ref->free(ref->pointer);
4143 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4148 void replace_returns(gchar *str)
4153 while (strstr(str, "\n")) {
4154 *strstr(str, "\n") = ' ';
4156 while (strstr(str, "\r")) {
4157 *strstr(str, "\r") = ' ';
4161 /* get_uri_part() - retrieves a URI starting from scanpos.
4162 Returns TRUE if succesful */
4163 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4164 const gchar **bp, const gchar **ep, gboolean hdr)
4167 gint parenthese_cnt = 0;
4169 g_return_val_if_fail(start != NULL, FALSE);
4170 g_return_val_if_fail(scanpos != NULL, FALSE);
4171 g_return_val_if_fail(bp != NULL, FALSE);
4172 g_return_val_if_fail(ep != NULL, FALSE);
4176 /* find end point of URI */
4177 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4178 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4179 !IS_ASCII(*(const guchar *)ep_) ||
4180 strchr("[]{}<>\"", *ep_)) {
4182 } else if (strchr("(", *ep_)) {
4184 } else if (strchr(")", *ep_)) {
4185 if (parenthese_cnt > 0)
4192 /* no punctuation at end of string */
4194 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4195 * should pass some URI type to this function and decide on that whether
4196 * to perform punctuation stripping */
4198 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4200 for (; ep_ - 1 > scanpos + 1 &&
4201 IS_REAL_PUNCT(*(ep_ - 1));
4205 #undef IS_REAL_PUNCT
4212 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4214 while (bp && *bp && g_ascii_isspace(*bp))
4216 return g_strndup(bp, ep - bp);
4219 /* valid mail address characters */
4220 #define IS_RFC822_CHAR(ch) \
4224 !g_ascii_isspace(ch) && \
4225 !strchr("(),;<>\"", (ch)))
4227 /* alphabet and number within 7bit ASCII */
4228 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4229 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4231 static GHashTable *create_domain_tab(void)
4233 static const gchar *toplvl_domains [] = {
4235 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4236 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4237 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4238 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4239 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4240 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4241 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4242 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4243 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4244 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4245 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4246 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4247 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4248 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4249 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4250 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4251 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4252 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4253 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4254 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4255 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4256 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4257 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4258 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4259 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4260 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4263 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4265 g_return_val_if_fail(htab, NULL);
4266 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4267 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4271 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4273 const gint MAX_LVL_DOM_NAME_LEN = 6;
4274 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4275 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4278 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4281 for (p = buf; p < m && first < last; *p++ = *first++)
4285 return g_hash_table_lookup(tab, buf) != NULL;
4288 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4289 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4290 const gchar **bp, const gchar **ep, gboolean hdr)
4292 /* more complex than the uri part because we need to scan back and forward starting from
4293 * the scan position. */
4294 gboolean result = FALSE;
4295 const gchar *bp_ = NULL;
4296 const gchar *ep_ = NULL;
4297 static GHashTable *dom_tab;
4298 const gchar *last_dot = NULL;
4299 const gchar *prelast_dot = NULL;
4300 const gchar *last_tld_char = NULL;
4302 /* the informative part of the email address (describing the name
4303 * of the email address owner) may contain quoted parts. the
4304 * closure stack stores the last encountered quotes. */
4305 gchar closure_stack[128];
4306 gchar *ptr = closure_stack;
4308 g_return_val_if_fail(start != NULL, FALSE);
4309 g_return_val_if_fail(scanpos != NULL, FALSE);
4310 g_return_val_if_fail(bp != NULL, FALSE);
4311 g_return_val_if_fail(ep != NULL, FALSE);
4314 const gchar *start_quote = NULL;
4315 const gchar *end_quote = NULL;
4317 /* go to the real start */
4318 if (start[0] == ',')
4320 if (start[0] == ';')
4322 while (start[0] == '\n' || start[0] == '\r')
4324 while (start[0] == ' ' || start[0] == '\t')
4329 /* check if there are quotes (to skip , in them) */
4330 if (*start == '"') {
4331 start_quote = start;
4333 end_quote = strstr(start, "\"");
4339 /* skip anything between quotes */
4340 if (start_quote && end_quote) {
4345 /* find end (either , or ; or end of line) */
4346 if (strstr(start, ",") && strstr(start, ";"))
4347 *ep = strstr(start,",") < strstr(start, ";")
4348 ? strstr(start, ",") : strstr(start, ";");
4349 else if (strstr(start, ","))
4350 *ep = strstr(start, ",");
4351 else if (strstr(start, ";"))
4352 *ep = strstr(start, ";");
4354 *ep = start+strlen(start);
4356 /* go back to real start */
4357 if (start_quote && end_quote) {
4358 start = start_quote;
4361 /* check there's still an @ in that, or search
4362 * further if possible */
4363 if (strstr(start, "@") && strstr(start, "@") < *ep)
4365 else if (*ep < start+strlen(start)) {
4368 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4376 dom_tab = create_domain_tab();
4377 g_return_val_if_fail(dom_tab, FALSE);
4379 /* scan start of address */
4380 for (bp_ = scanpos - 1;
4381 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4384 /* TODO: should start with an alnum? */
4386 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4389 if (bp_ != scanpos) {
4390 /* scan end of address */
4391 for (ep_ = scanpos + 1;
4392 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4394 prelast_dot = last_dot;
4396 if (*(last_dot + 1) == '.') {
4397 if (prelast_dot == NULL)
4399 last_dot = prelast_dot;
4404 /* TODO: really should terminate with an alnum? */
4405 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4410 if (last_dot == NULL)
4412 if (last_dot >= ep_)
4413 last_dot = prelast_dot;
4414 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4418 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4419 if (*last_tld_char == '?')
4422 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4429 if (!result) return FALSE;
4431 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4432 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4433 && IS_RFC822_CHAR(*(ep_ + 3))) {
4434 /* this informative part with an @ in it is
4435 * followed by the email address */
4438 /* go to matching '>' (or next non-rfc822 char, like \n) */
4439 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4442 /* include the bracket */
4443 if (*ep_ == '>') ep_++;
4445 /* include the leading quote */
4453 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4454 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4457 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4458 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4461 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4462 #define IN_STACK() (ptr > closure_stack)
4463 /* has underrun check */
4464 #define POP_STACK() if(IN_STACK()) --ptr
4465 /* has overrun check */
4466 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4467 /* has underrun check */
4468 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4472 /* scan for the informative part. */
4473 for (bp_ -= 2; bp_ >= start; bp_--) {
4474 /* if closure on the stack keep scanning */
4475 if (PEEK_STACK() == *bp_) {
4479 if (*bp_ == '\'' || *bp_ == '"') {
4484 /* if nothing in the closure stack, do the special conditions
4485 * the following if..else expression simply checks whether
4486 * a token is acceptable. if not acceptable, the clause
4487 * should terminate the loop with a 'break' */
4488 if (!PEEK_STACK()) {
4490 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4491 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4492 /* hyphens are allowed, but only in
4494 } else if (strchr(" \"'", *bp_)) {
4495 /* but anything not being a punctiation
4498 break; /* anything else is rejected */
4505 /* scan forward (should start with an alnum) */
4506 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4522 #undef IS_ASCII_ALNUM
4523 #undef IS_RFC822_CHAR
4525 gchar *make_email_string(const gchar *bp, const gchar *ep)
4527 /* returns a mailto: URI; mailto: is also used to detect the
4528 * uri type later on in the button_pressed signal handler */
4532 tmp = g_strndup(bp, ep - bp);
4533 result = g_strconcat("mailto:", tmp, NULL);
4539 gchar *make_http_string(const gchar *bp, const gchar *ep)
4541 /* returns an http: URI; */
4545 while (bp && *bp && g_ascii_isspace(*bp))
4547 tmp = g_strndup(bp, ep - bp);
4548 result = g_strconcat("http://", tmp, NULL);
4554 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4556 FILE *fp = fopen(path, "rb");
4557 gchar buf[BUFFSIZE];
4558 gchar *result = NULL;
4561 while (fgets(buf, sizeof (buf), fp) != NULL) {
4562 gchar **parts = g_strsplit(buf, ";", 3);
4563 gchar *trimmed = parts[0];
4564 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4566 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4567 trimmed[strlen(trimmed)-1] = '\0';
4569 if (!strcmp(trimmed, type)) {
4570 gboolean needsterminal = FALSE;
4571 if (parts[2] && strstr(parts[2], "needsterminal")) {
4572 needsterminal = TRUE;
4574 if (parts[2] && strstr(parts[2], "test=")) {
4575 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4576 gchar *testcmd = orig_testcmd;
4577 if (strstr(testcmd,";"))
4578 *(strstr(testcmd,";")) = '\0';
4579 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4581 while (testcmd[strlen(testcmd)-1] == '\n')
4582 testcmd[strlen(testcmd)-1] = '\0';
4583 while (testcmd[strlen(testcmd)-1] == '\r')
4584 testcmd[strlen(testcmd)-1] = '\0';
4585 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4586 testcmd[strlen(testcmd)-1] = '\0';
4588 if (strstr(testcmd, "%s")) {
4589 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4590 gint res = system(tmp);
4592 g_free(orig_testcmd);
4599 gint res = system(testcmd);
4600 g_free(orig_testcmd);
4610 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4612 while (trimmed[strlen(trimmed)-1] == '\n')
4613 trimmed[strlen(trimmed)-1] = '\0';
4614 while (trimmed[strlen(trimmed)-1] == '\r')
4615 trimmed[strlen(trimmed)-1] = '\0';
4616 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4617 trimmed[strlen(trimmed)-1] = '\0';
4618 result = g_strdup(trimmed);
4621 /* if there are no single quotes around %s, add them.
4622 * '.*%s.*' is ok, as in display 'png:%s'
4624 if (strstr(result, "%s")
4625 && !(strstr(result, "'") < strstr(result,"%s") &&
4626 strstr(strstr(result,"%s"), "'"))) {
4627 gchar *start = g_strdup(result);
4628 gchar *end = g_strdup(strstr(result, "%s")+2);
4630 *strstr(start, "%s") = '\0';
4631 tmp = g_strconcat(start,"'%s'",end, NULL);
4637 if (needsterminal) {
4638 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4649 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4651 gchar *result = NULL;
4655 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4656 result = mailcap_get_command_in_file(path, type, file_to_open);
4660 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4664 void mailcap_update_default(const gchar *type, const gchar *command)
4666 gchar *path = NULL, *outpath = NULL;
4667 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4668 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4669 FILE *fp = fopen(path, "rb");
4670 FILE *outfp = fopen(outpath, "wb");
4671 gchar buf[BUFFSIZE];
4672 gboolean err = FALSE;
4680 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4681 gchar **parts = g_strsplit(buf, ";", 3);
4682 gchar *trimmed = parts[0];
4683 while (trimmed[0] == ' ')
4685 while (trimmed[strlen(trimmed)-1] == ' ')
4686 trimmed[strlen(trimmed)-1] = '\0';
4688 if (!strcmp(trimmed, type)) {
4693 if(fputs(buf, outfp) == EOF) {
4700 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4706 if (fclose(outfp) == EOF)
4710 g_rename(outpath, path);
4716 gint copy_dir(const gchar *src, const gchar *dst)
4721 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4722 g_warning("failed to open directory: %s\n", src);
4726 if (make_dir(dst) < 0)
4729 while ((name = g_dir_read_name(dir)) != NULL) {
4730 gchar *old_file, *new_file;
4731 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4732 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4733 debug_print("copying: %s -> %s\n", old_file, new_file);
4734 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4735 gint r = copy_file(old_file, new_file, TRUE);
4742 /* Windows has no symlinks. Or well, Vista seems to
4743 have something like this but the semantics might be
4744 different. Thus we don't use it under Windows. */
4745 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4748 gchar *target = g_file_read_link(old_file, &error);
4750 r = symlink(target, new_file);
4757 #endif /*G_OS_WIN32*/
4758 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4759 gint r = copy_dir(old_file, new_file);
4770 /* crude test to see if a file is an email. */
4771 gboolean file_is_email (const gchar *filename)
4777 if (filename == NULL)
4779 if ((fp = g_fopen(filename, "rb")) == NULL)
4781 while (i < 60 && score < 3
4782 && fgets(buffer, sizeof (buffer), fp) > 0) {
4783 if (!strncmp(buffer, "From:", strlen("From:")))
4785 if (!strncmp(buffer, "To:", strlen("To:")))
4787 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4792 return (score >= 3);
4795 gboolean sc_g_list_bigger(GList *list, gint max)
4799 while (cur && i <= max+1) {
4806 gboolean sc_g_slist_bigger(GSList *list, gint max)
4810 while (cur && i <= max+1) {
4817 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4818 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4819 NULL, NULL, NULL, NULL, NULL, NULL};
4820 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4821 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4822 NULL, NULL, NULL, NULL, NULL, NULL};
4824 gint daynames_len[] = {0,0,0,0,0,0,0};
4825 gint monthnames_len[] = {0,0,0,0,0,0,
4827 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4828 gint s_monthnames_len[] = {0,0,0,0,0,0,
4830 const gchar *s_am_up = NULL;
4831 const gchar *s_pm_up = NULL;
4832 const gchar *s_am_low = NULL;
4833 const gchar *s_pm_low = NULL;
4835 gint s_am_up_len = 0;
4836 gint s_pm_up_len = 0;
4837 gint s_am_low_len = 0;
4838 gint s_pm_low_len = 0;
4840 const gchar *def_loc_format = NULL;
4841 const gchar *date_loc_format = NULL;
4842 const gchar *time_loc_format = NULL;
4843 const gchar *time_am_pm = NULL;
4845 static gboolean time_names_init_done = FALSE;
4847 static void init_time_names(void)
4851 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4852 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4853 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4854 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4855 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4856 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4857 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4859 monthnames[0] = Q_("Complete month name for use by strftime|January");
4860 monthnames[1] = Q_("Complete month name for use by strftime|February");
4861 monthnames[2] = Q_("Complete month name for use by strftime|March");
4862 monthnames[3] = Q_("Complete month name for use by strftime|April");
4863 monthnames[4] = Q_("Complete month name for use by strftime|May");
4864 monthnames[5] = Q_("Complete month name for use by strftime|June");
4865 monthnames[6] = Q_("Complete month name for use by strftime|July");
4866 monthnames[7] = Q_("Complete month name for use by strftime|August");
4867 monthnames[8] = Q_("Complete month name for use by strftime|September");
4868 monthnames[9] = Q_("Complete month name for use by strftime|October");
4869 monthnames[10] = Q_("Complete month name for use by strftime|November");
4870 monthnames[11] = Q_("Complete month name for use by strftime|December");
4872 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4873 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4874 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4875 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4876 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4877 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4878 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4880 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4881 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4882 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4883 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4884 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4885 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4886 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4887 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4888 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4889 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4890 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4891 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4893 for (i = 0; i < 7; i++) {
4894 daynames_len[i] = strlen(daynames[i]);
4895 s_daynames_len[i] = strlen(s_daynames[i]);
4897 for (i = 0; i < 12; i++) {
4898 monthnames_len[i] = strlen(monthnames[i]);
4899 s_monthnames_len[i] = strlen(s_monthnames[i]);
4902 s_am_up = Q_("For use by strftime (morning)|AM");
4903 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4904 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4905 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4907 s_am_up_len = strlen(s_am_up);
4908 s_pm_up_len = strlen(s_pm_up);
4909 s_am_low_len = strlen(s_am_low);
4910 s_pm_low_len = strlen(s_pm_low);
4912 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4913 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4914 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4916 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4918 time_names_init_done = TRUE;
4921 #define CHECK_SIZE() { \
4922 total_done += len; \
4923 if (total_done >= buflen) { \
4924 buf[buflen-1] = '\0'; \
4929 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4931 gchar *curpos = buf;
4932 gint total_done = 0;
4933 gchar subbuf[64], subfmt[64];
4934 static time_t last_tzset = (time_t)0;
4936 if (!time_names_init_done)
4939 if (format == NULL || lt == NULL)
4942 if (last_tzset != time(NULL)) {
4944 last_tzset = time(NULL);
4947 if (*format == '%') {
4948 gint len = 0, tmp = 0;
4952 len = 1; CHECK_SIZE();
4956 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4957 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4960 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4961 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4965 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4966 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4969 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4970 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4973 fast_strftime(subbuf, 64, def_loc_format, lt);
4974 len = strlen(subbuf); CHECK_SIZE();
4975 strncpy2(curpos, subbuf, buflen - total_done);
4978 total_done += 2; CHECK_SIZE();
4979 tmp = (lt->tm_year + 1900)/100;
4980 *curpos++ = '0'+(tmp / 10);
4981 *curpos++ = '0'+(tmp % 10);
4984 total_done += 2; CHECK_SIZE();
4985 *curpos++ = '0'+(lt->tm_mday / 10);
4986 *curpos++ = '0'+(lt->tm_mday % 10);
4989 total_done += 8; CHECK_SIZE();
4990 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4991 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4993 *curpos++ = '0'+(lt->tm_mday / 10);
4994 *curpos++ = '0'+(lt->tm_mday % 10);
4996 tmp = lt->tm_year%100;
4997 *curpos++ = '0'+(tmp / 10);
4998 *curpos++ = '0'+(tmp % 10);
5001 len = 2; CHECK_SIZE();
5002 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5005 len = 10; CHECK_SIZE();
5006 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5007 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5010 total_done += 2; CHECK_SIZE();
5011 *curpos++ = '0'+(lt->tm_hour / 10);
5012 *curpos++ = '0'+(lt->tm_hour % 10);
5015 total_done += 2; CHECK_SIZE();
5021 *curpos++ = '0'+(tmp / 10);
5022 *curpos++ = '0'+(tmp % 10);
5025 len = 3; CHECK_SIZE();
5026 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5029 len = 2; CHECK_SIZE();
5030 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5033 len = 2; CHECK_SIZE();
5039 snprintf(curpos, buflen - total_done, "%2d", tmp);
5042 total_done += 2; CHECK_SIZE();
5043 tmp = lt->tm_mon + 1;
5044 *curpos++ = '0'+(tmp / 10);
5045 *curpos++ = '0'+(tmp % 10);
5048 total_done += 2; CHECK_SIZE();
5049 *curpos++ = '0'+(lt->tm_min / 10);
5050 *curpos++ = '0'+(lt->tm_min % 10);
5053 len = 1; CHECK_SIZE();
5057 if (lt->tm_hour >= 12) {
5058 len = s_pm_up_len; CHECK_SIZE();
5059 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5061 len = s_am_up_len; CHECK_SIZE();
5062 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5066 if (lt->tm_hour >= 12) {
5067 len = s_pm_low_len; CHECK_SIZE();
5068 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5070 len = s_am_low_len; CHECK_SIZE();
5071 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5075 fast_strftime(subbuf, 64, time_am_pm, lt);
5076 len = strlen(subbuf); CHECK_SIZE();
5077 strncpy2(curpos, subbuf, buflen - total_done);
5080 total_done += 5; CHECK_SIZE();
5081 *curpos++ = '0'+(lt->tm_hour / 10);
5082 *curpos++ = '0'+(lt->tm_hour % 10);
5084 *curpos++ = '0'+(lt->tm_min / 10);
5085 *curpos++ = '0'+(lt->tm_min % 10);
5088 snprintf(subbuf, 64, "%ld", mktime(lt));
5089 len = strlen(subbuf); CHECK_SIZE();
5090 strncpy2(curpos, subbuf, buflen - total_done);
5093 total_done += 2; CHECK_SIZE();
5094 *curpos++ = '0'+(lt->tm_sec / 10);
5095 *curpos++ = '0'+(lt->tm_sec % 10);
5098 len = 1; CHECK_SIZE();
5102 total_done += 8; CHECK_SIZE();
5103 *curpos++ = '0'+(lt->tm_hour / 10);
5104 *curpos++ = '0'+(lt->tm_hour % 10);
5106 *curpos++ = '0'+(lt->tm_min / 10);
5107 *curpos++ = '0'+(lt->tm_min % 10);
5109 *curpos++ = '0'+(lt->tm_sec / 10);
5110 *curpos++ = '0'+(lt->tm_sec % 10);
5113 len = 1; CHECK_SIZE();
5114 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5117 len = 1; CHECK_SIZE();
5118 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5121 fast_strftime(subbuf, 64, date_loc_format, lt);
5122 len = strlen(subbuf); CHECK_SIZE();
5123 strncpy2(curpos, subbuf, buflen - total_done);
5126 fast_strftime(subbuf, 64, time_loc_format, lt);
5127 len = strlen(subbuf); CHECK_SIZE();
5128 strncpy2(curpos, subbuf, buflen - total_done);
5131 total_done += 2; CHECK_SIZE();
5132 tmp = lt->tm_year%100;
5133 *curpos++ = '0'+(tmp / 10);
5134 *curpos++ = '0'+(tmp % 10);
5137 len = 4; CHECK_SIZE();
5138 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5148 /* let these complicated ones be done with the libc */
5149 snprintf(subfmt, 64, "%%%c", *format);
5150 strftime(subbuf, 64, subfmt, lt);
5151 len = strlen(subbuf); CHECK_SIZE();
5152 strncpy2(curpos, subbuf, buflen - total_done);
5156 /* let these complicated modifiers be done with the libc */
5157 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5158 strftime(subbuf, 64, subfmt, lt);
5159 len = strlen(subbuf); CHECK_SIZE();
5160 strncpy2(curpos, subbuf, buflen - total_done);
5164 if (format && *format)
5165 g_warning("format error (%c)", *format);
5172 int len = 1; CHECK_SIZE();
5173 *curpos++ = *format++;
5180 gboolean prefs_common_get_use_shred(void);
5184 #define WEXITSTATUS(x) (x)
5187 int claws_unlink(const gchar *filename)
5190 static int found_shred = -1;
5191 static const gchar *args[4];
5193 if (filename == NULL)
5196 if (prefs_common_get_use_shred()) {
5197 if (found_shred == -1) {
5199 args[0] = g_find_program_in_path("shred");
5200 debug_print("found shred: %s\n", args[0]);
5201 found_shred = (args[0] != NULL) ? 1:0;
5205 if (found_shred == 1) {
5206 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5207 if (s.st_nlink == 1) {
5210 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5211 NULL, NULL, NULL, NULL, &status, NULL);
5212 debug_print("%s %s exited with status %d\n",
5213 args[0], filename, WEXITSTATUS(status));
5214 if (truncate(filename, 0) < 0)
5215 g_warning("couln't truncate");
5220 return g_unlink(filename);