2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
29 #include <glib/gi18n.h>
39 #include <sys/param.h>
41 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
49 #include <sys/types.h>
51 # include <sys/wait.h>
58 #include <sys/utsname.h>
72 # include <tablet-browser-interface.h>
74 # include <osso-browser-interface.h>
80 #include "../codeconv.h"
84 static gboolean debug_mode = FALSE;
86 static GSList *tempfiles=NULL;
89 /* Return true if we are running as root. This function should beused
90 instead of getuid () == 0. */
91 gboolean superuser_p (void)
94 return w32_is_administrator ();
102 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
103 gint g_chdir(const gchar *path)
106 if (G_WIN32_HAVE_WIDECHAR_API()) {
111 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
117 retval = _wchdir(wpath);
129 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
130 if (cp_path == NULL) {
135 retval = chdir(cp_path);
148 gint g_chmod(const gchar *path, gint mode)
151 if (G_WIN32_HAVE_WIDECHAR_API()) {
156 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
162 retval = _wchmod(wpath, mode);
174 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
175 if (cp_path == NULL) {
180 retval = chmod(cp_path, mode);
189 return chmod(path, mode);
193 FILE* g_fopen(const gchar *filename, const gchar *mode)
196 char *name = g_win32_locale_filename_from_utf8(filename);
197 FILE* fp = fopen(name, mode);
201 return fopen(filename, mode);
204 int g_open(const gchar *filename, int flags, int mode)
207 char *name = g_win32_locale_filename_from_utf8(filename);
208 int fd = open(name, flags, mode);
212 return open(filename, flags, mode);
215 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
219 gint mkstemp_name(gchar *template, gchar **name_used)
221 static gulong count=0; /* W32-_mktemp only supports up to 27
225 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
226 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
227 (S_IRUSR | S_IWUSR));
229 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
231 perror(g_strdup_printf("cant create %s",*name_used));
237 #endif /* G_OS_WIN32 */
240 gint mkstemp(gchar *template)
243 gint res = mkstemp_name(template, &dummyname);
247 #endif /* G_OS_WIN32 */
249 void list_free_strings(GList *list)
251 list = g_list_first(list);
253 while (list != NULL) {
259 void slist_free_strings(GSList *list)
261 while (list != NULL) {
267 void slist_free_strings_full(GSList *list)
269 #if GLIB_CHECK_VERSION(2,28,0)
270 g_slist_free_full(list, (GDestroyNotify)g_free);
272 while (list != NULL) {
280 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
285 void hash_free_strings(GHashTable *table)
287 g_hash_table_foreach(table, hash_free_strings_func, NULL);
290 gint str_case_equal(gconstpointer v, gconstpointer v2)
292 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
295 guint str_case_hash(gconstpointer key)
297 const gchar *p = key;
301 h = g_ascii_tolower(h);
302 for (p += 1; *p != '\0'; p++)
303 h = (h << 5) - h + g_ascii_tolower(*p);
309 void ptr_array_free_strings(GPtrArray *array)
314 cm_return_if_fail(array != NULL);
316 for (i = 0; i < array->len; i++) {
317 str = g_ptr_array_index(array, i);
322 gint to_number(const gchar *nstr)
324 register const gchar *p;
326 if (*nstr == '\0') return -1;
328 for (p = nstr; *p != '\0'; p++)
329 if (!g_ascii_isdigit(*p)) return -1;
334 /* convert integer into string,
335 nstr must be not lower than 11 characters length */
336 gchar *itos_buf(gchar *nstr, gint n)
338 g_snprintf(nstr, 11, "%d", n);
342 /* convert integer into string */
345 static gchar nstr[11];
347 return itos_buf(nstr, n);
350 #define divide(num,divisor,i,d) \
352 i = num >> divisor; \
353 d = num & ((1<<divisor)-1); \
354 d = (d*100) >> divisor; \
359 * \brief Convert a given size in bytes in a human-readable string
361 * \param size The size expressed in bytes to convert in string
362 * \return The string that respresents the size in an human-readable way
364 gchar *to_human_readable(goffset size)
366 static gchar str[14];
367 static gchar *b_format = NULL, *kb_format = NULL,
368 *mb_format = NULL, *gb_format = NULL;
369 register int t = 0, r = 0;
370 if (b_format == NULL) {
372 kb_format = _("%d.%02dKB");
373 mb_format = _("%d.%02dMB");
374 gb_format = _("%.2fGB");
377 if (size < (goffset)1024) {
378 g_snprintf(str, sizeof(str), b_format, (gint)size);
380 } else if (size >> 10 < (goffset)1024) {
381 divide(size, 10, t, r);
382 g_snprintf(str, sizeof(str), kb_format, t, r);
384 } else if (size >> 20 < (goffset)1024) {
385 divide(size, 20, t, r);
386 g_snprintf(str, sizeof(str), mb_format, t, r);
389 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
394 /* strcmp with NULL-checking */
395 gint strcmp2(const gchar *s1, const gchar *s2)
397 if (s1 == NULL || s2 == NULL)
400 return strcmp(s1, s2);
402 /* strstr with NULL-checking */
403 gchar *strstr2(const gchar *s1, const gchar *s2)
405 if (s1 == NULL || s2 == NULL)
408 return strstr(s1, s2);
411 gint path_cmp(const gchar *s1, const gchar *s2)
416 gchar *s1buf, *s2buf;
419 if (s1 == NULL || s2 == NULL) return -1;
420 if (*s1 == '\0' || *s2 == '\0') return -1;
423 s1buf = g_strdup (s1);
424 s2buf = g_strdup (s2);
425 subst_char (s1buf, '/', G_DIR_SEPARATOR);
426 subst_char (s2buf, '/', G_DIR_SEPARATOR);
429 #endif /* !G_OS_WIN32 */
434 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
435 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
437 rc = strncmp(s1, s2, MAX(len1, len2));
441 #endif /* !G_OS_WIN32 */
445 /* remove trailing return code */
446 gchar *strretchomp(gchar *str)
450 if (!*str) return str;
452 for (s = str + strlen(str) - 1;
453 s >= str && (*s == '\n' || *s == '\r');
460 /* remove trailing character */
461 gchar *strtailchomp(gchar *str, gchar tail_char)
465 if (!*str) return str;
466 if (tail_char == '\0') return str;
468 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
474 /* remove CR (carriage return) */
475 gchar *strcrchomp(gchar *str)
479 if (!*str) return str;
481 s = str + strlen(str) - 1;
482 if (*s == '\n' && s > str && *(s - 1) == '\r') {
490 gint file_strip_crs(const gchar *file)
492 FILE *fp = NULL, *outfp = NULL;
494 gchar *out = get_tmp_file();
498 fp = g_fopen(file, "rb");
502 outfp = g_fopen(out, "wb");
508 while (fgets(buf, sizeof (buf), fp) != NULL) {
510 if (fputs(buf, outfp) == EOF) {
518 if (fclose(outfp) == EOF) {
522 if (move_file(out, file, TRUE) < 0)
534 /* Similar to `strstr' but this function ignores the case of both strings. */
535 gchar *strcasestr(const gchar *haystack, const gchar *needle)
537 size_t haystack_len = strlen(haystack);
539 return strncasestr(haystack, haystack_len, needle);
542 gchar *strncasestr(const gchar *haystack, gint haystack_len, const gchar *needle)
544 register size_t needle_len;
546 needle_len = strlen(needle);
548 if (haystack_len < needle_len || needle_len == 0)
551 while (haystack_len >= needle_len) {
552 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
553 return (gchar *)haystack;
563 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
564 gconstpointer needle, size_t needlelen)
566 const gchar *haystack_ = (const gchar *)haystack;
567 const gchar *needle_ = (const gchar *)needle;
568 const gchar *haystack_cur = (const gchar *)haystack;
569 size_t haystack_left = haystacklen;
572 return memchr(haystack_, *needle_, haystacklen);
574 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
576 if (haystacklen - (haystack_cur - haystack_) < needlelen)
578 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
579 return (gpointer)haystack_cur;
582 haystack_left = haystacklen - (haystack_cur - haystack_);
589 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
590 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
592 register const gchar *s = src;
593 register gchar *d = dest;
603 /* Examine if next block is non-ASCII string */
604 gboolean is_next_nonascii(const gchar *s)
608 /* skip head space */
609 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
611 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
612 if (*(guchar *)p > 127 || *(guchar *)p < 32)
619 gint get_next_word_len(const gchar *s)
623 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
629 static void trim_subject_for_compare(gchar *str)
633 eliminate_parenthesis(str, '[', ']');
634 eliminate_parenthesis(str, '(', ')');
637 srcp = str + subject_get_prefix_length(str);
639 memmove(str, srcp, strlen(srcp) + 1);
642 static void trim_subject_for_sort(gchar *str)
648 srcp = str + subject_get_prefix_length(str);
650 memmove(str, srcp, strlen(srcp) + 1);
653 /* compare subjects */
654 gint subject_compare(const gchar *s1, const gchar *s2)
658 if (!s1 || !s2) return -1;
659 if (!*s1 || !*s2) return -1;
661 Xstrdup_a(str1, s1, return -1);
662 Xstrdup_a(str2, s2, return -1);
664 trim_subject_for_compare(str1);
665 trim_subject_for_compare(str2);
667 if (!*str1 || !*str2) return -1;
669 return strcmp(str1, str2);
672 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
676 if (!s1 || !s2) return -1;
678 Xstrdup_a(str1, s1, return -1);
679 Xstrdup_a(str2, s2, return -1);
681 trim_subject_for_sort(str1);
682 trim_subject_for_sort(str2);
684 return g_utf8_collate(str1, str2);
687 void trim_subject(gchar *str)
689 register gchar *srcp;
695 srcp = str + subject_get_prefix_length(str);
700 } else if (*srcp == '(') {
712 else if (*srcp == cl)
719 while (g_ascii_isspace(*srcp)) srcp++;
720 memmove(str, srcp, strlen(srcp) + 1);
723 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
725 register gchar *srcp, *destp;
730 while ((destp = strchr(destp, op))) {
736 else if (*srcp == cl)
742 while (g_ascii_isspace(*srcp)) srcp++;
743 memmove(destp, srcp, strlen(srcp) + 1);
747 void extract_parenthesis(gchar *str, gchar op, gchar cl)
749 register gchar *srcp, *destp;
754 while ((srcp = strchr(destp, op))) {
757 memmove(destp, srcp + 1, strlen(srcp));
762 else if (*destp == cl)
774 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
777 register gchar *srcp, *destp;
779 gboolean in_quote = FALSE;
783 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
786 memmove(destp, srcp + 1, strlen(srcp));
789 if (*destp == op && !in_quote)
791 else if (*destp == cl && !in_quote)
793 else if (*destp == quote_chr)
805 void extract_quote(gchar *str, gchar quote_chr)
809 if ((str = strchr(str, quote_chr))) {
811 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
812 memmove(p - 1, p, strlen(p) + 1);
817 memmove(str, str + 1, p - str);
822 void eliminate_address_comment(gchar *str)
824 register gchar *srcp, *destp;
829 while ((destp = strchr(destp, '"'))) {
830 if ((srcp = strchr(destp + 1, '"'))) {
835 while (g_ascii_isspace(*srcp)) srcp++;
836 memmove(destp, srcp, strlen(srcp) + 1);
846 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
852 else if (*srcp == ')')
858 while (g_ascii_isspace(*srcp)) srcp++;
859 memmove(destp, srcp, strlen(srcp) + 1);
863 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
865 gboolean in_quote = FALSE;
868 if (*str == c && !in_quote)
870 if (*str == quote_chr)
878 void extract_address(gchar *str)
880 eliminate_address_comment(str);
881 if (strchr_with_skip_quote(str, '"', '<'))
882 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
886 void extract_list_id_str(gchar *str)
888 if (strchr_with_skip_quote(str, '"', '<'))
889 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
893 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
898 if (!str) return addr_list;
900 Xstrdup_a(work, str, return addr_list);
903 eliminate_address_comment(work);
906 while (workp && *workp) {
909 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
915 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
916 extract_parenthesis_with_skip_quote
917 (workp, '"', '<', '>');
921 addr_list = g_slist_append(addr_list, g_strdup(workp));
929 GSList *address_list_append(GSList *addr_list, const gchar *str)
931 return address_list_append_real(addr_list, str, TRUE);
934 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
936 return address_list_append_real(addr_list, str, FALSE);
939 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
943 if (!str) return msgid_list;
946 while (strp && *strp) {
947 const gchar *start, *end;
950 if ((start = strchr(strp, '<')) != NULL) {
951 end = strchr(start + 1, '>');
956 msgid = g_strndup(start + 1, end - start - 1);
959 msgid_list = g_slist_prepend(msgid_list, msgid);
969 GSList *references_list_append(GSList *msgid_list, const gchar *str)
973 list = references_list_prepend(NULL, str);
974 list = g_slist_reverse(list);
975 msgid_list = g_slist_concat(msgid_list, list);
980 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
985 if (!str) return group_list;
987 Xstrdup_a(work, str, return group_list);
991 while (workp && *workp) {
994 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1002 group_list = g_slist_append(group_list,
1011 GList *add_history(GList *list, const gchar *str)
1016 cm_return_val_if_fail(str != NULL, list);
1018 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1021 list = g_list_remove(list, old->data);
1023 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1026 last = g_list_last(list);
1028 oldstr = last->data;
1029 list = g_list_remove(list, last->data);
1034 list = g_list_prepend(list, g_strdup(str));
1039 void remove_return(gchar *str)
1041 register gchar *p = str;
1044 if (*p == '\n' || *p == '\r')
1045 memmove(p, p + 1, strlen(p));
1051 void remove_space(gchar *str)
1053 register gchar *p = str;
1058 while (g_ascii_isspace(*(p + spc)))
1061 memmove(p, p + spc, strlen(p + spc) + 1);
1067 void unfold_line(gchar *str)
1069 register gchar *p = str;
1073 if (*p == '\n' || *p == '\r') {
1076 while (g_ascii_isspace(*(p + spc)))
1079 memmove(p, p + spc, strlen(p + spc) + 1);
1085 void subst_char(gchar *str, gchar orig, gchar subst)
1087 register gchar *p = str;
1096 void subst_chars(gchar *str, gchar *orig, gchar subst)
1098 register gchar *p = str;
1101 if (strchr(orig, *p) != NULL)
1107 void subst_for_filename(gchar *str)
1112 subst_chars(str, "\t\r\n\\/*:", '_');
1114 subst_chars(str, "\t\r\n\\/*", '_');
1118 void subst_for_shellsafe_filename(gchar *str)
1122 subst_for_filename(str);
1123 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1126 gboolean is_ascii_str(const gchar *str)
1128 const guchar *p = (const guchar *)str;
1130 while (*p != '\0') {
1131 if (*p != '\t' && *p != ' ' &&
1132 *p != '\r' && *p != '\n' &&
1133 (*p < 32 || *p >= 127))
1141 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1143 gchar * position = NULL;
1144 gchar * tmp_pos = NULL;
1147 if (quote_chars == NULL)
1150 for (i = 0; i < strlen(quote_chars); i++) {
1151 tmp_pos = strrchr (str, quote_chars[i]);
1153 || (tmp_pos != NULL && position <= tmp_pos) )
1159 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1161 const gchar *first_pos;
1162 const gchar *last_pos;
1163 const gchar *p = str;
1164 gint quote_level = -1;
1166 /* speed up line processing by only searching to the last '>' */
1167 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1168 /* skip a line if it contains a '<' before the initial '>' */
1169 if (memchr(str, '<', first_pos - str) != NULL)
1171 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1175 while (p <= last_pos) {
1176 while (p < last_pos) {
1177 if (g_ascii_isspace(*p))
1183 if (strchr(quote_chars, *p))
1185 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1186 /* any characters are allowed except '-','<' and space */
1187 while (*p != '-' && *p != '<'
1188 && !strchr(quote_chars, *p)
1189 && !g_ascii_isspace(*p)
1192 if (strchr(quote_chars, *p))
1204 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1206 const gchar *p = str, *q;
1207 gint cur_line = 0, len;
1209 while ((q = strchr(p, '\n')) != NULL) {
1211 if (len > max_chars) {
1221 if (len > max_chars) {
1230 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1232 gchar * position = NULL;
1233 gchar * tmp_pos = NULL;
1236 if (quote_chars == NULL)
1239 for (i = 0; i < strlen(quote_chars); i++) {
1240 tmp_pos = strchr (str, quote_chars[i]);
1242 || (tmp_pos != NULL && position >= tmp_pos) )
1248 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1250 register guint haystack_len, needle_len;
1251 gboolean in_squote = FALSE, in_dquote = FALSE;
1253 haystack_len = strlen(haystack);
1254 needle_len = strlen(needle);
1256 if (haystack_len < needle_len || needle_len == 0)
1259 while (haystack_len >= needle_len) {
1260 if (!in_squote && !in_dquote &&
1261 !strncmp(haystack, needle, needle_len))
1262 return (gchar *)haystack;
1264 /* 'foo"bar"' -> foo"bar"
1265 "foo'bar'" -> foo'bar' */
1266 if (*haystack == '\'') {
1269 else if (!in_dquote)
1271 } else if (*haystack == '\"') {
1274 else if (!in_squote)
1285 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1288 GSList *string_list = NULL, *slist;
1289 gchar **str_array, *s, *new_str;
1290 guint i, n = 1, len;
1292 cm_return_val_if_fail(str != NULL, NULL);
1293 cm_return_val_if_fail(delim != NULL, NULL);
1296 max_tokens = G_MAXINT;
1298 s = strstr_with_skip_quote(str, delim);
1300 guint delimiter_len = strlen(delim);
1304 new_str = g_strndup(str, len);
1306 if (new_str[0] == '\'' || new_str[0] == '\"') {
1307 if (new_str[len - 1] == new_str[0]) {
1308 new_str[len - 1] = '\0';
1309 memmove(new_str, new_str + 1, len - 1);
1312 string_list = g_slist_prepend(string_list, new_str);
1314 str = s + delimiter_len;
1315 s = strstr_with_skip_quote(str, delim);
1316 } while (--max_tokens && s);
1320 new_str = g_strdup(str);
1321 if (new_str[0] == '\'' || new_str[0] == '\"') {
1323 if (new_str[len - 1] == new_str[0]) {
1324 new_str[len - 1] = '\0';
1325 memmove(new_str, new_str + 1, len - 1);
1328 string_list = g_slist_prepend(string_list, new_str);
1332 str_array = g_new(gchar*, n);
1336 str_array[i--] = NULL;
1337 for (slist = string_list; slist; slist = slist->next)
1338 str_array[i--] = slist->data;
1340 g_slist_free(string_list);
1345 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1347 gchar *abbrev_group;
1349 const gchar *p = group;
1352 cm_return_val_if_fail(group != NULL, NULL);
1354 last = group + strlen(group);
1355 abbrev_group = ap = g_malloc(strlen(group) + 1);
1360 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1362 while (*p != '.') p++;
1365 return abbrev_group;
1370 return abbrev_group;
1373 gchar *trim_string(const gchar *str, gint len)
1375 const gchar *p = str;
1380 if (!str) return NULL;
1381 if (strlen(str) <= len)
1382 return g_strdup(str);
1383 if (g_utf8_validate(str, -1, NULL) == FALSE)
1384 return g_strdup(str);
1386 while (*p != '\0') {
1387 mb_len = g_utf8_skip[*(guchar *)p];
1390 else if (new_len + mb_len > len)
1397 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1398 return g_strconcat(new_str, "...", NULL);
1401 GList *uri_list_extract_filenames(const gchar *uri_list)
1403 GList *result = NULL;
1405 gchar *escaped_utf8uri;
1411 while (g_ascii_isspace(*p)) p++;
1412 if (!strncmp(p, "file:", 5)) {
1415 while (*q && *q != '\n' && *q != '\r') q++;
1418 gchar *file, *locale_file = NULL;
1420 while (q > p && g_ascii_isspace(*q))
1422 Xalloca(escaped_utf8uri, q - p + 2,
1424 Xalloca(file, q - p + 2,
1427 strncpy(escaped_utf8uri, p, q - p + 1);
1428 escaped_utf8uri[q - p + 1] = '\0';
1429 decode_uri(file, escaped_utf8uri);
1431 * g_filename_from_uri() rejects escaped/locale encoded uri
1432 * string which come from Nautilus.
1435 if (g_utf8_validate(file, -1, NULL))
1437 = conv_codeset_strdup(
1440 conv_get_locale_charset_str());
1442 locale_file = g_strdup(file + 5);
1444 locale_file = g_filename_from_uri(file, NULL, NULL);
1446 result = g_list_append(result, locale_file);
1450 p = strchr(p, '\n');
1457 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1460 static gint axtoi(const gchar *hexstr)
1462 gint hi, lo, result;
1465 if ('0' <= hi && hi <= '9') {
1468 if ('a' <= hi && hi <= 'f') {
1471 if ('A' <= hi && hi <= 'F') {
1476 if ('0' <= lo && lo <= '9') {
1479 if ('a' <= lo && lo <= 'f') {
1482 if ('A' <= lo && lo <= 'F') {
1485 result = lo + (16 * hi);
1489 gboolean is_uri_string(const gchar *str)
1491 while (str && *str && g_ascii_isspace(*str))
1493 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1494 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1495 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1496 g_ascii_strncasecmp(str, "www.", 4) == 0);
1499 gchar *get_uri_path(const gchar *uri)
1501 while (uri && *uri && g_ascii_isspace(*uri))
1503 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1504 return (gchar *)(uri + 7);
1505 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1506 return (gchar *)(uri + 8);
1507 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1508 return (gchar *)(uri + 6);
1510 return (gchar *)uri;
1513 gint get_uri_len(const gchar *str)
1517 if (is_uri_string(str)) {
1518 for (p = str; *p != '\0'; p++) {
1519 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1528 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1529 * plusses, and escape characters are used)
1531 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1533 gchar *dec = decoded_uri;
1534 const gchar *enc = encoded_uri;
1539 if (isxdigit((guchar)enc[0]) &&
1540 isxdigit((guchar)enc[1])) {
1546 if (with_plus && *enc == '+')
1558 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1560 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1563 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1565 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1566 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1570 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1571 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1575 const gchar *forbidden_uris[] = { ".gnupg/",
1581 gint num_attach = 0;
1582 gchar **my_att = NULL;
1584 Xstrdup_a(tmp_mailto, mailto, return -1);
1586 if (!strncmp(tmp_mailto, "mailto:", 7))
1589 p = strchr(tmp_mailto, '?');
1596 *to = decode_uri_gdup(tmp_mailto);
1598 my_att = g_malloc(sizeof(char *));
1602 gchar *field, *value;
1619 if (*value == '\0') continue;
1621 if (from && !g_ascii_strcasecmp(field, "from")) {
1623 *from = decode_uri_gdup(value);
1625 gchar *tmp = decode_uri_gdup(value);
1626 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1630 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1632 *cc = decode_uri_gdup(value);
1634 gchar *tmp = decode_uri_gdup(value);
1635 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1639 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1641 *bcc = decode_uri_gdup(value);
1643 gchar *tmp = decode_uri_gdup(value);
1644 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1648 } else if (subject && !*subject &&
1649 !g_ascii_strcasecmp(field, "subject")) {
1650 *subject = decode_uri_gdup(value);
1651 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1652 *body = decode_uri_gdup(value);
1653 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1654 gchar *tmp = decode_uri_gdup(value);
1655 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1656 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1660 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1662 gchar *tmp = decode_uri_gdup(value);
1663 for (; forbidden_uris[i]; i++) {
1664 if (strstr(tmp, forbidden_uris[i])) {
1665 g_print("Refusing to attach '%s', potential private data leak\n",
1673 /* attach is correct */
1675 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1676 my_att[num_attach-1] = tmp;
1677 my_att[num_attach] = NULL;
1679 } else if (inreplyto && !*inreplyto &&
1680 !g_ascii_strcasecmp(field, "in-reply-to")) {
1681 *inreplyto = decode_uri_gdup(value);
1692 #include <windows.h>
1693 #ifndef CSIDL_APPDATA
1694 #define CSIDL_APPDATA 0x001a
1696 #ifndef CSIDL_LOCAL_APPDATA
1697 #define CSIDL_LOCAL_APPDATA 0x001c
1699 #ifndef CSIDL_FLAG_CREATE
1700 #define CSIDL_FLAG_CREATE 0x8000
1702 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1706 w32_strerror (int w32_errno)
1708 static char strerr[256];
1709 int ec = (int)GetLastError ();
1713 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1714 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1715 strerr, DIM (strerr)-1, NULL);
1719 static __inline__ void *
1720 dlopen (const char * name, int flag)
1722 void * hd = LoadLibrary (name);
1726 static __inline__ void *
1727 dlsym (void * hd, const char * sym)
1731 void * fnc = GetProcAddress (hd, sym);
1740 static __inline__ const char *
1743 return w32_strerror (0);
1747 static __inline__ int
1759 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1761 static int initialized;
1762 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1766 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1772 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1774 handle = dlopen (dllnames[i], RTLD_LAZY);
1777 func = dlsym (handle, "SHGetFolderPathW");
1788 return func (a,b,c,d,e);
1793 /* Returns a static string with the directroy from which the module
1794 has been loaded. Returns an empty string on error. */
1795 static char *w32_get_module_dir(void)
1797 static char *moddir;
1800 char name[MAX_PATH+10];
1803 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1806 p = strrchr (name, '\\');
1812 moddir = g_strdup (name);
1816 #endif /* G_OS_WIN32 */
1818 /* Return a static string with the locale dir. */
1819 const gchar *get_locale_dir(void)
1821 static gchar *loc_dir;
1825 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1826 "\\share\\locale", NULL);
1829 loc_dir = LOCALEDIR;
1835 const gchar *get_home_dir(void)
1838 static char home_dir_utf16[MAX_PATH] = "";
1839 static gchar *home_dir_utf8 = NULL;
1840 if (home_dir_utf16[0] == '\0') {
1841 if (w32_shgetfolderpath
1842 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1843 NULL, 0, home_dir_utf16) < 0)
1844 strcpy (home_dir_utf16, "C:\\Sylpheed");
1845 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1847 return home_dir_utf8;
1849 static const gchar *homeenv = NULL;
1854 if (!homeenv && g_getenv("HOME") != NULL)
1855 homeenv = g_strdup(g_getenv("HOME"));
1857 homeenv = g_get_home_dir();
1863 static gchar *claws_rc_dir = NULL;
1864 static gboolean rc_dir_alt = FALSE;
1865 const gchar *get_rc_dir(void)
1869 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1872 return claws_rc_dir;
1875 void set_rc_dir(const gchar *dir)
1877 if (claws_rc_dir != NULL) {
1878 g_print("Error: rc_dir already set\n");
1881 if (g_path_is_absolute(dir))
1882 claws_rc_dir = g_strdup(dir);
1884 claws_rc_dir = g_strconcat(g_get_current_dir(),
1885 G_DIR_SEPARATOR_S, dir, NULL);
1887 debug_print("set rc_dir to %s\n", claws_rc_dir);
1888 if (!is_dir_exist(claws_rc_dir)) {
1889 if (make_dir_hier(claws_rc_dir) != 0) {
1890 g_print("Error: can't create %s\n",
1897 gboolean rc_dir_is_alt(void) {
1901 const gchar *get_mail_base_dir(void)
1903 return get_home_dir();
1907 const gchar *prefs_common_get_data_root(void);
1908 gchar *last_data_root = NULL;
1911 const gchar *get_news_cache_dir(void)
1913 static gchar *news_cache_dir = NULL;
1915 const gchar *data_root = prefs_common_get_data_root();
1916 if (strcmp2(data_root, last_data_root)) {
1917 g_free(news_cache_dir);
1918 news_cache_dir = NULL;
1921 if (!news_cache_dir)
1923 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1924 NEWS_CACHE_DIR, NULL);
1928 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1929 "Claws", G_DIR_SEPARATOR_S,
1930 g_get_user_name(), G_DIR_SEPARATOR_S,
1931 NEWS_CACHE_DIR, NULL);
1932 g_free(last_data_root);
1933 last_data_root = g_strdup(last_data_root);
1935 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1936 NEWS_CACHE_DIR, NULL);
1937 g_free(last_data_root);
1938 last_data_root = NULL;
1942 return news_cache_dir;
1945 const gchar *get_imap_cache_dir(void)
1947 static gchar *imap_cache_dir = NULL;
1949 const gchar *data_root = prefs_common_get_data_root();
1950 if (strcmp2(data_root, last_data_root)) {
1951 g_free(imap_cache_dir);
1952 imap_cache_dir = NULL;
1956 if (!imap_cache_dir)
1958 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1959 IMAP_CACHE_DIR, NULL);
1963 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1964 "Claws", G_DIR_SEPARATOR_S,
1965 g_get_user_name(), G_DIR_SEPARATOR_S,
1966 IMAP_CACHE_DIR, NULL);
1967 g_free(last_data_root);
1968 last_data_root = g_strdup(last_data_root);
1970 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1971 IMAP_CACHE_DIR, NULL);
1972 g_free(last_data_root);
1973 last_data_root = NULL;
1978 return imap_cache_dir;
1981 const gchar *get_mime_tmp_dir(void)
1983 static gchar *mime_tmp_dir = NULL;
1986 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1987 MIME_TMP_DIR, NULL);
1989 return mime_tmp_dir;
1992 const gchar *get_template_dir(void)
1994 static gchar *template_dir = NULL;
1997 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1998 TEMPLATE_DIR, NULL);
2000 return template_dir;
2004 const gchar *get_cert_file(void)
2006 const gchar *cert_file = NULL;
2008 cert_file = g_strconcat(w32_get_module_dir(),
2009 "\\share\\claws-mail\\",
2010 "ca-certificates.crt",
2016 /* Return the filepath of the claws-mail.desktop file */
2017 const gchar *get_desktop_file(void)
2019 #ifdef DESKTOPFILEPATH
2020 return DESKTOPFILEPATH;
2026 /* Return the default directory for Plugins. */
2027 const gchar *get_plugin_dir(void)
2030 static gchar *plugin_dir = NULL;
2033 plugin_dir = g_strconcat(w32_get_module_dir(),
2034 "\\lib\\claws-mail\\plugins\\",
2038 if (is_dir_exist(PLUGINDIR))
2041 static gchar *plugin_dir = NULL;
2043 plugin_dir = g_strconcat(get_rc_dir(),
2044 G_DIR_SEPARATOR_S, "plugins",
2045 G_DIR_SEPARATOR_S, NULL);
2053 /* Return the default directory for Themes. */
2054 const gchar *get_themes_dir(void)
2056 static gchar *themes_dir = NULL;
2059 themes_dir = g_strconcat(w32_get_module_dir(),
2060 "\\share\\claws-mail\\themes",
2066 const gchar *get_tmp_dir(void)
2068 static gchar *tmp_dir = NULL;
2071 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2077 gchar *get_tmp_file(void)
2080 static guint32 id = 0;
2082 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2083 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2088 const gchar *get_domain_name(void)
2091 static gchar *domain_name = NULL;
2097 if (gethostname(hostname, sizeof(hostname)) != 0) {
2098 perror("gethostname");
2099 domain_name = "unknown";
2101 hostname[sizeof(hostname) - 1] = '\0';
2102 if ((hp = my_gethostbyname(hostname)) == NULL) {
2103 perror("gethostbyname");
2104 domain_name = g_strdup(hostname);
2106 domain_name = g_strdup(hp->h_name);
2109 debug_print("domain name = %s\n", domain_name);
2118 off_t get_file_size(const gchar *file)
2122 if (g_stat(file, &s) < 0) {
2123 FILE_OP_ERROR(file, "stat");
2130 time_t get_file_mtime(const gchar *file)
2134 if (g_stat(file, &s) < 0) {
2135 FILE_OP_ERROR(file, "stat");
2142 off_t get_file_size_as_crlf(const gchar *file)
2146 gchar buf[BUFFSIZE];
2148 if ((fp = g_fopen(file, "rb")) == NULL) {
2149 FILE_OP_ERROR(file, "g_fopen");
2153 while (fgets(buf, sizeof(buf), fp) != NULL) {
2155 size += strlen(buf) + 2;
2159 FILE_OP_ERROR(file, "fgets");
2168 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2175 if (g_stat(file, &s) < 0) {
2176 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2180 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2187 /* Test on whether FILE is a relative file name. This is
2188 * straightforward for Unix but more complex for Windows. */
2189 gboolean is_relative_filename(const gchar *file)
2194 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2195 return FALSE; /* Prefixed with a hostname - this can't
2196 * be a relative name. */
2198 if ( ((*file >= 'a' && *file <= 'z')
2199 || (*file >= 'A' && *file <= 'Z'))
2201 file += 2; /* Skip drive letter. */
2203 return !(*file == '\\' || *file == '/');
2205 return !(*file == G_DIR_SEPARATOR);
2210 gboolean is_dir_exist(const gchar *dir)
2215 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2218 gboolean is_file_entry_exist(const gchar *file)
2223 return g_file_test(file, G_FILE_TEST_EXISTS);
2226 gboolean dirent_is_regular_file(struct dirent *d)
2228 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2229 if (d->d_type == DT_REG)
2231 else if (d->d_type != DT_UNKNOWN)
2235 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2238 gint change_dir(const gchar *dir)
2240 gchar *prevdir = NULL;
2243 prevdir = g_get_current_dir();
2245 if (g_chdir(dir) < 0) {
2246 FILE_OP_ERROR(dir, "chdir");
2247 if (debug_mode) g_free(prevdir);
2249 } else if (debug_mode) {
2252 cwd = g_get_current_dir();
2253 if (strcmp(prevdir, cwd) != 0)
2254 g_print("current dir: %s\n", cwd);
2262 gint make_dir(const gchar *dir)
2264 if (g_mkdir(dir, S_IRWXU) < 0) {
2265 FILE_OP_ERROR(dir, "mkdir");
2268 if (g_chmod(dir, S_IRWXU) < 0)
2269 FILE_OP_ERROR(dir, "chmod");
2274 gint make_dir_hier(const gchar *dir)
2279 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2280 parent_dir = g_strndup(dir, p - dir);
2281 if (*parent_dir != '\0') {
2282 if (!is_dir_exist(parent_dir)) {
2283 if (make_dir(parent_dir) < 0) {
2292 if (!is_dir_exist(dir)) {
2293 if (make_dir(dir) < 0)
2300 gint remove_all_files(const gchar *dir)
2303 const gchar *dir_name;
2306 prev_dir = g_get_current_dir();
2308 if (g_chdir(dir) < 0) {
2309 FILE_OP_ERROR(dir, "chdir");
2314 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2315 g_warning("failed to open directory: %s\n", dir);
2320 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2321 if (claws_unlink(dir_name) < 0)
2322 FILE_OP_ERROR(dir_name, "unlink");
2327 if (g_chdir(prev_dir) < 0) {
2328 FILE_OP_ERROR(prev_dir, "chdir");
2338 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2341 const gchar *dir_name;
2345 if (first == last) {
2346 /* Skip all the dir reading part. */
2347 gchar *filename = g_strdup_printf("%s%s%u", dir, G_DIR_SEPARATOR_S, first);
2348 if (claws_unlink(filename) < 0) {
2349 FILE_OP_ERROR(filename, "unlink");
2357 prev_dir = g_get_current_dir();
2359 if (g_chdir(dir) < 0) {
2360 FILE_OP_ERROR(dir, "chdir");
2365 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2366 g_warning("failed to open directory: %s\n", dir);
2371 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2372 file_no = to_number(dir_name);
2373 if (file_no > 0 && first <= file_no && file_no <= last) {
2374 if (is_dir_exist(dir_name))
2376 if (claws_unlink(dir_name) < 0)
2377 FILE_OP_ERROR(dir_name, "unlink");
2383 if (g_chdir(prev_dir) < 0) {
2384 FILE_OP_ERROR(prev_dir, "chdir");
2394 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2397 const gchar *dir_name;
2400 GHashTable *file_no_tbl;
2402 if (numberlist == NULL)
2405 prev_dir = g_get_current_dir();
2407 if (g_chdir(dir) < 0) {
2408 FILE_OP_ERROR(dir, "chdir");
2413 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2414 FILE_OP_ERROR(dir, "opendir");
2419 file_no_tbl = g_hash_table_new(g_direct_hash, g_direct_equal);
2420 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2421 file_no = to_number(dir_name);
2422 if (is_dir_exist(dir_name))
2425 g_hash_table_insert(file_no_tbl, GINT_TO_POINTER(file_no), GINT_TO_POINTER(1));
2429 if (g_hash_table_lookup(file_no_tbl, numberlist->data) == NULL) {
2430 debug_print("removing unwanted file %d from %s\n",
2431 GPOINTER_TO_INT(numberlist->data), dir);
2432 if (claws_unlink(dir_name) < 0)
2433 FILE_OP_ERROR(dir_name, "unlink");
2435 } while ((numberlist = g_slist_next(numberlist)));
2438 g_hash_table_destroy(file_no_tbl);
2440 if (g_chdir(prev_dir) < 0) {
2441 FILE_OP_ERROR(prev_dir, "chdir");
2451 gint remove_all_numbered_files(const gchar *dir)
2453 return remove_numbered_files(dir, 0, UINT_MAX);
2456 gint remove_dir_recursive(const gchar *dir)
2460 const gchar *dir_name;
2463 if (g_stat(dir, &s) < 0) {
2464 FILE_OP_ERROR(dir, "stat");
2465 if (ENOENT == errno) return 0;
2469 if (!S_ISDIR(s.st_mode)) {
2470 if (claws_unlink(dir) < 0) {
2471 FILE_OP_ERROR(dir, "unlink");
2478 prev_dir = g_get_current_dir();
2479 /* g_print("prev_dir = %s\n", prev_dir); */
2481 if (!path_cmp(prev_dir, dir)) {
2483 if (g_chdir("..") < 0) {
2484 FILE_OP_ERROR(dir, "chdir");
2487 prev_dir = g_get_current_dir();
2490 if (g_chdir(dir) < 0) {
2491 FILE_OP_ERROR(dir, "chdir");
2496 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2497 g_warning("failed to open directory: %s\n", dir);
2503 /* remove all files in the directory */
2504 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2505 /* g_print("removing %s\n", dir_name); */
2507 if (is_dir_exist(dir_name)) {
2508 if (remove_dir_recursive(dir_name) < 0) {
2509 g_warning("can't remove directory\n");
2513 if (claws_unlink(dir_name) < 0)
2514 FILE_OP_ERROR(dir_name, "unlink");
2520 if (g_chdir(prev_dir) < 0) {
2521 FILE_OP_ERROR(prev_dir, "chdir");
2528 if (g_rmdir(dir) < 0) {
2529 FILE_OP_ERROR(dir, "rmdir");
2536 gint rename_force(const gchar *oldpath, const gchar *newpath)
2539 if (!is_file_entry_exist(oldpath)) {
2543 if (is_file_exist(newpath)) {
2544 if (claws_unlink(newpath) < 0)
2545 FILE_OP_ERROR(newpath, "unlink");
2548 return g_rename(oldpath, newpath);
2552 * Append src file body to the tail of dest file.
2553 * Now keep_backup has no effects.
2555 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2557 FILE *src_fp, *dest_fp;
2561 gboolean err = FALSE;
2563 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2564 FILE_OP_ERROR(src, "g_fopen");
2568 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2569 FILE_OP_ERROR(dest, "g_fopen");
2574 if (change_file_mode_rw(dest_fp, dest) < 0) {
2575 FILE_OP_ERROR(dest, "chmod");
2576 g_warning("can't change file mode\n");
2579 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2580 if (n_read < sizeof(buf) && ferror(src_fp))
2582 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2583 g_warning("writing to %s failed.\n", dest);
2591 if (ferror(src_fp)) {
2592 FILE_OP_ERROR(src, "fread");
2596 if (fclose(dest_fp) == EOF) {
2597 FILE_OP_ERROR(dest, "fclose");
2609 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2611 FILE *src_fp, *dest_fp;
2614 gchar *dest_bak = NULL;
2615 gboolean err = FALSE;
2617 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2618 FILE_OP_ERROR(src, "g_fopen");
2621 if (is_file_exist(dest)) {
2622 dest_bak = g_strconcat(dest, ".bak", NULL);
2623 if (rename_force(dest, dest_bak) < 0) {
2624 FILE_OP_ERROR(dest, "rename");
2631 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2632 FILE_OP_ERROR(dest, "g_fopen");
2635 if (rename_force(dest_bak, dest) < 0)
2636 FILE_OP_ERROR(dest_bak, "rename");
2642 if (change_file_mode_rw(dest_fp, dest) < 0) {
2643 FILE_OP_ERROR(dest, "chmod");
2644 g_warning("can't change file mode\n");
2647 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2648 if (n_read < sizeof(buf) && ferror(src_fp))
2650 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2651 g_warning("writing to %s failed.\n", dest);
2656 if (rename_force(dest_bak, dest) < 0)
2657 FILE_OP_ERROR(dest_bak, "rename");
2664 if (ferror(src_fp)) {
2665 FILE_OP_ERROR(src, "fread");
2669 if (fclose(dest_fp) == EOF) {
2670 FILE_OP_ERROR(dest, "fclose");
2677 if (rename_force(dest_bak, dest) < 0)
2678 FILE_OP_ERROR(dest_bak, "rename");
2684 if (keep_backup == FALSE && dest_bak)
2685 claws_unlink(dest_bak);
2692 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2694 if (overwrite == FALSE && is_file_exist(dest)) {
2695 g_warning("move_file(): file %s already exists.", dest);
2699 if (rename_force(src, dest) == 0) return 0;
2701 if (EXDEV != errno) {
2702 FILE_OP_ERROR(src, "rename");
2706 if (copy_file(src, dest, FALSE) < 0) return -1;
2713 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2716 gint bytes_left, to_read;
2719 if (fseek(fp, offset, SEEK_SET) < 0) {
2724 bytes_left = length;
2725 to_read = MIN(bytes_left, sizeof(buf));
2727 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2728 if (n_read < to_read && ferror(fp))
2730 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2733 bytes_left -= n_read;
2734 if (bytes_left == 0)
2736 to_read = MIN(bytes_left, sizeof(buf));
2747 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2750 gboolean err = FALSE;
2752 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2753 FILE_OP_ERROR(dest, "g_fopen");
2757 if (change_file_mode_rw(dest_fp, dest) < 0) {
2758 FILE_OP_ERROR(dest, "chmod");
2759 g_warning("can't change file mode\n");
2762 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2765 if (!err && fclose(dest_fp) == EOF) {
2766 FILE_OP_ERROR(dest, "fclose");
2771 g_warning("writing to %s failed.\n", dest);
2779 /* convert line endings into CRLF. If the last line doesn't end with
2780 * linebreak, add it.
2782 gchar *canonicalize_str(const gchar *str)
2788 for (p = str; *p != '\0'; ++p) {
2795 if (p == str || *(p - 1) != '\n')
2798 out = outp = g_malloc(new_len + 1);
2799 for (p = str; *p != '\0'; ++p) {
2806 if (p == str || *(p - 1) != '\n') {
2815 gint canonicalize_file(const gchar *src, const gchar *dest)
2817 FILE *src_fp, *dest_fp;
2818 gchar buf[BUFFSIZE];
2820 gboolean err = FALSE;
2821 gboolean last_linebreak = FALSE;
2823 if (src == NULL || dest == NULL)
2826 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2827 FILE_OP_ERROR(src, "g_fopen");
2831 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2832 FILE_OP_ERROR(dest, "g_fopen");
2837 if (change_file_mode_rw(dest_fp, dest) < 0) {
2838 FILE_OP_ERROR(dest, "chmod");
2839 g_warning("can't change file mode\n");
2842 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2846 if (len == 0) break;
2847 last_linebreak = FALSE;
2849 if (buf[len - 1] != '\n') {
2850 last_linebreak = TRUE;
2851 r = fputs(buf, dest_fp);
2852 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2853 r = fputs(buf, dest_fp);
2856 r = fwrite(buf, 1, len - 1, dest_fp);
2861 r = fputs("\r\n", dest_fp);
2865 g_warning("writing to %s failed.\n", dest);
2873 if (last_linebreak == TRUE) {
2874 if (fputs("\r\n", dest_fp) == EOF)
2878 if (ferror(src_fp)) {
2879 FILE_OP_ERROR(src, "fgets");
2883 if (fclose(dest_fp) == EOF) {
2884 FILE_OP_ERROR(dest, "fclose");
2896 gint canonicalize_file_replace(const gchar *file)
2900 tmp_file = get_tmp_file();
2902 if (canonicalize_file(file, tmp_file) < 0) {
2907 if (move_file(tmp_file, file, TRUE) < 0) {
2908 g_warning("can't replace %s .\n", file);
2909 claws_unlink(tmp_file);
2918 gchar *normalize_newlines(const gchar *str)
2923 out = outp = g_malloc(strlen(str) + 1);
2924 for (p = str; *p != '\0'; ++p) {
2926 if (*(p + 1) != '\n')
2937 gchar *get_outgoing_rfc2822_str(FILE *fp)
2939 gchar buf[BUFFSIZE];
2943 str = g_string_new(NULL);
2945 /* output header part */
2946 while (fgets(buf, sizeof(buf), fp) != NULL) {
2948 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2955 else if (next != ' ' && next != '\t') {
2959 if (fgets(buf, sizeof(buf), fp) == NULL)
2963 g_string_append(str, buf);
2964 g_string_append(str, "\r\n");
2970 /* output body part */
2971 while (fgets(buf, sizeof(buf), fp) != NULL) {
2974 g_string_append_c(str, '.');
2975 g_string_append(str, buf);
2976 g_string_append(str, "\r\n");
2980 g_string_free(str, FALSE);
2986 * Create a new boundary in a way that it is very unlikely that this
2987 * will occur in the following text. It would be easy to ensure
2988 * uniqueness if everything is either quoted-printable or base64
2989 * encoded (note that conversion is allowed), but because MIME bodies
2990 * may be nested, it may happen that the same boundary has already
2993 * boundary := 0*69<bchars> bcharsnospace
2994 * bchars := bcharsnospace / " "
2995 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2996 * "+" / "_" / "," / "-" / "." /
2997 * "/" / ":" / "=" / "?"
2999 * some special characters removed because of buggy MTAs
3002 gchar *generate_mime_boundary(const gchar *prefix)
3004 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3005 "abcdefghijklmnopqrstuvwxyz"
3010 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3011 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3014 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
3018 gint change_file_mode_rw(FILE *fp, const gchar *file)
3021 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3023 return g_chmod(file, S_IRUSR|S_IWUSR);
3027 FILE *my_tmpfile(void)
3029 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3030 const gchar suffix[] = ".XXXXXX";
3031 const gchar *tmpdir;
3033 const gchar *progname;
3042 tmpdir = get_tmp_dir();
3043 tmplen = strlen(tmpdir);
3044 progname = g_get_prgname();
3045 if (progname == NULL)
3046 progname = "claws-mail";
3047 proglen = strlen(progname);
3048 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3051 memcpy(fname, tmpdir, tmplen);
3052 fname[tmplen] = G_DIR_SEPARATOR;
3053 memcpy(fname + tmplen + 1, progname, proglen);
3054 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3056 fd = mkstemp(fname);
3061 claws_unlink(fname);
3063 /* verify that we can write in the file after unlinking */
3064 if (write(fd, buf, 1) < 0) {
3071 fp = fdopen(fd, "w+b");
3079 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3084 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3088 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3089 dir, G_DIR_SEPARATOR);
3090 fd = mkstemp_name(template, filename);
3093 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3094 fd = mkstemp(*filename);
3096 return fdopen(fd, "w+");
3099 FILE *str_open_as_stream(const gchar *str)
3104 cm_return_val_if_fail(str != NULL, NULL);
3108 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3113 if (len == 0) return fp;
3115 if (fwrite(str, 1, len, fp) != len) {
3116 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3125 gint str_write_to_file(const gchar *str, const gchar *file)
3130 cm_return_val_if_fail(str != NULL, -1);
3131 cm_return_val_if_fail(file != NULL, -1);
3133 if ((fp = g_fopen(file, "wb")) == NULL) {
3134 FILE_OP_ERROR(file, "g_fopen");
3144 if (fwrite(str, 1, len, fp) != len) {
3145 FILE_OP_ERROR(file, "fwrite");
3151 if (fclose(fp) == EOF) {
3152 FILE_OP_ERROR(file, "fclose");
3160 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3167 cm_return_val_if_fail(fp != NULL, NULL);
3169 array = g_byte_array_new();
3171 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3172 if (n_read < sizeof(buf) && ferror(fp))
3174 g_byte_array_append(array, buf, n_read);
3178 FILE_OP_ERROR("file stream", "fread");
3179 g_byte_array_free(array, TRUE);
3184 g_byte_array_append(array, buf, 1);
3185 str = (gchar *)array->data;
3186 g_byte_array_free(array, FALSE);
3188 if (recode && !g_utf8_validate(str, -1, NULL)) {
3189 const gchar *src_codeset, *dest_codeset;
3191 src_codeset = conv_get_locale_charset_str();
3192 dest_codeset = CS_UTF_8;
3193 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3201 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3208 struct timeval timeout = {1, 0};
3213 cm_return_val_if_fail(file != NULL, NULL);
3215 if (g_stat(file, &s) != 0) {
3216 FILE_OP_ERROR(file, "stat");
3219 if (S_ISDIR(s.st_mode)) {
3220 g_warning("%s: is a directory\n", file);
3225 fp = g_fopen (file, "rb");
3227 FILE_OP_ERROR(file, "open");
3231 /* test whether the file is readable without blocking */
3232 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3234 FILE_OP_ERROR(file, "open");
3241 /* allow for one second */
3242 err = select(fd+1, &fds, NULL, NULL, &timeout);
3243 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3245 FILE_OP_ERROR(file, "select");
3247 g_warning("%s: doesn't seem readable\n", file);
3253 /* Now clear O_NONBLOCK */
3254 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3255 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3259 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3260 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3265 /* get the FILE pointer */
3266 fp = fdopen(fd, "rb");
3269 FILE_OP_ERROR(file, "fdopen");
3270 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3275 str = file_read_stream_to_str_full(fp, recode);
3282 gchar *file_read_to_str(const gchar *file)
3284 return file_read_to_str_full(file, TRUE);
3286 gchar *file_read_stream_to_str(FILE *fp)
3288 return file_read_stream_to_str_full(fp, TRUE);
3291 gchar *file_read_to_str_no_recode(const gchar *file)
3293 return file_read_to_str_full(file, FALSE);
3295 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3297 return file_read_stream_to_str_full(fp, FALSE);
3300 char *fgets_crlf(char *buf, int size, FILE *stream)
3302 gboolean is_cr = FALSE;
3303 gboolean last_was_cr = FALSE;
3308 while (--size > 0 && (c = getc(stream)) != EOF)
3311 is_cr = (c == '\r');
3321 last_was_cr = is_cr;
3323 if (c == EOF && cs == buf)
3331 static gint execute_async(gchar *const argv[])
3333 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3335 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3336 NULL, NULL, NULL, FALSE) == FALSE) {
3337 g_warning("Couldn't execute command: %s\n", argv[0]);
3344 static gint execute_sync(gchar *const argv[])
3348 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3351 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3352 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3353 g_warning("Couldn't execute command: %s\n", argv[0]);
3357 if (WIFEXITED(status))
3358 return WEXITSTATUS(status);
3362 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3363 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3364 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3365 g_warning("Couldn't execute command: %s\n", argv[0]);
3373 gint execute_command_line(const gchar *cmdline, gboolean async)
3378 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3380 argv = strsplit_with_quote(cmdline, " ", 0);
3383 ret = execute_async(argv);
3385 ret = execute_sync(argv);
3392 gchar *get_command_output(const gchar *cmdline)
3394 gchar *child_stdout;
3397 cm_return_val_if_fail(cmdline != NULL, NULL);
3399 debug_print("get_command_output(): executing: %s\n", cmdline);
3401 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3403 g_warning("Couldn't execute command: %s\n", cmdline);
3407 return child_stdout;
3410 static gint is_unchanged_uri_char(char c)
3421 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3427 for(i = 0; i < strlen(uri) ; i++) {
3428 if (is_unchanged_uri_char(uri[i])) {
3429 if (k + 2 >= bufsize)
3431 encoded_uri[k++] = uri[i];
3434 char * hexa = "0123456789ABCDEF";
3436 if (k + 4 >= bufsize)
3438 encoded_uri[k++] = '%';
3439 encoded_uri[k++] = hexa[uri[i] / 16];
3440 encoded_uri[k++] = hexa[uri[i] % 16];
3446 gint open_uri(const gchar *uri, const gchar *cmdline)
3450 gchar buf[BUFFSIZE];
3452 gchar encoded_uri[BUFFSIZE];
3453 cm_return_val_if_fail(uri != NULL, -1);
3455 /* an option to choose whether to use encode_uri or not ? */
3456 encode_uri(encoded_uri, BUFFSIZE, uri);
3459 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3460 !strchr(p + 2, '%'))
3461 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3464 g_warning("Open URI command-line is invalid "
3465 "(there must be only one '%%s'): %s",
3467 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3470 execute_command_line(buf, TRUE);
3472 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3475 extern osso_context_t *get_osso_context(void);
3476 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3477 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3478 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3483 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3485 gchar buf[BUFFSIZE];
3488 cm_return_val_if_fail(filepath != NULL, -1);
3491 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3492 !strchr(p + 2, '%'))
3493 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3496 g_warning("Open Text Editor command-line is invalid "
3497 "(there must be only one '%%s'): %s",
3499 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3502 execute_command_line(buf, TRUE);
3507 time_t remote_tzoffset_sec(const gchar *zone)
3509 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3515 time_t remoteoffset;
3517 strncpy(zone3, zone, 3);
3521 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3522 (c == '+' || c == '-')) {
3523 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3525 remoteoffset = -remoteoffset;
3526 } else if (!strncmp(zone, "UT" , 2) ||
3527 !strncmp(zone, "GMT", 2)) {
3529 } else if (strlen(zone3) == 3) {
3530 for (p = ustzstr; *p != '\0'; p += 3) {
3531 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3532 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3533 remoteoffset = iustz * 3600;
3539 } else if (strlen(zone3) == 1) {
3541 case 'Z': remoteoffset = 0; break;
3542 case 'A': remoteoffset = -1; break;
3543 case 'B': remoteoffset = -2; break;
3544 case 'C': remoteoffset = -3; break;
3545 case 'D': remoteoffset = -4; break;
3546 case 'E': remoteoffset = -5; break;
3547 case 'F': remoteoffset = -6; break;
3548 case 'G': remoteoffset = -7; break;
3549 case 'H': remoteoffset = -8; break;
3550 case 'I': remoteoffset = -9; break;
3551 case 'K': remoteoffset = -10; break; /* J is not used */
3552 case 'L': remoteoffset = -11; break;
3553 case 'M': remoteoffset = -12; break;
3554 case 'N': remoteoffset = 1; break;
3555 case 'O': remoteoffset = 2; break;
3556 case 'P': remoteoffset = 3; break;
3557 case 'Q': remoteoffset = 4; break;
3558 case 'R': remoteoffset = 5; break;
3559 case 'S': remoteoffset = 6; break;
3560 case 'T': remoteoffset = 7; break;
3561 case 'U': remoteoffset = 8; break;
3562 case 'V': remoteoffset = 9; break;
3563 case 'W': remoteoffset = 10; break;
3564 case 'X': remoteoffset = 11; break;
3565 case 'Y': remoteoffset = 12; break;
3566 default: remoteoffset = 0; break;
3568 remoteoffset = remoteoffset * 3600;
3572 return remoteoffset;
3575 time_t tzoffset_sec(time_t *now)
3579 struct tm buf1, buf2;
3581 if (now && *now < 0)
3584 gmt = *gmtime_r(now, &buf1);
3585 lt = localtime_r(now, &buf2);
3587 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3589 if (lt->tm_year < gmt.tm_year)
3591 else if (lt->tm_year > gmt.tm_year)
3593 else if (lt->tm_yday < gmt.tm_yday)
3595 else if (lt->tm_yday > gmt.tm_yday)
3598 if (off >= 24 * 60) /* should be impossible */
3599 off = 23 * 60 + 59; /* if not, insert silly value */
3600 if (off <= -24 * 60)
3601 off = -(23 * 60 + 59);
3606 /* calculate timezone offset */
3607 gchar *tzoffset(time_t *now)
3609 static gchar offset_string[6];
3613 struct tm buf1, buf2;
3615 if (now && *now < 0)
3618 gmt = *gmtime_r(now, &buf1);
3619 lt = localtime_r(now, &buf2);
3621 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3623 if (lt->tm_year < gmt.tm_year)
3625 else if (lt->tm_year > gmt.tm_year)
3627 else if (lt->tm_yday < gmt.tm_yday)
3629 else if (lt->tm_yday > gmt.tm_yday)
3637 if (off >= 24 * 60) /* should be impossible */
3638 off = 23 * 60 + 59; /* if not, insert silly value */
3640 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3642 return offset_string;
3645 void get_rfc822_date(gchar *buf, gint len)
3649 gchar day[4], mon[4];
3650 gint dd, hh, mm, ss, yyyy;
3652 gchar buf2[BUFFSIZE];
3655 lt = localtime_r(&t, &buf1);
3657 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3658 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3660 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3661 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3664 void debug_set_mode(gboolean mode)
3669 gboolean debug_get_mode(void)
3674 void debug_print_real(const gchar *format, ...)
3677 gchar buf[BUFFSIZE];
3679 if (!debug_mode) return;
3681 va_start(args, format);
3682 g_vsnprintf(buf, sizeof(buf), format, args);
3689 const char * debug_srcname(const char *file)
3691 const char *s = strrchr (file, '/');
3696 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3698 if (subject == NULL)
3701 subject += subject_get_prefix_length(subject);
3703 return g_hash_table_lookup(subject_table, subject);
3706 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3709 if (subject == NULL || *subject == 0)
3711 subject += subject_get_prefix_length(subject);
3712 g_hash_table_insert(subject_table, subject, data);
3715 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3717 if (subject == NULL)
3720 subject += subject_get_prefix_length(subject);
3721 g_hash_table_remove(subject_table, subject);
3725 static regex_t u_regex;
3726 static gboolean u_init_;
3729 void utils_free_regex(void)
3740 *\brief Check if a string is prefixed with known (combinations)
3741 * of prefixes. The function assumes that each prefix
3742 * is terminated by zero or exactly _one_ space.
3744 *\param str String to check for a prefixes
3746 *\return int Number of chars in the prefix that should be skipped
3747 * for a "clean" subject line. If no prefix was found, 0
3750 int subject_get_prefix_length(const gchar *subject)
3753 /*!< Array with allowable reply prefixes regexps. */
3754 static const gchar * const prefixes[] = {
3755 "Re\\:", /* "Re:" */
3756 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3757 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3758 "Aw\\:", /* "Aw:" (German) */
3759 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3760 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3761 "Fw\\:", /* "Fw:" Forward */
3762 "Fwd\\:", /* "Fwd:" Forward */
3763 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3764 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3765 "Rif\\:", /* "Rif:" (Italian Outlook) */
3766 "Sv\\:", /* "Sv" (Norwegian) */
3767 "Vs\\:", /* "Vs" (Norwegian) */
3768 "Ad\\:", /* "Ad" (Norwegian) */
3769 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3770 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3771 "Re \\:", /* "Re :" (French Yahoo Mail) */
3774 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3778 if (!subject) return 0;
3779 if (!*subject) return 0;
3782 GString *s = g_string_new("");
3784 for (n = 0; n < PREFIXES; n++)
3785 /* Terminate each prefix regexpression by a
3786 * "\ ?" (zero or ONE space), and OR them */
3787 g_string_append_printf(s, "(%s\\ ?)%s",
3792 g_string_prepend(s, "(");
3793 g_string_append(s, ")+"); /* match at least once */
3794 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3797 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3798 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3799 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3800 debug_print("Error compiling regexp %s\n", s->str);
3801 g_string_free(s, TRUE);
3805 g_string_free(s, TRUE);
3809 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3814 /*!< Array with allowable reply prefixes regexps. */
3815 static const gchar * const prefixes[] = {
3817 "antw:", /* "Antw:" (Dutch / German Outlook) */
3818 "aw:", /* "Aw:" (German) */
3819 "antwort:", /* "Antwort:" (German Lotus Notes) */
3820 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3821 "fw:", /* "Fw:" Forward */
3822 "fwd:", /* "Fwd:" Forward */
3823 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3824 "odp:", /* "Odp:" Re (Polish Outlook) */
3825 "rif:", /* "Rif:" (Italian Outlook) */
3826 "sv:", /* "Sv" (Norwegian) */
3827 "vs:", /* "Vs" (Norwegian) */
3828 "ad:", /* "Ad" (Norwegian) */
3829 "R\303\251f. :", /* "R�f. :" (French Lotus Notes) */
3830 "Re :", /* "Re :" (French Yahoo Mail) */
3833 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3836 if (!subject) return 0;
3837 if (!*subject) return 0;
3839 for (n = 0; n < PREFIXES; n++) {
3840 int len = strlen(prefixes[n]);
3841 if (!strncasecmp(subject, prefixes[n], len)) {
3842 if (subject[len] == ' ')
3851 static guint g_stricase_hash(gconstpointer gptr)
3853 guint hash_result = 0;
3856 for (str = gptr; str && *str; str++) {
3857 hash_result += toupper(*str);
3863 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3865 const char *str1 = gptr1;
3866 const char *str2 = gptr2;
3868 return !strcasecmp(str1, str2);
3871 gint g_int_compare(gconstpointer a, gconstpointer b)
3873 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3876 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3884 lt = localtime_r(&t, &buft);
3886 if (user_addr != NULL)
3887 addr = g_strdup_printf(".%s", user_addr);
3888 else if (strlen(buf) != 0)
3889 addr = g_strdup_printf("@%s", buf);
3891 addr = g_strdup_printf("@%s", get_domain_name());
3893 /* Replace all @ but the last one in addr, with underscores.
3894 * RFC 2822 States that msg-id syntax only allows one @.
3896 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3897 *(strchr(addr, '@')) = '_';
3899 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3900 lt->tm_year + 1900, lt->tm_mon + 1,
3901 lt->tm_mday, lt->tm_hour,
3902 lt->tm_min, lt->tm_sec,
3903 (guint) rand(), addr);
3910 quote_cmd_argument()
3912 return a quoted string safely usable in argument of a command.
3914 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3917 gint quote_cmd_argument(gchar * result, guint size,
3927 for(p = path ; * p != '\0' ; p ++) {
3929 if (isalnum((guchar)*p) || (* p == '/')) {
3930 if (remaining > 0) {
3936 result[size - 1] = '\0';
3941 if (remaining >= 2) {
3949 result[size - 1] = '\0';
3954 if (remaining > 0) {
3958 result[size - 1] = '\0';
3972 static void g_node_map_recursive(GNode *node, gpointer data)
3974 GNodeMapData *mapdata = (GNodeMapData *) data;
3976 GNodeMapData newmapdata;
3979 newdata = mapdata->func(node->data, mapdata->data);
3980 if (newdata != NULL) {
3981 newnode = g_node_new(newdata);
3982 g_node_append(mapdata->parent, newnode);
3984 newmapdata.parent = newnode;
3985 newmapdata.func = mapdata->func;
3986 newmapdata.data = mapdata->data;
3988 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3992 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3995 GNodeMapData mapdata;
3997 cm_return_val_if_fail(node != NULL, NULL);
3998 cm_return_val_if_fail(func != NULL, NULL);
4000 root = g_node_new(func(node->data, data));
4002 mapdata.parent = root;
4003 mapdata.func = func;
4004 mapdata.data = data;
4006 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4011 #define HEX_TO_INT(val, hex) \
4015 if ('0' <= c && c <= '9') { \
4017 } else if ('a' <= c && c <= 'f') { \
4018 val = c - 'a' + 10; \
4019 } else if ('A' <= c && c <= 'F') { \
4020 val = c - 'A' + 10; \
4026 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4033 if (hi == -1 || lo == -1)
4036 *out = (hi << 4) + lo;
4040 #define INT_TO_HEX(hex, val) \
4043 hex = '0' + (val); \
4045 hex = 'A' + (val) - 10; \
4048 void get_hex_str(gchar *out, guchar ch)
4052 INT_TO_HEX(hex, ch >> 4);
4054 INT_TO_HEX(hex, ch & 0x0f);
4060 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4062 #define G_PRINT_REF g_print
4066 *\brief Register ref counted pointer. It is based on GBoxed, so should
4067 * work with anything that uses the GType system. The semantics
4068 * are similar to a C++ auto pointer, with the exception that
4069 * C doesn't have automatic closure (calling destructors) when
4070 * exiting a block scope.
4071 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4072 * function directly.
4074 *\return GType A GType type.
4076 GType g_auto_pointer_register(void)
4078 static GType auto_pointer_type;
4079 if (!auto_pointer_type)
4081 g_boxed_type_register_static
4082 ("G_TYPE_AUTO_POINTER",
4083 (GBoxedCopyFunc) g_auto_pointer_copy,
4084 (GBoxedFreeFunc) g_auto_pointer_free);
4085 return auto_pointer_type;
4089 *\brief Structure with g_new() allocated pointer guarded by the
4092 typedef struct AutoPointerRef {
4093 void (*free) (gpointer);
4099 *\brief The auto pointer opaque structure that references the
4100 * pointer guard block.
4102 typedef struct AutoPointer {
4103 AutoPointerRef *ref;
4104 gpointer ptr; /*!< access to protected pointer */
4108 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4112 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4113 * ... when assigning, copying and freeing storage elements
4115 * gtk_list_store_new(N_S_COLUMNS,
4116 * G_TYPE_AUTO_POINTER,
4120 * Template *precious_data = g_new0(Template, 1);
4121 * g_pointer protect = g_auto_pointer_new(precious_data);
4123 * gtk_list_store_set(container, &iter,
4127 * ... the gtk_list_store has copied the pointer and
4128 * ... incremented its reference count, we should free
4129 * ... the auto pointer (in C++ a destructor would do
4130 * ... this for us when leaving block scope)
4132 * g_auto_pointer_free(protect);
4134 * ... gtk_list_store_set() now manages the data. When
4135 * ... *explicitly* requesting a pointer from the list
4136 * ... store, don't forget you get a copy that should be
4137 * ... freed with g_auto_pointer_free() eventually.
4141 *\param pointer Pointer to be guarded.
4143 *\return GAuto * Pointer that should be used in containers with
4146 GAuto *g_auto_pointer_new(gpointer p)
4148 AutoPointerRef *ref;
4154 ref = g_new0(AutoPointerRef, 1);
4155 ptr = g_new0(AutoPointer, 1);
4165 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4171 *\brief Allocate an autopointer using the passed \a free function to
4172 * free the guarded pointer
4174 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4181 aptr = g_auto_pointer_new(p);
4182 aptr->ref->free = free_;
4186 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4188 if (auto_ptr == NULL)
4190 return ((AutoPointer *) auto_ptr)->ptr;
4194 *\brief Copies an auto pointer by. It's mostly not necessary
4195 * to call this function directly, unless you copy/assign
4196 * the guarded pointer.
4198 *\param auto_ptr Auto pointer returned by previous call to
4199 * g_auto_pointer_new_XXX()
4201 *\return gpointer An auto pointer
4203 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4206 AutoPointerRef *ref;
4209 if (auto_ptr == NULL)
4214 newp = g_new0(AutoPointer, 1);
4217 newp->ptr = ref->pointer;
4221 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4227 *\brief Free an auto pointer
4229 void g_auto_pointer_free(GAuto *auto_ptr)
4232 AutoPointerRef *ref;
4234 if (auto_ptr == NULL)
4240 if (--(ref->cnt) == 0) {
4242 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4244 ref->free(ref->pointer);
4249 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4254 void replace_returns(gchar *str)
4259 while (strstr(str, "\n")) {
4260 *strstr(str, "\n") = ' ';
4262 while (strstr(str, "\r")) {
4263 *strstr(str, "\r") = ' ';
4267 /* get_uri_part() - retrieves a URI starting from scanpos.
4268 Returns TRUE if succesful */
4269 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4270 const gchar **bp, const gchar **ep, gboolean hdr)
4273 gint parenthese_cnt = 0;
4275 cm_return_val_if_fail(start != NULL, FALSE);
4276 cm_return_val_if_fail(scanpos != NULL, FALSE);
4277 cm_return_val_if_fail(bp != NULL, FALSE);
4278 cm_return_val_if_fail(ep != NULL, FALSE);
4282 /* find end point of URI */
4283 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4284 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4285 !IS_ASCII(*(const guchar *)ep_) ||
4286 strchr("[]{}<>\"", *ep_)) {
4288 } else if (strchr("(", *ep_)) {
4290 } else if (strchr(")", *ep_)) {
4291 if (parenthese_cnt > 0)
4298 /* no punctuation at end of string */
4300 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4301 * should pass some URI type to this function and decide on that whether
4302 * to perform punctuation stripping */
4304 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4306 for (; ep_ - 1 > scanpos + 1 &&
4307 IS_REAL_PUNCT(*(ep_ - 1));
4311 #undef IS_REAL_PUNCT
4318 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4320 while (bp && *bp && g_ascii_isspace(*bp))
4322 return g_strndup(bp, ep - bp);
4325 /* valid mail address characters */
4326 #define IS_RFC822_CHAR(ch) \
4330 !g_ascii_isspace(ch) && \
4331 !strchr("(),;<>\"", (ch)))
4333 /* alphabet and number within 7bit ASCII */
4334 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4335 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4337 static GHashTable *create_domain_tab(void)
4339 static const gchar *toplvl_domains [] = {
4341 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4342 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4343 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4344 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4345 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4346 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4347 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4348 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4349 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4350 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4351 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4352 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4353 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4354 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4355 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4356 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4357 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4358 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4359 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4360 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4361 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4362 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4363 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4364 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4365 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4366 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4369 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4371 cm_return_val_if_fail(htab, NULL);
4372 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4373 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4377 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4379 const gint MAX_LVL_DOM_NAME_LEN = 6;
4380 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4381 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4384 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4387 for (p = buf; p < m && first < last; *p++ = *first++)
4391 return g_hash_table_lookup(tab, buf) != NULL;
4394 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4395 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4396 const gchar **bp, const gchar **ep, gboolean hdr)
4398 /* more complex than the uri part because we need to scan back and forward starting from
4399 * the scan position. */
4400 gboolean result = FALSE;
4401 const gchar *bp_ = NULL;
4402 const gchar *ep_ = NULL;
4403 static GHashTable *dom_tab;
4404 const gchar *last_dot = NULL;
4405 const gchar *prelast_dot = NULL;
4406 const gchar *last_tld_char = NULL;
4408 /* the informative part of the email address (describing the name
4409 * of the email address owner) may contain quoted parts. the
4410 * closure stack stores the last encountered quotes. */
4411 gchar closure_stack[128];
4412 gchar *ptr = closure_stack;
4414 cm_return_val_if_fail(start != NULL, FALSE);
4415 cm_return_val_if_fail(scanpos != NULL, FALSE);
4416 cm_return_val_if_fail(bp != NULL, FALSE);
4417 cm_return_val_if_fail(ep != NULL, FALSE);
4420 const gchar *start_quote = NULL;
4421 const gchar *end_quote = NULL;
4423 /* go to the real start */
4424 if (start[0] == ',')
4426 if (start[0] == ';')
4428 while (start[0] == '\n' || start[0] == '\r')
4430 while (start[0] == ' ' || start[0] == '\t')
4435 /* check if there are quotes (to skip , in them) */
4436 if (*start == '"') {
4437 start_quote = start;
4439 end_quote = strstr(start, "\"");
4445 /* skip anything between quotes */
4446 if (start_quote && end_quote) {
4451 /* find end (either , or ; or end of line) */
4452 if (strstr(start, ",") && strstr(start, ";"))
4453 *ep = strstr(start,",") < strstr(start, ";")
4454 ? strstr(start, ",") : strstr(start, ";");
4455 else if (strstr(start, ","))
4456 *ep = strstr(start, ",");
4457 else if (strstr(start, ";"))
4458 *ep = strstr(start, ";");
4460 *ep = start+strlen(start);
4462 /* go back to real start */
4463 if (start_quote && end_quote) {
4464 start = start_quote;
4467 /* check there's still an @ in that, or search
4468 * further if possible */
4469 if (strstr(start, "@") && strstr(start, "@") < *ep)
4471 else if (*ep < start+strlen(start)) {
4474 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4482 dom_tab = create_domain_tab();
4483 cm_return_val_if_fail(dom_tab, FALSE);
4485 /* scan start of address */
4486 for (bp_ = scanpos - 1;
4487 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4490 /* TODO: should start with an alnum? */
4492 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4495 if (bp_ != scanpos) {
4496 /* scan end of address */
4497 for (ep_ = scanpos + 1;
4498 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4500 prelast_dot = last_dot;
4502 if (*(last_dot + 1) == '.') {
4503 if (prelast_dot == NULL)
4505 last_dot = prelast_dot;
4510 /* TODO: really should terminate with an alnum? */
4511 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4516 if (last_dot == NULL)
4518 if (last_dot >= ep_)
4519 last_dot = prelast_dot;
4520 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4524 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4525 if (*last_tld_char == '?')
4528 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4535 if (!result) return FALSE;
4537 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4538 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4539 && IS_RFC822_CHAR(*(ep_ + 3))) {
4540 /* this informative part with an @ in it is
4541 * followed by the email address */
4544 /* go to matching '>' (or next non-rfc822 char, like \n) */
4545 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4548 /* include the bracket */
4549 if (*ep_ == '>') ep_++;
4551 /* include the leading quote */
4559 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4560 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4563 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4564 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4567 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4568 #define IN_STACK() (ptr > closure_stack)
4569 /* has underrun check */
4570 #define POP_STACK() if(IN_STACK()) --ptr
4571 /* has overrun check */
4572 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4573 /* has underrun check */
4574 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4578 /* scan for the informative part. */
4579 for (bp_ -= 2; bp_ >= start; bp_--) {
4580 /* if closure on the stack keep scanning */
4581 if (PEEK_STACK() == *bp_) {
4585 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4590 /* if nothing in the closure stack, do the special conditions
4591 * the following if..else expression simply checks whether
4592 * a token is acceptable. if not acceptable, the clause
4593 * should terminate the loop with a 'break' */
4594 if (!PEEK_STACK()) {
4596 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4597 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4598 /* hyphens are allowed, but only in
4600 } else if (strchr(" \"'", *bp_)) {
4601 /* but anything not being a punctiation
4604 break; /* anything else is rejected */
4611 /* scan forward (should start with an alnum) */
4612 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4628 #undef IS_ASCII_ALNUM
4629 #undef IS_RFC822_CHAR
4631 gchar *make_email_string(const gchar *bp, const gchar *ep)
4633 /* returns a mailto: URI; mailto: is also used to detect the
4634 * uri type later on in the button_pressed signal handler */
4638 tmp = g_strndup(bp, ep - bp);
4639 result = g_strconcat("mailto:", tmp, NULL);
4645 gchar *make_http_string(const gchar *bp, const gchar *ep)
4647 /* returns an http: URI; */
4651 while (bp && *bp && g_ascii_isspace(*bp))
4653 tmp = g_strndup(bp, ep - bp);
4654 result = g_strconcat("http://", tmp, NULL);
4660 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4662 FILE *fp = g_fopen(path, "rb");
4663 gchar buf[BUFFSIZE];
4664 gchar *result = NULL;
4667 while (fgets(buf, sizeof (buf), fp) != NULL) {
4668 gchar **parts = g_strsplit(buf, ";", 3);
4669 gchar *trimmed = parts[0];
4670 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4672 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4673 trimmed[strlen(trimmed)-1] = '\0';
4675 if (!strcmp(trimmed, type)) {
4676 gboolean needsterminal = FALSE;
4677 if (parts[2] && strstr(parts[2], "needsterminal")) {
4678 needsterminal = TRUE;
4680 if (parts[2] && strstr(parts[2], "test=")) {
4681 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4682 gchar *testcmd = orig_testcmd;
4683 if (strstr(testcmd,";"))
4684 *(strstr(testcmd,";")) = '\0';
4685 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4687 while (testcmd[strlen(testcmd)-1] == '\n')
4688 testcmd[strlen(testcmd)-1] = '\0';
4689 while (testcmd[strlen(testcmd)-1] == '\r')
4690 testcmd[strlen(testcmd)-1] = '\0';
4691 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4692 testcmd[strlen(testcmd)-1] = '\0';
4694 if (strstr(testcmd, "%s")) {
4695 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4696 gint res = system(tmp);
4698 g_free(orig_testcmd);
4705 gint res = system(testcmd);
4706 g_free(orig_testcmd);
4716 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4718 while (trimmed[strlen(trimmed)-1] == '\n')
4719 trimmed[strlen(trimmed)-1] = '\0';
4720 while (trimmed[strlen(trimmed)-1] == '\r')
4721 trimmed[strlen(trimmed)-1] = '\0';
4722 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4723 trimmed[strlen(trimmed)-1] = '\0';
4724 result = g_strdup(trimmed);
4727 /* if there are no single quotes around %s, add them.
4728 * '.*%s.*' is ok, as in display 'png:%s'
4730 if (strstr(result, "%s")
4731 && !(strstr(result, "'") < strstr(result,"%s") &&
4732 strstr(strstr(result,"%s"), "'"))) {
4733 gchar *start = g_strdup(result);
4734 gchar *end = g_strdup(strstr(result, "%s")+2);
4736 *strstr(start, "%s") = '\0';
4737 tmp = g_strconcat(start,"'%s'",end, NULL);
4743 if (needsterminal) {
4744 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4755 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4757 gchar *result = NULL;
4761 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4762 result = mailcap_get_command_in_file(path, type, file_to_open);
4766 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4770 void mailcap_update_default(const gchar *type, const gchar *command)
4772 gchar *path = NULL, *outpath = NULL;
4773 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4774 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4775 FILE *fp = g_fopen(path, "rb");
4776 FILE *outfp = g_fopen(outpath, "wb");
4777 gchar buf[BUFFSIZE];
4778 gboolean err = FALSE;
4786 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4787 gchar **parts = g_strsplit(buf, ";", 3);
4788 gchar *trimmed = parts[0];
4789 while (trimmed[0] == ' ')
4791 while (trimmed[strlen(trimmed)-1] == ' ')
4792 trimmed[strlen(trimmed)-1] = '\0';
4794 if (!strcmp(trimmed, type)) {
4799 if(fputs(buf, outfp) == EOF) {
4806 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4812 if (fclose(outfp) == EOF)
4816 g_rename(outpath, path);
4822 gint copy_dir(const gchar *src, const gchar *dst)
4827 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4828 g_warning("failed to open directory: %s\n", src);
4832 if (make_dir(dst) < 0)
4835 while ((name = g_dir_read_name(dir)) != NULL) {
4836 gchar *old_file, *new_file;
4837 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4838 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4839 debug_print("copying: %s -> %s\n", old_file, new_file);
4840 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4841 gint r = copy_file(old_file, new_file, TRUE);
4848 /* Windows has no symlinks. Or well, Vista seems to
4849 have something like this but the semantics might be
4850 different. Thus we don't use it under Windows. */
4851 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4854 gchar *target = g_file_read_link(old_file, &error);
4856 r = symlink(target, new_file);
4863 #endif /*G_OS_WIN32*/
4864 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4865 gint r = copy_dir(old_file, new_file);
4876 /* crude test to see if a file is an email. */
4877 gboolean file_is_email (const gchar *filename)
4883 if (filename == NULL)
4885 if ((fp = g_fopen(filename, "rb")) == NULL)
4887 while (i < 60 && score < 3
4888 && fgets(buffer, sizeof (buffer), fp) > 0) {
4889 if (!strncmp(buffer, "From:", strlen("From:")))
4891 else if (!strncmp(buffer, "Date:", strlen("Date:")))
4893 else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
4895 else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4900 return (score >= 3);
4903 gboolean sc_g_list_bigger(GList *list, gint max)
4907 while (cur && i <= max+1) {
4914 gboolean sc_g_slist_bigger(GSList *list, gint max)
4918 while (cur && i <= max+1) {
4925 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4926 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4927 NULL, NULL, NULL, NULL, NULL, NULL};
4928 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4929 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4930 NULL, NULL, NULL, NULL, NULL, NULL};
4932 gint daynames_len[] = {0,0,0,0,0,0,0};
4933 gint monthnames_len[] = {0,0,0,0,0,0,
4935 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4936 gint s_monthnames_len[] = {0,0,0,0,0,0,
4938 const gchar *s_am_up = NULL;
4939 const gchar *s_pm_up = NULL;
4940 const gchar *s_am_low = NULL;
4941 const gchar *s_pm_low = NULL;
4943 gint s_am_up_len = 0;
4944 gint s_pm_up_len = 0;
4945 gint s_am_low_len = 0;
4946 gint s_pm_low_len = 0;
4948 const gchar *def_loc_format = NULL;
4949 const gchar *date_loc_format = NULL;
4950 const gchar *time_loc_format = NULL;
4951 const gchar *time_am_pm = NULL;
4953 static gboolean time_names_init_done = FALSE;
4955 static void init_time_names(void)
4959 daynames[0] = C_("Complete day name for use by strftime", "Sunday");
4960 daynames[1] = C_("Complete day name for use by strftime", "Monday");
4961 daynames[2] = C_("Complete day name for use by strftime", "Tuesday");
4962 daynames[3] = C_("Complete day name for use by strftime", "Wednesday");
4963 daynames[4] = C_("Complete day name for use by strftime", "Thursday");
4964 daynames[5] = C_("Complete day name for use by strftime", "Friday");
4965 daynames[6] = C_("Complete day name for use by strftime", "Saturday");
4967 monthnames[0] = C_("Complete month name for use by strftime", "January");
4968 monthnames[1] = C_("Complete month name for use by strftime", "February");
4969 monthnames[2] = C_("Complete month name for use by strftime", "March");
4970 monthnames[3] = C_("Complete month name for use by strftime", "April");
4971 monthnames[4] = C_("Complete month name for use by strftime", "May");
4972 monthnames[5] = C_("Complete month name for use by strftime", "June");
4973 monthnames[6] = C_("Complete month name for use by strftime", "July");
4974 monthnames[7] = C_("Complete month name for use by strftime", "August");
4975 monthnames[8] = C_("Complete month name for use by strftime", "September");
4976 monthnames[9] = C_("Complete month name for use by strftime", "October");
4977 monthnames[10] = C_("Complete month name for use by strftime", "November");
4978 monthnames[11] = C_("Complete month name for use by strftime", "December");
4980 s_daynames[0] = C_("Abbr. day name for use by strftime", "Sun");
4981 s_daynames[1] = C_("Abbr. day name for use by strftime", "Mon");
4982 s_daynames[2] = C_("Abbr. day name for use by strftime", "Tue");
4983 s_daynames[3] = C_("Abbr. day name for use by strftime", "Wed");
4984 s_daynames[4] = C_("Abbr. day name for use by strftime", "Thu");
4985 s_daynames[5] = C_("Abbr. day name for use by strftime", "Fri");
4986 s_daynames[6] = C_("Abbr. day name for use by strftime", "Sat");
4988 s_monthnames[0] = C_("Abbr. month name for use by strftime", "Jan");
4989 s_monthnames[1] = C_("Abbr. month name for use by strftime", "Feb");
4990 s_monthnames[2] = C_("Abbr. month name for use by strftime", "Mar");
4991 s_monthnames[3] = C_("Abbr. month name for use by strftime", "Apr");
4992 s_monthnames[4] = C_("Abbr. month name for use by strftime", "May");
4993 s_monthnames[5] = C_("Abbr. month name for use by strftime", "Jun");
4994 s_monthnames[6] = C_("Abbr. month name for use by strftime", "Jul");
4995 s_monthnames[7] = C_("Abbr. month name for use by strftime", "Aug");
4996 s_monthnames[8] = C_("Abbr. month name for use by strftime", "Sep");
4997 s_monthnames[9] = C_("Abbr. month name for use by strftime", "Oct");
4998 s_monthnames[10] = C_("Abbr. month name for use by strftime", "Nov");
4999 s_monthnames[11] = C_("Abbr. month name for use by strftime", "Dec");
5001 for (i = 0; i < 7; i++) {
5002 daynames_len[i] = strlen(daynames[i]);
5003 s_daynames_len[i] = strlen(s_daynames[i]);
5005 for (i = 0; i < 12; i++) {
5006 monthnames_len[i] = strlen(monthnames[i]);
5007 s_monthnames_len[i] = strlen(s_monthnames[i]);
5010 s_am_up = C_("For use by strftime (morning)", "AM");
5011 s_pm_up = C_("For use by strftime (afternoon)", "PM");
5012 s_am_low = C_("For use by strftime (morning, lowercase)", "am");
5013 s_pm_low = C_("For use by strftime (afternoon, lowercase)", "pm");
5015 s_am_up_len = strlen(s_am_up);
5016 s_pm_up_len = strlen(s_pm_up);
5017 s_am_low_len = strlen(s_am_low);
5018 s_pm_low_len = strlen(s_pm_low);
5020 def_loc_format = C_("For use by strftime (default date+time format)", "%a %b %e %H:%M:%S %Y");
5021 date_loc_format = C_("For use by strftime (default date format)", "%m/%d/%y");
5022 time_loc_format = C_("For use by strftime (default time format)", "%H:%M:%S");
5024 time_am_pm = C_("For use by strftime (default 12-hour time format)", "%I:%M:%S %p");
5026 time_names_init_done = TRUE;
5029 #define CHECK_SIZE() { \
5030 total_done += len; \
5031 if (total_done >= buflen) { \
5032 buf[buflen-1] = '\0'; \
5037 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5039 gchar *curpos = buf;
5040 gint total_done = 0;
5041 gchar subbuf[64], subfmt[64];
5042 static time_t last_tzset = (time_t)0;
5044 if (!time_names_init_done)
5047 if (format == NULL || lt == NULL)
5050 if (last_tzset != time(NULL)) {
5052 last_tzset = time(NULL);
5055 if (*format == '%') {
5056 gint len = 0, tmp = 0;
5060 len = 1; CHECK_SIZE();
5064 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5065 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5068 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5069 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5073 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5074 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5077 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5078 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5081 fast_strftime(subbuf, 64, def_loc_format, lt);
5082 len = strlen(subbuf); CHECK_SIZE();
5083 strncpy2(curpos, subbuf, buflen - total_done);
5086 total_done += 2; CHECK_SIZE();
5087 tmp = (lt->tm_year + 1900)/100;
5088 *curpos++ = '0'+(tmp / 10);
5089 *curpos++ = '0'+(tmp % 10);
5092 total_done += 2; CHECK_SIZE();
5093 *curpos++ = '0'+(lt->tm_mday / 10);
5094 *curpos++ = '0'+(lt->tm_mday % 10);
5097 total_done += 8; CHECK_SIZE();
5098 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5099 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5101 *curpos++ = '0'+(lt->tm_mday / 10);
5102 *curpos++ = '0'+(lt->tm_mday % 10);
5104 tmp = lt->tm_year%100;
5105 *curpos++ = '0'+(tmp / 10);
5106 *curpos++ = '0'+(tmp % 10);
5109 len = 2; CHECK_SIZE();
5110 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5113 len = 10; CHECK_SIZE();
5114 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5115 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5118 total_done += 2; CHECK_SIZE();
5119 *curpos++ = '0'+(lt->tm_hour / 10);
5120 *curpos++ = '0'+(lt->tm_hour % 10);
5123 total_done += 2; CHECK_SIZE();
5129 *curpos++ = '0'+(tmp / 10);
5130 *curpos++ = '0'+(tmp % 10);
5133 len = 3; CHECK_SIZE();
5134 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5137 len = 2; CHECK_SIZE();
5138 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5141 len = 2; CHECK_SIZE();
5147 snprintf(curpos, buflen - total_done, "%2d", tmp);
5150 total_done += 2; CHECK_SIZE();
5151 tmp = lt->tm_mon + 1;
5152 *curpos++ = '0'+(tmp / 10);
5153 *curpos++ = '0'+(tmp % 10);
5156 total_done += 2; CHECK_SIZE();
5157 *curpos++ = '0'+(lt->tm_min / 10);
5158 *curpos++ = '0'+(lt->tm_min % 10);
5161 len = 1; CHECK_SIZE();
5165 if (lt->tm_hour >= 12) {
5166 len = s_pm_up_len; CHECK_SIZE();
5167 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5169 len = s_am_up_len; CHECK_SIZE();
5170 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5174 if (lt->tm_hour >= 12) {
5175 len = s_pm_low_len; CHECK_SIZE();
5176 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5178 len = s_am_low_len; CHECK_SIZE();
5179 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5183 fast_strftime(subbuf, 64, time_am_pm, lt);
5184 len = strlen(subbuf); CHECK_SIZE();
5185 strncpy2(curpos, subbuf, buflen - total_done);
5188 total_done += 5; CHECK_SIZE();
5189 *curpos++ = '0'+(lt->tm_hour / 10);
5190 *curpos++ = '0'+(lt->tm_hour % 10);
5192 *curpos++ = '0'+(lt->tm_min / 10);
5193 *curpos++ = '0'+(lt->tm_min % 10);
5196 snprintf(subbuf, 64, "%ld", mktime(lt));
5197 len = strlen(subbuf); CHECK_SIZE();
5198 strncpy2(curpos, subbuf, buflen - total_done);
5201 total_done += 2; CHECK_SIZE();
5202 *curpos++ = '0'+(lt->tm_sec / 10);
5203 *curpos++ = '0'+(lt->tm_sec % 10);
5206 len = 1; CHECK_SIZE();
5210 total_done += 8; CHECK_SIZE();
5211 *curpos++ = '0'+(lt->tm_hour / 10);
5212 *curpos++ = '0'+(lt->tm_hour % 10);
5214 *curpos++ = '0'+(lt->tm_min / 10);
5215 *curpos++ = '0'+(lt->tm_min % 10);
5217 *curpos++ = '0'+(lt->tm_sec / 10);
5218 *curpos++ = '0'+(lt->tm_sec % 10);
5221 len = 1; CHECK_SIZE();
5222 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5225 len = 1; CHECK_SIZE();
5226 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5229 fast_strftime(subbuf, 64, date_loc_format, lt);
5230 len = strlen(subbuf); CHECK_SIZE();
5231 strncpy2(curpos, subbuf, buflen - total_done);
5234 fast_strftime(subbuf, 64, time_loc_format, lt);
5235 len = strlen(subbuf); CHECK_SIZE();
5236 strncpy2(curpos, subbuf, buflen - total_done);
5239 total_done += 2; CHECK_SIZE();
5240 tmp = lt->tm_year%100;
5241 *curpos++ = '0'+(tmp / 10);
5242 *curpos++ = '0'+(tmp % 10);
5245 len = 4; CHECK_SIZE();
5246 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5256 /* let these complicated ones be done with the libc */
5257 snprintf(subfmt, 64, "%%%c", *format);
5258 strftime(subbuf, 64, subfmt, lt);
5259 len = strlen(subbuf); CHECK_SIZE();
5260 strncpy2(curpos, subbuf, buflen - total_done);
5264 /* let these complicated modifiers be done with the libc */
5265 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5266 strftime(subbuf, 64, subfmt, lt);
5267 len = strlen(subbuf); CHECK_SIZE();
5268 strncpy2(curpos, subbuf, buflen - total_done);
5272 if (format && *format)
5273 g_warning("format error (%c)", *format);
5280 int len = 1; CHECK_SIZE();
5281 *curpos++ = *format++;
5288 gboolean prefs_common_get_use_shred(void);
5292 #define WEXITSTATUS(x) (x)
5295 int claws_unlink(const gchar *filename)
5298 static int found_shred = -1;
5299 static const gchar *args[4];
5301 if (filename == NULL)
5304 if (prefs_common_get_use_shred()) {
5305 if (found_shred == -1) {
5307 args[0] = g_find_program_in_path("shred");
5308 debug_print("found shred: %s\n", args[0]);
5309 found_shred = (args[0] != NULL) ? 1:0;
5313 if (found_shred == 1) {
5314 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5315 if (s.st_nlink == 1) {
5318 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5319 NULL, NULL, NULL, NULL, &status, NULL);
5320 debug_print("%s %s exited with status %d\n",
5321 args[0], filename, WEXITSTATUS(status));
5322 if (truncate(filename, 0) < 0)
5323 g_warning("couln't truncate");
5328 return g_unlink(filename);
5331 GMutex *cm_mutex_new(void) {
5332 #if GLIB_CHECK_VERSION(2,32,0)
5333 GMutex *m = g_new0(GMutex, 1);
5337 return g_mutex_new();
5341 void cm_mutex_free(GMutex *mutex) {
5342 #if GLIB_CHECK_VERSION(2,32,0)
5343 g_mutex_clear(mutex);
5346 g_mutex_free(mutex);