2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
67 # include <tablet-browser-interface.h>
69 # include <osso-browser-interface.h>
75 #include "../codeconv.h"
79 static gboolean debug_mode = FALSE;
81 static GSList *tempfiles=NULL;
84 /* Return true if we are running as root. This function should beused
85 instead of getuid () == 0. */
86 gboolean superuser_p (void)
89 return w32_is_administrator ();
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
101 if (G_WIN32_HAVE_WIDECHAR_API()) {
106 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
112 retval = _wchdir(wpath);
124 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125 if (cp_path == NULL) {
130 retval = chdir(cp_path);
143 gint g_chmod(const gchar *path, gint mode)
146 if (G_WIN32_HAVE_WIDECHAR_API()) {
151 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
157 retval = _wchmod(wpath, mode);
169 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170 if (cp_path == NULL) {
175 retval = chmod(cp_path, mode);
184 return chmod(path, mode);
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
191 char *name = g_win32_locale_filename_from_utf8(filename);
192 FILE* fp = fopen(name, mode);
196 return fopen(filename, mode);
199 int g_open(const gchar *filename, int flags, int mode)
202 char *name = g_win32_locale_filename_from_utf8(filename);
203 int fd = open(name, flags, mode);
207 return open(filename, flags, mode);
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
214 gint mkstemp_name(gchar *template, gchar **name_used)
216 static gulong count=0; /* W32-_mktemp only supports up to 27
220 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222 (S_IRUSR | S_IWUSR));
224 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
226 perror(g_strdup_printf("cant create %s",*name_used));
232 #endif /* G_OS_WIN32 */
235 gint mkstemp(gchar *template)
238 gint res = mkstemp_name(template, &dummyname);
242 #endif /* G_OS_WIN32 */
244 void list_free_strings(GList *list)
246 list = g_list_first(list);
248 while (list != NULL) {
254 void slist_free_strings(GSList *list)
256 while (list != NULL) {
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
267 void hash_free_strings(GHashTable *table)
269 g_hash_table_foreach(table, hash_free_strings_func, NULL);
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
274 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
277 guint str_case_hash(gconstpointer key)
279 const gchar *p = key;
283 h = g_ascii_tolower(h);
284 for (p += 1; *p != '\0'; p++)
285 h = (h << 5) - h + g_ascii_tolower(*p);
291 void ptr_array_free_strings(GPtrArray *array)
296 cm_return_if_fail(array != NULL);
298 for (i = 0; i < array->len; i++) {
299 str = g_ptr_array_index(array, i);
304 gboolean str_find(const gchar *haystack, const gchar *needle)
306 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
311 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
314 gint to_number(const gchar *nstr)
316 register const gchar *p;
318 if (*nstr == '\0') return -1;
320 for (p = nstr; *p != '\0'; p++)
321 if (!g_ascii_isdigit(*p)) return -1;
326 /* convert integer into string,
327 nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
330 g_snprintf(nstr, 11, "%d", n);
334 /* convert integer into string */
337 static gchar nstr[11];
339 return itos_buf(nstr, n);
342 #define divide(num,divisor,i,d) \
344 i = num >> divisor; \
345 d = num & ((1<<divisor)-1); \
346 d = (d*100) >> divisor; \
351 * \brief Convert a given size in bytes in a human-readable string
353 * \param size The size expressed in bytes to convert in string
354 * \return The string that respresents the size in an human-readable way
356 gchar *to_human_readable(goffset size)
358 static gchar str[14];
359 static gchar *b_format = NULL, *kb_format = NULL,
360 *mb_format = NULL, *gb_format = NULL;
361 register int t = 0, r = 0;
362 if (b_format == NULL) {
364 kb_format = _("%d.%02dKB");
365 mb_format = _("%d.%02dMB");
366 gb_format = _("%.2fGB");
369 if (size < (goffset)1024) {
370 g_snprintf(str, sizeof(str), b_format, (gint)size);
372 } else if (size >> 10 < (goffset)1024) {
373 divide(size, 10, t, r);
374 g_snprintf(str, sizeof(str), kb_format, t, r);
376 } else if (size >> 20 < (goffset)1024) {
377 divide(size, 20, t, r);
378 g_snprintf(str, sizeof(str), mb_format, t, r);
381 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
386 /* strcmp with NULL-checking */
387 gint strcmp2(const gchar *s1, const gchar *s2)
389 if (s1 == NULL || s2 == NULL)
392 return strcmp(s1, s2);
394 /* strstr with NULL-checking */
395 gchar *strstr2(const gchar *s1, const gchar *s2)
397 if (s1 == NULL || s2 == NULL)
400 return strstr(s1, s2);
403 gint path_cmp(const gchar *s1, const gchar *s2)
408 gchar *s1buf, *s2buf;
411 if (s1 == NULL || s2 == NULL) return -1;
412 if (*s1 == '\0' || *s2 == '\0') return -1;
415 s1buf = g_strdup (s1);
416 s2buf = g_strdup (s2);
417 subst_char (s1buf, '/', G_DIR_SEPARATOR);
418 subst_char (s2buf, '/', G_DIR_SEPARATOR);
421 #endif /* !G_OS_WIN32 */
426 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
427 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
429 rc = strncmp(s1, s2, MAX(len1, len2));
433 #endif /* !G_OS_WIN32 */
437 /* remove trailing return code */
438 gchar *strretchomp(gchar *str)
442 if (!*str) return str;
444 for (s = str + strlen(str) - 1;
445 s >= str && (*s == '\n' || *s == '\r');
452 /* remove trailing character */
453 gchar *strtailchomp(gchar *str, gchar tail_char)
457 if (!*str) return str;
458 if (tail_char == '\0') return str;
460 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
466 /* remove CR (carriage return) */
467 gchar *strcrchomp(gchar *str)
471 if (!*str) return str;
473 s = str + strlen(str) - 1;
474 if (*s == '\n' && s > str && *(s - 1) == '\r') {
482 gint file_strip_crs(const gchar *file)
484 FILE *fp = NULL, *outfp = NULL;
486 gchar *out = get_tmp_file();
490 fp = g_fopen(file, "rb");
494 outfp = g_fopen(out, "wb");
500 while (fgets(buf, sizeof (buf), fp) != NULL) {
502 if (fputs(buf, outfp) == EOF) {
510 if (fclose(outfp) == EOF) {
514 if (move_file(out, file, TRUE) < 0)
526 /* Similar to `strstr' but this function ignores the case of both strings. */
527 gchar *strcasestr(const gchar *haystack, const gchar *needle)
529 register size_t haystack_len, needle_len;
531 haystack_len = strlen(haystack);
532 needle_len = strlen(needle);
534 if (haystack_len < needle_len || needle_len == 0)
537 while (haystack_len >= needle_len) {
538 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
539 return (gchar *)haystack;
549 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
550 gconstpointer needle, size_t needlelen)
552 const gchar *haystack_ = (const gchar *)haystack;
553 const gchar *needle_ = (const gchar *)needle;
554 const gchar *haystack_cur = (const gchar *)haystack;
555 size_t haystack_left = haystacklen;
558 return memchr(haystack_, *needle_, haystacklen);
560 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
562 if (haystacklen - (haystack_cur - haystack_) < needlelen)
564 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
565 return (gpointer)haystack_cur;
568 haystack_left = haystacklen - (haystack_cur - haystack_);
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 static void trim_subject_for_compare(gchar *str)
619 eliminate_parenthesis(str, '[', ']');
620 eliminate_parenthesis(str, '(', ')');
623 srcp = str + subject_get_prefix_length(str);
625 memmove(str, srcp, strlen(srcp) + 1);
628 static void trim_subject_for_sort(gchar *str)
634 srcp = str + subject_get_prefix_length(str);
636 memmove(str, srcp, strlen(srcp) + 1);
639 /* compare subjects */
640 gint subject_compare(const gchar *s1, const gchar *s2)
644 if (!s1 || !s2) return -1;
645 if (!*s1 || !*s2) return -1;
647 Xstrdup_a(str1, s1, return -1);
648 Xstrdup_a(str2, s2, return -1);
650 trim_subject_for_compare(str1);
651 trim_subject_for_compare(str2);
653 if (!*str1 || !*str2) return -1;
655 return strcmp(str1, str2);
658 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
662 if (!s1 || !s2) return -1;
664 Xstrdup_a(str1, s1, return -1);
665 Xstrdup_a(str2, s2, return -1);
667 trim_subject_for_sort(str1);
668 trim_subject_for_sort(str2);
670 return g_utf8_collate(str1, str2);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void extract_quote(gchar *str, gchar quote_chr)
795 if ((str = strchr(str, quote_chr))) {
797 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798 memmove(p - 1, p, strlen(p) + 1);
803 memmove(str, str + 1, p - str);
808 void eliminate_address_comment(gchar *str)
810 register gchar *srcp, *destp;
815 while ((destp = strchr(destp, '"'))) {
816 if ((srcp = strchr(destp + 1, '"'))) {
821 while (g_ascii_isspace(*srcp)) srcp++;
822 memmove(destp, srcp, strlen(srcp) + 1);
832 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
838 else if (*srcp == ')')
844 while (g_ascii_isspace(*srcp)) srcp++;
845 memmove(destp, srcp, strlen(srcp) + 1);
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
851 gboolean in_quote = FALSE;
854 if (*str == c && !in_quote)
856 if (*str == quote_chr)
864 void extract_address(gchar *str)
866 eliminate_address_comment(str);
867 if (strchr_with_skip_quote(str, '"', '<'))
868 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
872 void extract_list_id_str(gchar *str)
874 if (strchr_with_skip_quote(str, '"', '<'))
875 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
879 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
884 if (!str) return addr_list;
886 Xstrdup_a(work, str, return addr_list);
889 eliminate_address_comment(work);
892 while (workp && *workp) {
895 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
901 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
902 extract_parenthesis_with_skip_quote
903 (workp, '"', '<', '>');
907 addr_list = g_slist_append(addr_list, g_strdup(workp));
915 GSList *address_list_append(GSList *addr_list, const gchar *str)
917 return address_list_append_real(addr_list, str, TRUE);
920 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
922 return address_list_append_real(addr_list, str, FALSE);
925 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
929 if (!str) return msgid_list;
932 while (strp && *strp) {
933 const gchar *start, *end;
936 if ((start = strchr(strp, '<')) != NULL) {
937 end = strchr(start + 1, '>');
942 msgid = g_strndup(start + 1, end - start - 1);
945 msgid_list = g_slist_prepend(msgid_list, msgid);
955 GSList *references_list_append(GSList *msgid_list, const gchar *str)
959 list = references_list_prepend(NULL, str);
960 list = g_slist_reverse(list);
961 msgid_list = g_slist_concat(msgid_list, list);
966 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
971 if (!str) return group_list;
973 Xstrdup_a(work, str, return group_list);
977 while (workp && *workp) {
980 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
988 group_list = g_slist_append(group_list,
997 GList *add_history(GList *list, const gchar *str)
1001 cm_return_val_if_fail(str != NULL, list);
1003 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1006 list = g_list_remove(list, old->data);
1007 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1010 last = g_list_last(list);
1013 list = g_list_remove(list, last->data);
1017 list = g_list_prepend(list, g_strdup(str));
1022 void remove_return(gchar *str)
1024 register gchar *p = str;
1027 if (*p == '\n' || *p == '\r')
1028 memmove(p, p + 1, strlen(p));
1034 void remove_space(gchar *str)
1036 register gchar *p = str;
1041 while (g_ascii_isspace(*(p + spc)))
1044 memmove(p, p + spc, strlen(p + spc) + 1);
1050 void unfold_line(gchar *str)
1052 register gchar *p = str;
1056 if (*p == '\n' || *p == '\r') {
1059 while (g_ascii_isspace(*(p + spc)))
1062 memmove(p, p + spc, strlen(p + spc) + 1);
1068 void subst_char(gchar *str, gchar orig, gchar subst)
1070 register gchar *p = str;
1079 void subst_chars(gchar *str, gchar *orig, gchar subst)
1081 register gchar *p = str;
1084 if (strchr(orig, *p) != NULL)
1090 void subst_for_filename(gchar *str)
1095 subst_chars(str, "\t\r\n\\/*:", '_');
1097 subst_chars(str, "\t\r\n\\/*", '_');
1101 void subst_for_shellsafe_filename(gchar *str)
1105 subst_for_filename(str);
1106 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1109 gboolean is_ascii_str(const gchar *str)
1111 const guchar *p = (const guchar *)str;
1113 while (*p != '\0') {
1114 if (*p != '\t' && *p != ' ' &&
1115 *p != '\r' && *p != '\n' &&
1116 (*p < 32 || *p >= 127))
1124 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1126 gchar * position = NULL;
1127 gchar * tmp_pos = NULL;
1130 if (quote_chars == NULL)
1133 for (i = 0; i < strlen(quote_chars); i++) {
1134 tmp_pos = strrchr (str, quote_chars[i]);
1136 || (tmp_pos != NULL && position <= tmp_pos) )
1142 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1144 const gchar *first_pos;
1145 const gchar *last_pos;
1146 const gchar *p = str;
1147 gint quote_level = -1;
1149 /* speed up line processing by only searching to the last '>' */
1150 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1151 /* skip a line if it contains a '<' before the initial '>' */
1152 if (memchr(str, '<', first_pos - str) != NULL)
1154 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1158 while (p <= last_pos) {
1159 while (p < last_pos) {
1160 if (g_ascii_isspace(*p))
1166 if (strchr(quote_chars, *p))
1168 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1169 /* any characters are allowed except '-' and space */
1171 && !strchr(quote_chars, *p)
1172 && !g_ascii_isspace(*p)
1175 if (strchr(quote_chars, *p))
1187 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1189 const gchar *p = str, *q;
1190 gint cur_line = 0, len;
1192 while ((q = strchr(p, '\n')) != NULL) {
1194 if (len > max_chars) {
1204 if (len > max_chars) {
1213 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1215 gchar * position = NULL;
1216 gchar * tmp_pos = NULL;
1219 if (quote_chars == NULL)
1222 for (i = 0; i < strlen(quote_chars); i++) {
1223 tmp_pos = strchr (str, quote_chars[i]);
1225 || (tmp_pos != NULL && position >= tmp_pos) )
1231 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1233 register guint haystack_len, needle_len;
1234 gboolean in_squote = FALSE, in_dquote = FALSE;
1236 haystack_len = strlen(haystack);
1237 needle_len = strlen(needle);
1239 if (haystack_len < needle_len || needle_len == 0)
1242 while (haystack_len >= needle_len) {
1243 if (!in_squote && !in_dquote &&
1244 !strncmp(haystack, needle, needle_len))
1245 return (gchar *)haystack;
1247 /* 'foo"bar"' -> foo"bar"
1248 "foo'bar'" -> foo'bar' */
1249 if (*haystack == '\'') {
1252 else if (!in_dquote)
1254 } else if (*haystack == '\"') {
1257 else if (!in_squote)
1268 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1271 GSList *string_list = NULL, *slist;
1272 gchar **str_array, *s, *new_str;
1273 guint i, n = 1, len;
1275 cm_return_val_if_fail(str != NULL, NULL);
1276 cm_return_val_if_fail(delim != NULL, NULL);
1279 max_tokens = G_MAXINT;
1281 s = strstr_with_skip_quote(str, delim);
1283 guint delimiter_len = strlen(delim);
1287 new_str = g_strndup(str, len);
1289 if (new_str[0] == '\'' || new_str[0] == '\"') {
1290 if (new_str[len - 1] == new_str[0]) {
1291 new_str[len - 1] = '\0';
1292 memmove(new_str, new_str + 1, len - 1);
1295 string_list = g_slist_prepend(string_list, new_str);
1297 str = s + delimiter_len;
1298 s = strstr_with_skip_quote(str, delim);
1299 } while (--max_tokens && s);
1303 new_str = g_strdup(str);
1304 if (new_str[0] == '\'' || new_str[0] == '\"') {
1306 if (new_str[len - 1] == new_str[0]) {
1307 new_str[len - 1] = '\0';
1308 memmove(new_str, new_str + 1, len - 1);
1311 string_list = g_slist_prepend(string_list, new_str);
1315 str_array = g_new(gchar*, n);
1319 str_array[i--] = NULL;
1320 for (slist = string_list; slist; slist = slist->next)
1321 str_array[i--] = slist->data;
1323 g_slist_free(string_list);
1328 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1330 gchar *abbrev_group;
1332 const gchar *p = group;
1335 cm_return_val_if_fail(group != NULL, NULL);
1337 last = group + strlen(group);
1338 abbrev_group = ap = g_malloc(strlen(group) + 1);
1343 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1345 while (*p != '.') p++;
1348 return abbrev_group;
1353 return abbrev_group;
1356 gchar *trim_string(const gchar *str, gint len)
1358 const gchar *p = str;
1363 if (!str) return NULL;
1364 if (strlen(str) <= len)
1365 return g_strdup(str);
1366 if (g_utf8_validate(str, -1, NULL) == FALSE)
1367 return g_strdup(str);
1369 while (*p != '\0') {
1370 mb_len = g_utf8_skip[*(guchar *)p];
1373 else if (new_len + mb_len > len)
1380 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1381 return g_strconcat(new_str, "...", NULL);
1384 GList *uri_list_extract_filenames(const gchar *uri_list)
1386 GList *result = NULL;
1388 gchar *escaped_utf8uri;
1394 while (g_ascii_isspace(*p)) p++;
1395 if (!strncmp(p, "file:", 5)) {
1398 while (*q && *q != '\n' && *q != '\r') q++;
1401 gchar *file, *locale_file = NULL;
1403 while (q > p && g_ascii_isspace(*q))
1405 Xalloca(escaped_utf8uri, q - p + 2,
1407 Xalloca(file, q - p + 2,
1410 strncpy(escaped_utf8uri, p, q - p + 1);
1411 escaped_utf8uri[q - p + 1] = '\0';
1412 decode_uri(file, escaped_utf8uri);
1414 * g_filename_from_uri() rejects escaped/locale encoded uri
1415 * string which come from Nautilus.
1418 if (g_utf8_validate(file, -1, NULL))
1420 = conv_codeset_strdup(
1423 conv_get_locale_charset_str());
1425 locale_file = g_strdup(file + 5);
1427 locale_file = g_filename_from_uri(file, NULL, NULL);
1429 result = g_list_append(result, locale_file);
1433 p = strchr(p, '\n');
1440 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1443 static gint axtoi(const gchar *hexstr)
1445 gint hi, lo, result;
1448 if ('0' <= hi && hi <= '9') {
1451 if ('a' <= hi && hi <= 'f') {
1454 if ('A' <= hi && hi <= 'F') {
1459 if ('0' <= lo && lo <= '9') {
1462 if ('a' <= lo && lo <= 'f') {
1465 if ('A' <= lo && lo <= 'F') {
1468 result = lo + (16 * hi);
1472 gboolean is_uri_string(const gchar *str)
1474 while (str && *str && g_ascii_isspace(*str))
1476 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1477 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1478 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1479 g_ascii_strncasecmp(str, "www.", 4) == 0);
1482 gchar *get_uri_path(const gchar *uri)
1484 while (uri && *uri && g_ascii_isspace(*uri))
1486 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1487 return (gchar *)(uri + 7);
1488 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1489 return (gchar *)(uri + 8);
1490 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1491 return (gchar *)(uri + 6);
1493 return (gchar *)uri;
1496 gint get_uri_len(const gchar *str)
1500 if (is_uri_string(str)) {
1501 for (p = str; *p != '\0'; p++) {
1502 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1511 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1512 * plusses, and escape characters are used)
1514 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1516 gchar *dec = decoded_uri;
1517 const gchar *enc = encoded_uri;
1522 if (isxdigit((guchar)enc[0]) &&
1523 isxdigit((guchar)enc[1])) {
1529 if (with_plus && *enc == '+')
1541 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1543 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1546 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1548 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1549 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1553 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1554 gchar **subject, gchar **body, gchar ***attach)
1558 const gchar *forbidden_uris[] = { ".gnupg/",
1564 gint num_attach = 0;
1565 gchar **my_att = NULL;
1567 Xstrdup_a(tmp_mailto, mailto, return -1);
1569 if (!strncmp(tmp_mailto, "mailto:", 7))
1572 p = strchr(tmp_mailto, '?');
1579 *to = decode_uri_gdup(tmp_mailto);
1581 my_att = g_malloc(sizeof(char *));
1585 gchar *field, *value;
1602 if (*value == '\0') continue;
1604 if (from && !g_ascii_strcasecmp(field, "from")) {
1606 *from = decode_uri_gdup(value);
1608 gchar *tmp = decode_uri_gdup(value);
1609 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1613 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1615 *cc = decode_uri_gdup(value);
1617 gchar *tmp = decode_uri_gdup(value);
1618 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1622 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1624 *bcc = decode_uri_gdup(value);
1626 gchar *tmp = decode_uri_gdup(value);
1627 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1631 } else if (subject && !*subject &&
1632 !g_ascii_strcasecmp(field, "subject")) {
1633 *subject = decode_uri_gdup(value);
1634 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1635 *body = decode_uri_gdup(value);
1636 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1637 gchar *tmp = decode_uri_gdup(value);
1638 if (!g_file_get_contents(value, body, NULL, NULL)) {
1639 g_error("Error: couldn't set insert file '%s' in body\n", value);
1643 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1645 gchar *tmp = decode_uri_gdup(value);
1646 for (; forbidden_uris[i]; i++) {
1647 if (strstr(tmp, forbidden_uris[i])) {
1648 g_print("Refusing to attach '%s', potential private data leak\n",
1656 /* attach is correct */
1658 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1659 my_att[num_attach-1] = tmp;
1660 my_att[num_attach] = NULL;
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 default directory for Plugins. */
1997 const gchar *get_plugin_dir(void)
2000 static gchar *plugin_dir = NULL;
2003 plugin_dir = g_strconcat(w32_get_module_dir(),
2004 "\\lib\\claws-mail\\plugins\\",
2008 if (is_dir_exist(PLUGINDIR))
2011 static gchar *plugin_dir = NULL;
2013 plugin_dir = g_strconcat(get_rc_dir(),
2014 G_DIR_SEPARATOR_S, "plugins",
2015 G_DIR_SEPARATOR_S, NULL);
2023 /* Return the default directory for Themes. */
2024 const gchar *get_themes_dir(void)
2026 static gchar *themes_dir = NULL;
2029 themes_dir = g_strconcat(w32_get_module_dir(),
2030 "\\share\\claws-mail\\themes",
2036 const gchar *get_tmp_dir(void)
2038 static gchar *tmp_dir = NULL;
2041 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2047 gchar *get_tmp_file(void)
2050 static guint32 id = 0;
2052 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2053 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2058 const gchar *get_domain_name(void)
2061 static gchar *domain_name = NULL;
2067 if (gethostname(hostname, sizeof(hostname)) != 0) {
2068 perror("gethostname");
2069 domain_name = "unknown";
2071 hostname[sizeof(hostname) - 1] = '\0';
2072 if ((hp = my_gethostbyname(hostname)) == NULL) {
2073 perror("gethostbyname");
2074 domain_name = g_strdup(hostname);
2076 domain_name = g_strdup(hp->h_name);
2079 debug_print("domain name = %s\n", domain_name);
2088 off_t get_file_size(const gchar *file)
2092 if (g_stat(file, &s) < 0) {
2093 FILE_OP_ERROR(file, "stat");
2100 time_t get_file_mtime(const gchar *file)
2104 if (g_stat(file, &s) < 0) {
2105 FILE_OP_ERROR(file, "stat");
2112 off_t get_file_size_as_crlf(const gchar *file)
2116 gchar buf[BUFFSIZE];
2118 if ((fp = g_fopen(file, "rb")) == NULL) {
2119 FILE_OP_ERROR(file, "g_fopen");
2123 while (fgets(buf, sizeof(buf), fp) != NULL) {
2125 size += strlen(buf) + 2;
2129 FILE_OP_ERROR(file, "fgets");
2138 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2145 if (g_stat(file, &s) < 0) {
2146 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2150 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2157 /* Test on whether FILE is a relative file name. This is
2158 * straightforward for Unix but more complex for Windows. */
2159 gboolean is_relative_filename(const gchar *file)
2164 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2165 return FALSE; /* Prefixed with a hostname - this can't
2166 * be a relative name. */
2168 if ( ((*file >= 'a' && *file <= 'z')
2169 || (*file >= 'A' && *file <= 'Z'))
2171 file += 2; /* Skip drive letter. */
2173 return !(*file == '\\' || *file == '/');
2175 return !(*file == G_DIR_SEPARATOR);
2180 gboolean is_dir_exist(const gchar *dir)
2185 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2188 gboolean is_file_entry_exist(const gchar *file)
2193 return g_file_test(file, G_FILE_TEST_EXISTS);
2196 gboolean dirent_is_regular_file(struct dirent *d)
2198 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2199 if (d->d_type == DT_REG)
2201 else if (d->d_type != DT_UNKNOWN)
2205 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2208 gint change_dir(const gchar *dir)
2210 gchar *prevdir = NULL;
2213 prevdir = g_get_current_dir();
2215 if (g_chdir(dir) < 0) {
2216 FILE_OP_ERROR(dir, "chdir");
2217 if (debug_mode) g_free(prevdir);
2219 } else if (debug_mode) {
2222 cwd = g_get_current_dir();
2223 if (strcmp(prevdir, cwd) != 0)
2224 g_print("current dir: %s\n", cwd);
2232 gint make_dir(const gchar *dir)
2234 if (g_mkdir(dir, S_IRWXU) < 0) {
2235 FILE_OP_ERROR(dir, "mkdir");
2238 if (g_chmod(dir, S_IRWXU) < 0)
2239 FILE_OP_ERROR(dir, "chmod");
2244 gint make_dir_hier(const gchar *dir)
2249 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2250 parent_dir = g_strndup(dir, p - dir);
2251 if (*parent_dir != '\0') {
2252 if (!is_dir_exist(parent_dir)) {
2253 if (make_dir(parent_dir) < 0) {
2262 if (!is_dir_exist(dir)) {
2263 if (make_dir(dir) < 0)
2270 gint remove_all_files(const gchar *dir)
2273 const gchar *dir_name;
2276 prev_dir = g_get_current_dir();
2278 if (g_chdir(dir) < 0) {
2279 FILE_OP_ERROR(dir, "chdir");
2284 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2285 g_warning("failed to open directory: %s\n", dir);
2290 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2291 if (claws_unlink(dir_name) < 0)
2292 FILE_OP_ERROR(dir_name, "unlink");
2297 if (g_chdir(prev_dir) < 0) {
2298 FILE_OP_ERROR(prev_dir, "chdir");
2308 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2311 const gchar *dir_name;
2315 prev_dir = g_get_current_dir();
2317 if (g_chdir(dir) < 0) {
2318 FILE_OP_ERROR(dir, "chdir");
2323 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2324 g_warning("failed to open directory: %s\n", dir);
2329 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2330 file_no = to_number(dir_name);
2331 if (file_no > 0 && first <= file_no && file_no <= last) {
2332 if (is_dir_exist(dir_name))
2334 if (claws_unlink(dir_name) < 0)
2335 FILE_OP_ERROR(dir_name, "unlink");
2341 if (g_chdir(prev_dir) < 0) {
2342 FILE_OP_ERROR(prev_dir, "chdir");
2352 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2355 const gchar *dir_name;
2359 prev_dir = g_get_current_dir();
2361 if (g_chdir(dir) < 0) {
2362 FILE_OP_ERROR(dir, "chdir");
2367 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2368 FILE_OP_ERROR(dir, "opendir");
2373 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2374 file_no = to_number(dir_name);
2375 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2376 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2377 if (is_dir_exist(dir_name))
2379 if (claws_unlink(dir_name) < 0)
2380 FILE_OP_ERROR(dir_name, "unlink");
2386 if (g_chdir(prev_dir) < 0) {
2387 FILE_OP_ERROR(prev_dir, "chdir");
2397 gint remove_all_numbered_files(const gchar *dir)
2399 return remove_numbered_files(dir, 0, UINT_MAX);
2402 gint remove_dir_recursive(const gchar *dir)
2406 const gchar *dir_name;
2409 if (g_stat(dir, &s) < 0) {
2410 FILE_OP_ERROR(dir, "stat");
2411 if (ENOENT == errno) return 0;
2415 if (!S_ISDIR(s.st_mode)) {
2416 if (claws_unlink(dir) < 0) {
2417 FILE_OP_ERROR(dir, "unlink");
2424 prev_dir = g_get_current_dir();
2425 /* g_print("prev_dir = %s\n", prev_dir); */
2427 if (!path_cmp(prev_dir, dir)) {
2429 if (g_chdir("..") < 0) {
2430 FILE_OP_ERROR(dir, "chdir");
2433 prev_dir = g_get_current_dir();
2436 if (g_chdir(dir) < 0) {
2437 FILE_OP_ERROR(dir, "chdir");
2442 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2443 g_warning("failed to open directory: %s\n", dir);
2449 /* remove all files in the directory */
2450 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2451 /* g_print("removing %s\n", dir_name); */
2453 if (is_dir_exist(dir_name)) {
2454 if (remove_dir_recursive(dir_name) < 0) {
2455 g_warning("can't remove directory\n");
2459 if (claws_unlink(dir_name) < 0)
2460 FILE_OP_ERROR(dir_name, "unlink");
2466 if (g_chdir(prev_dir) < 0) {
2467 FILE_OP_ERROR(prev_dir, "chdir");
2474 if (g_rmdir(dir) < 0) {
2475 FILE_OP_ERROR(dir, "rmdir");
2482 gint rename_force(const gchar *oldpath, const gchar *newpath)
2485 if (!is_file_entry_exist(oldpath)) {
2489 if (is_file_exist(newpath)) {
2490 if (claws_unlink(newpath) < 0)
2491 FILE_OP_ERROR(newpath, "unlink");
2494 return g_rename(oldpath, newpath);
2498 * Append src file body to the tail of dest file.
2499 * Now keep_backup has no effects.
2501 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2503 FILE *src_fp, *dest_fp;
2507 gboolean err = FALSE;
2509 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2510 FILE_OP_ERROR(src, "g_fopen");
2514 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2515 FILE_OP_ERROR(dest, "g_fopen");
2520 if (change_file_mode_rw(dest_fp, dest) < 0) {
2521 FILE_OP_ERROR(dest, "chmod");
2522 g_warning("can't change file mode\n");
2525 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2526 if (n_read < sizeof(buf) && ferror(src_fp))
2528 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2529 g_warning("writing to %s failed.\n", dest);
2537 if (ferror(src_fp)) {
2538 FILE_OP_ERROR(src, "fread");
2542 if (fclose(dest_fp) == EOF) {
2543 FILE_OP_ERROR(dest, "fclose");
2555 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2557 FILE *src_fp, *dest_fp;
2560 gchar *dest_bak = NULL;
2561 gboolean err = FALSE;
2563 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2564 FILE_OP_ERROR(src, "g_fopen");
2567 if (is_file_exist(dest)) {
2568 dest_bak = g_strconcat(dest, ".bak", NULL);
2569 if (rename_force(dest, dest_bak) < 0) {
2570 FILE_OP_ERROR(dest, "rename");
2577 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2578 FILE_OP_ERROR(dest, "g_fopen");
2581 if (rename_force(dest_bak, dest) < 0)
2582 FILE_OP_ERROR(dest_bak, "rename");
2588 if (change_file_mode_rw(dest_fp, dest) < 0) {
2589 FILE_OP_ERROR(dest, "chmod");
2590 g_warning("can't change file mode\n");
2593 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2594 if (n_read < sizeof(buf) && ferror(src_fp))
2596 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2597 g_warning("writing to %s failed.\n", dest);
2602 if (rename_force(dest_bak, dest) < 0)
2603 FILE_OP_ERROR(dest_bak, "rename");
2610 if (ferror(src_fp)) {
2611 FILE_OP_ERROR(src, "fread");
2615 if (fclose(dest_fp) == EOF) {
2616 FILE_OP_ERROR(dest, "fclose");
2623 if (rename_force(dest_bak, dest) < 0)
2624 FILE_OP_ERROR(dest_bak, "rename");
2630 if (keep_backup == FALSE && dest_bak)
2631 claws_unlink(dest_bak);
2638 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2640 if (overwrite == FALSE && is_file_exist(dest)) {
2641 g_warning("move_file(): file %s already exists.", dest);
2645 if (rename_force(src, dest) == 0) return 0;
2647 if (EXDEV != errno) {
2648 FILE_OP_ERROR(src, "rename");
2652 if (copy_file(src, dest, FALSE) < 0) return -1;
2659 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2662 gint bytes_left, to_read;
2665 if (fseek(fp, offset, SEEK_SET) < 0) {
2670 bytes_left = length;
2671 to_read = MIN(bytes_left, sizeof(buf));
2673 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2674 if (n_read < to_read && ferror(fp))
2676 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2679 bytes_left -= n_read;
2680 if (bytes_left == 0)
2682 to_read = MIN(bytes_left, sizeof(buf));
2693 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2696 gboolean err = FALSE;
2698 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2699 FILE_OP_ERROR(dest, "g_fopen");
2703 if (change_file_mode_rw(dest_fp, dest) < 0) {
2704 FILE_OP_ERROR(dest, "chmod");
2705 g_warning("can't change file mode\n");
2708 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2711 if (!err && fclose(dest_fp) == EOF) {
2712 FILE_OP_ERROR(dest, "fclose");
2717 g_warning("writing to %s failed.\n", dest);
2725 /* convert line endings into CRLF. If the last line doesn't end with
2726 * linebreak, add it.
2728 gchar *canonicalize_str(const gchar *str)
2734 for (p = str; *p != '\0'; ++p) {
2741 if (p == str || *(p - 1) != '\n')
2744 out = outp = g_malloc(new_len + 1);
2745 for (p = str; *p != '\0'; ++p) {
2752 if (p == str || *(p - 1) != '\n') {
2761 gint canonicalize_file(const gchar *src, const gchar *dest)
2763 FILE *src_fp, *dest_fp;
2764 gchar buf[BUFFSIZE];
2766 gboolean err = FALSE;
2767 gboolean last_linebreak = FALSE;
2769 if (src == NULL || dest == NULL)
2772 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2773 FILE_OP_ERROR(src, "g_fopen");
2777 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2778 FILE_OP_ERROR(dest, "g_fopen");
2783 if (change_file_mode_rw(dest_fp, dest) < 0) {
2784 FILE_OP_ERROR(dest, "chmod");
2785 g_warning("can't change file mode\n");
2788 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2792 if (len == 0) break;
2793 last_linebreak = FALSE;
2795 if (buf[len - 1] != '\n') {
2796 last_linebreak = TRUE;
2797 r = fputs(buf, dest_fp);
2798 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2799 r = fputs(buf, dest_fp);
2802 r = fwrite(buf, 1, len - 1, dest_fp);
2807 r = fputs("\r\n", dest_fp);
2811 g_warning("writing to %s failed.\n", dest);
2819 if (last_linebreak == TRUE) {
2820 if (fputs("\r\n", dest_fp) == EOF)
2824 if (ferror(src_fp)) {
2825 FILE_OP_ERROR(src, "fgets");
2829 if (fclose(dest_fp) == EOF) {
2830 FILE_OP_ERROR(dest, "fclose");
2842 gint canonicalize_file_replace(const gchar *file)
2846 tmp_file = get_tmp_file();
2848 if (canonicalize_file(file, tmp_file) < 0) {
2853 if (move_file(tmp_file, file, TRUE) < 0) {
2854 g_warning("can't replace %s .\n", file);
2855 claws_unlink(tmp_file);
2864 gchar *normalize_newlines(const gchar *str)
2866 const gchar *p = str;
2869 out = outp = g_malloc(strlen(str) + 1);
2870 for (p = str; *p != '\0'; ++p) {
2872 if (*(p + 1) != '\n')
2883 gchar *get_outgoing_rfc2822_str(FILE *fp)
2885 gchar buf[BUFFSIZE];
2889 str = g_string_new(NULL);
2891 /* output header part */
2892 while (fgets(buf, sizeof(buf), fp) != NULL) {
2894 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2901 else if (next != ' ' && next != '\t') {
2905 if (fgets(buf, sizeof(buf), fp) == NULL)
2909 g_string_append(str, buf);
2910 g_string_append(str, "\r\n");
2916 /* output body part */
2917 while (fgets(buf, sizeof(buf), fp) != NULL) {
2920 g_string_append_c(str, '.');
2921 g_string_append(str, buf);
2922 g_string_append(str, "\r\n");
2926 g_string_free(str, FALSE);
2932 * Create a new boundary in a way that it is very unlikely that this
2933 * will occur in the following text. It would be easy to ensure
2934 * uniqueness if everything is either quoted-printable or base64
2935 * encoded (note that conversion is allowed), but because MIME bodies
2936 * may be nested, it may happen that the same boundary has already
2939 * boundary := 0*69<bchars> bcharsnospace
2940 * bchars := bcharsnospace / " "
2941 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2942 * "+" / "_" / "," / "-" / "." /
2943 * "/" / ":" / "=" / "?"
2945 * some special characters removed because of buggy MTAs
2948 gchar *generate_mime_boundary(const gchar *prefix)
2950 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2951 "abcdefghijklmnopqrstuvwxyz"
2956 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2957 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2960 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2964 gint change_file_mode_rw(FILE *fp, const gchar *file)
2967 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2969 return g_chmod(file, S_IRUSR|S_IWUSR);
2973 FILE *my_tmpfile(void)
2975 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2976 const gchar suffix[] = ".XXXXXX";
2977 const gchar *tmpdir;
2979 const gchar *progname;
2988 tmpdir = get_tmp_dir();
2989 tmplen = strlen(tmpdir);
2990 progname = g_get_prgname();
2991 if (progname == NULL)
2992 progname = "claws-mail";
2993 proglen = strlen(progname);
2994 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2997 memcpy(fname, tmpdir, tmplen);
2998 fname[tmplen] = G_DIR_SEPARATOR;
2999 memcpy(fname + tmplen + 1, progname, proglen);
3000 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3002 fd = mkstemp(fname);
3007 claws_unlink(fname);
3009 /* verify that we can write in the file after unlinking */
3010 if (write(fd, buf, 1) < 0) {
3017 fp = fdopen(fd, "w+b");
3025 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3030 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3034 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3035 dir, G_DIR_SEPARATOR);
3036 fd = mkstemp_name(template, filename);
3039 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3040 fd = mkstemp(*filename);
3042 return fdopen(fd, "w+");
3045 FILE *str_open_as_stream(const gchar *str)
3050 cm_return_val_if_fail(str != NULL, NULL);
3054 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3059 if (len == 0) return fp;
3061 if (fwrite(str, 1, len, fp) != len) {
3062 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3071 gint str_write_to_file(const gchar *str, const gchar *file)
3076 cm_return_val_if_fail(str != NULL, -1);
3077 cm_return_val_if_fail(file != NULL, -1);
3079 if ((fp = g_fopen(file, "wb")) == NULL) {
3080 FILE_OP_ERROR(file, "g_fopen");
3090 if (fwrite(str, 1, len, fp) != len) {
3091 FILE_OP_ERROR(file, "fwrite");
3097 if (fclose(fp) == EOF) {
3098 FILE_OP_ERROR(file, "fclose");
3106 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3113 cm_return_val_if_fail(fp != NULL, NULL);
3115 array = g_byte_array_new();
3117 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3118 if (n_read < sizeof(buf) && ferror(fp))
3120 g_byte_array_append(array, buf, n_read);
3124 FILE_OP_ERROR("file stream", "fread");
3125 g_byte_array_free(array, TRUE);
3130 g_byte_array_append(array, buf, 1);
3131 str = (gchar *)array->data;
3132 g_byte_array_free(array, FALSE);
3134 if (recode && !g_utf8_validate(str, -1, NULL)) {
3135 const gchar *src_codeset, *dest_codeset;
3137 src_codeset = conv_get_locale_charset_str();
3138 dest_codeset = CS_UTF_8;
3139 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3147 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3154 struct timeval timeout = {1, 0};
3159 cm_return_val_if_fail(file != NULL, NULL);
3161 if (g_stat(file, &s) != 0) {
3162 FILE_OP_ERROR(file, "stat");
3165 if (S_ISDIR(s.st_mode)) {
3166 g_warning("%s: is a directory\n", file);
3171 fp = g_fopen (file, "rb");
3173 FILE_OP_ERROR(file, "open");
3177 /* test whether the file is readable without blocking */
3178 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3180 FILE_OP_ERROR(file, "open");
3187 /* allow for one second */
3188 err = select(fd+1, &fds, NULL, NULL, &timeout);
3189 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3191 FILE_OP_ERROR(file, "select");
3193 g_warning("%s: doesn't seem readable\n", file);
3199 /* Now clear O_NONBLOCK */
3200 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3201 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3205 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3206 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3211 /* get the FILE pointer */
3212 fp = fdopen(fd, "rb");
3215 FILE_OP_ERROR(file, "fdopen");
3216 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3221 str = file_read_stream_to_str_full(fp, recode);
3228 gchar *file_read_to_str(const gchar *file)
3230 return file_read_to_str_full(file, TRUE);
3232 gchar *file_read_stream_to_str(FILE *fp)
3234 return file_read_stream_to_str_full(fp, TRUE);
3237 gchar *file_read_to_str_no_recode(const gchar *file)
3239 return file_read_to_str_full(file, FALSE);
3241 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3243 return file_read_stream_to_str_full(fp, FALSE);
3246 char *fgets_crlf(char *buf, int size, FILE *stream)
3248 gboolean is_cr = FALSE;
3249 gboolean last_was_cr = FALSE;
3254 while (--size > 0 && (c = getc(stream)) != EOF)
3257 is_cr = (c == '\r');
3267 last_was_cr = is_cr;
3269 if (c == EOF && cs == buf)
3277 static gint execute_async(gchar *const argv[])
3279 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3281 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3282 NULL, NULL, NULL, FALSE) == FALSE) {
3283 g_warning("Couldn't execute command: %s\n", argv[0]);
3290 static gint execute_sync(gchar *const argv[])
3294 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3297 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3298 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3299 g_warning("Couldn't execute command: %s\n", argv[0]);
3303 if (WIFEXITED(status))
3304 return WEXITSTATUS(status);
3308 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3309 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3310 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3311 g_warning("Couldn't execute command: %s\n", argv[0]);
3319 gint execute_command_line(const gchar *cmdline, gboolean async)
3324 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3326 argv = strsplit_with_quote(cmdline, " ", 0);
3329 ret = execute_async(argv);
3331 ret = execute_sync(argv);
3338 gchar *get_command_output(const gchar *cmdline)
3340 gchar *child_stdout;
3343 cm_return_val_if_fail(cmdline != NULL, NULL);
3345 debug_print("get_command_output(): executing: %s\n", cmdline);
3347 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3349 g_warning("Couldn't execute command: %s\n", cmdline);
3353 return child_stdout;
3356 static gint is_unchanged_uri_char(char c)
3367 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3373 for(i = 0; i < strlen(uri) ; i++) {
3374 if (is_unchanged_uri_char(uri[i])) {
3375 if (k + 2 >= bufsize)
3377 encoded_uri[k++] = uri[i];
3380 char * hexa = "0123456789ABCDEF";
3382 if (k + 4 >= bufsize)
3384 encoded_uri[k++] = '%';
3385 encoded_uri[k++] = hexa[uri[i] / 16];
3386 encoded_uri[k++] = hexa[uri[i] % 16];
3392 gint open_uri(const gchar *uri, const gchar *cmdline)
3396 gchar buf[BUFFSIZE];
3398 gchar encoded_uri[BUFFSIZE];
3399 cm_return_val_if_fail(uri != NULL, -1);
3401 /* an option to choose whether to use encode_uri or not ? */
3402 encode_uri(encoded_uri, BUFFSIZE, uri);
3405 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3406 !strchr(p + 2, '%'))
3407 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3410 g_warning("Open URI command-line is invalid "
3411 "(there must be only one '%%s'): %s",
3413 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3416 execute_command_line(buf, TRUE);
3418 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3421 extern osso_context_t *get_osso_context(void);
3422 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3423 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3424 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3429 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3431 gchar buf[BUFFSIZE];
3434 cm_return_val_if_fail(filepath != NULL, -1);
3437 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3438 !strchr(p + 2, '%'))
3439 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3442 g_warning("Open Text Editor command-line is invalid "
3443 "(there must be only one '%%s'): %s",
3445 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3448 execute_command_line(buf, TRUE);
3453 time_t remote_tzoffset_sec(const gchar *zone)
3455 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3461 time_t remoteoffset;
3463 strncpy(zone3, zone, 3);
3467 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3468 (c == '+' || c == '-')) {
3469 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3471 remoteoffset = -remoteoffset;
3472 } else if (!strncmp(zone, "UT" , 2) ||
3473 !strncmp(zone, "GMT", 2)) {
3475 } else if (strlen(zone3) == 3) {
3476 for (p = ustzstr; *p != '\0'; p += 3) {
3477 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3478 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3479 remoteoffset = iustz * 3600;
3485 } else if (strlen(zone3) == 1) {
3487 case 'Z': remoteoffset = 0; break;
3488 case 'A': remoteoffset = -1; break;
3489 case 'B': remoteoffset = -2; break;
3490 case 'C': remoteoffset = -3; break;
3491 case 'D': remoteoffset = -4; break;
3492 case 'E': remoteoffset = -5; break;
3493 case 'F': remoteoffset = -6; break;
3494 case 'G': remoteoffset = -7; break;
3495 case 'H': remoteoffset = -8; break;
3496 case 'I': remoteoffset = -9; break;
3497 case 'K': remoteoffset = -10; break; /* J is not used */
3498 case 'L': remoteoffset = -11; break;
3499 case 'M': remoteoffset = -12; break;
3500 case 'N': remoteoffset = 1; break;
3501 case 'O': remoteoffset = 2; break;
3502 case 'P': remoteoffset = 3; break;
3503 case 'Q': remoteoffset = 4; break;
3504 case 'R': remoteoffset = 5; break;
3505 case 'S': remoteoffset = 6; break;
3506 case 'T': remoteoffset = 7; break;
3507 case 'U': remoteoffset = 8; break;
3508 case 'V': remoteoffset = 9; break;
3509 case 'W': remoteoffset = 10; break;
3510 case 'X': remoteoffset = 11; break;
3511 case 'Y': remoteoffset = 12; break;
3512 default: remoteoffset = 0; break;
3514 remoteoffset = remoteoffset * 3600;
3518 return remoteoffset;
3521 time_t tzoffset_sec(time_t *now)
3526 struct tm buf1, buf2;
3529 if (now && *now < 0)
3532 gmt = *gmtime_r(now, &buf1);
3533 lt = localtime_r(now, &buf2);
3535 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3537 if (lt->tm_year < gmt.tm_year)
3539 else if (lt->tm_year > gmt.tm_year)
3541 else if (lt->tm_yday < gmt.tm_yday)
3543 else if (lt->tm_yday > gmt.tm_yday)
3546 if (off >= 24 * 60) /* should be impossible */
3547 off = 23 * 60 + 59; /* if not, insert silly value */
3548 if (off <= -24 * 60)
3549 off = -(23 * 60 + 59);
3554 /* calculate timezone offset */
3555 gchar *tzoffset(time_t *now)
3557 static gchar offset_string[6];
3562 struct tm buf1, buf2;
3565 if (now && *now < 0)
3568 gmt = *gmtime_r(now, &buf1);
3569 lt = localtime_r(now, &buf2);
3571 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3573 if (lt->tm_year < gmt.tm_year)
3575 else if (lt->tm_year > gmt.tm_year)
3577 else if (lt->tm_yday < gmt.tm_yday)
3579 else if (lt->tm_yday > gmt.tm_yday)
3587 if (off >= 24 * 60) /* should be impossible */
3588 off = 23 * 60 + 59; /* if not, insert silly value */
3590 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3592 return offset_string;
3595 void get_rfc822_date(gchar *buf, gint len)
3599 gchar day[4], mon[4];
3600 gint dd, hh, mm, ss, yyyy;
3603 gchar buf2[BUFFSIZE];
3607 lt = localtime_r(&t, &buf1);
3609 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3610 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3612 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3613 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3616 void debug_set_mode(gboolean mode)
3621 gboolean debug_get_mode(void)
3626 void debug_print_real(const gchar *format, ...)
3629 gchar buf[BUFFSIZE];
3631 if (!debug_mode) return;
3633 va_start(args, format);
3634 g_vsnprintf(buf, sizeof(buf), format, args);
3641 const char * debug_srcname(const char *file)
3643 const char *s = strrchr (file, '/');
3648 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3650 if (subject == NULL)
3653 subject += subject_get_prefix_length(subject);
3655 return g_hash_table_lookup(subject_table, subject);
3658 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3661 if (subject == NULL || *subject == 0)
3663 subject += subject_get_prefix_length(subject);
3664 g_hash_table_insert(subject_table, subject, data);
3667 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3669 if (subject == NULL)
3672 subject += subject_get_prefix_length(subject);
3673 g_hash_table_remove(subject_table, subject);
3677 static regex_t u_regex;
3678 static gboolean u_init_;
3681 void utils_free_regex(void)
3692 *\brief Check if a string is prefixed with known (combinations)
3693 * of prefixes. The function assumes that each prefix
3694 * is terminated by zero or exactly _one_ space.
3696 *\param str String to check for a prefixes
3698 *\return int Number of chars in the prefix that should be skipped
3699 * for a "clean" subject line. If no prefix was found, 0
3702 int subject_get_prefix_length(const gchar *subject)
3705 /*!< Array with allowable reply prefixes regexps. */
3706 static const gchar * const prefixes[] = {
3707 "Re\\:", /* "Re:" */
3708 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3709 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3710 "Aw\\:", /* "Aw:" (German) */
3711 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3712 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3713 "Fw\\:", /* "Fw:" Forward */
3714 "Fwd\\:", /* "Fwd:" Forward */
3715 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3716 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3717 "Rif\\:", /* "Rif:" (Italian Outlook) */
3718 "Sv\\:", /* "Sv" (Norwegian) */
3719 "Vs\\:", /* "Vs" (Norwegian) */
3720 "Ad\\:", /* "Ad" (Norwegian) */
3721 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3722 "R\303\251f\\. \\:", /* "Réf. :" (French Lotus Notes) */
3725 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3729 if (!subject) return 0;
3730 if (!*subject) return 0;
3733 GString *s = g_string_new("");
3735 for (n = 0; n < PREFIXES; n++)
3736 /* Terminate each prefix regexpression by a
3737 * "\ ?" (zero or ONE space), and OR them */
3738 g_string_append_printf(s, "(%s\\ ?)%s",
3743 g_string_prepend(s, "(");
3744 g_string_append(s, ")+"); /* match at least once */
3745 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3748 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3749 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3750 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3751 debug_print("Error compiling regexp %s\n", s->str);
3752 g_string_free(s, TRUE);
3756 g_string_free(s, TRUE);
3760 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3765 /*!< Array with allowable reply prefixes regexps. */
3766 static const gchar * const prefixes[] = {
3768 "antw:", /* "Antw:" (Dutch / German Outlook) */
3769 "aw:", /* "Aw:" (German) */
3770 "antwort:", /* "Antwort:" (German Lotus Notes) */
3771 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3772 "fw:", /* "Fw:" Forward */
3773 "fwd:", /* "Fwd:" Forward */
3774 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3775 "odp:", /* "Odp:" Re (Polish Outlook) */
3776 "rif:", /* "Rif:" (Italian Outlook) */
3777 "sv:", /* "Sv" (Norwegian) */
3778 "vs:", /* "Vs" (Norwegian) */
3779 "ad:", /* "Ad" (Norwegian) */
3780 "R\303\251f. :", /* "Réf. :" (French Lotus Notes) */
3783 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3786 if (!subject) return 0;
3787 if (!*subject) return 0;
3789 for (n = 0; n < PREFIXES; n++) {
3790 int len = strlen(prefixes[n]);
3791 if (!strncasecmp(subject, prefixes[n], len)) {
3792 if (subject[len] == ' ')
3801 static guint g_stricase_hash(gconstpointer gptr)
3803 guint hash_result = 0;
3806 for (str = gptr; str && *str; str++) {
3807 hash_result += toupper(*str);
3813 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3815 const char *str1 = gptr1;
3816 const char *str2 = gptr2;
3818 return !strcasecmp(str1, str2);
3821 gint g_int_compare(gconstpointer a, gconstpointer b)
3823 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3826 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3836 lt = localtime_r(&t, &buft);
3838 if (strcmp(buf, "") == 0) {
3839 if (user_addr != NULL) {
3840 addr = g_strconcat(user_addr, "@", get_domain_name(), NULL);
3843 addr = g_strconcat("@", get_domain_name(), NULL);
3847 if (user_addr != NULL) {
3848 addr = g_strconcat(user_addr, "@", buf, NULL);
3851 addr = g_strconcat("@", buf, NULL);
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 if (!strncmp(buffer, "To:", strlen("To:")))
4849 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4854 return (score >= 3);
4857 gboolean sc_g_list_bigger(GList *list, gint max)
4861 while (cur && i <= max+1) {
4868 gboolean sc_g_slist_bigger(GSList *list, gint max)
4872 while (cur && i <= max+1) {
4879 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4880 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4881 NULL, NULL, NULL, NULL, NULL, NULL};
4882 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4883 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4884 NULL, NULL, NULL, NULL, NULL, NULL};
4886 gint daynames_len[] = {0,0,0,0,0,0,0};
4887 gint monthnames_len[] = {0,0,0,0,0,0,
4889 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4890 gint s_monthnames_len[] = {0,0,0,0,0,0,
4892 const gchar *s_am_up = NULL;
4893 const gchar *s_pm_up = NULL;
4894 const gchar *s_am_low = NULL;
4895 const gchar *s_pm_low = NULL;
4897 gint s_am_up_len = 0;
4898 gint s_pm_up_len = 0;
4899 gint s_am_low_len = 0;
4900 gint s_pm_low_len = 0;
4902 const gchar *def_loc_format = NULL;
4903 const gchar *date_loc_format = NULL;
4904 const gchar *time_loc_format = NULL;
4905 const gchar *time_am_pm = NULL;
4907 static gboolean time_names_init_done = FALSE;
4909 static void init_time_names(void)
4913 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4914 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4915 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4916 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4917 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4918 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4919 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4921 monthnames[0] = Q_("Complete month name for use by strftime|January");
4922 monthnames[1] = Q_("Complete month name for use by strftime|February");
4923 monthnames[2] = Q_("Complete month name for use by strftime|March");
4924 monthnames[3] = Q_("Complete month name for use by strftime|April");
4925 monthnames[4] = Q_("Complete month name for use by strftime|May");
4926 monthnames[5] = Q_("Complete month name for use by strftime|June");
4927 monthnames[6] = Q_("Complete month name for use by strftime|July");
4928 monthnames[7] = Q_("Complete month name for use by strftime|August");
4929 monthnames[8] = Q_("Complete month name for use by strftime|September");
4930 monthnames[9] = Q_("Complete month name for use by strftime|October");
4931 monthnames[10] = Q_("Complete month name for use by strftime|November");
4932 monthnames[11] = Q_("Complete month name for use by strftime|December");
4934 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4935 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4936 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4937 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4938 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4939 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4940 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4942 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4943 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4944 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4945 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4946 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4947 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4948 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4949 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4950 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4951 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4952 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4953 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4955 for (i = 0; i < 7; i++) {
4956 daynames_len[i] = strlen(daynames[i]);
4957 s_daynames_len[i] = strlen(s_daynames[i]);
4959 for (i = 0; i < 12; i++) {
4960 monthnames_len[i] = strlen(monthnames[i]);
4961 s_monthnames_len[i] = strlen(s_monthnames[i]);
4964 s_am_up = Q_("For use by strftime (morning)|AM");
4965 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4966 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4967 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4969 s_am_up_len = strlen(s_am_up);
4970 s_pm_up_len = strlen(s_pm_up);
4971 s_am_low_len = strlen(s_am_low);
4972 s_pm_low_len = strlen(s_pm_low);
4974 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4975 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4976 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4978 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4980 time_names_init_done = TRUE;
4983 #define CHECK_SIZE() { \
4984 total_done += len; \
4985 if (total_done >= buflen) { \
4986 buf[buflen-1] = '\0'; \
4991 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4993 gchar *curpos = buf;
4994 gint total_done = 0;
4995 gchar subbuf[64], subfmt[64];
4996 static time_t last_tzset = (time_t)0;
4998 if (!time_names_init_done)
5001 if (format == NULL || lt == NULL)
5004 if (last_tzset != time(NULL)) {
5006 last_tzset = time(NULL);
5009 if (*format == '%') {
5010 gint len = 0, tmp = 0;
5014 len = 1; CHECK_SIZE();
5018 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5019 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5022 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5023 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5027 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5028 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5031 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5032 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5035 fast_strftime(subbuf, 64, def_loc_format, lt);
5036 len = strlen(subbuf); CHECK_SIZE();
5037 strncpy2(curpos, subbuf, buflen - total_done);
5040 total_done += 2; CHECK_SIZE();
5041 tmp = (lt->tm_year + 1900)/100;
5042 *curpos++ = '0'+(tmp / 10);
5043 *curpos++ = '0'+(tmp % 10);
5046 total_done += 2; CHECK_SIZE();
5047 *curpos++ = '0'+(lt->tm_mday / 10);
5048 *curpos++ = '0'+(lt->tm_mday % 10);
5051 total_done += 8; CHECK_SIZE();
5052 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5053 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5055 *curpos++ = '0'+(lt->tm_mday / 10);
5056 *curpos++ = '0'+(lt->tm_mday % 10);
5058 tmp = lt->tm_year%100;
5059 *curpos++ = '0'+(tmp / 10);
5060 *curpos++ = '0'+(tmp % 10);
5063 len = 2; CHECK_SIZE();
5064 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5067 len = 10; CHECK_SIZE();
5068 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5069 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5072 total_done += 2; CHECK_SIZE();
5073 *curpos++ = '0'+(lt->tm_hour / 10);
5074 *curpos++ = '0'+(lt->tm_hour % 10);
5077 total_done += 2; CHECK_SIZE();
5083 *curpos++ = '0'+(tmp / 10);
5084 *curpos++ = '0'+(tmp % 10);
5087 len = 3; CHECK_SIZE();
5088 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5091 len = 2; CHECK_SIZE();
5092 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5095 len = 2; CHECK_SIZE();
5101 snprintf(curpos, buflen - total_done, "%2d", tmp);
5104 total_done += 2; CHECK_SIZE();
5105 tmp = lt->tm_mon + 1;
5106 *curpos++ = '0'+(tmp / 10);
5107 *curpos++ = '0'+(tmp % 10);
5110 total_done += 2; CHECK_SIZE();
5111 *curpos++ = '0'+(lt->tm_min / 10);
5112 *curpos++ = '0'+(lt->tm_min % 10);
5115 len = 1; CHECK_SIZE();
5119 if (lt->tm_hour >= 12) {
5120 len = s_pm_up_len; CHECK_SIZE();
5121 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5123 len = s_am_up_len; CHECK_SIZE();
5124 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5128 if (lt->tm_hour >= 12) {
5129 len = s_pm_low_len; CHECK_SIZE();
5130 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5132 len = s_am_low_len; CHECK_SIZE();
5133 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5137 fast_strftime(subbuf, 64, time_am_pm, lt);
5138 len = strlen(subbuf); CHECK_SIZE();
5139 strncpy2(curpos, subbuf, buflen - total_done);
5142 total_done += 5; CHECK_SIZE();
5143 *curpos++ = '0'+(lt->tm_hour / 10);
5144 *curpos++ = '0'+(lt->tm_hour % 10);
5146 *curpos++ = '0'+(lt->tm_min / 10);
5147 *curpos++ = '0'+(lt->tm_min % 10);
5150 snprintf(subbuf, 64, "%ld", mktime(lt));
5151 len = strlen(subbuf); CHECK_SIZE();
5152 strncpy2(curpos, subbuf, buflen - total_done);
5155 total_done += 2; CHECK_SIZE();
5156 *curpos++ = '0'+(lt->tm_sec / 10);
5157 *curpos++ = '0'+(lt->tm_sec % 10);
5160 len = 1; CHECK_SIZE();
5164 total_done += 8; CHECK_SIZE();
5165 *curpos++ = '0'+(lt->tm_hour / 10);
5166 *curpos++ = '0'+(lt->tm_hour % 10);
5168 *curpos++ = '0'+(lt->tm_min / 10);
5169 *curpos++ = '0'+(lt->tm_min % 10);
5171 *curpos++ = '0'+(lt->tm_sec / 10);
5172 *curpos++ = '0'+(lt->tm_sec % 10);
5175 len = 1; CHECK_SIZE();
5176 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5179 len = 1; CHECK_SIZE();
5180 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5183 fast_strftime(subbuf, 64, date_loc_format, lt);
5184 len = strlen(subbuf); CHECK_SIZE();
5185 strncpy2(curpos, subbuf, buflen - total_done);
5188 fast_strftime(subbuf, 64, time_loc_format, lt);
5189 len = strlen(subbuf); CHECK_SIZE();
5190 strncpy2(curpos, subbuf, buflen - total_done);
5193 total_done += 2; CHECK_SIZE();
5194 tmp = lt->tm_year%100;
5195 *curpos++ = '0'+(tmp / 10);
5196 *curpos++ = '0'+(tmp % 10);
5199 len = 4; CHECK_SIZE();
5200 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5210 /* let these complicated ones be done with the libc */
5211 snprintf(subfmt, 64, "%%%c", *format);
5212 strftime(subbuf, 64, subfmt, lt);
5213 len = strlen(subbuf); CHECK_SIZE();
5214 strncpy2(curpos, subbuf, buflen - total_done);
5218 /* let these complicated modifiers be done with the libc */
5219 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5220 strftime(subbuf, 64, subfmt, lt);
5221 len = strlen(subbuf); CHECK_SIZE();
5222 strncpy2(curpos, subbuf, buflen - total_done);
5226 if (format && *format)
5227 g_warning("format error (%c)", *format);
5234 int len = 1; CHECK_SIZE();
5235 *curpos++ = *format++;
5242 gboolean prefs_common_get_use_shred(void);
5246 #define WEXITSTATUS(x) (x)
5249 int claws_unlink(const gchar *filename)
5252 static int found_shred = -1;
5253 static const gchar *args[4];
5255 if (filename == NULL)
5258 if (prefs_common_get_use_shred()) {
5259 if (found_shred == -1) {
5261 args[0] = g_find_program_in_path("shred");
5262 debug_print("found shred: %s\n", args[0]);
5263 found_shred = (args[0] != NULL) ? 1:0;
5267 if (found_shred == 1) {
5268 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5269 if (s.st_nlink == 1) {
5272 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5273 NULL, NULL, NULL, NULL, &status, NULL);
5274 debug_print("%s %s exited with status %d\n",
5275 args[0], filename, WEXITSTATUS(status));
5276 if (truncate(filename, 0) < 0)
5277 g_warning("couln't truncate");
5282 return g_unlink(filename);