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, "SHGetFolderPathA");
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[MAX_PATH] = "";
1798 if (home_dir[0] == '\0') {
1799 if (w32_shgetfolderpath
1800 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1801 NULL, 0, home_dir) < 0)
1802 strcpy (home_dir, "C:\\Sylpheed");
1806 static const gchar *homeenv = NULL;
1811 if (!homeenv && g_getenv("HOME") != NULL)
1812 homeenv = g_strdup(g_getenv("HOME"));
1814 homeenv = g_get_home_dir();
1820 static gchar *claws_rc_dir = NULL;
1821 static gboolean rc_dir_alt = FALSE;
1822 const gchar *get_rc_dir(void)
1826 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1829 return claws_rc_dir;
1832 void set_rc_dir(const gchar *dir)
1834 if (claws_rc_dir != NULL) {
1835 g_print("Error: rc_dir already set\n");
1838 if (g_path_is_absolute(dir))
1839 claws_rc_dir = g_strdup(dir);
1841 claws_rc_dir = g_strconcat(g_get_current_dir(),
1842 G_DIR_SEPARATOR_S, dir, NULL);
1844 debug_print("set rc_dir to %s\n", claws_rc_dir);
1845 if (!is_dir_exist(claws_rc_dir)) {
1846 if (make_dir_hier(claws_rc_dir) != 0) {
1847 g_print("Error: can't create %s\n",
1854 gboolean rc_dir_is_alt(void) {
1858 const gchar *get_mail_base_dir(void)
1860 return get_home_dir();
1864 const gchar *prefs_common_get_data_root(void);
1865 gchar *last_data_root = NULL;
1868 const gchar *get_news_cache_dir(void)
1870 static gchar *news_cache_dir = NULL;
1872 const gchar *data_root = prefs_common_get_data_root();
1873 if (strcmp2(data_root, last_data_root)) {
1874 g_free(news_cache_dir);
1875 news_cache_dir = NULL;
1878 if (!news_cache_dir)
1880 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1881 NEWS_CACHE_DIR, NULL);
1885 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1886 "Claws", G_DIR_SEPARATOR_S,
1887 g_get_user_name(), G_DIR_SEPARATOR_S,
1888 NEWS_CACHE_DIR, NULL);
1889 g_free(last_data_root);
1890 last_data_root = g_strdup(last_data_root);
1892 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1893 NEWS_CACHE_DIR, NULL);
1894 g_free(last_data_root);
1895 last_data_root = NULL;
1899 return news_cache_dir;
1902 const gchar *get_imap_cache_dir(void)
1904 static gchar *imap_cache_dir = NULL;
1906 const gchar *data_root = prefs_common_get_data_root();
1907 if (strcmp2(data_root, last_data_root)) {
1908 g_free(imap_cache_dir);
1909 imap_cache_dir = NULL;
1913 if (!imap_cache_dir)
1915 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1916 IMAP_CACHE_DIR, NULL);
1920 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1921 "Claws", G_DIR_SEPARATOR_S,
1922 g_get_user_name(), G_DIR_SEPARATOR_S,
1923 IMAP_CACHE_DIR, NULL);
1924 g_free(last_data_root);
1925 last_data_root = g_strdup(last_data_root);
1927 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1928 IMAP_CACHE_DIR, NULL);
1929 g_free(last_data_root);
1930 last_data_root = NULL;
1935 return imap_cache_dir;
1938 const gchar *get_mime_tmp_dir(void)
1940 static gchar *mime_tmp_dir = NULL;
1943 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1944 MIME_TMP_DIR, NULL);
1946 return mime_tmp_dir;
1949 const gchar *get_template_dir(void)
1951 static gchar *template_dir = NULL;
1954 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1955 TEMPLATE_DIR, NULL);
1957 return template_dir;
1960 /* Return the default directory for Plugins. */
1961 const gchar *get_plugin_dir(void)
1964 static gchar *plugin_dir = NULL;
1967 plugin_dir = g_strconcat(w32_get_module_dir(),
1968 "\\lib\\claws-mail\\plugins\\",
1972 if (is_dir_exist(PLUGINDIR))
1975 static gchar *plugin_dir = NULL;
1977 plugin_dir = g_strconcat(get_rc_dir(),
1978 G_DIR_SEPARATOR_S, "plugins",
1979 G_DIR_SEPARATOR_S, NULL);
1985 const gchar *get_tmp_dir(void)
1987 static gchar *tmp_dir = NULL;
1990 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1996 gchar *get_tmp_file(void)
1999 static guint32 id = 0;
2001 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2002 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2007 const gchar *get_domain_name(void)
2010 static gchar *domain_name = NULL;
2016 if (gethostname(hostname, sizeof(hostname)) != 0) {
2017 perror("gethostname");
2018 domain_name = "unknown";
2020 hostname[sizeof(hostname) - 1] = '\0';
2021 if ((hp = my_gethostbyname(hostname)) == NULL) {
2022 perror("gethostbyname");
2023 domain_name = g_strdup(hostname);
2025 domain_name = g_strdup(hp->h_name);
2028 debug_print("domain name = %s\n", domain_name);
2037 off_t get_file_size(const gchar *file)
2041 if (g_stat(file, &s) < 0) {
2042 FILE_OP_ERROR(file, "stat");
2049 time_t get_file_mtime(const gchar *file)
2053 if (g_stat(file, &s) < 0) {
2054 FILE_OP_ERROR(file, "stat");
2061 off_t get_file_size_as_crlf(const gchar *file)
2065 gchar buf[BUFFSIZE];
2067 if ((fp = g_fopen(file, "rb")) == NULL) {
2068 FILE_OP_ERROR(file, "fopen");
2072 while (fgets(buf, sizeof(buf), fp) != NULL) {
2074 size += strlen(buf) + 2;
2078 FILE_OP_ERROR(file, "fgets");
2087 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2094 if (g_stat(file, &s) < 0) {
2095 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2099 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2106 /* Test on whether FILE is a relative file name. This is
2107 * straightforward for Unix but more complex for Windows. */
2108 gboolean is_relative_filename(const gchar *file)
2113 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2114 return FALSE; /* Prefixed with a hostname - this can't
2115 * be a relative name. */
2117 if ( ((*file >= 'a' && *file <= 'z')
2118 || (*file >= 'A' && *file <= 'Z'))
2120 file += 2; /* Skip drive letter. */
2122 return !(*file == '\\' || *file == '/');
2124 return !(*file == G_DIR_SEPARATOR);
2129 gboolean is_dir_exist(const gchar *dir)
2134 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2137 gboolean is_file_entry_exist(const gchar *file)
2142 return g_file_test(file, G_FILE_TEST_EXISTS);
2145 gboolean dirent_is_regular_file(struct dirent *d)
2147 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2148 if (d->d_type == DT_REG)
2150 else if (d->d_type != DT_UNKNOWN)
2154 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2157 gint change_dir(const gchar *dir)
2159 gchar *prevdir = NULL;
2162 prevdir = g_get_current_dir();
2164 if (g_chdir(dir) < 0) {
2165 FILE_OP_ERROR(dir, "chdir");
2166 if (debug_mode) g_free(prevdir);
2168 } else if (debug_mode) {
2171 cwd = g_get_current_dir();
2172 if (strcmp(prevdir, cwd) != 0)
2173 g_print("current dir: %s\n", cwd);
2181 gint make_dir(const gchar *dir)
2183 if (g_mkdir(dir, S_IRWXU) < 0) {
2184 FILE_OP_ERROR(dir, "mkdir");
2187 if (g_chmod(dir, S_IRWXU) < 0)
2188 FILE_OP_ERROR(dir, "chmod");
2193 gint make_dir_hier(const gchar *dir)
2198 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2199 parent_dir = g_strndup(dir, p - dir);
2200 if (*parent_dir != '\0') {
2201 if (!is_dir_exist(parent_dir)) {
2202 if (make_dir(parent_dir) < 0) {
2211 if (!is_dir_exist(dir)) {
2212 if (make_dir(dir) < 0)
2219 gint remove_all_files(const gchar *dir)
2222 const gchar *dir_name;
2225 prev_dir = g_get_current_dir();
2227 if (g_chdir(dir) < 0) {
2228 FILE_OP_ERROR(dir, "chdir");
2233 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2234 g_warning("failed to open directory: %s\n", dir);
2239 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2240 if (claws_unlink(dir_name) < 0)
2241 FILE_OP_ERROR(dir_name, "unlink");
2246 if (g_chdir(prev_dir) < 0) {
2247 FILE_OP_ERROR(prev_dir, "chdir");
2257 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2260 const gchar *dir_name;
2264 prev_dir = g_get_current_dir();
2266 if (g_chdir(dir) < 0) {
2267 FILE_OP_ERROR(dir, "chdir");
2272 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2273 g_warning("failed to open directory: %s\n", dir);
2278 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2279 file_no = to_number(dir_name);
2280 if (file_no > 0 && first <= file_no && file_no <= last) {
2281 if (is_dir_exist(dir_name))
2283 if (claws_unlink(dir_name) < 0)
2284 FILE_OP_ERROR(dir_name, "unlink");
2290 if (g_chdir(prev_dir) < 0) {
2291 FILE_OP_ERROR(prev_dir, "chdir");
2301 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2304 const gchar *dir_name;
2308 prev_dir = g_get_current_dir();
2310 if (g_chdir(dir) < 0) {
2311 FILE_OP_ERROR(dir, "chdir");
2316 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2317 FILE_OP_ERROR(dir, "opendir");
2322 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2323 file_no = to_number(dir_name);
2324 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2325 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2326 if (is_dir_exist(dir_name))
2328 if (claws_unlink(dir_name) < 0)
2329 FILE_OP_ERROR(dir_name, "unlink");
2335 if (g_chdir(prev_dir) < 0) {
2336 FILE_OP_ERROR(prev_dir, "chdir");
2346 gint remove_all_numbered_files(const gchar *dir)
2348 return remove_numbered_files(dir, 0, UINT_MAX);
2351 gint remove_dir_recursive(const gchar *dir)
2355 const gchar *dir_name;
2358 if (g_stat(dir, &s) < 0) {
2359 FILE_OP_ERROR(dir, "stat");
2360 if (ENOENT == errno) return 0;
2364 if (!S_ISDIR(s.st_mode)) {
2365 if (claws_unlink(dir) < 0) {
2366 FILE_OP_ERROR(dir, "unlink");
2373 prev_dir = g_get_current_dir();
2374 /* g_print("prev_dir = %s\n", prev_dir); */
2376 if (!path_cmp(prev_dir, dir)) {
2378 if (g_chdir("..") < 0) {
2379 FILE_OP_ERROR(dir, "chdir");
2382 prev_dir = g_get_current_dir();
2385 if (g_chdir(dir) < 0) {
2386 FILE_OP_ERROR(dir, "chdir");
2391 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2392 g_warning("failed to open directory: %s\n", dir);
2398 /* remove all files in the directory */
2399 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2400 /* g_print("removing %s\n", dir_name); */
2402 if (is_dir_exist(dir_name)) {
2403 if (remove_dir_recursive(dir_name) < 0) {
2404 g_warning("can't remove directory\n");
2408 if (claws_unlink(dir_name) < 0)
2409 FILE_OP_ERROR(dir_name, "unlink");
2415 if (g_chdir(prev_dir) < 0) {
2416 FILE_OP_ERROR(prev_dir, "chdir");
2423 if (g_rmdir(dir) < 0) {
2424 FILE_OP_ERROR(dir, "rmdir");
2431 gint rename_force(const gchar *oldpath, const gchar *newpath)
2434 if (!is_file_entry_exist(oldpath)) {
2438 if (is_file_exist(newpath)) {
2439 if (claws_unlink(newpath) < 0)
2440 FILE_OP_ERROR(newpath, "unlink");
2443 return g_rename(oldpath, newpath);
2447 * Append src file body to the tail of dest file.
2448 * Now keep_backup has no effects.
2450 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2452 FILE *src_fp, *dest_fp;
2456 gboolean err = FALSE;
2458 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2459 FILE_OP_ERROR(src, "fopen");
2463 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2464 FILE_OP_ERROR(dest, "fopen");
2469 if (change_file_mode_rw(dest_fp, dest) < 0) {
2470 FILE_OP_ERROR(dest, "chmod");
2471 g_warning("can't change file mode\n");
2474 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2475 if (n_read < sizeof(buf) && ferror(src_fp))
2477 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2478 g_warning("writing to %s failed.\n", dest);
2486 if (ferror(src_fp)) {
2487 FILE_OP_ERROR(src, "fread");
2491 if (fclose(dest_fp) == EOF) {
2492 FILE_OP_ERROR(dest, "fclose");
2504 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2506 FILE *src_fp, *dest_fp;
2509 gchar *dest_bak = NULL;
2510 gboolean err = FALSE;
2512 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2513 FILE_OP_ERROR(src, "fopen");
2516 if (is_file_exist(dest)) {
2517 dest_bak = g_strconcat(dest, ".bak", NULL);
2518 if (rename_force(dest, dest_bak) < 0) {
2519 FILE_OP_ERROR(dest, "rename");
2526 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2527 FILE_OP_ERROR(dest, "fopen");
2530 if (rename_force(dest_bak, dest) < 0)
2531 FILE_OP_ERROR(dest_bak, "rename");
2537 if (change_file_mode_rw(dest_fp, dest) < 0) {
2538 FILE_OP_ERROR(dest, "chmod");
2539 g_warning("can't change file mode\n");
2542 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2543 if (n_read < sizeof(buf) && ferror(src_fp))
2545 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2546 g_warning("writing to %s failed.\n", dest);
2551 if (rename_force(dest_bak, dest) < 0)
2552 FILE_OP_ERROR(dest_bak, "rename");
2559 if (ferror(src_fp)) {
2560 FILE_OP_ERROR(src, "fread");
2564 if (fclose(dest_fp) == EOF) {
2565 FILE_OP_ERROR(dest, "fclose");
2572 if (rename_force(dest_bak, dest) < 0)
2573 FILE_OP_ERROR(dest_bak, "rename");
2579 if (keep_backup == FALSE && dest_bak)
2580 claws_unlink(dest_bak);
2587 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2589 if (overwrite == FALSE && is_file_exist(dest)) {
2590 g_warning("move_file(): file %s already exists.", dest);
2594 if (rename_force(src, dest) == 0) return 0;
2596 if (EXDEV != errno) {
2597 FILE_OP_ERROR(src, "rename");
2601 if (copy_file(src, dest, FALSE) < 0) return -1;
2608 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2611 gint bytes_left, to_read;
2614 if (fseek(fp, offset, SEEK_SET) < 0) {
2619 bytes_left = length;
2620 to_read = MIN(bytes_left, sizeof(buf));
2622 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2623 if (n_read < to_read && ferror(fp))
2625 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2628 bytes_left -= n_read;
2629 if (bytes_left == 0)
2631 to_read = MIN(bytes_left, sizeof(buf));
2642 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2645 gboolean err = FALSE;
2647 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2648 FILE_OP_ERROR(dest, "fopen");
2652 if (change_file_mode_rw(dest_fp, dest) < 0) {
2653 FILE_OP_ERROR(dest, "chmod");
2654 g_warning("can't change file mode\n");
2657 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2660 if (!err && fclose(dest_fp) == EOF) {
2661 FILE_OP_ERROR(dest, "fclose");
2666 g_warning("writing to %s failed.\n", dest);
2674 /* convert line endings into CRLF. If the last line doesn't end with
2675 * linebreak, add it.
2677 gchar *canonicalize_str(const gchar *str)
2683 for (p = str; *p != '\0'; ++p) {
2690 if (p == str || *(p - 1) != '\n')
2693 out = outp = g_malloc(new_len + 1);
2694 for (p = str; *p != '\0'; ++p) {
2701 if (p == str || *(p - 1) != '\n') {
2710 gint canonicalize_file(const gchar *src, const gchar *dest)
2712 FILE *src_fp, *dest_fp;
2713 gchar buf[BUFFSIZE];
2715 gboolean err = FALSE;
2716 gboolean last_linebreak = FALSE;
2718 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2719 FILE_OP_ERROR(src, "fopen");
2723 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2724 FILE_OP_ERROR(dest, "fopen");
2729 if (change_file_mode_rw(dest_fp, dest) < 0) {
2730 FILE_OP_ERROR(dest, "chmod");
2731 g_warning("can't change file mode\n");
2734 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2738 if (len == 0) break;
2739 last_linebreak = FALSE;
2741 if (buf[len - 1] != '\n') {
2742 last_linebreak = TRUE;
2743 r = fputs(buf, dest_fp);
2744 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2745 r = fputs(buf, dest_fp);
2748 r = fwrite(buf, 1, len - 1, dest_fp);
2753 r = fputs("\r\n", dest_fp);
2757 g_warning("writing to %s failed.\n", dest);
2765 if (last_linebreak == TRUE) {
2766 if (fputs("\r\n", dest_fp) == EOF)
2770 if (ferror(src_fp)) {
2771 FILE_OP_ERROR(src, "fgets");
2775 if (fclose(dest_fp) == EOF) {
2776 FILE_OP_ERROR(dest, "fclose");
2788 gint canonicalize_file_replace(const gchar *file)
2792 tmp_file = get_tmp_file();
2794 if (canonicalize_file(file, tmp_file) < 0) {
2799 if (move_file(tmp_file, file, TRUE) < 0) {
2800 g_warning("can't replace %s .\n", file);
2801 claws_unlink(tmp_file);
2810 gchar *normalize_newlines(const gchar *str)
2812 const gchar *p = str;
2815 out = outp = g_malloc(strlen(str) + 1);
2816 for (p = str; *p != '\0'; ++p) {
2818 if (*(p + 1) != '\n')
2829 gchar *get_outgoing_rfc2822_str(FILE *fp)
2831 gchar buf[BUFFSIZE];
2835 str = g_string_new(NULL);
2837 /* output header part */
2838 while (fgets(buf, sizeof(buf), fp) != NULL) {
2840 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2847 else if (next != ' ' && next != '\t') {
2851 if (fgets(buf, sizeof(buf), fp) == NULL)
2855 g_string_append(str, buf);
2856 g_string_append(str, "\r\n");
2862 /* output body part */
2863 while (fgets(buf, sizeof(buf), fp) != NULL) {
2866 g_string_append_c(str, '.');
2867 g_string_append(str, buf);
2868 g_string_append(str, "\r\n");
2872 g_string_free(str, FALSE);
2878 * Create a new boundary in a way that it is very unlikely that this
2879 * will occur in the following text. It would be easy to ensure
2880 * uniqueness if everything is either quoted-printable or base64
2881 * encoded (note that conversion is allowed), but because MIME bodies
2882 * may be nested, it may happen that the same boundary has already
2885 * boundary := 0*69<bchars> bcharsnospace
2886 * bchars := bcharsnospace / " "
2887 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2888 * "+" / "_" / "," / "-" / "." /
2889 * "/" / ":" / "=" / "?"
2891 * some special characters removed because of buggy MTAs
2894 gchar *generate_mime_boundary(const gchar *prefix)
2896 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2897 "abcdefghijklmnopqrstuvwxyz"
2902 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2903 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2906 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2910 gint change_file_mode_rw(FILE *fp, const gchar *file)
2913 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2915 return g_chmod(file, S_IRUSR|S_IWUSR);
2919 FILE *my_tmpfile(void)
2921 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2922 const gchar suffix[] = ".XXXXXX";
2923 const gchar *tmpdir;
2925 const gchar *progname;
2934 tmpdir = get_tmp_dir();
2935 tmplen = strlen(tmpdir);
2936 progname = g_get_prgname();
2937 if (progname == NULL)
2938 progname = "claws-mail";
2939 proglen = strlen(progname);
2940 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2943 memcpy(fname, tmpdir, tmplen);
2944 fname[tmplen] = G_DIR_SEPARATOR;
2945 memcpy(fname + tmplen + 1, progname, proglen);
2946 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2948 fd = mkstemp(fname);
2953 claws_unlink(fname);
2955 /* verify that we can write in the file after unlinking */
2956 if (write(fd, buf, 1) < 0) {
2963 fp = fdopen(fd, "w+b");
2971 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2976 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2980 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2981 dir, G_DIR_SEPARATOR);
2982 fd = mkstemp_name(template, filename);
2985 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2986 fd = mkstemp(*filename);
2988 return fdopen(fd, "w+");
2991 FILE *str_open_as_stream(const gchar *str)
2996 g_return_val_if_fail(str != NULL, NULL);
3000 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3005 if (len == 0) return fp;
3007 if (fwrite(str, 1, len, fp) != len) {
3008 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3017 gint str_write_to_file(const gchar *str, const gchar *file)
3022 g_return_val_if_fail(str != NULL, -1);
3023 g_return_val_if_fail(file != NULL, -1);
3025 if ((fp = g_fopen(file, "wb")) == NULL) {
3026 FILE_OP_ERROR(file, "fopen");
3036 if (fwrite(str, 1, len, fp) != len) {
3037 FILE_OP_ERROR(file, "fwrite");
3043 if (fclose(fp) == EOF) {
3044 FILE_OP_ERROR(file, "fclose");
3052 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3059 g_return_val_if_fail(fp != NULL, NULL);
3061 array = g_byte_array_new();
3063 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3064 if (n_read < sizeof(buf) && ferror(fp))
3066 g_byte_array_append(array, buf, n_read);
3070 FILE_OP_ERROR("file stream", "fread");
3071 g_byte_array_free(array, TRUE);
3076 g_byte_array_append(array, buf, 1);
3077 str = (gchar *)array->data;
3078 g_byte_array_free(array, FALSE);
3080 if (recode && !g_utf8_validate(str, -1, NULL)) {
3081 const gchar *src_codeset, *dest_codeset;
3083 src_codeset = conv_get_locale_charset_str();
3084 dest_codeset = CS_UTF_8;
3085 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3093 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3100 struct timeval timeout = {1, 0};
3105 g_return_val_if_fail(file != NULL, NULL);
3107 if (g_stat(file, &s) != 0) {
3108 FILE_OP_ERROR(file, "stat");
3111 if (S_ISDIR(s.st_mode)) {
3112 g_warning("%s: is a directory\n", file);
3117 fp = fopen (file, "rb");
3119 FILE_OP_ERROR(file, "open");
3123 /* test whether the file is readable without blocking */
3124 fd = open(file, O_RDONLY | O_NONBLOCK);
3126 FILE_OP_ERROR(file, "open");
3133 /* allow for one second */
3134 err = select(fd+1, &fds, NULL, NULL, &timeout);
3135 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3137 FILE_OP_ERROR(file, "select");
3139 g_warning("%s: doesn't seem readable\n", file);
3145 /* Now clear O_NONBLOCK */
3146 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3147 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3151 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3152 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3157 /* get the FILE pointer */
3158 fp = fdopen(fd, "rb");
3161 FILE_OP_ERROR(file, "fdopen");
3162 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3167 str = file_read_stream_to_str_full(fp, recode);
3174 gchar *file_read_to_str(const gchar *file)
3176 return file_read_to_str_full(file, TRUE);
3178 gchar *file_read_stream_to_str(FILE *fp)
3180 return file_read_stream_to_str_full(fp, TRUE);
3183 gchar *file_read_to_str_no_recode(const gchar *file)
3185 return file_read_to_str_full(file, FALSE);
3187 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3189 return file_read_stream_to_str_full(fp, FALSE);
3192 char *fgets_crlf(char *buf, int size, FILE *stream)
3194 gboolean is_cr = FALSE;
3195 gboolean last_was_cr = FALSE;
3200 while (--size > 0 && (c = getc(stream)) != EOF)
3203 is_cr = (c == '\r');
3213 last_was_cr = is_cr;
3215 if (c == EOF && cs == buf)
3223 static gint execute_async(gchar *const argv[])
3225 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3227 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3228 NULL, NULL, NULL, FALSE) == FALSE) {
3229 g_warning("Couldn't execute command: %s\n", argv[0]);
3236 static gint execute_sync(gchar *const argv[])
3240 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3242 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3243 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3244 g_warning("Couldn't execute command: %s\n", argv[0]);
3249 if (WIFEXITED(status))
3250 return WEXITSTATUS(status);
3258 gint execute_command_line(const gchar *cmdline, gboolean async)
3263 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3265 argv = strsplit_with_quote(cmdline, " ", 0);
3268 ret = execute_async(argv);
3270 ret = execute_sync(argv);
3277 gchar *get_command_output(const gchar *cmdline)
3279 gchar *child_stdout;
3282 g_return_val_if_fail(cmdline != NULL, NULL);
3284 debug_print("get_command_output(): executing: %s\n", cmdline);
3286 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3288 g_warning("Couldn't execute command: %s\n", cmdline);
3292 return child_stdout;
3295 static gint is_unchanged_uri_char(char c)
3306 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3312 for(i = 0; i < strlen(uri) ; i++) {
3313 if (is_unchanged_uri_char(uri[i])) {
3314 if (k + 2 >= bufsize)
3316 encoded_uri[k++] = uri[i];
3319 char * hexa = "0123456789ABCDEF";
3321 if (k + 4 >= bufsize)
3323 encoded_uri[k++] = '%';
3324 encoded_uri[k++] = hexa[uri[i] / 16];
3325 encoded_uri[k++] = hexa[uri[i] % 16];
3331 gint open_uri(const gchar *uri, const gchar *cmdline)
3334 gchar buf[BUFFSIZE];
3336 gchar encoded_uri[BUFFSIZE];
3337 g_return_val_if_fail(uri != NULL, -1);
3339 /* an option to choose whether to use encode_uri or not ? */
3340 encode_uri(encoded_uri, BUFFSIZE, uri);
3343 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3344 !strchr(p + 2, '%'))
3345 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3348 g_warning("Open URI command line is invalid "
3349 "(there must be only one '%%s'): %s",
3351 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3354 execute_command_line(buf, TRUE);
3356 extern osso_context_t *get_osso_context(void);
3357 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3358 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3359 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3364 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3366 gchar buf[BUFFSIZE];
3369 g_return_val_if_fail(filepath != NULL, -1);
3372 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3373 !strchr(p + 2, '%'))
3374 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3377 g_warning("Open Text Editor command line is invalid "
3378 "(there must be only one '%%s'): %s",
3380 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3383 execute_command_line(buf, TRUE);
3388 time_t remote_tzoffset_sec(const gchar *zone)
3390 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3396 time_t remoteoffset;
3398 strncpy(zone3, zone, 3);
3402 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3403 (c == '+' || c == '-')) {
3404 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3406 remoteoffset = -remoteoffset;
3407 } else if (!strncmp(zone, "UT" , 2) ||
3408 !strncmp(zone, "GMT", 2)) {
3410 } else if (strlen(zone3) == 3) {
3411 for (p = ustzstr; *p != '\0'; p += 3) {
3412 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3413 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3414 remoteoffset = iustz * 3600;
3420 } else if (strlen(zone3) == 1) {
3422 case 'Z': remoteoffset = 0; break;
3423 case 'A': remoteoffset = -1; break;
3424 case 'B': remoteoffset = -2; break;
3425 case 'C': remoteoffset = -3; break;
3426 case 'D': remoteoffset = -4; break;
3427 case 'E': remoteoffset = -5; break;
3428 case 'F': remoteoffset = -6; break;
3429 case 'G': remoteoffset = -7; break;
3430 case 'H': remoteoffset = -8; break;
3431 case 'I': remoteoffset = -9; break;
3432 case 'K': remoteoffset = -10; break; /* J is not used */
3433 case 'L': remoteoffset = -11; break;
3434 case 'M': remoteoffset = -12; break;
3435 case 'N': remoteoffset = 1; break;
3436 case 'O': remoteoffset = 2; break;
3437 case 'P': remoteoffset = 3; break;
3438 case 'Q': remoteoffset = 4; break;
3439 case 'R': remoteoffset = 5; break;
3440 case 'S': remoteoffset = 6; break;
3441 case 'T': remoteoffset = 7; break;
3442 case 'U': remoteoffset = 8; break;
3443 case 'V': remoteoffset = 9; break;
3444 case 'W': remoteoffset = 10; break;
3445 case 'X': remoteoffset = 11; break;
3446 case 'Y': remoteoffset = 12; break;
3447 default: remoteoffset = 0; break;
3449 remoteoffset = remoteoffset * 3600;
3453 return remoteoffset;
3456 time_t tzoffset_sec(time_t *now)
3461 struct tm buf1, buf2;
3464 if (now && *now == -1)
3467 gmt = *gmtime_r(now, &buf1);
3468 lt = localtime_r(now, &buf2);
3470 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3472 if (lt->tm_year < gmt.tm_year)
3474 else if (lt->tm_year > gmt.tm_year)
3476 else if (lt->tm_yday < gmt.tm_yday)
3478 else if (lt->tm_yday > gmt.tm_yday)
3481 if (off >= 24 * 60) /* should be impossible */
3482 off = 23 * 60 + 59; /* if not, insert silly value */
3483 if (off <= -24 * 60)
3484 off = -(23 * 60 + 59);
3489 /* calculate timezone offset */
3490 gchar *tzoffset(time_t *now)
3492 static gchar offset_string[6];
3497 struct tm buf1, buf2;
3500 if (now && *now == -1)
3503 gmt = *gmtime_r(now, &buf1);
3504 lt = localtime_r(now, &buf2);
3506 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3508 if (lt->tm_year < gmt.tm_year)
3510 else if (lt->tm_year > gmt.tm_year)
3512 else if (lt->tm_yday < gmt.tm_yday)
3514 else if (lt->tm_yday > gmt.tm_yday)
3522 if (off >= 24 * 60) /* should be impossible */
3523 off = 23 * 60 + 59; /* if not, insert silly value */
3525 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3527 return offset_string;
3530 void get_rfc822_date(gchar *buf, gint len)
3534 gchar day[4], mon[4];
3535 gint dd, hh, mm, ss, yyyy;
3538 gchar buf2[BUFFSIZE];
3542 lt = localtime_r(&t, &buf1);
3544 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3545 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3547 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3548 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3551 void debug_set_mode(gboolean mode)
3556 gboolean debug_get_mode(void)
3561 void debug_print_real(const gchar *format, ...)
3564 gchar buf[BUFFSIZE];
3566 if (!debug_mode) return;
3568 va_start(args, format);
3569 g_vsnprintf(buf, sizeof(buf), format, args);
3576 const char * debug_srcname(const char *file)
3578 const char *s = strrchr (file, '/');
3583 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3585 if (subject == NULL)
3588 subject += subject_get_prefix_length(subject);
3590 return g_hash_table_lookup(subject_table, subject);
3593 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3596 if (subject == NULL || *subject == 0)
3598 subject += subject_get_prefix_length(subject);
3599 g_hash_table_insert(subject_table, subject, data);
3602 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3604 if (subject == NULL)
3607 subject += subject_get_prefix_length(subject);
3608 g_hash_table_remove(subject_table, subject);
3612 *\brief Check if a string is prefixed with known (combinations)
3613 * of prefixes. The function assumes that each prefix
3614 * is terminated by zero or exactly _one_ space.
3616 *\param str String to check for a prefixes
3618 *\return int Number of chars in the prefix that should be skipped
3619 * for a "clean" subject line. If no prefix was found, 0
3624 static regex_t u_regex;
3625 static gboolean u_init_;
3628 void utils_free_regex(void)
3638 int subject_get_prefix_length(const gchar *subject)
3641 /*!< Array with allowable reply prefixes regexps. */
3642 static const gchar * const prefixes[] = {
3643 "Re\\:", /* "Re:" */
3644 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3645 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3646 "Aw\\:", /* "Aw:" (German) */
3647 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3648 "Res\\:", /* "Res:" (Brazilian Outlook) */
3649 "Fw\\:", /* "Fw:" Forward */
3650 "Fwd\\:", /* "Fwd:" Forward */
3651 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3652 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3653 "Rif\\:", /* "Rif:" (Italian Outlook) */
3654 "Sv\\:", /* "Sv" (Norwegian) */
3655 "Vs\\:", /* "Vs" (Norwegian) */
3656 "Ad\\:", /* "Ad" (Norwegian) */
3657 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3660 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3664 if (!subject) return 0;
3665 if (!*subject) return 0;
3668 GString *s = g_string_new("");
3670 for (n = 0; n < PREFIXES; n++)
3671 /* Terminate each prefix regexpression by a
3672 * "\ ?" (zero or ONE space), and OR them */
3673 g_string_append_printf(s, "(%s\\ ?)%s",
3678 g_string_prepend(s, "(");
3679 g_string_append(s, ")+"); /* match at least once */
3680 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3683 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3684 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3685 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3686 debug_print("Error compiling regexp %s\n", s->str);
3687 g_string_free(s, TRUE);
3691 g_string_free(s, TRUE);
3695 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3700 /*!< Array with allowable reply prefixes regexps. */
3701 static const gchar * const prefixes[] = {
3703 "antw:", /* "Antw:" (Dutch / German Outlook) */
3704 "aw:", /* "Aw:" (German) */
3705 "antwort:", /* "Antwort:" (German Lotus Notes) */
3706 "res:", /* "Res:" (Brazilian Outlook) */
3707 "fw:", /* "Fw:" Forward */
3708 "fwd:", /* "Fwd:" Forward */
3709 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3710 "odp:", /* "Odp:" Re (Polish Outlook) */
3711 "rif:", /* "Rif:" (Italian Outlook) */
3712 "sv:", /* "Sv" (Norwegian) */
3713 "vs:", /* "Vs" (Norwegian) */
3714 "ad:", /* "Ad" (Norwegian) */
3717 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3720 if (!subject) return 0;
3721 if (!*subject) return 0;
3723 for (n = 0; n < PREFIXES; n++) {
3724 int len = strlen(prefixes[n]);
3725 if (!strncasecmp(subject, prefixes[n], len))
3731 static guint g_stricase_hash(gconstpointer gptr)
3733 guint hash_result = 0;
3736 for (str = gptr; str && *str; str++) {
3737 hash_result += toupper(*str);
3743 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3745 const char *str1 = gptr1;
3746 const char *str2 = gptr2;
3748 return !strcasecmp(str1, str2);
3751 gint g_int_compare(gconstpointer a, gconstpointer b)
3753 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3756 gchar *generate_msgid(gchar *buf, gint len)
3766 lt = localtime_r(&t, &buft);
3768 if (strcmp(buf, "") == 0) {
3769 addr = g_strconcat("@", get_domain_name(), NULL);
3772 addr = g_strconcat("@", buf, NULL);
3775 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3776 lt->tm_year + 1900, lt->tm_mon + 1,
3777 lt->tm_mday, lt->tm_hour,
3778 lt->tm_min, lt->tm_sec,
3779 (guint) rand(), addr);
3786 quote_cmd_argument()
3788 return a quoted string safely usable in argument of a command.
3790 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3793 gint quote_cmd_argument(gchar * result, guint size,
3803 for(p = path ; * p != '\0' ; p ++) {
3805 if (isalnum((guchar)*p) || (* p == '/')) {
3806 if (remaining > 0) {
3812 result[size - 1] = '\0';
3817 if (remaining >= 2) {
3825 result[size - 1] = '\0';
3830 if (remaining > 0) {
3834 result[size - 1] = '\0';
3848 static void g_node_map_recursive(GNode *node, gpointer data)
3850 GNodeMapData *mapdata = (GNodeMapData *) data;
3852 GNodeMapData newmapdata;
3855 newdata = mapdata->func(node->data, mapdata->data);
3856 if (newdata != NULL) {
3857 newnode = g_node_new(newdata);
3858 g_node_append(mapdata->parent, newnode);
3860 newmapdata.parent = newnode;
3861 newmapdata.func = mapdata->func;
3862 newmapdata.data = mapdata->data;
3864 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3868 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3871 GNodeMapData mapdata;
3873 g_return_val_if_fail(node != NULL, NULL);
3874 g_return_val_if_fail(func != NULL, NULL);
3876 root = g_node_new(func(node->data, data));
3878 mapdata.parent = root;
3879 mapdata.func = func;
3880 mapdata.data = data;
3882 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3887 #define HEX_TO_INT(val, hex) \
3891 if ('0' <= c && c <= '9') { \
3893 } else if ('a' <= c && c <= 'f') { \
3894 val = c - 'a' + 10; \
3895 } else if ('A' <= c && c <= 'F') { \
3896 val = c - 'A' + 10; \
3902 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3909 if (hi == -1 || lo == -1)
3912 *out = (hi << 4) + lo;
3916 #define INT_TO_HEX(hex, val) \
3919 hex = '0' + (val); \
3921 hex = 'A' + (val) - 10; \
3924 void get_hex_str(gchar *out, guchar ch)
3928 INT_TO_HEX(hex, ch >> 4);
3930 INT_TO_HEX(hex, ch & 0x0f);
3936 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3938 #define G_PRINT_REF g_print
3942 *\brief Register ref counted pointer. It is based on GBoxed, so should
3943 * work with anything that uses the GType system. The semantics
3944 * are similar to a C++ auto pointer, with the exception that
3945 * C doesn't have automatic closure (calling destructors) when
3946 * exiting a block scope.
3947 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3948 * function directly.
3950 *\return GType A GType type.
3952 GType g_auto_pointer_register(void)
3954 static GType auto_pointer_type;
3955 if (!auto_pointer_type)
3957 g_boxed_type_register_static
3958 ("G_TYPE_AUTO_POINTER",
3959 (GBoxedCopyFunc) g_auto_pointer_copy,
3960 (GBoxedFreeFunc) g_auto_pointer_free);
3961 return auto_pointer_type;
3965 *\brief Structure with g_new() allocated pointer guarded by the
3968 typedef struct AutoPointerRef {
3969 void (*free) (gpointer);
3975 *\brief The auto pointer opaque structure that references the
3976 * pointer guard block.
3978 typedef struct AutoPointer {
3979 AutoPointerRef *ref;
3980 gpointer ptr; /*!< access to protected pointer */
3984 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3988 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3989 * ... when assigning, copying and freeing storage elements
3991 * gtk_list_store_new(N_S_COLUMNS,
3992 * G_TYPE_AUTO_POINTER,
3996 * Template *precious_data = g_new0(Template, 1);
3997 * g_pointer protect = g_auto_pointer_new(precious_data);
3999 * gtk_list_store_set(container, &iter,
4003 * ... the gtk_list_store has copied the pointer and
4004 * ... incremented its reference count, we should free
4005 * ... the auto pointer (in C++ a destructor would do
4006 * ... this for us when leaving block scope)
4008 * g_auto_pointer_free(protect);
4010 * ... gtk_list_store_set() now manages the data. When
4011 * ... *explicitly* requesting a pointer from the list
4012 * ... store, don't forget you get a copy that should be
4013 * ... freed with g_auto_pointer_free() eventually.
4017 *\param pointer Pointer to be guarded.
4019 *\return GAuto * Pointer that should be used in containers with
4022 GAuto *g_auto_pointer_new(gpointer p)
4024 AutoPointerRef *ref;
4030 ref = g_new0(AutoPointerRef, 1);
4031 ptr = g_new0(AutoPointer, 1);
4041 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4047 *\brief Allocate an autopointer using the passed \a free function to
4048 * free the guarded pointer
4050 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4057 aptr = g_auto_pointer_new(p);
4058 aptr->ref->free = free_;
4062 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4064 if (auto_ptr == NULL)
4066 return ((AutoPointer *) auto_ptr)->ptr;
4070 *\brief Copies an auto pointer by. It's mostly not necessary
4071 * to call this function directly, unless you copy/assign
4072 * the guarded pointer.
4074 *\param auto_ptr Auto pointer returned by previous call to
4075 * g_auto_pointer_new_XXX()
4077 *\return gpointer An auto pointer
4079 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4082 AutoPointerRef *ref;
4085 if (auto_ptr == NULL)
4090 newp = g_new0(AutoPointer, 1);
4093 newp->ptr = ref->pointer;
4097 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4103 *\brief Free an auto pointer
4105 void g_auto_pointer_free(GAuto *auto_ptr)
4108 AutoPointerRef *ref;
4110 if (auto_ptr == NULL)
4116 if (--(ref->cnt) == 0) {
4118 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4120 ref->free(ref->pointer);
4125 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4130 void replace_returns(gchar *str)
4135 while (strstr(str, "\n")) {
4136 *strstr(str, "\n") = ' ';
4138 while (strstr(str, "\r")) {
4139 *strstr(str, "\r") = ' ';
4143 /* get_uri_part() - retrieves a URI starting from scanpos.
4144 Returns TRUE if succesful */
4145 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4146 const gchar **bp, const gchar **ep, gboolean hdr)
4149 gint parenthese_cnt = 0;
4151 g_return_val_if_fail(start != NULL, FALSE);
4152 g_return_val_if_fail(scanpos != NULL, FALSE);
4153 g_return_val_if_fail(bp != NULL, FALSE);
4154 g_return_val_if_fail(ep != NULL, FALSE);
4158 /* find end point of URI */
4159 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4160 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4161 !IS_ASCII(*(const guchar *)ep_) ||
4162 strchr("[]{}<>\"", *ep_)) {
4164 } else if (strchr("(", *ep_)) {
4166 } else if (strchr(")", *ep_)) {
4167 if (parenthese_cnt > 0)
4174 /* no punctuation at end of string */
4176 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4177 * should pass some URI type to this function and decide on that whether
4178 * to perform punctuation stripping */
4180 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4182 for (; ep_ - 1 > scanpos + 1 &&
4183 IS_REAL_PUNCT(*(ep_ - 1));
4187 #undef IS_REAL_PUNCT
4194 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4196 while (bp && *bp && g_ascii_isspace(*bp))
4198 return g_strndup(bp, ep - bp);
4201 /* valid mail address characters */
4202 #define IS_RFC822_CHAR(ch) \
4206 !g_ascii_isspace(ch) && \
4207 !strchr("(),;<>\"", (ch)))
4209 /* alphabet and number within 7bit ASCII */
4210 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4211 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4213 static GHashTable *create_domain_tab(void)
4215 static const gchar *toplvl_domains [] = {
4217 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4218 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4219 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4220 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4221 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4222 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4223 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4224 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4225 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4226 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4227 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4228 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4229 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4230 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4231 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4232 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4233 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4234 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4235 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4236 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4237 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4238 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4239 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4240 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4241 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4242 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4245 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4247 g_return_val_if_fail(htab, NULL);
4248 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4249 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4253 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4255 const gint MAX_LVL_DOM_NAME_LEN = 6;
4256 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4257 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4260 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4263 for (p = buf; p < m && first < last; *p++ = *first++)
4267 return g_hash_table_lookup(tab, buf) != NULL;
4270 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4271 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4272 const gchar **bp, const gchar **ep, gboolean hdr)
4274 /* more complex than the uri part because we need to scan back and forward starting from
4275 * the scan position. */
4276 gboolean result = FALSE;
4277 const gchar *bp_ = NULL;
4278 const gchar *ep_ = NULL;
4279 static GHashTable *dom_tab;
4280 const gchar *last_dot = NULL;
4281 const gchar *prelast_dot = NULL;
4282 const gchar *last_tld_char = NULL;
4284 /* the informative part of the email address (describing the name
4285 * of the email address owner) may contain quoted parts. the
4286 * closure stack stores the last encountered quotes. */
4287 gchar closure_stack[128];
4288 gchar *ptr = closure_stack;
4290 g_return_val_if_fail(start != NULL, FALSE);
4291 g_return_val_if_fail(scanpos != NULL, FALSE);
4292 g_return_val_if_fail(bp != NULL, FALSE);
4293 g_return_val_if_fail(ep != NULL, FALSE);
4296 const gchar *start_quote = NULL;
4297 const gchar *end_quote = NULL;
4299 /* go to the real start */
4300 if (start[0] == ',')
4302 if (start[0] == ';')
4304 while (start[0] == '\n' || start[0] == '\r')
4306 while (start[0] == ' ' || start[0] == '\t')
4311 /* check if there are quotes (to skip , in them) */
4312 if (*start == '"') {
4313 start_quote = start;
4315 end_quote = strstr(start, "\"");
4321 /* skip anything between quotes */
4322 if (start_quote && end_quote) {
4327 /* find end (either , or ; or end of line) */
4328 if (strstr(start, ",") && strstr(start, ";"))
4329 *ep = strstr(start,",") < strstr(start, ";")
4330 ? strstr(start, ",") : strstr(start, ";");
4331 else if (strstr(start, ","))
4332 *ep = strstr(start, ",");
4333 else if (strstr(start, ";"))
4334 *ep = strstr(start, ";");
4336 *ep = start+strlen(start);
4338 /* go back to real start */
4339 if (start_quote && end_quote) {
4340 start = start_quote;
4343 /* check there's still an @ in that, or search
4344 * further if possible */
4345 if (strstr(start, "@") && strstr(start, "@") < *ep)
4347 else if (*ep < start+strlen(start)) {
4350 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4358 dom_tab = create_domain_tab();
4359 g_return_val_if_fail(dom_tab, FALSE);
4361 /* scan start of address */
4362 for (bp_ = scanpos - 1;
4363 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4366 /* TODO: should start with an alnum? */
4368 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4371 if (bp_ != scanpos) {
4372 /* scan end of address */
4373 for (ep_ = scanpos + 1;
4374 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4376 prelast_dot = last_dot;
4378 if (*(last_dot + 1) == '.') {
4379 if (prelast_dot == NULL)
4381 last_dot = prelast_dot;
4386 /* TODO: really should terminate with an alnum? */
4387 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4392 if (last_dot == NULL)
4394 if (last_dot >= ep_)
4395 last_dot = prelast_dot;
4396 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4400 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4401 if (*last_tld_char == '?')
4404 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4411 if (!result) return FALSE;
4413 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4414 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4415 && IS_RFC822_CHAR(*(ep_ + 3))) {
4416 /* this informative part with an @ in it is
4417 * followed by the email address */
4420 /* go to matching '>' (or next non-rfc822 char, like \n) */
4421 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4424 /* include the bracket */
4425 if (*ep_ == '>') ep_++;
4427 /* include the leading quote */
4435 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4436 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4439 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4440 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4443 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4444 #define IN_STACK() (ptr > closure_stack)
4445 /* has underrun check */
4446 #define POP_STACK() if(IN_STACK()) --ptr
4447 /* has overrun check */
4448 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4449 /* has underrun check */
4450 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4454 /* scan for the informative part. */
4455 for (bp_ -= 2; bp_ >= start; bp_--) {
4456 /* if closure on the stack keep scanning */
4457 if (PEEK_STACK() == *bp_) {
4461 if (*bp_ == '\'' || *bp_ == '"') {
4466 /* if nothing in the closure stack, do the special conditions
4467 * the following if..else expression simply checks whether
4468 * a token is acceptable. if not acceptable, the clause
4469 * should terminate the loop with a 'break' */
4470 if (!PEEK_STACK()) {
4472 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4473 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4474 /* hyphens are allowed, but only in
4476 } else if (strchr(" \"'", *bp_)) {
4477 /* but anything not being a punctiation
4480 break; /* anything else is rejected */
4487 /* scan forward (should start with an alnum) */
4488 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4504 #undef IS_ASCII_ALNUM
4505 #undef IS_RFC822_CHAR
4507 gchar *make_email_string(const gchar *bp, const gchar *ep)
4509 /* returns a mailto: URI; mailto: is also used to detect the
4510 * uri type later on in the button_pressed signal handler */
4514 tmp = g_strndup(bp, ep - bp);
4515 result = g_strconcat("mailto:", tmp, NULL);
4521 gchar *make_http_string(const gchar *bp, const gchar *ep)
4523 /* returns an http: URI; */
4527 while (bp && *bp && g_ascii_isspace(*bp))
4529 tmp = g_strndup(bp, ep - bp);
4530 result = g_strconcat("http://", tmp, NULL);
4536 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4538 FILE *fp = fopen(path, "rb");
4539 gchar buf[BUFFSIZE];
4540 gchar *result = NULL;
4543 while (fgets(buf, sizeof (buf), fp) != NULL) {
4544 gchar **parts = g_strsplit(buf, ";", 3);
4545 gchar *trimmed = parts[0];
4546 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4548 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4549 trimmed[strlen(trimmed)-1] = '\0';
4551 if (!strcmp(trimmed, type)) {
4552 gboolean needsterminal = FALSE;
4553 if (parts[2] && strstr(parts[2], "needsterminal")) {
4554 needsterminal = TRUE;
4556 if (parts[2] && strstr(parts[2], "test=")) {
4557 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4558 gchar *testcmd = orig_testcmd;
4559 if (strstr(testcmd,";"))
4560 *(strstr(testcmd,";")) = '\0';
4561 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4563 while (testcmd[strlen(testcmd)-1] == '\n')
4564 testcmd[strlen(testcmd)-1] = '\0';
4565 while (testcmd[strlen(testcmd)-1] == '\r')
4566 testcmd[strlen(testcmd)-1] = '\0';
4567 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4568 testcmd[strlen(testcmd)-1] = '\0';
4570 if (strstr(testcmd, "%s")) {
4571 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4572 gint res = system(tmp);
4574 g_free(orig_testcmd);
4581 gint res = system(testcmd);
4582 g_free(orig_testcmd);
4592 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4594 while (trimmed[strlen(trimmed)-1] == '\n')
4595 trimmed[strlen(trimmed)-1] = '\0';
4596 while (trimmed[strlen(trimmed)-1] == '\r')
4597 trimmed[strlen(trimmed)-1] = '\0';
4598 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4599 trimmed[strlen(trimmed)-1] = '\0';
4600 result = g_strdup(trimmed);
4603 /* if there are no single quotes around %s, add them.
4604 * '.*%s.*' is ok, as in display 'png:%s'
4606 if (strstr(result, "%s")
4607 && !(strstr(result, "'") < strstr(result,"%s") &&
4608 strstr(strstr(result,"%s"), "'"))) {
4609 gchar *start = g_strdup(result);
4610 gchar *end = g_strdup(strstr(result, "%s")+2);
4612 *strstr(start, "%s") = '\0';
4613 tmp = g_strconcat(start,"'%s'",end, NULL);
4619 if (needsterminal) {
4620 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4631 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4633 gchar *result = NULL;
4637 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4638 result = mailcap_get_command_in_file(path, type, file_to_open);
4642 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4646 void mailcap_update_default(const gchar *type, const gchar *command)
4648 gchar *path = NULL, *outpath = NULL;
4649 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4650 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4651 FILE *fp = fopen(path, "rb");
4652 FILE *outfp = fopen(outpath, "wb");
4653 gchar buf[BUFFSIZE];
4654 gboolean err = FALSE;
4662 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4663 gchar **parts = g_strsplit(buf, ";", 3);
4664 gchar *trimmed = parts[0];
4665 while (trimmed[0] == ' ')
4667 while (trimmed[strlen(trimmed)-1] == ' ')
4668 trimmed[strlen(trimmed)-1] = '\0';
4670 if (!strcmp(trimmed, type)) {
4675 if(fputs(buf, outfp) == EOF) {
4682 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4688 if (fclose(outfp) == EOF)
4692 g_rename(outpath, path);
4698 gint copy_dir(const gchar *src, const gchar *dst)
4703 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4704 g_warning("failed to open directory: %s\n", src);
4708 if (make_dir(dst) < 0)
4711 while ((name = g_dir_read_name(dir)) != NULL) {
4712 gchar *old_file, *new_file;
4713 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4714 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4715 debug_print("copying: %s -> %s\n", old_file, new_file);
4716 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4717 gint r = copy_file(old_file, new_file, TRUE);
4724 /* Windows has no symlinks. Or well, Vista seems to
4725 have something like this but the semantics might be
4726 different. Thus we don't use it under Windows. */
4727 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4730 gchar *target = g_file_read_link(old_file, &error);
4732 r = symlink(target, new_file);
4739 #endif /*G_OS_WIN32*/
4740 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4741 gint r = copy_dir(old_file, new_file);
4752 /* crude test to see if a file is an email. */
4753 gboolean file_is_email (const gchar *filename)
4759 if (filename == NULL)
4761 if ((fp = g_fopen(filename, "rb")) == NULL)
4763 while (i < 60 && score < 3
4764 && fgets(buffer, sizeof (buffer), fp) > 0) {
4765 if (!strncmp(buffer, "From:", strlen("From:")))
4767 if (!strncmp(buffer, "To:", strlen("To:")))
4769 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4774 return (score >= 3);
4777 gboolean sc_g_list_bigger(GList *list, gint max)
4781 while (cur && i <= max+1) {
4788 gboolean sc_g_slist_bigger(GSList *list, gint max)
4792 while (cur && i <= max+1) {
4799 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4800 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4801 NULL, NULL, NULL, NULL, NULL, NULL};
4802 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4803 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4804 NULL, NULL, NULL, NULL, NULL, NULL};
4806 gint daynames_len[] = {0,0,0,0,0,0,0};
4807 gint monthnames_len[] = {0,0,0,0,0,0,
4809 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4810 gint s_monthnames_len[] = {0,0,0,0,0,0,
4812 const gchar *s_am_up = NULL;
4813 const gchar *s_pm_up = NULL;
4814 const gchar *s_am_low = NULL;
4815 const gchar *s_pm_low = NULL;
4817 gint s_am_up_len = 0;
4818 gint s_pm_up_len = 0;
4819 gint s_am_low_len = 0;
4820 gint s_pm_low_len = 0;
4822 const gchar *def_loc_format = NULL;
4823 const gchar *date_loc_format = NULL;
4824 const gchar *time_loc_format = NULL;
4825 const gchar *time_am_pm = NULL;
4827 static gboolean time_names_init_done = FALSE;
4829 static void init_time_names(void)
4833 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4834 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4835 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4836 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4837 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4838 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4839 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4841 monthnames[0] = Q_("Complete month name for use by strftime|January");
4842 monthnames[1] = Q_("Complete month name for use by strftime|February");
4843 monthnames[2] = Q_("Complete month name for use by strftime|March");
4844 monthnames[3] = Q_("Complete month name for use by strftime|April");
4845 monthnames[4] = Q_("Complete month name for use by strftime|May");
4846 monthnames[5] = Q_("Complete month name for use by strftime|June");
4847 monthnames[6] = Q_("Complete month name for use by strftime|July");
4848 monthnames[7] = Q_("Complete month name for use by strftime|August");
4849 monthnames[8] = Q_("Complete month name for use by strftime|September");
4850 monthnames[9] = Q_("Complete month name for use by strftime|October");
4851 monthnames[10] = Q_("Complete month name for use by strftime|November");
4852 monthnames[11] = Q_("Complete month name for use by strftime|December");
4854 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4855 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4856 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4857 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4858 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4859 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4860 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4862 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4863 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4864 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4865 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4866 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4867 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4868 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4869 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4870 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4871 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4872 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4873 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4875 for (i = 0; i < 7; i++) {
4876 daynames_len[i] = strlen(daynames[i]);
4877 s_daynames_len[i] = strlen(s_daynames[i]);
4879 for (i = 0; i < 12; i++) {
4880 monthnames_len[i] = strlen(monthnames[i]);
4881 s_monthnames_len[i] = strlen(s_monthnames[i]);
4884 s_am_up = Q_("For use by strftime (morning)|AM");
4885 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4886 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4887 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4889 s_am_up_len = strlen(s_am_up);
4890 s_pm_up_len = strlen(s_pm_up);
4891 s_am_low_len = strlen(s_am_low);
4892 s_pm_low_len = strlen(s_pm_low);
4894 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4895 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4896 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4898 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4900 time_names_init_done = TRUE;
4903 #define CHECK_SIZE() { \
4904 total_done += len; \
4905 if (total_done >= buflen) { \
4906 buf[buflen-1] = '\0'; \
4911 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4913 gchar *curpos = buf;
4914 gint total_done = 0;
4915 gchar subbuf[64], subfmt[64];
4916 static time_t last_tzset = (time_t)0;
4918 if (!time_names_init_done)
4921 if (format == NULL || lt == NULL)
4924 if (last_tzset != time(NULL)) {
4926 last_tzset = time(NULL);
4929 if (*format == '%') {
4930 gint len = 0, tmp = 0;
4934 len = 1; CHECK_SIZE();
4938 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4939 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4942 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4943 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4947 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4948 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4951 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4952 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4955 fast_strftime(subbuf, 64, def_loc_format, lt);
4956 len = strlen(subbuf); CHECK_SIZE();
4957 strncpy2(curpos, subbuf, buflen - total_done);
4960 total_done += 2; CHECK_SIZE();
4961 tmp = (lt->tm_year + 1900)/100;
4962 *curpos++ = '0'+(tmp / 10);
4963 *curpos++ = '0'+(tmp % 10);
4966 total_done += 2; CHECK_SIZE();
4967 *curpos++ = '0'+(lt->tm_mday / 10);
4968 *curpos++ = '0'+(lt->tm_mday % 10);
4971 total_done += 8; CHECK_SIZE();
4972 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4973 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4975 *curpos++ = '0'+(lt->tm_mday / 10);
4976 *curpos++ = '0'+(lt->tm_mday % 10);
4978 tmp = lt->tm_year%100;
4979 *curpos++ = '0'+(tmp / 10);
4980 *curpos++ = '0'+(tmp % 10);
4983 len = 2; CHECK_SIZE();
4984 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4987 len = 10; CHECK_SIZE();
4988 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4989 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4992 total_done += 2; CHECK_SIZE();
4993 *curpos++ = '0'+(lt->tm_hour / 10);
4994 *curpos++ = '0'+(lt->tm_hour % 10);
4997 total_done += 2; CHECK_SIZE();
5003 *curpos++ = '0'+(tmp / 10);
5004 *curpos++ = '0'+(tmp % 10);
5007 len = 3; CHECK_SIZE();
5008 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5011 len = 2; CHECK_SIZE();
5012 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5015 len = 2; CHECK_SIZE();
5021 snprintf(curpos, buflen - total_done, "%2d", tmp);
5024 total_done += 2; CHECK_SIZE();
5025 tmp = lt->tm_mon + 1;
5026 *curpos++ = '0'+(tmp / 10);
5027 *curpos++ = '0'+(tmp % 10);
5030 total_done += 2; CHECK_SIZE();
5031 *curpos++ = '0'+(lt->tm_min / 10);
5032 *curpos++ = '0'+(lt->tm_min % 10);
5035 len = 1; CHECK_SIZE();
5039 if (lt->tm_hour >= 12) {
5040 len = s_pm_up_len; CHECK_SIZE();
5041 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5043 len = s_am_up_len; CHECK_SIZE();
5044 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5048 if (lt->tm_hour >= 12) {
5049 len = s_pm_low_len; CHECK_SIZE();
5050 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5052 len = s_am_low_len; CHECK_SIZE();
5053 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5057 fast_strftime(subbuf, 64, time_am_pm, lt);
5058 len = strlen(subbuf); CHECK_SIZE();
5059 strncpy2(curpos, subbuf, buflen - total_done);
5062 total_done += 5; CHECK_SIZE();
5063 *curpos++ = '0'+(lt->tm_hour / 10);
5064 *curpos++ = '0'+(lt->tm_hour % 10);
5066 *curpos++ = '0'+(lt->tm_min / 10);
5067 *curpos++ = '0'+(lt->tm_min % 10);
5070 snprintf(subbuf, 64, "%ld", mktime(lt));
5071 len = strlen(subbuf); CHECK_SIZE();
5072 strncpy2(curpos, subbuf, buflen - total_done);
5075 total_done += 2; CHECK_SIZE();
5076 *curpos++ = '0'+(lt->tm_sec / 10);
5077 *curpos++ = '0'+(lt->tm_sec % 10);
5080 len = 1; CHECK_SIZE();
5084 total_done += 8; CHECK_SIZE();
5085 *curpos++ = '0'+(lt->tm_hour / 10);
5086 *curpos++ = '0'+(lt->tm_hour % 10);
5088 *curpos++ = '0'+(lt->tm_min / 10);
5089 *curpos++ = '0'+(lt->tm_min % 10);
5091 *curpos++ = '0'+(lt->tm_sec / 10);
5092 *curpos++ = '0'+(lt->tm_sec % 10);
5095 len = 1; CHECK_SIZE();
5096 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5099 len = 1; CHECK_SIZE();
5100 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5103 fast_strftime(subbuf, 64, date_loc_format, lt);
5104 len = strlen(subbuf); CHECK_SIZE();
5105 strncpy2(curpos, subbuf, buflen - total_done);
5108 fast_strftime(subbuf, 64, time_loc_format, lt);
5109 len = strlen(subbuf); CHECK_SIZE();
5110 strncpy2(curpos, subbuf, buflen - total_done);
5113 total_done += 2; CHECK_SIZE();
5114 tmp = lt->tm_year%100;
5115 *curpos++ = '0'+(tmp / 10);
5116 *curpos++ = '0'+(tmp % 10);
5119 len = 4; CHECK_SIZE();
5120 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5130 /* let these complicated ones be done with the libc */
5131 snprintf(subfmt, 64, "%%%c", *format);
5132 strftime(subbuf, 64, subfmt, lt);
5133 len = strlen(subbuf); CHECK_SIZE();
5134 strncpy2(curpos, subbuf, buflen - total_done);
5138 /* let these complicated modifiers be done with the libc */
5139 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5140 strftime(subbuf, 64, subfmt, lt);
5141 len = strlen(subbuf); CHECK_SIZE();
5142 strncpy2(curpos, subbuf, buflen - total_done);
5146 if (format && *format)
5147 g_warning("format error (%c)", *format);
5154 int len = 1; CHECK_SIZE();
5155 *curpos++ = *format++;
5162 gboolean prefs_common_get_use_shred(void);
5166 #define WEXITSTATUS(x) (x)
5169 int claws_unlink(const gchar *filename)
5172 static int found_shred = -1;
5173 static const gchar *args[4];
5175 if (filename == NULL)
5178 if (prefs_common_get_use_shred()) {
5179 if (found_shred == -1) {
5181 args[0] = g_find_program_in_path("shred");
5182 debug_print("found shred: %s\n", args[0]);
5183 found_shred = (args[0] != NULL) ? 1:0;
5187 if (found_shred == 1) {
5188 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5189 if (s.st_nlink == 1) {
5192 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5193 NULL, NULL, NULL, NULL, &status, NULL);
5194 debug_print("%s %s exited with status %d\n",
5195 args[0], filename, WEXITSTATUS(status));
5196 if (truncate(filename, 0) < 0)
5197 g_warning("couln't truncate");
5202 return g_unlink(filename);