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>
65 #include <osso-browser-interface.h>
70 #include "../codeconv.h"
74 static gboolean debug_mode = FALSE;
76 static GSList *tempfiles=NULL;
79 /* Return true if we are running as root. This function should beused
80 instead of getuid () == 0. */
81 gboolean superuser_p (void)
84 return w32_is_administrator ();
92 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
93 gint g_chdir(const gchar *path)
96 if (G_WIN32_HAVE_WIDECHAR_API()) {
101 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
107 retval = _wchdir(wpath);
119 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
120 if (cp_path == NULL) {
125 retval = chdir(cp_path);
138 gint g_chmod(const gchar *path, gint mode)
141 if (G_WIN32_HAVE_WIDECHAR_API()) {
146 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
152 retval = _wchmod(wpath, mode);
164 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
165 if (cp_path == NULL) {
170 retval = chmod(cp_path, mode);
179 return chmod(path, mode);
182 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
186 gint mkstemp_name(const gchar *template, gchar **name_used)
188 static gulong count=0; /* W32-_mktemp only supports up to 27
192 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
193 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
194 (S_IRUSR | S_IWUSR));
196 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
198 perror(g_strdup_printf("cant create %s",*name_used));
204 #endif /* G_OS_WIN32 */
207 gint mkstemp(const gchar *template)
210 gint res = mkstemp_name(template, &dummyname);
214 #endif /* G_OS_WIN32 */
216 void list_free_strings(GList *list)
218 list = g_list_first(list);
220 while (list != NULL) {
226 void slist_free_strings(GSList *list)
228 while (list != NULL) {
234 GSList *slist_concat_unique (GSList *first, GSList *second)
242 } else if (second == NULL)
245 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
246 if (g_slist_find(ret, tmp->data) == NULL)
247 ret = g_slist_prepend(ret, tmp->data);
252 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
257 void hash_free_strings(GHashTable *table)
259 g_hash_table_foreach(table, hash_free_strings_func, NULL);
262 static void hash_free_value_mem_func(gpointer key, gpointer value,
268 void hash_free_value_mem(GHashTable *table)
270 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
273 gint str_case_equal(gconstpointer v, gconstpointer v2)
275 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
278 guint str_case_hash(gconstpointer key)
280 const gchar *p = key;
284 h = g_ascii_tolower(h);
285 for (p += 1; *p != '\0'; p++)
286 h = (h << 5) - h + g_ascii_tolower(*p);
292 void ptr_array_free_strings(GPtrArray *array)
297 g_return_if_fail(array != NULL);
299 for (i = 0; i < array->len; i++) {
300 str = g_ptr_array_index(array, i);
305 gboolean str_find(const gchar *haystack, const gchar *needle)
307 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
310 gboolean str_case_find(const gchar *haystack, const gchar *needle)
312 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
315 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
317 return strcmp(haystack, needle) == 0;
320 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
322 return g_ascii_strcasecmp(haystack, needle) == 0;
325 gint to_number(const gchar *nstr)
327 register const gchar *p;
329 if (*nstr == '\0') return -1;
331 for (p = nstr; *p != '\0'; p++)
332 if (!g_ascii_isdigit(*p)) return -1;
337 /* convert integer into string,
338 nstr must be not lower than 11 characters length */
339 gchar *itos_buf(gchar *nstr, gint n)
341 g_snprintf(nstr, 11, "%d", n);
345 /* convert integer into string */
348 static gchar nstr[11];
350 return itos_buf(nstr, n);
353 #define divide(num,divisor,i,d) \
355 i = num >> divisor; \
356 d = num & ((1<<divisor)-1); \
357 d = (d*100) >> divisor; \
360 gchar *to_human_readable(off_t size)
362 static gchar str[14];
363 static gchar *b_format = NULL, *kb_format = NULL,
364 *mb_format = NULL, *gb_format = NULL;
365 register int t = 0, r = 0;
366 if (b_format == NULL) {
368 kb_format = _("%d.%02dKB");
369 mb_format = _("%d.%02dMB");
370 gb_format = _("%.2fGB");
373 if (size < (off_t)1024) {
374 g_snprintf(str, sizeof(str), b_format, (gint)size);
376 } else if (size >> 10 < (off_t)1024) {
377 divide(size, 10, t, r);
378 g_snprintf(str, sizeof(str), kb_format, t, r);
380 } else if (size >> 20 < (off_t)1024) {
381 divide(size, 20, t, r);
382 g_snprintf(str, sizeof(str), mb_format, t, r);
385 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
390 /* strcmp with NULL-checking */
391 gint strcmp2(const gchar *s1, const gchar *s2)
393 if (s1 == NULL || s2 == NULL)
396 return strcmp(s1, s2);
398 /* strstr with NULL-checking */
399 gchar *strstr2(const gchar *s1, const gchar *s2)
401 if (s1 == NULL || s2 == NULL)
404 return strstr(s1, s2);
407 gint path_cmp(const gchar *s1, const gchar *s2)
412 gchar *s1buf, *s2buf;
415 if (s1 == NULL || s2 == NULL) return -1;
416 if (*s1 == '\0' || *s2 == '\0') return -1;
419 s1buf = g_strdup (s1);
420 s2buf = g_strdup (s2);
421 subst_char (s1buf, '/', G_DIR_SEPARATOR);
422 subst_char (s2buf, '/', G_DIR_SEPARATOR);
425 #endif /* !G_OS_WIN32 */
430 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
431 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
433 rc = strncmp(s1, s2, MAX(len1, len2));
437 #endif /* !G_OS_WIN32 */
441 /* remove trailing return code */
442 gchar *strretchomp(gchar *str)
446 if (!*str) return str;
448 for (s = str + strlen(str) - 1;
449 s >= str && (*s == '\n' || *s == '\r');
456 /* remove trailing character */
457 gchar *strtailchomp(gchar *str, gchar tail_char)
461 if (!*str) return str;
462 if (tail_char == '\0') return str;
464 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
470 /* remove CR (carriage return) */
471 gchar *strcrchomp(gchar *str)
475 if (!*str) return str;
477 s = str + strlen(str) - 1;
478 if (*s == '\n' && s > str && *(s - 1) == '\r') {
486 void file_strip_crs(const gchar *file)
488 FILE *fp = NULL, *outfp = NULL;
490 gchar *out = get_tmp_file();
494 fp = fopen(file, "rb");
498 outfp = fopen(out, "wb");
504 while (fgets(buf, sizeof (buf), fp) != NULL) {
511 rename_force(out, file);
516 /* Similar to `strstr' but this function ignores the case of both strings. */
517 gchar *strcasestr(const gchar *haystack, const gchar *needle)
519 register size_t haystack_len, needle_len;
521 haystack_len = strlen(haystack);
522 needle_len = strlen(needle);
524 if (haystack_len < needle_len || needle_len == 0)
527 while (haystack_len >= needle_len) {
528 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
529 return (gchar *)haystack;
539 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
540 gconstpointer needle, size_t needlelen)
542 const gchar *haystack_ = (const gchar *)haystack;
543 const gchar *needle_ = (const gchar *)needle;
544 const gchar *haystack_cur = (const gchar *)haystack;
547 return memchr(haystack_, *needle_, haystacklen);
549 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
551 if (haystacklen - (haystack_cur - haystack_) < needlelen)
553 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
554 return (gpointer)haystack_cur;
562 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
563 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
565 register const gchar *s = src;
566 register gchar *d = dest;
576 /* Examine if next block is non-ASCII string */
577 gboolean is_next_nonascii(const gchar *s)
581 /* skip head space */
582 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
584 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
585 if (*(guchar *)p > 127 || *(guchar *)p < 32)
592 gint get_next_word_len(const gchar *s)
596 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
602 /* compare subjects */
603 gint subject_compare(const gchar *s1, const gchar *s2)
607 if (!s1 || !s2) return -1;
608 if (!*s1 || !*s2) return -1;
610 Xstrdup_a(str1, s1, return -1);
611 Xstrdup_a(str2, s2, return -1);
613 trim_subject_for_compare(str1);
614 trim_subject_for_compare(str2);
616 if (!*str1 || !*str2) return -1;
618 return strcmp(str1, str2);
621 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
625 if (!s1 || !s2) return -1;
627 Xstrdup_a(str1, s1, return -1);
628 Xstrdup_a(str2, s2, return -1);
630 trim_subject_for_sort(str1);
631 trim_subject_for_sort(str2);
633 return g_utf8_collate(str1, str2);
636 void trim_subject_for_compare(gchar *str)
640 eliminate_parenthesis(str, '[', ']');
641 eliminate_parenthesis(str, '(', ')');
644 srcp = str + subject_get_prefix_length(str);
646 memmove(str, srcp, strlen(srcp) + 1);
649 void trim_subject_for_sort(gchar *str)
655 srcp = str + subject_get_prefix_length(str);
657 memmove(str, srcp, strlen(srcp) + 1);
660 void trim_subject(gchar *str)
662 register gchar *srcp;
668 srcp = str + subject_get_prefix_length(str);
673 } else if (*srcp == '(') {
685 else if (*srcp == cl)
692 while (g_ascii_isspace(*srcp)) srcp++;
693 memmove(str, srcp, strlen(srcp) + 1);
696 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
698 register gchar *srcp, *destp;
703 while ((destp = strchr(destp, op))) {
709 else if (*srcp == cl)
715 while (g_ascii_isspace(*srcp)) srcp++;
716 memmove(destp, srcp, strlen(srcp) + 1);
720 void extract_parenthesis(gchar *str, gchar op, gchar cl)
722 register gchar *srcp, *destp;
727 while ((srcp = strchr(destp, op))) {
730 memmove(destp, srcp + 1, strlen(srcp));
735 else if (*destp == cl)
747 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
750 register gchar *srcp, *destp;
752 gboolean in_quote = FALSE;
756 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
759 memmove(destp, srcp + 1, strlen(srcp));
762 if (*destp == op && !in_quote)
764 else if (*destp == cl && !in_quote)
766 else if (*destp == quote_chr)
778 void eliminate_quote(gchar *str, gchar quote_chr)
780 register gchar *srcp, *destp;
784 while ((destp = strchr(destp, quote_chr))) {
785 if ((srcp = strchr(destp + 1, quote_chr))) {
787 while (g_ascii_isspace(*srcp)) srcp++;
788 memmove(destp, srcp, strlen(srcp) + 1);
796 void extract_quote(gchar *str, gchar quote_chr)
800 if ((str = strchr(str, quote_chr))) {
802 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
803 memmove(p - 1, p, strlen(p) + 1);
808 memmove(str, str + 1, p - str);
813 void eliminate_address_comment(gchar *str)
815 register gchar *srcp, *destp;
820 while ((destp = strchr(destp, '"'))) {
821 if ((srcp = strchr(destp + 1, '"'))) {
826 while (g_ascii_isspace(*srcp)) srcp++;
827 memmove(destp, srcp, strlen(srcp) + 1);
837 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
843 else if (*srcp == ')')
849 while (g_ascii_isspace(*srcp)) srcp++;
850 memmove(destp, srcp, strlen(srcp) + 1);
854 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
856 gboolean in_quote = FALSE;
859 if (*str == c && !in_quote)
861 if (*str == quote_chr)
869 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
871 gboolean in_quote = FALSE;
874 p = str + strlen(str) - 1;
876 if (*p == c && !in_quote)
886 void extract_address(gchar *str)
888 eliminate_address_comment(str);
889 if (strchr_with_skip_quote(str, '"', '<'))
890 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
894 void extract_list_id_str(gchar *str)
896 if (strchr_with_skip_quote(str, '"', '<'))
897 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
901 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
906 if (!str) return addr_list;
908 Xstrdup_a(work, str, return addr_list);
911 eliminate_address_comment(work);
914 while (workp && *workp) {
917 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
923 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
924 extract_parenthesis_with_skip_quote
925 (workp, '"', '<', '>');
929 addr_list = g_slist_append(addr_list, g_strdup(workp));
937 GSList *address_list_append(GSList *addr_list, const gchar *str)
939 return address_list_append_real(addr_list, str, TRUE);
942 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
944 return address_list_append_real(addr_list, str, FALSE);
947 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
951 if (!str) return msgid_list;
954 while (strp && *strp) {
955 const gchar *start, *end;
958 if ((start = strchr(strp, '<')) != NULL) {
959 end = strchr(start + 1, '>');
964 msgid = g_strndup(start + 1, end - start - 1);
967 msgid_list = g_slist_prepend(msgid_list, msgid);
977 GSList *references_list_append(GSList *msgid_list, const gchar *str)
981 list = references_list_prepend(NULL, str);
982 list = g_slist_reverse(list);
983 msgid_list = g_slist_concat(msgid_list, list);
988 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
993 if (!str) return group_list;
995 Xstrdup_a(work, str, return group_list);
999 while (workp && *workp) {
1002 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1010 group_list = g_slist_append(group_list,
1019 GList *add_history(GList *list, const gchar *str)
1023 g_return_val_if_fail(str != NULL, list);
1025 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1028 list = g_list_remove(list, old->data);
1029 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1032 last = g_list_last(list);
1035 list = g_list_remove(list, last->data);
1039 list = g_list_prepend(list, g_strdup(str));
1044 void remove_return(gchar *str)
1046 register gchar *p = str;
1049 if (*p == '\n' || *p == '\r')
1050 memmove(p, p + 1, strlen(p));
1056 void remove_space(gchar *str)
1058 register gchar *p = str;
1063 while (g_ascii_isspace(*(p + spc)))
1066 memmove(p, p + spc, strlen(p + spc) + 1);
1072 void unfold_line(gchar *str)
1074 register gchar *p = str;
1078 if (*p == '\n' || *p == '\r') {
1081 while (g_ascii_isspace(*(p + spc)))
1084 memmove(p, p + spc, strlen(p + spc) + 1);
1090 void subst_char(gchar *str, gchar orig, gchar subst)
1092 register gchar *p = str;
1101 void subst_chars(gchar *str, gchar *orig, gchar subst)
1103 register gchar *p = str;
1106 if (strchr(orig, *p) != NULL)
1112 void subst_for_filename(gchar *str)
1117 subst_chars(str, "\t\r\n\\/*:", '_');
1119 subst_chars(str, "\t\r\n\\/*", '_');
1123 void subst_for_shellsafe_filename(gchar *str)
1127 subst_for_filename(str);
1128 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1131 gboolean is_header_line(const gchar *str)
1133 if (str[0] == ':') return FALSE;
1135 while (*str != '\0' && *str != ' ') {
1144 gboolean is_ascii_str(const gchar *str)
1146 const guchar *p = (const guchar *)str;
1148 while (*p != '\0') {
1149 if (*p != '\t' && *p != ' ' &&
1150 *p != '\r' && *p != '\n' &&
1151 (*p < 32 || *p >= 127))
1159 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1161 const gchar *first_pos;
1162 const gchar *last_pos;
1163 const gchar *p = str;
1164 gint quote_level = -1;
1166 /* speed up line processing by only searching to the last '>' */
1167 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1168 /* skip a line if it contains a '<' before the initial '>' */
1169 if (memchr(str, '<', first_pos - str) != NULL)
1171 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1175 while (p <= last_pos) {
1176 while (p < last_pos) {
1177 if (g_ascii_isspace(*p))
1183 if (strchr(quote_chars, *p))
1185 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1186 /* any characters are allowed except '-' and space */
1188 && !strchr(quote_chars, *p)
1189 && !g_ascii_isspace(*p)
1192 if (strchr(quote_chars, *p))
1204 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1206 const gchar *p = str, *q;
1207 gint cur_line = 0, len;
1209 while ((q = strchr(p, '\n')) != NULL) {
1211 if (len > max_chars) {
1221 if (len > max_chars) {
1230 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1232 gchar * position = NULL;
1233 gchar * tmp_pos = NULL;
1236 if (quote_chars == NULL)
1239 for (i = 0; i < strlen(quote_chars); i++) {
1240 tmp_pos = strchr (str, quote_chars[i]);
1242 || (tmp_pos != NULL && position >= tmp_pos) )
1248 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1250 gchar * position = NULL;
1251 gchar * tmp_pos = NULL;
1254 if (quote_chars == NULL)
1257 for (i = 0; i < strlen(quote_chars); i++) {
1258 tmp_pos = strrchr (str, quote_chars[i]);
1260 || (tmp_pos != NULL && position <= tmp_pos) )
1266 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1268 register guint haystack_len, needle_len;
1269 gboolean in_squote = FALSE, in_dquote = FALSE;
1271 haystack_len = strlen(haystack);
1272 needle_len = strlen(needle);
1274 if (haystack_len < needle_len || needle_len == 0)
1277 while (haystack_len >= needle_len) {
1278 if (!in_squote && !in_dquote &&
1279 !strncmp(haystack, needle, needle_len))
1280 return (gchar *)haystack;
1282 /* 'foo"bar"' -> foo"bar"
1283 "foo'bar'" -> foo'bar' */
1284 if (*haystack == '\'') {
1287 else if (!in_dquote)
1289 } else if (*haystack == '\"') {
1292 else if (!in_squote)
1303 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1306 gchar quote_chr = '"';
1308 gboolean in_quote = FALSE;
1312 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1316 if (*p == op && !in_quote)
1318 else if (*p == cl && !in_quote)
1320 else if (*p == quote_chr)
1333 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1336 GSList *string_list = NULL, *slist;
1338 const gchar *s_op, *s_cl;
1341 g_return_val_if_fail(str != NULL, NULL);
1344 max_tokens = G_MAXINT;
1346 s_op = strchr_with_skip_quote(str, '"', op);
1347 if (!s_op) return NULL;
1349 s_cl = strchr_parenthesis_close(str, op, cl);
1357 new_string = g_new(gchar, len + 1);
1358 strncpy(new_string, str, len);
1359 new_string[len] = 0;
1360 string_list = g_slist_prepend(string_list, new_string);
1364 while (*str && g_ascii_isspace(*str)) str++;
1366 string_list = g_slist_prepend(string_list,
1369 s_op = strchr_with_skip_quote(str, '"', op);
1370 if (!--max_tokens || !s_op) break;
1374 s_cl = strchr_parenthesis_close(str, op, cl);
1375 } while (--max_tokens && s_cl);
1378 str_array = g_new(gchar*, n);
1382 str_array[i--] = NULL;
1383 for (slist = string_list; slist; slist = slist->next)
1384 str_array[i--] = slist->data;
1386 g_slist_free(string_list);
1391 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1394 GSList *string_list = NULL, *slist;
1395 gchar **str_array, *s, *new_str;
1396 guint i, n = 1, len;
1398 g_return_val_if_fail(str != NULL, NULL);
1399 g_return_val_if_fail(delim != NULL, NULL);
1402 max_tokens = G_MAXINT;
1404 s = strstr_with_skip_quote(str, delim);
1406 guint delimiter_len = strlen(delim);
1410 new_str = g_strndup(str, len);
1412 if (new_str[0] == '\'' || new_str[0] == '\"') {
1413 if (new_str[len - 1] == new_str[0]) {
1414 new_str[len - 1] = '\0';
1415 memmove(new_str, new_str + 1, len - 1);
1418 string_list = g_slist_prepend(string_list, new_str);
1420 str = s + delimiter_len;
1421 s = strstr_with_skip_quote(str, delim);
1422 } while (--max_tokens && s);
1426 new_str = g_strdup(str);
1427 if (new_str[0] == '\'' || new_str[0] == '\"') {
1429 if (new_str[len - 1] == new_str[0]) {
1430 new_str[len - 1] = '\0';
1431 memmove(new_str, new_str + 1, len - 1);
1434 string_list = g_slist_prepend(string_list, new_str);
1438 str_array = g_new(gchar*, n);
1442 str_array[i--] = NULL;
1443 for (slist = string_list; slist; slist = slist->next)
1444 str_array[i--] = slist->data;
1446 g_slist_free(string_list);
1451 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1453 gchar *abbrev_group;
1455 const gchar *p = group;
1458 g_return_val_if_fail(group != NULL, NULL);
1460 last = group + strlen(group);
1461 abbrev_group = ap = g_malloc(strlen(group) + 1);
1466 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1468 while (*p != '.') p++;
1471 return abbrev_group;
1476 return abbrev_group;
1479 gchar *trim_string(const gchar *str, gint len)
1481 const gchar *p = str;
1486 if (!str) return NULL;
1487 if (strlen(str) <= len)
1488 return g_strdup(str);
1489 if (g_utf8_validate(str, -1, NULL) == FALSE)
1490 return g_strdup(str);
1492 while (*p != '\0') {
1493 mb_len = g_utf8_skip[*(guchar *)p];
1496 else if (new_len + mb_len > len)
1503 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1504 return g_strconcat(new_str, "...", NULL);
1507 GList *uri_list_extract_filenames(const gchar *uri_list)
1509 GList *result = NULL;
1511 gchar *escaped_utf8uri;
1517 while (g_ascii_isspace(*p)) p++;
1518 if (!strncmp(p, "file:", 5)) {
1521 while (*q && *q != '\n' && *q != '\r') q++;
1524 gchar *file, *locale_file = NULL;
1526 while (q > p && g_ascii_isspace(*q))
1528 Xalloca(escaped_utf8uri, q - p + 2,
1530 Xalloca(file, q - p + 2,
1533 strncpy(escaped_utf8uri, p, q - p + 1);
1534 escaped_utf8uri[q - p + 1] = '\0';
1535 decode_uri(file, escaped_utf8uri);
1537 * g_filename_from_uri() rejects escaped/locale encoded uri
1538 * string which come from Nautilus.
1540 if (g_utf8_validate(file, -1, NULL))
1542 = conv_codeset_strdup(
1545 conv_get_locale_charset_str());
1547 locale_file = g_strdup(file + 5);
1548 result = g_list_append(result, locale_file);
1552 p = strchr(p, '\n');
1559 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1562 static gint axtoi(const gchar *hexstr)
1564 gint hi, lo, result;
1567 if ('0' <= hi && hi <= '9') {
1570 if ('a' <= hi && hi <= 'f') {
1573 if ('A' <= hi && hi <= 'F') {
1578 if ('0' <= lo && lo <= '9') {
1581 if ('a' <= lo && lo <= 'f') {
1584 if ('A' <= lo && lo <= 'F') {
1587 result = lo + (16 * hi);
1591 gboolean is_uri_string(const gchar *str)
1593 while (str && *str && g_ascii_isspace(*str))
1595 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1596 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1597 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1598 g_ascii_strncasecmp(str, "www.", 4) == 0);
1601 gchar *get_uri_path(const gchar *uri)
1603 while (uri && *uri && g_ascii_isspace(*uri))
1605 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1606 return (gchar *)(uri + 7);
1607 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1608 return (gchar *)(uri + 8);
1609 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1610 return (gchar *)(uri + 6);
1612 return (gchar *)uri;
1615 gint get_uri_len(const gchar *str)
1619 if (is_uri_string(str)) {
1620 for (p = str; *p != '\0'; p++) {
1621 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1630 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1631 * plusses, and escape characters are used)
1633 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1635 gchar *dec = decoded_uri;
1636 const gchar *enc = encoded_uri;
1641 if (isxdigit((guchar)enc[0]) &&
1642 isxdigit((guchar)enc[1])) {
1648 if (with_plus && *enc == '+')
1660 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1662 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1665 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1666 gchar **subject, gchar **body, gchar **attach)
1670 const gchar *forbidden_uris[] = { ".gnupg/",
1675 Xstrdup_a(tmp_mailto, mailto, return -1);
1677 if (!strncmp(tmp_mailto, "mailto:", 7))
1680 p = strchr(tmp_mailto, '?');
1687 *to = g_strdup(tmp_mailto);
1690 gchar *field, *value;
1707 if (*value == '\0') continue;
1709 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1710 *cc = g_strdup(value);
1711 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1712 *bcc = g_strdup(value);
1713 } else if (subject && !*subject &&
1714 !g_ascii_strcasecmp(field, "subject")) {
1715 *subject = g_malloc(strlen(value) + 1);
1716 decode_uri(*subject, value);
1717 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1718 *body = g_malloc(strlen(value) + 1);
1719 decode_uri(*body, value);
1720 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1722 *attach = g_malloc(strlen(value) + 1);
1723 decode_uri(*attach, value);
1724 for (; forbidden_uris[i]; i++) {
1725 if (strstr(*attach, forbidden_uris[i])) {
1726 printf("Refusing to attach '%s', potential private data leak\n",
1741 #include <windows.h>
1742 #ifndef CSIDL_APPDATA
1743 #define CSIDL_APPDATA 0x001a
1745 #ifndef CSIDL_LOCAL_APPDATA
1746 #define CSIDL_LOCAL_APPDATA 0x001c
1748 #ifndef CSIDL_FLAG_CREATE
1749 #define CSIDL_FLAG_CREATE 0x8000
1751 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1755 w32_strerror (int w32_errno)
1757 static char strerr[256];
1758 int ec = (int)GetLastError ();
1762 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1763 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1764 strerr, DIM (strerr)-1, NULL);
1768 static __inline__ void *
1769 dlopen (const char * name, int flag)
1771 void * hd = LoadLibrary (name);
1775 static __inline__ void *
1776 dlsym (void * hd, const char * sym)
1780 void * fnc = GetProcAddress (hd, sym);
1789 static __inline__ const char *
1792 return w32_strerror (0);
1796 static __inline__ int
1808 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1810 static int initialized;
1811 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1815 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1821 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1823 handle = dlopen (dllnames[i], RTLD_LAZY);
1826 func = dlsym (handle, "SHGetFolderPathA");
1837 return func (a,b,c,d,e);
1842 /* Returns a static string with the directroy from which the module
1843 has been loaded. Returns an empty string on error. */
1844 static char *w32_get_module_dir(void)
1846 static char *moddir;
1849 char name[MAX_PATH+10];
1852 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1855 p = strrchr (name, '\\');
1861 moddir = g_strdup (name);
1865 #endif /* G_OS_WIN32 */
1867 /* Return a static string with the locale dir. */
1868 const gchar *get_locale_dir(void)
1870 static gchar *loc_dir;
1874 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1875 "\\share\\locale", NULL);
1878 loc_dir = LOCALEDIR;
1884 const gchar *get_home_dir(void)
1887 static char home_dir[MAX_PATH] = "";
1889 if (home_dir[0] == '\0') {
1890 if (w32_shgetfolderpath
1891 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1892 NULL, 0, home_dir) < 0)
1893 strcpy (home_dir, "C:\\Sylpheed");
1897 static const gchar *homeenv = NULL;
1902 if (!homeenv && g_getenv("HOME") != NULL)
1903 homeenv = g_strdup(g_getenv("HOME"));
1905 homeenv = g_get_home_dir();
1911 const gchar *get_rc_dir(void)
1913 static gchar *rc_dir = NULL;
1916 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1922 const gchar *get_mail_base_dir(void)
1925 static gchar *mail_base_dir = NULL;
1928 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1931 return mail_base_dir;
1933 return get_home_dir();
1938 const gchar *prefs_common_get_data_root(void);
1939 gchar *last_data_root = NULL;
1942 const gchar *get_news_cache_dir(void)
1944 static gchar *news_cache_dir = NULL;
1946 const gchar *data_root = prefs_common_get_data_root();
1947 if (strcmp2(data_root, last_data_root)) {
1948 g_free(news_cache_dir);
1949 news_cache_dir = NULL;
1952 if (!news_cache_dir)
1954 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1955 NEWS_CACHE_DIR, NULL);
1959 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1960 "Claws", G_DIR_SEPARATOR_S,
1961 g_get_user_name(), G_DIR_SEPARATOR_S,
1962 NEWS_CACHE_DIR, NULL);
1963 g_free(last_data_root);
1964 last_data_root = g_strdup(last_data_root);
1966 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 NEWS_CACHE_DIR, NULL);
1968 g_free(last_data_root);
1969 last_data_root = NULL;
1973 return news_cache_dir;
1976 const gchar *get_imap_cache_dir(void)
1978 static gchar *imap_cache_dir = NULL;
1980 const gchar *data_root = prefs_common_get_data_root();
1981 if (strcmp2(data_root, last_data_root)) {
1982 g_free(imap_cache_dir);
1983 imap_cache_dir = NULL;
1987 if (!imap_cache_dir)
1989 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1990 IMAP_CACHE_DIR, NULL);
1994 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1995 "Claws", G_DIR_SEPARATOR_S,
1996 g_get_user_name(), G_DIR_SEPARATOR_S,
1997 IMAP_CACHE_DIR, NULL);
1998 g_free(last_data_root);
1999 last_data_root = g_strdup(last_data_root);
2001 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2002 IMAP_CACHE_DIR, NULL);
2003 g_free(last_data_root);
2004 last_data_root = NULL;
2009 return imap_cache_dir;
2012 const gchar *get_mbox_cache_dir(void)
2014 static gchar *mbox_cache_dir = NULL;
2016 const gchar *data_root = prefs_common_get_data_root();
2017 if (strcmp2(data_root, last_data_root)) {
2018 g_free(mbox_cache_dir);
2019 mbox_cache_dir = NULL;
2022 if (!mbox_cache_dir)
2024 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2025 MBOX_CACHE_DIR, NULL);
2029 mbox_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2030 "Claws", G_DIR_SEPARATOR_S,
2031 g_get_user_name(), G_DIR_SEPARATOR_S,
2032 MBOX_CACHE_DIR, NULL);
2033 g_free(last_data_root);
2034 last_data_root = g_strdup(last_data_root);
2036 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2037 MBOX_CACHE_DIR, NULL);
2038 g_free(last_data_root);
2039 last_data_root = NULL;
2044 return mbox_cache_dir;
2047 const gchar *get_mime_tmp_dir(void)
2049 static gchar *mime_tmp_dir = NULL;
2052 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2053 MIME_TMP_DIR, NULL);
2055 return mime_tmp_dir;
2058 const gchar *get_template_dir(void)
2060 static gchar *template_dir = NULL;
2063 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2064 TEMPLATE_DIR, NULL);
2066 return template_dir;
2069 const gchar *get_header_cache_dir(void)
2071 static gchar *header_dir = NULL;
2074 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2075 HEADER_CACHE_DIR, NULL);
2080 /* Return the default directory for Plugins. */
2081 const gchar *get_plugin_dir(void)
2084 static gchar *plugin_dir = NULL;
2087 plugin_dir = g_strconcat(w32_get_module_dir(),
2088 "\\lib\\claws-mail\\plugins\\",
2092 if (is_dir_exist(PLUGINDIR))
2095 static gchar *plugin_dir = NULL;
2097 plugin_dir = g_strconcat(get_rc_dir(),
2098 G_DIR_SEPARATOR_S, "plugins",
2099 G_DIR_SEPARATOR_S, NULL);
2105 const gchar *get_tmp_dir(void)
2107 static gchar *tmp_dir = NULL;
2110 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2116 gchar *get_tmp_file(void)
2119 static guint32 id = 0;
2121 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2122 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2127 const gchar *get_domain_name(void)
2130 static gchar *domain_name = NULL;
2136 if (gethostname(hostname, sizeof(hostname)) != 0) {
2137 perror("gethostname");
2138 domain_name = "unknown";
2140 hostname[sizeof(hostname) - 1] = '\0';
2141 if ((hp = my_gethostbyname(hostname)) == NULL) {
2142 perror("gethostbyname");
2143 domain_name = g_strdup(hostname);
2145 domain_name = g_strdup(hp->h_name);
2148 debug_print("domain name = %s\n", domain_name);
2157 off_t get_file_size(const gchar *file)
2161 if (g_stat(file, &s) < 0) {
2162 FILE_OP_ERROR(file, "stat");
2169 time_t get_file_mtime(const gchar *file)
2173 if (g_stat(file, &s) < 0) {
2174 FILE_OP_ERROR(file, "stat");
2181 off_t get_file_size_as_crlf(const gchar *file)
2185 gchar buf[BUFFSIZE];
2187 if ((fp = g_fopen(file, "rb")) == NULL) {
2188 FILE_OP_ERROR(file, "fopen");
2192 while (fgets(buf, sizeof(buf), fp) != NULL) {
2194 size += strlen(buf) + 2;
2198 FILE_OP_ERROR(file, "fgets");
2207 off_t get_left_file_size(FILE *fp)
2213 if ((pos = ftell(fp)) < 0) {
2217 if (fseek(fp, 0L, SEEK_END) < 0) {
2221 if ((end = ftell(fp)) < 0) {
2226 if (fseek(fp, pos, SEEK_SET) < 0) {
2234 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2241 if (g_stat(file, &s) < 0) {
2242 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2246 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2253 /* Test on whether FILE is a relative file name. This is
2254 * straightforward for Unix but more complex for Windows. */
2255 gboolean is_relative_filename(const gchar *file)
2260 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2261 return FALSE; /* Prefixed with a hostname - this can't
2262 * be a relative name. */
2264 if ( ((*file >= 'a' && *file <= 'z')
2265 || (*file >= 'A' && *file <= 'Z'))
2267 file += 2; /* Skip drive letter. */
2269 return !(*file == '\\' || *file == '/');
2271 return !(*file == G_DIR_SEPARATOR);
2276 gboolean is_dir_exist(const gchar *dir)
2281 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2284 gboolean is_file_entry_exist(const gchar *file)
2289 return g_file_test(file, G_FILE_TEST_EXISTS);
2292 gboolean dirent_is_regular_file(struct dirent *d)
2294 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2295 if (d->d_type == DT_REG)
2297 else if (d->d_type != DT_UNKNOWN)
2301 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2304 gboolean dirent_is_directory(struct dirent *d)
2306 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2307 if (d->d_type == DT_DIR)
2309 else if (d->d_type != DT_UNKNOWN)
2313 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2316 gint change_dir(const gchar *dir)
2318 gchar *prevdir = NULL;
2321 prevdir = g_get_current_dir();
2323 if (g_chdir(dir) < 0) {
2324 FILE_OP_ERROR(dir, "chdir");
2325 if (debug_mode) g_free(prevdir);
2327 } else if (debug_mode) {
2330 cwd = g_get_current_dir();
2331 if (strcmp(prevdir, cwd) != 0)
2332 g_print("current dir: %s\n", cwd);
2340 gint make_dir(const gchar *dir)
2342 if (g_mkdir(dir, S_IRWXU) < 0) {
2343 FILE_OP_ERROR(dir, "mkdir");
2346 if (g_chmod(dir, S_IRWXU) < 0)
2347 FILE_OP_ERROR(dir, "chmod");
2352 gint make_dir_hier(const gchar *dir)
2357 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2358 parent_dir = g_strndup(dir, p - dir);
2359 if (*parent_dir != '\0') {
2360 if (!is_dir_exist(parent_dir)) {
2361 if (make_dir(parent_dir) < 0) {
2370 if (!is_dir_exist(dir)) {
2371 if (make_dir(dir) < 0)
2378 gint remove_all_files(const gchar *dir)
2381 const gchar *dir_name;
2384 prev_dir = g_get_current_dir();
2386 if (g_chdir(dir) < 0) {
2387 FILE_OP_ERROR(dir, "chdir");
2392 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2393 g_warning("failed to open directory: %s\n", dir);
2398 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2399 if (g_unlink(dir_name) < 0)
2400 FILE_OP_ERROR(dir_name, "unlink");
2405 if (g_chdir(prev_dir) < 0) {
2406 FILE_OP_ERROR(prev_dir, "chdir");
2416 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2419 const gchar *dir_name;
2423 prev_dir = g_get_current_dir();
2425 if (g_chdir(dir) < 0) {
2426 FILE_OP_ERROR(dir, "chdir");
2431 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2432 g_warning("failed to open directory: %s\n", dir);
2437 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2438 file_no = to_number(dir_name);
2439 if (file_no > 0 && first <= file_no && file_no <= last) {
2440 if (is_dir_exist(dir_name))
2442 if (g_unlink(dir_name) < 0)
2443 FILE_OP_ERROR(dir_name, "unlink");
2449 if (g_chdir(prev_dir) < 0) {
2450 FILE_OP_ERROR(prev_dir, "chdir");
2460 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2463 const gchar *dir_name;
2467 prev_dir = g_get_current_dir();
2469 if (g_chdir(dir) < 0) {
2470 FILE_OP_ERROR(dir, "chdir");
2475 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2476 FILE_OP_ERROR(dir, "opendir");
2481 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2482 file_no = to_number(dir_name);
2483 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2484 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2485 if (is_dir_exist(dir_name))
2487 if (g_unlink(dir_name) < 0)
2488 FILE_OP_ERROR(dir_name, "unlink");
2494 if (g_chdir(prev_dir) < 0) {
2495 FILE_OP_ERROR(prev_dir, "chdir");
2505 gint remove_all_numbered_files(const gchar *dir)
2507 return remove_numbered_files(dir, 0, UINT_MAX);
2510 gint remove_expired_files(const gchar *dir, guint hours)
2513 const gchar *dir_name;
2517 time_t mtime, now, expire_time;
2519 prev_dir = g_get_current_dir();
2521 if (g_chdir(dir) < 0) {
2522 FILE_OP_ERROR(dir, "chdir");
2527 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2528 g_warning("failed to open directory: %s\n", dir);
2534 expire_time = hours * 60 * 60;
2536 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2537 file_no = to_number(dir_name);
2539 if (g_stat(dir_name, &s) < 0) {
2540 FILE_OP_ERROR(dir_name, "stat");
2543 if (S_ISDIR(s.st_mode))
2545 mtime = MAX(s.st_mtime, s.st_atime);
2546 if (now - mtime > expire_time) {
2547 if (g_unlink(dir_name) < 0)
2548 FILE_OP_ERROR(dir_name, "unlink");
2555 if (g_chdir(prev_dir) < 0) {
2556 FILE_OP_ERROR(prev_dir, "chdir");
2566 gint remove_dir_recursive(const gchar *dir)
2570 const gchar *dir_name;
2573 if (g_stat(dir, &s) < 0) {
2574 FILE_OP_ERROR(dir, "stat");
2575 if (ENOENT == errno) return 0;
2579 if (!S_ISDIR(s.st_mode)) {
2580 if (g_unlink(dir) < 0) {
2581 FILE_OP_ERROR(dir, "unlink");
2588 prev_dir = g_get_current_dir();
2589 /* g_print("prev_dir = %s\n", prev_dir); */
2591 if (!path_cmp(prev_dir, dir)) {
2593 if (g_chdir("..") < 0) {
2594 FILE_OP_ERROR(dir, "chdir");
2597 prev_dir = g_get_current_dir();
2600 if (g_chdir(dir) < 0) {
2601 FILE_OP_ERROR(dir, "chdir");
2606 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2607 g_warning("failed to open directory: %s\n", dir);
2613 /* remove all files in the directory */
2614 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2615 /* g_print("removing %s\n", dir_name); */
2617 if (is_dir_exist(dir_name)) {
2618 if (remove_dir_recursive(dir_name) < 0) {
2619 g_warning("can't remove directory\n");
2623 if (g_unlink(dir_name) < 0)
2624 FILE_OP_ERROR(dir_name, "unlink");
2630 if (g_chdir(prev_dir) < 0) {
2631 FILE_OP_ERROR(prev_dir, "chdir");
2638 if (g_rmdir(dir) < 0) {
2639 FILE_OP_ERROR(dir, "rmdir");
2646 gint rename_force(const gchar *oldpath, const gchar *newpath)
2649 if (!is_file_entry_exist(oldpath)) {
2653 if (is_file_exist(newpath)) {
2654 if (g_unlink(newpath) < 0)
2655 FILE_OP_ERROR(newpath, "unlink");
2658 return g_rename(oldpath, newpath);
2662 * Append src file body to the tail of dest file.
2663 * Now keep_backup has no effects.
2665 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2667 FILE *src_fp, *dest_fp;
2671 gboolean err = FALSE;
2673 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2674 FILE_OP_ERROR(src, "fopen");
2678 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2679 FILE_OP_ERROR(dest, "fopen");
2684 if (change_file_mode_rw(dest_fp, dest) < 0) {
2685 FILE_OP_ERROR(dest, "chmod");
2686 g_warning("can't change file mode\n");
2689 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2690 if (n_read < sizeof(buf) && ferror(src_fp))
2692 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2693 g_warning("writing to %s failed.\n", dest);
2701 if (ferror(src_fp)) {
2702 FILE_OP_ERROR(src, "fread");
2706 if (fclose(dest_fp) == EOF) {
2707 FILE_OP_ERROR(dest, "fclose");
2719 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2721 FILE *src_fp, *dest_fp;
2724 gchar *dest_bak = NULL;
2725 gboolean err = FALSE;
2727 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2728 FILE_OP_ERROR(src, "fopen");
2731 if (is_file_exist(dest)) {
2732 dest_bak = g_strconcat(dest, ".bak", NULL);
2733 if (rename_force(dest, dest_bak) < 0) {
2734 FILE_OP_ERROR(dest, "rename");
2741 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2742 FILE_OP_ERROR(dest, "fopen");
2745 if (rename_force(dest_bak, dest) < 0)
2746 FILE_OP_ERROR(dest_bak, "rename");
2752 if (change_file_mode_rw(dest_fp, dest) < 0) {
2753 FILE_OP_ERROR(dest, "chmod");
2754 g_warning("can't change file mode\n");
2757 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2758 if (n_read < sizeof(buf) && ferror(src_fp))
2760 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2761 g_warning("writing to %s failed.\n", dest);
2766 if (rename_force(dest_bak, dest) < 0)
2767 FILE_OP_ERROR(dest_bak, "rename");
2774 if (ferror(src_fp)) {
2775 FILE_OP_ERROR(src, "fread");
2779 if (fclose(dest_fp) == EOF) {
2780 FILE_OP_ERROR(dest, "fclose");
2787 if (rename_force(dest_bak, dest) < 0)
2788 FILE_OP_ERROR(dest_bak, "rename");
2794 if (keep_backup == FALSE && dest_bak)
2802 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2804 if (overwrite == FALSE && is_file_exist(dest)) {
2805 g_warning("move_file(): file %s already exists.", dest);
2809 if (rename_force(src, dest) == 0) return 0;
2811 if (EXDEV != errno) {
2812 FILE_OP_ERROR(src, "rename");
2816 if (copy_file(src, dest, FALSE) < 0) return -1;
2823 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2826 gint bytes_left, to_read;
2829 if (fseek(fp, offset, SEEK_SET) < 0) {
2834 bytes_left = length;
2835 to_read = MIN(bytes_left, sizeof(buf));
2837 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2838 if (n_read < to_read && ferror(fp))
2840 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2843 bytes_left -= n_read;
2844 if (bytes_left == 0)
2846 to_read = MIN(bytes_left, sizeof(buf));
2857 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2860 gboolean err = FALSE;
2862 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2863 FILE_OP_ERROR(dest, "fopen");
2867 if (change_file_mode_rw(dest_fp, dest) < 0) {
2868 FILE_OP_ERROR(dest, "chmod");
2869 g_warning("can't change file mode\n");
2872 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2875 if (!err && fclose(dest_fp) == EOF) {
2876 FILE_OP_ERROR(dest, "fclose");
2881 g_warning("writing to %s failed.\n", dest);
2889 /* convert line endings into CRLF. If the last line doesn't end with
2890 * linebreak, add it.
2892 gchar *canonicalize_str(const gchar *str)
2898 for (p = str; *p != '\0'; ++p) {
2905 if (p == str || *(p - 1) != '\n')
2908 out = outp = g_malloc(new_len + 1);
2909 for (p = str; *p != '\0'; ++p) {
2916 if (p == str || *(p - 1) != '\n') {
2925 gint canonicalize_file(const gchar *src, const gchar *dest)
2927 FILE *src_fp, *dest_fp;
2928 gchar buf[BUFFSIZE];
2930 gboolean err = FALSE;
2931 gboolean last_linebreak = FALSE;
2933 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2934 FILE_OP_ERROR(src, "fopen");
2938 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2939 FILE_OP_ERROR(dest, "fopen");
2944 if (change_file_mode_rw(dest_fp, dest) < 0) {
2945 FILE_OP_ERROR(dest, "chmod");
2946 g_warning("can't change file mode\n");
2949 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2953 if (len == 0) break;
2954 last_linebreak = FALSE;
2956 if (buf[len - 1] != '\n') {
2957 last_linebreak = TRUE;
2958 r = fputs(buf, dest_fp);
2959 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2960 r = fputs(buf, dest_fp);
2963 r = fwrite(buf, 1, len - 1, dest_fp);
2968 r = fputs("\r\n", dest_fp);
2972 g_warning("writing to %s failed.\n", dest);
2980 if (last_linebreak == TRUE) {
2981 if (fputs("\r\n", dest_fp) == EOF)
2985 if (ferror(src_fp)) {
2986 FILE_OP_ERROR(src, "fgets");
2990 if (fclose(dest_fp) == EOF) {
2991 FILE_OP_ERROR(dest, "fclose");
3003 gint canonicalize_file_replace(const gchar *file)
3007 tmp_file = get_tmp_file();
3009 if (canonicalize_file(file, tmp_file) < 0) {
3014 if (move_file(tmp_file, file, TRUE) < 0) {
3015 g_warning("can't replace %s .\n", file);
3025 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3027 FILE *src_fp, *dest_fp;
3028 gchar buf[BUFFSIZE];
3029 gboolean err = FALSE;
3031 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3032 FILE_OP_ERROR(src, "fopen");
3036 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3037 FILE_OP_ERROR(dest, "fopen");
3042 if (change_file_mode_rw(dest_fp, dest) < 0) {
3043 FILE_OP_ERROR(dest, "chmod");
3044 g_warning("can't change file mode\n");
3047 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3049 if (fputs(buf, dest_fp) == EOF) {
3050 g_warning("writing to %s failed.\n", dest);
3058 if (ferror(src_fp)) {
3059 FILE_OP_ERROR(src, "fgets");
3063 if (fclose(dest_fp) == EOF) {
3064 FILE_OP_ERROR(dest, "fclose");
3076 gint uncanonicalize_file_replace(const gchar *file)
3080 tmp_file = get_tmp_file();
3082 if (uncanonicalize_file(file, tmp_file) < 0) {
3087 if (move_file(tmp_file, file, TRUE) < 0) {
3088 g_warning("can't replace %s .\n", file);
3098 gchar *normalize_newlines(const gchar *str)
3100 const gchar *p = str;
3103 out = outp = g_malloc(strlen(str) + 1);
3104 for (p = str; *p != '\0'; ++p) {
3106 if (*(p + 1) != '\n')
3117 gchar *get_outgoing_rfc2822_str(FILE *fp)
3119 gchar buf[BUFFSIZE];
3123 str = g_string_new(NULL);
3125 /* output header part */
3126 while (fgets(buf, sizeof(buf), fp) != NULL) {
3128 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3135 else if (next != ' ' && next != '\t') {
3139 if (fgets(buf, sizeof(buf), fp) == NULL)
3143 g_string_append(str, buf);
3144 g_string_append(str, "\r\n");
3150 /* output body part */
3151 while (fgets(buf, sizeof(buf), fp) != NULL) {
3154 g_string_append_c(str, '.');
3155 g_string_append(str, buf);
3156 g_string_append(str, "\r\n");
3160 g_string_free(str, FALSE);
3166 * Create a new boundary in a way that it is very unlikely that this
3167 * will occur in the following text. It would be easy to ensure
3168 * uniqueness if everything is either quoted-printable or base64
3169 * encoded (note that conversion is allowed), but because MIME bodies
3170 * may be nested, it may happen that the same boundary has already
3173 * boundary := 0*69<bchars> bcharsnospace
3174 * bchars := bcharsnospace / " "
3175 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3176 * "+" / "_" / "," / "-" / "." /
3177 * "/" / ":" / "=" / "?"
3179 * some special characters removed because of buggy MTAs
3182 gchar *generate_mime_boundary(const gchar *prefix)
3184 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3185 "abcdefghijklmnopqrstuvwxyz"
3190 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3191 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3194 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3198 gint change_file_mode_rw(FILE *fp, const gchar *file)
3201 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3203 return g_chmod(file, S_IRUSR|S_IWUSR);
3207 FILE *my_tmpfile(void)
3209 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3210 const gchar suffix[] = ".XXXXXX";
3211 const gchar *tmpdir;
3213 const gchar *progname;
3220 tmpdir = get_tmp_dir();
3221 tmplen = strlen(tmpdir);
3222 progname = g_get_prgname();
3223 if (progname == NULL)
3224 progname = "claws-mail";
3225 proglen = strlen(progname);
3226 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3229 memcpy(fname, tmpdir, tmplen);
3230 fname[tmplen] = G_DIR_SEPARATOR;
3231 memcpy(fname + tmplen + 1, progname, proglen);
3232 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3234 fd = mkstemp(fname);
3241 /* verify that we can write in the file after unlinking */
3242 if (write(fd, buf, 1) < 0) {
3249 fp = fdopen(fd, "w+b");
3257 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3262 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3266 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3267 dir, G_DIR_SEPARATOR);
3268 fd = mkstemp_name(template, filename);
3271 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3272 fd = mkstemp(*filename);
3274 return fdopen(fd, "w+");
3277 FILE *str_open_as_stream(const gchar *str)
3282 g_return_val_if_fail(str != NULL, NULL);
3286 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3291 if (len == 0) return fp;
3293 if (fwrite(str, 1, len, fp) != len) {
3294 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3303 gint str_write_to_file(const gchar *str, const gchar *file)
3308 g_return_val_if_fail(str != NULL, -1);
3309 g_return_val_if_fail(file != NULL, -1);
3311 if ((fp = g_fopen(file, "wb")) == NULL) {
3312 FILE_OP_ERROR(file, "fopen");
3322 if (fwrite(str, 1, len, fp) != len) {
3323 FILE_OP_ERROR(file, "fwrite");
3329 if (fclose(fp) == EOF) {
3330 FILE_OP_ERROR(file, "fclose");
3338 gchar *file_read_to_str(const gchar *file)
3343 g_return_val_if_fail(file != NULL, NULL);
3345 if ((fp = g_fopen(file, "rb")) == NULL) {
3346 FILE_OP_ERROR(file, "fopen");
3350 str = file_read_stream_to_str(fp);
3357 gchar *file_read_stream_to_str(FILE *fp)
3364 g_return_val_if_fail(fp != NULL, NULL);
3366 array = g_byte_array_new();
3368 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3369 if (n_read < sizeof(buf) && ferror(fp))
3371 g_byte_array_append(array, buf, n_read);
3375 FILE_OP_ERROR("file stream", "fread");
3376 g_byte_array_free(array, TRUE);
3381 g_byte_array_append(array, buf, 1);
3382 str = (gchar *)array->data;
3383 g_byte_array_free(array, FALSE);
3385 if (!g_utf8_validate(str, -1, NULL)) {
3386 const gchar *src_codeset, *dest_codeset;
3388 src_codeset = conv_get_locale_charset_str();
3389 dest_codeset = CS_UTF_8;
3390 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3399 char *fgets_crlf(char *buf, int size, FILE *stream)
3401 gboolean is_cr = FALSE;
3402 gboolean last_was_cr = FALSE;
3407 while (--size > 0 && (c = getc(stream)) != EOF)
3410 is_cr = (c == '\r');
3420 last_was_cr = is_cr;
3422 if (c == EOF && cs == buf)
3430 static gint execute_async(gchar *const argv[])
3432 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3434 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3435 NULL, NULL, NULL, FALSE) == FALSE) {
3436 g_warning("Couldn't execute command: %s\n", argv[0]);
3443 static gint execute_sync(gchar *const argv[])
3447 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3449 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3450 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3451 g_warning("Couldn't execute command: %s\n", argv[0]);
3456 if (WIFEXITED(status))
3457 return WEXITSTATUS(status);
3465 gint execute_command_line(const gchar *cmdline, gboolean async)
3470 debug_print("execute_command_line(): executing: %s\n", cmdline);
3472 argv = strsplit_with_quote(cmdline, " ", 0);
3475 ret = execute_async(argv);
3477 ret = execute_sync(argv);
3484 gchar *get_command_output(const gchar *cmdline)
3486 gchar *child_stdout;
3489 g_return_val_if_fail(cmdline != NULL, NULL);
3491 debug_print("get_command_output(): executing: %s\n", cmdline);
3493 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3495 g_warning("Couldn't execute command: %s\n", cmdline);
3499 return child_stdout;
3502 static gint is_unchanged_uri_char(char c)
3513 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3519 for(i = 0; i < strlen(uri) ; i++) {
3520 if (is_unchanged_uri_char(uri[i])) {
3521 if (k + 2 >= bufsize)
3523 encoded_uri[k++] = uri[i];
3526 char * hexa = "0123456789ABCDEF";
3528 if (k + 4 >= bufsize)
3530 encoded_uri[k++] = '%';
3531 encoded_uri[k++] = hexa[uri[i] / 16];
3532 encoded_uri[k++] = hexa[uri[i] % 16];
3538 gint open_uri(const gchar *uri, const gchar *cmdline)
3541 gchar buf[BUFFSIZE];
3543 gchar encoded_uri[BUFFSIZE];
3544 g_return_val_if_fail(uri != NULL, -1);
3546 /* an option to choose whether to use encode_uri or not ? */
3547 encode_uri(encoded_uri, BUFFSIZE, uri);
3550 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3551 !strchr(p + 2, '%'))
3552 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3555 g_warning("Open URI command line is invalid "
3556 "(there must be only one '%%s'): %s",
3558 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3561 execute_command_line(buf, TRUE);
3563 extern osso_context_t *get_osso_context(void);
3564 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3565 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3566 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3571 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3573 gchar buf[BUFFSIZE];
3576 g_return_val_if_fail(filepath != NULL, -1);
3579 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3580 !strchr(p + 2, '%'))
3581 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3584 g_warning("Open Text Editor command line is invalid "
3585 "(there must be only one '%%s'): %s",
3587 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3590 execute_command_line(buf, TRUE);
3595 time_t remote_tzoffset_sec(const gchar *zone)
3597 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3603 time_t remoteoffset;
3605 strncpy(zone3, zone, 3);
3609 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3610 (c == '+' || c == '-')) {
3611 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3613 remoteoffset = -remoteoffset;
3614 } else if (!strncmp(zone, "UT" , 2) ||
3615 !strncmp(zone, "GMT", 2)) {
3617 } else if (strlen(zone3) == 3) {
3618 for (p = ustzstr; *p != '\0'; p += 3) {
3619 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3620 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3621 remoteoffset = iustz * 3600;
3627 } else if (strlen(zone3) == 1) {
3629 case 'Z': remoteoffset = 0; break;
3630 case 'A': remoteoffset = -1; break;
3631 case 'B': remoteoffset = -2; break;
3632 case 'C': remoteoffset = -3; break;
3633 case 'D': remoteoffset = -4; break;
3634 case 'E': remoteoffset = -5; break;
3635 case 'F': remoteoffset = -6; break;
3636 case 'G': remoteoffset = -7; break;
3637 case 'H': remoteoffset = -8; break;
3638 case 'I': remoteoffset = -9; break;
3639 case 'K': remoteoffset = -10; break; /* J is not used */
3640 case 'L': remoteoffset = -11; break;
3641 case 'M': remoteoffset = -12; break;
3642 case 'N': remoteoffset = 1; break;
3643 case 'O': remoteoffset = 2; break;
3644 case 'P': remoteoffset = 3; break;
3645 case 'Q': remoteoffset = 4; break;
3646 case 'R': remoteoffset = 5; break;
3647 case 'S': remoteoffset = 6; break;
3648 case 'T': remoteoffset = 7; break;
3649 case 'U': remoteoffset = 8; break;
3650 case 'V': remoteoffset = 9; break;
3651 case 'W': remoteoffset = 10; break;
3652 case 'X': remoteoffset = 11; break;
3653 case 'Y': remoteoffset = 12; break;
3654 default: remoteoffset = 0; break;
3656 remoteoffset = remoteoffset * 3600;
3660 return remoteoffset;
3663 time_t tzoffset_sec(time_t *now)
3667 struct tm buf1, buf2;
3669 gmt = *gmtime_r(now, &buf1);
3670 lt = localtime_r(now, &buf2);
3672 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3674 if (lt->tm_year < gmt.tm_year)
3676 else if (lt->tm_year > gmt.tm_year)
3678 else if (lt->tm_yday < gmt.tm_yday)
3680 else if (lt->tm_yday > gmt.tm_yday)
3683 if (off >= 24 * 60) /* should be impossible */
3684 off = 23 * 60 + 59; /* if not, insert silly value */
3685 if (off <= -24 * 60)
3686 off = -(23 * 60 + 59);
3691 /* calculate timezone offset */
3692 gchar *tzoffset(time_t *now)
3694 static gchar offset_string[6];
3698 struct tm buf1, buf2;
3700 gmt = *gmtime_r(now, &buf1);
3701 lt = localtime_r(now, &buf2);
3703 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3705 if (lt->tm_year < gmt.tm_year)
3707 else if (lt->tm_year > gmt.tm_year)
3709 else if (lt->tm_yday < gmt.tm_yday)
3711 else if (lt->tm_yday > gmt.tm_yday)
3719 if (off >= 24 * 60) /* should be impossible */
3720 off = 23 * 60 + 59; /* if not, insert silly value */
3722 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3724 return offset_string;
3727 void get_rfc822_date(gchar *buf, gint len)
3731 gchar day[4], mon[4];
3732 gint dd, hh, mm, ss, yyyy;
3734 gchar buf2[BUFFSIZE];
3737 lt = localtime_r(&t, &buf1);
3739 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3740 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3741 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3742 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3745 /* just a wrapper to suppress the warning of gcc about %c */
3746 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3747 const struct tm *tm)
3749 return strftime(s, max, format, tm);
3752 void debug_set_mode(gboolean mode)
3757 gboolean debug_get_mode(void)
3762 void debug_print_real(const gchar *format, ...)
3765 gchar buf[BUFFSIZE];
3767 if (!debug_mode) return;
3769 va_start(args, format);
3770 g_vsnprintf(buf, sizeof(buf), format, args);
3777 const char * debug_srcname(const char *file)
3779 const char *s = strrchr (file, '/');
3784 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3786 if (subject == NULL)
3789 subject += subject_get_prefix_length(subject);
3791 return g_hash_table_lookup(subject_table, subject);
3794 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3797 if (subject == NULL || *subject == 0)
3799 subject += subject_get_prefix_length(subject);
3800 g_hash_table_insert(subject_table, subject, data);
3803 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3805 if (subject == NULL)
3808 subject += subject_get_prefix_length(subject);
3809 g_hash_table_remove(subject_table, subject);
3813 *\brief Check if a string is prefixed with known (combinations)
3814 * of prefixes. The function assumes that each prefix
3815 * is terminated by zero or exactly _one_ space.
3817 *\param str String to check for a prefixes
3819 *\return int Number of chars in the prefix that should be skipped
3820 * for a "clean" subject line. If no prefix was found, 0
3823 int subject_get_prefix_length(const gchar *subject)
3825 /*!< Array with allowable reply prefixes regexps. */
3826 static const gchar * const prefixes[] = {
3827 "Re\\:", /* "Re:" */
3828 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3829 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3830 "Aw\\:", /* "Aw:" (German) */
3831 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3832 "Res\\:", /* "Res:" (Brazilian Outlook) */
3833 "Fw\\:", /* "Fw:" Forward */
3834 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3835 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3836 "Rif\\:", /* "Rif:" (Italian Outlook) */
3837 "Sv\\:", /* "Sv" (Norwegian) */
3838 "Vs\\:", /* "Vs" (Norwegian) */
3839 "Ad\\:", /* "Ad" (Norwegian) */
3840 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3843 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3846 static regex_t regex;
3847 static gboolean init_;
3849 if (!subject) return 0;
3850 if (!*subject) return 0;
3853 GString *s = g_string_new("");
3855 for (n = 0; n < PREFIXES; n++)
3856 /* Terminate each prefix regexpression by a
3857 * "\ ?" (zero or ONE space), and OR them */
3858 g_string_append_printf(s, "(%s\\ ?)%s",
3863 g_string_prepend(s, "(");
3864 g_string_append(s, ")+"); /* match at least once */
3865 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3868 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3869 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3870 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3871 debug_print("Error compiling regexp %s\n", s->str);
3872 g_string_free(s, TRUE);
3876 g_string_free(s, TRUE);
3880 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3886 guint g_stricase_hash(gconstpointer gptr)
3888 guint hash_result = 0;
3891 for (str = gptr; str && *str; str++) {
3892 hash_result += toupper(*str);
3898 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3900 const char *str1 = gptr1;
3901 const char *str2 = gptr2;
3903 return !strcasecmp(str1, str2);
3906 gint g_int_compare(gconstpointer a, gconstpointer b)
3908 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3911 gchar *generate_msgid(gchar *buf, gint len)
3919 lt = localtime_r(&t, &buft);
3921 addr = g_strconcat("@", get_domain_name(), NULL);
3923 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3924 lt->tm_year + 1900, lt->tm_mon + 1,
3925 lt->tm_mday, lt->tm_hour,
3926 lt->tm_min, lt->tm_sec,
3927 (guint) rand(), addr);
3934 quote_cmd_argument()
3936 return a quoted string safely usable in argument of a command.
3938 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3941 gint quote_cmd_argument(gchar * result, guint size,
3951 for(p = path ; * p != '\0' ; p ++) {
3953 if (isalnum((guchar)*p) || (* p == '/')) {
3954 if (remaining > 0) {
3960 result[size - 1] = '\0';
3965 if (remaining >= 2) {
3973 result[size - 1] = '\0';
3978 if (remaining > 0) {
3982 result[size - 1] = '\0';
3996 static void g_node_map_recursive(GNode *node, gpointer data)
3998 GNodeMapData *mapdata = (GNodeMapData *) data;
4000 GNodeMapData newmapdata;
4003 newdata = mapdata->func(node->data, mapdata->data);
4004 if (newdata != NULL) {
4005 newnode = g_node_new(newdata);
4006 g_node_append(mapdata->parent, newnode);
4008 newmapdata.parent = newnode;
4009 newmapdata.func = mapdata->func;
4010 newmapdata.data = mapdata->data;
4012 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4016 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4019 GNodeMapData mapdata;
4021 g_return_val_if_fail(node != NULL, NULL);
4022 g_return_val_if_fail(func != NULL, NULL);
4024 root = g_node_new(func(node->data, data));
4026 mapdata.parent = root;
4027 mapdata.func = func;
4028 mapdata.data = data;
4030 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4035 #define HEX_TO_INT(val, hex) \
4039 if ('0' <= c && c <= '9') { \
4041 } else if ('a' <= c && c <= 'f') { \
4042 val = c - 'a' + 10; \
4043 } else if ('A' <= c && c <= 'F') { \
4044 val = c - 'A' + 10; \
4050 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4057 if (hi == -1 || lo == -1)
4060 *out = (hi << 4) + lo;
4064 #define INT_TO_HEX(hex, val) \
4067 hex = '0' + (val); \
4069 hex = 'A' + (val) - 10; \
4072 void get_hex_str(gchar *out, guchar ch)
4076 INT_TO_HEX(hex, ch >> 4);
4078 INT_TO_HEX(hex, ch & 0x0f);
4084 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4086 #define G_PRINT_REF g_print
4090 *\brief Register ref counted pointer. It is based on GBoxed, so should
4091 * work with anything that uses the GType system. The semantics
4092 * are similar to a C++ auto pointer, with the exception that
4093 * C doesn't have automatic closure (calling destructors) when
4094 * exiting a block scope.
4095 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4096 * function directly.
4098 *\return GType A GType type.
4100 GType g_auto_pointer_register(void)
4102 static GType auto_pointer_type;
4103 if (!auto_pointer_type)
4105 g_boxed_type_register_static
4106 ("G_TYPE_AUTO_POINTER",
4107 (GBoxedCopyFunc) g_auto_pointer_copy,
4108 (GBoxedFreeFunc) g_auto_pointer_free);
4109 return auto_pointer_type;
4113 *\brief Structure with g_new() allocated pointer guarded by the
4116 typedef struct AutoPointerRef {
4117 void (*free) (gpointer);
4123 *\brief The auto pointer opaque structure that references the
4124 * pointer guard block.
4126 typedef struct AutoPointer {
4127 AutoPointerRef *ref;
4128 gpointer ptr; /*!< access to protected pointer */
4132 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4136 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4137 * ... when assigning, copying and freeing storage elements
4139 * gtk_list_store_new(N_S_COLUMNS,
4140 * G_TYPE_AUTO_POINTER,
4144 * Template *precious_data = g_new0(Template, 1);
4145 * g_pointer protect = g_auto_pointer_new(precious_data);
4147 * gtk_list_store_set(container, &iter,
4151 * ... the gtk_list_store has copied the pointer and
4152 * ... incremented its reference count, we should free
4153 * ... the auto pointer (in C++ a destructor would do
4154 * ... this for us when leaving block scope)
4156 * g_auto_pointer_free(protect);
4158 * ... gtk_list_store_set() now manages the data. When
4159 * ... *explicitly* requesting a pointer from the list
4160 * ... store, don't forget you get a copy that should be
4161 * ... freed with g_auto_pointer_free() eventually.
4165 *\param pointer Pointer to be guarded.
4167 *\return GAuto * Pointer that should be used in containers with
4170 GAuto *g_auto_pointer_new(gpointer p)
4172 AutoPointerRef *ref;
4178 ref = g_new0(AutoPointerRef, 1);
4179 ptr = g_new0(AutoPointer, 1);
4189 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4195 *\brief Allocate an autopointer using the passed \a free function to
4196 * free the guarded pointer
4198 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4205 aptr = g_auto_pointer_new(p);
4206 aptr->ref->free = free_;
4210 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4212 if (auto_ptr == NULL)
4214 return ((AutoPointer *) auto_ptr)->ptr;
4218 *\brief Copies an auto pointer by. It's mostly not necessary
4219 * to call this function directly, unless you copy/assign
4220 * the guarded pointer.
4222 *\param auto_ptr Auto pointer returned by previous call to
4223 * g_auto_pointer_new_XXX()
4225 *\return gpointer An auto pointer
4227 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4230 AutoPointerRef *ref;
4233 if (auto_ptr == NULL)
4238 newp = g_new0(AutoPointer, 1);
4241 newp->ptr = ref->pointer;
4245 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4251 *\brief Free an auto pointer
4253 void g_auto_pointer_free(GAuto *auto_ptr)
4256 AutoPointerRef *ref;
4258 if (auto_ptr == NULL)
4264 if (--(ref->cnt) == 0) {
4266 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4268 ref->free(ref->pointer);
4273 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4278 void replace_returns(gchar *str)
4283 while (strstr(str, "\n")) {
4284 *strstr(str, "\n") = ' ';
4286 while (strstr(str, "\r")) {
4287 *strstr(str, "\r") = ' ';
4291 /* get_uri_part() - retrieves a URI starting from scanpos.
4292 Returns TRUE if succesful */
4293 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4294 const gchar **bp, const gchar **ep, gboolean hdr)
4297 gint parenthese_cnt = 0;
4299 g_return_val_if_fail(start != NULL, FALSE);
4300 g_return_val_if_fail(scanpos != NULL, FALSE);
4301 g_return_val_if_fail(bp != NULL, FALSE);
4302 g_return_val_if_fail(ep != NULL, FALSE);
4306 /* find end point of URI */
4307 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4308 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4309 !IS_ASCII(*(const guchar *)ep_) ||
4310 strchr("[]{}<>\"", *ep_)) {
4312 } else if (strchr("(", *ep_)) {
4314 } else if (strchr(")", *ep_)) {
4315 if (parenthese_cnt > 0)
4322 /* no punctuation at end of string */
4324 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4325 * should pass some URI type to this function and decide on that whether
4326 * to perform punctuation stripping */
4328 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4330 for (; ep_ - 1 > scanpos + 1 &&
4331 IS_REAL_PUNCT(*(ep_ - 1));
4335 #undef IS_REAL_PUNCT
4342 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4344 while (bp && *bp && g_ascii_isspace(*bp))
4346 return g_strndup(bp, ep - bp);
4349 /* valid mail address characters */
4350 #define IS_RFC822_CHAR(ch) \
4354 !g_ascii_isspace(ch) && \
4355 !strchr("(),;<>\"", (ch)))
4357 /* alphabet and number within 7bit ASCII */
4358 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4359 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4361 static GHashTable *create_domain_tab(void)
4363 static const gchar *toplvl_domains [] = {
4365 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4366 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4367 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4368 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4369 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4370 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4371 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4372 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4373 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4374 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4375 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4376 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4377 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4378 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4379 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4380 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4381 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4382 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4383 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4384 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4385 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4386 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4387 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4388 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4389 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4390 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4393 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4395 g_return_val_if_fail(htab, NULL);
4396 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4397 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4401 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4403 const gint MAX_LVL_DOM_NAME_LEN = 6;
4404 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4405 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4408 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4411 for (p = buf; p < m && first < last; *p++ = *first++)
4415 return g_hash_table_lookup(tab, buf) != NULL;
4418 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4419 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4420 const gchar **bp, const gchar **ep, gboolean hdr)
4422 /* more complex than the uri part because we need to scan back and forward starting from
4423 * the scan position. */
4424 gboolean result = FALSE;
4425 const gchar *bp_ = NULL;
4426 const gchar *ep_ = NULL;
4427 static GHashTable *dom_tab;
4428 const gchar *last_dot = NULL;
4429 const gchar *prelast_dot = NULL;
4430 const gchar *last_tld_char = NULL;
4432 /* the informative part of the email address (describing the name
4433 * of the email address owner) may contain quoted parts. the
4434 * closure stack stores the last encountered quotes. */
4435 gchar closure_stack[128];
4436 gchar *ptr = closure_stack;
4438 g_return_val_if_fail(start != NULL, FALSE);
4439 g_return_val_if_fail(scanpos != NULL, FALSE);
4440 g_return_val_if_fail(bp != NULL, FALSE);
4441 g_return_val_if_fail(ep != NULL, FALSE);
4444 const gchar *start_quote = NULL;
4445 const gchar *end_quote = NULL;
4447 /* go to the real start */
4448 if (start[0] == ',')
4450 if (start[0] == ';')
4452 while (start[0] == '\n' || start[0] == '\r')
4454 while (start[0] == ' ' || start[0] == '\t')
4459 /* check if there are quotes (to skip , in them) */
4460 if (*start == '"') {
4461 start_quote = start;
4463 end_quote = strstr(start, "\"");
4469 /* skip anything between quotes */
4470 if (start_quote && end_quote) {
4475 /* find end (either , or ; or end of line) */
4476 if (strstr(start, ",") && strstr(start, ";"))
4477 *ep = strstr(start,",") < strstr(start, ";")
4478 ? strstr(start, ",") : strstr(start, ";");
4479 else if (strstr(start, ","))
4480 *ep = strstr(start, ",");
4481 else if (strstr(start, ";"))
4482 *ep = strstr(start, ";");
4484 *ep = start+strlen(start);
4486 /* go back to real start */
4487 if (start_quote && end_quote) {
4488 start = start_quote;
4491 /* check there's still an @ in that, or search
4492 * further if possible */
4493 if (strstr(start, "@") && strstr(start, "@") < *ep)
4495 else if (*ep < start+strlen(start)) {
4498 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4506 dom_tab = create_domain_tab();
4507 g_return_val_if_fail(dom_tab, FALSE);
4509 /* scan start of address */
4510 for (bp_ = scanpos - 1;
4511 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4514 /* TODO: should start with an alnum? */
4516 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4519 if (bp_ != scanpos) {
4520 /* scan end of address */
4521 for (ep_ = scanpos + 1;
4522 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4524 prelast_dot = last_dot;
4526 if (*(last_dot + 1) == '.') {
4527 if (prelast_dot == NULL)
4529 last_dot = prelast_dot;
4534 /* TODO: really should terminate with an alnum? */
4535 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4540 if (last_dot == NULL)
4542 if (last_dot >= ep_)
4543 last_dot = prelast_dot;
4544 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4548 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4549 if (*last_tld_char == '?')
4552 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4559 if (!result) return FALSE;
4561 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4562 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4563 && IS_RFC822_CHAR(*(ep_ + 3))) {
4564 /* this informative part with an @ in it is
4565 * followed by the email address */
4568 /* go to matching '>' (or next non-rfc822 char, like \n) */
4569 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4572 /* include the bracket */
4573 if (*ep_ == '>') ep_++;
4575 /* include the leading quote */
4583 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4584 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4587 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4588 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4591 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4592 #define IN_STACK() (ptr > closure_stack)
4593 /* has underrun check */
4594 #define POP_STACK() if(IN_STACK()) --ptr
4595 /* has overrun check */
4596 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4597 /* has underrun check */
4598 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4602 /* scan for the informative part. */
4603 for (bp_ -= 2; bp_ >= start; bp_--) {
4604 /* if closure on the stack keep scanning */
4605 if (PEEK_STACK() == *bp_) {
4609 if (*bp_ == '\'' || *bp_ == '"') {
4614 /* if nothing in the closure stack, do the special conditions
4615 * the following if..else expression simply checks whether
4616 * a token is acceptable. if not acceptable, the clause
4617 * should terminate the loop with a 'break' */
4618 if (!PEEK_STACK()) {
4620 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4621 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4622 /* hyphens are allowed, but only in
4624 } else if (strchr(" \"'", *bp_)) {
4625 /* but anything not being a punctiation
4628 break; /* anything else is rejected */
4635 /* scan forward (should start with an alnum) */
4636 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4652 #undef IS_ASCII_ALNUM
4653 #undef IS_RFC822_CHAR
4655 gchar *make_email_string(const gchar *bp, const gchar *ep)
4657 /* returns a mailto: URI; mailto: is also used to detect the
4658 * uri type later on in the button_pressed signal handler */
4662 tmp = g_strndup(bp, ep - bp);
4663 result = g_strconcat("mailto:", tmp, NULL);
4669 gchar *make_http_string(const gchar *bp, const gchar *ep)
4671 /* returns an http: URI; */
4675 while (bp && *bp && g_ascii_isspace(*bp))
4677 tmp = g_strndup(bp, ep - bp);
4678 result = g_strconcat("http://", tmp, NULL);
4684 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4686 FILE *fp = fopen(path, "rb");
4687 gchar buf[BUFFSIZE];
4688 gchar *result = NULL;
4691 while (fgets(buf, sizeof (buf), fp) != NULL) {
4692 gchar **parts = g_strsplit(buf, ";", 3);
4693 gchar *trimmed = parts[0];
4694 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4696 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4697 trimmed[strlen(trimmed)-1] = '\0';
4699 if (!strcmp(trimmed, type)) {
4700 gboolean needsterminal = FALSE;
4701 if (parts[2] && strstr(parts[2], "needsterminal")) {
4702 needsterminal = TRUE;
4704 if (parts[2] && strstr(parts[2], "test=")) {
4705 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4706 gchar *testcmd = orig_testcmd;
4707 if (strstr(testcmd,";"))
4708 *(strstr(testcmd,";")) = '\0';
4709 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4711 while (testcmd[strlen(testcmd)-1] == '\n')
4712 testcmd[strlen(testcmd)-1] = '\0';
4713 while (testcmd[strlen(testcmd)-1] == '\r')
4714 testcmd[strlen(testcmd)-1] = '\0';
4715 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4716 testcmd[strlen(testcmd)-1] = '\0';
4718 if (strstr(testcmd, "%s")) {
4719 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4720 gint res = system(tmp);
4722 g_free(orig_testcmd);
4729 gint res = system(testcmd);
4730 g_free(orig_testcmd);
4740 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4742 while (trimmed[strlen(trimmed)-1] == '\n')
4743 trimmed[strlen(trimmed)-1] = '\0';
4744 while (trimmed[strlen(trimmed)-1] == '\r')
4745 trimmed[strlen(trimmed)-1] = '\0';
4746 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4747 trimmed[strlen(trimmed)-1] = '\0';
4748 result = g_strdup(trimmed);
4751 /* if there are no single quotes around %s, add them.
4752 * '.*%s.*' is ok, as in display 'png:%s'
4754 if (strstr(result, "%s")
4755 && !(strstr(result, "'") < strstr(result,"%s") &&
4756 strstr(strstr(result,"%s"), "'"))) {
4757 gchar *start = g_strdup(result);
4758 gchar *end = g_strdup(strstr(result, "%s")+2);
4760 *strstr(start, "%s") = '\0';
4761 tmp = g_strconcat(start,"'%s'",end, NULL);
4767 if (needsterminal) {
4768 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4779 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4781 gchar *result = NULL;
4785 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4786 result = mailcap_get_command_in_file(path, type, file_to_open);
4790 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4794 void mailcap_update_default(const gchar *type, const gchar *command)
4796 gchar *path = NULL, *outpath = NULL;
4797 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4798 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4799 FILE *fp = fopen(path, "rb");
4800 FILE *outfp = fopen(outpath, "wb");
4801 gchar buf[BUFFSIZE];
4809 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4810 gchar **parts = g_strsplit(buf, ";", 3);
4811 gchar *trimmed = parts[0];
4812 while (trimmed[0] == ' ')
4814 while (trimmed[strlen(trimmed)-1] == ' ')
4815 trimmed[strlen(trimmed)-1] = '\0';
4817 if (!strcmp(trimmed, type)) {
4826 fprintf(outfp, "%s; %s\n", type, command);
4832 g_rename(outpath, path);
4835 gint copy_dir(const gchar *src, const gchar *dst)
4840 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4841 g_warning("failed to open directory: %s\n", src);
4845 if (make_dir(dst) < 0)
4848 while ((name = g_dir_read_name(dir)) != NULL) {
4849 gchar *old_file, *new_file;
4850 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4851 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4852 debug_print("copying: %s -> %s\n", old_file, new_file);
4853 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4854 gint r = copy_file(old_file, new_file, TRUE);
4861 /* Windows has no symlinks. Or well, Vista seems to
4862 have something like this but the semantics might be
4863 different. Thus we don't use it under Windows. */
4864 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4867 gchar *target = g_file_read_link(old_file, &error);
4869 r = symlink(target, new_file);
4876 #endif /*G_OS_WIN32*/
4877 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4878 gint r = copy_dir(old_file, new_file);
4889 /* crude test to see if a file is an email. */
4890 gboolean file_is_email (const gchar *filename)
4896 if (filename == NULL)
4898 if ((fp = g_fopen(filename, "rb")) == NULL)
4900 while (i < 60 && score < 3
4901 && fgets(buffer, sizeof (buffer), fp) > 0) {
4902 if (!strncmp(buffer, "From:", strlen("From:")))
4904 if (!strncmp(buffer, "To:", strlen("To:")))
4906 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4911 return (score >= 3);
4914 gboolean sc_g_list_bigger(GList *list, gint max)
4918 while (cur && i <= max+1) {
4925 gboolean sc_g_slist_bigger(GSList *list, gint max)
4929 while (cur && i <= max+1) {
4936 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4937 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4938 NULL, NULL, NULL, NULL, NULL, NULL};
4939 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4940 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4941 NULL, NULL, NULL, NULL, NULL, NULL};
4942 const gchar *s_am_up = NULL;
4943 const gchar *s_pm_up = NULL;
4944 const gchar *s_am_low = NULL;
4945 const gchar *s_pm_low = NULL;
4946 const gchar *def_loc_format = NULL;
4947 const gchar *date_loc_format = NULL;
4948 const gchar *time_loc_format = NULL;
4949 const gchar *time_am_pm = NULL;
4951 static gboolean time_names_init_done = FALSE;
4953 static void init_time_names(void)
4955 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4956 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4957 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4958 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4959 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4960 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4961 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4963 monthnames[0] = Q_("Complete month name for use by strftime|January");
4964 monthnames[1] = Q_("Complete month name for use by strftime|February");
4965 monthnames[2] = Q_("Complete month name for use by strftime|March");
4966 monthnames[3] = Q_("Complete month name for use by strftime|April");
4967 monthnames[4] = Q_("Complete month name for use by strftime|May");
4968 monthnames[5] = Q_("Complete month name for use by strftime|June");
4969 monthnames[6] = Q_("Complete month name for use by strftime|July");
4970 monthnames[7] = Q_("Complete month name for use by strftime|August");
4971 monthnames[8] = Q_("Complete month name for use by strftime|September");
4972 monthnames[9] = Q_("Complete month name for use by strftime|October");
4973 monthnames[10] = Q_("Complete month name for use by strftime|November");
4974 monthnames[11] = Q_("Complete month name for use by strftime|December");
4976 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4977 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4978 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4979 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4980 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4981 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4982 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4984 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4985 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4986 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4987 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4988 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4989 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4990 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4991 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4992 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4993 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4994 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4995 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4997 s_am_up = Q_("For use by strftime (morning)|AM");
4998 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4999 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5000 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5002 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5003 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5004 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5006 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5008 time_names_init_done = TRUE;
5011 #define CHECK_SIZE() { \
5012 total_done += len; \
5013 if (total_done >= buflen) { \
5014 buf[buflen-1] = '\0'; \
5019 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5021 gchar *curpos = buf;
5022 gint total_done = 0;
5023 gchar subbuf[64], subfmt[64];
5024 static time_t last_tzset = (time_t)0;
5026 if (!time_names_init_done)
5029 if (format == NULL || lt == NULL)
5032 if (last_tzset != time(NULL)) {
5034 last_tzset = time(NULL);
5037 if (*format == '%') {
5038 gint len = 0, tmp = 0;
5042 len = 1; CHECK_SIZE();
5046 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5047 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5050 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5051 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5055 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5056 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5059 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5060 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5063 fast_strftime(subbuf, 64, def_loc_format, lt);
5064 len = strlen(subbuf); CHECK_SIZE();
5065 strncpy2(curpos, subbuf, buflen - total_done);
5068 total_done += 2; CHECK_SIZE();
5069 tmp = (lt->tm_year + 1900)/100;
5070 *curpos++ = '0'+(tmp / 10);
5071 *curpos++ = '0'+(tmp % 10);
5074 total_done += 2; CHECK_SIZE();
5075 *curpos++ = '0'+(lt->tm_mday / 10);
5076 *curpos++ = '0'+(lt->tm_mday % 10);
5079 total_done += 8; CHECK_SIZE();
5080 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5081 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5083 *curpos++ = '0'+(lt->tm_mday / 10);
5084 *curpos++ = '0'+(lt->tm_mday % 10);
5086 tmp = lt->tm_year%100;
5087 *curpos++ = '0'+(tmp / 10);
5088 *curpos++ = '0'+(tmp % 10);
5091 len = 2; CHECK_SIZE();
5092 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5095 len = 10; CHECK_SIZE();
5096 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5097 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5100 total_done += 2; CHECK_SIZE();
5101 *curpos++ = '0'+(lt->tm_hour / 10);
5102 *curpos++ = '0'+(lt->tm_hour % 10);
5105 total_done += 2; CHECK_SIZE();
5111 *curpos++ = '0'+(tmp / 10);
5112 *curpos++ = '0'+(tmp % 10);
5115 len = 3; CHECK_SIZE();
5116 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5119 len = 2; CHECK_SIZE();
5120 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5123 len = 2; CHECK_SIZE();
5129 snprintf(curpos, buflen - total_done, "%2d", tmp);
5132 total_done += 2; CHECK_SIZE();
5133 tmp = lt->tm_mon + 1;
5134 *curpos++ = '0'+(tmp / 10);
5135 *curpos++ = '0'+(tmp % 10);
5138 total_done += 2; CHECK_SIZE();
5139 *curpos++ = '0'+(lt->tm_min / 10);
5140 *curpos++ = '0'+(lt->tm_min % 10);
5143 len = 1; CHECK_SIZE();
5147 if (lt->tm_hour >= 12) {
5148 len = strlen(s_pm_up); CHECK_SIZE();
5149 snprintf(curpos, buflen-total_done, s_pm_up);
5151 len = strlen(s_am_up); CHECK_SIZE();
5152 snprintf(curpos, buflen-total_done, s_am_up);
5156 if (lt->tm_hour >= 12) {
5157 len = strlen(s_pm_low); CHECK_SIZE();
5158 snprintf(curpos, buflen-total_done, s_pm_low);
5160 len = strlen(s_am_low); CHECK_SIZE();
5161 snprintf(curpos, buflen-total_done, s_am_low);
5165 fast_strftime(subbuf, 64, time_am_pm, lt);
5166 len = strlen(subbuf); CHECK_SIZE();
5167 strncpy2(curpos, subbuf, buflen - total_done);
5170 total_done += 5; CHECK_SIZE();
5171 *curpos++ = '0'+(lt->tm_hour / 10);
5172 *curpos++ = '0'+(lt->tm_hour % 10);
5174 *curpos++ = '0'+(lt->tm_min / 10);
5175 *curpos++ = '0'+(lt->tm_min % 10);
5178 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5179 len = strlen(subbuf); CHECK_SIZE();
5180 strncpy2(curpos, subbuf, buflen - total_done);
5183 total_done += 2; CHECK_SIZE();
5184 *curpos++ = '0'+(lt->tm_sec / 10);
5185 *curpos++ = '0'+(lt->tm_sec % 10);
5188 len = 1; CHECK_SIZE();
5192 total_done += 8; CHECK_SIZE();
5193 *curpos++ = '0'+(lt->tm_hour / 10);
5194 *curpos++ = '0'+(lt->tm_hour % 10);
5196 *curpos++ = '0'+(lt->tm_min / 10);
5197 *curpos++ = '0'+(lt->tm_min % 10);
5199 *curpos++ = '0'+(lt->tm_sec / 10);
5200 *curpos++ = '0'+(lt->tm_sec % 10);
5203 len = 1; CHECK_SIZE();
5204 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5207 len = 1; CHECK_SIZE();
5208 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5211 fast_strftime(subbuf, 64, date_loc_format, lt);
5212 len = strlen(subbuf); CHECK_SIZE();
5213 strncpy2(curpos, subbuf, buflen - total_done);
5216 fast_strftime(subbuf, 64, time_loc_format, lt);
5217 len = strlen(subbuf); CHECK_SIZE();
5218 strncpy2(curpos, subbuf, buflen - total_done);
5221 total_done += 2; CHECK_SIZE();
5222 tmp = lt->tm_year%100;
5223 *curpos++ = '0'+(tmp / 10);
5224 *curpos++ = '0'+(tmp % 10);
5227 len = 4; CHECK_SIZE();
5228 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5238 /* let these complicated ones be done with the libc */
5239 snprintf(subfmt, 64, "%%%c", *format);
5240 strftime(subbuf, 64, subfmt, lt);
5241 len = strlen(subbuf); CHECK_SIZE();
5242 strncpy2(curpos, subbuf, buflen - total_done);
5246 /* let these complicated modifiers be done with the libc */
5247 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5248 strftime(subbuf, 64, subfmt, lt);
5249 len = strlen(subbuf); CHECK_SIZE();
5250 strncpy2(curpos, subbuf, buflen - total_done);
5254 g_warning("format error (%c)", *format);
5261 int len = 1; CHECK_SIZE();
5262 *curpos++ = *format++;