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)
3335 gchar buf[BUFFSIZE];
3337 gchar encoded_uri[BUFFSIZE];
3338 g_return_val_if_fail(uri != NULL, -1);
3340 /* an option to choose whether to use encode_uri or not ? */
3341 encode_uri(encoded_uri, BUFFSIZE, uri);
3344 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3345 !strchr(p + 2, '%'))
3346 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3349 g_warning("Open URI command-line is invalid "
3350 "(there must be only one '%%s'): %s",
3352 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3355 execute_command_line(buf, TRUE);
3357 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3360 extern osso_context_t *get_osso_context(void);
3361 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3362 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3363 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3368 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3370 gchar buf[BUFFSIZE];
3373 g_return_val_if_fail(filepath != NULL, -1);
3376 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3377 !strchr(p + 2, '%'))
3378 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3381 g_warning("Open Text Editor command-line is invalid "
3382 "(there must be only one '%%s'): %s",
3384 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3387 execute_command_line(buf, TRUE);
3392 time_t remote_tzoffset_sec(const gchar *zone)
3394 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3400 time_t remoteoffset;
3402 strncpy(zone3, zone, 3);
3406 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3407 (c == '+' || c == '-')) {
3408 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3410 remoteoffset = -remoteoffset;
3411 } else if (!strncmp(zone, "UT" , 2) ||
3412 !strncmp(zone, "GMT", 2)) {
3414 } else if (strlen(zone3) == 3) {
3415 for (p = ustzstr; *p != '\0'; p += 3) {
3416 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3417 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3418 remoteoffset = iustz * 3600;
3424 } else if (strlen(zone3) == 1) {
3426 case 'Z': remoteoffset = 0; break;
3427 case 'A': remoteoffset = -1; break;
3428 case 'B': remoteoffset = -2; break;
3429 case 'C': remoteoffset = -3; break;
3430 case 'D': remoteoffset = -4; break;
3431 case 'E': remoteoffset = -5; break;
3432 case 'F': remoteoffset = -6; break;
3433 case 'G': remoteoffset = -7; break;
3434 case 'H': remoteoffset = -8; break;
3435 case 'I': remoteoffset = -9; break;
3436 case 'K': remoteoffset = -10; break; /* J is not used */
3437 case 'L': remoteoffset = -11; break;
3438 case 'M': remoteoffset = -12; break;
3439 case 'N': remoteoffset = 1; break;
3440 case 'O': remoteoffset = 2; break;
3441 case 'P': remoteoffset = 3; break;
3442 case 'Q': remoteoffset = 4; break;
3443 case 'R': remoteoffset = 5; break;
3444 case 'S': remoteoffset = 6; break;
3445 case 'T': remoteoffset = 7; break;
3446 case 'U': remoteoffset = 8; break;
3447 case 'V': remoteoffset = 9; break;
3448 case 'W': remoteoffset = 10; break;
3449 case 'X': remoteoffset = 11; break;
3450 case 'Y': remoteoffset = 12; break;
3451 default: remoteoffset = 0; break;
3453 remoteoffset = remoteoffset * 3600;
3457 return remoteoffset;
3460 time_t tzoffset_sec(time_t *now)
3465 struct tm buf1, buf2;
3468 if (now && *now < 0)
3471 gmt = *gmtime_r(now, &buf1);
3472 lt = localtime_r(now, &buf2);
3474 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3476 if (lt->tm_year < gmt.tm_year)
3478 else if (lt->tm_year > gmt.tm_year)
3480 else if (lt->tm_yday < gmt.tm_yday)
3482 else if (lt->tm_yday > gmt.tm_yday)
3485 if (off >= 24 * 60) /* should be impossible */
3486 off = 23 * 60 + 59; /* if not, insert silly value */
3487 if (off <= -24 * 60)
3488 off = -(23 * 60 + 59);
3493 /* calculate timezone offset */
3494 gchar *tzoffset(time_t *now)
3496 static gchar offset_string[6];
3501 struct tm buf1, buf2;
3504 if (now && *now < 0)
3507 gmt = *gmtime_r(now, &buf1);
3508 lt = localtime_r(now, &buf2);
3510 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3512 if (lt->tm_year < gmt.tm_year)
3514 else if (lt->tm_year > gmt.tm_year)
3516 else if (lt->tm_yday < gmt.tm_yday)
3518 else if (lt->tm_yday > gmt.tm_yday)
3526 if (off >= 24 * 60) /* should be impossible */
3527 off = 23 * 60 + 59; /* if not, insert silly value */
3529 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3531 return offset_string;
3534 void get_rfc822_date(gchar *buf, gint len)
3538 gchar day[4], mon[4];
3539 gint dd, hh, mm, ss, yyyy;
3542 gchar buf2[BUFFSIZE];
3546 lt = localtime_r(&t, &buf1);
3548 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3549 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3551 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3552 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3555 void debug_set_mode(gboolean mode)
3560 gboolean debug_get_mode(void)
3565 void debug_print_real(const gchar *format, ...)
3568 gchar buf[BUFFSIZE];
3570 if (!debug_mode) return;
3572 va_start(args, format);
3573 g_vsnprintf(buf, sizeof(buf), format, args);
3580 const char * debug_srcname(const char *file)
3582 const char *s = strrchr (file, '/');
3587 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3589 if (subject == NULL)
3592 subject += subject_get_prefix_length(subject);
3594 return g_hash_table_lookup(subject_table, subject);
3597 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3600 if (subject == NULL || *subject == 0)
3602 subject += subject_get_prefix_length(subject);
3603 g_hash_table_insert(subject_table, subject, data);
3606 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3608 if (subject == NULL)
3611 subject += subject_get_prefix_length(subject);
3612 g_hash_table_remove(subject_table, subject);
3616 *\brief Check if a string is prefixed with known (combinations)
3617 * of prefixes. The function assumes that each prefix
3618 * is terminated by zero or exactly _one_ space.
3620 *\param str String to check for a prefixes
3622 *\return int Number of chars in the prefix that should be skipped
3623 * for a "clean" subject line. If no prefix was found, 0
3628 static regex_t u_regex;
3629 static gboolean u_init_;
3632 void utils_free_regex(void)
3642 int subject_get_prefix_length(const gchar *subject)
3645 /*!< Array with allowable reply prefixes regexps. */
3646 static const gchar * const prefixes[] = {
3647 "Re\\:", /* "Re:" */
3648 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3649 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3650 "Aw\\:", /* "Aw:" (German) */
3651 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3652 "Res\\:", /* "Res:" (Brazilian Outlook) */
3653 "Fw\\:", /* "Fw:" Forward */
3654 "Fwd\\:", /* "Fwd:" Forward */
3655 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3656 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3657 "Rif\\:", /* "Rif:" (Italian Outlook) */
3658 "Sv\\:", /* "Sv" (Norwegian) */
3659 "Vs\\:", /* "Vs" (Norwegian) */
3660 "Ad\\:", /* "Ad" (Norwegian) */
3661 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3664 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3668 if (!subject) return 0;
3669 if (!*subject) return 0;
3672 GString *s = g_string_new("");
3674 for (n = 0; n < PREFIXES; n++)
3675 /* Terminate each prefix regexpression by a
3676 * "\ ?" (zero or ONE space), and OR them */
3677 g_string_append_printf(s, "(%s\\ ?)%s",
3682 g_string_prepend(s, "(");
3683 g_string_append(s, ")+"); /* match at least once */
3684 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3687 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3688 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3689 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3690 debug_print("Error compiling regexp %s\n", s->str);
3691 g_string_free(s, TRUE);
3695 g_string_free(s, TRUE);
3699 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3704 /*!< Array with allowable reply prefixes regexps. */
3705 static const gchar * const prefixes[] = {
3707 "antw:", /* "Antw:" (Dutch / German Outlook) */
3708 "aw:", /* "Aw:" (German) */
3709 "antwort:", /* "Antwort:" (German Lotus Notes) */
3710 "res:", /* "Res:" (Brazilian Outlook) */
3711 "fw:", /* "Fw:" Forward */
3712 "fwd:", /* "Fwd:" Forward */
3713 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3714 "odp:", /* "Odp:" Re (Polish Outlook) */
3715 "rif:", /* "Rif:" (Italian Outlook) */
3716 "sv:", /* "Sv" (Norwegian) */
3717 "vs:", /* "Vs" (Norwegian) */
3718 "ad:", /* "Ad" (Norwegian) */
3721 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3724 if (!subject) return 0;
3725 if (!*subject) return 0;
3727 for (n = 0; n < PREFIXES; n++) {
3728 int len = strlen(prefixes[n]);
3729 if (!strncasecmp(subject, prefixes[n], len))
3735 static guint g_stricase_hash(gconstpointer gptr)
3737 guint hash_result = 0;
3740 for (str = gptr; str && *str; str++) {
3741 hash_result += toupper(*str);
3747 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3749 const char *str1 = gptr1;
3750 const char *str2 = gptr2;
3752 return !strcasecmp(str1, str2);
3755 gint g_int_compare(gconstpointer a, gconstpointer b)
3757 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3760 gchar *generate_msgid(gchar *buf, gint len)
3770 lt = localtime_r(&t, &buft);
3772 if (strcmp(buf, "") == 0) {
3773 addr = g_strconcat("@", get_domain_name(), NULL);
3776 addr = g_strconcat("@", buf, NULL);
3779 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3780 lt->tm_year + 1900, lt->tm_mon + 1,
3781 lt->tm_mday, lt->tm_hour,
3782 lt->tm_min, lt->tm_sec,
3783 (guint) rand(), addr);
3790 quote_cmd_argument()
3792 return a quoted string safely usable in argument of a command.
3794 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3797 gint quote_cmd_argument(gchar * result, guint size,
3807 for(p = path ; * p != '\0' ; p ++) {
3809 if (isalnum((guchar)*p) || (* p == '/')) {
3810 if (remaining > 0) {
3816 result[size - 1] = '\0';
3821 if (remaining >= 2) {
3829 result[size - 1] = '\0';
3834 if (remaining > 0) {
3838 result[size - 1] = '\0';
3852 static void g_node_map_recursive(GNode *node, gpointer data)
3854 GNodeMapData *mapdata = (GNodeMapData *) data;
3856 GNodeMapData newmapdata;
3859 newdata = mapdata->func(node->data, mapdata->data);
3860 if (newdata != NULL) {
3861 newnode = g_node_new(newdata);
3862 g_node_append(mapdata->parent, newnode);
3864 newmapdata.parent = newnode;
3865 newmapdata.func = mapdata->func;
3866 newmapdata.data = mapdata->data;
3868 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3872 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3875 GNodeMapData mapdata;
3877 g_return_val_if_fail(node != NULL, NULL);
3878 g_return_val_if_fail(func != NULL, NULL);
3880 root = g_node_new(func(node->data, data));
3882 mapdata.parent = root;
3883 mapdata.func = func;
3884 mapdata.data = data;
3886 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3891 #define HEX_TO_INT(val, hex) \
3895 if ('0' <= c && c <= '9') { \
3897 } else if ('a' <= c && c <= 'f') { \
3898 val = c - 'a' + 10; \
3899 } else if ('A' <= c && c <= 'F') { \
3900 val = c - 'A' + 10; \
3906 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3913 if (hi == -1 || lo == -1)
3916 *out = (hi << 4) + lo;
3920 #define INT_TO_HEX(hex, val) \
3923 hex = '0' + (val); \
3925 hex = 'A' + (val) - 10; \
3928 void get_hex_str(gchar *out, guchar ch)
3932 INT_TO_HEX(hex, ch >> 4);
3934 INT_TO_HEX(hex, ch & 0x0f);
3940 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3942 #define G_PRINT_REF g_print
3946 *\brief Register ref counted pointer. It is based on GBoxed, so should
3947 * work with anything that uses the GType system. The semantics
3948 * are similar to a C++ auto pointer, with the exception that
3949 * C doesn't have automatic closure (calling destructors) when
3950 * exiting a block scope.
3951 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3952 * function directly.
3954 *\return GType A GType type.
3956 GType g_auto_pointer_register(void)
3958 static GType auto_pointer_type;
3959 if (!auto_pointer_type)
3961 g_boxed_type_register_static
3962 ("G_TYPE_AUTO_POINTER",
3963 (GBoxedCopyFunc) g_auto_pointer_copy,
3964 (GBoxedFreeFunc) g_auto_pointer_free);
3965 return auto_pointer_type;
3969 *\brief Structure with g_new() allocated pointer guarded by the
3972 typedef struct AutoPointerRef {
3973 void (*free) (gpointer);
3979 *\brief The auto pointer opaque structure that references the
3980 * pointer guard block.
3982 typedef struct AutoPointer {
3983 AutoPointerRef *ref;
3984 gpointer ptr; /*!< access to protected pointer */
3988 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3992 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3993 * ... when assigning, copying and freeing storage elements
3995 * gtk_list_store_new(N_S_COLUMNS,
3996 * G_TYPE_AUTO_POINTER,
4000 * Template *precious_data = g_new0(Template, 1);
4001 * g_pointer protect = g_auto_pointer_new(precious_data);
4003 * gtk_list_store_set(container, &iter,
4007 * ... the gtk_list_store has copied the pointer and
4008 * ... incremented its reference count, we should free
4009 * ... the auto pointer (in C++ a destructor would do
4010 * ... this for us when leaving block scope)
4012 * g_auto_pointer_free(protect);
4014 * ... gtk_list_store_set() now manages the data. When
4015 * ... *explicitly* requesting a pointer from the list
4016 * ... store, don't forget you get a copy that should be
4017 * ... freed with g_auto_pointer_free() eventually.
4021 *\param pointer Pointer to be guarded.
4023 *\return GAuto * Pointer that should be used in containers with
4026 GAuto *g_auto_pointer_new(gpointer p)
4028 AutoPointerRef *ref;
4034 ref = g_new0(AutoPointerRef, 1);
4035 ptr = g_new0(AutoPointer, 1);
4045 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4051 *\brief Allocate an autopointer using the passed \a free function to
4052 * free the guarded pointer
4054 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4061 aptr = g_auto_pointer_new(p);
4062 aptr->ref->free = free_;
4066 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4068 if (auto_ptr == NULL)
4070 return ((AutoPointer *) auto_ptr)->ptr;
4074 *\brief Copies an auto pointer by. It's mostly not necessary
4075 * to call this function directly, unless you copy/assign
4076 * the guarded pointer.
4078 *\param auto_ptr Auto pointer returned by previous call to
4079 * g_auto_pointer_new_XXX()
4081 *\return gpointer An auto pointer
4083 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4086 AutoPointerRef *ref;
4089 if (auto_ptr == NULL)
4094 newp = g_new0(AutoPointer, 1);
4097 newp->ptr = ref->pointer;
4101 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4107 *\brief Free an auto pointer
4109 void g_auto_pointer_free(GAuto *auto_ptr)
4112 AutoPointerRef *ref;
4114 if (auto_ptr == NULL)
4120 if (--(ref->cnt) == 0) {
4122 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4124 ref->free(ref->pointer);
4129 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4134 void replace_returns(gchar *str)
4139 while (strstr(str, "\n")) {
4140 *strstr(str, "\n") = ' ';
4142 while (strstr(str, "\r")) {
4143 *strstr(str, "\r") = ' ';
4147 /* get_uri_part() - retrieves a URI starting from scanpos.
4148 Returns TRUE if succesful */
4149 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4150 const gchar **bp, const gchar **ep, gboolean hdr)
4153 gint parenthese_cnt = 0;
4155 g_return_val_if_fail(start != NULL, FALSE);
4156 g_return_val_if_fail(scanpos != NULL, FALSE);
4157 g_return_val_if_fail(bp != NULL, FALSE);
4158 g_return_val_if_fail(ep != NULL, FALSE);
4162 /* find end point of URI */
4163 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4164 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4165 !IS_ASCII(*(const guchar *)ep_) ||
4166 strchr("[]{}<>\"", *ep_)) {
4168 } else if (strchr("(", *ep_)) {
4170 } else if (strchr(")", *ep_)) {
4171 if (parenthese_cnt > 0)
4178 /* no punctuation at end of string */
4180 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4181 * should pass some URI type to this function and decide on that whether
4182 * to perform punctuation stripping */
4184 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4186 for (; ep_ - 1 > scanpos + 1 &&
4187 IS_REAL_PUNCT(*(ep_ - 1));
4191 #undef IS_REAL_PUNCT
4198 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4200 while (bp && *bp && g_ascii_isspace(*bp))
4202 return g_strndup(bp, ep - bp);
4205 /* valid mail address characters */
4206 #define IS_RFC822_CHAR(ch) \
4210 !g_ascii_isspace(ch) && \
4211 !strchr("(),;<>\"", (ch)))
4213 /* alphabet and number within 7bit ASCII */
4214 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4215 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4217 static GHashTable *create_domain_tab(void)
4219 static const gchar *toplvl_domains [] = {
4221 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4222 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4223 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4224 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4225 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4226 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4227 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4228 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4229 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4230 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4231 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4232 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4233 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4234 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4235 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4236 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4237 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4238 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4239 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4240 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4241 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4242 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4243 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4244 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4245 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4246 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4249 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4251 g_return_val_if_fail(htab, NULL);
4252 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4253 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4257 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4259 const gint MAX_LVL_DOM_NAME_LEN = 6;
4260 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4261 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4264 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4267 for (p = buf; p < m && first < last; *p++ = *first++)
4271 return g_hash_table_lookup(tab, buf) != NULL;
4274 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4275 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4276 const gchar **bp, const gchar **ep, gboolean hdr)
4278 /* more complex than the uri part because we need to scan back and forward starting from
4279 * the scan position. */
4280 gboolean result = FALSE;
4281 const gchar *bp_ = NULL;
4282 const gchar *ep_ = NULL;
4283 static GHashTable *dom_tab;
4284 const gchar *last_dot = NULL;
4285 const gchar *prelast_dot = NULL;
4286 const gchar *last_tld_char = NULL;
4288 /* the informative part of the email address (describing the name
4289 * of the email address owner) may contain quoted parts. the
4290 * closure stack stores the last encountered quotes. */
4291 gchar closure_stack[128];
4292 gchar *ptr = closure_stack;
4294 g_return_val_if_fail(start != NULL, FALSE);
4295 g_return_val_if_fail(scanpos != NULL, FALSE);
4296 g_return_val_if_fail(bp != NULL, FALSE);
4297 g_return_val_if_fail(ep != NULL, FALSE);
4300 const gchar *start_quote = NULL;
4301 const gchar *end_quote = NULL;
4303 /* go to the real start */
4304 if (start[0] == ',')
4306 if (start[0] == ';')
4308 while (start[0] == '\n' || start[0] == '\r')
4310 while (start[0] == ' ' || start[0] == '\t')
4315 /* check if there are quotes (to skip , in them) */
4316 if (*start == '"') {
4317 start_quote = start;
4319 end_quote = strstr(start, "\"");
4325 /* skip anything between quotes */
4326 if (start_quote && end_quote) {
4331 /* find end (either , or ; or end of line) */
4332 if (strstr(start, ",") && strstr(start, ";"))
4333 *ep = strstr(start,",") < strstr(start, ";")
4334 ? strstr(start, ",") : strstr(start, ";");
4335 else if (strstr(start, ","))
4336 *ep = strstr(start, ",");
4337 else if (strstr(start, ";"))
4338 *ep = strstr(start, ";");
4340 *ep = start+strlen(start);
4342 /* go back to real start */
4343 if (start_quote && end_quote) {
4344 start = start_quote;
4347 /* check there's still an @ in that, or search
4348 * further if possible */
4349 if (strstr(start, "@") && strstr(start, "@") < *ep)
4351 else if (*ep < start+strlen(start)) {
4354 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4362 dom_tab = create_domain_tab();
4363 g_return_val_if_fail(dom_tab, FALSE);
4365 /* scan start of address */
4366 for (bp_ = scanpos - 1;
4367 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4370 /* TODO: should start with an alnum? */
4372 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4375 if (bp_ != scanpos) {
4376 /* scan end of address */
4377 for (ep_ = scanpos + 1;
4378 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4380 prelast_dot = last_dot;
4382 if (*(last_dot + 1) == '.') {
4383 if (prelast_dot == NULL)
4385 last_dot = prelast_dot;
4390 /* TODO: really should terminate with an alnum? */
4391 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4396 if (last_dot == NULL)
4398 if (last_dot >= ep_)
4399 last_dot = prelast_dot;
4400 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4404 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4405 if (*last_tld_char == '?')
4408 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4415 if (!result) return FALSE;
4417 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4418 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4419 && IS_RFC822_CHAR(*(ep_ + 3))) {
4420 /* this informative part with an @ in it is
4421 * followed by the email address */
4424 /* go to matching '>' (or next non-rfc822 char, like \n) */
4425 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4428 /* include the bracket */
4429 if (*ep_ == '>') ep_++;
4431 /* include the leading quote */
4439 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4440 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4443 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4444 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4447 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4448 #define IN_STACK() (ptr > closure_stack)
4449 /* has underrun check */
4450 #define POP_STACK() if(IN_STACK()) --ptr
4451 /* has overrun check */
4452 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4453 /* has underrun check */
4454 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4458 /* scan for the informative part. */
4459 for (bp_ -= 2; bp_ >= start; bp_--) {
4460 /* if closure on the stack keep scanning */
4461 if (PEEK_STACK() == *bp_) {
4465 if (*bp_ == '\'' || *bp_ == '"') {
4470 /* if nothing in the closure stack, do the special conditions
4471 * the following if..else expression simply checks whether
4472 * a token is acceptable. if not acceptable, the clause
4473 * should terminate the loop with a 'break' */
4474 if (!PEEK_STACK()) {
4476 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4477 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4478 /* hyphens are allowed, but only in
4480 } else if (strchr(" \"'", *bp_)) {
4481 /* but anything not being a punctiation
4484 break; /* anything else is rejected */
4491 /* scan forward (should start with an alnum) */
4492 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4508 #undef IS_ASCII_ALNUM
4509 #undef IS_RFC822_CHAR
4511 gchar *make_email_string(const gchar *bp, const gchar *ep)
4513 /* returns a mailto: URI; mailto: is also used to detect the
4514 * uri type later on in the button_pressed signal handler */
4518 tmp = g_strndup(bp, ep - bp);
4519 result = g_strconcat("mailto:", tmp, NULL);
4525 gchar *make_http_string(const gchar *bp, const gchar *ep)
4527 /* returns an http: URI; */
4531 while (bp && *bp && g_ascii_isspace(*bp))
4533 tmp = g_strndup(bp, ep - bp);
4534 result = g_strconcat("http://", tmp, NULL);
4540 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4542 FILE *fp = fopen(path, "rb");
4543 gchar buf[BUFFSIZE];
4544 gchar *result = NULL;
4547 while (fgets(buf, sizeof (buf), fp) != NULL) {
4548 gchar **parts = g_strsplit(buf, ";", 3);
4549 gchar *trimmed = parts[0];
4550 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4552 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4553 trimmed[strlen(trimmed)-1] = '\0';
4555 if (!strcmp(trimmed, type)) {
4556 gboolean needsterminal = FALSE;
4557 if (parts[2] && strstr(parts[2], "needsterminal")) {
4558 needsterminal = TRUE;
4560 if (parts[2] && strstr(parts[2], "test=")) {
4561 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4562 gchar *testcmd = orig_testcmd;
4563 if (strstr(testcmd,";"))
4564 *(strstr(testcmd,";")) = '\0';
4565 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4567 while (testcmd[strlen(testcmd)-1] == '\n')
4568 testcmd[strlen(testcmd)-1] = '\0';
4569 while (testcmd[strlen(testcmd)-1] == '\r')
4570 testcmd[strlen(testcmd)-1] = '\0';
4571 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4572 testcmd[strlen(testcmd)-1] = '\0';
4574 if (strstr(testcmd, "%s")) {
4575 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4576 gint res = system(tmp);
4578 g_free(orig_testcmd);
4585 gint res = system(testcmd);
4586 g_free(orig_testcmd);
4596 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4598 while (trimmed[strlen(trimmed)-1] == '\n')
4599 trimmed[strlen(trimmed)-1] = '\0';
4600 while (trimmed[strlen(trimmed)-1] == '\r')
4601 trimmed[strlen(trimmed)-1] = '\0';
4602 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4603 trimmed[strlen(trimmed)-1] = '\0';
4604 result = g_strdup(trimmed);
4607 /* if there are no single quotes around %s, add them.
4608 * '.*%s.*' is ok, as in display 'png:%s'
4610 if (strstr(result, "%s")
4611 && !(strstr(result, "'") < strstr(result,"%s") &&
4612 strstr(strstr(result,"%s"), "'"))) {
4613 gchar *start = g_strdup(result);
4614 gchar *end = g_strdup(strstr(result, "%s")+2);
4616 *strstr(start, "%s") = '\0';
4617 tmp = g_strconcat(start,"'%s'",end, NULL);
4623 if (needsterminal) {
4624 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4635 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4637 gchar *result = NULL;
4641 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4642 result = mailcap_get_command_in_file(path, type, file_to_open);
4646 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4650 void mailcap_update_default(const gchar *type, const gchar *command)
4652 gchar *path = NULL, *outpath = NULL;
4653 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4654 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4655 FILE *fp = fopen(path, "rb");
4656 FILE *outfp = fopen(outpath, "wb");
4657 gchar buf[BUFFSIZE];
4658 gboolean err = FALSE;
4666 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4667 gchar **parts = g_strsplit(buf, ";", 3);
4668 gchar *trimmed = parts[0];
4669 while (trimmed[0] == ' ')
4671 while (trimmed[strlen(trimmed)-1] == ' ')
4672 trimmed[strlen(trimmed)-1] = '\0';
4674 if (!strcmp(trimmed, type)) {
4679 if(fputs(buf, outfp) == EOF) {
4686 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4692 if (fclose(outfp) == EOF)
4696 g_rename(outpath, path);
4702 gint copy_dir(const gchar *src, const gchar *dst)
4707 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4708 g_warning("failed to open directory: %s\n", src);
4712 if (make_dir(dst) < 0)
4715 while ((name = g_dir_read_name(dir)) != NULL) {
4716 gchar *old_file, *new_file;
4717 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4718 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4719 debug_print("copying: %s -> %s\n", old_file, new_file);
4720 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4721 gint r = copy_file(old_file, new_file, TRUE);
4728 /* Windows has no symlinks. Or well, Vista seems to
4729 have something like this but the semantics might be
4730 different. Thus we don't use it under Windows. */
4731 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4734 gchar *target = g_file_read_link(old_file, &error);
4736 r = symlink(target, new_file);
4743 #endif /*G_OS_WIN32*/
4744 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4745 gint r = copy_dir(old_file, new_file);
4756 /* crude test to see if a file is an email. */
4757 gboolean file_is_email (const gchar *filename)
4763 if (filename == NULL)
4765 if ((fp = g_fopen(filename, "rb")) == NULL)
4767 while (i < 60 && score < 3
4768 && fgets(buffer, sizeof (buffer), fp) > 0) {
4769 if (!strncmp(buffer, "From:", strlen("From:")))
4771 if (!strncmp(buffer, "To:", strlen("To:")))
4773 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4778 return (score >= 3);
4781 gboolean sc_g_list_bigger(GList *list, gint max)
4785 while (cur && i <= max+1) {
4792 gboolean sc_g_slist_bigger(GSList *list, gint max)
4796 while (cur && i <= max+1) {
4803 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4804 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4805 NULL, NULL, NULL, NULL, NULL, NULL};
4806 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4807 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4808 NULL, NULL, NULL, NULL, NULL, NULL};
4810 gint daynames_len[] = {0,0,0,0,0,0,0};
4811 gint monthnames_len[] = {0,0,0,0,0,0,
4813 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4814 gint s_monthnames_len[] = {0,0,0,0,0,0,
4816 const gchar *s_am_up = NULL;
4817 const gchar *s_pm_up = NULL;
4818 const gchar *s_am_low = NULL;
4819 const gchar *s_pm_low = NULL;
4821 gint s_am_up_len = 0;
4822 gint s_pm_up_len = 0;
4823 gint s_am_low_len = 0;
4824 gint s_pm_low_len = 0;
4826 const gchar *def_loc_format = NULL;
4827 const gchar *date_loc_format = NULL;
4828 const gchar *time_loc_format = NULL;
4829 const gchar *time_am_pm = NULL;
4831 static gboolean time_names_init_done = FALSE;
4833 static void init_time_names(void)
4837 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4838 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4839 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4840 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4841 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4842 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4843 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4845 monthnames[0] = Q_("Complete month name for use by strftime|January");
4846 monthnames[1] = Q_("Complete month name for use by strftime|February");
4847 monthnames[2] = Q_("Complete month name for use by strftime|March");
4848 monthnames[3] = Q_("Complete month name for use by strftime|April");
4849 monthnames[4] = Q_("Complete month name for use by strftime|May");
4850 monthnames[5] = Q_("Complete month name for use by strftime|June");
4851 monthnames[6] = Q_("Complete month name for use by strftime|July");
4852 monthnames[7] = Q_("Complete month name for use by strftime|August");
4853 monthnames[8] = Q_("Complete month name for use by strftime|September");
4854 monthnames[9] = Q_("Complete month name for use by strftime|October");
4855 monthnames[10] = Q_("Complete month name for use by strftime|November");
4856 monthnames[11] = Q_("Complete month name for use by strftime|December");
4858 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4859 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4860 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4861 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4862 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4863 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4864 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4866 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4867 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4868 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4869 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4870 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4871 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4872 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4873 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4874 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4875 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4876 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4877 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4879 for (i = 0; i < 7; i++) {
4880 daynames_len[i] = strlen(daynames[i]);
4881 s_daynames_len[i] = strlen(s_daynames[i]);
4883 for (i = 0; i < 12; i++) {
4884 monthnames_len[i] = strlen(monthnames[i]);
4885 s_monthnames_len[i] = strlen(s_monthnames[i]);
4888 s_am_up = Q_("For use by strftime (morning)|AM");
4889 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4890 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4891 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4893 s_am_up_len = strlen(s_am_up);
4894 s_pm_up_len = strlen(s_pm_up);
4895 s_am_low_len = strlen(s_am_low);
4896 s_pm_low_len = strlen(s_pm_low);
4898 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4899 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4900 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4902 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4904 time_names_init_done = TRUE;
4907 #define CHECK_SIZE() { \
4908 total_done += len; \
4909 if (total_done >= buflen) { \
4910 buf[buflen-1] = '\0'; \
4915 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4917 gchar *curpos = buf;
4918 gint total_done = 0;
4919 gchar subbuf[64], subfmt[64];
4920 static time_t last_tzset = (time_t)0;
4922 if (!time_names_init_done)
4925 if (format == NULL || lt == NULL)
4928 if (last_tzset != time(NULL)) {
4930 last_tzset = time(NULL);
4933 if (*format == '%') {
4934 gint len = 0, tmp = 0;
4938 len = 1; CHECK_SIZE();
4942 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4943 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4946 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4947 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4951 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4952 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4955 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4956 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4959 fast_strftime(subbuf, 64, def_loc_format, lt);
4960 len = strlen(subbuf); CHECK_SIZE();
4961 strncpy2(curpos, subbuf, buflen - total_done);
4964 total_done += 2; CHECK_SIZE();
4965 tmp = (lt->tm_year + 1900)/100;
4966 *curpos++ = '0'+(tmp / 10);
4967 *curpos++ = '0'+(tmp % 10);
4970 total_done += 2; CHECK_SIZE();
4971 *curpos++ = '0'+(lt->tm_mday / 10);
4972 *curpos++ = '0'+(lt->tm_mday % 10);
4975 total_done += 8; CHECK_SIZE();
4976 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4977 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4979 *curpos++ = '0'+(lt->tm_mday / 10);
4980 *curpos++ = '0'+(lt->tm_mday % 10);
4982 tmp = lt->tm_year%100;
4983 *curpos++ = '0'+(tmp / 10);
4984 *curpos++ = '0'+(tmp % 10);
4987 len = 2; CHECK_SIZE();
4988 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4991 len = 10; CHECK_SIZE();
4992 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4993 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4996 total_done += 2; CHECK_SIZE();
4997 *curpos++ = '0'+(lt->tm_hour / 10);
4998 *curpos++ = '0'+(lt->tm_hour % 10);
5001 total_done += 2; CHECK_SIZE();
5007 *curpos++ = '0'+(tmp / 10);
5008 *curpos++ = '0'+(tmp % 10);
5011 len = 3; CHECK_SIZE();
5012 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5015 len = 2; CHECK_SIZE();
5016 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5019 len = 2; CHECK_SIZE();
5025 snprintf(curpos, buflen - total_done, "%2d", tmp);
5028 total_done += 2; CHECK_SIZE();
5029 tmp = lt->tm_mon + 1;
5030 *curpos++ = '0'+(tmp / 10);
5031 *curpos++ = '0'+(tmp % 10);
5034 total_done += 2; CHECK_SIZE();
5035 *curpos++ = '0'+(lt->tm_min / 10);
5036 *curpos++ = '0'+(lt->tm_min % 10);
5039 len = 1; CHECK_SIZE();
5043 if (lt->tm_hour >= 12) {
5044 len = s_pm_up_len; CHECK_SIZE();
5045 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5047 len = s_am_up_len; CHECK_SIZE();
5048 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5052 if (lt->tm_hour >= 12) {
5053 len = s_pm_low_len; CHECK_SIZE();
5054 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5056 len = s_am_low_len; CHECK_SIZE();
5057 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5061 fast_strftime(subbuf, 64, time_am_pm, lt);
5062 len = strlen(subbuf); CHECK_SIZE();
5063 strncpy2(curpos, subbuf, buflen - total_done);
5066 total_done += 5; CHECK_SIZE();
5067 *curpos++ = '0'+(lt->tm_hour / 10);
5068 *curpos++ = '0'+(lt->tm_hour % 10);
5070 *curpos++ = '0'+(lt->tm_min / 10);
5071 *curpos++ = '0'+(lt->tm_min % 10);
5074 snprintf(subbuf, 64, "%ld", mktime(lt));
5075 len = strlen(subbuf); CHECK_SIZE();
5076 strncpy2(curpos, subbuf, buflen - total_done);
5079 total_done += 2; CHECK_SIZE();
5080 *curpos++ = '0'+(lt->tm_sec / 10);
5081 *curpos++ = '0'+(lt->tm_sec % 10);
5084 len = 1; CHECK_SIZE();
5088 total_done += 8; CHECK_SIZE();
5089 *curpos++ = '0'+(lt->tm_hour / 10);
5090 *curpos++ = '0'+(lt->tm_hour % 10);
5092 *curpos++ = '0'+(lt->tm_min / 10);
5093 *curpos++ = '0'+(lt->tm_min % 10);
5095 *curpos++ = '0'+(lt->tm_sec / 10);
5096 *curpos++ = '0'+(lt->tm_sec % 10);
5099 len = 1; CHECK_SIZE();
5100 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5103 len = 1; CHECK_SIZE();
5104 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5107 fast_strftime(subbuf, 64, date_loc_format, lt);
5108 len = strlen(subbuf); CHECK_SIZE();
5109 strncpy2(curpos, subbuf, buflen - total_done);
5112 fast_strftime(subbuf, 64, time_loc_format, lt);
5113 len = strlen(subbuf); CHECK_SIZE();
5114 strncpy2(curpos, subbuf, buflen - total_done);
5117 total_done += 2; CHECK_SIZE();
5118 tmp = lt->tm_year%100;
5119 *curpos++ = '0'+(tmp / 10);
5120 *curpos++ = '0'+(tmp % 10);
5123 len = 4; CHECK_SIZE();
5124 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5134 /* let these complicated ones be done with the libc */
5135 snprintf(subfmt, 64, "%%%c", *format);
5136 strftime(subbuf, 64, subfmt, lt);
5137 len = strlen(subbuf); CHECK_SIZE();
5138 strncpy2(curpos, subbuf, buflen - total_done);
5142 /* let these complicated modifiers be done with the libc */
5143 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5144 strftime(subbuf, 64, subfmt, lt);
5145 len = strlen(subbuf); CHECK_SIZE();
5146 strncpy2(curpos, subbuf, buflen - total_done);
5150 if (format && *format)
5151 g_warning("format error (%c)", *format);
5158 int len = 1; CHECK_SIZE();
5159 *curpos++ = *format++;
5166 gboolean prefs_common_get_use_shred(void);
5170 #define WEXITSTATUS(x) (x)
5173 int claws_unlink(const gchar *filename)
5176 static int found_shred = -1;
5177 static const gchar *args[4];
5179 if (filename == NULL)
5182 if (prefs_common_get_use_shred()) {
5183 if (found_shred == -1) {
5185 args[0] = g_find_program_in_path("shred");
5186 debug_print("found shred: %s\n", args[0]);
5187 found_shred = (args[0] != NULL) ? 1:0;
5191 if (found_shred == 1) {
5192 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5193 if (s.st_nlink == 1) {
5196 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5197 NULL, NULL, NULL, NULL, &status, NULL);
5198 debug_print("%s %s exited with status %d\n",
5199 args[0], filename, WEXITSTATUS(status));
5200 if (truncate(filename, 0) < 0)
5201 g_warning("couln't truncate");
5206 return g_unlink(filename);