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();
1937 const gchar *get_news_cache_dir(void)
1939 static gchar *news_cache_dir = NULL;
1941 if (!news_cache_dir)
1942 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1943 NEWS_CACHE_DIR, NULL);
1945 return news_cache_dir;
1948 const gchar *get_imap_cache_dir(void)
1950 static gchar *imap_cache_dir = NULL;
1952 if (!imap_cache_dir)
1953 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1954 IMAP_CACHE_DIR, NULL);
1956 return imap_cache_dir;
1959 const gchar *get_mbox_cache_dir(void)
1961 static gchar *mbox_cache_dir = NULL;
1963 if (!mbox_cache_dir)
1964 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1965 MBOX_CACHE_DIR, NULL);
1967 return mbox_cache_dir;
1970 const gchar *get_mime_tmp_dir(void)
1972 static gchar *mime_tmp_dir = NULL;
1975 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1976 MIME_TMP_DIR, NULL);
1978 return mime_tmp_dir;
1981 const gchar *get_template_dir(void)
1983 static gchar *template_dir = NULL;
1986 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1987 TEMPLATE_DIR, NULL);
1989 return template_dir;
1992 const gchar *get_header_cache_dir(void)
1994 static gchar *header_dir = NULL;
1997 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1998 HEADER_CACHE_DIR, NULL);
2003 /* Return the default directory for Plugins. */
2004 const gchar *get_plugin_dir(void)
2007 static gchar *plugin_dir = NULL;
2010 plugin_dir = g_strconcat(w32_get_module_dir(),
2011 "\\lib\\claws-mail\\plugins\\",
2015 if (is_dir_exist(PLUGINDIR))
2018 static gchar *plugin_dir = NULL;
2020 plugin_dir = g_strconcat(get_rc_dir(),
2021 G_DIR_SEPARATOR_S, "plugins",
2022 G_DIR_SEPARATOR_S, NULL);
2028 const gchar *get_tmp_dir(void)
2030 static gchar *tmp_dir = NULL;
2033 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2039 gchar *get_tmp_file(void)
2042 static guint32 id = 0;
2044 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2045 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2050 const gchar *get_domain_name(void)
2053 static gchar *domain_name = NULL;
2059 if (gethostname(hostname, sizeof(hostname)) != 0) {
2060 perror("gethostname");
2061 domain_name = "unknown";
2063 hostname[sizeof(hostname) - 1] = '\0';
2064 if ((hp = my_gethostbyname(hostname)) == NULL) {
2065 perror("gethostbyname");
2066 domain_name = g_strdup(hostname);
2068 domain_name = g_strdup(hp->h_name);
2071 debug_print("domain name = %s\n", domain_name);
2080 off_t get_file_size(const gchar *file)
2084 if (g_stat(file, &s) < 0) {
2085 FILE_OP_ERROR(file, "stat");
2092 time_t get_file_mtime(const gchar *file)
2096 if (g_stat(file, &s) < 0) {
2097 FILE_OP_ERROR(file, "stat");
2104 off_t get_file_size_as_crlf(const gchar *file)
2108 gchar buf[BUFFSIZE];
2110 if ((fp = g_fopen(file, "rb")) == NULL) {
2111 FILE_OP_ERROR(file, "fopen");
2115 while (fgets(buf, sizeof(buf), fp) != NULL) {
2117 size += strlen(buf) + 2;
2121 FILE_OP_ERROR(file, "fgets");
2130 off_t get_left_file_size(FILE *fp)
2136 if ((pos = ftell(fp)) < 0) {
2140 if (fseek(fp, 0L, SEEK_END) < 0) {
2144 if ((end = ftell(fp)) < 0) {
2149 if (fseek(fp, pos, SEEK_SET) < 0) {
2157 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2164 if (g_stat(file, &s) < 0) {
2165 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2169 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2176 /* Test on whether FILE is a relative file name. This is
2177 * straightforward for Unix but more complex for Windows. */
2178 gboolean is_relative_filename(const gchar *file)
2183 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2184 return FALSE; /* Prefixed with a hostname - this can't
2185 * be a relative name. */
2187 if ( ((*file >= 'a' && *file <= 'z')
2188 || (*file >= 'A' && *file <= 'Z'))
2190 file += 2; /* Skip drive letter. */
2192 return !(*file == '\\' || *file == '/');
2194 return !(*file == G_DIR_SEPARATOR);
2199 gboolean is_dir_exist(const gchar *dir)
2204 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2207 gboolean is_file_entry_exist(const gchar *file)
2212 return g_file_test(file, G_FILE_TEST_EXISTS);
2215 gboolean dirent_is_regular_file(struct dirent *d)
2217 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2218 if (d->d_type == DT_REG)
2220 else if (d->d_type != DT_UNKNOWN)
2224 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2227 gboolean dirent_is_directory(struct dirent *d)
2229 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2230 if (d->d_type == DT_DIR)
2232 else if (d->d_type != DT_UNKNOWN)
2236 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2239 gint change_dir(const gchar *dir)
2241 gchar *prevdir = NULL;
2244 prevdir = g_get_current_dir();
2246 if (g_chdir(dir) < 0) {
2247 FILE_OP_ERROR(dir, "chdir");
2248 if (debug_mode) g_free(prevdir);
2250 } else if (debug_mode) {
2253 cwd = g_get_current_dir();
2254 if (strcmp(prevdir, cwd) != 0)
2255 g_print("current dir: %s\n", cwd);
2263 gint make_dir(const gchar *dir)
2265 if (g_mkdir(dir, S_IRWXU) < 0) {
2266 FILE_OP_ERROR(dir, "mkdir");
2269 if (g_chmod(dir, S_IRWXU) < 0)
2270 FILE_OP_ERROR(dir, "chmod");
2275 gint make_dir_hier(const gchar *dir)
2280 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2281 parent_dir = g_strndup(dir, p - dir);
2282 if (*parent_dir != '\0') {
2283 if (!is_dir_exist(parent_dir)) {
2284 if (make_dir(parent_dir) < 0) {
2293 if (!is_dir_exist(dir)) {
2294 if (make_dir(dir) < 0)
2301 gint remove_all_files(const gchar *dir)
2304 const gchar *dir_name;
2307 prev_dir = g_get_current_dir();
2309 if (g_chdir(dir) < 0) {
2310 FILE_OP_ERROR(dir, "chdir");
2315 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2316 g_warning("failed to open directory: %s\n", dir);
2321 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2322 if (g_unlink(dir_name) < 0)
2323 FILE_OP_ERROR(dir_name, "unlink");
2328 if (g_chdir(prev_dir) < 0) {
2329 FILE_OP_ERROR(prev_dir, "chdir");
2339 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2342 const gchar *dir_name;
2346 prev_dir = g_get_current_dir();
2348 if (g_chdir(dir) < 0) {
2349 FILE_OP_ERROR(dir, "chdir");
2354 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2355 g_warning("failed to open directory: %s\n", dir);
2360 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2361 file_no = to_number(dir_name);
2362 if (file_no > 0 && first <= file_no && file_no <= last) {
2363 if (is_dir_exist(dir_name))
2365 if (g_unlink(dir_name) < 0)
2366 FILE_OP_ERROR(dir_name, "unlink");
2372 if (g_chdir(prev_dir) < 0) {
2373 FILE_OP_ERROR(prev_dir, "chdir");
2383 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2386 const gchar *dir_name;
2390 prev_dir = g_get_current_dir();
2392 if (g_chdir(dir) < 0) {
2393 FILE_OP_ERROR(dir, "chdir");
2398 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2399 FILE_OP_ERROR(dir, "opendir");
2404 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2405 file_no = to_number(dir_name);
2406 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2407 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2408 if (is_dir_exist(dir_name))
2410 if (g_unlink(dir_name) < 0)
2411 FILE_OP_ERROR(dir_name, "unlink");
2417 if (g_chdir(prev_dir) < 0) {
2418 FILE_OP_ERROR(prev_dir, "chdir");
2428 gint remove_all_numbered_files(const gchar *dir)
2430 return remove_numbered_files(dir, 0, UINT_MAX);
2433 gint remove_expired_files(const gchar *dir, guint hours)
2436 const gchar *dir_name;
2440 time_t mtime, now, expire_time;
2442 prev_dir = g_get_current_dir();
2444 if (g_chdir(dir) < 0) {
2445 FILE_OP_ERROR(dir, "chdir");
2450 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2451 g_warning("failed to open directory: %s\n", dir);
2457 expire_time = hours * 60 * 60;
2459 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2460 file_no = to_number(dir_name);
2462 if (g_stat(dir_name, &s) < 0) {
2463 FILE_OP_ERROR(dir_name, "stat");
2466 if (S_ISDIR(s.st_mode))
2468 mtime = MAX(s.st_mtime, s.st_atime);
2469 if (now - mtime > expire_time) {
2470 if (g_unlink(dir_name) < 0)
2471 FILE_OP_ERROR(dir_name, "unlink");
2478 if (g_chdir(prev_dir) < 0) {
2479 FILE_OP_ERROR(prev_dir, "chdir");
2489 gint remove_dir_recursive(const gchar *dir)
2493 const gchar *dir_name;
2496 if (g_stat(dir, &s) < 0) {
2497 FILE_OP_ERROR(dir, "stat");
2498 if (ENOENT == errno) return 0;
2502 if (!S_ISDIR(s.st_mode)) {
2503 if (g_unlink(dir) < 0) {
2504 FILE_OP_ERROR(dir, "unlink");
2511 prev_dir = g_get_current_dir();
2512 /* g_print("prev_dir = %s\n", prev_dir); */
2514 if (!path_cmp(prev_dir, dir)) {
2516 if (g_chdir("..") < 0) {
2517 FILE_OP_ERROR(dir, "chdir");
2520 prev_dir = g_get_current_dir();
2523 if (g_chdir(dir) < 0) {
2524 FILE_OP_ERROR(dir, "chdir");
2529 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2530 g_warning("failed to open directory: %s\n", dir);
2536 /* remove all files in the directory */
2537 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2538 /* g_print("removing %s\n", dir_name); */
2540 if (is_dir_exist(dir_name)) {
2541 if (remove_dir_recursive(dir_name) < 0) {
2542 g_warning("can't remove directory\n");
2546 if (g_unlink(dir_name) < 0)
2547 FILE_OP_ERROR(dir_name, "unlink");
2553 if (g_chdir(prev_dir) < 0) {
2554 FILE_OP_ERROR(prev_dir, "chdir");
2561 if (g_rmdir(dir) < 0) {
2562 FILE_OP_ERROR(dir, "rmdir");
2569 gint rename_force(const gchar *oldpath, const gchar *newpath)
2572 if (!is_file_entry_exist(oldpath)) {
2576 if (is_file_exist(newpath)) {
2577 if (g_unlink(newpath) < 0)
2578 FILE_OP_ERROR(newpath, "unlink");
2581 return g_rename(oldpath, newpath);
2585 * Append src file body to the tail of dest file.
2586 * Now keep_backup has no effects.
2588 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2590 FILE *src_fp, *dest_fp;
2594 gboolean err = FALSE;
2596 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2597 FILE_OP_ERROR(src, "fopen");
2601 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2602 FILE_OP_ERROR(dest, "fopen");
2607 if (change_file_mode_rw(dest_fp, dest) < 0) {
2608 FILE_OP_ERROR(dest, "chmod");
2609 g_warning("can't change file mode\n");
2612 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2613 if (n_read < sizeof(buf) && ferror(src_fp))
2615 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2616 g_warning("writing to %s failed.\n", dest);
2624 if (ferror(src_fp)) {
2625 FILE_OP_ERROR(src, "fread");
2629 if (fclose(dest_fp) == EOF) {
2630 FILE_OP_ERROR(dest, "fclose");
2642 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2644 FILE *src_fp, *dest_fp;
2647 gchar *dest_bak = NULL;
2648 gboolean err = FALSE;
2650 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2651 FILE_OP_ERROR(src, "fopen");
2654 if (is_file_exist(dest)) {
2655 dest_bak = g_strconcat(dest, ".bak", NULL);
2656 if (rename_force(dest, dest_bak) < 0) {
2657 FILE_OP_ERROR(dest, "rename");
2664 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2665 FILE_OP_ERROR(dest, "fopen");
2668 if (rename_force(dest_bak, dest) < 0)
2669 FILE_OP_ERROR(dest_bak, "rename");
2675 if (change_file_mode_rw(dest_fp, dest) < 0) {
2676 FILE_OP_ERROR(dest, "chmod");
2677 g_warning("can't change file mode\n");
2680 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2681 if (n_read < sizeof(buf) && ferror(src_fp))
2683 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2684 g_warning("writing to %s failed.\n", dest);
2689 if (rename_force(dest_bak, dest) < 0)
2690 FILE_OP_ERROR(dest_bak, "rename");
2697 if (ferror(src_fp)) {
2698 FILE_OP_ERROR(src, "fread");
2702 if (fclose(dest_fp) == EOF) {
2703 FILE_OP_ERROR(dest, "fclose");
2710 if (rename_force(dest_bak, dest) < 0)
2711 FILE_OP_ERROR(dest_bak, "rename");
2717 if (keep_backup == FALSE && dest_bak)
2725 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2727 if (overwrite == FALSE && is_file_exist(dest)) {
2728 g_warning("move_file(): file %s already exists.", dest);
2732 if (rename_force(src, dest) == 0) return 0;
2734 if (EXDEV != errno) {
2735 FILE_OP_ERROR(src, "rename");
2739 if (copy_file(src, dest, FALSE) < 0) return -1;
2746 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2749 gint bytes_left, to_read;
2752 if (fseek(fp, offset, SEEK_SET) < 0) {
2757 bytes_left = length;
2758 to_read = MIN(bytes_left, sizeof(buf));
2760 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2761 if (n_read < to_read && ferror(fp))
2763 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2766 bytes_left -= n_read;
2767 if (bytes_left == 0)
2769 to_read = MIN(bytes_left, sizeof(buf));
2780 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2783 gboolean err = FALSE;
2785 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2786 FILE_OP_ERROR(dest, "fopen");
2790 if (change_file_mode_rw(dest_fp, dest) < 0) {
2791 FILE_OP_ERROR(dest, "chmod");
2792 g_warning("can't change file mode\n");
2795 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2798 if (!err && fclose(dest_fp) == EOF) {
2799 FILE_OP_ERROR(dest, "fclose");
2804 g_warning("writing to %s failed.\n", dest);
2812 /* convert line endings into CRLF. If the last line doesn't end with
2813 * linebreak, add it.
2815 gchar *canonicalize_str(const gchar *str)
2821 for (p = str; *p != '\0'; ++p) {
2828 if (p == str || *(p - 1) != '\n')
2831 out = outp = g_malloc(new_len + 1);
2832 for (p = str; *p != '\0'; ++p) {
2839 if (p == str || *(p - 1) != '\n') {
2848 gint canonicalize_file(const gchar *src, const gchar *dest)
2850 FILE *src_fp, *dest_fp;
2851 gchar buf[BUFFSIZE];
2853 gboolean err = FALSE;
2854 gboolean last_linebreak = FALSE;
2856 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2857 FILE_OP_ERROR(src, "fopen");
2861 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2862 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 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2876 if (len == 0) break;
2877 last_linebreak = FALSE;
2879 if (buf[len - 1] != '\n') {
2880 last_linebreak = TRUE;
2881 r = fputs(buf, dest_fp);
2882 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2883 r = fputs(buf, dest_fp);
2886 r = fwrite(buf, 1, len - 1, dest_fp);
2891 r = fputs("\r\n", dest_fp);
2895 g_warning("writing to %s failed.\n", dest);
2903 if (last_linebreak == TRUE) {
2904 if (fputs("\r\n", dest_fp) == EOF)
2908 if (ferror(src_fp)) {
2909 FILE_OP_ERROR(src, "fgets");
2913 if (fclose(dest_fp) == EOF) {
2914 FILE_OP_ERROR(dest, "fclose");
2926 gint canonicalize_file_replace(const gchar *file)
2930 tmp_file = get_tmp_file();
2932 if (canonicalize_file(file, tmp_file) < 0) {
2937 if (move_file(tmp_file, file, TRUE) < 0) {
2938 g_warning("can't replace %s .\n", file);
2948 gint uncanonicalize_file(const gchar *src, const gchar *dest)
2950 FILE *src_fp, *dest_fp;
2951 gchar buf[BUFFSIZE];
2952 gboolean err = FALSE;
2954 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2955 FILE_OP_ERROR(src, "fopen");
2959 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2960 FILE_OP_ERROR(dest, "fopen");
2965 if (change_file_mode_rw(dest_fp, dest) < 0) {
2966 FILE_OP_ERROR(dest, "chmod");
2967 g_warning("can't change file mode\n");
2970 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2972 if (fputs(buf, dest_fp) == EOF) {
2973 g_warning("writing to %s failed.\n", dest);
2981 if (ferror(src_fp)) {
2982 FILE_OP_ERROR(src, "fgets");
2986 if (fclose(dest_fp) == EOF) {
2987 FILE_OP_ERROR(dest, "fclose");
2999 gint uncanonicalize_file_replace(const gchar *file)
3003 tmp_file = get_tmp_file();
3005 if (uncanonicalize_file(file, tmp_file) < 0) {
3010 if (move_file(tmp_file, file, TRUE) < 0) {
3011 g_warning("can't replace %s .\n", file);
3021 gchar *normalize_newlines(const gchar *str)
3023 const gchar *p = str;
3026 out = outp = g_malloc(strlen(str) + 1);
3027 for (p = str; *p != '\0'; ++p) {
3029 if (*(p + 1) != '\n')
3040 gchar *get_outgoing_rfc2822_str(FILE *fp)
3042 gchar buf[BUFFSIZE];
3046 str = g_string_new(NULL);
3048 /* output header part */
3049 while (fgets(buf, sizeof(buf), fp) != NULL) {
3051 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3058 else if (next != ' ' && next != '\t') {
3062 if (fgets(buf, sizeof(buf), fp) == NULL)
3066 g_string_append(str, buf);
3067 g_string_append(str, "\r\n");
3073 /* output body part */
3074 while (fgets(buf, sizeof(buf), fp) != NULL) {
3077 g_string_append_c(str, '.');
3078 g_string_append(str, buf);
3079 g_string_append(str, "\r\n");
3083 g_string_free(str, FALSE);
3089 * Create a new boundary in a way that it is very unlikely that this
3090 * will occur in the following text. It would be easy to ensure
3091 * uniqueness if everything is either quoted-printable or base64
3092 * encoded (note that conversion is allowed), but because MIME bodies
3093 * may be nested, it may happen that the same boundary has already
3096 * boundary := 0*69<bchars> bcharsnospace
3097 * bchars := bcharsnospace / " "
3098 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3099 * "+" / "_" / "," / "-" / "." /
3100 * "/" / ":" / "=" / "?"
3102 * some special characters removed because of buggy MTAs
3105 gchar *generate_mime_boundary(const gchar *prefix)
3107 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3108 "abcdefghijklmnopqrstuvwxyz"
3113 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3114 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3117 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3121 gint change_file_mode_rw(FILE *fp, const gchar *file)
3124 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3126 return g_chmod(file, S_IRUSR|S_IWUSR);
3130 FILE *my_tmpfile(void)
3132 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3133 const gchar suffix[] = ".XXXXXX";
3134 const gchar *tmpdir;
3136 const gchar *progname;
3143 tmpdir = get_tmp_dir();
3144 tmplen = strlen(tmpdir);
3145 progname = g_get_prgname();
3146 if (progname == NULL)
3147 progname = "claws-mail";
3148 proglen = strlen(progname);
3149 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3152 memcpy(fname, tmpdir, tmplen);
3153 fname[tmplen] = G_DIR_SEPARATOR;
3154 memcpy(fname + tmplen + 1, progname, proglen);
3155 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3157 fd = mkstemp(fname);
3164 /* verify that we can write in the file after unlinking */
3165 if (write(fd, buf, 1) < 0) {
3172 fp = fdopen(fd, "w+b");
3180 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3185 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3189 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3190 dir, G_DIR_SEPARATOR);
3191 fd = mkstemp_name(template, filename);
3194 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3195 fd = mkstemp(*filename);
3197 return fdopen(fd, "w+");
3200 FILE *str_open_as_stream(const gchar *str)
3205 g_return_val_if_fail(str != NULL, NULL);
3209 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3214 if (len == 0) return fp;
3216 if (fwrite(str, 1, len, fp) != len) {
3217 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3226 gint str_write_to_file(const gchar *str, const gchar *file)
3231 g_return_val_if_fail(str != NULL, -1);
3232 g_return_val_if_fail(file != NULL, -1);
3234 if ((fp = g_fopen(file, "wb")) == NULL) {
3235 FILE_OP_ERROR(file, "fopen");
3245 if (fwrite(str, 1, len, fp) != len) {
3246 FILE_OP_ERROR(file, "fwrite");
3252 if (fclose(fp) == EOF) {
3253 FILE_OP_ERROR(file, "fclose");
3261 gchar *file_read_to_str(const gchar *file)
3266 g_return_val_if_fail(file != NULL, NULL);
3268 if ((fp = g_fopen(file, "rb")) == NULL) {
3269 FILE_OP_ERROR(file, "fopen");
3273 str = file_read_stream_to_str(fp);
3280 gchar *file_read_stream_to_str(FILE *fp)
3287 g_return_val_if_fail(fp != NULL, NULL);
3289 array = g_byte_array_new();
3291 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3292 if (n_read < sizeof(buf) && ferror(fp))
3294 g_byte_array_append(array, buf, n_read);
3298 FILE_OP_ERROR("file stream", "fread");
3299 g_byte_array_free(array, TRUE);
3304 g_byte_array_append(array, buf, 1);
3305 str = (gchar *)array->data;
3306 g_byte_array_free(array, FALSE);
3308 if (!g_utf8_validate(str, -1, NULL)) {
3309 const gchar *src_codeset, *dest_codeset;
3311 src_codeset = conv_get_locale_charset_str();
3312 dest_codeset = CS_UTF_8;
3313 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3322 char *fgets_crlf(char *buf, int size, FILE *stream)
3324 gboolean is_cr = FALSE;
3325 gboolean last_was_cr = FALSE;
3330 while (--size > 0 && (c = getc(stream)) != EOF)
3333 is_cr = (c == '\r');
3343 last_was_cr = is_cr;
3345 if (c == EOF && cs == buf)
3353 static gint execute_async(gchar *const argv[])
3355 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3357 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3358 NULL, NULL, NULL, FALSE) == FALSE) {
3359 g_warning("Couldn't execute command: %s\n", argv[0]);
3366 static gint execute_sync(gchar *const argv[])
3370 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3372 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3373 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3374 g_warning("Couldn't execute command: %s\n", argv[0]);
3379 if (WIFEXITED(status))
3380 return WEXITSTATUS(status);
3388 gint execute_command_line(const gchar *cmdline, gboolean async)
3393 debug_print("execute_command_line(): executing: %s\n", cmdline);
3395 argv = strsplit_with_quote(cmdline, " ", 0);
3398 ret = execute_async(argv);
3400 ret = execute_sync(argv);
3407 gchar *get_command_output(const gchar *cmdline)
3409 gchar *child_stdout;
3412 g_return_val_if_fail(cmdline != NULL, NULL);
3414 debug_print("get_command_output(): executing: %s\n", cmdline);
3416 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3418 g_warning("Couldn't execute command: %s\n", cmdline);
3422 return child_stdout;
3425 static gint is_unchanged_uri_char(char c)
3436 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3442 for(i = 0; i < strlen(uri) ; i++) {
3443 if (is_unchanged_uri_char(uri[i])) {
3444 if (k + 2 >= bufsize)
3446 encoded_uri[k++] = uri[i];
3449 char * hexa = "0123456789ABCDEF";
3451 if (k + 4 >= bufsize)
3453 encoded_uri[k++] = '%';
3454 encoded_uri[k++] = hexa[uri[i] / 16];
3455 encoded_uri[k++] = hexa[uri[i] % 16];
3461 gint open_uri(const gchar *uri, const gchar *cmdline)
3464 gchar buf[BUFFSIZE];
3466 gchar encoded_uri[BUFFSIZE];
3467 g_return_val_if_fail(uri != NULL, -1);
3469 /* an option to choose whether to use encode_uri or not ? */
3470 encode_uri(encoded_uri, BUFFSIZE, uri);
3473 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3474 !strchr(p + 2, '%'))
3475 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3478 g_warning("Open URI command line is invalid "
3479 "(there must be only one '%%s'): %s",
3481 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3484 execute_command_line(buf, TRUE);
3486 extern osso_context_t *get_osso_context(void);
3487 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3488 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3489 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3494 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3496 gchar buf[BUFFSIZE];
3499 g_return_val_if_fail(filepath != NULL, -1);
3502 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3503 !strchr(p + 2, '%'))
3504 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3507 g_warning("Open Text Editor command line is invalid "
3508 "(there must be only one '%%s'): %s",
3510 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3513 execute_command_line(buf, TRUE);
3518 time_t remote_tzoffset_sec(const gchar *zone)
3520 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3526 time_t remoteoffset;
3528 strncpy(zone3, zone, 3);
3532 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3533 (c == '+' || c == '-')) {
3534 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3536 remoteoffset = -remoteoffset;
3537 } else if (!strncmp(zone, "UT" , 2) ||
3538 !strncmp(zone, "GMT", 2)) {
3540 } else if (strlen(zone3) == 3) {
3541 for (p = ustzstr; *p != '\0'; p += 3) {
3542 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3543 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3544 remoteoffset = iustz * 3600;
3550 } else if (strlen(zone3) == 1) {
3552 case 'Z': remoteoffset = 0; break;
3553 case 'A': remoteoffset = -1; break;
3554 case 'B': remoteoffset = -2; break;
3555 case 'C': remoteoffset = -3; break;
3556 case 'D': remoteoffset = -4; break;
3557 case 'E': remoteoffset = -5; break;
3558 case 'F': remoteoffset = -6; break;
3559 case 'G': remoteoffset = -7; break;
3560 case 'H': remoteoffset = -8; break;
3561 case 'I': remoteoffset = -9; break;
3562 case 'K': remoteoffset = -10; break; /* J is not used */
3563 case 'L': remoteoffset = -11; break;
3564 case 'M': remoteoffset = -12; break;
3565 case 'N': remoteoffset = 1; break;
3566 case 'O': remoteoffset = 2; break;
3567 case 'P': remoteoffset = 3; break;
3568 case 'Q': remoteoffset = 4; break;
3569 case 'R': remoteoffset = 5; break;
3570 case 'S': remoteoffset = 6; break;
3571 case 'T': remoteoffset = 7; break;
3572 case 'U': remoteoffset = 8; break;
3573 case 'V': remoteoffset = 9; break;
3574 case 'W': remoteoffset = 10; break;
3575 case 'X': remoteoffset = 11; break;
3576 case 'Y': remoteoffset = 12; break;
3577 default: remoteoffset = 0; break;
3579 remoteoffset = remoteoffset * 3600;
3583 return remoteoffset;
3586 time_t tzoffset_sec(time_t *now)
3590 struct tm buf1, buf2;
3592 gmt = *gmtime_r(now, &buf1);
3593 lt = localtime_r(now, &buf2);
3595 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3597 if (lt->tm_year < gmt.tm_year)
3599 else if (lt->tm_year > gmt.tm_year)
3601 else if (lt->tm_yday < gmt.tm_yday)
3603 else if (lt->tm_yday > gmt.tm_yday)
3606 if (off >= 24 * 60) /* should be impossible */
3607 off = 23 * 60 + 59; /* if not, insert silly value */
3608 if (off <= -24 * 60)
3609 off = -(23 * 60 + 59);
3614 /* calculate timezone offset */
3615 gchar *tzoffset(time_t *now)
3617 static gchar offset_string[6];
3621 struct tm buf1, buf2;
3623 gmt = *gmtime_r(now, &buf1);
3624 lt = localtime_r(now, &buf2);
3626 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3628 if (lt->tm_year < gmt.tm_year)
3630 else if (lt->tm_year > gmt.tm_year)
3632 else if (lt->tm_yday < gmt.tm_yday)
3634 else if (lt->tm_yday > gmt.tm_yday)
3642 if (off >= 24 * 60) /* should be impossible */
3643 off = 23 * 60 + 59; /* if not, insert silly value */
3645 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3647 return offset_string;
3650 void get_rfc822_date(gchar *buf, gint len)
3654 gchar day[4], mon[4];
3655 gint dd, hh, mm, ss, yyyy;
3657 gchar buf2[BUFFSIZE];
3660 lt = localtime_r(&t, &buf1);
3662 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3663 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3664 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3665 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3668 /* just a wrapper to suppress the warning of gcc about %c */
3669 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3670 const struct tm *tm)
3672 return strftime(s, max, format, tm);
3675 void debug_set_mode(gboolean mode)
3680 gboolean debug_get_mode(void)
3685 void debug_print_real(const gchar *format, ...)
3688 gchar buf[BUFFSIZE];
3690 if (!debug_mode) return;
3692 va_start(args, format);
3693 g_vsnprintf(buf, sizeof(buf), format, args);
3700 const char * debug_srcname(const char *file)
3702 const char *s = strrchr (file, '/');
3707 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3709 if (subject == NULL)
3712 subject += subject_get_prefix_length(subject);
3714 return g_hash_table_lookup(subject_table, subject);
3717 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3720 if (subject == NULL || *subject == 0)
3722 subject += subject_get_prefix_length(subject);
3723 g_hash_table_insert(subject_table, subject, data);
3726 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3728 if (subject == NULL)
3731 subject += subject_get_prefix_length(subject);
3732 g_hash_table_remove(subject_table, subject);
3736 *\brief Check if a string is prefixed with known (combinations)
3737 * of prefixes. The function assumes that each prefix
3738 * is terminated by zero or exactly _one_ space.
3740 *\param str String to check for a prefixes
3742 *\return int Number of chars in the prefix that should be skipped
3743 * for a "clean" subject line. If no prefix was found, 0
3746 int subject_get_prefix_length(const gchar *subject)
3748 /*!< Array with allowable reply prefixes regexps. */
3749 static const gchar * const prefixes[] = {
3750 "Re\\:", /* "Re:" */
3751 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3752 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3753 "Aw\\:", /* "Aw:" (German) */
3754 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3755 "Res\\:", /* "Res:" (Brazilian Outlook) */
3756 "Fw\\:", /* "Fw:" Forward */
3757 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3758 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3759 "Rif\\:", /* "Rif:" (Italian Outlook) */
3760 "Sv\\:", /* "Sv" (Norwegian) */
3761 "Vs\\:", /* "Vs" (Norwegian) */
3762 "Ad\\:", /* "Ad" (Norwegian) */
3763 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3766 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3769 static regex_t regex;
3770 static gboolean init_;
3772 if (!subject) return 0;
3773 if (!*subject) return 0;
3776 GString *s = g_string_new("");
3778 for (n = 0; n < PREFIXES; n++)
3779 /* Terminate each prefix regexpression by a
3780 * "\ ?" (zero or ONE space), and OR them */
3781 g_string_append_printf(s, "(%s\\ ?)%s",
3786 g_string_prepend(s, "(");
3787 g_string_append(s, ")+"); /* match at least once */
3788 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3791 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3792 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3793 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3794 debug_print("Error compiling regexp %s\n", s->str);
3795 g_string_free(s, TRUE);
3799 g_string_free(s, TRUE);
3803 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3809 guint g_stricase_hash(gconstpointer gptr)
3811 guint hash_result = 0;
3814 for (str = gptr; str && *str; str++) {
3815 hash_result += toupper(*str);
3821 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3823 const char *str1 = gptr1;
3824 const char *str2 = gptr2;
3826 return !strcasecmp(str1, str2);
3829 gint g_int_compare(gconstpointer a, gconstpointer b)
3831 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3834 gchar *generate_msgid(gchar *buf, gint len)
3842 lt = localtime_r(&t, &buft);
3844 addr = g_strconcat("@", get_domain_name(), NULL);
3846 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3847 lt->tm_year + 1900, lt->tm_mon + 1,
3848 lt->tm_mday, lt->tm_hour,
3849 lt->tm_min, lt->tm_sec,
3850 (guint) rand(), addr);
3857 quote_cmd_argument()
3859 return a quoted string safely usable in argument of a command.
3861 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3864 gint quote_cmd_argument(gchar * result, guint size,
3874 for(p = path ; * p != '\0' ; p ++) {
3876 if (isalnum((guchar)*p) || (* p == '/')) {
3877 if (remaining > 0) {
3883 result[size - 1] = '\0';
3888 if (remaining >= 2) {
3896 result[size - 1] = '\0';
3901 if (remaining > 0) {
3905 result[size - 1] = '\0';
3919 static void g_node_map_recursive(GNode *node, gpointer data)
3921 GNodeMapData *mapdata = (GNodeMapData *) data;
3923 GNodeMapData newmapdata;
3926 newdata = mapdata->func(node->data, mapdata->data);
3927 if (newdata != NULL) {
3928 newnode = g_node_new(newdata);
3929 g_node_append(mapdata->parent, newnode);
3931 newmapdata.parent = newnode;
3932 newmapdata.func = mapdata->func;
3933 newmapdata.data = mapdata->data;
3935 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3939 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3942 GNodeMapData mapdata;
3944 g_return_val_if_fail(node != NULL, NULL);
3945 g_return_val_if_fail(func != NULL, NULL);
3947 root = g_node_new(func(node->data, data));
3949 mapdata.parent = root;
3950 mapdata.func = func;
3951 mapdata.data = data;
3953 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3958 #define HEX_TO_INT(val, hex) \
3962 if ('0' <= c && c <= '9') { \
3964 } else if ('a' <= c && c <= 'f') { \
3965 val = c - 'a' + 10; \
3966 } else if ('A' <= c && c <= 'F') { \
3967 val = c - 'A' + 10; \
3973 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3980 if (hi == -1 || lo == -1)
3983 *out = (hi << 4) + lo;
3987 #define INT_TO_HEX(hex, val) \
3990 hex = '0' + (val); \
3992 hex = 'A' + (val) - 10; \
3995 void get_hex_str(gchar *out, guchar ch)
3999 INT_TO_HEX(hex, ch >> 4);
4001 INT_TO_HEX(hex, ch & 0x0f);
4007 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4009 #define G_PRINT_REF g_print
4013 *\brief Register ref counted pointer. It is based on GBoxed, so should
4014 * work with anything that uses the GType system. The semantics
4015 * are similar to a C++ auto pointer, with the exception that
4016 * C doesn't have automatic closure (calling destructors) when
4017 * exiting a block scope.
4018 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4019 * function directly.
4021 *\return GType A GType type.
4023 GType g_auto_pointer_register(void)
4025 static GType auto_pointer_type;
4026 if (!auto_pointer_type)
4028 g_boxed_type_register_static
4029 ("G_TYPE_AUTO_POINTER",
4030 (GBoxedCopyFunc) g_auto_pointer_copy,
4031 (GBoxedFreeFunc) g_auto_pointer_free);
4032 return auto_pointer_type;
4036 *\brief Structure with g_new() allocated pointer guarded by the
4039 typedef struct AutoPointerRef {
4040 void (*free) (gpointer);
4046 *\brief The auto pointer opaque structure that references the
4047 * pointer guard block.
4049 typedef struct AutoPointer {
4050 AutoPointerRef *ref;
4051 gpointer ptr; /*!< access to protected pointer */
4055 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4059 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4060 * ... when assigning, copying and freeing storage elements
4062 * gtk_list_store_new(N_S_COLUMNS,
4063 * G_TYPE_AUTO_POINTER,
4067 * Template *precious_data = g_new0(Template, 1);
4068 * g_pointer protect = g_auto_pointer_new(precious_data);
4070 * gtk_list_store_set(container, &iter,
4074 * ... the gtk_list_store has copied the pointer and
4075 * ... incremented its reference count, we should free
4076 * ... the auto pointer (in C++ a destructor would do
4077 * ... this for us when leaving block scope)
4079 * g_auto_pointer_free(protect);
4081 * ... gtk_list_store_set() now manages the data. When
4082 * ... *explicitly* requesting a pointer from the list
4083 * ... store, don't forget you get a copy that should be
4084 * ... freed with g_auto_pointer_free() eventually.
4088 *\param pointer Pointer to be guarded.
4090 *\return GAuto * Pointer that should be used in containers with
4093 GAuto *g_auto_pointer_new(gpointer p)
4095 AutoPointerRef *ref;
4101 ref = g_new0(AutoPointerRef, 1);
4102 ptr = g_new0(AutoPointer, 1);
4112 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4118 *\brief Allocate an autopointer using the passed \a free function to
4119 * free the guarded pointer
4121 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4128 aptr = g_auto_pointer_new(p);
4129 aptr->ref->free = free_;
4133 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4135 if (auto_ptr == NULL)
4137 return ((AutoPointer *) auto_ptr)->ptr;
4141 *\brief Copies an auto pointer by. It's mostly not necessary
4142 * to call this function directly, unless you copy/assign
4143 * the guarded pointer.
4145 *\param auto_ptr Auto pointer returned by previous call to
4146 * g_auto_pointer_new_XXX()
4148 *\return gpointer An auto pointer
4150 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4153 AutoPointerRef *ref;
4156 if (auto_ptr == NULL)
4161 newp = g_new0(AutoPointer, 1);
4164 newp->ptr = ref->pointer;
4168 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4174 *\brief Free an auto pointer
4176 void g_auto_pointer_free(GAuto *auto_ptr)
4179 AutoPointerRef *ref;
4181 if (auto_ptr == NULL)
4187 if (--(ref->cnt) == 0) {
4189 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4191 ref->free(ref->pointer);
4196 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4201 void replace_returns(gchar *str)
4206 while (strstr(str, "\n")) {
4207 *strstr(str, "\n") = ' ';
4209 while (strstr(str, "\r")) {
4210 *strstr(str, "\r") = ' ';
4214 /* get_uri_part() - retrieves a URI starting from scanpos.
4215 Returns TRUE if succesful */
4216 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4217 const gchar **bp, const gchar **ep, gboolean hdr)
4220 gint parenthese_cnt = 0;
4222 g_return_val_if_fail(start != NULL, FALSE);
4223 g_return_val_if_fail(scanpos != NULL, FALSE);
4224 g_return_val_if_fail(bp != NULL, FALSE);
4225 g_return_val_if_fail(ep != NULL, FALSE);
4229 /* find end point of URI */
4230 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4231 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4232 !IS_ASCII(*(const guchar *)ep_) ||
4233 strchr("[]{}<>\"", *ep_)) {
4235 } else if (strchr("(", *ep_)) {
4237 } else if (strchr(")", *ep_)) {
4238 if (parenthese_cnt > 0)
4245 /* no punctuation at end of string */
4247 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4248 * should pass some URI type to this function and decide on that whether
4249 * to perform punctuation stripping */
4251 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4253 for (; ep_ - 1 > scanpos + 1 &&
4254 IS_REAL_PUNCT(*(ep_ - 1));
4258 #undef IS_REAL_PUNCT
4265 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4267 while (bp && *bp && g_ascii_isspace(*bp))
4269 return g_strndup(bp, ep - bp);
4272 /* valid mail address characters */
4273 #define IS_RFC822_CHAR(ch) \
4277 !g_ascii_isspace(ch) && \
4278 !strchr("(),;<>\"", (ch)))
4280 /* alphabet and number within 7bit ASCII */
4281 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4282 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4284 static GHashTable *create_domain_tab(void)
4286 static const gchar *toplvl_domains [] = {
4288 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4289 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4290 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4291 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4292 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4293 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4294 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4295 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4296 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4297 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4298 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4299 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4300 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4301 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4302 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4303 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4304 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4305 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4306 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4307 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4308 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4309 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4310 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4311 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4312 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4313 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4316 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4318 g_return_val_if_fail(htab, NULL);
4319 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4320 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4324 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4326 const gint MAX_LVL_DOM_NAME_LEN = 6;
4327 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4328 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4331 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4334 for (p = buf; p < m && first < last; *p++ = *first++)
4338 return g_hash_table_lookup(tab, buf) != NULL;
4341 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4342 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4343 const gchar **bp, const gchar **ep, gboolean hdr)
4345 /* more complex than the uri part because we need to scan back and forward starting from
4346 * the scan position. */
4347 gboolean result = FALSE;
4348 const gchar *bp_ = NULL;
4349 const gchar *ep_ = NULL;
4350 static GHashTable *dom_tab;
4351 const gchar *last_dot = NULL;
4352 const gchar *prelast_dot = NULL;
4353 const gchar *last_tld_char = NULL;
4355 /* the informative part of the email address (describing the name
4356 * of the email address owner) may contain quoted parts. the
4357 * closure stack stores the last encountered quotes. */
4358 gchar closure_stack[128];
4359 gchar *ptr = closure_stack;
4361 g_return_val_if_fail(start != NULL, FALSE);
4362 g_return_val_if_fail(scanpos != NULL, FALSE);
4363 g_return_val_if_fail(bp != NULL, FALSE);
4364 g_return_val_if_fail(ep != NULL, FALSE);
4367 const gchar *start_quote = NULL;
4368 const gchar *end_quote = NULL;
4370 /* go to the real start */
4371 if (start[0] == ',')
4373 if (start[0] == ';')
4375 while (start[0] == '\n' || start[0] == '\r')
4377 while (start[0] == ' ' || start[0] == '\t')
4382 /* check if there are quotes (to skip , in them) */
4383 if (*start == '"') {
4384 start_quote = start;
4386 end_quote = strstr(start, "\"");
4392 /* skip anything between quotes */
4393 if (start_quote && end_quote) {
4398 /* find end (either , or ; or end of line) */
4399 if (strstr(start, ",") && strstr(start, ";"))
4400 *ep = strstr(start,",") < strstr(start, ";")
4401 ? strstr(start, ",") : strstr(start, ";");
4402 else if (strstr(start, ","))
4403 *ep = strstr(start, ",");
4404 else if (strstr(start, ";"))
4405 *ep = strstr(start, ";");
4407 *ep = start+strlen(start);
4409 /* go back to real start */
4410 if (start_quote && end_quote) {
4411 start = start_quote;
4414 /* check there's still an @ in that, or search
4415 * further if possible */
4416 if (strstr(start, "@") && strstr(start, "@") < *ep)
4418 else if (*ep < start+strlen(start)) {
4421 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4429 dom_tab = create_domain_tab();
4430 g_return_val_if_fail(dom_tab, FALSE);
4432 /* scan start of address */
4433 for (bp_ = scanpos - 1;
4434 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4437 /* TODO: should start with an alnum? */
4439 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4442 if (bp_ != scanpos) {
4443 /* scan end of address */
4444 for (ep_ = scanpos + 1;
4445 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4447 prelast_dot = last_dot;
4449 if (*(last_dot + 1) == '.') {
4450 if (prelast_dot == NULL)
4452 last_dot = prelast_dot;
4457 /* TODO: really should terminate with an alnum? */
4458 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4463 if (last_dot == NULL)
4465 if (last_dot >= ep_)
4466 last_dot = prelast_dot;
4467 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4471 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4472 if (*last_tld_char == '?')
4475 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4482 if (!result) return FALSE;
4484 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4485 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4486 && IS_RFC822_CHAR(*(ep_ + 3))) {
4487 /* this informative part with an @ in it is
4488 * followed by the email address */
4491 /* go to matching '>' (or next non-rfc822 char, like \n) */
4492 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4495 /* include the bracket */
4496 if (*ep_ == '>') ep_++;
4498 /* include the leading quote */
4506 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4507 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4510 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4511 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4514 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4515 #define IN_STACK() (ptr > closure_stack)
4516 /* has underrun check */
4517 #define POP_STACK() if(IN_STACK()) --ptr
4518 /* has overrun check */
4519 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4520 /* has underrun check */
4521 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4525 /* scan for the informative part. */
4526 for (bp_ -= 2; bp_ >= start; bp_--) {
4527 /* if closure on the stack keep scanning */
4528 if (PEEK_STACK() == *bp_) {
4532 if (*bp_ == '\'' || *bp_ == '"') {
4537 /* if nothing in the closure stack, do the special conditions
4538 * the following if..else expression simply checks whether
4539 * a token is acceptable. if not acceptable, the clause
4540 * should terminate the loop with a 'break' */
4541 if (!PEEK_STACK()) {
4543 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4544 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4545 /* hyphens are allowed, but only in
4547 } else if (strchr(" \"'", *bp_)) {
4548 /* but anything not being a punctiation
4551 break; /* anything else is rejected */
4558 /* scan forward (should start with an alnum) */
4559 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4575 #undef IS_ASCII_ALNUM
4576 #undef IS_RFC822_CHAR
4578 gchar *make_email_string(const gchar *bp, const gchar *ep)
4580 /* returns a mailto: URI; mailto: is also used to detect the
4581 * uri type later on in the button_pressed signal handler */
4585 tmp = g_strndup(bp, ep - bp);
4586 result = g_strconcat("mailto:", tmp, NULL);
4592 gchar *make_http_string(const gchar *bp, const gchar *ep)
4594 /* returns an http: URI; */
4598 while (bp && *bp && g_ascii_isspace(*bp))
4600 tmp = g_strndup(bp, ep - bp);
4601 result = g_strconcat("http://", tmp, NULL);
4607 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4609 FILE *fp = fopen(path, "rb");
4610 gchar buf[BUFFSIZE];
4611 gchar *result = NULL;
4614 while (fgets(buf, sizeof (buf), fp) != NULL) {
4615 gchar **parts = g_strsplit(buf, ";", 3);
4616 gchar *trimmed = parts[0];
4617 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4619 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4620 trimmed[strlen(trimmed)-1] = '\0';
4622 if (!strcmp(trimmed, type)) {
4623 gboolean needsterminal = FALSE;
4624 if (parts[2] && strstr(parts[2], "needsterminal")) {
4625 needsterminal = TRUE;
4627 if (parts[2] && strstr(parts[2], "test=")) {
4628 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4629 gchar *testcmd = orig_testcmd;
4630 if (strstr(testcmd,";"))
4631 *(strstr(testcmd,";")) = '\0';
4632 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4634 while (testcmd[strlen(testcmd)-1] == '\n')
4635 testcmd[strlen(testcmd)-1] = '\0';
4636 while (testcmd[strlen(testcmd)-1] == '\r')
4637 testcmd[strlen(testcmd)-1] = '\0';
4638 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4639 testcmd[strlen(testcmd)-1] = '\0';
4641 if (strstr(testcmd, "%s")) {
4642 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4643 gint res = system(tmp);
4645 g_free(orig_testcmd);
4652 gint res = system(testcmd);
4653 g_free(orig_testcmd);
4663 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4665 while (trimmed[strlen(trimmed)-1] == '\n')
4666 trimmed[strlen(trimmed)-1] = '\0';
4667 while (trimmed[strlen(trimmed)-1] == '\r')
4668 trimmed[strlen(trimmed)-1] = '\0';
4669 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4670 trimmed[strlen(trimmed)-1] = '\0';
4671 result = g_strdup(trimmed);
4674 /* if there are no single quotes around %s, add them.
4675 * '.*%s.*' is ok, as in display 'png:%s'
4677 if (strstr(result, "%s")
4678 && !(strstr(result, "'") < strstr(result,"%s") &&
4679 strstr(strstr(result,"%s"), "'"))) {
4680 gchar *start = g_strdup(result);
4681 gchar *end = g_strdup(strstr(result, "%s")+2);
4683 *strstr(start, "%s") = '\0';
4684 tmp = g_strconcat(start,"'%s'",end, NULL);
4690 if (needsterminal) {
4691 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4702 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4704 gchar *result = NULL;
4708 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4709 result = mailcap_get_command_in_file(path, type, file_to_open);
4713 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4717 void mailcap_update_default(const gchar *type, const gchar *command)
4719 gchar *path = NULL, *outpath = NULL;
4720 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4721 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4722 FILE *fp = fopen(path, "rb");
4723 FILE *outfp = fopen(outpath, "wb");
4724 gchar buf[BUFFSIZE];
4732 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4733 gchar **parts = g_strsplit(buf, ";", 3);
4734 gchar *trimmed = parts[0];
4735 while (trimmed[0] == ' ')
4737 while (trimmed[strlen(trimmed)-1] == ' ')
4738 trimmed[strlen(trimmed)-1] = '\0';
4740 if (!strcmp(trimmed, type)) {
4749 fprintf(outfp, "%s; %s\n", type, command);
4755 g_rename(outpath, path);
4758 gint copy_dir(const gchar *src, const gchar *dst)
4763 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4764 g_warning("failed to open directory: %s\n", src);
4768 if (make_dir(dst) < 0)
4771 while ((name = g_dir_read_name(dir)) != NULL) {
4772 gchar *old_file, *new_file;
4773 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4774 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4775 debug_print("copying: %s -> %s\n", old_file, new_file);
4776 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4777 gint r = copy_file(old_file, new_file, TRUE);
4784 /* Windows has no symlinks. Or well, Vista seems to
4785 have something like this but the semantics might be
4786 different. Thus we don't use it under Windows. */
4787 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4790 gchar *target = g_file_read_link(old_file, &error);
4792 r = symlink(target, new_file);
4799 #endif /*G_OS_WIN32*/
4800 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4801 gint r = copy_dir(old_file, new_file);
4812 /* crude test to see if a file is an email. */
4813 gboolean file_is_email (const gchar *filename)
4819 if (filename == NULL)
4821 if ((fp = g_fopen(filename, "rb")) == NULL)
4823 while (i < 60 && score < 3
4824 && fgets(buffer, sizeof (buffer), fp) > 0) {
4825 if (!strncmp(buffer, "From:", strlen("From:")))
4827 if (!strncmp(buffer, "To:", strlen("To:")))
4829 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4834 return (score >= 3);
4837 gboolean sc_g_list_bigger(GList *list, gint max)
4841 while (cur && i <= max+1) {
4848 gboolean sc_g_slist_bigger(GSList *list, gint max)
4852 while (cur && i <= max+1) {
4859 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4860 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4861 NULL, NULL, NULL, NULL, NULL, NULL};
4862 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4863 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4864 NULL, NULL, NULL, NULL, NULL, NULL};
4865 const gchar *s_am_up = NULL;
4866 const gchar *s_pm_up = NULL;
4867 const gchar *s_am_low = NULL;
4868 const gchar *s_pm_low = NULL;
4869 const gchar *def_loc_format = NULL;
4870 const gchar *date_loc_format = NULL;
4871 const gchar *time_loc_format = NULL;
4872 const gchar *time_am_pm = NULL;
4874 static gboolean time_names_init_done = FALSE;
4876 static void init_time_names(void)
4878 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4879 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4880 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4881 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4882 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4883 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4884 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4886 monthnames[0] = Q_("Complete month name for use by strftime|January");
4887 monthnames[1] = Q_("Complete month name for use by strftime|February");
4888 monthnames[2] = Q_("Complete month name for use by strftime|March");
4889 monthnames[3] = Q_("Complete month name for use by strftime|April");
4890 monthnames[4] = Q_("Complete month name for use by strftime|May");
4891 monthnames[5] = Q_("Complete month name for use by strftime|June");
4892 monthnames[6] = Q_("Complete month name for use by strftime|July");
4893 monthnames[7] = Q_("Complete month name for use by strftime|August");
4894 monthnames[8] = Q_("Complete month name for use by strftime|September");
4895 monthnames[9] = Q_("Complete month name for use by strftime|October");
4896 monthnames[10] = Q_("Complete month name for use by strftime|November");
4897 monthnames[11] = Q_("Complete month name for use by strftime|December");
4899 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4900 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4901 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4902 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4903 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4904 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4905 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4907 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4908 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4909 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4910 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4911 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4912 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4913 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4914 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4915 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4916 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4917 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4918 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4920 s_am_up = Q_("For use by strftime (morning)|AM");
4921 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4922 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4923 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4925 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4926 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4927 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4929 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4931 time_names_init_done = TRUE;
4934 #define CHECK_SIZE() { \
4935 total_done += len; \
4936 if (total_done >= buflen) { \
4937 buf[buflen-1] = '\0'; \
4942 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4944 gchar *curpos = buf;
4945 gint total_done = 0;
4946 gchar subbuf[64], subfmt[64];
4947 static time_t last_tzset = (time_t)0;
4949 if (!time_names_init_done)
4952 if (format == NULL || lt == NULL)
4955 if (last_tzset != time(NULL)) {
4957 last_tzset = time(NULL);
4960 if (*format == '%') {
4961 gint len = 0, tmp = 0;
4965 len = 1; CHECK_SIZE();
4969 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4970 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4973 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4974 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4978 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4979 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4982 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4983 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4986 fast_strftime(subbuf, 64, def_loc_format, lt);
4987 len = strlen(subbuf); CHECK_SIZE();
4988 strncpy2(curpos, subbuf, buflen - total_done);
4991 total_done += 2; CHECK_SIZE();
4992 tmp = (lt->tm_year + 1900)/100;
4993 *curpos++ = '0'+(tmp / 10);
4994 *curpos++ = '0'+(tmp % 10);
4997 total_done += 2; CHECK_SIZE();
4998 *curpos++ = '0'+(lt->tm_mday / 10);
4999 *curpos++ = '0'+(lt->tm_mday % 10);
5002 total_done += 8; CHECK_SIZE();
5003 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5004 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5006 *curpos++ = '0'+(lt->tm_mday / 10);
5007 *curpos++ = '0'+(lt->tm_mday % 10);
5009 tmp = lt->tm_year%100;
5010 *curpos++ = '0'+(tmp / 10);
5011 *curpos++ = '0'+(tmp % 10);
5014 len = 2; CHECK_SIZE();
5015 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5018 len = 10; CHECK_SIZE();
5019 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5020 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5023 total_done += 2; CHECK_SIZE();
5024 *curpos++ = '0'+(lt->tm_hour / 10);
5025 *curpos++ = '0'+(lt->tm_hour % 10);
5028 total_done += 2; CHECK_SIZE();
5034 *curpos++ = '0'+(tmp / 10);
5035 *curpos++ = '0'+(tmp % 10);
5038 len = 3; CHECK_SIZE();
5039 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5042 len = 2; CHECK_SIZE();
5043 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5046 len = 2; CHECK_SIZE();
5052 snprintf(curpos, buflen - total_done, "%2d", tmp);
5055 total_done += 2; CHECK_SIZE();
5056 tmp = lt->tm_mon + 1;
5057 *curpos++ = '0'+(tmp / 10);
5058 *curpos++ = '0'+(tmp % 10);
5061 total_done += 2; CHECK_SIZE();
5062 *curpos++ = '0'+(lt->tm_min / 10);
5063 *curpos++ = '0'+(lt->tm_min % 10);
5066 len = 1; CHECK_SIZE();
5070 if (lt->tm_hour >= 12) {
5071 len = strlen(s_pm_up); CHECK_SIZE();
5072 snprintf(curpos, buflen-total_done, s_pm_up);
5074 len = strlen(s_am_up); CHECK_SIZE();
5075 snprintf(curpos, buflen-total_done, s_am_up);
5079 if (lt->tm_hour >= 12) {
5080 len = strlen(s_pm_low); CHECK_SIZE();
5081 snprintf(curpos, buflen-total_done, s_pm_low);
5083 len = strlen(s_am_low); CHECK_SIZE();
5084 snprintf(curpos, buflen-total_done, s_am_low);
5088 fast_strftime(subbuf, 64, time_am_pm, lt);
5089 len = strlen(subbuf); CHECK_SIZE();
5090 strncpy2(curpos, subbuf, buflen - total_done);
5093 total_done += 5; CHECK_SIZE();
5094 *curpos++ = '0'+(lt->tm_hour / 10);
5095 *curpos++ = '0'+(lt->tm_hour % 10);
5097 *curpos++ = '0'+(lt->tm_min / 10);
5098 *curpos++ = '0'+(lt->tm_min % 10);
5101 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5102 len = strlen(subbuf); CHECK_SIZE();
5103 strncpy2(curpos, subbuf, buflen - total_done);
5106 total_done += 2; CHECK_SIZE();
5107 *curpos++ = '0'+(lt->tm_sec / 10);
5108 *curpos++ = '0'+(lt->tm_sec % 10);
5111 len = 1; CHECK_SIZE();
5115 total_done += 8; CHECK_SIZE();
5116 *curpos++ = '0'+(lt->tm_hour / 10);
5117 *curpos++ = '0'+(lt->tm_hour % 10);
5119 *curpos++ = '0'+(lt->tm_min / 10);
5120 *curpos++ = '0'+(lt->tm_min % 10);
5122 *curpos++ = '0'+(lt->tm_sec / 10);
5123 *curpos++ = '0'+(lt->tm_sec % 10);
5126 len = 1; CHECK_SIZE();
5127 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5130 len = 1; CHECK_SIZE();
5131 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5134 fast_strftime(subbuf, 64, date_loc_format, lt);
5135 len = strlen(subbuf); CHECK_SIZE();
5136 strncpy2(curpos, subbuf, buflen - total_done);
5139 fast_strftime(subbuf, 64, time_loc_format, lt);
5140 len = strlen(subbuf); CHECK_SIZE();
5141 strncpy2(curpos, subbuf, buflen - total_done);
5144 total_done += 2; CHECK_SIZE();
5145 tmp = lt->tm_year%100;
5146 *curpos++ = '0'+(tmp / 10);
5147 *curpos++ = '0'+(tmp % 10);
5150 len = 4; CHECK_SIZE();
5151 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5161 /* let these complicated ones be done with the libc */
5162 snprintf(subfmt, 64, "%%%c", *format);
5163 strftime(subbuf, 64, subfmt, lt);
5164 len = strlen(subbuf); CHECK_SIZE();
5165 strncpy2(curpos, subbuf, buflen - total_done);
5169 /* let these complicated modifiers be done with the libc */
5170 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5171 strftime(subbuf, 64, subfmt, lt);
5172 len = strlen(subbuf); CHECK_SIZE();
5173 strncpy2(curpos, subbuf, buflen - total_done);
5177 g_warning("format error (%c)", *format);
5184 int len = 1; CHECK_SIZE();
5185 *curpos++ = *format++;