2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
67 # include <tablet-browser-interface.h>
69 # include <osso-browser-interface.h>
75 #include "../codeconv.h"
79 static gboolean debug_mode = FALSE;
81 static GSList *tempfiles=NULL;
84 /* Return true if we are running as root. This function should beused
85 instead of getuid () == 0. */
86 gboolean superuser_p (void)
89 return w32_is_administrator ();
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
101 if (G_WIN32_HAVE_WIDECHAR_API()) {
106 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
112 retval = _wchdir(wpath);
124 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125 if (cp_path == NULL) {
130 retval = chdir(cp_path);
143 gint g_chmod(const gchar *path, gint mode)
146 if (G_WIN32_HAVE_WIDECHAR_API()) {
151 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
157 retval = _wchmod(wpath, mode);
169 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170 if (cp_path == NULL) {
175 retval = chmod(cp_path, mode);
184 return chmod(path, mode);
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
191 char *name = g_win32_locale_filename_from_utf8(filename);
192 FILE* fp = fopen(name, mode);
196 return fopen(filename, mode);
199 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
203 gint mkstemp_name(gchar *template, gchar **name_used)
205 static gulong count=0; /* W32-_mktemp only supports up to 27
209 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
210 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
211 (S_IRUSR | S_IWUSR));
213 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
215 perror(g_strdup_printf("cant create %s",*name_used));
221 #endif /* G_OS_WIN32 */
224 gint mkstemp(gchar *template)
227 gint res = mkstemp_name(template, &dummyname);
231 #endif /* G_OS_WIN32 */
233 void list_free_strings(GList *list)
235 list = g_list_first(list);
237 while (list != NULL) {
243 void slist_free_strings(GSList *list)
245 while (list != NULL) {
251 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
256 void hash_free_strings(GHashTable *table)
258 g_hash_table_foreach(table, hash_free_strings_func, NULL);
261 gint str_case_equal(gconstpointer v, gconstpointer v2)
263 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
266 guint str_case_hash(gconstpointer key)
268 const gchar *p = key;
272 h = g_ascii_tolower(h);
273 for (p += 1; *p != '\0'; p++)
274 h = (h << 5) - h + g_ascii_tolower(*p);
280 void ptr_array_free_strings(GPtrArray *array)
285 g_return_if_fail(array != NULL);
287 for (i = 0; i < array->len; i++) {
288 str = g_ptr_array_index(array, i);
293 gboolean str_find(const gchar *haystack, const gchar *needle)
295 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
298 gboolean str_case_find(const gchar *haystack, const gchar *needle)
300 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
303 gint to_number(const gchar *nstr)
305 register const gchar *p;
307 if (*nstr == '\0') return -1;
309 for (p = nstr; *p != '\0'; p++)
310 if (!g_ascii_isdigit(*p)) return -1;
315 /* convert integer into string,
316 nstr must be not lower than 11 characters length */
317 gchar *itos_buf(gchar *nstr, gint n)
319 g_snprintf(nstr, 11, "%d", n);
323 /* convert integer into string */
326 static gchar nstr[11];
328 return itos_buf(nstr, n);
331 #define divide(num,divisor,i,d) \
333 i = num >> divisor; \
334 d = num & ((1<<divisor)-1); \
335 d = (d*100) >> divisor; \
338 gchar *to_human_readable(goffset size)
340 static gchar str[14];
341 static gchar *b_format = NULL, *kb_format = NULL,
342 *mb_format = NULL, *gb_format = NULL;
343 register int t = 0, r = 0;
344 if (b_format == NULL) {
346 kb_format = _("%d.%02dKB");
347 mb_format = _("%d.%02dMB");
348 gb_format = _("%.2fGB");
351 if (size < (goffset)1024) {
352 g_snprintf(str, sizeof(str), b_format, (gint)size);
354 } else if (size >> 10 < (goffset)1024) {
355 divide(size, 10, t, r);
356 g_snprintf(str, sizeof(str), kb_format, t, r);
358 } else if (size >> 20 < (goffset)1024) {
359 divide(size, 20, t, r);
360 g_snprintf(str, sizeof(str), mb_format, t, r);
363 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
368 /* strcmp with NULL-checking */
369 gint strcmp2(const gchar *s1, const gchar *s2)
371 if (s1 == NULL || s2 == NULL)
374 return strcmp(s1, s2);
376 /* strstr with NULL-checking */
377 gchar *strstr2(const gchar *s1, const gchar *s2)
379 if (s1 == NULL || s2 == NULL)
382 return strstr(s1, s2);
385 gint path_cmp(const gchar *s1, const gchar *s2)
390 gchar *s1buf, *s2buf;
393 if (s1 == NULL || s2 == NULL) return -1;
394 if (*s1 == '\0' || *s2 == '\0') return -1;
397 s1buf = g_strdup (s1);
398 s2buf = g_strdup (s2);
399 subst_char (s1buf, '/', G_DIR_SEPARATOR);
400 subst_char (s2buf, '/', G_DIR_SEPARATOR);
403 #endif /* !G_OS_WIN32 */
408 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
409 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
411 rc = strncmp(s1, s2, MAX(len1, len2));
415 #endif /* !G_OS_WIN32 */
419 /* remove trailing return code */
420 gchar *strretchomp(gchar *str)
424 if (!*str) return str;
426 for (s = str + strlen(str) - 1;
427 s >= str && (*s == '\n' || *s == '\r');
434 /* remove trailing character */
435 gchar *strtailchomp(gchar *str, gchar tail_char)
439 if (!*str) return str;
440 if (tail_char == '\0') return str;
442 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
448 /* remove CR (carriage return) */
449 gchar *strcrchomp(gchar *str)
453 if (!*str) return str;
455 s = str + strlen(str) - 1;
456 if (*s == '\n' && s > str && *(s - 1) == '\r') {
464 gint file_strip_crs(const gchar *file)
466 FILE *fp = NULL, *outfp = NULL;
468 gchar *out = get_tmp_file();
472 fp = fopen(file, "rb");
476 outfp = fopen(out, "wb");
482 while (fgets(buf, sizeof (buf), fp) != NULL) {
484 if (fputs(buf, outfp) == EOF) {
492 if (fclose(outfp) == EOF) {
496 if (rename_force(out, file) < 0)
508 /* Similar to `strstr' but this function ignores the case of both strings. */
509 gchar *strcasestr(const gchar *haystack, const gchar *needle)
511 register size_t haystack_len, needle_len;
513 haystack_len = strlen(haystack);
514 needle_len = strlen(needle);
516 if (haystack_len < needle_len || needle_len == 0)
519 while (haystack_len >= needle_len) {
520 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
521 return (gchar *)haystack;
531 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
532 gconstpointer needle, size_t needlelen)
534 const gchar *haystack_ = (const gchar *)haystack;
535 const gchar *needle_ = (const gchar *)needle;
536 const gchar *haystack_cur = (const gchar *)haystack;
537 size_t haystack_left = haystacklen;
540 return memchr(haystack_, *needle_, haystacklen);
542 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
544 if (haystacklen - (haystack_cur - haystack_) < needlelen)
546 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
547 return (gpointer)haystack_cur;
550 haystack_left = haystacklen - (haystack_cur - haystack_);
557 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
558 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
560 register const gchar *s = src;
561 register gchar *d = dest;
571 /* Examine if next block is non-ASCII string */
572 gboolean is_next_nonascii(const gchar *s)
576 /* skip head space */
577 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
579 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
580 if (*(guchar *)p > 127 || *(guchar *)p < 32)
587 gint get_next_word_len(const gchar *s)
591 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
597 static void trim_subject_for_compare(gchar *str)
601 eliminate_parenthesis(str, '[', ']');
602 eliminate_parenthesis(str, '(', ')');
605 srcp = str + subject_get_prefix_length(str);
607 memmove(str, srcp, strlen(srcp) + 1);
610 static void trim_subject_for_sort(gchar *str)
616 srcp = str + subject_get_prefix_length(str);
618 memmove(str, srcp, strlen(srcp) + 1);
621 /* compare subjects */
622 gint subject_compare(const gchar *s1, const gchar *s2)
626 if (!s1 || !s2) return -1;
627 if (!*s1 || !*s2) return -1;
629 Xstrdup_a(str1, s1, return -1);
630 Xstrdup_a(str2, s2, return -1);
632 trim_subject_for_compare(str1);
633 trim_subject_for_compare(str2);
635 if (!*str1 || !*str2) return -1;
637 return strcmp(str1, str2);
640 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
644 if (!s1 || !s2) return -1;
646 Xstrdup_a(str1, s1, return -1);
647 Xstrdup_a(str2, s2, return -1);
649 trim_subject_for_sort(str1);
650 trim_subject_for_sort(str2);
652 return g_utf8_collate(str1, str2);
655 void trim_subject(gchar *str)
657 register gchar *srcp;
663 srcp = str + subject_get_prefix_length(str);
668 } else if (*srcp == '(') {
680 else if (*srcp == cl)
687 while (g_ascii_isspace(*srcp)) srcp++;
688 memmove(str, srcp, strlen(srcp) + 1);
691 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
693 register gchar *srcp, *destp;
698 while ((destp = strchr(destp, op))) {
704 else if (*srcp == cl)
710 while (g_ascii_isspace(*srcp)) srcp++;
711 memmove(destp, srcp, strlen(srcp) + 1);
715 void extract_parenthesis(gchar *str, gchar op, gchar cl)
717 register gchar *srcp, *destp;
722 while ((srcp = strchr(destp, op))) {
725 memmove(destp, srcp + 1, strlen(srcp));
730 else if (*destp == cl)
742 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
745 register gchar *srcp, *destp;
747 gboolean in_quote = FALSE;
751 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
754 memmove(destp, srcp + 1, strlen(srcp));
757 if (*destp == op && !in_quote)
759 else if (*destp == cl && !in_quote)
761 else if (*destp == quote_chr)
773 void extract_quote(gchar *str, gchar quote_chr)
777 if ((str = strchr(str, quote_chr))) {
779 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
780 memmove(p - 1, p, strlen(p) + 1);
785 memmove(str, str + 1, p - str);
790 void eliminate_address_comment(gchar *str)
792 register gchar *srcp, *destp;
797 while ((destp = strchr(destp, '"'))) {
798 if ((srcp = strchr(destp + 1, '"'))) {
803 while (g_ascii_isspace(*srcp)) srcp++;
804 memmove(destp, srcp, strlen(srcp) + 1);
814 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
820 else if (*srcp == ')')
826 while (g_ascii_isspace(*srcp)) srcp++;
827 memmove(destp, srcp, strlen(srcp) + 1);
831 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
833 gboolean in_quote = FALSE;
836 if (*str == c && !in_quote)
838 if (*str == quote_chr)
846 void extract_address(gchar *str)
848 eliminate_address_comment(str);
849 if (strchr_with_skip_quote(str, '"', '<'))
850 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
854 void extract_list_id_str(gchar *str)
856 if (strchr_with_skip_quote(str, '"', '<'))
857 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
861 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
866 if (!str) return addr_list;
868 Xstrdup_a(work, str, return addr_list);
871 eliminate_address_comment(work);
874 while (workp && *workp) {
877 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
883 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
884 extract_parenthesis_with_skip_quote
885 (workp, '"', '<', '>');
889 addr_list = g_slist_append(addr_list, g_strdup(workp));
897 GSList *address_list_append(GSList *addr_list, const gchar *str)
899 return address_list_append_real(addr_list, str, TRUE);
902 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
904 return address_list_append_real(addr_list, str, FALSE);
907 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
911 if (!str) return msgid_list;
914 while (strp && *strp) {
915 const gchar *start, *end;
918 if ((start = strchr(strp, '<')) != NULL) {
919 end = strchr(start + 1, '>');
924 msgid = g_strndup(start + 1, end - start - 1);
927 msgid_list = g_slist_prepend(msgid_list, msgid);
937 GSList *references_list_append(GSList *msgid_list, const gchar *str)
941 list = references_list_prepend(NULL, str);
942 list = g_slist_reverse(list);
943 msgid_list = g_slist_concat(msgid_list, list);
948 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
953 if (!str) return group_list;
955 Xstrdup_a(work, str, return group_list);
959 while (workp && *workp) {
962 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
970 group_list = g_slist_append(group_list,
979 GList *add_history(GList *list, const gchar *str)
983 g_return_val_if_fail(str != NULL, list);
985 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
988 list = g_list_remove(list, old->data);
989 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
992 last = g_list_last(list);
995 list = g_list_remove(list, last->data);
999 list = g_list_prepend(list, g_strdup(str));
1004 void remove_return(gchar *str)
1006 register gchar *p = str;
1009 if (*p == '\n' || *p == '\r')
1010 memmove(p, p + 1, strlen(p));
1016 void remove_space(gchar *str)
1018 register gchar *p = str;
1023 while (g_ascii_isspace(*(p + spc)))
1026 memmove(p, p + spc, strlen(p + spc) + 1);
1032 void unfold_line(gchar *str)
1034 register gchar *p = str;
1038 if (*p == '\n' || *p == '\r') {
1041 while (g_ascii_isspace(*(p + spc)))
1044 memmove(p, p + spc, strlen(p + spc) + 1);
1050 void subst_char(gchar *str, gchar orig, gchar subst)
1052 register gchar *p = str;
1061 void subst_chars(gchar *str, gchar *orig, gchar subst)
1063 register gchar *p = str;
1066 if (strchr(orig, *p) != NULL)
1072 void subst_for_filename(gchar *str)
1077 subst_chars(str, "\t\r\n\\/*:", '_');
1079 subst_chars(str, "\t\r\n\\/*", '_');
1083 void subst_for_shellsafe_filename(gchar *str)
1087 subst_for_filename(str);
1088 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1091 gboolean is_ascii_str(const gchar *str)
1093 const guchar *p = (const guchar *)str;
1095 while (*p != '\0') {
1096 if (*p != '\t' && *p != ' ' &&
1097 *p != '\r' && *p != '\n' &&
1098 (*p < 32 || *p >= 127))
1106 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1108 gchar * position = NULL;
1109 gchar * tmp_pos = NULL;
1112 if (quote_chars == NULL)
1115 for (i = 0; i < strlen(quote_chars); i++) {
1116 tmp_pos = strrchr (str, quote_chars[i]);
1118 || (tmp_pos != NULL && position <= tmp_pos) )
1124 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1126 const gchar *first_pos;
1127 const gchar *last_pos;
1128 const gchar *p = str;
1129 gint quote_level = -1;
1131 /* speed up line processing by only searching to the last '>' */
1132 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1133 /* skip a line if it contains a '<' before the initial '>' */
1134 if (memchr(str, '<', first_pos - str) != NULL)
1136 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1140 while (p <= last_pos) {
1141 while (p < last_pos) {
1142 if (g_ascii_isspace(*p))
1148 if (strchr(quote_chars, *p))
1150 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1151 /* any characters are allowed except '-' and space */
1153 && !strchr(quote_chars, *p)
1154 && !g_ascii_isspace(*p)
1157 if (strchr(quote_chars, *p))
1169 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1171 const gchar *p = str, *q;
1172 gint cur_line = 0, len;
1174 while ((q = strchr(p, '\n')) != NULL) {
1176 if (len > max_chars) {
1186 if (len > max_chars) {
1195 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1197 gchar * position = NULL;
1198 gchar * tmp_pos = NULL;
1201 if (quote_chars == NULL)
1204 for (i = 0; i < strlen(quote_chars); i++) {
1205 tmp_pos = strchr (str, quote_chars[i]);
1207 || (tmp_pos != NULL && position >= tmp_pos) )
1213 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1215 register guint haystack_len, needle_len;
1216 gboolean in_squote = FALSE, in_dquote = FALSE;
1218 haystack_len = strlen(haystack);
1219 needle_len = strlen(needle);
1221 if (haystack_len < needle_len || needle_len == 0)
1224 while (haystack_len >= needle_len) {
1225 if (!in_squote && !in_dquote &&
1226 !strncmp(haystack, needle, needle_len))
1227 return (gchar *)haystack;
1229 /* 'foo"bar"' -> foo"bar"
1230 "foo'bar'" -> foo'bar' */
1231 if (*haystack == '\'') {
1234 else if (!in_dquote)
1236 } else if (*haystack == '\"') {
1239 else if (!in_squote)
1250 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1253 GSList *string_list = NULL, *slist;
1254 gchar **str_array, *s, *new_str;
1255 guint i, n = 1, len;
1257 g_return_val_if_fail(str != NULL, NULL);
1258 g_return_val_if_fail(delim != NULL, NULL);
1261 max_tokens = G_MAXINT;
1263 s = strstr_with_skip_quote(str, delim);
1265 guint delimiter_len = strlen(delim);
1269 new_str = g_strndup(str, len);
1271 if (new_str[0] == '\'' || new_str[0] == '\"') {
1272 if (new_str[len - 1] == new_str[0]) {
1273 new_str[len - 1] = '\0';
1274 memmove(new_str, new_str + 1, len - 1);
1277 string_list = g_slist_prepend(string_list, new_str);
1279 str = s + delimiter_len;
1280 s = strstr_with_skip_quote(str, delim);
1281 } while (--max_tokens && s);
1285 new_str = g_strdup(str);
1286 if (new_str[0] == '\'' || new_str[0] == '\"') {
1288 if (new_str[len - 1] == new_str[0]) {
1289 new_str[len - 1] = '\0';
1290 memmove(new_str, new_str + 1, len - 1);
1293 string_list = g_slist_prepend(string_list, new_str);
1297 str_array = g_new(gchar*, n);
1301 str_array[i--] = NULL;
1302 for (slist = string_list; slist; slist = slist->next)
1303 str_array[i--] = slist->data;
1305 g_slist_free(string_list);
1310 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1312 gchar *abbrev_group;
1314 const gchar *p = group;
1317 g_return_val_if_fail(group != NULL, NULL);
1319 last = group + strlen(group);
1320 abbrev_group = ap = g_malloc(strlen(group) + 1);
1325 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1327 while (*p != '.') p++;
1330 return abbrev_group;
1335 return abbrev_group;
1338 gchar *trim_string(const gchar *str, gint len)
1340 const gchar *p = str;
1345 if (!str) return NULL;
1346 if (strlen(str) <= len)
1347 return g_strdup(str);
1348 if (g_utf8_validate(str, -1, NULL) == FALSE)
1349 return g_strdup(str);
1351 while (*p != '\0') {
1352 mb_len = g_utf8_skip[*(guchar *)p];
1355 else if (new_len + mb_len > len)
1362 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1363 return g_strconcat(new_str, "...", NULL);
1366 GList *uri_list_extract_filenames(const gchar *uri_list)
1368 GList *result = NULL;
1370 gchar *escaped_utf8uri;
1376 while (g_ascii_isspace(*p)) p++;
1377 if (!strncmp(p, "file:", 5)) {
1380 while (*q && *q != '\n' && *q != '\r') q++;
1383 gchar *file, *locale_file = NULL;
1385 while (q > p && g_ascii_isspace(*q))
1387 Xalloca(escaped_utf8uri, q - p + 2,
1389 Xalloca(file, q - p + 2,
1392 strncpy(escaped_utf8uri, p, q - p + 1);
1393 escaped_utf8uri[q - p + 1] = '\0';
1394 decode_uri(file, escaped_utf8uri);
1396 * g_filename_from_uri() rejects escaped/locale encoded uri
1397 * string which come from Nautilus.
1399 if (g_utf8_validate(file, -1, NULL))
1401 = conv_codeset_strdup(
1404 conv_get_locale_charset_str());
1406 locale_file = g_strdup(file + 5);
1407 result = g_list_append(result, locale_file);
1411 p = strchr(p, '\n');
1418 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1421 static gint axtoi(const gchar *hexstr)
1423 gint hi, lo, result;
1426 if ('0' <= hi && hi <= '9') {
1429 if ('a' <= hi && hi <= 'f') {
1432 if ('A' <= hi && hi <= 'F') {
1437 if ('0' <= lo && lo <= '9') {
1440 if ('a' <= lo && lo <= 'f') {
1443 if ('A' <= lo && lo <= 'F') {
1446 result = lo + (16 * hi);
1450 gboolean is_uri_string(const gchar *str)
1452 while (str && *str && g_ascii_isspace(*str))
1454 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1455 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1456 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1457 g_ascii_strncasecmp(str, "www.", 4) == 0);
1460 gchar *get_uri_path(const gchar *uri)
1462 while (uri && *uri && g_ascii_isspace(*uri))
1464 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1465 return (gchar *)(uri + 7);
1466 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1467 return (gchar *)(uri + 8);
1468 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1469 return (gchar *)(uri + 6);
1471 return (gchar *)uri;
1474 gint get_uri_len(const gchar *str)
1478 if (is_uri_string(str)) {
1479 for (p = str; *p != '\0'; p++) {
1480 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1489 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1490 * plusses, and escape characters are used)
1492 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1494 gchar *dec = decoded_uri;
1495 const gchar *enc = encoded_uri;
1500 if (isxdigit((guchar)enc[0]) &&
1501 isxdigit((guchar)enc[1])) {
1507 if (with_plus && *enc == '+')
1519 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1521 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1524 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1526 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1527 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1531 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1532 gchar **subject, gchar **body, gchar ***attach)
1536 const gchar *forbidden_uris[] = { ".gnupg/",
1542 gint num_attach = 0;
1543 gchar **my_att = NULL;
1545 Xstrdup_a(tmp_mailto, mailto, return -1);
1547 if (!strncmp(tmp_mailto, "mailto:", 7))
1550 p = strchr(tmp_mailto, '?');
1557 *to = decode_uri_gdup(tmp_mailto);
1559 my_att = g_malloc(sizeof(char *));
1563 gchar *field, *value;
1580 if (*value == '\0') continue;
1582 if (from && !g_ascii_strcasecmp(field, "from")) {
1584 *from = decode_uri_gdup(value);
1586 gchar *tmp = decode_uri_gdup(value);
1587 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1591 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1593 *cc = decode_uri_gdup(value);
1595 gchar *tmp = decode_uri_gdup(value);
1596 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1600 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1602 *bcc = decode_uri_gdup(value);
1604 gchar *tmp = decode_uri_gdup(value);
1605 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1609 } else if (subject && !*subject &&
1610 !g_ascii_strcasecmp(field, "subject")) {
1611 *subject = decode_uri_gdup(value);
1612 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1613 *body = decode_uri_gdup(value);
1614 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1615 gchar *tmp = decode_uri_gdup(value);
1616 if (!g_file_get_contents(value, body, NULL, NULL)) {
1617 g_error("Error: couldn't set insert file '%s' in body\n", value);
1621 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1623 gchar *tmp = decode_uri_gdup(value);
1624 for (; forbidden_uris[i]; i++) {
1625 if (strstr(tmp, forbidden_uris[i])) {
1626 g_print("Refusing to attach '%s', potential private data leak\n",
1634 /* attach is correct */
1636 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1637 my_att[num_attach-1] = tmp;
1638 my_att[num_attach] = NULL;
1650 #include <windows.h>
1651 #ifndef CSIDL_APPDATA
1652 #define CSIDL_APPDATA 0x001a
1654 #ifndef CSIDL_LOCAL_APPDATA
1655 #define CSIDL_LOCAL_APPDATA 0x001c
1657 #ifndef CSIDL_FLAG_CREATE
1658 #define CSIDL_FLAG_CREATE 0x8000
1660 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1664 w32_strerror (int w32_errno)
1666 static char strerr[256];
1667 int ec = (int)GetLastError ();
1671 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1672 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1673 strerr, DIM (strerr)-1, NULL);
1677 static __inline__ void *
1678 dlopen (const char * name, int flag)
1680 void * hd = LoadLibrary (name);
1684 static __inline__ void *
1685 dlsym (void * hd, const char * sym)
1689 void * fnc = GetProcAddress (hd, sym);
1698 static __inline__ const char *
1701 return w32_strerror (0);
1705 static __inline__ int
1717 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1719 static int initialized;
1720 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1724 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1730 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1732 handle = dlopen (dllnames[i], RTLD_LAZY);
1735 func = dlsym (handle, "SHGetFolderPathW");
1746 return func (a,b,c,d,e);
1751 /* Returns a static string with the directroy from which the module
1752 has been loaded. Returns an empty string on error. */
1753 static char *w32_get_module_dir(void)
1755 static char *moddir;
1758 char name[MAX_PATH+10];
1761 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1764 p = strrchr (name, '\\');
1770 moddir = g_strdup (name);
1774 #endif /* G_OS_WIN32 */
1776 /* Return a static string with the locale dir. */
1777 const gchar *get_locale_dir(void)
1779 static gchar *loc_dir;
1783 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1784 "\\share\\locale", NULL);
1787 loc_dir = LOCALEDIR;
1793 const gchar *get_home_dir(void)
1796 static char home_dir_utf16[MAX_PATH] = "";
1797 static gchar *home_dir_utf8 = NULL;
1798 if (home_dir_utf16[0] == '\0') {
1799 if (w32_shgetfolderpath
1800 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1801 NULL, 0, home_dir_utf16) < 0)
1802 strcpy (home_dir_utf16, "C:\\Sylpheed");
1803 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1805 return home_dir_utf8;
1807 static const gchar *homeenv = NULL;
1812 if (!homeenv && g_getenv("HOME") != NULL)
1813 homeenv = g_strdup(g_getenv("HOME"));
1815 homeenv = g_get_home_dir();
1821 static gchar *claws_rc_dir = NULL;
1822 static gboolean rc_dir_alt = FALSE;
1823 const gchar *get_rc_dir(void)
1827 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1830 return claws_rc_dir;
1833 void set_rc_dir(const gchar *dir)
1835 if (claws_rc_dir != NULL) {
1836 g_print("Error: rc_dir already set\n");
1839 if (g_path_is_absolute(dir))
1840 claws_rc_dir = g_strdup(dir);
1842 claws_rc_dir = g_strconcat(g_get_current_dir(),
1843 G_DIR_SEPARATOR_S, dir, NULL);
1845 debug_print("set rc_dir to %s\n", claws_rc_dir);
1846 if (!is_dir_exist(claws_rc_dir)) {
1847 if (make_dir_hier(claws_rc_dir) != 0) {
1848 g_print("Error: can't create %s\n",
1855 gboolean rc_dir_is_alt(void) {
1859 const gchar *get_mail_base_dir(void)
1861 return get_home_dir();
1865 const gchar *prefs_common_get_data_root(void);
1866 gchar *last_data_root = NULL;
1869 const gchar *get_news_cache_dir(void)
1871 static gchar *news_cache_dir = NULL;
1873 const gchar *data_root = prefs_common_get_data_root();
1874 if (strcmp2(data_root, last_data_root)) {
1875 g_free(news_cache_dir);
1876 news_cache_dir = NULL;
1879 if (!news_cache_dir)
1881 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1882 NEWS_CACHE_DIR, NULL);
1886 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1887 "Claws", G_DIR_SEPARATOR_S,
1888 g_get_user_name(), G_DIR_SEPARATOR_S,
1889 NEWS_CACHE_DIR, NULL);
1890 g_free(last_data_root);
1891 last_data_root = g_strdup(last_data_root);
1893 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1894 NEWS_CACHE_DIR, NULL);
1895 g_free(last_data_root);
1896 last_data_root = NULL;
1900 return news_cache_dir;
1903 const gchar *get_imap_cache_dir(void)
1905 static gchar *imap_cache_dir = NULL;
1907 const gchar *data_root = prefs_common_get_data_root();
1908 if (strcmp2(data_root, last_data_root)) {
1909 g_free(imap_cache_dir);
1910 imap_cache_dir = NULL;
1914 if (!imap_cache_dir)
1916 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1917 IMAP_CACHE_DIR, NULL);
1921 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1922 "Claws", G_DIR_SEPARATOR_S,
1923 g_get_user_name(), G_DIR_SEPARATOR_S,
1924 IMAP_CACHE_DIR, NULL);
1925 g_free(last_data_root);
1926 last_data_root = g_strdup(last_data_root);
1928 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1929 IMAP_CACHE_DIR, NULL);
1930 g_free(last_data_root);
1931 last_data_root = NULL;
1936 return imap_cache_dir;
1939 const gchar *get_mime_tmp_dir(void)
1941 static gchar *mime_tmp_dir = NULL;
1944 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1945 MIME_TMP_DIR, NULL);
1947 return mime_tmp_dir;
1950 const gchar *get_template_dir(void)
1952 static gchar *template_dir = NULL;
1955 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1956 TEMPLATE_DIR, NULL);
1958 return template_dir;
1961 /* Return the default directory for Plugins. */
1962 const gchar *get_plugin_dir(void)
1965 static gchar *plugin_dir = NULL;
1968 plugin_dir = g_strconcat(w32_get_module_dir(),
1969 "\\lib\\claws-mail\\plugins\\",
1973 if (is_dir_exist(PLUGINDIR))
1976 static gchar *plugin_dir = NULL;
1978 plugin_dir = g_strconcat(get_rc_dir(),
1979 G_DIR_SEPARATOR_S, "plugins",
1980 G_DIR_SEPARATOR_S, NULL);
1986 const gchar *get_tmp_dir(void)
1988 static gchar *tmp_dir = NULL;
1991 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1997 gchar *get_tmp_file(void)
2000 static guint32 id = 0;
2002 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2003 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2008 const gchar *get_domain_name(void)
2011 static gchar *domain_name = NULL;
2017 if (gethostname(hostname, sizeof(hostname)) != 0) {
2018 perror("gethostname");
2019 domain_name = "unknown";
2021 hostname[sizeof(hostname) - 1] = '\0';
2022 if ((hp = my_gethostbyname(hostname)) == NULL) {
2023 perror("gethostbyname");
2024 domain_name = g_strdup(hostname);
2026 domain_name = g_strdup(hp->h_name);
2029 debug_print("domain name = %s\n", domain_name);
2038 off_t get_file_size(const gchar *file)
2042 if (g_stat(file, &s) < 0) {
2043 FILE_OP_ERROR(file, "stat");
2050 time_t get_file_mtime(const gchar *file)
2054 if (g_stat(file, &s) < 0) {
2055 FILE_OP_ERROR(file, "stat");
2062 off_t get_file_size_as_crlf(const gchar *file)
2066 gchar buf[BUFFSIZE];
2068 if ((fp = g_fopen(file, "rb")) == NULL) {
2069 FILE_OP_ERROR(file, "fopen");
2073 while (fgets(buf, sizeof(buf), fp) != NULL) {
2075 size += strlen(buf) + 2;
2079 FILE_OP_ERROR(file, "fgets");
2088 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2095 if (g_stat(file, &s) < 0) {
2096 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2100 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2107 /* Test on whether FILE is a relative file name. This is
2108 * straightforward for Unix but more complex for Windows. */
2109 gboolean is_relative_filename(const gchar *file)
2114 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2115 return FALSE; /* Prefixed with a hostname - this can't
2116 * be a relative name. */
2118 if ( ((*file >= 'a' && *file <= 'z')
2119 || (*file >= 'A' && *file <= 'Z'))
2121 file += 2; /* Skip drive letter. */
2123 return !(*file == '\\' || *file == '/');
2125 return !(*file == G_DIR_SEPARATOR);
2130 gboolean is_dir_exist(const gchar *dir)
2135 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2138 gboolean is_file_entry_exist(const gchar *file)
2143 return g_file_test(file, G_FILE_TEST_EXISTS);
2146 gboolean dirent_is_regular_file(struct dirent *d)
2148 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2149 if (d->d_type == DT_REG)
2151 else if (d->d_type != DT_UNKNOWN)
2155 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2158 gint change_dir(const gchar *dir)
2160 gchar *prevdir = NULL;
2163 prevdir = g_get_current_dir();
2165 if (g_chdir(dir) < 0) {
2166 FILE_OP_ERROR(dir, "chdir");
2167 if (debug_mode) g_free(prevdir);
2169 } else if (debug_mode) {
2172 cwd = g_get_current_dir();
2173 if (strcmp(prevdir, cwd) != 0)
2174 g_print("current dir: %s\n", cwd);
2182 gint make_dir(const gchar *dir)
2184 if (g_mkdir(dir, S_IRWXU) < 0) {
2185 FILE_OP_ERROR(dir, "mkdir");
2188 if (g_chmod(dir, S_IRWXU) < 0)
2189 FILE_OP_ERROR(dir, "chmod");
2194 gint make_dir_hier(const gchar *dir)
2199 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2200 parent_dir = g_strndup(dir, p - dir);
2201 if (*parent_dir != '\0') {
2202 if (!is_dir_exist(parent_dir)) {
2203 if (make_dir(parent_dir) < 0) {
2212 if (!is_dir_exist(dir)) {
2213 if (make_dir(dir) < 0)
2220 gint remove_all_files(const gchar *dir)
2223 const gchar *dir_name;
2226 prev_dir = g_get_current_dir();
2228 if (g_chdir(dir) < 0) {
2229 FILE_OP_ERROR(dir, "chdir");
2234 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2235 g_warning("failed to open directory: %s\n", dir);
2240 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2241 if (claws_unlink(dir_name) < 0)
2242 FILE_OP_ERROR(dir_name, "unlink");
2247 if (g_chdir(prev_dir) < 0) {
2248 FILE_OP_ERROR(prev_dir, "chdir");
2258 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2261 const gchar *dir_name;
2265 prev_dir = g_get_current_dir();
2267 if (g_chdir(dir) < 0) {
2268 FILE_OP_ERROR(dir, "chdir");
2273 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2274 g_warning("failed to open directory: %s\n", dir);
2279 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2280 file_no = to_number(dir_name);
2281 if (file_no > 0 && first <= file_no && file_no <= last) {
2282 if (is_dir_exist(dir_name))
2284 if (claws_unlink(dir_name) < 0)
2285 FILE_OP_ERROR(dir_name, "unlink");
2291 if (g_chdir(prev_dir) < 0) {
2292 FILE_OP_ERROR(prev_dir, "chdir");
2302 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2305 const gchar *dir_name;
2309 prev_dir = g_get_current_dir();
2311 if (g_chdir(dir) < 0) {
2312 FILE_OP_ERROR(dir, "chdir");
2317 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2318 FILE_OP_ERROR(dir, "opendir");
2323 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2324 file_no = to_number(dir_name);
2325 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2326 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2327 if (is_dir_exist(dir_name))
2329 if (claws_unlink(dir_name) < 0)
2330 FILE_OP_ERROR(dir_name, "unlink");
2336 if (g_chdir(prev_dir) < 0) {
2337 FILE_OP_ERROR(prev_dir, "chdir");
2347 gint remove_all_numbered_files(const gchar *dir)
2349 return remove_numbered_files(dir, 0, UINT_MAX);
2352 gint remove_dir_recursive(const gchar *dir)
2356 const gchar *dir_name;
2359 if (g_stat(dir, &s) < 0) {
2360 FILE_OP_ERROR(dir, "stat");
2361 if (ENOENT == errno) return 0;
2365 if (!S_ISDIR(s.st_mode)) {
2366 if (claws_unlink(dir) < 0) {
2367 FILE_OP_ERROR(dir, "unlink");
2374 prev_dir = g_get_current_dir();
2375 /* g_print("prev_dir = %s\n", prev_dir); */
2377 if (!path_cmp(prev_dir, dir)) {
2379 if (g_chdir("..") < 0) {
2380 FILE_OP_ERROR(dir, "chdir");
2383 prev_dir = g_get_current_dir();
2386 if (g_chdir(dir) < 0) {
2387 FILE_OP_ERROR(dir, "chdir");
2392 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2393 g_warning("failed to open directory: %s\n", dir);
2399 /* remove all files in the directory */
2400 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2401 /* g_print("removing %s\n", dir_name); */
2403 if (is_dir_exist(dir_name)) {
2404 if (remove_dir_recursive(dir_name) < 0) {
2405 g_warning("can't remove directory\n");
2409 if (claws_unlink(dir_name) < 0)
2410 FILE_OP_ERROR(dir_name, "unlink");
2416 if (g_chdir(prev_dir) < 0) {
2417 FILE_OP_ERROR(prev_dir, "chdir");
2424 if (g_rmdir(dir) < 0) {
2425 FILE_OP_ERROR(dir, "rmdir");
2432 gint rename_force(const gchar *oldpath, const gchar *newpath)
2435 if (!is_file_entry_exist(oldpath)) {
2439 if (is_file_exist(newpath)) {
2440 if (claws_unlink(newpath) < 0)
2441 FILE_OP_ERROR(newpath, "unlink");
2444 return g_rename(oldpath, newpath);
2448 * Append src file body to the tail of dest file.
2449 * Now keep_backup has no effects.
2451 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2453 FILE *src_fp, *dest_fp;
2457 gboolean err = FALSE;
2459 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2460 FILE_OP_ERROR(src, "fopen");
2464 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2465 FILE_OP_ERROR(dest, "fopen");
2470 if (change_file_mode_rw(dest_fp, dest) < 0) {
2471 FILE_OP_ERROR(dest, "chmod");
2472 g_warning("can't change file mode\n");
2475 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2476 if (n_read < sizeof(buf) && ferror(src_fp))
2478 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2479 g_warning("writing to %s failed.\n", dest);
2487 if (ferror(src_fp)) {
2488 FILE_OP_ERROR(src, "fread");
2492 if (fclose(dest_fp) == EOF) {
2493 FILE_OP_ERROR(dest, "fclose");
2505 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2507 FILE *src_fp, *dest_fp;
2510 gchar *dest_bak = NULL;
2511 gboolean err = FALSE;
2513 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2514 FILE_OP_ERROR(src, "fopen");
2517 if (is_file_exist(dest)) {
2518 dest_bak = g_strconcat(dest, ".bak", NULL);
2519 if (rename_force(dest, dest_bak) < 0) {
2520 FILE_OP_ERROR(dest, "rename");
2527 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2528 FILE_OP_ERROR(dest, "fopen");
2531 if (rename_force(dest_bak, dest) < 0)
2532 FILE_OP_ERROR(dest_bak, "rename");
2538 if (change_file_mode_rw(dest_fp, dest) < 0) {
2539 FILE_OP_ERROR(dest, "chmod");
2540 g_warning("can't change file mode\n");
2543 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2544 if (n_read < sizeof(buf) && ferror(src_fp))
2546 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2547 g_warning("writing to %s failed.\n", dest);
2552 if (rename_force(dest_bak, dest) < 0)
2553 FILE_OP_ERROR(dest_bak, "rename");
2560 if (ferror(src_fp)) {
2561 FILE_OP_ERROR(src, "fread");
2565 if (fclose(dest_fp) == EOF) {
2566 FILE_OP_ERROR(dest, "fclose");
2573 if (rename_force(dest_bak, dest) < 0)
2574 FILE_OP_ERROR(dest_bak, "rename");
2580 if (keep_backup == FALSE && dest_bak)
2581 claws_unlink(dest_bak);
2588 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2590 if (overwrite == FALSE && is_file_exist(dest)) {
2591 g_warning("move_file(): file %s already exists.", dest);
2595 if (rename_force(src, dest) == 0) return 0;
2597 if (EXDEV != errno) {
2598 FILE_OP_ERROR(src, "rename");
2602 if (copy_file(src, dest, FALSE) < 0) return -1;
2609 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2612 gint bytes_left, to_read;
2615 if (fseek(fp, offset, SEEK_SET) < 0) {
2620 bytes_left = length;
2621 to_read = MIN(bytes_left, sizeof(buf));
2623 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2624 if (n_read < to_read && ferror(fp))
2626 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2629 bytes_left -= n_read;
2630 if (bytes_left == 0)
2632 to_read = MIN(bytes_left, sizeof(buf));
2643 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2646 gboolean err = FALSE;
2648 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2649 FILE_OP_ERROR(dest, "fopen");
2653 if (change_file_mode_rw(dest_fp, dest) < 0) {
2654 FILE_OP_ERROR(dest, "chmod");
2655 g_warning("can't change file mode\n");
2658 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2661 if (!err && fclose(dest_fp) == EOF) {
2662 FILE_OP_ERROR(dest, "fclose");
2667 g_warning("writing to %s failed.\n", dest);
2675 /* convert line endings into CRLF. If the last line doesn't end with
2676 * linebreak, add it.
2678 gchar *canonicalize_str(const gchar *str)
2684 for (p = str; *p != '\0'; ++p) {
2691 if (p == str || *(p - 1) != '\n')
2694 out = outp = g_malloc(new_len + 1);
2695 for (p = str; *p != '\0'; ++p) {
2702 if (p == str || *(p - 1) != '\n') {
2711 gint canonicalize_file(const gchar *src, const gchar *dest)
2713 FILE *src_fp, *dest_fp;
2714 gchar buf[BUFFSIZE];
2716 gboolean err = FALSE;
2717 gboolean last_linebreak = FALSE;
2719 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2720 FILE_OP_ERROR(src, "fopen");
2724 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2725 FILE_OP_ERROR(dest, "fopen");
2730 if (change_file_mode_rw(dest_fp, dest) < 0) {
2731 FILE_OP_ERROR(dest, "chmod");
2732 g_warning("can't change file mode\n");
2735 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2739 if (len == 0) break;
2740 last_linebreak = FALSE;
2742 if (buf[len - 1] != '\n') {
2743 last_linebreak = TRUE;
2744 r = fputs(buf, dest_fp);
2745 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2746 r = fputs(buf, dest_fp);
2749 r = fwrite(buf, 1, len - 1, dest_fp);
2754 r = fputs("\r\n", dest_fp);
2758 g_warning("writing to %s failed.\n", dest);
2766 if (last_linebreak == TRUE) {
2767 if (fputs("\r\n", dest_fp) == EOF)
2771 if (ferror(src_fp)) {
2772 FILE_OP_ERROR(src, "fgets");
2776 if (fclose(dest_fp) == EOF) {
2777 FILE_OP_ERROR(dest, "fclose");
2789 gint canonicalize_file_replace(const gchar *file)
2793 tmp_file = get_tmp_file();
2795 if (canonicalize_file(file, tmp_file) < 0) {
2800 if (move_file(tmp_file, file, TRUE) < 0) {
2801 g_warning("can't replace %s .\n", file);
2802 claws_unlink(tmp_file);
2811 gchar *normalize_newlines(const gchar *str)
2813 const gchar *p = str;
2816 out = outp = g_malloc(strlen(str) + 1);
2817 for (p = str; *p != '\0'; ++p) {
2819 if (*(p + 1) != '\n')
2830 gchar *get_outgoing_rfc2822_str(FILE *fp)
2832 gchar buf[BUFFSIZE];
2836 str = g_string_new(NULL);
2838 /* output header part */
2839 while (fgets(buf, sizeof(buf), fp) != NULL) {
2841 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2848 else if (next != ' ' && next != '\t') {
2852 if (fgets(buf, sizeof(buf), fp) == NULL)
2856 g_string_append(str, buf);
2857 g_string_append(str, "\r\n");
2863 /* output body part */
2864 while (fgets(buf, sizeof(buf), fp) != NULL) {
2867 g_string_append_c(str, '.');
2868 g_string_append(str, buf);
2869 g_string_append(str, "\r\n");
2873 g_string_free(str, FALSE);
2879 * Create a new boundary in a way that it is very unlikely that this
2880 * will occur in the following text. It would be easy to ensure
2881 * uniqueness if everything is either quoted-printable or base64
2882 * encoded (note that conversion is allowed), but because MIME bodies
2883 * may be nested, it may happen that the same boundary has already
2886 * boundary := 0*69<bchars> bcharsnospace
2887 * bchars := bcharsnospace / " "
2888 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2889 * "+" / "_" / "," / "-" / "." /
2890 * "/" / ":" / "=" / "?"
2892 * some special characters removed because of buggy MTAs
2895 gchar *generate_mime_boundary(const gchar *prefix)
2897 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2898 "abcdefghijklmnopqrstuvwxyz"
2903 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2904 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2907 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2911 gint change_file_mode_rw(FILE *fp, const gchar *file)
2914 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2916 return g_chmod(file, S_IRUSR|S_IWUSR);
2920 FILE *my_tmpfile(void)
2922 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2923 const gchar suffix[] = ".XXXXXX";
2924 const gchar *tmpdir;
2926 const gchar *progname;
2935 tmpdir = get_tmp_dir();
2936 tmplen = strlen(tmpdir);
2937 progname = g_get_prgname();
2938 if (progname == NULL)
2939 progname = "claws-mail";
2940 proglen = strlen(progname);
2941 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2944 memcpy(fname, tmpdir, tmplen);
2945 fname[tmplen] = G_DIR_SEPARATOR;
2946 memcpy(fname + tmplen + 1, progname, proglen);
2947 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2949 fd = mkstemp(fname);
2954 claws_unlink(fname);
2956 /* verify that we can write in the file after unlinking */
2957 if (write(fd, buf, 1) < 0) {
2964 fp = fdopen(fd, "w+b");
2972 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2977 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2981 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2982 dir, G_DIR_SEPARATOR);
2983 fd = mkstemp_name(template, filename);
2986 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2987 fd = mkstemp(*filename);
2989 return fdopen(fd, "w+");
2992 FILE *str_open_as_stream(const gchar *str)
2997 g_return_val_if_fail(str != NULL, NULL);
3001 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3006 if (len == 0) return fp;
3008 if (fwrite(str, 1, len, fp) != len) {
3009 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3018 gint str_write_to_file(const gchar *str, const gchar *file)
3023 g_return_val_if_fail(str != NULL, -1);
3024 g_return_val_if_fail(file != NULL, -1);
3026 if ((fp = g_fopen(file, "wb")) == NULL) {
3027 FILE_OP_ERROR(file, "fopen");
3037 if (fwrite(str, 1, len, fp) != len) {
3038 FILE_OP_ERROR(file, "fwrite");
3044 if (fclose(fp) == EOF) {
3045 FILE_OP_ERROR(file, "fclose");
3053 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3060 g_return_val_if_fail(fp != NULL, NULL);
3062 array = g_byte_array_new();
3064 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3065 if (n_read < sizeof(buf) && ferror(fp))
3067 g_byte_array_append(array, buf, n_read);
3071 FILE_OP_ERROR("file stream", "fread");
3072 g_byte_array_free(array, TRUE);
3077 g_byte_array_append(array, buf, 1);
3078 str = (gchar *)array->data;
3079 g_byte_array_free(array, FALSE);
3081 if (recode && !g_utf8_validate(str, -1, NULL)) {
3082 const gchar *src_codeset, *dest_codeset;
3084 src_codeset = conv_get_locale_charset_str();
3085 dest_codeset = CS_UTF_8;
3086 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3094 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3101 struct timeval timeout = {1, 0};
3106 g_return_val_if_fail(file != NULL, NULL);
3108 if (g_stat(file, &s) != 0) {
3109 FILE_OP_ERROR(file, "stat");
3112 if (S_ISDIR(s.st_mode)) {
3113 g_warning("%s: is a directory\n", file);
3118 fp = fopen (file, "rb");
3120 FILE_OP_ERROR(file, "open");
3124 /* test whether the file is readable without blocking */
3125 fd = open(file, O_RDONLY | O_NONBLOCK);
3127 FILE_OP_ERROR(file, "open");
3134 /* allow for one second */
3135 err = select(fd+1, &fds, NULL, NULL, &timeout);
3136 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3138 FILE_OP_ERROR(file, "select");
3140 g_warning("%s: doesn't seem readable\n", file);
3146 /* Now clear O_NONBLOCK */
3147 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3148 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3152 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3153 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3158 /* get the FILE pointer */
3159 fp = fdopen(fd, "rb");
3162 FILE_OP_ERROR(file, "fdopen");
3163 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3168 str = file_read_stream_to_str_full(fp, recode);
3175 gchar *file_read_to_str(const gchar *file)
3177 return file_read_to_str_full(file, TRUE);
3179 gchar *file_read_stream_to_str(FILE *fp)
3181 return file_read_stream_to_str_full(fp, TRUE);
3184 gchar *file_read_to_str_no_recode(const gchar *file)
3186 return file_read_to_str_full(file, FALSE);
3188 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3190 return file_read_stream_to_str_full(fp, FALSE);
3193 char *fgets_crlf(char *buf, int size, FILE *stream)
3195 gboolean is_cr = FALSE;
3196 gboolean last_was_cr = FALSE;
3201 while (--size > 0 && (c = getc(stream)) != EOF)
3204 is_cr = (c == '\r');
3214 last_was_cr = is_cr;
3216 if (c == EOF && cs == buf)
3224 static gint execute_async(gchar *const argv[])
3226 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3228 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3229 NULL, NULL, NULL, FALSE) == FALSE) {
3230 g_warning("Couldn't execute command: %s\n", argv[0]);
3237 static gint execute_sync(gchar *const argv[])
3241 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3243 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3244 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3245 g_warning("Couldn't execute command: %s\n", argv[0]);
3250 if (WIFEXITED(status))
3251 return WEXITSTATUS(status);
3259 gint execute_command_line(const gchar *cmdline, gboolean async)
3264 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3266 argv = strsplit_with_quote(cmdline, " ", 0);
3269 ret = execute_async(argv);
3271 ret = execute_sync(argv);
3278 gchar *get_command_output(const gchar *cmdline)
3280 gchar *child_stdout;
3283 g_return_val_if_fail(cmdline != NULL, NULL);
3285 debug_print("get_command_output(): executing: %s\n", cmdline);
3287 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3289 g_warning("Couldn't execute command: %s\n", cmdline);
3293 return child_stdout;
3296 static gint is_unchanged_uri_char(char c)
3307 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3313 for(i = 0; i < strlen(uri) ; i++) {
3314 if (is_unchanged_uri_char(uri[i])) {
3315 if (k + 2 >= bufsize)
3317 encoded_uri[k++] = uri[i];
3320 char * hexa = "0123456789ABCDEF";
3322 if (k + 4 >= bufsize)
3324 encoded_uri[k++] = '%';
3325 encoded_uri[k++] = hexa[uri[i] / 16];
3326 encoded_uri[k++] = hexa[uri[i] % 16];
3332 gint open_uri(const gchar *uri, const gchar *cmdline)
3336 gchar buf[BUFFSIZE];
3338 gchar encoded_uri[BUFFSIZE];
3339 g_return_val_if_fail(uri != NULL, -1);
3341 /* an option to choose whether to use encode_uri or not ? */
3342 encode_uri(encoded_uri, BUFFSIZE, uri);
3345 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3346 !strchr(p + 2, '%'))
3347 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3350 g_warning("Open URI command-line is invalid "
3351 "(there must be only one '%%s'): %s",
3353 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3356 execute_command_line(buf, TRUE);
3358 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3361 extern osso_context_t *get_osso_context(void);
3362 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3363 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3364 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3369 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3371 gchar buf[BUFFSIZE];
3374 g_return_val_if_fail(filepath != NULL, -1);
3377 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3378 !strchr(p + 2, '%'))
3379 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3382 g_warning("Open Text Editor command-line is invalid "
3383 "(there must be only one '%%s'): %s",
3385 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3388 execute_command_line(buf, TRUE);
3393 time_t remote_tzoffset_sec(const gchar *zone)
3395 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3401 time_t remoteoffset;
3403 strncpy(zone3, zone, 3);
3407 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3408 (c == '+' || c == '-')) {
3409 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3411 remoteoffset = -remoteoffset;
3412 } else if (!strncmp(zone, "UT" , 2) ||
3413 !strncmp(zone, "GMT", 2)) {
3415 } else if (strlen(zone3) == 3) {
3416 for (p = ustzstr; *p != '\0'; p += 3) {
3417 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3418 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3419 remoteoffset = iustz * 3600;
3425 } else if (strlen(zone3) == 1) {
3427 case 'Z': remoteoffset = 0; break;
3428 case 'A': remoteoffset = -1; break;
3429 case 'B': remoteoffset = -2; break;
3430 case 'C': remoteoffset = -3; break;
3431 case 'D': remoteoffset = -4; break;
3432 case 'E': remoteoffset = -5; break;
3433 case 'F': remoteoffset = -6; break;
3434 case 'G': remoteoffset = -7; break;
3435 case 'H': remoteoffset = -8; break;
3436 case 'I': remoteoffset = -9; break;
3437 case 'K': remoteoffset = -10; break; /* J is not used */
3438 case 'L': remoteoffset = -11; break;
3439 case 'M': remoteoffset = -12; break;
3440 case 'N': remoteoffset = 1; break;
3441 case 'O': remoteoffset = 2; break;
3442 case 'P': remoteoffset = 3; break;
3443 case 'Q': remoteoffset = 4; break;
3444 case 'R': remoteoffset = 5; break;
3445 case 'S': remoteoffset = 6; break;
3446 case 'T': remoteoffset = 7; break;
3447 case 'U': remoteoffset = 8; break;
3448 case 'V': remoteoffset = 9; break;
3449 case 'W': remoteoffset = 10; break;
3450 case 'X': remoteoffset = 11; break;
3451 case 'Y': remoteoffset = 12; break;
3452 default: remoteoffset = 0; break;
3454 remoteoffset = remoteoffset * 3600;
3458 return remoteoffset;
3461 time_t tzoffset_sec(time_t *now)
3466 struct tm buf1, buf2;
3469 if (now && *now < 0)
3472 gmt = *gmtime_r(now, &buf1);
3473 lt = localtime_r(now, &buf2);
3475 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3477 if (lt->tm_year < gmt.tm_year)
3479 else if (lt->tm_year > gmt.tm_year)
3481 else if (lt->tm_yday < gmt.tm_yday)
3483 else if (lt->tm_yday > gmt.tm_yday)
3486 if (off >= 24 * 60) /* should be impossible */
3487 off = 23 * 60 + 59; /* if not, insert silly value */
3488 if (off <= -24 * 60)
3489 off = -(23 * 60 + 59);
3494 /* calculate timezone offset */
3495 gchar *tzoffset(time_t *now)
3497 static gchar offset_string[6];
3502 struct tm buf1, buf2;
3505 if (now && *now < 0)
3508 gmt = *gmtime_r(now, &buf1);
3509 lt = localtime_r(now, &buf2);
3511 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3513 if (lt->tm_year < gmt.tm_year)
3515 else if (lt->tm_year > gmt.tm_year)
3517 else if (lt->tm_yday < gmt.tm_yday)
3519 else if (lt->tm_yday > gmt.tm_yday)
3527 if (off >= 24 * 60) /* should be impossible */
3528 off = 23 * 60 + 59; /* if not, insert silly value */
3530 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3532 return offset_string;
3535 void get_rfc822_date(gchar *buf, gint len)
3539 gchar day[4], mon[4];
3540 gint dd, hh, mm, ss, yyyy;
3543 gchar buf2[BUFFSIZE];
3547 lt = localtime_r(&t, &buf1);
3549 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3550 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3552 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3553 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3556 void debug_set_mode(gboolean mode)
3561 gboolean debug_get_mode(void)
3566 void debug_print_real(const gchar *format, ...)
3569 gchar buf[BUFFSIZE];
3571 if (!debug_mode) return;
3573 va_start(args, format);
3574 g_vsnprintf(buf, sizeof(buf), format, args);
3581 const char * debug_srcname(const char *file)
3583 const char *s = strrchr (file, '/');
3588 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3590 if (subject == NULL)
3593 subject += subject_get_prefix_length(subject);
3595 return g_hash_table_lookup(subject_table, subject);
3598 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3601 if (subject == NULL || *subject == 0)
3603 subject += subject_get_prefix_length(subject);
3604 g_hash_table_insert(subject_table, subject, data);
3607 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3609 if (subject == NULL)
3612 subject += subject_get_prefix_length(subject);
3613 g_hash_table_remove(subject_table, subject);
3617 *\brief Check if a string is prefixed with known (combinations)
3618 * of prefixes. The function assumes that each prefix
3619 * is terminated by zero or exactly _one_ space.
3621 *\param str String to check for a prefixes
3623 *\return int Number of chars in the prefix that should be skipped
3624 * for a "clean" subject line. If no prefix was found, 0
3629 static regex_t u_regex;
3630 static gboolean u_init_;
3633 void utils_free_regex(void)
3643 int subject_get_prefix_length(const gchar *subject)
3646 /*!< Array with allowable reply prefixes regexps. */
3647 static const gchar * const prefixes[] = {
3648 "Re\\:", /* "Re:" */
3649 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3650 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3651 "Aw\\:", /* "Aw:" (German) */
3652 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3653 "Res\\:", /* "Res:" (Brazilian Outlook) */
3654 "Fw\\:", /* "Fw:" Forward */
3655 "Fwd\\:", /* "Fwd:" Forward */
3656 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3657 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3658 "Rif\\:", /* "Rif:" (Italian Outlook) */
3659 "Sv\\:", /* "Sv" (Norwegian) */
3660 "Vs\\:", /* "Vs" (Norwegian) */
3661 "Ad\\:", /* "Ad" (Norwegian) */
3662 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3665 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3669 if (!subject) return 0;
3670 if (!*subject) return 0;
3673 GString *s = g_string_new("");
3675 for (n = 0; n < PREFIXES; n++)
3676 /* Terminate each prefix regexpression by a
3677 * "\ ?" (zero or ONE space), and OR them */
3678 g_string_append_printf(s, "(%s\\ ?)%s",
3683 g_string_prepend(s, "(");
3684 g_string_append(s, ")+"); /* match at least once */
3685 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3688 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3689 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3690 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3691 debug_print("Error compiling regexp %s\n", s->str);
3692 g_string_free(s, TRUE);
3696 g_string_free(s, TRUE);
3700 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3705 /*!< Array with allowable reply prefixes regexps. */
3706 static const gchar * const prefixes[] = {
3708 "antw:", /* "Antw:" (Dutch / German Outlook) */
3709 "aw:", /* "Aw:" (German) */
3710 "antwort:", /* "Antwort:" (German Lotus Notes) */
3711 "res:", /* "Res:" (Brazilian Outlook) */
3712 "fw:", /* "Fw:" Forward */
3713 "fwd:", /* "Fwd:" Forward */
3714 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3715 "odp:", /* "Odp:" Re (Polish Outlook) */
3716 "rif:", /* "Rif:" (Italian Outlook) */
3717 "sv:", /* "Sv" (Norwegian) */
3718 "vs:", /* "Vs" (Norwegian) */
3719 "ad:", /* "Ad" (Norwegian) */
3722 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3725 if (!subject) return 0;
3726 if (!*subject) return 0;
3728 for (n = 0; n < PREFIXES; n++) {
3729 int len = strlen(prefixes[n]);
3730 if (!strncasecmp(subject, prefixes[n], len))
3736 static guint g_stricase_hash(gconstpointer gptr)
3738 guint hash_result = 0;
3741 for (str = gptr; str && *str; str++) {
3742 hash_result += toupper(*str);
3748 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3750 const char *str1 = gptr1;
3751 const char *str2 = gptr2;
3753 return !strcasecmp(str1, str2);
3756 gint g_int_compare(gconstpointer a, gconstpointer b)
3758 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3761 gchar *generate_msgid(gchar *buf, gint len)
3771 lt = localtime_r(&t, &buft);
3773 if (strcmp(buf, "") == 0) {
3774 addr = g_strconcat("@", get_domain_name(), NULL);
3777 addr = g_strconcat("@", buf, NULL);
3780 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3781 lt->tm_year + 1900, lt->tm_mon + 1,
3782 lt->tm_mday, lt->tm_hour,
3783 lt->tm_min, lt->tm_sec,
3784 (guint) rand(), addr);
3791 quote_cmd_argument()
3793 return a quoted string safely usable in argument of a command.
3795 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3798 gint quote_cmd_argument(gchar * result, guint size,
3808 for(p = path ; * p != '\0' ; p ++) {
3810 if (isalnum((guchar)*p) || (* p == '/')) {
3811 if (remaining > 0) {
3817 result[size - 1] = '\0';
3822 if (remaining >= 2) {
3830 result[size - 1] = '\0';
3835 if (remaining > 0) {
3839 result[size - 1] = '\0';
3853 static void g_node_map_recursive(GNode *node, gpointer data)
3855 GNodeMapData *mapdata = (GNodeMapData *) data;
3857 GNodeMapData newmapdata;
3860 newdata = mapdata->func(node->data, mapdata->data);
3861 if (newdata != NULL) {
3862 newnode = g_node_new(newdata);
3863 g_node_append(mapdata->parent, newnode);
3865 newmapdata.parent = newnode;
3866 newmapdata.func = mapdata->func;
3867 newmapdata.data = mapdata->data;
3869 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3873 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3876 GNodeMapData mapdata;
3878 g_return_val_if_fail(node != NULL, NULL);
3879 g_return_val_if_fail(func != NULL, NULL);
3881 root = g_node_new(func(node->data, data));
3883 mapdata.parent = root;
3884 mapdata.func = func;
3885 mapdata.data = data;
3887 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3892 #define HEX_TO_INT(val, hex) \
3896 if ('0' <= c && c <= '9') { \
3898 } else if ('a' <= c && c <= 'f') { \
3899 val = c - 'a' + 10; \
3900 } else if ('A' <= c && c <= 'F') { \
3901 val = c - 'A' + 10; \
3907 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3914 if (hi == -1 || lo == -1)
3917 *out = (hi << 4) + lo;
3921 #define INT_TO_HEX(hex, val) \
3924 hex = '0' + (val); \
3926 hex = 'A' + (val) - 10; \
3929 void get_hex_str(gchar *out, guchar ch)
3933 INT_TO_HEX(hex, ch >> 4);
3935 INT_TO_HEX(hex, ch & 0x0f);
3941 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3943 #define G_PRINT_REF g_print
3947 *\brief Register ref counted pointer. It is based on GBoxed, so should
3948 * work with anything that uses the GType system. The semantics
3949 * are similar to a C++ auto pointer, with the exception that
3950 * C doesn't have automatic closure (calling destructors) when
3951 * exiting a block scope.
3952 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3953 * function directly.
3955 *\return GType A GType type.
3957 GType g_auto_pointer_register(void)
3959 static GType auto_pointer_type;
3960 if (!auto_pointer_type)
3962 g_boxed_type_register_static
3963 ("G_TYPE_AUTO_POINTER",
3964 (GBoxedCopyFunc) g_auto_pointer_copy,
3965 (GBoxedFreeFunc) g_auto_pointer_free);
3966 return auto_pointer_type;
3970 *\brief Structure with g_new() allocated pointer guarded by the
3973 typedef struct AutoPointerRef {
3974 void (*free) (gpointer);
3980 *\brief The auto pointer opaque structure that references the
3981 * pointer guard block.
3983 typedef struct AutoPointer {
3984 AutoPointerRef *ref;
3985 gpointer ptr; /*!< access to protected pointer */
3989 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3993 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3994 * ... when assigning, copying and freeing storage elements
3996 * gtk_list_store_new(N_S_COLUMNS,
3997 * G_TYPE_AUTO_POINTER,
4001 * Template *precious_data = g_new0(Template, 1);
4002 * g_pointer protect = g_auto_pointer_new(precious_data);
4004 * gtk_list_store_set(container, &iter,
4008 * ... the gtk_list_store has copied the pointer and
4009 * ... incremented its reference count, we should free
4010 * ... the auto pointer (in C++ a destructor would do
4011 * ... this for us when leaving block scope)
4013 * g_auto_pointer_free(protect);
4015 * ... gtk_list_store_set() now manages the data. When
4016 * ... *explicitly* requesting a pointer from the list
4017 * ... store, don't forget you get a copy that should be
4018 * ... freed with g_auto_pointer_free() eventually.
4022 *\param pointer Pointer to be guarded.
4024 *\return GAuto * Pointer that should be used in containers with
4027 GAuto *g_auto_pointer_new(gpointer p)
4029 AutoPointerRef *ref;
4035 ref = g_new0(AutoPointerRef, 1);
4036 ptr = g_new0(AutoPointer, 1);
4046 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4052 *\brief Allocate an autopointer using the passed \a free function to
4053 * free the guarded pointer
4055 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4062 aptr = g_auto_pointer_new(p);
4063 aptr->ref->free = free_;
4067 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4069 if (auto_ptr == NULL)
4071 return ((AutoPointer *) auto_ptr)->ptr;
4075 *\brief Copies an auto pointer by. It's mostly not necessary
4076 * to call this function directly, unless you copy/assign
4077 * the guarded pointer.
4079 *\param auto_ptr Auto pointer returned by previous call to
4080 * g_auto_pointer_new_XXX()
4082 *\return gpointer An auto pointer
4084 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4087 AutoPointerRef *ref;
4090 if (auto_ptr == NULL)
4095 newp = g_new0(AutoPointer, 1);
4098 newp->ptr = ref->pointer;
4102 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4108 *\brief Free an auto pointer
4110 void g_auto_pointer_free(GAuto *auto_ptr)
4113 AutoPointerRef *ref;
4115 if (auto_ptr == NULL)
4121 if (--(ref->cnt) == 0) {
4123 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4125 ref->free(ref->pointer);
4130 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4135 void replace_returns(gchar *str)
4140 while (strstr(str, "\n")) {
4141 *strstr(str, "\n") = ' ';
4143 while (strstr(str, "\r")) {
4144 *strstr(str, "\r") = ' ';
4148 /* get_uri_part() - retrieves a URI starting from scanpos.
4149 Returns TRUE if succesful */
4150 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4151 const gchar **bp, const gchar **ep, gboolean hdr)
4154 gint parenthese_cnt = 0;
4156 g_return_val_if_fail(start != NULL, FALSE);
4157 g_return_val_if_fail(scanpos != NULL, FALSE);
4158 g_return_val_if_fail(bp != NULL, FALSE);
4159 g_return_val_if_fail(ep != NULL, FALSE);
4163 /* find end point of URI */
4164 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4165 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4166 !IS_ASCII(*(const guchar *)ep_) ||
4167 strchr("[]{}<>\"", *ep_)) {
4169 } else if (strchr("(", *ep_)) {
4171 } else if (strchr(")", *ep_)) {
4172 if (parenthese_cnt > 0)
4179 /* no punctuation at end of string */
4181 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4182 * should pass some URI type to this function and decide on that whether
4183 * to perform punctuation stripping */
4185 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4187 for (; ep_ - 1 > scanpos + 1 &&
4188 IS_REAL_PUNCT(*(ep_ - 1));
4192 #undef IS_REAL_PUNCT
4199 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4201 while (bp && *bp && g_ascii_isspace(*bp))
4203 return g_strndup(bp, ep - bp);
4206 /* valid mail address characters */
4207 #define IS_RFC822_CHAR(ch) \
4211 !g_ascii_isspace(ch) && \
4212 !strchr("(),;<>\"", (ch)))
4214 /* alphabet and number within 7bit ASCII */
4215 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4216 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4218 static GHashTable *create_domain_tab(void)
4220 static const gchar *toplvl_domains [] = {
4222 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4223 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4224 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4225 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4226 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4227 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4228 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4229 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4230 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4231 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4232 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4233 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4234 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4235 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4236 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4237 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4238 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4239 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4240 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4241 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4242 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4243 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4244 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4245 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4246 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4247 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4250 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4252 g_return_val_if_fail(htab, NULL);
4253 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4254 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4258 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4260 const gint MAX_LVL_DOM_NAME_LEN = 6;
4261 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4262 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4265 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4268 for (p = buf; p < m && first < last; *p++ = *first++)
4272 return g_hash_table_lookup(tab, buf) != NULL;
4275 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4276 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4277 const gchar **bp, const gchar **ep, gboolean hdr)
4279 /* more complex than the uri part because we need to scan back and forward starting from
4280 * the scan position. */
4281 gboolean result = FALSE;
4282 const gchar *bp_ = NULL;
4283 const gchar *ep_ = NULL;
4284 static GHashTable *dom_tab;
4285 const gchar *last_dot = NULL;
4286 const gchar *prelast_dot = NULL;
4287 const gchar *last_tld_char = NULL;
4289 /* the informative part of the email address (describing the name
4290 * of the email address owner) may contain quoted parts. the
4291 * closure stack stores the last encountered quotes. */
4292 gchar closure_stack[128];
4293 gchar *ptr = closure_stack;
4295 g_return_val_if_fail(start != NULL, FALSE);
4296 g_return_val_if_fail(scanpos != NULL, FALSE);
4297 g_return_val_if_fail(bp != NULL, FALSE);
4298 g_return_val_if_fail(ep != NULL, FALSE);
4301 const gchar *start_quote = NULL;
4302 const gchar *end_quote = NULL;
4304 /* go to the real start */
4305 if (start[0] == ',')
4307 if (start[0] == ';')
4309 while (start[0] == '\n' || start[0] == '\r')
4311 while (start[0] == ' ' || start[0] == '\t')
4316 /* check if there are quotes (to skip , in them) */
4317 if (*start == '"') {
4318 start_quote = start;
4320 end_quote = strstr(start, "\"");
4326 /* skip anything between quotes */
4327 if (start_quote && end_quote) {
4332 /* find end (either , or ; or end of line) */
4333 if (strstr(start, ",") && strstr(start, ";"))
4334 *ep = strstr(start,",") < strstr(start, ";")
4335 ? strstr(start, ",") : strstr(start, ";");
4336 else if (strstr(start, ","))
4337 *ep = strstr(start, ",");
4338 else if (strstr(start, ";"))
4339 *ep = strstr(start, ";");
4341 *ep = start+strlen(start);
4343 /* go back to real start */
4344 if (start_quote && end_quote) {
4345 start = start_quote;
4348 /* check there's still an @ in that, or search
4349 * further if possible */
4350 if (strstr(start, "@") && strstr(start, "@") < *ep)
4352 else if (*ep < start+strlen(start)) {
4355 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4363 dom_tab = create_domain_tab();
4364 g_return_val_if_fail(dom_tab, FALSE);
4366 /* scan start of address */
4367 for (bp_ = scanpos - 1;
4368 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4371 /* TODO: should start with an alnum? */
4373 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4376 if (bp_ != scanpos) {
4377 /* scan end of address */
4378 for (ep_ = scanpos + 1;
4379 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4381 prelast_dot = last_dot;
4383 if (*(last_dot + 1) == '.') {
4384 if (prelast_dot == NULL)
4386 last_dot = prelast_dot;
4391 /* TODO: really should terminate with an alnum? */
4392 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4397 if (last_dot == NULL)
4399 if (last_dot >= ep_)
4400 last_dot = prelast_dot;
4401 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4405 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4406 if (*last_tld_char == '?')
4409 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4416 if (!result) return FALSE;
4418 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4419 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4420 && IS_RFC822_CHAR(*(ep_ + 3))) {
4421 /* this informative part with an @ in it is
4422 * followed by the email address */
4425 /* go to matching '>' (or next non-rfc822 char, like \n) */
4426 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4429 /* include the bracket */
4430 if (*ep_ == '>') ep_++;
4432 /* include the leading quote */
4440 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4441 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4444 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4445 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4448 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4449 #define IN_STACK() (ptr > closure_stack)
4450 /* has underrun check */
4451 #define POP_STACK() if(IN_STACK()) --ptr
4452 /* has overrun check */
4453 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4454 /* has underrun check */
4455 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4459 /* scan for the informative part. */
4460 for (bp_ -= 2; bp_ >= start; bp_--) {
4461 /* if closure on the stack keep scanning */
4462 if (PEEK_STACK() == *bp_) {
4466 if (*bp_ == '\'' || *bp_ == '"') {
4471 /* if nothing in the closure stack, do the special conditions
4472 * the following if..else expression simply checks whether
4473 * a token is acceptable. if not acceptable, the clause
4474 * should terminate the loop with a 'break' */
4475 if (!PEEK_STACK()) {
4477 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4478 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4479 /* hyphens are allowed, but only in
4481 } else if (strchr(" \"'", *bp_)) {
4482 /* but anything not being a punctiation
4485 break; /* anything else is rejected */
4492 /* scan forward (should start with an alnum) */
4493 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4509 #undef IS_ASCII_ALNUM
4510 #undef IS_RFC822_CHAR
4512 gchar *make_email_string(const gchar *bp, const gchar *ep)
4514 /* returns a mailto: URI; mailto: is also used to detect the
4515 * uri type later on in the button_pressed signal handler */
4519 tmp = g_strndup(bp, ep - bp);
4520 result = g_strconcat("mailto:", tmp, NULL);
4526 gchar *make_http_string(const gchar *bp, const gchar *ep)
4528 /* returns an http: URI; */
4532 while (bp && *bp && g_ascii_isspace(*bp))
4534 tmp = g_strndup(bp, ep - bp);
4535 result = g_strconcat("http://", tmp, NULL);
4541 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4543 FILE *fp = fopen(path, "rb");
4544 gchar buf[BUFFSIZE];
4545 gchar *result = NULL;
4548 while (fgets(buf, sizeof (buf), fp) != NULL) {
4549 gchar **parts = g_strsplit(buf, ";", 3);
4550 gchar *trimmed = parts[0];
4551 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4553 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4554 trimmed[strlen(trimmed)-1] = '\0';
4556 if (!strcmp(trimmed, type)) {
4557 gboolean needsterminal = FALSE;
4558 if (parts[2] && strstr(parts[2], "needsterminal")) {
4559 needsterminal = TRUE;
4561 if (parts[2] && strstr(parts[2], "test=")) {
4562 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4563 gchar *testcmd = orig_testcmd;
4564 if (strstr(testcmd,";"))
4565 *(strstr(testcmd,";")) = '\0';
4566 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4568 while (testcmd[strlen(testcmd)-1] == '\n')
4569 testcmd[strlen(testcmd)-1] = '\0';
4570 while (testcmd[strlen(testcmd)-1] == '\r')
4571 testcmd[strlen(testcmd)-1] = '\0';
4572 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4573 testcmd[strlen(testcmd)-1] = '\0';
4575 if (strstr(testcmd, "%s")) {
4576 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4577 gint res = system(tmp);
4579 g_free(orig_testcmd);
4586 gint res = system(testcmd);
4587 g_free(orig_testcmd);
4597 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4599 while (trimmed[strlen(trimmed)-1] == '\n')
4600 trimmed[strlen(trimmed)-1] = '\0';
4601 while (trimmed[strlen(trimmed)-1] == '\r')
4602 trimmed[strlen(trimmed)-1] = '\0';
4603 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4604 trimmed[strlen(trimmed)-1] = '\0';
4605 result = g_strdup(trimmed);
4608 /* if there are no single quotes around %s, add them.
4609 * '.*%s.*' is ok, as in display 'png:%s'
4611 if (strstr(result, "%s")
4612 && !(strstr(result, "'") < strstr(result,"%s") &&
4613 strstr(strstr(result,"%s"), "'"))) {
4614 gchar *start = g_strdup(result);
4615 gchar *end = g_strdup(strstr(result, "%s")+2);
4617 *strstr(start, "%s") = '\0';
4618 tmp = g_strconcat(start,"'%s'",end, NULL);
4624 if (needsterminal) {
4625 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4636 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4638 gchar *result = NULL;
4642 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4643 result = mailcap_get_command_in_file(path, type, file_to_open);
4647 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4651 void mailcap_update_default(const gchar *type, const gchar *command)
4653 gchar *path = NULL, *outpath = NULL;
4654 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4655 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4656 FILE *fp = fopen(path, "rb");
4657 FILE *outfp = fopen(outpath, "wb");
4658 gchar buf[BUFFSIZE];
4659 gboolean err = FALSE;
4667 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4668 gchar **parts = g_strsplit(buf, ";", 3);
4669 gchar *trimmed = parts[0];
4670 while (trimmed[0] == ' ')
4672 while (trimmed[strlen(trimmed)-1] == ' ')
4673 trimmed[strlen(trimmed)-1] = '\0';
4675 if (!strcmp(trimmed, type)) {
4680 if(fputs(buf, outfp) == EOF) {
4687 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4693 if (fclose(outfp) == EOF)
4697 g_rename(outpath, path);
4703 gint copy_dir(const gchar *src, const gchar *dst)
4708 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4709 g_warning("failed to open directory: %s\n", src);
4713 if (make_dir(dst) < 0)
4716 while ((name = g_dir_read_name(dir)) != NULL) {
4717 gchar *old_file, *new_file;
4718 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4719 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4720 debug_print("copying: %s -> %s\n", old_file, new_file);
4721 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4722 gint r = copy_file(old_file, new_file, TRUE);
4729 /* Windows has no symlinks. Or well, Vista seems to
4730 have something like this but the semantics might be
4731 different. Thus we don't use it under Windows. */
4732 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4735 gchar *target = g_file_read_link(old_file, &error);
4737 r = symlink(target, new_file);
4744 #endif /*G_OS_WIN32*/
4745 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4746 gint r = copy_dir(old_file, new_file);
4757 /* crude test to see if a file is an email. */
4758 gboolean file_is_email (const gchar *filename)
4764 if (filename == NULL)
4766 if ((fp = g_fopen(filename, "rb")) == NULL)
4768 while (i < 60 && score < 3
4769 && fgets(buffer, sizeof (buffer), fp) > 0) {
4770 if (!strncmp(buffer, "From:", strlen("From:")))
4772 if (!strncmp(buffer, "To:", strlen("To:")))
4774 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4779 return (score >= 3);
4782 gboolean sc_g_list_bigger(GList *list, gint max)
4786 while (cur && i <= max+1) {
4793 gboolean sc_g_slist_bigger(GSList *list, gint max)
4797 while (cur && i <= max+1) {
4804 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4805 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4806 NULL, NULL, NULL, NULL, NULL, NULL};
4807 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4808 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4809 NULL, NULL, NULL, NULL, NULL, NULL};
4811 gint daynames_len[] = {0,0,0,0,0,0,0};
4812 gint monthnames_len[] = {0,0,0,0,0,0,
4814 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4815 gint s_monthnames_len[] = {0,0,0,0,0,0,
4817 const gchar *s_am_up = NULL;
4818 const gchar *s_pm_up = NULL;
4819 const gchar *s_am_low = NULL;
4820 const gchar *s_pm_low = NULL;
4822 gint s_am_up_len = 0;
4823 gint s_pm_up_len = 0;
4824 gint s_am_low_len = 0;
4825 gint s_pm_low_len = 0;
4827 const gchar *def_loc_format = NULL;
4828 const gchar *date_loc_format = NULL;
4829 const gchar *time_loc_format = NULL;
4830 const gchar *time_am_pm = NULL;
4832 static gboolean time_names_init_done = FALSE;
4834 static void init_time_names(void)
4838 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4839 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4840 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4841 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4842 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4843 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4844 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4846 monthnames[0] = Q_("Complete month name for use by strftime|January");
4847 monthnames[1] = Q_("Complete month name for use by strftime|February");
4848 monthnames[2] = Q_("Complete month name for use by strftime|March");
4849 monthnames[3] = Q_("Complete month name for use by strftime|April");
4850 monthnames[4] = Q_("Complete month name for use by strftime|May");
4851 monthnames[5] = Q_("Complete month name for use by strftime|June");
4852 monthnames[6] = Q_("Complete month name for use by strftime|July");
4853 monthnames[7] = Q_("Complete month name for use by strftime|August");
4854 monthnames[8] = Q_("Complete month name for use by strftime|September");
4855 monthnames[9] = Q_("Complete month name for use by strftime|October");
4856 monthnames[10] = Q_("Complete month name for use by strftime|November");
4857 monthnames[11] = Q_("Complete month name for use by strftime|December");
4859 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4860 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4861 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4862 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4863 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4864 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4865 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4867 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4868 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4869 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4870 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4871 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4872 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4873 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4874 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4875 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4876 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4877 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4878 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4880 for (i = 0; i < 7; i++) {
4881 daynames_len[i] = strlen(daynames[i]);
4882 s_daynames_len[i] = strlen(s_daynames[i]);
4884 for (i = 0; i < 12; i++) {
4885 monthnames_len[i] = strlen(monthnames[i]);
4886 s_monthnames_len[i] = strlen(s_monthnames[i]);
4889 s_am_up = Q_("For use by strftime (morning)|AM");
4890 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4891 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4892 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4894 s_am_up_len = strlen(s_am_up);
4895 s_pm_up_len = strlen(s_pm_up);
4896 s_am_low_len = strlen(s_am_low);
4897 s_pm_low_len = strlen(s_pm_low);
4899 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4900 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4901 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4903 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4905 time_names_init_done = TRUE;
4908 #define CHECK_SIZE() { \
4909 total_done += len; \
4910 if (total_done >= buflen) { \
4911 buf[buflen-1] = '\0'; \
4916 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4918 gchar *curpos = buf;
4919 gint total_done = 0;
4920 gchar subbuf[64], subfmt[64];
4921 static time_t last_tzset = (time_t)0;
4923 if (!time_names_init_done)
4926 if (format == NULL || lt == NULL)
4929 if (last_tzset != time(NULL)) {
4931 last_tzset = time(NULL);
4934 if (*format == '%') {
4935 gint len = 0, tmp = 0;
4939 len = 1; CHECK_SIZE();
4943 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4944 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4947 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4948 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4952 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4953 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4956 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4957 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4960 fast_strftime(subbuf, 64, def_loc_format, lt);
4961 len = strlen(subbuf); CHECK_SIZE();
4962 strncpy2(curpos, subbuf, buflen - total_done);
4965 total_done += 2; CHECK_SIZE();
4966 tmp = (lt->tm_year + 1900)/100;
4967 *curpos++ = '0'+(tmp / 10);
4968 *curpos++ = '0'+(tmp % 10);
4971 total_done += 2; CHECK_SIZE();
4972 *curpos++ = '0'+(lt->tm_mday / 10);
4973 *curpos++ = '0'+(lt->tm_mday % 10);
4976 total_done += 8; CHECK_SIZE();
4977 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4978 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4980 *curpos++ = '0'+(lt->tm_mday / 10);
4981 *curpos++ = '0'+(lt->tm_mday % 10);
4983 tmp = lt->tm_year%100;
4984 *curpos++ = '0'+(tmp / 10);
4985 *curpos++ = '0'+(tmp % 10);
4988 len = 2; CHECK_SIZE();
4989 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4992 len = 10; CHECK_SIZE();
4993 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4994 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4997 total_done += 2; CHECK_SIZE();
4998 *curpos++ = '0'+(lt->tm_hour / 10);
4999 *curpos++ = '0'+(lt->tm_hour % 10);
5002 total_done += 2; CHECK_SIZE();
5008 *curpos++ = '0'+(tmp / 10);
5009 *curpos++ = '0'+(tmp % 10);
5012 len = 3; CHECK_SIZE();
5013 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5016 len = 2; CHECK_SIZE();
5017 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5020 len = 2; CHECK_SIZE();
5026 snprintf(curpos, buflen - total_done, "%2d", tmp);
5029 total_done += 2; CHECK_SIZE();
5030 tmp = lt->tm_mon + 1;
5031 *curpos++ = '0'+(tmp / 10);
5032 *curpos++ = '0'+(tmp % 10);
5035 total_done += 2; CHECK_SIZE();
5036 *curpos++ = '0'+(lt->tm_min / 10);
5037 *curpos++ = '0'+(lt->tm_min % 10);
5040 len = 1; CHECK_SIZE();
5044 if (lt->tm_hour >= 12) {
5045 len = s_pm_up_len; CHECK_SIZE();
5046 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5048 len = s_am_up_len; CHECK_SIZE();
5049 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5053 if (lt->tm_hour >= 12) {
5054 len = s_pm_low_len; CHECK_SIZE();
5055 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5057 len = s_am_low_len; CHECK_SIZE();
5058 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5062 fast_strftime(subbuf, 64, time_am_pm, lt);
5063 len = strlen(subbuf); CHECK_SIZE();
5064 strncpy2(curpos, subbuf, buflen - total_done);
5067 total_done += 5; CHECK_SIZE();
5068 *curpos++ = '0'+(lt->tm_hour / 10);
5069 *curpos++ = '0'+(lt->tm_hour % 10);
5071 *curpos++ = '0'+(lt->tm_min / 10);
5072 *curpos++ = '0'+(lt->tm_min % 10);
5075 snprintf(subbuf, 64, "%ld", mktime(lt));
5076 len = strlen(subbuf); CHECK_SIZE();
5077 strncpy2(curpos, subbuf, buflen - total_done);
5080 total_done += 2; CHECK_SIZE();
5081 *curpos++ = '0'+(lt->tm_sec / 10);
5082 *curpos++ = '0'+(lt->tm_sec % 10);
5085 len = 1; CHECK_SIZE();
5089 total_done += 8; CHECK_SIZE();
5090 *curpos++ = '0'+(lt->tm_hour / 10);
5091 *curpos++ = '0'+(lt->tm_hour % 10);
5093 *curpos++ = '0'+(lt->tm_min / 10);
5094 *curpos++ = '0'+(lt->tm_min % 10);
5096 *curpos++ = '0'+(lt->tm_sec / 10);
5097 *curpos++ = '0'+(lt->tm_sec % 10);
5100 len = 1; CHECK_SIZE();
5101 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5104 len = 1; CHECK_SIZE();
5105 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5108 fast_strftime(subbuf, 64, date_loc_format, lt);
5109 len = strlen(subbuf); CHECK_SIZE();
5110 strncpy2(curpos, subbuf, buflen - total_done);
5113 fast_strftime(subbuf, 64, time_loc_format, lt);
5114 len = strlen(subbuf); CHECK_SIZE();
5115 strncpy2(curpos, subbuf, buflen - total_done);
5118 total_done += 2; CHECK_SIZE();
5119 tmp = lt->tm_year%100;
5120 *curpos++ = '0'+(tmp / 10);
5121 *curpos++ = '0'+(tmp % 10);
5124 len = 4; CHECK_SIZE();
5125 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5135 /* let these complicated ones be done with the libc */
5136 snprintf(subfmt, 64, "%%%c", *format);
5137 strftime(subbuf, 64, subfmt, lt);
5138 len = strlen(subbuf); CHECK_SIZE();
5139 strncpy2(curpos, subbuf, buflen - total_done);
5143 /* let these complicated modifiers be done with the libc */
5144 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5145 strftime(subbuf, 64, subfmt, lt);
5146 len = strlen(subbuf); CHECK_SIZE();
5147 strncpy2(curpos, subbuf, buflen - total_done);
5151 if (format && *format)
5152 g_warning("format error (%c)", *format);
5159 int len = 1; CHECK_SIZE();
5160 *curpos++ = *format++;
5167 gboolean prefs_common_get_use_shred(void);
5171 #define WEXITSTATUS(x) (x)
5174 int claws_unlink(const gchar *filename)
5177 static int found_shred = -1;
5178 static const gchar *args[4];
5180 if (filename == NULL)
5183 if (prefs_common_get_use_shred()) {
5184 if (found_shred == -1) {
5186 args[0] = g_find_program_in_path("shred");
5187 debug_print("found shred: %s\n", args[0]);
5188 found_shred = (args[0] != NULL) ? 1:0;
5192 if (found_shred == 1) {
5193 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5194 if (s.st_nlink == 1) {
5197 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5198 NULL, NULL, NULL, NULL, &status, NULL);
5199 debug_print("%s %s exited with status %d\n",
5200 args[0], filename, WEXITSTATUS(status));
5201 if (truncate(filename, 0) < 0)
5202 g_warning("couln't truncate");
5207 return g_unlink(filename);