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
3622 static regex_t u_regex;
3623 static gboolean u_init_;
3625 void utils_free_regex(void)
3633 int subject_get_prefix_length(const gchar *subject)
3635 /*!< Array with allowable reply prefixes regexps. */
3636 static const gchar * const prefixes[] = {
3637 "Re\\:", /* "Re:" */
3638 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3639 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3640 "Aw\\:", /* "Aw:" (German) */
3641 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3642 "Res\\:", /* "Res:" (Brazilian Outlook) */
3643 "Fw\\:", /* "Fw:" Forward */
3644 "Fwd\\:", /* "Fwd:" Forward */
3645 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3646 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3647 "Rif\\:", /* "Rif:" (Italian Outlook) */
3648 "Sv\\:", /* "Sv" (Norwegian) */
3649 "Vs\\:", /* "Vs" (Norwegian) */
3650 "Ad\\:", /* "Ad" (Norwegian) */
3651 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3654 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3658 if (!subject) return 0;
3659 if (!*subject) return 0;
3662 GString *s = g_string_new("");
3664 for (n = 0; n < PREFIXES; n++)
3665 /* Terminate each prefix regexpression by a
3666 * "\ ?" (zero or ONE space), and OR them */
3667 g_string_append_printf(s, "(%s\\ ?)%s",
3672 g_string_prepend(s, "(");
3673 g_string_append(s, ")+"); /* match at least once */
3674 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3677 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3678 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3679 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3680 debug_print("Error compiling regexp %s\n", s->str);
3681 g_string_free(s, TRUE);
3685 g_string_free(s, TRUE);
3689 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3695 static guint g_stricase_hash(gconstpointer gptr)
3697 guint hash_result = 0;
3700 for (str = gptr; str && *str; str++) {
3701 hash_result += toupper(*str);
3707 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3709 const char *str1 = gptr1;
3710 const char *str2 = gptr2;
3712 return !strcasecmp(str1, str2);
3715 gint g_int_compare(gconstpointer a, gconstpointer b)
3717 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3720 gchar *generate_msgid(gchar *buf, gint len)
3730 lt = localtime_r(&t, &buft);
3732 if (strcmp(buf, "") == 0) {
3733 addr = g_strconcat("@", get_domain_name(), NULL);
3736 addr = g_strconcat("@", buf, NULL);
3739 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3740 lt->tm_year + 1900, lt->tm_mon + 1,
3741 lt->tm_mday, lt->tm_hour,
3742 lt->tm_min, lt->tm_sec,
3743 (guint) rand(), addr);
3750 quote_cmd_argument()
3752 return a quoted string safely usable in argument of a command.
3754 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3757 gint quote_cmd_argument(gchar * result, guint size,
3767 for(p = path ; * p != '\0' ; p ++) {
3769 if (isalnum((guchar)*p) || (* p == '/')) {
3770 if (remaining > 0) {
3776 result[size - 1] = '\0';
3781 if (remaining >= 2) {
3789 result[size - 1] = '\0';
3794 if (remaining > 0) {
3798 result[size - 1] = '\0';
3812 static void g_node_map_recursive(GNode *node, gpointer data)
3814 GNodeMapData *mapdata = (GNodeMapData *) data;
3816 GNodeMapData newmapdata;
3819 newdata = mapdata->func(node->data, mapdata->data);
3820 if (newdata != NULL) {
3821 newnode = g_node_new(newdata);
3822 g_node_append(mapdata->parent, newnode);
3824 newmapdata.parent = newnode;
3825 newmapdata.func = mapdata->func;
3826 newmapdata.data = mapdata->data;
3828 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3832 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3835 GNodeMapData mapdata;
3837 g_return_val_if_fail(node != NULL, NULL);
3838 g_return_val_if_fail(func != NULL, NULL);
3840 root = g_node_new(func(node->data, data));
3842 mapdata.parent = root;
3843 mapdata.func = func;
3844 mapdata.data = data;
3846 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3851 #define HEX_TO_INT(val, hex) \
3855 if ('0' <= c && c <= '9') { \
3857 } else if ('a' <= c && c <= 'f') { \
3858 val = c - 'a' + 10; \
3859 } else if ('A' <= c && c <= 'F') { \
3860 val = c - 'A' + 10; \
3866 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3873 if (hi == -1 || lo == -1)
3876 *out = (hi << 4) + lo;
3880 #define INT_TO_HEX(hex, val) \
3883 hex = '0' + (val); \
3885 hex = 'A' + (val) - 10; \
3888 void get_hex_str(gchar *out, guchar ch)
3892 INT_TO_HEX(hex, ch >> 4);
3894 INT_TO_HEX(hex, ch & 0x0f);
3900 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3902 #define G_PRINT_REF g_print
3906 *\brief Register ref counted pointer. It is based on GBoxed, so should
3907 * work with anything that uses the GType system. The semantics
3908 * are similar to a C++ auto pointer, with the exception that
3909 * C doesn't have automatic closure (calling destructors) when
3910 * exiting a block scope.
3911 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3912 * function directly.
3914 *\return GType A GType type.
3916 GType g_auto_pointer_register(void)
3918 static GType auto_pointer_type;
3919 if (!auto_pointer_type)
3921 g_boxed_type_register_static
3922 ("G_TYPE_AUTO_POINTER",
3923 (GBoxedCopyFunc) g_auto_pointer_copy,
3924 (GBoxedFreeFunc) g_auto_pointer_free);
3925 return auto_pointer_type;
3929 *\brief Structure with g_new() allocated pointer guarded by the
3932 typedef struct AutoPointerRef {
3933 void (*free) (gpointer);
3939 *\brief The auto pointer opaque structure that references the
3940 * pointer guard block.
3942 typedef struct AutoPointer {
3943 AutoPointerRef *ref;
3944 gpointer ptr; /*!< access to protected pointer */
3948 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3952 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3953 * ... when assigning, copying and freeing storage elements
3955 * gtk_list_store_new(N_S_COLUMNS,
3956 * G_TYPE_AUTO_POINTER,
3960 * Template *precious_data = g_new0(Template, 1);
3961 * g_pointer protect = g_auto_pointer_new(precious_data);
3963 * gtk_list_store_set(container, &iter,
3967 * ... the gtk_list_store has copied the pointer and
3968 * ... incremented its reference count, we should free
3969 * ... the auto pointer (in C++ a destructor would do
3970 * ... this for us when leaving block scope)
3972 * g_auto_pointer_free(protect);
3974 * ... gtk_list_store_set() now manages the data. When
3975 * ... *explicitly* requesting a pointer from the list
3976 * ... store, don't forget you get a copy that should be
3977 * ... freed with g_auto_pointer_free() eventually.
3981 *\param pointer Pointer to be guarded.
3983 *\return GAuto * Pointer that should be used in containers with
3986 GAuto *g_auto_pointer_new(gpointer p)
3988 AutoPointerRef *ref;
3994 ref = g_new0(AutoPointerRef, 1);
3995 ptr = g_new0(AutoPointer, 1);
4005 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4011 *\brief Allocate an autopointer using the passed \a free function to
4012 * free the guarded pointer
4014 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4021 aptr = g_auto_pointer_new(p);
4022 aptr->ref->free = free_;
4026 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4028 if (auto_ptr == NULL)
4030 return ((AutoPointer *) auto_ptr)->ptr;
4034 *\brief Copies an auto pointer by. It's mostly not necessary
4035 * to call this function directly, unless you copy/assign
4036 * the guarded pointer.
4038 *\param auto_ptr Auto pointer returned by previous call to
4039 * g_auto_pointer_new_XXX()
4041 *\return gpointer An auto pointer
4043 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4046 AutoPointerRef *ref;
4049 if (auto_ptr == NULL)
4054 newp = g_new0(AutoPointer, 1);
4057 newp->ptr = ref->pointer;
4061 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4067 *\brief Free an auto pointer
4069 void g_auto_pointer_free(GAuto *auto_ptr)
4072 AutoPointerRef *ref;
4074 if (auto_ptr == NULL)
4080 if (--(ref->cnt) == 0) {
4082 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4084 ref->free(ref->pointer);
4089 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4094 void replace_returns(gchar *str)
4099 while (strstr(str, "\n")) {
4100 *strstr(str, "\n") = ' ';
4102 while (strstr(str, "\r")) {
4103 *strstr(str, "\r") = ' ';
4107 /* get_uri_part() - retrieves a URI starting from scanpos.
4108 Returns TRUE if succesful */
4109 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4110 const gchar **bp, const gchar **ep, gboolean hdr)
4113 gint parenthese_cnt = 0;
4115 g_return_val_if_fail(start != NULL, FALSE);
4116 g_return_val_if_fail(scanpos != NULL, FALSE);
4117 g_return_val_if_fail(bp != NULL, FALSE);
4118 g_return_val_if_fail(ep != NULL, FALSE);
4122 /* find end point of URI */
4123 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4124 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4125 !IS_ASCII(*(const guchar *)ep_) ||
4126 strchr("[]{}<>\"", *ep_)) {
4128 } else if (strchr("(", *ep_)) {
4130 } else if (strchr(")", *ep_)) {
4131 if (parenthese_cnt > 0)
4138 /* no punctuation at end of string */
4140 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4141 * should pass some URI type to this function and decide on that whether
4142 * to perform punctuation stripping */
4144 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4146 for (; ep_ - 1 > scanpos + 1 &&
4147 IS_REAL_PUNCT(*(ep_ - 1));
4151 #undef IS_REAL_PUNCT
4158 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4160 while (bp && *bp && g_ascii_isspace(*bp))
4162 return g_strndup(bp, ep - bp);
4165 /* valid mail address characters */
4166 #define IS_RFC822_CHAR(ch) \
4170 !g_ascii_isspace(ch) && \
4171 !strchr("(),;<>\"", (ch)))
4173 /* alphabet and number within 7bit ASCII */
4174 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4175 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4177 static GHashTable *create_domain_tab(void)
4179 static const gchar *toplvl_domains [] = {
4181 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4182 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4183 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4184 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4185 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4186 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4187 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4188 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4189 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4190 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4191 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4192 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4193 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4194 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4195 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4196 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4197 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4198 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4199 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4200 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4201 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4202 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4203 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4204 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4205 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4206 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4209 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4211 g_return_val_if_fail(htab, NULL);
4212 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4213 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4217 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4219 const gint MAX_LVL_DOM_NAME_LEN = 6;
4220 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4221 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4224 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4227 for (p = buf; p < m && first < last; *p++ = *first++)
4231 return g_hash_table_lookup(tab, buf) != NULL;
4234 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4235 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4236 const gchar **bp, const gchar **ep, gboolean hdr)
4238 /* more complex than the uri part because we need to scan back and forward starting from
4239 * the scan position. */
4240 gboolean result = FALSE;
4241 const gchar *bp_ = NULL;
4242 const gchar *ep_ = NULL;
4243 static GHashTable *dom_tab;
4244 const gchar *last_dot = NULL;
4245 const gchar *prelast_dot = NULL;
4246 const gchar *last_tld_char = NULL;
4248 /* the informative part of the email address (describing the name
4249 * of the email address owner) may contain quoted parts. the
4250 * closure stack stores the last encountered quotes. */
4251 gchar closure_stack[128];
4252 gchar *ptr = closure_stack;
4254 g_return_val_if_fail(start != NULL, FALSE);
4255 g_return_val_if_fail(scanpos != NULL, FALSE);
4256 g_return_val_if_fail(bp != NULL, FALSE);
4257 g_return_val_if_fail(ep != NULL, FALSE);
4260 const gchar *start_quote = NULL;
4261 const gchar *end_quote = NULL;
4263 /* go to the real start */
4264 if (start[0] == ',')
4266 if (start[0] == ';')
4268 while (start[0] == '\n' || start[0] == '\r')
4270 while (start[0] == ' ' || start[0] == '\t')
4275 /* check if there are quotes (to skip , in them) */
4276 if (*start == '"') {
4277 start_quote = start;
4279 end_quote = strstr(start, "\"");
4285 /* skip anything between quotes */
4286 if (start_quote && end_quote) {
4291 /* find end (either , or ; or end of line) */
4292 if (strstr(start, ",") && strstr(start, ";"))
4293 *ep = strstr(start,",") < strstr(start, ";")
4294 ? strstr(start, ",") : strstr(start, ";");
4295 else if (strstr(start, ","))
4296 *ep = strstr(start, ",");
4297 else if (strstr(start, ";"))
4298 *ep = strstr(start, ";");
4300 *ep = start+strlen(start);
4302 /* go back to real start */
4303 if (start_quote && end_quote) {
4304 start = start_quote;
4307 /* check there's still an @ in that, or search
4308 * further if possible */
4309 if (strstr(start, "@") && strstr(start, "@") < *ep)
4311 else if (*ep < start+strlen(start)) {
4314 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4322 dom_tab = create_domain_tab();
4323 g_return_val_if_fail(dom_tab, FALSE);
4325 /* scan start of address */
4326 for (bp_ = scanpos - 1;
4327 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4330 /* TODO: should start with an alnum? */
4332 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4335 if (bp_ != scanpos) {
4336 /* scan end of address */
4337 for (ep_ = scanpos + 1;
4338 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4340 prelast_dot = last_dot;
4342 if (*(last_dot + 1) == '.') {
4343 if (prelast_dot == NULL)
4345 last_dot = prelast_dot;
4350 /* TODO: really should terminate with an alnum? */
4351 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4356 if (last_dot == NULL)
4358 if (last_dot >= ep_)
4359 last_dot = prelast_dot;
4360 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4364 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4365 if (*last_tld_char == '?')
4368 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4375 if (!result) return FALSE;
4377 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4378 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4379 && IS_RFC822_CHAR(*(ep_ + 3))) {
4380 /* this informative part with an @ in it is
4381 * followed by the email address */
4384 /* go to matching '>' (or next non-rfc822 char, like \n) */
4385 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4388 /* include the bracket */
4389 if (*ep_ == '>') ep_++;
4391 /* include the leading quote */
4399 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4400 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4403 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4404 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4407 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4408 #define IN_STACK() (ptr > closure_stack)
4409 /* has underrun check */
4410 #define POP_STACK() if(IN_STACK()) --ptr
4411 /* has overrun check */
4412 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4413 /* has underrun check */
4414 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4418 /* scan for the informative part. */
4419 for (bp_ -= 2; bp_ >= start; bp_--) {
4420 /* if closure on the stack keep scanning */
4421 if (PEEK_STACK() == *bp_) {
4425 if (*bp_ == '\'' || *bp_ == '"') {
4430 /* if nothing in the closure stack, do the special conditions
4431 * the following if..else expression simply checks whether
4432 * a token is acceptable. if not acceptable, the clause
4433 * should terminate the loop with a 'break' */
4434 if (!PEEK_STACK()) {
4436 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4437 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4438 /* hyphens are allowed, but only in
4440 } else if (strchr(" \"'", *bp_)) {
4441 /* but anything not being a punctiation
4444 break; /* anything else is rejected */
4451 /* scan forward (should start with an alnum) */
4452 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4468 #undef IS_ASCII_ALNUM
4469 #undef IS_RFC822_CHAR
4471 gchar *make_email_string(const gchar *bp, const gchar *ep)
4473 /* returns a mailto: URI; mailto: is also used to detect the
4474 * uri type later on in the button_pressed signal handler */
4478 tmp = g_strndup(bp, ep - bp);
4479 result = g_strconcat("mailto:", tmp, NULL);
4485 gchar *make_http_string(const gchar *bp, const gchar *ep)
4487 /* returns an http: URI; */
4491 while (bp && *bp && g_ascii_isspace(*bp))
4493 tmp = g_strndup(bp, ep - bp);
4494 result = g_strconcat("http://", tmp, NULL);
4500 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4502 FILE *fp = fopen(path, "rb");
4503 gchar buf[BUFFSIZE];
4504 gchar *result = NULL;
4507 while (fgets(buf, sizeof (buf), fp) != NULL) {
4508 gchar **parts = g_strsplit(buf, ";", 3);
4509 gchar *trimmed = parts[0];
4510 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4512 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4513 trimmed[strlen(trimmed)-1] = '\0';
4515 if (!strcmp(trimmed, type)) {
4516 gboolean needsterminal = FALSE;
4517 if (parts[2] && strstr(parts[2], "needsterminal")) {
4518 needsterminal = TRUE;
4520 if (parts[2] && strstr(parts[2], "test=")) {
4521 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4522 gchar *testcmd = orig_testcmd;
4523 if (strstr(testcmd,";"))
4524 *(strstr(testcmd,";")) = '\0';
4525 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4527 while (testcmd[strlen(testcmd)-1] == '\n')
4528 testcmd[strlen(testcmd)-1] = '\0';
4529 while (testcmd[strlen(testcmd)-1] == '\r')
4530 testcmd[strlen(testcmd)-1] = '\0';
4531 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4532 testcmd[strlen(testcmd)-1] = '\0';
4534 if (strstr(testcmd, "%s")) {
4535 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4536 gint res = system(tmp);
4538 g_free(orig_testcmd);
4545 gint res = system(testcmd);
4546 g_free(orig_testcmd);
4556 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4558 while (trimmed[strlen(trimmed)-1] == '\n')
4559 trimmed[strlen(trimmed)-1] = '\0';
4560 while (trimmed[strlen(trimmed)-1] == '\r')
4561 trimmed[strlen(trimmed)-1] = '\0';
4562 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4563 trimmed[strlen(trimmed)-1] = '\0';
4564 result = g_strdup(trimmed);
4567 /* if there are no single quotes around %s, add them.
4568 * '.*%s.*' is ok, as in display 'png:%s'
4570 if (strstr(result, "%s")
4571 && !(strstr(result, "'") < strstr(result,"%s") &&
4572 strstr(strstr(result,"%s"), "'"))) {
4573 gchar *start = g_strdup(result);
4574 gchar *end = g_strdup(strstr(result, "%s")+2);
4576 *strstr(start, "%s") = '\0';
4577 tmp = g_strconcat(start,"'%s'",end, NULL);
4583 if (needsterminal) {
4584 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4595 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4597 gchar *result = NULL;
4601 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4602 result = mailcap_get_command_in_file(path, type, file_to_open);
4606 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4610 void mailcap_update_default(const gchar *type, const gchar *command)
4612 gchar *path = NULL, *outpath = NULL;
4613 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4614 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4615 FILE *fp = fopen(path, "rb");
4616 FILE *outfp = fopen(outpath, "wb");
4617 gchar buf[BUFFSIZE];
4618 gboolean err = FALSE;
4626 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4627 gchar **parts = g_strsplit(buf, ";", 3);
4628 gchar *trimmed = parts[0];
4629 while (trimmed[0] == ' ')
4631 while (trimmed[strlen(trimmed)-1] == ' ')
4632 trimmed[strlen(trimmed)-1] = '\0';
4634 if (!strcmp(trimmed, type)) {
4639 if(fputs(buf, outfp) == EOF) {
4646 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4652 if (fclose(outfp) == EOF)
4656 g_rename(outpath, path);
4662 gint copy_dir(const gchar *src, const gchar *dst)
4667 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4668 g_warning("failed to open directory: %s\n", src);
4672 if (make_dir(dst) < 0)
4675 while ((name = g_dir_read_name(dir)) != NULL) {
4676 gchar *old_file, *new_file;
4677 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4678 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4679 debug_print("copying: %s -> %s\n", old_file, new_file);
4680 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4681 gint r = copy_file(old_file, new_file, TRUE);
4688 /* Windows has no symlinks. Or well, Vista seems to
4689 have something like this but the semantics might be
4690 different. Thus we don't use it under Windows. */
4691 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4694 gchar *target = g_file_read_link(old_file, &error);
4696 r = symlink(target, new_file);
4703 #endif /*G_OS_WIN32*/
4704 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4705 gint r = copy_dir(old_file, new_file);
4716 /* crude test to see if a file is an email. */
4717 gboolean file_is_email (const gchar *filename)
4723 if (filename == NULL)
4725 if ((fp = g_fopen(filename, "rb")) == NULL)
4727 while (i < 60 && score < 3
4728 && fgets(buffer, sizeof (buffer), fp) > 0) {
4729 if (!strncmp(buffer, "From:", strlen("From:")))
4731 if (!strncmp(buffer, "To:", strlen("To:")))
4733 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4738 return (score >= 3);
4741 gboolean sc_g_list_bigger(GList *list, gint max)
4745 while (cur && i <= max+1) {
4752 gboolean sc_g_slist_bigger(GSList *list, gint max)
4756 while (cur && i <= max+1) {
4763 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4764 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4765 NULL, NULL, NULL, NULL, NULL, NULL};
4766 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4767 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4768 NULL, NULL, NULL, NULL, NULL, NULL};
4770 gint daynames_len[] = {0,0,0,0,0,0,0};
4771 gint monthnames_len[] = {0,0,0,0,0,0,
4773 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4774 gint s_monthnames_len[] = {0,0,0,0,0,0,
4776 const gchar *s_am_up = NULL;
4777 const gchar *s_pm_up = NULL;
4778 const gchar *s_am_low = NULL;
4779 const gchar *s_pm_low = NULL;
4781 gint s_am_up_len = 0;
4782 gint s_pm_up_len = 0;
4783 gint s_am_low_len = 0;
4784 gint s_pm_low_len = 0;
4786 const gchar *def_loc_format = NULL;
4787 const gchar *date_loc_format = NULL;
4788 const gchar *time_loc_format = NULL;
4789 const gchar *time_am_pm = NULL;
4791 static gboolean time_names_init_done = FALSE;
4793 static void init_time_names(void)
4797 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4798 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4799 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4800 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4801 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4802 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4803 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4805 monthnames[0] = Q_("Complete month name for use by strftime|January");
4806 monthnames[1] = Q_("Complete month name for use by strftime|February");
4807 monthnames[2] = Q_("Complete month name for use by strftime|March");
4808 monthnames[3] = Q_("Complete month name for use by strftime|April");
4809 monthnames[4] = Q_("Complete month name for use by strftime|May");
4810 monthnames[5] = Q_("Complete month name for use by strftime|June");
4811 monthnames[6] = Q_("Complete month name for use by strftime|July");
4812 monthnames[7] = Q_("Complete month name for use by strftime|August");
4813 monthnames[8] = Q_("Complete month name for use by strftime|September");
4814 monthnames[9] = Q_("Complete month name for use by strftime|October");
4815 monthnames[10] = Q_("Complete month name for use by strftime|November");
4816 monthnames[11] = Q_("Complete month name for use by strftime|December");
4818 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4819 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4820 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4821 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4822 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4823 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4824 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4826 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4827 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4828 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4829 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4830 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4831 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4832 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4833 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4834 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4835 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4836 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4837 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4839 for (i = 0; i < 7; i++) {
4840 daynames_len[i] = strlen(daynames[i]);
4841 s_daynames_len[i] = strlen(s_daynames[i]);
4843 for (i = 0; i < 12; i++) {
4844 monthnames_len[i] = strlen(monthnames[i]);
4845 s_monthnames_len[i] = strlen(s_monthnames[i]);
4848 s_am_up = Q_("For use by strftime (morning)|AM");
4849 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4850 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4851 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4853 s_am_up_len = strlen(s_am_up);
4854 s_pm_up_len = strlen(s_pm_up);
4855 s_am_low_len = strlen(s_am_low);
4856 s_pm_low_len = strlen(s_pm_low);
4858 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4859 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4860 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4862 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4864 time_names_init_done = TRUE;
4867 #define CHECK_SIZE() { \
4868 total_done += len; \
4869 if (total_done >= buflen) { \
4870 buf[buflen-1] = '\0'; \
4875 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4877 gchar *curpos = buf;
4878 gint total_done = 0;
4879 gchar subbuf[64], subfmt[64];
4880 static time_t last_tzset = (time_t)0;
4882 if (!time_names_init_done)
4885 if (format == NULL || lt == NULL)
4888 if (last_tzset != time(NULL)) {
4890 last_tzset = time(NULL);
4893 if (*format == '%') {
4894 gint len = 0, tmp = 0;
4898 len = 1; CHECK_SIZE();
4902 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4903 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4906 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4907 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4911 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4912 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4915 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4916 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4919 fast_strftime(subbuf, 64, def_loc_format, lt);
4920 len = strlen(subbuf); CHECK_SIZE();
4921 strncpy2(curpos, subbuf, buflen - total_done);
4924 total_done += 2; CHECK_SIZE();
4925 tmp = (lt->tm_year + 1900)/100;
4926 *curpos++ = '0'+(tmp / 10);
4927 *curpos++ = '0'+(tmp % 10);
4930 total_done += 2; CHECK_SIZE();
4931 *curpos++ = '0'+(lt->tm_mday / 10);
4932 *curpos++ = '0'+(lt->tm_mday % 10);
4935 total_done += 8; CHECK_SIZE();
4936 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4937 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4939 *curpos++ = '0'+(lt->tm_mday / 10);
4940 *curpos++ = '0'+(lt->tm_mday % 10);
4942 tmp = lt->tm_year%100;
4943 *curpos++ = '0'+(tmp / 10);
4944 *curpos++ = '0'+(tmp % 10);
4947 len = 2; CHECK_SIZE();
4948 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4951 len = 10; CHECK_SIZE();
4952 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4953 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4956 total_done += 2; CHECK_SIZE();
4957 *curpos++ = '0'+(lt->tm_hour / 10);
4958 *curpos++ = '0'+(lt->tm_hour % 10);
4961 total_done += 2; CHECK_SIZE();
4967 *curpos++ = '0'+(tmp / 10);
4968 *curpos++ = '0'+(tmp % 10);
4971 len = 3; CHECK_SIZE();
4972 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4975 len = 2; CHECK_SIZE();
4976 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4979 len = 2; CHECK_SIZE();
4985 snprintf(curpos, buflen - total_done, "%2d", tmp);
4988 total_done += 2; CHECK_SIZE();
4989 tmp = lt->tm_mon + 1;
4990 *curpos++ = '0'+(tmp / 10);
4991 *curpos++ = '0'+(tmp % 10);
4994 total_done += 2; CHECK_SIZE();
4995 *curpos++ = '0'+(lt->tm_min / 10);
4996 *curpos++ = '0'+(lt->tm_min % 10);
4999 len = 1; CHECK_SIZE();
5003 if (lt->tm_hour >= 12) {
5004 len = s_pm_up_len; CHECK_SIZE();
5005 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5007 len = s_am_up_len; CHECK_SIZE();
5008 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5012 if (lt->tm_hour >= 12) {
5013 len = s_pm_low_len; CHECK_SIZE();
5014 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5016 len = s_am_low_len; CHECK_SIZE();
5017 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5021 fast_strftime(subbuf, 64, time_am_pm, lt);
5022 len = strlen(subbuf); CHECK_SIZE();
5023 strncpy2(curpos, subbuf, buflen - total_done);
5026 total_done += 5; CHECK_SIZE();
5027 *curpos++ = '0'+(lt->tm_hour / 10);
5028 *curpos++ = '0'+(lt->tm_hour % 10);
5030 *curpos++ = '0'+(lt->tm_min / 10);
5031 *curpos++ = '0'+(lt->tm_min % 10);
5034 snprintf(subbuf, 64, "%ld", mktime(lt));
5035 len = strlen(subbuf); CHECK_SIZE();
5036 strncpy2(curpos, subbuf, buflen - total_done);
5039 total_done += 2; CHECK_SIZE();
5040 *curpos++ = '0'+(lt->tm_sec / 10);
5041 *curpos++ = '0'+(lt->tm_sec % 10);
5044 len = 1; CHECK_SIZE();
5048 total_done += 8; CHECK_SIZE();
5049 *curpos++ = '0'+(lt->tm_hour / 10);
5050 *curpos++ = '0'+(lt->tm_hour % 10);
5052 *curpos++ = '0'+(lt->tm_min / 10);
5053 *curpos++ = '0'+(lt->tm_min % 10);
5055 *curpos++ = '0'+(lt->tm_sec / 10);
5056 *curpos++ = '0'+(lt->tm_sec % 10);
5059 len = 1; CHECK_SIZE();
5060 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5063 len = 1; CHECK_SIZE();
5064 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5067 fast_strftime(subbuf, 64, date_loc_format, lt);
5068 len = strlen(subbuf); CHECK_SIZE();
5069 strncpy2(curpos, subbuf, buflen - total_done);
5072 fast_strftime(subbuf, 64, time_loc_format, lt);
5073 len = strlen(subbuf); CHECK_SIZE();
5074 strncpy2(curpos, subbuf, buflen - total_done);
5077 total_done += 2; CHECK_SIZE();
5078 tmp = lt->tm_year%100;
5079 *curpos++ = '0'+(tmp / 10);
5080 *curpos++ = '0'+(tmp % 10);
5083 len = 4; CHECK_SIZE();
5084 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5094 /* let these complicated ones be done with the libc */
5095 snprintf(subfmt, 64, "%%%c", *format);
5096 strftime(subbuf, 64, subfmt, lt);
5097 len = strlen(subbuf); CHECK_SIZE();
5098 strncpy2(curpos, subbuf, buflen - total_done);
5102 /* let these complicated modifiers be done with the libc */
5103 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5104 strftime(subbuf, 64, subfmt, lt);
5105 len = strlen(subbuf); CHECK_SIZE();
5106 strncpy2(curpos, subbuf, buflen - total_done);
5110 if (format && *format)
5111 g_warning("format error (%c)", *format);
5118 int len = 1; CHECK_SIZE();
5119 *curpos++ = *format++;
5126 gboolean prefs_common_get_use_shred(void);
5130 #define WEXITSTATUS(x) (x)
5133 int claws_unlink(const gchar *filename)
5136 static int found_shred = -1;
5137 static const gchar *args[4];
5139 if (filename == NULL)
5142 if (prefs_common_get_use_shred()) {
5143 if (found_shred == -1) {
5145 args[0] = g_find_program_in_path("shred");
5146 debug_print("found shred: %s\n", args[0]);
5147 found_shred = (args[0] != NULL) ? 1:0;
5151 if (found_shred == 1) {
5152 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5153 if (s.st_nlink == 1) {
5156 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5157 NULL, NULL, NULL, NULL, &status, NULL);
5158 debug_print("%s %s exited with status %d\n",
5159 args[0], filename, WEXITSTATUS(status));
5160 if (truncate(filename, 0) < 0)
5161 g_warning("couln't truncate");
5166 return g_unlink(filename);