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 g_print("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?cmdline:"(null)");
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);
3740 sscanf(asctime_r(lt, buf2, sizeof(buf2)), "%3s %3s %d %d:%d:%d %d\n",
3741 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3743 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3744 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3746 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3747 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3750 /* just a wrapper to suppress the warning of gcc about %c */
3751 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3752 const struct tm *tm)
3754 return strftime(s, max, format, tm);
3757 void debug_set_mode(gboolean mode)
3762 gboolean debug_get_mode(void)
3767 void debug_print_real(const gchar *format, ...)
3770 gchar buf[BUFFSIZE];
3772 if (!debug_mode) return;
3774 va_start(args, format);
3775 g_vsnprintf(buf, sizeof(buf), format, args);
3782 const char * debug_srcname(const char *file)
3784 const char *s = strrchr (file, '/');
3789 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3791 if (subject == NULL)
3794 subject += subject_get_prefix_length(subject);
3796 return g_hash_table_lookup(subject_table, subject);
3799 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3802 if (subject == NULL || *subject == 0)
3804 subject += subject_get_prefix_length(subject);
3805 g_hash_table_insert(subject_table, subject, data);
3808 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3810 if (subject == NULL)
3813 subject += subject_get_prefix_length(subject);
3814 g_hash_table_remove(subject_table, subject);
3818 *\brief Check if a string is prefixed with known (combinations)
3819 * of prefixes. The function assumes that each prefix
3820 * is terminated by zero or exactly _one_ space.
3822 *\param str String to check for a prefixes
3824 *\return int Number of chars in the prefix that should be skipped
3825 * for a "clean" subject line. If no prefix was found, 0
3828 int subject_get_prefix_length(const gchar *subject)
3830 /*!< Array with allowable reply prefixes regexps. */
3831 static const gchar * const prefixes[] = {
3832 "Re\\:", /* "Re:" */
3833 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3834 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3835 "Aw\\:", /* "Aw:" (German) */
3836 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3837 "Res\\:", /* "Res:" (Brazilian Outlook) */
3838 "Fw\\:", /* "Fw:" Forward */
3839 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3840 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3841 "Rif\\:", /* "Rif:" (Italian Outlook) */
3842 "Sv\\:", /* "Sv" (Norwegian) */
3843 "Vs\\:", /* "Vs" (Norwegian) */
3844 "Ad\\:", /* "Ad" (Norwegian) */
3845 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3848 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3851 static regex_t regex;
3852 static gboolean init_;
3854 if (!subject) return 0;
3855 if (!*subject) return 0;
3858 GString *s = g_string_new("");
3860 for (n = 0; n < PREFIXES; n++)
3861 /* Terminate each prefix regexpression by a
3862 * "\ ?" (zero or ONE space), and OR them */
3863 g_string_append_printf(s, "(%s\\ ?)%s",
3868 g_string_prepend(s, "(");
3869 g_string_append(s, ")+"); /* match at least once */
3870 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3873 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3874 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3875 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3876 debug_print("Error compiling regexp %s\n", s->str);
3877 g_string_free(s, TRUE);
3881 g_string_free(s, TRUE);
3885 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3891 guint g_stricase_hash(gconstpointer gptr)
3893 guint hash_result = 0;
3896 for (str = gptr; str && *str; str++) {
3897 hash_result += toupper(*str);
3903 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3905 const char *str1 = gptr1;
3906 const char *str2 = gptr2;
3908 return !strcasecmp(str1, str2);
3911 gint g_int_compare(gconstpointer a, gconstpointer b)
3913 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3916 gchar *generate_msgid(gchar *buf, gint len)
3924 lt = localtime_r(&t, &buft);
3926 if (strcmp(buf, "") == 0) {
3927 addr = g_strconcat("@", get_domain_name(), NULL);
3930 addr = g_strconcat("@", buf, NULL);
3933 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3934 lt->tm_year + 1900, lt->tm_mon + 1,
3935 lt->tm_mday, lt->tm_hour,
3936 lt->tm_min, lt->tm_sec,
3937 (guint) rand(), addr);
3944 quote_cmd_argument()
3946 return a quoted string safely usable in argument of a command.
3948 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3951 gint quote_cmd_argument(gchar * result, guint size,
3961 for(p = path ; * p != '\0' ; p ++) {
3963 if (isalnum((guchar)*p) || (* p == '/')) {
3964 if (remaining > 0) {
3970 result[size - 1] = '\0';
3975 if (remaining >= 2) {
3983 result[size - 1] = '\0';
3988 if (remaining > 0) {
3992 result[size - 1] = '\0';
4006 static void g_node_map_recursive(GNode *node, gpointer data)
4008 GNodeMapData *mapdata = (GNodeMapData *) data;
4010 GNodeMapData newmapdata;
4013 newdata = mapdata->func(node->data, mapdata->data);
4014 if (newdata != NULL) {
4015 newnode = g_node_new(newdata);
4016 g_node_append(mapdata->parent, newnode);
4018 newmapdata.parent = newnode;
4019 newmapdata.func = mapdata->func;
4020 newmapdata.data = mapdata->data;
4022 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4026 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4029 GNodeMapData mapdata;
4031 g_return_val_if_fail(node != NULL, NULL);
4032 g_return_val_if_fail(func != NULL, NULL);
4034 root = g_node_new(func(node->data, data));
4036 mapdata.parent = root;
4037 mapdata.func = func;
4038 mapdata.data = data;
4040 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4045 #define HEX_TO_INT(val, hex) \
4049 if ('0' <= c && c <= '9') { \
4051 } else if ('a' <= c && c <= 'f') { \
4052 val = c - 'a' + 10; \
4053 } else if ('A' <= c && c <= 'F') { \
4054 val = c - 'A' + 10; \
4060 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4067 if (hi == -1 || lo == -1)
4070 *out = (hi << 4) + lo;
4074 #define INT_TO_HEX(hex, val) \
4077 hex = '0' + (val); \
4079 hex = 'A' + (val) - 10; \
4082 void get_hex_str(gchar *out, guchar ch)
4086 INT_TO_HEX(hex, ch >> 4);
4088 INT_TO_HEX(hex, ch & 0x0f);
4094 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4096 #define G_PRINT_REF g_print
4100 *\brief Register ref counted pointer. It is based on GBoxed, so should
4101 * work with anything that uses the GType system. The semantics
4102 * are similar to a C++ auto pointer, with the exception that
4103 * C doesn't have automatic closure (calling destructors) when
4104 * exiting a block scope.
4105 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4106 * function directly.
4108 *\return GType A GType type.
4110 GType g_auto_pointer_register(void)
4112 static GType auto_pointer_type;
4113 if (!auto_pointer_type)
4115 g_boxed_type_register_static
4116 ("G_TYPE_AUTO_POINTER",
4117 (GBoxedCopyFunc) g_auto_pointer_copy,
4118 (GBoxedFreeFunc) g_auto_pointer_free);
4119 return auto_pointer_type;
4123 *\brief Structure with g_new() allocated pointer guarded by the
4126 typedef struct AutoPointerRef {
4127 void (*free) (gpointer);
4133 *\brief The auto pointer opaque structure that references the
4134 * pointer guard block.
4136 typedef struct AutoPointer {
4137 AutoPointerRef *ref;
4138 gpointer ptr; /*!< access to protected pointer */
4142 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4146 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4147 * ... when assigning, copying and freeing storage elements
4149 * gtk_list_store_new(N_S_COLUMNS,
4150 * G_TYPE_AUTO_POINTER,
4154 * Template *precious_data = g_new0(Template, 1);
4155 * g_pointer protect = g_auto_pointer_new(precious_data);
4157 * gtk_list_store_set(container, &iter,
4161 * ... the gtk_list_store has copied the pointer and
4162 * ... incremented its reference count, we should free
4163 * ... the auto pointer (in C++ a destructor would do
4164 * ... this for us when leaving block scope)
4166 * g_auto_pointer_free(protect);
4168 * ... gtk_list_store_set() now manages the data. When
4169 * ... *explicitly* requesting a pointer from the list
4170 * ... store, don't forget you get a copy that should be
4171 * ... freed with g_auto_pointer_free() eventually.
4175 *\param pointer Pointer to be guarded.
4177 *\return GAuto * Pointer that should be used in containers with
4180 GAuto *g_auto_pointer_new(gpointer p)
4182 AutoPointerRef *ref;
4188 ref = g_new0(AutoPointerRef, 1);
4189 ptr = g_new0(AutoPointer, 1);
4199 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4205 *\brief Allocate an autopointer using the passed \a free function to
4206 * free the guarded pointer
4208 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4215 aptr = g_auto_pointer_new(p);
4216 aptr->ref->free = free_;
4220 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4222 if (auto_ptr == NULL)
4224 return ((AutoPointer *) auto_ptr)->ptr;
4228 *\brief Copies an auto pointer by. It's mostly not necessary
4229 * to call this function directly, unless you copy/assign
4230 * the guarded pointer.
4232 *\param auto_ptr Auto pointer returned by previous call to
4233 * g_auto_pointer_new_XXX()
4235 *\return gpointer An auto pointer
4237 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4240 AutoPointerRef *ref;
4243 if (auto_ptr == NULL)
4248 newp = g_new0(AutoPointer, 1);
4251 newp->ptr = ref->pointer;
4255 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4261 *\brief Free an auto pointer
4263 void g_auto_pointer_free(GAuto *auto_ptr)
4266 AutoPointerRef *ref;
4268 if (auto_ptr == NULL)
4274 if (--(ref->cnt) == 0) {
4276 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4278 ref->free(ref->pointer);
4283 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4288 void replace_returns(gchar *str)
4293 while (strstr(str, "\n")) {
4294 *strstr(str, "\n") = ' ';
4296 while (strstr(str, "\r")) {
4297 *strstr(str, "\r") = ' ';
4301 /* get_uri_part() - retrieves a URI starting from scanpos.
4302 Returns TRUE if succesful */
4303 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4304 const gchar **bp, const gchar **ep, gboolean hdr)
4307 gint parenthese_cnt = 0;
4309 g_return_val_if_fail(start != NULL, FALSE);
4310 g_return_val_if_fail(scanpos != NULL, FALSE);
4311 g_return_val_if_fail(bp != NULL, FALSE);
4312 g_return_val_if_fail(ep != NULL, FALSE);
4316 /* find end point of URI */
4317 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4318 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4319 !IS_ASCII(*(const guchar *)ep_) ||
4320 strchr("[]{}<>\"", *ep_)) {
4322 } else if (strchr("(", *ep_)) {
4324 } else if (strchr(")", *ep_)) {
4325 if (parenthese_cnt > 0)
4332 /* no punctuation at end of string */
4334 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4335 * should pass some URI type to this function and decide on that whether
4336 * to perform punctuation stripping */
4338 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4340 for (; ep_ - 1 > scanpos + 1 &&
4341 IS_REAL_PUNCT(*(ep_ - 1));
4345 #undef IS_REAL_PUNCT
4352 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4354 while (bp && *bp && g_ascii_isspace(*bp))
4356 return g_strndup(bp, ep - bp);
4359 /* valid mail address characters */
4360 #define IS_RFC822_CHAR(ch) \
4364 !g_ascii_isspace(ch) && \
4365 !strchr("(),;<>\"", (ch)))
4367 /* alphabet and number within 7bit ASCII */
4368 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4369 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4371 static GHashTable *create_domain_tab(void)
4373 static const gchar *toplvl_domains [] = {
4375 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4376 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4377 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4378 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4379 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4380 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4381 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4382 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4383 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4384 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4385 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4386 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4387 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4388 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4389 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4390 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4391 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4392 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4393 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4394 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4395 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4396 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4397 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4398 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4399 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4400 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4403 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4405 g_return_val_if_fail(htab, NULL);
4406 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4407 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4411 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4413 const gint MAX_LVL_DOM_NAME_LEN = 6;
4414 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4415 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4418 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4421 for (p = buf; p < m && first < last; *p++ = *first++)
4425 return g_hash_table_lookup(tab, buf) != NULL;
4428 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4429 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4430 const gchar **bp, const gchar **ep, gboolean hdr)
4432 /* more complex than the uri part because we need to scan back and forward starting from
4433 * the scan position. */
4434 gboolean result = FALSE;
4435 const gchar *bp_ = NULL;
4436 const gchar *ep_ = NULL;
4437 static GHashTable *dom_tab;
4438 const gchar *last_dot = NULL;
4439 const gchar *prelast_dot = NULL;
4440 const gchar *last_tld_char = NULL;
4442 /* the informative part of the email address (describing the name
4443 * of the email address owner) may contain quoted parts. the
4444 * closure stack stores the last encountered quotes. */
4445 gchar closure_stack[128];
4446 gchar *ptr = closure_stack;
4448 g_return_val_if_fail(start != NULL, FALSE);
4449 g_return_val_if_fail(scanpos != NULL, FALSE);
4450 g_return_val_if_fail(bp != NULL, FALSE);
4451 g_return_val_if_fail(ep != NULL, FALSE);
4454 const gchar *start_quote = NULL;
4455 const gchar *end_quote = NULL;
4457 /* go to the real start */
4458 if (start[0] == ',')
4460 if (start[0] == ';')
4462 while (start[0] == '\n' || start[0] == '\r')
4464 while (start[0] == ' ' || start[0] == '\t')
4469 /* check if there are quotes (to skip , in them) */
4470 if (*start == '"') {
4471 start_quote = start;
4473 end_quote = strstr(start, "\"");
4479 /* skip anything between quotes */
4480 if (start_quote && end_quote) {
4485 /* find end (either , or ; or end of line) */
4486 if (strstr(start, ",") && strstr(start, ";"))
4487 *ep = strstr(start,",") < strstr(start, ";")
4488 ? strstr(start, ",") : strstr(start, ";");
4489 else if (strstr(start, ","))
4490 *ep = strstr(start, ",");
4491 else if (strstr(start, ";"))
4492 *ep = strstr(start, ";");
4494 *ep = start+strlen(start);
4496 /* go back to real start */
4497 if (start_quote && end_quote) {
4498 start = start_quote;
4501 /* check there's still an @ in that, or search
4502 * further if possible */
4503 if (strstr(start, "@") && strstr(start, "@") < *ep)
4505 else if (*ep < start+strlen(start)) {
4508 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4516 dom_tab = create_domain_tab();
4517 g_return_val_if_fail(dom_tab, FALSE);
4519 /* scan start of address */
4520 for (bp_ = scanpos - 1;
4521 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4524 /* TODO: should start with an alnum? */
4526 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4529 if (bp_ != scanpos) {
4530 /* scan end of address */
4531 for (ep_ = scanpos + 1;
4532 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4534 prelast_dot = last_dot;
4536 if (*(last_dot + 1) == '.') {
4537 if (prelast_dot == NULL)
4539 last_dot = prelast_dot;
4544 /* TODO: really should terminate with an alnum? */
4545 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4550 if (last_dot == NULL)
4552 if (last_dot >= ep_)
4553 last_dot = prelast_dot;
4554 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4558 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4559 if (*last_tld_char == '?')
4562 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4569 if (!result) return FALSE;
4571 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4572 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4573 && IS_RFC822_CHAR(*(ep_ + 3))) {
4574 /* this informative part with an @ in it is
4575 * followed by the email address */
4578 /* go to matching '>' (or next non-rfc822 char, like \n) */
4579 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4582 /* include the bracket */
4583 if (*ep_ == '>') ep_++;
4585 /* include the leading quote */
4593 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4594 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4597 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4598 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4601 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4602 #define IN_STACK() (ptr > closure_stack)
4603 /* has underrun check */
4604 #define POP_STACK() if(IN_STACK()) --ptr
4605 /* has overrun check */
4606 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4607 /* has underrun check */
4608 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4612 /* scan for the informative part. */
4613 for (bp_ -= 2; bp_ >= start; bp_--) {
4614 /* if closure on the stack keep scanning */
4615 if (PEEK_STACK() == *bp_) {
4619 if (*bp_ == '\'' || *bp_ == '"') {
4624 /* if nothing in the closure stack, do the special conditions
4625 * the following if..else expression simply checks whether
4626 * a token is acceptable. if not acceptable, the clause
4627 * should terminate the loop with a 'break' */
4628 if (!PEEK_STACK()) {
4630 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4631 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4632 /* hyphens are allowed, but only in
4634 } else if (strchr(" \"'", *bp_)) {
4635 /* but anything not being a punctiation
4638 break; /* anything else is rejected */
4645 /* scan forward (should start with an alnum) */
4646 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4662 #undef IS_ASCII_ALNUM
4663 #undef IS_RFC822_CHAR
4665 gchar *make_email_string(const gchar *bp, const gchar *ep)
4667 /* returns a mailto: URI; mailto: is also used to detect the
4668 * uri type later on in the button_pressed signal handler */
4672 tmp = g_strndup(bp, ep - bp);
4673 result = g_strconcat("mailto:", tmp, NULL);
4679 gchar *make_http_string(const gchar *bp, const gchar *ep)
4681 /* returns an http: URI; */
4685 while (bp && *bp && g_ascii_isspace(*bp))
4687 tmp = g_strndup(bp, ep - bp);
4688 result = g_strconcat("http://", tmp, NULL);
4694 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4696 FILE *fp = fopen(path, "rb");
4697 gchar buf[BUFFSIZE];
4698 gchar *result = NULL;
4701 while (fgets(buf, sizeof (buf), fp) != NULL) {
4702 gchar **parts = g_strsplit(buf, ";", 3);
4703 gchar *trimmed = parts[0];
4704 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4706 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4707 trimmed[strlen(trimmed)-1] = '\0';
4709 if (!strcmp(trimmed, type)) {
4710 gboolean needsterminal = FALSE;
4711 if (parts[2] && strstr(parts[2], "needsterminal")) {
4712 needsterminal = TRUE;
4714 if (parts[2] && strstr(parts[2], "test=")) {
4715 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4716 gchar *testcmd = orig_testcmd;
4717 if (strstr(testcmd,";"))
4718 *(strstr(testcmd,";")) = '\0';
4719 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4721 while (testcmd[strlen(testcmd)-1] == '\n')
4722 testcmd[strlen(testcmd)-1] = '\0';
4723 while (testcmd[strlen(testcmd)-1] == '\r')
4724 testcmd[strlen(testcmd)-1] = '\0';
4725 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4726 testcmd[strlen(testcmd)-1] = '\0';
4728 if (strstr(testcmd, "%s")) {
4729 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4730 gint res = system(tmp);
4732 g_free(orig_testcmd);
4739 gint res = system(testcmd);
4740 g_free(orig_testcmd);
4750 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4752 while (trimmed[strlen(trimmed)-1] == '\n')
4753 trimmed[strlen(trimmed)-1] = '\0';
4754 while (trimmed[strlen(trimmed)-1] == '\r')
4755 trimmed[strlen(trimmed)-1] = '\0';
4756 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4757 trimmed[strlen(trimmed)-1] = '\0';
4758 result = g_strdup(trimmed);
4761 /* if there are no single quotes around %s, add them.
4762 * '.*%s.*' is ok, as in display 'png:%s'
4764 if (strstr(result, "%s")
4765 && !(strstr(result, "'") < strstr(result,"%s") &&
4766 strstr(strstr(result,"%s"), "'"))) {
4767 gchar *start = g_strdup(result);
4768 gchar *end = g_strdup(strstr(result, "%s")+2);
4770 *strstr(start, "%s") = '\0';
4771 tmp = g_strconcat(start,"'%s'",end, NULL);
4777 if (needsterminal) {
4778 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4789 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4791 gchar *result = NULL;
4795 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4796 result = mailcap_get_command_in_file(path, type, file_to_open);
4800 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4804 void mailcap_update_default(const gchar *type, const gchar *command)
4806 gchar *path = NULL, *outpath = NULL;
4807 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4808 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4809 FILE *fp = fopen(path, "rb");
4810 FILE *outfp = fopen(outpath, "wb");
4811 gchar buf[BUFFSIZE];
4819 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4820 gchar **parts = g_strsplit(buf, ";", 3);
4821 gchar *trimmed = parts[0];
4822 while (trimmed[0] == ' ')
4824 while (trimmed[strlen(trimmed)-1] == ' ')
4825 trimmed[strlen(trimmed)-1] = '\0';
4827 if (!strcmp(trimmed, type)) {
4836 fprintf(outfp, "%s; %s\n", type, command);
4842 g_rename(outpath, path);
4845 gint copy_dir(const gchar *src, const gchar *dst)
4850 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4851 g_warning("failed to open directory: %s\n", src);
4855 if (make_dir(dst) < 0)
4858 while ((name = g_dir_read_name(dir)) != NULL) {
4859 gchar *old_file, *new_file;
4860 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4861 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4862 debug_print("copying: %s -> %s\n", old_file, new_file);
4863 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4864 gint r = copy_file(old_file, new_file, TRUE);
4871 /* Windows has no symlinks. Or well, Vista seems to
4872 have something like this but the semantics might be
4873 different. Thus we don't use it under Windows. */
4874 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4877 gchar *target = g_file_read_link(old_file, &error);
4879 r = symlink(target, new_file);
4886 #endif /*G_OS_WIN32*/
4887 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4888 gint r = copy_dir(old_file, new_file);
4899 /* crude test to see if a file is an email. */
4900 gboolean file_is_email (const gchar *filename)
4906 if (filename == NULL)
4908 if ((fp = g_fopen(filename, "rb")) == NULL)
4910 while (i < 60 && score < 3
4911 && fgets(buffer, sizeof (buffer), fp) > 0) {
4912 if (!strncmp(buffer, "From:", strlen("From:")))
4914 if (!strncmp(buffer, "To:", strlen("To:")))
4916 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4921 return (score >= 3);
4924 gboolean sc_g_list_bigger(GList *list, gint max)
4928 while (cur && i <= max+1) {
4935 gboolean sc_g_slist_bigger(GSList *list, gint max)
4939 while (cur && i <= max+1) {
4946 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4947 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4948 NULL, NULL, NULL, NULL, NULL, NULL};
4949 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4950 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4951 NULL, NULL, NULL, NULL, NULL, NULL};
4952 const gchar *s_am_up = NULL;
4953 const gchar *s_pm_up = NULL;
4954 const gchar *s_am_low = NULL;
4955 const gchar *s_pm_low = NULL;
4956 const gchar *def_loc_format = NULL;
4957 const gchar *date_loc_format = NULL;
4958 const gchar *time_loc_format = NULL;
4959 const gchar *time_am_pm = NULL;
4961 static gboolean time_names_init_done = FALSE;
4963 static void init_time_names(void)
4965 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4966 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4967 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4968 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4969 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4970 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4971 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4973 monthnames[0] = Q_("Complete month name for use by strftime|January");
4974 monthnames[1] = Q_("Complete month name for use by strftime|February");
4975 monthnames[2] = Q_("Complete month name for use by strftime|March");
4976 monthnames[3] = Q_("Complete month name for use by strftime|April");
4977 monthnames[4] = Q_("Complete month name for use by strftime|May");
4978 monthnames[5] = Q_("Complete month name for use by strftime|June");
4979 monthnames[6] = Q_("Complete month name for use by strftime|July");
4980 monthnames[7] = Q_("Complete month name for use by strftime|August");
4981 monthnames[8] = Q_("Complete month name for use by strftime|September");
4982 monthnames[9] = Q_("Complete month name for use by strftime|October");
4983 monthnames[10] = Q_("Complete month name for use by strftime|November");
4984 monthnames[11] = Q_("Complete month name for use by strftime|December");
4986 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4987 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4988 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4989 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4990 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4991 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4992 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4994 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4995 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4996 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4997 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4998 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4999 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
5000 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
5001 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
5002 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
5003 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
5004 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
5005 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
5007 s_am_up = Q_("For use by strftime (morning)|AM");
5008 s_pm_up = Q_("For use by strftime (afternoon)|PM");
5009 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5010 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5012 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5013 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5014 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5016 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5018 time_names_init_done = TRUE;
5021 #define CHECK_SIZE() { \
5022 total_done += len; \
5023 if (total_done >= buflen) { \
5024 buf[buflen-1] = '\0'; \
5029 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5031 gchar *curpos = buf;
5032 gint total_done = 0;
5033 gchar subbuf[64], subfmt[64];
5034 static time_t last_tzset = (time_t)0;
5036 if (!time_names_init_done)
5039 if (format == NULL || lt == NULL)
5042 if (last_tzset != time(NULL)) {
5044 last_tzset = time(NULL);
5047 if (*format == '%') {
5048 gint len = 0, tmp = 0;
5052 len = 1; CHECK_SIZE();
5056 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5057 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5060 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5061 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5065 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5066 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5069 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5070 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5073 fast_strftime(subbuf, 64, def_loc_format, lt);
5074 len = strlen(subbuf); CHECK_SIZE();
5075 strncpy2(curpos, subbuf, buflen - total_done);
5078 total_done += 2; CHECK_SIZE();
5079 tmp = (lt->tm_year + 1900)/100;
5080 *curpos++ = '0'+(tmp / 10);
5081 *curpos++ = '0'+(tmp % 10);
5084 total_done += 2; CHECK_SIZE();
5085 *curpos++ = '0'+(lt->tm_mday / 10);
5086 *curpos++ = '0'+(lt->tm_mday % 10);
5089 total_done += 8; CHECK_SIZE();
5090 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5091 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5093 *curpos++ = '0'+(lt->tm_mday / 10);
5094 *curpos++ = '0'+(lt->tm_mday % 10);
5096 tmp = lt->tm_year%100;
5097 *curpos++ = '0'+(tmp / 10);
5098 *curpos++ = '0'+(tmp % 10);
5101 len = 2; CHECK_SIZE();
5102 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5105 len = 10; CHECK_SIZE();
5106 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5107 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5110 total_done += 2; CHECK_SIZE();
5111 *curpos++ = '0'+(lt->tm_hour / 10);
5112 *curpos++ = '0'+(lt->tm_hour % 10);
5115 total_done += 2; CHECK_SIZE();
5121 *curpos++ = '0'+(tmp / 10);
5122 *curpos++ = '0'+(tmp % 10);
5125 len = 3; CHECK_SIZE();
5126 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5129 len = 2; CHECK_SIZE();
5130 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5133 len = 2; CHECK_SIZE();
5139 snprintf(curpos, buflen - total_done, "%2d", tmp);
5142 total_done += 2; CHECK_SIZE();
5143 tmp = lt->tm_mon + 1;
5144 *curpos++ = '0'+(tmp / 10);
5145 *curpos++ = '0'+(tmp % 10);
5148 total_done += 2; CHECK_SIZE();
5149 *curpos++ = '0'+(lt->tm_min / 10);
5150 *curpos++ = '0'+(lt->tm_min % 10);
5153 len = 1; CHECK_SIZE();
5157 if (lt->tm_hour >= 12) {
5158 len = strlen(s_pm_up); CHECK_SIZE();
5159 snprintf(curpos, buflen-total_done, s_pm_up);
5161 len = strlen(s_am_up); CHECK_SIZE();
5162 snprintf(curpos, buflen-total_done, s_am_up);
5166 if (lt->tm_hour >= 12) {
5167 len = strlen(s_pm_low); CHECK_SIZE();
5168 snprintf(curpos, buflen-total_done, s_pm_low);
5170 len = strlen(s_am_low); CHECK_SIZE();
5171 snprintf(curpos, buflen-total_done, s_am_low);
5175 fast_strftime(subbuf, 64, time_am_pm, lt);
5176 len = strlen(subbuf); CHECK_SIZE();
5177 strncpy2(curpos, subbuf, buflen - total_done);
5180 total_done += 5; CHECK_SIZE();
5181 *curpos++ = '0'+(lt->tm_hour / 10);
5182 *curpos++ = '0'+(lt->tm_hour % 10);
5184 *curpos++ = '0'+(lt->tm_min / 10);
5185 *curpos++ = '0'+(lt->tm_min % 10);
5188 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5189 len = strlen(subbuf); CHECK_SIZE();
5190 strncpy2(curpos, subbuf, buflen - total_done);
5193 total_done += 2; CHECK_SIZE();
5194 *curpos++ = '0'+(lt->tm_sec / 10);
5195 *curpos++ = '0'+(lt->tm_sec % 10);
5198 len = 1; CHECK_SIZE();
5202 total_done += 8; CHECK_SIZE();
5203 *curpos++ = '0'+(lt->tm_hour / 10);
5204 *curpos++ = '0'+(lt->tm_hour % 10);
5206 *curpos++ = '0'+(lt->tm_min / 10);
5207 *curpos++ = '0'+(lt->tm_min % 10);
5209 *curpos++ = '0'+(lt->tm_sec / 10);
5210 *curpos++ = '0'+(lt->tm_sec % 10);
5213 len = 1; CHECK_SIZE();
5214 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5217 len = 1; CHECK_SIZE();
5218 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5221 fast_strftime(subbuf, 64, date_loc_format, lt);
5222 len = strlen(subbuf); CHECK_SIZE();
5223 strncpy2(curpos, subbuf, buflen - total_done);
5226 fast_strftime(subbuf, 64, time_loc_format, lt);
5227 len = strlen(subbuf); CHECK_SIZE();
5228 strncpy2(curpos, subbuf, buflen - total_done);
5231 total_done += 2; CHECK_SIZE();
5232 tmp = lt->tm_year%100;
5233 *curpos++ = '0'+(tmp / 10);
5234 *curpos++ = '0'+(tmp % 10);
5237 len = 4; CHECK_SIZE();
5238 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5248 /* let these complicated ones be done with the libc */
5249 snprintf(subfmt, 64, "%%%c", *format);
5250 strftime(subbuf, 64, subfmt, lt);
5251 len = strlen(subbuf); CHECK_SIZE();
5252 strncpy2(curpos, subbuf, buflen - total_done);
5256 /* let these complicated modifiers be done with the libc */
5257 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5258 strftime(subbuf, 64, subfmt, lt);
5259 len = strlen(subbuf); CHECK_SIZE();
5260 strncpy2(curpos, subbuf, buflen - total_done);
5264 g_warning("format error (%c)", *format);
5271 int len = 1; CHECK_SIZE();
5272 *curpos++ = *format++;