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 /* test whether the file is readable without blocking */
3123 fd = open(file, O_RDONLY | O_NONBLOCK);
3125 FILE_OP_ERROR(file, "open");
3132 /* allow for one second */
3133 err = select(fd+1, &fds, NULL, NULL, &timeout);
3134 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3136 FILE_OP_ERROR(file, "select");
3138 g_warning("%s: doesn't seem readable\n", file);
3144 /* Now clear O_NONBLOCK */
3145 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3146 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3150 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3151 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3156 /* get the FILE pointer */
3157 fp = fdopen(fd, "rb");
3160 FILE_OP_ERROR(file, "fdopen");
3161 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3166 str = file_read_stream_to_str_full(fp, recode);
3173 gchar *file_read_to_str(const gchar *file)
3175 return file_read_to_str_full(file, TRUE);
3177 gchar *file_read_stream_to_str(FILE *fp)
3179 return file_read_stream_to_str_full(fp, TRUE);
3182 gchar *file_read_to_str_no_recode(const gchar *file)
3184 return file_read_to_str_full(file, FALSE);
3186 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3188 return file_read_stream_to_str_full(fp, FALSE);
3191 char *fgets_crlf(char *buf, int size, FILE *stream)
3193 gboolean is_cr = FALSE;
3194 gboolean last_was_cr = FALSE;
3199 while (--size > 0 && (c = getc(stream)) != EOF)
3202 is_cr = (c == '\r');
3212 last_was_cr = is_cr;
3214 if (c == EOF && cs == buf)
3222 static gint execute_async(gchar *const argv[])
3224 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3226 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3227 NULL, NULL, NULL, FALSE) == FALSE) {
3228 g_warning("Couldn't execute command: %s\n", argv[0]);
3235 static gint execute_sync(gchar *const argv[])
3239 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3241 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3242 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3243 g_warning("Couldn't execute command: %s\n", argv[0]);
3248 if (WIFEXITED(status))
3249 return WEXITSTATUS(status);
3257 gint execute_command_line(const gchar *cmdline, gboolean async)
3262 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3264 argv = strsplit_with_quote(cmdline, " ", 0);
3267 ret = execute_async(argv);
3269 ret = execute_sync(argv);
3276 gchar *get_command_output(const gchar *cmdline)
3278 gchar *child_stdout;
3281 g_return_val_if_fail(cmdline != NULL, NULL);
3283 debug_print("get_command_output(): executing: %s\n", cmdline);
3285 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3287 g_warning("Couldn't execute command: %s\n", cmdline);
3291 return child_stdout;
3294 static gint is_unchanged_uri_char(char c)
3305 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3311 for(i = 0; i < strlen(uri) ; i++) {
3312 if (is_unchanged_uri_char(uri[i])) {
3313 if (k + 2 >= bufsize)
3315 encoded_uri[k++] = uri[i];
3318 char * hexa = "0123456789ABCDEF";
3320 if (k + 4 >= bufsize)
3322 encoded_uri[k++] = '%';
3323 encoded_uri[k++] = hexa[uri[i] / 16];
3324 encoded_uri[k++] = hexa[uri[i] % 16];
3330 gint open_uri(const gchar *uri, const gchar *cmdline)
3333 gchar buf[BUFFSIZE];
3335 gchar encoded_uri[BUFFSIZE];
3336 g_return_val_if_fail(uri != NULL, -1);
3338 /* an option to choose whether to use encode_uri or not ? */
3339 encode_uri(encoded_uri, BUFFSIZE, uri);
3342 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3343 !strchr(p + 2, '%'))
3344 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3347 g_warning("Open URI command line is invalid "
3348 "(there must be only one '%%s'): %s",
3350 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3353 execute_command_line(buf, TRUE);
3355 extern osso_context_t *get_osso_context(void);
3356 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3357 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3358 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3363 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3365 gchar buf[BUFFSIZE];
3368 g_return_val_if_fail(filepath != NULL, -1);
3371 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3372 !strchr(p + 2, '%'))
3373 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3376 g_warning("Open Text Editor command line is invalid "
3377 "(there must be only one '%%s'): %s",
3379 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3382 execute_command_line(buf, TRUE);
3387 time_t remote_tzoffset_sec(const gchar *zone)
3389 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3395 time_t remoteoffset;
3397 strncpy(zone3, zone, 3);
3401 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3402 (c == '+' || c == '-')) {
3403 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3405 remoteoffset = -remoteoffset;
3406 } else if (!strncmp(zone, "UT" , 2) ||
3407 !strncmp(zone, "GMT", 2)) {
3409 } else if (strlen(zone3) == 3) {
3410 for (p = ustzstr; *p != '\0'; p += 3) {
3411 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3412 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3413 remoteoffset = iustz * 3600;
3419 } else if (strlen(zone3) == 1) {
3421 case 'Z': remoteoffset = 0; break;
3422 case 'A': remoteoffset = -1; break;
3423 case 'B': remoteoffset = -2; break;
3424 case 'C': remoteoffset = -3; break;
3425 case 'D': remoteoffset = -4; break;
3426 case 'E': remoteoffset = -5; break;
3427 case 'F': remoteoffset = -6; break;
3428 case 'G': remoteoffset = -7; break;
3429 case 'H': remoteoffset = -8; break;
3430 case 'I': remoteoffset = -9; break;
3431 case 'K': remoteoffset = -10; break; /* J is not used */
3432 case 'L': remoteoffset = -11; break;
3433 case 'M': remoteoffset = -12; break;
3434 case 'N': remoteoffset = 1; break;
3435 case 'O': remoteoffset = 2; break;
3436 case 'P': remoteoffset = 3; break;
3437 case 'Q': remoteoffset = 4; break;
3438 case 'R': remoteoffset = 5; break;
3439 case 'S': remoteoffset = 6; break;
3440 case 'T': remoteoffset = 7; break;
3441 case 'U': remoteoffset = 8; break;
3442 case 'V': remoteoffset = 9; break;
3443 case 'W': remoteoffset = 10; break;
3444 case 'X': remoteoffset = 11; break;
3445 case 'Y': remoteoffset = 12; break;
3446 default: remoteoffset = 0; break;
3448 remoteoffset = remoteoffset * 3600;
3452 return remoteoffset;
3455 time_t tzoffset_sec(time_t *now)
3460 struct tm buf1, buf2;
3463 gmt = *gmtime_r(now, &buf1);
3464 lt = localtime_r(now, &buf2);
3466 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3468 if (lt->tm_year < gmt.tm_year)
3470 else if (lt->tm_year > gmt.tm_year)
3472 else if (lt->tm_yday < gmt.tm_yday)
3474 else if (lt->tm_yday > gmt.tm_yday)
3477 if (off >= 24 * 60) /* should be impossible */
3478 off = 23 * 60 + 59; /* if not, insert silly value */
3479 if (off <= -24 * 60)
3480 off = -(23 * 60 + 59);
3485 /* calculate timezone offset */
3486 gchar *tzoffset(time_t *now)
3488 static gchar offset_string[6];
3493 struct tm buf1, buf2;
3496 gmt = *gmtime_r(now, &buf1);
3497 lt = localtime_r(now, &buf2);
3499 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3501 if (lt->tm_year < gmt.tm_year)
3503 else if (lt->tm_year > gmt.tm_year)
3505 else if (lt->tm_yday < gmt.tm_yday)
3507 else if (lt->tm_yday > gmt.tm_yday)
3515 if (off >= 24 * 60) /* should be impossible */
3516 off = 23 * 60 + 59; /* if not, insert silly value */
3518 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3520 return offset_string;
3523 void get_rfc822_date(gchar *buf, gint len)
3527 gchar day[4], mon[4];
3528 gint dd, hh, mm, ss, yyyy;
3531 gchar buf2[BUFFSIZE];
3535 lt = localtime_r(&t, &buf1);
3537 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3538 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3540 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3541 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3544 void debug_set_mode(gboolean mode)
3549 gboolean debug_get_mode(void)
3554 void debug_print_real(const gchar *format, ...)
3557 gchar buf[BUFFSIZE];
3559 if (!debug_mode) return;
3561 va_start(args, format);
3562 g_vsnprintf(buf, sizeof(buf), format, args);
3569 const char * debug_srcname(const char *file)
3571 const char *s = strrchr (file, '/');
3576 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3578 if (subject == NULL)
3581 subject += subject_get_prefix_length(subject);
3583 return g_hash_table_lookup(subject_table, subject);
3586 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3589 if (subject == NULL || *subject == 0)
3591 subject += subject_get_prefix_length(subject);
3592 g_hash_table_insert(subject_table, subject, data);
3595 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3597 if (subject == NULL)
3600 subject += subject_get_prefix_length(subject);
3601 g_hash_table_remove(subject_table, subject);
3605 *\brief Check if a string is prefixed with known (combinations)
3606 * of prefixes. The function assumes that each prefix
3607 * is terminated by zero or exactly _one_ space.
3609 *\param str String to check for a prefixes
3611 *\return int Number of chars in the prefix that should be skipped
3612 * for a "clean" subject line. If no prefix was found, 0
3615 static regex_t u_regex;
3616 static gboolean u_init_;
3618 void utils_free_regex(void)
3626 int subject_get_prefix_length(const gchar *subject)
3628 /*!< Array with allowable reply prefixes regexps. */
3629 static const gchar * const prefixes[] = {
3630 "Re\\:", /* "Re:" */
3631 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3632 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3633 "Aw\\:", /* "Aw:" (German) */
3634 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3635 "Res\\:", /* "Res:" (Brazilian Outlook) */
3636 "Fw\\:", /* "Fw:" Forward */
3637 "Fwd\\:", /* "Fwd:" Forward */
3638 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3639 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3640 "Rif\\:", /* "Rif:" (Italian Outlook) */
3641 "Sv\\:", /* "Sv" (Norwegian) */
3642 "Vs\\:", /* "Vs" (Norwegian) */
3643 "Ad\\:", /* "Ad" (Norwegian) */
3644 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3647 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3651 if (!subject) return 0;
3652 if (!*subject) return 0;
3655 GString *s = g_string_new("");
3657 for (n = 0; n < PREFIXES; n++)
3658 /* Terminate each prefix regexpression by a
3659 * "\ ?" (zero or ONE space), and OR them */
3660 g_string_append_printf(s, "(%s\\ ?)%s",
3665 g_string_prepend(s, "(");
3666 g_string_append(s, ")+"); /* match at least once */
3667 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3670 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3671 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3672 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3673 debug_print("Error compiling regexp %s\n", s->str);
3674 g_string_free(s, TRUE);
3678 g_string_free(s, TRUE);
3682 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3688 static guint g_stricase_hash(gconstpointer gptr)
3690 guint hash_result = 0;
3693 for (str = gptr; str && *str; str++) {
3694 hash_result += toupper(*str);
3700 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3702 const char *str1 = gptr1;
3703 const char *str2 = gptr2;
3705 return !strcasecmp(str1, str2);
3708 gint g_int_compare(gconstpointer a, gconstpointer b)
3710 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3713 gchar *generate_msgid(gchar *buf, gint len)
3723 lt = localtime_r(&t, &buft);
3725 if (strcmp(buf, "") == 0) {
3726 addr = g_strconcat("@", get_domain_name(), NULL);
3729 addr = g_strconcat("@", buf, NULL);
3732 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3733 lt->tm_year + 1900, lt->tm_mon + 1,
3734 lt->tm_mday, lt->tm_hour,
3735 lt->tm_min, lt->tm_sec,
3736 (guint) rand(), addr);
3743 quote_cmd_argument()
3745 return a quoted string safely usable in argument of a command.
3747 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3750 gint quote_cmd_argument(gchar * result, guint size,
3760 for(p = path ; * p != '\0' ; p ++) {
3762 if (isalnum((guchar)*p) || (* p == '/')) {
3763 if (remaining > 0) {
3769 result[size - 1] = '\0';
3774 if (remaining >= 2) {
3782 result[size - 1] = '\0';
3787 if (remaining > 0) {
3791 result[size - 1] = '\0';
3805 static void g_node_map_recursive(GNode *node, gpointer data)
3807 GNodeMapData *mapdata = (GNodeMapData *) data;
3809 GNodeMapData newmapdata;
3812 newdata = mapdata->func(node->data, mapdata->data);
3813 if (newdata != NULL) {
3814 newnode = g_node_new(newdata);
3815 g_node_append(mapdata->parent, newnode);
3817 newmapdata.parent = newnode;
3818 newmapdata.func = mapdata->func;
3819 newmapdata.data = mapdata->data;
3821 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3825 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3828 GNodeMapData mapdata;
3830 g_return_val_if_fail(node != NULL, NULL);
3831 g_return_val_if_fail(func != NULL, NULL);
3833 root = g_node_new(func(node->data, data));
3835 mapdata.parent = root;
3836 mapdata.func = func;
3837 mapdata.data = data;
3839 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3844 #define HEX_TO_INT(val, hex) \
3848 if ('0' <= c && c <= '9') { \
3850 } else if ('a' <= c && c <= 'f') { \
3851 val = c - 'a' + 10; \
3852 } else if ('A' <= c && c <= 'F') { \
3853 val = c - 'A' + 10; \
3859 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3866 if (hi == -1 || lo == -1)
3869 *out = (hi << 4) + lo;
3873 #define INT_TO_HEX(hex, val) \
3876 hex = '0' + (val); \
3878 hex = 'A' + (val) - 10; \
3881 void get_hex_str(gchar *out, guchar ch)
3885 INT_TO_HEX(hex, ch >> 4);
3887 INT_TO_HEX(hex, ch & 0x0f);
3893 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3895 #define G_PRINT_REF g_print
3899 *\brief Register ref counted pointer. It is based on GBoxed, so should
3900 * work with anything that uses the GType system. The semantics
3901 * are similar to a C++ auto pointer, with the exception that
3902 * C doesn't have automatic closure (calling destructors) when
3903 * exiting a block scope.
3904 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3905 * function directly.
3907 *\return GType A GType type.
3909 GType g_auto_pointer_register(void)
3911 static GType auto_pointer_type;
3912 if (!auto_pointer_type)
3914 g_boxed_type_register_static
3915 ("G_TYPE_AUTO_POINTER",
3916 (GBoxedCopyFunc) g_auto_pointer_copy,
3917 (GBoxedFreeFunc) g_auto_pointer_free);
3918 return auto_pointer_type;
3922 *\brief Structure with g_new() allocated pointer guarded by the
3925 typedef struct AutoPointerRef {
3926 void (*free) (gpointer);
3932 *\brief The auto pointer opaque structure that references the
3933 * pointer guard block.
3935 typedef struct AutoPointer {
3936 AutoPointerRef *ref;
3937 gpointer ptr; /*!< access to protected pointer */
3941 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3945 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3946 * ... when assigning, copying and freeing storage elements
3948 * gtk_list_store_new(N_S_COLUMNS,
3949 * G_TYPE_AUTO_POINTER,
3953 * Template *precious_data = g_new0(Template, 1);
3954 * g_pointer protect = g_auto_pointer_new(precious_data);
3956 * gtk_list_store_set(container, &iter,
3960 * ... the gtk_list_store has copied the pointer and
3961 * ... incremented its reference count, we should free
3962 * ... the auto pointer (in C++ a destructor would do
3963 * ... this for us when leaving block scope)
3965 * g_auto_pointer_free(protect);
3967 * ... gtk_list_store_set() now manages the data. When
3968 * ... *explicitly* requesting a pointer from the list
3969 * ... store, don't forget you get a copy that should be
3970 * ... freed with g_auto_pointer_free() eventually.
3974 *\param pointer Pointer to be guarded.
3976 *\return GAuto * Pointer that should be used in containers with
3979 GAuto *g_auto_pointer_new(gpointer p)
3981 AutoPointerRef *ref;
3987 ref = g_new0(AutoPointerRef, 1);
3988 ptr = g_new0(AutoPointer, 1);
3998 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4004 *\brief Allocate an autopointer using the passed \a free function to
4005 * free the guarded pointer
4007 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4014 aptr = g_auto_pointer_new(p);
4015 aptr->ref->free = free_;
4019 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4021 if (auto_ptr == NULL)
4023 return ((AutoPointer *) auto_ptr)->ptr;
4027 *\brief Copies an auto pointer by. It's mostly not necessary
4028 * to call this function directly, unless you copy/assign
4029 * the guarded pointer.
4031 *\param auto_ptr Auto pointer returned by previous call to
4032 * g_auto_pointer_new_XXX()
4034 *\return gpointer An auto pointer
4036 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4039 AutoPointerRef *ref;
4042 if (auto_ptr == NULL)
4047 newp = g_new0(AutoPointer, 1);
4050 newp->ptr = ref->pointer;
4054 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4060 *\brief Free an auto pointer
4062 void g_auto_pointer_free(GAuto *auto_ptr)
4065 AutoPointerRef *ref;
4067 if (auto_ptr == NULL)
4073 if (--(ref->cnt) == 0) {
4075 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4077 ref->free(ref->pointer);
4082 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4087 void replace_returns(gchar *str)
4092 while (strstr(str, "\n")) {
4093 *strstr(str, "\n") = ' ';
4095 while (strstr(str, "\r")) {
4096 *strstr(str, "\r") = ' ';
4100 /* get_uri_part() - retrieves a URI starting from scanpos.
4101 Returns TRUE if succesful */
4102 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4103 const gchar **bp, const gchar **ep, gboolean hdr)
4106 gint parenthese_cnt = 0;
4108 g_return_val_if_fail(start != NULL, FALSE);
4109 g_return_val_if_fail(scanpos != NULL, FALSE);
4110 g_return_val_if_fail(bp != NULL, FALSE);
4111 g_return_val_if_fail(ep != NULL, FALSE);
4115 /* find end point of URI */
4116 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4117 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4118 !IS_ASCII(*(const guchar *)ep_) ||
4119 strchr("[]{}<>\"", *ep_)) {
4121 } else if (strchr("(", *ep_)) {
4123 } else if (strchr(")", *ep_)) {
4124 if (parenthese_cnt > 0)
4131 /* no punctuation at end of string */
4133 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4134 * should pass some URI type to this function and decide on that whether
4135 * to perform punctuation stripping */
4137 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4139 for (; ep_ - 1 > scanpos + 1 &&
4140 IS_REAL_PUNCT(*(ep_ - 1));
4144 #undef IS_REAL_PUNCT
4151 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4153 while (bp && *bp && g_ascii_isspace(*bp))
4155 return g_strndup(bp, ep - bp);
4158 /* valid mail address characters */
4159 #define IS_RFC822_CHAR(ch) \
4163 !g_ascii_isspace(ch) && \
4164 !strchr("(),;<>\"", (ch)))
4166 /* alphabet and number within 7bit ASCII */
4167 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4168 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4170 static GHashTable *create_domain_tab(void)
4172 static const gchar *toplvl_domains [] = {
4174 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4175 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4176 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4177 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4178 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4179 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4180 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4181 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4182 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4183 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4184 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4185 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4186 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4187 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4188 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4189 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4190 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4191 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4192 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4193 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4194 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4195 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4196 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4197 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4198 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4199 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4202 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4204 g_return_val_if_fail(htab, NULL);
4205 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4206 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4210 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4212 const gint MAX_LVL_DOM_NAME_LEN = 6;
4213 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4214 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4217 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4220 for (p = buf; p < m && first < last; *p++ = *first++)
4224 return g_hash_table_lookup(tab, buf) != NULL;
4227 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4228 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4229 const gchar **bp, const gchar **ep, gboolean hdr)
4231 /* more complex than the uri part because we need to scan back and forward starting from
4232 * the scan position. */
4233 gboolean result = FALSE;
4234 const gchar *bp_ = NULL;
4235 const gchar *ep_ = NULL;
4236 static GHashTable *dom_tab;
4237 const gchar *last_dot = NULL;
4238 const gchar *prelast_dot = NULL;
4239 const gchar *last_tld_char = NULL;
4241 /* the informative part of the email address (describing the name
4242 * of the email address owner) may contain quoted parts. the
4243 * closure stack stores the last encountered quotes. */
4244 gchar closure_stack[128];
4245 gchar *ptr = closure_stack;
4247 g_return_val_if_fail(start != NULL, FALSE);
4248 g_return_val_if_fail(scanpos != NULL, FALSE);
4249 g_return_val_if_fail(bp != NULL, FALSE);
4250 g_return_val_if_fail(ep != NULL, FALSE);
4253 const gchar *start_quote = NULL;
4254 const gchar *end_quote = NULL;
4256 /* go to the real start */
4257 if (start[0] == ',')
4259 if (start[0] == ';')
4261 while (start[0] == '\n' || start[0] == '\r')
4263 while (start[0] == ' ' || start[0] == '\t')
4268 /* check if there are quotes (to skip , in them) */
4269 if (*start == '"') {
4270 start_quote = start;
4272 end_quote = strstr(start, "\"");
4278 /* skip anything between quotes */
4279 if (start_quote && end_quote) {
4284 /* find end (either , or ; or end of line) */
4285 if (strstr(start, ",") && strstr(start, ";"))
4286 *ep = strstr(start,",") < strstr(start, ";")
4287 ? strstr(start, ",") : strstr(start, ";");
4288 else if (strstr(start, ","))
4289 *ep = strstr(start, ",");
4290 else if (strstr(start, ";"))
4291 *ep = strstr(start, ";");
4293 *ep = start+strlen(start);
4295 /* go back to real start */
4296 if (start_quote && end_quote) {
4297 start = start_quote;
4300 /* check there's still an @ in that, or search
4301 * further if possible */
4302 if (strstr(start, "@") && strstr(start, "@") < *ep)
4304 else if (*ep < start+strlen(start)) {
4307 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4315 dom_tab = create_domain_tab();
4316 g_return_val_if_fail(dom_tab, FALSE);
4318 /* scan start of address */
4319 for (bp_ = scanpos - 1;
4320 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4323 /* TODO: should start with an alnum? */
4325 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4328 if (bp_ != scanpos) {
4329 /* scan end of address */
4330 for (ep_ = scanpos + 1;
4331 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4333 prelast_dot = last_dot;
4335 if (*(last_dot + 1) == '.') {
4336 if (prelast_dot == NULL)
4338 last_dot = prelast_dot;
4343 /* TODO: really should terminate with an alnum? */
4344 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4349 if (last_dot == NULL)
4351 if (last_dot >= ep_)
4352 last_dot = prelast_dot;
4353 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4357 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4358 if (*last_tld_char == '?')
4361 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4368 if (!result) return FALSE;
4370 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4371 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4372 && IS_RFC822_CHAR(*(ep_ + 3))) {
4373 /* this informative part with an @ in it is
4374 * followed by the email address */
4377 /* go to matching '>' (or next non-rfc822 char, like \n) */
4378 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4381 /* include the bracket */
4382 if (*ep_ == '>') ep_++;
4384 /* include the leading quote */
4392 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4393 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4396 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4397 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4400 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4401 #define IN_STACK() (ptr > closure_stack)
4402 /* has underrun check */
4403 #define POP_STACK() if(IN_STACK()) --ptr
4404 /* has overrun check */
4405 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4406 /* has underrun check */
4407 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4411 /* scan for the informative part. */
4412 for (bp_ -= 2; bp_ >= start; bp_--) {
4413 /* if closure on the stack keep scanning */
4414 if (PEEK_STACK() == *bp_) {
4418 if (*bp_ == '\'' || *bp_ == '"') {
4423 /* if nothing in the closure stack, do the special conditions
4424 * the following if..else expression simply checks whether
4425 * a token is acceptable. if not acceptable, the clause
4426 * should terminate the loop with a 'break' */
4427 if (!PEEK_STACK()) {
4429 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4430 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4431 /* hyphens are allowed, but only in
4433 } else if (strchr(" \"'", *bp_)) {
4434 /* but anything not being a punctiation
4437 break; /* anything else is rejected */
4444 /* scan forward (should start with an alnum) */
4445 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4461 #undef IS_ASCII_ALNUM
4462 #undef IS_RFC822_CHAR
4464 gchar *make_email_string(const gchar *bp, const gchar *ep)
4466 /* returns a mailto: URI; mailto: is also used to detect the
4467 * uri type later on in the button_pressed signal handler */
4471 tmp = g_strndup(bp, ep - bp);
4472 result = g_strconcat("mailto:", tmp, NULL);
4478 gchar *make_http_string(const gchar *bp, const gchar *ep)
4480 /* returns an http: URI; */
4484 while (bp && *bp && g_ascii_isspace(*bp))
4486 tmp = g_strndup(bp, ep - bp);
4487 result = g_strconcat("http://", tmp, NULL);
4493 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4495 FILE *fp = fopen(path, "rb");
4496 gchar buf[BUFFSIZE];
4497 gchar *result = NULL;
4500 while (fgets(buf, sizeof (buf), fp) != NULL) {
4501 gchar **parts = g_strsplit(buf, ";", 3);
4502 gchar *trimmed = parts[0];
4503 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4505 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4506 trimmed[strlen(trimmed)-1] = '\0';
4508 if (!strcmp(trimmed, type)) {
4509 gboolean needsterminal = FALSE;
4510 if (parts[2] && strstr(parts[2], "needsterminal")) {
4511 needsterminal = TRUE;
4513 if (parts[2] && strstr(parts[2], "test=")) {
4514 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4515 gchar *testcmd = orig_testcmd;
4516 if (strstr(testcmd,";"))
4517 *(strstr(testcmd,";")) = '\0';
4518 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4520 while (testcmd[strlen(testcmd)-1] == '\n')
4521 testcmd[strlen(testcmd)-1] = '\0';
4522 while (testcmd[strlen(testcmd)-1] == '\r')
4523 testcmd[strlen(testcmd)-1] = '\0';
4524 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4525 testcmd[strlen(testcmd)-1] = '\0';
4527 if (strstr(testcmd, "%s")) {
4528 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4529 gint res = system(tmp);
4531 g_free(orig_testcmd);
4538 gint res = system(testcmd);
4539 g_free(orig_testcmd);
4549 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4551 while (trimmed[strlen(trimmed)-1] == '\n')
4552 trimmed[strlen(trimmed)-1] = '\0';
4553 while (trimmed[strlen(trimmed)-1] == '\r')
4554 trimmed[strlen(trimmed)-1] = '\0';
4555 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4556 trimmed[strlen(trimmed)-1] = '\0';
4557 result = g_strdup(trimmed);
4560 /* if there are no single quotes around %s, add them.
4561 * '.*%s.*' is ok, as in display 'png:%s'
4563 if (strstr(result, "%s")
4564 && !(strstr(result, "'") < strstr(result,"%s") &&
4565 strstr(strstr(result,"%s"), "'"))) {
4566 gchar *start = g_strdup(result);
4567 gchar *end = g_strdup(strstr(result, "%s")+2);
4569 *strstr(start, "%s") = '\0';
4570 tmp = g_strconcat(start,"'%s'",end, NULL);
4576 if (needsterminal) {
4577 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4588 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4590 gchar *result = NULL;
4594 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4595 result = mailcap_get_command_in_file(path, type, file_to_open);
4599 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4603 void mailcap_update_default(const gchar *type, const gchar *command)
4605 gchar *path = NULL, *outpath = NULL;
4606 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4607 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4608 FILE *fp = fopen(path, "rb");
4609 FILE *outfp = fopen(outpath, "wb");
4610 gchar buf[BUFFSIZE];
4611 gboolean err = FALSE;
4619 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4620 gchar **parts = g_strsplit(buf, ";", 3);
4621 gchar *trimmed = parts[0];
4622 while (trimmed[0] == ' ')
4624 while (trimmed[strlen(trimmed)-1] == ' ')
4625 trimmed[strlen(trimmed)-1] = '\0';
4627 if (!strcmp(trimmed, type)) {
4632 if(fputs(buf, outfp) == EOF) {
4639 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4645 if (fclose(outfp) == EOF)
4649 g_rename(outpath, path);
4655 gint copy_dir(const gchar *src, const gchar *dst)
4660 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4661 g_warning("failed to open directory: %s\n", src);
4665 if (make_dir(dst) < 0)
4668 while ((name = g_dir_read_name(dir)) != NULL) {
4669 gchar *old_file, *new_file;
4670 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4671 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4672 debug_print("copying: %s -> %s\n", old_file, new_file);
4673 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4674 gint r = copy_file(old_file, new_file, TRUE);
4681 /* Windows has no symlinks. Or well, Vista seems to
4682 have something like this but the semantics might be
4683 different. Thus we don't use it under Windows. */
4684 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4687 gchar *target = g_file_read_link(old_file, &error);
4689 r = symlink(target, new_file);
4696 #endif /*G_OS_WIN32*/
4697 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4698 gint r = copy_dir(old_file, new_file);
4709 /* crude test to see if a file is an email. */
4710 gboolean file_is_email (const gchar *filename)
4716 if (filename == NULL)
4718 if ((fp = g_fopen(filename, "rb")) == NULL)
4720 while (i < 60 && score < 3
4721 && fgets(buffer, sizeof (buffer), fp) > 0) {
4722 if (!strncmp(buffer, "From:", strlen("From:")))
4724 if (!strncmp(buffer, "To:", strlen("To:")))
4726 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4731 return (score >= 3);
4734 gboolean sc_g_list_bigger(GList *list, gint max)
4738 while (cur && i <= max+1) {
4745 gboolean sc_g_slist_bigger(GSList *list, gint max)
4749 while (cur && i <= max+1) {
4756 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4757 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4758 NULL, NULL, NULL, NULL, NULL, NULL};
4759 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4760 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4761 NULL, NULL, NULL, NULL, NULL, NULL};
4762 const gchar *s_am_up = NULL;
4763 const gchar *s_pm_up = NULL;
4764 const gchar *s_am_low = NULL;
4765 const gchar *s_pm_low = NULL;
4766 const gchar *def_loc_format = NULL;
4767 const gchar *date_loc_format = NULL;
4768 const gchar *time_loc_format = NULL;
4769 const gchar *time_am_pm = NULL;
4771 static gboolean time_names_init_done = FALSE;
4773 static void init_time_names(void)
4775 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4776 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4777 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4778 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4779 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4780 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4781 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4783 monthnames[0] = Q_("Complete month name for use by strftime|January");
4784 monthnames[1] = Q_("Complete month name for use by strftime|February");
4785 monthnames[2] = Q_("Complete month name for use by strftime|March");
4786 monthnames[3] = Q_("Complete month name for use by strftime|April");
4787 monthnames[4] = Q_("Complete month name for use by strftime|May");
4788 monthnames[5] = Q_("Complete month name for use by strftime|June");
4789 monthnames[6] = Q_("Complete month name for use by strftime|July");
4790 monthnames[7] = Q_("Complete month name for use by strftime|August");
4791 monthnames[8] = Q_("Complete month name for use by strftime|September");
4792 monthnames[9] = Q_("Complete month name for use by strftime|October");
4793 monthnames[10] = Q_("Complete month name for use by strftime|November");
4794 monthnames[11] = Q_("Complete month name for use by strftime|December");
4796 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4797 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4798 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4799 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4800 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4801 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4802 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4804 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4805 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4806 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4807 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4808 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4809 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4810 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4811 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4812 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4813 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4814 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4815 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4817 s_am_up = Q_("For use by strftime (morning)|AM");
4818 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4819 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4820 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4822 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4823 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4824 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4826 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4828 time_names_init_done = TRUE;
4831 #define CHECK_SIZE() { \
4832 total_done += len; \
4833 if (total_done >= buflen) { \
4834 buf[buflen-1] = '\0'; \
4839 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4841 gchar *curpos = buf;
4842 gint total_done = 0;
4843 gchar subbuf[64], subfmt[64];
4844 static time_t last_tzset = (time_t)0;
4846 if (!time_names_init_done)
4849 if (format == NULL || lt == NULL)
4852 if (last_tzset != time(NULL)) {
4854 last_tzset = time(NULL);
4857 if (*format == '%') {
4858 gint len = 0, tmp = 0;
4862 len = 1; CHECK_SIZE();
4866 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4867 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4870 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4871 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4875 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4876 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4879 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4880 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4883 fast_strftime(subbuf, 64, def_loc_format, lt);
4884 len = strlen(subbuf); CHECK_SIZE();
4885 strncpy2(curpos, subbuf, buflen - total_done);
4888 total_done += 2; CHECK_SIZE();
4889 tmp = (lt->tm_year + 1900)/100;
4890 *curpos++ = '0'+(tmp / 10);
4891 *curpos++ = '0'+(tmp % 10);
4894 total_done += 2; CHECK_SIZE();
4895 *curpos++ = '0'+(lt->tm_mday / 10);
4896 *curpos++ = '0'+(lt->tm_mday % 10);
4899 total_done += 8; CHECK_SIZE();
4900 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4901 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4903 *curpos++ = '0'+(lt->tm_mday / 10);
4904 *curpos++ = '0'+(lt->tm_mday % 10);
4906 tmp = lt->tm_year%100;
4907 *curpos++ = '0'+(tmp / 10);
4908 *curpos++ = '0'+(tmp % 10);
4911 len = 2; CHECK_SIZE();
4912 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4915 len = 10; CHECK_SIZE();
4916 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4917 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4920 total_done += 2; CHECK_SIZE();
4921 *curpos++ = '0'+(lt->tm_hour / 10);
4922 *curpos++ = '0'+(lt->tm_hour % 10);
4925 total_done += 2; CHECK_SIZE();
4931 *curpos++ = '0'+(tmp / 10);
4932 *curpos++ = '0'+(tmp % 10);
4935 len = 3; CHECK_SIZE();
4936 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4939 len = 2; CHECK_SIZE();
4940 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4943 len = 2; CHECK_SIZE();
4949 snprintf(curpos, buflen - total_done, "%2d", tmp);
4952 total_done += 2; CHECK_SIZE();
4953 tmp = lt->tm_mon + 1;
4954 *curpos++ = '0'+(tmp / 10);
4955 *curpos++ = '0'+(tmp % 10);
4958 total_done += 2; CHECK_SIZE();
4959 *curpos++ = '0'+(lt->tm_min / 10);
4960 *curpos++ = '0'+(lt->tm_min % 10);
4963 len = 1; CHECK_SIZE();
4967 if (lt->tm_hour >= 12) {
4968 len = strlen(s_pm_up); CHECK_SIZE();
4969 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
4971 len = strlen(s_am_up); CHECK_SIZE();
4972 snprintf(curpos, buflen-total_done, "%s", s_am_up);
4976 if (lt->tm_hour >= 12) {
4977 len = strlen(s_pm_low); CHECK_SIZE();
4978 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
4980 len = strlen(s_am_low); CHECK_SIZE();
4981 snprintf(curpos, buflen-total_done, "%s", s_am_low);
4985 fast_strftime(subbuf, 64, time_am_pm, lt);
4986 len = strlen(subbuf); CHECK_SIZE();
4987 strncpy2(curpos, subbuf, buflen - total_done);
4990 total_done += 5; CHECK_SIZE();
4991 *curpos++ = '0'+(lt->tm_hour / 10);
4992 *curpos++ = '0'+(lt->tm_hour % 10);
4994 *curpos++ = '0'+(lt->tm_min / 10);
4995 *curpos++ = '0'+(lt->tm_min % 10);
4998 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4999 len = strlen(subbuf); CHECK_SIZE();
5000 strncpy2(curpos, subbuf, buflen - total_done);
5003 total_done += 2; CHECK_SIZE();
5004 *curpos++ = '0'+(lt->tm_sec / 10);
5005 *curpos++ = '0'+(lt->tm_sec % 10);
5008 len = 1; CHECK_SIZE();
5012 total_done += 8; CHECK_SIZE();
5013 *curpos++ = '0'+(lt->tm_hour / 10);
5014 *curpos++ = '0'+(lt->tm_hour % 10);
5016 *curpos++ = '0'+(lt->tm_min / 10);
5017 *curpos++ = '0'+(lt->tm_min % 10);
5019 *curpos++ = '0'+(lt->tm_sec / 10);
5020 *curpos++ = '0'+(lt->tm_sec % 10);
5023 len = 1; CHECK_SIZE();
5024 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5027 len = 1; CHECK_SIZE();
5028 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5031 fast_strftime(subbuf, 64, date_loc_format, lt);
5032 len = strlen(subbuf); CHECK_SIZE();
5033 strncpy2(curpos, subbuf, buflen - total_done);
5036 fast_strftime(subbuf, 64, time_loc_format, lt);
5037 len = strlen(subbuf); CHECK_SIZE();
5038 strncpy2(curpos, subbuf, buflen - total_done);
5041 total_done += 2; CHECK_SIZE();
5042 tmp = lt->tm_year%100;
5043 *curpos++ = '0'+(tmp / 10);
5044 *curpos++ = '0'+(tmp % 10);
5047 len = 4; CHECK_SIZE();
5048 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5058 /* let these complicated ones be done with the libc */
5059 snprintf(subfmt, 64, "%%%c", *format);
5060 strftime(subbuf, 64, subfmt, lt);
5061 len = strlen(subbuf); CHECK_SIZE();
5062 strncpy2(curpos, subbuf, buflen - total_done);
5066 /* let these complicated modifiers be done with the libc */
5067 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5068 strftime(subbuf, 64, subfmt, lt);
5069 len = strlen(subbuf); CHECK_SIZE();
5070 strncpy2(curpos, subbuf, buflen - total_done);
5074 g_warning("format error (%c)", *format);
5081 int len = 1; CHECK_SIZE();
5082 *curpos++ = *format++;
5089 gboolean prefs_common_get_use_shred(void);
5093 #define WEXITSTATUS(x) (x)
5096 int claws_unlink(const gchar *filename)
5099 static int found_shred = -1;
5100 static const gchar *args[4];
5102 if (filename == NULL)
5105 if (prefs_common_get_use_shred()) {
5106 if (found_shred == -1) {
5108 args[0] = g_find_program_in_path("shred");
5109 debug_print("found shred: %s\n", args[0]);
5110 found_shred = (args[0] != NULL) ? 1:0;
5114 if (found_shred == 1) {
5115 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5116 if (s.st_nlink == 1) {
5119 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5120 NULL, NULL, NULL, NULL, &status, NULL);
5121 debug_print("%s %s exited with status %d\n",
5122 args[0], filename, WEXITSTATUS(status));
5123 if (truncate(filename, 0) < 0)
5124 g_warning("couln't truncate");
5129 return g_unlink(filename);