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 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
272 void hash_free_strings(GHashTable *table)
274 g_hash_table_foreach(table, hash_free_strings_func, NULL);
277 gint str_case_equal(gconstpointer v, gconstpointer v2)
279 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
282 guint str_case_hash(gconstpointer key)
284 const gchar *p = key;
288 h = g_ascii_tolower(h);
289 for (p += 1; *p != '\0'; p++)
290 h = (h << 5) - h + g_ascii_tolower(*p);
296 void ptr_array_free_strings(GPtrArray *array)
301 cm_return_if_fail(array != NULL);
303 for (i = 0; i < array->len; i++) {
304 str = g_ptr_array_index(array, i);
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 #define divide(num,divisor,i,d) \
339 i = num >> divisor; \
340 d = num & ((1<<divisor)-1); \
341 d = (d*100) >> divisor; \
346 * \brief Convert a given size in bytes in a human-readable string
348 * \param size The size expressed in bytes to convert in string
349 * \return The string that respresents the size in an human-readable way
351 gchar *to_human_readable(goffset size)
353 static gchar str[14];
354 static gchar *b_format = NULL, *kb_format = NULL,
355 *mb_format = NULL, *gb_format = NULL;
356 register int t = 0, r = 0;
357 if (b_format == NULL) {
359 kb_format = _("%d.%02dKB");
360 mb_format = _("%d.%02dMB");
361 gb_format = _("%.2fGB");
364 if (size < (goffset)1024) {
365 g_snprintf(str, sizeof(str), b_format, (gint)size);
367 } else if (size >> 10 < (goffset)1024) {
368 divide(size, 10, t, r);
369 g_snprintf(str, sizeof(str), kb_format, t, r);
371 } else if (size >> 20 < (goffset)1024) {
372 divide(size, 20, t, r);
373 g_snprintf(str, sizeof(str), mb_format, t, r);
376 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
381 /* strcmp with NULL-checking */
382 gint strcmp2(const gchar *s1, const gchar *s2)
384 if (s1 == NULL || s2 == NULL)
387 return strcmp(s1, s2);
389 /* strstr with NULL-checking */
390 gchar *strstr2(const gchar *s1, const gchar *s2)
392 if (s1 == NULL || s2 == NULL)
395 return strstr(s1, s2);
398 gint path_cmp(const gchar *s1, const gchar *s2)
403 gchar *s1buf, *s2buf;
406 if (s1 == NULL || s2 == NULL) return -1;
407 if (*s1 == '\0' || *s2 == '\0') return -1;
410 s1buf = g_strdup (s1);
411 s2buf = g_strdup (s2);
412 subst_char (s1buf, '/', G_DIR_SEPARATOR);
413 subst_char (s2buf, '/', G_DIR_SEPARATOR);
416 #endif /* !G_OS_WIN32 */
421 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
422 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
424 rc = strncmp(s1, s2, MAX(len1, len2));
428 #endif /* !G_OS_WIN32 */
432 /* remove trailing return code */
433 gchar *strretchomp(gchar *str)
437 if (!*str) return str;
439 for (s = str + strlen(str) - 1;
440 s >= str && (*s == '\n' || *s == '\r');
447 /* remove trailing character */
448 gchar *strtailchomp(gchar *str, gchar tail_char)
452 if (!*str) return str;
453 if (tail_char == '\0') return str;
455 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
461 /* remove CR (carriage return) */
462 gchar *strcrchomp(gchar *str)
466 if (!*str) return str;
468 s = str + strlen(str) - 1;
469 if (*s == '\n' && s > str && *(s - 1) == '\r') {
477 gint file_strip_crs(const gchar *file)
479 FILE *fp = NULL, *outfp = NULL;
481 gchar *out = get_tmp_file();
485 fp = g_fopen(file, "rb");
489 outfp = g_fopen(out, "wb");
495 while (fgets(buf, sizeof (buf), fp) != NULL) {
497 if (fputs(buf, outfp) == EOF) {
505 if (fclose(outfp) == EOF) {
509 if (move_file(out, file, TRUE) < 0)
521 /* Similar to `strstr' but this function ignores the case of both strings. */
522 gchar *strcasestr(const gchar *haystack, const gchar *needle)
524 register size_t haystack_len, needle_len;
526 haystack_len = strlen(haystack);
527 needle_len = strlen(needle);
529 if (haystack_len < needle_len || needle_len == 0)
532 while (haystack_len >= needle_len) {
533 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
534 return (gchar *)haystack;
544 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
545 gconstpointer needle, size_t needlelen)
547 const gchar *haystack_ = (const gchar *)haystack;
548 const gchar *needle_ = (const gchar *)needle;
549 const gchar *haystack_cur = (const gchar *)haystack;
550 size_t haystack_left = haystacklen;
553 return memchr(haystack_, *needle_, haystacklen);
555 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
557 if (haystacklen - (haystack_cur - haystack_) < needlelen)
559 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
560 return (gpointer)haystack_cur;
563 haystack_left = haystacklen - (haystack_cur - haystack_);
570 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
571 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
573 register const gchar *s = src;
574 register gchar *d = dest;
584 /* Examine if next block is non-ASCII string */
585 gboolean is_next_nonascii(const gchar *s)
589 /* skip head space */
590 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
592 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
593 if (*(guchar *)p > 127 || *(guchar *)p < 32)
600 gint get_next_word_len(const gchar *s)
604 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
610 static void trim_subject_for_compare(gchar *str)
614 eliminate_parenthesis(str, '[', ']');
615 eliminate_parenthesis(str, '(', ')');
618 srcp = str + subject_get_prefix_length(str);
620 memmove(str, srcp, strlen(srcp) + 1);
623 static void trim_subject_for_sort(gchar *str)
629 srcp = str + subject_get_prefix_length(str);
631 memmove(str, srcp, strlen(srcp) + 1);
634 /* compare subjects */
635 gint subject_compare(const gchar *s1, const gchar *s2)
639 if (!s1 || !s2) return -1;
640 if (!*s1 || !*s2) return -1;
642 Xstrdup_a(str1, s1, return -1);
643 Xstrdup_a(str2, s2, return -1);
645 trim_subject_for_compare(str1);
646 trim_subject_for_compare(str2);
648 if (!*str1 || !*str2) return -1;
650 return strcmp(str1, str2);
653 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
657 if (!s1 || !s2) return -1;
659 Xstrdup_a(str1, s1, return -1);
660 Xstrdup_a(str2, s2, return -1);
662 trim_subject_for_sort(str1);
663 trim_subject_for_sort(str2);
665 return g_utf8_collate(str1, str2);
668 void trim_subject(gchar *str)
670 register gchar *srcp;
676 srcp = str + subject_get_prefix_length(str);
681 } else if (*srcp == '(') {
693 else if (*srcp == cl)
700 while (g_ascii_isspace(*srcp)) srcp++;
701 memmove(str, srcp, strlen(srcp) + 1);
704 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
706 register gchar *srcp, *destp;
711 while ((destp = strchr(destp, op))) {
717 else if (*srcp == cl)
723 while (g_ascii_isspace(*srcp)) srcp++;
724 memmove(destp, srcp, strlen(srcp) + 1);
728 void extract_parenthesis(gchar *str, gchar op, gchar cl)
730 register gchar *srcp, *destp;
735 while ((srcp = strchr(destp, op))) {
738 memmove(destp, srcp + 1, strlen(srcp));
743 else if (*destp == cl)
755 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
758 register gchar *srcp, *destp;
760 gboolean in_quote = FALSE;
764 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
767 memmove(destp, srcp + 1, strlen(srcp));
770 if (*destp == op && !in_quote)
772 else if (*destp == cl && !in_quote)
774 else if (*destp == quote_chr)
786 void extract_quote(gchar *str, gchar quote_chr)
790 if ((str = strchr(str, quote_chr))) {
792 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
793 memmove(p - 1, p, strlen(p) + 1);
798 memmove(str, str + 1, p - str);
803 void eliminate_address_comment(gchar *str)
805 register gchar *srcp, *destp;
810 while ((destp = strchr(destp, '"'))) {
811 if ((srcp = strchr(destp + 1, '"'))) {
816 while (g_ascii_isspace(*srcp)) srcp++;
817 memmove(destp, srcp, strlen(srcp) + 1);
827 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
833 else if (*srcp == ')')
839 while (g_ascii_isspace(*srcp)) srcp++;
840 memmove(destp, srcp, strlen(srcp) + 1);
844 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
846 gboolean in_quote = FALSE;
849 if (*str == c && !in_quote)
851 if (*str == quote_chr)
859 void extract_address(gchar *str)
861 eliminate_address_comment(str);
862 if (strchr_with_skip_quote(str, '"', '<'))
863 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
867 void extract_list_id_str(gchar *str)
869 if (strchr_with_skip_quote(str, '"', '<'))
870 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
874 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
879 if (!str) return addr_list;
881 Xstrdup_a(work, str, return addr_list);
884 eliminate_address_comment(work);
887 while (workp && *workp) {
890 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
896 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
897 extract_parenthesis_with_skip_quote
898 (workp, '"', '<', '>');
902 addr_list = g_slist_append(addr_list, g_strdup(workp));
910 GSList *address_list_append(GSList *addr_list, const gchar *str)
912 return address_list_append_real(addr_list, str, TRUE);
915 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
917 return address_list_append_real(addr_list, str, FALSE);
920 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
924 if (!str) return msgid_list;
927 while (strp && *strp) {
928 const gchar *start, *end;
931 if ((start = strchr(strp, '<')) != NULL) {
932 end = strchr(start + 1, '>');
937 msgid = g_strndup(start + 1, end - start - 1);
940 msgid_list = g_slist_prepend(msgid_list, msgid);
950 GSList *references_list_append(GSList *msgid_list, const gchar *str)
954 list = references_list_prepend(NULL, str);
955 list = g_slist_reverse(list);
956 msgid_list = g_slist_concat(msgid_list, list);
961 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
966 if (!str) return group_list;
968 Xstrdup_a(work, str, return group_list);
972 while (workp && *workp) {
975 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
983 group_list = g_slist_append(group_list,
992 GList *add_history(GList *list, const gchar *str)
997 cm_return_val_if_fail(str != NULL, list);
999 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1002 list = g_list_remove(list, old->data);
1004 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1007 last = g_list_last(list);
1009 oldstr = last->data;
1010 list = g_list_remove(list, last->data);
1015 list = g_list_prepend(list, g_strdup(str));
1020 void remove_return(gchar *str)
1022 register gchar *p = str;
1025 if (*p == '\n' || *p == '\r')
1026 memmove(p, p + 1, strlen(p));
1032 void remove_space(gchar *str)
1034 register gchar *p = str;
1039 while (g_ascii_isspace(*(p + spc)))
1042 memmove(p, p + spc, strlen(p + spc) + 1);
1048 void unfold_line(gchar *str)
1050 register gchar *p = str;
1054 if (*p == '\n' || *p == '\r') {
1057 while (g_ascii_isspace(*(p + spc)))
1060 memmove(p, p + spc, strlen(p + spc) + 1);
1066 void subst_char(gchar *str, gchar orig, gchar subst)
1068 register gchar *p = str;
1077 void subst_chars(gchar *str, gchar *orig, gchar subst)
1079 register gchar *p = str;
1082 if (strchr(orig, *p) != NULL)
1088 void subst_for_filename(gchar *str)
1093 subst_chars(str, "\t\r\n\\/*:", '_');
1095 subst_chars(str, "\t\r\n\\/*", '_');
1099 void subst_for_shellsafe_filename(gchar *str)
1103 subst_for_filename(str);
1104 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1107 gboolean is_ascii_str(const gchar *str)
1109 const guchar *p = (const guchar *)str;
1111 while (*p != '\0') {
1112 if (*p != '\t' && *p != ' ' &&
1113 *p != '\r' && *p != '\n' &&
1114 (*p < 32 || *p >= 127))
1122 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1124 gchar * position = NULL;
1125 gchar * tmp_pos = NULL;
1128 if (quote_chars == NULL)
1131 for (i = 0; i < strlen(quote_chars); i++) {
1132 tmp_pos = strrchr (str, quote_chars[i]);
1134 || (tmp_pos != NULL && position <= tmp_pos) )
1140 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1142 const gchar *first_pos;
1143 const gchar *last_pos;
1144 const gchar *p = str;
1145 gint quote_level = -1;
1147 /* speed up line processing by only searching to the last '>' */
1148 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1149 /* skip a line if it contains a '<' before the initial '>' */
1150 if (memchr(str, '<', first_pos - str) != NULL)
1152 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1156 while (p <= last_pos) {
1157 while (p < last_pos) {
1158 if (g_ascii_isspace(*p))
1164 if (strchr(quote_chars, *p))
1166 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1167 /* any characters are allowed except '-','<' and space */
1168 while (*p != '-' && *p != '<'
1169 && !strchr(quote_chars, *p)
1170 && !g_ascii_isspace(*p)
1173 if (strchr(quote_chars, *p))
1185 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1187 const gchar *p = str, *q;
1188 gint cur_line = 0, len;
1190 while ((q = strchr(p, '\n')) != NULL) {
1192 if (len > max_chars) {
1202 if (len > max_chars) {
1211 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1213 gchar * position = NULL;
1214 gchar * tmp_pos = NULL;
1217 if (quote_chars == NULL)
1220 for (i = 0; i < strlen(quote_chars); i++) {
1221 tmp_pos = strchr (str, quote_chars[i]);
1223 || (tmp_pos != NULL && position >= tmp_pos) )
1229 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1231 register guint haystack_len, needle_len;
1232 gboolean in_squote = FALSE, in_dquote = FALSE;
1234 haystack_len = strlen(haystack);
1235 needle_len = strlen(needle);
1237 if (haystack_len < needle_len || needle_len == 0)
1240 while (haystack_len >= needle_len) {
1241 if (!in_squote && !in_dquote &&
1242 !strncmp(haystack, needle, needle_len))
1243 return (gchar *)haystack;
1245 /* 'foo"bar"' -> foo"bar"
1246 "foo'bar'" -> foo'bar' */
1247 if (*haystack == '\'') {
1250 else if (!in_dquote)
1252 } else if (*haystack == '\"') {
1255 else if (!in_squote)
1266 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1269 GSList *string_list = NULL, *slist;
1270 gchar **str_array, *s, *new_str;
1271 guint i, n = 1, len;
1273 cm_return_val_if_fail(str != NULL, NULL);
1274 cm_return_val_if_fail(delim != NULL, NULL);
1277 max_tokens = G_MAXINT;
1279 s = strstr_with_skip_quote(str, delim);
1281 guint delimiter_len = strlen(delim);
1285 new_str = g_strndup(str, len);
1287 if (new_str[0] == '\'' || new_str[0] == '\"') {
1288 if (new_str[len - 1] == new_str[0]) {
1289 new_str[len - 1] = '\0';
1290 memmove(new_str, new_str + 1, len - 1);
1293 string_list = g_slist_prepend(string_list, new_str);
1295 str = s + delimiter_len;
1296 s = strstr_with_skip_quote(str, delim);
1297 } while (--max_tokens && s);
1301 new_str = g_strdup(str);
1302 if (new_str[0] == '\'' || new_str[0] == '\"') {
1304 if (new_str[len - 1] == new_str[0]) {
1305 new_str[len - 1] = '\0';
1306 memmove(new_str, new_str + 1, len - 1);
1309 string_list = g_slist_prepend(string_list, new_str);
1313 str_array = g_new(gchar*, n);
1317 str_array[i--] = NULL;
1318 for (slist = string_list; slist; slist = slist->next)
1319 str_array[i--] = slist->data;
1321 g_slist_free(string_list);
1326 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1328 gchar *abbrev_group;
1330 const gchar *p = group;
1333 cm_return_val_if_fail(group != NULL, NULL);
1335 last = group + strlen(group);
1336 abbrev_group = ap = g_malloc(strlen(group) + 1);
1341 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1343 while (*p != '.') p++;
1346 return abbrev_group;
1351 return abbrev_group;
1354 gchar *trim_string(const gchar *str, gint len)
1356 const gchar *p = str;
1361 if (!str) return NULL;
1362 if (strlen(str) <= len)
1363 return g_strdup(str);
1364 if (g_utf8_validate(str, -1, NULL) == FALSE)
1365 return g_strdup(str);
1367 while (*p != '\0') {
1368 mb_len = g_utf8_skip[*(guchar *)p];
1371 else if (new_len + mb_len > len)
1378 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1379 return g_strconcat(new_str, "...", NULL);
1382 GList *uri_list_extract_filenames(const gchar *uri_list)
1384 GList *result = NULL;
1386 gchar *escaped_utf8uri;
1392 while (g_ascii_isspace(*p)) p++;
1393 if (!strncmp(p, "file:", 5)) {
1396 while (*q && *q != '\n' && *q != '\r') q++;
1399 gchar *file, *locale_file = NULL;
1401 while (q > p && g_ascii_isspace(*q))
1403 Xalloca(escaped_utf8uri, q - p + 2,
1405 Xalloca(file, q - p + 2,
1408 strncpy(escaped_utf8uri, p, q - p + 1);
1409 escaped_utf8uri[q - p + 1] = '\0';
1410 decode_uri(file, escaped_utf8uri);
1412 * g_filename_from_uri() rejects escaped/locale encoded uri
1413 * string which come from Nautilus.
1416 if (g_utf8_validate(file, -1, NULL))
1418 = conv_codeset_strdup(
1421 conv_get_locale_charset_str());
1423 locale_file = g_strdup(file + 5);
1425 locale_file = g_filename_from_uri(file, NULL, NULL);
1427 result = g_list_append(result, locale_file);
1431 p = strchr(p, '\n');
1438 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1441 static gint axtoi(const gchar *hexstr)
1443 gint hi, lo, result;
1446 if ('0' <= hi && hi <= '9') {
1449 if ('a' <= hi && hi <= 'f') {
1452 if ('A' <= hi && hi <= 'F') {
1457 if ('0' <= lo && lo <= '9') {
1460 if ('a' <= lo && lo <= 'f') {
1463 if ('A' <= lo && lo <= 'F') {
1466 result = lo + (16 * hi);
1470 gboolean is_uri_string(const gchar *str)
1472 while (str && *str && g_ascii_isspace(*str))
1474 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1475 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1476 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1477 g_ascii_strncasecmp(str, "www.", 4) == 0);
1480 gchar *get_uri_path(const gchar *uri)
1482 while (uri && *uri && g_ascii_isspace(*uri))
1484 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1485 return (gchar *)(uri + 7);
1486 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1487 return (gchar *)(uri + 8);
1488 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1489 return (gchar *)(uri + 6);
1491 return (gchar *)uri;
1494 gint get_uri_len(const gchar *str)
1498 if (is_uri_string(str)) {
1499 for (p = str; *p != '\0'; p++) {
1500 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1509 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1510 * plusses, and escape characters are used)
1512 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1514 gchar *dec = decoded_uri;
1515 const gchar *enc = encoded_uri;
1520 if (isxdigit((guchar)enc[0]) &&
1521 isxdigit((guchar)enc[1])) {
1527 if (with_plus && *enc == '+')
1539 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1541 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1544 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1546 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1547 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1551 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1552 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1556 const gchar *forbidden_uris[] = { ".gnupg/",
1562 gint num_attach = 0;
1563 gchar **my_att = NULL;
1565 Xstrdup_a(tmp_mailto, mailto, return -1);
1567 if (!strncmp(tmp_mailto, "mailto:", 7))
1570 p = strchr(tmp_mailto, '?');
1577 *to = decode_uri_gdup(tmp_mailto);
1579 my_att = g_malloc(sizeof(char *));
1583 gchar *field, *value;
1600 if (*value == '\0') continue;
1602 if (from && !g_ascii_strcasecmp(field, "from")) {
1604 *from = decode_uri_gdup(value);
1606 gchar *tmp = decode_uri_gdup(value);
1607 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1611 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1613 *cc = decode_uri_gdup(value);
1615 gchar *tmp = decode_uri_gdup(value);
1616 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1620 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1622 *bcc = decode_uri_gdup(value);
1624 gchar *tmp = decode_uri_gdup(value);
1625 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1629 } else if (subject && !*subject &&
1630 !g_ascii_strcasecmp(field, "subject")) {
1631 *subject = decode_uri_gdup(value);
1632 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1633 *body = decode_uri_gdup(value);
1634 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1635 gchar *tmp = decode_uri_gdup(value);
1636 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1637 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1641 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1643 gchar *tmp = decode_uri_gdup(value);
1644 for (; forbidden_uris[i]; i++) {
1645 if (strstr(tmp, forbidden_uris[i])) {
1646 g_print("Refusing to attach '%s', potential private data leak\n",
1654 /* attach is correct */
1656 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1657 my_att[num_attach-1] = tmp;
1658 my_att[num_attach] = NULL;
1660 } else if (inreplyto && !*inreplyto &&
1661 !g_ascii_strcasecmp(field, "in-reply-to")) {
1662 *inreplyto = decode_uri_gdup(value);
1673 #include <windows.h>
1674 #ifndef CSIDL_APPDATA
1675 #define CSIDL_APPDATA 0x001a
1677 #ifndef CSIDL_LOCAL_APPDATA
1678 #define CSIDL_LOCAL_APPDATA 0x001c
1680 #ifndef CSIDL_FLAG_CREATE
1681 #define CSIDL_FLAG_CREATE 0x8000
1683 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1687 w32_strerror (int w32_errno)
1689 static char strerr[256];
1690 int ec = (int)GetLastError ();
1694 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1695 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1696 strerr, DIM (strerr)-1, NULL);
1700 static __inline__ void *
1701 dlopen (const char * name, int flag)
1703 void * hd = LoadLibrary (name);
1707 static __inline__ void *
1708 dlsym (void * hd, const char * sym)
1712 void * fnc = GetProcAddress (hd, sym);
1721 static __inline__ const char *
1724 return w32_strerror (0);
1728 static __inline__ int
1740 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1742 static int initialized;
1743 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1747 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1753 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1755 handle = dlopen (dllnames[i], RTLD_LAZY);
1758 func = dlsym (handle, "SHGetFolderPathW");
1769 return func (a,b,c,d,e);
1774 /* Returns a static string with the directroy from which the module
1775 has been loaded. Returns an empty string on error. */
1776 static char *w32_get_module_dir(void)
1778 static char *moddir;
1781 char name[MAX_PATH+10];
1784 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1787 p = strrchr (name, '\\');
1793 moddir = g_strdup (name);
1797 #endif /* G_OS_WIN32 */
1799 /* Return a static string with the locale dir. */
1800 const gchar *get_locale_dir(void)
1802 static gchar *loc_dir;
1806 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1807 "\\share\\locale", NULL);
1810 loc_dir = LOCALEDIR;
1816 const gchar *get_home_dir(void)
1819 static char home_dir_utf16[MAX_PATH] = "";
1820 static gchar *home_dir_utf8 = NULL;
1821 if (home_dir_utf16[0] == '\0') {
1822 if (w32_shgetfolderpath
1823 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1824 NULL, 0, home_dir_utf16) < 0)
1825 strcpy (home_dir_utf16, "C:\\Sylpheed");
1826 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1828 return home_dir_utf8;
1830 static const gchar *homeenv = NULL;
1835 if (!homeenv && g_getenv("HOME") != NULL)
1836 homeenv = g_strdup(g_getenv("HOME"));
1838 homeenv = g_get_home_dir();
1844 static gchar *claws_rc_dir = NULL;
1845 static gboolean rc_dir_alt = FALSE;
1846 const gchar *get_rc_dir(void)
1850 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1853 return claws_rc_dir;
1856 void set_rc_dir(const gchar *dir)
1858 if (claws_rc_dir != NULL) {
1859 g_print("Error: rc_dir already set\n");
1862 if (g_path_is_absolute(dir))
1863 claws_rc_dir = g_strdup(dir);
1865 claws_rc_dir = g_strconcat(g_get_current_dir(),
1866 G_DIR_SEPARATOR_S, dir, NULL);
1868 debug_print("set rc_dir to %s\n", claws_rc_dir);
1869 if (!is_dir_exist(claws_rc_dir)) {
1870 if (make_dir_hier(claws_rc_dir) != 0) {
1871 g_print("Error: can't create %s\n",
1878 gboolean rc_dir_is_alt(void) {
1882 const gchar *get_mail_base_dir(void)
1884 return get_home_dir();
1888 const gchar *prefs_common_get_data_root(void);
1889 gchar *last_data_root = NULL;
1892 const gchar *get_news_cache_dir(void)
1894 static gchar *news_cache_dir = NULL;
1896 const gchar *data_root = prefs_common_get_data_root();
1897 if (strcmp2(data_root, last_data_root)) {
1898 g_free(news_cache_dir);
1899 news_cache_dir = NULL;
1902 if (!news_cache_dir)
1904 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1905 NEWS_CACHE_DIR, NULL);
1909 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1910 "Claws", G_DIR_SEPARATOR_S,
1911 g_get_user_name(), G_DIR_SEPARATOR_S,
1912 NEWS_CACHE_DIR, NULL);
1913 g_free(last_data_root);
1914 last_data_root = g_strdup(last_data_root);
1916 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1917 NEWS_CACHE_DIR, NULL);
1918 g_free(last_data_root);
1919 last_data_root = NULL;
1923 return news_cache_dir;
1926 const gchar *get_imap_cache_dir(void)
1928 static gchar *imap_cache_dir = NULL;
1930 const gchar *data_root = prefs_common_get_data_root();
1931 if (strcmp2(data_root, last_data_root)) {
1932 g_free(imap_cache_dir);
1933 imap_cache_dir = NULL;
1937 if (!imap_cache_dir)
1939 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1940 IMAP_CACHE_DIR, NULL);
1944 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1945 "Claws", G_DIR_SEPARATOR_S,
1946 g_get_user_name(), G_DIR_SEPARATOR_S,
1947 IMAP_CACHE_DIR, NULL);
1948 g_free(last_data_root);
1949 last_data_root = g_strdup(last_data_root);
1951 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1952 IMAP_CACHE_DIR, NULL);
1953 g_free(last_data_root);
1954 last_data_root = NULL;
1959 return imap_cache_dir;
1962 const gchar *get_mime_tmp_dir(void)
1964 static gchar *mime_tmp_dir = NULL;
1967 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1968 MIME_TMP_DIR, NULL);
1970 return mime_tmp_dir;
1973 const gchar *get_template_dir(void)
1975 static gchar *template_dir = NULL;
1978 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1979 TEMPLATE_DIR, NULL);
1981 return template_dir;
1985 const gchar *get_cert_file(void)
1987 const gchar *cert_file = NULL;
1989 cert_file = g_strconcat(w32_get_module_dir(),
1990 "\\share\\claws-mail\\",
1991 "ca-certificates.crt",
1997 /* Return the filepath of the claws-mail.desktop file */
1998 const gchar *get_desktop_file(void)
2000 #ifdef DESKTOPFILEPATH
2001 return DESKTOPFILEPATH;
2007 /* Return the default directory for Plugins. */
2008 const gchar *get_plugin_dir(void)
2011 static gchar *plugin_dir = NULL;
2014 plugin_dir = g_strconcat(w32_get_module_dir(),
2015 "\\lib\\claws-mail\\plugins\\",
2019 if (is_dir_exist(PLUGINDIR))
2022 static gchar *plugin_dir = NULL;
2024 plugin_dir = g_strconcat(get_rc_dir(),
2025 G_DIR_SEPARATOR_S, "plugins",
2026 G_DIR_SEPARATOR_S, NULL);
2034 /* Return the default directory for Themes. */
2035 const gchar *get_themes_dir(void)
2037 static gchar *themes_dir = NULL;
2040 themes_dir = g_strconcat(w32_get_module_dir(),
2041 "\\share\\claws-mail\\themes",
2047 const gchar *get_tmp_dir(void)
2049 static gchar *tmp_dir = NULL;
2052 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2058 gchar *get_tmp_file(void)
2061 static guint32 id = 0;
2063 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2064 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2069 const gchar *get_domain_name(void)
2072 static gchar *domain_name = NULL;
2078 if (gethostname(hostname, sizeof(hostname)) != 0) {
2079 perror("gethostname");
2080 domain_name = "unknown";
2082 hostname[sizeof(hostname) - 1] = '\0';
2083 if ((hp = my_gethostbyname(hostname)) == NULL) {
2084 perror("gethostbyname");
2085 domain_name = g_strdup(hostname);
2087 domain_name = g_strdup(hp->h_name);
2090 debug_print("domain name = %s\n", domain_name);
2099 off_t get_file_size(const gchar *file)
2103 if (g_stat(file, &s) < 0) {
2104 FILE_OP_ERROR(file, "stat");
2111 time_t get_file_mtime(const gchar *file)
2115 if (g_stat(file, &s) < 0) {
2116 FILE_OP_ERROR(file, "stat");
2123 off_t get_file_size_as_crlf(const gchar *file)
2127 gchar buf[BUFFSIZE];
2129 if ((fp = g_fopen(file, "rb")) == NULL) {
2130 FILE_OP_ERROR(file, "g_fopen");
2134 while (fgets(buf, sizeof(buf), fp) != NULL) {
2136 size += strlen(buf) + 2;
2140 FILE_OP_ERROR(file, "fgets");
2149 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2156 if (g_stat(file, &s) < 0) {
2157 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2161 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2168 /* Test on whether FILE is a relative file name. This is
2169 * straightforward for Unix but more complex for Windows. */
2170 gboolean is_relative_filename(const gchar *file)
2175 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2176 return FALSE; /* Prefixed with a hostname - this can't
2177 * be a relative name. */
2179 if ( ((*file >= 'a' && *file <= 'z')
2180 || (*file >= 'A' && *file <= 'Z'))
2182 file += 2; /* Skip drive letter. */
2184 return !(*file == '\\' || *file == '/');
2186 return !(*file == G_DIR_SEPARATOR);
2191 gboolean is_dir_exist(const gchar *dir)
2196 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2199 gboolean is_file_entry_exist(const gchar *file)
2204 return g_file_test(file, G_FILE_TEST_EXISTS);
2207 gboolean dirent_is_regular_file(struct dirent *d)
2209 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2210 if (d->d_type == DT_REG)
2212 else if (d->d_type != DT_UNKNOWN)
2216 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2219 gint change_dir(const gchar *dir)
2221 gchar *prevdir = NULL;
2224 prevdir = g_get_current_dir();
2226 if (g_chdir(dir) < 0) {
2227 FILE_OP_ERROR(dir, "chdir");
2228 if (debug_mode) g_free(prevdir);
2230 } else if (debug_mode) {
2233 cwd = g_get_current_dir();
2234 if (strcmp(prevdir, cwd) != 0)
2235 g_print("current dir: %s\n", cwd);
2243 gint make_dir(const gchar *dir)
2245 if (g_mkdir(dir, S_IRWXU) < 0) {
2246 FILE_OP_ERROR(dir, "mkdir");
2249 if (g_chmod(dir, S_IRWXU) < 0)
2250 FILE_OP_ERROR(dir, "chmod");
2255 gint make_dir_hier(const gchar *dir)
2260 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2261 parent_dir = g_strndup(dir, p - dir);
2262 if (*parent_dir != '\0') {
2263 if (!is_dir_exist(parent_dir)) {
2264 if (make_dir(parent_dir) < 0) {
2273 if (!is_dir_exist(dir)) {
2274 if (make_dir(dir) < 0)
2281 gint remove_all_files(const gchar *dir)
2284 const gchar *dir_name;
2287 prev_dir = g_get_current_dir();
2289 if (g_chdir(dir) < 0) {
2290 FILE_OP_ERROR(dir, "chdir");
2295 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2296 g_warning("failed to open directory: %s\n", dir);
2301 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2302 if (claws_unlink(dir_name) < 0)
2303 FILE_OP_ERROR(dir_name, "unlink");
2308 if (g_chdir(prev_dir) < 0) {
2309 FILE_OP_ERROR(prev_dir, "chdir");
2319 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2322 const gchar *dir_name;
2326 if (first == last) {
2327 /* Skip all the dir reading part. */
2328 gchar *filename = g_strdup_printf("%s%s%u", dir, G_DIR_SEPARATOR_S, first);
2329 if (claws_unlink(filename) < 0) {
2330 FILE_OP_ERROR(filename, "unlink");
2338 prev_dir = g_get_current_dir();
2340 if (g_chdir(dir) < 0) {
2341 FILE_OP_ERROR(dir, "chdir");
2346 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2347 g_warning("failed to open directory: %s\n", dir);
2352 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2353 file_no = to_number(dir_name);
2354 if (file_no > 0 && first <= file_no && file_no <= last) {
2355 if (is_dir_exist(dir_name))
2357 if (claws_unlink(dir_name) < 0)
2358 FILE_OP_ERROR(dir_name, "unlink");
2364 if (g_chdir(prev_dir) < 0) {
2365 FILE_OP_ERROR(prev_dir, "chdir");
2375 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2378 const gchar *dir_name;
2382 prev_dir = g_get_current_dir();
2384 if (g_chdir(dir) < 0) {
2385 FILE_OP_ERROR(dir, "chdir");
2390 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2391 FILE_OP_ERROR(dir, "opendir");
2396 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2397 file_no = to_number(dir_name);
2398 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2399 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2400 if (is_dir_exist(dir_name))
2402 if (claws_unlink(dir_name) < 0)
2403 FILE_OP_ERROR(dir_name, "unlink");
2409 if (g_chdir(prev_dir) < 0) {
2410 FILE_OP_ERROR(prev_dir, "chdir");
2420 gint remove_all_numbered_files(const gchar *dir)
2422 return remove_numbered_files(dir, 0, UINT_MAX);
2425 gint remove_dir_recursive(const gchar *dir)
2429 const gchar *dir_name;
2432 if (g_stat(dir, &s) < 0) {
2433 FILE_OP_ERROR(dir, "stat");
2434 if (ENOENT == errno) return 0;
2438 if (!S_ISDIR(s.st_mode)) {
2439 if (claws_unlink(dir) < 0) {
2440 FILE_OP_ERROR(dir, "unlink");
2447 prev_dir = g_get_current_dir();
2448 /* g_print("prev_dir = %s\n", prev_dir); */
2450 if (!path_cmp(prev_dir, dir)) {
2452 if (g_chdir("..") < 0) {
2453 FILE_OP_ERROR(dir, "chdir");
2456 prev_dir = g_get_current_dir();
2459 if (g_chdir(dir) < 0) {
2460 FILE_OP_ERROR(dir, "chdir");
2465 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2466 g_warning("failed to open directory: %s\n", dir);
2472 /* remove all files in the directory */
2473 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2474 /* g_print("removing %s\n", dir_name); */
2476 if (is_dir_exist(dir_name)) {
2477 if (remove_dir_recursive(dir_name) < 0) {
2478 g_warning("can't remove directory\n");
2482 if (claws_unlink(dir_name) < 0)
2483 FILE_OP_ERROR(dir_name, "unlink");
2489 if (g_chdir(prev_dir) < 0) {
2490 FILE_OP_ERROR(prev_dir, "chdir");
2497 if (g_rmdir(dir) < 0) {
2498 FILE_OP_ERROR(dir, "rmdir");
2505 gint rename_force(const gchar *oldpath, const gchar *newpath)
2508 if (!is_file_entry_exist(oldpath)) {
2512 if (is_file_exist(newpath)) {
2513 if (claws_unlink(newpath) < 0)
2514 FILE_OP_ERROR(newpath, "unlink");
2517 return g_rename(oldpath, newpath);
2521 * Append src file body to the tail of dest file.
2522 * Now keep_backup has no effects.
2524 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2526 FILE *src_fp, *dest_fp;
2530 gboolean err = FALSE;
2532 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2533 FILE_OP_ERROR(src, "g_fopen");
2537 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2538 FILE_OP_ERROR(dest, "g_fopen");
2543 if (change_file_mode_rw(dest_fp, dest) < 0) {
2544 FILE_OP_ERROR(dest, "chmod");
2545 g_warning("can't change file mode\n");
2548 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2549 if (n_read < sizeof(buf) && ferror(src_fp))
2551 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2552 g_warning("writing to %s failed.\n", dest);
2560 if (ferror(src_fp)) {
2561 FILE_OP_ERROR(src, "fread");
2565 if (fclose(dest_fp) == EOF) {
2566 FILE_OP_ERROR(dest, "fclose");
2578 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2580 FILE *src_fp, *dest_fp;
2583 gchar *dest_bak = NULL;
2584 gboolean err = FALSE;
2586 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2587 FILE_OP_ERROR(src, "g_fopen");
2590 if (is_file_exist(dest)) {
2591 dest_bak = g_strconcat(dest, ".bak", NULL);
2592 if (rename_force(dest, dest_bak) < 0) {
2593 FILE_OP_ERROR(dest, "rename");
2600 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2601 FILE_OP_ERROR(dest, "g_fopen");
2604 if (rename_force(dest_bak, dest) < 0)
2605 FILE_OP_ERROR(dest_bak, "rename");
2611 if (change_file_mode_rw(dest_fp, dest) < 0) {
2612 FILE_OP_ERROR(dest, "chmod");
2613 g_warning("can't change file mode\n");
2616 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2617 if (n_read < sizeof(buf) && ferror(src_fp))
2619 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2620 g_warning("writing to %s failed.\n", dest);
2625 if (rename_force(dest_bak, dest) < 0)
2626 FILE_OP_ERROR(dest_bak, "rename");
2633 if (ferror(src_fp)) {
2634 FILE_OP_ERROR(src, "fread");
2638 if (fclose(dest_fp) == EOF) {
2639 FILE_OP_ERROR(dest, "fclose");
2646 if (rename_force(dest_bak, dest) < 0)
2647 FILE_OP_ERROR(dest_bak, "rename");
2653 if (keep_backup == FALSE && dest_bak)
2654 claws_unlink(dest_bak);
2661 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2663 if (overwrite == FALSE && is_file_exist(dest)) {
2664 g_warning("move_file(): file %s already exists.", dest);
2668 if (rename_force(src, dest) == 0) return 0;
2670 if (EXDEV != errno) {
2671 FILE_OP_ERROR(src, "rename");
2675 if (copy_file(src, dest, FALSE) < 0) return -1;
2682 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2685 gint bytes_left, to_read;
2688 if (fseek(fp, offset, SEEK_SET) < 0) {
2693 bytes_left = length;
2694 to_read = MIN(bytes_left, sizeof(buf));
2696 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2697 if (n_read < to_read && ferror(fp))
2699 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2702 bytes_left -= n_read;
2703 if (bytes_left == 0)
2705 to_read = MIN(bytes_left, sizeof(buf));
2716 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2719 gboolean err = FALSE;
2721 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2722 FILE_OP_ERROR(dest, "g_fopen");
2726 if (change_file_mode_rw(dest_fp, dest) < 0) {
2727 FILE_OP_ERROR(dest, "chmod");
2728 g_warning("can't change file mode\n");
2731 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2734 if (!err && fclose(dest_fp) == EOF) {
2735 FILE_OP_ERROR(dest, "fclose");
2740 g_warning("writing to %s failed.\n", dest);
2748 /* convert line endings into CRLF. If the last line doesn't end with
2749 * linebreak, add it.
2751 gchar *canonicalize_str(const gchar *str)
2757 for (p = str; *p != '\0'; ++p) {
2764 if (p == str || *(p - 1) != '\n')
2767 out = outp = g_malloc(new_len + 1);
2768 for (p = str; *p != '\0'; ++p) {
2775 if (p == str || *(p - 1) != '\n') {
2784 gint canonicalize_file(const gchar *src, const gchar *dest)
2786 FILE *src_fp, *dest_fp;
2787 gchar buf[BUFFSIZE];
2789 gboolean err = FALSE;
2790 gboolean last_linebreak = FALSE;
2792 if (src == NULL || dest == NULL)
2795 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2796 FILE_OP_ERROR(src, "g_fopen");
2800 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2801 FILE_OP_ERROR(dest, "g_fopen");
2806 if (change_file_mode_rw(dest_fp, dest) < 0) {
2807 FILE_OP_ERROR(dest, "chmod");
2808 g_warning("can't change file mode\n");
2811 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2815 if (len == 0) break;
2816 last_linebreak = FALSE;
2818 if (buf[len - 1] != '\n') {
2819 last_linebreak = TRUE;
2820 r = fputs(buf, dest_fp);
2821 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2822 r = fputs(buf, dest_fp);
2825 r = fwrite(buf, 1, len - 1, dest_fp);
2830 r = fputs("\r\n", dest_fp);
2834 g_warning("writing to %s failed.\n", dest);
2842 if (last_linebreak == TRUE) {
2843 if (fputs("\r\n", dest_fp) == EOF)
2847 if (ferror(src_fp)) {
2848 FILE_OP_ERROR(src, "fgets");
2852 if (fclose(dest_fp) == EOF) {
2853 FILE_OP_ERROR(dest, "fclose");
2865 gint canonicalize_file_replace(const gchar *file)
2869 tmp_file = get_tmp_file();
2871 if (canonicalize_file(file, tmp_file) < 0) {
2876 if (move_file(tmp_file, file, TRUE) < 0) {
2877 g_warning("can't replace %s .\n", file);
2878 claws_unlink(tmp_file);
2887 gchar *normalize_newlines(const gchar *str)
2889 const gchar *p = str;
2892 out = outp = g_malloc(strlen(str) + 1);
2893 for (p = str; *p != '\0'; ++p) {
2895 if (*(p + 1) != '\n')
2906 gchar *get_outgoing_rfc2822_str(FILE *fp)
2908 gchar buf[BUFFSIZE];
2912 str = g_string_new(NULL);
2914 /* output header part */
2915 while (fgets(buf, sizeof(buf), fp) != NULL) {
2917 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2924 else if (next != ' ' && next != '\t') {
2928 if (fgets(buf, sizeof(buf), fp) == NULL)
2932 g_string_append(str, buf);
2933 g_string_append(str, "\r\n");
2939 /* output body part */
2940 while (fgets(buf, sizeof(buf), fp) != NULL) {
2943 g_string_append_c(str, '.');
2944 g_string_append(str, buf);
2945 g_string_append(str, "\r\n");
2949 g_string_free(str, FALSE);
2955 * Create a new boundary in a way that it is very unlikely that this
2956 * will occur in the following text. It would be easy to ensure
2957 * uniqueness if everything is either quoted-printable or base64
2958 * encoded (note that conversion is allowed), but because MIME bodies
2959 * may be nested, it may happen that the same boundary has already
2962 * boundary := 0*69<bchars> bcharsnospace
2963 * bchars := bcharsnospace / " "
2964 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2965 * "+" / "_" / "," / "-" / "." /
2966 * "/" / ":" / "=" / "?"
2968 * some special characters removed because of buggy MTAs
2971 gchar *generate_mime_boundary(const gchar *prefix)
2973 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2974 "abcdefghijklmnopqrstuvwxyz"
2979 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2980 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2983 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2987 gint change_file_mode_rw(FILE *fp, const gchar *file)
2990 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2992 return g_chmod(file, S_IRUSR|S_IWUSR);
2996 FILE *my_tmpfile(void)
2998 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2999 const gchar suffix[] = ".XXXXXX";
3000 const gchar *tmpdir;
3002 const gchar *progname;
3011 tmpdir = get_tmp_dir();
3012 tmplen = strlen(tmpdir);
3013 progname = g_get_prgname();
3014 if (progname == NULL)
3015 progname = "claws-mail";
3016 proglen = strlen(progname);
3017 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3020 memcpy(fname, tmpdir, tmplen);
3021 fname[tmplen] = G_DIR_SEPARATOR;
3022 memcpy(fname + tmplen + 1, progname, proglen);
3023 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3025 fd = mkstemp(fname);
3030 claws_unlink(fname);
3032 /* verify that we can write in the file after unlinking */
3033 if (write(fd, buf, 1) < 0) {
3040 fp = fdopen(fd, "w+b");
3048 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3053 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3057 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3058 dir, G_DIR_SEPARATOR);
3059 fd = mkstemp_name(template, filename);
3062 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3063 fd = mkstemp(*filename);
3065 return fdopen(fd, "w+");
3068 FILE *str_open_as_stream(const gchar *str)
3073 cm_return_val_if_fail(str != NULL, NULL);
3077 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3082 if (len == 0) return fp;
3084 if (fwrite(str, 1, len, fp) != len) {
3085 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3094 gint str_write_to_file(const gchar *str, const gchar *file)
3099 cm_return_val_if_fail(str != NULL, -1);
3100 cm_return_val_if_fail(file != NULL, -1);
3102 if ((fp = g_fopen(file, "wb")) == NULL) {
3103 FILE_OP_ERROR(file, "g_fopen");
3113 if (fwrite(str, 1, len, fp) != len) {
3114 FILE_OP_ERROR(file, "fwrite");
3120 if (fclose(fp) == EOF) {
3121 FILE_OP_ERROR(file, "fclose");
3129 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3136 cm_return_val_if_fail(fp != NULL, NULL);
3138 array = g_byte_array_new();
3140 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3141 if (n_read < sizeof(buf) && ferror(fp))
3143 g_byte_array_append(array, buf, n_read);
3147 FILE_OP_ERROR("file stream", "fread");
3148 g_byte_array_free(array, TRUE);
3153 g_byte_array_append(array, buf, 1);
3154 str = (gchar *)array->data;
3155 g_byte_array_free(array, FALSE);
3157 if (recode && !g_utf8_validate(str, -1, NULL)) {
3158 const gchar *src_codeset, *dest_codeset;
3160 src_codeset = conv_get_locale_charset_str();
3161 dest_codeset = CS_UTF_8;
3162 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3170 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3177 struct timeval timeout = {1, 0};
3182 cm_return_val_if_fail(file != NULL, NULL);
3184 if (g_stat(file, &s) != 0) {
3185 FILE_OP_ERROR(file, "stat");
3188 if (S_ISDIR(s.st_mode)) {
3189 g_warning("%s: is a directory\n", file);
3194 fp = g_fopen (file, "rb");
3196 FILE_OP_ERROR(file, "open");
3200 /* test whether the file is readable without blocking */
3201 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3203 FILE_OP_ERROR(file, "open");
3210 /* allow for one second */
3211 err = select(fd+1, &fds, NULL, NULL, &timeout);
3212 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3214 FILE_OP_ERROR(file, "select");
3216 g_warning("%s: doesn't seem readable\n", file);
3222 /* Now clear O_NONBLOCK */
3223 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3224 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3228 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3229 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3234 /* get the FILE pointer */
3235 fp = fdopen(fd, "rb");
3238 FILE_OP_ERROR(file, "fdopen");
3239 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3244 str = file_read_stream_to_str_full(fp, recode);
3251 gchar *file_read_to_str(const gchar *file)
3253 return file_read_to_str_full(file, TRUE);
3255 gchar *file_read_stream_to_str(FILE *fp)
3257 return file_read_stream_to_str_full(fp, TRUE);
3260 gchar *file_read_to_str_no_recode(const gchar *file)
3262 return file_read_to_str_full(file, FALSE);
3264 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3266 return file_read_stream_to_str_full(fp, FALSE);
3269 char *fgets_crlf(char *buf, int size, FILE *stream)
3271 gboolean is_cr = FALSE;
3272 gboolean last_was_cr = FALSE;
3277 while (--size > 0 && (c = getc(stream)) != EOF)
3280 is_cr = (c == '\r');
3290 last_was_cr = is_cr;
3292 if (c == EOF && cs == buf)
3300 static gint execute_async(gchar *const argv[])
3302 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3304 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3305 NULL, NULL, NULL, FALSE) == FALSE) {
3306 g_warning("Couldn't execute command: %s\n", argv[0]);
3313 static gint execute_sync(gchar *const argv[])
3317 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3320 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3321 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3322 g_warning("Couldn't execute command: %s\n", argv[0]);
3326 if (WIFEXITED(status))
3327 return WEXITSTATUS(status);
3331 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3332 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3333 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3334 g_warning("Couldn't execute command: %s\n", argv[0]);
3342 gint execute_command_line(const gchar *cmdline, gboolean async)
3347 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3349 argv = strsplit_with_quote(cmdline, " ", 0);
3352 ret = execute_async(argv);
3354 ret = execute_sync(argv);
3361 gchar *get_command_output(const gchar *cmdline)
3363 gchar *child_stdout;
3366 cm_return_val_if_fail(cmdline != NULL, NULL);
3368 debug_print("get_command_output(): executing: %s\n", cmdline);
3370 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3372 g_warning("Couldn't execute command: %s\n", cmdline);
3376 return child_stdout;
3379 static gint is_unchanged_uri_char(char c)
3390 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3396 for(i = 0; i < strlen(uri) ; i++) {
3397 if (is_unchanged_uri_char(uri[i])) {
3398 if (k + 2 >= bufsize)
3400 encoded_uri[k++] = uri[i];
3403 char * hexa = "0123456789ABCDEF";
3405 if (k + 4 >= bufsize)
3407 encoded_uri[k++] = '%';
3408 encoded_uri[k++] = hexa[uri[i] / 16];
3409 encoded_uri[k++] = hexa[uri[i] % 16];
3415 gint open_uri(const gchar *uri, const gchar *cmdline)
3419 gchar buf[BUFFSIZE];
3421 gchar encoded_uri[BUFFSIZE];
3422 cm_return_val_if_fail(uri != NULL, -1);
3424 /* an option to choose whether to use encode_uri or not ? */
3425 encode_uri(encoded_uri, BUFFSIZE, uri);
3428 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3429 !strchr(p + 2, '%'))
3430 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3433 g_warning("Open URI command-line is invalid "
3434 "(there must be only one '%%s'): %s",
3436 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3439 execute_command_line(buf, TRUE);
3441 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3444 extern osso_context_t *get_osso_context(void);
3445 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3446 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3447 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3452 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3454 gchar buf[BUFFSIZE];
3457 cm_return_val_if_fail(filepath != NULL, -1);
3460 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3461 !strchr(p + 2, '%'))
3462 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3465 g_warning("Open Text Editor command-line is invalid "
3466 "(there must be only one '%%s'): %s",
3468 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3471 execute_command_line(buf, TRUE);
3476 time_t remote_tzoffset_sec(const gchar *zone)
3478 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3484 time_t remoteoffset;
3486 strncpy(zone3, zone, 3);
3490 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3491 (c == '+' || c == '-')) {
3492 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3494 remoteoffset = -remoteoffset;
3495 } else if (!strncmp(zone, "UT" , 2) ||
3496 !strncmp(zone, "GMT", 2)) {
3498 } else if (strlen(zone3) == 3) {
3499 for (p = ustzstr; *p != '\0'; p += 3) {
3500 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3501 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3502 remoteoffset = iustz * 3600;
3508 } else if (strlen(zone3) == 1) {
3510 case 'Z': remoteoffset = 0; break;
3511 case 'A': remoteoffset = -1; break;
3512 case 'B': remoteoffset = -2; break;
3513 case 'C': remoteoffset = -3; break;
3514 case 'D': remoteoffset = -4; break;
3515 case 'E': remoteoffset = -5; break;
3516 case 'F': remoteoffset = -6; break;
3517 case 'G': remoteoffset = -7; break;
3518 case 'H': remoteoffset = -8; break;
3519 case 'I': remoteoffset = -9; break;
3520 case 'K': remoteoffset = -10; break; /* J is not used */
3521 case 'L': remoteoffset = -11; break;
3522 case 'M': remoteoffset = -12; break;
3523 case 'N': remoteoffset = 1; break;
3524 case 'O': remoteoffset = 2; break;
3525 case 'P': remoteoffset = 3; break;
3526 case 'Q': remoteoffset = 4; break;
3527 case 'R': remoteoffset = 5; break;
3528 case 'S': remoteoffset = 6; break;
3529 case 'T': remoteoffset = 7; break;
3530 case 'U': remoteoffset = 8; break;
3531 case 'V': remoteoffset = 9; break;
3532 case 'W': remoteoffset = 10; break;
3533 case 'X': remoteoffset = 11; break;
3534 case 'Y': remoteoffset = 12; break;
3535 default: remoteoffset = 0; break;
3537 remoteoffset = remoteoffset * 3600;
3541 return remoteoffset;
3544 time_t tzoffset_sec(time_t *now)
3548 struct tm buf1, buf2;
3550 if (now && *now < 0)
3553 gmt = *gmtime_r(now, &buf1);
3554 lt = localtime_r(now, &buf2);
3556 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3558 if (lt->tm_year < gmt.tm_year)
3560 else if (lt->tm_year > gmt.tm_year)
3562 else if (lt->tm_yday < gmt.tm_yday)
3564 else if (lt->tm_yday > gmt.tm_yday)
3567 if (off >= 24 * 60) /* should be impossible */
3568 off = 23 * 60 + 59; /* if not, insert silly value */
3569 if (off <= -24 * 60)
3570 off = -(23 * 60 + 59);
3575 /* calculate timezone offset */
3576 gchar *tzoffset(time_t *now)
3578 static gchar offset_string[6];
3582 struct tm buf1, buf2;
3584 if (now && *now < 0)
3587 gmt = *gmtime_r(now, &buf1);
3588 lt = localtime_r(now, &buf2);
3590 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3592 if (lt->tm_year < gmt.tm_year)
3594 else if (lt->tm_year > gmt.tm_year)
3596 else if (lt->tm_yday < gmt.tm_yday)
3598 else if (lt->tm_yday > gmt.tm_yday)
3606 if (off >= 24 * 60) /* should be impossible */
3607 off = 23 * 60 + 59; /* if not, insert silly value */
3609 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3611 return offset_string;
3614 void get_rfc822_date(gchar *buf, gint len)
3618 gchar day[4], mon[4];
3619 gint dd, hh, mm, ss, yyyy;
3621 gchar buf2[BUFFSIZE];
3624 lt = localtime_r(&t, &buf1);
3626 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3627 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3629 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3630 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3633 void debug_set_mode(gboolean mode)
3638 gboolean debug_get_mode(void)
3643 void debug_print_real(const gchar *format, ...)
3646 gchar buf[BUFFSIZE];
3648 if (!debug_mode) return;
3650 va_start(args, format);
3651 g_vsnprintf(buf, sizeof(buf), format, args);
3658 const char * debug_srcname(const char *file)
3660 const char *s = strrchr (file, '/');
3665 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3667 if (subject == NULL)
3670 subject += subject_get_prefix_length(subject);
3672 return g_hash_table_lookup(subject_table, subject);
3675 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3678 if (subject == NULL || *subject == 0)
3680 subject += subject_get_prefix_length(subject);
3681 g_hash_table_insert(subject_table, subject, data);
3684 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3686 if (subject == NULL)
3689 subject += subject_get_prefix_length(subject);
3690 g_hash_table_remove(subject_table, subject);
3694 static regex_t u_regex;
3695 static gboolean u_init_;
3698 void utils_free_regex(void)
3709 *\brief Check if a string is prefixed with known (combinations)
3710 * of prefixes. The function assumes that each prefix
3711 * is terminated by zero or exactly _one_ space.
3713 *\param str String to check for a prefixes
3715 *\return int Number of chars in the prefix that should be skipped
3716 * for a "clean" subject line. If no prefix was found, 0
3719 int subject_get_prefix_length(const gchar *subject)
3722 /*!< Array with allowable reply prefixes regexps. */
3723 static const gchar * const prefixes[] = {
3724 "Re\\:", /* "Re:" */
3725 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3726 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3727 "Aw\\:", /* "Aw:" (German) */
3728 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3729 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3730 "Fw\\:", /* "Fw:" Forward */
3731 "Fwd\\:", /* "Fwd:" Forward */
3732 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3733 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3734 "Rif\\:", /* "Rif:" (Italian Outlook) */
3735 "Sv\\:", /* "Sv" (Norwegian) */
3736 "Vs\\:", /* "Vs" (Norwegian) */
3737 "Ad\\:", /* "Ad" (Norwegian) */
3738 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3739 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3740 "Re \\:", /* "Re :" (French Yahoo Mail) */
3743 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3747 if (!subject) return 0;
3748 if (!*subject) return 0;
3751 GString *s = g_string_new("");
3753 for (n = 0; n < PREFIXES; n++)
3754 /* Terminate each prefix regexpression by a
3755 * "\ ?" (zero or ONE space), and OR them */
3756 g_string_append_printf(s, "(%s\\ ?)%s",
3761 g_string_prepend(s, "(");
3762 g_string_append(s, ")+"); /* match at least once */
3763 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3766 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3767 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3768 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3769 debug_print("Error compiling regexp %s\n", s->str);
3770 g_string_free(s, TRUE);
3774 g_string_free(s, TRUE);
3778 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3783 /*!< Array with allowable reply prefixes regexps. */
3784 static const gchar * const prefixes[] = {
3786 "antw:", /* "Antw:" (Dutch / German Outlook) */
3787 "aw:", /* "Aw:" (German) */
3788 "antwort:", /* "Antwort:" (German Lotus Notes) */
3789 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3790 "fw:", /* "Fw:" Forward */
3791 "fwd:", /* "Fwd:" Forward */
3792 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3793 "odp:", /* "Odp:" Re (Polish Outlook) */
3794 "rif:", /* "Rif:" (Italian Outlook) */
3795 "sv:", /* "Sv" (Norwegian) */
3796 "vs:", /* "Vs" (Norwegian) */
3797 "ad:", /* "Ad" (Norwegian) */
3798 "R\303\251f. :", /* "R�f. :" (French Lotus Notes) */
3799 "Re :", /* "Re :" (French Yahoo Mail) */
3802 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3805 if (!subject) return 0;
3806 if (!*subject) return 0;
3808 for (n = 0; n < PREFIXES; n++) {
3809 int len = strlen(prefixes[n]);
3810 if (!strncasecmp(subject, prefixes[n], len)) {
3811 if (subject[len] == ' ')
3820 static guint g_stricase_hash(gconstpointer gptr)
3822 guint hash_result = 0;
3825 for (str = gptr; str && *str; str++) {
3826 hash_result += toupper(*str);
3832 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3834 const char *str1 = gptr1;
3835 const char *str2 = gptr2;
3837 return !strcasecmp(str1, str2);
3840 gint g_int_compare(gconstpointer a, gconstpointer b)
3842 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3845 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3853 lt = localtime_r(&t, &buft);
3855 if (user_addr != NULL)
3856 addr = g_strdup_printf(".%s", user_addr);
3857 else if (strlen(buf) != 0)
3858 addr = g_strdup_printf("@%s", buf);
3860 addr = g_strdup_printf("@%s", get_domain_name());
3862 /* Replace all @ but the last one in addr, with underscores.
3863 * RFC 2822 States that msg-id syntax only allows one @.
3865 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3866 *(strchr(addr, '@')) = '_';
3868 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3869 lt->tm_year + 1900, lt->tm_mon + 1,
3870 lt->tm_mday, lt->tm_hour,
3871 lt->tm_min, lt->tm_sec,
3872 (guint) rand(), addr);
3879 quote_cmd_argument()
3881 return a quoted string safely usable in argument of a command.
3883 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3886 gint quote_cmd_argument(gchar * result, guint size,
3896 for(p = path ; * p != '\0' ; p ++) {
3898 if (isalnum((guchar)*p) || (* p == '/')) {
3899 if (remaining > 0) {
3905 result[size - 1] = '\0';
3910 if (remaining >= 2) {
3918 result[size - 1] = '\0';
3923 if (remaining > 0) {
3927 result[size - 1] = '\0';
3941 static void g_node_map_recursive(GNode *node, gpointer data)
3943 GNodeMapData *mapdata = (GNodeMapData *) data;
3945 GNodeMapData newmapdata;
3948 newdata = mapdata->func(node->data, mapdata->data);
3949 if (newdata != NULL) {
3950 newnode = g_node_new(newdata);
3951 g_node_append(mapdata->parent, newnode);
3953 newmapdata.parent = newnode;
3954 newmapdata.func = mapdata->func;
3955 newmapdata.data = mapdata->data;
3957 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3961 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3964 GNodeMapData mapdata;
3966 cm_return_val_if_fail(node != NULL, NULL);
3967 cm_return_val_if_fail(func != NULL, NULL);
3969 root = g_node_new(func(node->data, data));
3971 mapdata.parent = root;
3972 mapdata.func = func;
3973 mapdata.data = data;
3975 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3980 #define HEX_TO_INT(val, hex) \
3984 if ('0' <= c && c <= '9') { \
3986 } else if ('a' <= c && c <= 'f') { \
3987 val = c - 'a' + 10; \
3988 } else if ('A' <= c && c <= 'F') { \
3989 val = c - 'A' + 10; \
3995 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4002 if (hi == -1 || lo == -1)
4005 *out = (hi << 4) + lo;
4009 #define INT_TO_HEX(hex, val) \
4012 hex = '0' + (val); \
4014 hex = 'A' + (val) - 10; \
4017 void get_hex_str(gchar *out, guchar ch)
4021 INT_TO_HEX(hex, ch >> 4);
4023 INT_TO_HEX(hex, ch & 0x0f);
4029 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4031 #define G_PRINT_REF g_print
4035 *\brief Register ref counted pointer. It is based on GBoxed, so should
4036 * work with anything that uses the GType system. The semantics
4037 * are similar to a C++ auto pointer, with the exception that
4038 * C doesn't have automatic closure (calling destructors) when
4039 * exiting a block scope.
4040 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4041 * function directly.
4043 *\return GType A GType type.
4045 GType g_auto_pointer_register(void)
4047 static GType auto_pointer_type;
4048 if (!auto_pointer_type)
4050 g_boxed_type_register_static
4051 ("G_TYPE_AUTO_POINTER",
4052 (GBoxedCopyFunc) g_auto_pointer_copy,
4053 (GBoxedFreeFunc) g_auto_pointer_free);
4054 return auto_pointer_type;
4058 *\brief Structure with g_new() allocated pointer guarded by the
4061 typedef struct AutoPointerRef {
4062 void (*free) (gpointer);
4068 *\brief The auto pointer opaque structure that references the
4069 * pointer guard block.
4071 typedef struct AutoPointer {
4072 AutoPointerRef *ref;
4073 gpointer ptr; /*!< access to protected pointer */
4077 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4081 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4082 * ... when assigning, copying and freeing storage elements
4084 * gtk_list_store_new(N_S_COLUMNS,
4085 * G_TYPE_AUTO_POINTER,
4089 * Template *precious_data = g_new0(Template, 1);
4090 * g_pointer protect = g_auto_pointer_new(precious_data);
4092 * gtk_list_store_set(container, &iter,
4096 * ... the gtk_list_store has copied the pointer and
4097 * ... incremented its reference count, we should free
4098 * ... the auto pointer (in C++ a destructor would do
4099 * ... this for us when leaving block scope)
4101 * g_auto_pointer_free(protect);
4103 * ... gtk_list_store_set() now manages the data. When
4104 * ... *explicitly* requesting a pointer from the list
4105 * ... store, don't forget you get a copy that should be
4106 * ... freed with g_auto_pointer_free() eventually.
4110 *\param pointer Pointer to be guarded.
4112 *\return GAuto * Pointer that should be used in containers with
4115 GAuto *g_auto_pointer_new(gpointer p)
4117 AutoPointerRef *ref;
4123 ref = g_new0(AutoPointerRef, 1);
4124 ptr = g_new0(AutoPointer, 1);
4134 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4140 *\brief Allocate an autopointer using the passed \a free function to
4141 * free the guarded pointer
4143 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4150 aptr = g_auto_pointer_new(p);
4151 aptr->ref->free = free_;
4155 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4157 if (auto_ptr == NULL)
4159 return ((AutoPointer *) auto_ptr)->ptr;
4163 *\brief Copies an auto pointer by. It's mostly not necessary
4164 * to call this function directly, unless you copy/assign
4165 * the guarded pointer.
4167 *\param auto_ptr Auto pointer returned by previous call to
4168 * g_auto_pointer_new_XXX()
4170 *\return gpointer An auto pointer
4172 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4175 AutoPointerRef *ref;
4178 if (auto_ptr == NULL)
4183 newp = g_new0(AutoPointer, 1);
4186 newp->ptr = ref->pointer;
4190 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4196 *\brief Free an auto pointer
4198 void g_auto_pointer_free(GAuto *auto_ptr)
4201 AutoPointerRef *ref;
4203 if (auto_ptr == NULL)
4209 if (--(ref->cnt) == 0) {
4211 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4213 ref->free(ref->pointer);
4218 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4223 void replace_returns(gchar *str)
4228 while (strstr(str, "\n")) {
4229 *strstr(str, "\n") = ' ';
4231 while (strstr(str, "\r")) {
4232 *strstr(str, "\r") = ' ';
4236 /* get_uri_part() - retrieves a URI starting from scanpos.
4237 Returns TRUE if succesful */
4238 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4239 const gchar **bp, const gchar **ep, gboolean hdr)
4242 gint parenthese_cnt = 0;
4244 cm_return_val_if_fail(start != NULL, FALSE);
4245 cm_return_val_if_fail(scanpos != NULL, FALSE);
4246 cm_return_val_if_fail(bp != NULL, FALSE);
4247 cm_return_val_if_fail(ep != NULL, FALSE);
4251 /* find end point of URI */
4252 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4253 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4254 !IS_ASCII(*(const guchar *)ep_) ||
4255 strchr("[]{}<>\"", *ep_)) {
4257 } else if (strchr("(", *ep_)) {
4259 } else if (strchr(")", *ep_)) {
4260 if (parenthese_cnt > 0)
4267 /* no punctuation at end of string */
4269 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4270 * should pass some URI type to this function and decide on that whether
4271 * to perform punctuation stripping */
4273 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4275 for (; ep_ - 1 > scanpos + 1 &&
4276 IS_REAL_PUNCT(*(ep_ - 1));
4280 #undef IS_REAL_PUNCT
4287 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4289 while (bp && *bp && g_ascii_isspace(*bp))
4291 return g_strndup(bp, ep - bp);
4294 /* valid mail address characters */
4295 #define IS_RFC822_CHAR(ch) \
4299 !g_ascii_isspace(ch) && \
4300 !strchr("(),;<>\"", (ch)))
4302 /* alphabet and number within 7bit ASCII */
4303 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4304 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4306 static GHashTable *create_domain_tab(void)
4308 static const gchar *toplvl_domains [] = {
4310 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4311 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4312 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4313 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4314 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4315 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4316 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4317 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4318 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4319 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4320 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4321 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4322 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4323 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4324 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4325 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4326 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4327 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4328 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4329 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4330 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4331 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4332 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4333 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4334 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4335 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4338 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4340 cm_return_val_if_fail(htab, NULL);
4341 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4342 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4346 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4348 const gint MAX_LVL_DOM_NAME_LEN = 6;
4349 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4350 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4353 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4356 for (p = buf; p < m && first < last; *p++ = *first++)
4360 return g_hash_table_lookup(tab, buf) != NULL;
4363 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4364 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4365 const gchar **bp, const gchar **ep, gboolean hdr)
4367 /* more complex than the uri part because we need to scan back and forward starting from
4368 * the scan position. */
4369 gboolean result = FALSE;
4370 const gchar *bp_ = NULL;
4371 const gchar *ep_ = NULL;
4372 static GHashTable *dom_tab;
4373 const gchar *last_dot = NULL;
4374 const gchar *prelast_dot = NULL;
4375 const gchar *last_tld_char = NULL;
4377 /* the informative part of the email address (describing the name
4378 * of the email address owner) may contain quoted parts. the
4379 * closure stack stores the last encountered quotes. */
4380 gchar closure_stack[128];
4381 gchar *ptr = closure_stack;
4383 cm_return_val_if_fail(start != NULL, FALSE);
4384 cm_return_val_if_fail(scanpos != NULL, FALSE);
4385 cm_return_val_if_fail(bp != NULL, FALSE);
4386 cm_return_val_if_fail(ep != NULL, FALSE);
4389 const gchar *start_quote = NULL;
4390 const gchar *end_quote = NULL;
4392 /* go to the real start */
4393 if (start[0] == ',')
4395 if (start[0] == ';')
4397 while (start[0] == '\n' || start[0] == '\r')
4399 while (start[0] == ' ' || start[0] == '\t')
4404 /* check if there are quotes (to skip , in them) */
4405 if (*start == '"') {
4406 start_quote = start;
4408 end_quote = strstr(start, "\"");
4414 /* skip anything between quotes */
4415 if (start_quote && end_quote) {
4420 /* find end (either , or ; or end of line) */
4421 if (strstr(start, ",") && strstr(start, ";"))
4422 *ep = strstr(start,",") < strstr(start, ";")
4423 ? strstr(start, ",") : strstr(start, ";");
4424 else if (strstr(start, ","))
4425 *ep = strstr(start, ",");
4426 else if (strstr(start, ";"))
4427 *ep = strstr(start, ";");
4429 *ep = start+strlen(start);
4431 /* go back to real start */
4432 if (start_quote && end_quote) {
4433 start = start_quote;
4436 /* check there's still an @ in that, or search
4437 * further if possible */
4438 if (strstr(start, "@") && strstr(start, "@") < *ep)
4440 else if (*ep < start+strlen(start)) {
4443 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4451 dom_tab = create_domain_tab();
4452 cm_return_val_if_fail(dom_tab, FALSE);
4454 /* scan start of address */
4455 for (bp_ = scanpos - 1;
4456 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4459 /* TODO: should start with an alnum? */
4461 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4464 if (bp_ != scanpos) {
4465 /* scan end of address */
4466 for (ep_ = scanpos + 1;
4467 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4469 prelast_dot = last_dot;
4471 if (*(last_dot + 1) == '.') {
4472 if (prelast_dot == NULL)
4474 last_dot = prelast_dot;
4479 /* TODO: really should terminate with an alnum? */
4480 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4485 if (last_dot == NULL)
4487 if (last_dot >= ep_)
4488 last_dot = prelast_dot;
4489 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4493 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4494 if (*last_tld_char == '?')
4497 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4504 if (!result) return FALSE;
4506 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4507 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4508 && IS_RFC822_CHAR(*(ep_ + 3))) {
4509 /* this informative part with an @ in it is
4510 * followed by the email address */
4513 /* go to matching '>' (or next non-rfc822 char, like \n) */
4514 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4517 /* include the bracket */
4518 if (*ep_ == '>') ep_++;
4520 /* include the leading quote */
4528 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4529 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4532 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4533 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4536 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4537 #define IN_STACK() (ptr > closure_stack)
4538 /* has underrun check */
4539 #define POP_STACK() if(IN_STACK()) --ptr
4540 /* has overrun check */
4541 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4542 /* has underrun check */
4543 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4547 /* scan for the informative part. */
4548 for (bp_ -= 2; bp_ >= start; bp_--) {
4549 /* if closure on the stack keep scanning */
4550 if (PEEK_STACK() == *bp_) {
4554 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4559 /* if nothing in the closure stack, do the special conditions
4560 * the following if..else expression simply checks whether
4561 * a token is acceptable. if not acceptable, the clause
4562 * should terminate the loop with a 'break' */
4563 if (!PEEK_STACK()) {
4565 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4566 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4567 /* hyphens are allowed, but only in
4569 } else if (strchr(" \"'", *bp_)) {
4570 /* but anything not being a punctiation
4573 break; /* anything else is rejected */
4580 /* scan forward (should start with an alnum) */
4581 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4597 #undef IS_ASCII_ALNUM
4598 #undef IS_RFC822_CHAR
4600 gchar *make_email_string(const gchar *bp, const gchar *ep)
4602 /* returns a mailto: URI; mailto: is also used to detect the
4603 * uri type later on in the button_pressed signal handler */
4607 tmp = g_strndup(bp, ep - bp);
4608 result = g_strconcat("mailto:", tmp, NULL);
4614 gchar *make_http_string(const gchar *bp, const gchar *ep)
4616 /* returns an http: URI; */
4620 while (bp && *bp && g_ascii_isspace(*bp))
4622 tmp = g_strndup(bp, ep - bp);
4623 result = g_strconcat("http://", tmp, NULL);
4629 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4631 FILE *fp = g_fopen(path, "rb");
4632 gchar buf[BUFFSIZE];
4633 gchar *result = NULL;
4636 while (fgets(buf, sizeof (buf), fp) != NULL) {
4637 gchar **parts = g_strsplit(buf, ";", 3);
4638 gchar *trimmed = parts[0];
4639 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4641 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4642 trimmed[strlen(trimmed)-1] = '\0';
4644 if (!strcmp(trimmed, type)) {
4645 gboolean needsterminal = FALSE;
4646 if (parts[2] && strstr(parts[2], "needsterminal")) {
4647 needsterminal = TRUE;
4649 if (parts[2] && strstr(parts[2], "test=")) {
4650 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4651 gchar *testcmd = orig_testcmd;
4652 if (strstr(testcmd,";"))
4653 *(strstr(testcmd,";")) = '\0';
4654 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4656 while (testcmd[strlen(testcmd)-1] == '\n')
4657 testcmd[strlen(testcmd)-1] = '\0';
4658 while (testcmd[strlen(testcmd)-1] == '\r')
4659 testcmd[strlen(testcmd)-1] = '\0';
4660 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4661 testcmd[strlen(testcmd)-1] = '\0';
4663 if (strstr(testcmd, "%s")) {
4664 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4665 gint res = system(tmp);
4667 g_free(orig_testcmd);
4674 gint res = system(testcmd);
4675 g_free(orig_testcmd);
4685 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4687 while (trimmed[strlen(trimmed)-1] == '\n')
4688 trimmed[strlen(trimmed)-1] = '\0';
4689 while (trimmed[strlen(trimmed)-1] == '\r')
4690 trimmed[strlen(trimmed)-1] = '\0';
4691 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4692 trimmed[strlen(trimmed)-1] = '\0';
4693 result = g_strdup(trimmed);
4696 /* if there are no single quotes around %s, add them.
4697 * '.*%s.*' is ok, as in display 'png:%s'
4699 if (strstr(result, "%s")
4700 && !(strstr(result, "'") < strstr(result,"%s") &&
4701 strstr(strstr(result,"%s"), "'"))) {
4702 gchar *start = g_strdup(result);
4703 gchar *end = g_strdup(strstr(result, "%s")+2);
4705 *strstr(start, "%s") = '\0';
4706 tmp = g_strconcat(start,"'%s'",end, NULL);
4712 if (needsterminal) {
4713 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4724 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4726 gchar *result = NULL;
4730 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4731 result = mailcap_get_command_in_file(path, type, file_to_open);
4735 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4739 void mailcap_update_default(const gchar *type, const gchar *command)
4741 gchar *path = NULL, *outpath = NULL;
4742 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4743 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4744 FILE *fp = g_fopen(path, "rb");
4745 FILE *outfp = g_fopen(outpath, "wb");
4746 gchar buf[BUFFSIZE];
4747 gboolean err = FALSE;
4755 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4756 gchar **parts = g_strsplit(buf, ";", 3);
4757 gchar *trimmed = parts[0];
4758 while (trimmed[0] == ' ')
4760 while (trimmed[strlen(trimmed)-1] == ' ')
4761 trimmed[strlen(trimmed)-1] = '\0';
4763 if (!strcmp(trimmed, type)) {
4768 if(fputs(buf, outfp) == EOF) {
4775 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4781 if (fclose(outfp) == EOF)
4785 g_rename(outpath, path);
4791 gint copy_dir(const gchar *src, const gchar *dst)
4796 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4797 g_warning("failed to open directory: %s\n", src);
4801 if (make_dir(dst) < 0)
4804 while ((name = g_dir_read_name(dir)) != NULL) {
4805 gchar *old_file, *new_file;
4806 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4807 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4808 debug_print("copying: %s -> %s\n", old_file, new_file);
4809 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4810 gint r = copy_file(old_file, new_file, TRUE);
4817 /* Windows has no symlinks. Or well, Vista seems to
4818 have something like this but the semantics might be
4819 different. Thus we don't use it under Windows. */
4820 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4823 gchar *target = g_file_read_link(old_file, &error);
4825 r = symlink(target, new_file);
4832 #endif /*G_OS_WIN32*/
4833 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4834 gint r = copy_dir(old_file, new_file);
4845 /* crude test to see if a file is an email. */
4846 gboolean file_is_email (const gchar *filename)
4852 if (filename == NULL)
4854 if ((fp = g_fopen(filename, "rb")) == NULL)
4856 while (i < 60 && score < 3
4857 && fgets(buffer, sizeof (buffer), fp) > 0) {
4858 if (!strncmp(buffer, "From:", strlen("From:")))
4860 else if (!strncmp(buffer, "Date:", strlen("Date:")))
4862 else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
4864 else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4869 return (score >= 3);
4872 gboolean sc_g_list_bigger(GList *list, gint max)
4876 while (cur && i <= max+1) {
4883 gboolean sc_g_slist_bigger(GSList *list, gint max)
4887 while (cur && i <= max+1) {
4894 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4895 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4896 NULL, NULL, NULL, NULL, NULL, NULL};
4897 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4898 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4899 NULL, NULL, NULL, NULL, NULL, NULL};
4901 gint daynames_len[] = {0,0,0,0,0,0,0};
4902 gint monthnames_len[] = {0,0,0,0,0,0,
4904 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4905 gint s_monthnames_len[] = {0,0,0,0,0,0,
4907 const gchar *s_am_up = NULL;
4908 const gchar *s_pm_up = NULL;
4909 const gchar *s_am_low = NULL;
4910 const gchar *s_pm_low = NULL;
4912 gint s_am_up_len = 0;
4913 gint s_pm_up_len = 0;
4914 gint s_am_low_len = 0;
4915 gint s_pm_low_len = 0;
4917 const gchar *def_loc_format = NULL;
4918 const gchar *date_loc_format = NULL;
4919 const gchar *time_loc_format = NULL;
4920 const gchar *time_am_pm = NULL;
4922 static gboolean time_names_init_done = FALSE;
4924 static void init_time_names(void)
4928 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4929 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4930 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4931 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4932 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4933 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4934 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4936 monthnames[0] = Q_("Complete month name for use by strftime|January");
4937 monthnames[1] = Q_("Complete month name for use by strftime|February");
4938 monthnames[2] = Q_("Complete month name for use by strftime|March");
4939 monthnames[3] = Q_("Complete month name for use by strftime|April");
4940 monthnames[4] = Q_("Complete month name for use by strftime|May");
4941 monthnames[5] = Q_("Complete month name for use by strftime|June");
4942 monthnames[6] = Q_("Complete month name for use by strftime|July");
4943 monthnames[7] = Q_("Complete month name for use by strftime|August");
4944 monthnames[8] = Q_("Complete month name for use by strftime|September");
4945 monthnames[9] = Q_("Complete month name for use by strftime|October");
4946 monthnames[10] = Q_("Complete month name for use by strftime|November");
4947 monthnames[11] = Q_("Complete month name for use by strftime|December");
4949 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4950 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4951 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4952 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4953 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4954 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4955 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4957 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4958 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4959 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4960 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4961 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4962 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4963 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4964 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4965 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4966 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4967 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4968 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4970 for (i = 0; i < 7; i++) {
4971 daynames_len[i] = strlen(daynames[i]);
4972 s_daynames_len[i] = strlen(s_daynames[i]);
4974 for (i = 0; i < 12; i++) {
4975 monthnames_len[i] = strlen(monthnames[i]);
4976 s_monthnames_len[i] = strlen(s_monthnames[i]);
4979 s_am_up = Q_("For use by strftime (morning)|AM");
4980 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4981 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4982 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4984 s_am_up_len = strlen(s_am_up);
4985 s_pm_up_len = strlen(s_pm_up);
4986 s_am_low_len = strlen(s_am_low);
4987 s_pm_low_len = strlen(s_pm_low);
4989 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4990 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4991 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4993 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4995 time_names_init_done = TRUE;
4998 #define CHECK_SIZE() { \
4999 total_done += len; \
5000 if (total_done >= buflen) { \
5001 buf[buflen-1] = '\0'; \
5006 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5008 gchar *curpos = buf;
5009 gint total_done = 0;
5010 gchar subbuf[64], subfmt[64];
5011 static time_t last_tzset = (time_t)0;
5013 if (!time_names_init_done)
5016 if (format == NULL || lt == NULL)
5019 if (last_tzset != time(NULL)) {
5021 last_tzset = time(NULL);
5024 if (*format == '%') {
5025 gint len = 0, tmp = 0;
5029 len = 1; CHECK_SIZE();
5033 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5034 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5037 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5038 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5042 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5043 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5046 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5047 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5050 fast_strftime(subbuf, 64, def_loc_format, lt);
5051 len = strlen(subbuf); CHECK_SIZE();
5052 strncpy2(curpos, subbuf, buflen - total_done);
5055 total_done += 2; CHECK_SIZE();
5056 tmp = (lt->tm_year + 1900)/100;
5057 *curpos++ = '0'+(tmp / 10);
5058 *curpos++ = '0'+(tmp % 10);
5061 total_done += 2; CHECK_SIZE();
5062 *curpos++ = '0'+(lt->tm_mday / 10);
5063 *curpos++ = '0'+(lt->tm_mday % 10);
5066 total_done += 8; CHECK_SIZE();
5067 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5068 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5070 *curpos++ = '0'+(lt->tm_mday / 10);
5071 *curpos++ = '0'+(lt->tm_mday % 10);
5073 tmp = lt->tm_year%100;
5074 *curpos++ = '0'+(tmp / 10);
5075 *curpos++ = '0'+(tmp % 10);
5078 len = 2; CHECK_SIZE();
5079 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5082 len = 10; CHECK_SIZE();
5083 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5084 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5087 total_done += 2; CHECK_SIZE();
5088 *curpos++ = '0'+(lt->tm_hour / 10);
5089 *curpos++ = '0'+(lt->tm_hour % 10);
5092 total_done += 2; CHECK_SIZE();
5098 *curpos++ = '0'+(tmp / 10);
5099 *curpos++ = '0'+(tmp % 10);
5102 len = 3; CHECK_SIZE();
5103 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5106 len = 2; CHECK_SIZE();
5107 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5110 len = 2; CHECK_SIZE();
5116 snprintf(curpos, buflen - total_done, "%2d", tmp);
5119 total_done += 2; CHECK_SIZE();
5120 tmp = lt->tm_mon + 1;
5121 *curpos++ = '0'+(tmp / 10);
5122 *curpos++ = '0'+(tmp % 10);
5125 total_done += 2; CHECK_SIZE();
5126 *curpos++ = '0'+(lt->tm_min / 10);
5127 *curpos++ = '0'+(lt->tm_min % 10);
5130 len = 1; CHECK_SIZE();
5134 if (lt->tm_hour >= 12) {
5135 len = s_pm_up_len; CHECK_SIZE();
5136 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5138 len = s_am_up_len; CHECK_SIZE();
5139 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5143 if (lt->tm_hour >= 12) {
5144 len = s_pm_low_len; CHECK_SIZE();
5145 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5147 len = s_am_low_len; CHECK_SIZE();
5148 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5152 fast_strftime(subbuf, 64, time_am_pm, lt);
5153 len = strlen(subbuf); CHECK_SIZE();
5154 strncpy2(curpos, subbuf, buflen - total_done);
5157 total_done += 5; CHECK_SIZE();
5158 *curpos++ = '0'+(lt->tm_hour / 10);
5159 *curpos++ = '0'+(lt->tm_hour % 10);
5161 *curpos++ = '0'+(lt->tm_min / 10);
5162 *curpos++ = '0'+(lt->tm_min % 10);
5165 snprintf(subbuf, 64, "%ld", mktime(lt));
5166 len = strlen(subbuf); CHECK_SIZE();
5167 strncpy2(curpos, subbuf, buflen - total_done);
5170 total_done += 2; CHECK_SIZE();
5171 *curpos++ = '0'+(lt->tm_sec / 10);
5172 *curpos++ = '0'+(lt->tm_sec % 10);
5175 len = 1; CHECK_SIZE();
5179 total_done += 8; CHECK_SIZE();
5180 *curpos++ = '0'+(lt->tm_hour / 10);
5181 *curpos++ = '0'+(lt->tm_hour % 10);
5183 *curpos++ = '0'+(lt->tm_min / 10);
5184 *curpos++ = '0'+(lt->tm_min % 10);
5186 *curpos++ = '0'+(lt->tm_sec / 10);
5187 *curpos++ = '0'+(lt->tm_sec % 10);
5190 len = 1; CHECK_SIZE();
5191 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5194 len = 1; CHECK_SIZE();
5195 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5198 fast_strftime(subbuf, 64, date_loc_format, lt);
5199 len = strlen(subbuf); CHECK_SIZE();
5200 strncpy2(curpos, subbuf, buflen - total_done);
5203 fast_strftime(subbuf, 64, time_loc_format, lt);
5204 len = strlen(subbuf); CHECK_SIZE();
5205 strncpy2(curpos, subbuf, buflen - total_done);
5208 total_done += 2; CHECK_SIZE();
5209 tmp = lt->tm_year%100;
5210 *curpos++ = '0'+(tmp / 10);
5211 *curpos++ = '0'+(tmp % 10);
5214 len = 4; CHECK_SIZE();
5215 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5225 /* let these complicated ones be done with the libc */
5226 snprintf(subfmt, 64, "%%%c", *format);
5227 strftime(subbuf, 64, subfmt, lt);
5228 len = strlen(subbuf); CHECK_SIZE();
5229 strncpy2(curpos, subbuf, buflen - total_done);
5233 /* let these complicated modifiers be done with the libc */
5234 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5235 strftime(subbuf, 64, subfmt, lt);
5236 len = strlen(subbuf); CHECK_SIZE();
5237 strncpy2(curpos, subbuf, buflen - total_done);
5241 if (format && *format)
5242 g_warning("format error (%c)", *format);
5249 int len = 1; CHECK_SIZE();
5250 *curpos++ = *format++;
5257 gboolean prefs_common_get_use_shred(void);
5261 #define WEXITSTATUS(x) (x)
5264 int claws_unlink(const gchar *filename)
5267 static int found_shred = -1;
5268 static const gchar *args[4];
5270 if (filename == NULL)
5273 if (prefs_common_get_use_shred()) {
5274 if (found_shred == -1) {
5276 args[0] = g_find_program_in_path("shred");
5277 debug_print("found shred: %s\n", args[0]);
5278 found_shred = (args[0] != NULL) ? 1:0;
5282 if (found_shred == 1) {
5283 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5284 if (s.st_nlink == 1) {
5287 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5288 NULL, NULL, NULL, NULL, &status, NULL);
5289 debug_print("%s %s exited with status %d\n",
5290 args[0], filename, WEXITSTATUS(status));
5291 if (truncate(filename, 0) < 0)
5292 g_warning("couln't truncate");
5297 return g_unlink(filename);