2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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>
38 #include <sys/param.h>
40 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
48 #include <sys/types.h>
50 # include <sys/wait.h>
57 #include <sys/utsname.h>
71 # include <tablet-browser-interface.h>
73 # include <osso-browser-interface.h>
79 #include "../codeconv.h"
83 static gboolean debug_mode = FALSE;
85 static GSList *tempfiles=NULL;
88 /* Return true if we are running as root. This function should beused
89 instead of getuid () == 0. */
90 gboolean superuser_p (void)
93 return w32_is_administrator ();
101 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
102 gint g_chdir(const gchar *path)
105 if (G_WIN32_HAVE_WIDECHAR_API()) {
110 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
116 retval = _wchdir(wpath);
128 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
129 if (cp_path == NULL) {
134 retval = chdir(cp_path);
147 gint g_chmod(const gchar *path, gint mode)
150 if (G_WIN32_HAVE_WIDECHAR_API()) {
155 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
161 retval = _wchmod(wpath, mode);
173 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
174 if (cp_path == NULL) {
179 retval = chmod(cp_path, mode);
188 return chmod(path, mode);
192 FILE* g_fopen(const gchar *filename, const gchar *mode)
195 char *name = g_win32_locale_filename_from_utf8(filename);
196 FILE* fp = fopen(name, mode);
200 return fopen(filename, mode);
203 int g_open(const gchar *filename, int flags, int mode)
206 char *name = g_win32_locale_filename_from_utf8(filename);
207 int fd = open(name, flags, mode);
211 return open(filename, flags, mode);
214 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
218 gint mkstemp_name(gchar *template, gchar **name_used)
220 static gulong count=0; /* W32-_mktemp only supports up to 27
224 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
225 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
226 (S_IRUSR | S_IWUSR));
228 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
230 perror(g_strdup_printf("cant create %s",*name_used));
236 #endif /* G_OS_WIN32 */
239 gint mkstemp(gchar *template)
242 gint res = mkstemp_name(template, &dummyname);
246 #endif /* G_OS_WIN32 */
248 void list_free_strings(GList *list)
250 list = g_list_first(list);
252 while (list != NULL) {
258 void slist_free_strings(GSList *list)
260 while (list != NULL) {
266 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
271 void hash_free_strings(GHashTable *table)
273 g_hash_table_foreach(table, hash_free_strings_func, NULL);
276 gint str_case_equal(gconstpointer v, gconstpointer v2)
278 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
281 guint str_case_hash(gconstpointer key)
283 const gchar *p = key;
287 h = g_ascii_tolower(h);
288 for (p += 1; *p != '\0'; p++)
289 h = (h << 5) - h + g_ascii_tolower(*p);
295 void ptr_array_free_strings(GPtrArray *array)
300 cm_return_if_fail(array != NULL);
302 for (i = 0; i < array->len; i++) {
303 str = g_ptr_array_index(array, i);
308 gint to_number(const gchar *nstr)
310 register const gchar *p;
312 if (*nstr == '\0') return -1;
314 for (p = nstr; *p != '\0'; p++)
315 if (!g_ascii_isdigit(*p)) return -1;
320 /* convert integer into string,
321 nstr must be not lower than 11 characters length */
322 gchar *itos_buf(gchar *nstr, gint n)
324 g_snprintf(nstr, 11, "%d", n);
328 /* convert integer into string */
331 static gchar nstr[11];
333 return itos_buf(nstr, n);
336 #define divide(num,divisor,i,d) \
338 i = num >> divisor; \
339 d = num & ((1<<divisor)-1); \
340 d = (d*100) >> divisor; \
345 * \brief Convert a given size in bytes in a human-readable string
347 * \param size The size expressed in bytes to convert in string
348 * \return The string that respresents the size in an human-readable way
350 gchar *to_human_readable(goffset size)
352 static gchar str[14];
353 static gchar *b_format = NULL, *kb_format = NULL,
354 *mb_format = NULL, *gb_format = NULL;
355 register int t = 0, r = 0;
356 if (b_format == NULL) {
358 kb_format = _("%d.%02dKB");
359 mb_format = _("%d.%02dMB");
360 gb_format = _("%.2fGB");
363 if (size < (goffset)1024) {
364 g_snprintf(str, sizeof(str), b_format, (gint)size);
366 } else if (size >> 10 < (goffset)1024) {
367 divide(size, 10, t, r);
368 g_snprintf(str, sizeof(str), kb_format, t, r);
370 } else if (size >> 20 < (goffset)1024) {
371 divide(size, 20, t, r);
372 g_snprintf(str, sizeof(str), mb_format, t, r);
375 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
380 /* strcmp with NULL-checking */
381 gint strcmp2(const gchar *s1, const gchar *s2)
383 if (s1 == NULL || s2 == NULL)
386 return strcmp(s1, s2);
388 /* strstr with NULL-checking */
389 gchar *strstr2(const gchar *s1, const gchar *s2)
391 if (s1 == NULL || s2 == NULL)
394 return strstr(s1, s2);
397 gint path_cmp(const gchar *s1, const gchar *s2)
402 gchar *s1buf, *s2buf;
405 if (s1 == NULL || s2 == NULL) return -1;
406 if (*s1 == '\0' || *s2 == '\0') return -1;
409 s1buf = g_strdup (s1);
410 s2buf = g_strdup (s2);
411 subst_char (s1buf, '/', G_DIR_SEPARATOR);
412 subst_char (s2buf, '/', G_DIR_SEPARATOR);
415 #endif /* !G_OS_WIN32 */
420 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
421 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
423 rc = strncmp(s1, s2, MAX(len1, len2));
427 #endif /* !G_OS_WIN32 */
431 /* remove trailing return code */
432 gchar *strretchomp(gchar *str)
436 if (!*str) return str;
438 for (s = str + strlen(str) - 1;
439 s >= str && (*s == '\n' || *s == '\r');
446 /* remove trailing character */
447 gchar *strtailchomp(gchar *str, gchar tail_char)
451 if (!*str) return str;
452 if (tail_char == '\0') return str;
454 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
460 /* remove CR (carriage return) */
461 gchar *strcrchomp(gchar *str)
465 if (!*str) return str;
467 s = str + strlen(str) - 1;
468 if (*s == '\n' && s > str && *(s - 1) == '\r') {
476 gint file_strip_crs(const gchar *file)
478 FILE *fp = NULL, *outfp = NULL;
480 gchar *out = get_tmp_file();
484 fp = g_fopen(file, "rb");
488 outfp = g_fopen(out, "wb");
494 while (fgets(buf, sizeof (buf), fp) != NULL) {
496 if (fputs(buf, outfp) == EOF) {
504 if (fclose(outfp) == EOF) {
508 if (move_file(out, file, TRUE) < 0)
520 /* Similar to `strstr' but this function ignores the case of both strings. */
521 gchar *strcasestr(const gchar *haystack, const gchar *needle)
523 register size_t haystack_len, needle_len;
525 haystack_len = strlen(haystack);
526 needle_len = strlen(needle);
528 if (haystack_len < needle_len || needle_len == 0)
531 while (haystack_len >= needle_len) {
532 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
533 return (gchar *)haystack;
543 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
544 gconstpointer needle, size_t needlelen)
546 const gchar *haystack_ = (const gchar *)haystack;
547 const gchar *needle_ = (const gchar *)needle;
548 const gchar *haystack_cur = (const gchar *)haystack;
549 size_t haystack_left = haystacklen;
552 return memchr(haystack_, *needle_, haystacklen);
554 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
556 if (haystacklen - (haystack_cur - haystack_) < needlelen)
558 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
559 return (gpointer)haystack_cur;
562 haystack_left = haystacklen - (haystack_cur - haystack_);
569 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
570 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
572 register const gchar *s = src;
573 register gchar *d = dest;
583 /* Examine if next block is non-ASCII string */
584 gboolean is_next_nonascii(const gchar *s)
588 /* skip head space */
589 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
591 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
592 if (*(guchar *)p > 127 || *(guchar *)p < 32)
599 gint get_next_word_len(const gchar *s)
603 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
609 static void trim_subject_for_compare(gchar *str)
613 eliminate_parenthesis(str, '[', ']');
614 eliminate_parenthesis(str, '(', ')');
617 srcp = str + subject_get_prefix_length(str);
619 memmove(str, srcp, strlen(srcp) + 1);
622 static void trim_subject_for_sort(gchar *str)
628 srcp = str + subject_get_prefix_length(str);
630 memmove(str, srcp, strlen(srcp) + 1);
633 /* compare subjects */
634 gint subject_compare(const gchar *s1, const gchar *s2)
638 if (!s1 || !s2) return -1;
639 if (!*s1 || !*s2) return -1;
641 Xstrdup_a(str1, s1, return -1);
642 Xstrdup_a(str2, s2, return -1);
644 trim_subject_for_compare(str1);
645 trim_subject_for_compare(str2);
647 if (!*str1 || !*str2) return -1;
649 return strcmp(str1, str2);
652 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
656 if (!s1 || !s2) return -1;
658 Xstrdup_a(str1, s1, return -1);
659 Xstrdup_a(str2, s2, return -1);
661 trim_subject_for_sort(str1);
662 trim_subject_for_sort(str2);
664 return g_utf8_collate(str1, str2);
667 void trim_subject(gchar *str)
669 register gchar *srcp;
675 srcp = str + subject_get_prefix_length(str);
680 } else if (*srcp == '(') {
692 else if (*srcp == cl)
699 while (g_ascii_isspace(*srcp)) srcp++;
700 memmove(str, srcp, strlen(srcp) + 1);
703 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
705 register gchar *srcp, *destp;
710 while ((destp = strchr(destp, op))) {
716 else if (*srcp == cl)
722 while (g_ascii_isspace(*srcp)) srcp++;
723 memmove(destp, srcp, strlen(srcp) + 1);
727 void extract_parenthesis(gchar *str, gchar op, gchar cl)
729 register gchar *srcp, *destp;
734 while ((srcp = strchr(destp, op))) {
737 memmove(destp, srcp + 1, strlen(srcp));
742 else if (*destp == cl)
754 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
757 register gchar *srcp, *destp;
759 gboolean in_quote = FALSE;
763 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
766 memmove(destp, srcp + 1, strlen(srcp));
769 if (*destp == op && !in_quote)
771 else if (*destp == cl && !in_quote)
773 else if (*destp == quote_chr)
785 void extract_quote(gchar *str, gchar quote_chr)
789 if ((str = strchr(str, quote_chr))) {
791 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
792 memmove(p - 1, p, strlen(p) + 1);
797 memmove(str, str + 1, p - str);
802 void eliminate_address_comment(gchar *str)
804 register gchar *srcp, *destp;
809 while ((destp = strchr(destp, '"'))) {
810 if ((srcp = strchr(destp + 1, '"'))) {
815 while (g_ascii_isspace(*srcp)) srcp++;
816 memmove(destp, srcp, strlen(srcp) + 1);
826 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
832 else if (*srcp == ')')
838 while (g_ascii_isspace(*srcp)) srcp++;
839 memmove(destp, srcp, strlen(srcp) + 1);
843 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
845 gboolean in_quote = FALSE;
848 if (*str == c && !in_quote)
850 if (*str == quote_chr)
858 void extract_address(gchar *str)
860 eliminate_address_comment(str);
861 if (strchr_with_skip_quote(str, '"', '<'))
862 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
866 void extract_list_id_str(gchar *str)
868 if (strchr_with_skip_quote(str, '"', '<'))
869 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
873 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
878 if (!str) return addr_list;
880 Xstrdup_a(work, str, return addr_list);
883 eliminate_address_comment(work);
886 while (workp && *workp) {
889 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
895 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
896 extract_parenthesis_with_skip_quote
897 (workp, '"', '<', '>');
901 addr_list = g_slist_append(addr_list, g_strdup(workp));
909 GSList *address_list_append(GSList *addr_list, const gchar *str)
911 return address_list_append_real(addr_list, str, TRUE);
914 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
916 return address_list_append_real(addr_list, str, FALSE);
919 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
923 if (!str) return msgid_list;
926 while (strp && *strp) {
927 const gchar *start, *end;
930 if ((start = strchr(strp, '<')) != NULL) {
931 end = strchr(start + 1, '>');
936 msgid = g_strndup(start + 1, end - start - 1);
939 msgid_list = g_slist_prepend(msgid_list, msgid);
949 GSList *references_list_append(GSList *msgid_list, const gchar *str)
953 list = references_list_prepend(NULL, str);
954 list = g_slist_reverse(list);
955 msgid_list = g_slist_concat(msgid_list, list);
960 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
965 if (!str) return group_list;
967 Xstrdup_a(work, str, return group_list);
971 while (workp && *workp) {
974 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
982 group_list = g_slist_append(group_list,
991 GList *add_history(GList *list, const gchar *str)
996 cm_return_val_if_fail(str != NULL, list);
998 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1001 list = g_list_remove(list, old->data);
1003 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1006 last = g_list_last(list);
1008 oldstr = last->data;
1009 list = g_list_remove(list, last->data);
1014 list = g_list_prepend(list, g_strdup(str));
1019 void remove_return(gchar *str)
1021 register gchar *p = str;
1024 if (*p == '\n' || *p == '\r')
1025 memmove(p, p + 1, strlen(p));
1031 void remove_space(gchar *str)
1033 register gchar *p = str;
1038 while (g_ascii_isspace(*(p + spc)))
1041 memmove(p, p + spc, strlen(p + spc) + 1);
1047 void unfold_line(gchar *str)
1049 register gchar *p = str;
1053 if (*p == '\n' || *p == '\r') {
1056 while (g_ascii_isspace(*(p + spc)))
1059 memmove(p, p + spc, strlen(p + spc) + 1);
1065 void subst_char(gchar *str, gchar orig, gchar subst)
1067 register gchar *p = str;
1076 void subst_chars(gchar *str, gchar *orig, gchar subst)
1078 register gchar *p = str;
1081 if (strchr(orig, *p) != NULL)
1087 void subst_for_filename(gchar *str)
1092 subst_chars(str, "\t\r\n\\/*:", '_');
1094 subst_chars(str, "\t\r\n\\/*", '_');
1098 void subst_for_shellsafe_filename(gchar *str)
1102 subst_for_filename(str);
1103 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1106 gboolean is_ascii_str(const gchar *str)
1108 const guchar *p = (const guchar *)str;
1110 while (*p != '\0') {
1111 if (*p != '\t' && *p != ' ' &&
1112 *p != '\r' && *p != '\n' &&
1113 (*p < 32 || *p >= 127))
1121 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1123 gchar * position = NULL;
1124 gchar * tmp_pos = NULL;
1127 if (quote_chars == NULL)
1130 for (i = 0; i < strlen(quote_chars); i++) {
1131 tmp_pos = strrchr (str, quote_chars[i]);
1133 || (tmp_pos != NULL && position <= tmp_pos) )
1139 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1141 const gchar *first_pos;
1142 const gchar *last_pos;
1143 const gchar *p = str;
1144 gint quote_level = -1;
1146 /* speed up line processing by only searching to the last '>' */
1147 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1148 /* skip a line if it contains a '<' before the initial '>' */
1149 if (memchr(str, '<', first_pos - str) != NULL)
1151 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1155 while (p <= last_pos) {
1156 while (p < last_pos) {
1157 if (g_ascii_isspace(*p))
1163 if (strchr(quote_chars, *p))
1165 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1166 /* any characters are allowed except '-','<' and space */
1167 while (*p != '-' && *p != '<'
1168 && !strchr(quote_chars, *p)
1169 && !g_ascii_isspace(*p)
1172 if (strchr(quote_chars, *p))
1184 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1186 const gchar *p = str, *q;
1187 gint cur_line = 0, len;
1189 while ((q = strchr(p, '\n')) != NULL) {
1191 if (len > max_chars) {
1201 if (len > max_chars) {
1210 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1212 gchar * position = NULL;
1213 gchar * tmp_pos = NULL;
1216 if (quote_chars == NULL)
1219 for (i = 0; i < strlen(quote_chars); i++) {
1220 tmp_pos = strchr (str, quote_chars[i]);
1222 || (tmp_pos != NULL && position >= tmp_pos) )
1228 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1230 register guint haystack_len, needle_len;
1231 gboolean in_squote = FALSE, in_dquote = FALSE;
1233 haystack_len = strlen(haystack);
1234 needle_len = strlen(needle);
1236 if (haystack_len < needle_len || needle_len == 0)
1239 while (haystack_len >= needle_len) {
1240 if (!in_squote && !in_dquote &&
1241 !strncmp(haystack, needle, needle_len))
1242 return (gchar *)haystack;
1244 /* 'foo"bar"' -> foo"bar"
1245 "foo'bar'" -> foo'bar' */
1246 if (*haystack == '\'') {
1249 else if (!in_dquote)
1251 } else if (*haystack == '\"') {
1254 else if (!in_squote)
1265 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1268 GSList *string_list = NULL, *slist;
1269 gchar **str_array, *s, *new_str;
1270 guint i, n = 1, len;
1272 cm_return_val_if_fail(str != NULL, NULL);
1273 cm_return_val_if_fail(delim != NULL, NULL);
1276 max_tokens = G_MAXINT;
1278 s = strstr_with_skip_quote(str, delim);
1280 guint delimiter_len = strlen(delim);
1284 new_str = g_strndup(str, len);
1286 if (new_str[0] == '\'' || new_str[0] == '\"') {
1287 if (new_str[len - 1] == new_str[0]) {
1288 new_str[len - 1] = '\0';
1289 memmove(new_str, new_str + 1, len - 1);
1292 string_list = g_slist_prepend(string_list, new_str);
1294 str = s + delimiter_len;
1295 s = strstr_with_skip_quote(str, delim);
1296 } while (--max_tokens && s);
1300 new_str = g_strdup(str);
1301 if (new_str[0] == '\'' || new_str[0] == '\"') {
1303 if (new_str[len - 1] == new_str[0]) {
1304 new_str[len - 1] = '\0';
1305 memmove(new_str, new_str + 1, len - 1);
1308 string_list = g_slist_prepend(string_list, new_str);
1312 str_array = g_new(gchar*, n);
1316 str_array[i--] = NULL;
1317 for (slist = string_list; slist; slist = slist->next)
1318 str_array[i--] = slist->data;
1320 g_slist_free(string_list);
1325 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1327 gchar *abbrev_group;
1329 const gchar *p = group;
1332 cm_return_val_if_fail(group != NULL, NULL);
1334 last = group + strlen(group);
1335 abbrev_group = ap = g_malloc(strlen(group) + 1);
1340 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1342 while (*p != '.') p++;
1345 return abbrev_group;
1350 return abbrev_group;
1353 gchar *trim_string(const gchar *str, gint len)
1355 const gchar *p = str;
1360 if (!str) return NULL;
1361 if (strlen(str) <= len)
1362 return g_strdup(str);
1363 if (g_utf8_validate(str, -1, NULL) == FALSE)
1364 return g_strdup(str);
1366 while (*p != '\0') {
1367 mb_len = g_utf8_skip[*(guchar *)p];
1370 else if (new_len + mb_len > len)
1377 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1378 return g_strconcat(new_str, "...", NULL);
1381 GList *uri_list_extract_filenames(const gchar *uri_list)
1383 GList *result = NULL;
1385 gchar *escaped_utf8uri;
1391 while (g_ascii_isspace(*p)) p++;
1392 if (!strncmp(p, "file:", 5)) {
1395 while (*q && *q != '\n' && *q != '\r') q++;
1398 gchar *file, *locale_file = NULL;
1400 while (q > p && g_ascii_isspace(*q))
1402 Xalloca(escaped_utf8uri, q - p + 2,
1404 Xalloca(file, q - p + 2,
1407 strncpy(escaped_utf8uri, p, q - p + 1);
1408 escaped_utf8uri[q - p + 1] = '\0';
1409 decode_uri(file, escaped_utf8uri);
1411 * g_filename_from_uri() rejects escaped/locale encoded uri
1412 * string which come from Nautilus.
1415 if (g_utf8_validate(file, -1, NULL))
1417 = conv_codeset_strdup(
1420 conv_get_locale_charset_str());
1422 locale_file = g_strdup(file + 5);
1424 locale_file = g_filename_from_uri(file, NULL, NULL);
1426 result = g_list_append(result, locale_file);
1430 p = strchr(p, '\n');
1437 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1440 static gint axtoi(const gchar *hexstr)
1442 gint hi, lo, result;
1445 if ('0' <= hi && hi <= '9') {
1448 if ('a' <= hi && hi <= 'f') {
1451 if ('A' <= hi && hi <= 'F') {
1456 if ('0' <= lo && lo <= '9') {
1459 if ('a' <= lo && lo <= 'f') {
1462 if ('A' <= lo && lo <= 'F') {
1465 result = lo + (16 * hi);
1469 gboolean is_uri_string(const gchar *str)
1471 while (str && *str && g_ascii_isspace(*str))
1473 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1474 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1475 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1476 g_ascii_strncasecmp(str, "www.", 4) == 0);
1479 gchar *get_uri_path(const gchar *uri)
1481 while (uri && *uri && g_ascii_isspace(*uri))
1483 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1484 return (gchar *)(uri + 7);
1485 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1486 return (gchar *)(uri + 8);
1487 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1488 return (gchar *)(uri + 6);
1490 return (gchar *)uri;
1493 gint get_uri_len(const gchar *str)
1497 if (is_uri_string(str)) {
1498 for (p = str; *p != '\0'; p++) {
1499 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1508 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1509 * plusses, and escape characters are used)
1511 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1513 gchar *dec = decoded_uri;
1514 const gchar *enc = encoded_uri;
1519 if (isxdigit((guchar)enc[0]) &&
1520 isxdigit((guchar)enc[1])) {
1526 if (with_plus && *enc == '+')
1538 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1540 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1543 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1545 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1546 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1550 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1551 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1555 const gchar *forbidden_uris[] = { ".gnupg/",
1561 gint num_attach = 0;
1562 gchar **my_att = NULL;
1564 Xstrdup_a(tmp_mailto, mailto, return -1);
1566 if (!strncmp(tmp_mailto, "mailto:", 7))
1569 p = strchr(tmp_mailto, '?');
1576 *to = decode_uri_gdup(tmp_mailto);
1578 my_att = g_malloc(sizeof(char *));
1582 gchar *field, *value;
1599 if (*value == '\0') continue;
1601 if (from && !g_ascii_strcasecmp(field, "from")) {
1603 *from = decode_uri_gdup(value);
1605 gchar *tmp = decode_uri_gdup(value);
1606 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1610 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1612 *cc = decode_uri_gdup(value);
1614 gchar *tmp = decode_uri_gdup(value);
1615 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1619 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1621 *bcc = decode_uri_gdup(value);
1623 gchar *tmp = decode_uri_gdup(value);
1624 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1628 } else if (subject && !*subject &&
1629 !g_ascii_strcasecmp(field, "subject")) {
1630 *subject = decode_uri_gdup(value);
1631 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1632 *body = decode_uri_gdup(value);
1633 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1634 gchar *tmp = decode_uri_gdup(value);
1635 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1636 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1640 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1642 gchar *tmp = decode_uri_gdup(value);
1643 for (; forbidden_uris[i]; i++) {
1644 if (strstr(tmp, forbidden_uris[i])) {
1645 g_print("Refusing to attach '%s', potential private data leak\n",
1653 /* attach is correct */
1655 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1656 my_att[num_attach-1] = tmp;
1657 my_att[num_attach] = NULL;
1659 } else if (inreplyto && !*inreplyto &&
1660 !g_ascii_strcasecmp(field, "in-reply-to")) {
1661 *inreplyto = decode_uri_gdup(value);
1672 #include <windows.h>
1673 #ifndef CSIDL_APPDATA
1674 #define CSIDL_APPDATA 0x001a
1676 #ifndef CSIDL_LOCAL_APPDATA
1677 #define CSIDL_LOCAL_APPDATA 0x001c
1679 #ifndef CSIDL_FLAG_CREATE
1680 #define CSIDL_FLAG_CREATE 0x8000
1682 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1686 w32_strerror (int w32_errno)
1688 static char strerr[256];
1689 int ec = (int)GetLastError ();
1693 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1694 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1695 strerr, DIM (strerr)-1, NULL);
1699 static __inline__ void *
1700 dlopen (const char * name, int flag)
1702 void * hd = LoadLibrary (name);
1706 static __inline__ void *
1707 dlsym (void * hd, const char * sym)
1711 void * fnc = GetProcAddress (hd, sym);
1720 static __inline__ const char *
1723 return w32_strerror (0);
1727 static __inline__ int
1739 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1741 static int initialized;
1742 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1746 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1752 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1754 handle = dlopen (dllnames[i], RTLD_LAZY);
1757 func = dlsym (handle, "SHGetFolderPathW");
1768 return func (a,b,c,d,e);
1773 /* Returns a static string with the directroy from which the module
1774 has been loaded. Returns an empty string on error. */
1775 static char *w32_get_module_dir(void)
1777 static char *moddir;
1780 char name[MAX_PATH+10];
1783 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1786 p = strrchr (name, '\\');
1792 moddir = g_strdup (name);
1796 #endif /* G_OS_WIN32 */
1798 /* Return a static string with the locale dir. */
1799 const gchar *get_locale_dir(void)
1801 static gchar *loc_dir;
1805 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1806 "\\share\\locale", NULL);
1809 loc_dir = LOCALEDIR;
1815 const gchar *get_home_dir(void)
1818 static char home_dir_utf16[MAX_PATH] = "";
1819 static gchar *home_dir_utf8 = NULL;
1820 if (home_dir_utf16[0] == '\0') {
1821 if (w32_shgetfolderpath
1822 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1823 NULL, 0, home_dir_utf16) < 0)
1824 strcpy (home_dir_utf16, "C:\\Sylpheed");
1825 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1827 return home_dir_utf8;
1829 static const gchar *homeenv = NULL;
1834 if (!homeenv && g_getenv("HOME") != NULL)
1835 homeenv = g_strdup(g_getenv("HOME"));
1837 homeenv = g_get_home_dir();
1843 static gchar *claws_rc_dir = NULL;
1844 static gboolean rc_dir_alt = FALSE;
1845 const gchar *get_rc_dir(void)
1849 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1852 return claws_rc_dir;
1855 void set_rc_dir(const gchar *dir)
1857 if (claws_rc_dir != NULL) {
1858 g_print("Error: rc_dir already set\n");
1861 if (g_path_is_absolute(dir))
1862 claws_rc_dir = g_strdup(dir);
1864 claws_rc_dir = g_strconcat(g_get_current_dir(),
1865 G_DIR_SEPARATOR_S, dir, NULL);
1867 debug_print("set rc_dir to %s\n", claws_rc_dir);
1868 if (!is_dir_exist(claws_rc_dir)) {
1869 if (make_dir_hier(claws_rc_dir) != 0) {
1870 g_print("Error: can't create %s\n",
1877 gboolean rc_dir_is_alt(void) {
1881 const gchar *get_mail_base_dir(void)
1883 return get_home_dir();
1887 const gchar *prefs_common_get_data_root(void);
1888 gchar *last_data_root = NULL;
1891 const gchar *get_news_cache_dir(void)
1893 static gchar *news_cache_dir = NULL;
1895 const gchar *data_root = prefs_common_get_data_root();
1896 if (strcmp2(data_root, last_data_root)) {
1897 g_free(news_cache_dir);
1898 news_cache_dir = NULL;
1901 if (!news_cache_dir)
1903 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904 NEWS_CACHE_DIR, NULL);
1908 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1909 "Claws", G_DIR_SEPARATOR_S,
1910 g_get_user_name(), G_DIR_SEPARATOR_S,
1911 NEWS_CACHE_DIR, NULL);
1912 g_free(last_data_root);
1913 last_data_root = g_strdup(last_data_root);
1915 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1916 NEWS_CACHE_DIR, NULL);
1917 g_free(last_data_root);
1918 last_data_root = NULL;
1922 return news_cache_dir;
1925 const gchar *get_imap_cache_dir(void)
1927 static gchar *imap_cache_dir = NULL;
1929 const gchar *data_root = prefs_common_get_data_root();
1930 if (strcmp2(data_root, last_data_root)) {
1931 g_free(imap_cache_dir);
1932 imap_cache_dir = NULL;
1936 if (!imap_cache_dir)
1938 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1939 IMAP_CACHE_DIR, NULL);
1943 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1944 "Claws", G_DIR_SEPARATOR_S,
1945 g_get_user_name(), G_DIR_SEPARATOR_S,
1946 IMAP_CACHE_DIR, NULL);
1947 g_free(last_data_root);
1948 last_data_root = g_strdup(last_data_root);
1950 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1951 IMAP_CACHE_DIR, NULL);
1952 g_free(last_data_root);
1953 last_data_root = NULL;
1958 return imap_cache_dir;
1961 const gchar *get_mime_tmp_dir(void)
1963 static gchar *mime_tmp_dir = NULL;
1966 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 MIME_TMP_DIR, NULL);
1969 return mime_tmp_dir;
1972 const gchar *get_template_dir(void)
1974 static gchar *template_dir = NULL;
1977 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1978 TEMPLATE_DIR, NULL);
1980 return template_dir;
1984 const gchar *get_cert_file(void)
1986 const gchar *cert_file = NULL;
1988 cert_file = g_strconcat(w32_get_module_dir(),
1989 "\\share\\claws-mail\\",
1990 "ca-certificates.crt",
1996 /* Return the filepath of the claws-mail.desktop file */
1997 const gchar *get_desktop_file(void)
1999 #ifdef DESKTOPFILEPATH
2000 return DESKTOPFILEPATH;
2006 /* Return the default directory for Plugins. */
2007 const gchar *get_plugin_dir(void)
2010 static gchar *plugin_dir = NULL;
2013 plugin_dir = g_strconcat(w32_get_module_dir(),
2014 "\\lib\\claws-mail\\plugins\\",
2018 if (is_dir_exist(PLUGINDIR))
2021 static gchar *plugin_dir = NULL;
2023 plugin_dir = g_strconcat(get_rc_dir(),
2024 G_DIR_SEPARATOR_S, "plugins",
2025 G_DIR_SEPARATOR_S, NULL);
2033 /* Return the default directory for Themes. */
2034 const gchar *get_themes_dir(void)
2036 static gchar *themes_dir = NULL;
2039 themes_dir = g_strconcat(w32_get_module_dir(),
2040 "\\share\\claws-mail\\themes",
2046 const gchar *get_tmp_dir(void)
2048 static gchar *tmp_dir = NULL;
2051 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2057 gchar *get_tmp_file(void)
2060 static guint32 id = 0;
2062 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2063 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2068 const gchar *get_domain_name(void)
2071 static gchar *domain_name = NULL;
2077 if (gethostname(hostname, sizeof(hostname)) != 0) {
2078 perror("gethostname");
2079 domain_name = "unknown";
2081 hostname[sizeof(hostname) - 1] = '\0';
2082 if ((hp = my_gethostbyname(hostname)) == NULL) {
2083 perror("gethostbyname");
2084 domain_name = g_strdup(hostname);
2086 domain_name = g_strdup(hp->h_name);
2089 debug_print("domain name = %s\n", domain_name);
2098 off_t get_file_size(const gchar *file)
2102 if (g_stat(file, &s) < 0) {
2103 FILE_OP_ERROR(file, "stat");
2110 time_t get_file_mtime(const gchar *file)
2114 if (g_stat(file, &s) < 0) {
2115 FILE_OP_ERROR(file, "stat");
2122 off_t get_file_size_as_crlf(const gchar *file)
2126 gchar buf[BUFFSIZE];
2128 if ((fp = g_fopen(file, "rb")) == NULL) {
2129 FILE_OP_ERROR(file, "g_fopen");
2133 while (fgets(buf, sizeof(buf), fp) != NULL) {
2135 size += strlen(buf) + 2;
2139 FILE_OP_ERROR(file, "fgets");
2148 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2155 if (g_stat(file, &s) < 0) {
2156 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2160 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2167 /* Test on whether FILE is a relative file name. This is
2168 * straightforward for Unix but more complex for Windows. */
2169 gboolean is_relative_filename(const gchar *file)
2174 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2175 return FALSE; /* Prefixed with a hostname - this can't
2176 * be a relative name. */
2178 if ( ((*file >= 'a' && *file <= 'z')
2179 || (*file >= 'A' && *file <= 'Z'))
2181 file += 2; /* Skip drive letter. */
2183 return !(*file == '\\' || *file == '/');
2185 return !(*file == G_DIR_SEPARATOR);
2190 gboolean is_dir_exist(const gchar *dir)
2195 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2198 gboolean is_file_entry_exist(const gchar *file)
2203 return g_file_test(file, G_FILE_TEST_EXISTS);
2206 gboolean dirent_is_regular_file(struct dirent *d)
2208 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2209 if (d->d_type == DT_REG)
2211 else if (d->d_type != DT_UNKNOWN)
2215 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2218 gint change_dir(const gchar *dir)
2220 gchar *prevdir = NULL;
2223 prevdir = g_get_current_dir();
2225 if (g_chdir(dir) < 0) {
2226 FILE_OP_ERROR(dir, "chdir");
2227 if (debug_mode) g_free(prevdir);
2229 } else if (debug_mode) {
2232 cwd = g_get_current_dir();
2233 if (strcmp(prevdir, cwd) != 0)
2234 g_print("current dir: %s\n", cwd);
2242 gint make_dir(const gchar *dir)
2244 if (g_mkdir(dir, S_IRWXU) < 0) {
2245 FILE_OP_ERROR(dir, "mkdir");
2248 if (g_chmod(dir, S_IRWXU) < 0)
2249 FILE_OP_ERROR(dir, "chmod");
2254 gint make_dir_hier(const gchar *dir)
2259 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2260 parent_dir = g_strndup(dir, p - dir);
2261 if (*parent_dir != '\0') {
2262 if (!is_dir_exist(parent_dir)) {
2263 if (make_dir(parent_dir) < 0) {
2272 if (!is_dir_exist(dir)) {
2273 if (make_dir(dir) < 0)
2280 gint remove_all_files(const gchar *dir)
2283 const gchar *dir_name;
2286 prev_dir = g_get_current_dir();
2288 if (g_chdir(dir) < 0) {
2289 FILE_OP_ERROR(dir, "chdir");
2294 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2295 g_warning("failed to open directory: %s\n", dir);
2300 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2301 if (claws_unlink(dir_name) < 0)
2302 FILE_OP_ERROR(dir_name, "unlink");
2307 if (g_chdir(prev_dir) < 0) {
2308 FILE_OP_ERROR(prev_dir, "chdir");
2318 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2321 const gchar *dir_name;
2325 if (first == last) {
2326 /* Skip all the dir reading part. */
2327 gchar *filename = g_strdup_printf("%s%s%u", dir, G_DIR_SEPARATOR_S, first);
2328 if (claws_unlink(filename) < 0) {
2329 FILE_OP_ERROR(filename, "unlink");
2337 prev_dir = g_get_current_dir();
2339 if (g_chdir(dir) < 0) {
2340 FILE_OP_ERROR(dir, "chdir");
2345 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2346 g_warning("failed to open directory: %s\n", dir);
2351 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2352 file_no = to_number(dir_name);
2353 if (file_no > 0 && first <= file_no && file_no <= last) {
2354 if (is_dir_exist(dir_name))
2356 if (claws_unlink(dir_name) < 0)
2357 FILE_OP_ERROR(dir_name, "unlink");
2363 if (g_chdir(prev_dir) < 0) {
2364 FILE_OP_ERROR(prev_dir, "chdir");
2374 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2377 const gchar *dir_name;
2381 prev_dir = g_get_current_dir();
2383 if (g_chdir(dir) < 0) {
2384 FILE_OP_ERROR(dir, "chdir");
2389 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2390 FILE_OP_ERROR(dir, "opendir");
2395 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2396 file_no = to_number(dir_name);
2397 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2398 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2399 if (is_dir_exist(dir_name))
2401 if (claws_unlink(dir_name) < 0)
2402 FILE_OP_ERROR(dir_name, "unlink");
2408 if (g_chdir(prev_dir) < 0) {
2409 FILE_OP_ERROR(prev_dir, "chdir");
2419 gint remove_all_numbered_files(const gchar *dir)
2421 return remove_numbered_files(dir, 0, UINT_MAX);
2424 gint remove_dir_recursive(const gchar *dir)
2428 const gchar *dir_name;
2431 if (g_stat(dir, &s) < 0) {
2432 FILE_OP_ERROR(dir, "stat");
2433 if (ENOENT == errno) return 0;
2437 if (!S_ISDIR(s.st_mode)) {
2438 if (claws_unlink(dir) < 0) {
2439 FILE_OP_ERROR(dir, "unlink");
2446 prev_dir = g_get_current_dir();
2447 /* g_print("prev_dir = %s\n", prev_dir); */
2449 if (!path_cmp(prev_dir, dir)) {
2451 if (g_chdir("..") < 0) {
2452 FILE_OP_ERROR(dir, "chdir");
2455 prev_dir = g_get_current_dir();
2458 if (g_chdir(dir) < 0) {
2459 FILE_OP_ERROR(dir, "chdir");
2464 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2465 g_warning("failed to open directory: %s\n", dir);
2471 /* remove all files in the directory */
2472 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2473 /* g_print("removing %s\n", dir_name); */
2475 if (is_dir_exist(dir_name)) {
2476 if (remove_dir_recursive(dir_name) < 0) {
2477 g_warning("can't remove directory\n");
2481 if (claws_unlink(dir_name) < 0)
2482 FILE_OP_ERROR(dir_name, "unlink");
2488 if (g_chdir(prev_dir) < 0) {
2489 FILE_OP_ERROR(prev_dir, "chdir");
2496 if (g_rmdir(dir) < 0) {
2497 FILE_OP_ERROR(dir, "rmdir");
2504 gint rename_force(const gchar *oldpath, const gchar *newpath)
2507 if (!is_file_entry_exist(oldpath)) {
2511 if (is_file_exist(newpath)) {
2512 if (claws_unlink(newpath) < 0)
2513 FILE_OP_ERROR(newpath, "unlink");
2516 return g_rename(oldpath, newpath);
2520 * Append src file body to the tail of dest file.
2521 * Now keep_backup has no effects.
2523 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2525 FILE *src_fp, *dest_fp;
2529 gboolean err = FALSE;
2531 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2532 FILE_OP_ERROR(src, "g_fopen");
2536 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2537 FILE_OP_ERROR(dest, "g_fopen");
2542 if (change_file_mode_rw(dest_fp, dest) < 0) {
2543 FILE_OP_ERROR(dest, "chmod");
2544 g_warning("can't change file mode\n");
2547 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2548 if (n_read < sizeof(buf) && ferror(src_fp))
2550 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2551 g_warning("writing to %s failed.\n", dest);
2559 if (ferror(src_fp)) {
2560 FILE_OP_ERROR(src, "fread");
2564 if (fclose(dest_fp) == EOF) {
2565 FILE_OP_ERROR(dest, "fclose");
2577 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2579 FILE *src_fp, *dest_fp;
2582 gchar *dest_bak = NULL;
2583 gboolean err = FALSE;
2585 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2586 FILE_OP_ERROR(src, "g_fopen");
2589 if (is_file_exist(dest)) {
2590 dest_bak = g_strconcat(dest, ".bak", NULL);
2591 if (rename_force(dest, dest_bak) < 0) {
2592 FILE_OP_ERROR(dest, "rename");
2599 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2600 FILE_OP_ERROR(dest, "g_fopen");
2603 if (rename_force(dest_bak, dest) < 0)
2604 FILE_OP_ERROR(dest_bak, "rename");
2610 if (change_file_mode_rw(dest_fp, dest) < 0) {
2611 FILE_OP_ERROR(dest, "chmod");
2612 g_warning("can't change file mode\n");
2615 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2616 if (n_read < sizeof(buf) && ferror(src_fp))
2618 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2619 g_warning("writing to %s failed.\n", dest);
2624 if (rename_force(dest_bak, dest) < 0)
2625 FILE_OP_ERROR(dest_bak, "rename");
2632 if (ferror(src_fp)) {
2633 FILE_OP_ERROR(src, "fread");
2637 if (fclose(dest_fp) == EOF) {
2638 FILE_OP_ERROR(dest, "fclose");
2645 if (rename_force(dest_bak, dest) < 0)
2646 FILE_OP_ERROR(dest_bak, "rename");
2652 if (keep_backup == FALSE && dest_bak)
2653 claws_unlink(dest_bak);
2660 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2662 if (overwrite == FALSE && is_file_exist(dest)) {
2663 g_warning("move_file(): file %s already exists.", dest);
2667 if (rename_force(src, dest) == 0) return 0;
2669 if (EXDEV != errno) {
2670 FILE_OP_ERROR(src, "rename");
2674 if (copy_file(src, dest, FALSE) < 0) return -1;
2681 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2684 gint bytes_left, to_read;
2687 if (fseek(fp, offset, SEEK_SET) < 0) {
2692 bytes_left = length;
2693 to_read = MIN(bytes_left, sizeof(buf));
2695 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2696 if (n_read < to_read && ferror(fp))
2698 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2701 bytes_left -= n_read;
2702 if (bytes_left == 0)
2704 to_read = MIN(bytes_left, sizeof(buf));
2715 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2718 gboolean err = FALSE;
2720 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2721 FILE_OP_ERROR(dest, "g_fopen");
2725 if (change_file_mode_rw(dest_fp, dest) < 0) {
2726 FILE_OP_ERROR(dest, "chmod");
2727 g_warning("can't change file mode\n");
2730 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2733 if (!err && fclose(dest_fp) == EOF) {
2734 FILE_OP_ERROR(dest, "fclose");
2739 g_warning("writing to %s failed.\n", dest);
2747 /* convert line endings into CRLF. If the last line doesn't end with
2748 * linebreak, add it.
2750 gchar *canonicalize_str(const gchar *str)
2756 for (p = str; *p != '\0'; ++p) {
2763 if (p == str || *(p - 1) != '\n')
2766 out = outp = g_malloc(new_len + 1);
2767 for (p = str; *p != '\0'; ++p) {
2774 if (p == str || *(p - 1) != '\n') {
2783 gint canonicalize_file(const gchar *src, const gchar *dest)
2785 FILE *src_fp, *dest_fp;
2786 gchar buf[BUFFSIZE];
2788 gboolean err = FALSE;
2789 gboolean last_linebreak = FALSE;
2791 if (src == NULL || dest == NULL)
2794 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2795 FILE_OP_ERROR(src, "g_fopen");
2799 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2800 FILE_OP_ERROR(dest, "g_fopen");
2805 if (change_file_mode_rw(dest_fp, dest) < 0) {
2806 FILE_OP_ERROR(dest, "chmod");
2807 g_warning("can't change file mode\n");
2810 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2814 if (len == 0) break;
2815 last_linebreak = FALSE;
2817 if (buf[len - 1] != '\n') {
2818 last_linebreak = TRUE;
2819 r = fputs(buf, dest_fp);
2820 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2821 r = fputs(buf, dest_fp);
2824 r = fwrite(buf, 1, len - 1, dest_fp);
2829 r = fputs("\r\n", dest_fp);
2833 g_warning("writing to %s failed.\n", dest);
2841 if (last_linebreak == TRUE) {
2842 if (fputs("\r\n", dest_fp) == EOF)
2846 if (ferror(src_fp)) {
2847 FILE_OP_ERROR(src, "fgets");
2851 if (fclose(dest_fp) == EOF) {
2852 FILE_OP_ERROR(dest, "fclose");
2864 gint canonicalize_file_replace(const gchar *file)
2868 tmp_file = get_tmp_file();
2870 if (canonicalize_file(file, tmp_file) < 0) {
2875 if (move_file(tmp_file, file, TRUE) < 0) {
2876 g_warning("can't replace %s .\n", file);
2877 claws_unlink(tmp_file);
2886 gchar *normalize_newlines(const gchar *str)
2888 const gchar *p = str;
2891 out = outp = g_malloc(strlen(str) + 1);
2892 for (p = str; *p != '\0'; ++p) {
2894 if (*(p + 1) != '\n')
2905 gchar *get_outgoing_rfc2822_str(FILE *fp)
2907 gchar buf[BUFFSIZE];
2911 str = g_string_new(NULL);
2913 /* output header part */
2914 while (fgets(buf, sizeof(buf), fp) != NULL) {
2916 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2923 else if (next != ' ' && next != '\t') {
2927 if (fgets(buf, sizeof(buf), fp) == NULL)
2931 g_string_append(str, buf);
2932 g_string_append(str, "\r\n");
2938 /* output body part */
2939 while (fgets(buf, sizeof(buf), fp) != NULL) {
2942 g_string_append_c(str, '.');
2943 g_string_append(str, buf);
2944 g_string_append(str, "\r\n");
2948 g_string_free(str, FALSE);
2954 * Create a new boundary in a way that it is very unlikely that this
2955 * will occur in the following text. It would be easy to ensure
2956 * uniqueness if everything is either quoted-printable or base64
2957 * encoded (note that conversion is allowed), but because MIME bodies
2958 * may be nested, it may happen that the same boundary has already
2961 * boundary := 0*69<bchars> bcharsnospace
2962 * bchars := bcharsnospace / " "
2963 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2964 * "+" / "_" / "," / "-" / "." /
2965 * "/" / ":" / "=" / "?"
2967 * some special characters removed because of buggy MTAs
2970 gchar *generate_mime_boundary(const gchar *prefix)
2972 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2973 "abcdefghijklmnopqrstuvwxyz"
2978 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2979 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2982 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2986 gint change_file_mode_rw(FILE *fp, const gchar *file)
2989 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2991 return g_chmod(file, S_IRUSR|S_IWUSR);
2995 FILE *my_tmpfile(void)
2997 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2998 const gchar suffix[] = ".XXXXXX";
2999 const gchar *tmpdir;
3001 const gchar *progname;
3010 tmpdir = get_tmp_dir();
3011 tmplen = strlen(tmpdir);
3012 progname = g_get_prgname();
3013 if (progname == NULL)
3014 progname = "claws-mail";
3015 proglen = strlen(progname);
3016 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3019 memcpy(fname, tmpdir, tmplen);
3020 fname[tmplen] = G_DIR_SEPARATOR;
3021 memcpy(fname + tmplen + 1, progname, proglen);
3022 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3024 fd = mkstemp(fname);
3029 claws_unlink(fname);
3031 /* verify that we can write in the file after unlinking */
3032 if (write(fd, buf, 1) < 0) {
3039 fp = fdopen(fd, "w+b");
3047 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3052 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3056 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3057 dir, G_DIR_SEPARATOR);
3058 fd = mkstemp_name(template, filename);
3061 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3062 fd = mkstemp(*filename);
3064 return fdopen(fd, "w+");
3067 FILE *str_open_as_stream(const gchar *str)
3072 cm_return_val_if_fail(str != NULL, NULL);
3076 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3081 if (len == 0) return fp;
3083 if (fwrite(str, 1, len, fp) != len) {
3084 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3093 gint str_write_to_file(const gchar *str, const gchar *file)
3098 cm_return_val_if_fail(str != NULL, -1);
3099 cm_return_val_if_fail(file != NULL, -1);
3101 if ((fp = g_fopen(file, "wb")) == NULL) {
3102 FILE_OP_ERROR(file, "g_fopen");
3112 if (fwrite(str, 1, len, fp) != len) {
3113 FILE_OP_ERROR(file, "fwrite");
3119 if (fclose(fp) == EOF) {
3120 FILE_OP_ERROR(file, "fclose");
3128 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3135 cm_return_val_if_fail(fp != NULL, NULL);
3137 array = g_byte_array_new();
3139 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3140 if (n_read < sizeof(buf) && ferror(fp))
3142 g_byte_array_append(array, buf, n_read);
3146 FILE_OP_ERROR("file stream", "fread");
3147 g_byte_array_free(array, TRUE);
3152 g_byte_array_append(array, buf, 1);
3153 str = (gchar *)array->data;
3154 g_byte_array_free(array, FALSE);
3156 if (recode && !g_utf8_validate(str, -1, NULL)) {
3157 const gchar *src_codeset, *dest_codeset;
3159 src_codeset = conv_get_locale_charset_str();
3160 dest_codeset = CS_UTF_8;
3161 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3169 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3176 struct timeval timeout = {1, 0};
3181 cm_return_val_if_fail(file != NULL, NULL);
3183 if (g_stat(file, &s) != 0) {
3184 FILE_OP_ERROR(file, "stat");
3187 if (S_ISDIR(s.st_mode)) {
3188 g_warning("%s: is a directory\n", file);
3193 fp = g_fopen (file, "rb");
3195 FILE_OP_ERROR(file, "open");
3199 /* test whether the file is readable without blocking */
3200 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3202 FILE_OP_ERROR(file, "open");
3209 /* allow for one second */
3210 err = select(fd+1, &fds, NULL, NULL, &timeout);
3211 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3213 FILE_OP_ERROR(file, "select");
3215 g_warning("%s: doesn't seem readable\n", file);
3221 /* Now clear O_NONBLOCK */
3222 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3223 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3227 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3228 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3233 /* get the FILE pointer */
3234 fp = fdopen(fd, "rb");
3237 FILE_OP_ERROR(file, "fdopen");
3238 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3243 str = file_read_stream_to_str_full(fp, recode);
3250 gchar *file_read_to_str(const gchar *file)
3252 return file_read_to_str_full(file, TRUE);
3254 gchar *file_read_stream_to_str(FILE *fp)
3256 return file_read_stream_to_str_full(fp, TRUE);
3259 gchar *file_read_to_str_no_recode(const gchar *file)
3261 return file_read_to_str_full(file, FALSE);
3263 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3265 return file_read_stream_to_str_full(fp, FALSE);
3268 char *fgets_crlf(char *buf, int size, FILE *stream)
3270 gboolean is_cr = FALSE;
3271 gboolean last_was_cr = FALSE;
3276 while (--size > 0 && (c = getc(stream)) != EOF)
3279 is_cr = (c == '\r');
3289 last_was_cr = is_cr;
3291 if (c == EOF && cs == buf)
3299 static gint execute_async(gchar *const argv[])
3301 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3303 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3304 NULL, NULL, NULL, FALSE) == FALSE) {
3305 g_warning("Couldn't execute command: %s\n", argv[0]);
3312 static gint execute_sync(gchar *const argv[])
3316 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3319 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3320 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3321 g_warning("Couldn't execute command: %s\n", argv[0]);
3325 if (WIFEXITED(status))
3326 return WEXITSTATUS(status);
3330 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3331 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3332 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3333 g_warning("Couldn't execute command: %s\n", argv[0]);
3341 gint execute_command_line(const gchar *cmdline, gboolean async)
3346 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3348 argv = strsplit_with_quote(cmdline, " ", 0);
3351 ret = execute_async(argv);
3353 ret = execute_sync(argv);
3360 gchar *get_command_output(const gchar *cmdline)
3362 gchar *child_stdout;
3365 cm_return_val_if_fail(cmdline != NULL, NULL);
3367 debug_print("get_command_output(): executing: %s\n", cmdline);
3369 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3371 g_warning("Couldn't execute command: %s\n", cmdline);
3375 return child_stdout;
3378 static gint is_unchanged_uri_char(char c)
3389 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3395 for(i = 0; i < strlen(uri) ; i++) {
3396 if (is_unchanged_uri_char(uri[i])) {
3397 if (k + 2 >= bufsize)
3399 encoded_uri[k++] = uri[i];
3402 char * hexa = "0123456789ABCDEF";
3404 if (k + 4 >= bufsize)
3406 encoded_uri[k++] = '%';
3407 encoded_uri[k++] = hexa[uri[i] / 16];
3408 encoded_uri[k++] = hexa[uri[i] % 16];
3414 gint open_uri(const gchar *uri, const gchar *cmdline)
3418 gchar buf[BUFFSIZE];
3420 gchar encoded_uri[BUFFSIZE];
3421 cm_return_val_if_fail(uri != NULL, -1);
3423 /* an option to choose whether to use encode_uri or not ? */
3424 encode_uri(encoded_uri, BUFFSIZE, uri);
3427 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3428 !strchr(p + 2, '%'))
3429 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3432 g_warning("Open URI command-line is invalid "
3433 "(there must be only one '%%s'): %s",
3435 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3438 execute_command_line(buf, TRUE);
3440 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3443 extern osso_context_t *get_osso_context(void);
3444 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3445 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3446 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3451 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3453 gchar buf[BUFFSIZE];
3456 cm_return_val_if_fail(filepath != NULL, -1);
3459 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3460 !strchr(p + 2, '%'))
3461 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3464 g_warning("Open Text Editor command-line is invalid "
3465 "(there must be only one '%%s'): %s",
3467 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3470 execute_command_line(buf, TRUE);
3475 time_t remote_tzoffset_sec(const gchar *zone)
3477 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3483 time_t remoteoffset;
3485 strncpy(zone3, zone, 3);
3489 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3490 (c == '+' || c == '-')) {
3491 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3493 remoteoffset = -remoteoffset;
3494 } else if (!strncmp(zone, "UT" , 2) ||
3495 !strncmp(zone, "GMT", 2)) {
3497 } else if (strlen(zone3) == 3) {
3498 for (p = ustzstr; *p != '\0'; p += 3) {
3499 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3500 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3501 remoteoffset = iustz * 3600;
3507 } else if (strlen(zone3) == 1) {
3509 case 'Z': remoteoffset = 0; break;
3510 case 'A': remoteoffset = -1; break;
3511 case 'B': remoteoffset = -2; break;
3512 case 'C': remoteoffset = -3; break;
3513 case 'D': remoteoffset = -4; break;
3514 case 'E': remoteoffset = -5; break;
3515 case 'F': remoteoffset = -6; break;
3516 case 'G': remoteoffset = -7; break;
3517 case 'H': remoteoffset = -8; break;
3518 case 'I': remoteoffset = -9; break;
3519 case 'K': remoteoffset = -10; break; /* J is not used */
3520 case 'L': remoteoffset = -11; break;
3521 case 'M': remoteoffset = -12; break;
3522 case 'N': remoteoffset = 1; break;
3523 case 'O': remoteoffset = 2; break;
3524 case 'P': remoteoffset = 3; break;
3525 case 'Q': remoteoffset = 4; break;
3526 case 'R': remoteoffset = 5; break;
3527 case 'S': remoteoffset = 6; break;
3528 case 'T': remoteoffset = 7; break;
3529 case 'U': remoteoffset = 8; break;
3530 case 'V': remoteoffset = 9; break;
3531 case 'W': remoteoffset = 10; break;
3532 case 'X': remoteoffset = 11; break;
3533 case 'Y': remoteoffset = 12; break;
3534 default: remoteoffset = 0; break;
3536 remoteoffset = remoteoffset * 3600;
3540 return remoteoffset;
3543 time_t tzoffset_sec(time_t *now)
3547 struct tm buf1, buf2;
3549 if (now && *now < 0)
3552 gmt = *gmtime_r(now, &buf1);
3553 lt = localtime_r(now, &buf2);
3555 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3557 if (lt->tm_year < gmt.tm_year)
3559 else if (lt->tm_year > gmt.tm_year)
3561 else if (lt->tm_yday < gmt.tm_yday)
3563 else if (lt->tm_yday > gmt.tm_yday)
3566 if (off >= 24 * 60) /* should be impossible */
3567 off = 23 * 60 + 59; /* if not, insert silly value */
3568 if (off <= -24 * 60)
3569 off = -(23 * 60 + 59);
3574 /* calculate timezone offset */
3575 gchar *tzoffset(time_t *now)
3577 static gchar offset_string[6];
3581 struct tm buf1, buf2;
3583 if (now && *now < 0)
3586 gmt = *gmtime_r(now, &buf1);
3587 lt = localtime_r(now, &buf2);
3589 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3591 if (lt->tm_year < gmt.tm_year)
3593 else if (lt->tm_year > gmt.tm_year)
3595 else if (lt->tm_yday < gmt.tm_yday)
3597 else if (lt->tm_yday > gmt.tm_yday)
3605 if (off >= 24 * 60) /* should be impossible */
3606 off = 23 * 60 + 59; /* if not, insert silly value */
3608 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3610 return offset_string;
3613 void get_rfc822_date(gchar *buf, gint len)
3617 gchar day[4], mon[4];
3618 gint dd, hh, mm, ss, yyyy;
3620 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)
3852 lt = localtime_r(&t, &buft);
3854 if (user_addr != NULL)
3855 addr = g_strdup_printf(".%s", user_addr);
3856 else if (strlen(buf) != 0)
3857 addr = g_strdup_printf("@%s", buf);
3859 addr = g_strdup_printf("@%s", get_domain_name());
3861 /* Replace all @ but the last one in addr, with underscores.
3862 * RFC 2822 States that msg-id syntax only allows one @.
3864 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3865 *(strchr(addr, '@')) = '_';
3867 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3868 lt->tm_year + 1900, lt->tm_mon + 1,
3869 lt->tm_mday, lt->tm_hour,
3870 lt->tm_min, lt->tm_sec,
3871 (guint) rand(), addr);
3878 quote_cmd_argument()
3880 return a quoted string safely usable in argument of a command.
3882 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3885 gint quote_cmd_argument(gchar * result, guint size,
3895 for(p = path ; * p != '\0' ; p ++) {
3897 if (isalnum((guchar)*p) || (* p == '/')) {
3898 if (remaining > 0) {
3904 result[size - 1] = '\0';
3909 if (remaining >= 2) {
3917 result[size - 1] = '\0';
3922 if (remaining > 0) {
3926 result[size - 1] = '\0';
3940 static void g_node_map_recursive(GNode *node, gpointer data)
3942 GNodeMapData *mapdata = (GNodeMapData *) data;
3944 GNodeMapData newmapdata;
3947 newdata = mapdata->func(node->data, mapdata->data);
3948 if (newdata != NULL) {
3949 newnode = g_node_new(newdata);
3950 g_node_append(mapdata->parent, newnode);
3952 newmapdata.parent = newnode;
3953 newmapdata.func = mapdata->func;
3954 newmapdata.data = mapdata->data;
3956 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3960 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3963 GNodeMapData mapdata;
3965 cm_return_val_if_fail(node != NULL, NULL);
3966 cm_return_val_if_fail(func != NULL, NULL);
3968 root = g_node_new(func(node->data, data));
3970 mapdata.parent = root;
3971 mapdata.func = func;
3972 mapdata.data = data;
3974 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3979 #define HEX_TO_INT(val, hex) \
3983 if ('0' <= c && c <= '9') { \
3985 } else if ('a' <= c && c <= 'f') { \
3986 val = c - 'a' + 10; \
3987 } else if ('A' <= c && c <= 'F') { \
3988 val = c - 'A' + 10; \
3994 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4001 if (hi == -1 || lo == -1)
4004 *out = (hi << 4) + lo;
4008 #define INT_TO_HEX(hex, val) \
4011 hex = '0' + (val); \
4013 hex = 'A' + (val) - 10; \
4016 void get_hex_str(gchar *out, guchar ch)
4020 INT_TO_HEX(hex, ch >> 4);
4022 INT_TO_HEX(hex, ch & 0x0f);
4028 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4030 #define G_PRINT_REF g_print
4034 *\brief Register ref counted pointer. It is based on GBoxed, so should
4035 * work with anything that uses the GType system. The semantics
4036 * are similar to a C++ auto pointer, with the exception that
4037 * C doesn't have automatic closure (calling destructors) when
4038 * exiting a block scope.
4039 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4040 * function directly.
4042 *\return GType A GType type.
4044 GType g_auto_pointer_register(void)
4046 static GType auto_pointer_type;
4047 if (!auto_pointer_type)
4049 g_boxed_type_register_static
4050 ("G_TYPE_AUTO_POINTER",
4051 (GBoxedCopyFunc) g_auto_pointer_copy,
4052 (GBoxedFreeFunc) g_auto_pointer_free);
4053 return auto_pointer_type;
4057 *\brief Structure with g_new() allocated pointer guarded by the
4060 typedef struct AutoPointerRef {
4061 void (*free) (gpointer);
4067 *\brief The auto pointer opaque structure that references the
4068 * pointer guard block.
4070 typedef struct AutoPointer {
4071 AutoPointerRef *ref;
4072 gpointer ptr; /*!< access to protected pointer */
4076 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4080 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4081 * ... when assigning, copying and freeing storage elements
4083 * gtk_list_store_new(N_S_COLUMNS,
4084 * G_TYPE_AUTO_POINTER,
4088 * Template *precious_data = g_new0(Template, 1);
4089 * g_pointer protect = g_auto_pointer_new(precious_data);
4091 * gtk_list_store_set(container, &iter,
4095 * ... the gtk_list_store has copied the pointer and
4096 * ... incremented its reference count, we should free
4097 * ... the auto pointer (in C++ a destructor would do
4098 * ... this for us when leaving block scope)
4100 * g_auto_pointer_free(protect);
4102 * ... gtk_list_store_set() now manages the data. When
4103 * ... *explicitly* requesting a pointer from the list
4104 * ... store, don't forget you get a copy that should be
4105 * ... freed with g_auto_pointer_free() eventually.
4109 *\param pointer Pointer to be guarded.
4111 *\return GAuto * Pointer that should be used in containers with
4114 GAuto *g_auto_pointer_new(gpointer p)
4116 AutoPointerRef *ref;
4122 ref = g_new0(AutoPointerRef, 1);
4123 ptr = g_new0(AutoPointer, 1);
4133 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4139 *\brief Allocate an autopointer using the passed \a free function to
4140 * free the guarded pointer
4142 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4149 aptr = g_auto_pointer_new(p);
4150 aptr->ref->free = free_;
4154 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4156 if (auto_ptr == NULL)
4158 return ((AutoPointer *) auto_ptr)->ptr;
4162 *\brief Copies an auto pointer by. It's mostly not necessary
4163 * to call this function directly, unless you copy/assign
4164 * the guarded pointer.
4166 *\param auto_ptr Auto pointer returned by previous call to
4167 * g_auto_pointer_new_XXX()
4169 *\return gpointer An auto pointer
4171 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4174 AutoPointerRef *ref;
4177 if (auto_ptr == NULL)
4182 newp = g_new0(AutoPointer, 1);
4185 newp->ptr = ref->pointer;
4189 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4195 *\brief Free an auto pointer
4197 void g_auto_pointer_free(GAuto *auto_ptr)
4200 AutoPointerRef *ref;
4202 if (auto_ptr == NULL)
4208 if (--(ref->cnt) == 0) {
4210 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4212 ref->free(ref->pointer);
4217 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4222 void replace_returns(gchar *str)
4227 while (strstr(str, "\n")) {
4228 *strstr(str, "\n") = ' ';
4230 while (strstr(str, "\r")) {
4231 *strstr(str, "\r") = ' ';
4235 /* get_uri_part() - retrieves a URI starting from scanpos.
4236 Returns TRUE if succesful */
4237 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4238 const gchar **bp, const gchar **ep, gboolean hdr)
4241 gint parenthese_cnt = 0;
4243 cm_return_val_if_fail(start != NULL, FALSE);
4244 cm_return_val_if_fail(scanpos != NULL, FALSE);
4245 cm_return_val_if_fail(bp != NULL, FALSE);
4246 cm_return_val_if_fail(ep != NULL, FALSE);
4250 /* find end point of URI */
4251 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4252 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4253 !IS_ASCII(*(const guchar *)ep_) ||
4254 strchr("[]{}<>\"", *ep_)) {
4256 } else if (strchr("(", *ep_)) {
4258 } else if (strchr(")", *ep_)) {
4259 if (parenthese_cnt > 0)
4266 /* no punctuation at end of string */
4268 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4269 * should pass some URI type to this function and decide on that whether
4270 * to perform punctuation stripping */
4272 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4274 for (; ep_ - 1 > scanpos + 1 &&
4275 IS_REAL_PUNCT(*(ep_ - 1));
4279 #undef IS_REAL_PUNCT
4286 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4288 while (bp && *bp && g_ascii_isspace(*bp))
4290 return g_strndup(bp, ep - bp);
4293 /* valid mail address characters */
4294 #define IS_RFC822_CHAR(ch) \
4298 !g_ascii_isspace(ch) && \
4299 !strchr("(),;<>\"", (ch)))
4301 /* alphabet and number within 7bit ASCII */
4302 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4303 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4305 static GHashTable *create_domain_tab(void)
4307 static const gchar *toplvl_domains [] = {
4309 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4310 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4311 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4312 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4313 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4314 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4315 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4316 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4317 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4318 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4319 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4320 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4321 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4322 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4323 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4324 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4325 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4326 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4327 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4328 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4329 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4330 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4331 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4332 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4333 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4334 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4337 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4339 cm_return_val_if_fail(htab, NULL);
4340 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4341 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4345 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4347 const gint MAX_LVL_DOM_NAME_LEN = 6;
4348 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4349 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4352 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4355 for (p = buf; p < m && first < last; *p++ = *first++)
4359 return g_hash_table_lookup(tab, buf) != NULL;
4362 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4363 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4364 const gchar **bp, const gchar **ep, gboolean hdr)
4366 /* more complex than the uri part because we need to scan back and forward starting from
4367 * the scan position. */
4368 gboolean result = FALSE;
4369 const gchar *bp_ = NULL;
4370 const gchar *ep_ = NULL;
4371 static GHashTable *dom_tab;
4372 const gchar *last_dot = NULL;
4373 const gchar *prelast_dot = NULL;
4374 const gchar *last_tld_char = NULL;
4376 /* the informative part of the email address (describing the name
4377 * of the email address owner) may contain quoted parts. the
4378 * closure stack stores the last encountered quotes. */
4379 gchar closure_stack[128];
4380 gchar *ptr = closure_stack;
4382 cm_return_val_if_fail(start != NULL, FALSE);
4383 cm_return_val_if_fail(scanpos != NULL, FALSE);
4384 cm_return_val_if_fail(bp != NULL, FALSE);
4385 cm_return_val_if_fail(ep != NULL, FALSE);
4388 const gchar *start_quote = NULL;
4389 const gchar *end_quote = NULL;
4391 /* go to the real start */
4392 if (start[0] == ',')
4394 if (start[0] == ';')
4396 while (start[0] == '\n' || start[0] == '\r')
4398 while (start[0] == ' ' || start[0] == '\t')
4403 /* check if there are quotes (to skip , in them) */
4404 if (*start == '"') {
4405 start_quote = start;
4407 end_quote = strstr(start, "\"");
4413 /* skip anything between quotes */
4414 if (start_quote && end_quote) {
4419 /* find end (either , or ; or end of line) */
4420 if (strstr(start, ",") && strstr(start, ";"))
4421 *ep = strstr(start,",") < strstr(start, ";")
4422 ? strstr(start, ",") : strstr(start, ";");
4423 else if (strstr(start, ","))
4424 *ep = strstr(start, ",");
4425 else if (strstr(start, ";"))
4426 *ep = strstr(start, ";");
4428 *ep = start+strlen(start);
4430 /* go back to real start */
4431 if (start_quote && end_quote) {
4432 start = start_quote;
4435 /* check there's still an @ in that, or search
4436 * further if possible */
4437 if (strstr(start, "@") && strstr(start, "@") < *ep)
4439 else if (*ep < start+strlen(start)) {
4442 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4450 dom_tab = create_domain_tab();
4451 cm_return_val_if_fail(dom_tab, FALSE);
4453 /* scan start of address */
4454 for (bp_ = scanpos - 1;
4455 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4458 /* TODO: should start with an alnum? */
4460 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4463 if (bp_ != scanpos) {
4464 /* scan end of address */
4465 for (ep_ = scanpos + 1;
4466 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4468 prelast_dot = last_dot;
4470 if (*(last_dot + 1) == '.') {
4471 if (prelast_dot == NULL)
4473 last_dot = prelast_dot;
4478 /* TODO: really should terminate with an alnum? */
4479 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4484 if (last_dot == NULL)
4486 if (last_dot >= ep_)
4487 last_dot = prelast_dot;
4488 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4492 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4493 if (*last_tld_char == '?')
4496 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4503 if (!result) return FALSE;
4505 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4506 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4507 && IS_RFC822_CHAR(*(ep_ + 3))) {
4508 /* this informative part with an @ in it is
4509 * followed by the email address */
4512 /* go to matching '>' (or next non-rfc822 char, like \n) */
4513 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4516 /* include the bracket */
4517 if (*ep_ == '>') ep_++;
4519 /* include the leading quote */
4527 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4528 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4531 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4532 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4535 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4536 #define IN_STACK() (ptr > closure_stack)
4537 /* has underrun check */
4538 #define POP_STACK() if(IN_STACK()) --ptr
4539 /* has overrun check */
4540 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4541 /* has underrun check */
4542 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4546 /* scan for the informative part. */
4547 for (bp_ -= 2; bp_ >= start; bp_--) {
4548 /* if closure on the stack keep scanning */
4549 if (PEEK_STACK() == *bp_) {
4553 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4558 /* if nothing in the closure stack, do the special conditions
4559 * the following if..else expression simply checks whether
4560 * a token is acceptable. if not acceptable, the clause
4561 * should terminate the loop with a 'break' */
4562 if (!PEEK_STACK()) {
4564 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4565 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4566 /* hyphens are allowed, but only in
4568 } else if (strchr(" \"'", *bp_)) {
4569 /* but anything not being a punctiation
4572 break; /* anything else is rejected */
4579 /* scan forward (should start with an alnum) */
4580 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4596 #undef IS_ASCII_ALNUM
4597 #undef IS_RFC822_CHAR
4599 gchar *make_email_string(const gchar *bp, const gchar *ep)
4601 /* returns a mailto: URI; mailto: is also used to detect the
4602 * uri type later on in the button_pressed signal handler */
4606 tmp = g_strndup(bp, ep - bp);
4607 result = g_strconcat("mailto:", tmp, NULL);
4613 gchar *make_http_string(const gchar *bp, const gchar *ep)
4615 /* returns an http: URI; */
4619 while (bp && *bp && g_ascii_isspace(*bp))
4621 tmp = g_strndup(bp, ep - bp);
4622 result = g_strconcat("http://", tmp, NULL);
4628 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4630 FILE *fp = g_fopen(path, "rb");
4631 gchar buf[BUFFSIZE];
4632 gchar *result = NULL;
4635 while (fgets(buf, sizeof (buf), fp) != NULL) {
4636 gchar **parts = g_strsplit(buf, ";", 3);
4637 gchar *trimmed = parts[0];
4638 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4640 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4641 trimmed[strlen(trimmed)-1] = '\0';
4643 if (!strcmp(trimmed, type)) {
4644 gboolean needsterminal = FALSE;
4645 if (parts[2] && strstr(parts[2], "needsterminal")) {
4646 needsterminal = TRUE;
4648 if (parts[2] && strstr(parts[2], "test=")) {
4649 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4650 gchar *testcmd = orig_testcmd;
4651 if (strstr(testcmd,";"))
4652 *(strstr(testcmd,";")) = '\0';
4653 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4655 while (testcmd[strlen(testcmd)-1] == '\n')
4656 testcmd[strlen(testcmd)-1] = '\0';
4657 while (testcmd[strlen(testcmd)-1] == '\r')
4658 testcmd[strlen(testcmd)-1] = '\0';
4659 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4660 testcmd[strlen(testcmd)-1] = '\0';
4662 if (strstr(testcmd, "%s")) {
4663 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4664 gint res = system(tmp);
4666 g_free(orig_testcmd);
4673 gint res = system(testcmd);
4674 g_free(orig_testcmd);
4684 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4686 while (trimmed[strlen(trimmed)-1] == '\n')
4687 trimmed[strlen(trimmed)-1] = '\0';
4688 while (trimmed[strlen(trimmed)-1] == '\r')
4689 trimmed[strlen(trimmed)-1] = '\0';
4690 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4691 trimmed[strlen(trimmed)-1] = '\0';
4692 result = g_strdup(trimmed);
4695 /* if there are no single quotes around %s, add them.
4696 * '.*%s.*' is ok, as in display 'png:%s'
4698 if (strstr(result, "%s")
4699 && !(strstr(result, "'") < strstr(result,"%s") &&
4700 strstr(strstr(result,"%s"), "'"))) {
4701 gchar *start = g_strdup(result);
4702 gchar *end = g_strdup(strstr(result, "%s")+2);
4704 *strstr(start, "%s") = '\0';
4705 tmp = g_strconcat(start,"'%s'",end, NULL);
4711 if (needsterminal) {
4712 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4723 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4725 gchar *result = NULL;
4729 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4730 result = mailcap_get_command_in_file(path, type, file_to_open);
4734 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4738 void mailcap_update_default(const gchar *type, const gchar *command)
4740 gchar *path = NULL, *outpath = NULL;
4741 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4742 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4743 FILE *fp = g_fopen(path, "rb");
4744 FILE *outfp = g_fopen(outpath, "wb");
4745 gchar buf[BUFFSIZE];
4746 gboolean err = FALSE;
4754 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4755 gchar **parts = g_strsplit(buf, ";", 3);
4756 gchar *trimmed = parts[0];
4757 while (trimmed[0] == ' ')
4759 while (trimmed[strlen(trimmed)-1] == ' ')
4760 trimmed[strlen(trimmed)-1] = '\0';
4762 if (!strcmp(trimmed, type)) {
4767 if(fputs(buf, outfp) == EOF) {
4774 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4780 if (fclose(outfp) == EOF)
4784 g_rename(outpath, path);
4790 gint copy_dir(const gchar *src, const gchar *dst)
4795 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4796 g_warning("failed to open directory: %s\n", src);
4800 if (make_dir(dst) < 0)
4803 while ((name = g_dir_read_name(dir)) != NULL) {
4804 gchar *old_file, *new_file;
4805 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4806 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4807 debug_print("copying: %s -> %s\n", old_file, new_file);
4808 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4809 gint r = copy_file(old_file, new_file, TRUE);
4816 /* Windows has no symlinks. Or well, Vista seems to
4817 have something like this but the semantics might be
4818 different. Thus we don't use it under Windows. */
4819 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4822 gchar *target = g_file_read_link(old_file, &error);
4824 r = symlink(target, new_file);
4831 #endif /*G_OS_WIN32*/
4832 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4833 gint r = copy_dir(old_file, new_file);
4844 /* crude test to see if a file is an email. */
4845 gboolean file_is_email (const gchar *filename)
4851 if (filename == NULL)
4853 if ((fp = g_fopen(filename, "rb")) == NULL)
4855 while (i < 60 && score < 3
4856 && fgets(buffer, sizeof (buffer), fp) > 0) {
4857 if (!strncmp(buffer, "From:", strlen("From:")))
4859 else if (!strncmp(buffer, "Date:", strlen("Date:")))
4861 else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
4863 else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4868 return (score >= 3);
4871 gboolean sc_g_list_bigger(GList *list, gint max)
4875 while (cur && i <= max+1) {
4882 gboolean sc_g_slist_bigger(GSList *list, gint max)
4886 while (cur && i <= max+1) {
4893 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4894 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4895 NULL, NULL, NULL, NULL, NULL, NULL};
4896 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4897 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4898 NULL, NULL, NULL, NULL, NULL, NULL};
4900 gint daynames_len[] = {0,0,0,0,0,0,0};
4901 gint monthnames_len[] = {0,0,0,0,0,0,
4903 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4904 gint s_monthnames_len[] = {0,0,0,0,0,0,
4906 const gchar *s_am_up = NULL;
4907 const gchar *s_pm_up = NULL;
4908 const gchar *s_am_low = NULL;
4909 const gchar *s_pm_low = NULL;
4911 gint s_am_up_len = 0;
4912 gint s_pm_up_len = 0;
4913 gint s_am_low_len = 0;
4914 gint s_pm_low_len = 0;
4916 const gchar *def_loc_format = NULL;
4917 const gchar *date_loc_format = NULL;
4918 const gchar *time_loc_format = NULL;
4919 const gchar *time_am_pm = NULL;
4921 static gboolean time_names_init_done = FALSE;
4923 static void init_time_names(void)
4927 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4928 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4929 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4930 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4931 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4932 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4933 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4935 monthnames[0] = Q_("Complete month name for use by strftime|January");
4936 monthnames[1] = Q_("Complete month name for use by strftime|February");
4937 monthnames[2] = Q_("Complete month name for use by strftime|March");
4938 monthnames[3] = Q_("Complete month name for use by strftime|April");
4939 monthnames[4] = Q_("Complete month name for use by strftime|May");
4940 monthnames[5] = Q_("Complete month name for use by strftime|June");
4941 monthnames[6] = Q_("Complete month name for use by strftime|July");
4942 monthnames[7] = Q_("Complete month name for use by strftime|August");
4943 monthnames[8] = Q_("Complete month name for use by strftime|September");
4944 monthnames[9] = Q_("Complete month name for use by strftime|October");
4945 monthnames[10] = Q_("Complete month name for use by strftime|November");
4946 monthnames[11] = Q_("Complete month name for use by strftime|December");
4948 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4949 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4950 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4951 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4952 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4953 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4954 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4956 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4957 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4958 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4959 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4960 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4961 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4962 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4963 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4964 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4965 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4966 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4967 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4969 for (i = 0; i < 7; i++) {
4970 daynames_len[i] = strlen(daynames[i]);
4971 s_daynames_len[i] = strlen(s_daynames[i]);
4973 for (i = 0; i < 12; i++) {
4974 monthnames_len[i] = strlen(monthnames[i]);
4975 s_monthnames_len[i] = strlen(s_monthnames[i]);
4978 s_am_up = Q_("For use by strftime (morning)|AM");
4979 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4980 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4981 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4983 s_am_up_len = strlen(s_am_up);
4984 s_pm_up_len = strlen(s_pm_up);
4985 s_am_low_len = strlen(s_am_low);
4986 s_pm_low_len = strlen(s_pm_low);
4988 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4989 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4990 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4992 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4994 time_names_init_done = TRUE;
4997 #define CHECK_SIZE() { \
4998 total_done += len; \
4999 if (total_done >= buflen) { \
5000 buf[buflen-1] = '\0'; \
5005 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5007 gchar *curpos = buf;
5008 gint total_done = 0;
5009 gchar subbuf[64], subfmt[64];
5010 static time_t last_tzset = (time_t)0;
5012 if (!time_names_init_done)
5015 if (format == NULL || lt == NULL)
5018 if (last_tzset != time(NULL)) {
5020 last_tzset = time(NULL);
5023 if (*format == '%') {
5024 gint len = 0, tmp = 0;
5028 len = 1; CHECK_SIZE();
5032 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5033 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5036 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5037 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5041 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5042 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5045 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5046 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5049 fast_strftime(subbuf, 64, def_loc_format, lt);
5050 len = strlen(subbuf); CHECK_SIZE();
5051 strncpy2(curpos, subbuf, buflen - total_done);
5054 total_done += 2; CHECK_SIZE();
5055 tmp = (lt->tm_year + 1900)/100;
5056 *curpos++ = '0'+(tmp / 10);
5057 *curpos++ = '0'+(tmp % 10);
5060 total_done += 2; CHECK_SIZE();
5061 *curpos++ = '0'+(lt->tm_mday / 10);
5062 *curpos++ = '0'+(lt->tm_mday % 10);
5065 total_done += 8; CHECK_SIZE();
5066 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5067 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5069 *curpos++ = '0'+(lt->tm_mday / 10);
5070 *curpos++ = '0'+(lt->tm_mday % 10);
5072 tmp = lt->tm_year%100;
5073 *curpos++ = '0'+(tmp / 10);
5074 *curpos++ = '0'+(tmp % 10);
5077 len = 2; CHECK_SIZE();
5078 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5081 len = 10; CHECK_SIZE();
5082 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5083 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5086 total_done += 2; CHECK_SIZE();
5087 *curpos++ = '0'+(lt->tm_hour / 10);
5088 *curpos++ = '0'+(lt->tm_hour % 10);
5091 total_done += 2; CHECK_SIZE();
5097 *curpos++ = '0'+(tmp / 10);
5098 *curpos++ = '0'+(tmp % 10);
5101 len = 3; CHECK_SIZE();
5102 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5105 len = 2; CHECK_SIZE();
5106 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5109 len = 2; CHECK_SIZE();
5115 snprintf(curpos, buflen - total_done, "%2d", tmp);
5118 total_done += 2; CHECK_SIZE();
5119 tmp = lt->tm_mon + 1;
5120 *curpos++ = '0'+(tmp / 10);
5121 *curpos++ = '0'+(tmp % 10);
5124 total_done += 2; CHECK_SIZE();
5125 *curpos++ = '0'+(lt->tm_min / 10);
5126 *curpos++ = '0'+(lt->tm_min % 10);
5129 len = 1; CHECK_SIZE();
5133 if (lt->tm_hour >= 12) {
5134 len = s_pm_up_len; CHECK_SIZE();
5135 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5137 len = s_am_up_len; CHECK_SIZE();
5138 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5142 if (lt->tm_hour >= 12) {
5143 len = s_pm_low_len; CHECK_SIZE();
5144 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5146 len = s_am_low_len; CHECK_SIZE();
5147 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5151 fast_strftime(subbuf, 64, time_am_pm, lt);
5152 len = strlen(subbuf); CHECK_SIZE();
5153 strncpy2(curpos, subbuf, buflen - total_done);
5156 total_done += 5; CHECK_SIZE();
5157 *curpos++ = '0'+(lt->tm_hour / 10);
5158 *curpos++ = '0'+(lt->tm_hour % 10);
5160 *curpos++ = '0'+(lt->tm_min / 10);
5161 *curpos++ = '0'+(lt->tm_min % 10);
5164 snprintf(subbuf, 64, "%ld", mktime(lt));
5165 len = strlen(subbuf); CHECK_SIZE();
5166 strncpy2(curpos, subbuf, buflen - total_done);
5169 total_done += 2; CHECK_SIZE();
5170 *curpos++ = '0'+(lt->tm_sec / 10);
5171 *curpos++ = '0'+(lt->tm_sec % 10);
5174 len = 1; CHECK_SIZE();
5178 total_done += 8; CHECK_SIZE();
5179 *curpos++ = '0'+(lt->tm_hour / 10);
5180 *curpos++ = '0'+(lt->tm_hour % 10);
5182 *curpos++ = '0'+(lt->tm_min / 10);
5183 *curpos++ = '0'+(lt->tm_min % 10);
5185 *curpos++ = '0'+(lt->tm_sec / 10);
5186 *curpos++ = '0'+(lt->tm_sec % 10);
5189 len = 1; CHECK_SIZE();
5190 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5193 len = 1; CHECK_SIZE();
5194 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5197 fast_strftime(subbuf, 64, date_loc_format, lt);
5198 len = strlen(subbuf); CHECK_SIZE();
5199 strncpy2(curpos, subbuf, buflen - total_done);
5202 fast_strftime(subbuf, 64, time_loc_format, lt);
5203 len = strlen(subbuf); CHECK_SIZE();
5204 strncpy2(curpos, subbuf, buflen - total_done);
5207 total_done += 2; CHECK_SIZE();
5208 tmp = lt->tm_year%100;
5209 *curpos++ = '0'+(tmp / 10);
5210 *curpos++ = '0'+(tmp % 10);
5213 len = 4; CHECK_SIZE();
5214 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5224 /* let these complicated ones be done with the libc */
5225 snprintf(subfmt, 64, "%%%c", *format);
5226 strftime(subbuf, 64, subfmt, lt);
5227 len = strlen(subbuf); CHECK_SIZE();
5228 strncpy2(curpos, subbuf, buflen - total_done);
5232 /* let these complicated modifiers be done with the libc */
5233 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5234 strftime(subbuf, 64, subfmt, lt);
5235 len = strlen(subbuf); CHECK_SIZE();
5236 strncpy2(curpos, subbuf, buflen - total_done);
5240 if (format && *format)
5241 g_warning("format error (%c)", *format);
5248 int len = 1; CHECK_SIZE();
5249 *curpos++ = *format++;
5256 gboolean prefs_common_get_use_shred(void);
5260 #define WEXITSTATUS(x) (x)
5263 int claws_unlink(const gchar *filename)
5266 static int found_shred = -1;
5267 static const gchar *args[4];
5269 if (filename == NULL)
5272 if (prefs_common_get_use_shred()) {
5273 if (found_shred == -1) {
5275 args[0] = g_find_program_in_path("shred");
5276 debug_print("found shred: %s\n", args[0]);
5277 found_shred = (args[0] != NULL) ? 1:0;
5281 if (found_shred == 1) {
5282 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5283 if (s.st_nlink == 1) {
5286 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5287 NULL, NULL, NULL, NULL, &status, NULL);
5288 debug_print("%s %s exited with status %d\n",
5289 args[0], filename, WEXITSTATUS(status));
5290 if (truncate(filename, 0) < 0)
5291 g_warning("couln't truncate");
5296 return g_unlink(filename);