2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
38 #include <sys/param.h>
40 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
48 #include <sys/types.h>
50 # include <sys/wait.h>
57 #include <sys/utsname.h>
71 # include <tablet-browser-interface.h>
73 # include <osso-browser-interface.h>
79 #include "../codeconv.h"
83 static gboolean debug_mode = FALSE;
85 static GSList *tempfiles=NULL;
88 /* Return true if we are running as root. This function should beused
89 instead of getuid () == 0. */
90 gboolean superuser_p (void)
93 return w32_is_administrator ();
101 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
102 gint g_chdir(const gchar *path)
105 if (G_WIN32_HAVE_WIDECHAR_API()) {
110 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
116 retval = _wchdir(wpath);
128 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
129 if (cp_path == NULL) {
134 retval = chdir(cp_path);
147 gint g_chmod(const gchar *path, gint mode)
150 if (G_WIN32_HAVE_WIDECHAR_API()) {
155 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
161 retval = _wchmod(wpath, mode);
173 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
174 if (cp_path == NULL) {
179 retval = chmod(cp_path, mode);
188 return chmod(path, mode);
192 FILE* g_fopen(const gchar *filename, const gchar *mode)
195 char *name = g_win32_locale_filename_from_utf8(filename);
196 FILE* fp = fopen(name, mode);
200 return fopen(filename, mode);
203 int g_open(const gchar *filename, int flags, int mode)
206 char *name = g_win32_locale_filename_from_utf8(filename);
207 int fd = open(name, flags, mode);
211 return open(filename, flags, mode);
214 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
218 gint mkstemp_name(gchar *template, gchar **name_used)
220 static gulong count=0; /* W32-_mktemp only supports up to 27
224 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
225 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
226 (S_IRUSR | S_IWUSR));
228 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
230 perror(g_strdup_printf("cant create %s",*name_used));
236 #endif /* G_OS_WIN32 */
239 gint mkstemp(gchar *template)
242 gint res = mkstemp_name(template, &dummyname);
246 #endif /* G_OS_WIN32 */
248 void list_free_strings(GList *list)
250 list = g_list_first(list);
252 while (list != NULL) {
258 void slist_free_strings(GSList *list)
260 while (list != NULL) {
266 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
271 void hash_free_strings(GHashTable *table)
273 g_hash_table_foreach(table, hash_free_strings_func, NULL);
276 gint str_case_equal(gconstpointer v, gconstpointer v2)
278 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
281 guint str_case_hash(gconstpointer key)
283 const gchar *p = key;
287 h = g_ascii_tolower(h);
288 for (p += 1; *p != '\0'; p++)
289 h = (h << 5) - h + g_ascii_tolower(*p);
295 void ptr_array_free_strings(GPtrArray *array)
300 cm_return_if_fail(array != NULL);
302 for (i = 0; i < array->len; i++) {
303 str = g_ptr_array_index(array, i);
308 gint to_number(const gchar *nstr)
310 register const gchar *p;
312 if (*nstr == '\0') return -1;
314 for (p = nstr; *p != '\0'; p++)
315 if (!g_ascii_isdigit(*p)) return -1;
320 /* convert integer into string,
321 nstr must be not lower than 11 characters length */
322 gchar *itos_buf(gchar *nstr, gint n)
324 g_snprintf(nstr, 11, "%d", n);
328 /* convert integer into string */
331 static gchar nstr[11];
333 return itos_buf(nstr, n);
336 #define divide(num,divisor,i,d) \
338 i = num >> divisor; \
339 d = num & ((1<<divisor)-1); \
340 d = (d*100) >> divisor; \
345 * \brief Convert a given size in bytes in a human-readable string
347 * \param size The size expressed in bytes to convert in string
348 * \return The string that respresents the size in an human-readable way
350 gchar *to_human_readable(goffset size)
352 static gchar str[14];
353 static gchar *b_format = NULL, *kb_format = NULL,
354 *mb_format = NULL, *gb_format = NULL;
355 register int t = 0, r = 0;
356 if (b_format == NULL) {
358 kb_format = _("%d.%02dKB");
359 mb_format = _("%d.%02dMB");
360 gb_format = _("%.2fGB");
363 if (size < (goffset)1024) {
364 g_snprintf(str, sizeof(str), b_format, (gint)size);
366 } else if (size >> 10 < (goffset)1024) {
367 divide(size, 10, t, r);
368 g_snprintf(str, sizeof(str), kb_format, t, r);
370 } else if (size >> 20 < (goffset)1024) {
371 divide(size, 20, t, r);
372 g_snprintf(str, sizeof(str), mb_format, t, r);
375 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
380 /* strcmp with NULL-checking */
381 gint strcmp2(const gchar *s1, const gchar *s2)
383 if (s1 == NULL || s2 == NULL)
386 return strcmp(s1, s2);
388 /* strstr with NULL-checking */
389 gchar *strstr2(const gchar *s1, const gchar *s2)
391 if (s1 == NULL || s2 == NULL)
394 return strstr(s1, s2);
397 gint path_cmp(const gchar *s1, const gchar *s2)
402 gchar *s1buf, *s2buf;
405 if (s1 == NULL || s2 == NULL) return -1;
406 if (*s1 == '\0' || *s2 == '\0') return -1;
409 s1buf = g_strdup (s1);
410 s2buf = g_strdup (s2);
411 subst_char (s1buf, '/', G_DIR_SEPARATOR);
412 subst_char (s2buf, '/', G_DIR_SEPARATOR);
415 #endif /* !G_OS_WIN32 */
420 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
421 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
423 rc = strncmp(s1, s2, MAX(len1, len2));
427 #endif /* !G_OS_WIN32 */
431 /* remove trailing return code */
432 gchar *strretchomp(gchar *str)
436 if (!*str) return str;
438 for (s = str + strlen(str) - 1;
439 s >= str && (*s == '\n' || *s == '\r');
446 /* remove trailing character */
447 gchar *strtailchomp(gchar *str, gchar tail_char)
451 if (!*str) return str;
452 if (tail_char == '\0') return str;
454 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
460 /* remove CR (carriage return) */
461 gchar *strcrchomp(gchar *str)
465 if (!*str) return str;
467 s = str + strlen(str) - 1;
468 if (*s == '\n' && s > str && *(s - 1) == '\r') {
476 gint file_strip_crs(const gchar *file)
478 FILE *fp = NULL, *outfp = NULL;
480 gchar *out = get_tmp_file();
484 fp = g_fopen(file, "rb");
488 outfp = g_fopen(out, "wb");
494 while (fgets(buf, sizeof (buf), fp) != NULL) {
496 if (fputs(buf, outfp) == EOF) {
504 if (fclose(outfp) == EOF) {
508 if (move_file(out, file, TRUE) < 0)
520 /* Similar to `strstr' but this function ignores the case of both strings. */
521 gchar *strcasestr(const gchar *haystack, const gchar *needle)
523 register size_t haystack_len, needle_len;
525 haystack_len = strlen(haystack);
526 needle_len = strlen(needle);
528 if (haystack_len < needle_len || needle_len == 0)
531 while (haystack_len >= needle_len) {
532 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
533 return (gchar *)haystack;
543 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
544 gconstpointer needle, size_t needlelen)
546 const gchar *haystack_ = (const gchar *)haystack;
547 const gchar *needle_ = (const gchar *)needle;
548 const gchar *haystack_cur = (const gchar *)haystack;
549 size_t haystack_left = haystacklen;
552 return memchr(haystack_, *needle_, haystacklen);
554 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
556 if (haystacklen - (haystack_cur - haystack_) < needlelen)
558 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
559 return (gpointer)haystack_cur;
562 haystack_left = haystacklen - (haystack_cur - haystack_);
569 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
570 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
572 register const gchar *s = src;
573 register gchar *d = dest;
583 /* Examine if next block is non-ASCII string */
584 gboolean is_next_nonascii(const gchar *s)
588 /* skip head space */
589 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
591 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
592 if (*(guchar *)p > 127 || *(guchar *)p < 32)
599 gint get_next_word_len(const gchar *s)
603 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
609 static void trim_subject_for_compare(gchar *str)
613 eliminate_parenthesis(str, '[', ']');
614 eliminate_parenthesis(str, '(', ')');
617 srcp = str + subject_get_prefix_length(str);
619 memmove(str, srcp, strlen(srcp) + 1);
622 static void trim_subject_for_sort(gchar *str)
628 srcp = str + subject_get_prefix_length(str);
630 memmove(str, srcp, strlen(srcp) + 1);
633 /* compare subjects */
634 gint subject_compare(const gchar *s1, const gchar *s2)
638 if (!s1 || !s2) return -1;
639 if (!*s1 || !*s2) return -1;
641 Xstrdup_a(str1, s1, return -1);
642 Xstrdup_a(str2, s2, return -1);
644 trim_subject_for_compare(str1);
645 trim_subject_for_compare(str2);
647 if (!*str1 || !*str2) return -1;
649 return strcmp(str1, str2);
652 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
656 if (!s1 || !s2) return -1;
658 Xstrdup_a(str1, s1, return -1);
659 Xstrdup_a(str2, s2, return -1);
661 trim_subject_for_sort(str1);
662 trim_subject_for_sort(str2);
664 return g_utf8_collate(str1, str2);
667 void trim_subject(gchar *str)
669 register gchar *srcp;
675 srcp = str + subject_get_prefix_length(str);
680 } else if (*srcp == '(') {
692 else if (*srcp == cl)
699 while (g_ascii_isspace(*srcp)) srcp++;
700 memmove(str, srcp, strlen(srcp) + 1);
703 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
705 register gchar *srcp, *destp;
710 while ((destp = strchr(destp, op))) {
716 else if (*srcp == cl)
722 while (g_ascii_isspace(*srcp)) srcp++;
723 memmove(destp, srcp, strlen(srcp) + 1);
727 void extract_parenthesis(gchar *str, gchar op, gchar cl)
729 register gchar *srcp, *destp;
734 while ((srcp = strchr(destp, op))) {
737 memmove(destp, srcp + 1, strlen(srcp));
742 else if (*destp == cl)
754 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
757 register gchar *srcp, *destp;
759 gboolean in_quote = FALSE;
763 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
766 memmove(destp, srcp + 1, strlen(srcp));
769 if (*destp == op && !in_quote)
771 else if (*destp == cl && !in_quote)
773 else if (*destp == quote_chr)
785 void extract_quote(gchar *str, gchar quote_chr)
789 if ((str = strchr(str, quote_chr))) {
791 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
792 memmove(p - 1, p, strlen(p) + 1);
797 memmove(str, str + 1, p - str);
802 void eliminate_address_comment(gchar *str)
804 register gchar *srcp, *destp;
809 while ((destp = strchr(destp, '"'))) {
810 if ((srcp = strchr(destp + 1, '"'))) {
815 while (g_ascii_isspace(*srcp)) srcp++;
816 memmove(destp, srcp, strlen(srcp) + 1);
826 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
832 else if (*srcp == ')')
838 while (g_ascii_isspace(*srcp)) srcp++;
839 memmove(destp, srcp, strlen(srcp) + 1);
843 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
845 gboolean in_quote = FALSE;
848 if (*str == c && !in_quote)
850 if (*str == quote_chr)
858 void extract_address(gchar *str)
860 eliminate_address_comment(str);
861 if (strchr_with_skip_quote(str, '"', '<'))
862 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
866 void extract_list_id_str(gchar *str)
868 if (strchr_with_skip_quote(str, '"', '<'))
869 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
873 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
878 if (!str) return addr_list;
880 Xstrdup_a(work, str, return addr_list);
883 eliminate_address_comment(work);
886 while (workp && *workp) {
889 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
895 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
896 extract_parenthesis_with_skip_quote
897 (workp, '"', '<', '>');
901 addr_list = g_slist_append(addr_list, g_strdup(workp));
909 GSList *address_list_append(GSList *addr_list, const gchar *str)
911 return address_list_append_real(addr_list, str, TRUE);
914 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
916 return address_list_append_real(addr_list, str, FALSE);
919 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
923 if (!str) return msgid_list;
926 while (strp && *strp) {
927 const gchar *start, *end;
930 if ((start = strchr(strp, '<')) != NULL) {
931 end = strchr(start + 1, '>');
936 msgid = g_strndup(start + 1, end - start - 1);
939 msgid_list = g_slist_prepend(msgid_list, msgid);
949 GSList *references_list_append(GSList *msgid_list, const gchar *str)
953 list = references_list_prepend(NULL, str);
954 list = g_slist_reverse(list);
955 msgid_list = g_slist_concat(msgid_list, list);
960 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
965 if (!str) return group_list;
967 Xstrdup_a(work, str, return group_list);
971 while (workp && *workp) {
974 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
982 group_list = g_slist_append(group_list,
991 GList *add_history(GList *list, const gchar *str)
996 cm_return_val_if_fail(str != NULL, list);
998 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1001 list = g_list_remove(list, old->data);
1003 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1006 last = g_list_last(list);
1008 oldstr = last->data;
1009 list = g_list_remove(list, last->data);
1014 list = g_list_prepend(list, g_strdup(str));
1019 void remove_return(gchar *str)
1021 register gchar *p = str;
1024 if (*p == '\n' || *p == '\r')
1025 memmove(p, p + 1, strlen(p));
1031 void remove_space(gchar *str)
1033 register gchar *p = str;
1038 while (g_ascii_isspace(*(p + spc)))
1041 memmove(p, p + spc, strlen(p + spc) + 1);
1047 void unfold_line(gchar *str)
1049 register gchar *p = str;
1053 if (*p == '\n' || *p == '\r') {
1056 while (g_ascii_isspace(*(p + spc)))
1059 memmove(p, p + spc, strlen(p + spc) + 1);
1065 void subst_char(gchar *str, gchar orig, gchar subst)
1067 register gchar *p = str;
1076 void subst_chars(gchar *str, gchar *orig, gchar subst)
1078 register gchar *p = str;
1081 if (strchr(orig, *p) != NULL)
1087 void subst_for_filename(gchar *str)
1092 subst_chars(str, "\t\r\n\\/*:", '_');
1094 subst_chars(str, "\t\r\n\\/*", '_');
1098 void subst_for_shellsafe_filename(gchar *str)
1102 subst_for_filename(str);
1103 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1106 gboolean is_ascii_str(const gchar *str)
1108 const guchar *p = (const guchar *)str;
1110 while (*p != '\0') {
1111 if (*p != '\t' && *p != ' ' &&
1112 *p != '\r' && *p != '\n' &&
1113 (*p < 32 || *p >= 127))
1121 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1123 gchar * position = NULL;
1124 gchar * tmp_pos = NULL;
1127 if (quote_chars == NULL)
1130 for (i = 0; i < strlen(quote_chars); i++) {
1131 tmp_pos = strrchr (str, quote_chars[i]);
1133 || (tmp_pos != NULL && position <= tmp_pos) )
1139 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1141 const gchar *first_pos;
1142 const gchar *last_pos;
1143 const gchar *p = str;
1144 gint quote_level = -1;
1146 /* speed up line processing by only searching to the last '>' */
1147 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1148 /* skip a line if it contains a '<' before the initial '>' */
1149 if (memchr(str, '<', first_pos - str) != NULL)
1151 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1155 while (p <= last_pos) {
1156 while (p < last_pos) {
1157 if (g_ascii_isspace(*p))
1163 if (strchr(quote_chars, *p))
1165 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1166 /* any characters are allowed except '-','<' and space */
1167 while (*p != '-' && *p != '<'
1168 && !strchr(quote_chars, *p)
1169 && !g_ascii_isspace(*p)
1172 if (strchr(quote_chars, *p))
1184 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1186 const gchar *p = str, *q;
1187 gint cur_line = 0, len;
1189 while ((q = strchr(p, '\n')) != NULL) {
1191 if (len > max_chars) {
1201 if (len > max_chars) {
1210 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1212 gchar * position = NULL;
1213 gchar * tmp_pos = NULL;
1216 if (quote_chars == NULL)
1219 for (i = 0; i < strlen(quote_chars); i++) {
1220 tmp_pos = strchr (str, quote_chars[i]);
1222 || (tmp_pos != NULL && position >= tmp_pos) )
1228 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1230 register guint haystack_len, needle_len;
1231 gboolean in_squote = FALSE, in_dquote = FALSE;
1233 haystack_len = strlen(haystack);
1234 needle_len = strlen(needle);
1236 if (haystack_len < needle_len || needle_len == 0)
1239 while (haystack_len >= needle_len) {
1240 if (!in_squote && !in_dquote &&
1241 !strncmp(haystack, needle, needle_len))
1242 return (gchar *)haystack;
1244 /* 'foo"bar"' -> foo"bar"
1245 "foo'bar'" -> foo'bar' */
1246 if (*haystack == '\'') {
1249 else if (!in_dquote)
1251 } else if (*haystack == '\"') {
1254 else if (!in_squote)
1265 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1268 GSList *string_list = NULL, *slist;
1269 gchar **str_array, *s, *new_str;
1270 guint i, n = 1, len;
1272 cm_return_val_if_fail(str != NULL, NULL);
1273 cm_return_val_if_fail(delim != NULL, NULL);
1276 max_tokens = G_MAXINT;
1278 s = strstr_with_skip_quote(str, delim);
1280 guint delimiter_len = strlen(delim);
1284 new_str = g_strndup(str, len);
1286 if (new_str[0] == '\'' || new_str[0] == '\"') {
1287 if (new_str[len - 1] == new_str[0]) {
1288 new_str[len - 1] = '\0';
1289 memmove(new_str, new_str + 1, len - 1);
1292 string_list = g_slist_prepend(string_list, new_str);
1294 str = s + delimiter_len;
1295 s = strstr_with_skip_quote(str, delim);
1296 } while (--max_tokens && s);
1300 new_str = g_strdup(str);
1301 if (new_str[0] == '\'' || new_str[0] == '\"') {
1303 if (new_str[len - 1] == new_str[0]) {
1304 new_str[len - 1] = '\0';
1305 memmove(new_str, new_str + 1, len - 1);
1308 string_list = g_slist_prepend(string_list, new_str);
1312 str_array = g_new(gchar*, n);
1316 str_array[i--] = NULL;
1317 for (slist = string_list; slist; slist = slist->next)
1318 str_array[i--] = slist->data;
1320 g_slist_free(string_list);
1325 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1327 gchar *abbrev_group;
1329 const gchar *p = group;
1332 cm_return_val_if_fail(group != NULL, NULL);
1334 last = group + strlen(group);
1335 abbrev_group = ap = g_malloc(strlen(group) + 1);
1340 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1342 while (*p != '.') p++;
1345 return abbrev_group;
1350 return abbrev_group;
1353 gchar *trim_string(const gchar *str, gint len)
1355 const gchar *p = str;
1360 if (!str) return NULL;
1361 if (strlen(str) <= len)
1362 return g_strdup(str);
1363 if (g_utf8_validate(str, -1, NULL) == FALSE)
1364 return g_strdup(str);
1366 while (*p != '\0') {
1367 mb_len = g_utf8_skip[*(guchar *)p];
1370 else if (new_len + mb_len > len)
1377 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1378 return g_strconcat(new_str, "...", NULL);
1381 GList *uri_list_extract_filenames(const gchar *uri_list)
1383 GList *result = NULL;
1385 gchar *escaped_utf8uri;
1391 while (g_ascii_isspace(*p)) p++;
1392 if (!strncmp(p, "file:", 5)) {
1395 while (*q && *q != '\n' && *q != '\r') q++;
1398 gchar *file, *locale_file = NULL;
1400 while (q > p && g_ascii_isspace(*q))
1402 Xalloca(escaped_utf8uri, q - p + 2,
1404 Xalloca(file, q - p + 2,
1407 strncpy(escaped_utf8uri, p, q - p + 1);
1408 escaped_utf8uri[q - p + 1] = '\0';
1409 decode_uri(file, escaped_utf8uri);
1411 * g_filename_from_uri() rejects escaped/locale encoded uri
1412 * string which come from Nautilus.
1415 if (g_utf8_validate(file, -1, NULL))
1417 = conv_codeset_strdup(
1420 conv_get_locale_charset_str());
1422 locale_file = g_strdup(file + 5);
1424 locale_file = g_filename_from_uri(file, NULL, NULL);
1426 result = g_list_append(result, locale_file);
1430 p = strchr(p, '\n');
1437 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1440 static gint axtoi(const gchar *hexstr)
1442 gint hi, lo, result;
1445 if ('0' <= hi && hi <= '9') {
1448 if ('a' <= hi && hi <= 'f') {
1451 if ('A' <= hi && hi <= 'F') {
1456 if ('0' <= lo && lo <= '9') {
1459 if ('a' <= lo && lo <= 'f') {
1462 if ('A' <= lo && lo <= 'F') {
1465 result = lo + (16 * hi);
1469 gboolean is_uri_string(const gchar *str)
1471 while (str && *str && g_ascii_isspace(*str))
1473 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1474 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1475 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1476 g_ascii_strncasecmp(str, "www.", 4) == 0);
1479 gchar *get_uri_path(const gchar *uri)
1481 while (uri && *uri && g_ascii_isspace(*uri))
1483 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1484 return (gchar *)(uri + 7);
1485 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1486 return (gchar *)(uri + 8);
1487 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1488 return (gchar *)(uri + 6);
1490 return (gchar *)uri;
1493 gint get_uri_len(const gchar *str)
1497 if (is_uri_string(str)) {
1498 for (p = str; *p != '\0'; p++) {
1499 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1508 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1509 * plusses, and escape characters are used)
1511 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1513 gchar *dec = decoded_uri;
1514 const gchar *enc = encoded_uri;
1519 if (isxdigit((guchar)enc[0]) &&
1520 isxdigit((guchar)enc[1])) {
1526 if (with_plus && *enc == '+')
1538 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1540 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1543 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1545 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1546 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1550 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1551 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1555 const gchar *forbidden_uris[] = { ".gnupg/",
1561 gint num_attach = 0;
1562 gchar **my_att = NULL;
1564 Xstrdup_a(tmp_mailto, mailto, return -1);
1566 if (!strncmp(tmp_mailto, "mailto:", 7))
1569 p = strchr(tmp_mailto, '?');
1576 *to = decode_uri_gdup(tmp_mailto);
1578 my_att = g_malloc(sizeof(char *));
1582 gchar *field, *value;
1599 if (*value == '\0') continue;
1601 if (from && !g_ascii_strcasecmp(field, "from")) {
1603 *from = decode_uri_gdup(value);
1605 gchar *tmp = decode_uri_gdup(value);
1606 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1610 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1612 *cc = decode_uri_gdup(value);
1614 gchar *tmp = decode_uri_gdup(value);
1615 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1619 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1621 *bcc = decode_uri_gdup(value);
1623 gchar *tmp = decode_uri_gdup(value);
1624 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1628 } else if (subject && !*subject &&
1629 !g_ascii_strcasecmp(field, "subject")) {
1630 *subject = decode_uri_gdup(value);
1631 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1632 *body = decode_uri_gdup(value);
1633 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1634 gchar *tmp = decode_uri_gdup(value);
1635 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1636 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1640 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1642 gchar *tmp = decode_uri_gdup(value);
1643 for (; forbidden_uris[i]; i++) {
1644 if (strstr(tmp, forbidden_uris[i])) {
1645 g_print("Refusing to attach '%s', potential private data leak\n",
1653 /* attach is correct */
1655 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1656 my_att[num_attach-1] = tmp;
1657 my_att[num_attach] = NULL;
1659 } else if (inreplyto && !*inreplyto &&
1660 !g_ascii_strcasecmp(field, "in-reply-to")) {
1661 *inreplyto = decode_uri_gdup(value);
1672 #include <windows.h>
1673 #ifndef CSIDL_APPDATA
1674 #define CSIDL_APPDATA 0x001a
1676 #ifndef CSIDL_LOCAL_APPDATA
1677 #define CSIDL_LOCAL_APPDATA 0x001c
1679 #ifndef CSIDL_FLAG_CREATE
1680 #define CSIDL_FLAG_CREATE 0x8000
1682 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1686 w32_strerror (int w32_errno)
1688 static char strerr[256];
1689 int ec = (int)GetLastError ();
1693 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1694 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1695 strerr, DIM (strerr)-1, NULL);
1699 static __inline__ void *
1700 dlopen (const char * name, int flag)
1702 void * hd = LoadLibrary (name);
1706 static __inline__ void *
1707 dlsym (void * hd, const char * sym)
1711 void * fnc = GetProcAddress (hd, sym);
1720 static __inline__ const char *
1723 return w32_strerror (0);
1727 static __inline__ int
1739 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1741 static int initialized;
1742 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1746 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1752 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1754 handle = dlopen (dllnames[i], RTLD_LAZY);
1757 func = dlsym (handle, "SHGetFolderPathW");
1768 return func (a,b,c,d,e);
1773 /* Returns a static string with the directroy from which the module
1774 has been loaded. Returns an empty string on error. */
1775 static char *w32_get_module_dir(void)
1777 static char *moddir;
1780 char name[MAX_PATH+10];
1783 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1786 p = strrchr (name, '\\');
1792 moddir = g_strdup (name);
1796 #endif /* G_OS_WIN32 */
1798 /* Return a static string with the locale dir. */
1799 const gchar *get_locale_dir(void)
1801 static gchar *loc_dir;
1805 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1806 "\\share\\locale", NULL);
1809 loc_dir = LOCALEDIR;
1815 const gchar *get_home_dir(void)
1818 static char home_dir_utf16[MAX_PATH] = "";
1819 static gchar *home_dir_utf8 = NULL;
1820 if (home_dir_utf16[0] == '\0') {
1821 if (w32_shgetfolderpath
1822 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1823 NULL, 0, home_dir_utf16) < 0)
1824 strcpy (home_dir_utf16, "C:\\Sylpheed");
1825 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1827 return home_dir_utf8;
1829 static const gchar *homeenv = NULL;
1834 if (!homeenv && g_getenv("HOME") != NULL)
1835 homeenv = g_strdup(g_getenv("HOME"));
1837 homeenv = g_get_home_dir();
1843 static gchar *claws_rc_dir = NULL;
1844 static gboolean rc_dir_alt = FALSE;
1845 const gchar *get_rc_dir(void)
1849 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1852 return claws_rc_dir;
1855 void set_rc_dir(const gchar *dir)
1857 if (claws_rc_dir != NULL) {
1858 g_print("Error: rc_dir already set\n");
1861 if (g_path_is_absolute(dir))
1862 claws_rc_dir = g_strdup(dir);
1864 claws_rc_dir = g_strconcat(g_get_current_dir(),
1865 G_DIR_SEPARATOR_S, dir, NULL);
1867 debug_print("set rc_dir to %s\n", claws_rc_dir);
1868 if (!is_dir_exist(claws_rc_dir)) {
1869 if (make_dir_hier(claws_rc_dir) != 0) {
1870 g_print("Error: can't create %s\n",
1877 gboolean rc_dir_is_alt(void) {
1881 const gchar *get_mail_base_dir(void)
1883 return get_home_dir();
1887 const gchar *prefs_common_get_data_root(void);
1888 gchar *last_data_root = NULL;
1891 const gchar *get_news_cache_dir(void)
1893 static gchar *news_cache_dir = NULL;
1895 const gchar *data_root = prefs_common_get_data_root();
1896 if (strcmp2(data_root, last_data_root)) {
1897 g_free(news_cache_dir);
1898 news_cache_dir = NULL;
1901 if (!news_cache_dir)
1903 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904 NEWS_CACHE_DIR, NULL);
1908 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1909 "Claws", G_DIR_SEPARATOR_S,
1910 g_get_user_name(), G_DIR_SEPARATOR_S,
1911 NEWS_CACHE_DIR, NULL);
1912 g_free(last_data_root);
1913 last_data_root = g_strdup(last_data_root);
1915 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1916 NEWS_CACHE_DIR, NULL);
1917 g_free(last_data_root);
1918 last_data_root = NULL;
1922 return news_cache_dir;
1925 const gchar *get_imap_cache_dir(void)
1927 static gchar *imap_cache_dir = NULL;
1929 const gchar *data_root = prefs_common_get_data_root();
1930 if (strcmp2(data_root, last_data_root)) {
1931 g_free(imap_cache_dir);
1932 imap_cache_dir = NULL;
1936 if (!imap_cache_dir)
1938 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1939 IMAP_CACHE_DIR, NULL);
1943 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1944 "Claws", G_DIR_SEPARATOR_S,
1945 g_get_user_name(), G_DIR_SEPARATOR_S,
1946 IMAP_CACHE_DIR, NULL);
1947 g_free(last_data_root);
1948 last_data_root = g_strdup(last_data_root);
1950 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1951 IMAP_CACHE_DIR, NULL);
1952 g_free(last_data_root);
1953 last_data_root = NULL;
1958 return imap_cache_dir;
1961 const gchar *get_mime_tmp_dir(void)
1963 static gchar *mime_tmp_dir = NULL;
1966 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 MIME_TMP_DIR, NULL);
1969 return mime_tmp_dir;
1972 const gchar *get_template_dir(void)
1974 static gchar *template_dir = NULL;
1977 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1978 TEMPLATE_DIR, NULL);
1980 return template_dir;
1984 const gchar *get_cert_file(void)
1986 const gchar *cert_file = NULL;
1988 cert_file = g_strconcat(w32_get_module_dir(),
1989 "\\share\\claws-mail\\",
1990 "ca-certificates.crt",
1996 /* Return the filepath of the claws-mail.desktop file */
1997 const gchar *get_desktop_file(void)
1999 #ifdef DESKTOPFILEPATH
2000 return DESKTOPFILEPATH;
2006 /* Return the default directory for Plugins. */
2007 const gchar *get_plugin_dir(void)
2010 static gchar *plugin_dir = NULL;
2013 plugin_dir = g_strconcat(w32_get_module_dir(),
2014 "\\lib\\claws-mail\\plugins\\",
2018 if (is_dir_exist(PLUGINDIR))
2021 static gchar *plugin_dir = NULL;
2023 plugin_dir = g_strconcat(get_rc_dir(),
2024 G_DIR_SEPARATOR_S, "plugins",
2025 G_DIR_SEPARATOR_S, NULL);
2033 /* Return the default directory for Themes. */
2034 const gchar *get_themes_dir(void)
2036 static gchar *themes_dir = NULL;
2039 themes_dir = g_strconcat(w32_get_module_dir(),
2040 "\\share\\claws-mail\\themes",
2046 const gchar *get_tmp_dir(void)
2048 static gchar *tmp_dir = NULL;
2051 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2057 gchar *get_tmp_file(void)
2060 static guint32 id = 0;
2062 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2063 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2068 const gchar *get_domain_name(void)
2071 static gchar *domain_name = NULL;
2077 if (gethostname(hostname, sizeof(hostname)) != 0) {
2078 perror("gethostname");
2079 domain_name = "unknown";
2081 hostname[sizeof(hostname) - 1] = '\0';
2082 if ((hp = my_gethostbyname(hostname)) == NULL) {
2083 perror("gethostbyname");
2084 domain_name = g_strdup(hostname);
2086 domain_name = g_strdup(hp->h_name);
2089 debug_print("domain name = %s\n", domain_name);
2098 off_t get_file_size(const gchar *file)
2102 if (g_stat(file, &s) < 0) {
2103 FILE_OP_ERROR(file, "stat");
2110 time_t get_file_mtime(const gchar *file)
2114 if (g_stat(file, &s) < 0) {
2115 FILE_OP_ERROR(file, "stat");
2122 off_t get_file_size_as_crlf(const gchar *file)
2126 gchar buf[BUFFSIZE];
2128 if ((fp = g_fopen(file, "rb")) == NULL) {
2129 FILE_OP_ERROR(file, "g_fopen");
2133 while (fgets(buf, sizeof(buf), fp) != NULL) {
2135 size += strlen(buf) + 2;
2139 FILE_OP_ERROR(file, "fgets");
2148 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2155 if (g_stat(file, &s) < 0) {
2156 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2160 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2167 /* Test on whether FILE is a relative file name. This is
2168 * straightforward for Unix but more complex for Windows. */
2169 gboolean is_relative_filename(const gchar *file)
2174 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2175 return FALSE; /* Prefixed with a hostname - this can't
2176 * be a relative name. */
2178 if ( ((*file >= 'a' && *file <= 'z')
2179 || (*file >= 'A' && *file <= 'Z'))
2181 file += 2; /* Skip drive letter. */
2183 return !(*file == '\\' || *file == '/');
2185 return !(*file == G_DIR_SEPARATOR);
2190 gboolean is_dir_exist(const gchar *dir)
2195 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2198 gboolean is_file_entry_exist(const gchar *file)
2203 return g_file_test(file, G_FILE_TEST_EXISTS);
2206 gboolean dirent_is_regular_file(struct dirent *d)
2208 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2209 if (d->d_type == DT_REG)
2211 else if (d->d_type != DT_UNKNOWN)
2215 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2218 gint change_dir(const gchar *dir)
2220 gchar *prevdir = NULL;
2223 prevdir = g_get_current_dir();
2225 if (g_chdir(dir) < 0) {
2226 FILE_OP_ERROR(dir, "chdir");
2227 if (debug_mode) g_free(prevdir);
2229 } else if (debug_mode) {
2232 cwd = g_get_current_dir();
2233 if (strcmp(prevdir, cwd) != 0)
2234 g_print("current dir: %s\n", cwd);
2242 gint make_dir(const gchar *dir)
2244 if (g_mkdir(dir, S_IRWXU) < 0) {
2245 FILE_OP_ERROR(dir, "mkdir");
2248 if (g_chmod(dir, S_IRWXU) < 0)
2249 FILE_OP_ERROR(dir, "chmod");
2254 gint make_dir_hier(const gchar *dir)
2259 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2260 parent_dir = g_strndup(dir, p - dir);
2261 if (*parent_dir != '\0') {
2262 if (!is_dir_exist(parent_dir)) {
2263 if (make_dir(parent_dir) < 0) {
2272 if (!is_dir_exist(dir)) {
2273 if (make_dir(dir) < 0)
2280 gint remove_all_files(const gchar *dir)
2283 const gchar *dir_name;
2286 prev_dir = g_get_current_dir();
2288 if (g_chdir(dir) < 0) {
2289 FILE_OP_ERROR(dir, "chdir");
2294 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2295 g_warning("failed to open directory: %s\n", dir);
2300 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2301 if (claws_unlink(dir_name) < 0)
2302 FILE_OP_ERROR(dir_name, "unlink");
2307 if (g_chdir(prev_dir) < 0) {
2308 FILE_OP_ERROR(prev_dir, "chdir");
2318 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2321 const gchar *dir_name;
2325 prev_dir = g_get_current_dir();
2327 if (g_chdir(dir) < 0) {
2328 FILE_OP_ERROR(dir, "chdir");
2333 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2334 g_warning("failed to open directory: %s\n", dir);
2339 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2340 file_no = to_number(dir_name);
2341 if (file_no > 0 && first <= file_no && file_no <= last) {
2342 if (is_dir_exist(dir_name))
2344 if (claws_unlink(dir_name) < 0)
2345 FILE_OP_ERROR(dir_name, "unlink");
2351 if (g_chdir(prev_dir) < 0) {
2352 FILE_OP_ERROR(prev_dir, "chdir");
2362 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2365 const gchar *dir_name;
2369 prev_dir = g_get_current_dir();
2371 if (g_chdir(dir) < 0) {
2372 FILE_OP_ERROR(dir, "chdir");
2377 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2378 FILE_OP_ERROR(dir, "opendir");
2383 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2384 file_no = to_number(dir_name);
2385 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2386 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2387 if (is_dir_exist(dir_name))
2389 if (claws_unlink(dir_name) < 0)
2390 FILE_OP_ERROR(dir_name, "unlink");
2396 if (g_chdir(prev_dir) < 0) {
2397 FILE_OP_ERROR(prev_dir, "chdir");
2407 gint remove_all_numbered_files(const gchar *dir)
2409 return remove_numbered_files(dir, 0, UINT_MAX);
2412 gint remove_dir_recursive(const gchar *dir)
2416 const gchar *dir_name;
2419 if (g_stat(dir, &s) < 0) {
2420 FILE_OP_ERROR(dir, "stat");
2421 if (ENOENT == errno) return 0;
2425 if (!S_ISDIR(s.st_mode)) {
2426 if (claws_unlink(dir) < 0) {
2427 FILE_OP_ERROR(dir, "unlink");
2434 prev_dir = g_get_current_dir();
2435 /* g_print("prev_dir = %s\n", prev_dir); */
2437 if (!path_cmp(prev_dir, dir)) {
2439 if (g_chdir("..") < 0) {
2440 FILE_OP_ERROR(dir, "chdir");
2443 prev_dir = g_get_current_dir();
2446 if (g_chdir(dir) < 0) {
2447 FILE_OP_ERROR(dir, "chdir");
2452 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2453 g_warning("failed to open directory: %s\n", dir);
2459 /* remove all files in the directory */
2460 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2461 /* g_print("removing %s\n", dir_name); */
2463 if (is_dir_exist(dir_name)) {
2464 if (remove_dir_recursive(dir_name) < 0) {
2465 g_warning("can't remove directory\n");
2469 if (claws_unlink(dir_name) < 0)
2470 FILE_OP_ERROR(dir_name, "unlink");
2476 if (g_chdir(prev_dir) < 0) {
2477 FILE_OP_ERROR(prev_dir, "chdir");
2484 if (g_rmdir(dir) < 0) {
2485 FILE_OP_ERROR(dir, "rmdir");
2492 gint rename_force(const gchar *oldpath, const gchar *newpath)
2495 if (!is_file_entry_exist(oldpath)) {
2499 if (is_file_exist(newpath)) {
2500 if (claws_unlink(newpath) < 0)
2501 FILE_OP_ERROR(newpath, "unlink");
2504 return g_rename(oldpath, newpath);
2508 * Append src file body to the tail of dest file.
2509 * Now keep_backup has no effects.
2511 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2513 FILE *src_fp, *dest_fp;
2517 gboolean err = FALSE;
2519 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2520 FILE_OP_ERROR(src, "g_fopen");
2524 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2525 FILE_OP_ERROR(dest, "g_fopen");
2530 if (change_file_mode_rw(dest_fp, dest) < 0) {
2531 FILE_OP_ERROR(dest, "chmod");
2532 g_warning("can't change file mode\n");
2535 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2536 if (n_read < sizeof(buf) && ferror(src_fp))
2538 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2539 g_warning("writing to %s failed.\n", dest);
2547 if (ferror(src_fp)) {
2548 FILE_OP_ERROR(src, "fread");
2552 if (fclose(dest_fp) == EOF) {
2553 FILE_OP_ERROR(dest, "fclose");
2565 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2567 FILE *src_fp, *dest_fp;
2570 gchar *dest_bak = NULL;
2571 gboolean err = FALSE;
2573 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2574 FILE_OP_ERROR(src, "g_fopen");
2577 if (is_file_exist(dest)) {
2578 dest_bak = g_strconcat(dest, ".bak", NULL);
2579 if (rename_force(dest, dest_bak) < 0) {
2580 FILE_OP_ERROR(dest, "rename");
2587 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2588 FILE_OP_ERROR(dest, "g_fopen");
2591 if (rename_force(dest_bak, dest) < 0)
2592 FILE_OP_ERROR(dest_bak, "rename");
2598 if (change_file_mode_rw(dest_fp, dest) < 0) {
2599 FILE_OP_ERROR(dest, "chmod");
2600 g_warning("can't change file mode\n");
2603 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2604 if (n_read < sizeof(buf) && ferror(src_fp))
2606 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2607 g_warning("writing to %s failed.\n", dest);
2612 if (rename_force(dest_bak, dest) < 0)
2613 FILE_OP_ERROR(dest_bak, "rename");
2620 if (ferror(src_fp)) {
2621 FILE_OP_ERROR(src, "fread");
2625 if (fclose(dest_fp) == EOF) {
2626 FILE_OP_ERROR(dest, "fclose");
2633 if (rename_force(dest_bak, dest) < 0)
2634 FILE_OP_ERROR(dest_bak, "rename");
2640 if (keep_backup == FALSE && dest_bak)
2641 claws_unlink(dest_bak);
2648 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2650 if (overwrite == FALSE && is_file_exist(dest)) {
2651 g_warning("move_file(): file %s already exists.", dest);
2655 if (rename_force(src, dest) == 0) return 0;
2657 if (EXDEV != errno) {
2658 FILE_OP_ERROR(src, "rename");
2662 if (copy_file(src, dest, FALSE) < 0) return -1;
2669 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2672 gint bytes_left, to_read;
2675 if (fseek(fp, offset, SEEK_SET) < 0) {
2680 bytes_left = length;
2681 to_read = MIN(bytes_left, sizeof(buf));
2683 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2684 if (n_read < to_read && ferror(fp))
2686 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2689 bytes_left -= n_read;
2690 if (bytes_left == 0)
2692 to_read = MIN(bytes_left, sizeof(buf));
2703 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2706 gboolean err = FALSE;
2708 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2709 FILE_OP_ERROR(dest, "g_fopen");
2713 if (change_file_mode_rw(dest_fp, dest) < 0) {
2714 FILE_OP_ERROR(dest, "chmod");
2715 g_warning("can't change file mode\n");
2718 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2721 if (!err && fclose(dest_fp) == EOF) {
2722 FILE_OP_ERROR(dest, "fclose");
2727 g_warning("writing to %s failed.\n", dest);
2735 /* convert line endings into CRLF. If the last line doesn't end with
2736 * linebreak, add it.
2738 gchar *canonicalize_str(const gchar *str)
2744 for (p = str; *p != '\0'; ++p) {
2751 if (p == str || *(p - 1) != '\n')
2754 out = outp = g_malloc(new_len + 1);
2755 for (p = str; *p != '\0'; ++p) {
2762 if (p == str || *(p - 1) != '\n') {
2771 gint canonicalize_file(const gchar *src, const gchar *dest)
2773 FILE *src_fp, *dest_fp;
2774 gchar buf[BUFFSIZE];
2776 gboolean err = FALSE;
2777 gboolean last_linebreak = FALSE;
2779 if (src == NULL || dest == NULL)
2782 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2783 FILE_OP_ERROR(src, "g_fopen");
2787 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2788 FILE_OP_ERROR(dest, "g_fopen");
2793 if (change_file_mode_rw(dest_fp, dest) < 0) {
2794 FILE_OP_ERROR(dest, "chmod");
2795 g_warning("can't change file mode\n");
2798 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2802 if (len == 0) break;
2803 last_linebreak = FALSE;
2805 if (buf[len - 1] != '\n') {
2806 last_linebreak = TRUE;
2807 r = fputs(buf, dest_fp);
2808 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2809 r = fputs(buf, dest_fp);
2812 r = fwrite(buf, 1, len - 1, dest_fp);
2817 r = fputs("\r\n", dest_fp);
2821 g_warning("writing to %s failed.\n", dest);
2829 if (last_linebreak == TRUE) {
2830 if (fputs("\r\n", dest_fp) == EOF)
2834 if (ferror(src_fp)) {
2835 FILE_OP_ERROR(src, "fgets");
2839 if (fclose(dest_fp) == EOF) {
2840 FILE_OP_ERROR(dest, "fclose");
2852 gint canonicalize_file_replace(const gchar *file)
2856 tmp_file = get_tmp_file();
2858 if (canonicalize_file(file, tmp_file) < 0) {
2863 if (move_file(tmp_file, file, TRUE) < 0) {
2864 g_warning("can't replace %s .\n", file);
2865 claws_unlink(tmp_file);
2874 gchar *normalize_newlines(const gchar *str)
2876 const gchar *p = str;
2879 out = outp = g_malloc(strlen(str) + 1);
2880 for (p = str; *p != '\0'; ++p) {
2882 if (*(p + 1) != '\n')
2893 gchar *get_outgoing_rfc2822_str(FILE *fp)
2895 gchar buf[BUFFSIZE];
2899 str = g_string_new(NULL);
2901 /* output header part */
2902 while (fgets(buf, sizeof(buf), fp) != NULL) {
2904 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2911 else if (next != ' ' && next != '\t') {
2915 if (fgets(buf, sizeof(buf), fp) == NULL)
2919 g_string_append(str, buf);
2920 g_string_append(str, "\r\n");
2926 /* output body part */
2927 while (fgets(buf, sizeof(buf), fp) != NULL) {
2930 g_string_append_c(str, '.');
2931 g_string_append(str, buf);
2932 g_string_append(str, "\r\n");
2936 g_string_free(str, FALSE);
2942 * Create a new boundary in a way that it is very unlikely that this
2943 * will occur in the following text. It would be easy to ensure
2944 * uniqueness if everything is either quoted-printable or base64
2945 * encoded (note that conversion is allowed), but because MIME bodies
2946 * may be nested, it may happen that the same boundary has already
2949 * boundary := 0*69<bchars> bcharsnospace
2950 * bchars := bcharsnospace / " "
2951 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2952 * "+" / "_" / "," / "-" / "." /
2953 * "/" / ":" / "=" / "?"
2955 * some special characters removed because of buggy MTAs
2958 gchar *generate_mime_boundary(const gchar *prefix)
2960 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2961 "abcdefghijklmnopqrstuvwxyz"
2966 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2967 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2970 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2974 gint change_file_mode_rw(FILE *fp, const gchar *file)
2977 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2979 return g_chmod(file, S_IRUSR|S_IWUSR);
2983 FILE *my_tmpfile(void)
2985 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2986 const gchar suffix[] = ".XXXXXX";
2987 const gchar *tmpdir;
2989 const gchar *progname;
2998 tmpdir = get_tmp_dir();
2999 tmplen = strlen(tmpdir);
3000 progname = g_get_prgname();
3001 if (progname == NULL)
3002 progname = "claws-mail";
3003 proglen = strlen(progname);
3004 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3007 memcpy(fname, tmpdir, tmplen);
3008 fname[tmplen] = G_DIR_SEPARATOR;
3009 memcpy(fname + tmplen + 1, progname, proglen);
3010 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3012 fd = mkstemp(fname);
3017 claws_unlink(fname);
3019 /* verify that we can write in the file after unlinking */
3020 if (write(fd, buf, 1) < 0) {
3027 fp = fdopen(fd, "w+b");
3035 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3040 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3044 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3045 dir, G_DIR_SEPARATOR);
3046 fd = mkstemp_name(template, filename);
3049 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3050 fd = mkstemp(*filename);
3052 return fdopen(fd, "w+");
3055 FILE *str_open_as_stream(const gchar *str)
3060 cm_return_val_if_fail(str != NULL, NULL);
3064 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3069 if (len == 0) return fp;
3071 if (fwrite(str, 1, len, fp) != len) {
3072 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3081 gint str_write_to_file(const gchar *str, const gchar *file)
3086 cm_return_val_if_fail(str != NULL, -1);
3087 cm_return_val_if_fail(file != NULL, -1);
3089 if ((fp = g_fopen(file, "wb")) == NULL) {
3090 FILE_OP_ERROR(file, "g_fopen");
3100 if (fwrite(str, 1, len, fp) != len) {
3101 FILE_OP_ERROR(file, "fwrite");
3107 if (fclose(fp) == EOF) {
3108 FILE_OP_ERROR(file, "fclose");
3116 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3123 cm_return_val_if_fail(fp != NULL, NULL);
3125 array = g_byte_array_new();
3127 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3128 if (n_read < sizeof(buf) && ferror(fp))
3130 g_byte_array_append(array, buf, n_read);
3134 FILE_OP_ERROR("file stream", "fread");
3135 g_byte_array_free(array, TRUE);
3140 g_byte_array_append(array, buf, 1);
3141 str = (gchar *)array->data;
3142 g_byte_array_free(array, FALSE);
3144 if (recode && !g_utf8_validate(str, -1, NULL)) {
3145 const gchar *src_codeset, *dest_codeset;
3147 src_codeset = conv_get_locale_charset_str();
3148 dest_codeset = CS_UTF_8;
3149 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3157 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3164 struct timeval timeout = {1, 0};
3169 cm_return_val_if_fail(file != NULL, NULL);
3171 if (g_stat(file, &s) != 0) {
3172 FILE_OP_ERROR(file, "stat");
3175 if (S_ISDIR(s.st_mode)) {
3176 g_warning("%s: is a directory\n", file);
3181 fp = g_fopen (file, "rb");
3183 FILE_OP_ERROR(file, "open");
3187 /* test whether the file is readable without blocking */
3188 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3190 FILE_OP_ERROR(file, "open");
3197 /* allow for one second */
3198 err = select(fd+1, &fds, NULL, NULL, &timeout);
3199 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3201 FILE_OP_ERROR(file, "select");
3203 g_warning("%s: doesn't seem readable\n", file);
3209 /* Now clear O_NONBLOCK */
3210 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3211 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3215 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3216 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3221 /* get the FILE pointer */
3222 fp = fdopen(fd, "rb");
3225 FILE_OP_ERROR(file, "fdopen");
3226 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3231 str = file_read_stream_to_str_full(fp, recode);
3238 gchar *file_read_to_str(const gchar *file)
3240 return file_read_to_str_full(file, TRUE);
3242 gchar *file_read_stream_to_str(FILE *fp)
3244 return file_read_stream_to_str_full(fp, TRUE);
3247 gchar *file_read_to_str_no_recode(const gchar *file)
3249 return file_read_to_str_full(file, FALSE);
3251 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3253 return file_read_stream_to_str_full(fp, FALSE);
3256 char *fgets_crlf(char *buf, int size, FILE *stream)
3258 gboolean is_cr = FALSE;
3259 gboolean last_was_cr = FALSE;
3264 while (--size > 0 && (c = getc(stream)) != EOF)
3267 is_cr = (c == '\r');
3277 last_was_cr = is_cr;
3279 if (c == EOF && cs == buf)
3287 static gint execute_async(gchar *const argv[])
3289 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3291 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3292 NULL, NULL, NULL, FALSE) == FALSE) {
3293 g_warning("Couldn't execute command: %s\n", argv[0]);
3300 static gint execute_sync(gchar *const argv[])
3304 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3307 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3308 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3309 g_warning("Couldn't execute command: %s\n", argv[0]);
3313 if (WIFEXITED(status))
3314 return WEXITSTATUS(status);
3318 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3319 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3320 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3321 g_warning("Couldn't execute command: %s\n", argv[0]);
3329 gint execute_command_line(const gchar *cmdline, gboolean async)
3334 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3336 argv = strsplit_with_quote(cmdline, " ", 0);
3339 ret = execute_async(argv);
3341 ret = execute_sync(argv);
3348 gchar *get_command_output(const gchar *cmdline)
3350 gchar *child_stdout;
3353 cm_return_val_if_fail(cmdline != NULL, NULL);
3355 debug_print("get_command_output(): executing: %s\n", cmdline);
3357 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3359 g_warning("Couldn't execute command: %s\n", cmdline);
3363 return child_stdout;
3366 static gint is_unchanged_uri_char(char c)
3377 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3383 for(i = 0; i < strlen(uri) ; i++) {
3384 if (is_unchanged_uri_char(uri[i])) {
3385 if (k + 2 >= bufsize)
3387 encoded_uri[k++] = uri[i];
3390 char * hexa = "0123456789ABCDEF";
3392 if (k + 4 >= bufsize)
3394 encoded_uri[k++] = '%';
3395 encoded_uri[k++] = hexa[uri[i] / 16];
3396 encoded_uri[k++] = hexa[uri[i] % 16];
3402 gint open_uri(const gchar *uri, const gchar *cmdline)
3406 gchar buf[BUFFSIZE];
3408 gchar encoded_uri[BUFFSIZE];
3409 cm_return_val_if_fail(uri != NULL, -1);
3411 /* an option to choose whether to use encode_uri or not ? */
3412 encode_uri(encoded_uri, BUFFSIZE, uri);
3415 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3416 !strchr(p + 2, '%'))
3417 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3420 g_warning("Open URI command-line is invalid "
3421 "(there must be only one '%%s'): %s",
3423 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3426 execute_command_line(buf, TRUE);
3428 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3431 extern osso_context_t *get_osso_context(void);
3432 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3433 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3434 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3439 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3441 gchar buf[BUFFSIZE];
3444 cm_return_val_if_fail(filepath != NULL, -1);
3447 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3448 !strchr(p + 2, '%'))
3449 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3452 g_warning("Open Text Editor command-line is invalid "
3453 "(there must be only one '%%s'): %s",
3455 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3458 execute_command_line(buf, TRUE);
3463 time_t remote_tzoffset_sec(const gchar *zone)
3465 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3471 time_t remoteoffset;
3473 strncpy(zone3, zone, 3);
3477 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3478 (c == '+' || c == '-')) {
3479 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3481 remoteoffset = -remoteoffset;
3482 } else if (!strncmp(zone, "UT" , 2) ||
3483 !strncmp(zone, "GMT", 2)) {
3485 } else if (strlen(zone3) == 3) {
3486 for (p = ustzstr; *p != '\0'; p += 3) {
3487 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3488 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3489 remoteoffset = iustz * 3600;
3495 } else if (strlen(zone3) == 1) {
3497 case 'Z': remoteoffset = 0; break;
3498 case 'A': remoteoffset = -1; break;
3499 case 'B': remoteoffset = -2; break;
3500 case 'C': remoteoffset = -3; break;
3501 case 'D': remoteoffset = -4; break;
3502 case 'E': remoteoffset = -5; break;
3503 case 'F': remoteoffset = -6; break;
3504 case 'G': remoteoffset = -7; break;
3505 case 'H': remoteoffset = -8; break;
3506 case 'I': remoteoffset = -9; break;
3507 case 'K': remoteoffset = -10; break; /* J is not used */
3508 case 'L': remoteoffset = -11; break;
3509 case 'M': remoteoffset = -12; break;
3510 case 'N': remoteoffset = 1; break;
3511 case 'O': remoteoffset = 2; break;
3512 case 'P': remoteoffset = 3; break;
3513 case 'Q': remoteoffset = 4; break;
3514 case 'R': remoteoffset = 5; break;
3515 case 'S': remoteoffset = 6; break;
3516 case 'T': remoteoffset = 7; break;
3517 case 'U': remoteoffset = 8; break;
3518 case 'V': remoteoffset = 9; break;
3519 case 'W': remoteoffset = 10; break;
3520 case 'X': remoteoffset = 11; break;
3521 case 'Y': remoteoffset = 12; break;
3522 default: remoteoffset = 0; break;
3524 remoteoffset = remoteoffset * 3600;
3528 return remoteoffset;
3531 time_t tzoffset_sec(time_t *now)
3535 struct tm buf1, buf2;
3537 if (now && *now < 0)
3540 gmt = *gmtime_r(now, &buf1);
3541 lt = localtime_r(now, &buf2);
3543 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3545 if (lt->tm_year < gmt.tm_year)
3547 else if (lt->tm_year > gmt.tm_year)
3549 else if (lt->tm_yday < gmt.tm_yday)
3551 else if (lt->tm_yday > gmt.tm_yday)
3554 if (off >= 24 * 60) /* should be impossible */
3555 off = 23 * 60 + 59; /* if not, insert silly value */
3556 if (off <= -24 * 60)
3557 off = -(23 * 60 + 59);
3562 /* calculate timezone offset */
3563 gchar *tzoffset(time_t *now)
3565 static gchar offset_string[6];
3569 struct tm buf1, buf2;
3571 if (now && *now < 0)
3574 gmt = *gmtime_r(now, &buf1);
3575 lt = localtime_r(now, &buf2);
3577 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3579 if (lt->tm_year < gmt.tm_year)
3581 else if (lt->tm_year > gmt.tm_year)
3583 else if (lt->tm_yday < gmt.tm_yday)
3585 else if (lt->tm_yday > gmt.tm_yday)
3593 if (off >= 24 * 60) /* should be impossible */
3594 off = 23 * 60 + 59; /* if not, insert silly value */
3596 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3598 return offset_string;
3601 void get_rfc822_date(gchar *buf, gint len)
3605 gchar day[4], mon[4];
3606 gint dd, hh, mm, ss, yyyy;
3608 gchar buf2[BUFFSIZE];
3611 lt = localtime_r(&t, &buf1);
3613 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3614 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3616 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3617 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3620 void debug_set_mode(gboolean mode)
3625 gboolean debug_get_mode(void)
3630 void debug_print_real(const gchar *format, ...)
3633 gchar buf[BUFFSIZE];
3635 if (!debug_mode) return;
3637 va_start(args, format);
3638 g_vsnprintf(buf, sizeof(buf), format, args);
3645 const char * debug_srcname(const char *file)
3647 const char *s = strrchr (file, '/');
3652 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3654 if (subject == NULL)
3657 subject += subject_get_prefix_length(subject);
3659 return g_hash_table_lookup(subject_table, subject);
3662 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3665 if (subject == NULL || *subject == 0)
3667 subject += subject_get_prefix_length(subject);
3668 g_hash_table_insert(subject_table, subject, data);
3671 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3673 if (subject == NULL)
3676 subject += subject_get_prefix_length(subject);
3677 g_hash_table_remove(subject_table, subject);
3681 static regex_t u_regex;
3682 static gboolean u_init_;
3685 void utils_free_regex(void)
3696 *\brief Check if a string is prefixed with known (combinations)
3697 * of prefixes. The function assumes that each prefix
3698 * is terminated by zero or exactly _one_ space.
3700 *\param str String to check for a prefixes
3702 *\return int Number of chars in the prefix that should be skipped
3703 * for a "clean" subject line. If no prefix was found, 0
3706 int subject_get_prefix_length(const gchar *subject)
3709 /*!< Array with allowable reply prefixes regexps. */
3710 static const gchar * const prefixes[] = {
3711 "Re\\:", /* "Re:" */
3712 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3713 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3714 "Aw\\:", /* "Aw:" (German) */
3715 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3716 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3717 "Fw\\:", /* "Fw:" Forward */
3718 "Fwd\\:", /* "Fwd:" Forward */
3719 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3720 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3721 "Rif\\:", /* "Rif:" (Italian Outlook) */
3722 "Sv\\:", /* "Sv" (Norwegian) */
3723 "Vs\\:", /* "Vs" (Norwegian) */
3724 "Ad\\:", /* "Ad" (Norwegian) */
3725 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3726 "R\303\251f\\. \\:", /* "Réf. :" (French Lotus Notes) */
3727 "Re \\:", /* "Re :" (French Yahoo Mail) */
3730 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3734 if (!subject) return 0;
3735 if (!*subject) return 0;
3738 GString *s = g_string_new("");
3740 for (n = 0; n < PREFIXES; n++)
3741 /* Terminate each prefix regexpression by a
3742 * "\ ?" (zero or ONE space), and OR them */
3743 g_string_append_printf(s, "(%s\\ ?)%s",
3748 g_string_prepend(s, "(");
3749 g_string_append(s, ")+"); /* match at least once */
3750 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3753 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3754 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3755 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3756 debug_print("Error compiling regexp %s\n", s->str);
3757 g_string_free(s, TRUE);
3761 g_string_free(s, TRUE);
3765 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3770 /*!< Array with allowable reply prefixes regexps. */
3771 static const gchar * const prefixes[] = {
3773 "antw:", /* "Antw:" (Dutch / German Outlook) */
3774 "aw:", /* "Aw:" (German) */
3775 "antwort:", /* "Antwort:" (German Lotus Notes) */
3776 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3777 "fw:", /* "Fw:" Forward */
3778 "fwd:", /* "Fwd:" Forward */
3779 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3780 "odp:", /* "Odp:" Re (Polish Outlook) */
3781 "rif:", /* "Rif:" (Italian Outlook) */
3782 "sv:", /* "Sv" (Norwegian) */
3783 "vs:", /* "Vs" (Norwegian) */
3784 "ad:", /* "Ad" (Norwegian) */
3785 "R\303\251f. :", /* "Réf. :" (French Lotus Notes) */
3786 "Re :", /* "Re :" (French Yahoo Mail) */
3789 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3792 if (!subject) return 0;
3793 if (!*subject) return 0;
3795 for (n = 0; n < PREFIXES; n++) {
3796 int len = strlen(prefixes[n]);
3797 if (!strncasecmp(subject, prefixes[n], len)) {
3798 if (subject[len] == ' ')
3807 static guint g_stricase_hash(gconstpointer gptr)
3809 guint hash_result = 0;
3812 for (str = gptr; str && *str; str++) {
3813 hash_result += toupper(*str);
3819 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3821 const char *str1 = gptr1;
3822 const char *str2 = gptr2;
3824 return !strcasecmp(str1, str2);
3827 gint g_int_compare(gconstpointer a, gconstpointer b)
3829 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3832 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3840 lt = localtime_r(&t, &buft);
3842 if (user_addr != NULL)
3843 addr = g_strdup_printf(".%s", user_addr);
3844 else if (strlen(buf) != 0)
3845 addr = g_strdup_printf("@%s", buf);
3847 addr = g_strdup_printf("@%s", get_domain_name());
3849 /* Replace all @ but the last one in addr, with underscores.
3850 * RFC 2822 States that msg-id syntax only allows one @.
3852 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3853 *(strchr(addr, '@')) = '_';
3855 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3856 lt->tm_year + 1900, lt->tm_mon + 1,
3857 lt->tm_mday, lt->tm_hour,
3858 lt->tm_min, lt->tm_sec,
3859 (guint) rand(), addr);
3866 quote_cmd_argument()
3868 return a quoted string safely usable in argument of a command.
3870 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3873 gint quote_cmd_argument(gchar * result, guint size,
3883 for(p = path ; * p != '\0' ; p ++) {
3885 if (isalnum((guchar)*p) || (* p == '/')) {
3886 if (remaining > 0) {
3892 result[size - 1] = '\0';
3897 if (remaining >= 2) {
3905 result[size - 1] = '\0';
3910 if (remaining > 0) {
3914 result[size - 1] = '\0';
3928 static void g_node_map_recursive(GNode *node, gpointer data)
3930 GNodeMapData *mapdata = (GNodeMapData *) data;
3932 GNodeMapData newmapdata;
3935 newdata = mapdata->func(node->data, mapdata->data);
3936 if (newdata != NULL) {
3937 newnode = g_node_new(newdata);
3938 g_node_append(mapdata->parent, newnode);
3940 newmapdata.parent = newnode;
3941 newmapdata.func = mapdata->func;
3942 newmapdata.data = mapdata->data;
3944 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3948 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3951 GNodeMapData mapdata;
3953 cm_return_val_if_fail(node != NULL, NULL);
3954 cm_return_val_if_fail(func != NULL, NULL);
3956 root = g_node_new(func(node->data, data));
3958 mapdata.parent = root;
3959 mapdata.func = func;
3960 mapdata.data = data;
3962 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3967 #define HEX_TO_INT(val, hex) \
3971 if ('0' <= c && c <= '9') { \
3973 } else if ('a' <= c && c <= 'f') { \
3974 val = c - 'a' + 10; \
3975 } else if ('A' <= c && c <= 'F') { \
3976 val = c - 'A' + 10; \
3982 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3989 if (hi == -1 || lo == -1)
3992 *out = (hi << 4) + lo;
3996 #define INT_TO_HEX(hex, val) \
3999 hex = '0' + (val); \
4001 hex = 'A' + (val) - 10; \
4004 void get_hex_str(gchar *out, guchar ch)
4008 INT_TO_HEX(hex, ch >> 4);
4010 INT_TO_HEX(hex, ch & 0x0f);
4016 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4018 #define G_PRINT_REF g_print
4022 *\brief Register ref counted pointer. It is based on GBoxed, so should
4023 * work with anything that uses the GType system. The semantics
4024 * are similar to a C++ auto pointer, with the exception that
4025 * C doesn't have automatic closure (calling destructors) when
4026 * exiting a block scope.
4027 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4028 * function directly.
4030 *\return GType A GType type.
4032 GType g_auto_pointer_register(void)
4034 static GType auto_pointer_type;
4035 if (!auto_pointer_type)
4037 g_boxed_type_register_static
4038 ("G_TYPE_AUTO_POINTER",
4039 (GBoxedCopyFunc) g_auto_pointer_copy,
4040 (GBoxedFreeFunc) g_auto_pointer_free);
4041 return auto_pointer_type;
4045 *\brief Structure with g_new() allocated pointer guarded by the
4048 typedef struct AutoPointerRef {
4049 void (*free) (gpointer);
4055 *\brief The auto pointer opaque structure that references the
4056 * pointer guard block.
4058 typedef struct AutoPointer {
4059 AutoPointerRef *ref;
4060 gpointer ptr; /*!< access to protected pointer */
4064 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4068 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4069 * ... when assigning, copying and freeing storage elements
4071 * gtk_list_store_new(N_S_COLUMNS,
4072 * G_TYPE_AUTO_POINTER,
4076 * Template *precious_data = g_new0(Template, 1);
4077 * g_pointer protect = g_auto_pointer_new(precious_data);
4079 * gtk_list_store_set(container, &iter,
4083 * ... the gtk_list_store has copied the pointer and
4084 * ... incremented its reference count, we should free
4085 * ... the auto pointer (in C++ a destructor would do
4086 * ... this for us when leaving block scope)
4088 * g_auto_pointer_free(protect);
4090 * ... gtk_list_store_set() now manages the data. When
4091 * ... *explicitly* requesting a pointer from the list
4092 * ... store, don't forget you get a copy that should be
4093 * ... freed with g_auto_pointer_free() eventually.
4097 *\param pointer Pointer to be guarded.
4099 *\return GAuto * Pointer that should be used in containers with
4102 GAuto *g_auto_pointer_new(gpointer p)
4104 AutoPointerRef *ref;
4110 ref = g_new0(AutoPointerRef, 1);
4111 ptr = g_new0(AutoPointer, 1);
4121 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4127 *\brief Allocate an autopointer using the passed \a free function to
4128 * free the guarded pointer
4130 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4137 aptr = g_auto_pointer_new(p);
4138 aptr->ref->free = free_;
4142 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4144 if (auto_ptr == NULL)
4146 return ((AutoPointer *) auto_ptr)->ptr;
4150 *\brief Copies an auto pointer by. It's mostly not necessary
4151 * to call this function directly, unless you copy/assign
4152 * the guarded pointer.
4154 *\param auto_ptr Auto pointer returned by previous call to
4155 * g_auto_pointer_new_XXX()
4157 *\return gpointer An auto pointer
4159 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4162 AutoPointerRef *ref;
4165 if (auto_ptr == NULL)
4170 newp = g_new0(AutoPointer, 1);
4173 newp->ptr = ref->pointer;
4177 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4183 *\brief Free an auto pointer
4185 void g_auto_pointer_free(GAuto *auto_ptr)
4188 AutoPointerRef *ref;
4190 if (auto_ptr == NULL)
4196 if (--(ref->cnt) == 0) {
4198 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4200 ref->free(ref->pointer);
4205 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4210 void replace_returns(gchar *str)
4215 while (strstr(str, "\n")) {
4216 *strstr(str, "\n") = ' ';
4218 while (strstr(str, "\r")) {
4219 *strstr(str, "\r") = ' ';
4223 /* get_uri_part() - retrieves a URI starting from scanpos.
4224 Returns TRUE if succesful */
4225 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4226 const gchar **bp, const gchar **ep, gboolean hdr)
4229 gint parenthese_cnt = 0;
4231 cm_return_val_if_fail(start != NULL, FALSE);
4232 cm_return_val_if_fail(scanpos != NULL, FALSE);
4233 cm_return_val_if_fail(bp != NULL, FALSE);
4234 cm_return_val_if_fail(ep != NULL, FALSE);
4238 /* find end point of URI */
4239 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4240 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4241 !IS_ASCII(*(const guchar *)ep_) ||
4242 strchr("[]{}<>\"", *ep_)) {
4244 } else if (strchr("(", *ep_)) {
4246 } else if (strchr(")", *ep_)) {
4247 if (parenthese_cnt > 0)
4254 /* no punctuation at end of string */
4256 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4257 * should pass some URI type to this function and decide on that whether
4258 * to perform punctuation stripping */
4260 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4262 for (; ep_ - 1 > scanpos + 1 &&
4263 IS_REAL_PUNCT(*(ep_ - 1));
4267 #undef IS_REAL_PUNCT
4274 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4276 while (bp && *bp && g_ascii_isspace(*bp))
4278 return g_strndup(bp, ep - bp);
4281 /* valid mail address characters */
4282 #define IS_RFC822_CHAR(ch) \
4286 !g_ascii_isspace(ch) && \
4287 !strchr("(),;<>\"", (ch)))
4289 /* alphabet and number within 7bit ASCII */
4290 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4291 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4293 static GHashTable *create_domain_tab(void)
4295 static const gchar *toplvl_domains [] = {
4297 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4298 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4299 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4300 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4301 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4302 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4303 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4304 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4305 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4306 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4307 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4308 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4309 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4310 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4311 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4312 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4313 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4314 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4315 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4316 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4317 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4318 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4319 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4320 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4321 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4322 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4325 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4327 cm_return_val_if_fail(htab, NULL);
4328 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4329 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4333 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4335 const gint MAX_LVL_DOM_NAME_LEN = 6;
4336 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4337 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4340 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4343 for (p = buf; p < m && first < last; *p++ = *first++)
4347 return g_hash_table_lookup(tab, buf) != NULL;
4350 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4351 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4352 const gchar **bp, const gchar **ep, gboolean hdr)
4354 /* more complex than the uri part because we need to scan back and forward starting from
4355 * the scan position. */
4356 gboolean result = FALSE;
4357 const gchar *bp_ = NULL;
4358 const gchar *ep_ = NULL;
4359 static GHashTable *dom_tab;
4360 const gchar *last_dot = NULL;
4361 const gchar *prelast_dot = NULL;
4362 const gchar *last_tld_char = NULL;
4364 /* the informative part of the email address (describing the name
4365 * of the email address owner) may contain quoted parts. the
4366 * closure stack stores the last encountered quotes. */
4367 gchar closure_stack[128];
4368 gchar *ptr = closure_stack;
4370 cm_return_val_if_fail(start != NULL, FALSE);
4371 cm_return_val_if_fail(scanpos != NULL, FALSE);
4372 cm_return_val_if_fail(bp != NULL, FALSE);
4373 cm_return_val_if_fail(ep != NULL, FALSE);
4376 const gchar *start_quote = NULL;
4377 const gchar *end_quote = NULL;
4379 /* go to the real start */
4380 if (start[0] == ',')
4382 if (start[0] == ';')
4384 while (start[0] == '\n' || start[0] == '\r')
4386 while (start[0] == ' ' || start[0] == '\t')
4391 /* check if there are quotes (to skip , in them) */
4392 if (*start == '"') {
4393 start_quote = start;
4395 end_quote = strstr(start, "\"");
4401 /* skip anything between quotes */
4402 if (start_quote && end_quote) {
4407 /* find end (either , or ; or end of line) */
4408 if (strstr(start, ",") && strstr(start, ";"))
4409 *ep = strstr(start,",") < strstr(start, ";")
4410 ? strstr(start, ",") : strstr(start, ";");
4411 else if (strstr(start, ","))
4412 *ep = strstr(start, ",");
4413 else if (strstr(start, ";"))
4414 *ep = strstr(start, ";");
4416 *ep = start+strlen(start);
4418 /* go back to real start */
4419 if (start_quote && end_quote) {
4420 start = start_quote;
4423 /* check there's still an @ in that, or search
4424 * further if possible */
4425 if (strstr(start, "@") && strstr(start, "@") < *ep)
4427 else if (*ep < start+strlen(start)) {
4430 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4438 dom_tab = create_domain_tab();
4439 cm_return_val_if_fail(dom_tab, FALSE);
4441 /* scan start of address */
4442 for (bp_ = scanpos - 1;
4443 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4446 /* TODO: should start with an alnum? */
4448 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4451 if (bp_ != scanpos) {
4452 /* scan end of address */
4453 for (ep_ = scanpos + 1;
4454 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4456 prelast_dot = last_dot;
4458 if (*(last_dot + 1) == '.') {
4459 if (prelast_dot == NULL)
4461 last_dot = prelast_dot;
4466 /* TODO: really should terminate with an alnum? */
4467 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4472 if (last_dot == NULL)
4474 if (last_dot >= ep_)
4475 last_dot = prelast_dot;
4476 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4480 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4481 if (*last_tld_char == '?')
4484 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4491 if (!result) return FALSE;
4493 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4494 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4495 && IS_RFC822_CHAR(*(ep_ + 3))) {
4496 /* this informative part with an @ in it is
4497 * followed by the email address */
4500 /* go to matching '>' (or next non-rfc822 char, like \n) */
4501 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4504 /* include the bracket */
4505 if (*ep_ == '>') ep_++;
4507 /* include the leading quote */
4515 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4516 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4519 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4520 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4523 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4524 #define IN_STACK() (ptr > closure_stack)
4525 /* has underrun check */
4526 #define POP_STACK() if(IN_STACK()) --ptr
4527 /* has overrun check */
4528 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4529 /* has underrun check */
4530 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4534 /* scan for the informative part. */
4535 for (bp_ -= 2; bp_ >= start; bp_--) {
4536 /* if closure on the stack keep scanning */
4537 if (PEEK_STACK() == *bp_) {
4541 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4546 /* if nothing in the closure stack, do the special conditions
4547 * the following if..else expression simply checks whether
4548 * a token is acceptable. if not acceptable, the clause
4549 * should terminate the loop with a 'break' */
4550 if (!PEEK_STACK()) {
4552 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4553 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4554 /* hyphens are allowed, but only in
4556 } else if (strchr(" \"'", *bp_)) {
4557 /* but anything not being a punctiation
4560 break; /* anything else is rejected */
4567 /* scan forward (should start with an alnum) */
4568 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4584 #undef IS_ASCII_ALNUM
4585 #undef IS_RFC822_CHAR
4587 gchar *make_email_string(const gchar *bp, const gchar *ep)
4589 /* returns a mailto: URI; mailto: is also used to detect the
4590 * uri type later on in the button_pressed signal handler */
4594 tmp = g_strndup(bp, ep - bp);
4595 result = g_strconcat("mailto:", tmp, NULL);
4601 gchar *make_http_string(const gchar *bp, const gchar *ep)
4603 /* returns an http: URI; */
4607 while (bp && *bp && g_ascii_isspace(*bp))
4609 tmp = g_strndup(bp, ep - bp);
4610 result = g_strconcat("http://", tmp, NULL);
4616 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4618 FILE *fp = g_fopen(path, "rb");
4619 gchar buf[BUFFSIZE];
4620 gchar *result = NULL;
4623 while (fgets(buf, sizeof (buf), fp) != NULL) {
4624 gchar **parts = g_strsplit(buf, ";", 3);
4625 gchar *trimmed = parts[0];
4626 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4628 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4629 trimmed[strlen(trimmed)-1] = '\0';
4631 if (!strcmp(trimmed, type)) {
4632 gboolean needsterminal = FALSE;
4633 if (parts[2] && strstr(parts[2], "needsterminal")) {
4634 needsterminal = TRUE;
4636 if (parts[2] && strstr(parts[2], "test=")) {
4637 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4638 gchar *testcmd = orig_testcmd;
4639 if (strstr(testcmd,";"))
4640 *(strstr(testcmd,";")) = '\0';
4641 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4643 while (testcmd[strlen(testcmd)-1] == '\n')
4644 testcmd[strlen(testcmd)-1] = '\0';
4645 while (testcmd[strlen(testcmd)-1] == '\r')
4646 testcmd[strlen(testcmd)-1] = '\0';
4647 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4648 testcmd[strlen(testcmd)-1] = '\0';
4650 if (strstr(testcmd, "%s")) {
4651 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4652 gint res = system(tmp);
4654 g_free(orig_testcmd);
4661 gint res = system(testcmd);
4662 g_free(orig_testcmd);
4672 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4674 while (trimmed[strlen(trimmed)-1] == '\n')
4675 trimmed[strlen(trimmed)-1] = '\0';
4676 while (trimmed[strlen(trimmed)-1] == '\r')
4677 trimmed[strlen(trimmed)-1] = '\0';
4678 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4679 trimmed[strlen(trimmed)-1] = '\0';
4680 result = g_strdup(trimmed);
4683 /* if there are no single quotes around %s, add them.
4684 * '.*%s.*' is ok, as in display 'png:%s'
4686 if (strstr(result, "%s")
4687 && !(strstr(result, "'") < strstr(result,"%s") &&
4688 strstr(strstr(result,"%s"), "'"))) {
4689 gchar *start = g_strdup(result);
4690 gchar *end = g_strdup(strstr(result, "%s")+2);
4692 *strstr(start, "%s") = '\0';
4693 tmp = g_strconcat(start,"'%s'",end, NULL);
4699 if (needsterminal) {
4700 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4711 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4713 gchar *result = NULL;
4717 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4718 result = mailcap_get_command_in_file(path, type, file_to_open);
4722 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4726 void mailcap_update_default(const gchar *type, const gchar *command)
4728 gchar *path = NULL, *outpath = NULL;
4729 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4730 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4731 FILE *fp = g_fopen(path, "rb");
4732 FILE *outfp = g_fopen(outpath, "wb");
4733 gchar buf[BUFFSIZE];
4734 gboolean err = FALSE;
4742 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4743 gchar **parts = g_strsplit(buf, ";", 3);
4744 gchar *trimmed = parts[0];
4745 while (trimmed[0] == ' ')
4747 while (trimmed[strlen(trimmed)-1] == ' ')
4748 trimmed[strlen(trimmed)-1] = '\0';
4750 if (!strcmp(trimmed, type)) {
4755 if(fputs(buf, outfp) == EOF) {
4762 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4768 if (fclose(outfp) == EOF)
4772 g_rename(outpath, path);
4778 gint copy_dir(const gchar *src, const gchar *dst)
4783 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4784 g_warning("failed to open directory: %s\n", src);
4788 if (make_dir(dst) < 0)
4791 while ((name = g_dir_read_name(dir)) != NULL) {
4792 gchar *old_file, *new_file;
4793 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4794 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4795 debug_print("copying: %s -> %s\n", old_file, new_file);
4796 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4797 gint r = copy_file(old_file, new_file, TRUE);
4804 /* Windows has no symlinks. Or well, Vista seems to
4805 have something like this but the semantics might be
4806 different. Thus we don't use it under Windows. */
4807 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4810 gchar *target = g_file_read_link(old_file, &error);
4812 r = symlink(target, new_file);
4819 #endif /*G_OS_WIN32*/
4820 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4821 gint r = copy_dir(old_file, new_file);
4832 /* crude test to see if a file is an email. */
4833 gboolean file_is_email (const gchar *filename)
4839 if (filename == NULL)
4841 if ((fp = g_fopen(filename, "rb")) == NULL)
4843 while (i < 60 && score < 3
4844 && fgets(buffer, sizeof (buffer), fp) > 0) {
4845 if (!strncmp(buffer, "From:", strlen("From:")))
4847 else if (!strncmp(buffer, "Date:", strlen("Date:")))
4849 else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
4851 else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4856 return (score >= 3);
4859 gboolean sc_g_list_bigger(GList *list, gint max)
4863 while (cur && i <= max+1) {
4870 gboolean sc_g_slist_bigger(GSList *list, gint max)
4874 while (cur && i <= max+1) {
4881 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4882 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4883 NULL, NULL, NULL, NULL, NULL, NULL};
4884 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4885 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4886 NULL, NULL, NULL, NULL, NULL, NULL};
4888 gint daynames_len[] = {0,0,0,0,0,0,0};
4889 gint monthnames_len[] = {0,0,0,0,0,0,
4891 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4892 gint s_monthnames_len[] = {0,0,0,0,0,0,
4894 const gchar *s_am_up = NULL;
4895 const gchar *s_pm_up = NULL;
4896 const gchar *s_am_low = NULL;
4897 const gchar *s_pm_low = NULL;
4899 gint s_am_up_len = 0;
4900 gint s_pm_up_len = 0;
4901 gint s_am_low_len = 0;
4902 gint s_pm_low_len = 0;
4904 const gchar *def_loc_format = NULL;
4905 const gchar *date_loc_format = NULL;
4906 const gchar *time_loc_format = NULL;
4907 const gchar *time_am_pm = NULL;
4909 static gboolean time_names_init_done = FALSE;
4911 static void init_time_names(void)
4915 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4916 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4917 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4918 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4919 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4920 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4921 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4923 monthnames[0] = Q_("Complete month name for use by strftime|January");
4924 monthnames[1] = Q_("Complete month name for use by strftime|February");
4925 monthnames[2] = Q_("Complete month name for use by strftime|March");
4926 monthnames[3] = Q_("Complete month name for use by strftime|April");
4927 monthnames[4] = Q_("Complete month name for use by strftime|May");
4928 monthnames[5] = Q_("Complete month name for use by strftime|June");
4929 monthnames[6] = Q_("Complete month name for use by strftime|July");
4930 monthnames[7] = Q_("Complete month name for use by strftime|August");
4931 monthnames[8] = Q_("Complete month name for use by strftime|September");
4932 monthnames[9] = Q_("Complete month name for use by strftime|October");
4933 monthnames[10] = Q_("Complete month name for use by strftime|November");
4934 monthnames[11] = Q_("Complete month name for use by strftime|December");
4936 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4937 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4938 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4939 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4940 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4941 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4942 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4944 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4945 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4946 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4947 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4948 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4949 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4950 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4951 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4952 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4953 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4954 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4955 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4957 for (i = 0; i < 7; i++) {
4958 daynames_len[i] = strlen(daynames[i]);
4959 s_daynames_len[i] = strlen(s_daynames[i]);
4961 for (i = 0; i < 12; i++) {
4962 monthnames_len[i] = strlen(monthnames[i]);
4963 s_monthnames_len[i] = strlen(s_monthnames[i]);
4966 s_am_up = Q_("For use by strftime (morning)|AM");
4967 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4968 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4969 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4971 s_am_up_len = strlen(s_am_up);
4972 s_pm_up_len = strlen(s_pm_up);
4973 s_am_low_len = strlen(s_am_low);
4974 s_pm_low_len = strlen(s_pm_low);
4976 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4977 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4978 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4980 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4982 time_names_init_done = TRUE;
4985 #define CHECK_SIZE() { \
4986 total_done += len; \
4987 if (total_done >= buflen) { \
4988 buf[buflen-1] = '\0'; \
4993 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4995 gchar *curpos = buf;
4996 gint total_done = 0;
4997 gchar subbuf[64], subfmt[64];
4998 static time_t last_tzset = (time_t)0;
5000 if (!time_names_init_done)
5003 if (format == NULL || lt == NULL)
5006 if (last_tzset != time(NULL)) {
5008 last_tzset = time(NULL);
5011 if (*format == '%') {
5012 gint len = 0, tmp = 0;
5016 len = 1; CHECK_SIZE();
5020 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5021 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5024 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5025 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5029 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5030 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5033 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5034 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5037 fast_strftime(subbuf, 64, def_loc_format, lt);
5038 len = strlen(subbuf); CHECK_SIZE();
5039 strncpy2(curpos, subbuf, buflen - total_done);
5042 total_done += 2; CHECK_SIZE();
5043 tmp = (lt->tm_year + 1900)/100;
5044 *curpos++ = '0'+(tmp / 10);
5045 *curpos++ = '0'+(tmp % 10);
5048 total_done += 2; CHECK_SIZE();
5049 *curpos++ = '0'+(lt->tm_mday / 10);
5050 *curpos++ = '0'+(lt->tm_mday % 10);
5053 total_done += 8; CHECK_SIZE();
5054 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5055 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5057 *curpos++ = '0'+(lt->tm_mday / 10);
5058 *curpos++ = '0'+(lt->tm_mday % 10);
5060 tmp = lt->tm_year%100;
5061 *curpos++ = '0'+(tmp / 10);
5062 *curpos++ = '0'+(tmp % 10);
5065 len = 2; CHECK_SIZE();
5066 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5069 len = 10; CHECK_SIZE();
5070 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5071 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5074 total_done += 2; CHECK_SIZE();
5075 *curpos++ = '0'+(lt->tm_hour / 10);
5076 *curpos++ = '0'+(lt->tm_hour % 10);
5079 total_done += 2; CHECK_SIZE();
5085 *curpos++ = '0'+(tmp / 10);
5086 *curpos++ = '0'+(tmp % 10);
5089 len = 3; CHECK_SIZE();
5090 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5093 len = 2; CHECK_SIZE();
5094 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5097 len = 2; CHECK_SIZE();
5103 snprintf(curpos, buflen - total_done, "%2d", tmp);
5106 total_done += 2; CHECK_SIZE();
5107 tmp = lt->tm_mon + 1;
5108 *curpos++ = '0'+(tmp / 10);
5109 *curpos++ = '0'+(tmp % 10);
5112 total_done += 2; CHECK_SIZE();
5113 *curpos++ = '0'+(lt->tm_min / 10);
5114 *curpos++ = '0'+(lt->tm_min % 10);
5117 len = 1; CHECK_SIZE();
5121 if (lt->tm_hour >= 12) {
5122 len = s_pm_up_len; CHECK_SIZE();
5123 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5125 len = s_am_up_len; CHECK_SIZE();
5126 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5130 if (lt->tm_hour >= 12) {
5131 len = s_pm_low_len; CHECK_SIZE();
5132 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5134 len = s_am_low_len; CHECK_SIZE();
5135 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5139 fast_strftime(subbuf, 64, time_am_pm, lt);
5140 len = strlen(subbuf); CHECK_SIZE();
5141 strncpy2(curpos, subbuf, buflen - total_done);
5144 total_done += 5; CHECK_SIZE();
5145 *curpos++ = '0'+(lt->tm_hour / 10);
5146 *curpos++ = '0'+(lt->tm_hour % 10);
5148 *curpos++ = '0'+(lt->tm_min / 10);
5149 *curpos++ = '0'+(lt->tm_min % 10);
5152 snprintf(subbuf, 64, "%ld", mktime(lt));
5153 len = strlen(subbuf); CHECK_SIZE();
5154 strncpy2(curpos, subbuf, buflen - total_done);
5157 total_done += 2; CHECK_SIZE();
5158 *curpos++ = '0'+(lt->tm_sec / 10);
5159 *curpos++ = '0'+(lt->tm_sec % 10);
5162 len = 1; CHECK_SIZE();
5166 total_done += 8; CHECK_SIZE();
5167 *curpos++ = '0'+(lt->tm_hour / 10);
5168 *curpos++ = '0'+(lt->tm_hour % 10);
5170 *curpos++ = '0'+(lt->tm_min / 10);
5171 *curpos++ = '0'+(lt->tm_min % 10);
5173 *curpos++ = '0'+(lt->tm_sec / 10);
5174 *curpos++ = '0'+(lt->tm_sec % 10);
5177 len = 1; CHECK_SIZE();
5178 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5181 len = 1; CHECK_SIZE();
5182 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5185 fast_strftime(subbuf, 64, date_loc_format, lt);
5186 len = strlen(subbuf); CHECK_SIZE();
5187 strncpy2(curpos, subbuf, buflen - total_done);
5190 fast_strftime(subbuf, 64, time_loc_format, lt);
5191 len = strlen(subbuf); CHECK_SIZE();
5192 strncpy2(curpos, subbuf, buflen - total_done);
5195 total_done += 2; CHECK_SIZE();
5196 tmp = lt->tm_year%100;
5197 *curpos++ = '0'+(tmp / 10);
5198 *curpos++ = '0'+(tmp % 10);
5201 len = 4; CHECK_SIZE();
5202 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5212 /* let these complicated ones be done with the libc */
5213 snprintf(subfmt, 64, "%%%c", *format);
5214 strftime(subbuf, 64, subfmt, lt);
5215 len = strlen(subbuf); CHECK_SIZE();
5216 strncpy2(curpos, subbuf, buflen - total_done);
5220 /* let these complicated modifiers be done with the libc */
5221 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5222 strftime(subbuf, 64, subfmt, lt);
5223 len = strlen(subbuf); CHECK_SIZE();
5224 strncpy2(curpos, subbuf, buflen - total_done);
5228 if (format && *format)
5229 g_warning("format error (%c)", *format);
5236 int len = 1; CHECK_SIZE();
5237 *curpos++ = *format++;
5244 gboolean prefs_common_get_use_shred(void);
5248 #define WEXITSTATUS(x) (x)
5251 int claws_unlink(const gchar *filename)
5254 static int found_shred = -1;
5255 static const gchar *args[4];
5257 if (filename == NULL)
5260 if (prefs_common_get_use_shred()) {
5261 if (found_shred == -1) {
5263 args[0] = g_find_program_in_path("shred");
5264 debug_print("found shred: %s\n", args[0]);
5265 found_shred = (args[0] != NULL) ? 1:0;
5269 if (found_shred == 1) {
5270 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5271 if (s.st_nlink == 1) {
5274 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5275 NULL, NULL, NULL, NULL, &status, NULL);
5276 debug_print("%s %s exited with status %d\n",
5277 args[0], filename, WEXITSTATUS(status));
5278 if (truncate(filename, 0) < 0)
5279 g_warning("couln't truncate");
5284 return g_unlink(filename);