2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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 int g_open(const gchar *filename, int flags, int mode)
202 char *name = g_win32_locale_filename_from_utf8(filename);
203 int fd = open(name, flags, mode);
207 return open(filename, flags, mode);
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
214 gint mkstemp_name(gchar *template, gchar **name_used)
216 static gulong count=0; /* W32-_mktemp only supports up to 27
220 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222 (S_IRUSR | S_IWUSR));
224 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
226 perror(g_strdup_printf("cant create %s",*name_used));
232 #endif /* G_OS_WIN32 */
235 gint mkstemp(gchar *template)
238 gint res = mkstemp_name(template, &dummyname);
242 #endif /* G_OS_WIN32 */
244 void list_free_strings(GList *list)
246 list = g_list_first(list);
248 while (list != NULL) {
254 void slist_free_strings(GSList *list)
256 while (list != NULL) {
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
267 void hash_free_strings(GHashTable *table)
269 g_hash_table_foreach(table, hash_free_strings_func, NULL);
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
274 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
277 guint str_case_hash(gconstpointer key)
279 const gchar *p = key;
283 h = g_ascii_tolower(h);
284 for (p += 1; *p != '\0'; p++)
285 h = (h << 5) - h + g_ascii_tolower(*p);
291 void ptr_array_free_strings(GPtrArray *array)
296 cm_return_if_fail(array != NULL);
298 for (i = 0; i < array->len; i++) {
299 str = g_ptr_array_index(array, i);
304 gboolean str_find(const gchar *haystack, const gchar *needle)
306 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
311 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
314 gint to_number(const gchar *nstr)
316 register const gchar *p;
318 if (*nstr == '\0') return -1;
320 for (p = nstr; *p != '\0'; p++)
321 if (!g_ascii_isdigit(*p)) return -1;
326 /* convert integer into string,
327 nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
330 g_snprintf(nstr, 11, "%d", n);
334 /* convert integer into string */
337 static gchar nstr[11];
339 return itos_buf(nstr, n);
342 #define divide(num,divisor,i,d) \
344 i = num >> divisor; \
345 d = num & ((1<<divisor)-1); \
346 d = (d*100) >> divisor; \
351 * \brief Convert a given size in bytes in a human-readable string
353 * \param size The size expressed in bytes to convert in string
354 * \return The string that respresents the size in an human-readable way
356 gchar *to_human_readable(goffset size)
358 static gchar str[14];
359 static gchar *b_format = NULL, *kb_format = NULL,
360 *mb_format = NULL, *gb_format = NULL;
361 register int t = 0, r = 0;
362 if (b_format == NULL) {
364 kb_format = _("%d.%02dKB");
365 mb_format = _("%d.%02dMB");
366 gb_format = _("%.2fGB");
369 if (size < (goffset)1024) {
370 g_snprintf(str, sizeof(str), b_format, (gint)size);
372 } else if (size >> 10 < (goffset)1024) {
373 divide(size, 10, t, r);
374 g_snprintf(str, sizeof(str), kb_format, t, r);
376 } else if (size >> 20 < (goffset)1024) {
377 divide(size, 20, t, r);
378 g_snprintf(str, sizeof(str), mb_format, t, r);
381 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
386 /* strcmp with NULL-checking */
387 gint strcmp2(const gchar *s1, const gchar *s2)
389 if (s1 == NULL || s2 == NULL)
392 return strcmp(s1, s2);
394 /* strstr with NULL-checking */
395 gchar *strstr2(const gchar *s1, const gchar *s2)
397 if (s1 == NULL || s2 == NULL)
400 return strstr(s1, s2);
403 gint path_cmp(const gchar *s1, const gchar *s2)
408 gchar *s1buf, *s2buf;
411 if (s1 == NULL || s2 == NULL) return -1;
412 if (*s1 == '\0' || *s2 == '\0') return -1;
415 s1buf = g_strdup (s1);
416 s2buf = g_strdup (s2);
417 subst_char (s1buf, '/', G_DIR_SEPARATOR);
418 subst_char (s2buf, '/', G_DIR_SEPARATOR);
421 #endif /* !G_OS_WIN32 */
426 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
427 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
429 rc = strncmp(s1, s2, MAX(len1, len2));
433 #endif /* !G_OS_WIN32 */
437 /* remove trailing return code */
438 gchar *strretchomp(gchar *str)
442 if (!*str) return str;
444 for (s = str + strlen(str) - 1;
445 s >= str && (*s == '\n' || *s == '\r');
452 /* remove trailing character */
453 gchar *strtailchomp(gchar *str, gchar tail_char)
457 if (!*str) return str;
458 if (tail_char == '\0') return str;
460 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
466 /* remove CR (carriage return) */
467 gchar *strcrchomp(gchar *str)
471 if (!*str) return str;
473 s = str + strlen(str) - 1;
474 if (*s == '\n' && s > str && *(s - 1) == '\r') {
482 gint file_strip_crs(const gchar *file)
484 FILE *fp = NULL, *outfp = NULL;
486 gchar *out = get_tmp_file();
490 fp = g_fopen(file, "rb");
494 outfp = g_fopen(out, "wb");
500 while (fgets(buf, sizeof (buf), fp) != NULL) {
502 if (fputs(buf, outfp) == EOF) {
510 if (fclose(outfp) == EOF) {
514 if (move_file(out, file, TRUE) < 0)
526 /* Similar to `strstr' but this function ignores the case of both strings. */
527 gchar *strcasestr(const gchar *haystack, const gchar *needle)
529 register size_t haystack_len, needle_len;
531 haystack_len = strlen(haystack);
532 needle_len = strlen(needle);
534 if (haystack_len < needle_len || needle_len == 0)
537 while (haystack_len >= needle_len) {
538 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
539 return (gchar *)haystack;
549 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
550 gconstpointer needle, size_t needlelen)
552 const gchar *haystack_ = (const gchar *)haystack;
553 const gchar *needle_ = (const gchar *)needle;
554 const gchar *haystack_cur = (const gchar *)haystack;
555 size_t haystack_left = haystacklen;
558 return memchr(haystack_, *needle_, haystacklen);
560 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
562 if (haystacklen - (haystack_cur - haystack_) < needlelen)
564 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
565 return (gpointer)haystack_cur;
568 haystack_left = haystacklen - (haystack_cur - haystack_);
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 static void trim_subject_for_compare(gchar *str)
619 eliminate_parenthesis(str, '[', ']');
620 eliminate_parenthesis(str, '(', ')');
623 srcp = str + subject_get_prefix_length(str);
625 memmove(str, srcp, strlen(srcp) + 1);
628 static void trim_subject_for_sort(gchar *str)
634 srcp = str + subject_get_prefix_length(str);
636 memmove(str, srcp, strlen(srcp) + 1);
639 /* compare subjects */
640 gint subject_compare(const gchar *s1, const gchar *s2)
644 if (!s1 || !s2) return -1;
645 if (!*s1 || !*s2) return -1;
647 Xstrdup_a(str1, s1, return -1);
648 Xstrdup_a(str2, s2, return -1);
650 trim_subject_for_compare(str1);
651 trim_subject_for_compare(str2);
653 if (!*str1 || !*str2) return -1;
655 return strcmp(str1, str2);
658 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
662 if (!s1 || !s2) return -1;
664 Xstrdup_a(str1, s1, return -1);
665 Xstrdup_a(str2, s2, return -1);
667 trim_subject_for_sort(str1);
668 trim_subject_for_sort(str2);
670 return g_utf8_collate(str1, str2);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void extract_quote(gchar *str, gchar quote_chr)
795 if ((str = strchr(str, quote_chr))) {
797 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798 memmove(p - 1, p, strlen(p) + 1);
803 memmove(str, str + 1, p - str);
808 void eliminate_address_comment(gchar *str)
810 register gchar *srcp, *destp;
815 while ((destp = strchr(destp, '"'))) {
816 if ((srcp = strchr(destp + 1, '"'))) {
821 while (g_ascii_isspace(*srcp)) srcp++;
822 memmove(destp, srcp, strlen(srcp) + 1);
832 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
838 else if (*srcp == ')')
844 while (g_ascii_isspace(*srcp)) srcp++;
845 memmove(destp, srcp, strlen(srcp) + 1);
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
851 gboolean in_quote = FALSE;
854 if (*str == c && !in_quote)
856 if (*str == quote_chr)
864 void extract_address(gchar *str)
866 eliminate_address_comment(str);
867 if (strchr_with_skip_quote(str, '"', '<'))
868 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
872 void extract_list_id_str(gchar *str)
874 if (strchr_with_skip_quote(str, '"', '<'))
875 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
879 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
884 if (!str) return addr_list;
886 Xstrdup_a(work, str, return addr_list);
889 eliminate_address_comment(work);
892 while (workp && *workp) {
895 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
901 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
902 extract_parenthesis_with_skip_quote
903 (workp, '"', '<', '>');
907 addr_list = g_slist_append(addr_list, g_strdup(workp));
915 GSList *address_list_append(GSList *addr_list, const gchar *str)
917 return address_list_append_real(addr_list, str, TRUE);
920 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
922 return address_list_append_real(addr_list, str, FALSE);
925 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
929 if (!str) return msgid_list;
932 while (strp && *strp) {
933 const gchar *start, *end;
936 if ((start = strchr(strp, '<')) != NULL) {
937 end = strchr(start + 1, '>');
942 msgid = g_strndup(start + 1, end - start - 1);
945 msgid_list = g_slist_prepend(msgid_list, msgid);
955 GSList *references_list_append(GSList *msgid_list, const gchar *str)
959 list = references_list_prepend(NULL, str);
960 list = g_slist_reverse(list);
961 msgid_list = g_slist_concat(msgid_list, list);
966 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
971 if (!str) return group_list;
973 Xstrdup_a(work, str, return group_list);
977 while (workp && *workp) {
980 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
988 group_list = g_slist_append(group_list,
997 GList *add_history(GList *list, const gchar *str)
1001 cm_return_val_if_fail(str != NULL, list);
1003 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1006 list = g_list_remove(list, old->data);
1007 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1010 last = g_list_last(list);
1013 list = g_list_remove(list, last->data);
1017 list = g_list_prepend(list, g_strdup(str));
1022 void remove_return(gchar *str)
1024 register gchar *p = str;
1027 if (*p == '\n' || *p == '\r')
1028 memmove(p, p + 1, strlen(p));
1034 void remove_space(gchar *str)
1036 register gchar *p = str;
1041 while (g_ascii_isspace(*(p + spc)))
1044 memmove(p, p + spc, strlen(p + spc) + 1);
1050 void unfold_line(gchar *str)
1052 register gchar *p = str;
1056 if (*p == '\n' || *p == '\r') {
1059 while (g_ascii_isspace(*(p + spc)))
1062 memmove(p, p + spc, strlen(p + spc) + 1);
1068 void subst_char(gchar *str, gchar orig, gchar subst)
1070 register gchar *p = str;
1079 void subst_chars(gchar *str, gchar *orig, gchar subst)
1081 register gchar *p = str;
1084 if (strchr(orig, *p) != NULL)
1090 void subst_for_filename(gchar *str)
1095 subst_chars(str, "\t\r\n\\/*:", '_');
1097 subst_chars(str, "\t\r\n\\/*", '_');
1101 void subst_for_shellsafe_filename(gchar *str)
1105 subst_for_filename(str);
1106 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1109 gboolean is_ascii_str(const gchar *str)
1111 const guchar *p = (const guchar *)str;
1113 while (*p != '\0') {
1114 if (*p != '\t' && *p != ' ' &&
1115 *p != '\r' && *p != '\n' &&
1116 (*p < 32 || *p >= 127))
1124 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1126 gchar * position = NULL;
1127 gchar * tmp_pos = NULL;
1130 if (quote_chars == NULL)
1133 for (i = 0; i < strlen(quote_chars); i++) {
1134 tmp_pos = strrchr (str, quote_chars[i]);
1136 || (tmp_pos != NULL && position <= tmp_pos) )
1142 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1144 const gchar *first_pos;
1145 const gchar *last_pos;
1146 const gchar *p = str;
1147 gint quote_level = -1;
1149 /* speed up line processing by only searching to the last '>' */
1150 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1151 /* skip a line if it contains a '<' before the initial '>' */
1152 if (memchr(str, '<', first_pos - str) != NULL)
1154 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1158 while (p <= last_pos) {
1159 while (p < last_pos) {
1160 if (g_ascii_isspace(*p))
1166 if (strchr(quote_chars, *p))
1168 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1169 /* any characters are allowed except '-','<' and space */
1170 while (*p != '-' && *p != '<'
1171 && !strchr(quote_chars, *p)
1172 && !g_ascii_isspace(*p)
1175 if (strchr(quote_chars, *p))
1187 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1189 const gchar *p = str, *q;
1190 gint cur_line = 0, len;
1192 while ((q = strchr(p, '\n')) != NULL) {
1194 if (len > max_chars) {
1204 if (len > max_chars) {
1213 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1215 gchar * position = NULL;
1216 gchar * tmp_pos = NULL;
1219 if (quote_chars == NULL)
1222 for (i = 0; i < strlen(quote_chars); i++) {
1223 tmp_pos = strchr (str, quote_chars[i]);
1225 || (tmp_pos != NULL && position >= tmp_pos) )
1231 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1233 register guint haystack_len, needle_len;
1234 gboolean in_squote = FALSE, in_dquote = FALSE;
1236 haystack_len = strlen(haystack);
1237 needle_len = strlen(needle);
1239 if (haystack_len < needle_len || needle_len == 0)
1242 while (haystack_len >= needle_len) {
1243 if (!in_squote && !in_dquote &&
1244 !strncmp(haystack, needle, needle_len))
1245 return (gchar *)haystack;
1247 /* 'foo"bar"' -> foo"bar"
1248 "foo'bar'" -> foo'bar' */
1249 if (*haystack == '\'') {
1252 else if (!in_dquote)
1254 } else if (*haystack == '\"') {
1257 else if (!in_squote)
1268 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1271 GSList *string_list = NULL, *slist;
1272 gchar **str_array, *s, *new_str;
1273 guint i, n = 1, len;
1275 cm_return_val_if_fail(str != NULL, NULL);
1276 cm_return_val_if_fail(delim != NULL, NULL);
1279 max_tokens = G_MAXINT;
1281 s = strstr_with_skip_quote(str, delim);
1283 guint delimiter_len = strlen(delim);
1287 new_str = g_strndup(str, len);
1289 if (new_str[0] == '\'' || new_str[0] == '\"') {
1290 if (new_str[len - 1] == new_str[0]) {
1291 new_str[len - 1] = '\0';
1292 memmove(new_str, new_str + 1, len - 1);
1295 string_list = g_slist_prepend(string_list, new_str);
1297 str = s + delimiter_len;
1298 s = strstr_with_skip_quote(str, delim);
1299 } while (--max_tokens && s);
1303 new_str = g_strdup(str);
1304 if (new_str[0] == '\'' || new_str[0] == '\"') {
1306 if (new_str[len - 1] == new_str[0]) {
1307 new_str[len - 1] = '\0';
1308 memmove(new_str, new_str + 1, len - 1);
1311 string_list = g_slist_prepend(string_list, new_str);
1315 str_array = g_new(gchar*, n);
1319 str_array[i--] = NULL;
1320 for (slist = string_list; slist; slist = slist->next)
1321 str_array[i--] = slist->data;
1323 g_slist_free(string_list);
1328 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1330 gchar *abbrev_group;
1332 const gchar *p = group;
1335 cm_return_val_if_fail(group != NULL, NULL);
1337 last = group + strlen(group);
1338 abbrev_group = ap = g_malloc(strlen(group) + 1);
1343 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1345 while (*p != '.') p++;
1348 return abbrev_group;
1353 return abbrev_group;
1356 gchar *trim_string(const gchar *str, gint len)
1358 const gchar *p = str;
1363 if (!str) return NULL;
1364 if (strlen(str) <= len)
1365 return g_strdup(str);
1366 if (g_utf8_validate(str, -1, NULL) == FALSE)
1367 return g_strdup(str);
1369 while (*p != '\0') {
1370 mb_len = g_utf8_skip[*(guchar *)p];
1373 else if (new_len + mb_len > len)
1380 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1381 return g_strconcat(new_str, "...", NULL);
1384 GList *uri_list_extract_filenames(const gchar *uri_list)
1386 GList *result = NULL;
1388 gchar *escaped_utf8uri;
1394 while (g_ascii_isspace(*p)) p++;
1395 if (!strncmp(p, "file:", 5)) {
1398 while (*q && *q != '\n' && *q != '\r') q++;
1401 gchar *file, *locale_file = NULL;
1403 while (q > p && g_ascii_isspace(*q))
1405 Xalloca(escaped_utf8uri, q - p + 2,
1407 Xalloca(file, q - p + 2,
1410 strncpy(escaped_utf8uri, p, q - p + 1);
1411 escaped_utf8uri[q - p + 1] = '\0';
1412 decode_uri(file, escaped_utf8uri);
1414 * g_filename_from_uri() rejects escaped/locale encoded uri
1415 * string which come from Nautilus.
1418 if (g_utf8_validate(file, -1, NULL))
1420 = conv_codeset_strdup(
1423 conv_get_locale_charset_str());
1425 locale_file = g_strdup(file + 5);
1427 locale_file = g_filename_from_uri(file, NULL, NULL);
1429 result = g_list_append(result, locale_file);
1433 p = strchr(p, '\n');
1440 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1443 static gint axtoi(const gchar *hexstr)
1445 gint hi, lo, result;
1448 if ('0' <= hi && hi <= '9') {
1451 if ('a' <= hi && hi <= 'f') {
1454 if ('A' <= hi && hi <= 'F') {
1459 if ('0' <= lo && lo <= '9') {
1462 if ('a' <= lo && lo <= 'f') {
1465 if ('A' <= lo && lo <= 'F') {
1468 result = lo + (16 * hi);
1472 gboolean is_uri_string(const gchar *str)
1474 while (str && *str && g_ascii_isspace(*str))
1476 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1477 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1478 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1479 g_ascii_strncasecmp(str, "www.", 4) == 0);
1482 gchar *get_uri_path(const gchar *uri)
1484 while (uri && *uri && g_ascii_isspace(*uri))
1486 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1487 return (gchar *)(uri + 7);
1488 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1489 return (gchar *)(uri + 8);
1490 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1491 return (gchar *)(uri + 6);
1493 return (gchar *)uri;
1496 gint get_uri_len(const gchar *str)
1500 if (is_uri_string(str)) {
1501 for (p = str; *p != '\0'; p++) {
1502 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1511 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1512 * plusses, and escape characters are used)
1514 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1516 gchar *dec = decoded_uri;
1517 const gchar *enc = encoded_uri;
1522 if (isxdigit((guchar)enc[0]) &&
1523 isxdigit((guchar)enc[1])) {
1529 if (with_plus && *enc == '+')
1541 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1543 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1546 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1548 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1549 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1553 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1554 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1558 const gchar *forbidden_uris[] = { ".gnupg/",
1564 gint num_attach = 0;
1565 gchar **my_att = NULL;
1567 Xstrdup_a(tmp_mailto, mailto, return -1);
1569 if (!strncmp(tmp_mailto, "mailto:", 7))
1572 p = strchr(tmp_mailto, '?');
1579 *to = decode_uri_gdup(tmp_mailto);
1581 my_att = g_malloc(sizeof(char *));
1585 gchar *field, *value;
1602 if (*value == '\0') continue;
1604 if (from && !g_ascii_strcasecmp(field, "from")) {
1606 *from = decode_uri_gdup(value);
1608 gchar *tmp = decode_uri_gdup(value);
1609 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1613 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1615 *cc = decode_uri_gdup(value);
1617 gchar *tmp = decode_uri_gdup(value);
1618 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1622 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1624 *bcc = decode_uri_gdup(value);
1626 gchar *tmp = decode_uri_gdup(value);
1627 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1631 } else if (subject && !*subject &&
1632 !g_ascii_strcasecmp(field, "subject")) {
1633 *subject = decode_uri_gdup(value);
1634 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1635 *body = decode_uri_gdup(value);
1636 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1637 gchar *tmp = decode_uri_gdup(value);
1638 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1639 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1643 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1645 gchar *tmp = decode_uri_gdup(value);
1646 for (; forbidden_uris[i]; i++) {
1647 if (strstr(tmp, forbidden_uris[i])) {
1648 g_print("Refusing to attach '%s', potential private data leak\n",
1656 /* attach is correct */
1658 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1659 my_att[num_attach-1] = tmp;
1660 my_att[num_attach] = NULL;
1662 } else if (inreplyto && !*inreplyto &&
1663 !g_ascii_strcasecmp(field, "in-reply-to")) {
1664 *inreplyto = decode_uri_gdup(value);
1675 #include <windows.h>
1676 #ifndef CSIDL_APPDATA
1677 #define CSIDL_APPDATA 0x001a
1679 #ifndef CSIDL_LOCAL_APPDATA
1680 #define CSIDL_LOCAL_APPDATA 0x001c
1682 #ifndef CSIDL_FLAG_CREATE
1683 #define CSIDL_FLAG_CREATE 0x8000
1685 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1689 w32_strerror (int w32_errno)
1691 static char strerr[256];
1692 int ec = (int)GetLastError ();
1696 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1697 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1698 strerr, DIM (strerr)-1, NULL);
1702 static __inline__ void *
1703 dlopen (const char * name, int flag)
1705 void * hd = LoadLibrary (name);
1709 static __inline__ void *
1710 dlsym (void * hd, const char * sym)
1714 void * fnc = GetProcAddress (hd, sym);
1723 static __inline__ const char *
1726 return w32_strerror (0);
1730 static __inline__ int
1742 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1744 static int initialized;
1745 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1749 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1755 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1757 handle = dlopen (dllnames[i], RTLD_LAZY);
1760 func = dlsym (handle, "SHGetFolderPathW");
1771 return func (a,b,c,d,e);
1776 /* Returns a static string with the directroy from which the module
1777 has been loaded. Returns an empty string on error. */
1778 static char *w32_get_module_dir(void)
1780 static char *moddir;
1783 char name[MAX_PATH+10];
1786 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1789 p = strrchr (name, '\\');
1795 moddir = g_strdup (name);
1799 #endif /* G_OS_WIN32 */
1801 /* Return a static string with the locale dir. */
1802 const gchar *get_locale_dir(void)
1804 static gchar *loc_dir;
1808 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1809 "\\share\\locale", NULL);
1812 loc_dir = LOCALEDIR;
1818 const gchar *get_home_dir(void)
1821 static char home_dir_utf16[MAX_PATH] = "";
1822 static gchar *home_dir_utf8 = NULL;
1823 if (home_dir_utf16[0] == '\0') {
1824 if (w32_shgetfolderpath
1825 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1826 NULL, 0, home_dir_utf16) < 0)
1827 strcpy (home_dir_utf16, "C:\\Sylpheed");
1828 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1830 return home_dir_utf8;
1832 static const gchar *homeenv = NULL;
1837 if (!homeenv && g_getenv("HOME") != NULL)
1838 homeenv = g_strdup(g_getenv("HOME"));
1840 homeenv = g_get_home_dir();
1846 static gchar *claws_rc_dir = NULL;
1847 static gboolean rc_dir_alt = FALSE;
1848 const gchar *get_rc_dir(void)
1852 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1855 return claws_rc_dir;
1858 void set_rc_dir(const gchar *dir)
1860 if (claws_rc_dir != NULL) {
1861 g_print("Error: rc_dir already set\n");
1864 if (g_path_is_absolute(dir))
1865 claws_rc_dir = g_strdup(dir);
1867 claws_rc_dir = g_strconcat(g_get_current_dir(),
1868 G_DIR_SEPARATOR_S, dir, NULL);
1870 debug_print("set rc_dir to %s\n", claws_rc_dir);
1871 if (!is_dir_exist(claws_rc_dir)) {
1872 if (make_dir_hier(claws_rc_dir) != 0) {
1873 g_print("Error: can't create %s\n",
1880 gboolean rc_dir_is_alt(void) {
1884 const gchar *get_mail_base_dir(void)
1886 return get_home_dir();
1890 const gchar *prefs_common_get_data_root(void);
1891 gchar *last_data_root = NULL;
1894 const gchar *get_news_cache_dir(void)
1896 static gchar *news_cache_dir = NULL;
1898 const gchar *data_root = prefs_common_get_data_root();
1899 if (strcmp2(data_root, last_data_root)) {
1900 g_free(news_cache_dir);
1901 news_cache_dir = NULL;
1904 if (!news_cache_dir)
1906 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1907 NEWS_CACHE_DIR, NULL);
1911 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1912 "Claws", G_DIR_SEPARATOR_S,
1913 g_get_user_name(), G_DIR_SEPARATOR_S,
1914 NEWS_CACHE_DIR, NULL);
1915 g_free(last_data_root);
1916 last_data_root = g_strdup(last_data_root);
1918 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1919 NEWS_CACHE_DIR, NULL);
1920 g_free(last_data_root);
1921 last_data_root = NULL;
1925 return news_cache_dir;
1928 const gchar *get_imap_cache_dir(void)
1930 static gchar *imap_cache_dir = NULL;
1932 const gchar *data_root = prefs_common_get_data_root();
1933 if (strcmp2(data_root, last_data_root)) {
1934 g_free(imap_cache_dir);
1935 imap_cache_dir = NULL;
1939 if (!imap_cache_dir)
1941 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1942 IMAP_CACHE_DIR, NULL);
1946 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1947 "Claws", G_DIR_SEPARATOR_S,
1948 g_get_user_name(), G_DIR_SEPARATOR_S,
1949 IMAP_CACHE_DIR, NULL);
1950 g_free(last_data_root);
1951 last_data_root = g_strdup(last_data_root);
1953 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1954 IMAP_CACHE_DIR, NULL);
1955 g_free(last_data_root);
1956 last_data_root = NULL;
1961 return imap_cache_dir;
1964 const gchar *get_mime_tmp_dir(void)
1966 static gchar *mime_tmp_dir = NULL;
1969 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1970 MIME_TMP_DIR, NULL);
1972 return mime_tmp_dir;
1975 const gchar *get_template_dir(void)
1977 static gchar *template_dir = NULL;
1980 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1981 TEMPLATE_DIR, NULL);
1983 return template_dir;
1987 const gchar *get_cert_file(void)
1989 const gchar *cert_file = NULL;
1991 cert_file = g_strconcat(w32_get_module_dir(),
1992 "\\share\\claws-mail\\",
1993 "ca-certificates.crt",
1999 /* Return the filepath of the claws-mail.desktop file */
2000 const gchar *get_desktop_file(void)
2002 #ifdef DESKTOPFILEPATH
2003 return DESKTOPFILEPATH;
2009 /* Return the default directory for Plugins. */
2010 const gchar *get_plugin_dir(void)
2013 static gchar *plugin_dir = NULL;
2016 plugin_dir = g_strconcat(w32_get_module_dir(),
2017 "\\lib\\claws-mail\\plugins\\",
2021 if (is_dir_exist(PLUGINDIR))
2024 static gchar *plugin_dir = NULL;
2026 plugin_dir = g_strconcat(get_rc_dir(),
2027 G_DIR_SEPARATOR_S, "plugins",
2028 G_DIR_SEPARATOR_S, NULL);
2036 /* Return the default directory for Themes. */
2037 const gchar *get_themes_dir(void)
2039 static gchar *themes_dir = NULL;
2042 themes_dir = g_strconcat(w32_get_module_dir(),
2043 "\\share\\claws-mail\\themes",
2049 const gchar *get_tmp_dir(void)
2051 static gchar *tmp_dir = NULL;
2054 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2060 gchar *get_tmp_file(void)
2063 static guint32 id = 0;
2065 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2066 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2071 const gchar *get_domain_name(void)
2074 static gchar *domain_name = NULL;
2080 if (gethostname(hostname, sizeof(hostname)) != 0) {
2081 perror("gethostname");
2082 domain_name = "unknown";
2084 hostname[sizeof(hostname) - 1] = '\0';
2085 if ((hp = my_gethostbyname(hostname)) == NULL) {
2086 perror("gethostbyname");
2087 domain_name = g_strdup(hostname);
2089 domain_name = g_strdup(hp->h_name);
2092 debug_print("domain name = %s\n", domain_name);
2101 off_t get_file_size(const gchar *file)
2105 if (g_stat(file, &s) < 0) {
2106 FILE_OP_ERROR(file, "stat");
2113 time_t get_file_mtime(const gchar *file)
2117 if (g_stat(file, &s) < 0) {
2118 FILE_OP_ERROR(file, "stat");
2125 off_t get_file_size_as_crlf(const gchar *file)
2129 gchar buf[BUFFSIZE];
2131 if ((fp = g_fopen(file, "rb")) == NULL) {
2132 FILE_OP_ERROR(file, "g_fopen");
2136 while (fgets(buf, sizeof(buf), fp) != NULL) {
2138 size += strlen(buf) + 2;
2142 FILE_OP_ERROR(file, "fgets");
2151 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2158 if (g_stat(file, &s) < 0) {
2159 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2163 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2170 /* Test on whether FILE is a relative file name. This is
2171 * straightforward for Unix but more complex for Windows. */
2172 gboolean is_relative_filename(const gchar *file)
2177 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2178 return FALSE; /* Prefixed with a hostname - this can't
2179 * be a relative name. */
2181 if ( ((*file >= 'a' && *file <= 'z')
2182 || (*file >= 'A' && *file <= 'Z'))
2184 file += 2; /* Skip drive letter. */
2186 return !(*file == '\\' || *file == '/');
2188 return !(*file == G_DIR_SEPARATOR);
2193 gboolean is_dir_exist(const gchar *dir)
2198 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2201 gboolean is_file_entry_exist(const gchar *file)
2206 return g_file_test(file, G_FILE_TEST_EXISTS);
2209 gboolean dirent_is_regular_file(struct dirent *d)
2211 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2212 if (d->d_type == DT_REG)
2214 else if (d->d_type != DT_UNKNOWN)
2218 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2221 gint change_dir(const gchar *dir)
2223 gchar *prevdir = NULL;
2226 prevdir = g_get_current_dir();
2228 if (g_chdir(dir) < 0) {
2229 FILE_OP_ERROR(dir, "chdir");
2230 if (debug_mode) g_free(prevdir);
2232 } else if (debug_mode) {
2235 cwd = g_get_current_dir();
2236 if (strcmp(prevdir, cwd) != 0)
2237 g_print("current dir: %s\n", cwd);
2245 gint make_dir(const gchar *dir)
2247 if (g_mkdir(dir, S_IRWXU) < 0) {
2248 FILE_OP_ERROR(dir, "mkdir");
2251 if (g_chmod(dir, S_IRWXU) < 0)
2252 FILE_OP_ERROR(dir, "chmod");
2257 gint make_dir_hier(const gchar *dir)
2262 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2263 parent_dir = g_strndup(dir, p - dir);
2264 if (*parent_dir != '\0') {
2265 if (!is_dir_exist(parent_dir)) {
2266 if (make_dir(parent_dir) < 0) {
2275 if (!is_dir_exist(dir)) {
2276 if (make_dir(dir) < 0)
2283 gint remove_all_files(const gchar *dir)
2286 const gchar *dir_name;
2289 prev_dir = g_get_current_dir();
2291 if (g_chdir(dir) < 0) {
2292 FILE_OP_ERROR(dir, "chdir");
2297 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2298 g_warning("failed to open directory: %s\n", dir);
2303 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2304 if (claws_unlink(dir_name) < 0)
2305 FILE_OP_ERROR(dir_name, "unlink");
2310 if (g_chdir(prev_dir) < 0) {
2311 FILE_OP_ERROR(prev_dir, "chdir");
2321 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2324 const gchar *dir_name;
2328 prev_dir = g_get_current_dir();
2330 if (g_chdir(dir) < 0) {
2331 FILE_OP_ERROR(dir, "chdir");
2336 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2337 g_warning("failed to open directory: %s\n", dir);
2342 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2343 file_no = to_number(dir_name);
2344 if (file_no > 0 && first <= file_no && file_no <= last) {
2345 if (is_dir_exist(dir_name))
2347 if (claws_unlink(dir_name) < 0)
2348 FILE_OP_ERROR(dir_name, "unlink");
2354 if (g_chdir(prev_dir) < 0) {
2355 FILE_OP_ERROR(prev_dir, "chdir");
2365 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2368 const gchar *dir_name;
2372 prev_dir = g_get_current_dir();
2374 if (g_chdir(dir) < 0) {
2375 FILE_OP_ERROR(dir, "chdir");
2380 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2381 FILE_OP_ERROR(dir, "opendir");
2386 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2387 file_no = to_number(dir_name);
2388 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2389 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2390 if (is_dir_exist(dir_name))
2392 if (claws_unlink(dir_name) < 0)
2393 FILE_OP_ERROR(dir_name, "unlink");
2399 if (g_chdir(prev_dir) < 0) {
2400 FILE_OP_ERROR(prev_dir, "chdir");
2410 gint remove_all_numbered_files(const gchar *dir)
2412 return remove_numbered_files(dir, 0, UINT_MAX);
2415 gint remove_dir_recursive(const gchar *dir)
2419 const gchar *dir_name;
2422 if (g_stat(dir, &s) < 0) {
2423 FILE_OP_ERROR(dir, "stat");
2424 if (ENOENT == errno) return 0;
2428 if (!S_ISDIR(s.st_mode)) {
2429 if (claws_unlink(dir) < 0) {
2430 FILE_OP_ERROR(dir, "unlink");
2437 prev_dir = g_get_current_dir();
2438 /* g_print("prev_dir = %s\n", prev_dir); */
2440 if (!path_cmp(prev_dir, dir)) {
2442 if (g_chdir("..") < 0) {
2443 FILE_OP_ERROR(dir, "chdir");
2446 prev_dir = g_get_current_dir();
2449 if (g_chdir(dir) < 0) {
2450 FILE_OP_ERROR(dir, "chdir");
2455 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2456 g_warning("failed to open directory: %s\n", dir);
2462 /* remove all files in the directory */
2463 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2464 /* g_print("removing %s\n", dir_name); */
2466 if (is_dir_exist(dir_name)) {
2467 if (remove_dir_recursive(dir_name) < 0) {
2468 g_warning("can't remove directory\n");
2472 if (claws_unlink(dir_name) < 0)
2473 FILE_OP_ERROR(dir_name, "unlink");
2479 if (g_chdir(prev_dir) < 0) {
2480 FILE_OP_ERROR(prev_dir, "chdir");
2487 if (g_rmdir(dir) < 0) {
2488 FILE_OP_ERROR(dir, "rmdir");
2495 gint rename_force(const gchar *oldpath, const gchar *newpath)
2498 if (!is_file_entry_exist(oldpath)) {
2502 if (is_file_exist(newpath)) {
2503 if (claws_unlink(newpath) < 0)
2504 FILE_OP_ERROR(newpath, "unlink");
2507 return g_rename(oldpath, newpath);
2511 * Append src file body to the tail of dest file.
2512 * Now keep_backup has no effects.
2514 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2516 FILE *src_fp, *dest_fp;
2520 gboolean err = FALSE;
2522 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2523 FILE_OP_ERROR(src, "g_fopen");
2527 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2528 FILE_OP_ERROR(dest, "g_fopen");
2533 if (change_file_mode_rw(dest_fp, dest) < 0) {
2534 FILE_OP_ERROR(dest, "chmod");
2535 g_warning("can't change file mode\n");
2538 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2539 if (n_read < sizeof(buf) && ferror(src_fp))
2541 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2542 g_warning("writing to %s failed.\n", dest);
2550 if (ferror(src_fp)) {
2551 FILE_OP_ERROR(src, "fread");
2555 if (fclose(dest_fp) == EOF) {
2556 FILE_OP_ERROR(dest, "fclose");
2568 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2570 FILE *src_fp, *dest_fp;
2573 gchar *dest_bak = NULL;
2574 gboolean err = FALSE;
2576 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2577 FILE_OP_ERROR(src, "g_fopen");
2580 if (is_file_exist(dest)) {
2581 dest_bak = g_strconcat(dest, ".bak", NULL);
2582 if (rename_force(dest, dest_bak) < 0) {
2583 FILE_OP_ERROR(dest, "rename");
2590 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2591 FILE_OP_ERROR(dest, "g_fopen");
2594 if (rename_force(dest_bak, dest) < 0)
2595 FILE_OP_ERROR(dest_bak, "rename");
2601 if (change_file_mode_rw(dest_fp, dest) < 0) {
2602 FILE_OP_ERROR(dest, "chmod");
2603 g_warning("can't change file mode\n");
2606 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2607 if (n_read < sizeof(buf) && ferror(src_fp))
2609 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2610 g_warning("writing to %s failed.\n", dest);
2615 if (rename_force(dest_bak, dest) < 0)
2616 FILE_OP_ERROR(dest_bak, "rename");
2623 if (ferror(src_fp)) {
2624 FILE_OP_ERROR(src, "fread");
2628 if (fclose(dest_fp) == EOF) {
2629 FILE_OP_ERROR(dest, "fclose");
2636 if (rename_force(dest_bak, dest) < 0)
2637 FILE_OP_ERROR(dest_bak, "rename");
2643 if (keep_backup == FALSE && dest_bak)
2644 claws_unlink(dest_bak);
2651 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2653 if (overwrite == FALSE && is_file_exist(dest)) {
2654 g_warning("move_file(): file %s already exists.", dest);
2658 if (rename_force(src, dest) == 0) return 0;
2660 if (EXDEV != errno) {
2661 FILE_OP_ERROR(src, "rename");
2665 if (copy_file(src, dest, FALSE) < 0) return -1;
2672 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2675 gint bytes_left, to_read;
2678 if (fseek(fp, offset, SEEK_SET) < 0) {
2683 bytes_left = length;
2684 to_read = MIN(bytes_left, sizeof(buf));
2686 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2687 if (n_read < to_read && ferror(fp))
2689 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2692 bytes_left -= n_read;
2693 if (bytes_left == 0)
2695 to_read = MIN(bytes_left, sizeof(buf));
2706 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2709 gboolean err = FALSE;
2711 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2712 FILE_OP_ERROR(dest, "g_fopen");
2716 if (change_file_mode_rw(dest_fp, dest) < 0) {
2717 FILE_OP_ERROR(dest, "chmod");
2718 g_warning("can't change file mode\n");
2721 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2724 if (!err && fclose(dest_fp) == EOF) {
2725 FILE_OP_ERROR(dest, "fclose");
2730 g_warning("writing to %s failed.\n", dest);
2738 /* convert line endings into CRLF. If the last line doesn't end with
2739 * linebreak, add it.
2741 gchar *canonicalize_str(const gchar *str)
2747 for (p = str; *p != '\0'; ++p) {
2754 if (p == str || *(p - 1) != '\n')
2757 out = outp = g_malloc(new_len + 1);
2758 for (p = str; *p != '\0'; ++p) {
2765 if (p == str || *(p - 1) != '\n') {
2774 gint canonicalize_file(const gchar *src, const gchar *dest)
2776 FILE *src_fp, *dest_fp;
2777 gchar buf[BUFFSIZE];
2779 gboolean err = FALSE;
2780 gboolean last_linebreak = FALSE;
2782 if (src == NULL || dest == NULL)
2785 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2786 FILE_OP_ERROR(src, "g_fopen");
2790 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2791 FILE_OP_ERROR(dest, "g_fopen");
2796 if (change_file_mode_rw(dest_fp, dest) < 0) {
2797 FILE_OP_ERROR(dest, "chmod");
2798 g_warning("can't change file mode\n");
2801 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2805 if (len == 0) break;
2806 last_linebreak = FALSE;
2808 if (buf[len - 1] != '\n') {
2809 last_linebreak = TRUE;
2810 r = fputs(buf, dest_fp);
2811 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2812 r = fputs(buf, dest_fp);
2815 r = fwrite(buf, 1, len - 1, dest_fp);
2820 r = fputs("\r\n", dest_fp);
2824 g_warning("writing to %s failed.\n", dest);
2832 if (last_linebreak == TRUE) {
2833 if (fputs("\r\n", dest_fp) == EOF)
2837 if (ferror(src_fp)) {
2838 FILE_OP_ERROR(src, "fgets");
2842 if (fclose(dest_fp) == EOF) {
2843 FILE_OP_ERROR(dest, "fclose");
2855 gint canonicalize_file_replace(const gchar *file)
2859 tmp_file = get_tmp_file();
2861 if (canonicalize_file(file, tmp_file) < 0) {
2866 if (move_file(tmp_file, file, TRUE) < 0) {
2867 g_warning("can't replace %s .\n", file);
2868 claws_unlink(tmp_file);
2877 gchar *normalize_newlines(const gchar *str)
2879 const gchar *p = str;
2882 out = outp = g_malloc(strlen(str) + 1);
2883 for (p = str; *p != '\0'; ++p) {
2885 if (*(p + 1) != '\n')
2896 gchar *get_outgoing_rfc2822_str(FILE *fp)
2898 gchar buf[BUFFSIZE];
2902 str = g_string_new(NULL);
2904 /* output header part */
2905 while (fgets(buf, sizeof(buf), fp) != NULL) {
2907 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2914 else if (next != ' ' && next != '\t') {
2918 if (fgets(buf, sizeof(buf), fp) == NULL)
2922 g_string_append(str, buf);
2923 g_string_append(str, "\r\n");
2929 /* output body part */
2930 while (fgets(buf, sizeof(buf), fp) != NULL) {
2933 g_string_append_c(str, '.');
2934 g_string_append(str, buf);
2935 g_string_append(str, "\r\n");
2939 g_string_free(str, FALSE);
2945 * Create a new boundary in a way that it is very unlikely that this
2946 * will occur in the following text. It would be easy to ensure
2947 * uniqueness if everything is either quoted-printable or base64
2948 * encoded (note that conversion is allowed), but because MIME bodies
2949 * may be nested, it may happen that the same boundary has already
2952 * boundary := 0*69<bchars> bcharsnospace
2953 * bchars := bcharsnospace / " "
2954 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2955 * "+" / "_" / "," / "-" / "." /
2956 * "/" / ":" / "=" / "?"
2958 * some special characters removed because of buggy MTAs
2961 gchar *generate_mime_boundary(const gchar *prefix)
2963 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2964 "abcdefghijklmnopqrstuvwxyz"
2969 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2970 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2973 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2977 gint change_file_mode_rw(FILE *fp, const gchar *file)
2980 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2982 return g_chmod(file, S_IRUSR|S_IWUSR);
2986 FILE *my_tmpfile(void)
2988 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2989 const gchar suffix[] = ".XXXXXX";
2990 const gchar *tmpdir;
2992 const gchar *progname;
3001 tmpdir = get_tmp_dir();
3002 tmplen = strlen(tmpdir);
3003 progname = g_get_prgname();
3004 if (progname == NULL)
3005 progname = "claws-mail";
3006 proglen = strlen(progname);
3007 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3010 memcpy(fname, tmpdir, tmplen);
3011 fname[tmplen] = G_DIR_SEPARATOR;
3012 memcpy(fname + tmplen + 1, progname, proglen);
3013 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3015 fd = mkstemp(fname);
3020 claws_unlink(fname);
3022 /* verify that we can write in the file after unlinking */
3023 if (write(fd, buf, 1) < 0) {
3030 fp = fdopen(fd, "w+b");
3038 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3043 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3047 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3048 dir, G_DIR_SEPARATOR);
3049 fd = mkstemp_name(template, filename);
3052 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3053 fd = mkstemp(*filename);
3055 return fdopen(fd, "w+");
3058 FILE *str_open_as_stream(const gchar *str)
3063 cm_return_val_if_fail(str != NULL, NULL);
3067 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3072 if (len == 0) return fp;
3074 if (fwrite(str, 1, len, fp) != len) {
3075 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3084 gint str_write_to_file(const gchar *str, const gchar *file)
3089 cm_return_val_if_fail(str != NULL, -1);
3090 cm_return_val_if_fail(file != NULL, -1);
3092 if ((fp = g_fopen(file, "wb")) == NULL) {
3093 FILE_OP_ERROR(file, "g_fopen");
3103 if (fwrite(str, 1, len, fp) != len) {
3104 FILE_OP_ERROR(file, "fwrite");
3110 if (fclose(fp) == EOF) {
3111 FILE_OP_ERROR(file, "fclose");
3119 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3126 cm_return_val_if_fail(fp != NULL, NULL);
3128 array = g_byte_array_new();
3130 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3131 if (n_read < sizeof(buf) && ferror(fp))
3133 g_byte_array_append(array, buf, n_read);
3137 FILE_OP_ERROR("file stream", "fread");
3138 g_byte_array_free(array, TRUE);
3143 g_byte_array_append(array, buf, 1);
3144 str = (gchar *)array->data;
3145 g_byte_array_free(array, FALSE);
3147 if (recode && !g_utf8_validate(str, -1, NULL)) {
3148 const gchar *src_codeset, *dest_codeset;
3150 src_codeset = conv_get_locale_charset_str();
3151 dest_codeset = CS_UTF_8;
3152 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3160 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3167 struct timeval timeout = {1, 0};
3172 cm_return_val_if_fail(file != NULL, NULL);
3174 if (g_stat(file, &s) != 0) {
3175 FILE_OP_ERROR(file, "stat");
3178 if (S_ISDIR(s.st_mode)) {
3179 g_warning("%s: is a directory\n", file);
3184 fp = g_fopen (file, "rb");
3186 FILE_OP_ERROR(file, "open");
3190 /* test whether the file is readable without blocking */
3191 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3193 FILE_OP_ERROR(file, "open");
3200 /* allow for one second */
3201 err = select(fd+1, &fds, NULL, NULL, &timeout);
3202 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3204 FILE_OP_ERROR(file, "select");
3206 g_warning("%s: doesn't seem readable\n", file);
3212 /* Now clear O_NONBLOCK */
3213 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3214 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3218 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3219 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3224 /* get the FILE pointer */
3225 fp = fdopen(fd, "rb");
3228 FILE_OP_ERROR(file, "fdopen");
3229 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3234 str = file_read_stream_to_str_full(fp, recode);
3241 gchar *file_read_to_str(const gchar *file)
3243 return file_read_to_str_full(file, TRUE);
3245 gchar *file_read_stream_to_str(FILE *fp)
3247 return file_read_stream_to_str_full(fp, TRUE);
3250 gchar *file_read_to_str_no_recode(const gchar *file)
3252 return file_read_to_str_full(file, FALSE);
3254 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3256 return file_read_stream_to_str_full(fp, FALSE);
3259 char *fgets_crlf(char *buf, int size, FILE *stream)
3261 gboolean is_cr = FALSE;
3262 gboolean last_was_cr = FALSE;
3267 while (--size > 0 && (c = getc(stream)) != EOF)
3270 is_cr = (c == '\r');
3280 last_was_cr = is_cr;
3282 if (c == EOF && cs == buf)
3290 static gint execute_async(gchar *const argv[])
3292 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3294 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3295 NULL, NULL, NULL, FALSE) == FALSE) {
3296 g_warning("Couldn't execute command: %s\n", argv[0]);
3303 static gint execute_sync(gchar *const argv[])
3307 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3310 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3311 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3312 g_warning("Couldn't execute command: %s\n", argv[0]);
3316 if (WIFEXITED(status))
3317 return WEXITSTATUS(status);
3321 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3322 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3323 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3324 g_warning("Couldn't execute command: %s\n", argv[0]);
3332 gint execute_command_line(const gchar *cmdline, gboolean async)
3337 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3339 argv = strsplit_with_quote(cmdline, " ", 0);
3342 ret = execute_async(argv);
3344 ret = execute_sync(argv);
3351 gchar *get_command_output(const gchar *cmdline)
3353 gchar *child_stdout;
3356 cm_return_val_if_fail(cmdline != NULL, NULL);
3358 debug_print("get_command_output(): executing: %s\n", cmdline);
3360 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3362 g_warning("Couldn't execute command: %s\n", cmdline);
3366 return child_stdout;
3369 static gint is_unchanged_uri_char(char c)
3380 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3386 for(i = 0; i < strlen(uri) ; i++) {
3387 if (is_unchanged_uri_char(uri[i])) {
3388 if (k + 2 >= bufsize)
3390 encoded_uri[k++] = uri[i];
3393 char * hexa = "0123456789ABCDEF";
3395 if (k + 4 >= bufsize)
3397 encoded_uri[k++] = '%';
3398 encoded_uri[k++] = hexa[uri[i] / 16];
3399 encoded_uri[k++] = hexa[uri[i] % 16];
3405 gint open_uri(const gchar *uri, const gchar *cmdline)
3409 gchar buf[BUFFSIZE];
3411 gchar encoded_uri[BUFFSIZE];
3412 cm_return_val_if_fail(uri != NULL, -1);
3414 /* an option to choose whether to use encode_uri or not ? */
3415 encode_uri(encoded_uri, BUFFSIZE, uri);
3418 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3419 !strchr(p + 2, '%'))
3420 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3423 g_warning("Open URI command-line is invalid "
3424 "(there must be only one '%%s'): %s",
3426 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3429 execute_command_line(buf, TRUE);
3431 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3434 extern osso_context_t *get_osso_context(void);
3435 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3436 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3437 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3442 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3444 gchar buf[BUFFSIZE];
3447 cm_return_val_if_fail(filepath != NULL, -1);
3450 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3451 !strchr(p + 2, '%'))
3452 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3455 g_warning("Open Text Editor command-line is invalid "
3456 "(there must be only one '%%s'): %s",
3458 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3461 execute_command_line(buf, TRUE);
3466 time_t remote_tzoffset_sec(const gchar *zone)
3468 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3474 time_t remoteoffset;
3476 strncpy(zone3, zone, 3);
3480 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3481 (c == '+' || c == '-')) {
3482 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3484 remoteoffset = -remoteoffset;
3485 } else if (!strncmp(zone, "UT" , 2) ||
3486 !strncmp(zone, "GMT", 2)) {
3488 } else if (strlen(zone3) == 3) {
3489 for (p = ustzstr; *p != '\0'; p += 3) {
3490 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3491 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3492 remoteoffset = iustz * 3600;
3498 } else if (strlen(zone3) == 1) {
3500 case 'Z': remoteoffset = 0; break;
3501 case 'A': remoteoffset = -1; break;
3502 case 'B': remoteoffset = -2; break;
3503 case 'C': remoteoffset = -3; break;
3504 case 'D': remoteoffset = -4; break;
3505 case 'E': remoteoffset = -5; break;
3506 case 'F': remoteoffset = -6; break;
3507 case 'G': remoteoffset = -7; break;
3508 case 'H': remoteoffset = -8; break;
3509 case 'I': remoteoffset = -9; break;
3510 case 'K': remoteoffset = -10; break; /* J is not used */
3511 case 'L': remoteoffset = -11; break;
3512 case 'M': remoteoffset = -12; break;
3513 case 'N': remoteoffset = 1; break;
3514 case 'O': remoteoffset = 2; break;
3515 case 'P': remoteoffset = 3; break;
3516 case 'Q': remoteoffset = 4; break;
3517 case 'R': remoteoffset = 5; break;
3518 case 'S': remoteoffset = 6; break;
3519 case 'T': remoteoffset = 7; break;
3520 case 'U': remoteoffset = 8; break;
3521 case 'V': remoteoffset = 9; break;
3522 case 'W': remoteoffset = 10; break;
3523 case 'X': remoteoffset = 11; break;
3524 case 'Y': remoteoffset = 12; break;
3525 default: remoteoffset = 0; break;
3527 remoteoffset = remoteoffset * 3600;
3531 return remoteoffset;
3534 time_t tzoffset_sec(time_t *now)
3539 struct tm buf1, buf2;
3542 if (now && *now < 0)
3545 gmt = *gmtime_r(now, &buf1);
3546 lt = localtime_r(now, &buf2);
3548 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3550 if (lt->tm_year < gmt.tm_year)
3552 else if (lt->tm_year > gmt.tm_year)
3554 else if (lt->tm_yday < gmt.tm_yday)
3556 else if (lt->tm_yday > gmt.tm_yday)
3559 if (off >= 24 * 60) /* should be impossible */
3560 off = 23 * 60 + 59; /* if not, insert silly value */
3561 if (off <= -24 * 60)
3562 off = -(23 * 60 + 59);
3567 /* calculate timezone offset */
3568 gchar *tzoffset(time_t *now)
3570 static gchar offset_string[6];
3575 struct tm buf1, buf2;
3578 if (now && *now < 0)
3581 gmt = *gmtime_r(now, &buf1);
3582 lt = localtime_r(now, &buf2);
3584 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3586 if (lt->tm_year < gmt.tm_year)
3588 else if (lt->tm_year > gmt.tm_year)
3590 else if (lt->tm_yday < gmt.tm_yday)
3592 else if (lt->tm_yday > gmt.tm_yday)
3600 if (off >= 24 * 60) /* should be impossible */
3601 off = 23 * 60 + 59; /* if not, insert silly value */
3603 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3605 return offset_string;
3608 void get_rfc822_date(gchar *buf, gint len)
3612 gchar day[4], mon[4];
3613 gint dd, hh, mm, ss, yyyy;
3616 gchar buf2[BUFFSIZE];
3620 lt = localtime_r(&t, &buf1);
3622 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3623 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3625 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3626 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3629 void debug_set_mode(gboolean mode)
3634 gboolean debug_get_mode(void)
3639 void debug_print_real(const gchar *format, ...)
3642 gchar buf[BUFFSIZE];
3644 if (!debug_mode) return;
3646 va_start(args, format);
3647 g_vsnprintf(buf, sizeof(buf), format, args);
3654 const char * debug_srcname(const char *file)
3656 const char *s = strrchr (file, '/');
3661 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3663 if (subject == NULL)
3666 subject += subject_get_prefix_length(subject);
3668 return g_hash_table_lookup(subject_table, subject);
3671 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3674 if (subject == NULL || *subject == 0)
3676 subject += subject_get_prefix_length(subject);
3677 g_hash_table_insert(subject_table, subject, data);
3680 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3682 if (subject == NULL)
3685 subject += subject_get_prefix_length(subject);
3686 g_hash_table_remove(subject_table, subject);
3690 static regex_t u_regex;
3691 static gboolean u_init_;
3694 void utils_free_regex(void)
3705 *\brief Check if a string is prefixed with known (combinations)
3706 * of prefixes. The function assumes that each prefix
3707 * is terminated by zero or exactly _one_ space.
3709 *\param str String to check for a prefixes
3711 *\return int Number of chars in the prefix that should be skipped
3712 * for a "clean" subject line. If no prefix was found, 0
3715 int subject_get_prefix_length(const gchar *subject)
3718 /*!< Array with allowable reply prefixes regexps. */
3719 static const gchar * const prefixes[] = {
3720 "Re\\:", /* "Re:" */
3721 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3722 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3723 "Aw\\:", /* "Aw:" (German) */
3724 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3725 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3726 "Fw\\:", /* "Fw:" Forward */
3727 "Fwd\\:", /* "Fwd:" Forward */
3728 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3729 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3730 "Rif\\:", /* "Rif:" (Italian Outlook) */
3731 "Sv\\:", /* "Sv" (Norwegian) */
3732 "Vs\\:", /* "Vs" (Norwegian) */
3733 "Ad\\:", /* "Ad" (Norwegian) */
3734 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3735 "R\303\251f\\. \\:", /* "Réf. :" (French Lotus Notes) */
3736 "Re \\:", /* "Re :" (French Yahoo Mail) */
3739 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3743 if (!subject) return 0;
3744 if (!*subject) return 0;
3747 GString *s = g_string_new("");
3749 for (n = 0; n < PREFIXES; n++)
3750 /* Terminate each prefix regexpression by a
3751 * "\ ?" (zero or ONE space), and OR them */
3752 g_string_append_printf(s, "(%s\\ ?)%s",
3757 g_string_prepend(s, "(");
3758 g_string_append(s, ")+"); /* match at least once */
3759 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3762 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3763 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3764 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3765 debug_print("Error compiling regexp %s\n", s->str);
3766 g_string_free(s, TRUE);
3770 g_string_free(s, TRUE);
3774 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3779 /*!< Array with allowable reply prefixes regexps. */
3780 static const gchar * const prefixes[] = {
3782 "antw:", /* "Antw:" (Dutch / German Outlook) */
3783 "aw:", /* "Aw:" (German) */
3784 "antwort:", /* "Antwort:" (German Lotus Notes) */
3785 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3786 "fw:", /* "Fw:" Forward */
3787 "fwd:", /* "Fwd:" Forward */
3788 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3789 "odp:", /* "Odp:" Re (Polish Outlook) */
3790 "rif:", /* "Rif:" (Italian Outlook) */
3791 "sv:", /* "Sv" (Norwegian) */
3792 "vs:", /* "Vs" (Norwegian) */
3793 "ad:", /* "Ad" (Norwegian) */
3794 "R\303\251f. :", /* "Réf. :" (French Lotus Notes) */
3795 "Re :", /* "Re :" (French Yahoo Mail) */
3798 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3801 if (!subject) return 0;
3802 if (!*subject) return 0;
3804 for (n = 0; n < PREFIXES; n++) {
3805 int len = strlen(prefixes[n]);
3806 if (!strncasecmp(subject, prefixes[n], len)) {
3807 if (subject[len] == ' ')
3816 static guint g_stricase_hash(gconstpointer gptr)
3818 guint hash_result = 0;
3821 for (str = gptr; str && *str; str++) {
3822 hash_result += toupper(*str);
3828 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3830 const char *str1 = gptr1;
3831 const char *str2 = gptr2;
3833 return !strcasecmp(str1, str2);
3836 gint g_int_compare(gconstpointer a, gconstpointer b)
3838 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3841 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3851 lt = localtime_r(&t, &buft);
3853 if (strcmp(buf, "") == 0) {
3854 if (user_addr != NULL) {
3855 addr = g_strconcat(user_addr, "@", get_domain_name(), NULL);
3858 addr = g_strconcat("@", get_domain_name(), NULL);
3862 if (user_addr != NULL) {
3863 addr = g_strconcat(user_addr, "@", buf, NULL);
3866 addr = g_strconcat("@", buf, NULL);
3870 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3871 lt->tm_year + 1900, lt->tm_mon + 1,
3872 lt->tm_mday, lt->tm_hour,
3873 lt->tm_min, lt->tm_sec,
3874 (guint) rand(), addr);
3881 quote_cmd_argument()
3883 return a quoted string safely usable in argument of a command.
3885 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3888 gint quote_cmd_argument(gchar * result, guint size,
3898 for(p = path ; * p != '\0' ; p ++) {
3900 if (isalnum((guchar)*p) || (* p == '/')) {
3901 if (remaining > 0) {
3907 result[size - 1] = '\0';
3912 if (remaining >= 2) {
3920 result[size - 1] = '\0';
3925 if (remaining > 0) {
3929 result[size - 1] = '\0';
3943 static void g_node_map_recursive(GNode *node, gpointer data)
3945 GNodeMapData *mapdata = (GNodeMapData *) data;
3947 GNodeMapData newmapdata;
3950 newdata = mapdata->func(node->data, mapdata->data);
3951 if (newdata != NULL) {
3952 newnode = g_node_new(newdata);
3953 g_node_append(mapdata->parent, newnode);
3955 newmapdata.parent = newnode;
3956 newmapdata.func = mapdata->func;
3957 newmapdata.data = mapdata->data;
3959 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3963 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3966 GNodeMapData mapdata;
3968 cm_return_val_if_fail(node != NULL, NULL);
3969 cm_return_val_if_fail(func != NULL, NULL);
3971 root = g_node_new(func(node->data, data));
3973 mapdata.parent = root;
3974 mapdata.func = func;
3975 mapdata.data = data;
3977 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3982 #define HEX_TO_INT(val, hex) \
3986 if ('0' <= c && c <= '9') { \
3988 } else if ('a' <= c && c <= 'f') { \
3989 val = c - 'a' + 10; \
3990 } else if ('A' <= c && c <= 'F') { \
3991 val = c - 'A' + 10; \
3997 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4004 if (hi == -1 || lo == -1)
4007 *out = (hi << 4) + lo;
4011 #define INT_TO_HEX(hex, val) \
4014 hex = '0' + (val); \
4016 hex = 'A' + (val) - 10; \
4019 void get_hex_str(gchar *out, guchar ch)
4023 INT_TO_HEX(hex, ch >> 4);
4025 INT_TO_HEX(hex, ch & 0x0f);
4031 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4033 #define G_PRINT_REF g_print
4037 *\brief Register ref counted pointer. It is based on GBoxed, so should
4038 * work with anything that uses the GType system. The semantics
4039 * are similar to a C++ auto pointer, with the exception that
4040 * C doesn't have automatic closure (calling destructors) when
4041 * exiting a block scope.
4042 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4043 * function directly.
4045 *\return GType A GType type.
4047 GType g_auto_pointer_register(void)
4049 static GType auto_pointer_type;
4050 if (!auto_pointer_type)
4052 g_boxed_type_register_static
4053 ("G_TYPE_AUTO_POINTER",
4054 (GBoxedCopyFunc) g_auto_pointer_copy,
4055 (GBoxedFreeFunc) g_auto_pointer_free);
4056 return auto_pointer_type;
4060 *\brief Structure with g_new() allocated pointer guarded by the
4063 typedef struct AutoPointerRef {
4064 void (*free) (gpointer);
4070 *\brief The auto pointer opaque structure that references the
4071 * pointer guard block.
4073 typedef struct AutoPointer {
4074 AutoPointerRef *ref;
4075 gpointer ptr; /*!< access to protected pointer */
4079 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4083 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4084 * ... when assigning, copying and freeing storage elements
4086 * gtk_list_store_new(N_S_COLUMNS,
4087 * G_TYPE_AUTO_POINTER,
4091 * Template *precious_data = g_new0(Template, 1);
4092 * g_pointer protect = g_auto_pointer_new(precious_data);
4094 * gtk_list_store_set(container, &iter,
4098 * ... the gtk_list_store has copied the pointer and
4099 * ... incremented its reference count, we should free
4100 * ... the auto pointer (in C++ a destructor would do
4101 * ... this for us when leaving block scope)
4103 * g_auto_pointer_free(protect);
4105 * ... gtk_list_store_set() now manages the data. When
4106 * ... *explicitly* requesting a pointer from the list
4107 * ... store, don't forget you get a copy that should be
4108 * ... freed with g_auto_pointer_free() eventually.
4112 *\param pointer Pointer to be guarded.
4114 *\return GAuto * Pointer that should be used in containers with
4117 GAuto *g_auto_pointer_new(gpointer p)
4119 AutoPointerRef *ref;
4125 ref = g_new0(AutoPointerRef, 1);
4126 ptr = g_new0(AutoPointer, 1);
4136 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4142 *\brief Allocate an autopointer using the passed \a free function to
4143 * free the guarded pointer
4145 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4152 aptr = g_auto_pointer_new(p);
4153 aptr->ref->free = free_;
4157 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4159 if (auto_ptr == NULL)
4161 return ((AutoPointer *) auto_ptr)->ptr;
4165 *\brief Copies an auto pointer by. It's mostly not necessary
4166 * to call this function directly, unless you copy/assign
4167 * the guarded pointer.
4169 *\param auto_ptr Auto pointer returned by previous call to
4170 * g_auto_pointer_new_XXX()
4172 *\return gpointer An auto pointer
4174 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4177 AutoPointerRef *ref;
4180 if (auto_ptr == NULL)
4185 newp = g_new0(AutoPointer, 1);
4188 newp->ptr = ref->pointer;
4192 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4198 *\brief Free an auto pointer
4200 void g_auto_pointer_free(GAuto *auto_ptr)
4203 AutoPointerRef *ref;
4205 if (auto_ptr == NULL)
4211 if (--(ref->cnt) == 0) {
4213 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4215 ref->free(ref->pointer);
4220 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4225 void replace_returns(gchar *str)
4230 while (strstr(str, "\n")) {
4231 *strstr(str, "\n") = ' ';
4233 while (strstr(str, "\r")) {
4234 *strstr(str, "\r") = ' ';
4238 /* get_uri_part() - retrieves a URI starting from scanpos.
4239 Returns TRUE if succesful */
4240 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4241 const gchar **bp, const gchar **ep, gboolean hdr)
4244 gint parenthese_cnt = 0;
4246 cm_return_val_if_fail(start != NULL, FALSE);
4247 cm_return_val_if_fail(scanpos != NULL, FALSE);
4248 cm_return_val_if_fail(bp != NULL, FALSE);
4249 cm_return_val_if_fail(ep != NULL, FALSE);
4253 /* find end point of URI */
4254 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4255 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4256 !IS_ASCII(*(const guchar *)ep_) ||
4257 strchr("[]{}<>\"", *ep_)) {
4259 } else if (strchr("(", *ep_)) {
4261 } else if (strchr(")", *ep_)) {
4262 if (parenthese_cnt > 0)
4269 /* no punctuation at end of string */
4271 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4272 * should pass some URI type to this function and decide on that whether
4273 * to perform punctuation stripping */
4275 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4277 for (; ep_ - 1 > scanpos + 1 &&
4278 IS_REAL_PUNCT(*(ep_ - 1));
4282 #undef IS_REAL_PUNCT
4289 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4291 while (bp && *bp && g_ascii_isspace(*bp))
4293 return g_strndup(bp, ep - bp);
4296 /* valid mail address characters */
4297 #define IS_RFC822_CHAR(ch) \
4301 !g_ascii_isspace(ch) && \
4302 !strchr("(),;<>\"", (ch)))
4304 /* alphabet and number within 7bit ASCII */
4305 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4306 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4308 static GHashTable *create_domain_tab(void)
4310 static const gchar *toplvl_domains [] = {
4312 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4313 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4314 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4315 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4316 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4317 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4318 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4319 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4320 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4321 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4322 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4323 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4324 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4325 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4326 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4327 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4328 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4329 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4330 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4331 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4332 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4333 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4334 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4335 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4336 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4337 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4340 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4342 cm_return_val_if_fail(htab, NULL);
4343 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4344 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4348 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4350 const gint MAX_LVL_DOM_NAME_LEN = 6;
4351 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4352 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4355 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4358 for (p = buf; p < m && first < last; *p++ = *first++)
4362 return g_hash_table_lookup(tab, buf) != NULL;
4365 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4366 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4367 const gchar **bp, const gchar **ep, gboolean hdr)
4369 /* more complex than the uri part because we need to scan back and forward starting from
4370 * the scan position. */
4371 gboolean result = FALSE;
4372 const gchar *bp_ = NULL;
4373 const gchar *ep_ = NULL;
4374 static GHashTable *dom_tab;
4375 const gchar *last_dot = NULL;
4376 const gchar *prelast_dot = NULL;
4377 const gchar *last_tld_char = NULL;
4379 /* the informative part of the email address (describing the name
4380 * of the email address owner) may contain quoted parts. the
4381 * closure stack stores the last encountered quotes. */
4382 gchar closure_stack[128];
4383 gchar *ptr = closure_stack;
4385 cm_return_val_if_fail(start != NULL, FALSE);
4386 cm_return_val_if_fail(scanpos != NULL, FALSE);
4387 cm_return_val_if_fail(bp != NULL, FALSE);
4388 cm_return_val_if_fail(ep != NULL, FALSE);
4391 const gchar *start_quote = NULL;
4392 const gchar *end_quote = NULL;
4394 /* go to the real start */
4395 if (start[0] == ',')
4397 if (start[0] == ';')
4399 while (start[0] == '\n' || start[0] == '\r')
4401 while (start[0] == ' ' || start[0] == '\t')
4406 /* check if there are quotes (to skip , in them) */
4407 if (*start == '"') {
4408 start_quote = start;
4410 end_quote = strstr(start, "\"");
4416 /* skip anything between quotes */
4417 if (start_quote && end_quote) {
4422 /* find end (either , or ; or end of line) */
4423 if (strstr(start, ",") && strstr(start, ";"))
4424 *ep = strstr(start,",") < strstr(start, ";")
4425 ? strstr(start, ",") : strstr(start, ";");
4426 else if (strstr(start, ","))
4427 *ep = strstr(start, ",");
4428 else if (strstr(start, ";"))
4429 *ep = strstr(start, ";");
4431 *ep = start+strlen(start);
4433 /* go back to real start */
4434 if (start_quote && end_quote) {
4435 start = start_quote;
4438 /* check there's still an @ in that, or search
4439 * further if possible */
4440 if (strstr(start, "@") && strstr(start, "@") < *ep)
4442 else if (*ep < start+strlen(start)) {
4445 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4453 dom_tab = create_domain_tab();
4454 cm_return_val_if_fail(dom_tab, FALSE);
4456 /* scan start of address */
4457 for (bp_ = scanpos - 1;
4458 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4461 /* TODO: should start with an alnum? */
4463 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4466 if (bp_ != scanpos) {
4467 /* scan end of address */
4468 for (ep_ = scanpos + 1;
4469 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4471 prelast_dot = last_dot;
4473 if (*(last_dot + 1) == '.') {
4474 if (prelast_dot == NULL)
4476 last_dot = prelast_dot;
4481 /* TODO: really should terminate with an alnum? */
4482 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4487 if (last_dot == NULL)
4489 if (last_dot >= ep_)
4490 last_dot = prelast_dot;
4491 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4495 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4496 if (*last_tld_char == '?')
4499 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4506 if (!result) return FALSE;
4508 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4509 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4510 && IS_RFC822_CHAR(*(ep_ + 3))) {
4511 /* this informative part with an @ in it is
4512 * followed by the email address */
4515 /* go to matching '>' (or next non-rfc822 char, like \n) */
4516 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4519 /* include the bracket */
4520 if (*ep_ == '>') ep_++;
4522 /* include the leading quote */
4530 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4531 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4534 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4535 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4538 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4539 #define IN_STACK() (ptr > closure_stack)
4540 /* has underrun check */
4541 #define POP_STACK() if(IN_STACK()) --ptr
4542 /* has overrun check */
4543 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4544 /* has underrun check */
4545 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4549 /* scan for the informative part. */
4550 for (bp_ -= 2; bp_ >= start; bp_--) {
4551 /* if closure on the stack keep scanning */
4552 if (PEEK_STACK() == *bp_) {
4556 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4561 /* if nothing in the closure stack, do the special conditions
4562 * the following if..else expression simply checks whether
4563 * a token is acceptable. if not acceptable, the clause
4564 * should terminate the loop with a 'break' */
4565 if (!PEEK_STACK()) {
4567 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4568 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4569 /* hyphens are allowed, but only in
4571 } else if (strchr(" \"'", *bp_)) {
4572 /* but anything not being a punctiation
4575 break; /* anything else is rejected */
4582 /* scan forward (should start with an alnum) */
4583 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4599 #undef IS_ASCII_ALNUM
4600 #undef IS_RFC822_CHAR
4602 gchar *make_email_string(const gchar *bp, const gchar *ep)
4604 /* returns a mailto: URI; mailto: is also used to detect the
4605 * uri type later on in the button_pressed signal handler */
4609 tmp = g_strndup(bp, ep - bp);
4610 result = g_strconcat("mailto:", tmp, NULL);
4616 gchar *make_http_string(const gchar *bp, const gchar *ep)
4618 /* returns an http: URI; */
4622 while (bp && *bp && g_ascii_isspace(*bp))
4624 tmp = g_strndup(bp, ep - bp);
4625 result = g_strconcat("http://", tmp, NULL);
4631 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4633 FILE *fp = g_fopen(path, "rb");
4634 gchar buf[BUFFSIZE];
4635 gchar *result = NULL;
4638 while (fgets(buf, sizeof (buf), fp) != NULL) {
4639 gchar **parts = g_strsplit(buf, ";", 3);
4640 gchar *trimmed = parts[0];
4641 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4643 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4644 trimmed[strlen(trimmed)-1] = '\0';
4646 if (!strcmp(trimmed, type)) {
4647 gboolean needsterminal = FALSE;
4648 if (parts[2] && strstr(parts[2], "needsterminal")) {
4649 needsterminal = TRUE;
4651 if (parts[2] && strstr(parts[2], "test=")) {
4652 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4653 gchar *testcmd = orig_testcmd;
4654 if (strstr(testcmd,";"))
4655 *(strstr(testcmd,";")) = '\0';
4656 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4658 while (testcmd[strlen(testcmd)-1] == '\n')
4659 testcmd[strlen(testcmd)-1] = '\0';
4660 while (testcmd[strlen(testcmd)-1] == '\r')
4661 testcmd[strlen(testcmd)-1] = '\0';
4662 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4663 testcmd[strlen(testcmd)-1] = '\0';
4665 if (strstr(testcmd, "%s")) {
4666 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4667 gint res = system(tmp);
4669 g_free(orig_testcmd);
4676 gint res = system(testcmd);
4677 g_free(orig_testcmd);
4687 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4689 while (trimmed[strlen(trimmed)-1] == '\n')
4690 trimmed[strlen(trimmed)-1] = '\0';
4691 while (trimmed[strlen(trimmed)-1] == '\r')
4692 trimmed[strlen(trimmed)-1] = '\0';
4693 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4694 trimmed[strlen(trimmed)-1] = '\0';
4695 result = g_strdup(trimmed);
4698 /* if there are no single quotes around %s, add them.
4699 * '.*%s.*' is ok, as in display 'png:%s'
4701 if (strstr(result, "%s")
4702 && !(strstr(result, "'") < strstr(result,"%s") &&
4703 strstr(strstr(result,"%s"), "'"))) {
4704 gchar *start = g_strdup(result);
4705 gchar *end = g_strdup(strstr(result, "%s")+2);
4707 *strstr(start, "%s") = '\0';
4708 tmp = g_strconcat(start,"'%s'",end, NULL);
4714 if (needsterminal) {
4715 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4726 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4728 gchar *result = NULL;
4732 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4733 result = mailcap_get_command_in_file(path, type, file_to_open);
4737 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4741 void mailcap_update_default(const gchar *type, const gchar *command)
4743 gchar *path = NULL, *outpath = NULL;
4744 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4745 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4746 FILE *fp = g_fopen(path, "rb");
4747 FILE *outfp = g_fopen(outpath, "wb");
4748 gchar buf[BUFFSIZE];
4749 gboolean err = FALSE;
4757 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4758 gchar **parts = g_strsplit(buf, ";", 3);
4759 gchar *trimmed = parts[0];
4760 while (trimmed[0] == ' ')
4762 while (trimmed[strlen(trimmed)-1] == ' ')
4763 trimmed[strlen(trimmed)-1] = '\0';
4765 if (!strcmp(trimmed, type)) {
4770 if(fputs(buf, outfp) == EOF) {
4777 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4783 if (fclose(outfp) == EOF)
4787 g_rename(outpath, path);
4793 gint copy_dir(const gchar *src, const gchar *dst)
4798 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4799 g_warning("failed to open directory: %s\n", src);
4803 if (make_dir(dst) < 0)
4806 while ((name = g_dir_read_name(dir)) != NULL) {
4807 gchar *old_file, *new_file;
4808 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4809 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4810 debug_print("copying: %s -> %s\n", old_file, new_file);
4811 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4812 gint r = copy_file(old_file, new_file, TRUE);
4819 /* Windows has no symlinks. Or well, Vista seems to
4820 have something like this but the semantics might be
4821 different. Thus we don't use it under Windows. */
4822 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4825 gchar *target = g_file_read_link(old_file, &error);
4827 r = symlink(target, new_file);
4834 #endif /*G_OS_WIN32*/
4835 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4836 gint r = copy_dir(old_file, new_file);
4847 /* crude test to see if a file is an email. */
4848 gboolean file_is_email (const gchar *filename)
4854 if (filename == NULL)
4856 if ((fp = g_fopen(filename, "rb")) == NULL)
4858 while (i < 60 && score < 3
4859 && fgets(buffer, sizeof (buffer), fp) > 0) {
4860 if (!strncmp(buffer, "From:", strlen("From:")))
4862 if (!strncmp(buffer, "To:", strlen("To:")))
4864 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4869 return (score >= 3);
4872 gboolean sc_g_list_bigger(GList *list, gint max)
4876 while (cur && i <= max+1) {
4883 gboolean sc_g_slist_bigger(GSList *list, gint max)
4887 while (cur && i <= max+1) {
4894 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4895 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4896 NULL, NULL, NULL, NULL, NULL, NULL};
4897 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4898 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4899 NULL, NULL, NULL, NULL, NULL, NULL};
4901 gint daynames_len[] = {0,0,0,0,0,0,0};
4902 gint monthnames_len[] = {0,0,0,0,0,0,
4904 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4905 gint s_monthnames_len[] = {0,0,0,0,0,0,
4907 const gchar *s_am_up = NULL;
4908 const gchar *s_pm_up = NULL;
4909 const gchar *s_am_low = NULL;
4910 const gchar *s_pm_low = NULL;
4912 gint s_am_up_len = 0;
4913 gint s_pm_up_len = 0;
4914 gint s_am_low_len = 0;
4915 gint s_pm_low_len = 0;
4917 const gchar *def_loc_format = NULL;
4918 const gchar *date_loc_format = NULL;
4919 const gchar *time_loc_format = NULL;
4920 const gchar *time_am_pm = NULL;
4922 static gboolean time_names_init_done = FALSE;
4924 static void init_time_names(void)
4928 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4929 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4930 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4931 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4932 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4933 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4934 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4936 monthnames[0] = Q_("Complete month name for use by strftime|January");
4937 monthnames[1] = Q_("Complete month name for use by strftime|February");
4938 monthnames[2] = Q_("Complete month name for use by strftime|March");
4939 monthnames[3] = Q_("Complete month name for use by strftime|April");
4940 monthnames[4] = Q_("Complete month name for use by strftime|May");
4941 monthnames[5] = Q_("Complete month name for use by strftime|June");
4942 monthnames[6] = Q_("Complete month name for use by strftime|July");
4943 monthnames[7] = Q_("Complete month name for use by strftime|August");
4944 monthnames[8] = Q_("Complete month name for use by strftime|September");
4945 monthnames[9] = Q_("Complete month name for use by strftime|October");
4946 monthnames[10] = Q_("Complete month name for use by strftime|November");
4947 monthnames[11] = Q_("Complete month name for use by strftime|December");
4949 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4950 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4951 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4952 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4953 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4954 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4955 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4957 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4958 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4959 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4960 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4961 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4962 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4963 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4964 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4965 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4966 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4967 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4968 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4970 for (i = 0; i < 7; i++) {
4971 daynames_len[i] = strlen(daynames[i]);
4972 s_daynames_len[i] = strlen(s_daynames[i]);
4974 for (i = 0; i < 12; i++) {
4975 monthnames_len[i] = strlen(monthnames[i]);
4976 s_monthnames_len[i] = strlen(s_monthnames[i]);
4979 s_am_up = Q_("For use by strftime (morning)|AM");
4980 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4981 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4982 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4984 s_am_up_len = strlen(s_am_up);
4985 s_pm_up_len = strlen(s_pm_up);
4986 s_am_low_len = strlen(s_am_low);
4987 s_pm_low_len = strlen(s_pm_low);
4989 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4990 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4991 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4993 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4995 time_names_init_done = TRUE;
4998 #define CHECK_SIZE() { \
4999 total_done += len; \
5000 if (total_done >= buflen) { \
5001 buf[buflen-1] = '\0'; \
5006 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5008 gchar *curpos = buf;
5009 gint total_done = 0;
5010 gchar subbuf[64], subfmt[64];
5011 static time_t last_tzset = (time_t)0;
5013 if (!time_names_init_done)
5016 if (format == NULL || lt == NULL)
5019 if (last_tzset != time(NULL)) {
5021 last_tzset = time(NULL);
5024 if (*format == '%') {
5025 gint len = 0, tmp = 0;
5029 len = 1; CHECK_SIZE();
5033 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5034 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5037 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5038 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5042 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5043 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5046 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5047 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5050 fast_strftime(subbuf, 64, def_loc_format, lt);
5051 len = strlen(subbuf); CHECK_SIZE();
5052 strncpy2(curpos, subbuf, buflen - total_done);
5055 total_done += 2; CHECK_SIZE();
5056 tmp = (lt->tm_year + 1900)/100;
5057 *curpos++ = '0'+(tmp / 10);
5058 *curpos++ = '0'+(tmp % 10);
5061 total_done += 2; CHECK_SIZE();
5062 *curpos++ = '0'+(lt->tm_mday / 10);
5063 *curpos++ = '0'+(lt->tm_mday % 10);
5066 total_done += 8; CHECK_SIZE();
5067 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5068 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5070 *curpos++ = '0'+(lt->tm_mday / 10);
5071 *curpos++ = '0'+(lt->tm_mday % 10);
5073 tmp = lt->tm_year%100;
5074 *curpos++ = '0'+(tmp / 10);
5075 *curpos++ = '0'+(tmp % 10);
5078 len = 2; CHECK_SIZE();
5079 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5082 len = 10; CHECK_SIZE();
5083 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5084 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5087 total_done += 2; CHECK_SIZE();
5088 *curpos++ = '0'+(lt->tm_hour / 10);
5089 *curpos++ = '0'+(lt->tm_hour % 10);
5092 total_done += 2; CHECK_SIZE();
5098 *curpos++ = '0'+(tmp / 10);
5099 *curpos++ = '0'+(tmp % 10);
5102 len = 3; CHECK_SIZE();
5103 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5106 len = 2; CHECK_SIZE();
5107 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5110 len = 2; CHECK_SIZE();
5116 snprintf(curpos, buflen - total_done, "%2d", tmp);
5119 total_done += 2; CHECK_SIZE();
5120 tmp = lt->tm_mon + 1;
5121 *curpos++ = '0'+(tmp / 10);
5122 *curpos++ = '0'+(tmp % 10);
5125 total_done += 2; CHECK_SIZE();
5126 *curpos++ = '0'+(lt->tm_min / 10);
5127 *curpos++ = '0'+(lt->tm_min % 10);
5130 len = 1; CHECK_SIZE();
5134 if (lt->tm_hour >= 12) {
5135 len = s_pm_up_len; CHECK_SIZE();
5136 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5138 len = s_am_up_len; CHECK_SIZE();
5139 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5143 if (lt->tm_hour >= 12) {
5144 len = s_pm_low_len; CHECK_SIZE();
5145 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5147 len = s_am_low_len; CHECK_SIZE();
5148 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5152 fast_strftime(subbuf, 64, time_am_pm, lt);
5153 len = strlen(subbuf); CHECK_SIZE();
5154 strncpy2(curpos, subbuf, buflen - total_done);
5157 total_done += 5; CHECK_SIZE();
5158 *curpos++ = '0'+(lt->tm_hour / 10);
5159 *curpos++ = '0'+(lt->tm_hour % 10);
5161 *curpos++ = '0'+(lt->tm_min / 10);
5162 *curpos++ = '0'+(lt->tm_min % 10);
5165 snprintf(subbuf, 64, "%ld", mktime(lt));
5166 len = strlen(subbuf); CHECK_SIZE();
5167 strncpy2(curpos, subbuf, buflen - total_done);
5170 total_done += 2; CHECK_SIZE();
5171 *curpos++ = '0'+(lt->tm_sec / 10);
5172 *curpos++ = '0'+(lt->tm_sec % 10);
5175 len = 1; CHECK_SIZE();
5179 total_done += 8; CHECK_SIZE();
5180 *curpos++ = '0'+(lt->tm_hour / 10);
5181 *curpos++ = '0'+(lt->tm_hour % 10);
5183 *curpos++ = '0'+(lt->tm_min / 10);
5184 *curpos++ = '0'+(lt->tm_min % 10);
5186 *curpos++ = '0'+(lt->tm_sec / 10);
5187 *curpos++ = '0'+(lt->tm_sec % 10);
5190 len = 1; CHECK_SIZE();
5191 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5194 len = 1; CHECK_SIZE();
5195 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5198 fast_strftime(subbuf, 64, date_loc_format, lt);
5199 len = strlen(subbuf); CHECK_SIZE();
5200 strncpy2(curpos, subbuf, buflen - total_done);
5203 fast_strftime(subbuf, 64, time_loc_format, lt);
5204 len = strlen(subbuf); CHECK_SIZE();
5205 strncpy2(curpos, subbuf, buflen - total_done);
5208 total_done += 2; CHECK_SIZE();
5209 tmp = lt->tm_year%100;
5210 *curpos++ = '0'+(tmp / 10);
5211 *curpos++ = '0'+(tmp % 10);
5214 len = 4; CHECK_SIZE();
5215 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5225 /* let these complicated ones be done with the libc */
5226 snprintf(subfmt, 64, "%%%c", *format);
5227 strftime(subbuf, 64, subfmt, lt);
5228 len = strlen(subbuf); CHECK_SIZE();
5229 strncpy2(curpos, subbuf, buflen - total_done);
5233 /* let these complicated modifiers be done with the libc */
5234 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5235 strftime(subbuf, 64, subfmt, lt);
5236 len = strlen(subbuf); CHECK_SIZE();
5237 strncpy2(curpos, subbuf, buflen - total_done);
5241 if (format && *format)
5242 g_warning("format error (%c)", *format);
5249 int len = 1; CHECK_SIZE();
5250 *curpos++ = *format++;
5257 gboolean prefs_common_get_use_shred(void);
5261 #define WEXITSTATUS(x) (x)
5264 int claws_unlink(const gchar *filename)
5267 static int found_shred = -1;
5268 static const gchar *args[4];
5270 if (filename == NULL)
5273 if (prefs_common_get_use_shred()) {
5274 if (found_shred == -1) {
5276 args[0] = g_find_program_in_path("shred");
5277 debug_print("found shred: %s\n", args[0]);
5278 found_shred = (args[0] != NULL) ? 1:0;
5282 if (found_shred == 1) {
5283 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5284 if (s.st_nlink == 1) {
5287 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5288 NULL, NULL, NULL, NULL, &status, NULL);
5289 debug_print("%s %s exited with status %d\n",
5290 args[0], filename, WEXITSTATUS(status));
5291 if (truncate(filename, 0) < 0)
5292 g_warning("couln't truncate");
5297 return g_unlink(filename);