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)
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;
1672 #include <windows.h>
1673 #ifndef CSIDL_APPDATA
1674 #define CSIDL_APPDATA 0x001a
1676 #ifndef CSIDL_LOCAL_APPDATA
1677 #define CSIDL_LOCAL_APPDATA 0x001c
1679 #ifndef CSIDL_FLAG_CREATE
1680 #define CSIDL_FLAG_CREATE 0x8000
1682 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1686 w32_strerror (int w32_errno)
1688 static char strerr[256];
1689 int ec = (int)GetLastError ();
1693 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1694 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1695 strerr, DIM (strerr)-1, NULL);
1699 static __inline__ void *
1700 dlopen (const char * name, int flag)
1702 void * hd = LoadLibrary (name);
1706 static __inline__ void *
1707 dlsym (void * hd, const char * sym)
1711 void * fnc = GetProcAddress (hd, sym);
1720 static __inline__ const char *
1723 return w32_strerror (0);
1727 static __inline__ int
1739 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1741 static int initialized;
1742 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1746 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1752 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1754 handle = dlopen (dllnames[i], RTLD_LAZY);
1757 func = dlsym (handle, "SHGetFolderPathW");
1768 return func (a,b,c,d,e);
1773 /* Returns a static string with the directroy from which the module
1774 has been loaded. Returns an empty string on error. */
1775 static char *w32_get_module_dir(void)
1777 static char *moddir;
1780 char name[MAX_PATH+10];
1783 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1786 p = strrchr (name, '\\');
1792 moddir = g_strdup (name);
1796 #endif /* G_OS_WIN32 */
1798 /* Return a static string with the locale dir. */
1799 const gchar *get_locale_dir(void)
1801 static gchar *loc_dir;
1805 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1806 "\\share\\locale", NULL);
1809 loc_dir = LOCALEDIR;
1815 const gchar *get_home_dir(void)
1818 static char home_dir_utf16[MAX_PATH] = "";
1819 static gchar *home_dir_utf8 = NULL;
1820 if (home_dir_utf16[0] == '\0') {
1821 if (w32_shgetfolderpath
1822 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1823 NULL, 0, home_dir_utf16) < 0)
1824 strcpy (home_dir_utf16, "C:\\Sylpheed");
1825 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1827 return home_dir_utf8;
1829 static const gchar *homeenv = NULL;
1834 if (!homeenv && g_getenv("HOME") != NULL)
1835 homeenv = g_strdup(g_getenv("HOME"));
1837 homeenv = g_get_home_dir();
1843 static gchar *claws_rc_dir = NULL;
1844 static gboolean rc_dir_alt = FALSE;
1845 const gchar *get_rc_dir(void)
1849 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1852 return claws_rc_dir;
1855 void set_rc_dir(const gchar *dir)
1857 if (claws_rc_dir != NULL) {
1858 g_print("Error: rc_dir already set\n");
1861 if (g_path_is_absolute(dir))
1862 claws_rc_dir = g_strdup(dir);
1864 claws_rc_dir = g_strconcat(g_get_current_dir(),
1865 G_DIR_SEPARATOR_S, dir, NULL);
1867 debug_print("set rc_dir to %s\n", claws_rc_dir);
1868 if (!is_dir_exist(claws_rc_dir)) {
1869 if (make_dir_hier(claws_rc_dir) != 0) {
1870 g_print("Error: can't create %s\n",
1877 gboolean rc_dir_is_alt(void) {
1881 const gchar *get_mail_base_dir(void)
1883 return get_home_dir();
1887 const gchar *prefs_common_get_data_root(void);
1888 gchar *last_data_root = NULL;
1891 const gchar *get_news_cache_dir(void)
1893 static gchar *news_cache_dir = NULL;
1895 const gchar *data_root = prefs_common_get_data_root();
1896 if (strcmp2(data_root, last_data_root)) {
1897 g_free(news_cache_dir);
1898 news_cache_dir = NULL;
1901 if (!news_cache_dir)
1903 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904 NEWS_CACHE_DIR, NULL);
1908 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1909 "Claws", G_DIR_SEPARATOR_S,
1910 g_get_user_name(), G_DIR_SEPARATOR_S,
1911 NEWS_CACHE_DIR, NULL);
1912 g_free(last_data_root);
1913 last_data_root = g_strdup(last_data_root);
1915 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1916 NEWS_CACHE_DIR, NULL);
1917 g_free(last_data_root);
1918 last_data_root = NULL;
1922 return news_cache_dir;
1925 const gchar *get_imap_cache_dir(void)
1927 static gchar *imap_cache_dir = NULL;
1929 const gchar *data_root = prefs_common_get_data_root();
1930 if (strcmp2(data_root, last_data_root)) {
1931 g_free(imap_cache_dir);
1932 imap_cache_dir = NULL;
1936 if (!imap_cache_dir)
1938 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1939 IMAP_CACHE_DIR, NULL);
1943 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1944 "Claws", G_DIR_SEPARATOR_S,
1945 g_get_user_name(), G_DIR_SEPARATOR_S,
1946 IMAP_CACHE_DIR, NULL);
1947 g_free(last_data_root);
1948 last_data_root = g_strdup(last_data_root);
1950 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1951 IMAP_CACHE_DIR, NULL);
1952 g_free(last_data_root);
1953 last_data_root = NULL;
1958 return imap_cache_dir;
1961 const gchar *get_mime_tmp_dir(void)
1963 static gchar *mime_tmp_dir = NULL;
1966 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 MIME_TMP_DIR, NULL);
1969 return mime_tmp_dir;
1972 const gchar *get_template_dir(void)
1974 static gchar *template_dir = NULL;
1977 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1978 TEMPLATE_DIR, NULL);
1980 return template_dir;
1984 const gchar *get_cert_file(void)
1986 const gchar *cert_file = NULL;
1988 cert_file = g_strconcat(w32_get_module_dir(),
1989 "\\share\\claws-mail\\",
1990 "ca-certificates.crt",
1996 /* Return the filepath of the claws-mail.desktop file */
1997 const gchar *get_desktop_file(void)
1999 #ifdef DESKTOPFILEPATH
2000 return DESKTOPFILEPATH;
2006 /* Return the default directory for Plugins. */
2007 const gchar *get_plugin_dir(void)
2010 static gchar *plugin_dir = NULL;
2013 plugin_dir = g_strconcat(w32_get_module_dir(),
2014 "\\lib\\claws-mail\\plugins\\",
2018 if (is_dir_exist(PLUGINDIR))
2021 static gchar *plugin_dir = NULL;
2023 plugin_dir = g_strconcat(get_rc_dir(),
2024 G_DIR_SEPARATOR_S, "plugins",
2025 G_DIR_SEPARATOR_S, NULL);
2033 /* Return the default directory for Themes. */
2034 const gchar *get_themes_dir(void)
2036 static gchar *themes_dir = NULL;
2039 themes_dir = g_strconcat(w32_get_module_dir(),
2040 "\\share\\claws-mail\\themes",
2046 const gchar *get_tmp_dir(void)
2048 static gchar *tmp_dir = NULL;
2051 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2057 gchar *get_tmp_file(void)
2060 static guint32 id = 0;
2062 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2063 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2068 const gchar *get_domain_name(void)
2071 static gchar *domain_name = NULL;
2077 if (gethostname(hostname, sizeof(hostname)) != 0) {
2078 perror("gethostname");
2079 domain_name = "unknown";
2081 hostname[sizeof(hostname) - 1] = '\0';
2082 if ((hp = my_gethostbyname(hostname)) == NULL) {
2083 perror("gethostbyname");
2084 domain_name = g_strdup(hostname);
2086 domain_name = g_strdup(hp->h_name);
2089 debug_print("domain name = %s\n", domain_name);
2098 off_t get_file_size(const gchar *file)
2102 if (g_stat(file, &s) < 0) {
2103 FILE_OP_ERROR(file, "stat");
2110 time_t get_file_mtime(const gchar *file)
2114 if (g_stat(file, &s) < 0) {
2115 FILE_OP_ERROR(file, "stat");
2122 off_t get_file_size_as_crlf(const gchar *file)
2126 gchar buf[BUFFSIZE];
2128 if ((fp = g_fopen(file, "rb")) == NULL) {
2129 FILE_OP_ERROR(file, "g_fopen");
2133 while (fgets(buf, sizeof(buf), fp) != NULL) {
2135 size += strlen(buf) + 2;
2139 FILE_OP_ERROR(file, "fgets");
2148 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2155 if (g_stat(file, &s) < 0) {
2156 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2160 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2167 /* Test on whether FILE is a relative file name. This is
2168 * straightforward for Unix but more complex for Windows. */
2169 gboolean is_relative_filename(const gchar *file)
2174 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2175 return FALSE; /* Prefixed with a hostname - this can't
2176 * be a relative name. */
2178 if ( ((*file >= 'a' && *file <= 'z')
2179 || (*file >= 'A' && *file <= 'Z'))
2181 file += 2; /* Skip drive letter. */
2183 return !(*file == '\\' || *file == '/');
2185 return !(*file == G_DIR_SEPARATOR);
2190 gboolean is_dir_exist(const gchar *dir)
2195 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2198 gboolean is_file_entry_exist(const gchar *file)
2203 return g_file_test(file, G_FILE_TEST_EXISTS);
2206 gboolean dirent_is_regular_file(struct dirent *d)
2208 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2209 if (d->d_type == DT_REG)
2211 else if (d->d_type != DT_UNKNOWN)
2215 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2218 gint change_dir(const gchar *dir)
2220 gchar *prevdir = NULL;
2223 prevdir = g_get_current_dir();
2225 if (g_chdir(dir) < 0) {
2226 FILE_OP_ERROR(dir, "chdir");
2227 if (debug_mode) g_free(prevdir);
2229 } else if (debug_mode) {
2232 cwd = g_get_current_dir();
2233 if (strcmp(prevdir, cwd) != 0)
2234 g_print("current dir: %s\n", cwd);
2242 gint make_dir(const gchar *dir)
2244 if (g_mkdir(dir, S_IRWXU) < 0) {
2245 FILE_OP_ERROR(dir, "mkdir");
2248 if (g_chmod(dir, S_IRWXU) < 0)
2249 FILE_OP_ERROR(dir, "chmod");
2254 gint make_dir_hier(const gchar *dir)
2259 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2260 parent_dir = g_strndup(dir, p - dir);
2261 if (*parent_dir != '\0') {
2262 if (!is_dir_exist(parent_dir)) {
2263 if (make_dir(parent_dir) < 0) {
2272 if (!is_dir_exist(dir)) {
2273 if (make_dir(dir) < 0)
2280 gint remove_all_files(const gchar *dir)
2283 const gchar *dir_name;
2286 prev_dir = g_get_current_dir();
2288 if (g_chdir(dir) < 0) {
2289 FILE_OP_ERROR(dir, "chdir");
2294 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2295 g_warning("failed to open directory: %s\n", dir);
2300 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2301 if (claws_unlink(dir_name) < 0)
2302 FILE_OP_ERROR(dir_name, "unlink");
2307 if (g_chdir(prev_dir) < 0) {
2308 FILE_OP_ERROR(prev_dir, "chdir");
2318 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2321 const gchar *dir_name;
2325 prev_dir = g_get_current_dir();
2327 if (g_chdir(dir) < 0) {
2328 FILE_OP_ERROR(dir, "chdir");
2333 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2334 g_warning("failed to open directory: %s\n", dir);
2339 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2340 file_no = to_number(dir_name);
2341 if (file_no > 0 && first <= file_no && file_no <= last) {
2342 if (is_dir_exist(dir_name))
2344 if (claws_unlink(dir_name) < 0)
2345 FILE_OP_ERROR(dir_name, "unlink");
2351 if (g_chdir(prev_dir) < 0) {
2352 FILE_OP_ERROR(prev_dir, "chdir");
2362 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2365 const gchar *dir_name;
2369 prev_dir = g_get_current_dir();
2371 if (g_chdir(dir) < 0) {
2372 FILE_OP_ERROR(dir, "chdir");
2377 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2378 FILE_OP_ERROR(dir, "opendir");
2383 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2384 file_no = to_number(dir_name);
2385 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2386 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2387 if (is_dir_exist(dir_name))
2389 if (claws_unlink(dir_name) < 0)
2390 FILE_OP_ERROR(dir_name, "unlink");
2396 if (g_chdir(prev_dir) < 0) {
2397 FILE_OP_ERROR(prev_dir, "chdir");
2407 gint remove_all_numbered_files(const gchar *dir)
2409 return remove_numbered_files(dir, 0, UINT_MAX);
2412 gint remove_dir_recursive(const gchar *dir)
2416 const gchar *dir_name;
2419 if (g_stat(dir, &s) < 0) {
2420 FILE_OP_ERROR(dir, "stat");
2421 if (ENOENT == errno) return 0;
2425 if (!S_ISDIR(s.st_mode)) {
2426 if (claws_unlink(dir) < 0) {
2427 FILE_OP_ERROR(dir, "unlink");
2434 prev_dir = g_get_current_dir();
2435 /* g_print("prev_dir = %s\n", prev_dir); */
2437 if (!path_cmp(prev_dir, dir)) {
2439 if (g_chdir("..") < 0) {
2440 FILE_OP_ERROR(dir, "chdir");
2443 prev_dir = g_get_current_dir();
2446 if (g_chdir(dir) < 0) {
2447 FILE_OP_ERROR(dir, "chdir");
2452 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2453 g_warning("failed to open directory: %s\n", dir);
2459 /* remove all files in the directory */
2460 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2461 /* g_print("removing %s\n", dir_name); */
2463 if (is_dir_exist(dir_name)) {
2464 if (remove_dir_recursive(dir_name) < 0) {
2465 g_warning("can't remove directory\n");
2469 if (claws_unlink(dir_name) < 0)
2470 FILE_OP_ERROR(dir_name, "unlink");
2476 if (g_chdir(prev_dir) < 0) {
2477 FILE_OP_ERROR(prev_dir, "chdir");
2484 if (g_rmdir(dir) < 0) {
2485 FILE_OP_ERROR(dir, "rmdir");
2492 gint rename_force(const gchar *oldpath, const gchar *newpath)
2495 if (!is_file_entry_exist(oldpath)) {
2499 if (is_file_exist(newpath)) {
2500 if (claws_unlink(newpath) < 0)
2501 FILE_OP_ERROR(newpath, "unlink");
2504 return g_rename(oldpath, newpath);
2508 * Append src file body to the tail of dest file.
2509 * Now keep_backup has no effects.
2511 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2513 FILE *src_fp, *dest_fp;
2517 gboolean err = FALSE;
2519 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2520 FILE_OP_ERROR(src, "g_fopen");
2524 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2525 FILE_OP_ERROR(dest, "g_fopen");
2530 if (change_file_mode_rw(dest_fp, dest) < 0) {
2531 FILE_OP_ERROR(dest, "chmod");
2532 g_warning("can't change file mode\n");
2535 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2536 if (n_read < sizeof(buf) && ferror(src_fp))
2538 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2539 g_warning("writing to %s failed.\n", dest);
2547 if (ferror(src_fp)) {
2548 FILE_OP_ERROR(src, "fread");
2552 if (fclose(dest_fp) == EOF) {
2553 FILE_OP_ERROR(dest, "fclose");
2565 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2567 FILE *src_fp, *dest_fp;
2570 gchar *dest_bak = NULL;
2571 gboolean err = FALSE;
2573 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2574 FILE_OP_ERROR(src, "g_fopen");
2577 if (is_file_exist(dest)) {
2578 dest_bak = g_strconcat(dest, ".bak", NULL);
2579 if (rename_force(dest, dest_bak) < 0) {
2580 FILE_OP_ERROR(dest, "rename");
2587 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2588 FILE_OP_ERROR(dest, "g_fopen");
2591 if (rename_force(dest_bak, dest) < 0)
2592 FILE_OP_ERROR(dest_bak, "rename");
2598 if (change_file_mode_rw(dest_fp, dest) < 0) {
2599 FILE_OP_ERROR(dest, "chmod");
2600 g_warning("can't change file mode\n");
2603 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2604 if (n_read < sizeof(buf) && ferror(src_fp))
2606 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2607 g_warning("writing to %s failed.\n", dest);
2612 if (rename_force(dest_bak, dest) < 0)
2613 FILE_OP_ERROR(dest_bak, "rename");
2620 if (ferror(src_fp)) {
2621 FILE_OP_ERROR(src, "fread");
2625 if (fclose(dest_fp) == EOF) {
2626 FILE_OP_ERROR(dest, "fclose");
2633 if (rename_force(dest_bak, dest) < 0)
2634 FILE_OP_ERROR(dest_bak, "rename");
2640 if (keep_backup == FALSE && dest_bak)
2641 claws_unlink(dest_bak);
2648 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2650 if (overwrite == FALSE && is_file_exist(dest)) {
2651 g_warning("move_file(): file %s already exists.", dest);
2655 if (rename_force(src, dest) == 0) return 0;
2657 if (EXDEV != errno) {
2658 FILE_OP_ERROR(src, "rename");
2662 if (copy_file(src, dest, FALSE) < 0) return -1;
2669 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2672 gint bytes_left, to_read;
2675 if (fseek(fp, offset, SEEK_SET) < 0) {
2680 bytes_left = length;
2681 to_read = MIN(bytes_left, sizeof(buf));
2683 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2684 if (n_read < to_read && ferror(fp))
2686 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2689 bytes_left -= n_read;
2690 if (bytes_left == 0)
2692 to_read = MIN(bytes_left, sizeof(buf));
2703 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2706 gboolean err = FALSE;
2708 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2709 FILE_OP_ERROR(dest, "g_fopen");
2713 if (change_file_mode_rw(dest_fp, dest) < 0) {
2714 FILE_OP_ERROR(dest, "chmod");
2715 g_warning("can't change file mode\n");
2718 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2721 if (!err && fclose(dest_fp) == EOF) {
2722 FILE_OP_ERROR(dest, "fclose");
2727 g_warning("writing to %s failed.\n", dest);
2735 /* convert line endings into CRLF. If the last line doesn't end with
2736 * linebreak, add it.
2738 gchar *canonicalize_str(const gchar *str)
2744 for (p = str; *p != '\0'; ++p) {
2751 if (p == str || *(p - 1) != '\n')
2754 out = outp = g_malloc(new_len + 1);
2755 for (p = str; *p != '\0'; ++p) {
2762 if (p == str || *(p - 1) != '\n') {
2771 gint canonicalize_file(const gchar *src, const gchar *dest)
2773 FILE *src_fp, *dest_fp;
2774 gchar buf[BUFFSIZE];
2776 gboolean err = FALSE;
2777 gboolean last_linebreak = FALSE;
2779 if (src == NULL || dest == NULL)
2782 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2783 FILE_OP_ERROR(src, "g_fopen");
2787 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2788 FILE_OP_ERROR(dest, "g_fopen");
2793 if (change_file_mode_rw(dest_fp, dest) < 0) {
2794 FILE_OP_ERROR(dest, "chmod");
2795 g_warning("can't change file mode\n");
2798 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2802 if (len == 0) break;
2803 last_linebreak = FALSE;
2805 if (buf[len - 1] != '\n') {
2806 last_linebreak = TRUE;
2807 r = fputs(buf, dest_fp);
2808 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2809 r = fputs(buf, dest_fp);
2812 r = fwrite(buf, 1, len - 1, dest_fp);
2817 r = fputs("\r\n", dest_fp);
2821 g_warning("writing to %s failed.\n", dest);
2829 if (last_linebreak == TRUE) {
2830 if (fputs("\r\n", dest_fp) == EOF)
2834 if (ferror(src_fp)) {
2835 FILE_OP_ERROR(src, "fgets");
2839 if (fclose(dest_fp) == EOF) {
2840 FILE_OP_ERROR(dest, "fclose");
2852 gint canonicalize_file_replace(const gchar *file)
2856 tmp_file = get_tmp_file();
2858 if (canonicalize_file(file, tmp_file) < 0) {
2863 if (move_file(tmp_file, file, TRUE) < 0) {
2864 g_warning("can't replace %s .\n", file);
2865 claws_unlink(tmp_file);
2874 gchar *normalize_newlines(const gchar *str)
2876 const gchar *p = str;
2879 out = outp = g_malloc(strlen(str) + 1);
2880 for (p = str; *p != '\0'; ++p) {
2882 if (*(p + 1) != '\n')
2893 gchar *get_outgoing_rfc2822_str(FILE *fp)
2895 gchar buf[BUFFSIZE];
2899 str = g_string_new(NULL);
2901 /* output header part */
2902 while (fgets(buf, sizeof(buf), fp) != NULL) {
2904 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2911 else if (next != ' ' && next != '\t') {
2915 if (fgets(buf, sizeof(buf), fp) == NULL)
2919 g_string_append(str, buf);
2920 g_string_append(str, "\r\n");
2926 /* output body part */
2927 while (fgets(buf, sizeof(buf), fp) != NULL) {
2930 g_string_append_c(str, '.');
2931 g_string_append(str, buf);
2932 g_string_append(str, "\r\n");
2936 g_string_free(str, FALSE);
2942 * Create a new boundary in a way that it is very unlikely that this
2943 * will occur in the following text. It would be easy to ensure
2944 * uniqueness if everything is either quoted-printable or base64
2945 * encoded (note that conversion is allowed), but because MIME bodies
2946 * may be nested, it may happen that the same boundary has already
2949 * boundary := 0*69<bchars> bcharsnospace
2950 * bchars := bcharsnospace / " "
2951 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2952 * "+" / "_" / "," / "-" / "." /
2953 * "/" / ":" / "=" / "?"
2955 * some special characters removed because of buggy MTAs
2958 gchar *generate_mime_boundary(const gchar *prefix)
2960 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2961 "abcdefghijklmnopqrstuvwxyz"
2966 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2967 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2970 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2974 gint change_file_mode_rw(FILE *fp, const gchar *file)
2977 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2979 return g_chmod(file, S_IRUSR|S_IWUSR);
2983 FILE *my_tmpfile(void)
2985 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2986 const gchar suffix[] = ".XXXXXX";
2987 const gchar *tmpdir;
2989 const gchar *progname;
2998 tmpdir = get_tmp_dir();
2999 tmplen = strlen(tmpdir);
3000 progname = g_get_prgname();
3001 if (progname == NULL)
3002 progname = "claws-mail";
3003 proglen = strlen(progname);
3004 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3007 memcpy(fname, tmpdir, tmplen);
3008 fname[tmplen] = G_DIR_SEPARATOR;
3009 memcpy(fname + tmplen + 1, progname, proglen);
3010 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3012 fd = mkstemp(fname);
3017 claws_unlink(fname);
3019 /* verify that we can write in the file after unlinking */
3020 if (write(fd, buf, 1) < 0) {
3027 fp = fdopen(fd, "w+b");
3035 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3040 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3044 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3045 dir, G_DIR_SEPARATOR);
3046 fd = mkstemp_name(template, filename);
3049 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3050 fd = mkstemp(*filename);
3052 return fdopen(fd, "w+");
3055 FILE *str_open_as_stream(const gchar *str)
3060 cm_return_val_if_fail(str != NULL, NULL);
3064 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3069 if (len == 0) return fp;
3071 if (fwrite(str, 1, len, fp) != len) {
3072 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3081 gint str_write_to_file(const gchar *str, const gchar *file)
3086 cm_return_val_if_fail(str != NULL, -1);
3087 cm_return_val_if_fail(file != NULL, -1);
3089 if ((fp = g_fopen(file, "wb")) == NULL) {
3090 FILE_OP_ERROR(file, "g_fopen");
3100 if (fwrite(str, 1, len, fp) != len) {
3101 FILE_OP_ERROR(file, "fwrite");
3107 if (fclose(fp) == EOF) {
3108 FILE_OP_ERROR(file, "fclose");
3116 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3123 cm_return_val_if_fail(fp != NULL, NULL);
3125 array = g_byte_array_new();
3127 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3128 if (n_read < sizeof(buf) && ferror(fp))
3130 g_byte_array_append(array, buf, n_read);
3134 FILE_OP_ERROR("file stream", "fread");
3135 g_byte_array_free(array, TRUE);
3140 g_byte_array_append(array, buf, 1);
3141 str = (gchar *)array->data;
3142 g_byte_array_free(array, FALSE);
3144 if (recode && !g_utf8_validate(str, -1, NULL)) {
3145 const gchar *src_codeset, *dest_codeset;
3147 src_codeset = conv_get_locale_charset_str();
3148 dest_codeset = CS_UTF_8;
3149 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3157 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3164 struct timeval timeout = {1, 0};
3169 cm_return_val_if_fail(file != NULL, NULL);
3171 if (g_stat(file, &s) != 0) {
3172 FILE_OP_ERROR(file, "stat");
3175 if (S_ISDIR(s.st_mode)) {
3176 g_warning("%s: is a directory\n", file);
3181 fp = g_fopen (file, "rb");
3183 FILE_OP_ERROR(file, "open");
3187 /* test whether the file is readable without blocking */
3188 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3190 FILE_OP_ERROR(file, "open");
3197 /* allow for one second */
3198 err = select(fd+1, &fds, NULL, NULL, &timeout);
3199 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3201 FILE_OP_ERROR(file, "select");
3203 g_warning("%s: doesn't seem readable\n", file);
3209 /* Now clear O_NONBLOCK */
3210 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3211 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3215 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3216 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3221 /* get the FILE pointer */
3222 fp = fdopen(fd, "rb");
3225 FILE_OP_ERROR(file, "fdopen");
3226 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3231 str = file_read_stream_to_str_full(fp, recode);
3238 gchar *file_read_to_str(const gchar *file)
3240 return file_read_to_str_full(file, TRUE);
3242 gchar *file_read_stream_to_str(FILE *fp)
3244 return file_read_stream_to_str_full(fp, TRUE);
3247 gchar *file_read_to_str_no_recode(const gchar *file)
3249 return file_read_to_str_full(file, FALSE);
3251 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3253 return file_read_stream_to_str_full(fp, FALSE);
3256 char *fgets_crlf(char *buf, int size, FILE *stream)
3258 gboolean is_cr = FALSE;
3259 gboolean last_was_cr = FALSE;
3264 while (--size > 0 && (c = getc(stream)) != EOF)
3267 is_cr = (c == '\r');
3277 last_was_cr = is_cr;
3279 if (c == EOF && cs == buf)
3287 static gint execute_async(gchar *const argv[])
3289 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3291 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3292 NULL, NULL, NULL, FALSE) == FALSE) {
3293 g_warning("Couldn't execute command: %s\n", argv[0]);
3300 static gint execute_sync(gchar *const argv[])
3304 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3307 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3308 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3309 g_warning("Couldn't execute command: %s\n", argv[0]);
3313 if (WIFEXITED(status))
3314 return WEXITSTATUS(status);
3318 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3319 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3320 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3321 g_warning("Couldn't execute command: %s\n", argv[0]);
3329 gint execute_command_line(const gchar *cmdline, gboolean async)
3334 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3336 argv = strsplit_with_quote(cmdline, " ", 0);
3339 ret = execute_async(argv);
3341 ret = execute_sync(argv);
3348 gchar *get_command_output(const gchar *cmdline)
3350 gchar *child_stdout;
3353 cm_return_val_if_fail(cmdline != NULL, NULL);
3355 debug_print("get_command_output(): executing: %s\n", cmdline);
3357 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3359 g_warning("Couldn't execute command: %s\n", cmdline);
3363 return child_stdout;
3366 static gint is_unchanged_uri_char(char c)
3377 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3383 for(i = 0; i < strlen(uri) ; i++) {
3384 if (is_unchanged_uri_char(uri[i])) {
3385 if (k + 2 >= bufsize)
3387 encoded_uri[k++] = uri[i];
3390 char * hexa = "0123456789ABCDEF";
3392 if (k + 4 >= bufsize)
3394 encoded_uri[k++] = '%';
3395 encoded_uri[k++] = hexa[uri[i] / 16];
3396 encoded_uri[k++] = hexa[uri[i] % 16];
3402 gint open_uri(const gchar *uri, const gchar *cmdline)
3406 gchar buf[BUFFSIZE];
3408 gchar encoded_uri[BUFFSIZE];
3409 cm_return_val_if_fail(uri != NULL, -1);
3411 /* an option to choose whether to use encode_uri or not ? */
3412 encode_uri(encoded_uri, BUFFSIZE, uri);
3415 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3416 !strchr(p + 2, '%'))
3417 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3420 g_warning("Open URI command-line is invalid "
3421 "(there must be only one '%%s'): %s",
3423 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3426 execute_command_line(buf, TRUE);
3428 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3431 extern osso_context_t *get_osso_context(void);
3432 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3433 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3434 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3439 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3441 gchar buf[BUFFSIZE];
3444 cm_return_val_if_fail(filepath != NULL, -1);
3447 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3448 !strchr(p + 2, '%'))
3449 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3452 g_warning("Open Text Editor command-line is invalid "
3453 "(there must be only one '%%s'): %s",
3455 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3458 execute_command_line(buf, TRUE);
3463 time_t remote_tzoffset_sec(const gchar *zone)
3465 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3471 time_t remoteoffset;
3473 strncpy(zone3, zone, 3);
3477 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3478 (c == '+' || c == '-')) {
3479 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3481 remoteoffset = -remoteoffset;
3482 } else if (!strncmp(zone, "UT" , 2) ||
3483 !strncmp(zone, "GMT", 2)) {
3485 } else if (strlen(zone3) == 3) {
3486 for (p = ustzstr; *p != '\0'; p += 3) {
3487 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3488 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3489 remoteoffset = iustz * 3600;
3495 } else if (strlen(zone3) == 1) {
3497 case 'Z': remoteoffset = 0; break;
3498 case 'A': remoteoffset = -1; break;
3499 case 'B': remoteoffset = -2; break;
3500 case 'C': remoteoffset = -3; break;
3501 case 'D': remoteoffset = -4; break;
3502 case 'E': remoteoffset = -5; break;
3503 case 'F': remoteoffset = -6; break;
3504 case 'G': remoteoffset = -7; break;
3505 case 'H': remoteoffset = -8; break;
3506 case 'I': remoteoffset = -9; break;
3507 case 'K': remoteoffset = -10; break; /* J is not used */
3508 case 'L': remoteoffset = -11; break;
3509 case 'M': remoteoffset = -12; break;
3510 case 'N': remoteoffset = 1; break;
3511 case 'O': remoteoffset = 2; break;
3512 case 'P': remoteoffset = 3; break;
3513 case 'Q': remoteoffset = 4; break;
3514 case 'R': remoteoffset = 5; break;
3515 case 'S': remoteoffset = 6; break;
3516 case 'T': remoteoffset = 7; break;
3517 case 'U': remoteoffset = 8; break;
3518 case 'V': remoteoffset = 9; break;
3519 case 'W': remoteoffset = 10; break;
3520 case 'X': remoteoffset = 11; break;
3521 case 'Y': remoteoffset = 12; break;
3522 default: remoteoffset = 0; break;
3524 remoteoffset = remoteoffset * 3600;
3528 return remoteoffset;
3531 time_t tzoffset_sec(time_t *now)
3536 struct tm buf1, buf2;
3539 if (now && *now < 0)
3542 gmt = *gmtime_r(now, &buf1);
3543 lt = localtime_r(now, &buf2);
3545 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3547 if (lt->tm_year < gmt.tm_year)
3549 else if (lt->tm_year > gmt.tm_year)
3551 else if (lt->tm_yday < gmt.tm_yday)
3553 else if (lt->tm_yday > gmt.tm_yday)
3556 if (off >= 24 * 60) /* should be impossible */
3557 off = 23 * 60 + 59; /* if not, insert silly value */
3558 if (off <= -24 * 60)
3559 off = -(23 * 60 + 59);
3564 /* calculate timezone offset */
3565 gchar *tzoffset(time_t *now)
3567 static gchar offset_string[6];
3572 struct tm buf1, buf2;
3575 if (now && *now < 0)
3578 gmt = *gmtime_r(now, &buf1);
3579 lt = localtime_r(now, &buf2);
3581 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3583 if (lt->tm_year < gmt.tm_year)
3585 else if (lt->tm_year > gmt.tm_year)
3587 else if (lt->tm_yday < gmt.tm_yday)
3589 else if (lt->tm_yday > gmt.tm_yday)
3597 if (off >= 24 * 60) /* should be impossible */
3598 off = 23 * 60 + 59; /* if not, insert silly value */
3600 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3602 return offset_string;
3605 void get_rfc822_date(gchar *buf, gint len)
3609 gchar day[4], mon[4];
3610 gint dd, hh, mm, ss, yyyy;
3613 gchar buf2[BUFFSIZE];
3617 lt = localtime_r(&t, &buf1);
3619 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3620 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3622 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3623 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3626 void debug_set_mode(gboolean mode)
3631 gboolean debug_get_mode(void)
3636 void debug_print_real(const gchar *format, ...)
3639 gchar buf[BUFFSIZE];
3641 if (!debug_mode) return;
3643 va_start(args, format);
3644 g_vsnprintf(buf, sizeof(buf), format, args);
3651 const char * debug_srcname(const char *file)
3653 const char *s = strrchr (file, '/');
3658 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3660 if (subject == NULL)
3663 subject += subject_get_prefix_length(subject);
3665 return g_hash_table_lookup(subject_table, subject);
3668 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3671 if (subject == NULL || *subject == 0)
3673 subject += subject_get_prefix_length(subject);
3674 g_hash_table_insert(subject_table, subject, data);
3677 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3679 if (subject == NULL)
3682 subject += subject_get_prefix_length(subject);
3683 g_hash_table_remove(subject_table, subject);
3687 static regex_t u_regex;
3688 static gboolean u_init_;
3691 void utils_free_regex(void)
3702 *\brief Check if a string is prefixed with known (combinations)
3703 * of prefixes. The function assumes that each prefix
3704 * is terminated by zero or exactly _one_ space.
3706 *\param str String to check for a prefixes
3708 *\return int Number of chars in the prefix that should be skipped
3709 * for a "clean" subject line. If no prefix was found, 0
3712 int subject_get_prefix_length(const gchar *subject)
3715 /*!< Array with allowable reply prefixes regexps. */
3716 static const gchar * const prefixes[] = {
3717 "Re\\:", /* "Re:" */
3718 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3719 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3720 "Aw\\:", /* "Aw:" (German) */
3721 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3722 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3723 "Fw\\:", /* "Fw:" Forward */
3724 "Fwd\\:", /* "Fwd:" Forward */
3725 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3726 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3727 "Rif\\:", /* "Rif:" (Italian Outlook) */
3728 "Sv\\:", /* "Sv" (Norwegian) */
3729 "Vs\\:", /* "Vs" (Norwegian) */
3730 "Ad\\:", /* "Ad" (Norwegian) */
3731 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3732 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3733 "Re \\:", /* "Re :" (French Yahoo Mail) */
3736 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3740 if (!subject) return 0;
3741 if (!*subject) return 0;
3744 GString *s = g_string_new("");
3746 for (n = 0; n < PREFIXES; n++)
3747 /* Terminate each prefix regexpression by a
3748 * "\ ?" (zero or ONE space), and OR them */
3749 g_string_append_printf(s, "(%s\\ ?)%s",
3754 g_string_prepend(s, "(");
3755 g_string_append(s, ")+"); /* match at least once */
3756 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3759 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3760 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3761 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3762 debug_print("Error compiling regexp %s\n", s->str);
3763 g_string_free(s, TRUE);
3767 g_string_free(s, TRUE);
3771 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3776 /*!< Array with allowable reply prefixes regexps. */
3777 static const gchar * const prefixes[] = {
3779 "antw:", /* "Antw:" (Dutch / German Outlook) */
3780 "aw:", /* "Aw:" (German) */
3781 "antwort:", /* "Antwort:" (German Lotus Notes) */
3782 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3783 "fw:", /* "Fw:" Forward */
3784 "fwd:", /* "Fwd:" Forward */
3785 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3786 "odp:", /* "Odp:" Re (Polish Outlook) */
3787 "rif:", /* "Rif:" (Italian Outlook) */
3788 "sv:", /* "Sv" (Norwegian) */
3789 "vs:", /* "Vs" (Norwegian) */
3790 "ad:", /* "Ad" (Norwegian) */
3791 "R\303\251f. :", /* "R�f. :" (French Lotus Notes) */
3792 "Re :", /* "Re :" (French Yahoo Mail) */
3795 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3798 if (!subject) return 0;
3799 if (!*subject) return 0;
3801 for (n = 0; n < PREFIXES; n++) {
3802 int len = strlen(prefixes[n]);
3803 if (!strncasecmp(subject, prefixes[n], len)) {
3804 if (subject[len] == ' ')
3813 static guint g_stricase_hash(gconstpointer gptr)
3815 guint hash_result = 0;
3818 for (str = gptr; str && *str; str++) {
3819 hash_result += toupper(*str);
3825 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3827 const char *str1 = gptr1;
3828 const char *str2 = gptr2;
3830 return !strcasecmp(str1, str2);
3833 gint g_int_compare(gconstpointer a, gconstpointer b)
3835 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3838 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3848 lt = localtime_r(&t, &buft);
3850 if (strcmp(buf, "") == 0) {
3851 if (user_addr != NULL) {
3852 addr = g_strconcat(user_addr, "@", get_domain_name(), NULL);
3855 addr = g_strconcat("@", get_domain_name(), NULL);
3859 if (user_addr != NULL) {
3860 addr = g_strconcat(user_addr, "@", buf, NULL);
3863 addr = g_strconcat("@", buf, NULL);
3867 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3868 lt->tm_year + 1900, lt->tm_mon + 1,
3869 lt->tm_mday, lt->tm_hour,
3870 lt->tm_min, lt->tm_sec,
3871 (guint) rand(), addr);
3878 quote_cmd_argument()
3880 return a quoted string safely usable in argument of a command.
3882 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3885 gint quote_cmd_argument(gchar * result, guint size,
3895 for(p = path ; * p != '\0' ; p ++) {
3897 if (isalnum((guchar)*p) || (* p == '/')) {
3898 if (remaining > 0) {
3904 result[size - 1] = '\0';
3909 if (remaining >= 2) {
3917 result[size - 1] = '\0';
3922 if (remaining > 0) {
3926 result[size - 1] = '\0';
3940 static void g_node_map_recursive(GNode *node, gpointer data)
3942 GNodeMapData *mapdata = (GNodeMapData *) data;
3944 GNodeMapData newmapdata;
3947 newdata = mapdata->func(node->data, mapdata->data);
3948 if (newdata != NULL) {
3949 newnode = g_node_new(newdata);
3950 g_node_append(mapdata->parent, newnode);
3952 newmapdata.parent = newnode;
3953 newmapdata.func = mapdata->func;
3954 newmapdata.data = mapdata->data;
3956 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3960 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3963 GNodeMapData mapdata;
3965 cm_return_val_if_fail(node != NULL, NULL);
3966 cm_return_val_if_fail(func != NULL, NULL);
3968 root = g_node_new(func(node->data, data));
3970 mapdata.parent = root;
3971 mapdata.func = func;
3972 mapdata.data = data;
3974 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3979 #define HEX_TO_INT(val, hex) \
3983 if ('0' <= c && c <= '9') { \
3985 } else if ('a' <= c && c <= 'f') { \
3986 val = c - 'a' + 10; \
3987 } else if ('A' <= c && c <= 'F') { \
3988 val = c - 'A' + 10; \
3994 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4001 if (hi == -1 || lo == -1)
4004 *out = (hi << 4) + lo;
4008 #define INT_TO_HEX(hex, val) \
4011 hex = '0' + (val); \
4013 hex = 'A' + (val) - 10; \
4016 void get_hex_str(gchar *out, guchar ch)
4020 INT_TO_HEX(hex, ch >> 4);
4022 INT_TO_HEX(hex, ch & 0x0f);
4028 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4030 #define G_PRINT_REF g_print
4034 *\brief Register ref counted pointer. It is based on GBoxed, so should
4035 * work with anything that uses the GType system. The semantics
4036 * are similar to a C++ auto pointer, with the exception that
4037 * C doesn't have automatic closure (calling destructors) when
4038 * exiting a block scope.
4039 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4040 * function directly.
4042 *\return GType A GType type.
4044 GType g_auto_pointer_register(void)
4046 static GType auto_pointer_type;
4047 if (!auto_pointer_type)
4049 g_boxed_type_register_static
4050 ("G_TYPE_AUTO_POINTER",
4051 (GBoxedCopyFunc) g_auto_pointer_copy,
4052 (GBoxedFreeFunc) g_auto_pointer_free);
4053 return auto_pointer_type;
4057 *\brief Structure with g_new() allocated pointer guarded by the
4060 typedef struct AutoPointerRef {
4061 void (*free) (gpointer);
4067 *\brief The auto pointer opaque structure that references the
4068 * pointer guard block.
4070 typedef struct AutoPointer {
4071 AutoPointerRef *ref;
4072 gpointer ptr; /*!< access to protected pointer */
4076 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4080 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4081 * ... when assigning, copying and freeing storage elements
4083 * gtk_list_store_new(N_S_COLUMNS,
4084 * G_TYPE_AUTO_POINTER,
4088 * Template *precious_data = g_new0(Template, 1);
4089 * g_pointer protect = g_auto_pointer_new(precious_data);
4091 * gtk_list_store_set(container, &iter,
4095 * ... the gtk_list_store has copied the pointer and
4096 * ... incremented its reference count, we should free
4097 * ... the auto pointer (in C++ a destructor would do
4098 * ... this for us when leaving block scope)
4100 * g_auto_pointer_free(protect);
4102 * ... gtk_list_store_set() now manages the data. When
4103 * ... *explicitly* requesting a pointer from the list
4104 * ... store, don't forget you get a copy that should be
4105 * ... freed with g_auto_pointer_free() eventually.
4109 *\param pointer Pointer to be guarded.
4111 *\return GAuto * Pointer that should be used in containers with
4114 GAuto *g_auto_pointer_new(gpointer p)
4116 AutoPointerRef *ref;
4122 ref = g_new0(AutoPointerRef, 1);
4123 ptr = g_new0(AutoPointer, 1);
4133 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4139 *\brief Allocate an autopointer using the passed \a free function to
4140 * free the guarded pointer
4142 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4149 aptr = g_auto_pointer_new(p);
4150 aptr->ref->free = free_;
4154 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4156 if (auto_ptr == NULL)
4158 return ((AutoPointer *) auto_ptr)->ptr;
4162 *\brief Copies an auto pointer by. It's mostly not necessary
4163 * to call this function directly, unless you copy/assign
4164 * the guarded pointer.
4166 *\param auto_ptr Auto pointer returned by previous call to
4167 * g_auto_pointer_new_XXX()
4169 *\return gpointer An auto pointer
4171 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4174 AutoPointerRef *ref;
4177 if (auto_ptr == NULL)
4182 newp = g_new0(AutoPointer, 1);
4185 newp->ptr = ref->pointer;
4189 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4195 *\brief Free an auto pointer
4197 void g_auto_pointer_free(GAuto *auto_ptr)
4200 AutoPointerRef *ref;
4202 if (auto_ptr == NULL)
4208 if (--(ref->cnt) == 0) {
4210 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4212 ref->free(ref->pointer);
4217 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4222 void replace_returns(gchar *str)
4227 while (strstr(str, "\n")) {
4228 *strstr(str, "\n") = ' ';
4230 while (strstr(str, "\r")) {
4231 *strstr(str, "\r") = ' ';
4235 /* get_uri_part() - retrieves a URI starting from scanpos.
4236 Returns TRUE if succesful */
4237 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4238 const gchar **bp, const gchar **ep, gboolean hdr)
4241 gint parenthese_cnt = 0;
4243 cm_return_val_if_fail(start != NULL, FALSE);
4244 cm_return_val_if_fail(scanpos != NULL, FALSE);
4245 cm_return_val_if_fail(bp != NULL, FALSE);
4246 cm_return_val_if_fail(ep != NULL, FALSE);
4250 /* find end point of URI */
4251 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4252 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4253 !IS_ASCII(*(const guchar *)ep_) ||
4254 strchr("[]{}<>\"", *ep_)) {
4256 } else if (strchr("(", *ep_)) {
4258 } else if (strchr(")", *ep_)) {
4259 if (parenthese_cnt > 0)
4266 /* no punctuation at end of string */
4268 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4269 * should pass some URI type to this function and decide on that whether
4270 * to perform punctuation stripping */
4272 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4274 for (; ep_ - 1 > scanpos + 1 &&
4275 IS_REAL_PUNCT(*(ep_ - 1));
4279 #undef IS_REAL_PUNCT
4286 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4288 while (bp && *bp && g_ascii_isspace(*bp))
4290 return g_strndup(bp, ep - bp);
4293 /* valid mail address characters */
4294 #define IS_RFC822_CHAR(ch) \
4298 !g_ascii_isspace(ch) && \
4299 !strchr("(),;<>\"", (ch)))
4301 /* alphabet and number within 7bit ASCII */
4302 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4303 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4305 static GHashTable *create_domain_tab(void)
4307 static const gchar *toplvl_domains [] = {
4309 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4310 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4311 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4312 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4313 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4314 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4315 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4316 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4317 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4318 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4319 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4320 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4321 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4322 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4323 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4324 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4325 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4326 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4327 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4328 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4329 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4330 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4331 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4332 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4333 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4334 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4337 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4339 cm_return_val_if_fail(htab, NULL);
4340 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4341 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4345 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4347 const gint MAX_LVL_DOM_NAME_LEN = 6;
4348 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4349 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4352 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4355 for (p = buf; p < m && first < last; *p++ = *first++)
4359 return g_hash_table_lookup(tab, buf) != NULL;
4362 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4363 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4364 const gchar **bp, const gchar **ep, gboolean hdr)
4366 /* more complex than the uri part because we need to scan back and forward starting from
4367 * the scan position. */
4368 gboolean result = FALSE;
4369 const gchar *bp_ = NULL;
4370 const gchar *ep_ = NULL;
4371 static GHashTable *dom_tab;
4372 const gchar *last_dot = NULL;
4373 const gchar *prelast_dot = NULL;
4374 const gchar *last_tld_char = NULL;
4376 /* the informative part of the email address (describing the name
4377 * of the email address owner) may contain quoted parts. the
4378 * closure stack stores the last encountered quotes. */
4379 gchar closure_stack[128];
4380 gchar *ptr = closure_stack;
4382 cm_return_val_if_fail(start != NULL, FALSE);
4383 cm_return_val_if_fail(scanpos != NULL, FALSE);
4384 cm_return_val_if_fail(bp != NULL, FALSE);
4385 cm_return_val_if_fail(ep != NULL, FALSE);
4388 const gchar *start_quote = NULL;
4389 const gchar *end_quote = NULL;
4391 /* go to the real start */
4392 if (start[0] == ',')
4394 if (start[0] == ';')
4396 while (start[0] == '\n' || start[0] == '\r')
4398 while (start[0] == ' ' || start[0] == '\t')
4403 /* check if there are quotes (to skip , in them) */
4404 if (*start == '"') {
4405 start_quote = start;
4407 end_quote = strstr(start, "\"");
4413 /* skip anything between quotes */
4414 if (start_quote && end_quote) {
4419 /* find end (either , or ; or end of line) */
4420 if (strstr(start, ",") && strstr(start, ";"))
4421 *ep = strstr(start,",") < strstr(start, ";")
4422 ? strstr(start, ",") : strstr(start, ";");
4423 else if (strstr(start, ","))
4424 *ep = strstr(start, ",");
4425 else if (strstr(start, ";"))
4426 *ep = strstr(start, ";");
4428 *ep = start+strlen(start);
4430 /* go back to real start */
4431 if (start_quote && end_quote) {
4432 start = start_quote;
4435 /* check there's still an @ in that, or search
4436 * further if possible */
4437 if (strstr(start, "@") && strstr(start, "@") < *ep)
4439 else if (*ep < start+strlen(start)) {
4442 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4450 dom_tab = create_domain_tab();
4451 cm_return_val_if_fail(dom_tab, FALSE);
4453 /* scan start of address */
4454 for (bp_ = scanpos - 1;
4455 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4458 /* TODO: should start with an alnum? */
4460 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4463 if (bp_ != scanpos) {
4464 /* scan end of address */
4465 for (ep_ = scanpos + 1;
4466 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4468 prelast_dot = last_dot;
4470 if (*(last_dot + 1) == '.') {
4471 if (prelast_dot == NULL)
4473 last_dot = prelast_dot;
4478 /* TODO: really should terminate with an alnum? */
4479 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4484 if (last_dot == NULL)
4486 if (last_dot >= ep_)
4487 last_dot = prelast_dot;
4488 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4492 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4493 if (*last_tld_char == '?')
4496 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4503 if (!result) return FALSE;
4505 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4506 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4507 && IS_RFC822_CHAR(*(ep_ + 3))) {
4508 /* this informative part with an @ in it is
4509 * followed by the email address */
4512 /* go to matching '>' (or next non-rfc822 char, like \n) */
4513 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4516 /* include the bracket */
4517 if (*ep_ == '>') ep_++;
4519 /* include the leading quote */
4527 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4528 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4531 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4532 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4535 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4536 #define IN_STACK() (ptr > closure_stack)
4537 /* has underrun check */
4538 #define POP_STACK() if(IN_STACK()) --ptr
4539 /* has overrun check */
4540 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4541 /* has underrun check */
4542 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4546 /* scan for the informative part. */
4547 for (bp_ -= 2; bp_ >= start; bp_--) {
4548 /* if closure on the stack keep scanning */
4549 if (PEEK_STACK() == *bp_) {
4553 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4558 /* if nothing in the closure stack, do the special conditions
4559 * the following if..else expression simply checks whether
4560 * a token is acceptable. if not acceptable, the clause
4561 * should terminate the loop with a 'break' */
4562 if (!PEEK_STACK()) {
4564 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4565 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4566 /* hyphens are allowed, but only in
4568 } else if (strchr(" \"'", *bp_)) {
4569 /* but anything not being a punctiation
4572 break; /* anything else is rejected */
4579 /* scan forward (should start with an alnum) */
4580 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4596 #undef IS_ASCII_ALNUM
4597 #undef IS_RFC822_CHAR
4599 gchar *make_email_string(const gchar *bp, const gchar *ep)
4601 /* returns a mailto: URI; mailto: is also used to detect the
4602 * uri type later on in the button_pressed signal handler */
4606 tmp = g_strndup(bp, ep - bp);
4607 result = g_strconcat("mailto:", tmp, NULL);
4613 gchar *make_http_string(const gchar *bp, const gchar *ep)
4615 /* returns an http: URI; */
4619 while (bp && *bp && g_ascii_isspace(*bp))
4621 tmp = g_strndup(bp, ep - bp);
4622 result = g_strconcat("http://", tmp, NULL);
4628 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4630 FILE *fp = g_fopen(path, "rb");
4631 gchar buf[BUFFSIZE];
4632 gchar *result = NULL;
4635 while (fgets(buf, sizeof (buf), fp) != NULL) {
4636 gchar **parts = g_strsplit(buf, ";", 3);
4637 gchar *trimmed = parts[0];
4638 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4640 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4641 trimmed[strlen(trimmed)-1] = '\0';
4643 if (!strcmp(trimmed, type)) {
4644 gboolean needsterminal = FALSE;
4645 if (parts[2] && strstr(parts[2], "needsterminal")) {
4646 needsterminal = TRUE;
4648 if (parts[2] && strstr(parts[2], "test=")) {
4649 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4650 gchar *testcmd = orig_testcmd;
4651 if (strstr(testcmd,";"))
4652 *(strstr(testcmd,";")) = '\0';
4653 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4655 while (testcmd[strlen(testcmd)-1] == '\n')
4656 testcmd[strlen(testcmd)-1] = '\0';
4657 while (testcmd[strlen(testcmd)-1] == '\r')
4658 testcmd[strlen(testcmd)-1] = '\0';
4659 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4660 testcmd[strlen(testcmd)-1] = '\0';
4662 if (strstr(testcmd, "%s")) {
4663 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4664 gint res = system(tmp);
4666 g_free(orig_testcmd);
4673 gint res = system(testcmd);
4674 g_free(orig_testcmd);
4684 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4686 while (trimmed[strlen(trimmed)-1] == '\n')
4687 trimmed[strlen(trimmed)-1] = '\0';
4688 while (trimmed[strlen(trimmed)-1] == '\r')
4689 trimmed[strlen(trimmed)-1] = '\0';
4690 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4691 trimmed[strlen(trimmed)-1] = '\0';
4692 result = g_strdup(trimmed);
4695 /* if there are no single quotes around %s, add them.
4696 * '.*%s.*' is ok, as in display 'png:%s'
4698 if (strstr(result, "%s")
4699 && !(strstr(result, "'") < strstr(result,"%s") &&
4700 strstr(strstr(result,"%s"), "'"))) {
4701 gchar *start = g_strdup(result);
4702 gchar *end = g_strdup(strstr(result, "%s")+2);
4704 *strstr(start, "%s") = '\0';
4705 tmp = g_strconcat(start,"'%s'",end, NULL);
4711 if (needsterminal) {
4712 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4723 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4725 gchar *result = NULL;
4729 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4730 result = mailcap_get_command_in_file(path, type, file_to_open);
4734 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4738 void mailcap_update_default(const gchar *type, const gchar *command)
4740 gchar *path = NULL, *outpath = NULL;
4741 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4742 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4743 FILE *fp = g_fopen(path, "rb");
4744 FILE *outfp = g_fopen(outpath, "wb");
4745 gchar buf[BUFFSIZE];
4746 gboolean err = FALSE;
4754 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4755 gchar **parts = g_strsplit(buf, ";", 3);
4756 gchar *trimmed = parts[0];
4757 while (trimmed[0] == ' ')
4759 while (trimmed[strlen(trimmed)-1] == ' ')
4760 trimmed[strlen(trimmed)-1] = '\0';
4762 if (!strcmp(trimmed, type)) {
4767 if(fputs(buf, outfp) == EOF) {
4774 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4780 if (fclose(outfp) == EOF)
4784 g_rename(outpath, path);
4790 gint copy_dir(const gchar *src, const gchar *dst)
4795 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4796 g_warning("failed to open directory: %s\n", src);
4800 if (make_dir(dst) < 0)
4803 while ((name = g_dir_read_name(dir)) != NULL) {
4804 gchar *old_file, *new_file;
4805 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4806 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4807 debug_print("copying: %s -> %s\n", old_file, new_file);
4808 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4809 gint r = copy_file(old_file, new_file, TRUE);
4816 /* Windows has no symlinks. Or well, Vista seems to
4817 have something like this but the semantics might be
4818 different. Thus we don't use it under Windows. */
4819 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4822 gchar *target = g_file_read_link(old_file, &error);
4824 r = symlink(target, new_file);
4831 #endif /*G_OS_WIN32*/
4832 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4833 gint r = copy_dir(old_file, new_file);
4844 /* crude test to see if a file is an email. */
4845 gboolean file_is_email (const gchar *filename)
4851 if (filename == NULL)
4853 if ((fp = g_fopen(filename, "rb")) == NULL)
4855 while (i < 60 && score < 3
4856 && fgets(buffer, sizeof (buffer), fp) > 0) {
4857 if (!strncmp(buffer, "From:", strlen("From:")))
4859 if (!strncmp(buffer, "To:", strlen("To:")))
4861 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4866 return (score >= 3);
4869 gboolean sc_g_list_bigger(GList *list, gint max)
4873 while (cur && i <= max+1) {
4880 gboolean sc_g_slist_bigger(GSList *list, gint max)
4884 while (cur && i <= max+1) {
4891 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4892 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4893 NULL, NULL, NULL, NULL, NULL, NULL};
4894 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4895 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4896 NULL, NULL, NULL, NULL, NULL, NULL};
4898 gint daynames_len[] = {0,0,0,0,0,0,0};
4899 gint monthnames_len[] = {0,0,0,0,0,0,
4901 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4902 gint s_monthnames_len[] = {0,0,0,0,0,0,
4904 const gchar *s_am_up = NULL;
4905 const gchar *s_pm_up = NULL;
4906 const gchar *s_am_low = NULL;
4907 const gchar *s_pm_low = NULL;
4909 gint s_am_up_len = 0;
4910 gint s_pm_up_len = 0;
4911 gint s_am_low_len = 0;
4912 gint s_pm_low_len = 0;
4914 const gchar *def_loc_format = NULL;
4915 const gchar *date_loc_format = NULL;
4916 const gchar *time_loc_format = NULL;
4917 const gchar *time_am_pm = NULL;
4919 static gboolean time_names_init_done = FALSE;
4921 static void init_time_names(void)
4925 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4926 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4927 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4928 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4929 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4930 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4931 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4933 monthnames[0] = Q_("Complete month name for use by strftime|January");
4934 monthnames[1] = Q_("Complete month name for use by strftime|February");
4935 monthnames[2] = Q_("Complete month name for use by strftime|March");
4936 monthnames[3] = Q_("Complete month name for use by strftime|April");
4937 monthnames[4] = Q_("Complete month name for use by strftime|May");
4938 monthnames[5] = Q_("Complete month name for use by strftime|June");
4939 monthnames[6] = Q_("Complete month name for use by strftime|July");
4940 monthnames[7] = Q_("Complete month name for use by strftime|August");
4941 monthnames[8] = Q_("Complete month name for use by strftime|September");
4942 monthnames[9] = Q_("Complete month name for use by strftime|October");
4943 monthnames[10] = Q_("Complete month name for use by strftime|November");
4944 monthnames[11] = Q_("Complete month name for use by strftime|December");
4946 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4947 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4948 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4949 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4950 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4951 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4952 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4954 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4955 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4956 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4957 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4958 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4959 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4960 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4961 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4962 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4963 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4964 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4965 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4967 for (i = 0; i < 7; i++) {
4968 daynames_len[i] = strlen(daynames[i]);
4969 s_daynames_len[i] = strlen(s_daynames[i]);
4971 for (i = 0; i < 12; i++) {
4972 monthnames_len[i] = strlen(monthnames[i]);
4973 s_monthnames_len[i] = strlen(s_monthnames[i]);
4976 s_am_up = Q_("For use by strftime (morning)|AM");
4977 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4978 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4979 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4981 s_am_up_len = strlen(s_am_up);
4982 s_pm_up_len = strlen(s_pm_up);
4983 s_am_low_len = strlen(s_am_low);
4984 s_pm_low_len = strlen(s_pm_low);
4986 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4987 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4988 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4990 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4992 time_names_init_done = TRUE;
4995 #define CHECK_SIZE() { \
4996 total_done += len; \
4997 if (total_done >= buflen) { \
4998 buf[buflen-1] = '\0'; \
5003 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5005 gchar *curpos = buf;
5006 gint total_done = 0;
5007 gchar subbuf[64], subfmt[64];
5008 static time_t last_tzset = (time_t)0;
5010 if (!time_names_init_done)
5013 if (format == NULL || lt == NULL)
5016 if (last_tzset != time(NULL)) {
5018 last_tzset = time(NULL);
5021 if (*format == '%') {
5022 gint len = 0, tmp = 0;
5026 len = 1; CHECK_SIZE();
5030 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5031 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5034 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5035 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5039 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5040 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5043 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5044 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5047 fast_strftime(subbuf, 64, def_loc_format, lt);
5048 len = strlen(subbuf); CHECK_SIZE();
5049 strncpy2(curpos, subbuf, buflen - total_done);
5052 total_done += 2; CHECK_SIZE();
5053 tmp = (lt->tm_year + 1900)/100;
5054 *curpos++ = '0'+(tmp / 10);
5055 *curpos++ = '0'+(tmp % 10);
5058 total_done += 2; CHECK_SIZE();
5059 *curpos++ = '0'+(lt->tm_mday / 10);
5060 *curpos++ = '0'+(lt->tm_mday % 10);
5063 total_done += 8; CHECK_SIZE();
5064 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5065 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5067 *curpos++ = '0'+(lt->tm_mday / 10);
5068 *curpos++ = '0'+(lt->tm_mday % 10);
5070 tmp = lt->tm_year%100;
5071 *curpos++ = '0'+(tmp / 10);
5072 *curpos++ = '0'+(tmp % 10);
5075 len = 2; CHECK_SIZE();
5076 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5079 len = 10; CHECK_SIZE();
5080 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5081 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5084 total_done += 2; CHECK_SIZE();
5085 *curpos++ = '0'+(lt->tm_hour / 10);
5086 *curpos++ = '0'+(lt->tm_hour % 10);
5089 total_done += 2; CHECK_SIZE();
5095 *curpos++ = '0'+(tmp / 10);
5096 *curpos++ = '0'+(tmp % 10);
5099 len = 3; CHECK_SIZE();
5100 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5103 len = 2; CHECK_SIZE();
5104 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5107 len = 2; CHECK_SIZE();
5113 snprintf(curpos, buflen - total_done, "%2d", tmp);
5116 total_done += 2; CHECK_SIZE();
5117 tmp = lt->tm_mon + 1;
5118 *curpos++ = '0'+(tmp / 10);
5119 *curpos++ = '0'+(tmp % 10);
5122 total_done += 2; CHECK_SIZE();
5123 *curpos++ = '0'+(lt->tm_min / 10);
5124 *curpos++ = '0'+(lt->tm_min % 10);
5127 len = 1; CHECK_SIZE();
5131 if (lt->tm_hour >= 12) {
5132 len = s_pm_up_len; CHECK_SIZE();
5133 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5135 len = s_am_up_len; CHECK_SIZE();
5136 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5140 if (lt->tm_hour >= 12) {
5141 len = s_pm_low_len; CHECK_SIZE();
5142 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5144 len = s_am_low_len; CHECK_SIZE();
5145 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5149 fast_strftime(subbuf, 64, time_am_pm, lt);
5150 len = strlen(subbuf); CHECK_SIZE();
5151 strncpy2(curpos, subbuf, buflen - total_done);
5154 total_done += 5; CHECK_SIZE();
5155 *curpos++ = '0'+(lt->tm_hour / 10);
5156 *curpos++ = '0'+(lt->tm_hour % 10);
5158 *curpos++ = '0'+(lt->tm_min / 10);
5159 *curpos++ = '0'+(lt->tm_min % 10);
5162 snprintf(subbuf, 64, "%ld", mktime(lt));
5163 len = strlen(subbuf); CHECK_SIZE();
5164 strncpy2(curpos, subbuf, buflen - total_done);
5167 total_done += 2; CHECK_SIZE();
5168 *curpos++ = '0'+(lt->tm_sec / 10);
5169 *curpos++ = '0'+(lt->tm_sec % 10);
5172 len = 1; CHECK_SIZE();
5176 total_done += 8; CHECK_SIZE();
5177 *curpos++ = '0'+(lt->tm_hour / 10);
5178 *curpos++ = '0'+(lt->tm_hour % 10);
5180 *curpos++ = '0'+(lt->tm_min / 10);
5181 *curpos++ = '0'+(lt->tm_min % 10);
5183 *curpos++ = '0'+(lt->tm_sec / 10);
5184 *curpos++ = '0'+(lt->tm_sec % 10);
5187 len = 1; CHECK_SIZE();
5188 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5191 len = 1; CHECK_SIZE();
5192 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5195 fast_strftime(subbuf, 64, date_loc_format, lt);
5196 len = strlen(subbuf); CHECK_SIZE();
5197 strncpy2(curpos, subbuf, buflen - total_done);
5200 fast_strftime(subbuf, 64, time_loc_format, lt);
5201 len = strlen(subbuf); CHECK_SIZE();
5202 strncpy2(curpos, subbuf, buflen - total_done);
5205 total_done += 2; CHECK_SIZE();
5206 tmp = lt->tm_year%100;
5207 *curpos++ = '0'+(tmp / 10);
5208 *curpos++ = '0'+(tmp % 10);
5211 len = 4; CHECK_SIZE();
5212 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5222 /* let these complicated ones be done with the libc */
5223 snprintf(subfmt, 64, "%%%c", *format);
5224 strftime(subbuf, 64, subfmt, lt);
5225 len = strlen(subbuf); CHECK_SIZE();
5226 strncpy2(curpos, subbuf, buflen - total_done);
5230 /* let these complicated modifiers be done with the libc */
5231 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5232 strftime(subbuf, 64, subfmt, lt);
5233 len = strlen(subbuf); CHECK_SIZE();
5234 strncpy2(curpos, subbuf, buflen - total_done);
5238 if (format && *format)
5239 g_warning("format error (%c)", *format);
5246 int len = 1; CHECK_SIZE();
5247 *curpos++ = *format++;
5254 gboolean prefs_common_get_use_shred(void);
5258 #define WEXITSTATUS(x) (x)
5261 int claws_unlink(const gchar *filename)
5264 static int found_shred = -1;
5265 static const gchar *args[4];
5267 if (filename == NULL)
5270 if (prefs_common_get_use_shred()) {
5271 if (found_shred == -1) {
5273 args[0] = g_find_program_in_path("shred");
5274 debug_print("found shred: %s\n", args[0]);
5275 found_shred = (args[0] != NULL) ? 1:0;
5279 if (found_shred == 1) {
5280 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5281 if (s.st_nlink == 1) {
5284 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5285 NULL, NULL, NULL, NULL, &status, NULL);
5286 debug_print("%s %s exited with status %d\n",
5287 args[0], filename, WEXITSTATUS(status));
5288 if (truncate(filename, 0) < 0)
5289 g_warning("couln't truncate");
5294 return g_unlink(filename);