2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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);
187 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
191 gint mkstemp_name(gchar *template, gchar **name_used)
193 static gulong count=0; /* W32-_mktemp only supports up to 27
197 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
198 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
199 (S_IRUSR | S_IWUSR));
201 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
203 perror(g_strdup_printf("cant create %s",*name_used));
209 #endif /* G_OS_WIN32 */
212 gint mkstemp(gchar *template)
215 gint res = mkstemp_name(template, &dummyname);
219 #endif /* G_OS_WIN32 */
221 void list_free_strings(GList *list)
223 list = g_list_first(list);
225 while (list != NULL) {
231 void slist_free_strings(GSList *list)
233 while (list != NULL) {
239 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
244 void hash_free_strings(GHashTable *table)
246 g_hash_table_foreach(table, hash_free_strings_func, NULL);
249 gint str_case_equal(gconstpointer v, gconstpointer v2)
251 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
254 guint str_case_hash(gconstpointer key)
256 const gchar *p = key;
260 h = g_ascii_tolower(h);
261 for (p += 1; *p != '\0'; p++)
262 h = (h << 5) - h + g_ascii_tolower(*p);
268 void ptr_array_free_strings(GPtrArray *array)
273 g_return_if_fail(array != NULL);
275 for (i = 0; i < array->len; i++) {
276 str = g_ptr_array_index(array, i);
281 gboolean str_find(const gchar *haystack, const gchar *needle)
283 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
286 gboolean str_case_find(const gchar *haystack, const gchar *needle)
288 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
291 gint to_number(const gchar *nstr)
293 register const gchar *p;
295 if (*nstr == '\0') return -1;
297 for (p = nstr; *p != '\0'; p++)
298 if (!g_ascii_isdigit(*p)) return -1;
303 /* convert integer into string,
304 nstr must be not lower than 11 characters length */
305 gchar *itos_buf(gchar *nstr, gint n)
307 g_snprintf(nstr, 11, "%d", n);
311 /* convert integer into string */
314 static gchar nstr[11];
316 return itos_buf(nstr, n);
319 #define divide(num,divisor,i,d) \
321 i = num >> divisor; \
322 d = num & ((1<<divisor)-1); \
323 d = (d*100) >> divisor; \
326 gchar *to_human_readable(goffset size)
328 static gchar str[14];
329 static gchar *b_format = NULL, *kb_format = NULL,
330 *mb_format = NULL, *gb_format = NULL;
331 register int t = 0, r = 0;
332 if (b_format == NULL) {
334 kb_format = _("%d.%02dKB");
335 mb_format = _("%d.%02dMB");
336 gb_format = _("%.2fGB");
339 if (size < (goffset)1024) {
340 g_snprintf(str, sizeof(str), b_format, (gint)size);
342 } else if (size >> 10 < (goffset)1024) {
343 divide(size, 10, t, r);
344 g_snprintf(str, sizeof(str), kb_format, t, r);
346 } else if (size >> 20 < (goffset)1024) {
347 divide(size, 20, t, r);
348 g_snprintf(str, sizeof(str), mb_format, t, r);
351 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
356 /* strcmp with NULL-checking */
357 gint strcmp2(const gchar *s1, const gchar *s2)
359 if (s1 == NULL || s2 == NULL)
362 return strcmp(s1, s2);
364 /* strstr with NULL-checking */
365 gchar *strstr2(const gchar *s1, const gchar *s2)
367 if (s1 == NULL || s2 == NULL)
370 return strstr(s1, s2);
373 gint path_cmp(const gchar *s1, const gchar *s2)
378 gchar *s1buf, *s2buf;
381 if (s1 == NULL || s2 == NULL) return -1;
382 if (*s1 == '\0' || *s2 == '\0') return -1;
385 s1buf = g_strdup (s1);
386 s2buf = g_strdup (s2);
387 subst_char (s1buf, '/', G_DIR_SEPARATOR);
388 subst_char (s2buf, '/', G_DIR_SEPARATOR);
391 #endif /* !G_OS_WIN32 */
396 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
397 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
399 rc = strncmp(s1, s2, MAX(len1, len2));
403 #endif /* !G_OS_WIN32 */
407 /* remove trailing return code */
408 gchar *strretchomp(gchar *str)
412 if (!*str) return str;
414 for (s = str + strlen(str) - 1;
415 s >= str && (*s == '\n' || *s == '\r');
422 /* remove trailing character */
423 gchar *strtailchomp(gchar *str, gchar tail_char)
427 if (!*str) return str;
428 if (tail_char == '\0') return str;
430 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
436 /* remove CR (carriage return) */
437 gchar *strcrchomp(gchar *str)
441 if (!*str) return str;
443 s = str + strlen(str) - 1;
444 if (*s == '\n' && s > str && *(s - 1) == '\r') {
452 gint file_strip_crs(const gchar *file)
454 FILE *fp = NULL, *outfp = NULL;
456 gchar *out = get_tmp_file();
460 fp = fopen(file, "rb");
464 outfp = fopen(out, "wb");
470 while (fgets(buf, sizeof (buf), fp) != NULL) {
472 if (fputs(buf, outfp) == EOF) {
480 if (fclose(outfp) == EOF) {
484 if (rename_force(out, file) < 0)
496 /* Similar to `strstr' but this function ignores the case of both strings. */
497 gchar *strcasestr(const gchar *haystack, const gchar *needle)
499 register size_t haystack_len, needle_len;
501 haystack_len = strlen(haystack);
502 needle_len = strlen(needle);
504 if (haystack_len < needle_len || needle_len == 0)
507 while (haystack_len >= needle_len) {
508 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
509 return (gchar *)haystack;
519 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
520 gconstpointer needle, size_t needlelen)
522 const gchar *haystack_ = (const gchar *)haystack;
523 const gchar *needle_ = (const gchar *)needle;
524 const gchar *haystack_cur = (const gchar *)haystack;
525 size_t haystack_left = haystacklen;
528 return memchr(haystack_, *needle_, haystacklen);
530 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
532 if (haystacklen - (haystack_cur - haystack_) < needlelen)
534 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
535 return (gpointer)haystack_cur;
538 haystack_left = haystacklen - (haystack_cur - haystack_);
545 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
546 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
548 register const gchar *s = src;
549 register gchar *d = dest;
559 /* Examine if next block is non-ASCII string */
560 gboolean is_next_nonascii(const gchar *s)
564 /* skip head space */
565 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
567 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
568 if (*(guchar *)p > 127 || *(guchar *)p < 32)
575 gint get_next_word_len(const gchar *s)
579 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
585 static void trim_subject_for_compare(gchar *str)
589 eliminate_parenthesis(str, '[', ']');
590 eliminate_parenthesis(str, '(', ')');
593 srcp = str + subject_get_prefix_length(str);
595 memmove(str, srcp, strlen(srcp) + 1);
598 static void trim_subject_for_sort(gchar *str)
604 srcp = str + subject_get_prefix_length(str);
606 memmove(str, srcp, strlen(srcp) + 1);
609 /* compare subjects */
610 gint subject_compare(const gchar *s1, const gchar *s2)
614 if (!s1 || !s2) return -1;
615 if (!*s1 || !*s2) return -1;
617 Xstrdup_a(str1, s1, return -1);
618 Xstrdup_a(str2, s2, return -1);
620 trim_subject_for_compare(str1);
621 trim_subject_for_compare(str2);
623 if (!*str1 || !*str2) return -1;
625 return strcmp(str1, str2);
628 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
632 if (!s1 || !s2) return -1;
634 Xstrdup_a(str1, s1, return -1);
635 Xstrdup_a(str2, s2, return -1);
637 trim_subject_for_sort(str1);
638 trim_subject_for_sort(str2);
640 return g_utf8_collate(str1, str2);
643 void trim_subject(gchar *str)
645 register gchar *srcp;
651 srcp = str + subject_get_prefix_length(str);
656 } else if (*srcp == '(') {
668 else if (*srcp == cl)
675 while (g_ascii_isspace(*srcp)) srcp++;
676 memmove(str, srcp, strlen(srcp) + 1);
679 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
681 register gchar *srcp, *destp;
686 while ((destp = strchr(destp, op))) {
692 else if (*srcp == cl)
698 while (g_ascii_isspace(*srcp)) srcp++;
699 memmove(destp, srcp, strlen(srcp) + 1);
703 void extract_parenthesis(gchar *str, gchar op, gchar cl)
705 register gchar *srcp, *destp;
710 while ((srcp = strchr(destp, op))) {
713 memmove(destp, srcp + 1, strlen(srcp));
718 else if (*destp == cl)
730 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
733 register gchar *srcp, *destp;
735 gboolean in_quote = FALSE;
739 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
742 memmove(destp, srcp + 1, strlen(srcp));
745 if (*destp == op && !in_quote)
747 else if (*destp == cl && !in_quote)
749 else if (*destp == quote_chr)
761 void extract_quote(gchar *str, gchar quote_chr)
765 if ((str = strchr(str, quote_chr))) {
767 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
768 memmove(p - 1, p, strlen(p) + 1);
773 memmove(str, str + 1, p - str);
778 void eliminate_address_comment(gchar *str)
780 register gchar *srcp, *destp;
785 while ((destp = strchr(destp, '"'))) {
786 if ((srcp = strchr(destp + 1, '"'))) {
791 while (g_ascii_isspace(*srcp)) srcp++;
792 memmove(destp, srcp, strlen(srcp) + 1);
802 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
808 else if (*srcp == ')')
814 while (g_ascii_isspace(*srcp)) srcp++;
815 memmove(destp, srcp, strlen(srcp) + 1);
819 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
821 gboolean in_quote = FALSE;
824 if (*str == c && !in_quote)
826 if (*str == quote_chr)
834 void extract_address(gchar *str)
836 eliminate_address_comment(str);
837 if (strchr_with_skip_quote(str, '"', '<'))
838 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
842 void extract_list_id_str(gchar *str)
844 if (strchr_with_skip_quote(str, '"', '<'))
845 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
849 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
854 if (!str) return addr_list;
856 Xstrdup_a(work, str, return addr_list);
859 eliminate_address_comment(work);
862 while (workp && *workp) {
865 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
871 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
872 extract_parenthesis_with_skip_quote
873 (workp, '"', '<', '>');
877 addr_list = g_slist_append(addr_list, g_strdup(workp));
885 GSList *address_list_append(GSList *addr_list, const gchar *str)
887 return address_list_append_real(addr_list, str, TRUE);
890 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
892 return address_list_append_real(addr_list, str, FALSE);
895 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
899 if (!str) return msgid_list;
902 while (strp && *strp) {
903 const gchar *start, *end;
906 if ((start = strchr(strp, '<')) != NULL) {
907 end = strchr(start + 1, '>');
912 msgid = g_strndup(start + 1, end - start - 1);
915 msgid_list = g_slist_prepend(msgid_list, msgid);
925 GSList *references_list_append(GSList *msgid_list, const gchar *str)
929 list = references_list_prepend(NULL, str);
930 list = g_slist_reverse(list);
931 msgid_list = g_slist_concat(msgid_list, list);
936 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
941 if (!str) return group_list;
943 Xstrdup_a(work, str, return group_list);
947 while (workp && *workp) {
950 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
958 group_list = g_slist_append(group_list,
967 GList *add_history(GList *list, const gchar *str)
971 g_return_val_if_fail(str != NULL, list);
973 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
976 list = g_list_remove(list, old->data);
977 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
980 last = g_list_last(list);
983 list = g_list_remove(list, last->data);
987 list = g_list_prepend(list, g_strdup(str));
992 void remove_return(gchar *str)
994 register gchar *p = str;
997 if (*p == '\n' || *p == '\r')
998 memmove(p, p + 1, strlen(p));
1004 void remove_space(gchar *str)
1006 register gchar *p = str;
1011 while (g_ascii_isspace(*(p + spc)))
1014 memmove(p, p + spc, strlen(p + spc) + 1);
1020 void unfold_line(gchar *str)
1022 register gchar *p = str;
1026 if (*p == '\n' || *p == '\r') {
1029 while (g_ascii_isspace(*(p + spc)))
1032 memmove(p, p + spc, strlen(p + spc) + 1);
1038 void subst_char(gchar *str, gchar orig, gchar subst)
1040 register gchar *p = str;
1049 void subst_chars(gchar *str, gchar *orig, gchar subst)
1051 register gchar *p = str;
1054 if (strchr(orig, *p) != NULL)
1060 void subst_for_filename(gchar *str)
1065 subst_chars(str, "\t\r\n\\/*:", '_');
1067 subst_chars(str, "\t\r\n\\/*", '_');
1071 void subst_for_shellsafe_filename(gchar *str)
1075 subst_for_filename(str);
1076 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1079 gboolean is_ascii_str(const gchar *str)
1081 const guchar *p = (const guchar *)str;
1083 while (*p != '\0') {
1084 if (*p != '\t' && *p != ' ' &&
1085 *p != '\r' && *p != '\n' &&
1086 (*p < 32 || *p >= 127))
1094 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1096 gchar * position = NULL;
1097 gchar * tmp_pos = NULL;
1100 if (quote_chars == NULL)
1103 for (i = 0; i < strlen(quote_chars); i++) {
1104 tmp_pos = strrchr (str, quote_chars[i]);
1106 || (tmp_pos != NULL && position <= tmp_pos) )
1112 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1114 const gchar *first_pos;
1115 const gchar *last_pos;
1116 const gchar *p = str;
1117 gint quote_level = -1;
1119 /* speed up line processing by only searching to the last '>' */
1120 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1121 /* skip a line if it contains a '<' before the initial '>' */
1122 if (memchr(str, '<', first_pos - str) != NULL)
1124 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1128 while (p <= last_pos) {
1129 while (p < last_pos) {
1130 if (g_ascii_isspace(*p))
1136 if (strchr(quote_chars, *p))
1138 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1139 /* any characters are allowed except '-' and space */
1141 && !strchr(quote_chars, *p)
1142 && !g_ascii_isspace(*p)
1145 if (strchr(quote_chars, *p))
1157 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1159 const gchar *p = str, *q;
1160 gint cur_line = 0, len;
1162 while ((q = strchr(p, '\n')) != NULL) {
1164 if (len > max_chars) {
1174 if (len > max_chars) {
1183 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1185 gchar * position = NULL;
1186 gchar * tmp_pos = NULL;
1189 if (quote_chars == NULL)
1192 for (i = 0; i < strlen(quote_chars); i++) {
1193 tmp_pos = strchr (str, quote_chars[i]);
1195 || (tmp_pos != NULL && position >= tmp_pos) )
1201 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1203 register guint haystack_len, needle_len;
1204 gboolean in_squote = FALSE, in_dquote = FALSE;
1206 haystack_len = strlen(haystack);
1207 needle_len = strlen(needle);
1209 if (haystack_len < needle_len || needle_len == 0)
1212 while (haystack_len >= needle_len) {
1213 if (!in_squote && !in_dquote &&
1214 !strncmp(haystack, needle, needle_len))
1215 return (gchar *)haystack;
1217 /* 'foo"bar"' -> foo"bar"
1218 "foo'bar'" -> foo'bar' */
1219 if (*haystack == '\'') {
1222 else if (!in_dquote)
1224 } else if (*haystack == '\"') {
1227 else if (!in_squote)
1238 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1241 GSList *string_list = NULL, *slist;
1242 gchar **str_array, *s, *new_str;
1243 guint i, n = 1, len;
1245 g_return_val_if_fail(str != NULL, NULL);
1246 g_return_val_if_fail(delim != NULL, NULL);
1249 max_tokens = G_MAXINT;
1251 s = strstr_with_skip_quote(str, delim);
1253 guint delimiter_len = strlen(delim);
1257 new_str = g_strndup(str, len);
1259 if (new_str[0] == '\'' || new_str[0] == '\"') {
1260 if (new_str[len - 1] == new_str[0]) {
1261 new_str[len - 1] = '\0';
1262 memmove(new_str, new_str + 1, len - 1);
1265 string_list = g_slist_prepend(string_list, new_str);
1267 str = s + delimiter_len;
1268 s = strstr_with_skip_quote(str, delim);
1269 } while (--max_tokens && s);
1273 new_str = g_strdup(str);
1274 if (new_str[0] == '\'' || new_str[0] == '\"') {
1276 if (new_str[len - 1] == new_str[0]) {
1277 new_str[len - 1] = '\0';
1278 memmove(new_str, new_str + 1, len - 1);
1281 string_list = g_slist_prepend(string_list, new_str);
1285 str_array = g_new(gchar*, n);
1289 str_array[i--] = NULL;
1290 for (slist = string_list; slist; slist = slist->next)
1291 str_array[i--] = slist->data;
1293 g_slist_free(string_list);
1298 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1300 gchar *abbrev_group;
1302 const gchar *p = group;
1305 g_return_val_if_fail(group != NULL, NULL);
1307 last = group + strlen(group);
1308 abbrev_group = ap = g_malloc(strlen(group) + 1);
1313 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1315 while (*p != '.') p++;
1318 return abbrev_group;
1323 return abbrev_group;
1326 gchar *trim_string(const gchar *str, gint len)
1328 const gchar *p = str;
1333 if (!str) return NULL;
1334 if (strlen(str) <= len)
1335 return g_strdup(str);
1336 if (g_utf8_validate(str, -1, NULL) == FALSE)
1337 return g_strdup(str);
1339 while (*p != '\0') {
1340 mb_len = g_utf8_skip[*(guchar *)p];
1343 else if (new_len + mb_len > len)
1350 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1351 return g_strconcat(new_str, "...", NULL);
1354 GList *uri_list_extract_filenames(const gchar *uri_list)
1356 GList *result = NULL;
1358 gchar *escaped_utf8uri;
1364 while (g_ascii_isspace(*p)) p++;
1365 if (!strncmp(p, "file:", 5)) {
1368 while (*q && *q != '\n' && *q != '\r') q++;
1371 gchar *file, *locale_file = NULL;
1373 while (q > p && g_ascii_isspace(*q))
1375 Xalloca(escaped_utf8uri, q - p + 2,
1377 Xalloca(file, q - p + 2,
1380 strncpy(escaped_utf8uri, p, q - p + 1);
1381 escaped_utf8uri[q - p + 1] = '\0';
1382 decode_uri(file, escaped_utf8uri);
1384 * g_filename_from_uri() rejects escaped/locale encoded uri
1385 * string which come from Nautilus.
1387 if (g_utf8_validate(file, -1, NULL))
1389 = conv_codeset_strdup(
1392 conv_get_locale_charset_str());
1394 locale_file = g_strdup(file + 5);
1395 result = g_list_append(result, locale_file);
1399 p = strchr(p, '\n');
1406 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1409 static gint axtoi(const gchar *hexstr)
1411 gint hi, lo, result;
1414 if ('0' <= hi && hi <= '9') {
1417 if ('a' <= hi && hi <= 'f') {
1420 if ('A' <= hi && hi <= 'F') {
1425 if ('0' <= lo && lo <= '9') {
1428 if ('a' <= lo && lo <= 'f') {
1431 if ('A' <= lo && lo <= 'F') {
1434 result = lo + (16 * hi);
1438 gboolean is_uri_string(const gchar *str)
1440 while (str && *str && g_ascii_isspace(*str))
1442 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1443 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1444 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1445 g_ascii_strncasecmp(str, "www.", 4) == 0);
1448 gchar *get_uri_path(const gchar *uri)
1450 while (uri && *uri && g_ascii_isspace(*uri))
1452 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1453 return (gchar *)(uri + 7);
1454 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1455 return (gchar *)(uri + 8);
1456 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1457 return (gchar *)(uri + 6);
1459 return (gchar *)uri;
1462 gint get_uri_len(const gchar *str)
1466 if (is_uri_string(str)) {
1467 for (p = str; *p != '\0'; p++) {
1468 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1477 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1478 * plusses, and escape characters are used)
1480 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1482 gchar *dec = decoded_uri;
1483 const gchar *enc = encoded_uri;
1488 if (isxdigit((guchar)enc[0]) &&
1489 isxdigit((guchar)enc[1])) {
1495 if (with_plus && *enc == '+')
1507 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1509 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1512 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1514 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1515 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1519 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1520 gchar **subject, gchar **body, gchar ***attach)
1524 const gchar *forbidden_uris[] = { ".gnupg/",
1530 gint num_attach = 0;
1531 gchar **my_att = NULL;
1533 Xstrdup_a(tmp_mailto, mailto, return -1);
1535 if (!strncmp(tmp_mailto, "mailto:", 7))
1538 p = strchr(tmp_mailto, '?');
1545 *to = decode_uri_gdup(tmp_mailto);
1547 my_att = g_malloc(sizeof(char *));
1551 gchar *field, *value;
1568 if (*value == '\0') continue;
1570 if (from && !g_ascii_strcasecmp(field, "from")) {
1572 *from = decode_uri_gdup(value);
1574 gchar *tmp = decode_uri_gdup(value);
1575 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1579 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1581 *cc = decode_uri_gdup(value);
1583 gchar *tmp = decode_uri_gdup(value);
1584 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1588 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1590 *bcc = decode_uri_gdup(value);
1592 gchar *tmp = decode_uri_gdup(value);
1593 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1597 } else if (subject && !*subject &&
1598 !g_ascii_strcasecmp(field, "subject")) {
1599 *subject = decode_uri_gdup(value);
1600 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1601 *body = decode_uri_gdup(value);
1602 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1603 gchar *tmp = decode_uri_gdup(value);
1604 if (!g_file_get_contents(value, body, NULL, NULL)) {
1605 g_error("Error: couldn't set insert file '%s' in body\n", value);
1609 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1611 gchar *tmp = decode_uri_gdup(value);
1612 for (; forbidden_uris[i]; i++) {
1613 if (strstr(tmp, forbidden_uris[i])) {
1614 g_print("Refusing to attach '%s', potential private data leak\n",
1622 /* attach is correct */
1624 printf("realloc my_att %d\n", (num_attach+1));
1625 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1626 my_att[num_attach-1] = tmp;
1627 my_att[num_attach] = NULL;
1639 #include <windows.h>
1640 #ifndef CSIDL_APPDATA
1641 #define CSIDL_APPDATA 0x001a
1643 #ifndef CSIDL_LOCAL_APPDATA
1644 #define CSIDL_LOCAL_APPDATA 0x001c
1646 #ifndef CSIDL_FLAG_CREATE
1647 #define CSIDL_FLAG_CREATE 0x8000
1649 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1653 w32_strerror (int w32_errno)
1655 static char strerr[256];
1656 int ec = (int)GetLastError ();
1660 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1661 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1662 strerr, DIM (strerr)-1, NULL);
1666 static __inline__ void *
1667 dlopen (const char * name, int flag)
1669 void * hd = LoadLibrary (name);
1673 static __inline__ void *
1674 dlsym (void * hd, const char * sym)
1678 void * fnc = GetProcAddress (hd, sym);
1687 static __inline__ const char *
1690 return w32_strerror (0);
1694 static __inline__ int
1706 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1708 static int initialized;
1709 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1713 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1719 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1721 handle = dlopen (dllnames[i], RTLD_LAZY);
1724 func = dlsym (handle, "SHGetFolderPathA");
1735 return func (a,b,c,d,e);
1740 /* Returns a static string with the directroy from which the module
1741 has been loaded. Returns an empty string on error. */
1742 static char *w32_get_module_dir(void)
1744 static char *moddir;
1747 char name[MAX_PATH+10];
1750 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1753 p = strrchr (name, '\\');
1759 moddir = g_strdup (name);
1763 #endif /* G_OS_WIN32 */
1765 /* Return a static string with the locale dir. */
1766 const gchar *get_locale_dir(void)
1768 static gchar *loc_dir;
1772 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1773 "\\share\\locale", NULL);
1776 loc_dir = LOCALEDIR;
1782 const gchar *get_home_dir(void)
1785 static char home_dir[MAX_PATH] = "";
1787 if (home_dir[0] == '\0') {
1788 if (w32_shgetfolderpath
1789 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1790 NULL, 0, home_dir) < 0)
1791 strcpy (home_dir, "C:\\Sylpheed");
1795 static const gchar *homeenv = NULL;
1800 if (!homeenv && g_getenv("HOME") != NULL)
1801 homeenv = g_strdup(g_getenv("HOME"));
1803 homeenv = g_get_home_dir();
1809 static gchar *claws_rc_dir = NULL;
1810 static gboolean rc_dir_alt = FALSE;
1811 const gchar *get_rc_dir(void)
1815 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1818 return claws_rc_dir;
1821 void set_rc_dir(const gchar *dir)
1823 if (claws_rc_dir != NULL) {
1824 g_print("Error: rc_dir already set\n");
1827 if (g_path_is_absolute(dir))
1828 claws_rc_dir = g_strdup(dir);
1830 claws_rc_dir = g_strconcat(g_get_current_dir(),
1831 G_DIR_SEPARATOR_S, dir, NULL);
1833 debug_print("set rc_dir to %s\n", claws_rc_dir);
1834 if (!is_dir_exist(claws_rc_dir)) {
1835 if (make_dir_hier(claws_rc_dir) != 0) {
1836 g_print("Error: can't create %s\n",
1843 gboolean rc_dir_is_alt(void) {
1847 const gchar *get_mail_base_dir(void)
1850 static gchar *mail_base_dir = NULL;
1853 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1856 return mail_base_dir;
1858 return get_home_dir();
1863 const gchar *prefs_common_get_data_root(void);
1864 gchar *last_data_root = NULL;
1867 const gchar *get_news_cache_dir(void)
1869 static gchar *news_cache_dir = NULL;
1871 const gchar *data_root = prefs_common_get_data_root();
1872 if (strcmp2(data_root, last_data_root)) {
1873 g_free(news_cache_dir);
1874 news_cache_dir = NULL;
1877 if (!news_cache_dir)
1879 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1880 NEWS_CACHE_DIR, NULL);
1884 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1885 "Claws", G_DIR_SEPARATOR_S,
1886 g_get_user_name(), G_DIR_SEPARATOR_S,
1887 NEWS_CACHE_DIR, NULL);
1888 g_free(last_data_root);
1889 last_data_root = g_strdup(last_data_root);
1891 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1892 NEWS_CACHE_DIR, NULL);
1893 g_free(last_data_root);
1894 last_data_root = NULL;
1898 return news_cache_dir;
1901 const gchar *get_imap_cache_dir(void)
1903 static gchar *imap_cache_dir = NULL;
1905 const gchar *data_root = prefs_common_get_data_root();
1906 if (strcmp2(data_root, last_data_root)) {
1907 g_free(imap_cache_dir);
1908 imap_cache_dir = NULL;
1912 if (!imap_cache_dir)
1914 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1915 IMAP_CACHE_DIR, NULL);
1919 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1920 "Claws", G_DIR_SEPARATOR_S,
1921 g_get_user_name(), G_DIR_SEPARATOR_S,
1922 IMAP_CACHE_DIR, NULL);
1923 g_free(last_data_root);
1924 last_data_root = g_strdup(last_data_root);
1926 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1927 IMAP_CACHE_DIR, NULL);
1928 g_free(last_data_root);
1929 last_data_root = NULL;
1934 return imap_cache_dir;
1937 const gchar *get_mime_tmp_dir(void)
1939 static gchar *mime_tmp_dir = NULL;
1942 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1943 MIME_TMP_DIR, NULL);
1945 return mime_tmp_dir;
1948 const gchar *get_template_dir(void)
1950 static gchar *template_dir = NULL;
1953 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1954 TEMPLATE_DIR, NULL);
1956 return template_dir;
1959 /* Return the default directory for Plugins. */
1960 const gchar *get_plugin_dir(void)
1963 static gchar *plugin_dir = NULL;
1966 plugin_dir = g_strconcat(w32_get_module_dir(),
1967 "\\lib\\claws-mail\\plugins\\",
1971 if (is_dir_exist(PLUGINDIR))
1974 static gchar *plugin_dir = NULL;
1976 plugin_dir = g_strconcat(get_rc_dir(),
1977 G_DIR_SEPARATOR_S, "plugins",
1978 G_DIR_SEPARATOR_S, NULL);
1984 const gchar *get_tmp_dir(void)
1986 static gchar *tmp_dir = NULL;
1989 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1995 gchar *get_tmp_file(void)
1998 static guint32 id = 0;
2000 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2001 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2006 const gchar *get_domain_name(void)
2009 static gchar *domain_name = NULL;
2015 if (gethostname(hostname, sizeof(hostname)) != 0) {
2016 perror("gethostname");
2017 domain_name = "unknown";
2019 hostname[sizeof(hostname) - 1] = '\0';
2020 if ((hp = my_gethostbyname(hostname)) == NULL) {
2021 perror("gethostbyname");
2022 domain_name = g_strdup(hostname);
2024 domain_name = g_strdup(hp->h_name);
2027 debug_print("domain name = %s\n", domain_name);
2036 off_t get_file_size(const gchar *file)
2040 if (g_stat(file, &s) < 0) {
2041 FILE_OP_ERROR(file, "stat");
2048 time_t get_file_mtime(const gchar *file)
2052 if (g_stat(file, &s) < 0) {
2053 FILE_OP_ERROR(file, "stat");
2060 off_t get_file_size_as_crlf(const gchar *file)
2064 gchar buf[BUFFSIZE];
2066 if ((fp = g_fopen(file, "rb")) == NULL) {
2067 FILE_OP_ERROR(file, "fopen");
2071 while (fgets(buf, sizeof(buf), fp) != NULL) {
2073 size += strlen(buf) + 2;
2077 FILE_OP_ERROR(file, "fgets");
2086 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2093 if (g_stat(file, &s) < 0) {
2094 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2098 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2105 /* Test on whether FILE is a relative file name. This is
2106 * straightforward for Unix but more complex for Windows. */
2107 gboolean is_relative_filename(const gchar *file)
2112 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2113 return FALSE; /* Prefixed with a hostname - this can't
2114 * be a relative name. */
2116 if ( ((*file >= 'a' && *file <= 'z')
2117 || (*file >= 'A' && *file <= 'Z'))
2119 file += 2; /* Skip drive letter. */
2121 return !(*file == '\\' || *file == '/');
2123 return !(*file == G_DIR_SEPARATOR);
2128 gboolean is_dir_exist(const gchar *dir)
2133 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2136 gboolean is_file_entry_exist(const gchar *file)
2141 return g_file_test(file, G_FILE_TEST_EXISTS);
2144 gboolean dirent_is_regular_file(struct dirent *d)
2146 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2147 if (d->d_type == DT_REG)
2149 else if (d->d_type != DT_UNKNOWN)
2153 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2156 gint change_dir(const gchar *dir)
2158 gchar *prevdir = NULL;
2161 prevdir = g_get_current_dir();
2163 if (g_chdir(dir) < 0) {
2164 FILE_OP_ERROR(dir, "chdir");
2165 if (debug_mode) g_free(prevdir);
2167 } else if (debug_mode) {
2170 cwd = g_get_current_dir();
2171 if (strcmp(prevdir, cwd) != 0)
2172 g_print("current dir: %s\n", cwd);
2180 gint make_dir(const gchar *dir)
2182 if (g_mkdir(dir, S_IRWXU) < 0) {
2183 FILE_OP_ERROR(dir, "mkdir");
2186 if (g_chmod(dir, S_IRWXU) < 0)
2187 FILE_OP_ERROR(dir, "chmod");
2192 gint make_dir_hier(const gchar *dir)
2197 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2198 parent_dir = g_strndup(dir, p - dir);
2199 if (*parent_dir != '\0') {
2200 if (!is_dir_exist(parent_dir)) {
2201 if (make_dir(parent_dir) < 0) {
2210 if (!is_dir_exist(dir)) {
2211 if (make_dir(dir) < 0)
2218 gint remove_all_files(const gchar *dir)
2221 const gchar *dir_name;
2224 prev_dir = g_get_current_dir();
2226 if (g_chdir(dir) < 0) {
2227 FILE_OP_ERROR(dir, "chdir");
2232 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2233 g_warning("failed to open directory: %s\n", dir);
2238 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2239 if (claws_unlink(dir_name) < 0)
2240 FILE_OP_ERROR(dir_name, "unlink");
2245 if (g_chdir(prev_dir) < 0) {
2246 FILE_OP_ERROR(prev_dir, "chdir");
2256 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2259 const gchar *dir_name;
2263 prev_dir = g_get_current_dir();
2265 if (g_chdir(dir) < 0) {
2266 FILE_OP_ERROR(dir, "chdir");
2271 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2272 g_warning("failed to open directory: %s\n", dir);
2277 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2278 file_no = to_number(dir_name);
2279 if (file_no > 0 && first <= file_no && file_no <= last) {
2280 if (is_dir_exist(dir_name))
2282 if (claws_unlink(dir_name) < 0)
2283 FILE_OP_ERROR(dir_name, "unlink");
2289 if (g_chdir(prev_dir) < 0) {
2290 FILE_OP_ERROR(prev_dir, "chdir");
2300 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2303 const gchar *dir_name;
2307 prev_dir = g_get_current_dir();
2309 if (g_chdir(dir) < 0) {
2310 FILE_OP_ERROR(dir, "chdir");
2315 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2316 FILE_OP_ERROR(dir, "opendir");
2321 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2322 file_no = to_number(dir_name);
2323 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2324 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2325 if (is_dir_exist(dir_name))
2327 if (claws_unlink(dir_name) < 0)
2328 FILE_OP_ERROR(dir_name, "unlink");
2334 if (g_chdir(prev_dir) < 0) {
2335 FILE_OP_ERROR(prev_dir, "chdir");
2345 gint remove_all_numbered_files(const gchar *dir)
2347 return remove_numbered_files(dir, 0, UINT_MAX);
2350 gint remove_dir_recursive(const gchar *dir)
2354 const gchar *dir_name;
2357 if (g_stat(dir, &s) < 0) {
2358 FILE_OP_ERROR(dir, "stat");
2359 if (ENOENT == errno) return 0;
2363 if (!S_ISDIR(s.st_mode)) {
2364 if (claws_unlink(dir) < 0) {
2365 FILE_OP_ERROR(dir, "unlink");
2372 prev_dir = g_get_current_dir();
2373 /* g_print("prev_dir = %s\n", prev_dir); */
2375 if (!path_cmp(prev_dir, dir)) {
2377 if (g_chdir("..") < 0) {
2378 FILE_OP_ERROR(dir, "chdir");
2381 prev_dir = g_get_current_dir();
2384 if (g_chdir(dir) < 0) {
2385 FILE_OP_ERROR(dir, "chdir");
2390 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2391 g_warning("failed to open directory: %s\n", dir);
2397 /* remove all files in the directory */
2398 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2399 /* g_print("removing %s\n", dir_name); */
2401 if (is_dir_exist(dir_name)) {
2402 if (remove_dir_recursive(dir_name) < 0) {
2403 g_warning("can't remove directory\n");
2407 if (claws_unlink(dir_name) < 0)
2408 FILE_OP_ERROR(dir_name, "unlink");
2414 if (g_chdir(prev_dir) < 0) {
2415 FILE_OP_ERROR(prev_dir, "chdir");
2422 if (g_rmdir(dir) < 0) {
2423 FILE_OP_ERROR(dir, "rmdir");
2430 gint rename_force(const gchar *oldpath, const gchar *newpath)
2433 if (!is_file_entry_exist(oldpath)) {
2437 if (is_file_exist(newpath)) {
2438 if (claws_unlink(newpath) < 0)
2439 FILE_OP_ERROR(newpath, "unlink");
2442 return g_rename(oldpath, newpath);
2446 * Append src file body to the tail of dest file.
2447 * Now keep_backup has no effects.
2449 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2451 FILE *src_fp, *dest_fp;
2455 gboolean err = FALSE;
2457 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2458 FILE_OP_ERROR(src, "fopen");
2462 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2463 FILE_OP_ERROR(dest, "fopen");
2468 if (change_file_mode_rw(dest_fp, dest) < 0) {
2469 FILE_OP_ERROR(dest, "chmod");
2470 g_warning("can't change file mode\n");
2473 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2474 if (n_read < sizeof(buf) && ferror(src_fp))
2476 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2477 g_warning("writing to %s failed.\n", dest);
2485 if (ferror(src_fp)) {
2486 FILE_OP_ERROR(src, "fread");
2490 if (fclose(dest_fp) == EOF) {
2491 FILE_OP_ERROR(dest, "fclose");
2503 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2505 FILE *src_fp, *dest_fp;
2508 gchar *dest_bak = NULL;
2509 gboolean err = FALSE;
2511 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2512 FILE_OP_ERROR(src, "fopen");
2515 if (is_file_exist(dest)) {
2516 dest_bak = g_strconcat(dest, ".bak", NULL);
2517 if (rename_force(dest, dest_bak) < 0) {
2518 FILE_OP_ERROR(dest, "rename");
2525 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2526 FILE_OP_ERROR(dest, "fopen");
2529 if (rename_force(dest_bak, dest) < 0)
2530 FILE_OP_ERROR(dest_bak, "rename");
2536 if (change_file_mode_rw(dest_fp, dest) < 0) {
2537 FILE_OP_ERROR(dest, "chmod");
2538 g_warning("can't change file mode\n");
2541 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2542 if (n_read < sizeof(buf) && ferror(src_fp))
2544 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2545 g_warning("writing to %s failed.\n", dest);
2550 if (rename_force(dest_bak, dest) < 0)
2551 FILE_OP_ERROR(dest_bak, "rename");
2558 if (ferror(src_fp)) {
2559 FILE_OP_ERROR(src, "fread");
2563 if (fclose(dest_fp) == EOF) {
2564 FILE_OP_ERROR(dest, "fclose");
2571 if (rename_force(dest_bak, dest) < 0)
2572 FILE_OP_ERROR(dest_bak, "rename");
2578 if (keep_backup == FALSE && dest_bak)
2579 claws_unlink(dest_bak);
2586 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2588 if (overwrite == FALSE && is_file_exist(dest)) {
2589 g_warning("move_file(): file %s already exists.", dest);
2593 if (rename_force(src, dest) == 0) return 0;
2595 if (EXDEV != errno) {
2596 FILE_OP_ERROR(src, "rename");
2600 if (copy_file(src, dest, FALSE) < 0) return -1;
2607 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2610 gint bytes_left, to_read;
2613 if (fseek(fp, offset, SEEK_SET) < 0) {
2618 bytes_left = length;
2619 to_read = MIN(bytes_left, sizeof(buf));
2621 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2622 if (n_read < to_read && ferror(fp))
2624 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2627 bytes_left -= n_read;
2628 if (bytes_left == 0)
2630 to_read = MIN(bytes_left, sizeof(buf));
2641 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2644 gboolean err = FALSE;
2646 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2647 FILE_OP_ERROR(dest, "fopen");
2651 if (change_file_mode_rw(dest_fp, dest) < 0) {
2652 FILE_OP_ERROR(dest, "chmod");
2653 g_warning("can't change file mode\n");
2656 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2659 if (!err && fclose(dest_fp) == EOF) {
2660 FILE_OP_ERROR(dest, "fclose");
2665 g_warning("writing to %s failed.\n", dest);
2673 /* convert line endings into CRLF. If the last line doesn't end with
2674 * linebreak, add it.
2676 gchar *canonicalize_str(const gchar *str)
2682 for (p = str; *p != '\0'; ++p) {
2689 if (p == str || *(p - 1) != '\n')
2692 out = outp = g_malloc(new_len + 1);
2693 for (p = str; *p != '\0'; ++p) {
2700 if (p == str || *(p - 1) != '\n') {
2709 gint canonicalize_file(const gchar *src, const gchar *dest)
2711 FILE *src_fp, *dest_fp;
2712 gchar buf[BUFFSIZE];
2714 gboolean err = FALSE;
2715 gboolean last_linebreak = FALSE;
2717 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2718 FILE_OP_ERROR(src, "fopen");
2722 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2723 FILE_OP_ERROR(dest, "fopen");
2728 if (change_file_mode_rw(dest_fp, dest) < 0) {
2729 FILE_OP_ERROR(dest, "chmod");
2730 g_warning("can't change file mode\n");
2733 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2737 if (len == 0) break;
2738 last_linebreak = FALSE;
2740 if (buf[len - 1] != '\n') {
2741 last_linebreak = TRUE;
2742 r = fputs(buf, dest_fp);
2743 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2744 r = fputs(buf, dest_fp);
2747 r = fwrite(buf, 1, len - 1, dest_fp);
2752 r = fputs("\r\n", dest_fp);
2756 g_warning("writing to %s failed.\n", dest);
2764 if (last_linebreak == TRUE) {
2765 if (fputs("\r\n", dest_fp) == EOF)
2769 if (ferror(src_fp)) {
2770 FILE_OP_ERROR(src, "fgets");
2774 if (fclose(dest_fp) == EOF) {
2775 FILE_OP_ERROR(dest, "fclose");
2787 gint canonicalize_file_replace(const gchar *file)
2791 tmp_file = get_tmp_file();
2793 if (canonicalize_file(file, tmp_file) < 0) {
2798 if (move_file(tmp_file, file, TRUE) < 0) {
2799 g_warning("can't replace %s .\n", file);
2800 claws_unlink(tmp_file);
2809 gchar *normalize_newlines(const gchar *str)
2811 const gchar *p = str;
2814 out = outp = g_malloc(strlen(str) + 1);
2815 for (p = str; *p != '\0'; ++p) {
2817 if (*(p + 1) != '\n')
2828 gchar *get_outgoing_rfc2822_str(FILE *fp)
2830 gchar buf[BUFFSIZE];
2834 str = g_string_new(NULL);
2836 /* output header part */
2837 while (fgets(buf, sizeof(buf), fp) != NULL) {
2839 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2846 else if (next != ' ' && next != '\t') {
2850 if (fgets(buf, sizeof(buf), fp) == NULL)
2854 g_string_append(str, buf);
2855 g_string_append(str, "\r\n");
2861 /* output body part */
2862 while (fgets(buf, sizeof(buf), fp) != NULL) {
2865 g_string_append_c(str, '.');
2866 g_string_append(str, buf);
2867 g_string_append(str, "\r\n");
2871 g_string_free(str, FALSE);
2877 * Create a new boundary in a way that it is very unlikely that this
2878 * will occur in the following text. It would be easy to ensure
2879 * uniqueness if everything is either quoted-printable or base64
2880 * encoded (note that conversion is allowed), but because MIME bodies
2881 * may be nested, it may happen that the same boundary has already
2884 * boundary := 0*69<bchars> bcharsnospace
2885 * bchars := bcharsnospace / " "
2886 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2887 * "+" / "_" / "," / "-" / "." /
2888 * "/" / ":" / "=" / "?"
2890 * some special characters removed because of buggy MTAs
2893 gchar *generate_mime_boundary(const gchar *prefix)
2895 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2896 "abcdefghijklmnopqrstuvwxyz"
2901 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2902 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2905 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2909 gint change_file_mode_rw(FILE *fp, const gchar *file)
2912 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2914 return g_chmod(file, S_IRUSR|S_IWUSR);
2918 FILE *my_tmpfile(void)
2920 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2921 const gchar suffix[] = ".XXXXXX";
2922 const gchar *tmpdir;
2924 const gchar *progname;
2933 tmpdir = get_tmp_dir();
2934 tmplen = strlen(tmpdir);
2935 progname = g_get_prgname();
2936 if (progname == NULL)
2937 progname = "claws-mail";
2938 proglen = strlen(progname);
2939 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2942 memcpy(fname, tmpdir, tmplen);
2943 fname[tmplen] = G_DIR_SEPARATOR;
2944 memcpy(fname + tmplen + 1, progname, proglen);
2945 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2947 fd = mkstemp(fname);
2952 claws_unlink(fname);
2954 /* verify that we can write in the file after unlinking */
2955 if (write(fd, buf, 1) < 0) {
2962 fp = fdopen(fd, "w+b");
2970 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2975 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2979 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2980 dir, G_DIR_SEPARATOR);
2981 fd = mkstemp_name(template, filename);
2984 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2985 fd = mkstemp(*filename);
2987 return fdopen(fd, "w+");
2990 FILE *str_open_as_stream(const gchar *str)
2995 g_return_val_if_fail(str != NULL, NULL);
2999 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3004 if (len == 0) return fp;
3006 if (fwrite(str, 1, len, fp) != len) {
3007 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3016 gint str_write_to_file(const gchar *str, const gchar *file)
3021 g_return_val_if_fail(str != NULL, -1);
3022 g_return_val_if_fail(file != NULL, -1);
3024 if ((fp = g_fopen(file, "wb")) == NULL) {
3025 FILE_OP_ERROR(file, "fopen");
3035 if (fwrite(str, 1, len, fp) != len) {
3036 FILE_OP_ERROR(file, "fwrite");
3042 if (fclose(fp) == EOF) {
3043 FILE_OP_ERROR(file, "fclose");
3051 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3058 g_return_val_if_fail(fp != NULL, NULL);
3060 array = g_byte_array_new();
3062 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3063 if (n_read < sizeof(buf) && ferror(fp))
3065 g_byte_array_append(array, buf, n_read);
3069 FILE_OP_ERROR("file stream", "fread");
3070 g_byte_array_free(array, TRUE);
3075 g_byte_array_append(array, buf, 1);
3076 str = (gchar *)array->data;
3077 g_byte_array_free(array, FALSE);
3079 if (recode && !g_utf8_validate(str, -1, NULL)) {
3080 const gchar *src_codeset, *dest_codeset;
3082 src_codeset = conv_get_locale_charset_str();
3083 dest_codeset = CS_UTF_8;
3084 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3092 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3099 struct timeval timeout = {1, 0};
3104 g_return_val_if_fail(file != NULL, NULL);
3106 if (g_stat(file, &s) != 0) {
3107 FILE_OP_ERROR(file, "stat");
3110 if (S_ISDIR(s.st_mode)) {
3111 g_warning("%s: is a directory\n", file);
3116 fp = fopen (file, "rb");
3118 FILE_OP_ERROR(file, "open");
3122 fd = open(file, O_RDONLY | O_NONBLOCK);
3123 /* test whether the file is readable without blocking */
3124 fd = open(file, O_RDONLY | O_NONBLOCK);
3126 FILE_OP_ERROR(file, "open");
3133 /* allow for one second */
3134 err = select(fd+1, &fds, NULL, NULL, &timeout);
3135 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3137 FILE_OP_ERROR(file, "select");
3139 g_warning("%s: doesn't seem readable\n", file);
3145 /* Now clear O_NONBLOCK */
3146 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3147 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3151 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3152 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3157 /* get the FILE pointer */
3158 fp = fdopen(fd, "rb");
3161 FILE_OP_ERROR(file, "fdopen");
3162 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3167 str = file_read_stream_to_str_full(fp, recode);
3174 gchar *file_read_to_str(const gchar *file)
3176 return file_read_to_str_full(file, TRUE);
3178 gchar *file_read_stream_to_str(FILE *fp)
3180 return file_read_stream_to_str_full(fp, TRUE);
3183 gchar *file_read_to_str_no_recode(const gchar *file)
3185 return file_read_to_str_full(file, FALSE);
3187 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3189 return file_read_stream_to_str_full(fp, FALSE);
3192 char *fgets_crlf(char *buf, int size, FILE *stream)
3194 gboolean is_cr = FALSE;
3195 gboolean last_was_cr = FALSE;
3200 while (--size > 0 && (c = getc(stream)) != EOF)
3203 is_cr = (c == '\r');
3213 last_was_cr = is_cr;
3215 if (c == EOF && cs == buf)
3223 static gint execute_async(gchar *const argv[])
3225 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3227 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3228 NULL, NULL, NULL, FALSE) == FALSE) {
3229 g_warning("Couldn't execute command: %s\n", argv[0]);
3236 static gint execute_sync(gchar *const argv[])
3240 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3242 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3243 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3244 g_warning("Couldn't execute command: %s\n", argv[0]);
3249 if (WIFEXITED(status))
3250 return WEXITSTATUS(status);
3258 gint execute_command_line(const gchar *cmdline, gboolean async)
3263 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3265 argv = strsplit_with_quote(cmdline, " ", 0);
3268 ret = execute_async(argv);
3270 ret = execute_sync(argv);
3277 gchar *get_command_output(const gchar *cmdline)
3279 gchar *child_stdout;
3282 g_return_val_if_fail(cmdline != NULL, NULL);
3284 debug_print("get_command_output(): executing: %s\n", cmdline);
3286 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3288 g_warning("Couldn't execute command: %s\n", cmdline);
3292 return child_stdout;
3295 static gint is_unchanged_uri_char(char c)
3306 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3312 for(i = 0; i < strlen(uri) ; i++) {
3313 if (is_unchanged_uri_char(uri[i])) {
3314 if (k + 2 >= bufsize)
3316 encoded_uri[k++] = uri[i];
3319 char * hexa = "0123456789ABCDEF";
3321 if (k + 4 >= bufsize)
3323 encoded_uri[k++] = '%';
3324 encoded_uri[k++] = hexa[uri[i] / 16];
3325 encoded_uri[k++] = hexa[uri[i] % 16];
3331 gint open_uri(const gchar *uri, const gchar *cmdline)
3334 gchar buf[BUFFSIZE];
3336 gchar encoded_uri[BUFFSIZE];
3337 g_return_val_if_fail(uri != NULL, -1);
3339 /* an option to choose whether to use encode_uri or not ? */
3340 encode_uri(encoded_uri, BUFFSIZE, uri);
3343 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3344 !strchr(p + 2, '%'))
3345 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3348 g_warning("Open URI command line is invalid "
3349 "(there must be only one '%%s'): %s",
3351 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3354 execute_command_line(buf, TRUE);
3356 extern osso_context_t *get_osso_context(void);
3357 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3358 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3359 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3364 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3366 gchar buf[BUFFSIZE];
3369 g_return_val_if_fail(filepath != NULL, -1);
3372 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3373 !strchr(p + 2, '%'))
3374 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3377 g_warning("Open Text Editor command line is invalid "
3378 "(there must be only one '%%s'): %s",
3380 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3383 execute_command_line(buf, TRUE);
3388 time_t remote_tzoffset_sec(const gchar *zone)
3390 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3396 time_t remoteoffset;
3398 strncpy(zone3, zone, 3);
3402 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3403 (c == '+' || c == '-')) {
3404 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3406 remoteoffset = -remoteoffset;
3407 } else if (!strncmp(zone, "UT" , 2) ||
3408 !strncmp(zone, "GMT", 2)) {
3410 } else if (strlen(zone3) == 3) {
3411 for (p = ustzstr; *p != '\0'; p += 3) {
3412 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3413 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3414 remoteoffset = iustz * 3600;
3420 } else if (strlen(zone3) == 1) {
3422 case 'Z': remoteoffset = 0; break;
3423 case 'A': remoteoffset = -1; break;
3424 case 'B': remoteoffset = -2; break;
3425 case 'C': remoteoffset = -3; break;
3426 case 'D': remoteoffset = -4; break;
3427 case 'E': remoteoffset = -5; break;
3428 case 'F': remoteoffset = -6; break;
3429 case 'G': remoteoffset = -7; break;
3430 case 'H': remoteoffset = -8; break;
3431 case 'I': remoteoffset = -9; break;
3432 case 'K': remoteoffset = -10; break; /* J is not used */
3433 case 'L': remoteoffset = -11; break;
3434 case 'M': remoteoffset = -12; break;
3435 case 'N': remoteoffset = 1; break;
3436 case 'O': remoteoffset = 2; break;
3437 case 'P': remoteoffset = 3; break;
3438 case 'Q': remoteoffset = 4; break;
3439 case 'R': remoteoffset = 5; break;
3440 case 'S': remoteoffset = 6; break;
3441 case 'T': remoteoffset = 7; break;
3442 case 'U': remoteoffset = 8; break;
3443 case 'V': remoteoffset = 9; break;
3444 case 'W': remoteoffset = 10; break;
3445 case 'X': remoteoffset = 11; break;
3446 case 'Y': remoteoffset = 12; break;
3447 default: remoteoffset = 0; break;
3449 remoteoffset = remoteoffset * 3600;
3453 return remoteoffset;
3456 time_t tzoffset_sec(time_t *now)
3461 struct tm buf1, buf2;
3464 gmt = *gmtime_r(now, &buf1);
3465 lt = localtime_r(now, &buf2);
3467 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3469 if (lt->tm_year < gmt.tm_year)
3471 else if (lt->tm_year > gmt.tm_year)
3473 else if (lt->tm_yday < gmt.tm_yday)
3475 else if (lt->tm_yday > gmt.tm_yday)
3478 if (off >= 24 * 60) /* should be impossible */
3479 off = 23 * 60 + 59; /* if not, insert silly value */
3480 if (off <= -24 * 60)
3481 off = -(23 * 60 + 59);
3486 /* calculate timezone offset */
3487 gchar *tzoffset(time_t *now)
3489 static gchar offset_string[6];
3494 struct tm buf1, buf2;
3497 gmt = *gmtime_r(now, &buf1);
3498 lt = localtime_r(now, &buf2);
3500 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3502 if (lt->tm_year < gmt.tm_year)
3504 else if (lt->tm_year > gmt.tm_year)
3506 else if (lt->tm_yday < gmt.tm_yday)
3508 else if (lt->tm_yday > gmt.tm_yday)
3516 if (off >= 24 * 60) /* should be impossible */
3517 off = 23 * 60 + 59; /* if not, insert silly value */
3519 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3521 return offset_string;
3524 void get_rfc822_date(gchar *buf, gint len)
3528 gchar day[4], mon[4];
3529 gint dd, hh, mm, ss, yyyy;
3532 gchar buf2[BUFFSIZE];
3536 lt = localtime_r(&t, &buf1);
3538 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3539 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3541 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3542 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3545 void debug_set_mode(gboolean mode)
3550 gboolean debug_get_mode(void)
3555 void debug_print_real(const gchar *format, ...)
3558 gchar buf[BUFFSIZE];
3560 if (!debug_mode) return;
3562 va_start(args, format);
3563 g_vsnprintf(buf, sizeof(buf), format, args);
3570 const char * debug_srcname(const char *file)
3572 const char *s = strrchr (file, '/');
3577 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3579 if (subject == NULL)
3582 subject += subject_get_prefix_length(subject);
3584 return g_hash_table_lookup(subject_table, subject);
3587 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3590 if (subject == NULL || *subject == 0)
3592 subject += subject_get_prefix_length(subject);
3593 g_hash_table_insert(subject_table, subject, data);
3596 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3598 if (subject == NULL)
3601 subject += subject_get_prefix_length(subject);
3602 g_hash_table_remove(subject_table, subject);
3606 *\brief Check if a string is prefixed with known (combinations)
3607 * of prefixes. The function assumes that each prefix
3608 * is terminated by zero or exactly _one_ space.
3610 *\param str String to check for a prefixes
3612 *\return int Number of chars in the prefix that should be skipped
3613 * for a "clean" subject line. If no prefix was found, 0
3616 static regex_t u_regex;
3617 static gboolean u_init_;
3619 void utils_free_regex(void)
3627 int subject_get_prefix_length(const gchar *subject)
3629 /*!< Array with allowable reply prefixes regexps. */
3630 static const gchar * const prefixes[] = {
3631 "Re\\:", /* "Re:" */
3632 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3633 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3634 "Aw\\:", /* "Aw:" (German) */
3635 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3636 "Res\\:", /* "Res:" (Brazilian Outlook) */
3637 "Fw\\:", /* "Fw:" Forward */
3638 "Fwd\\:", /* "Fwd:" Forward */
3639 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3640 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3641 "Rif\\:", /* "Rif:" (Italian Outlook) */
3642 "Sv\\:", /* "Sv" (Norwegian) */
3643 "Vs\\:", /* "Vs" (Norwegian) */
3644 "Ad\\:", /* "Ad" (Norwegian) */
3645 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3648 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3652 if (!subject) return 0;
3653 if (!*subject) return 0;
3656 GString *s = g_string_new("");
3658 for (n = 0; n < PREFIXES; n++)
3659 /* Terminate each prefix regexpression by a
3660 * "\ ?" (zero or ONE space), and OR them */
3661 g_string_append_printf(s, "(%s\\ ?)%s",
3666 g_string_prepend(s, "(");
3667 g_string_append(s, ")+"); /* match at least once */
3668 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3671 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3672 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3673 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3674 debug_print("Error compiling regexp %s\n", s->str);
3675 g_string_free(s, TRUE);
3679 g_string_free(s, TRUE);
3683 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3689 static guint g_stricase_hash(gconstpointer gptr)
3691 guint hash_result = 0;
3694 for (str = gptr; str && *str; str++) {
3695 hash_result += toupper(*str);
3701 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3703 const char *str1 = gptr1;
3704 const char *str2 = gptr2;
3706 return !strcasecmp(str1, str2);
3709 gint g_int_compare(gconstpointer a, gconstpointer b)
3711 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3714 gchar *generate_msgid(gchar *buf, gint len)
3724 lt = localtime_r(&t, &buft);
3726 if (strcmp(buf, "") == 0) {
3727 addr = g_strconcat("@", get_domain_name(), NULL);
3730 addr = g_strconcat("@", buf, NULL);
3733 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3734 lt->tm_year + 1900, lt->tm_mon + 1,
3735 lt->tm_mday, lt->tm_hour,
3736 lt->tm_min, lt->tm_sec,
3737 (guint) rand(), addr);
3744 quote_cmd_argument()
3746 return a quoted string safely usable in argument of a command.
3748 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3751 gint quote_cmd_argument(gchar * result, guint size,
3761 for(p = path ; * p != '\0' ; p ++) {
3763 if (isalnum((guchar)*p) || (* p == '/')) {
3764 if (remaining > 0) {
3770 result[size - 1] = '\0';
3775 if (remaining >= 2) {
3783 result[size - 1] = '\0';
3788 if (remaining > 0) {
3792 result[size - 1] = '\0';
3806 static void g_node_map_recursive(GNode *node, gpointer data)
3808 GNodeMapData *mapdata = (GNodeMapData *) data;
3810 GNodeMapData newmapdata;
3813 newdata = mapdata->func(node->data, mapdata->data);
3814 if (newdata != NULL) {
3815 newnode = g_node_new(newdata);
3816 g_node_append(mapdata->parent, newnode);
3818 newmapdata.parent = newnode;
3819 newmapdata.func = mapdata->func;
3820 newmapdata.data = mapdata->data;
3822 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3826 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3829 GNodeMapData mapdata;
3831 g_return_val_if_fail(node != NULL, NULL);
3832 g_return_val_if_fail(func != NULL, NULL);
3834 root = g_node_new(func(node->data, data));
3836 mapdata.parent = root;
3837 mapdata.func = func;
3838 mapdata.data = data;
3840 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3845 #define HEX_TO_INT(val, hex) \
3849 if ('0' <= c && c <= '9') { \
3851 } else if ('a' <= c && c <= 'f') { \
3852 val = c - 'a' + 10; \
3853 } else if ('A' <= c && c <= 'F') { \
3854 val = c - 'A' + 10; \
3860 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3867 if (hi == -1 || lo == -1)
3870 *out = (hi << 4) + lo;
3874 #define INT_TO_HEX(hex, val) \
3877 hex = '0' + (val); \
3879 hex = 'A' + (val) - 10; \
3882 void get_hex_str(gchar *out, guchar ch)
3886 INT_TO_HEX(hex, ch >> 4);
3888 INT_TO_HEX(hex, ch & 0x0f);
3894 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3896 #define G_PRINT_REF g_print
3900 *\brief Register ref counted pointer. It is based on GBoxed, so should
3901 * work with anything that uses the GType system. The semantics
3902 * are similar to a C++ auto pointer, with the exception that
3903 * C doesn't have automatic closure (calling destructors) when
3904 * exiting a block scope.
3905 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3906 * function directly.
3908 *\return GType A GType type.
3910 GType g_auto_pointer_register(void)
3912 static GType auto_pointer_type;
3913 if (!auto_pointer_type)
3915 g_boxed_type_register_static
3916 ("G_TYPE_AUTO_POINTER",
3917 (GBoxedCopyFunc) g_auto_pointer_copy,
3918 (GBoxedFreeFunc) g_auto_pointer_free);
3919 return auto_pointer_type;
3923 *\brief Structure with g_new() allocated pointer guarded by the
3926 typedef struct AutoPointerRef {
3927 void (*free) (gpointer);
3933 *\brief The auto pointer opaque structure that references the
3934 * pointer guard block.
3936 typedef struct AutoPointer {
3937 AutoPointerRef *ref;
3938 gpointer ptr; /*!< access to protected pointer */
3942 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3946 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3947 * ... when assigning, copying and freeing storage elements
3949 * gtk_list_store_new(N_S_COLUMNS,
3950 * G_TYPE_AUTO_POINTER,
3954 * Template *precious_data = g_new0(Template, 1);
3955 * g_pointer protect = g_auto_pointer_new(precious_data);
3957 * gtk_list_store_set(container, &iter,
3961 * ... the gtk_list_store has copied the pointer and
3962 * ... incremented its reference count, we should free
3963 * ... the auto pointer (in C++ a destructor would do
3964 * ... this for us when leaving block scope)
3966 * g_auto_pointer_free(protect);
3968 * ... gtk_list_store_set() now manages the data. When
3969 * ... *explicitly* requesting a pointer from the list
3970 * ... store, don't forget you get a copy that should be
3971 * ... freed with g_auto_pointer_free() eventually.
3975 *\param pointer Pointer to be guarded.
3977 *\return GAuto * Pointer that should be used in containers with
3980 GAuto *g_auto_pointer_new(gpointer p)
3982 AutoPointerRef *ref;
3988 ref = g_new0(AutoPointerRef, 1);
3989 ptr = g_new0(AutoPointer, 1);
3999 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4005 *\brief Allocate an autopointer using the passed \a free function to
4006 * free the guarded pointer
4008 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4015 aptr = g_auto_pointer_new(p);
4016 aptr->ref->free = free_;
4020 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4022 if (auto_ptr == NULL)
4024 return ((AutoPointer *) auto_ptr)->ptr;
4028 *\brief Copies an auto pointer by. It's mostly not necessary
4029 * to call this function directly, unless you copy/assign
4030 * the guarded pointer.
4032 *\param auto_ptr Auto pointer returned by previous call to
4033 * g_auto_pointer_new_XXX()
4035 *\return gpointer An auto pointer
4037 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4040 AutoPointerRef *ref;
4043 if (auto_ptr == NULL)
4048 newp = g_new0(AutoPointer, 1);
4051 newp->ptr = ref->pointer;
4055 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4061 *\brief Free an auto pointer
4063 void g_auto_pointer_free(GAuto *auto_ptr)
4066 AutoPointerRef *ref;
4068 if (auto_ptr == NULL)
4074 if (--(ref->cnt) == 0) {
4076 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4078 ref->free(ref->pointer);
4083 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4088 void replace_returns(gchar *str)
4093 while (strstr(str, "\n")) {
4094 *strstr(str, "\n") = ' ';
4096 while (strstr(str, "\r")) {
4097 *strstr(str, "\r") = ' ';
4101 /* get_uri_part() - retrieves a URI starting from scanpos.
4102 Returns TRUE if succesful */
4103 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4104 const gchar **bp, const gchar **ep, gboolean hdr)
4107 gint parenthese_cnt = 0;
4109 g_return_val_if_fail(start != NULL, FALSE);
4110 g_return_val_if_fail(scanpos != NULL, FALSE);
4111 g_return_val_if_fail(bp != NULL, FALSE);
4112 g_return_val_if_fail(ep != NULL, FALSE);
4116 /* find end point of URI */
4117 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4118 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4119 !IS_ASCII(*(const guchar *)ep_) ||
4120 strchr("[]{}<>\"", *ep_)) {
4122 } else if (strchr("(", *ep_)) {
4124 } else if (strchr(")", *ep_)) {
4125 if (parenthese_cnt > 0)
4132 /* no punctuation at end of string */
4134 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4135 * should pass some URI type to this function and decide on that whether
4136 * to perform punctuation stripping */
4138 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4140 for (; ep_ - 1 > scanpos + 1 &&
4141 IS_REAL_PUNCT(*(ep_ - 1));
4145 #undef IS_REAL_PUNCT
4152 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4154 while (bp && *bp && g_ascii_isspace(*bp))
4156 return g_strndup(bp, ep - bp);
4159 /* valid mail address characters */
4160 #define IS_RFC822_CHAR(ch) \
4164 !g_ascii_isspace(ch) && \
4165 !strchr("(),;<>\"", (ch)))
4167 /* alphabet and number within 7bit ASCII */
4168 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4169 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4171 static GHashTable *create_domain_tab(void)
4173 static const gchar *toplvl_domains [] = {
4175 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4176 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4177 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4178 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4179 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4180 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4181 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4182 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4183 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4184 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4185 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4186 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4187 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4188 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4189 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4190 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4191 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4192 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4193 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4194 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4195 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4196 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4197 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4198 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4199 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4200 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4203 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4205 g_return_val_if_fail(htab, NULL);
4206 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4207 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4211 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4213 const gint MAX_LVL_DOM_NAME_LEN = 6;
4214 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4215 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4218 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4221 for (p = buf; p < m && first < last; *p++ = *first++)
4225 return g_hash_table_lookup(tab, buf) != NULL;
4228 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4229 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4230 const gchar **bp, const gchar **ep, gboolean hdr)
4232 /* more complex than the uri part because we need to scan back and forward starting from
4233 * the scan position. */
4234 gboolean result = FALSE;
4235 const gchar *bp_ = NULL;
4236 const gchar *ep_ = NULL;
4237 static GHashTable *dom_tab;
4238 const gchar *last_dot = NULL;
4239 const gchar *prelast_dot = NULL;
4240 const gchar *last_tld_char = NULL;
4242 /* the informative part of the email address (describing the name
4243 * of the email address owner) may contain quoted parts. the
4244 * closure stack stores the last encountered quotes. */
4245 gchar closure_stack[128];
4246 gchar *ptr = closure_stack;
4248 g_return_val_if_fail(start != NULL, FALSE);
4249 g_return_val_if_fail(scanpos != NULL, FALSE);
4250 g_return_val_if_fail(bp != NULL, FALSE);
4251 g_return_val_if_fail(ep != NULL, FALSE);
4254 const gchar *start_quote = NULL;
4255 const gchar *end_quote = NULL;
4257 /* go to the real start */
4258 if (start[0] == ',')
4260 if (start[0] == ';')
4262 while (start[0] == '\n' || start[0] == '\r')
4264 while (start[0] == ' ' || start[0] == '\t')
4269 /* check if there are quotes (to skip , in them) */
4270 if (*start == '"') {
4271 start_quote = start;
4273 end_quote = strstr(start, "\"");
4279 /* skip anything between quotes */
4280 if (start_quote && end_quote) {
4285 /* find end (either , or ; or end of line) */
4286 if (strstr(start, ",") && strstr(start, ";"))
4287 *ep = strstr(start,",") < strstr(start, ";")
4288 ? strstr(start, ",") : strstr(start, ";");
4289 else if (strstr(start, ","))
4290 *ep = strstr(start, ",");
4291 else if (strstr(start, ";"))
4292 *ep = strstr(start, ";");
4294 *ep = start+strlen(start);
4296 /* go back to real start */
4297 if (start_quote && end_quote) {
4298 start = start_quote;
4301 /* check there's still an @ in that, or search
4302 * further if possible */
4303 if (strstr(start, "@") && strstr(start, "@") < *ep)
4305 else if (*ep < start+strlen(start)) {
4308 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4316 dom_tab = create_domain_tab();
4317 g_return_val_if_fail(dom_tab, FALSE);
4319 /* scan start of address */
4320 for (bp_ = scanpos - 1;
4321 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4324 /* TODO: should start with an alnum? */
4326 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4329 if (bp_ != scanpos) {
4330 /* scan end of address */
4331 for (ep_ = scanpos + 1;
4332 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4334 prelast_dot = last_dot;
4336 if (*(last_dot + 1) == '.') {
4337 if (prelast_dot == NULL)
4339 last_dot = prelast_dot;
4344 /* TODO: really should terminate with an alnum? */
4345 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4350 if (last_dot == NULL)
4352 if (last_dot >= ep_)
4353 last_dot = prelast_dot;
4354 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4358 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4359 if (*last_tld_char == '?')
4362 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4369 if (!result) return FALSE;
4371 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4372 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4373 && IS_RFC822_CHAR(*(ep_ + 3))) {
4374 /* this informative part with an @ in it is
4375 * followed by the email address */
4378 /* go to matching '>' (or next non-rfc822 char, like \n) */
4379 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4382 /* include the bracket */
4383 if (*ep_ == '>') ep_++;
4385 /* include the leading quote */
4393 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4394 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4397 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4398 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4401 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4402 #define IN_STACK() (ptr > closure_stack)
4403 /* has underrun check */
4404 #define POP_STACK() if(IN_STACK()) --ptr
4405 /* has overrun check */
4406 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4407 /* has underrun check */
4408 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4412 /* scan for the informative part. */
4413 for (bp_ -= 2; bp_ >= start; bp_--) {
4414 /* if closure on the stack keep scanning */
4415 if (PEEK_STACK() == *bp_) {
4419 if (*bp_ == '\'' || *bp_ == '"') {
4424 /* if nothing in the closure stack, do the special conditions
4425 * the following if..else expression simply checks whether
4426 * a token is acceptable. if not acceptable, the clause
4427 * should terminate the loop with a 'break' */
4428 if (!PEEK_STACK()) {
4430 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4431 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4432 /* hyphens are allowed, but only in
4434 } else if (strchr(" \"'", *bp_)) {
4435 /* but anything not being a punctiation
4438 break; /* anything else is rejected */
4445 /* scan forward (should start with an alnum) */
4446 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4462 #undef IS_ASCII_ALNUM
4463 #undef IS_RFC822_CHAR
4465 gchar *make_email_string(const gchar *bp, const gchar *ep)
4467 /* returns a mailto: URI; mailto: is also used to detect the
4468 * uri type later on in the button_pressed signal handler */
4472 tmp = g_strndup(bp, ep - bp);
4473 result = g_strconcat("mailto:", tmp, NULL);
4479 gchar *make_http_string(const gchar *bp, const gchar *ep)
4481 /* returns an http: URI; */
4485 while (bp && *bp && g_ascii_isspace(*bp))
4487 tmp = g_strndup(bp, ep - bp);
4488 result = g_strconcat("http://", tmp, NULL);
4494 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4496 FILE *fp = fopen(path, "rb");
4497 gchar buf[BUFFSIZE];
4498 gchar *result = NULL;
4501 while (fgets(buf, sizeof (buf), fp) != NULL) {
4502 gchar **parts = g_strsplit(buf, ";", 3);
4503 gchar *trimmed = parts[0];
4504 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4506 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4507 trimmed[strlen(trimmed)-1] = '\0';
4509 if (!strcmp(trimmed, type)) {
4510 gboolean needsterminal = FALSE;
4511 if (parts[2] && strstr(parts[2], "needsterminal")) {
4512 needsterminal = TRUE;
4514 if (parts[2] && strstr(parts[2], "test=")) {
4515 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4516 gchar *testcmd = orig_testcmd;
4517 if (strstr(testcmd,";"))
4518 *(strstr(testcmd,";")) = '\0';
4519 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4521 while (testcmd[strlen(testcmd)-1] == '\n')
4522 testcmd[strlen(testcmd)-1] = '\0';
4523 while (testcmd[strlen(testcmd)-1] == '\r')
4524 testcmd[strlen(testcmd)-1] = '\0';
4525 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4526 testcmd[strlen(testcmd)-1] = '\0';
4528 if (strstr(testcmd, "%s")) {
4529 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4530 gint res = system(tmp);
4532 g_free(orig_testcmd);
4539 gint res = system(testcmd);
4540 g_free(orig_testcmd);
4550 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4552 while (trimmed[strlen(trimmed)-1] == '\n')
4553 trimmed[strlen(trimmed)-1] = '\0';
4554 while (trimmed[strlen(trimmed)-1] == '\r')
4555 trimmed[strlen(trimmed)-1] = '\0';
4556 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4557 trimmed[strlen(trimmed)-1] = '\0';
4558 result = g_strdup(trimmed);
4561 /* if there are no single quotes around %s, add them.
4562 * '.*%s.*' is ok, as in display 'png:%s'
4564 if (strstr(result, "%s")
4565 && !(strstr(result, "'") < strstr(result,"%s") &&
4566 strstr(strstr(result,"%s"), "'"))) {
4567 gchar *start = g_strdup(result);
4568 gchar *end = g_strdup(strstr(result, "%s")+2);
4570 *strstr(start, "%s") = '\0';
4571 tmp = g_strconcat(start,"'%s'",end, NULL);
4577 if (needsterminal) {
4578 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4589 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4591 gchar *result = NULL;
4595 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4596 result = mailcap_get_command_in_file(path, type, file_to_open);
4600 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4604 void mailcap_update_default(const gchar *type, const gchar *command)
4606 gchar *path = NULL, *outpath = NULL;
4607 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4608 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4609 FILE *fp = fopen(path, "rb");
4610 FILE *outfp = fopen(outpath, "wb");
4611 gchar buf[BUFFSIZE];
4612 gboolean err = FALSE;
4620 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4621 gchar **parts = g_strsplit(buf, ";", 3);
4622 gchar *trimmed = parts[0];
4623 while (trimmed[0] == ' ')
4625 while (trimmed[strlen(trimmed)-1] == ' ')
4626 trimmed[strlen(trimmed)-1] = '\0';
4628 if (!strcmp(trimmed, type)) {
4633 if(fputs(buf, outfp) == EOF) {
4640 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4646 if (fclose(outfp) == EOF)
4650 g_rename(outpath, path);
4656 gint copy_dir(const gchar *src, const gchar *dst)
4661 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4662 g_warning("failed to open directory: %s\n", src);
4666 if (make_dir(dst) < 0)
4669 while ((name = g_dir_read_name(dir)) != NULL) {
4670 gchar *old_file, *new_file;
4671 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4672 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4673 debug_print("copying: %s -> %s\n", old_file, new_file);
4674 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4675 gint r = copy_file(old_file, new_file, TRUE);
4682 /* Windows has no symlinks. Or well, Vista seems to
4683 have something like this but the semantics might be
4684 different. Thus we don't use it under Windows. */
4685 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4688 gchar *target = g_file_read_link(old_file, &error);
4690 r = symlink(target, new_file);
4697 #endif /*G_OS_WIN32*/
4698 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4699 gint r = copy_dir(old_file, new_file);
4710 /* crude test to see if a file is an email. */
4711 gboolean file_is_email (const gchar *filename)
4717 if (filename == NULL)
4719 if ((fp = g_fopen(filename, "rb")) == NULL)
4721 while (i < 60 && score < 3
4722 && fgets(buffer, sizeof (buffer), fp) > 0) {
4723 if (!strncmp(buffer, "From:", strlen("From:")))
4725 if (!strncmp(buffer, "To:", strlen("To:")))
4727 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4732 return (score >= 3);
4735 gboolean sc_g_list_bigger(GList *list, gint max)
4739 while (cur && i <= max+1) {
4746 gboolean sc_g_slist_bigger(GSList *list, gint max)
4750 while (cur && i <= max+1) {
4757 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4758 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4759 NULL, NULL, NULL, NULL, NULL, NULL};
4760 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4761 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4762 NULL, NULL, NULL, NULL, NULL, NULL};
4763 const gchar *s_am_up = NULL;
4764 const gchar *s_pm_up = NULL;
4765 const gchar *s_am_low = NULL;
4766 const gchar *s_pm_low = NULL;
4767 const gchar *def_loc_format = NULL;
4768 const gchar *date_loc_format = NULL;
4769 const gchar *time_loc_format = NULL;
4770 const gchar *time_am_pm = NULL;
4772 static gboolean time_names_init_done = FALSE;
4774 static void init_time_names(void)
4776 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4777 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4778 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4779 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4780 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4781 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4782 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4784 monthnames[0] = Q_("Complete month name for use by strftime|January");
4785 monthnames[1] = Q_("Complete month name for use by strftime|February");
4786 monthnames[2] = Q_("Complete month name for use by strftime|March");
4787 monthnames[3] = Q_("Complete month name for use by strftime|April");
4788 monthnames[4] = Q_("Complete month name for use by strftime|May");
4789 monthnames[5] = Q_("Complete month name for use by strftime|June");
4790 monthnames[6] = Q_("Complete month name for use by strftime|July");
4791 monthnames[7] = Q_("Complete month name for use by strftime|August");
4792 monthnames[8] = Q_("Complete month name for use by strftime|September");
4793 monthnames[9] = Q_("Complete month name for use by strftime|October");
4794 monthnames[10] = Q_("Complete month name for use by strftime|November");
4795 monthnames[11] = Q_("Complete month name for use by strftime|December");
4797 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4798 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4799 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4800 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4801 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4802 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4803 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4805 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4806 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4807 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4808 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4809 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4810 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4811 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4812 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4813 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4814 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4815 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4816 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4818 s_am_up = Q_("For use by strftime (morning)|AM");
4819 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4820 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4821 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4823 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4824 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4825 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4827 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4829 time_names_init_done = TRUE;
4832 #define CHECK_SIZE() { \
4833 total_done += len; \
4834 if (total_done >= buflen) { \
4835 buf[buflen-1] = '\0'; \
4840 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4842 gchar *curpos = buf;
4843 gint total_done = 0;
4844 gchar subbuf[64], subfmt[64];
4845 static time_t last_tzset = (time_t)0;
4847 if (!time_names_init_done)
4850 if (format == NULL || lt == NULL)
4853 if (last_tzset != time(NULL)) {
4855 last_tzset = time(NULL);
4858 if (*format == '%') {
4859 gint len = 0, tmp = 0;
4863 len = 1; CHECK_SIZE();
4867 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4868 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4871 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4872 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4876 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4877 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4880 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4881 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4884 fast_strftime(subbuf, 64, def_loc_format, lt);
4885 len = strlen(subbuf); CHECK_SIZE();
4886 strncpy2(curpos, subbuf, buflen - total_done);
4889 total_done += 2; CHECK_SIZE();
4890 tmp = (lt->tm_year + 1900)/100;
4891 *curpos++ = '0'+(tmp / 10);
4892 *curpos++ = '0'+(tmp % 10);
4895 total_done += 2; CHECK_SIZE();
4896 *curpos++ = '0'+(lt->tm_mday / 10);
4897 *curpos++ = '0'+(lt->tm_mday % 10);
4900 total_done += 8; CHECK_SIZE();
4901 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4902 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4904 *curpos++ = '0'+(lt->tm_mday / 10);
4905 *curpos++ = '0'+(lt->tm_mday % 10);
4907 tmp = lt->tm_year%100;
4908 *curpos++ = '0'+(tmp / 10);
4909 *curpos++ = '0'+(tmp % 10);
4912 len = 2; CHECK_SIZE();
4913 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4916 len = 10; CHECK_SIZE();
4917 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4918 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4921 total_done += 2; CHECK_SIZE();
4922 *curpos++ = '0'+(lt->tm_hour / 10);
4923 *curpos++ = '0'+(lt->tm_hour % 10);
4926 total_done += 2; CHECK_SIZE();
4932 *curpos++ = '0'+(tmp / 10);
4933 *curpos++ = '0'+(tmp % 10);
4936 len = 3; CHECK_SIZE();
4937 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4940 len = 2; CHECK_SIZE();
4941 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4944 len = 2; CHECK_SIZE();
4950 snprintf(curpos, buflen - total_done, "%2d", tmp);
4953 total_done += 2; CHECK_SIZE();
4954 tmp = lt->tm_mon + 1;
4955 *curpos++ = '0'+(tmp / 10);
4956 *curpos++ = '0'+(tmp % 10);
4959 total_done += 2; CHECK_SIZE();
4960 *curpos++ = '0'+(lt->tm_min / 10);
4961 *curpos++ = '0'+(lt->tm_min % 10);
4964 len = 1; CHECK_SIZE();
4968 if (lt->tm_hour >= 12) {
4969 len = strlen(s_pm_up); CHECK_SIZE();
4970 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
4972 len = strlen(s_am_up); CHECK_SIZE();
4973 snprintf(curpos, buflen-total_done, "%s", s_am_up);
4977 if (lt->tm_hour >= 12) {
4978 len = strlen(s_pm_low); CHECK_SIZE();
4979 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
4981 len = strlen(s_am_low); CHECK_SIZE();
4982 snprintf(curpos, buflen-total_done, "%s", s_am_low);
4986 fast_strftime(subbuf, 64, time_am_pm, lt);
4987 len = strlen(subbuf); CHECK_SIZE();
4988 strncpy2(curpos, subbuf, buflen - total_done);
4991 total_done += 5; CHECK_SIZE();
4992 *curpos++ = '0'+(lt->tm_hour / 10);
4993 *curpos++ = '0'+(lt->tm_hour % 10);
4995 *curpos++ = '0'+(lt->tm_min / 10);
4996 *curpos++ = '0'+(lt->tm_min % 10);
4999 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5000 len = strlen(subbuf); CHECK_SIZE();
5001 strncpy2(curpos, subbuf, buflen - total_done);
5004 total_done += 2; CHECK_SIZE();
5005 *curpos++ = '0'+(lt->tm_sec / 10);
5006 *curpos++ = '0'+(lt->tm_sec % 10);
5009 len = 1; CHECK_SIZE();
5013 total_done += 8; CHECK_SIZE();
5014 *curpos++ = '0'+(lt->tm_hour / 10);
5015 *curpos++ = '0'+(lt->tm_hour % 10);
5017 *curpos++ = '0'+(lt->tm_min / 10);
5018 *curpos++ = '0'+(lt->tm_min % 10);
5020 *curpos++ = '0'+(lt->tm_sec / 10);
5021 *curpos++ = '0'+(lt->tm_sec % 10);
5024 len = 1; CHECK_SIZE();
5025 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5028 len = 1; CHECK_SIZE();
5029 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5032 fast_strftime(subbuf, 64, date_loc_format, lt);
5033 len = strlen(subbuf); CHECK_SIZE();
5034 strncpy2(curpos, subbuf, buflen - total_done);
5037 fast_strftime(subbuf, 64, time_loc_format, lt);
5038 len = strlen(subbuf); CHECK_SIZE();
5039 strncpy2(curpos, subbuf, buflen - total_done);
5042 total_done += 2; CHECK_SIZE();
5043 tmp = lt->tm_year%100;
5044 *curpos++ = '0'+(tmp / 10);
5045 *curpos++ = '0'+(tmp % 10);
5048 len = 4; CHECK_SIZE();
5049 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5059 /* let these complicated ones be done with the libc */
5060 snprintf(subfmt, 64, "%%%c", *format);
5061 strftime(subbuf, 64, subfmt, lt);
5062 len = strlen(subbuf); CHECK_SIZE();
5063 strncpy2(curpos, subbuf, buflen - total_done);
5067 /* let these complicated modifiers be done with the libc */
5068 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5069 strftime(subbuf, 64, subfmt, lt);
5070 len = strlen(subbuf); CHECK_SIZE();
5071 strncpy2(curpos, subbuf, buflen - total_done);
5075 g_warning("format error (%c)", *format);
5082 int len = 1; CHECK_SIZE();
5083 *curpos++ = *format++;
5090 gboolean prefs_common_get_use_shred(void);
5094 #define WEXITSTATUS(x) (x)
5097 int claws_unlink(const gchar *filename)
5100 static int found_shred = -1;
5101 static const gchar *args[4];
5103 if (filename == NULL)
5106 if (prefs_common_get_use_shred()) {
5107 if (found_shred == -1) {
5109 args[0] = g_find_program_in_path("shred");
5110 debug_print("found shred: %s\n", args[0]);
5111 found_shred = (args[0] != NULL) ? 1:0;
5115 if (found_shred == 1) {
5116 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5117 if (s.st_nlink == 1) {
5120 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5121 NULL, NULL, NULL, NULL, &status, NULL);
5122 debug_print("%s %s exited with status %d\n",
5123 args[0], filename, WEXITSTATUS(status));
5124 if (truncate(filename, 0) < 0)
5125 g_warning("couln't truncate");
5130 return g_unlink(filename);