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 prev_dir = g_get_current_dir();
2327 if (g_chdir(dir) < 0) {
2328 FILE_OP_ERROR(dir, "chdir");
2333 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2334 g_warning("failed to open directory: %s\n", dir);
2339 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2340 file_no = to_number(dir_name);
2341 if (file_no > 0 && first <= file_no && file_no <= last) {
2342 if (is_dir_exist(dir_name))
2344 if (claws_unlink(dir_name) < 0)
2345 FILE_OP_ERROR(dir_name, "unlink");
2351 if (g_chdir(prev_dir) < 0) {
2352 FILE_OP_ERROR(prev_dir, "chdir");
2362 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2365 const gchar *dir_name;
2369 prev_dir = g_get_current_dir();
2371 if (g_chdir(dir) < 0) {
2372 FILE_OP_ERROR(dir, "chdir");
2377 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2378 FILE_OP_ERROR(dir, "opendir");
2383 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2384 file_no = to_number(dir_name);
2385 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2386 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2387 if (is_dir_exist(dir_name))
2389 if (claws_unlink(dir_name) < 0)
2390 FILE_OP_ERROR(dir_name, "unlink");
2396 if (g_chdir(prev_dir) < 0) {
2397 FILE_OP_ERROR(prev_dir, "chdir");
2407 gint remove_all_numbered_files(const gchar *dir)
2409 return remove_numbered_files(dir, 0, UINT_MAX);
2412 gint remove_dir_recursive(const gchar *dir)
2416 const gchar *dir_name;
2419 if (g_stat(dir, &s) < 0) {
2420 FILE_OP_ERROR(dir, "stat");
2421 if (ENOENT == errno) return 0;
2425 if (!S_ISDIR(s.st_mode)) {
2426 if (claws_unlink(dir) < 0) {
2427 FILE_OP_ERROR(dir, "unlink");
2434 prev_dir = g_get_current_dir();
2435 /* g_print("prev_dir = %s\n", prev_dir); */
2437 if (!path_cmp(prev_dir, dir)) {
2439 if (g_chdir("..") < 0) {
2440 FILE_OP_ERROR(dir, "chdir");
2443 prev_dir = g_get_current_dir();
2446 if (g_chdir(dir) < 0) {
2447 FILE_OP_ERROR(dir, "chdir");
2452 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2453 g_warning("failed to open directory: %s\n", dir);
2459 /* remove all files in the directory */
2460 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2461 /* g_print("removing %s\n", dir_name); */
2463 if (is_dir_exist(dir_name)) {
2464 if (remove_dir_recursive(dir_name) < 0) {
2465 g_warning("can't remove directory\n");
2469 if (claws_unlink(dir_name) < 0)
2470 FILE_OP_ERROR(dir_name, "unlink");
2476 if (g_chdir(prev_dir) < 0) {
2477 FILE_OP_ERROR(prev_dir, "chdir");
2484 if (g_rmdir(dir) < 0) {
2485 FILE_OP_ERROR(dir, "rmdir");
2492 gint rename_force(const gchar *oldpath, const gchar *newpath)
2495 if (!is_file_entry_exist(oldpath)) {
2499 if (is_file_exist(newpath)) {
2500 if (claws_unlink(newpath) < 0)
2501 FILE_OP_ERROR(newpath, "unlink");
2504 return g_rename(oldpath, newpath);
2508 * Append src file body to the tail of dest file.
2509 * Now keep_backup has no effects.
2511 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2513 FILE *src_fp, *dest_fp;
2517 gboolean err = FALSE;
2519 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2520 FILE_OP_ERROR(src, "g_fopen");
2524 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2525 FILE_OP_ERROR(dest, "g_fopen");
2530 if (change_file_mode_rw(dest_fp, dest) < 0) {
2531 FILE_OP_ERROR(dest, "chmod");
2532 g_warning("can't change file mode\n");
2535 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2536 if (n_read < sizeof(buf) && ferror(src_fp))
2538 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2539 g_warning("writing to %s failed.\n", dest);
2547 if (ferror(src_fp)) {
2548 FILE_OP_ERROR(src, "fread");
2552 if (fclose(dest_fp) == EOF) {
2553 FILE_OP_ERROR(dest, "fclose");
2565 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2567 FILE *src_fp, *dest_fp;
2570 gchar *dest_bak = NULL;
2571 gboolean err = FALSE;
2573 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2574 FILE_OP_ERROR(src, "g_fopen");
2577 if (is_file_exist(dest)) {
2578 dest_bak = g_strconcat(dest, ".bak", NULL);
2579 if (rename_force(dest, dest_bak) < 0) {
2580 FILE_OP_ERROR(dest, "rename");
2587 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2588 FILE_OP_ERROR(dest, "g_fopen");
2591 if (rename_force(dest_bak, dest) < 0)
2592 FILE_OP_ERROR(dest_bak, "rename");
2598 if (change_file_mode_rw(dest_fp, dest) < 0) {
2599 FILE_OP_ERROR(dest, "chmod");
2600 g_warning("can't change file mode\n");
2603 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2604 if (n_read < sizeof(buf) && ferror(src_fp))
2606 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2607 g_warning("writing to %s failed.\n", dest);
2612 if (rename_force(dest_bak, dest) < 0)
2613 FILE_OP_ERROR(dest_bak, "rename");
2620 if (ferror(src_fp)) {
2621 FILE_OP_ERROR(src, "fread");
2625 if (fclose(dest_fp) == EOF) {
2626 FILE_OP_ERROR(dest, "fclose");
2633 if (rename_force(dest_bak, dest) < 0)
2634 FILE_OP_ERROR(dest_bak, "rename");
2640 if (keep_backup == FALSE && dest_bak)
2641 claws_unlink(dest_bak);
2648 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2650 if (overwrite == FALSE && is_file_exist(dest)) {
2651 g_warning("move_file(): file %s already exists.", dest);
2655 if (rename_force(src, dest) == 0) return 0;
2657 if (EXDEV != errno) {
2658 FILE_OP_ERROR(src, "rename");
2662 if (copy_file(src, dest, FALSE) < 0) return -1;
2669 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2672 gint bytes_left, to_read;
2675 if (fseek(fp, offset, SEEK_SET) < 0) {
2680 bytes_left = length;
2681 to_read = MIN(bytes_left, sizeof(buf));
2683 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2684 if (n_read < to_read && ferror(fp))
2686 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2689 bytes_left -= n_read;
2690 if (bytes_left == 0)
2692 to_read = MIN(bytes_left, sizeof(buf));
2703 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2706 gboolean err = FALSE;
2708 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2709 FILE_OP_ERROR(dest, "g_fopen");
2713 if (change_file_mode_rw(dest_fp, dest) < 0) {
2714 FILE_OP_ERROR(dest, "chmod");
2715 g_warning("can't change file mode\n");
2718 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2721 if (!err && fclose(dest_fp) == EOF) {
2722 FILE_OP_ERROR(dest, "fclose");
2727 g_warning("writing to %s failed.\n", dest);
2735 /* convert line endings into CRLF. If the last line doesn't end with
2736 * linebreak, add it.
2738 gchar *canonicalize_str(const gchar *str)
2744 for (p = str; *p != '\0'; ++p) {
2751 if (p == str || *(p - 1) != '\n')
2754 out = outp = g_malloc(new_len + 1);
2755 for (p = str; *p != '\0'; ++p) {
2762 if (p == str || *(p - 1) != '\n') {
2771 gint canonicalize_file(const gchar *src, const gchar *dest)
2773 FILE *src_fp, *dest_fp;
2774 gchar buf[BUFFSIZE];
2776 gboolean err = FALSE;
2777 gboolean last_linebreak = FALSE;
2779 if (src == NULL || dest == NULL)
2782 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2783 FILE_OP_ERROR(src, "g_fopen");
2787 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2788 FILE_OP_ERROR(dest, "g_fopen");
2793 if (change_file_mode_rw(dest_fp, dest) < 0) {
2794 FILE_OP_ERROR(dest, "chmod");
2795 g_warning("can't change file mode\n");
2798 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2802 if (len == 0) break;
2803 last_linebreak = FALSE;
2805 if (buf[len - 1] != '\n') {
2806 last_linebreak = TRUE;
2807 r = fputs(buf, dest_fp);
2808 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2809 r = fputs(buf, dest_fp);
2812 r = fwrite(buf, 1, len - 1, dest_fp);
2817 r = fputs("\r\n", dest_fp);
2821 g_warning("writing to %s failed.\n", dest);
2829 if (last_linebreak == TRUE) {
2830 if (fputs("\r\n", dest_fp) == EOF)
2834 if (ferror(src_fp)) {
2835 FILE_OP_ERROR(src, "fgets");
2839 if (fclose(dest_fp) == EOF) {
2840 FILE_OP_ERROR(dest, "fclose");
2852 gint canonicalize_file_replace(const gchar *file)
2856 tmp_file = get_tmp_file();
2858 if (canonicalize_file(file, tmp_file) < 0) {
2863 if (move_file(tmp_file, file, TRUE) < 0) {
2864 g_warning("can't replace %s .\n", file);
2865 claws_unlink(tmp_file);
2874 gchar *normalize_newlines(const gchar *str)
2876 const gchar *p = str;
2879 out = outp = g_malloc(strlen(str) + 1);
2880 for (p = str; *p != '\0'; ++p) {
2882 if (*(p + 1) != '\n')
2893 gchar *get_outgoing_rfc2822_str(FILE *fp)
2895 gchar buf[BUFFSIZE];
2899 str = g_string_new(NULL);
2901 /* output header part */
2902 while (fgets(buf, sizeof(buf), fp) != NULL) {
2904 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2911 else if (next != ' ' && next != '\t') {
2915 if (fgets(buf, sizeof(buf), fp) == NULL)
2919 g_string_append(str, buf);
2920 g_string_append(str, "\r\n");
2926 /* output body part */
2927 while (fgets(buf, sizeof(buf), fp) != NULL) {
2930 g_string_append_c(str, '.');
2931 g_string_append(str, buf);
2932 g_string_append(str, "\r\n");
2936 g_string_free(str, FALSE);
2942 * Create a new boundary in a way that it is very unlikely that this
2943 * will occur in the following text. It would be easy to ensure
2944 * uniqueness if everything is either quoted-printable or base64
2945 * encoded (note that conversion is allowed), but because MIME bodies
2946 * may be nested, it may happen that the same boundary has already
2949 * boundary := 0*69<bchars> bcharsnospace
2950 * bchars := bcharsnospace / " "
2951 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2952 * "+" / "_" / "," / "-" / "." /
2953 * "/" / ":" / "=" / "?"
2955 * some special characters removed because of buggy MTAs
2958 gchar *generate_mime_boundary(const gchar *prefix)
2960 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2961 "abcdefghijklmnopqrstuvwxyz"
2966 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2967 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2970 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2974 gint change_file_mode_rw(FILE *fp, const gchar *file)
2977 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2979 return g_chmod(file, S_IRUSR|S_IWUSR);
2983 FILE *my_tmpfile(void)
2985 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2986 const gchar suffix[] = ".XXXXXX";
2987 const gchar *tmpdir;
2989 const gchar *progname;
2998 tmpdir = get_tmp_dir();
2999 tmplen = strlen(tmpdir);
3000 progname = g_get_prgname();
3001 if (progname == NULL)
3002 progname = "claws-mail";
3003 proglen = strlen(progname);
3004 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3007 memcpy(fname, tmpdir, tmplen);
3008 fname[tmplen] = G_DIR_SEPARATOR;
3009 memcpy(fname + tmplen + 1, progname, proglen);
3010 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3012 fd = mkstemp(fname);
3017 claws_unlink(fname);
3019 /* verify that we can write in the file after unlinking */
3020 if (write(fd, buf, 1) < 0) {
3027 fp = fdopen(fd, "w+b");
3035 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3040 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3044 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3045 dir, G_DIR_SEPARATOR);
3046 fd = mkstemp_name(template, filename);
3049 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3050 fd = mkstemp(*filename);
3052 return fdopen(fd, "w+");
3055 FILE *str_open_as_stream(const gchar *str)
3060 cm_return_val_if_fail(str != NULL, NULL);
3064 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3069 if (len == 0) return fp;
3071 if (fwrite(str, 1, len, fp) != len) {
3072 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3081 gint str_write_to_file(const gchar *str, const gchar *file)
3086 cm_return_val_if_fail(str != NULL, -1);
3087 cm_return_val_if_fail(file != NULL, -1);
3089 if ((fp = g_fopen(file, "wb")) == NULL) {
3090 FILE_OP_ERROR(file, "g_fopen");
3100 if (fwrite(str, 1, len, fp) != len) {
3101 FILE_OP_ERROR(file, "fwrite");
3107 if (fclose(fp) == EOF) {
3108 FILE_OP_ERROR(file, "fclose");
3116 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3123 cm_return_val_if_fail(fp != NULL, NULL);
3125 array = g_byte_array_new();
3127 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3128 if (n_read < sizeof(buf) && ferror(fp))
3130 g_byte_array_append(array, buf, n_read);
3134 FILE_OP_ERROR("file stream", "fread");
3135 g_byte_array_free(array, TRUE);
3140 g_byte_array_append(array, buf, 1);
3141 str = (gchar *)array->data;
3142 g_byte_array_free(array, FALSE);
3144 if (recode && !g_utf8_validate(str, -1, NULL)) {
3145 const gchar *src_codeset, *dest_codeset;
3147 src_codeset = conv_get_locale_charset_str();
3148 dest_codeset = CS_UTF_8;
3149 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3157 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3164 struct timeval timeout = {1, 0};
3169 cm_return_val_if_fail(file != NULL, NULL);
3171 if (g_stat(file, &s) != 0) {
3172 FILE_OP_ERROR(file, "stat");
3175 if (S_ISDIR(s.st_mode)) {
3176 g_warning("%s: is a directory\n", file);
3181 fp = g_fopen (file, "rb");
3183 FILE_OP_ERROR(file, "open");
3187 /* test whether the file is readable without blocking */
3188 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3190 FILE_OP_ERROR(file, "open");
3197 /* allow for one second */
3198 err = select(fd+1, &fds, NULL, NULL, &timeout);
3199 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3201 FILE_OP_ERROR(file, "select");
3203 g_warning("%s: doesn't seem readable\n", file);
3209 /* Now clear O_NONBLOCK */
3210 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3211 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3215 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3216 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3221 /* get the FILE pointer */
3222 fp = fdopen(fd, "rb");
3225 FILE_OP_ERROR(file, "fdopen");
3226 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3231 str = file_read_stream_to_str_full(fp, recode);
3238 gchar *file_read_to_str(const gchar *file)
3240 return file_read_to_str_full(file, TRUE);
3242 gchar *file_read_stream_to_str(FILE *fp)
3244 return file_read_stream_to_str_full(fp, TRUE);
3247 gchar *file_read_to_str_no_recode(const gchar *file)
3249 return file_read_to_str_full(file, FALSE);
3251 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3253 return file_read_stream_to_str_full(fp, FALSE);
3256 char *fgets_crlf(char *buf, int size, FILE *stream)
3258 gboolean is_cr = FALSE;
3259 gboolean last_was_cr = FALSE;
3264 while (--size > 0 && (c = getc(stream)) != EOF)
3267 is_cr = (c == '\r');
3277 last_was_cr = is_cr;
3279 if (c == EOF && cs == buf)
3287 static gint execute_async(gchar *const argv[])
3289 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3291 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3292 NULL, NULL, NULL, FALSE) == FALSE) {
3293 g_warning("Couldn't execute command: %s\n", argv[0]);
3300 static gint execute_sync(gchar *const argv[])
3304 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3307 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3308 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3309 g_warning("Couldn't execute command: %s\n", argv[0]);
3313 if (WIFEXITED(status))
3314 return WEXITSTATUS(status);
3318 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3319 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3320 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3321 g_warning("Couldn't execute command: %s\n", argv[0]);
3329 gint execute_command_line(const gchar *cmdline, gboolean async)
3334 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3336 argv = strsplit_with_quote(cmdline, " ", 0);
3339 ret = execute_async(argv);
3341 ret = execute_sync(argv);
3348 gchar *get_command_output(const gchar *cmdline)
3350 gchar *child_stdout;
3353 cm_return_val_if_fail(cmdline != NULL, NULL);
3355 debug_print("get_command_output(): executing: %s\n", cmdline);
3357 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3359 g_warning("Couldn't execute command: %s\n", cmdline);
3363 return child_stdout;
3366 static gint is_unchanged_uri_char(char c)
3377 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3383 for(i = 0; i < strlen(uri) ; i++) {
3384 if (is_unchanged_uri_char(uri[i])) {
3385 if (k + 2 >= bufsize)
3387 encoded_uri[k++] = uri[i];
3390 char * hexa = "0123456789ABCDEF";
3392 if (k + 4 >= bufsize)
3394 encoded_uri[k++] = '%';
3395 encoded_uri[k++] = hexa[uri[i] / 16];
3396 encoded_uri[k++] = hexa[uri[i] % 16];
3402 gint open_uri(const gchar *uri, const gchar *cmdline)
3406 gchar buf[BUFFSIZE];
3408 gchar encoded_uri[BUFFSIZE];
3409 cm_return_val_if_fail(uri != NULL, -1);
3411 /* an option to choose whether to use encode_uri or not ? */
3412 encode_uri(encoded_uri, BUFFSIZE, uri);
3415 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3416 !strchr(p + 2, '%'))
3417 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3420 g_warning("Open URI command-line is invalid "
3421 "(there must be only one '%%s'): %s",
3423 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3426 execute_command_line(buf, TRUE);
3428 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3431 extern osso_context_t *get_osso_context(void);
3432 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3433 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3434 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3439 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3441 gchar buf[BUFFSIZE];
3444 cm_return_val_if_fail(filepath != NULL, -1);
3447 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3448 !strchr(p + 2, '%'))
3449 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3452 g_warning("Open Text Editor command-line is invalid "
3453 "(there must be only one '%%s'): %s",
3455 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3458 execute_command_line(buf, TRUE);
3463 time_t remote_tzoffset_sec(const gchar *zone)
3465 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3471 time_t remoteoffset;
3473 strncpy(zone3, zone, 3);
3477 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3478 (c == '+' || c == '-')) {
3479 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3481 remoteoffset = -remoteoffset;
3482 } else if (!strncmp(zone, "UT" , 2) ||
3483 !strncmp(zone, "GMT", 2)) {
3485 } else if (strlen(zone3) == 3) {
3486 for (p = ustzstr; *p != '\0'; p += 3) {
3487 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3488 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3489 remoteoffset = iustz * 3600;
3495 } else if (strlen(zone3) == 1) {
3497 case 'Z': remoteoffset = 0; break;
3498 case 'A': remoteoffset = -1; break;
3499 case 'B': remoteoffset = -2; break;
3500 case 'C': remoteoffset = -3; break;
3501 case 'D': remoteoffset = -4; break;
3502 case 'E': remoteoffset = -5; break;
3503 case 'F': remoteoffset = -6; break;
3504 case 'G': remoteoffset = -7; break;
3505 case 'H': remoteoffset = -8; break;
3506 case 'I': remoteoffset = -9; break;
3507 case 'K': remoteoffset = -10; break; /* J is not used */
3508 case 'L': remoteoffset = -11; break;
3509 case 'M': remoteoffset = -12; break;
3510 case 'N': remoteoffset = 1; break;
3511 case 'O': remoteoffset = 2; break;
3512 case 'P': remoteoffset = 3; break;
3513 case 'Q': remoteoffset = 4; break;
3514 case 'R': remoteoffset = 5; break;
3515 case 'S': remoteoffset = 6; break;
3516 case 'T': remoteoffset = 7; break;
3517 case 'U': remoteoffset = 8; break;
3518 case 'V': remoteoffset = 9; break;
3519 case 'W': remoteoffset = 10; break;
3520 case 'X': remoteoffset = 11; break;
3521 case 'Y': remoteoffset = 12; break;
3522 default: remoteoffset = 0; break;
3524 remoteoffset = remoteoffset * 3600;
3528 return remoteoffset;
3531 time_t tzoffset_sec(time_t *now)
3535 struct tm buf1, buf2;
3537 if (now && *now < 0)
3540 gmt = *gmtime_r(now, &buf1);
3541 lt = localtime_r(now, &buf2);
3543 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3545 if (lt->tm_year < gmt.tm_year)
3547 else if (lt->tm_year > gmt.tm_year)
3549 else if (lt->tm_yday < gmt.tm_yday)
3551 else if (lt->tm_yday > gmt.tm_yday)
3554 if (off >= 24 * 60) /* should be impossible */
3555 off = 23 * 60 + 59; /* if not, insert silly value */
3556 if (off <= -24 * 60)
3557 off = -(23 * 60 + 59);
3562 /* calculate timezone offset */
3563 gchar *tzoffset(time_t *now)
3565 static gchar offset_string[6];
3569 struct tm buf1, buf2;
3571 if (now && *now < 0)
3574 gmt = *gmtime_r(now, &buf1);
3575 lt = localtime_r(now, &buf2);
3577 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3579 if (lt->tm_year < gmt.tm_year)
3581 else if (lt->tm_year > gmt.tm_year)
3583 else if (lt->tm_yday < gmt.tm_yday)
3585 else if (lt->tm_yday > gmt.tm_yday)
3593 if (off >= 24 * 60) /* should be impossible */
3594 off = 23 * 60 + 59; /* if not, insert silly value */
3596 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3598 return offset_string;
3601 void get_rfc822_date(gchar *buf, gint len)
3605 gchar day[4], mon[4];
3606 gint dd, hh, mm, ss, yyyy;
3608 gchar buf2[BUFFSIZE];
3611 lt = localtime_r(&t, &buf1);
3613 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3614 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3616 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3617 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3620 void debug_set_mode(gboolean mode)
3625 gboolean debug_get_mode(void)
3630 void debug_print_real(const gchar *format, ...)
3633 gchar buf[BUFFSIZE];
3635 if (!debug_mode) return;
3637 va_start(args, format);
3638 g_vsnprintf(buf, sizeof(buf), format, args);
3645 const char * debug_srcname(const char *file)
3647 const char *s = strrchr (file, '/');
3652 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3654 if (subject == NULL)
3657 subject += subject_get_prefix_length(subject);
3659 return g_hash_table_lookup(subject_table, subject);
3662 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3665 if (subject == NULL || *subject == 0)
3667 subject += subject_get_prefix_length(subject);
3668 g_hash_table_insert(subject_table, subject, data);
3671 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3673 if (subject == NULL)
3676 subject += subject_get_prefix_length(subject);
3677 g_hash_table_remove(subject_table, subject);
3681 static regex_t u_regex;
3682 static gboolean u_init_;
3685 void utils_free_regex(void)
3696 *\brief Check if a string is prefixed with known (combinations)
3697 * of prefixes. The function assumes that each prefix
3698 * is terminated by zero or exactly _one_ space.
3700 *\param str String to check for a prefixes
3702 *\return int Number of chars in the prefix that should be skipped
3703 * for a "clean" subject line. If no prefix was found, 0
3706 int subject_get_prefix_length(const gchar *subject)
3709 /*!< Array with allowable reply prefixes regexps. */
3710 static const gchar * const prefixes[] = {
3711 "Re\\:", /* "Re:" */
3712 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3713 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3714 "Aw\\:", /* "Aw:" (German) */
3715 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3716 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3717 "Fw\\:", /* "Fw:" Forward */
3718 "Fwd\\:", /* "Fwd:" Forward */
3719 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3720 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3721 "Rif\\:", /* "Rif:" (Italian Outlook) */
3722 "Sv\\:", /* "Sv" (Norwegian) */
3723 "Vs\\:", /* "Vs" (Norwegian) */
3724 "Ad\\:", /* "Ad" (Norwegian) */
3725 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3726 "R\303\251f\\. \\:", /* "Réf. :" (French Lotus Notes) */
3727 "Re \\:", /* "Re :" (French Yahoo Mail) */
3730 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3734 if (!subject) return 0;
3735 if (!*subject) return 0;
3738 GString *s = g_string_new("");
3740 for (n = 0; n < PREFIXES; n++)
3741 /* Terminate each prefix regexpression by a
3742 * "\ ?" (zero or ONE space), and OR them */
3743 g_string_append_printf(s, "(%s\\ ?)%s",
3748 g_string_prepend(s, "(");
3749 g_string_append(s, ")+"); /* match at least once */
3750 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3753 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3754 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3755 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3756 debug_print("Error compiling regexp %s\n", s->str);
3757 g_string_free(s, TRUE);
3761 g_string_free(s, TRUE);
3765 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3770 /*!< Array with allowable reply prefixes regexps. */
3771 static const gchar * const prefixes[] = {
3773 "antw:", /* "Antw:" (Dutch / German Outlook) */
3774 "aw:", /* "Aw:" (German) */
3775 "antwort:", /* "Antwort:" (German Lotus Notes) */
3776 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3777 "fw:", /* "Fw:" Forward */
3778 "fwd:", /* "Fwd:" Forward */
3779 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3780 "odp:", /* "Odp:" Re (Polish Outlook) */
3781 "rif:", /* "Rif:" (Italian Outlook) */
3782 "sv:", /* "Sv" (Norwegian) */
3783 "vs:", /* "Vs" (Norwegian) */
3784 "ad:", /* "Ad" (Norwegian) */
3785 "R\303\251f. :", /* "Réf. :" (French Lotus Notes) */
3786 "Re :", /* "Re :" (French Yahoo Mail) */
3789 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3792 if (!subject) return 0;
3793 if (!*subject) return 0;
3795 for (n = 0; n < PREFIXES; n++) {
3796 int len = strlen(prefixes[n]);
3797 if (!strncasecmp(subject, prefixes[n], len)) {
3798 if (subject[len] == ' ')
3807 static guint g_stricase_hash(gconstpointer gptr)
3809 guint hash_result = 0;
3812 for (str = gptr; str && *str; str++) {
3813 hash_result += toupper(*str);
3819 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3821 const char *str1 = gptr1;
3822 const char *str2 = gptr2;
3824 return !strcasecmp(str1, str2);
3827 gint g_int_compare(gconstpointer a, gconstpointer b)
3829 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3832 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3840 lt = localtime_r(&t, &buft);
3842 if (strcmp(buf, "") == 0) {
3843 if (user_addr != NULL)
3844 addr = g_strconcat(".", user_addr, "@", get_domain_name(), NULL);
3846 addr = g_strconcat("@", get_domain_name(), NULL);
3848 if (user_addr != NULL)
3849 addr = g_strconcat(".", user_addr, "@", buf, NULL);
3851 addr = g_strconcat("@", buf, NULL);
3854 /* Replace all @ but the last one in addr, with underscores.
3855 * RFC 2822 States that msg-id syntax only allows one @.
3857 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3858 *(strchr(addr, '@')) = '_';
3860 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3861 lt->tm_year + 1900, lt->tm_mon + 1,
3862 lt->tm_mday, lt->tm_hour,
3863 lt->tm_min, lt->tm_sec,
3864 (guint) rand(), addr);
3871 quote_cmd_argument()
3873 return a quoted string safely usable in argument of a command.
3875 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3878 gint quote_cmd_argument(gchar * result, guint size,
3888 for(p = path ; * p != '\0' ; p ++) {
3890 if (isalnum((guchar)*p) || (* p == '/')) {
3891 if (remaining > 0) {
3897 result[size - 1] = '\0';
3902 if (remaining >= 2) {
3910 result[size - 1] = '\0';
3915 if (remaining > 0) {
3919 result[size - 1] = '\0';
3933 static void g_node_map_recursive(GNode *node, gpointer data)
3935 GNodeMapData *mapdata = (GNodeMapData *) data;
3937 GNodeMapData newmapdata;
3940 newdata = mapdata->func(node->data, mapdata->data);
3941 if (newdata != NULL) {
3942 newnode = g_node_new(newdata);
3943 g_node_append(mapdata->parent, newnode);
3945 newmapdata.parent = newnode;
3946 newmapdata.func = mapdata->func;
3947 newmapdata.data = mapdata->data;
3949 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3953 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3956 GNodeMapData mapdata;
3958 cm_return_val_if_fail(node != NULL, NULL);
3959 cm_return_val_if_fail(func != NULL, NULL);
3961 root = g_node_new(func(node->data, data));
3963 mapdata.parent = root;
3964 mapdata.func = func;
3965 mapdata.data = data;
3967 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3972 #define HEX_TO_INT(val, hex) \
3976 if ('0' <= c && c <= '9') { \
3978 } else if ('a' <= c && c <= 'f') { \
3979 val = c - 'a' + 10; \
3980 } else if ('A' <= c && c <= 'F') { \
3981 val = c - 'A' + 10; \
3987 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3994 if (hi == -1 || lo == -1)
3997 *out = (hi << 4) + lo;
4001 #define INT_TO_HEX(hex, val) \
4004 hex = '0' + (val); \
4006 hex = 'A' + (val) - 10; \
4009 void get_hex_str(gchar *out, guchar ch)
4013 INT_TO_HEX(hex, ch >> 4);
4015 INT_TO_HEX(hex, ch & 0x0f);
4021 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4023 #define G_PRINT_REF g_print
4027 *\brief Register ref counted pointer. It is based on GBoxed, so should
4028 * work with anything that uses the GType system. The semantics
4029 * are similar to a C++ auto pointer, with the exception that
4030 * C doesn't have automatic closure (calling destructors) when
4031 * exiting a block scope.
4032 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4033 * function directly.
4035 *\return GType A GType type.
4037 GType g_auto_pointer_register(void)
4039 static GType auto_pointer_type;
4040 if (!auto_pointer_type)
4042 g_boxed_type_register_static
4043 ("G_TYPE_AUTO_POINTER",
4044 (GBoxedCopyFunc) g_auto_pointer_copy,
4045 (GBoxedFreeFunc) g_auto_pointer_free);
4046 return auto_pointer_type;
4050 *\brief Structure with g_new() allocated pointer guarded by the
4053 typedef struct AutoPointerRef {
4054 void (*free) (gpointer);
4060 *\brief The auto pointer opaque structure that references the
4061 * pointer guard block.
4063 typedef struct AutoPointer {
4064 AutoPointerRef *ref;
4065 gpointer ptr; /*!< access to protected pointer */
4069 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4073 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4074 * ... when assigning, copying and freeing storage elements
4076 * gtk_list_store_new(N_S_COLUMNS,
4077 * G_TYPE_AUTO_POINTER,
4081 * Template *precious_data = g_new0(Template, 1);
4082 * g_pointer protect = g_auto_pointer_new(precious_data);
4084 * gtk_list_store_set(container, &iter,
4088 * ... the gtk_list_store has copied the pointer and
4089 * ... incremented its reference count, we should free
4090 * ... the auto pointer (in C++ a destructor would do
4091 * ... this for us when leaving block scope)
4093 * g_auto_pointer_free(protect);
4095 * ... gtk_list_store_set() now manages the data. When
4096 * ... *explicitly* requesting a pointer from the list
4097 * ... store, don't forget you get a copy that should be
4098 * ... freed with g_auto_pointer_free() eventually.
4102 *\param pointer Pointer to be guarded.
4104 *\return GAuto * Pointer that should be used in containers with
4107 GAuto *g_auto_pointer_new(gpointer p)
4109 AutoPointerRef *ref;
4115 ref = g_new0(AutoPointerRef, 1);
4116 ptr = g_new0(AutoPointer, 1);
4126 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4132 *\brief Allocate an autopointer using the passed \a free function to
4133 * free the guarded pointer
4135 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4142 aptr = g_auto_pointer_new(p);
4143 aptr->ref->free = free_;
4147 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4149 if (auto_ptr == NULL)
4151 return ((AutoPointer *) auto_ptr)->ptr;
4155 *\brief Copies an auto pointer by. It's mostly not necessary
4156 * to call this function directly, unless you copy/assign
4157 * the guarded pointer.
4159 *\param auto_ptr Auto pointer returned by previous call to
4160 * g_auto_pointer_new_XXX()
4162 *\return gpointer An auto pointer
4164 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4167 AutoPointerRef *ref;
4170 if (auto_ptr == NULL)
4175 newp = g_new0(AutoPointer, 1);
4178 newp->ptr = ref->pointer;
4182 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4188 *\brief Free an auto pointer
4190 void g_auto_pointer_free(GAuto *auto_ptr)
4193 AutoPointerRef *ref;
4195 if (auto_ptr == NULL)
4201 if (--(ref->cnt) == 0) {
4203 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4205 ref->free(ref->pointer);
4210 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4215 void replace_returns(gchar *str)
4220 while (strstr(str, "\n")) {
4221 *strstr(str, "\n") = ' ';
4223 while (strstr(str, "\r")) {
4224 *strstr(str, "\r") = ' ';
4228 /* get_uri_part() - retrieves a URI starting from scanpos.
4229 Returns TRUE if succesful */
4230 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4231 const gchar **bp, const gchar **ep, gboolean hdr)
4234 gint parenthese_cnt = 0;
4236 cm_return_val_if_fail(start != NULL, FALSE);
4237 cm_return_val_if_fail(scanpos != NULL, FALSE);
4238 cm_return_val_if_fail(bp != NULL, FALSE);
4239 cm_return_val_if_fail(ep != NULL, FALSE);
4243 /* find end point of URI */
4244 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4245 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4246 !IS_ASCII(*(const guchar *)ep_) ||
4247 strchr("[]{}<>\"", *ep_)) {
4249 } else if (strchr("(", *ep_)) {
4251 } else if (strchr(")", *ep_)) {
4252 if (parenthese_cnt > 0)
4259 /* no punctuation at end of string */
4261 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4262 * should pass some URI type to this function and decide on that whether
4263 * to perform punctuation stripping */
4265 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4267 for (; ep_ - 1 > scanpos + 1 &&
4268 IS_REAL_PUNCT(*(ep_ - 1));
4272 #undef IS_REAL_PUNCT
4279 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4281 while (bp && *bp && g_ascii_isspace(*bp))
4283 return g_strndup(bp, ep - bp);
4286 /* valid mail address characters */
4287 #define IS_RFC822_CHAR(ch) \
4291 !g_ascii_isspace(ch) && \
4292 !strchr("(),;<>\"", (ch)))
4294 /* alphabet and number within 7bit ASCII */
4295 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4296 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4298 static GHashTable *create_domain_tab(void)
4300 static const gchar *toplvl_domains [] = {
4302 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4303 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4304 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4305 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4306 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4307 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4308 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4309 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4310 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4311 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4312 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4313 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4314 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4315 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4316 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4317 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4318 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4319 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4320 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4321 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4322 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4323 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4324 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4325 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4326 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4327 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4330 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4332 cm_return_val_if_fail(htab, NULL);
4333 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4334 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4338 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4340 const gint MAX_LVL_DOM_NAME_LEN = 6;
4341 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4342 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4345 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4348 for (p = buf; p < m && first < last; *p++ = *first++)
4352 return g_hash_table_lookup(tab, buf) != NULL;
4355 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4356 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4357 const gchar **bp, const gchar **ep, gboolean hdr)
4359 /* more complex than the uri part because we need to scan back and forward starting from
4360 * the scan position. */
4361 gboolean result = FALSE;
4362 const gchar *bp_ = NULL;
4363 const gchar *ep_ = NULL;
4364 static GHashTable *dom_tab;
4365 const gchar *last_dot = NULL;
4366 const gchar *prelast_dot = NULL;
4367 const gchar *last_tld_char = NULL;
4369 /* the informative part of the email address (describing the name
4370 * of the email address owner) may contain quoted parts. the
4371 * closure stack stores the last encountered quotes. */
4372 gchar closure_stack[128];
4373 gchar *ptr = closure_stack;
4375 cm_return_val_if_fail(start != NULL, FALSE);
4376 cm_return_val_if_fail(scanpos != NULL, FALSE);
4377 cm_return_val_if_fail(bp != NULL, FALSE);
4378 cm_return_val_if_fail(ep != NULL, FALSE);
4381 const gchar *start_quote = NULL;
4382 const gchar *end_quote = NULL;
4384 /* go to the real start */
4385 if (start[0] == ',')
4387 if (start[0] == ';')
4389 while (start[0] == '\n' || start[0] == '\r')
4391 while (start[0] == ' ' || start[0] == '\t')
4396 /* check if there are quotes (to skip , in them) */
4397 if (*start == '"') {
4398 start_quote = start;
4400 end_quote = strstr(start, "\"");
4406 /* skip anything between quotes */
4407 if (start_quote && end_quote) {
4412 /* find end (either , or ; or end of line) */
4413 if (strstr(start, ",") && strstr(start, ";"))
4414 *ep = strstr(start,",") < strstr(start, ";")
4415 ? strstr(start, ",") : strstr(start, ";");
4416 else if (strstr(start, ","))
4417 *ep = strstr(start, ",");
4418 else if (strstr(start, ";"))
4419 *ep = strstr(start, ";");
4421 *ep = start+strlen(start);
4423 /* go back to real start */
4424 if (start_quote && end_quote) {
4425 start = start_quote;
4428 /* check there's still an @ in that, or search
4429 * further if possible */
4430 if (strstr(start, "@") && strstr(start, "@") < *ep)
4432 else if (*ep < start+strlen(start)) {
4435 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4443 dom_tab = create_domain_tab();
4444 cm_return_val_if_fail(dom_tab, FALSE);
4446 /* scan start of address */
4447 for (bp_ = scanpos - 1;
4448 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4451 /* TODO: should start with an alnum? */
4453 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4456 if (bp_ != scanpos) {
4457 /* scan end of address */
4458 for (ep_ = scanpos + 1;
4459 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4461 prelast_dot = last_dot;
4463 if (*(last_dot + 1) == '.') {
4464 if (prelast_dot == NULL)
4466 last_dot = prelast_dot;
4471 /* TODO: really should terminate with an alnum? */
4472 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4477 if (last_dot == NULL)
4479 if (last_dot >= ep_)
4480 last_dot = prelast_dot;
4481 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4485 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4486 if (*last_tld_char == '?')
4489 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4496 if (!result) return FALSE;
4498 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4499 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4500 && IS_RFC822_CHAR(*(ep_ + 3))) {
4501 /* this informative part with an @ in it is
4502 * followed by the email address */
4505 /* go to matching '>' (or next non-rfc822 char, like \n) */
4506 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4509 /* include the bracket */
4510 if (*ep_ == '>') ep_++;
4512 /* include the leading quote */
4520 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4521 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4524 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4525 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4528 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4529 #define IN_STACK() (ptr > closure_stack)
4530 /* has underrun check */
4531 #define POP_STACK() if(IN_STACK()) --ptr
4532 /* has overrun check */
4533 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4534 /* has underrun check */
4535 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4539 /* scan for the informative part. */
4540 for (bp_ -= 2; bp_ >= start; bp_--) {
4541 /* if closure on the stack keep scanning */
4542 if (PEEK_STACK() == *bp_) {
4546 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4551 /* if nothing in the closure stack, do the special conditions
4552 * the following if..else expression simply checks whether
4553 * a token is acceptable. if not acceptable, the clause
4554 * should terminate the loop with a 'break' */
4555 if (!PEEK_STACK()) {
4557 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4558 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4559 /* hyphens are allowed, but only in
4561 } else if (strchr(" \"'", *bp_)) {
4562 /* but anything not being a punctiation
4565 break; /* anything else is rejected */
4572 /* scan forward (should start with an alnum) */
4573 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4589 #undef IS_ASCII_ALNUM
4590 #undef IS_RFC822_CHAR
4592 gchar *make_email_string(const gchar *bp, const gchar *ep)
4594 /* returns a mailto: URI; mailto: is also used to detect the
4595 * uri type later on in the button_pressed signal handler */
4599 tmp = g_strndup(bp, ep - bp);
4600 result = g_strconcat("mailto:", tmp, NULL);
4606 gchar *make_http_string(const gchar *bp, const gchar *ep)
4608 /* returns an http: URI; */
4612 while (bp && *bp && g_ascii_isspace(*bp))
4614 tmp = g_strndup(bp, ep - bp);
4615 result = g_strconcat("http://", tmp, NULL);
4621 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4623 FILE *fp = g_fopen(path, "rb");
4624 gchar buf[BUFFSIZE];
4625 gchar *result = NULL;
4628 while (fgets(buf, sizeof (buf), fp) != NULL) {
4629 gchar **parts = g_strsplit(buf, ";", 3);
4630 gchar *trimmed = parts[0];
4631 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4633 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4634 trimmed[strlen(trimmed)-1] = '\0';
4636 if (!strcmp(trimmed, type)) {
4637 gboolean needsterminal = FALSE;
4638 if (parts[2] && strstr(parts[2], "needsterminal")) {
4639 needsterminal = TRUE;
4641 if (parts[2] && strstr(parts[2], "test=")) {
4642 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4643 gchar *testcmd = orig_testcmd;
4644 if (strstr(testcmd,";"))
4645 *(strstr(testcmd,";")) = '\0';
4646 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4648 while (testcmd[strlen(testcmd)-1] == '\n')
4649 testcmd[strlen(testcmd)-1] = '\0';
4650 while (testcmd[strlen(testcmd)-1] == '\r')
4651 testcmd[strlen(testcmd)-1] = '\0';
4652 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4653 testcmd[strlen(testcmd)-1] = '\0';
4655 if (strstr(testcmd, "%s")) {
4656 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4657 gint res = system(tmp);
4659 g_free(orig_testcmd);
4666 gint res = system(testcmd);
4667 g_free(orig_testcmd);
4677 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4679 while (trimmed[strlen(trimmed)-1] == '\n')
4680 trimmed[strlen(trimmed)-1] = '\0';
4681 while (trimmed[strlen(trimmed)-1] == '\r')
4682 trimmed[strlen(trimmed)-1] = '\0';
4683 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4684 trimmed[strlen(trimmed)-1] = '\0';
4685 result = g_strdup(trimmed);
4688 /* if there are no single quotes around %s, add them.
4689 * '.*%s.*' is ok, as in display 'png:%s'
4691 if (strstr(result, "%s")
4692 && !(strstr(result, "'") < strstr(result,"%s") &&
4693 strstr(strstr(result,"%s"), "'"))) {
4694 gchar *start = g_strdup(result);
4695 gchar *end = g_strdup(strstr(result, "%s")+2);
4697 *strstr(start, "%s") = '\0';
4698 tmp = g_strconcat(start,"'%s'",end, NULL);
4704 if (needsterminal) {
4705 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4716 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4718 gchar *result = NULL;
4722 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4723 result = mailcap_get_command_in_file(path, type, file_to_open);
4727 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4731 void mailcap_update_default(const gchar *type, const gchar *command)
4733 gchar *path = NULL, *outpath = NULL;
4734 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4735 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4736 FILE *fp = g_fopen(path, "rb");
4737 FILE *outfp = g_fopen(outpath, "wb");
4738 gchar buf[BUFFSIZE];
4739 gboolean err = FALSE;
4747 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4748 gchar **parts = g_strsplit(buf, ";", 3);
4749 gchar *trimmed = parts[0];
4750 while (trimmed[0] == ' ')
4752 while (trimmed[strlen(trimmed)-1] == ' ')
4753 trimmed[strlen(trimmed)-1] = '\0';
4755 if (!strcmp(trimmed, type)) {
4760 if(fputs(buf, outfp) == EOF) {
4767 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4773 if (fclose(outfp) == EOF)
4777 g_rename(outpath, path);
4783 gint copy_dir(const gchar *src, const gchar *dst)
4788 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4789 g_warning("failed to open directory: %s\n", src);
4793 if (make_dir(dst) < 0)
4796 while ((name = g_dir_read_name(dir)) != NULL) {
4797 gchar *old_file, *new_file;
4798 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4799 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4800 debug_print("copying: %s -> %s\n", old_file, new_file);
4801 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4802 gint r = copy_file(old_file, new_file, TRUE);
4809 /* Windows has no symlinks. Or well, Vista seems to
4810 have something like this but the semantics might be
4811 different. Thus we don't use it under Windows. */
4812 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4815 gchar *target = g_file_read_link(old_file, &error);
4817 r = symlink(target, new_file);
4824 #endif /*G_OS_WIN32*/
4825 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4826 gint r = copy_dir(old_file, new_file);
4837 /* crude test to see if a file is an email. */
4838 gboolean file_is_email (const gchar *filename)
4844 if (filename == NULL)
4846 if ((fp = g_fopen(filename, "rb")) == NULL)
4848 while (i < 60 && score < 3
4849 && fgets(buffer, sizeof (buffer), fp) > 0) {
4850 if (!strncmp(buffer, "From:", strlen("From:")))
4852 if (!strncmp(buffer, "To:", strlen("To:")))
4854 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4859 return (score >= 3);
4862 gboolean sc_g_list_bigger(GList *list, gint max)
4866 while (cur && i <= max+1) {
4873 gboolean sc_g_slist_bigger(GSList *list, gint max)
4877 while (cur && i <= max+1) {
4884 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4885 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4886 NULL, NULL, NULL, NULL, NULL, NULL};
4887 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4888 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4889 NULL, NULL, NULL, NULL, NULL, NULL};
4891 gint daynames_len[] = {0,0,0,0,0,0,0};
4892 gint monthnames_len[] = {0,0,0,0,0,0,
4894 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4895 gint s_monthnames_len[] = {0,0,0,0,0,0,
4897 const gchar *s_am_up = NULL;
4898 const gchar *s_pm_up = NULL;
4899 const gchar *s_am_low = NULL;
4900 const gchar *s_pm_low = NULL;
4902 gint s_am_up_len = 0;
4903 gint s_pm_up_len = 0;
4904 gint s_am_low_len = 0;
4905 gint s_pm_low_len = 0;
4907 const gchar *def_loc_format = NULL;
4908 const gchar *date_loc_format = NULL;
4909 const gchar *time_loc_format = NULL;
4910 const gchar *time_am_pm = NULL;
4912 static gboolean time_names_init_done = FALSE;
4914 static void init_time_names(void)
4918 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4919 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4920 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4921 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4922 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4923 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4924 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4926 monthnames[0] = Q_("Complete month name for use by strftime|January");
4927 monthnames[1] = Q_("Complete month name for use by strftime|February");
4928 monthnames[2] = Q_("Complete month name for use by strftime|March");
4929 monthnames[3] = Q_("Complete month name for use by strftime|April");
4930 monthnames[4] = Q_("Complete month name for use by strftime|May");
4931 monthnames[5] = Q_("Complete month name for use by strftime|June");
4932 monthnames[6] = Q_("Complete month name for use by strftime|July");
4933 monthnames[7] = Q_("Complete month name for use by strftime|August");
4934 monthnames[8] = Q_("Complete month name for use by strftime|September");
4935 monthnames[9] = Q_("Complete month name for use by strftime|October");
4936 monthnames[10] = Q_("Complete month name for use by strftime|November");
4937 monthnames[11] = Q_("Complete month name for use by strftime|December");
4939 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4940 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4941 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4942 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4943 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4944 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4945 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4947 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4948 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4949 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4950 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4951 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4952 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4953 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4954 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4955 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4956 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4957 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4958 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4960 for (i = 0; i < 7; i++) {
4961 daynames_len[i] = strlen(daynames[i]);
4962 s_daynames_len[i] = strlen(s_daynames[i]);
4964 for (i = 0; i < 12; i++) {
4965 monthnames_len[i] = strlen(monthnames[i]);
4966 s_monthnames_len[i] = strlen(s_monthnames[i]);
4969 s_am_up = Q_("For use by strftime (morning)|AM");
4970 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4971 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4972 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4974 s_am_up_len = strlen(s_am_up);
4975 s_pm_up_len = strlen(s_pm_up);
4976 s_am_low_len = strlen(s_am_low);
4977 s_pm_low_len = strlen(s_pm_low);
4979 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4980 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4981 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4983 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4985 time_names_init_done = TRUE;
4988 #define CHECK_SIZE() { \
4989 total_done += len; \
4990 if (total_done >= buflen) { \
4991 buf[buflen-1] = '\0'; \
4996 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4998 gchar *curpos = buf;
4999 gint total_done = 0;
5000 gchar subbuf[64], subfmt[64];
5001 static time_t last_tzset = (time_t)0;
5003 if (!time_names_init_done)
5006 if (format == NULL || lt == NULL)
5009 if (last_tzset != time(NULL)) {
5011 last_tzset = time(NULL);
5014 if (*format == '%') {
5015 gint len = 0, tmp = 0;
5019 len = 1; CHECK_SIZE();
5023 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5024 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5027 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5028 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5032 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5033 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5036 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5037 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5040 fast_strftime(subbuf, 64, def_loc_format, lt);
5041 len = strlen(subbuf); CHECK_SIZE();
5042 strncpy2(curpos, subbuf, buflen - total_done);
5045 total_done += 2; CHECK_SIZE();
5046 tmp = (lt->tm_year + 1900)/100;
5047 *curpos++ = '0'+(tmp / 10);
5048 *curpos++ = '0'+(tmp % 10);
5051 total_done += 2; CHECK_SIZE();
5052 *curpos++ = '0'+(lt->tm_mday / 10);
5053 *curpos++ = '0'+(lt->tm_mday % 10);
5056 total_done += 8; CHECK_SIZE();
5057 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5058 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5060 *curpos++ = '0'+(lt->tm_mday / 10);
5061 *curpos++ = '0'+(lt->tm_mday % 10);
5063 tmp = lt->tm_year%100;
5064 *curpos++ = '0'+(tmp / 10);
5065 *curpos++ = '0'+(tmp % 10);
5068 len = 2; CHECK_SIZE();
5069 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5072 len = 10; CHECK_SIZE();
5073 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5074 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5077 total_done += 2; CHECK_SIZE();
5078 *curpos++ = '0'+(lt->tm_hour / 10);
5079 *curpos++ = '0'+(lt->tm_hour % 10);
5082 total_done += 2; CHECK_SIZE();
5088 *curpos++ = '0'+(tmp / 10);
5089 *curpos++ = '0'+(tmp % 10);
5092 len = 3; CHECK_SIZE();
5093 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5096 len = 2; CHECK_SIZE();
5097 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5100 len = 2; CHECK_SIZE();
5106 snprintf(curpos, buflen - total_done, "%2d", tmp);
5109 total_done += 2; CHECK_SIZE();
5110 tmp = lt->tm_mon + 1;
5111 *curpos++ = '0'+(tmp / 10);
5112 *curpos++ = '0'+(tmp % 10);
5115 total_done += 2; CHECK_SIZE();
5116 *curpos++ = '0'+(lt->tm_min / 10);
5117 *curpos++ = '0'+(lt->tm_min % 10);
5120 len = 1; CHECK_SIZE();
5124 if (lt->tm_hour >= 12) {
5125 len = s_pm_up_len; CHECK_SIZE();
5126 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5128 len = s_am_up_len; CHECK_SIZE();
5129 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5133 if (lt->tm_hour >= 12) {
5134 len = s_pm_low_len; CHECK_SIZE();
5135 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5137 len = s_am_low_len; CHECK_SIZE();
5138 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5142 fast_strftime(subbuf, 64, time_am_pm, lt);
5143 len = strlen(subbuf); CHECK_SIZE();
5144 strncpy2(curpos, subbuf, buflen - total_done);
5147 total_done += 5; CHECK_SIZE();
5148 *curpos++ = '0'+(lt->tm_hour / 10);
5149 *curpos++ = '0'+(lt->tm_hour % 10);
5151 *curpos++ = '0'+(lt->tm_min / 10);
5152 *curpos++ = '0'+(lt->tm_min % 10);
5155 snprintf(subbuf, 64, "%ld", mktime(lt));
5156 len = strlen(subbuf); CHECK_SIZE();
5157 strncpy2(curpos, subbuf, buflen - total_done);
5160 total_done += 2; CHECK_SIZE();
5161 *curpos++ = '0'+(lt->tm_sec / 10);
5162 *curpos++ = '0'+(lt->tm_sec % 10);
5165 len = 1; CHECK_SIZE();
5169 total_done += 8; CHECK_SIZE();
5170 *curpos++ = '0'+(lt->tm_hour / 10);
5171 *curpos++ = '0'+(lt->tm_hour % 10);
5173 *curpos++ = '0'+(lt->tm_min / 10);
5174 *curpos++ = '0'+(lt->tm_min % 10);
5176 *curpos++ = '0'+(lt->tm_sec / 10);
5177 *curpos++ = '0'+(lt->tm_sec % 10);
5180 len = 1; CHECK_SIZE();
5181 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5184 len = 1; CHECK_SIZE();
5185 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5188 fast_strftime(subbuf, 64, date_loc_format, lt);
5189 len = strlen(subbuf); CHECK_SIZE();
5190 strncpy2(curpos, subbuf, buflen - total_done);
5193 fast_strftime(subbuf, 64, time_loc_format, lt);
5194 len = strlen(subbuf); CHECK_SIZE();
5195 strncpy2(curpos, subbuf, buflen - total_done);
5198 total_done += 2; CHECK_SIZE();
5199 tmp = lt->tm_year%100;
5200 *curpos++ = '0'+(tmp / 10);
5201 *curpos++ = '0'+(tmp % 10);
5204 len = 4; CHECK_SIZE();
5205 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5215 /* let these complicated ones be done with the libc */
5216 snprintf(subfmt, 64, "%%%c", *format);
5217 strftime(subbuf, 64, subfmt, lt);
5218 len = strlen(subbuf); CHECK_SIZE();
5219 strncpy2(curpos, subbuf, buflen - total_done);
5223 /* let these complicated modifiers be done with the libc */
5224 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5225 strftime(subbuf, 64, subfmt, lt);
5226 len = strlen(subbuf); CHECK_SIZE();
5227 strncpy2(curpos, subbuf, buflen - total_done);
5231 if (format && *format)
5232 g_warning("format error (%c)", *format);
5239 int len = 1; CHECK_SIZE();
5240 *curpos++ = *format++;
5247 gboolean prefs_common_get_use_shred(void);
5251 #define WEXITSTATUS(x) (x)
5254 int claws_unlink(const gchar *filename)
5257 static int found_shred = -1;
5258 static const gchar *args[4];
5260 if (filename == NULL)
5263 if (prefs_common_get_use_shred()) {
5264 if (found_shred == -1) {
5266 args[0] = g_find_program_in_path("shred");
5267 debug_print("found shred: %s\n", args[0]);
5268 found_shred = (args[0] != NULL) ? 1:0;
5272 if (found_shred == 1) {
5273 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5274 if (s.st_nlink == 1) {
5277 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5278 NULL, NULL, NULL, NULL, &status, NULL);
5279 debug_print("%s %s exited with status %d\n",
5280 args[0], filename, WEXITSTATUS(status));
5281 if (truncate(filename, 0) < 0)
5282 g_warning("couln't truncate");
5287 return g_unlink(filename);