2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
29 #include <glib/gi18n.h>
39 #include <sys/param.h>
41 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
49 #include <sys/types.h>
51 # include <sys/wait.h>
58 #include <sys/utsname.h>
72 # include <tablet-browser-interface.h>
74 # include <osso-browser-interface.h>
80 #include "../codeconv.h"
84 static gboolean debug_mode = FALSE;
86 static GSList *tempfiles=NULL;
89 /* Return true if we are running as root. This function should beused
90 instead of getuid () == 0. */
91 gboolean superuser_p (void)
94 return w32_is_administrator ();
102 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
103 gint g_chdir(const gchar *path)
106 if (G_WIN32_HAVE_WIDECHAR_API()) {
111 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
117 retval = _wchdir(wpath);
129 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
130 if (cp_path == NULL) {
135 retval = chdir(cp_path);
148 gint g_chmod(const gchar *path, gint mode)
151 if (G_WIN32_HAVE_WIDECHAR_API()) {
156 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
162 retval = _wchmod(wpath, mode);
174 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
175 if (cp_path == NULL) {
180 retval = chmod(cp_path, mode);
189 return chmod(path, mode);
193 FILE* g_fopen(const gchar *filename, const gchar *mode)
196 char *name = g_win32_locale_filename_from_utf8(filename);
197 FILE* fp = fopen(name, mode);
201 return fopen(filename, mode);
204 int g_open(const gchar *filename, int flags, int mode)
207 char *name = g_win32_locale_filename_from_utf8(filename);
208 int fd = open(name, flags, mode);
212 return open(filename, flags, mode);
215 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
219 gint mkstemp_name(gchar *template, gchar **name_used)
221 static gulong count=0; /* W32-_mktemp only supports up to 27
225 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
226 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
227 (S_IRUSR | S_IWUSR));
229 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
231 perror(g_strdup_printf("cant create %s",*name_used));
237 #endif /* G_OS_WIN32 */
240 gint mkstemp(gchar *template)
243 gint res = mkstemp_name(template, &dummyname);
247 #endif /* G_OS_WIN32 */
249 void list_free_strings(GList *list)
251 list = g_list_first(list);
253 while (list != NULL) {
259 void slist_free_strings(GSList *list)
261 #if GLIB_CHECK_VERSION(2,28,0)
262 g_slist_free_full(list, (GDestroyNotify)g_free);
264 while (list != NULL) {
271 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
276 void hash_free_strings(GHashTable *table)
278 g_hash_table_foreach(table, hash_free_strings_func, NULL);
281 gint str_case_equal(gconstpointer v, gconstpointer v2)
283 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
286 guint str_case_hash(gconstpointer key)
288 const gchar *p = key;
292 h = g_ascii_tolower(h);
293 for (p += 1; *p != '\0'; p++)
294 h = (h << 5) - h + g_ascii_tolower(*p);
300 void ptr_array_free_strings(GPtrArray *array)
305 cm_return_if_fail(array != NULL);
307 for (i = 0; i < array->len; i++) {
308 str = g_ptr_array_index(array, i);
313 gint to_number(const gchar *nstr)
315 register const gchar *p;
317 if (*nstr == '\0') return -1;
319 for (p = nstr; *p != '\0'; p++)
320 if (!g_ascii_isdigit(*p)) return -1;
325 /* convert integer into string,
326 nstr must be not lower than 11 characters length */
327 gchar *itos_buf(gchar *nstr, gint n)
329 g_snprintf(nstr, 11, "%d", n);
333 /* convert integer into string */
336 static gchar nstr[11];
338 return itos_buf(nstr, n);
341 #define divide(num,divisor,i,d) \
343 i = num >> divisor; \
344 d = num & ((1<<divisor)-1); \
345 d = (d*100) >> divisor; \
350 * \brief Convert a given size in bytes in a human-readable string
352 * \param size The size expressed in bytes to convert in string
353 * \return The string that respresents the size in an human-readable way
355 gchar *to_human_readable(goffset size)
357 static gchar str[14];
358 static gchar *b_format = NULL, *kb_format = NULL,
359 *mb_format = NULL, *gb_format = NULL;
360 register int t = 0, r = 0;
361 if (b_format == NULL) {
363 kb_format = _("%d.%02dKB");
364 mb_format = _("%d.%02dMB");
365 gb_format = _("%.2fGB");
368 if (size < (goffset)1024) {
369 g_snprintf(str, sizeof(str), b_format, (gint)size);
371 } else if (size >> 10 < (goffset)1024) {
372 divide(size, 10, t, r);
373 g_snprintf(str, sizeof(str), kb_format, t, r);
375 } else if (size >> 20 < (goffset)1024) {
376 divide(size, 20, t, r);
377 g_snprintf(str, sizeof(str), mb_format, t, r);
380 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
385 /* strcmp with NULL-checking */
386 gint strcmp2(const gchar *s1, const gchar *s2)
388 if (s1 == NULL || s2 == NULL)
391 return strcmp(s1, s2);
393 /* strstr with NULL-checking */
394 gchar *strstr2(const gchar *s1, const gchar *s2)
396 if (s1 == NULL || s2 == NULL)
399 return strstr(s1, s2);
402 gint path_cmp(const gchar *s1, const gchar *s2)
407 gchar *s1buf, *s2buf;
410 if (s1 == NULL || s2 == NULL) return -1;
411 if (*s1 == '\0' || *s2 == '\0') return -1;
414 s1buf = g_strdup (s1);
415 s2buf = g_strdup (s2);
416 subst_char (s1buf, '/', G_DIR_SEPARATOR);
417 subst_char (s2buf, '/', G_DIR_SEPARATOR);
420 #endif /* !G_OS_WIN32 */
425 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
426 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
428 rc = strncmp(s1, s2, MAX(len1, len2));
432 #endif /* !G_OS_WIN32 */
436 /* remove trailing return code */
437 gchar *strretchomp(gchar *str)
441 if (!*str) return str;
443 for (s = str + strlen(str) - 1;
444 s >= str && (*s == '\n' || *s == '\r');
451 /* remove trailing character */
452 gchar *strtailchomp(gchar *str, gchar tail_char)
456 if (!*str) return str;
457 if (tail_char == '\0') return str;
459 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
465 /* remove CR (carriage return) */
466 gchar *strcrchomp(gchar *str)
470 if (!*str) return str;
472 s = str + strlen(str) - 1;
473 if (*s == '\n' && s > str && *(s - 1) == '\r') {
481 gint file_strip_crs(const gchar *file)
483 FILE *fp = NULL, *outfp = NULL;
485 gchar *out = get_tmp_file();
489 fp = g_fopen(file, "rb");
493 outfp = g_fopen(out, "wb");
499 while (fgets(buf, sizeof (buf), fp) != NULL) {
501 if (fputs(buf, outfp) == EOF) {
509 if (fclose(outfp) == EOF) {
513 if (move_file(out, file, TRUE) < 0)
525 /* Similar to `strstr' but this function ignores the case of both strings. */
526 gchar *strcasestr(const gchar *haystack, const gchar *needle)
528 register size_t haystack_len, needle_len;
530 haystack_len = strlen(haystack);
531 needle_len = strlen(needle);
533 if (haystack_len < needle_len || needle_len == 0)
536 while (haystack_len >= needle_len) {
537 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
538 return (gchar *)haystack;
548 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
549 gconstpointer needle, size_t needlelen)
551 const gchar *haystack_ = (const gchar *)haystack;
552 const gchar *needle_ = (const gchar *)needle;
553 const gchar *haystack_cur = (const gchar *)haystack;
554 size_t haystack_left = haystacklen;
557 return memchr(haystack_, *needle_, haystacklen);
559 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
561 if (haystacklen - (haystack_cur - haystack_) < needlelen)
563 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
564 return (gpointer)haystack_cur;
567 haystack_left = haystacklen - (haystack_cur - haystack_);
574 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
575 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
577 register const gchar *s = src;
578 register gchar *d = dest;
588 /* Examine if next block is non-ASCII string */
589 gboolean is_next_nonascii(const gchar *s)
593 /* skip head space */
594 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
596 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
597 if (*(guchar *)p > 127 || *(guchar *)p < 32)
604 gint get_next_word_len(const gchar *s)
608 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
614 static void trim_subject_for_compare(gchar *str)
618 eliminate_parenthesis(str, '[', ']');
619 eliminate_parenthesis(str, '(', ')');
622 srcp = str + subject_get_prefix_length(str);
624 memmove(str, srcp, strlen(srcp) + 1);
627 static void trim_subject_for_sort(gchar *str)
633 srcp = str + subject_get_prefix_length(str);
635 memmove(str, srcp, strlen(srcp) + 1);
638 /* compare subjects */
639 gint subject_compare(const gchar *s1, const gchar *s2)
643 if (!s1 || !s2) return -1;
644 if (!*s1 || !*s2) return -1;
646 Xstrdup_a(str1, s1, return -1);
647 Xstrdup_a(str2, s2, return -1);
649 trim_subject_for_compare(str1);
650 trim_subject_for_compare(str2);
652 if (!*str1 || !*str2) return -1;
654 return strcmp(str1, str2);
657 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
661 if (!s1 || !s2) return -1;
663 Xstrdup_a(str1, s1, return -1);
664 Xstrdup_a(str2, s2, return -1);
666 trim_subject_for_sort(str1);
667 trim_subject_for_sort(str2);
669 return g_utf8_collate(str1, str2);
672 void trim_subject(gchar *str)
674 register gchar *srcp;
680 srcp = str + subject_get_prefix_length(str);
685 } else if (*srcp == '(') {
697 else if (*srcp == cl)
704 while (g_ascii_isspace(*srcp)) srcp++;
705 memmove(str, srcp, strlen(srcp) + 1);
708 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
710 register gchar *srcp, *destp;
715 while ((destp = strchr(destp, op))) {
721 else if (*srcp == cl)
727 while (g_ascii_isspace(*srcp)) srcp++;
728 memmove(destp, srcp, strlen(srcp) + 1);
732 void extract_parenthesis(gchar *str, gchar op, gchar cl)
734 register gchar *srcp, *destp;
739 while ((srcp = strchr(destp, op))) {
742 memmove(destp, srcp + 1, strlen(srcp));
747 else if (*destp == cl)
759 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
762 register gchar *srcp, *destp;
764 gboolean in_quote = FALSE;
768 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
771 memmove(destp, srcp + 1, strlen(srcp));
774 if (*destp == op && !in_quote)
776 else if (*destp == cl && !in_quote)
778 else if (*destp == quote_chr)
790 void extract_quote(gchar *str, gchar quote_chr)
794 if ((str = strchr(str, quote_chr))) {
796 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
797 memmove(p - 1, p, strlen(p) + 1);
802 memmove(str, str + 1, p - str);
807 void eliminate_address_comment(gchar *str)
809 register gchar *srcp, *destp;
814 while ((destp = strchr(destp, '"'))) {
815 if ((srcp = strchr(destp + 1, '"'))) {
820 while (g_ascii_isspace(*srcp)) srcp++;
821 memmove(destp, srcp, strlen(srcp) + 1);
831 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
837 else if (*srcp == ')')
843 while (g_ascii_isspace(*srcp)) srcp++;
844 memmove(destp, srcp, strlen(srcp) + 1);
848 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
850 gboolean in_quote = FALSE;
853 if (*str == c && !in_quote)
855 if (*str == quote_chr)
863 void extract_address(gchar *str)
865 eliminate_address_comment(str);
866 if (strchr_with_skip_quote(str, '"', '<'))
867 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
871 void extract_list_id_str(gchar *str)
873 if (strchr_with_skip_quote(str, '"', '<'))
874 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
878 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
883 if (!str) return addr_list;
885 Xstrdup_a(work, str, return addr_list);
888 eliminate_address_comment(work);
891 while (workp && *workp) {
894 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
900 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
901 extract_parenthesis_with_skip_quote
902 (workp, '"', '<', '>');
906 addr_list = g_slist_append(addr_list, g_strdup(workp));
914 GSList *address_list_append(GSList *addr_list, const gchar *str)
916 return address_list_append_real(addr_list, str, TRUE);
919 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
921 return address_list_append_real(addr_list, str, FALSE);
924 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
928 if (!str) return msgid_list;
931 while (strp && *strp) {
932 const gchar *start, *end;
935 if ((start = strchr(strp, '<')) != NULL) {
936 end = strchr(start + 1, '>');
941 msgid = g_strndup(start + 1, end - start - 1);
944 msgid_list = g_slist_prepend(msgid_list, msgid);
954 GSList *references_list_append(GSList *msgid_list, const gchar *str)
958 list = references_list_prepend(NULL, str);
959 list = g_slist_reverse(list);
960 msgid_list = g_slist_concat(msgid_list, list);
965 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
970 if (!str) return group_list;
972 Xstrdup_a(work, str, return group_list);
976 while (workp && *workp) {
979 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
987 group_list = g_slist_append(group_list,
996 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);
1008 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1011 last = g_list_last(list);
1013 oldstr = last->data;
1014 list = g_list_remove(list, last->data);
1019 list = g_list_prepend(list, g_strdup(str));
1024 void remove_return(gchar *str)
1026 register gchar *p = str;
1029 if (*p == '\n' || *p == '\r')
1030 memmove(p, p + 1, strlen(p));
1036 void remove_space(gchar *str)
1038 register gchar *p = str;
1043 while (g_ascii_isspace(*(p + spc)))
1046 memmove(p, p + spc, strlen(p + spc) + 1);
1052 void unfold_line(gchar *str)
1054 register gchar *p = str;
1058 if (*p == '\n' || *p == '\r') {
1061 while (g_ascii_isspace(*(p + spc)))
1064 memmove(p, p + spc, strlen(p + spc) + 1);
1070 void subst_char(gchar *str, gchar orig, gchar subst)
1072 register gchar *p = str;
1081 void subst_chars(gchar *str, gchar *orig, gchar subst)
1083 register gchar *p = str;
1086 if (strchr(orig, *p) != NULL)
1092 void subst_for_filename(gchar *str)
1097 subst_chars(str, "\t\r\n\\/*:", '_');
1099 subst_chars(str, "\t\r\n\\/*", '_');
1103 void subst_for_shellsafe_filename(gchar *str)
1107 subst_for_filename(str);
1108 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1111 gboolean is_ascii_str(const gchar *str)
1113 const guchar *p = (const guchar *)str;
1115 while (*p != '\0') {
1116 if (*p != '\t' && *p != ' ' &&
1117 *p != '\r' && *p != '\n' &&
1118 (*p < 32 || *p >= 127))
1126 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1128 gchar * position = NULL;
1129 gchar * tmp_pos = NULL;
1132 if (quote_chars == NULL)
1135 for (i = 0; i < strlen(quote_chars); i++) {
1136 tmp_pos = strrchr (str, quote_chars[i]);
1138 || (tmp_pos != NULL && position <= tmp_pos) )
1144 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1146 const gchar *first_pos;
1147 const gchar *last_pos;
1148 const gchar *p = str;
1149 gint quote_level = -1;
1151 /* speed up line processing by only searching to the last '>' */
1152 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1153 /* skip a line if it contains a '<' before the initial '>' */
1154 if (memchr(str, '<', first_pos - str) != NULL)
1156 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1160 while (p <= last_pos) {
1161 while (p < last_pos) {
1162 if (g_ascii_isspace(*p))
1168 if (strchr(quote_chars, *p))
1170 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1171 /* any characters are allowed except '-','<' and space */
1172 while (*p != '-' && *p != '<'
1173 && !strchr(quote_chars, *p)
1174 && !g_ascii_isspace(*p)
1177 if (strchr(quote_chars, *p))
1189 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1191 const gchar *p = str, *q;
1192 gint cur_line = 0, len;
1194 while ((q = strchr(p, '\n')) != NULL) {
1196 if (len > max_chars) {
1206 if (len > max_chars) {
1215 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1217 gchar * position = NULL;
1218 gchar * tmp_pos = NULL;
1221 if (quote_chars == NULL)
1224 for (i = 0; i < strlen(quote_chars); i++) {
1225 tmp_pos = strchr (str, quote_chars[i]);
1227 || (tmp_pos != NULL && position >= tmp_pos) )
1233 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1235 register guint haystack_len, needle_len;
1236 gboolean in_squote = FALSE, in_dquote = FALSE;
1238 haystack_len = strlen(haystack);
1239 needle_len = strlen(needle);
1241 if (haystack_len < needle_len || needle_len == 0)
1244 while (haystack_len >= needle_len) {
1245 if (!in_squote && !in_dquote &&
1246 !strncmp(haystack, needle, needle_len))
1247 return (gchar *)haystack;
1249 /* 'foo"bar"' -> foo"bar"
1250 "foo'bar'" -> foo'bar' */
1251 if (*haystack == '\'') {
1254 else if (!in_dquote)
1256 } else if (*haystack == '\"') {
1259 else if (!in_squote)
1270 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1273 GSList *string_list = NULL, *slist;
1274 gchar **str_array, *s, *new_str;
1275 guint i, n = 1, len;
1277 cm_return_val_if_fail(str != NULL, NULL);
1278 cm_return_val_if_fail(delim != NULL, NULL);
1281 max_tokens = G_MAXINT;
1283 s = strstr_with_skip_quote(str, delim);
1285 guint delimiter_len = strlen(delim);
1289 new_str = g_strndup(str, len);
1291 if (new_str[0] == '\'' || new_str[0] == '\"') {
1292 if (new_str[len - 1] == new_str[0]) {
1293 new_str[len - 1] = '\0';
1294 memmove(new_str, new_str + 1, len - 1);
1297 string_list = g_slist_prepend(string_list, new_str);
1299 str = s + delimiter_len;
1300 s = strstr_with_skip_quote(str, delim);
1301 } while (--max_tokens && s);
1305 new_str = g_strdup(str);
1306 if (new_str[0] == '\'' || new_str[0] == '\"') {
1308 if (new_str[len - 1] == new_str[0]) {
1309 new_str[len - 1] = '\0';
1310 memmove(new_str, new_str + 1, len - 1);
1313 string_list = g_slist_prepend(string_list, new_str);
1317 str_array = g_new(gchar*, n);
1321 str_array[i--] = NULL;
1322 for (slist = string_list; slist; slist = slist->next)
1323 str_array[i--] = slist->data;
1325 g_slist_free(string_list);
1330 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1332 gchar *abbrev_group;
1334 const gchar *p = group;
1337 cm_return_val_if_fail(group != NULL, NULL);
1339 last = group + strlen(group);
1340 abbrev_group = ap = g_malloc(strlen(group) + 1);
1345 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1347 while (*p != '.') p++;
1350 return abbrev_group;
1355 return abbrev_group;
1358 gchar *trim_string(const gchar *str, gint len)
1360 const gchar *p = str;
1365 if (!str) return NULL;
1366 if (strlen(str) <= len)
1367 return g_strdup(str);
1368 if (g_utf8_validate(str, -1, NULL) == FALSE)
1369 return g_strdup(str);
1371 while (*p != '\0') {
1372 mb_len = g_utf8_skip[*(guchar *)p];
1375 else if (new_len + mb_len > len)
1382 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1383 return g_strconcat(new_str, "...", NULL);
1386 GList *uri_list_extract_filenames(const gchar *uri_list)
1388 GList *result = NULL;
1390 gchar *escaped_utf8uri;
1396 while (g_ascii_isspace(*p)) p++;
1397 if (!strncmp(p, "file:", 5)) {
1400 while (*q && *q != '\n' && *q != '\r') q++;
1403 gchar *file, *locale_file = NULL;
1405 while (q > p && g_ascii_isspace(*q))
1407 Xalloca(escaped_utf8uri, q - p + 2,
1409 Xalloca(file, q - p + 2,
1412 strncpy(escaped_utf8uri, p, q - p + 1);
1413 escaped_utf8uri[q - p + 1] = '\0';
1414 decode_uri(file, escaped_utf8uri);
1416 * g_filename_from_uri() rejects escaped/locale encoded uri
1417 * string which come from Nautilus.
1420 if (g_utf8_validate(file, -1, NULL))
1422 = conv_codeset_strdup(
1425 conv_get_locale_charset_str());
1427 locale_file = g_strdup(file + 5);
1429 locale_file = g_filename_from_uri(file, NULL, NULL);
1431 result = g_list_append(result, locale_file);
1435 p = strchr(p, '\n');
1442 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1445 static gint axtoi(const gchar *hexstr)
1447 gint hi, lo, result;
1450 if ('0' <= hi && hi <= '9') {
1453 if ('a' <= hi && hi <= 'f') {
1456 if ('A' <= hi && hi <= 'F') {
1461 if ('0' <= lo && lo <= '9') {
1464 if ('a' <= lo && lo <= 'f') {
1467 if ('A' <= lo && lo <= 'F') {
1470 result = lo + (16 * hi);
1474 gboolean is_uri_string(const gchar *str)
1476 while (str && *str && g_ascii_isspace(*str))
1478 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1479 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1480 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1481 g_ascii_strncasecmp(str, "www.", 4) == 0);
1484 gchar *get_uri_path(const gchar *uri)
1486 while (uri && *uri && g_ascii_isspace(*uri))
1488 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1489 return (gchar *)(uri + 7);
1490 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1491 return (gchar *)(uri + 8);
1492 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1493 return (gchar *)(uri + 6);
1495 return (gchar *)uri;
1498 gint get_uri_len(const gchar *str)
1502 if (is_uri_string(str)) {
1503 for (p = str; *p != '\0'; p++) {
1504 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1513 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1514 * plusses, and escape characters are used)
1516 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1518 gchar *dec = decoded_uri;
1519 const gchar *enc = encoded_uri;
1524 if (isxdigit((guchar)enc[0]) &&
1525 isxdigit((guchar)enc[1])) {
1531 if (with_plus && *enc == '+')
1543 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1545 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1548 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1550 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1551 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1555 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1556 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1560 const gchar *forbidden_uris[] = { ".gnupg/",
1566 gint num_attach = 0;
1567 gchar **my_att = NULL;
1569 Xstrdup_a(tmp_mailto, mailto, return -1);
1571 if (!strncmp(tmp_mailto, "mailto:", 7))
1574 p = strchr(tmp_mailto, '?');
1581 *to = decode_uri_gdup(tmp_mailto);
1583 my_att = g_malloc(sizeof(char *));
1587 gchar *field, *value;
1604 if (*value == '\0') continue;
1606 if (from && !g_ascii_strcasecmp(field, "from")) {
1608 *from = decode_uri_gdup(value);
1610 gchar *tmp = decode_uri_gdup(value);
1611 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1615 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1617 *cc = decode_uri_gdup(value);
1619 gchar *tmp = decode_uri_gdup(value);
1620 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1624 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1626 *bcc = decode_uri_gdup(value);
1628 gchar *tmp = decode_uri_gdup(value);
1629 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1633 } else if (subject && !*subject &&
1634 !g_ascii_strcasecmp(field, "subject")) {
1635 *subject = decode_uri_gdup(value);
1636 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1637 *body = decode_uri_gdup(value);
1638 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1639 gchar *tmp = decode_uri_gdup(value);
1640 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1641 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1645 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1647 gchar *tmp = decode_uri_gdup(value);
1648 for (; forbidden_uris[i]; i++) {
1649 if (strstr(tmp, forbidden_uris[i])) {
1650 g_print("Refusing to attach '%s', potential private data leak\n",
1658 /* attach is correct */
1660 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1661 my_att[num_attach-1] = tmp;
1662 my_att[num_attach] = NULL;
1664 } else if (inreplyto && !*inreplyto &&
1665 !g_ascii_strcasecmp(field, "in-reply-to")) {
1666 *inreplyto = decode_uri_gdup(value);
1677 #include <windows.h>
1678 #ifndef CSIDL_APPDATA
1679 #define CSIDL_APPDATA 0x001a
1681 #ifndef CSIDL_LOCAL_APPDATA
1682 #define CSIDL_LOCAL_APPDATA 0x001c
1684 #ifndef CSIDL_FLAG_CREATE
1685 #define CSIDL_FLAG_CREATE 0x8000
1687 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1691 w32_strerror (int w32_errno)
1693 static char strerr[256];
1694 int ec = (int)GetLastError ();
1698 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1699 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1700 strerr, DIM (strerr)-1, NULL);
1704 static __inline__ void *
1705 dlopen (const char * name, int flag)
1707 void * hd = LoadLibrary (name);
1711 static __inline__ void *
1712 dlsym (void * hd, const char * sym)
1716 void * fnc = GetProcAddress (hd, sym);
1725 static __inline__ const char *
1728 return w32_strerror (0);
1732 static __inline__ int
1744 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1746 static int initialized;
1747 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1751 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1757 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1759 handle = dlopen (dllnames[i], RTLD_LAZY);
1762 func = dlsym (handle, "SHGetFolderPathW");
1773 return func (a,b,c,d,e);
1778 /* Returns a static string with the directroy from which the module
1779 has been loaded. Returns an empty string on error. */
1780 static char *w32_get_module_dir(void)
1782 static char *moddir;
1785 char name[MAX_PATH+10];
1788 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1791 p = strrchr (name, '\\');
1797 moddir = g_strdup (name);
1801 #endif /* G_OS_WIN32 */
1803 /* Return a static string with the locale dir. */
1804 const gchar *get_locale_dir(void)
1806 static gchar *loc_dir;
1810 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1811 "\\share\\locale", NULL);
1814 loc_dir = LOCALEDIR;
1820 const gchar *get_home_dir(void)
1823 static char home_dir_utf16[MAX_PATH] = "";
1824 static gchar *home_dir_utf8 = NULL;
1825 if (home_dir_utf16[0] == '\0') {
1826 if (w32_shgetfolderpath
1827 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1828 NULL, 0, home_dir_utf16) < 0)
1829 strcpy (home_dir_utf16, "C:\\Sylpheed");
1830 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1832 return home_dir_utf8;
1834 static const gchar *homeenv = NULL;
1839 if (!homeenv && g_getenv("HOME") != NULL)
1840 homeenv = g_strdup(g_getenv("HOME"));
1842 homeenv = g_get_home_dir();
1848 static gchar *claws_rc_dir = NULL;
1849 static gboolean rc_dir_alt = FALSE;
1850 const gchar *get_rc_dir(void)
1854 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1857 return claws_rc_dir;
1860 void set_rc_dir(const gchar *dir)
1862 if (claws_rc_dir != NULL) {
1863 g_print("Error: rc_dir already set\n");
1866 if (g_path_is_absolute(dir))
1867 claws_rc_dir = g_strdup(dir);
1869 claws_rc_dir = g_strconcat(g_get_current_dir(),
1870 G_DIR_SEPARATOR_S, dir, NULL);
1872 debug_print("set rc_dir to %s\n", claws_rc_dir);
1873 if (!is_dir_exist(claws_rc_dir)) {
1874 if (make_dir_hier(claws_rc_dir) != 0) {
1875 g_print("Error: can't create %s\n",
1882 gboolean rc_dir_is_alt(void) {
1886 const gchar *get_mail_base_dir(void)
1888 return get_home_dir();
1892 const gchar *prefs_common_get_data_root(void);
1893 gchar *last_data_root = NULL;
1896 const gchar *get_news_cache_dir(void)
1898 static gchar *news_cache_dir = NULL;
1900 const gchar *data_root = prefs_common_get_data_root();
1901 if (strcmp2(data_root, last_data_root)) {
1902 g_free(news_cache_dir);
1903 news_cache_dir = NULL;
1906 if (!news_cache_dir)
1908 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1909 NEWS_CACHE_DIR, NULL);
1913 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1914 "Claws", G_DIR_SEPARATOR_S,
1915 g_get_user_name(), G_DIR_SEPARATOR_S,
1916 NEWS_CACHE_DIR, NULL);
1917 g_free(last_data_root);
1918 last_data_root = g_strdup(last_data_root);
1920 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1921 NEWS_CACHE_DIR, NULL);
1922 g_free(last_data_root);
1923 last_data_root = NULL;
1927 return news_cache_dir;
1930 const gchar *get_imap_cache_dir(void)
1932 static gchar *imap_cache_dir = NULL;
1934 const gchar *data_root = prefs_common_get_data_root();
1935 if (strcmp2(data_root, last_data_root)) {
1936 g_free(imap_cache_dir);
1937 imap_cache_dir = NULL;
1941 if (!imap_cache_dir)
1943 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1944 IMAP_CACHE_DIR, NULL);
1948 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1949 "Claws", G_DIR_SEPARATOR_S,
1950 g_get_user_name(), G_DIR_SEPARATOR_S,
1951 IMAP_CACHE_DIR, NULL);
1952 g_free(last_data_root);
1953 last_data_root = g_strdup(last_data_root);
1955 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1956 IMAP_CACHE_DIR, NULL);
1957 g_free(last_data_root);
1958 last_data_root = NULL;
1963 return imap_cache_dir;
1966 const gchar *get_mime_tmp_dir(void)
1968 static gchar *mime_tmp_dir = NULL;
1971 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1972 MIME_TMP_DIR, NULL);
1974 return mime_tmp_dir;
1977 const gchar *get_template_dir(void)
1979 static gchar *template_dir = NULL;
1982 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1983 TEMPLATE_DIR, NULL);
1985 return template_dir;
1989 const gchar *get_cert_file(void)
1991 const gchar *cert_file = NULL;
1993 cert_file = g_strconcat(w32_get_module_dir(),
1994 "\\share\\claws-mail\\",
1995 "ca-certificates.crt",
2001 /* Return the filepath of the claws-mail.desktop file */
2002 const gchar *get_desktop_file(void)
2004 #ifdef DESKTOPFILEPATH
2005 return DESKTOPFILEPATH;
2011 /* Return the default directory for Plugins. */
2012 const gchar *get_plugin_dir(void)
2015 static gchar *plugin_dir = NULL;
2018 plugin_dir = g_strconcat(w32_get_module_dir(),
2019 "\\lib\\claws-mail\\plugins\\",
2023 if (is_dir_exist(PLUGINDIR))
2026 static gchar *plugin_dir = NULL;
2028 plugin_dir = g_strconcat(get_rc_dir(),
2029 G_DIR_SEPARATOR_S, "plugins",
2030 G_DIR_SEPARATOR_S, NULL);
2038 /* Return the default directory for Themes. */
2039 const gchar *get_themes_dir(void)
2041 static gchar *themes_dir = NULL;
2044 themes_dir = g_strconcat(w32_get_module_dir(),
2045 "\\share\\claws-mail\\themes",
2051 const gchar *get_tmp_dir(void)
2053 static gchar *tmp_dir = NULL;
2056 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2062 gchar *get_tmp_file(void)
2065 static guint32 id = 0;
2067 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2068 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2073 const gchar *get_domain_name(void)
2076 static gchar *domain_name = NULL;
2082 if (gethostname(hostname, sizeof(hostname)) != 0) {
2083 perror("gethostname");
2084 domain_name = "unknown";
2086 hostname[sizeof(hostname) - 1] = '\0';
2087 if ((hp = my_gethostbyname(hostname)) == NULL) {
2088 perror("gethostbyname");
2089 domain_name = g_strdup(hostname);
2091 domain_name = g_strdup(hp->h_name);
2094 debug_print("domain name = %s\n", domain_name);
2103 off_t get_file_size(const gchar *file)
2107 if (g_stat(file, &s) < 0) {
2108 FILE_OP_ERROR(file, "stat");
2115 time_t get_file_mtime(const gchar *file)
2119 if (g_stat(file, &s) < 0) {
2120 FILE_OP_ERROR(file, "stat");
2127 off_t get_file_size_as_crlf(const gchar *file)
2131 gchar buf[BUFFSIZE];
2133 if ((fp = g_fopen(file, "rb")) == NULL) {
2134 FILE_OP_ERROR(file, "g_fopen");
2138 while (fgets(buf, sizeof(buf), fp) != NULL) {
2140 size += strlen(buf) + 2;
2144 FILE_OP_ERROR(file, "fgets");
2153 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2160 if (g_stat(file, &s) < 0) {
2161 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2165 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2172 /* Test on whether FILE is a relative file name. This is
2173 * straightforward for Unix but more complex for Windows. */
2174 gboolean is_relative_filename(const gchar *file)
2179 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2180 return FALSE; /* Prefixed with a hostname - this can't
2181 * be a relative name. */
2183 if ( ((*file >= 'a' && *file <= 'z')
2184 || (*file >= 'A' && *file <= 'Z'))
2186 file += 2; /* Skip drive letter. */
2188 return !(*file == '\\' || *file == '/');
2190 return !(*file == G_DIR_SEPARATOR);
2195 gboolean is_dir_exist(const gchar *dir)
2200 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2203 gboolean is_file_entry_exist(const gchar *file)
2208 return g_file_test(file, G_FILE_TEST_EXISTS);
2211 gboolean dirent_is_regular_file(struct dirent *d)
2213 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2214 if (d->d_type == DT_REG)
2216 else if (d->d_type != DT_UNKNOWN)
2220 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2223 gint change_dir(const gchar *dir)
2225 gchar *prevdir = NULL;
2228 prevdir = g_get_current_dir();
2230 if (g_chdir(dir) < 0) {
2231 FILE_OP_ERROR(dir, "chdir");
2232 if (debug_mode) g_free(prevdir);
2234 } else if (debug_mode) {
2237 cwd = g_get_current_dir();
2238 if (strcmp(prevdir, cwd) != 0)
2239 g_print("current dir: %s\n", cwd);
2247 gint make_dir(const gchar *dir)
2249 if (g_mkdir(dir, S_IRWXU) < 0) {
2250 FILE_OP_ERROR(dir, "mkdir");
2253 if (g_chmod(dir, S_IRWXU) < 0)
2254 FILE_OP_ERROR(dir, "chmod");
2259 gint make_dir_hier(const gchar *dir)
2264 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2265 parent_dir = g_strndup(dir, p - dir);
2266 if (*parent_dir != '\0') {
2267 if (!is_dir_exist(parent_dir)) {
2268 if (make_dir(parent_dir) < 0) {
2277 if (!is_dir_exist(dir)) {
2278 if (make_dir(dir) < 0)
2285 gint remove_all_files(const gchar *dir)
2288 const gchar *dir_name;
2291 prev_dir = g_get_current_dir();
2293 if (g_chdir(dir) < 0) {
2294 FILE_OP_ERROR(dir, "chdir");
2299 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2300 g_warning("failed to open directory: %s\n", dir);
2305 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2306 if (claws_unlink(dir_name) < 0)
2307 FILE_OP_ERROR(dir_name, "unlink");
2312 if (g_chdir(prev_dir) < 0) {
2313 FILE_OP_ERROR(prev_dir, "chdir");
2323 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2326 const gchar *dir_name;
2330 if (first == last) {
2331 /* Skip all the dir reading part. */
2332 gchar *filename = g_strdup_printf("%s%s%u", dir, G_DIR_SEPARATOR_S, first);
2333 if (claws_unlink(filename) < 0) {
2334 FILE_OP_ERROR(filename, "unlink");
2342 prev_dir = g_get_current_dir();
2344 if (g_chdir(dir) < 0) {
2345 FILE_OP_ERROR(dir, "chdir");
2350 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2351 g_warning("failed to open directory: %s\n", dir);
2356 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2357 file_no = to_number(dir_name);
2358 if (file_no > 0 && first <= file_no && file_no <= last) {
2359 if (is_dir_exist(dir_name))
2361 if (claws_unlink(dir_name) < 0)
2362 FILE_OP_ERROR(dir_name, "unlink");
2368 if (g_chdir(prev_dir) < 0) {
2369 FILE_OP_ERROR(prev_dir, "chdir");
2379 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2382 const gchar *dir_name;
2386 prev_dir = g_get_current_dir();
2388 if (g_chdir(dir) < 0) {
2389 FILE_OP_ERROR(dir, "chdir");
2394 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2395 FILE_OP_ERROR(dir, "opendir");
2400 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2401 file_no = to_number(dir_name);
2402 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2403 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2404 if (is_dir_exist(dir_name))
2406 if (claws_unlink(dir_name) < 0)
2407 FILE_OP_ERROR(dir_name, "unlink");
2413 if (g_chdir(prev_dir) < 0) {
2414 FILE_OP_ERROR(prev_dir, "chdir");
2424 gint remove_all_numbered_files(const gchar *dir)
2426 return remove_numbered_files(dir, 0, UINT_MAX);
2429 gint remove_dir_recursive(const gchar *dir)
2433 const gchar *dir_name;
2436 if (g_stat(dir, &s) < 0) {
2437 FILE_OP_ERROR(dir, "stat");
2438 if (ENOENT == errno) return 0;
2442 if (!S_ISDIR(s.st_mode)) {
2443 if (claws_unlink(dir) < 0) {
2444 FILE_OP_ERROR(dir, "unlink");
2451 prev_dir = g_get_current_dir();
2452 /* g_print("prev_dir = %s\n", prev_dir); */
2454 if (!path_cmp(prev_dir, dir)) {
2456 if (g_chdir("..") < 0) {
2457 FILE_OP_ERROR(dir, "chdir");
2460 prev_dir = g_get_current_dir();
2463 if (g_chdir(dir) < 0) {
2464 FILE_OP_ERROR(dir, "chdir");
2469 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2470 g_warning("failed to open directory: %s\n", dir);
2476 /* remove all files in the directory */
2477 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2478 /* g_print("removing %s\n", dir_name); */
2480 if (is_dir_exist(dir_name)) {
2481 if (remove_dir_recursive(dir_name) < 0) {
2482 g_warning("can't remove directory\n");
2486 if (claws_unlink(dir_name) < 0)
2487 FILE_OP_ERROR(dir_name, "unlink");
2493 if (g_chdir(prev_dir) < 0) {
2494 FILE_OP_ERROR(prev_dir, "chdir");
2501 if (g_rmdir(dir) < 0) {
2502 FILE_OP_ERROR(dir, "rmdir");
2509 gint rename_force(const gchar *oldpath, const gchar *newpath)
2512 if (!is_file_entry_exist(oldpath)) {
2516 if (is_file_exist(newpath)) {
2517 if (claws_unlink(newpath) < 0)
2518 FILE_OP_ERROR(newpath, "unlink");
2521 return g_rename(oldpath, newpath);
2525 * Append src file body to the tail of dest file.
2526 * Now keep_backup has no effects.
2528 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2530 FILE *src_fp, *dest_fp;
2534 gboolean err = FALSE;
2536 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2537 FILE_OP_ERROR(src, "g_fopen");
2541 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2542 FILE_OP_ERROR(dest, "g_fopen");
2547 if (change_file_mode_rw(dest_fp, dest) < 0) {
2548 FILE_OP_ERROR(dest, "chmod");
2549 g_warning("can't change file mode\n");
2552 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2553 if (n_read < sizeof(buf) && ferror(src_fp))
2555 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2556 g_warning("writing to %s failed.\n", dest);
2564 if (ferror(src_fp)) {
2565 FILE_OP_ERROR(src, "fread");
2569 if (fclose(dest_fp) == EOF) {
2570 FILE_OP_ERROR(dest, "fclose");
2582 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2584 FILE *src_fp, *dest_fp;
2587 gchar *dest_bak = NULL;
2588 gboolean err = FALSE;
2590 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2591 FILE_OP_ERROR(src, "g_fopen");
2594 if (is_file_exist(dest)) {
2595 dest_bak = g_strconcat(dest, ".bak", NULL);
2596 if (rename_force(dest, dest_bak) < 0) {
2597 FILE_OP_ERROR(dest, "rename");
2604 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2605 FILE_OP_ERROR(dest, "g_fopen");
2608 if (rename_force(dest_bak, dest) < 0)
2609 FILE_OP_ERROR(dest_bak, "rename");
2615 if (change_file_mode_rw(dest_fp, dest) < 0) {
2616 FILE_OP_ERROR(dest, "chmod");
2617 g_warning("can't change file mode\n");
2620 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2621 if (n_read < sizeof(buf) && ferror(src_fp))
2623 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2624 g_warning("writing to %s failed.\n", dest);
2629 if (rename_force(dest_bak, dest) < 0)
2630 FILE_OP_ERROR(dest_bak, "rename");
2637 if (ferror(src_fp)) {
2638 FILE_OP_ERROR(src, "fread");
2642 if (fclose(dest_fp) == EOF) {
2643 FILE_OP_ERROR(dest, "fclose");
2650 if (rename_force(dest_bak, dest) < 0)
2651 FILE_OP_ERROR(dest_bak, "rename");
2657 if (keep_backup == FALSE && dest_bak)
2658 claws_unlink(dest_bak);
2665 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2667 if (overwrite == FALSE && is_file_exist(dest)) {
2668 g_warning("move_file(): file %s already exists.", dest);
2672 if (rename_force(src, dest) == 0) return 0;
2674 if (EXDEV != errno) {
2675 FILE_OP_ERROR(src, "rename");
2679 if (copy_file(src, dest, FALSE) < 0) return -1;
2686 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2689 gint bytes_left, to_read;
2692 if (fseek(fp, offset, SEEK_SET) < 0) {
2697 bytes_left = length;
2698 to_read = MIN(bytes_left, sizeof(buf));
2700 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2701 if (n_read < to_read && ferror(fp))
2703 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2706 bytes_left -= n_read;
2707 if (bytes_left == 0)
2709 to_read = MIN(bytes_left, sizeof(buf));
2720 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2723 gboolean err = FALSE;
2725 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2726 FILE_OP_ERROR(dest, "g_fopen");
2730 if (change_file_mode_rw(dest_fp, dest) < 0) {
2731 FILE_OP_ERROR(dest, "chmod");
2732 g_warning("can't change file mode\n");
2735 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2738 if (!err && fclose(dest_fp) == EOF) {
2739 FILE_OP_ERROR(dest, "fclose");
2744 g_warning("writing to %s failed.\n", dest);
2752 /* convert line endings into CRLF. If the last line doesn't end with
2753 * linebreak, add it.
2755 gchar *canonicalize_str(const gchar *str)
2761 for (p = str; *p != '\0'; ++p) {
2768 if (p == str || *(p - 1) != '\n')
2771 out = outp = g_malloc(new_len + 1);
2772 for (p = str; *p != '\0'; ++p) {
2779 if (p == str || *(p - 1) != '\n') {
2788 gint canonicalize_file(const gchar *src, const gchar *dest)
2790 FILE *src_fp, *dest_fp;
2791 gchar buf[BUFFSIZE];
2793 gboolean err = FALSE;
2794 gboolean last_linebreak = FALSE;
2796 if (src == NULL || dest == NULL)
2799 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2800 FILE_OP_ERROR(src, "g_fopen");
2804 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2805 FILE_OP_ERROR(dest, "g_fopen");
2810 if (change_file_mode_rw(dest_fp, dest) < 0) {
2811 FILE_OP_ERROR(dest, "chmod");
2812 g_warning("can't change file mode\n");
2815 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2819 if (len == 0) break;
2820 last_linebreak = FALSE;
2822 if (buf[len - 1] != '\n') {
2823 last_linebreak = TRUE;
2824 r = fputs(buf, dest_fp);
2825 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2826 r = fputs(buf, dest_fp);
2829 r = fwrite(buf, 1, len - 1, dest_fp);
2834 r = fputs("\r\n", dest_fp);
2838 g_warning("writing to %s failed.\n", dest);
2846 if (last_linebreak == TRUE) {
2847 if (fputs("\r\n", dest_fp) == EOF)
2851 if (ferror(src_fp)) {
2852 FILE_OP_ERROR(src, "fgets");
2856 if (fclose(dest_fp) == EOF) {
2857 FILE_OP_ERROR(dest, "fclose");
2869 gint canonicalize_file_replace(const gchar *file)
2873 tmp_file = get_tmp_file();
2875 if (canonicalize_file(file, tmp_file) < 0) {
2880 if (move_file(tmp_file, file, TRUE) < 0) {
2881 g_warning("can't replace %s .\n", file);
2882 claws_unlink(tmp_file);
2891 gchar *normalize_newlines(const gchar *str)
2896 out = outp = g_malloc(strlen(str) + 1);
2897 for (p = str; *p != '\0'; ++p) {
2899 if (*(p + 1) != '\n')
2910 gchar *get_outgoing_rfc2822_str(FILE *fp)
2912 gchar buf[BUFFSIZE];
2916 str = g_string_new(NULL);
2918 /* output header part */
2919 while (fgets(buf, sizeof(buf), fp) != NULL) {
2921 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2928 else if (next != ' ' && next != '\t') {
2932 if (fgets(buf, sizeof(buf), fp) == NULL)
2936 g_string_append(str, buf);
2937 g_string_append(str, "\r\n");
2943 /* output body part */
2944 while (fgets(buf, sizeof(buf), fp) != NULL) {
2947 g_string_append_c(str, '.');
2948 g_string_append(str, buf);
2949 g_string_append(str, "\r\n");
2953 g_string_free(str, FALSE);
2959 * Create a new boundary in a way that it is very unlikely that this
2960 * will occur in the following text. It would be easy to ensure
2961 * uniqueness if everything is either quoted-printable or base64
2962 * encoded (note that conversion is allowed), but because MIME bodies
2963 * may be nested, it may happen that the same boundary has already
2966 * boundary := 0*69<bchars> bcharsnospace
2967 * bchars := bcharsnospace / " "
2968 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2969 * "+" / "_" / "," / "-" / "." /
2970 * "/" / ":" / "=" / "?"
2972 * some special characters removed because of buggy MTAs
2975 gchar *generate_mime_boundary(const gchar *prefix)
2977 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2978 "abcdefghijklmnopqrstuvwxyz"
2983 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2984 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2987 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2991 gint change_file_mode_rw(FILE *fp, const gchar *file)
2994 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2996 return g_chmod(file, S_IRUSR|S_IWUSR);
3000 FILE *my_tmpfile(void)
3002 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3003 const gchar suffix[] = ".XXXXXX";
3004 const gchar *tmpdir;
3006 const gchar *progname;
3015 tmpdir = get_tmp_dir();
3016 tmplen = strlen(tmpdir);
3017 progname = g_get_prgname();
3018 if (progname == NULL)
3019 progname = "claws-mail";
3020 proglen = strlen(progname);
3021 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3024 memcpy(fname, tmpdir, tmplen);
3025 fname[tmplen] = G_DIR_SEPARATOR;
3026 memcpy(fname + tmplen + 1, progname, proglen);
3027 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3029 fd = mkstemp(fname);
3034 claws_unlink(fname);
3036 /* verify that we can write in the file after unlinking */
3037 if (write(fd, buf, 1) < 0) {
3044 fp = fdopen(fd, "w+b");
3052 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3057 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3061 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3062 dir, G_DIR_SEPARATOR);
3063 fd = mkstemp_name(template, filename);
3066 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3067 fd = mkstemp(*filename);
3069 return fdopen(fd, "w+");
3072 FILE *str_open_as_stream(const gchar *str)
3077 cm_return_val_if_fail(str != NULL, NULL);
3081 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3086 if (len == 0) return fp;
3088 if (fwrite(str, 1, len, fp) != len) {
3089 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3098 gint str_write_to_file(const gchar *str, const gchar *file)
3103 cm_return_val_if_fail(str != NULL, -1);
3104 cm_return_val_if_fail(file != NULL, -1);
3106 if ((fp = g_fopen(file, "wb")) == NULL) {
3107 FILE_OP_ERROR(file, "g_fopen");
3117 if (fwrite(str, 1, len, fp) != len) {
3118 FILE_OP_ERROR(file, "fwrite");
3124 if (fclose(fp) == EOF) {
3125 FILE_OP_ERROR(file, "fclose");
3133 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3140 cm_return_val_if_fail(fp != NULL, NULL);
3142 array = g_byte_array_new();
3144 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3145 if (n_read < sizeof(buf) && ferror(fp))
3147 g_byte_array_append(array, buf, n_read);
3151 FILE_OP_ERROR("file stream", "fread");
3152 g_byte_array_free(array, TRUE);
3157 g_byte_array_append(array, buf, 1);
3158 str = (gchar *)array->data;
3159 g_byte_array_free(array, FALSE);
3161 if (recode && !g_utf8_validate(str, -1, NULL)) {
3162 const gchar *src_codeset, *dest_codeset;
3164 src_codeset = conv_get_locale_charset_str();
3165 dest_codeset = CS_UTF_8;
3166 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3174 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3181 struct timeval timeout = {1, 0};
3186 cm_return_val_if_fail(file != NULL, NULL);
3188 if (g_stat(file, &s) != 0) {
3189 FILE_OP_ERROR(file, "stat");
3192 if (S_ISDIR(s.st_mode)) {
3193 g_warning("%s: is a directory\n", file);
3198 fp = g_fopen (file, "rb");
3200 FILE_OP_ERROR(file, "open");
3204 /* test whether the file is readable without blocking */
3205 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3207 FILE_OP_ERROR(file, "open");
3214 /* allow for one second */
3215 err = select(fd+1, &fds, NULL, NULL, &timeout);
3216 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3218 FILE_OP_ERROR(file, "select");
3220 g_warning("%s: doesn't seem readable\n", file);
3226 /* Now clear O_NONBLOCK */
3227 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3228 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3232 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3233 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3238 /* get the FILE pointer */
3239 fp = fdopen(fd, "rb");
3242 FILE_OP_ERROR(file, "fdopen");
3243 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3248 str = file_read_stream_to_str_full(fp, recode);
3255 gchar *file_read_to_str(const gchar *file)
3257 return file_read_to_str_full(file, TRUE);
3259 gchar *file_read_stream_to_str(FILE *fp)
3261 return file_read_stream_to_str_full(fp, TRUE);
3264 gchar *file_read_to_str_no_recode(const gchar *file)
3266 return file_read_to_str_full(file, FALSE);
3268 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3270 return file_read_stream_to_str_full(fp, FALSE);
3273 char *fgets_crlf(char *buf, int size, FILE *stream)
3275 gboolean is_cr = FALSE;
3276 gboolean last_was_cr = FALSE;
3281 while (--size > 0 && (c = getc(stream)) != EOF)
3284 is_cr = (c == '\r');
3294 last_was_cr = is_cr;
3296 if (c == EOF && cs == buf)
3304 static gint execute_async(gchar *const argv[])
3306 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3308 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3309 NULL, NULL, NULL, FALSE) == FALSE) {
3310 g_warning("Couldn't execute command: %s\n", argv[0]);
3317 static gint execute_sync(gchar *const argv[])
3321 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3324 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3325 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3326 g_warning("Couldn't execute command: %s\n", argv[0]);
3330 if (WIFEXITED(status))
3331 return WEXITSTATUS(status);
3335 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3336 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3337 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3338 g_warning("Couldn't execute command: %s\n", argv[0]);
3346 gint execute_command_line(const gchar *cmdline, gboolean async)
3351 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3353 argv = strsplit_with_quote(cmdline, " ", 0);
3356 ret = execute_async(argv);
3358 ret = execute_sync(argv);
3365 gchar *get_command_output(const gchar *cmdline)
3367 gchar *child_stdout;
3370 cm_return_val_if_fail(cmdline != NULL, NULL);
3372 debug_print("get_command_output(): executing: %s\n", cmdline);
3374 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3376 g_warning("Couldn't execute command: %s\n", cmdline);
3380 return child_stdout;
3383 static gint is_unchanged_uri_char(char c)
3394 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3400 for(i = 0; i < strlen(uri) ; i++) {
3401 if (is_unchanged_uri_char(uri[i])) {
3402 if (k + 2 >= bufsize)
3404 encoded_uri[k++] = uri[i];
3407 char * hexa = "0123456789ABCDEF";
3409 if (k + 4 >= bufsize)
3411 encoded_uri[k++] = '%';
3412 encoded_uri[k++] = hexa[uri[i] / 16];
3413 encoded_uri[k++] = hexa[uri[i] % 16];
3419 gint open_uri(const gchar *uri, const gchar *cmdline)
3423 gchar buf[BUFFSIZE];
3425 gchar encoded_uri[BUFFSIZE];
3426 cm_return_val_if_fail(uri != NULL, -1);
3428 /* an option to choose whether to use encode_uri or not ? */
3429 encode_uri(encoded_uri, BUFFSIZE, uri);
3432 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3433 !strchr(p + 2, '%'))
3434 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3437 g_warning("Open URI command-line is invalid "
3438 "(there must be only one '%%s'): %s",
3440 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3443 execute_command_line(buf, TRUE);
3445 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3448 extern osso_context_t *get_osso_context(void);
3449 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3450 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3451 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3456 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3458 gchar buf[BUFFSIZE];
3461 cm_return_val_if_fail(filepath != NULL, -1);
3464 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3465 !strchr(p + 2, '%'))
3466 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3469 g_warning("Open Text Editor command-line is invalid "
3470 "(there must be only one '%%s'): %s",
3472 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3475 execute_command_line(buf, TRUE);
3480 time_t remote_tzoffset_sec(const gchar *zone)
3482 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3488 time_t remoteoffset;
3490 strncpy(zone3, zone, 3);
3494 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3495 (c == '+' || c == '-')) {
3496 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3498 remoteoffset = -remoteoffset;
3499 } else if (!strncmp(zone, "UT" , 2) ||
3500 !strncmp(zone, "GMT", 2)) {
3502 } else if (strlen(zone3) == 3) {
3503 for (p = ustzstr; *p != '\0'; p += 3) {
3504 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3505 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3506 remoteoffset = iustz * 3600;
3512 } else if (strlen(zone3) == 1) {
3514 case 'Z': remoteoffset = 0; break;
3515 case 'A': remoteoffset = -1; break;
3516 case 'B': remoteoffset = -2; break;
3517 case 'C': remoteoffset = -3; break;
3518 case 'D': remoteoffset = -4; break;
3519 case 'E': remoteoffset = -5; break;
3520 case 'F': remoteoffset = -6; break;
3521 case 'G': remoteoffset = -7; break;
3522 case 'H': remoteoffset = -8; break;
3523 case 'I': remoteoffset = -9; break;
3524 case 'K': remoteoffset = -10; break; /* J is not used */
3525 case 'L': remoteoffset = -11; break;
3526 case 'M': remoteoffset = -12; break;
3527 case 'N': remoteoffset = 1; break;
3528 case 'O': remoteoffset = 2; break;
3529 case 'P': remoteoffset = 3; break;
3530 case 'Q': remoteoffset = 4; break;
3531 case 'R': remoteoffset = 5; break;
3532 case 'S': remoteoffset = 6; break;
3533 case 'T': remoteoffset = 7; break;
3534 case 'U': remoteoffset = 8; break;
3535 case 'V': remoteoffset = 9; break;
3536 case 'W': remoteoffset = 10; break;
3537 case 'X': remoteoffset = 11; break;
3538 case 'Y': remoteoffset = 12; break;
3539 default: remoteoffset = 0; break;
3541 remoteoffset = remoteoffset * 3600;
3545 return remoteoffset;
3548 time_t tzoffset_sec(time_t *now)
3552 struct tm buf1, buf2;
3554 if (now && *now < 0)
3557 gmt = *gmtime_r(now, &buf1);
3558 lt = localtime_r(now, &buf2);
3560 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3562 if (lt->tm_year < gmt.tm_year)
3564 else if (lt->tm_year > gmt.tm_year)
3566 else if (lt->tm_yday < gmt.tm_yday)
3568 else if (lt->tm_yday > gmt.tm_yday)
3571 if (off >= 24 * 60) /* should be impossible */
3572 off = 23 * 60 + 59; /* if not, insert silly value */
3573 if (off <= -24 * 60)
3574 off = -(23 * 60 + 59);
3579 /* calculate timezone offset */
3580 gchar *tzoffset(time_t *now)
3582 static gchar offset_string[6];
3586 struct tm buf1, buf2;
3588 if (now && *now < 0)
3591 gmt = *gmtime_r(now, &buf1);
3592 lt = localtime_r(now, &buf2);
3594 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3596 if (lt->tm_year < gmt.tm_year)
3598 else if (lt->tm_year > gmt.tm_year)
3600 else if (lt->tm_yday < gmt.tm_yday)
3602 else if (lt->tm_yday > gmt.tm_yday)
3610 if (off >= 24 * 60) /* should be impossible */
3611 off = 23 * 60 + 59; /* if not, insert silly value */
3613 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3615 return offset_string;
3618 void get_rfc822_date(gchar *buf, gint len)
3622 gchar day[4], mon[4];
3623 gint dd, hh, mm, ss, yyyy;
3625 gchar buf2[BUFFSIZE];
3628 lt = localtime_r(&t, &buf1);
3630 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3631 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3633 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3634 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3637 void debug_set_mode(gboolean mode)
3642 gboolean debug_get_mode(void)
3647 void debug_print_real(const gchar *format, ...)
3650 gchar buf[BUFFSIZE];
3652 if (!debug_mode) return;
3654 va_start(args, format);
3655 g_vsnprintf(buf, sizeof(buf), format, args);
3662 const char * debug_srcname(const char *file)
3664 const char *s = strrchr (file, '/');
3669 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3671 if (subject == NULL)
3674 subject += subject_get_prefix_length(subject);
3676 return g_hash_table_lookup(subject_table, subject);
3679 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3682 if (subject == NULL || *subject == 0)
3684 subject += subject_get_prefix_length(subject);
3685 g_hash_table_insert(subject_table, subject, data);
3688 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3690 if (subject == NULL)
3693 subject += subject_get_prefix_length(subject);
3694 g_hash_table_remove(subject_table, subject);
3698 static regex_t u_regex;
3699 static gboolean u_init_;
3702 void utils_free_regex(void)
3713 *\brief Check if a string is prefixed with known (combinations)
3714 * of prefixes. The function assumes that each prefix
3715 * is terminated by zero or exactly _one_ space.
3717 *\param str String to check for a prefixes
3719 *\return int Number of chars in the prefix that should be skipped
3720 * for a "clean" subject line. If no prefix was found, 0
3723 int subject_get_prefix_length(const gchar *subject)
3726 /*!< Array with allowable reply prefixes regexps. */
3727 static const gchar * const prefixes[] = {
3728 "Re\\:", /* "Re:" */
3729 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3730 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3731 "Aw\\:", /* "Aw:" (German) */
3732 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3733 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3734 "Fw\\:", /* "Fw:" Forward */
3735 "Fwd\\:", /* "Fwd:" Forward */
3736 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3737 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3738 "Rif\\:", /* "Rif:" (Italian Outlook) */
3739 "Sv\\:", /* "Sv" (Norwegian) */
3740 "Vs\\:", /* "Vs" (Norwegian) */
3741 "Ad\\:", /* "Ad" (Norwegian) */
3742 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3743 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3744 "Re \\:", /* "Re :" (French Yahoo Mail) */
3747 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3751 if (!subject) return 0;
3752 if (!*subject) return 0;
3755 GString *s = g_string_new("");
3757 for (n = 0; n < PREFIXES; n++)
3758 /* Terminate each prefix regexpression by a
3759 * "\ ?" (zero or ONE space), and OR them */
3760 g_string_append_printf(s, "(%s\\ ?)%s",
3765 g_string_prepend(s, "(");
3766 g_string_append(s, ")+"); /* match at least once */
3767 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3770 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3771 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3772 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3773 debug_print("Error compiling regexp %s\n", s->str);
3774 g_string_free(s, TRUE);
3778 g_string_free(s, TRUE);
3782 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3787 /*!< Array with allowable reply prefixes regexps. */
3788 static const gchar * const prefixes[] = {
3790 "antw:", /* "Antw:" (Dutch / German Outlook) */
3791 "aw:", /* "Aw:" (German) */
3792 "antwort:", /* "Antwort:" (German Lotus Notes) */
3793 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3794 "fw:", /* "Fw:" Forward */
3795 "fwd:", /* "Fwd:" Forward */
3796 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3797 "odp:", /* "Odp:" Re (Polish Outlook) */
3798 "rif:", /* "Rif:" (Italian Outlook) */
3799 "sv:", /* "Sv" (Norwegian) */
3800 "vs:", /* "Vs" (Norwegian) */
3801 "ad:", /* "Ad" (Norwegian) */
3802 "R\303\251f. :", /* "R�f. :" (French Lotus Notes) */
3803 "Re :", /* "Re :" (French Yahoo Mail) */
3806 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3809 if (!subject) return 0;
3810 if (!*subject) return 0;
3812 for (n = 0; n < PREFIXES; n++) {
3813 int len = strlen(prefixes[n]);
3814 if (!strncasecmp(subject, prefixes[n], len)) {
3815 if (subject[len] == ' ')
3824 static guint g_stricase_hash(gconstpointer gptr)
3826 guint hash_result = 0;
3829 for (str = gptr; str && *str; str++) {
3830 hash_result += toupper(*str);
3836 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3838 const char *str1 = gptr1;
3839 const char *str2 = gptr2;
3841 return !strcasecmp(str1, str2);
3844 gint g_int_compare(gconstpointer a, gconstpointer b)
3846 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3849 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3857 lt = localtime_r(&t, &buft);
3859 if (user_addr != NULL)
3860 addr = g_strdup_printf(".%s", user_addr);
3861 else if (strlen(buf) != 0)
3862 addr = g_strdup_printf("@%s", buf);
3864 addr = g_strdup_printf("@%s", get_domain_name());
3866 /* Replace all @ but the last one in addr, with underscores.
3867 * RFC 2822 States that msg-id syntax only allows one @.
3869 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3870 *(strchr(addr, '@')) = '_';
3872 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3873 lt->tm_year + 1900, lt->tm_mon + 1,
3874 lt->tm_mday, lt->tm_hour,
3875 lt->tm_min, lt->tm_sec,
3876 (guint) rand(), addr);
3883 quote_cmd_argument()
3885 return a quoted string safely usable in argument of a command.
3887 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3890 gint quote_cmd_argument(gchar * result, guint size,
3900 for(p = path ; * p != '\0' ; p ++) {
3902 if (isalnum((guchar)*p) || (* p == '/')) {
3903 if (remaining > 0) {
3909 result[size - 1] = '\0';
3914 if (remaining >= 2) {
3922 result[size - 1] = '\0';
3927 if (remaining > 0) {
3931 result[size - 1] = '\0';
3945 static void g_node_map_recursive(GNode *node, gpointer data)
3947 GNodeMapData *mapdata = (GNodeMapData *) data;
3949 GNodeMapData newmapdata;
3952 newdata = mapdata->func(node->data, mapdata->data);
3953 if (newdata != NULL) {
3954 newnode = g_node_new(newdata);
3955 g_node_append(mapdata->parent, newnode);
3957 newmapdata.parent = newnode;
3958 newmapdata.func = mapdata->func;
3959 newmapdata.data = mapdata->data;
3961 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3965 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3968 GNodeMapData mapdata;
3970 cm_return_val_if_fail(node != NULL, NULL);
3971 cm_return_val_if_fail(func != NULL, NULL);
3973 root = g_node_new(func(node->data, data));
3975 mapdata.parent = root;
3976 mapdata.func = func;
3977 mapdata.data = data;
3979 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3984 #define HEX_TO_INT(val, hex) \
3988 if ('0' <= c && c <= '9') { \
3990 } else if ('a' <= c && c <= 'f') { \
3991 val = c - 'a' + 10; \
3992 } else if ('A' <= c && c <= 'F') { \
3993 val = c - 'A' + 10; \
3999 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4006 if (hi == -1 || lo == -1)
4009 *out = (hi << 4) + lo;
4013 #define INT_TO_HEX(hex, val) \
4016 hex = '0' + (val); \
4018 hex = 'A' + (val) - 10; \
4021 void get_hex_str(gchar *out, guchar ch)
4025 INT_TO_HEX(hex, ch >> 4);
4027 INT_TO_HEX(hex, ch & 0x0f);
4033 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4035 #define G_PRINT_REF g_print
4039 *\brief Register ref counted pointer. It is based on GBoxed, so should
4040 * work with anything that uses the GType system. The semantics
4041 * are similar to a C++ auto pointer, with the exception that
4042 * C doesn't have automatic closure (calling destructors) when
4043 * exiting a block scope.
4044 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4045 * function directly.
4047 *\return GType A GType type.
4049 GType g_auto_pointer_register(void)
4051 static GType auto_pointer_type;
4052 if (!auto_pointer_type)
4054 g_boxed_type_register_static
4055 ("G_TYPE_AUTO_POINTER",
4056 (GBoxedCopyFunc) g_auto_pointer_copy,
4057 (GBoxedFreeFunc) g_auto_pointer_free);
4058 return auto_pointer_type;
4062 *\brief Structure with g_new() allocated pointer guarded by the
4065 typedef struct AutoPointerRef {
4066 void (*free) (gpointer);
4072 *\brief The auto pointer opaque structure that references the
4073 * pointer guard block.
4075 typedef struct AutoPointer {
4076 AutoPointerRef *ref;
4077 gpointer ptr; /*!< access to protected pointer */
4081 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4085 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4086 * ... when assigning, copying and freeing storage elements
4088 * gtk_list_store_new(N_S_COLUMNS,
4089 * G_TYPE_AUTO_POINTER,
4093 * Template *precious_data = g_new0(Template, 1);
4094 * g_pointer protect = g_auto_pointer_new(precious_data);
4096 * gtk_list_store_set(container, &iter,
4100 * ... the gtk_list_store has copied the pointer and
4101 * ... incremented its reference count, we should free
4102 * ... the auto pointer (in C++ a destructor would do
4103 * ... this for us when leaving block scope)
4105 * g_auto_pointer_free(protect);
4107 * ... gtk_list_store_set() now manages the data. When
4108 * ... *explicitly* requesting a pointer from the list
4109 * ... store, don't forget you get a copy that should be
4110 * ... freed with g_auto_pointer_free() eventually.
4114 *\param pointer Pointer to be guarded.
4116 *\return GAuto * Pointer that should be used in containers with
4119 GAuto *g_auto_pointer_new(gpointer p)
4121 AutoPointerRef *ref;
4127 ref = g_new0(AutoPointerRef, 1);
4128 ptr = g_new0(AutoPointer, 1);
4138 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4144 *\brief Allocate an autopointer using the passed \a free function to
4145 * free the guarded pointer
4147 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4154 aptr = g_auto_pointer_new(p);
4155 aptr->ref->free = free_;
4159 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4161 if (auto_ptr == NULL)
4163 return ((AutoPointer *) auto_ptr)->ptr;
4167 *\brief Copies an auto pointer by. It's mostly not necessary
4168 * to call this function directly, unless you copy/assign
4169 * the guarded pointer.
4171 *\param auto_ptr Auto pointer returned by previous call to
4172 * g_auto_pointer_new_XXX()
4174 *\return gpointer An auto pointer
4176 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4179 AutoPointerRef *ref;
4182 if (auto_ptr == NULL)
4187 newp = g_new0(AutoPointer, 1);
4190 newp->ptr = ref->pointer;
4194 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4200 *\brief Free an auto pointer
4202 void g_auto_pointer_free(GAuto *auto_ptr)
4205 AutoPointerRef *ref;
4207 if (auto_ptr == NULL)
4213 if (--(ref->cnt) == 0) {
4215 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4217 ref->free(ref->pointer);
4222 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4227 void replace_returns(gchar *str)
4232 while (strstr(str, "\n")) {
4233 *strstr(str, "\n") = ' ';
4235 while (strstr(str, "\r")) {
4236 *strstr(str, "\r") = ' ';
4240 /* get_uri_part() - retrieves a URI starting from scanpos.
4241 Returns TRUE if succesful */
4242 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4243 const gchar **bp, const gchar **ep, gboolean hdr)
4246 gint parenthese_cnt = 0;
4248 cm_return_val_if_fail(start != NULL, FALSE);
4249 cm_return_val_if_fail(scanpos != NULL, FALSE);
4250 cm_return_val_if_fail(bp != NULL, FALSE);
4251 cm_return_val_if_fail(ep != NULL, FALSE);
4255 /* find end point of URI */
4256 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4257 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4258 !IS_ASCII(*(const guchar *)ep_) ||
4259 strchr("[]{}<>\"", *ep_)) {
4261 } else if (strchr("(", *ep_)) {
4263 } else if (strchr(")", *ep_)) {
4264 if (parenthese_cnt > 0)
4271 /* no punctuation at end of string */
4273 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4274 * should pass some URI type to this function and decide on that whether
4275 * to perform punctuation stripping */
4277 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4279 for (; ep_ - 1 > scanpos + 1 &&
4280 IS_REAL_PUNCT(*(ep_ - 1));
4284 #undef IS_REAL_PUNCT
4291 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4293 while (bp && *bp && g_ascii_isspace(*bp))
4295 return g_strndup(bp, ep - bp);
4298 /* valid mail address characters */
4299 #define IS_RFC822_CHAR(ch) \
4303 !g_ascii_isspace(ch) && \
4304 !strchr("(),;<>\"", (ch)))
4306 /* alphabet and number within 7bit ASCII */
4307 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4308 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4310 static GHashTable *create_domain_tab(void)
4312 static const gchar *toplvl_domains [] = {
4314 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4315 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4316 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4317 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4318 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4319 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4320 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4321 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4322 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4323 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4324 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4325 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4326 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4327 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4328 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4329 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4330 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4331 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4332 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4333 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4334 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4335 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4336 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4337 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4338 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4339 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4342 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4344 cm_return_val_if_fail(htab, NULL);
4345 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4346 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4350 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4352 const gint MAX_LVL_DOM_NAME_LEN = 6;
4353 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4354 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4357 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4360 for (p = buf; p < m && first < last; *p++ = *first++)
4364 return g_hash_table_lookup(tab, buf) != NULL;
4367 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4368 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4369 const gchar **bp, const gchar **ep, gboolean hdr)
4371 /* more complex than the uri part because we need to scan back and forward starting from
4372 * the scan position. */
4373 gboolean result = FALSE;
4374 const gchar *bp_ = NULL;
4375 const gchar *ep_ = NULL;
4376 static GHashTable *dom_tab;
4377 const gchar *last_dot = NULL;
4378 const gchar *prelast_dot = NULL;
4379 const gchar *last_tld_char = NULL;
4381 /* the informative part of the email address (describing the name
4382 * of the email address owner) may contain quoted parts. the
4383 * closure stack stores the last encountered quotes. */
4384 gchar closure_stack[128];
4385 gchar *ptr = closure_stack;
4387 cm_return_val_if_fail(start != NULL, FALSE);
4388 cm_return_val_if_fail(scanpos != NULL, FALSE);
4389 cm_return_val_if_fail(bp != NULL, FALSE);
4390 cm_return_val_if_fail(ep != NULL, FALSE);
4393 const gchar *start_quote = NULL;
4394 const gchar *end_quote = NULL;
4396 /* go to the real start */
4397 if (start[0] == ',')
4399 if (start[0] == ';')
4401 while (start[0] == '\n' || start[0] == '\r')
4403 while (start[0] == ' ' || start[0] == '\t')
4408 /* check if there are quotes (to skip , in them) */
4409 if (*start == '"') {
4410 start_quote = start;
4412 end_quote = strstr(start, "\"");
4418 /* skip anything between quotes */
4419 if (start_quote && end_quote) {
4424 /* find end (either , or ; or end of line) */
4425 if (strstr(start, ",") && strstr(start, ";"))
4426 *ep = strstr(start,",") < strstr(start, ";")
4427 ? strstr(start, ",") : strstr(start, ";");
4428 else if (strstr(start, ","))
4429 *ep = strstr(start, ",");
4430 else if (strstr(start, ";"))
4431 *ep = strstr(start, ";");
4433 *ep = start+strlen(start);
4435 /* go back to real start */
4436 if (start_quote && end_quote) {
4437 start = start_quote;
4440 /* check there's still an @ in that, or search
4441 * further if possible */
4442 if (strstr(start, "@") && strstr(start, "@") < *ep)
4444 else if (*ep < start+strlen(start)) {
4447 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4455 dom_tab = create_domain_tab();
4456 cm_return_val_if_fail(dom_tab, FALSE);
4458 /* scan start of address */
4459 for (bp_ = scanpos - 1;
4460 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4463 /* TODO: should start with an alnum? */
4465 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4468 if (bp_ != scanpos) {
4469 /* scan end of address */
4470 for (ep_ = scanpos + 1;
4471 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4473 prelast_dot = last_dot;
4475 if (*(last_dot + 1) == '.') {
4476 if (prelast_dot == NULL)
4478 last_dot = prelast_dot;
4483 /* TODO: really should terminate with an alnum? */
4484 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4489 if (last_dot == NULL)
4491 if (last_dot >= ep_)
4492 last_dot = prelast_dot;
4493 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4497 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4498 if (*last_tld_char == '?')
4501 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4508 if (!result) return FALSE;
4510 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4511 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4512 && IS_RFC822_CHAR(*(ep_ + 3))) {
4513 /* this informative part with an @ in it is
4514 * followed by the email address */
4517 /* go to matching '>' (or next non-rfc822 char, like \n) */
4518 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4521 /* include the bracket */
4522 if (*ep_ == '>') ep_++;
4524 /* include the leading quote */
4532 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4533 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4536 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4537 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4540 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4541 #define IN_STACK() (ptr > closure_stack)
4542 /* has underrun check */
4543 #define POP_STACK() if(IN_STACK()) --ptr
4544 /* has overrun check */
4545 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4546 /* has underrun check */
4547 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4551 /* scan for the informative part. */
4552 for (bp_ -= 2; bp_ >= start; bp_--) {
4553 /* if closure on the stack keep scanning */
4554 if (PEEK_STACK() == *bp_) {
4558 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4563 /* if nothing in the closure stack, do the special conditions
4564 * the following if..else expression simply checks whether
4565 * a token is acceptable. if not acceptable, the clause
4566 * should terminate the loop with a 'break' */
4567 if (!PEEK_STACK()) {
4569 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4570 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4571 /* hyphens are allowed, but only in
4573 } else if (strchr(" \"'", *bp_)) {
4574 /* but anything not being a punctiation
4577 break; /* anything else is rejected */
4584 /* scan forward (should start with an alnum) */
4585 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4601 #undef IS_ASCII_ALNUM
4602 #undef IS_RFC822_CHAR
4604 gchar *make_email_string(const gchar *bp, const gchar *ep)
4606 /* returns a mailto: URI; mailto: is also used to detect the
4607 * uri type later on in the button_pressed signal handler */
4611 tmp = g_strndup(bp, ep - bp);
4612 result = g_strconcat("mailto:", tmp, NULL);
4618 gchar *make_http_string(const gchar *bp, const gchar *ep)
4620 /* returns an http: URI; */
4624 while (bp && *bp && g_ascii_isspace(*bp))
4626 tmp = g_strndup(bp, ep - bp);
4627 result = g_strconcat("http://", tmp, NULL);
4633 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4635 FILE *fp = g_fopen(path, "rb");
4636 gchar buf[BUFFSIZE];
4637 gchar *result = NULL;
4640 while (fgets(buf, sizeof (buf), fp) != NULL) {
4641 gchar **parts = g_strsplit(buf, ";", 3);
4642 gchar *trimmed = parts[0];
4643 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4645 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4646 trimmed[strlen(trimmed)-1] = '\0';
4648 if (!strcmp(trimmed, type)) {
4649 gboolean needsterminal = FALSE;
4650 if (parts[2] && strstr(parts[2], "needsterminal")) {
4651 needsterminal = TRUE;
4653 if (parts[2] && strstr(parts[2], "test=")) {
4654 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4655 gchar *testcmd = orig_testcmd;
4656 if (strstr(testcmd,";"))
4657 *(strstr(testcmd,";")) = '\0';
4658 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4660 while (testcmd[strlen(testcmd)-1] == '\n')
4661 testcmd[strlen(testcmd)-1] = '\0';
4662 while (testcmd[strlen(testcmd)-1] == '\r')
4663 testcmd[strlen(testcmd)-1] = '\0';
4664 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4665 testcmd[strlen(testcmd)-1] = '\0';
4667 if (strstr(testcmd, "%s")) {
4668 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4669 gint res = system(tmp);
4671 g_free(orig_testcmd);
4678 gint res = system(testcmd);
4679 g_free(orig_testcmd);
4689 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4691 while (trimmed[strlen(trimmed)-1] == '\n')
4692 trimmed[strlen(trimmed)-1] = '\0';
4693 while (trimmed[strlen(trimmed)-1] == '\r')
4694 trimmed[strlen(trimmed)-1] = '\0';
4695 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4696 trimmed[strlen(trimmed)-1] = '\0';
4697 result = g_strdup(trimmed);
4700 /* if there are no single quotes around %s, add them.
4701 * '.*%s.*' is ok, as in display 'png:%s'
4703 if (strstr(result, "%s")
4704 && !(strstr(result, "'") < strstr(result,"%s") &&
4705 strstr(strstr(result,"%s"), "'"))) {
4706 gchar *start = g_strdup(result);
4707 gchar *end = g_strdup(strstr(result, "%s")+2);
4709 *strstr(start, "%s") = '\0';
4710 tmp = g_strconcat(start,"'%s'",end, NULL);
4716 if (needsterminal) {
4717 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4728 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4730 gchar *result = NULL;
4734 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4735 result = mailcap_get_command_in_file(path, type, file_to_open);
4739 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4743 void mailcap_update_default(const gchar *type, const gchar *command)
4745 gchar *path = NULL, *outpath = NULL;
4746 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4747 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4748 FILE *fp = g_fopen(path, "rb");
4749 FILE *outfp = g_fopen(outpath, "wb");
4750 gchar buf[BUFFSIZE];
4751 gboolean err = FALSE;
4759 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4760 gchar **parts = g_strsplit(buf, ";", 3);
4761 gchar *trimmed = parts[0];
4762 while (trimmed[0] == ' ')
4764 while (trimmed[strlen(trimmed)-1] == ' ')
4765 trimmed[strlen(trimmed)-1] = '\0';
4767 if (!strcmp(trimmed, type)) {
4772 if(fputs(buf, outfp) == EOF) {
4779 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4785 if (fclose(outfp) == EOF)
4789 g_rename(outpath, path);
4795 gint copy_dir(const gchar *src, const gchar *dst)
4800 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4801 g_warning("failed to open directory: %s\n", src);
4805 if (make_dir(dst) < 0)
4808 while ((name = g_dir_read_name(dir)) != NULL) {
4809 gchar *old_file, *new_file;
4810 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4811 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4812 debug_print("copying: %s -> %s\n", old_file, new_file);
4813 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4814 gint r = copy_file(old_file, new_file, TRUE);
4821 /* Windows has no symlinks. Or well, Vista seems to
4822 have something like this but the semantics might be
4823 different. Thus we don't use it under Windows. */
4824 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4827 gchar *target = g_file_read_link(old_file, &error);
4829 r = symlink(target, new_file);
4836 #endif /*G_OS_WIN32*/
4837 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4838 gint r = copy_dir(old_file, new_file);
4849 /* crude test to see if a file is an email. */
4850 gboolean file_is_email (const gchar *filename)
4856 if (filename == NULL)
4858 if ((fp = g_fopen(filename, "rb")) == NULL)
4860 while (i < 60 && score < 3
4861 && fgets(buffer, sizeof (buffer), fp) > 0) {
4862 if (!strncmp(buffer, "From:", strlen("From:")))
4864 else if (!strncmp(buffer, "Date:", strlen("Date:")))
4866 else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
4868 else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4873 return (score >= 3);
4876 gboolean sc_g_list_bigger(GList *list, gint max)
4880 while (cur && i <= max+1) {
4887 gboolean sc_g_slist_bigger(GSList *list, gint max)
4891 while (cur && i <= max+1) {
4898 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4899 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4900 NULL, NULL, NULL, NULL, NULL, NULL};
4901 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4902 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4903 NULL, NULL, NULL, NULL, NULL, NULL};
4905 gint daynames_len[] = {0,0,0,0,0,0,0};
4906 gint monthnames_len[] = {0,0,0,0,0,0,
4908 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4909 gint s_monthnames_len[] = {0,0,0,0,0,0,
4911 const gchar *s_am_up = NULL;
4912 const gchar *s_pm_up = NULL;
4913 const gchar *s_am_low = NULL;
4914 const gchar *s_pm_low = NULL;
4916 gint s_am_up_len = 0;
4917 gint s_pm_up_len = 0;
4918 gint s_am_low_len = 0;
4919 gint s_pm_low_len = 0;
4921 const gchar *def_loc_format = NULL;
4922 const gchar *date_loc_format = NULL;
4923 const gchar *time_loc_format = NULL;
4924 const gchar *time_am_pm = NULL;
4926 static gboolean time_names_init_done = FALSE;
4928 static void init_time_names(void)
4932 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4933 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4934 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4935 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4936 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4937 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4938 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4940 monthnames[0] = Q_("Complete month name for use by strftime|January");
4941 monthnames[1] = Q_("Complete month name for use by strftime|February");
4942 monthnames[2] = Q_("Complete month name for use by strftime|March");
4943 monthnames[3] = Q_("Complete month name for use by strftime|April");
4944 monthnames[4] = Q_("Complete month name for use by strftime|May");
4945 monthnames[5] = Q_("Complete month name for use by strftime|June");
4946 monthnames[6] = Q_("Complete month name for use by strftime|July");
4947 monthnames[7] = Q_("Complete month name for use by strftime|August");
4948 monthnames[8] = Q_("Complete month name for use by strftime|September");
4949 monthnames[9] = Q_("Complete month name for use by strftime|October");
4950 monthnames[10] = Q_("Complete month name for use by strftime|November");
4951 monthnames[11] = Q_("Complete month name for use by strftime|December");
4953 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4954 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4955 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4956 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4957 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4958 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4959 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4961 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4962 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4963 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4964 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4965 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4966 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4967 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4968 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4969 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4970 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4971 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4972 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4974 for (i = 0; i < 7; i++) {
4975 daynames_len[i] = strlen(daynames[i]);
4976 s_daynames_len[i] = strlen(s_daynames[i]);
4978 for (i = 0; i < 12; i++) {
4979 monthnames_len[i] = strlen(monthnames[i]);
4980 s_monthnames_len[i] = strlen(s_monthnames[i]);
4983 s_am_up = Q_("For use by strftime (morning)|AM");
4984 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4985 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4986 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4988 s_am_up_len = strlen(s_am_up);
4989 s_pm_up_len = strlen(s_pm_up);
4990 s_am_low_len = strlen(s_am_low);
4991 s_pm_low_len = strlen(s_pm_low);
4993 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4994 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4995 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4997 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4999 time_names_init_done = TRUE;
5002 #define CHECK_SIZE() { \
5003 total_done += len; \
5004 if (total_done >= buflen) { \
5005 buf[buflen-1] = '\0'; \
5010 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5012 gchar *curpos = buf;
5013 gint total_done = 0;
5014 gchar subbuf[64], subfmt[64];
5015 static time_t last_tzset = (time_t)0;
5017 if (!time_names_init_done)
5020 if (format == NULL || lt == NULL)
5023 if (last_tzset != time(NULL)) {
5025 last_tzset = time(NULL);
5028 if (*format == '%') {
5029 gint len = 0, tmp = 0;
5033 len = 1; CHECK_SIZE();
5037 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5038 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5041 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5042 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5046 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5047 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5050 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5051 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5054 fast_strftime(subbuf, 64, def_loc_format, lt);
5055 len = strlen(subbuf); CHECK_SIZE();
5056 strncpy2(curpos, subbuf, buflen - total_done);
5059 total_done += 2; CHECK_SIZE();
5060 tmp = (lt->tm_year + 1900)/100;
5061 *curpos++ = '0'+(tmp / 10);
5062 *curpos++ = '0'+(tmp % 10);
5065 total_done += 2; CHECK_SIZE();
5066 *curpos++ = '0'+(lt->tm_mday / 10);
5067 *curpos++ = '0'+(lt->tm_mday % 10);
5070 total_done += 8; CHECK_SIZE();
5071 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5072 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5074 *curpos++ = '0'+(lt->tm_mday / 10);
5075 *curpos++ = '0'+(lt->tm_mday % 10);
5077 tmp = lt->tm_year%100;
5078 *curpos++ = '0'+(tmp / 10);
5079 *curpos++ = '0'+(tmp % 10);
5082 len = 2; CHECK_SIZE();
5083 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5086 len = 10; CHECK_SIZE();
5087 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5088 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5091 total_done += 2; CHECK_SIZE();
5092 *curpos++ = '0'+(lt->tm_hour / 10);
5093 *curpos++ = '0'+(lt->tm_hour % 10);
5096 total_done += 2; CHECK_SIZE();
5102 *curpos++ = '0'+(tmp / 10);
5103 *curpos++ = '0'+(tmp % 10);
5106 len = 3; CHECK_SIZE();
5107 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5110 len = 2; CHECK_SIZE();
5111 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5114 len = 2; CHECK_SIZE();
5120 snprintf(curpos, buflen - total_done, "%2d", tmp);
5123 total_done += 2; CHECK_SIZE();
5124 tmp = lt->tm_mon + 1;
5125 *curpos++ = '0'+(tmp / 10);
5126 *curpos++ = '0'+(tmp % 10);
5129 total_done += 2; CHECK_SIZE();
5130 *curpos++ = '0'+(lt->tm_min / 10);
5131 *curpos++ = '0'+(lt->tm_min % 10);
5134 len = 1; CHECK_SIZE();
5138 if (lt->tm_hour >= 12) {
5139 len = s_pm_up_len; CHECK_SIZE();
5140 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5142 len = s_am_up_len; CHECK_SIZE();
5143 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5147 if (lt->tm_hour >= 12) {
5148 len = s_pm_low_len; CHECK_SIZE();
5149 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5151 len = s_am_low_len; CHECK_SIZE();
5152 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5156 fast_strftime(subbuf, 64, time_am_pm, lt);
5157 len = strlen(subbuf); CHECK_SIZE();
5158 strncpy2(curpos, subbuf, buflen - total_done);
5161 total_done += 5; CHECK_SIZE();
5162 *curpos++ = '0'+(lt->tm_hour / 10);
5163 *curpos++ = '0'+(lt->tm_hour % 10);
5165 *curpos++ = '0'+(lt->tm_min / 10);
5166 *curpos++ = '0'+(lt->tm_min % 10);
5169 snprintf(subbuf, 64, "%ld", mktime(lt));
5170 len = strlen(subbuf); CHECK_SIZE();
5171 strncpy2(curpos, subbuf, buflen - total_done);
5174 total_done += 2; CHECK_SIZE();
5175 *curpos++ = '0'+(lt->tm_sec / 10);
5176 *curpos++ = '0'+(lt->tm_sec % 10);
5179 len = 1; CHECK_SIZE();
5183 total_done += 8; CHECK_SIZE();
5184 *curpos++ = '0'+(lt->tm_hour / 10);
5185 *curpos++ = '0'+(lt->tm_hour % 10);
5187 *curpos++ = '0'+(lt->tm_min / 10);
5188 *curpos++ = '0'+(lt->tm_min % 10);
5190 *curpos++ = '0'+(lt->tm_sec / 10);
5191 *curpos++ = '0'+(lt->tm_sec % 10);
5194 len = 1; CHECK_SIZE();
5195 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5198 len = 1; CHECK_SIZE();
5199 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5202 fast_strftime(subbuf, 64, date_loc_format, lt);
5203 len = strlen(subbuf); CHECK_SIZE();
5204 strncpy2(curpos, subbuf, buflen - total_done);
5207 fast_strftime(subbuf, 64, time_loc_format, lt);
5208 len = strlen(subbuf); CHECK_SIZE();
5209 strncpy2(curpos, subbuf, buflen - total_done);
5212 total_done += 2; CHECK_SIZE();
5213 tmp = lt->tm_year%100;
5214 *curpos++ = '0'+(tmp / 10);
5215 *curpos++ = '0'+(tmp % 10);
5218 len = 4; CHECK_SIZE();
5219 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5229 /* let these complicated ones be done with the libc */
5230 snprintf(subfmt, 64, "%%%c", *format);
5231 strftime(subbuf, 64, subfmt, lt);
5232 len = strlen(subbuf); CHECK_SIZE();
5233 strncpy2(curpos, subbuf, buflen - total_done);
5237 /* let these complicated modifiers be done with the libc */
5238 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5239 strftime(subbuf, 64, subfmt, lt);
5240 len = strlen(subbuf); CHECK_SIZE();
5241 strncpy2(curpos, subbuf, buflen - total_done);
5245 if (format && *format)
5246 g_warning("format error (%c)", *format);
5253 int len = 1; CHECK_SIZE();
5254 *curpos++ = *format++;
5261 gboolean prefs_common_get_use_shred(void);
5265 #define WEXITSTATUS(x) (x)
5268 int claws_unlink(const gchar *filename)
5271 static int found_shred = -1;
5272 static const gchar *args[4];
5274 if (filename == NULL)
5277 if (prefs_common_get_use_shred()) {
5278 if (found_shred == -1) {
5280 args[0] = g_find_program_in_path("shred");
5281 debug_print("found shred: %s\n", args[0]);
5282 found_shred = (args[0] != NULL) ? 1:0;
5286 if (found_shred == 1) {
5287 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5288 if (s.st_nlink == 1) {
5291 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5292 NULL, NULL, NULL, NULL, &status, NULL);
5293 debug_print("%s %s exited with status %d\n",
5294 args[0], filename, WEXITSTATUS(status));
5295 if (truncate(filename, 0) < 0)
5296 g_warning("couln't truncate");
5301 return g_unlink(filename);