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(const 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(const 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(size_t 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 < (size_t)1024) {
340 g_snprintf(str, sizeof(str), b_format, (gint)size);
342 } else if (size >> 10 < (size_t)1024) {
343 divide(size, 10, t, r);
344 g_snprintf(str, sizeof(str), kb_format, t, r);
346 } else if (size >> 20 < (size_t)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) / (1 << 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;
2931 tmpdir = get_tmp_dir();
2932 tmplen = strlen(tmpdir);
2933 progname = g_get_prgname();
2934 if (progname == NULL)
2935 progname = "claws-mail";
2936 proglen = strlen(progname);
2937 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2940 memcpy(fname, tmpdir, tmplen);
2941 fname[tmplen] = G_DIR_SEPARATOR;
2942 memcpy(fname + tmplen + 1, progname, proglen);
2943 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2945 fd = mkstemp(fname);
2950 claws_unlink(fname);
2952 /* verify that we can write in the file after unlinking */
2953 if (write(fd, buf, 1) < 0) {
2960 fp = fdopen(fd, "w+b");
2968 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2973 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2977 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2978 dir, G_DIR_SEPARATOR);
2979 fd = mkstemp_name(template, filename);
2982 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2983 fd = mkstemp(*filename);
2985 return fdopen(fd, "w+");
2988 FILE *str_open_as_stream(const gchar *str)
2993 g_return_val_if_fail(str != NULL, NULL);
2997 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3002 if (len == 0) return fp;
3004 if (fwrite(str, 1, len, fp) != len) {
3005 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3014 gint str_write_to_file(const gchar *str, const gchar *file)
3019 g_return_val_if_fail(str != NULL, -1);
3020 g_return_val_if_fail(file != NULL, -1);
3022 if ((fp = g_fopen(file, "wb")) == NULL) {
3023 FILE_OP_ERROR(file, "fopen");
3033 if (fwrite(str, 1, len, fp) != len) {
3034 FILE_OP_ERROR(file, "fwrite");
3040 if (fclose(fp) == EOF) {
3041 FILE_OP_ERROR(file, "fclose");
3049 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3056 g_return_val_if_fail(fp != NULL, NULL);
3058 array = g_byte_array_new();
3060 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3061 if (n_read < sizeof(buf) && ferror(fp))
3063 g_byte_array_append(array, buf, n_read);
3067 FILE_OP_ERROR("file stream", "fread");
3068 g_byte_array_free(array, TRUE);
3073 g_byte_array_append(array, buf, 1);
3074 str = (gchar *)array->data;
3075 g_byte_array_free(array, FALSE);
3077 if (recode && !g_utf8_validate(str, -1, NULL)) {
3078 const gchar *src_codeset, *dest_codeset;
3080 src_codeset = conv_get_locale_charset_str();
3081 dest_codeset = CS_UTF_8;
3082 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3090 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3096 struct timeval timeout = {1, 0};
3100 g_return_val_if_fail(file != NULL, NULL);
3102 if (g_stat(file, &s) != 0) {
3103 FILE_OP_ERROR(file, "stat");
3106 if (S_ISDIR(s.st_mode)) {
3107 g_warning("%s: is a directory\n", file);
3111 /* test whether the file is readable without blocking */
3112 fd = open(file, O_RDONLY | O_NONBLOCK);
3114 FILE_OP_ERROR(file, "open");
3120 /* allow for one second */
3121 err = select(fd+1, &fds, NULL, NULL, &timeout);
3122 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3124 FILE_OP_ERROR(file, "select");
3126 g_warning("%s: doesn't seem readable\n", file);
3132 /* Now clear O_NONBLOCK */
3133 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3134 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3138 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3139 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3144 /* get the FILE pointer */
3145 fp = fdopen(fd, "rb");
3148 FILE_OP_ERROR(file, "fdopen");
3149 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3153 str = file_read_stream_to_str_full(fp, recode);
3160 gchar *file_read_to_str(const gchar *file)
3162 return file_read_to_str_full(file, TRUE);
3164 gchar *file_read_stream_to_str(FILE *fp)
3166 return file_read_stream_to_str_full(fp, TRUE);
3169 gchar *file_read_to_str_no_recode(const gchar *file)
3171 return file_read_to_str_full(file, FALSE);
3173 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3175 return file_read_stream_to_str_full(fp, FALSE);
3178 char *fgets_crlf(char *buf, int size, FILE *stream)
3180 gboolean is_cr = FALSE;
3181 gboolean last_was_cr = FALSE;
3186 while (--size > 0 && (c = getc(stream)) != EOF)
3189 is_cr = (c == '\r');
3199 last_was_cr = is_cr;
3201 if (c == EOF && cs == buf)
3209 static gint execute_async(gchar *const argv[])
3211 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3213 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3214 NULL, NULL, NULL, FALSE) == FALSE) {
3215 g_warning("Couldn't execute command: %s\n", argv[0]);
3222 static gint execute_sync(gchar *const argv[])
3226 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3228 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3229 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3230 g_warning("Couldn't execute command: %s\n", argv[0]);
3235 if (WIFEXITED(status))
3236 return WEXITSTATUS(status);
3244 gint execute_command_line(const gchar *cmdline, gboolean async)
3249 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3251 argv = strsplit_with_quote(cmdline, " ", 0);
3254 ret = execute_async(argv);
3256 ret = execute_sync(argv);
3263 gchar *get_command_output(const gchar *cmdline)
3265 gchar *child_stdout;
3268 g_return_val_if_fail(cmdline != NULL, NULL);
3270 debug_print("get_command_output(): executing: %s\n", cmdline);
3272 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3274 g_warning("Couldn't execute command: %s\n", cmdline);
3278 return child_stdout;
3281 static gint is_unchanged_uri_char(char c)
3292 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3298 for(i = 0; i < strlen(uri) ; i++) {
3299 if (is_unchanged_uri_char(uri[i])) {
3300 if (k + 2 >= bufsize)
3302 encoded_uri[k++] = uri[i];
3305 char * hexa = "0123456789ABCDEF";
3307 if (k + 4 >= bufsize)
3309 encoded_uri[k++] = '%';
3310 encoded_uri[k++] = hexa[uri[i] / 16];
3311 encoded_uri[k++] = hexa[uri[i] % 16];
3317 gint open_uri(const gchar *uri, const gchar *cmdline)
3320 gchar buf[BUFFSIZE];
3322 gchar encoded_uri[BUFFSIZE];
3323 g_return_val_if_fail(uri != NULL, -1);
3325 /* an option to choose whether to use encode_uri or not ? */
3326 encode_uri(encoded_uri, BUFFSIZE, uri);
3329 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3330 !strchr(p + 2, '%'))
3331 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3334 g_warning("Open URI command line is invalid "
3335 "(there must be only one '%%s'): %s",
3337 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3340 execute_command_line(buf, TRUE);
3342 extern osso_context_t *get_osso_context(void);
3343 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3344 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3345 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3350 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3352 gchar buf[BUFFSIZE];
3355 g_return_val_if_fail(filepath != NULL, -1);
3358 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3359 !strchr(p + 2, '%'))
3360 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3363 g_warning("Open Text Editor command line is invalid "
3364 "(there must be only one '%%s'): %s",
3366 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3369 execute_command_line(buf, TRUE);
3374 time_t remote_tzoffset_sec(const gchar *zone)
3376 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3382 time_t remoteoffset;
3384 strncpy(zone3, zone, 3);
3388 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3389 (c == '+' || c == '-')) {
3390 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3392 remoteoffset = -remoteoffset;
3393 } else if (!strncmp(zone, "UT" , 2) ||
3394 !strncmp(zone, "GMT", 2)) {
3396 } else if (strlen(zone3) == 3) {
3397 for (p = ustzstr; *p != '\0'; p += 3) {
3398 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3399 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3400 remoteoffset = iustz * 3600;
3406 } else if (strlen(zone3) == 1) {
3408 case 'Z': remoteoffset = 0; break;
3409 case 'A': remoteoffset = -1; break;
3410 case 'B': remoteoffset = -2; break;
3411 case 'C': remoteoffset = -3; break;
3412 case 'D': remoteoffset = -4; break;
3413 case 'E': remoteoffset = -5; break;
3414 case 'F': remoteoffset = -6; break;
3415 case 'G': remoteoffset = -7; break;
3416 case 'H': remoteoffset = -8; break;
3417 case 'I': remoteoffset = -9; break;
3418 case 'K': remoteoffset = -10; break; /* J is not used */
3419 case 'L': remoteoffset = -11; break;
3420 case 'M': remoteoffset = -12; break;
3421 case 'N': remoteoffset = 1; break;
3422 case 'O': remoteoffset = 2; break;
3423 case 'P': remoteoffset = 3; break;
3424 case 'Q': remoteoffset = 4; break;
3425 case 'R': remoteoffset = 5; break;
3426 case 'S': remoteoffset = 6; break;
3427 case 'T': remoteoffset = 7; break;
3428 case 'U': remoteoffset = 8; break;
3429 case 'V': remoteoffset = 9; break;
3430 case 'W': remoteoffset = 10; break;
3431 case 'X': remoteoffset = 11; break;
3432 case 'Y': remoteoffset = 12; break;
3433 default: remoteoffset = 0; break;
3435 remoteoffset = remoteoffset * 3600;
3439 return remoteoffset;
3442 time_t tzoffset_sec(time_t *now)
3446 struct tm buf1, buf2;
3448 gmt = *gmtime_r(now, &buf1);
3449 lt = localtime_r(now, &buf2);
3451 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3453 if (lt->tm_year < gmt.tm_year)
3455 else if (lt->tm_year > gmt.tm_year)
3457 else if (lt->tm_yday < gmt.tm_yday)
3459 else if (lt->tm_yday > gmt.tm_yday)
3462 if (off >= 24 * 60) /* should be impossible */
3463 off = 23 * 60 + 59; /* if not, insert silly value */
3464 if (off <= -24 * 60)
3465 off = -(23 * 60 + 59);
3470 /* calculate timezone offset */
3471 gchar *tzoffset(time_t *now)
3473 static gchar offset_string[6];
3477 struct tm buf1, buf2;
3479 gmt = *gmtime_r(now, &buf1);
3480 lt = localtime_r(now, &buf2);
3482 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3484 if (lt->tm_year < gmt.tm_year)
3486 else if (lt->tm_year > gmt.tm_year)
3488 else if (lt->tm_yday < gmt.tm_yday)
3490 else if (lt->tm_yday > gmt.tm_yday)
3498 if (off >= 24 * 60) /* should be impossible */
3499 off = 23 * 60 + 59; /* if not, insert silly value */
3501 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3503 return offset_string;
3506 void get_rfc822_date(gchar *buf, gint len)
3510 gchar day[4], mon[4];
3511 gint dd, hh, mm, ss, yyyy;
3513 gchar buf2[BUFFSIZE];
3516 lt = localtime_r(&t, &buf1);
3518 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3519 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3521 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3522 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3525 void debug_set_mode(gboolean mode)
3530 gboolean debug_get_mode(void)
3535 void debug_print_real(const gchar *format, ...)
3538 gchar buf[BUFFSIZE];
3540 if (!debug_mode) return;
3542 va_start(args, format);
3543 g_vsnprintf(buf, sizeof(buf), format, args);
3550 const char * debug_srcname(const char *file)
3552 const char *s = strrchr (file, '/');
3557 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3559 if (subject == NULL)
3562 subject += subject_get_prefix_length(subject);
3564 return g_hash_table_lookup(subject_table, subject);
3567 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3570 if (subject == NULL || *subject == 0)
3572 subject += subject_get_prefix_length(subject);
3573 g_hash_table_insert(subject_table, subject, data);
3576 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3578 if (subject == NULL)
3581 subject += subject_get_prefix_length(subject);
3582 g_hash_table_remove(subject_table, subject);
3586 *\brief Check if a string is prefixed with known (combinations)
3587 * of prefixes. The function assumes that each prefix
3588 * is terminated by zero or exactly _one_ space.
3590 *\param str String to check for a prefixes
3592 *\return int Number of chars in the prefix that should be skipped
3593 * for a "clean" subject line. If no prefix was found, 0
3596 static regex_t u_regex;
3597 static gboolean u_init_;
3599 void utils_free_regex(void)
3607 int subject_get_prefix_length(const gchar *subject)
3609 /*!< Array with allowable reply prefixes regexps. */
3610 static const gchar * const prefixes[] = {
3611 "Re\\:", /* "Re:" */
3612 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3613 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3614 "Aw\\:", /* "Aw:" (German) */
3615 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3616 "Res\\:", /* "Res:" (Brazilian Outlook) */
3617 "Fw\\:", /* "Fw:" Forward */
3618 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3619 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3620 "Rif\\:", /* "Rif:" (Italian Outlook) */
3621 "Sv\\:", /* "Sv" (Norwegian) */
3622 "Vs\\:", /* "Vs" (Norwegian) */
3623 "Ad\\:", /* "Ad" (Norwegian) */
3624 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3627 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3631 if (!subject) return 0;
3632 if (!*subject) return 0;
3635 GString *s = g_string_new("");
3637 for (n = 0; n < PREFIXES; n++)
3638 /* Terminate each prefix regexpression by a
3639 * "\ ?" (zero or ONE space), and OR them */
3640 g_string_append_printf(s, "(%s\\ ?)%s",
3645 g_string_prepend(s, "(");
3646 g_string_append(s, ")+"); /* match at least once */
3647 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3650 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3651 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3652 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3653 debug_print("Error compiling regexp %s\n", s->str);
3654 g_string_free(s, TRUE);
3658 g_string_free(s, TRUE);
3662 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3668 static guint g_stricase_hash(gconstpointer gptr)
3670 guint hash_result = 0;
3673 for (str = gptr; str && *str; str++) {
3674 hash_result += toupper(*str);
3680 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3682 const char *str1 = gptr1;
3683 const char *str2 = gptr2;
3685 return !strcasecmp(str1, str2);
3688 gint g_int_compare(gconstpointer a, gconstpointer b)
3690 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3693 gchar *generate_msgid(gchar *buf, gint len)
3701 lt = localtime_r(&t, &buft);
3703 if (strcmp(buf, "") == 0) {
3704 addr = g_strconcat("@", get_domain_name(), NULL);
3707 addr = g_strconcat("@", buf, NULL);
3710 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3711 lt->tm_year + 1900, lt->tm_mon + 1,
3712 lt->tm_mday, lt->tm_hour,
3713 lt->tm_min, lt->tm_sec,
3714 (guint) rand(), addr);
3721 quote_cmd_argument()
3723 return a quoted string safely usable in argument of a command.
3725 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3728 gint quote_cmd_argument(gchar * result, guint size,
3738 for(p = path ; * p != '\0' ; p ++) {
3740 if (isalnum((guchar)*p) || (* p == '/')) {
3741 if (remaining > 0) {
3747 result[size - 1] = '\0';
3752 if (remaining >= 2) {
3760 result[size - 1] = '\0';
3765 if (remaining > 0) {
3769 result[size - 1] = '\0';
3783 static void g_node_map_recursive(GNode *node, gpointer data)
3785 GNodeMapData *mapdata = (GNodeMapData *) data;
3787 GNodeMapData newmapdata;
3790 newdata = mapdata->func(node->data, mapdata->data);
3791 if (newdata != NULL) {
3792 newnode = g_node_new(newdata);
3793 g_node_append(mapdata->parent, newnode);
3795 newmapdata.parent = newnode;
3796 newmapdata.func = mapdata->func;
3797 newmapdata.data = mapdata->data;
3799 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3803 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3806 GNodeMapData mapdata;
3808 g_return_val_if_fail(node != NULL, NULL);
3809 g_return_val_if_fail(func != NULL, NULL);
3811 root = g_node_new(func(node->data, data));
3813 mapdata.parent = root;
3814 mapdata.func = func;
3815 mapdata.data = data;
3817 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3822 #define HEX_TO_INT(val, hex) \
3826 if ('0' <= c && c <= '9') { \
3828 } else if ('a' <= c && c <= 'f') { \
3829 val = c - 'a' + 10; \
3830 } else if ('A' <= c && c <= 'F') { \
3831 val = c - 'A' + 10; \
3837 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3844 if (hi == -1 || lo == -1)
3847 *out = (hi << 4) + lo;
3851 #define INT_TO_HEX(hex, val) \
3854 hex = '0' + (val); \
3856 hex = 'A' + (val) - 10; \
3859 void get_hex_str(gchar *out, guchar ch)
3863 INT_TO_HEX(hex, ch >> 4);
3865 INT_TO_HEX(hex, ch & 0x0f);
3871 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3873 #define G_PRINT_REF g_print
3877 *\brief Register ref counted pointer. It is based on GBoxed, so should
3878 * work with anything that uses the GType system. The semantics
3879 * are similar to a C++ auto pointer, with the exception that
3880 * C doesn't have automatic closure (calling destructors) when
3881 * exiting a block scope.
3882 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3883 * function directly.
3885 *\return GType A GType type.
3887 GType g_auto_pointer_register(void)
3889 static GType auto_pointer_type;
3890 if (!auto_pointer_type)
3892 g_boxed_type_register_static
3893 ("G_TYPE_AUTO_POINTER",
3894 (GBoxedCopyFunc) g_auto_pointer_copy,
3895 (GBoxedFreeFunc) g_auto_pointer_free);
3896 return auto_pointer_type;
3900 *\brief Structure with g_new() allocated pointer guarded by the
3903 typedef struct AutoPointerRef {
3904 void (*free) (gpointer);
3910 *\brief The auto pointer opaque structure that references the
3911 * pointer guard block.
3913 typedef struct AutoPointer {
3914 AutoPointerRef *ref;
3915 gpointer ptr; /*!< access to protected pointer */
3919 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3923 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3924 * ... when assigning, copying and freeing storage elements
3926 * gtk_list_store_new(N_S_COLUMNS,
3927 * G_TYPE_AUTO_POINTER,
3931 * Template *precious_data = g_new0(Template, 1);
3932 * g_pointer protect = g_auto_pointer_new(precious_data);
3934 * gtk_list_store_set(container, &iter,
3938 * ... the gtk_list_store has copied the pointer and
3939 * ... incremented its reference count, we should free
3940 * ... the auto pointer (in C++ a destructor would do
3941 * ... this for us when leaving block scope)
3943 * g_auto_pointer_free(protect);
3945 * ... gtk_list_store_set() now manages the data. When
3946 * ... *explicitly* requesting a pointer from the list
3947 * ... store, don't forget you get a copy that should be
3948 * ... freed with g_auto_pointer_free() eventually.
3952 *\param pointer Pointer to be guarded.
3954 *\return GAuto * Pointer that should be used in containers with
3957 GAuto *g_auto_pointer_new(gpointer p)
3959 AutoPointerRef *ref;
3965 ref = g_new0(AutoPointerRef, 1);
3966 ptr = g_new0(AutoPointer, 1);
3976 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3982 *\brief Allocate an autopointer using the passed \a free function to
3983 * free the guarded pointer
3985 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3992 aptr = g_auto_pointer_new(p);
3993 aptr->ref->free = free_;
3997 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3999 if (auto_ptr == NULL)
4001 return ((AutoPointer *) auto_ptr)->ptr;
4005 *\brief Copies an auto pointer by. It's mostly not necessary
4006 * to call this function directly, unless you copy/assign
4007 * the guarded pointer.
4009 *\param auto_ptr Auto pointer returned by previous call to
4010 * g_auto_pointer_new_XXX()
4012 *\return gpointer An auto pointer
4014 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4017 AutoPointerRef *ref;
4020 if (auto_ptr == NULL)
4025 newp = g_new0(AutoPointer, 1);
4028 newp->ptr = ref->pointer;
4032 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4038 *\brief Free an auto pointer
4040 void g_auto_pointer_free(GAuto *auto_ptr)
4043 AutoPointerRef *ref;
4045 if (auto_ptr == NULL)
4051 if (--(ref->cnt) == 0) {
4053 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4055 ref->free(ref->pointer);
4060 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4065 void replace_returns(gchar *str)
4070 while (strstr(str, "\n")) {
4071 *strstr(str, "\n") = ' ';
4073 while (strstr(str, "\r")) {
4074 *strstr(str, "\r") = ' ';
4078 /* get_uri_part() - retrieves a URI starting from scanpos.
4079 Returns TRUE if succesful */
4080 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4081 const gchar **bp, const gchar **ep, gboolean hdr)
4084 gint parenthese_cnt = 0;
4086 g_return_val_if_fail(start != NULL, FALSE);
4087 g_return_val_if_fail(scanpos != NULL, FALSE);
4088 g_return_val_if_fail(bp != NULL, FALSE);
4089 g_return_val_if_fail(ep != NULL, FALSE);
4093 /* find end point of URI */
4094 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4095 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4096 !IS_ASCII(*(const guchar *)ep_) ||
4097 strchr("[]{}<>\"", *ep_)) {
4099 } else if (strchr("(", *ep_)) {
4101 } else if (strchr(")", *ep_)) {
4102 if (parenthese_cnt > 0)
4109 /* no punctuation at end of string */
4111 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4112 * should pass some URI type to this function and decide on that whether
4113 * to perform punctuation stripping */
4115 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4117 for (; ep_ - 1 > scanpos + 1 &&
4118 IS_REAL_PUNCT(*(ep_ - 1));
4122 #undef IS_REAL_PUNCT
4129 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4131 while (bp && *bp && g_ascii_isspace(*bp))
4133 return g_strndup(bp, ep - bp);
4136 /* valid mail address characters */
4137 #define IS_RFC822_CHAR(ch) \
4141 !g_ascii_isspace(ch) && \
4142 !strchr("(),;<>\"", (ch)))
4144 /* alphabet and number within 7bit ASCII */
4145 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4146 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4148 static GHashTable *create_domain_tab(void)
4150 static const gchar *toplvl_domains [] = {
4152 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4153 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4154 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4155 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4156 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4157 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4158 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4159 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4160 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4161 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4162 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4163 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4164 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4165 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4166 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4167 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4168 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4169 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4170 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4171 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4172 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4173 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4174 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4175 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4176 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4177 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4180 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4182 g_return_val_if_fail(htab, NULL);
4183 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4184 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4188 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4190 const gint MAX_LVL_DOM_NAME_LEN = 6;
4191 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4192 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4195 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4198 for (p = buf; p < m && first < last; *p++ = *first++)
4202 return g_hash_table_lookup(tab, buf) != NULL;
4205 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4206 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4207 const gchar **bp, const gchar **ep, gboolean hdr)
4209 /* more complex than the uri part because we need to scan back and forward starting from
4210 * the scan position. */
4211 gboolean result = FALSE;
4212 const gchar *bp_ = NULL;
4213 const gchar *ep_ = NULL;
4214 static GHashTable *dom_tab;
4215 const gchar *last_dot = NULL;
4216 const gchar *prelast_dot = NULL;
4217 const gchar *last_tld_char = NULL;
4219 /* the informative part of the email address (describing the name
4220 * of the email address owner) may contain quoted parts. the
4221 * closure stack stores the last encountered quotes. */
4222 gchar closure_stack[128];
4223 gchar *ptr = closure_stack;
4225 g_return_val_if_fail(start != NULL, FALSE);
4226 g_return_val_if_fail(scanpos != NULL, FALSE);
4227 g_return_val_if_fail(bp != NULL, FALSE);
4228 g_return_val_if_fail(ep != NULL, FALSE);
4231 const gchar *start_quote = NULL;
4232 const gchar *end_quote = NULL;
4234 /* go to the real start */
4235 if (start[0] == ',')
4237 if (start[0] == ';')
4239 while (start[0] == '\n' || start[0] == '\r')
4241 while (start[0] == ' ' || start[0] == '\t')
4246 /* check if there are quotes (to skip , in them) */
4247 if (*start == '"') {
4248 start_quote = start;
4250 end_quote = strstr(start, "\"");
4256 /* skip anything between quotes */
4257 if (start_quote && end_quote) {
4262 /* find end (either , or ; or end of line) */
4263 if (strstr(start, ",") && strstr(start, ";"))
4264 *ep = strstr(start,",") < strstr(start, ";")
4265 ? strstr(start, ",") : strstr(start, ";");
4266 else if (strstr(start, ","))
4267 *ep = strstr(start, ",");
4268 else if (strstr(start, ";"))
4269 *ep = strstr(start, ";");
4271 *ep = start+strlen(start);
4273 /* go back to real start */
4274 if (start_quote && end_quote) {
4275 start = start_quote;
4278 /* check there's still an @ in that, or search
4279 * further if possible */
4280 if (strstr(start, "@") && strstr(start, "@") < *ep)
4282 else if (*ep < start+strlen(start)) {
4285 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4293 dom_tab = create_domain_tab();
4294 g_return_val_if_fail(dom_tab, FALSE);
4296 /* scan start of address */
4297 for (bp_ = scanpos - 1;
4298 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4301 /* TODO: should start with an alnum? */
4303 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4306 if (bp_ != scanpos) {
4307 /* scan end of address */
4308 for (ep_ = scanpos + 1;
4309 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4311 prelast_dot = last_dot;
4313 if (*(last_dot + 1) == '.') {
4314 if (prelast_dot == NULL)
4316 last_dot = prelast_dot;
4321 /* TODO: really should terminate with an alnum? */
4322 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4327 if (last_dot == NULL)
4329 if (last_dot >= ep_)
4330 last_dot = prelast_dot;
4331 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4335 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4336 if (*last_tld_char == '?')
4339 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4346 if (!result) return FALSE;
4348 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4349 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4350 && IS_RFC822_CHAR(*(ep_ + 3))) {
4351 /* this informative part with an @ in it is
4352 * followed by the email address */
4355 /* go to matching '>' (or next non-rfc822 char, like \n) */
4356 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4359 /* include the bracket */
4360 if (*ep_ == '>') ep_++;
4362 /* include the leading quote */
4370 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4371 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4374 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4375 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4378 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4379 #define IN_STACK() (ptr > closure_stack)
4380 /* has underrun check */
4381 #define POP_STACK() if(IN_STACK()) --ptr
4382 /* has overrun check */
4383 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4384 /* has underrun check */
4385 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4389 /* scan for the informative part. */
4390 for (bp_ -= 2; bp_ >= start; bp_--) {
4391 /* if closure on the stack keep scanning */
4392 if (PEEK_STACK() == *bp_) {
4396 if (*bp_ == '\'' || *bp_ == '"') {
4401 /* if nothing in the closure stack, do the special conditions
4402 * the following if..else expression simply checks whether
4403 * a token is acceptable. if not acceptable, the clause
4404 * should terminate the loop with a 'break' */
4405 if (!PEEK_STACK()) {
4407 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4408 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4409 /* hyphens are allowed, but only in
4411 } else if (strchr(" \"'", *bp_)) {
4412 /* but anything not being a punctiation
4415 break; /* anything else is rejected */
4422 /* scan forward (should start with an alnum) */
4423 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4439 #undef IS_ASCII_ALNUM
4440 #undef IS_RFC822_CHAR
4442 gchar *make_email_string(const gchar *bp, const gchar *ep)
4444 /* returns a mailto: URI; mailto: is also used to detect the
4445 * uri type later on in the button_pressed signal handler */
4449 tmp = g_strndup(bp, ep - bp);
4450 result = g_strconcat("mailto:", tmp, NULL);
4456 gchar *make_http_string(const gchar *bp, const gchar *ep)
4458 /* returns an http: URI; */
4462 while (bp && *bp && g_ascii_isspace(*bp))
4464 tmp = g_strndup(bp, ep - bp);
4465 result = g_strconcat("http://", tmp, NULL);
4471 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4473 FILE *fp = fopen(path, "rb");
4474 gchar buf[BUFFSIZE];
4475 gchar *result = NULL;
4478 while (fgets(buf, sizeof (buf), fp) != NULL) {
4479 gchar **parts = g_strsplit(buf, ";", 3);
4480 gchar *trimmed = parts[0];
4481 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4483 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4484 trimmed[strlen(trimmed)-1] = '\0';
4486 if (!strcmp(trimmed, type)) {
4487 gboolean needsterminal = FALSE;
4488 if (parts[2] && strstr(parts[2], "needsterminal")) {
4489 needsterminal = TRUE;
4491 if (parts[2] && strstr(parts[2], "test=")) {
4492 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4493 gchar *testcmd = orig_testcmd;
4494 if (strstr(testcmd,";"))
4495 *(strstr(testcmd,";")) = '\0';
4496 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4498 while (testcmd[strlen(testcmd)-1] == '\n')
4499 testcmd[strlen(testcmd)-1] = '\0';
4500 while (testcmd[strlen(testcmd)-1] == '\r')
4501 testcmd[strlen(testcmd)-1] = '\0';
4502 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4503 testcmd[strlen(testcmd)-1] = '\0';
4505 if (strstr(testcmd, "%s")) {
4506 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4507 gint res = system(tmp);
4509 g_free(orig_testcmd);
4516 gint res = system(testcmd);
4517 g_free(orig_testcmd);
4527 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4529 while (trimmed[strlen(trimmed)-1] == '\n')
4530 trimmed[strlen(trimmed)-1] = '\0';
4531 while (trimmed[strlen(trimmed)-1] == '\r')
4532 trimmed[strlen(trimmed)-1] = '\0';
4533 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4534 trimmed[strlen(trimmed)-1] = '\0';
4535 result = g_strdup(trimmed);
4538 /* if there are no single quotes around %s, add them.
4539 * '.*%s.*' is ok, as in display 'png:%s'
4541 if (strstr(result, "%s")
4542 && !(strstr(result, "'") < strstr(result,"%s") &&
4543 strstr(strstr(result,"%s"), "'"))) {
4544 gchar *start = g_strdup(result);
4545 gchar *end = g_strdup(strstr(result, "%s")+2);
4547 *strstr(start, "%s") = '\0';
4548 tmp = g_strconcat(start,"'%s'",end, NULL);
4554 if (needsterminal) {
4555 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4566 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4568 gchar *result = NULL;
4572 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4573 result = mailcap_get_command_in_file(path, type, file_to_open);
4577 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4581 void mailcap_update_default(const gchar *type, const gchar *command)
4583 gchar *path = NULL, *outpath = NULL;
4584 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4585 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4586 FILE *fp = fopen(path, "rb");
4587 FILE *outfp = fopen(outpath, "wb");
4588 gchar buf[BUFFSIZE];
4589 gboolean err = FALSE;
4597 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4598 gchar **parts = g_strsplit(buf, ";", 3);
4599 gchar *trimmed = parts[0];
4600 while (trimmed[0] == ' ')
4602 while (trimmed[strlen(trimmed)-1] == ' ')
4603 trimmed[strlen(trimmed)-1] = '\0';
4605 if (!strcmp(trimmed, type)) {
4610 if(fputs(buf, outfp) == EOF) {
4617 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4623 if (fclose(outfp) == EOF)
4627 g_rename(outpath, path);
4633 gint copy_dir(const gchar *src, const gchar *dst)
4638 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4639 g_warning("failed to open directory: %s\n", src);
4643 if (make_dir(dst) < 0)
4646 while ((name = g_dir_read_name(dir)) != NULL) {
4647 gchar *old_file, *new_file;
4648 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4649 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4650 debug_print("copying: %s -> %s\n", old_file, new_file);
4651 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4652 gint r = copy_file(old_file, new_file, TRUE);
4659 /* Windows has no symlinks. Or well, Vista seems to
4660 have something like this but the semantics might be
4661 different. Thus we don't use it under Windows. */
4662 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4665 gchar *target = g_file_read_link(old_file, &error);
4667 r = symlink(target, new_file);
4674 #endif /*G_OS_WIN32*/
4675 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4676 gint r = copy_dir(old_file, new_file);
4687 /* crude test to see if a file is an email. */
4688 gboolean file_is_email (const gchar *filename)
4694 if (filename == NULL)
4696 if ((fp = g_fopen(filename, "rb")) == NULL)
4698 while (i < 60 && score < 3
4699 && fgets(buffer, sizeof (buffer), fp) > 0) {
4700 if (!strncmp(buffer, "From:", strlen("From:")))
4702 if (!strncmp(buffer, "To:", strlen("To:")))
4704 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4709 return (score >= 3);
4712 gboolean sc_g_list_bigger(GList *list, gint max)
4716 while (cur && i <= max+1) {
4723 gboolean sc_g_slist_bigger(GSList *list, gint max)
4727 while (cur && i <= max+1) {
4734 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4735 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4736 NULL, NULL, NULL, NULL, NULL, NULL};
4737 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4738 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4739 NULL, NULL, NULL, NULL, NULL, NULL};
4740 const gchar *s_am_up = NULL;
4741 const gchar *s_pm_up = NULL;
4742 const gchar *s_am_low = NULL;
4743 const gchar *s_pm_low = NULL;
4744 const gchar *def_loc_format = NULL;
4745 const gchar *date_loc_format = NULL;
4746 const gchar *time_loc_format = NULL;
4747 const gchar *time_am_pm = NULL;
4749 static gboolean time_names_init_done = FALSE;
4751 static void init_time_names(void)
4753 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4754 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4755 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4756 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4757 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4758 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4759 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4761 monthnames[0] = Q_("Complete month name for use by strftime|January");
4762 monthnames[1] = Q_("Complete month name for use by strftime|February");
4763 monthnames[2] = Q_("Complete month name for use by strftime|March");
4764 monthnames[3] = Q_("Complete month name for use by strftime|April");
4765 monthnames[4] = Q_("Complete month name for use by strftime|May");
4766 monthnames[5] = Q_("Complete month name for use by strftime|June");
4767 monthnames[6] = Q_("Complete month name for use by strftime|July");
4768 monthnames[7] = Q_("Complete month name for use by strftime|August");
4769 monthnames[8] = Q_("Complete month name for use by strftime|September");
4770 monthnames[9] = Q_("Complete month name for use by strftime|October");
4771 monthnames[10] = Q_("Complete month name for use by strftime|November");
4772 monthnames[11] = Q_("Complete month name for use by strftime|December");
4774 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4775 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4776 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4777 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4778 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4779 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4780 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4782 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4783 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4784 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4785 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4786 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4787 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4788 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4789 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4790 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4791 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4792 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4793 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4795 s_am_up = Q_("For use by strftime (morning)|AM");
4796 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4797 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4798 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4800 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4801 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4802 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4804 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4806 time_names_init_done = TRUE;
4809 #define CHECK_SIZE() { \
4810 total_done += len; \
4811 if (total_done >= buflen) { \
4812 buf[buflen-1] = '\0'; \
4817 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4819 gchar *curpos = buf;
4820 gint total_done = 0;
4821 gchar subbuf[64], subfmt[64];
4822 static time_t last_tzset = (time_t)0;
4824 if (!time_names_init_done)
4827 if (format == NULL || lt == NULL)
4830 if (last_tzset != time(NULL)) {
4832 last_tzset = time(NULL);
4835 if (*format == '%') {
4836 gint len = 0, tmp = 0;
4840 len = 1; CHECK_SIZE();
4844 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4845 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4848 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4849 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4853 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4854 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4857 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4858 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4861 fast_strftime(subbuf, 64, def_loc_format, lt);
4862 len = strlen(subbuf); CHECK_SIZE();
4863 strncpy2(curpos, subbuf, buflen - total_done);
4866 total_done += 2; CHECK_SIZE();
4867 tmp = (lt->tm_year + 1900)/100;
4868 *curpos++ = '0'+(tmp / 10);
4869 *curpos++ = '0'+(tmp % 10);
4872 total_done += 2; CHECK_SIZE();
4873 *curpos++ = '0'+(lt->tm_mday / 10);
4874 *curpos++ = '0'+(lt->tm_mday % 10);
4877 total_done += 8; CHECK_SIZE();
4878 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4879 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4881 *curpos++ = '0'+(lt->tm_mday / 10);
4882 *curpos++ = '0'+(lt->tm_mday % 10);
4884 tmp = lt->tm_year%100;
4885 *curpos++ = '0'+(tmp / 10);
4886 *curpos++ = '0'+(tmp % 10);
4889 len = 2; CHECK_SIZE();
4890 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4893 len = 10; CHECK_SIZE();
4894 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4895 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4898 total_done += 2; CHECK_SIZE();
4899 *curpos++ = '0'+(lt->tm_hour / 10);
4900 *curpos++ = '0'+(lt->tm_hour % 10);
4903 total_done += 2; CHECK_SIZE();
4909 *curpos++ = '0'+(tmp / 10);
4910 *curpos++ = '0'+(tmp % 10);
4913 len = 3; CHECK_SIZE();
4914 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4917 len = 2; CHECK_SIZE();
4918 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4921 len = 2; CHECK_SIZE();
4927 snprintf(curpos, buflen - total_done, "%2d", tmp);
4930 total_done += 2; CHECK_SIZE();
4931 tmp = lt->tm_mon + 1;
4932 *curpos++ = '0'+(tmp / 10);
4933 *curpos++ = '0'+(tmp % 10);
4936 total_done += 2; CHECK_SIZE();
4937 *curpos++ = '0'+(lt->tm_min / 10);
4938 *curpos++ = '0'+(lt->tm_min % 10);
4941 len = 1; CHECK_SIZE();
4945 if (lt->tm_hour >= 12) {
4946 len = strlen(s_pm_up); CHECK_SIZE();
4947 snprintf(curpos, buflen-total_done, s_pm_up);
4949 len = strlen(s_am_up); CHECK_SIZE();
4950 snprintf(curpos, buflen-total_done, s_am_up);
4954 if (lt->tm_hour >= 12) {
4955 len = strlen(s_pm_low); CHECK_SIZE();
4956 snprintf(curpos, buflen-total_done, s_pm_low);
4958 len = strlen(s_am_low); CHECK_SIZE();
4959 snprintf(curpos, buflen-total_done, s_am_low);
4963 fast_strftime(subbuf, 64, time_am_pm, lt);
4964 len = strlen(subbuf); CHECK_SIZE();
4965 strncpy2(curpos, subbuf, buflen - total_done);
4968 total_done += 5; CHECK_SIZE();
4969 *curpos++ = '0'+(lt->tm_hour / 10);
4970 *curpos++ = '0'+(lt->tm_hour % 10);
4972 *curpos++ = '0'+(lt->tm_min / 10);
4973 *curpos++ = '0'+(lt->tm_min % 10);
4976 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4977 len = strlen(subbuf); CHECK_SIZE();
4978 strncpy2(curpos, subbuf, buflen - total_done);
4981 total_done += 2; CHECK_SIZE();
4982 *curpos++ = '0'+(lt->tm_sec / 10);
4983 *curpos++ = '0'+(lt->tm_sec % 10);
4986 len = 1; CHECK_SIZE();
4990 total_done += 8; 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);
4997 *curpos++ = '0'+(lt->tm_sec / 10);
4998 *curpos++ = '0'+(lt->tm_sec % 10);
5001 len = 1; CHECK_SIZE();
5002 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5005 len = 1; CHECK_SIZE();
5006 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5009 fast_strftime(subbuf, 64, date_loc_format, lt);
5010 len = strlen(subbuf); CHECK_SIZE();
5011 strncpy2(curpos, subbuf, buflen - total_done);
5014 fast_strftime(subbuf, 64, time_loc_format, lt);
5015 len = strlen(subbuf); CHECK_SIZE();
5016 strncpy2(curpos, subbuf, buflen - total_done);
5019 total_done += 2; CHECK_SIZE();
5020 tmp = lt->tm_year%100;
5021 *curpos++ = '0'+(tmp / 10);
5022 *curpos++ = '0'+(tmp % 10);
5025 len = 4; CHECK_SIZE();
5026 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5036 /* let these complicated ones be done with the libc */
5037 snprintf(subfmt, 64, "%%%c", *format);
5038 strftime(subbuf, 64, subfmt, lt);
5039 len = strlen(subbuf); CHECK_SIZE();
5040 strncpy2(curpos, subbuf, buflen - total_done);
5044 /* let these complicated modifiers be done with the libc */
5045 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5046 strftime(subbuf, 64, subfmt, lt);
5047 len = strlen(subbuf); CHECK_SIZE();
5048 strncpy2(curpos, subbuf, buflen - total_done);
5052 g_warning("format error (%c)", *format);
5059 int len = 1; CHECK_SIZE();
5060 *curpos++ = *format++;
5067 gboolean prefs_common_get_use_shred(void);
5069 int claws_unlink(const gchar *filename)
5072 static int found_shred = -1;
5073 static const gchar *args[4];
5075 if (filename == NULL)
5078 if (prefs_common_get_use_shred()) {
5079 if (found_shred == -1) {
5081 args[0] = g_find_program_in_path("shred");
5082 debug_print("found shred: %s\n", args[0]);
5083 found_shred = (args[0] != NULL) ? 1:0;
5087 if (found_shred == 1) {
5088 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5089 if (s.st_nlink == 1) {
5092 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5093 NULL, NULL, NULL, NULL, &status, NULL);
5094 debug_print("%s %s exited with status %d\n",
5095 args[0], filename, WEXITSTATUS(status));
5096 truncate(filename, 0);
5101 return g_unlink(filename);