2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
67 # include <tablet-browser-interface.h>
69 # include <osso-browser-interface.h>
75 #include "../codeconv.h"
79 static gboolean debug_mode = FALSE;
81 static GSList *tempfiles=NULL;
84 /* Return true if we are running as root. This function should beused
85 instead of getuid () == 0. */
86 gboolean superuser_p (void)
89 return w32_is_administrator ();
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
101 if (G_WIN32_HAVE_WIDECHAR_API()) {
106 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
112 retval = _wchdir(wpath);
124 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125 if (cp_path == NULL) {
130 retval = chdir(cp_path);
143 gint g_chmod(const gchar *path, gint mode)
146 if (G_WIN32_HAVE_WIDECHAR_API()) {
151 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
157 retval = _wchmod(wpath, mode);
169 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170 if (cp_path == NULL) {
175 retval = chmod(cp_path, mode);
184 return chmod(path, mode);
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
191 char *name = g_win32_locale_filename_from_utf8(filename);
192 FILE* fp = fopen(name, mode);
196 return fopen(filename, mode);
199 int g_open(const gchar *filename, int flags, int mode)
202 char *name = g_win32_locale_filename_from_utf8(filename);
203 int fd = open(name, flags, mode);
207 return open(filename, flags, mode);
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
214 gint mkstemp_name(gchar *template, gchar **name_used)
216 static gulong count=0; /* W32-_mktemp only supports up to 27
220 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222 (S_IRUSR | S_IWUSR));
224 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
226 perror(g_strdup_printf("cant create %s",*name_used));
232 #endif /* G_OS_WIN32 */
235 gint mkstemp(gchar *template)
238 gint res = mkstemp_name(template, &dummyname);
242 #endif /* G_OS_WIN32 */
244 void list_free_strings(GList *list)
246 list = g_list_first(list);
248 while (list != NULL) {
254 void slist_free_strings(GSList *list)
256 while (list != NULL) {
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
267 void hash_free_strings(GHashTable *table)
269 g_hash_table_foreach(table, hash_free_strings_func, NULL);
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
274 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
277 guint str_case_hash(gconstpointer key)
279 const gchar *p = key;
283 h = g_ascii_tolower(h);
284 for (p += 1; *p != '\0'; p++)
285 h = (h << 5) - h + g_ascii_tolower(*p);
291 void ptr_array_free_strings(GPtrArray *array)
296 cm_return_if_fail(array != NULL);
298 for (i = 0; i < array->len; i++) {
299 str = g_ptr_array_index(array, i);
304 gboolean str_find(const gchar *haystack, const gchar *needle)
306 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
311 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
314 gint to_number(const gchar *nstr)
316 register const gchar *p;
318 if (*nstr == '\0') return -1;
320 for (p = nstr; *p != '\0'; p++)
321 if (!g_ascii_isdigit(*p)) return -1;
326 /* convert integer into string,
327 nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
330 g_snprintf(nstr, 11, "%d", n);
334 /* convert integer into string */
337 static gchar nstr[11];
339 return itos_buf(nstr, n);
342 #define divide(num,divisor,i,d) \
344 i = num >> divisor; \
345 d = num & ((1<<divisor)-1); \
346 d = (d*100) >> divisor; \
351 * \brief Convert a given size in bytes in a human-readable string
353 * \param size The size expressed in bytes to convert in string
354 * \return The string that respresents the size in an human-readable way
356 gchar *to_human_readable(goffset size)
358 static gchar str[14];
359 static gchar *b_format = NULL, *kb_format = NULL,
360 *mb_format = NULL, *gb_format = NULL;
361 register int t = 0, r = 0;
362 if (b_format == NULL) {
364 kb_format = _("%d.%02dKB");
365 mb_format = _("%d.%02dMB");
366 gb_format = _("%.2fGB");
369 if (size < (goffset)1024) {
370 g_snprintf(str, sizeof(str), b_format, (gint)size);
372 } else if (size >> 10 < (goffset)1024) {
373 divide(size, 10, t, r);
374 g_snprintf(str, sizeof(str), kb_format, t, r);
376 } else if (size >> 20 < (goffset)1024) {
377 divide(size, 20, t, r);
378 g_snprintf(str, sizeof(str), mb_format, t, r);
381 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
386 /* strcmp with NULL-checking */
387 gint strcmp2(const gchar *s1, const gchar *s2)
389 if (s1 == NULL || s2 == NULL)
392 return strcmp(s1, s2);
394 /* strstr with NULL-checking */
395 gchar *strstr2(const gchar *s1, const gchar *s2)
397 if (s1 == NULL || s2 == NULL)
400 return strstr(s1, s2);
403 gint path_cmp(const gchar *s1, const gchar *s2)
408 gchar *s1buf, *s2buf;
411 if (s1 == NULL || s2 == NULL) return -1;
412 if (*s1 == '\0' || *s2 == '\0') return -1;
415 s1buf = g_strdup (s1);
416 s2buf = g_strdup (s2);
417 subst_char (s1buf, '/', G_DIR_SEPARATOR);
418 subst_char (s2buf, '/', G_DIR_SEPARATOR);
421 #endif /* !G_OS_WIN32 */
426 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
427 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
429 rc = strncmp(s1, s2, MAX(len1, len2));
433 #endif /* !G_OS_WIN32 */
437 /* remove trailing return code */
438 gchar *strretchomp(gchar *str)
442 if (!*str) return str;
444 for (s = str + strlen(str) - 1;
445 s >= str && (*s == '\n' || *s == '\r');
452 /* remove trailing character */
453 gchar *strtailchomp(gchar *str, gchar tail_char)
457 if (!*str) return str;
458 if (tail_char == '\0') return str;
460 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
466 /* remove CR (carriage return) */
467 gchar *strcrchomp(gchar *str)
471 if (!*str) return str;
473 s = str + strlen(str) - 1;
474 if (*s == '\n' && s > str && *(s - 1) == '\r') {
482 gint file_strip_crs(const gchar *file)
484 FILE *fp = NULL, *outfp = NULL;
486 gchar *out = get_tmp_file();
490 fp = g_fopen(file, "rb");
494 outfp = g_fopen(out, "wb");
500 while (fgets(buf, sizeof (buf), fp) != NULL) {
502 if (fputs(buf, outfp) == EOF) {
510 if (fclose(outfp) == EOF) {
514 if (move_file(out, file, TRUE) < 0)
526 /* Similar to `strstr' but this function ignores the case of both strings. */
527 gchar *strcasestr(const gchar *haystack, const gchar *needle)
529 register size_t haystack_len, needle_len;
531 haystack_len = strlen(haystack);
532 needle_len = strlen(needle);
534 if (haystack_len < needle_len || needle_len == 0)
537 while (haystack_len >= needle_len) {
538 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
539 return (gchar *)haystack;
549 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
550 gconstpointer needle, size_t needlelen)
552 const gchar *haystack_ = (const gchar *)haystack;
553 const gchar *needle_ = (const gchar *)needle;
554 const gchar *haystack_cur = (const gchar *)haystack;
555 size_t haystack_left = haystacklen;
558 return memchr(haystack_, *needle_, haystacklen);
560 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
562 if (haystacklen - (haystack_cur - haystack_) < needlelen)
564 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
565 return (gpointer)haystack_cur;
568 haystack_left = haystacklen - (haystack_cur - haystack_);
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 static void trim_subject_for_compare(gchar *str)
619 eliminate_parenthesis(str, '[', ']');
620 eliminate_parenthesis(str, '(', ')');
623 srcp = str + subject_get_prefix_length(str);
625 memmove(str, srcp, strlen(srcp) + 1);
628 static void trim_subject_for_sort(gchar *str)
634 srcp = str + subject_get_prefix_length(str);
636 memmove(str, srcp, strlen(srcp) + 1);
639 /* compare subjects */
640 gint subject_compare(const gchar *s1, const gchar *s2)
644 if (!s1 || !s2) return -1;
645 if (!*s1 || !*s2) return -1;
647 Xstrdup_a(str1, s1, return -1);
648 Xstrdup_a(str2, s2, return -1);
650 trim_subject_for_compare(str1);
651 trim_subject_for_compare(str2);
653 if (!*str1 || !*str2) return -1;
655 return strcmp(str1, str2);
658 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
662 if (!s1 || !s2) return -1;
664 Xstrdup_a(str1, s1, return -1);
665 Xstrdup_a(str2, s2, return -1);
667 trim_subject_for_sort(str1);
668 trim_subject_for_sort(str2);
670 return g_utf8_collate(str1, str2);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void extract_quote(gchar *str, gchar quote_chr)
795 if ((str = strchr(str, quote_chr))) {
797 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798 memmove(p - 1, p, strlen(p) + 1);
803 memmove(str, str + 1, p - str);
808 void eliminate_address_comment(gchar *str)
810 register gchar *srcp, *destp;
815 while ((destp = strchr(destp, '"'))) {
816 if ((srcp = strchr(destp + 1, '"'))) {
821 while (g_ascii_isspace(*srcp)) srcp++;
822 memmove(destp, srcp, strlen(srcp) + 1);
832 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
838 else if (*srcp == ')')
844 while (g_ascii_isspace(*srcp)) srcp++;
845 memmove(destp, srcp, strlen(srcp) + 1);
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
851 gboolean in_quote = FALSE;
854 if (*str == c && !in_quote)
856 if (*str == quote_chr)
864 void extract_address(gchar *str)
866 eliminate_address_comment(str);
867 if (strchr_with_skip_quote(str, '"', '<'))
868 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
872 void extract_list_id_str(gchar *str)
874 if (strchr_with_skip_quote(str, '"', '<'))
875 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
879 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
884 if (!str) return addr_list;
886 Xstrdup_a(work, str, return addr_list);
889 eliminate_address_comment(work);
892 while (workp && *workp) {
895 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
901 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
902 extract_parenthesis_with_skip_quote
903 (workp, '"', '<', '>');
907 addr_list = g_slist_append(addr_list, g_strdup(workp));
915 GSList *address_list_append(GSList *addr_list, const gchar *str)
917 return address_list_append_real(addr_list, str, TRUE);
920 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
922 return address_list_append_real(addr_list, str, FALSE);
925 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
929 if (!str) return msgid_list;
932 while (strp && *strp) {
933 const gchar *start, *end;
936 if ((start = strchr(strp, '<')) != NULL) {
937 end = strchr(start + 1, '>');
942 msgid = g_strndup(start + 1, end - start - 1);
945 msgid_list = g_slist_prepend(msgid_list, msgid);
955 GSList *references_list_append(GSList *msgid_list, const gchar *str)
959 list = references_list_prepend(NULL, str);
960 list = g_slist_reverse(list);
961 msgid_list = g_slist_concat(msgid_list, list);
966 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
971 if (!str) return group_list;
973 Xstrdup_a(work, str, return group_list);
977 while (workp && *workp) {
980 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
988 group_list = g_slist_append(group_list,
997 GList *add_history(GList *list, const gchar *str)
1002 cm_return_val_if_fail(str != NULL, list);
1004 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1007 list = g_list_remove(list, old->data);
1009 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1012 last = g_list_last(list);
1014 oldstr = last->data;
1015 list = g_list_remove(list, last->data);
1020 list = g_list_prepend(list, g_strdup(str));
1025 void remove_return(gchar *str)
1027 register gchar *p = str;
1030 if (*p == '\n' || *p == '\r')
1031 memmove(p, p + 1, strlen(p));
1037 void remove_space(gchar *str)
1039 register gchar *p = str;
1044 while (g_ascii_isspace(*(p + spc)))
1047 memmove(p, p + spc, strlen(p + spc) + 1);
1053 void unfold_line(gchar *str)
1055 register gchar *p = str;
1059 if (*p == '\n' || *p == '\r') {
1062 while (g_ascii_isspace(*(p + spc)))
1065 memmove(p, p + spc, strlen(p + spc) + 1);
1071 void subst_char(gchar *str, gchar orig, gchar subst)
1073 register gchar *p = str;
1082 void subst_chars(gchar *str, gchar *orig, gchar subst)
1084 register gchar *p = str;
1087 if (strchr(orig, *p) != NULL)
1093 void subst_for_filename(gchar *str)
1098 subst_chars(str, "\t\r\n\\/*:", '_');
1100 subst_chars(str, "\t\r\n\\/*", '_');
1104 void subst_for_shellsafe_filename(gchar *str)
1108 subst_for_filename(str);
1109 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1112 gboolean is_ascii_str(const gchar *str)
1114 const guchar *p = (const guchar *)str;
1116 while (*p != '\0') {
1117 if (*p != '\t' && *p != ' ' &&
1118 *p != '\r' && *p != '\n' &&
1119 (*p < 32 || *p >= 127))
1127 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1129 gchar * position = NULL;
1130 gchar * tmp_pos = NULL;
1133 if (quote_chars == NULL)
1136 for (i = 0; i < strlen(quote_chars); i++) {
1137 tmp_pos = strrchr (str, quote_chars[i]);
1139 || (tmp_pos != NULL && position <= tmp_pos) )
1145 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1147 const gchar *first_pos;
1148 const gchar *last_pos;
1149 const gchar *p = str;
1150 gint quote_level = -1;
1152 /* speed up line processing by only searching to the last '>' */
1153 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1154 /* skip a line if it contains a '<' before the initial '>' */
1155 if (memchr(str, '<', first_pos - str) != NULL)
1157 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1161 while (p <= last_pos) {
1162 while (p < last_pos) {
1163 if (g_ascii_isspace(*p))
1169 if (strchr(quote_chars, *p))
1171 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1172 /* any characters are allowed except '-','<' and space */
1173 while (*p != '-' && *p != '<'
1174 && !strchr(quote_chars, *p)
1175 && !g_ascii_isspace(*p)
1178 if (strchr(quote_chars, *p))
1190 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1192 const gchar *p = str, *q;
1193 gint cur_line = 0, len;
1195 while ((q = strchr(p, '\n')) != NULL) {
1197 if (len > max_chars) {
1207 if (len > max_chars) {
1216 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1218 gchar * position = NULL;
1219 gchar * tmp_pos = NULL;
1222 if (quote_chars == NULL)
1225 for (i = 0; i < strlen(quote_chars); i++) {
1226 tmp_pos = strchr (str, quote_chars[i]);
1228 || (tmp_pos != NULL && position >= tmp_pos) )
1234 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1236 register guint haystack_len, needle_len;
1237 gboolean in_squote = FALSE, in_dquote = FALSE;
1239 haystack_len = strlen(haystack);
1240 needle_len = strlen(needle);
1242 if (haystack_len < needle_len || needle_len == 0)
1245 while (haystack_len >= needle_len) {
1246 if (!in_squote && !in_dquote &&
1247 !strncmp(haystack, needle, needle_len))
1248 return (gchar *)haystack;
1250 /* 'foo"bar"' -> foo"bar"
1251 "foo'bar'" -> foo'bar' */
1252 if (*haystack == '\'') {
1255 else if (!in_dquote)
1257 } else if (*haystack == '\"') {
1260 else if (!in_squote)
1271 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1274 GSList *string_list = NULL, *slist;
1275 gchar **str_array, *s, *new_str;
1276 guint i, n = 1, len;
1278 cm_return_val_if_fail(str != NULL, NULL);
1279 cm_return_val_if_fail(delim != NULL, NULL);
1282 max_tokens = G_MAXINT;
1284 s = strstr_with_skip_quote(str, delim);
1286 guint delimiter_len = strlen(delim);
1290 new_str = g_strndup(str, len);
1292 if (new_str[0] == '\'' || new_str[0] == '\"') {
1293 if (new_str[len - 1] == new_str[0]) {
1294 new_str[len - 1] = '\0';
1295 memmove(new_str, new_str + 1, len - 1);
1298 string_list = g_slist_prepend(string_list, new_str);
1300 str = s + delimiter_len;
1301 s = strstr_with_skip_quote(str, delim);
1302 } while (--max_tokens && s);
1306 new_str = g_strdup(str);
1307 if (new_str[0] == '\'' || new_str[0] == '\"') {
1309 if (new_str[len - 1] == new_str[0]) {
1310 new_str[len - 1] = '\0';
1311 memmove(new_str, new_str + 1, len - 1);
1314 string_list = g_slist_prepend(string_list, new_str);
1318 str_array = g_new(gchar*, n);
1322 str_array[i--] = NULL;
1323 for (slist = string_list; slist; slist = slist->next)
1324 str_array[i--] = slist->data;
1326 g_slist_free(string_list);
1331 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1333 gchar *abbrev_group;
1335 const gchar *p = group;
1338 cm_return_val_if_fail(group != NULL, NULL);
1340 last = group + strlen(group);
1341 abbrev_group = ap = g_malloc(strlen(group) + 1);
1346 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1348 while (*p != '.') p++;
1351 return abbrev_group;
1356 return abbrev_group;
1359 gchar *trim_string(const gchar *str, gint len)
1361 const gchar *p = str;
1366 if (!str) return NULL;
1367 if (strlen(str) <= len)
1368 return g_strdup(str);
1369 if (g_utf8_validate(str, -1, NULL) == FALSE)
1370 return g_strdup(str);
1372 while (*p != '\0') {
1373 mb_len = g_utf8_skip[*(guchar *)p];
1376 else if (new_len + mb_len > len)
1383 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1384 return g_strconcat(new_str, "...", NULL);
1387 GList *uri_list_extract_filenames(const gchar *uri_list)
1389 GList *result = NULL;
1391 gchar *escaped_utf8uri;
1397 while (g_ascii_isspace(*p)) p++;
1398 if (!strncmp(p, "file:", 5)) {
1401 while (*q && *q != '\n' && *q != '\r') q++;
1404 gchar *file, *locale_file = NULL;
1406 while (q > p && g_ascii_isspace(*q))
1408 Xalloca(escaped_utf8uri, q - p + 2,
1410 Xalloca(file, q - p + 2,
1413 strncpy(escaped_utf8uri, p, q - p + 1);
1414 escaped_utf8uri[q - p + 1] = '\0';
1415 decode_uri(file, escaped_utf8uri);
1417 * g_filename_from_uri() rejects escaped/locale encoded uri
1418 * string which come from Nautilus.
1421 if (g_utf8_validate(file, -1, NULL))
1423 = conv_codeset_strdup(
1426 conv_get_locale_charset_str());
1428 locale_file = g_strdup(file + 5);
1430 locale_file = g_filename_from_uri(file, NULL, NULL);
1432 result = g_list_append(result, locale_file);
1436 p = strchr(p, '\n');
1443 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1446 static gint axtoi(const gchar *hexstr)
1448 gint hi, lo, result;
1451 if ('0' <= hi && hi <= '9') {
1454 if ('a' <= hi && hi <= 'f') {
1457 if ('A' <= hi && hi <= 'F') {
1462 if ('0' <= lo && lo <= '9') {
1465 if ('a' <= lo && lo <= 'f') {
1468 if ('A' <= lo && lo <= 'F') {
1471 result = lo + (16 * hi);
1475 gboolean is_uri_string(const gchar *str)
1477 while (str && *str && g_ascii_isspace(*str))
1479 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1480 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1481 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1482 g_ascii_strncasecmp(str, "www.", 4) == 0);
1485 gchar *get_uri_path(const gchar *uri)
1487 while (uri && *uri && g_ascii_isspace(*uri))
1489 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1490 return (gchar *)(uri + 7);
1491 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1492 return (gchar *)(uri + 8);
1493 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1494 return (gchar *)(uri + 6);
1496 return (gchar *)uri;
1499 gint get_uri_len(const gchar *str)
1503 if (is_uri_string(str)) {
1504 for (p = str; *p != '\0'; p++) {
1505 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1514 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1515 * plusses, and escape characters are used)
1517 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1519 gchar *dec = decoded_uri;
1520 const gchar *enc = encoded_uri;
1525 if (isxdigit((guchar)enc[0]) &&
1526 isxdigit((guchar)enc[1])) {
1532 if (with_plus && *enc == '+')
1544 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1546 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1549 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1551 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1552 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1556 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1557 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1561 const gchar *forbidden_uris[] = { ".gnupg/",
1567 gint num_attach = 0;
1568 gchar **my_att = NULL;
1570 Xstrdup_a(tmp_mailto, mailto, return -1);
1572 if (!strncmp(tmp_mailto, "mailto:", 7))
1575 p = strchr(tmp_mailto, '?');
1582 *to = decode_uri_gdup(tmp_mailto);
1584 my_att = g_malloc(sizeof(char *));
1588 gchar *field, *value;
1605 if (*value == '\0') continue;
1607 if (from && !g_ascii_strcasecmp(field, "from")) {
1609 *from = decode_uri_gdup(value);
1611 gchar *tmp = decode_uri_gdup(value);
1612 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1616 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1618 *cc = decode_uri_gdup(value);
1620 gchar *tmp = decode_uri_gdup(value);
1621 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1625 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1627 *bcc = decode_uri_gdup(value);
1629 gchar *tmp = decode_uri_gdup(value);
1630 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1634 } else if (subject && !*subject &&
1635 !g_ascii_strcasecmp(field, "subject")) {
1636 *subject = decode_uri_gdup(value);
1637 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1638 *body = decode_uri_gdup(value);
1639 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1640 gchar *tmp = decode_uri_gdup(value);
1641 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1642 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1646 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1648 gchar *tmp = decode_uri_gdup(value);
1649 for (; forbidden_uris[i]; i++) {
1650 if (strstr(tmp, forbidden_uris[i])) {
1651 g_print("Refusing to attach '%s', potential private data leak\n",
1659 /* attach is correct */
1661 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1662 my_att[num_attach-1] = tmp;
1663 my_att[num_attach] = NULL;
1665 } else if (inreplyto && !*inreplyto &&
1666 !g_ascii_strcasecmp(field, "in-reply-to")) {
1667 *inreplyto = decode_uri_gdup(value);
1678 #include <windows.h>
1679 #ifndef CSIDL_APPDATA
1680 #define CSIDL_APPDATA 0x001a
1682 #ifndef CSIDL_LOCAL_APPDATA
1683 #define CSIDL_LOCAL_APPDATA 0x001c
1685 #ifndef CSIDL_FLAG_CREATE
1686 #define CSIDL_FLAG_CREATE 0x8000
1688 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1692 w32_strerror (int w32_errno)
1694 static char strerr[256];
1695 int ec = (int)GetLastError ();
1699 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1700 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1701 strerr, DIM (strerr)-1, NULL);
1705 static __inline__ void *
1706 dlopen (const char * name, int flag)
1708 void * hd = LoadLibrary (name);
1712 static __inline__ void *
1713 dlsym (void * hd, const char * sym)
1717 void * fnc = GetProcAddress (hd, sym);
1726 static __inline__ const char *
1729 return w32_strerror (0);
1733 static __inline__ int
1745 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1747 static int initialized;
1748 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1752 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1758 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1760 handle = dlopen (dllnames[i], RTLD_LAZY);
1763 func = dlsym (handle, "SHGetFolderPathW");
1774 return func (a,b,c,d,e);
1779 /* Returns a static string with the directroy from which the module
1780 has been loaded. Returns an empty string on error. */
1781 static char *w32_get_module_dir(void)
1783 static char *moddir;
1786 char name[MAX_PATH+10];
1789 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1792 p = strrchr (name, '\\');
1798 moddir = g_strdup (name);
1802 #endif /* G_OS_WIN32 */
1804 /* Return a static string with the locale dir. */
1805 const gchar *get_locale_dir(void)
1807 static gchar *loc_dir;
1811 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1812 "\\share\\locale", NULL);
1815 loc_dir = LOCALEDIR;
1821 const gchar *get_home_dir(void)
1824 static char home_dir_utf16[MAX_PATH] = "";
1825 static gchar *home_dir_utf8 = NULL;
1826 if (home_dir_utf16[0] == '\0') {
1827 if (w32_shgetfolderpath
1828 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1829 NULL, 0, home_dir_utf16) < 0)
1830 strcpy (home_dir_utf16, "C:\\Sylpheed");
1831 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1833 return home_dir_utf8;
1835 static const gchar *homeenv = NULL;
1840 if (!homeenv && g_getenv("HOME") != NULL)
1841 homeenv = g_strdup(g_getenv("HOME"));
1843 homeenv = g_get_home_dir();
1849 static gchar *claws_rc_dir = NULL;
1850 static gboolean rc_dir_alt = FALSE;
1851 const gchar *get_rc_dir(void)
1855 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1858 return claws_rc_dir;
1861 void set_rc_dir(const gchar *dir)
1863 if (claws_rc_dir != NULL) {
1864 g_print("Error: rc_dir already set\n");
1867 if (g_path_is_absolute(dir))
1868 claws_rc_dir = g_strdup(dir);
1870 claws_rc_dir = g_strconcat(g_get_current_dir(),
1871 G_DIR_SEPARATOR_S, dir, NULL);
1873 debug_print("set rc_dir to %s\n", claws_rc_dir);
1874 if (!is_dir_exist(claws_rc_dir)) {
1875 if (make_dir_hier(claws_rc_dir) != 0) {
1876 g_print("Error: can't create %s\n",
1883 gboolean rc_dir_is_alt(void) {
1887 const gchar *get_mail_base_dir(void)
1889 return get_home_dir();
1893 const gchar *prefs_common_get_data_root(void);
1894 gchar *last_data_root = NULL;
1897 const gchar *get_news_cache_dir(void)
1899 static gchar *news_cache_dir = NULL;
1901 const gchar *data_root = prefs_common_get_data_root();
1902 if (strcmp2(data_root, last_data_root)) {
1903 g_free(news_cache_dir);
1904 news_cache_dir = NULL;
1907 if (!news_cache_dir)
1909 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1910 NEWS_CACHE_DIR, NULL);
1914 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1915 "Claws", G_DIR_SEPARATOR_S,
1916 g_get_user_name(), G_DIR_SEPARATOR_S,
1917 NEWS_CACHE_DIR, NULL);
1918 g_free(last_data_root);
1919 last_data_root = g_strdup(last_data_root);
1921 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1922 NEWS_CACHE_DIR, NULL);
1923 g_free(last_data_root);
1924 last_data_root = NULL;
1928 return news_cache_dir;
1931 const gchar *get_imap_cache_dir(void)
1933 static gchar *imap_cache_dir = NULL;
1935 const gchar *data_root = prefs_common_get_data_root();
1936 if (strcmp2(data_root, last_data_root)) {
1937 g_free(imap_cache_dir);
1938 imap_cache_dir = NULL;
1942 if (!imap_cache_dir)
1944 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1945 IMAP_CACHE_DIR, NULL);
1949 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1950 "Claws", G_DIR_SEPARATOR_S,
1951 g_get_user_name(), G_DIR_SEPARATOR_S,
1952 IMAP_CACHE_DIR, NULL);
1953 g_free(last_data_root);
1954 last_data_root = g_strdup(last_data_root);
1956 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1957 IMAP_CACHE_DIR, NULL);
1958 g_free(last_data_root);
1959 last_data_root = NULL;
1964 return imap_cache_dir;
1967 const gchar *get_mime_tmp_dir(void)
1969 static gchar *mime_tmp_dir = NULL;
1972 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1973 MIME_TMP_DIR, NULL);
1975 return mime_tmp_dir;
1978 const gchar *get_template_dir(void)
1980 static gchar *template_dir = NULL;
1983 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1984 TEMPLATE_DIR, NULL);
1986 return template_dir;
1990 const gchar *get_cert_file(void)
1992 const gchar *cert_file = NULL;
1994 cert_file = g_strconcat(w32_get_module_dir(),
1995 "\\share\\claws-mail\\",
1996 "ca-certificates.crt",
2002 /* Return the filepath of the claws-mail.desktop file */
2003 const gchar *get_desktop_file(void)
2005 #ifdef DESKTOPFILEPATH
2006 return DESKTOPFILEPATH;
2012 /* Return the default directory for Plugins. */
2013 const gchar *get_plugin_dir(void)
2016 static gchar *plugin_dir = NULL;
2019 plugin_dir = g_strconcat(w32_get_module_dir(),
2020 "\\lib\\claws-mail\\plugins\\",
2024 if (is_dir_exist(PLUGINDIR))
2027 static gchar *plugin_dir = NULL;
2029 plugin_dir = g_strconcat(get_rc_dir(),
2030 G_DIR_SEPARATOR_S, "plugins",
2031 G_DIR_SEPARATOR_S, NULL);
2039 /* Return the default directory for Themes. */
2040 const gchar *get_themes_dir(void)
2042 static gchar *themes_dir = NULL;
2045 themes_dir = g_strconcat(w32_get_module_dir(),
2046 "\\share\\claws-mail\\themes",
2052 const gchar *get_tmp_dir(void)
2054 static gchar *tmp_dir = NULL;
2057 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2063 gchar *get_tmp_file(void)
2066 static guint32 id = 0;
2068 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2069 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2074 const gchar *get_domain_name(void)
2077 static gchar *domain_name = NULL;
2083 if (gethostname(hostname, sizeof(hostname)) != 0) {
2084 perror("gethostname");
2085 domain_name = "unknown";
2087 hostname[sizeof(hostname) - 1] = '\0';
2088 if ((hp = my_gethostbyname(hostname)) == NULL) {
2089 perror("gethostbyname");
2090 domain_name = g_strdup(hostname);
2092 domain_name = g_strdup(hp->h_name);
2095 debug_print("domain name = %s\n", domain_name);
2104 off_t get_file_size(const gchar *file)
2108 if (g_stat(file, &s) < 0) {
2109 FILE_OP_ERROR(file, "stat");
2116 time_t get_file_mtime(const gchar *file)
2120 if (g_stat(file, &s) < 0) {
2121 FILE_OP_ERROR(file, "stat");
2128 off_t get_file_size_as_crlf(const gchar *file)
2132 gchar buf[BUFFSIZE];
2134 if ((fp = g_fopen(file, "rb")) == NULL) {
2135 FILE_OP_ERROR(file, "g_fopen");
2139 while (fgets(buf, sizeof(buf), fp) != NULL) {
2141 size += strlen(buf) + 2;
2145 FILE_OP_ERROR(file, "fgets");
2154 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2161 if (g_stat(file, &s) < 0) {
2162 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2166 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2173 /* Test on whether FILE is a relative file name. This is
2174 * straightforward for Unix but more complex for Windows. */
2175 gboolean is_relative_filename(const gchar *file)
2180 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2181 return FALSE; /* Prefixed with a hostname - this can't
2182 * be a relative name. */
2184 if ( ((*file >= 'a' && *file <= 'z')
2185 || (*file >= 'A' && *file <= 'Z'))
2187 file += 2; /* Skip drive letter. */
2189 return !(*file == '\\' || *file == '/');
2191 return !(*file == G_DIR_SEPARATOR);
2196 gboolean is_dir_exist(const gchar *dir)
2201 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2204 gboolean is_file_entry_exist(const gchar *file)
2209 return g_file_test(file, G_FILE_TEST_EXISTS);
2212 gboolean dirent_is_regular_file(struct dirent *d)
2214 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2215 if (d->d_type == DT_REG)
2217 else if (d->d_type != DT_UNKNOWN)
2221 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2224 gint change_dir(const gchar *dir)
2226 gchar *prevdir = NULL;
2229 prevdir = g_get_current_dir();
2231 if (g_chdir(dir) < 0) {
2232 FILE_OP_ERROR(dir, "chdir");
2233 if (debug_mode) g_free(prevdir);
2235 } else if (debug_mode) {
2238 cwd = g_get_current_dir();
2239 if (strcmp(prevdir, cwd) != 0)
2240 g_print("current dir: %s\n", cwd);
2248 gint make_dir(const gchar *dir)
2250 if (g_mkdir(dir, S_IRWXU) < 0) {
2251 FILE_OP_ERROR(dir, "mkdir");
2254 if (g_chmod(dir, S_IRWXU) < 0)
2255 FILE_OP_ERROR(dir, "chmod");
2260 gint make_dir_hier(const gchar *dir)
2265 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2266 parent_dir = g_strndup(dir, p - dir);
2267 if (*parent_dir != '\0') {
2268 if (!is_dir_exist(parent_dir)) {
2269 if (make_dir(parent_dir) < 0) {
2278 if (!is_dir_exist(dir)) {
2279 if (make_dir(dir) < 0)
2286 gint remove_all_files(const gchar *dir)
2289 const gchar *dir_name;
2292 prev_dir = g_get_current_dir();
2294 if (g_chdir(dir) < 0) {
2295 FILE_OP_ERROR(dir, "chdir");
2300 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2301 g_warning("failed to open directory: %s\n", dir);
2306 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2307 if (claws_unlink(dir_name) < 0)
2308 FILE_OP_ERROR(dir_name, "unlink");
2313 if (g_chdir(prev_dir) < 0) {
2314 FILE_OP_ERROR(prev_dir, "chdir");
2324 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2327 const gchar *dir_name;
2331 prev_dir = g_get_current_dir();
2333 if (g_chdir(dir) < 0) {
2334 FILE_OP_ERROR(dir, "chdir");
2339 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2340 g_warning("failed to open directory: %s\n", dir);
2345 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2346 file_no = to_number(dir_name);
2347 if (file_no > 0 && first <= file_no && file_no <= last) {
2348 if (is_dir_exist(dir_name))
2350 if (claws_unlink(dir_name) < 0)
2351 FILE_OP_ERROR(dir_name, "unlink");
2357 if (g_chdir(prev_dir) < 0) {
2358 FILE_OP_ERROR(prev_dir, "chdir");
2368 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2371 const gchar *dir_name;
2375 prev_dir = g_get_current_dir();
2377 if (g_chdir(dir) < 0) {
2378 FILE_OP_ERROR(dir, "chdir");
2383 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2384 FILE_OP_ERROR(dir, "opendir");
2389 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2390 file_no = to_number(dir_name);
2391 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2392 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2393 if (is_dir_exist(dir_name))
2395 if (claws_unlink(dir_name) < 0)
2396 FILE_OP_ERROR(dir_name, "unlink");
2402 if (g_chdir(prev_dir) < 0) {
2403 FILE_OP_ERROR(prev_dir, "chdir");
2413 gint remove_all_numbered_files(const gchar *dir)
2415 return remove_numbered_files(dir, 0, UINT_MAX);
2418 gint remove_dir_recursive(const gchar *dir)
2422 const gchar *dir_name;
2425 if (g_stat(dir, &s) < 0) {
2426 FILE_OP_ERROR(dir, "stat");
2427 if (ENOENT == errno) return 0;
2431 if (!S_ISDIR(s.st_mode)) {
2432 if (claws_unlink(dir) < 0) {
2433 FILE_OP_ERROR(dir, "unlink");
2440 prev_dir = g_get_current_dir();
2441 /* g_print("prev_dir = %s\n", prev_dir); */
2443 if (!path_cmp(prev_dir, dir)) {
2445 if (g_chdir("..") < 0) {
2446 FILE_OP_ERROR(dir, "chdir");
2449 prev_dir = g_get_current_dir();
2452 if (g_chdir(dir) < 0) {
2453 FILE_OP_ERROR(dir, "chdir");
2458 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2459 g_warning("failed to open directory: %s\n", dir);
2465 /* remove all files in the directory */
2466 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2467 /* g_print("removing %s\n", dir_name); */
2469 if (is_dir_exist(dir_name)) {
2470 if (remove_dir_recursive(dir_name) < 0) {
2471 g_warning("can't remove directory\n");
2475 if (claws_unlink(dir_name) < 0)
2476 FILE_OP_ERROR(dir_name, "unlink");
2482 if (g_chdir(prev_dir) < 0) {
2483 FILE_OP_ERROR(prev_dir, "chdir");
2490 if (g_rmdir(dir) < 0) {
2491 FILE_OP_ERROR(dir, "rmdir");
2498 gint rename_force(const gchar *oldpath, const gchar *newpath)
2501 if (!is_file_entry_exist(oldpath)) {
2505 if (is_file_exist(newpath)) {
2506 if (claws_unlink(newpath) < 0)
2507 FILE_OP_ERROR(newpath, "unlink");
2510 return g_rename(oldpath, newpath);
2514 * Append src file body to the tail of dest file.
2515 * Now keep_backup has no effects.
2517 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2519 FILE *src_fp, *dest_fp;
2523 gboolean err = FALSE;
2525 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2526 FILE_OP_ERROR(src, "g_fopen");
2530 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2531 FILE_OP_ERROR(dest, "g_fopen");
2536 if (change_file_mode_rw(dest_fp, dest) < 0) {
2537 FILE_OP_ERROR(dest, "chmod");
2538 g_warning("can't change file mode\n");
2541 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2542 if (n_read < sizeof(buf) && ferror(src_fp))
2544 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2545 g_warning("writing to %s failed.\n", dest);
2553 if (ferror(src_fp)) {
2554 FILE_OP_ERROR(src, "fread");
2558 if (fclose(dest_fp) == EOF) {
2559 FILE_OP_ERROR(dest, "fclose");
2571 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2573 FILE *src_fp, *dest_fp;
2576 gchar *dest_bak = NULL;
2577 gboolean err = FALSE;
2579 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2580 FILE_OP_ERROR(src, "g_fopen");
2583 if (is_file_exist(dest)) {
2584 dest_bak = g_strconcat(dest, ".bak", NULL);
2585 if (rename_force(dest, dest_bak) < 0) {
2586 FILE_OP_ERROR(dest, "rename");
2593 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2594 FILE_OP_ERROR(dest, "g_fopen");
2597 if (rename_force(dest_bak, dest) < 0)
2598 FILE_OP_ERROR(dest_bak, "rename");
2604 if (change_file_mode_rw(dest_fp, dest) < 0) {
2605 FILE_OP_ERROR(dest, "chmod");
2606 g_warning("can't change file mode\n");
2609 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2610 if (n_read < sizeof(buf) && ferror(src_fp))
2612 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2613 g_warning("writing to %s failed.\n", dest);
2618 if (rename_force(dest_bak, dest) < 0)
2619 FILE_OP_ERROR(dest_bak, "rename");
2626 if (ferror(src_fp)) {
2627 FILE_OP_ERROR(src, "fread");
2631 if (fclose(dest_fp) == EOF) {
2632 FILE_OP_ERROR(dest, "fclose");
2639 if (rename_force(dest_bak, dest) < 0)
2640 FILE_OP_ERROR(dest_bak, "rename");
2646 if (keep_backup == FALSE && dest_bak)
2647 claws_unlink(dest_bak);
2654 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2656 if (overwrite == FALSE && is_file_exist(dest)) {
2657 g_warning("move_file(): file %s already exists.", dest);
2661 if (rename_force(src, dest) == 0) return 0;
2663 if (EXDEV != errno) {
2664 FILE_OP_ERROR(src, "rename");
2668 if (copy_file(src, dest, FALSE) < 0) return -1;
2675 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2678 gint bytes_left, to_read;
2681 if (fseek(fp, offset, SEEK_SET) < 0) {
2686 bytes_left = length;
2687 to_read = MIN(bytes_left, sizeof(buf));
2689 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2690 if (n_read < to_read && ferror(fp))
2692 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2695 bytes_left -= n_read;
2696 if (bytes_left == 0)
2698 to_read = MIN(bytes_left, sizeof(buf));
2709 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2712 gboolean err = FALSE;
2714 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2715 FILE_OP_ERROR(dest, "g_fopen");
2719 if (change_file_mode_rw(dest_fp, dest) < 0) {
2720 FILE_OP_ERROR(dest, "chmod");
2721 g_warning("can't change file mode\n");
2724 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2727 if (!err && fclose(dest_fp) == EOF) {
2728 FILE_OP_ERROR(dest, "fclose");
2733 g_warning("writing to %s failed.\n", dest);
2741 /* convert line endings into CRLF. If the last line doesn't end with
2742 * linebreak, add it.
2744 gchar *canonicalize_str(const gchar *str)
2750 for (p = str; *p != '\0'; ++p) {
2757 if (p == str || *(p - 1) != '\n')
2760 out = outp = g_malloc(new_len + 1);
2761 for (p = str; *p != '\0'; ++p) {
2768 if (p == str || *(p - 1) != '\n') {
2777 gint canonicalize_file(const gchar *src, const gchar *dest)
2779 FILE *src_fp, *dest_fp;
2780 gchar buf[BUFFSIZE];
2782 gboolean err = FALSE;
2783 gboolean last_linebreak = FALSE;
2785 if (src == NULL || dest == NULL)
2788 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2789 FILE_OP_ERROR(src, "g_fopen");
2793 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2794 FILE_OP_ERROR(dest, "g_fopen");
2799 if (change_file_mode_rw(dest_fp, dest) < 0) {
2800 FILE_OP_ERROR(dest, "chmod");
2801 g_warning("can't change file mode\n");
2804 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2808 if (len == 0) break;
2809 last_linebreak = FALSE;
2811 if (buf[len - 1] != '\n') {
2812 last_linebreak = TRUE;
2813 r = fputs(buf, dest_fp);
2814 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2815 r = fputs(buf, dest_fp);
2818 r = fwrite(buf, 1, len - 1, dest_fp);
2823 r = fputs("\r\n", dest_fp);
2827 g_warning("writing to %s failed.\n", dest);
2835 if (last_linebreak == TRUE) {
2836 if (fputs("\r\n", dest_fp) == EOF)
2840 if (ferror(src_fp)) {
2841 FILE_OP_ERROR(src, "fgets");
2845 if (fclose(dest_fp) == EOF) {
2846 FILE_OP_ERROR(dest, "fclose");
2858 gint canonicalize_file_replace(const gchar *file)
2862 tmp_file = get_tmp_file();
2864 if (canonicalize_file(file, tmp_file) < 0) {
2869 if (move_file(tmp_file, file, TRUE) < 0) {
2870 g_warning("can't replace %s .\n", file);
2871 claws_unlink(tmp_file);
2880 gchar *normalize_newlines(const gchar *str)
2882 const gchar *p = str;
2885 out = outp = g_malloc(strlen(str) + 1);
2886 for (p = str; *p != '\0'; ++p) {
2888 if (*(p + 1) != '\n')
2899 gchar *get_outgoing_rfc2822_str(FILE *fp)
2901 gchar buf[BUFFSIZE];
2905 str = g_string_new(NULL);
2907 /* output header part */
2908 while (fgets(buf, sizeof(buf), fp) != NULL) {
2910 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2917 else if (next != ' ' && next != '\t') {
2921 if (fgets(buf, sizeof(buf), fp) == NULL)
2925 g_string_append(str, buf);
2926 g_string_append(str, "\r\n");
2932 /* output body part */
2933 while (fgets(buf, sizeof(buf), fp) != NULL) {
2936 g_string_append_c(str, '.');
2937 g_string_append(str, buf);
2938 g_string_append(str, "\r\n");
2942 g_string_free(str, FALSE);
2948 * Create a new boundary in a way that it is very unlikely that this
2949 * will occur in the following text. It would be easy to ensure
2950 * uniqueness if everything is either quoted-printable or base64
2951 * encoded (note that conversion is allowed), but because MIME bodies
2952 * may be nested, it may happen that the same boundary has already
2955 * boundary := 0*69<bchars> bcharsnospace
2956 * bchars := bcharsnospace / " "
2957 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2958 * "+" / "_" / "," / "-" / "." /
2959 * "/" / ":" / "=" / "?"
2961 * some special characters removed because of buggy MTAs
2964 gchar *generate_mime_boundary(const gchar *prefix)
2966 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2967 "abcdefghijklmnopqrstuvwxyz"
2972 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2973 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2976 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2980 gint change_file_mode_rw(FILE *fp, const gchar *file)
2983 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2985 return g_chmod(file, S_IRUSR|S_IWUSR);
2989 FILE *my_tmpfile(void)
2991 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2992 const gchar suffix[] = ".XXXXXX";
2993 const gchar *tmpdir;
2995 const gchar *progname;
3004 tmpdir = get_tmp_dir();
3005 tmplen = strlen(tmpdir);
3006 progname = g_get_prgname();
3007 if (progname == NULL)
3008 progname = "claws-mail";
3009 proglen = strlen(progname);
3010 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3013 memcpy(fname, tmpdir, tmplen);
3014 fname[tmplen] = G_DIR_SEPARATOR;
3015 memcpy(fname + tmplen + 1, progname, proglen);
3016 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3018 fd = mkstemp(fname);
3023 claws_unlink(fname);
3025 /* verify that we can write in the file after unlinking */
3026 if (write(fd, buf, 1) < 0) {
3033 fp = fdopen(fd, "w+b");
3041 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3046 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3050 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3051 dir, G_DIR_SEPARATOR);
3052 fd = mkstemp_name(template, filename);
3055 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3056 fd = mkstemp(*filename);
3058 return fdopen(fd, "w+");
3061 FILE *str_open_as_stream(const gchar *str)
3066 cm_return_val_if_fail(str != NULL, NULL);
3070 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3075 if (len == 0) return fp;
3077 if (fwrite(str, 1, len, fp) != len) {
3078 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3087 gint str_write_to_file(const gchar *str, const gchar *file)
3092 cm_return_val_if_fail(str != NULL, -1);
3093 cm_return_val_if_fail(file != NULL, -1);
3095 if ((fp = g_fopen(file, "wb")) == NULL) {
3096 FILE_OP_ERROR(file, "g_fopen");
3106 if (fwrite(str, 1, len, fp) != len) {
3107 FILE_OP_ERROR(file, "fwrite");
3113 if (fclose(fp) == EOF) {
3114 FILE_OP_ERROR(file, "fclose");
3122 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3129 cm_return_val_if_fail(fp != NULL, NULL);
3131 array = g_byte_array_new();
3133 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3134 if (n_read < sizeof(buf) && ferror(fp))
3136 g_byte_array_append(array, buf, n_read);
3140 FILE_OP_ERROR("file stream", "fread");
3141 g_byte_array_free(array, TRUE);
3146 g_byte_array_append(array, buf, 1);
3147 str = (gchar *)array->data;
3148 g_byte_array_free(array, FALSE);
3150 if (recode && !g_utf8_validate(str, -1, NULL)) {
3151 const gchar *src_codeset, *dest_codeset;
3153 src_codeset = conv_get_locale_charset_str();
3154 dest_codeset = CS_UTF_8;
3155 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3163 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3170 struct timeval timeout = {1, 0};
3175 cm_return_val_if_fail(file != NULL, NULL);
3177 if (g_stat(file, &s) != 0) {
3178 FILE_OP_ERROR(file, "stat");
3181 if (S_ISDIR(s.st_mode)) {
3182 g_warning("%s: is a directory\n", file);
3187 fp = g_fopen (file, "rb");
3189 FILE_OP_ERROR(file, "open");
3193 /* test whether the file is readable without blocking */
3194 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3196 FILE_OP_ERROR(file, "open");
3203 /* allow for one second */
3204 err = select(fd+1, &fds, NULL, NULL, &timeout);
3205 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3207 FILE_OP_ERROR(file, "select");
3209 g_warning("%s: doesn't seem readable\n", file);
3215 /* Now clear O_NONBLOCK */
3216 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3217 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3221 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3222 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3227 /* get the FILE pointer */
3228 fp = fdopen(fd, "rb");
3231 FILE_OP_ERROR(file, "fdopen");
3232 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3237 str = file_read_stream_to_str_full(fp, recode);
3244 gchar *file_read_to_str(const gchar *file)
3246 return file_read_to_str_full(file, TRUE);
3248 gchar *file_read_stream_to_str(FILE *fp)
3250 return file_read_stream_to_str_full(fp, TRUE);
3253 gchar *file_read_to_str_no_recode(const gchar *file)
3255 return file_read_to_str_full(file, FALSE);
3257 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3259 return file_read_stream_to_str_full(fp, FALSE);
3262 char *fgets_crlf(char *buf, int size, FILE *stream)
3264 gboolean is_cr = FALSE;
3265 gboolean last_was_cr = FALSE;
3270 while (--size > 0 && (c = getc(stream)) != EOF)
3273 is_cr = (c == '\r');
3283 last_was_cr = is_cr;
3285 if (c == EOF && cs == buf)
3293 static gint execute_async(gchar *const argv[])
3295 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3297 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3298 NULL, NULL, NULL, FALSE) == FALSE) {
3299 g_warning("Couldn't execute command: %s\n", argv[0]);
3306 static gint execute_sync(gchar *const argv[])
3310 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3313 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3314 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3315 g_warning("Couldn't execute command: %s\n", argv[0]);
3319 if (WIFEXITED(status))
3320 return WEXITSTATUS(status);
3324 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3325 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3326 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3327 g_warning("Couldn't execute command: %s\n", argv[0]);
3335 gint execute_command_line(const gchar *cmdline, gboolean async)
3340 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3342 argv = strsplit_with_quote(cmdline, " ", 0);
3345 ret = execute_async(argv);
3347 ret = execute_sync(argv);
3354 gchar *get_command_output(const gchar *cmdline)
3356 gchar *child_stdout;
3359 cm_return_val_if_fail(cmdline != NULL, NULL);
3361 debug_print("get_command_output(): executing: %s\n", cmdline);
3363 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3365 g_warning("Couldn't execute command: %s\n", cmdline);
3369 return child_stdout;
3372 static gint is_unchanged_uri_char(char c)
3383 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3389 for(i = 0; i < strlen(uri) ; i++) {
3390 if (is_unchanged_uri_char(uri[i])) {
3391 if (k + 2 >= bufsize)
3393 encoded_uri[k++] = uri[i];
3396 char * hexa = "0123456789ABCDEF";
3398 if (k + 4 >= bufsize)
3400 encoded_uri[k++] = '%';
3401 encoded_uri[k++] = hexa[uri[i] / 16];
3402 encoded_uri[k++] = hexa[uri[i] % 16];
3408 gint open_uri(const gchar *uri, const gchar *cmdline)
3412 gchar buf[BUFFSIZE];
3414 gchar encoded_uri[BUFFSIZE];
3415 cm_return_val_if_fail(uri != NULL, -1);
3417 /* an option to choose whether to use encode_uri or not ? */
3418 encode_uri(encoded_uri, BUFFSIZE, uri);
3421 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3422 !strchr(p + 2, '%'))
3423 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3426 g_warning("Open URI command-line is invalid "
3427 "(there must be only one '%%s'): %s",
3429 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3432 execute_command_line(buf, TRUE);
3434 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3437 extern osso_context_t *get_osso_context(void);
3438 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3439 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3440 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3445 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3447 gchar buf[BUFFSIZE];
3450 cm_return_val_if_fail(filepath != NULL, -1);
3453 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3454 !strchr(p + 2, '%'))
3455 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3458 g_warning("Open Text Editor command-line is invalid "
3459 "(there must be only one '%%s'): %s",
3461 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3464 execute_command_line(buf, TRUE);
3469 time_t remote_tzoffset_sec(const gchar *zone)
3471 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3477 time_t remoteoffset;
3479 strncpy(zone3, zone, 3);
3483 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3484 (c == '+' || c == '-')) {
3485 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3487 remoteoffset = -remoteoffset;
3488 } else if (!strncmp(zone, "UT" , 2) ||
3489 !strncmp(zone, "GMT", 2)) {
3491 } else if (strlen(zone3) == 3) {
3492 for (p = ustzstr; *p != '\0'; p += 3) {
3493 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3494 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3495 remoteoffset = iustz * 3600;
3501 } else if (strlen(zone3) == 1) {
3503 case 'Z': remoteoffset = 0; break;
3504 case 'A': remoteoffset = -1; break;
3505 case 'B': remoteoffset = -2; break;
3506 case 'C': remoteoffset = -3; break;
3507 case 'D': remoteoffset = -4; break;
3508 case 'E': remoteoffset = -5; break;
3509 case 'F': remoteoffset = -6; break;
3510 case 'G': remoteoffset = -7; break;
3511 case 'H': remoteoffset = -8; break;
3512 case 'I': remoteoffset = -9; break;
3513 case 'K': remoteoffset = -10; break; /* J is not used */
3514 case 'L': remoteoffset = -11; break;
3515 case 'M': remoteoffset = -12; break;
3516 case 'N': remoteoffset = 1; break;
3517 case 'O': remoteoffset = 2; break;
3518 case 'P': remoteoffset = 3; break;
3519 case 'Q': remoteoffset = 4; break;
3520 case 'R': remoteoffset = 5; break;
3521 case 'S': remoteoffset = 6; break;
3522 case 'T': remoteoffset = 7; break;
3523 case 'U': remoteoffset = 8; break;
3524 case 'V': remoteoffset = 9; break;
3525 case 'W': remoteoffset = 10; break;
3526 case 'X': remoteoffset = 11; break;
3527 case 'Y': remoteoffset = 12; break;
3528 default: remoteoffset = 0; break;
3530 remoteoffset = remoteoffset * 3600;
3534 return remoteoffset;
3537 time_t tzoffset_sec(time_t *now)
3542 struct tm buf1, buf2;
3545 if (now && *now < 0)
3548 gmt = *gmtime_r(now, &buf1);
3549 lt = localtime_r(now, &buf2);
3551 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3553 if (lt->tm_year < gmt.tm_year)
3555 else if (lt->tm_year > gmt.tm_year)
3557 else if (lt->tm_yday < gmt.tm_yday)
3559 else if (lt->tm_yday > gmt.tm_yday)
3562 if (off >= 24 * 60) /* should be impossible */
3563 off = 23 * 60 + 59; /* if not, insert silly value */
3564 if (off <= -24 * 60)
3565 off = -(23 * 60 + 59);
3570 /* calculate timezone offset */
3571 gchar *tzoffset(time_t *now)
3573 static gchar offset_string[6];
3578 struct tm buf1, buf2;
3581 if (now && *now < 0)
3584 gmt = *gmtime_r(now, &buf1);
3585 lt = localtime_r(now, &buf2);
3587 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3589 if (lt->tm_year < gmt.tm_year)
3591 else if (lt->tm_year > gmt.tm_year)
3593 else if (lt->tm_yday < gmt.tm_yday)
3595 else if (lt->tm_yday > gmt.tm_yday)
3603 if (off >= 24 * 60) /* should be impossible */
3604 off = 23 * 60 + 59; /* if not, insert silly value */
3606 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3608 return offset_string;
3611 void get_rfc822_date(gchar *buf, gint len)
3615 gchar day[4], mon[4];
3616 gint dd, hh, mm, ss, yyyy;
3619 gchar buf2[BUFFSIZE];
3623 lt = localtime_r(&t, &buf1);
3625 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3626 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3628 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3629 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3632 void debug_set_mode(gboolean mode)
3637 gboolean debug_get_mode(void)
3642 void debug_print_real(const gchar *format, ...)
3645 gchar buf[BUFFSIZE];
3647 if (!debug_mode) return;
3649 va_start(args, format);
3650 g_vsnprintf(buf, sizeof(buf), format, args);
3657 const char * debug_srcname(const char *file)
3659 const char *s = strrchr (file, '/');
3664 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3666 if (subject == NULL)
3669 subject += subject_get_prefix_length(subject);
3671 return g_hash_table_lookup(subject_table, subject);
3674 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3677 if (subject == NULL || *subject == 0)
3679 subject += subject_get_prefix_length(subject);
3680 g_hash_table_insert(subject_table, subject, data);
3683 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3685 if (subject == NULL)
3688 subject += subject_get_prefix_length(subject);
3689 g_hash_table_remove(subject_table, subject);
3693 static regex_t u_regex;
3694 static gboolean u_init_;
3697 void utils_free_regex(void)
3708 *\brief Check if a string is prefixed with known (combinations)
3709 * of prefixes. The function assumes that each prefix
3710 * is terminated by zero or exactly _one_ space.
3712 *\param str String to check for a prefixes
3714 *\return int Number of chars in the prefix that should be skipped
3715 * for a "clean" subject line. If no prefix was found, 0
3718 int subject_get_prefix_length(const gchar *subject)
3721 /*!< Array with allowable reply prefixes regexps. */
3722 static const gchar * const prefixes[] = {
3723 "Re\\:", /* "Re:" */
3724 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3725 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3726 "Aw\\:", /* "Aw:" (German) */
3727 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3728 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3729 "Fw\\:", /* "Fw:" Forward */
3730 "Fwd\\:", /* "Fwd:" Forward */
3731 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3732 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3733 "Rif\\:", /* "Rif:" (Italian Outlook) */
3734 "Sv\\:", /* "Sv" (Norwegian) */
3735 "Vs\\:", /* "Vs" (Norwegian) */
3736 "Ad\\:", /* "Ad" (Norwegian) */
3737 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3738 "R\303\251f\\. \\:", /* "Réf. :" (French Lotus Notes) */
3739 "Re \\:", /* "Re :" (French Yahoo Mail) */
3742 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3746 if (!subject) return 0;
3747 if (!*subject) return 0;
3750 GString *s = g_string_new("");
3752 for (n = 0; n < PREFIXES; n++)
3753 /* Terminate each prefix regexpression by a
3754 * "\ ?" (zero or ONE space), and OR them */
3755 g_string_append_printf(s, "(%s\\ ?)%s",
3760 g_string_prepend(s, "(");
3761 g_string_append(s, ")+"); /* match at least once */
3762 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3765 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3766 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3767 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3768 debug_print("Error compiling regexp %s\n", s->str);
3769 g_string_free(s, TRUE);
3773 g_string_free(s, TRUE);
3777 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3782 /*!< Array with allowable reply prefixes regexps. */
3783 static const gchar * const prefixes[] = {
3785 "antw:", /* "Antw:" (Dutch / German Outlook) */
3786 "aw:", /* "Aw:" (German) */
3787 "antwort:", /* "Antwort:" (German Lotus Notes) */
3788 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3789 "fw:", /* "Fw:" Forward */
3790 "fwd:", /* "Fwd:" Forward */
3791 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3792 "odp:", /* "Odp:" Re (Polish Outlook) */
3793 "rif:", /* "Rif:" (Italian Outlook) */
3794 "sv:", /* "Sv" (Norwegian) */
3795 "vs:", /* "Vs" (Norwegian) */
3796 "ad:", /* "Ad" (Norwegian) */
3797 "R\303\251f. :", /* "Réf. :" (French Lotus Notes) */
3798 "Re :", /* "Re :" (French Yahoo Mail) */
3801 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3804 if (!subject) return 0;
3805 if (!*subject) return 0;
3807 for (n = 0; n < PREFIXES; n++) {
3808 int len = strlen(prefixes[n]);
3809 if (!strncasecmp(subject, prefixes[n], len)) {
3810 if (subject[len] == ' ')
3819 static guint g_stricase_hash(gconstpointer gptr)
3821 guint hash_result = 0;
3824 for (str = gptr; str && *str; str++) {
3825 hash_result += toupper(*str);
3831 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3833 const char *str1 = gptr1;
3834 const char *str2 = gptr2;
3836 return !strcasecmp(str1, str2);
3839 gint g_int_compare(gconstpointer a, gconstpointer b)
3841 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3844 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3854 lt = localtime_r(&t, &buft);
3856 if (strcmp(buf, "") == 0) {
3857 if (user_addr != NULL) {
3858 addr = g_strconcat(user_addr, "@", get_domain_name(), NULL);
3861 addr = g_strconcat("@", get_domain_name(), NULL);
3865 if (user_addr != NULL) {
3866 addr = g_strconcat(user_addr, "@", buf, NULL);
3869 addr = g_strconcat("@", buf, NULL);
3873 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3874 lt->tm_year + 1900, lt->tm_mon + 1,
3875 lt->tm_mday, lt->tm_hour,
3876 lt->tm_min, lt->tm_sec,
3877 (guint) rand(), addr);
3884 quote_cmd_argument()
3886 return a quoted string safely usable in argument of a command.
3888 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3891 gint quote_cmd_argument(gchar * result, guint size,
3901 for(p = path ; * p != '\0' ; p ++) {
3903 if (isalnum((guchar)*p) || (* p == '/')) {
3904 if (remaining > 0) {
3910 result[size - 1] = '\0';
3915 if (remaining >= 2) {
3923 result[size - 1] = '\0';
3928 if (remaining > 0) {
3932 result[size - 1] = '\0';
3946 static void g_node_map_recursive(GNode *node, gpointer data)
3948 GNodeMapData *mapdata = (GNodeMapData *) data;
3950 GNodeMapData newmapdata;
3953 newdata = mapdata->func(node->data, mapdata->data);
3954 if (newdata != NULL) {
3955 newnode = g_node_new(newdata);
3956 g_node_append(mapdata->parent, newnode);
3958 newmapdata.parent = newnode;
3959 newmapdata.func = mapdata->func;
3960 newmapdata.data = mapdata->data;
3962 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3966 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3969 GNodeMapData mapdata;
3971 cm_return_val_if_fail(node != NULL, NULL);
3972 cm_return_val_if_fail(func != NULL, NULL);
3974 root = g_node_new(func(node->data, data));
3976 mapdata.parent = root;
3977 mapdata.func = func;
3978 mapdata.data = data;
3980 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3985 #define HEX_TO_INT(val, hex) \
3989 if ('0' <= c && c <= '9') { \
3991 } else if ('a' <= c && c <= 'f') { \
3992 val = c - 'a' + 10; \
3993 } else if ('A' <= c && c <= 'F') { \
3994 val = c - 'A' + 10; \
4000 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4007 if (hi == -1 || lo == -1)
4010 *out = (hi << 4) + lo;
4014 #define INT_TO_HEX(hex, val) \
4017 hex = '0' + (val); \
4019 hex = 'A' + (val) - 10; \
4022 void get_hex_str(gchar *out, guchar ch)
4026 INT_TO_HEX(hex, ch >> 4);
4028 INT_TO_HEX(hex, ch & 0x0f);
4034 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4036 #define G_PRINT_REF g_print
4040 *\brief Register ref counted pointer. It is based on GBoxed, so should
4041 * work with anything that uses the GType system. The semantics
4042 * are similar to a C++ auto pointer, with the exception that
4043 * C doesn't have automatic closure (calling destructors) when
4044 * exiting a block scope.
4045 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4046 * function directly.
4048 *\return GType A GType type.
4050 GType g_auto_pointer_register(void)
4052 static GType auto_pointer_type;
4053 if (!auto_pointer_type)
4055 g_boxed_type_register_static
4056 ("G_TYPE_AUTO_POINTER",
4057 (GBoxedCopyFunc) g_auto_pointer_copy,
4058 (GBoxedFreeFunc) g_auto_pointer_free);
4059 return auto_pointer_type;
4063 *\brief Structure with g_new() allocated pointer guarded by the
4066 typedef struct AutoPointerRef {
4067 void (*free) (gpointer);
4073 *\brief The auto pointer opaque structure that references the
4074 * pointer guard block.
4076 typedef struct AutoPointer {
4077 AutoPointerRef *ref;
4078 gpointer ptr; /*!< access to protected pointer */
4082 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4086 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4087 * ... when assigning, copying and freeing storage elements
4089 * gtk_list_store_new(N_S_COLUMNS,
4090 * G_TYPE_AUTO_POINTER,
4094 * Template *precious_data = g_new0(Template, 1);
4095 * g_pointer protect = g_auto_pointer_new(precious_data);
4097 * gtk_list_store_set(container, &iter,
4101 * ... the gtk_list_store has copied the pointer and
4102 * ... incremented its reference count, we should free
4103 * ... the auto pointer (in C++ a destructor would do
4104 * ... this for us when leaving block scope)
4106 * g_auto_pointer_free(protect);
4108 * ... gtk_list_store_set() now manages the data. When
4109 * ... *explicitly* requesting a pointer from the list
4110 * ... store, don't forget you get a copy that should be
4111 * ... freed with g_auto_pointer_free() eventually.
4115 *\param pointer Pointer to be guarded.
4117 *\return GAuto * Pointer that should be used in containers with
4120 GAuto *g_auto_pointer_new(gpointer p)
4122 AutoPointerRef *ref;
4128 ref = g_new0(AutoPointerRef, 1);
4129 ptr = g_new0(AutoPointer, 1);
4139 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4145 *\brief Allocate an autopointer using the passed \a free function to
4146 * free the guarded pointer
4148 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4155 aptr = g_auto_pointer_new(p);
4156 aptr->ref->free = free_;
4160 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4162 if (auto_ptr == NULL)
4164 return ((AutoPointer *) auto_ptr)->ptr;
4168 *\brief Copies an auto pointer by. It's mostly not necessary
4169 * to call this function directly, unless you copy/assign
4170 * the guarded pointer.
4172 *\param auto_ptr Auto pointer returned by previous call to
4173 * g_auto_pointer_new_XXX()
4175 *\return gpointer An auto pointer
4177 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4180 AutoPointerRef *ref;
4183 if (auto_ptr == NULL)
4188 newp = g_new0(AutoPointer, 1);
4191 newp->ptr = ref->pointer;
4195 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4201 *\brief Free an auto pointer
4203 void g_auto_pointer_free(GAuto *auto_ptr)
4206 AutoPointerRef *ref;
4208 if (auto_ptr == NULL)
4214 if (--(ref->cnt) == 0) {
4216 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4218 ref->free(ref->pointer);
4223 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4228 void replace_returns(gchar *str)
4233 while (strstr(str, "\n")) {
4234 *strstr(str, "\n") = ' ';
4236 while (strstr(str, "\r")) {
4237 *strstr(str, "\r") = ' ';
4241 /* get_uri_part() - retrieves a URI starting from scanpos.
4242 Returns TRUE if succesful */
4243 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4244 const gchar **bp, const gchar **ep, gboolean hdr)
4247 gint parenthese_cnt = 0;
4249 cm_return_val_if_fail(start != NULL, FALSE);
4250 cm_return_val_if_fail(scanpos != NULL, FALSE);
4251 cm_return_val_if_fail(bp != NULL, FALSE);
4252 cm_return_val_if_fail(ep != NULL, FALSE);
4256 /* find end point of URI */
4257 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4258 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4259 !IS_ASCII(*(const guchar *)ep_) ||
4260 strchr("[]{}<>\"", *ep_)) {
4262 } else if (strchr("(", *ep_)) {
4264 } else if (strchr(")", *ep_)) {
4265 if (parenthese_cnt > 0)
4272 /* no punctuation at end of string */
4274 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4275 * should pass some URI type to this function and decide on that whether
4276 * to perform punctuation stripping */
4278 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4280 for (; ep_ - 1 > scanpos + 1 &&
4281 IS_REAL_PUNCT(*(ep_ - 1));
4285 #undef IS_REAL_PUNCT
4292 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4294 while (bp && *bp && g_ascii_isspace(*bp))
4296 return g_strndup(bp, ep - bp);
4299 /* valid mail address characters */
4300 #define IS_RFC822_CHAR(ch) \
4304 !g_ascii_isspace(ch) && \
4305 !strchr("(),;<>\"", (ch)))
4307 /* alphabet and number within 7bit ASCII */
4308 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4309 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4311 static GHashTable *create_domain_tab(void)
4313 static const gchar *toplvl_domains [] = {
4315 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4316 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4317 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4318 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4319 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4320 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4321 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4322 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4323 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4324 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4325 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4326 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4327 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4328 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4329 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4330 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4331 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4332 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4333 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4334 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4335 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4336 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4337 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4338 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4339 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4340 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4343 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4345 cm_return_val_if_fail(htab, NULL);
4346 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4347 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4351 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4353 const gint MAX_LVL_DOM_NAME_LEN = 6;
4354 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4355 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4358 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4361 for (p = buf; p < m && first < last; *p++ = *first++)
4365 return g_hash_table_lookup(tab, buf) != NULL;
4368 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4369 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4370 const gchar **bp, const gchar **ep, gboolean hdr)
4372 /* more complex than the uri part because we need to scan back and forward starting from
4373 * the scan position. */
4374 gboolean result = FALSE;
4375 const gchar *bp_ = NULL;
4376 const gchar *ep_ = NULL;
4377 static GHashTable *dom_tab;
4378 const gchar *last_dot = NULL;
4379 const gchar *prelast_dot = NULL;
4380 const gchar *last_tld_char = NULL;
4382 /* the informative part of the email address (describing the name
4383 * of the email address owner) may contain quoted parts. the
4384 * closure stack stores the last encountered quotes. */
4385 gchar closure_stack[128];
4386 gchar *ptr = closure_stack;
4388 cm_return_val_if_fail(start != NULL, FALSE);
4389 cm_return_val_if_fail(scanpos != NULL, FALSE);
4390 cm_return_val_if_fail(bp != NULL, FALSE);
4391 cm_return_val_if_fail(ep != NULL, FALSE);
4394 const gchar *start_quote = NULL;
4395 const gchar *end_quote = NULL;
4397 /* go to the real start */
4398 if (start[0] == ',')
4400 if (start[0] == ';')
4402 while (start[0] == '\n' || start[0] == '\r')
4404 while (start[0] == ' ' || start[0] == '\t')
4409 /* check if there are quotes (to skip , in them) */
4410 if (*start == '"') {
4411 start_quote = start;
4413 end_quote = strstr(start, "\"");
4419 /* skip anything between quotes */
4420 if (start_quote && end_quote) {
4425 /* find end (either , or ; or end of line) */
4426 if (strstr(start, ",") && strstr(start, ";"))
4427 *ep = strstr(start,",") < strstr(start, ";")
4428 ? strstr(start, ",") : strstr(start, ";");
4429 else if (strstr(start, ","))
4430 *ep = strstr(start, ",");
4431 else if (strstr(start, ";"))
4432 *ep = strstr(start, ";");
4434 *ep = start+strlen(start);
4436 /* go back to real start */
4437 if (start_quote && end_quote) {
4438 start = start_quote;
4441 /* check there's still an @ in that, or search
4442 * further if possible */
4443 if (strstr(start, "@") && strstr(start, "@") < *ep)
4445 else if (*ep < start+strlen(start)) {
4448 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4456 dom_tab = create_domain_tab();
4457 cm_return_val_if_fail(dom_tab, FALSE);
4459 /* scan start of address */
4460 for (bp_ = scanpos - 1;
4461 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4464 /* TODO: should start with an alnum? */
4466 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4469 if (bp_ != scanpos) {
4470 /* scan end of address */
4471 for (ep_ = scanpos + 1;
4472 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4474 prelast_dot = last_dot;
4476 if (*(last_dot + 1) == '.') {
4477 if (prelast_dot == NULL)
4479 last_dot = prelast_dot;
4484 /* TODO: really should terminate with an alnum? */
4485 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4490 if (last_dot == NULL)
4492 if (last_dot >= ep_)
4493 last_dot = prelast_dot;
4494 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4498 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4499 if (*last_tld_char == '?')
4502 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4509 if (!result) return FALSE;
4511 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4512 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4513 && IS_RFC822_CHAR(*(ep_ + 3))) {
4514 /* this informative part with an @ in it is
4515 * followed by the email address */
4518 /* go to matching '>' (or next non-rfc822 char, like \n) */
4519 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4522 /* include the bracket */
4523 if (*ep_ == '>') ep_++;
4525 /* include the leading quote */
4533 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4534 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4537 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4538 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4541 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4542 #define IN_STACK() (ptr > closure_stack)
4543 /* has underrun check */
4544 #define POP_STACK() if(IN_STACK()) --ptr
4545 /* has overrun check */
4546 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4547 /* has underrun check */
4548 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4552 /* scan for the informative part. */
4553 for (bp_ -= 2; bp_ >= start; bp_--) {
4554 /* if closure on the stack keep scanning */
4555 if (PEEK_STACK() == *bp_) {
4559 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4564 /* if nothing in the closure stack, do the special conditions
4565 * the following if..else expression simply checks whether
4566 * a token is acceptable. if not acceptable, the clause
4567 * should terminate the loop with a 'break' */
4568 if (!PEEK_STACK()) {
4570 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4571 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4572 /* hyphens are allowed, but only in
4574 } else if (strchr(" \"'", *bp_)) {
4575 /* but anything not being a punctiation
4578 break; /* anything else is rejected */
4585 /* scan forward (should start with an alnum) */
4586 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4602 #undef IS_ASCII_ALNUM
4603 #undef IS_RFC822_CHAR
4605 gchar *make_email_string(const gchar *bp, const gchar *ep)
4607 /* returns a mailto: URI; mailto: is also used to detect the
4608 * uri type later on in the button_pressed signal handler */
4612 tmp = g_strndup(bp, ep - bp);
4613 result = g_strconcat("mailto:", tmp, NULL);
4619 gchar *make_http_string(const gchar *bp, const gchar *ep)
4621 /* returns an http: URI; */
4625 while (bp && *bp && g_ascii_isspace(*bp))
4627 tmp = g_strndup(bp, ep - bp);
4628 result = g_strconcat("http://", tmp, NULL);
4634 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4636 FILE *fp = g_fopen(path, "rb");
4637 gchar buf[BUFFSIZE];
4638 gchar *result = NULL;
4641 while (fgets(buf, sizeof (buf), fp) != NULL) {
4642 gchar **parts = g_strsplit(buf, ";", 3);
4643 gchar *trimmed = parts[0];
4644 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4646 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4647 trimmed[strlen(trimmed)-1] = '\0';
4649 if (!strcmp(trimmed, type)) {
4650 gboolean needsterminal = FALSE;
4651 if (parts[2] && strstr(parts[2], "needsterminal")) {
4652 needsterminal = TRUE;
4654 if (parts[2] && strstr(parts[2], "test=")) {
4655 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4656 gchar *testcmd = orig_testcmd;
4657 if (strstr(testcmd,";"))
4658 *(strstr(testcmd,";")) = '\0';
4659 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4661 while (testcmd[strlen(testcmd)-1] == '\n')
4662 testcmd[strlen(testcmd)-1] = '\0';
4663 while (testcmd[strlen(testcmd)-1] == '\r')
4664 testcmd[strlen(testcmd)-1] = '\0';
4665 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4666 testcmd[strlen(testcmd)-1] = '\0';
4668 if (strstr(testcmd, "%s")) {
4669 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4670 gint res = system(tmp);
4672 g_free(orig_testcmd);
4679 gint res = system(testcmd);
4680 g_free(orig_testcmd);
4690 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4692 while (trimmed[strlen(trimmed)-1] == '\n')
4693 trimmed[strlen(trimmed)-1] = '\0';
4694 while (trimmed[strlen(trimmed)-1] == '\r')
4695 trimmed[strlen(trimmed)-1] = '\0';
4696 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4697 trimmed[strlen(trimmed)-1] = '\0';
4698 result = g_strdup(trimmed);
4701 /* if there are no single quotes around %s, add them.
4702 * '.*%s.*' is ok, as in display 'png:%s'
4704 if (strstr(result, "%s")
4705 && !(strstr(result, "'") < strstr(result,"%s") &&
4706 strstr(strstr(result,"%s"), "'"))) {
4707 gchar *start = g_strdup(result);
4708 gchar *end = g_strdup(strstr(result, "%s")+2);
4710 *strstr(start, "%s") = '\0';
4711 tmp = g_strconcat(start,"'%s'",end, NULL);
4717 if (needsterminal) {
4718 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4729 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4731 gchar *result = NULL;
4735 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4736 result = mailcap_get_command_in_file(path, type, file_to_open);
4740 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4744 void mailcap_update_default(const gchar *type, const gchar *command)
4746 gchar *path = NULL, *outpath = NULL;
4747 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4748 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4749 FILE *fp = g_fopen(path, "rb");
4750 FILE *outfp = g_fopen(outpath, "wb");
4751 gchar buf[BUFFSIZE];
4752 gboolean err = FALSE;
4760 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4761 gchar **parts = g_strsplit(buf, ";", 3);
4762 gchar *trimmed = parts[0];
4763 while (trimmed[0] == ' ')
4765 while (trimmed[strlen(trimmed)-1] == ' ')
4766 trimmed[strlen(trimmed)-1] = '\0';
4768 if (!strcmp(trimmed, type)) {
4773 if(fputs(buf, outfp) == EOF) {
4780 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4786 if (fclose(outfp) == EOF)
4790 g_rename(outpath, path);
4796 gint copy_dir(const gchar *src, const gchar *dst)
4801 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4802 g_warning("failed to open directory: %s\n", src);
4806 if (make_dir(dst) < 0)
4809 while ((name = g_dir_read_name(dir)) != NULL) {
4810 gchar *old_file, *new_file;
4811 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4812 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4813 debug_print("copying: %s -> %s\n", old_file, new_file);
4814 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4815 gint r = copy_file(old_file, new_file, TRUE);
4822 /* Windows has no symlinks. Or well, Vista seems to
4823 have something like this but the semantics might be
4824 different. Thus we don't use it under Windows. */
4825 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4828 gchar *target = g_file_read_link(old_file, &error);
4830 r = symlink(target, new_file);
4837 #endif /*G_OS_WIN32*/
4838 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4839 gint r = copy_dir(old_file, new_file);
4850 /* crude test to see if a file is an email. */
4851 gboolean file_is_email (const gchar *filename)
4857 if (filename == NULL)
4859 if ((fp = g_fopen(filename, "rb")) == NULL)
4861 while (i < 60 && score < 3
4862 && fgets(buffer, sizeof (buffer), fp) > 0) {
4863 if (!strncmp(buffer, "From:", strlen("From:")))
4865 if (!strncmp(buffer, "To:", strlen("To:")))
4867 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4872 return (score >= 3);
4875 gboolean sc_g_list_bigger(GList *list, gint max)
4879 while (cur && i <= max+1) {
4886 gboolean sc_g_slist_bigger(GSList *list, gint max)
4890 while (cur && i <= max+1) {
4897 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4898 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4899 NULL, NULL, NULL, NULL, NULL, NULL};
4900 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4901 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4902 NULL, NULL, NULL, NULL, NULL, NULL};
4904 gint daynames_len[] = {0,0,0,0,0,0,0};
4905 gint monthnames_len[] = {0,0,0,0,0,0,
4907 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4908 gint s_monthnames_len[] = {0,0,0,0,0,0,
4910 const gchar *s_am_up = NULL;
4911 const gchar *s_pm_up = NULL;
4912 const gchar *s_am_low = NULL;
4913 const gchar *s_pm_low = NULL;
4915 gint s_am_up_len = 0;
4916 gint s_pm_up_len = 0;
4917 gint s_am_low_len = 0;
4918 gint s_pm_low_len = 0;
4920 const gchar *def_loc_format = NULL;
4921 const gchar *date_loc_format = NULL;
4922 const gchar *time_loc_format = NULL;
4923 const gchar *time_am_pm = NULL;
4925 static gboolean time_names_init_done = FALSE;
4927 static void init_time_names(void)
4931 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4932 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4933 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4934 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4935 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4936 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4937 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4939 monthnames[0] = Q_("Complete month name for use by strftime|January");
4940 monthnames[1] = Q_("Complete month name for use by strftime|February");
4941 monthnames[2] = Q_("Complete month name for use by strftime|March");
4942 monthnames[3] = Q_("Complete month name for use by strftime|April");
4943 monthnames[4] = Q_("Complete month name for use by strftime|May");
4944 monthnames[5] = Q_("Complete month name for use by strftime|June");
4945 monthnames[6] = Q_("Complete month name for use by strftime|July");
4946 monthnames[7] = Q_("Complete month name for use by strftime|August");
4947 monthnames[8] = Q_("Complete month name for use by strftime|September");
4948 monthnames[9] = Q_("Complete month name for use by strftime|October");
4949 monthnames[10] = Q_("Complete month name for use by strftime|November");
4950 monthnames[11] = Q_("Complete month name for use by strftime|December");
4952 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4953 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4954 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4955 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4956 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4957 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4958 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4960 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4961 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4962 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4963 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4964 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4965 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4966 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4967 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4968 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4969 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4970 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4971 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4973 for (i = 0; i < 7; i++) {
4974 daynames_len[i] = strlen(daynames[i]);
4975 s_daynames_len[i] = strlen(s_daynames[i]);
4977 for (i = 0; i < 12; i++) {
4978 monthnames_len[i] = strlen(monthnames[i]);
4979 s_monthnames_len[i] = strlen(s_monthnames[i]);
4982 s_am_up = Q_("For use by strftime (morning)|AM");
4983 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4984 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4985 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4987 s_am_up_len = strlen(s_am_up);
4988 s_pm_up_len = strlen(s_pm_up);
4989 s_am_low_len = strlen(s_am_low);
4990 s_pm_low_len = strlen(s_pm_low);
4992 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4993 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4994 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4996 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4998 time_names_init_done = TRUE;
5001 #define CHECK_SIZE() { \
5002 total_done += len; \
5003 if (total_done >= buflen) { \
5004 buf[buflen-1] = '\0'; \
5009 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5011 gchar *curpos = buf;
5012 gint total_done = 0;
5013 gchar subbuf[64], subfmt[64];
5014 static time_t last_tzset = (time_t)0;
5016 if (!time_names_init_done)
5019 if (format == NULL || lt == NULL)
5022 if (last_tzset != time(NULL)) {
5024 last_tzset = time(NULL);
5027 if (*format == '%') {
5028 gint len = 0, tmp = 0;
5032 len = 1; CHECK_SIZE();
5036 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5037 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5040 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5041 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5045 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5046 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5049 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5050 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5053 fast_strftime(subbuf, 64, def_loc_format, lt);
5054 len = strlen(subbuf); CHECK_SIZE();
5055 strncpy2(curpos, subbuf, buflen - total_done);
5058 total_done += 2; CHECK_SIZE();
5059 tmp = (lt->tm_year + 1900)/100;
5060 *curpos++ = '0'+(tmp / 10);
5061 *curpos++ = '0'+(tmp % 10);
5064 total_done += 2; CHECK_SIZE();
5065 *curpos++ = '0'+(lt->tm_mday / 10);
5066 *curpos++ = '0'+(lt->tm_mday % 10);
5069 total_done += 8; CHECK_SIZE();
5070 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5071 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5073 *curpos++ = '0'+(lt->tm_mday / 10);
5074 *curpos++ = '0'+(lt->tm_mday % 10);
5076 tmp = lt->tm_year%100;
5077 *curpos++ = '0'+(tmp / 10);
5078 *curpos++ = '0'+(tmp % 10);
5081 len = 2; CHECK_SIZE();
5082 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5085 len = 10; CHECK_SIZE();
5086 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5087 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5090 total_done += 2; CHECK_SIZE();
5091 *curpos++ = '0'+(lt->tm_hour / 10);
5092 *curpos++ = '0'+(lt->tm_hour % 10);
5095 total_done += 2; CHECK_SIZE();
5101 *curpos++ = '0'+(tmp / 10);
5102 *curpos++ = '0'+(tmp % 10);
5105 len = 3; CHECK_SIZE();
5106 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5109 len = 2; CHECK_SIZE();
5110 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5113 len = 2; CHECK_SIZE();
5119 snprintf(curpos, buflen - total_done, "%2d", tmp);
5122 total_done += 2; CHECK_SIZE();
5123 tmp = lt->tm_mon + 1;
5124 *curpos++ = '0'+(tmp / 10);
5125 *curpos++ = '0'+(tmp % 10);
5128 total_done += 2; CHECK_SIZE();
5129 *curpos++ = '0'+(lt->tm_min / 10);
5130 *curpos++ = '0'+(lt->tm_min % 10);
5133 len = 1; CHECK_SIZE();
5137 if (lt->tm_hour >= 12) {
5138 len = s_pm_up_len; CHECK_SIZE();
5139 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5141 len = s_am_up_len; CHECK_SIZE();
5142 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5146 if (lt->tm_hour >= 12) {
5147 len = s_pm_low_len; CHECK_SIZE();
5148 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5150 len = s_am_low_len; CHECK_SIZE();
5151 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5155 fast_strftime(subbuf, 64, time_am_pm, lt);
5156 len = strlen(subbuf); CHECK_SIZE();
5157 strncpy2(curpos, subbuf, buflen - total_done);
5160 total_done += 5; CHECK_SIZE();
5161 *curpos++ = '0'+(lt->tm_hour / 10);
5162 *curpos++ = '0'+(lt->tm_hour % 10);
5164 *curpos++ = '0'+(lt->tm_min / 10);
5165 *curpos++ = '0'+(lt->tm_min % 10);
5168 snprintf(subbuf, 64, "%ld", mktime(lt));
5169 len = strlen(subbuf); CHECK_SIZE();
5170 strncpy2(curpos, subbuf, buflen - total_done);
5173 total_done += 2; CHECK_SIZE();
5174 *curpos++ = '0'+(lt->tm_sec / 10);
5175 *curpos++ = '0'+(lt->tm_sec % 10);
5178 len = 1; CHECK_SIZE();
5182 total_done += 8; CHECK_SIZE();
5183 *curpos++ = '0'+(lt->tm_hour / 10);
5184 *curpos++ = '0'+(lt->tm_hour % 10);
5186 *curpos++ = '0'+(lt->tm_min / 10);
5187 *curpos++ = '0'+(lt->tm_min % 10);
5189 *curpos++ = '0'+(lt->tm_sec / 10);
5190 *curpos++ = '0'+(lt->tm_sec % 10);
5193 len = 1; CHECK_SIZE();
5194 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5197 len = 1; CHECK_SIZE();
5198 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5201 fast_strftime(subbuf, 64, date_loc_format, lt);
5202 len = strlen(subbuf); CHECK_SIZE();
5203 strncpy2(curpos, subbuf, buflen - total_done);
5206 fast_strftime(subbuf, 64, time_loc_format, lt);
5207 len = strlen(subbuf); CHECK_SIZE();
5208 strncpy2(curpos, subbuf, buflen - total_done);
5211 total_done += 2; CHECK_SIZE();
5212 tmp = lt->tm_year%100;
5213 *curpos++ = '0'+(tmp / 10);
5214 *curpos++ = '0'+(tmp % 10);
5217 len = 4; CHECK_SIZE();
5218 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5228 /* let these complicated ones be done with the libc */
5229 snprintf(subfmt, 64, "%%%c", *format);
5230 strftime(subbuf, 64, subfmt, lt);
5231 len = strlen(subbuf); CHECK_SIZE();
5232 strncpy2(curpos, subbuf, buflen - total_done);
5236 /* let these complicated modifiers be done with the libc */
5237 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5238 strftime(subbuf, 64, subfmt, lt);
5239 len = strlen(subbuf); CHECK_SIZE();
5240 strncpy2(curpos, subbuf, buflen - total_done);
5244 if (format && *format)
5245 g_warning("format error (%c)", *format);
5252 int len = 1; CHECK_SIZE();
5253 *curpos++ = *format++;
5260 gboolean prefs_common_get_use_shred(void);
5264 #define WEXITSTATUS(x) (x)
5267 int claws_unlink(const gchar *filename)
5270 static int found_shred = -1;
5271 static const gchar *args[4];
5273 if (filename == NULL)
5276 if (prefs_common_get_use_shred()) {
5277 if (found_shred == -1) {
5279 args[0] = g_find_program_in_path("shred");
5280 debug_print("found shred: %s\n", args[0]);
5281 found_shred = (args[0] != NULL) ? 1:0;
5285 if (found_shred == 1) {
5286 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5287 if (s.st_nlink == 1) {
5290 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5291 NULL, NULL, NULL, NULL, &status, NULL);
5292 debug_print("%s %s exited with status %d\n",
5293 args[0], filename, WEXITSTATUS(status));
5294 if (truncate(filename, 0) < 0)
5295 g_warning("couln't truncate");
5300 return g_unlink(filename);