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>
66 # include <tablet-browser-interface.h>
68 # include <osso-browser-interface.h>
74 #include "../codeconv.h"
78 static gboolean debug_mode = FALSE;
80 static GSList *tempfiles=NULL;
83 /* Return true if we are running as root. This function should beused
84 instead of getuid () == 0. */
85 gboolean superuser_p (void)
88 return w32_is_administrator ();
96 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
97 gint g_chdir(const gchar *path)
100 if (G_WIN32_HAVE_WIDECHAR_API()) {
105 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
111 retval = _wchdir(wpath);
123 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
124 if (cp_path == NULL) {
129 retval = chdir(cp_path);
142 gint g_chmod(const gchar *path, gint mode)
145 if (G_WIN32_HAVE_WIDECHAR_API()) {
150 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
156 retval = _wchmod(wpath, mode);
168 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
169 if (cp_path == NULL) {
174 retval = chmod(cp_path, mode);
183 return chmod(path, mode);
186 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
190 gint mkstemp_name(const gchar *template, gchar **name_used)
192 static gulong count=0; /* W32-_mktemp only supports up to 27
196 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
197 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
198 (S_IRUSR | S_IWUSR));
200 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
202 perror(g_strdup_printf("cant create %s",*name_used));
208 #endif /* G_OS_WIN32 */
211 gint mkstemp(const gchar *template)
214 gint res = mkstemp_name(template, &dummyname);
218 #endif /* G_OS_WIN32 */
220 void list_free_strings(GList *list)
222 list = g_list_first(list);
224 while (list != NULL) {
230 void slist_free_strings(GSList *list)
232 while (list != NULL) {
238 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
243 void hash_free_strings(GHashTable *table)
245 g_hash_table_foreach(table, hash_free_strings_func, NULL);
248 gint str_case_equal(gconstpointer v, gconstpointer v2)
250 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
253 guint str_case_hash(gconstpointer key)
255 const gchar *p = key;
259 h = g_ascii_tolower(h);
260 for (p += 1; *p != '\0'; p++)
261 h = (h << 5) - h + g_ascii_tolower(*p);
267 void ptr_array_free_strings(GPtrArray *array)
272 g_return_if_fail(array != NULL);
274 for (i = 0; i < array->len; i++) {
275 str = g_ptr_array_index(array, i);
280 gboolean str_find(const gchar *haystack, const gchar *needle)
282 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
285 gboolean str_case_find(const gchar *haystack, const gchar *needle)
287 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
290 gint to_number(const gchar *nstr)
292 register const gchar *p;
294 if (*nstr == '\0') return -1;
296 for (p = nstr; *p != '\0'; p++)
297 if (!g_ascii_isdigit(*p)) return -1;
302 /* convert integer into string,
303 nstr must be not lower than 11 characters length */
304 gchar *itos_buf(gchar *nstr, gint n)
306 g_snprintf(nstr, 11, "%d", n);
310 /* convert integer into string */
313 static gchar nstr[11];
315 return itos_buf(nstr, n);
318 #define divide(num,divisor,i,d) \
320 i = num >> divisor; \
321 d = num & ((1<<divisor)-1); \
322 d = (d*100) >> divisor; \
325 gchar *to_human_readable(size_t size)
327 static gchar str[14];
328 static gchar *b_format = NULL, *kb_format = NULL,
329 *mb_format = NULL, *gb_format = NULL;
330 register int t = 0, r = 0;
331 if (b_format == NULL) {
333 kb_format = _("%d.%02dKB");
334 mb_format = _("%d.%02dMB");
335 gb_format = _("%.2fGB");
338 if (size < (size_t)1024) {
339 g_snprintf(str, sizeof(str), b_format, (gint)size);
341 } else if (size >> 10 < (size_t)1024) {
342 divide(size, 10, t, r);
343 g_snprintf(str, sizeof(str), kb_format, t, r);
345 } else if (size >> 20 < (size_t)1024) {
346 divide(size, 20, t, r);
347 g_snprintf(str, sizeof(str), mb_format, t, r);
350 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size) / (1 << 30));
355 /* strcmp with NULL-checking */
356 gint strcmp2(const gchar *s1, const gchar *s2)
358 if (s1 == NULL || s2 == NULL)
361 return strcmp(s1, s2);
363 /* strstr with NULL-checking */
364 gchar *strstr2(const gchar *s1, const gchar *s2)
366 if (s1 == NULL || s2 == NULL)
369 return strstr(s1, s2);
372 gint path_cmp(const gchar *s1, const gchar *s2)
377 gchar *s1buf, *s2buf;
380 if (s1 == NULL || s2 == NULL) return -1;
381 if (*s1 == '\0' || *s2 == '\0') return -1;
384 s1buf = g_strdup (s1);
385 s2buf = g_strdup (s2);
386 subst_char (s1buf, '/', G_DIR_SEPARATOR);
387 subst_char (s2buf, '/', G_DIR_SEPARATOR);
390 #endif /* !G_OS_WIN32 */
395 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
396 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
398 rc = strncmp(s1, s2, MAX(len1, len2));
402 #endif /* !G_OS_WIN32 */
406 /* remove trailing return code */
407 gchar *strretchomp(gchar *str)
411 if (!*str) return str;
413 for (s = str + strlen(str) - 1;
414 s >= str && (*s == '\n' || *s == '\r');
421 /* remove trailing character */
422 gchar *strtailchomp(gchar *str, gchar tail_char)
426 if (!*str) return str;
427 if (tail_char == '\0') return str;
429 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
435 /* remove CR (carriage return) */
436 gchar *strcrchomp(gchar *str)
440 if (!*str) return str;
442 s = str + strlen(str) - 1;
443 if (*s == '\n' && s > str && *(s - 1) == '\r') {
451 gint file_strip_crs(const gchar *file)
453 FILE *fp = NULL, *outfp = NULL;
455 gchar *out = get_tmp_file();
459 fp = fopen(file, "rb");
463 outfp = fopen(out, "wb");
469 while (fgets(buf, sizeof (buf), fp) != NULL) {
471 if (fputs(buf, outfp) == EOF) {
479 if (fclose(outfp) == EOF) {
483 if (rename_force(out, file) < 0)
494 /* Similar to `strstr' but this function ignores the case of both strings. */
495 gchar *strcasestr(const gchar *haystack, const gchar *needle)
497 register size_t haystack_len, needle_len;
499 haystack_len = strlen(haystack);
500 needle_len = strlen(needle);
502 if (haystack_len < needle_len || needle_len == 0)
505 while (haystack_len >= needle_len) {
506 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
507 return (gchar *)haystack;
517 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
518 gconstpointer needle, size_t needlelen)
520 const gchar *haystack_ = (const gchar *)haystack;
521 const gchar *needle_ = (const gchar *)needle;
522 const gchar *haystack_cur = (const gchar *)haystack;
523 size_t haystack_left = haystacklen;
526 return memchr(haystack_, *needle_, haystacklen);
528 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
530 if (haystacklen - (haystack_cur - haystack_) < needlelen)
532 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
533 return (gpointer)haystack_cur;
536 haystack_left = haystacklen - (haystack_cur - haystack_);
543 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
544 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
546 register const gchar *s = src;
547 register gchar *d = dest;
557 /* Examine if next block is non-ASCII string */
558 gboolean is_next_nonascii(const gchar *s)
562 /* skip head space */
563 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
565 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
566 if (*(guchar *)p > 127 || *(guchar *)p < 32)
573 gint get_next_word_len(const gchar *s)
577 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
583 static void trim_subject_for_compare(gchar *str)
587 eliminate_parenthesis(str, '[', ']');
588 eliminate_parenthesis(str, '(', ')');
591 srcp = str + subject_get_prefix_length(str);
593 memmove(str, srcp, strlen(srcp) + 1);
596 static void trim_subject_for_sort(gchar *str)
602 srcp = str + subject_get_prefix_length(str);
604 memmove(str, srcp, strlen(srcp) + 1);
607 /* compare subjects */
608 gint subject_compare(const gchar *s1, const gchar *s2)
612 if (!s1 || !s2) return -1;
613 if (!*s1 || !*s2) return -1;
615 Xstrdup_a(str1, s1, return -1);
616 Xstrdup_a(str2, s2, return -1);
618 trim_subject_for_compare(str1);
619 trim_subject_for_compare(str2);
621 if (!*str1 || !*str2) return -1;
623 return strcmp(str1, str2);
626 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
630 if (!s1 || !s2) return -1;
632 Xstrdup_a(str1, s1, return -1);
633 Xstrdup_a(str2, s2, return -1);
635 trim_subject_for_sort(str1);
636 trim_subject_for_sort(str2);
638 return g_utf8_collate(str1, str2);
641 void trim_subject(gchar *str)
643 register gchar *srcp;
649 srcp = str + subject_get_prefix_length(str);
654 } else if (*srcp == '(') {
666 else if (*srcp == cl)
673 while (g_ascii_isspace(*srcp)) srcp++;
674 memmove(str, srcp, strlen(srcp) + 1);
677 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
679 register gchar *srcp, *destp;
684 while ((destp = strchr(destp, op))) {
690 else if (*srcp == cl)
696 while (g_ascii_isspace(*srcp)) srcp++;
697 memmove(destp, srcp, strlen(srcp) + 1);
701 void extract_parenthesis(gchar *str, gchar op, gchar cl)
703 register gchar *srcp, *destp;
708 while ((srcp = strchr(destp, op))) {
711 memmove(destp, srcp + 1, strlen(srcp));
716 else if (*destp == cl)
728 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
731 register gchar *srcp, *destp;
733 gboolean in_quote = FALSE;
737 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
740 memmove(destp, srcp + 1, strlen(srcp));
743 if (*destp == op && !in_quote)
745 else if (*destp == cl && !in_quote)
747 else if (*destp == quote_chr)
759 void extract_quote(gchar *str, gchar quote_chr)
763 if ((str = strchr(str, quote_chr))) {
765 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
766 memmove(p - 1, p, strlen(p) + 1);
771 memmove(str, str + 1, p - str);
776 void eliminate_address_comment(gchar *str)
778 register gchar *srcp, *destp;
783 while ((destp = strchr(destp, '"'))) {
784 if ((srcp = strchr(destp + 1, '"'))) {
789 while (g_ascii_isspace(*srcp)) srcp++;
790 memmove(destp, srcp, strlen(srcp) + 1);
800 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
806 else if (*srcp == ')')
812 while (g_ascii_isspace(*srcp)) srcp++;
813 memmove(destp, srcp, strlen(srcp) + 1);
817 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
819 gboolean in_quote = FALSE;
822 if (*str == c && !in_quote)
824 if (*str == quote_chr)
832 void extract_address(gchar *str)
834 eliminate_address_comment(str);
835 if (strchr_with_skip_quote(str, '"', '<'))
836 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
840 void extract_list_id_str(gchar *str)
842 if (strchr_with_skip_quote(str, '"', '<'))
843 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
847 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
852 if (!str) return addr_list;
854 Xstrdup_a(work, str, return addr_list);
857 eliminate_address_comment(work);
860 while (workp && *workp) {
863 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
869 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
870 extract_parenthesis_with_skip_quote
871 (workp, '"', '<', '>');
875 addr_list = g_slist_append(addr_list, g_strdup(workp));
883 GSList *address_list_append(GSList *addr_list, const gchar *str)
885 return address_list_append_real(addr_list, str, TRUE);
888 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
890 return address_list_append_real(addr_list, str, FALSE);
893 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
897 if (!str) return msgid_list;
900 while (strp && *strp) {
901 const gchar *start, *end;
904 if ((start = strchr(strp, '<')) != NULL) {
905 end = strchr(start + 1, '>');
910 msgid = g_strndup(start + 1, end - start - 1);
913 msgid_list = g_slist_prepend(msgid_list, msgid);
923 GSList *references_list_append(GSList *msgid_list, const gchar *str)
927 list = references_list_prepend(NULL, str);
928 list = g_slist_reverse(list);
929 msgid_list = g_slist_concat(msgid_list, list);
934 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
939 if (!str) return group_list;
941 Xstrdup_a(work, str, return group_list);
945 while (workp && *workp) {
948 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
956 group_list = g_slist_append(group_list,
965 GList *add_history(GList *list, const gchar *str)
969 g_return_val_if_fail(str != NULL, list);
971 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
974 list = g_list_remove(list, old->data);
975 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
978 last = g_list_last(list);
981 list = g_list_remove(list, last->data);
985 list = g_list_prepend(list, g_strdup(str));
990 void remove_return(gchar *str)
992 register gchar *p = str;
995 if (*p == '\n' || *p == '\r')
996 memmove(p, p + 1, strlen(p));
1002 void remove_space(gchar *str)
1004 register gchar *p = str;
1009 while (g_ascii_isspace(*(p + spc)))
1012 memmove(p, p + spc, strlen(p + spc) + 1);
1018 void unfold_line(gchar *str)
1020 register gchar *p = str;
1024 if (*p == '\n' || *p == '\r') {
1027 while (g_ascii_isspace(*(p + spc)))
1030 memmove(p, p + spc, strlen(p + spc) + 1);
1036 void subst_char(gchar *str, gchar orig, gchar subst)
1038 register gchar *p = str;
1047 void subst_chars(gchar *str, gchar *orig, gchar subst)
1049 register gchar *p = str;
1052 if (strchr(orig, *p) != NULL)
1058 void subst_for_filename(gchar *str)
1063 subst_chars(str, "\t\r\n\\/*:", '_');
1065 subst_chars(str, "\t\r\n\\/*", '_');
1069 void subst_for_shellsafe_filename(gchar *str)
1073 subst_for_filename(str);
1074 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1077 gboolean is_ascii_str(const gchar *str)
1079 const guchar *p = (const guchar *)str;
1081 while (*p != '\0') {
1082 if (*p != '\t' && *p != ' ' &&
1083 *p != '\r' && *p != '\n' &&
1084 (*p < 32 || *p >= 127))
1092 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1094 gchar * position = NULL;
1095 gchar * tmp_pos = NULL;
1098 if (quote_chars == NULL)
1101 for (i = 0; i < strlen(quote_chars); i++) {
1102 tmp_pos = strrchr (str, quote_chars[i]);
1104 || (tmp_pos != NULL && position <= tmp_pos) )
1110 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1112 const gchar *first_pos;
1113 const gchar *last_pos;
1114 const gchar *p = str;
1115 gint quote_level = -1;
1117 /* speed up line processing by only searching to the last '>' */
1118 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1119 /* skip a line if it contains a '<' before the initial '>' */
1120 if (memchr(str, '<', first_pos - str) != NULL)
1122 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1126 while (p <= last_pos) {
1127 while (p < last_pos) {
1128 if (g_ascii_isspace(*p))
1134 if (strchr(quote_chars, *p))
1136 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1137 /* any characters are allowed except '-' and space */
1139 && !strchr(quote_chars, *p)
1140 && !g_ascii_isspace(*p)
1143 if (strchr(quote_chars, *p))
1155 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1157 const gchar *p = str, *q;
1158 gint cur_line = 0, len;
1160 while ((q = strchr(p, '\n')) != NULL) {
1162 if (len > max_chars) {
1172 if (len > max_chars) {
1181 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1183 gchar * position = NULL;
1184 gchar * tmp_pos = NULL;
1187 if (quote_chars == NULL)
1190 for (i = 0; i < strlen(quote_chars); i++) {
1191 tmp_pos = strchr (str, quote_chars[i]);
1193 || (tmp_pos != NULL && position >= tmp_pos) )
1199 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1201 register guint haystack_len, needle_len;
1202 gboolean in_squote = FALSE, in_dquote = FALSE;
1204 haystack_len = strlen(haystack);
1205 needle_len = strlen(needle);
1207 if (haystack_len < needle_len || needle_len == 0)
1210 while (haystack_len >= needle_len) {
1211 if (!in_squote && !in_dquote &&
1212 !strncmp(haystack, needle, needle_len))
1213 return (gchar *)haystack;
1215 /* 'foo"bar"' -> foo"bar"
1216 "foo'bar'" -> foo'bar' */
1217 if (*haystack == '\'') {
1220 else if (!in_dquote)
1222 } else if (*haystack == '\"') {
1225 else if (!in_squote)
1236 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1239 GSList *string_list = NULL, *slist;
1240 gchar **str_array, *s, *new_str;
1241 guint i, n = 1, len;
1243 g_return_val_if_fail(str != NULL, NULL);
1244 g_return_val_if_fail(delim != NULL, NULL);
1247 max_tokens = G_MAXINT;
1249 s = strstr_with_skip_quote(str, delim);
1251 guint delimiter_len = strlen(delim);
1255 new_str = g_strndup(str, len);
1257 if (new_str[0] == '\'' || new_str[0] == '\"') {
1258 if (new_str[len - 1] == new_str[0]) {
1259 new_str[len - 1] = '\0';
1260 memmove(new_str, new_str + 1, len - 1);
1263 string_list = g_slist_prepend(string_list, new_str);
1265 str = s + delimiter_len;
1266 s = strstr_with_skip_quote(str, delim);
1267 } while (--max_tokens && s);
1271 new_str = g_strdup(str);
1272 if (new_str[0] == '\'' || new_str[0] == '\"') {
1274 if (new_str[len - 1] == new_str[0]) {
1275 new_str[len - 1] = '\0';
1276 memmove(new_str, new_str + 1, len - 1);
1279 string_list = g_slist_prepend(string_list, new_str);
1283 str_array = g_new(gchar*, n);
1287 str_array[i--] = NULL;
1288 for (slist = string_list; slist; slist = slist->next)
1289 str_array[i--] = slist->data;
1291 g_slist_free(string_list);
1296 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1298 gchar *abbrev_group;
1300 const gchar *p = group;
1303 g_return_val_if_fail(group != NULL, NULL);
1305 last = group + strlen(group);
1306 abbrev_group = ap = g_malloc(strlen(group) + 1);
1311 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1313 while (*p != '.') p++;
1316 return abbrev_group;
1321 return abbrev_group;
1324 gchar *trim_string(const gchar *str, gint len)
1326 const gchar *p = str;
1331 if (!str) return NULL;
1332 if (strlen(str) <= len)
1333 return g_strdup(str);
1334 if (g_utf8_validate(str, -1, NULL) == FALSE)
1335 return g_strdup(str);
1337 while (*p != '\0') {
1338 mb_len = g_utf8_skip[*(guchar *)p];
1341 else if (new_len + mb_len > len)
1348 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1349 return g_strconcat(new_str, "...", NULL);
1352 GList *uri_list_extract_filenames(const gchar *uri_list)
1354 GList *result = NULL;
1356 gchar *escaped_utf8uri;
1362 while (g_ascii_isspace(*p)) p++;
1363 if (!strncmp(p, "file:", 5)) {
1366 while (*q && *q != '\n' && *q != '\r') q++;
1369 gchar *file, *locale_file = NULL;
1371 while (q > p && g_ascii_isspace(*q))
1373 Xalloca(escaped_utf8uri, q - p + 2,
1375 Xalloca(file, q - p + 2,
1378 strncpy(escaped_utf8uri, p, q - p + 1);
1379 escaped_utf8uri[q - p + 1] = '\0';
1380 decode_uri(file, escaped_utf8uri);
1382 * g_filename_from_uri() rejects escaped/locale encoded uri
1383 * string which come from Nautilus.
1385 if (g_utf8_validate(file, -1, NULL))
1387 = conv_codeset_strdup(
1390 conv_get_locale_charset_str());
1392 locale_file = g_strdup(file + 5);
1393 result = g_list_append(result, locale_file);
1397 p = strchr(p, '\n');
1404 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1407 static gint axtoi(const gchar *hexstr)
1409 gint hi, lo, result;
1412 if ('0' <= hi && hi <= '9') {
1415 if ('a' <= hi && hi <= 'f') {
1418 if ('A' <= hi && hi <= 'F') {
1423 if ('0' <= lo && lo <= '9') {
1426 if ('a' <= lo && lo <= 'f') {
1429 if ('A' <= lo && lo <= 'F') {
1432 result = lo + (16 * hi);
1436 gboolean is_uri_string(const gchar *str)
1438 while (str && *str && g_ascii_isspace(*str))
1440 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1441 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1442 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1443 g_ascii_strncasecmp(str, "www.", 4) == 0);
1446 gchar *get_uri_path(const gchar *uri)
1448 while (uri && *uri && g_ascii_isspace(*uri))
1450 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1451 return (gchar *)(uri + 7);
1452 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1453 return (gchar *)(uri + 8);
1454 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1455 return (gchar *)(uri + 6);
1457 return (gchar *)uri;
1460 gint get_uri_len(const gchar *str)
1464 if (is_uri_string(str)) {
1465 for (p = str; *p != '\0'; p++) {
1466 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1475 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1476 * plusses, and escape characters are used)
1478 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1480 gchar *dec = decoded_uri;
1481 const gchar *enc = encoded_uri;
1486 if (isxdigit((guchar)enc[0]) &&
1487 isxdigit((guchar)enc[1])) {
1493 if (with_plus && *enc == '+')
1505 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1507 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1510 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1512 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1513 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1517 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1518 gchar **subject, gchar **body, gchar ***attach)
1522 const gchar *forbidden_uris[] = { ".gnupg/",
1528 gint num_attach = 0;
1529 gchar **my_att = NULL;
1531 Xstrdup_a(tmp_mailto, mailto, return -1);
1533 if (!strncmp(tmp_mailto, "mailto:", 7))
1536 p = strchr(tmp_mailto, '?');
1543 *to = decode_uri_gdup(tmp_mailto);
1545 my_att = g_malloc(sizeof(char *));
1549 gchar *field, *value;
1566 if (*value == '\0') continue;
1568 if (from && !g_ascii_strcasecmp(field, "from")) {
1570 *from = decode_uri_gdup(value);
1572 gchar *tmp = decode_uri_gdup(value);
1573 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1577 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1579 *cc = decode_uri_gdup(value);
1581 gchar *tmp = decode_uri_gdup(value);
1582 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1586 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1588 *bcc = decode_uri_gdup(value);
1590 gchar *tmp = decode_uri_gdup(value);
1591 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1595 } else if (subject && !*subject &&
1596 !g_ascii_strcasecmp(field, "subject")) {
1597 *subject = decode_uri_gdup(value);
1598 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1599 *body = decode_uri_gdup(value);
1600 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1601 gchar *tmp = decode_uri_gdup(value);
1602 if (!g_file_get_contents(value, body, NULL, NULL)) {
1603 g_error("Error: couldn't set insert file '%s' in body\n", value);
1607 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1609 gchar *tmp = decode_uri_gdup(value);
1610 for (; forbidden_uris[i]; i++) {
1611 if (strstr(tmp, forbidden_uris[i])) {
1612 g_print("Refusing to attach '%s', potential private data leak\n",
1620 /* attach is correct */
1622 printf("realloc my_att %d\n", (num_attach+1));
1623 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1624 my_att[num_attach-1] = tmp;
1625 my_att[num_attach] = NULL;
1637 #include <windows.h>
1638 #ifndef CSIDL_APPDATA
1639 #define CSIDL_APPDATA 0x001a
1641 #ifndef CSIDL_LOCAL_APPDATA
1642 #define CSIDL_LOCAL_APPDATA 0x001c
1644 #ifndef CSIDL_FLAG_CREATE
1645 #define CSIDL_FLAG_CREATE 0x8000
1647 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1651 w32_strerror (int w32_errno)
1653 static char strerr[256];
1654 int ec = (int)GetLastError ();
1658 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1659 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1660 strerr, DIM (strerr)-1, NULL);
1664 static __inline__ void *
1665 dlopen (const char * name, int flag)
1667 void * hd = LoadLibrary (name);
1671 static __inline__ void *
1672 dlsym (void * hd, const char * sym)
1676 void * fnc = GetProcAddress (hd, sym);
1685 static __inline__ const char *
1688 return w32_strerror (0);
1692 static __inline__ int
1704 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1706 static int initialized;
1707 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1711 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1717 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1719 handle = dlopen (dllnames[i], RTLD_LAZY);
1722 func = dlsym (handle, "SHGetFolderPathA");
1733 return func (a,b,c,d,e);
1738 /* Returns a static string with the directroy from which the module
1739 has been loaded. Returns an empty string on error. */
1740 static char *w32_get_module_dir(void)
1742 static char *moddir;
1745 char name[MAX_PATH+10];
1748 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1751 p = strrchr (name, '\\');
1757 moddir = g_strdup (name);
1761 #endif /* G_OS_WIN32 */
1763 /* Return a static string with the locale dir. */
1764 const gchar *get_locale_dir(void)
1766 static gchar *loc_dir;
1770 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1771 "\\share\\locale", NULL);
1774 loc_dir = LOCALEDIR;
1780 const gchar *get_home_dir(void)
1783 static char home_dir[MAX_PATH] = "";
1785 if (home_dir[0] == '\0') {
1786 if (w32_shgetfolderpath
1787 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1788 NULL, 0, home_dir) < 0)
1789 strcpy (home_dir, "C:\\Sylpheed");
1793 static const gchar *homeenv = NULL;
1798 if (!homeenv && g_getenv("HOME") != NULL)
1799 homeenv = g_strdup(g_getenv("HOME"));
1801 homeenv = g_get_home_dir();
1807 static gchar *claws_rc_dir = NULL;
1808 static gboolean rc_dir_alt = FALSE;
1809 const gchar *get_rc_dir(void)
1813 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1816 return claws_rc_dir;
1819 void set_rc_dir(const gchar *dir)
1821 if (claws_rc_dir != NULL) {
1822 g_print("Error: rc_dir already set\n");
1825 if (g_path_is_absolute(dir))
1826 claws_rc_dir = g_strdup(dir);
1828 claws_rc_dir = g_strconcat(g_get_current_dir(),
1829 G_DIR_SEPARATOR_S, dir, NULL);
1831 debug_print("set rc_dir to %s\n", claws_rc_dir);
1832 if (!is_dir_exist(claws_rc_dir)) {
1833 if (make_dir_hier(claws_rc_dir) != 0) {
1834 g_print("Error: can't create %s\n",
1841 gboolean rc_dir_is_alt(void) {
1845 const gchar *get_mail_base_dir(void)
1848 static gchar *mail_base_dir = NULL;
1851 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1854 return mail_base_dir;
1856 return get_home_dir();
1861 const gchar *prefs_common_get_data_root(void);
1862 gchar *last_data_root = NULL;
1865 const gchar *get_news_cache_dir(void)
1867 static gchar *news_cache_dir = NULL;
1869 const gchar *data_root = prefs_common_get_data_root();
1870 if (strcmp2(data_root, last_data_root)) {
1871 g_free(news_cache_dir);
1872 news_cache_dir = NULL;
1875 if (!news_cache_dir)
1877 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1878 NEWS_CACHE_DIR, NULL);
1882 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1883 "Claws", G_DIR_SEPARATOR_S,
1884 g_get_user_name(), G_DIR_SEPARATOR_S,
1885 NEWS_CACHE_DIR, NULL);
1886 g_free(last_data_root);
1887 last_data_root = g_strdup(last_data_root);
1889 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1890 NEWS_CACHE_DIR, NULL);
1891 g_free(last_data_root);
1892 last_data_root = NULL;
1896 return news_cache_dir;
1899 const gchar *get_imap_cache_dir(void)
1901 static gchar *imap_cache_dir = NULL;
1903 const gchar *data_root = prefs_common_get_data_root();
1904 if (strcmp2(data_root, last_data_root)) {
1905 g_free(imap_cache_dir);
1906 imap_cache_dir = NULL;
1910 if (!imap_cache_dir)
1912 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1913 IMAP_CACHE_DIR, NULL);
1917 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1918 "Claws", G_DIR_SEPARATOR_S,
1919 g_get_user_name(), G_DIR_SEPARATOR_S,
1920 IMAP_CACHE_DIR, NULL);
1921 g_free(last_data_root);
1922 last_data_root = g_strdup(last_data_root);
1924 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1925 IMAP_CACHE_DIR, NULL);
1926 g_free(last_data_root);
1927 last_data_root = NULL;
1932 return imap_cache_dir;
1935 const gchar *get_mime_tmp_dir(void)
1937 static gchar *mime_tmp_dir = NULL;
1940 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1941 MIME_TMP_DIR, NULL);
1943 return mime_tmp_dir;
1946 const gchar *get_template_dir(void)
1948 static gchar *template_dir = NULL;
1951 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1952 TEMPLATE_DIR, NULL);
1954 return template_dir;
1957 /* Return the default directory for Plugins. */
1958 const gchar *get_plugin_dir(void)
1961 static gchar *plugin_dir = NULL;
1964 plugin_dir = g_strconcat(w32_get_module_dir(),
1965 "\\lib\\claws-mail\\plugins\\",
1969 if (is_dir_exist(PLUGINDIR))
1972 static gchar *plugin_dir = NULL;
1974 plugin_dir = g_strconcat(get_rc_dir(),
1975 G_DIR_SEPARATOR_S, "plugins",
1976 G_DIR_SEPARATOR_S, NULL);
1982 const gchar *get_tmp_dir(void)
1984 static gchar *tmp_dir = NULL;
1987 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1993 gchar *get_tmp_file(void)
1996 static guint32 id = 0;
1998 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1999 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2004 const gchar *get_domain_name(void)
2007 static gchar *domain_name = NULL;
2013 if (gethostname(hostname, sizeof(hostname)) != 0) {
2014 perror("gethostname");
2015 domain_name = "unknown";
2017 hostname[sizeof(hostname) - 1] = '\0';
2018 if ((hp = my_gethostbyname(hostname)) == NULL) {
2019 perror("gethostbyname");
2020 domain_name = g_strdup(hostname);
2022 domain_name = g_strdup(hp->h_name);
2025 debug_print("domain name = %s\n", domain_name);
2034 off_t get_file_size(const gchar *file)
2038 if (g_stat(file, &s) < 0) {
2039 FILE_OP_ERROR(file, "stat");
2046 time_t get_file_mtime(const gchar *file)
2050 if (g_stat(file, &s) < 0) {
2051 FILE_OP_ERROR(file, "stat");
2058 off_t get_file_size_as_crlf(const gchar *file)
2062 gchar buf[BUFFSIZE];
2064 if ((fp = g_fopen(file, "rb")) == NULL) {
2065 FILE_OP_ERROR(file, "fopen");
2069 while (fgets(buf, sizeof(buf), fp) != NULL) {
2071 size += strlen(buf) + 2;
2075 FILE_OP_ERROR(file, "fgets");
2084 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2091 if (g_stat(file, &s) < 0) {
2092 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2096 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2103 /* Test on whether FILE is a relative file name. This is
2104 * straightforward for Unix but more complex for Windows. */
2105 gboolean is_relative_filename(const gchar *file)
2110 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2111 return FALSE; /* Prefixed with a hostname - this can't
2112 * be a relative name. */
2114 if ( ((*file >= 'a' && *file <= 'z')
2115 || (*file >= 'A' && *file <= 'Z'))
2117 file += 2; /* Skip drive letter. */
2119 return !(*file == '\\' || *file == '/');
2121 return !(*file == G_DIR_SEPARATOR);
2126 gboolean is_dir_exist(const gchar *dir)
2131 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2134 gboolean is_file_entry_exist(const gchar *file)
2139 return g_file_test(file, G_FILE_TEST_EXISTS);
2142 gboolean dirent_is_regular_file(struct dirent *d)
2144 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2145 if (d->d_type == DT_REG)
2147 else if (d->d_type != DT_UNKNOWN)
2151 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2154 gint change_dir(const gchar *dir)
2156 gchar *prevdir = NULL;
2159 prevdir = g_get_current_dir();
2161 if (g_chdir(dir) < 0) {
2162 FILE_OP_ERROR(dir, "chdir");
2163 if (debug_mode) g_free(prevdir);
2165 } else if (debug_mode) {
2168 cwd = g_get_current_dir();
2169 if (strcmp(prevdir, cwd) != 0)
2170 g_print("current dir: %s\n", cwd);
2178 gint make_dir(const gchar *dir)
2180 if (g_mkdir(dir, S_IRWXU) < 0) {
2181 FILE_OP_ERROR(dir, "mkdir");
2184 if (g_chmod(dir, S_IRWXU) < 0)
2185 FILE_OP_ERROR(dir, "chmod");
2190 gint make_dir_hier(const gchar *dir)
2195 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2196 parent_dir = g_strndup(dir, p - dir);
2197 if (*parent_dir != '\0') {
2198 if (!is_dir_exist(parent_dir)) {
2199 if (make_dir(parent_dir) < 0) {
2208 if (!is_dir_exist(dir)) {
2209 if (make_dir(dir) < 0)
2216 gint remove_all_files(const gchar *dir)
2219 const gchar *dir_name;
2222 prev_dir = g_get_current_dir();
2224 if (g_chdir(dir) < 0) {
2225 FILE_OP_ERROR(dir, "chdir");
2230 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2231 g_warning("failed to open directory: %s\n", dir);
2236 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2237 if (claws_unlink(dir_name) < 0)
2238 FILE_OP_ERROR(dir_name, "unlink");
2243 if (g_chdir(prev_dir) < 0) {
2244 FILE_OP_ERROR(prev_dir, "chdir");
2254 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2257 const gchar *dir_name;
2261 prev_dir = g_get_current_dir();
2263 if (g_chdir(dir) < 0) {
2264 FILE_OP_ERROR(dir, "chdir");
2269 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2270 g_warning("failed to open directory: %s\n", dir);
2275 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2276 file_no = to_number(dir_name);
2277 if (file_no > 0 && first <= file_no && file_no <= last) {
2278 if (is_dir_exist(dir_name))
2280 if (claws_unlink(dir_name) < 0)
2281 FILE_OP_ERROR(dir_name, "unlink");
2287 if (g_chdir(prev_dir) < 0) {
2288 FILE_OP_ERROR(prev_dir, "chdir");
2298 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2301 const gchar *dir_name;
2305 prev_dir = g_get_current_dir();
2307 if (g_chdir(dir) < 0) {
2308 FILE_OP_ERROR(dir, "chdir");
2313 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2314 FILE_OP_ERROR(dir, "opendir");
2319 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2320 file_no = to_number(dir_name);
2321 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2322 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2323 if (is_dir_exist(dir_name))
2325 if (claws_unlink(dir_name) < 0)
2326 FILE_OP_ERROR(dir_name, "unlink");
2332 if (g_chdir(prev_dir) < 0) {
2333 FILE_OP_ERROR(prev_dir, "chdir");
2343 gint remove_all_numbered_files(const gchar *dir)
2345 return remove_numbered_files(dir, 0, UINT_MAX);
2348 gint remove_dir_recursive(const gchar *dir)
2352 const gchar *dir_name;
2355 if (g_stat(dir, &s) < 0) {
2356 FILE_OP_ERROR(dir, "stat");
2357 if (ENOENT == errno) return 0;
2361 if (!S_ISDIR(s.st_mode)) {
2362 if (claws_unlink(dir) < 0) {
2363 FILE_OP_ERROR(dir, "unlink");
2370 prev_dir = g_get_current_dir();
2371 /* g_print("prev_dir = %s\n", prev_dir); */
2373 if (!path_cmp(prev_dir, dir)) {
2375 if (g_chdir("..") < 0) {
2376 FILE_OP_ERROR(dir, "chdir");
2379 prev_dir = g_get_current_dir();
2382 if (g_chdir(dir) < 0) {
2383 FILE_OP_ERROR(dir, "chdir");
2388 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2389 g_warning("failed to open directory: %s\n", dir);
2395 /* remove all files in the directory */
2396 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2397 /* g_print("removing %s\n", dir_name); */
2399 if (is_dir_exist(dir_name)) {
2400 if (remove_dir_recursive(dir_name) < 0) {
2401 g_warning("can't remove directory\n");
2405 if (claws_unlink(dir_name) < 0)
2406 FILE_OP_ERROR(dir_name, "unlink");
2412 if (g_chdir(prev_dir) < 0) {
2413 FILE_OP_ERROR(prev_dir, "chdir");
2420 if (g_rmdir(dir) < 0) {
2421 FILE_OP_ERROR(dir, "rmdir");
2428 gint rename_force(const gchar *oldpath, const gchar *newpath)
2431 if (!is_file_entry_exist(oldpath)) {
2435 if (is_file_exist(newpath)) {
2436 if (claws_unlink(newpath) < 0)
2437 FILE_OP_ERROR(newpath, "unlink");
2440 return g_rename(oldpath, newpath);
2444 * Append src file body to the tail of dest file.
2445 * Now keep_backup has no effects.
2447 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2449 FILE *src_fp, *dest_fp;
2453 gboolean err = FALSE;
2455 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2456 FILE_OP_ERROR(src, "fopen");
2460 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2461 FILE_OP_ERROR(dest, "fopen");
2466 if (change_file_mode_rw(dest_fp, dest) < 0) {
2467 FILE_OP_ERROR(dest, "chmod");
2468 g_warning("can't change file mode\n");
2471 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2472 if (n_read < sizeof(buf) && ferror(src_fp))
2474 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2475 g_warning("writing to %s failed.\n", dest);
2483 if (ferror(src_fp)) {
2484 FILE_OP_ERROR(src, "fread");
2488 if (fclose(dest_fp) == EOF) {
2489 FILE_OP_ERROR(dest, "fclose");
2501 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2503 FILE *src_fp, *dest_fp;
2506 gchar *dest_bak = NULL;
2507 gboolean err = FALSE;
2509 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2510 FILE_OP_ERROR(src, "fopen");
2513 if (is_file_exist(dest)) {
2514 dest_bak = g_strconcat(dest, ".bak", NULL);
2515 if (rename_force(dest, dest_bak) < 0) {
2516 FILE_OP_ERROR(dest, "rename");
2523 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2524 FILE_OP_ERROR(dest, "fopen");
2527 if (rename_force(dest_bak, dest) < 0)
2528 FILE_OP_ERROR(dest_bak, "rename");
2534 if (change_file_mode_rw(dest_fp, dest) < 0) {
2535 FILE_OP_ERROR(dest, "chmod");
2536 g_warning("can't change file mode\n");
2539 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2540 if (n_read < sizeof(buf) && ferror(src_fp))
2542 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2543 g_warning("writing to %s failed.\n", dest);
2548 if (rename_force(dest_bak, dest) < 0)
2549 FILE_OP_ERROR(dest_bak, "rename");
2556 if (ferror(src_fp)) {
2557 FILE_OP_ERROR(src, "fread");
2561 if (fclose(dest_fp) == EOF) {
2562 FILE_OP_ERROR(dest, "fclose");
2569 if (rename_force(dest_bak, dest) < 0)
2570 FILE_OP_ERROR(dest_bak, "rename");
2576 if (keep_backup == FALSE && dest_bak)
2577 claws_unlink(dest_bak);
2584 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2586 if (overwrite == FALSE && is_file_exist(dest)) {
2587 g_warning("move_file(): file %s already exists.", dest);
2591 if (rename_force(src, dest) == 0) return 0;
2593 if (EXDEV != errno) {
2594 FILE_OP_ERROR(src, "rename");
2598 if (copy_file(src, dest, FALSE) < 0) return -1;
2605 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2608 gint bytes_left, to_read;
2611 if (fseek(fp, offset, SEEK_SET) < 0) {
2616 bytes_left = length;
2617 to_read = MIN(bytes_left, sizeof(buf));
2619 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2620 if (n_read < to_read && ferror(fp))
2622 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2625 bytes_left -= n_read;
2626 if (bytes_left == 0)
2628 to_read = MIN(bytes_left, sizeof(buf));
2639 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2642 gboolean err = FALSE;
2644 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2645 FILE_OP_ERROR(dest, "fopen");
2649 if (change_file_mode_rw(dest_fp, dest) < 0) {
2650 FILE_OP_ERROR(dest, "chmod");
2651 g_warning("can't change file mode\n");
2654 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2657 if (!err && fclose(dest_fp) == EOF) {
2658 FILE_OP_ERROR(dest, "fclose");
2663 g_warning("writing to %s failed.\n", dest);
2671 /* convert line endings into CRLF. If the last line doesn't end with
2672 * linebreak, add it.
2674 gchar *canonicalize_str(const gchar *str)
2680 for (p = str; *p != '\0'; ++p) {
2687 if (p == str || *(p - 1) != '\n')
2690 out = outp = g_malloc(new_len + 1);
2691 for (p = str; *p != '\0'; ++p) {
2698 if (p == str || *(p - 1) != '\n') {
2707 gint canonicalize_file(const gchar *src, const gchar *dest)
2709 FILE *src_fp, *dest_fp;
2710 gchar buf[BUFFSIZE];
2712 gboolean err = FALSE;
2713 gboolean last_linebreak = FALSE;
2715 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2716 FILE_OP_ERROR(src, "fopen");
2720 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2721 FILE_OP_ERROR(dest, "fopen");
2726 if (change_file_mode_rw(dest_fp, dest) < 0) {
2727 FILE_OP_ERROR(dest, "chmod");
2728 g_warning("can't change file mode\n");
2731 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2735 if (len == 0) break;
2736 last_linebreak = FALSE;
2738 if (buf[len - 1] != '\n') {
2739 last_linebreak = TRUE;
2740 r = fputs(buf, dest_fp);
2741 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2742 r = fputs(buf, dest_fp);
2745 r = fwrite(buf, 1, len - 1, dest_fp);
2750 r = fputs("\r\n", dest_fp);
2754 g_warning("writing to %s failed.\n", dest);
2762 if (last_linebreak == TRUE) {
2763 if (fputs("\r\n", dest_fp) == EOF)
2767 if (ferror(src_fp)) {
2768 FILE_OP_ERROR(src, "fgets");
2772 if (fclose(dest_fp) == EOF) {
2773 FILE_OP_ERROR(dest, "fclose");
2785 gint canonicalize_file_replace(const gchar *file)
2789 tmp_file = get_tmp_file();
2791 if (canonicalize_file(file, tmp_file) < 0) {
2796 if (move_file(tmp_file, file, TRUE) < 0) {
2797 g_warning("can't replace %s .\n", file);
2798 claws_unlink(tmp_file);
2807 gchar *normalize_newlines(const gchar *str)
2809 const gchar *p = str;
2812 out = outp = g_malloc(strlen(str) + 1);
2813 for (p = str; *p != '\0'; ++p) {
2815 if (*(p + 1) != '\n')
2826 gchar *get_outgoing_rfc2822_str(FILE *fp)
2828 gchar buf[BUFFSIZE];
2832 str = g_string_new(NULL);
2834 /* output header part */
2835 while (fgets(buf, sizeof(buf), fp) != NULL) {
2837 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2844 else if (next != ' ' && next != '\t') {
2848 if (fgets(buf, sizeof(buf), fp) == NULL)
2852 g_string_append(str, buf);
2853 g_string_append(str, "\r\n");
2859 /* output body part */
2860 while (fgets(buf, sizeof(buf), fp) != NULL) {
2863 g_string_append_c(str, '.');
2864 g_string_append(str, buf);
2865 g_string_append(str, "\r\n");
2869 g_string_free(str, FALSE);
2875 * Create a new boundary in a way that it is very unlikely that this
2876 * will occur in the following text. It would be easy to ensure
2877 * uniqueness if everything is either quoted-printable or base64
2878 * encoded (note that conversion is allowed), but because MIME bodies
2879 * may be nested, it may happen that the same boundary has already
2882 * boundary := 0*69<bchars> bcharsnospace
2883 * bchars := bcharsnospace / " "
2884 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2885 * "+" / "_" / "," / "-" / "." /
2886 * "/" / ":" / "=" / "?"
2888 * some special characters removed because of buggy MTAs
2891 gchar *generate_mime_boundary(const gchar *prefix)
2893 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2894 "abcdefghijklmnopqrstuvwxyz"
2899 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2900 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2903 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2907 gint change_file_mode_rw(FILE *fp, const gchar *file)
2910 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2912 return g_chmod(file, S_IRUSR|S_IWUSR);
2916 FILE *my_tmpfile(void)
2918 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2919 const gchar suffix[] = ".XXXXXX";
2920 const gchar *tmpdir;
2922 const gchar *progname;
2929 tmpdir = get_tmp_dir();
2930 tmplen = strlen(tmpdir);
2931 progname = g_get_prgname();
2932 if (progname == NULL)
2933 progname = "claws-mail";
2934 proglen = strlen(progname);
2935 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2938 memcpy(fname, tmpdir, tmplen);
2939 fname[tmplen] = G_DIR_SEPARATOR;
2940 memcpy(fname + tmplen + 1, progname, proglen);
2941 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2943 fd = mkstemp(fname);
2948 claws_unlink(fname);
2950 /* verify that we can write in the file after unlinking */
2951 if (write(fd, buf, 1) < 0) {
2958 fp = fdopen(fd, "w+b");
2966 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2971 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2975 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2976 dir, G_DIR_SEPARATOR);
2977 fd = mkstemp_name(template, filename);
2980 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2981 fd = mkstemp(*filename);
2983 return fdopen(fd, "w+");
2986 FILE *str_open_as_stream(const gchar *str)
2991 g_return_val_if_fail(str != NULL, NULL);
2995 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3000 if (len == 0) return fp;
3002 if (fwrite(str, 1, len, fp) != len) {
3003 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3012 gint str_write_to_file(const gchar *str, const gchar *file)
3017 g_return_val_if_fail(str != NULL, -1);
3018 g_return_val_if_fail(file != NULL, -1);
3020 if ((fp = g_fopen(file, "wb")) == NULL) {
3021 FILE_OP_ERROR(file, "fopen");
3031 if (fwrite(str, 1, len, fp) != len) {
3032 FILE_OP_ERROR(file, "fwrite");
3038 if (fclose(fp) == EOF) {
3039 FILE_OP_ERROR(file, "fclose");
3047 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3054 g_return_val_if_fail(fp != NULL, NULL);
3056 array = g_byte_array_new();
3058 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3059 if (n_read < sizeof(buf) && ferror(fp))
3061 g_byte_array_append(array, buf, n_read);
3065 FILE_OP_ERROR("file stream", "fread");
3066 g_byte_array_free(array, TRUE);
3071 g_byte_array_append(array, buf, 1);
3072 str = (gchar *)array->data;
3073 g_byte_array_free(array, FALSE);
3075 if (recode && !g_utf8_validate(str, -1, NULL)) {
3076 const gchar *src_codeset, *dest_codeset;
3078 src_codeset = conv_get_locale_charset_str();
3079 dest_codeset = CS_UTF_8;
3080 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3088 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3093 g_return_val_if_fail(file != NULL, NULL);
3095 if ((fp = g_fopen(file, "rb")) == NULL) {
3096 FILE_OP_ERROR(file, "fopen");
3100 str = file_read_stream_to_str_full(fp, recode);
3107 gchar *file_read_to_str(const gchar *file)
3109 return file_read_to_str_full(file, TRUE);
3111 gchar *file_read_stream_to_str(FILE *fp)
3113 return file_read_stream_to_str_full(fp, TRUE);
3116 gchar *file_read_to_str_no_recode(const gchar *file)
3118 return file_read_to_str_full(file, FALSE);
3120 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3122 return file_read_stream_to_str_full(fp, FALSE);
3125 char *fgets_crlf(char *buf, int size, FILE *stream)
3127 gboolean is_cr = FALSE;
3128 gboolean last_was_cr = FALSE;
3133 while (--size > 0 && (c = getc(stream)) != EOF)
3136 is_cr = (c == '\r');
3146 last_was_cr = is_cr;
3148 if (c == EOF && cs == buf)
3156 static gint execute_async(gchar *const argv[])
3158 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3160 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3161 NULL, NULL, NULL, FALSE) == FALSE) {
3162 g_warning("Couldn't execute command: %s\n", argv[0]);
3169 static gint execute_sync(gchar *const argv[])
3173 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3175 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3176 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3177 g_warning("Couldn't execute command: %s\n", argv[0]);
3182 if (WIFEXITED(status))
3183 return WEXITSTATUS(status);
3191 gint execute_command_line(const gchar *cmdline, gboolean async)
3196 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3198 argv = strsplit_with_quote(cmdline, " ", 0);
3201 ret = execute_async(argv);
3203 ret = execute_sync(argv);
3210 gchar *get_command_output(const gchar *cmdline)
3212 gchar *child_stdout;
3215 g_return_val_if_fail(cmdline != NULL, NULL);
3217 debug_print("get_command_output(): executing: %s\n", cmdline);
3219 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3221 g_warning("Couldn't execute command: %s\n", cmdline);
3225 return child_stdout;
3228 static gint is_unchanged_uri_char(char c)
3239 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3245 for(i = 0; i < strlen(uri) ; i++) {
3246 if (is_unchanged_uri_char(uri[i])) {
3247 if (k + 2 >= bufsize)
3249 encoded_uri[k++] = uri[i];
3252 char * hexa = "0123456789ABCDEF";
3254 if (k + 4 >= bufsize)
3256 encoded_uri[k++] = '%';
3257 encoded_uri[k++] = hexa[uri[i] / 16];
3258 encoded_uri[k++] = hexa[uri[i] % 16];
3264 gint open_uri(const gchar *uri, const gchar *cmdline)
3267 gchar buf[BUFFSIZE];
3269 gchar encoded_uri[BUFFSIZE];
3270 g_return_val_if_fail(uri != NULL, -1);
3272 /* an option to choose whether to use encode_uri or not ? */
3273 encode_uri(encoded_uri, BUFFSIZE, uri);
3276 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3277 !strchr(p + 2, '%'))
3278 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3281 g_warning("Open URI command line is invalid "
3282 "(there must be only one '%%s'): %s",
3284 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3287 execute_command_line(buf, TRUE);
3289 extern osso_context_t *get_osso_context(void);
3290 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3291 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3292 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3297 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3299 gchar buf[BUFFSIZE];
3302 g_return_val_if_fail(filepath != NULL, -1);
3305 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3306 !strchr(p + 2, '%'))
3307 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3310 g_warning("Open Text Editor command line is invalid "
3311 "(there must be only one '%%s'): %s",
3313 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3316 execute_command_line(buf, TRUE);
3321 time_t remote_tzoffset_sec(const gchar *zone)
3323 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3329 time_t remoteoffset;
3331 strncpy(zone3, zone, 3);
3335 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3336 (c == '+' || c == '-')) {
3337 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3339 remoteoffset = -remoteoffset;
3340 } else if (!strncmp(zone, "UT" , 2) ||
3341 !strncmp(zone, "GMT", 2)) {
3343 } else if (strlen(zone3) == 3) {
3344 for (p = ustzstr; *p != '\0'; p += 3) {
3345 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3346 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3347 remoteoffset = iustz * 3600;
3353 } else if (strlen(zone3) == 1) {
3355 case 'Z': remoteoffset = 0; break;
3356 case 'A': remoteoffset = -1; break;
3357 case 'B': remoteoffset = -2; break;
3358 case 'C': remoteoffset = -3; break;
3359 case 'D': remoteoffset = -4; break;
3360 case 'E': remoteoffset = -5; break;
3361 case 'F': remoteoffset = -6; break;
3362 case 'G': remoteoffset = -7; break;
3363 case 'H': remoteoffset = -8; break;
3364 case 'I': remoteoffset = -9; break;
3365 case 'K': remoteoffset = -10; break; /* J is not used */
3366 case 'L': remoteoffset = -11; break;
3367 case 'M': remoteoffset = -12; break;
3368 case 'N': remoteoffset = 1; break;
3369 case 'O': remoteoffset = 2; break;
3370 case 'P': remoteoffset = 3; break;
3371 case 'Q': remoteoffset = 4; break;
3372 case 'R': remoteoffset = 5; break;
3373 case 'S': remoteoffset = 6; break;
3374 case 'T': remoteoffset = 7; break;
3375 case 'U': remoteoffset = 8; break;
3376 case 'V': remoteoffset = 9; break;
3377 case 'W': remoteoffset = 10; break;
3378 case 'X': remoteoffset = 11; break;
3379 case 'Y': remoteoffset = 12; break;
3380 default: remoteoffset = 0; break;
3382 remoteoffset = remoteoffset * 3600;
3386 return remoteoffset;
3389 time_t tzoffset_sec(time_t *now)
3393 struct tm buf1, buf2;
3395 gmt = *gmtime_r(now, &buf1);
3396 lt = localtime_r(now, &buf2);
3398 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3400 if (lt->tm_year < gmt.tm_year)
3402 else if (lt->tm_year > gmt.tm_year)
3404 else if (lt->tm_yday < gmt.tm_yday)
3406 else if (lt->tm_yday > gmt.tm_yday)
3409 if (off >= 24 * 60) /* should be impossible */
3410 off = 23 * 60 + 59; /* if not, insert silly value */
3411 if (off <= -24 * 60)
3412 off = -(23 * 60 + 59);
3417 /* calculate timezone offset */
3418 gchar *tzoffset(time_t *now)
3420 static gchar offset_string[6];
3424 struct tm buf1, buf2;
3426 gmt = *gmtime_r(now, &buf1);
3427 lt = localtime_r(now, &buf2);
3429 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3431 if (lt->tm_year < gmt.tm_year)
3433 else if (lt->tm_year > gmt.tm_year)
3435 else if (lt->tm_yday < gmt.tm_yday)
3437 else if (lt->tm_yday > gmt.tm_yday)
3445 if (off >= 24 * 60) /* should be impossible */
3446 off = 23 * 60 + 59; /* if not, insert silly value */
3448 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3450 return offset_string;
3453 void get_rfc822_date(gchar *buf, gint len)
3457 gchar day[4], mon[4];
3458 gint dd, hh, mm, ss, yyyy;
3460 gchar buf2[BUFFSIZE];
3463 lt = localtime_r(&t, &buf1);
3465 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3466 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3468 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3469 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3472 void debug_set_mode(gboolean mode)
3477 gboolean debug_get_mode(void)
3482 void debug_print_real(const gchar *format, ...)
3485 gchar buf[BUFFSIZE];
3487 if (!debug_mode) return;
3489 va_start(args, format);
3490 g_vsnprintf(buf, sizeof(buf), format, args);
3497 const char * debug_srcname(const char *file)
3499 const char *s = strrchr (file, '/');
3504 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3506 if (subject == NULL)
3509 subject += subject_get_prefix_length(subject);
3511 return g_hash_table_lookup(subject_table, subject);
3514 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3517 if (subject == NULL || *subject == 0)
3519 subject += subject_get_prefix_length(subject);
3520 g_hash_table_insert(subject_table, subject, data);
3523 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3525 if (subject == NULL)
3528 subject += subject_get_prefix_length(subject);
3529 g_hash_table_remove(subject_table, subject);
3533 *\brief Check if a string is prefixed with known (combinations)
3534 * of prefixes. The function assumes that each prefix
3535 * is terminated by zero or exactly _one_ space.
3537 *\param str String to check for a prefixes
3539 *\return int Number of chars in the prefix that should be skipped
3540 * for a "clean" subject line. If no prefix was found, 0
3543 static regex_t u_regex;
3544 static gboolean u_init_;
3546 void utils_free_regex(void)
3554 int subject_get_prefix_length(const gchar *subject)
3556 /*!< Array with allowable reply prefixes regexps. */
3557 static const gchar * const prefixes[] = {
3558 "Re\\:", /* "Re:" */
3559 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3560 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3561 "Aw\\:", /* "Aw:" (German) */
3562 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3563 "Res\\:", /* "Res:" (Brazilian Outlook) */
3564 "Fw\\:", /* "Fw:" Forward */
3565 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3566 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3567 "Rif\\:", /* "Rif:" (Italian Outlook) */
3568 "Sv\\:", /* "Sv" (Norwegian) */
3569 "Vs\\:", /* "Vs" (Norwegian) */
3570 "Ad\\:", /* "Ad" (Norwegian) */
3571 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3574 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3578 if (!subject) return 0;
3579 if (!*subject) return 0;
3582 GString *s = g_string_new("");
3584 for (n = 0; n < PREFIXES; n++)
3585 /* Terminate each prefix regexpression by a
3586 * "\ ?" (zero or ONE space), and OR them */
3587 g_string_append_printf(s, "(%s\\ ?)%s",
3592 g_string_prepend(s, "(");
3593 g_string_append(s, ")+"); /* match at least once */
3594 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3597 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3598 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3599 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3600 debug_print("Error compiling regexp %s\n", s->str);
3601 g_string_free(s, TRUE);
3605 g_string_free(s, TRUE);
3609 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3615 static guint g_stricase_hash(gconstpointer gptr)
3617 guint hash_result = 0;
3620 for (str = gptr; str && *str; str++) {
3621 hash_result += toupper(*str);
3627 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3629 const char *str1 = gptr1;
3630 const char *str2 = gptr2;
3632 return !strcasecmp(str1, str2);
3635 gint g_int_compare(gconstpointer a, gconstpointer b)
3637 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3640 gchar *generate_msgid(gchar *buf, gint len)
3648 lt = localtime_r(&t, &buft);
3650 if (strcmp(buf, "") == 0) {
3651 addr = g_strconcat("@", get_domain_name(), NULL);
3654 addr = g_strconcat("@", buf, NULL);
3657 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3658 lt->tm_year + 1900, lt->tm_mon + 1,
3659 lt->tm_mday, lt->tm_hour,
3660 lt->tm_min, lt->tm_sec,
3661 (guint) rand(), addr);
3668 quote_cmd_argument()
3670 return a quoted string safely usable in argument of a command.
3672 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3675 gint quote_cmd_argument(gchar * result, guint size,
3685 for(p = path ; * p != '\0' ; p ++) {
3687 if (isalnum((guchar)*p) || (* p == '/')) {
3688 if (remaining > 0) {
3694 result[size - 1] = '\0';
3699 if (remaining >= 2) {
3707 result[size - 1] = '\0';
3712 if (remaining > 0) {
3716 result[size - 1] = '\0';
3730 static void g_node_map_recursive(GNode *node, gpointer data)
3732 GNodeMapData *mapdata = (GNodeMapData *) data;
3734 GNodeMapData newmapdata;
3737 newdata = mapdata->func(node->data, mapdata->data);
3738 if (newdata != NULL) {
3739 newnode = g_node_new(newdata);
3740 g_node_append(mapdata->parent, newnode);
3742 newmapdata.parent = newnode;
3743 newmapdata.func = mapdata->func;
3744 newmapdata.data = mapdata->data;
3746 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3750 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3753 GNodeMapData mapdata;
3755 g_return_val_if_fail(node != NULL, NULL);
3756 g_return_val_if_fail(func != NULL, NULL);
3758 root = g_node_new(func(node->data, data));
3760 mapdata.parent = root;
3761 mapdata.func = func;
3762 mapdata.data = data;
3764 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3769 #define HEX_TO_INT(val, hex) \
3773 if ('0' <= c && c <= '9') { \
3775 } else if ('a' <= c && c <= 'f') { \
3776 val = c - 'a' + 10; \
3777 } else if ('A' <= c && c <= 'F') { \
3778 val = c - 'A' + 10; \
3784 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3791 if (hi == -1 || lo == -1)
3794 *out = (hi << 4) + lo;
3798 #define INT_TO_HEX(hex, val) \
3801 hex = '0' + (val); \
3803 hex = 'A' + (val) - 10; \
3806 void get_hex_str(gchar *out, guchar ch)
3810 INT_TO_HEX(hex, ch >> 4);
3812 INT_TO_HEX(hex, ch & 0x0f);
3818 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3820 #define G_PRINT_REF g_print
3824 *\brief Register ref counted pointer. It is based on GBoxed, so should
3825 * work with anything that uses the GType system. The semantics
3826 * are similar to a C++ auto pointer, with the exception that
3827 * C doesn't have automatic closure (calling destructors) when
3828 * exiting a block scope.
3829 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3830 * function directly.
3832 *\return GType A GType type.
3834 GType g_auto_pointer_register(void)
3836 static GType auto_pointer_type;
3837 if (!auto_pointer_type)
3839 g_boxed_type_register_static
3840 ("G_TYPE_AUTO_POINTER",
3841 (GBoxedCopyFunc) g_auto_pointer_copy,
3842 (GBoxedFreeFunc) g_auto_pointer_free);
3843 return auto_pointer_type;
3847 *\brief Structure with g_new() allocated pointer guarded by the
3850 typedef struct AutoPointerRef {
3851 void (*free) (gpointer);
3857 *\brief The auto pointer opaque structure that references the
3858 * pointer guard block.
3860 typedef struct AutoPointer {
3861 AutoPointerRef *ref;
3862 gpointer ptr; /*!< access to protected pointer */
3866 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3870 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3871 * ... when assigning, copying and freeing storage elements
3873 * gtk_list_store_new(N_S_COLUMNS,
3874 * G_TYPE_AUTO_POINTER,
3878 * Template *precious_data = g_new0(Template, 1);
3879 * g_pointer protect = g_auto_pointer_new(precious_data);
3881 * gtk_list_store_set(container, &iter,
3885 * ... the gtk_list_store has copied the pointer and
3886 * ... incremented its reference count, we should free
3887 * ... the auto pointer (in C++ a destructor would do
3888 * ... this for us when leaving block scope)
3890 * g_auto_pointer_free(protect);
3892 * ... gtk_list_store_set() now manages the data. When
3893 * ... *explicitly* requesting a pointer from the list
3894 * ... store, don't forget you get a copy that should be
3895 * ... freed with g_auto_pointer_free() eventually.
3899 *\param pointer Pointer to be guarded.
3901 *\return GAuto * Pointer that should be used in containers with
3904 GAuto *g_auto_pointer_new(gpointer p)
3906 AutoPointerRef *ref;
3912 ref = g_new0(AutoPointerRef, 1);
3913 ptr = g_new0(AutoPointer, 1);
3923 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3929 *\brief Allocate an autopointer using the passed \a free function to
3930 * free the guarded pointer
3932 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3939 aptr = g_auto_pointer_new(p);
3940 aptr->ref->free = free_;
3944 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3946 if (auto_ptr == NULL)
3948 return ((AutoPointer *) auto_ptr)->ptr;
3952 *\brief Copies an auto pointer by. It's mostly not necessary
3953 * to call this function directly, unless you copy/assign
3954 * the guarded pointer.
3956 *\param auto_ptr Auto pointer returned by previous call to
3957 * g_auto_pointer_new_XXX()
3959 *\return gpointer An auto pointer
3961 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3964 AutoPointerRef *ref;
3967 if (auto_ptr == NULL)
3972 newp = g_new0(AutoPointer, 1);
3975 newp->ptr = ref->pointer;
3979 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3985 *\brief Free an auto pointer
3987 void g_auto_pointer_free(GAuto *auto_ptr)
3990 AutoPointerRef *ref;
3992 if (auto_ptr == NULL)
3998 if (--(ref->cnt) == 0) {
4000 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4002 ref->free(ref->pointer);
4007 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4012 void replace_returns(gchar *str)
4017 while (strstr(str, "\n")) {
4018 *strstr(str, "\n") = ' ';
4020 while (strstr(str, "\r")) {
4021 *strstr(str, "\r") = ' ';
4025 /* get_uri_part() - retrieves a URI starting from scanpos.
4026 Returns TRUE if succesful */
4027 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4028 const gchar **bp, const gchar **ep, gboolean hdr)
4031 gint parenthese_cnt = 0;
4033 g_return_val_if_fail(start != NULL, FALSE);
4034 g_return_val_if_fail(scanpos != NULL, FALSE);
4035 g_return_val_if_fail(bp != NULL, FALSE);
4036 g_return_val_if_fail(ep != NULL, FALSE);
4040 /* find end point of URI */
4041 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4042 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4043 !IS_ASCII(*(const guchar *)ep_) ||
4044 strchr("[]{}<>\"", *ep_)) {
4046 } else if (strchr("(", *ep_)) {
4048 } else if (strchr(")", *ep_)) {
4049 if (parenthese_cnt > 0)
4056 /* no punctuation at end of string */
4058 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4059 * should pass some URI type to this function and decide on that whether
4060 * to perform punctuation stripping */
4062 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4064 for (; ep_ - 1 > scanpos + 1 &&
4065 IS_REAL_PUNCT(*(ep_ - 1));
4069 #undef IS_REAL_PUNCT
4076 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4078 while (bp && *bp && g_ascii_isspace(*bp))
4080 return g_strndup(bp, ep - bp);
4083 /* valid mail address characters */
4084 #define IS_RFC822_CHAR(ch) \
4088 !g_ascii_isspace(ch) && \
4089 !strchr("(),;<>\"", (ch)))
4091 /* alphabet and number within 7bit ASCII */
4092 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4093 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4095 static GHashTable *create_domain_tab(void)
4097 static const gchar *toplvl_domains [] = {
4099 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4100 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4101 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4102 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4103 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4104 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4105 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4106 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4107 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4108 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4109 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4110 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4111 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4112 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4113 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4114 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4115 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4116 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4117 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4118 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4119 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4120 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4121 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4122 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4123 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4124 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4127 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4129 g_return_val_if_fail(htab, NULL);
4130 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4131 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4135 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4137 const gint MAX_LVL_DOM_NAME_LEN = 6;
4138 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4139 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4142 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4145 for (p = buf; p < m && first < last; *p++ = *first++)
4149 return g_hash_table_lookup(tab, buf) != NULL;
4152 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4153 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4154 const gchar **bp, const gchar **ep, gboolean hdr)
4156 /* more complex than the uri part because we need to scan back and forward starting from
4157 * the scan position. */
4158 gboolean result = FALSE;
4159 const gchar *bp_ = NULL;
4160 const gchar *ep_ = NULL;
4161 static GHashTable *dom_tab;
4162 const gchar *last_dot = NULL;
4163 const gchar *prelast_dot = NULL;
4164 const gchar *last_tld_char = NULL;
4166 /* the informative part of the email address (describing the name
4167 * of the email address owner) may contain quoted parts. the
4168 * closure stack stores the last encountered quotes. */
4169 gchar closure_stack[128];
4170 gchar *ptr = closure_stack;
4172 g_return_val_if_fail(start != NULL, FALSE);
4173 g_return_val_if_fail(scanpos != NULL, FALSE);
4174 g_return_val_if_fail(bp != NULL, FALSE);
4175 g_return_val_if_fail(ep != NULL, FALSE);
4178 const gchar *start_quote = NULL;
4179 const gchar *end_quote = NULL;
4181 /* go to the real start */
4182 if (start[0] == ',')
4184 if (start[0] == ';')
4186 while (start[0] == '\n' || start[0] == '\r')
4188 while (start[0] == ' ' || start[0] == '\t')
4193 /* check if there are quotes (to skip , in them) */
4194 if (*start == '"') {
4195 start_quote = start;
4197 end_quote = strstr(start, "\"");
4203 /* skip anything between quotes */
4204 if (start_quote && end_quote) {
4209 /* find end (either , or ; or end of line) */
4210 if (strstr(start, ",") && strstr(start, ";"))
4211 *ep = strstr(start,",") < strstr(start, ";")
4212 ? strstr(start, ",") : strstr(start, ";");
4213 else if (strstr(start, ","))
4214 *ep = strstr(start, ",");
4215 else if (strstr(start, ";"))
4216 *ep = strstr(start, ";");
4218 *ep = start+strlen(start);
4220 /* go back to real start */
4221 if (start_quote && end_quote) {
4222 start = start_quote;
4225 /* check there's still an @ in that, or search
4226 * further if possible */
4227 if (strstr(start, "@") && strstr(start, "@") < *ep)
4229 else if (*ep < start+strlen(start)) {
4232 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4240 dom_tab = create_domain_tab();
4241 g_return_val_if_fail(dom_tab, FALSE);
4243 /* scan start of address */
4244 for (bp_ = scanpos - 1;
4245 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4248 /* TODO: should start with an alnum? */
4250 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4253 if (bp_ != scanpos) {
4254 /* scan end of address */
4255 for (ep_ = scanpos + 1;
4256 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4258 prelast_dot = last_dot;
4260 if (*(last_dot + 1) == '.') {
4261 if (prelast_dot == NULL)
4263 last_dot = prelast_dot;
4268 /* TODO: really should terminate with an alnum? */
4269 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4274 if (last_dot == NULL)
4276 if (last_dot >= ep_)
4277 last_dot = prelast_dot;
4278 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4282 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4283 if (*last_tld_char == '?')
4286 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4293 if (!result) return FALSE;
4295 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4296 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4297 && IS_RFC822_CHAR(*(ep_ + 3))) {
4298 /* this informative part with an @ in it is
4299 * followed by the email address */
4302 /* go to matching '>' (or next non-rfc822 char, like \n) */
4303 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4306 /* include the bracket */
4307 if (*ep_ == '>') ep_++;
4309 /* include the leading quote */
4317 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4318 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4321 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4322 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4325 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4326 #define IN_STACK() (ptr > closure_stack)
4327 /* has underrun check */
4328 #define POP_STACK() if(IN_STACK()) --ptr
4329 /* has overrun check */
4330 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4331 /* has underrun check */
4332 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4336 /* scan for the informative part. */
4337 for (bp_ -= 2; bp_ >= start; bp_--) {
4338 /* if closure on the stack keep scanning */
4339 if (PEEK_STACK() == *bp_) {
4343 if (*bp_ == '\'' || *bp_ == '"') {
4348 /* if nothing in the closure stack, do the special conditions
4349 * the following if..else expression simply checks whether
4350 * a token is acceptable. if not acceptable, the clause
4351 * should terminate the loop with a 'break' */
4352 if (!PEEK_STACK()) {
4354 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4355 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4356 /* hyphens are allowed, but only in
4358 } else if (strchr(" \"'", *bp_)) {
4359 /* but anything not being a punctiation
4362 break; /* anything else is rejected */
4369 /* scan forward (should start with an alnum) */
4370 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4386 #undef IS_ASCII_ALNUM
4387 #undef IS_RFC822_CHAR
4389 gchar *make_email_string(const gchar *bp, const gchar *ep)
4391 /* returns a mailto: URI; mailto: is also used to detect the
4392 * uri type later on in the button_pressed signal handler */
4396 tmp = g_strndup(bp, ep - bp);
4397 result = g_strconcat("mailto:", tmp, NULL);
4403 gchar *make_http_string(const gchar *bp, const gchar *ep)
4405 /* returns an http: URI; */
4409 while (bp && *bp && g_ascii_isspace(*bp))
4411 tmp = g_strndup(bp, ep - bp);
4412 result = g_strconcat("http://", tmp, NULL);
4418 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4420 FILE *fp = fopen(path, "rb");
4421 gchar buf[BUFFSIZE];
4422 gchar *result = NULL;
4425 while (fgets(buf, sizeof (buf), fp) != NULL) {
4426 gchar **parts = g_strsplit(buf, ";", 3);
4427 gchar *trimmed = parts[0];
4428 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4430 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4431 trimmed[strlen(trimmed)-1] = '\0';
4433 if (!strcmp(trimmed, type)) {
4434 gboolean needsterminal = FALSE;
4435 if (parts[2] && strstr(parts[2], "needsterminal")) {
4436 needsterminal = TRUE;
4438 if (parts[2] && strstr(parts[2], "test=")) {
4439 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4440 gchar *testcmd = orig_testcmd;
4441 if (strstr(testcmd,";"))
4442 *(strstr(testcmd,";")) = '\0';
4443 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4445 while (testcmd[strlen(testcmd)-1] == '\n')
4446 testcmd[strlen(testcmd)-1] = '\0';
4447 while (testcmd[strlen(testcmd)-1] == '\r')
4448 testcmd[strlen(testcmd)-1] = '\0';
4449 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4450 testcmd[strlen(testcmd)-1] = '\0';
4452 if (strstr(testcmd, "%s")) {
4453 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4454 gint res = system(tmp);
4456 g_free(orig_testcmd);
4463 gint res = system(testcmd);
4464 g_free(orig_testcmd);
4474 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4476 while (trimmed[strlen(trimmed)-1] == '\n')
4477 trimmed[strlen(trimmed)-1] = '\0';
4478 while (trimmed[strlen(trimmed)-1] == '\r')
4479 trimmed[strlen(trimmed)-1] = '\0';
4480 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4481 trimmed[strlen(trimmed)-1] = '\0';
4482 result = g_strdup(trimmed);
4485 /* if there are no single quotes around %s, add them.
4486 * '.*%s.*' is ok, as in display 'png:%s'
4488 if (strstr(result, "%s")
4489 && !(strstr(result, "'") < strstr(result,"%s") &&
4490 strstr(strstr(result,"%s"), "'"))) {
4491 gchar *start = g_strdup(result);
4492 gchar *end = g_strdup(strstr(result, "%s")+2);
4494 *strstr(start, "%s") = '\0';
4495 tmp = g_strconcat(start,"'%s'",end, NULL);
4501 if (needsterminal) {
4502 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4513 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4515 gchar *result = NULL;
4519 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4520 result = mailcap_get_command_in_file(path, type, file_to_open);
4524 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4528 void mailcap_update_default(const gchar *type, const gchar *command)
4530 gchar *path = NULL, *outpath = NULL;
4531 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4532 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4533 FILE *fp = fopen(path, "rb");
4534 FILE *outfp = fopen(outpath, "wb");
4535 gchar buf[BUFFSIZE];
4536 gboolean err = FALSE;
4544 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4545 gchar **parts = g_strsplit(buf, ";", 3);
4546 gchar *trimmed = parts[0];
4547 while (trimmed[0] == ' ')
4549 while (trimmed[strlen(trimmed)-1] == ' ')
4550 trimmed[strlen(trimmed)-1] = '\0';
4552 if (!strcmp(trimmed, type)) {
4557 if(fputs(buf, outfp) == EOF) {
4564 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4570 if (fclose(outfp) == EOF)
4574 g_rename(outpath, path);
4580 gint copy_dir(const gchar *src, const gchar *dst)
4585 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4586 g_warning("failed to open directory: %s\n", src);
4590 if (make_dir(dst) < 0)
4593 while ((name = g_dir_read_name(dir)) != NULL) {
4594 gchar *old_file, *new_file;
4595 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4596 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4597 debug_print("copying: %s -> %s\n", old_file, new_file);
4598 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4599 gint r = copy_file(old_file, new_file, TRUE);
4606 /* Windows has no symlinks. Or well, Vista seems to
4607 have something like this but the semantics might be
4608 different. Thus we don't use it under Windows. */
4609 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4612 gchar *target = g_file_read_link(old_file, &error);
4614 r = symlink(target, new_file);
4621 #endif /*G_OS_WIN32*/
4622 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4623 gint r = copy_dir(old_file, new_file);
4634 /* crude test to see if a file is an email. */
4635 gboolean file_is_email (const gchar *filename)
4641 if (filename == NULL)
4643 if ((fp = g_fopen(filename, "rb")) == NULL)
4645 while (i < 60 && score < 3
4646 && fgets(buffer, sizeof (buffer), fp) > 0) {
4647 if (!strncmp(buffer, "From:", strlen("From:")))
4649 if (!strncmp(buffer, "To:", strlen("To:")))
4651 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4656 return (score >= 3);
4659 gboolean sc_g_list_bigger(GList *list, gint max)
4663 while (cur && i <= max+1) {
4670 gboolean sc_g_slist_bigger(GSList *list, gint max)
4674 while (cur && i <= max+1) {
4681 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4682 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4683 NULL, NULL, NULL, NULL, NULL, NULL};
4684 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4685 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4686 NULL, NULL, NULL, NULL, NULL, NULL};
4687 const gchar *s_am_up = NULL;
4688 const gchar *s_pm_up = NULL;
4689 const gchar *s_am_low = NULL;
4690 const gchar *s_pm_low = NULL;
4691 const gchar *def_loc_format = NULL;
4692 const gchar *date_loc_format = NULL;
4693 const gchar *time_loc_format = NULL;
4694 const gchar *time_am_pm = NULL;
4696 static gboolean time_names_init_done = FALSE;
4698 static void init_time_names(void)
4700 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4701 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4702 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4703 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4704 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4705 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4706 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4708 monthnames[0] = Q_("Complete month name for use by strftime|January");
4709 monthnames[1] = Q_("Complete month name for use by strftime|February");
4710 monthnames[2] = Q_("Complete month name for use by strftime|March");
4711 monthnames[3] = Q_("Complete month name for use by strftime|April");
4712 monthnames[4] = Q_("Complete month name for use by strftime|May");
4713 monthnames[5] = Q_("Complete month name for use by strftime|June");
4714 monthnames[6] = Q_("Complete month name for use by strftime|July");
4715 monthnames[7] = Q_("Complete month name for use by strftime|August");
4716 monthnames[8] = Q_("Complete month name for use by strftime|September");
4717 monthnames[9] = Q_("Complete month name for use by strftime|October");
4718 monthnames[10] = Q_("Complete month name for use by strftime|November");
4719 monthnames[11] = Q_("Complete month name for use by strftime|December");
4721 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4722 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4723 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4724 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4725 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4726 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4727 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4729 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4730 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4731 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4732 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4733 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4734 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4735 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4736 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4737 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4738 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4739 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4740 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4742 s_am_up = Q_("For use by strftime (morning)|AM");
4743 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4744 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4745 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4747 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4748 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4749 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4751 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4753 time_names_init_done = TRUE;
4756 #define CHECK_SIZE() { \
4757 total_done += len; \
4758 if (total_done >= buflen) { \
4759 buf[buflen-1] = '\0'; \
4764 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4766 gchar *curpos = buf;
4767 gint total_done = 0;
4768 gchar subbuf[64], subfmt[64];
4769 static time_t last_tzset = (time_t)0;
4771 if (!time_names_init_done)
4774 if (format == NULL || lt == NULL)
4777 if (last_tzset != time(NULL)) {
4779 last_tzset = time(NULL);
4782 if (*format == '%') {
4783 gint len = 0, tmp = 0;
4787 len = 1; CHECK_SIZE();
4791 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4792 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4795 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4796 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4800 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4801 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4804 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4805 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4808 fast_strftime(subbuf, 64, def_loc_format, lt);
4809 len = strlen(subbuf); CHECK_SIZE();
4810 strncpy2(curpos, subbuf, buflen - total_done);
4813 total_done += 2; CHECK_SIZE();
4814 tmp = (lt->tm_year + 1900)/100;
4815 *curpos++ = '0'+(tmp / 10);
4816 *curpos++ = '0'+(tmp % 10);
4819 total_done += 2; CHECK_SIZE();
4820 *curpos++ = '0'+(lt->tm_mday / 10);
4821 *curpos++ = '0'+(lt->tm_mday % 10);
4824 total_done += 8; CHECK_SIZE();
4825 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4826 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4828 *curpos++ = '0'+(lt->tm_mday / 10);
4829 *curpos++ = '0'+(lt->tm_mday % 10);
4831 tmp = lt->tm_year%100;
4832 *curpos++ = '0'+(tmp / 10);
4833 *curpos++ = '0'+(tmp % 10);
4836 len = 2; CHECK_SIZE();
4837 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4840 len = 10; CHECK_SIZE();
4841 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
4842 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4845 total_done += 2; CHECK_SIZE();
4846 *curpos++ = '0'+(lt->tm_hour / 10);
4847 *curpos++ = '0'+(lt->tm_hour % 10);
4850 total_done += 2; CHECK_SIZE();
4856 *curpos++ = '0'+(tmp / 10);
4857 *curpos++ = '0'+(tmp % 10);
4860 len = 3; CHECK_SIZE();
4861 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4864 len = 2; CHECK_SIZE();
4865 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4868 len = 2; CHECK_SIZE();
4874 snprintf(curpos, buflen - total_done, "%2d", tmp);
4877 total_done += 2; CHECK_SIZE();
4878 tmp = lt->tm_mon + 1;
4879 *curpos++ = '0'+(tmp / 10);
4880 *curpos++ = '0'+(tmp % 10);
4883 total_done += 2; CHECK_SIZE();
4884 *curpos++ = '0'+(lt->tm_min / 10);
4885 *curpos++ = '0'+(lt->tm_min % 10);
4888 len = 1; CHECK_SIZE();
4892 if (lt->tm_hour >= 12) {
4893 len = strlen(s_pm_up); CHECK_SIZE();
4894 snprintf(curpos, buflen-total_done, s_pm_up);
4896 len = strlen(s_am_up); CHECK_SIZE();
4897 snprintf(curpos, buflen-total_done, s_am_up);
4901 if (lt->tm_hour >= 12) {
4902 len = strlen(s_pm_low); CHECK_SIZE();
4903 snprintf(curpos, buflen-total_done, s_pm_low);
4905 len = strlen(s_am_low); CHECK_SIZE();
4906 snprintf(curpos, buflen-total_done, s_am_low);
4910 fast_strftime(subbuf, 64, time_am_pm, lt);
4911 len = strlen(subbuf); CHECK_SIZE();
4912 strncpy2(curpos, subbuf, buflen - total_done);
4915 total_done += 5; CHECK_SIZE();
4916 *curpos++ = '0'+(lt->tm_hour / 10);
4917 *curpos++ = '0'+(lt->tm_hour % 10);
4919 *curpos++ = '0'+(lt->tm_min / 10);
4920 *curpos++ = '0'+(lt->tm_min % 10);
4923 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4924 len = strlen(subbuf); CHECK_SIZE();
4925 strncpy2(curpos, subbuf, buflen - total_done);
4928 total_done += 2; CHECK_SIZE();
4929 *curpos++ = '0'+(lt->tm_sec / 10);
4930 *curpos++ = '0'+(lt->tm_sec % 10);
4933 len = 1; CHECK_SIZE();
4937 total_done += 8; CHECK_SIZE();
4938 *curpos++ = '0'+(lt->tm_hour / 10);
4939 *curpos++ = '0'+(lt->tm_hour % 10);
4941 *curpos++ = '0'+(lt->tm_min / 10);
4942 *curpos++ = '0'+(lt->tm_min % 10);
4944 *curpos++ = '0'+(lt->tm_sec / 10);
4945 *curpos++ = '0'+(lt->tm_sec % 10);
4948 len = 1; CHECK_SIZE();
4949 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4952 len = 1; CHECK_SIZE();
4953 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4956 fast_strftime(subbuf, 64, date_loc_format, lt);
4957 len = strlen(subbuf); CHECK_SIZE();
4958 strncpy2(curpos, subbuf, buflen - total_done);
4961 fast_strftime(subbuf, 64, time_loc_format, lt);
4962 len = strlen(subbuf); CHECK_SIZE();
4963 strncpy2(curpos, subbuf, buflen - total_done);
4966 total_done += 2; CHECK_SIZE();
4967 tmp = lt->tm_year%100;
4968 *curpos++ = '0'+(tmp / 10);
4969 *curpos++ = '0'+(tmp % 10);
4972 len = 4; CHECK_SIZE();
4973 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4983 /* let these complicated ones be done with the libc */
4984 snprintf(subfmt, 64, "%%%c", *format);
4985 strftime(subbuf, 64, subfmt, lt);
4986 len = strlen(subbuf); CHECK_SIZE();
4987 strncpy2(curpos, subbuf, buflen - total_done);
4991 /* let these complicated modifiers be done with the libc */
4992 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4993 strftime(subbuf, 64, subfmt, lt);
4994 len = strlen(subbuf); CHECK_SIZE();
4995 strncpy2(curpos, subbuf, buflen - total_done);
4999 g_warning("format error (%c)", *format);
5006 int len = 1; CHECK_SIZE();
5007 *curpos++ = *format++;
5014 gboolean prefs_common_get_use_shred(void);
5016 int claws_unlink(const gchar *filename)
5019 static int found_shred = -1;
5020 static const gchar *args[4];
5022 if (found_shred == -1) {
5024 args[0] = g_find_program_in_path("shred");
5025 debug_print("found shred: %s\n", args[0]);
5026 found_shred = (args[0] != NULL) ? 1:0;
5030 if (found_shred == 1 && prefs_common_get_use_shred()) {
5031 if (is_file_exist(filename) && g_stat(filename, &s) == 0) {
5032 if (s.st_nlink == 1) {
5035 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5036 NULL, NULL, NULL, NULL, &status, NULL);
5037 debug_print("%s %s exited with status %d\n",
5038 args[0], filename, WEXITSTATUS(status));
5039 truncate(filename, 0);
5043 return g_unlink(filename);