2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
65 #include <osso-browser-interface.h>
70 #include "../codeconv.h"
74 static gboolean debug_mode = FALSE;
76 static GSList *tempfiles=NULL;
79 /* Return true if we are running as root. This function should beused
80 instead of getuid () == 0. */
81 gboolean superuser_p (void)
84 return w32_is_administrator ();
92 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
93 gint g_chdir(const gchar *path)
96 if (G_WIN32_HAVE_WIDECHAR_API()) {
101 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
107 retval = _wchdir(wpath);
119 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
120 if (cp_path == NULL) {
125 retval = chdir(cp_path);
138 gint g_chmod(const gchar *path, gint mode)
141 if (G_WIN32_HAVE_WIDECHAR_API()) {
146 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
152 retval = _wchmod(wpath, mode);
164 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
165 if (cp_path == NULL) {
170 retval = chmod(cp_path, mode);
179 return chmod(path, mode);
182 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
186 gint mkstemp_name(const gchar *template, gchar **name_used)
188 static gulong count=0; /* W32-_mktemp only supports up to 27
192 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
193 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
194 (S_IRUSR | S_IWUSR));
196 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
198 perror(g_strdup_printf("cant create %s",*name_used));
204 #endif /* G_OS_WIN32 */
207 gint mkstemp(const gchar *template)
210 gint res = mkstemp_name(template, &dummyname);
214 #endif /* G_OS_WIN32 */
216 void list_free_strings(GList *list)
218 list = g_list_first(list);
220 while (list != NULL) {
226 void slist_free_strings(GSList *list)
228 while (list != NULL) {
234 GSList *slist_concat_unique (GSList *first, GSList *second)
242 } else if (second == NULL)
245 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
246 if (g_slist_find(ret, tmp->data) == NULL)
247 ret = g_slist_prepend(ret, tmp->data);
252 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
257 void hash_free_strings(GHashTable *table)
259 g_hash_table_foreach(table, hash_free_strings_func, NULL);
262 static void hash_free_value_mem_func(gpointer key, gpointer value,
268 void hash_free_value_mem(GHashTable *table)
270 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
273 gint str_case_equal(gconstpointer v, gconstpointer v2)
275 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
278 guint str_case_hash(gconstpointer key)
280 const gchar *p = key;
284 h = g_ascii_tolower(h);
285 for (p += 1; *p != '\0'; p++)
286 h = (h << 5) - h + g_ascii_tolower(*p);
292 void ptr_array_free_strings(GPtrArray *array)
297 g_return_if_fail(array != NULL);
299 for (i = 0; i < array->len; i++) {
300 str = g_ptr_array_index(array, i);
305 gboolean str_find(const gchar *haystack, const gchar *needle)
307 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
310 gboolean str_case_find(const gchar *haystack, const gchar *needle)
312 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
315 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
317 return strcmp(haystack, needle) == 0;
320 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
322 return g_ascii_strcasecmp(haystack, needle) == 0;
325 gint to_number(const gchar *nstr)
327 register const gchar *p;
329 if (*nstr == '\0') return -1;
331 for (p = nstr; *p != '\0'; p++)
332 if (!g_ascii_isdigit(*p)) return -1;
337 /* convert integer into string,
338 nstr must be not lower than 11 characters length */
339 gchar *itos_buf(gchar *nstr, gint n)
341 g_snprintf(nstr, 11, "%d", n);
345 /* convert integer into string */
348 static gchar nstr[11];
350 return itos_buf(nstr, n);
353 #define divide(num,divisor,i,d) \
355 i = num >> divisor; \
356 d = num & ((1<<divisor)-1); \
357 d = (d*100) >> divisor; \
360 gchar *to_human_readable(off_t size)
362 static gchar str[14];
363 static gchar *b_format = NULL, *kb_format = NULL,
364 *mb_format = NULL, *gb_format = NULL;
365 register int t = 0, r = 0;
366 if (b_format == NULL) {
368 kb_format = _("%d.%02dKB");
369 mb_format = _("%d.%02dMB");
370 gb_format = _("%.2fGB");
373 if (size < (off_t)1024) {
374 g_snprintf(str, sizeof(str), b_format, (gint)size);
376 } else if (size >> 10 < (off_t)1024) {
377 divide(size, 10, t, r);
378 g_snprintf(str, sizeof(str), kb_format, t, r);
380 } else if (size >> 20 < (off_t)1024) {
381 divide(size, 20, t, r);
382 g_snprintf(str, sizeof(str), mb_format, t, r);
385 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
390 /* strcmp with NULL-checking */
391 gint strcmp2(const gchar *s1, const gchar *s2)
393 if (s1 == NULL || s2 == NULL)
396 return strcmp(s1, s2);
398 /* strstr with NULL-checking */
399 gchar *strstr2(const gchar *s1, const gchar *s2)
401 if (s1 == NULL || s2 == NULL)
404 return strstr(s1, s2);
407 gint path_cmp(const gchar *s1, const gchar *s2)
412 gchar *s1buf, *s2buf;
415 if (s1 == NULL || s2 == NULL) return -1;
416 if (*s1 == '\0' || *s2 == '\0') return -1;
419 s1buf = g_strdup (s1);
420 s2buf = g_strdup (s2);
421 subst_char (s1buf, '/', G_DIR_SEPARATOR);
422 subst_char (s2buf, '/', G_DIR_SEPARATOR);
425 #endif /* !G_OS_WIN32 */
430 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
431 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
433 rc = strncmp(s1, s2, MAX(len1, len2));
437 #endif /* !G_OS_WIN32 */
441 /* remove trailing return code */
442 gchar *strretchomp(gchar *str)
446 if (!*str) return str;
448 for (s = str + strlen(str) - 1;
449 s >= str && (*s == '\n' || *s == '\r');
456 /* remove trailing character */
457 gchar *strtailchomp(gchar *str, gchar tail_char)
461 if (!*str) return str;
462 if (tail_char == '\0') return str;
464 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
470 /* remove CR (carriage return) */
471 gchar *strcrchomp(gchar *str)
475 if (!*str) return str;
477 s = str + strlen(str) - 1;
478 if (*s == '\n' && s > str && *(s - 1) == '\r') {
486 void file_strip_crs(const gchar *file)
488 FILE *fp = NULL, *outfp = NULL;
490 gchar *out = get_tmp_file();
494 fp = fopen(file, "rb");
498 outfp = fopen(out, "wb");
504 while (fgets(buf, sizeof (buf), fp) != NULL) {
511 rename_force(out, file);
516 /* Similar to `strstr' but this function ignores the case of both strings. */
517 gchar *strcasestr(const gchar *haystack, const gchar *needle)
519 register size_t haystack_len, needle_len;
521 haystack_len = strlen(haystack);
522 needle_len = strlen(needle);
524 if (haystack_len < needle_len || needle_len == 0)
527 while (haystack_len >= needle_len) {
528 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
529 return (gchar *)haystack;
539 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
540 gconstpointer needle, size_t needlelen)
542 const gchar *haystack_ = (const gchar *)haystack;
543 const gchar *needle_ = (const gchar *)needle;
544 const gchar *haystack_cur = (const gchar *)haystack;
547 return memchr(haystack_, *needle_, haystacklen);
549 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
551 if (haystacklen - (haystack_cur - haystack_) < needlelen)
553 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
554 return (gpointer)haystack_cur;
562 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
563 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
565 register const gchar *s = src;
566 register gchar *d = dest;
576 /* Examine if next block is non-ASCII string */
577 gboolean is_next_nonascii(const gchar *s)
581 /* skip head space */
582 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
584 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
585 if (*(guchar *)p > 127 || *(guchar *)p < 32)
592 gint get_next_word_len(const gchar *s)
596 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
602 /* compare subjects */
603 gint subject_compare(const gchar *s1, const gchar *s2)
607 if (!s1 || !s2) return -1;
608 if (!*s1 || !*s2) return -1;
610 Xstrdup_a(str1, s1, return -1);
611 Xstrdup_a(str2, s2, return -1);
613 trim_subject_for_compare(str1);
614 trim_subject_for_compare(str2);
616 if (!*str1 || !*str2) return -1;
618 return strcmp(str1, str2);
621 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
625 if (!s1 || !s2) return -1;
627 Xstrdup_a(str1, s1, return -1);
628 Xstrdup_a(str2, s2, return -1);
630 trim_subject_for_sort(str1);
631 trim_subject_for_sort(str2);
633 return g_utf8_collate(str1, str2);
636 void trim_subject_for_compare(gchar *str)
640 eliminate_parenthesis(str, '[', ']');
641 eliminate_parenthesis(str, '(', ')');
644 srcp = str + subject_get_prefix_length(str);
646 memmove(str, srcp, strlen(srcp) + 1);
649 void trim_subject_for_sort(gchar *str)
655 srcp = str + subject_get_prefix_length(str);
657 memmove(str, srcp, strlen(srcp) + 1);
660 void trim_subject(gchar *str)
662 register gchar *srcp;
668 srcp = str + subject_get_prefix_length(str);
673 } else if (*srcp == '(') {
685 else if (*srcp == cl)
692 while (g_ascii_isspace(*srcp)) srcp++;
693 memmove(str, srcp, strlen(srcp) + 1);
696 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
698 register gchar *srcp, *destp;
703 while ((destp = strchr(destp, op))) {
709 else if (*srcp == cl)
715 while (g_ascii_isspace(*srcp)) srcp++;
716 memmove(destp, srcp, strlen(srcp) + 1);
720 void extract_parenthesis(gchar *str, gchar op, gchar cl)
722 register gchar *srcp, *destp;
727 while ((srcp = strchr(destp, op))) {
730 memmove(destp, srcp + 1, strlen(srcp));
735 else if (*destp == cl)
747 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
750 register gchar *srcp, *destp;
752 gboolean in_quote = FALSE;
756 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
759 memmove(destp, srcp + 1, strlen(srcp));
762 if (*destp == op && !in_quote)
764 else if (*destp == cl && !in_quote)
766 else if (*destp == quote_chr)
778 void eliminate_quote(gchar *str, gchar quote_chr)
780 register gchar *srcp, *destp;
784 while ((destp = strchr(destp, quote_chr))) {
785 if ((srcp = strchr(destp + 1, quote_chr))) {
787 while (g_ascii_isspace(*srcp)) srcp++;
788 memmove(destp, srcp, strlen(srcp) + 1);
796 void extract_quote(gchar *str, gchar quote_chr)
800 if ((str = strchr(str, quote_chr))) {
802 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
803 memmove(p - 1, p, strlen(p) + 1);
808 memmove(str, str + 1, p - str);
813 void eliminate_address_comment(gchar *str)
815 register gchar *srcp, *destp;
820 while ((destp = strchr(destp, '"'))) {
821 if ((srcp = strchr(destp + 1, '"'))) {
826 while (g_ascii_isspace(*srcp)) srcp++;
827 memmove(destp, srcp, strlen(srcp) + 1);
837 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
843 else if (*srcp == ')')
849 while (g_ascii_isspace(*srcp)) srcp++;
850 memmove(destp, srcp, strlen(srcp) + 1);
854 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
856 gboolean in_quote = FALSE;
859 if (*str == c && !in_quote)
861 if (*str == quote_chr)
869 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
871 gboolean in_quote = FALSE;
874 p = str + strlen(str) - 1;
876 if (*p == c && !in_quote)
886 void extract_address(gchar *str)
888 eliminate_address_comment(str);
889 if (strchr_with_skip_quote(str, '"', '<'))
890 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
894 void extract_list_id_str(gchar *str)
896 if (strchr_with_skip_quote(str, '"', '<'))
897 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
901 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
906 if (!str) return addr_list;
908 Xstrdup_a(work, str, return addr_list);
911 eliminate_address_comment(work);
914 while (workp && *workp) {
917 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
923 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
924 extract_parenthesis_with_skip_quote
925 (workp, '"', '<', '>');
929 addr_list = g_slist_append(addr_list, g_strdup(workp));
937 GSList *address_list_append(GSList *addr_list, const gchar *str)
939 return address_list_append_real(addr_list, str, TRUE);
942 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
944 return address_list_append_real(addr_list, str, FALSE);
947 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
951 if (!str) return msgid_list;
954 while (strp && *strp) {
955 const gchar *start, *end;
958 if ((start = strchr(strp, '<')) != NULL) {
959 end = strchr(start + 1, '>');
964 msgid = g_strndup(start + 1, end - start - 1);
967 msgid_list = g_slist_prepend(msgid_list, msgid);
977 GSList *references_list_append(GSList *msgid_list, const gchar *str)
981 list = references_list_prepend(NULL, str);
982 list = g_slist_reverse(list);
983 msgid_list = g_slist_concat(msgid_list, list);
988 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
993 if (!str) return group_list;
995 Xstrdup_a(work, str, return group_list);
999 while (workp && *workp) {
1002 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1010 group_list = g_slist_append(group_list,
1019 GList *add_history(GList *list, const gchar *str)
1023 g_return_val_if_fail(str != NULL, list);
1025 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1028 list = g_list_remove(list, old->data);
1029 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1032 last = g_list_last(list);
1035 list = g_list_remove(list, last->data);
1039 list = g_list_prepend(list, g_strdup(str));
1044 void remove_return(gchar *str)
1046 register gchar *p = str;
1049 if (*p == '\n' || *p == '\r')
1050 memmove(p, p + 1, strlen(p));
1056 void remove_space(gchar *str)
1058 register gchar *p = str;
1063 while (g_ascii_isspace(*(p + spc)))
1066 memmove(p, p + spc, strlen(p + spc) + 1);
1072 void unfold_line(gchar *str)
1074 register gchar *p = str;
1078 if (*p == '\n' || *p == '\r') {
1081 while (g_ascii_isspace(*(p + spc)))
1084 memmove(p, p + spc, strlen(p + spc) + 1);
1090 void subst_char(gchar *str, gchar orig, gchar subst)
1092 register gchar *p = str;
1101 void subst_chars(gchar *str, gchar *orig, gchar subst)
1103 register gchar *p = str;
1106 if (strchr(orig, *p) != NULL)
1112 void subst_for_filename(gchar *str)
1117 subst_chars(str, "\t\r\n\\/*:", '_');
1119 subst_chars(str, "\t\r\n\\/*", '_');
1123 void subst_for_shellsafe_filename(gchar *str)
1127 subst_for_filename(str);
1128 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1131 gboolean is_header_line(const gchar *str)
1133 if (str[0] == ':') return FALSE;
1135 while (*str != '\0' && *str != ' ') {
1144 gboolean is_ascii_str(const gchar *str)
1146 const guchar *p = (const guchar *)str;
1148 while (*p != '\0') {
1149 if (*p != '\t' && *p != ' ' &&
1150 *p != '\r' && *p != '\n' &&
1151 (*p < 32 || *p >= 127))
1159 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1161 const gchar *first_pos;
1162 const gchar *last_pos;
1163 const gchar *p = str;
1164 gint quote_level = -1;
1166 /* speed up line processing by only searching to the last '>' */
1167 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1168 /* skip a line if it contains a '<' before the initial '>' */
1169 if (memchr(str, '<', first_pos - str) != NULL)
1171 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1175 while (p <= last_pos) {
1176 while (p < last_pos) {
1177 if (g_ascii_isspace(*p))
1183 if (strchr(quote_chars, *p))
1185 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1186 /* any characters are allowed except '-' and space */
1188 && !strchr(quote_chars, *p)
1189 && !g_ascii_isspace(*p)
1192 if (strchr(quote_chars, *p))
1204 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1206 const gchar *p = str, *q;
1207 gint cur_line = 0, len;
1209 while ((q = strchr(p, '\n')) != NULL) {
1211 if (len > max_chars) {
1221 if (len > max_chars) {
1230 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1232 gchar * position = NULL;
1233 gchar * tmp_pos = NULL;
1236 if (quote_chars == NULL)
1239 for (i = 0; i < strlen(quote_chars); i++) {
1240 tmp_pos = strchr (str, quote_chars[i]);
1242 || (tmp_pos != NULL && position >= tmp_pos) )
1248 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1250 gchar * position = NULL;
1251 gchar * tmp_pos = NULL;
1254 if (quote_chars == NULL)
1257 for (i = 0; i < strlen(quote_chars); i++) {
1258 tmp_pos = strrchr (str, quote_chars[i]);
1260 || (tmp_pos != NULL && position <= tmp_pos) )
1266 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1268 register guint haystack_len, needle_len;
1269 gboolean in_squote = FALSE, in_dquote = FALSE;
1271 haystack_len = strlen(haystack);
1272 needle_len = strlen(needle);
1274 if (haystack_len < needle_len || needle_len == 0)
1277 while (haystack_len >= needle_len) {
1278 if (!in_squote && !in_dquote &&
1279 !strncmp(haystack, needle, needle_len))
1280 return (gchar *)haystack;
1282 /* 'foo"bar"' -> foo"bar"
1283 "foo'bar'" -> foo'bar' */
1284 if (*haystack == '\'') {
1287 else if (!in_dquote)
1289 } else if (*haystack == '\"') {
1292 else if (!in_squote)
1303 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1306 gchar quote_chr = '"';
1308 gboolean in_quote = FALSE;
1312 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1316 if (*p == op && !in_quote)
1318 else if (*p == cl && !in_quote)
1320 else if (*p == quote_chr)
1333 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1336 GSList *string_list = NULL, *slist;
1338 const gchar *s_op, *s_cl;
1341 g_return_val_if_fail(str != NULL, NULL);
1344 max_tokens = G_MAXINT;
1346 s_op = strchr_with_skip_quote(str, '"', op);
1347 if (!s_op) return NULL;
1349 s_cl = strchr_parenthesis_close(str, op, cl);
1357 new_string = g_new(gchar, len + 1);
1358 strncpy(new_string, str, len);
1359 new_string[len] = 0;
1360 string_list = g_slist_prepend(string_list, new_string);
1364 while (*str && g_ascii_isspace(*str)) str++;
1366 string_list = g_slist_prepend(string_list,
1369 s_op = strchr_with_skip_quote(str, '"', op);
1370 if (!--max_tokens || !s_op) break;
1374 s_cl = strchr_parenthesis_close(str, op, cl);
1375 } while (--max_tokens && s_cl);
1378 str_array = g_new(gchar*, n);
1382 str_array[i--] = NULL;
1383 for (slist = string_list; slist; slist = slist->next)
1384 str_array[i--] = slist->data;
1386 g_slist_free(string_list);
1391 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1394 GSList *string_list = NULL, *slist;
1395 gchar **str_array, *s, *new_str;
1396 guint i, n = 1, len;
1398 g_return_val_if_fail(str != NULL, NULL);
1399 g_return_val_if_fail(delim != NULL, NULL);
1402 max_tokens = G_MAXINT;
1404 s = strstr_with_skip_quote(str, delim);
1406 guint delimiter_len = strlen(delim);
1410 new_str = g_strndup(str, len);
1412 if (new_str[0] == '\'' || new_str[0] == '\"') {
1413 if (new_str[len - 1] == new_str[0]) {
1414 new_str[len - 1] = '\0';
1415 memmove(new_str, new_str + 1, len - 1);
1418 string_list = g_slist_prepend(string_list, new_str);
1420 str = s + delimiter_len;
1421 s = strstr_with_skip_quote(str, delim);
1422 } while (--max_tokens && s);
1426 new_str = g_strdup(str);
1427 if (new_str[0] == '\'' || new_str[0] == '\"') {
1429 if (new_str[len - 1] == new_str[0]) {
1430 new_str[len - 1] = '\0';
1431 memmove(new_str, new_str + 1, len - 1);
1434 string_list = g_slist_prepend(string_list, new_str);
1438 str_array = g_new(gchar*, n);
1442 str_array[i--] = NULL;
1443 for (slist = string_list; slist; slist = slist->next)
1444 str_array[i--] = slist->data;
1446 g_slist_free(string_list);
1451 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1453 gchar *abbrev_group;
1455 const gchar *p = group;
1458 g_return_val_if_fail(group != NULL, NULL);
1460 last = group + strlen(group);
1461 abbrev_group = ap = g_malloc(strlen(group) + 1);
1466 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1468 while (*p != '.') p++;
1471 return abbrev_group;
1476 return abbrev_group;
1479 gchar *trim_string(const gchar *str, gint len)
1481 const gchar *p = str;
1486 if (!str) return NULL;
1487 if (strlen(str) <= len)
1488 return g_strdup(str);
1489 if (g_utf8_validate(str, -1, NULL) == FALSE)
1490 return g_strdup(str);
1492 while (*p != '\0') {
1493 mb_len = g_utf8_skip[*(guchar *)p];
1496 else if (new_len + mb_len > len)
1503 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1504 return g_strconcat(new_str, "...", NULL);
1507 GList *uri_list_extract_filenames(const gchar *uri_list)
1509 GList *result = NULL;
1511 gchar *escaped_utf8uri;
1517 while (g_ascii_isspace(*p)) p++;
1518 if (!strncmp(p, "file:", 5)) {
1521 while (*q && *q != '\n' && *q != '\r') q++;
1524 gchar *file, *locale_file = NULL;
1526 while (q > p && g_ascii_isspace(*q))
1528 Xalloca(escaped_utf8uri, q - p + 2,
1530 Xalloca(file, q - p + 2,
1533 strncpy(escaped_utf8uri, p, q - p + 1);
1534 escaped_utf8uri[q - p + 1] = '\0';
1535 decode_uri(file, escaped_utf8uri);
1537 * g_filename_from_uri() rejects escaped/locale encoded uri
1538 * string which come from Nautilus.
1540 if (g_utf8_validate(file, -1, NULL))
1542 = conv_codeset_strdup(
1545 conv_get_locale_charset_str());
1547 locale_file = g_strdup(file + 5);
1548 result = g_list_append(result, locale_file);
1552 p = strchr(p, '\n');
1559 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1562 static gint axtoi(const gchar *hexstr)
1564 gint hi, lo, result;
1567 if ('0' <= hi && hi <= '9') {
1570 if ('a' <= hi && hi <= 'f') {
1573 if ('A' <= hi && hi <= 'F') {
1578 if ('0' <= lo && lo <= '9') {
1581 if ('a' <= lo && lo <= 'f') {
1584 if ('A' <= lo && lo <= 'F') {
1587 result = lo + (16 * hi);
1591 gboolean is_uri_string(const gchar *str)
1593 while (str && *str && g_ascii_isspace(*str))
1595 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1596 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1597 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1598 g_ascii_strncasecmp(str, "www.", 4) == 0);
1601 gchar *get_uri_path(const gchar *uri)
1603 while (uri && *uri && g_ascii_isspace(*uri))
1605 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1606 return (gchar *)(uri + 7);
1607 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1608 return (gchar *)(uri + 8);
1609 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1610 return (gchar *)(uri + 6);
1612 return (gchar *)uri;
1615 gint get_uri_len(const gchar *str)
1619 if (is_uri_string(str)) {
1620 for (p = str; *p != '\0'; p++) {
1621 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1630 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1631 * plusses, and escape characters are used)
1633 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1635 gchar *dec = decoded_uri;
1636 const gchar *enc = encoded_uri;
1641 if (isxdigit((guchar)enc[0]) &&
1642 isxdigit((guchar)enc[1])) {
1648 if (with_plus && *enc == '+')
1660 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1662 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1665 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1666 gchar **subject, gchar **body, gchar **attach)
1670 const gchar *forbidden_uris[] = { ".gnupg/",
1675 Xstrdup_a(tmp_mailto, mailto, return -1);
1677 if (!strncmp(tmp_mailto, "mailto:", 7))
1680 p = strchr(tmp_mailto, '?');
1687 *to = g_strdup(tmp_mailto);
1690 gchar *field, *value;
1707 if (*value == '\0') continue;
1709 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1710 *cc = g_strdup(value);
1711 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1712 *bcc = g_strdup(value);
1713 } else if (subject && !*subject &&
1714 !g_ascii_strcasecmp(field, "subject")) {
1715 *subject = g_malloc(strlen(value) + 1);
1716 decode_uri(*subject, value);
1717 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1718 *body = g_malloc(strlen(value) + 1);
1719 decode_uri(*body, value);
1720 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1722 *attach = g_malloc(strlen(value) + 1);
1723 decode_uri(*attach, value);
1724 for (; forbidden_uris[i]; i++) {
1725 if (strstr(*attach, forbidden_uris[i])) {
1726 printf("Refusing to attach '%s', potential private data leak\n",
1741 #include <windows.h>
1742 #ifndef CSIDL_APPDATA
1743 #define CSIDL_APPDATA 0x001a
1745 #ifndef CSIDL_LOCAL_APPDATA
1746 #define CSIDL_LOCAL_APPDATA 0x001c
1748 #ifndef CSIDL_FLAG_CREATE
1749 #define CSIDL_FLAG_CREATE 0x8000
1751 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1755 w32_strerror (int w32_errno)
1757 static char strerr[256];
1758 int ec = (int)GetLastError ();
1762 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1763 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1764 strerr, DIM (strerr)-1, NULL);
1768 static __inline__ void *
1769 dlopen (const char * name, int flag)
1771 void * hd = LoadLibrary (name);
1775 static __inline__ void *
1776 dlsym (void * hd, const char * sym)
1780 void * fnc = GetProcAddress (hd, sym);
1789 static __inline__ const char *
1792 return w32_strerror (0);
1796 static __inline__ int
1808 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1810 static int initialized;
1811 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1815 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1821 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1823 handle = dlopen (dllnames[i], RTLD_LAZY);
1826 func = dlsym (handle, "SHGetFolderPathA");
1837 return func (a,b,c,d,e);
1842 /* Returns a static string with the directroy from which the module
1843 has been loaded. Returns an empty string on error. */
1844 static char *w32_get_module_dir(void)
1846 static char *moddir;
1849 char name[MAX_PATH+10];
1852 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1855 p = strrchr (name, '\\');
1861 moddir = g_strdup (name);
1865 #endif /* G_OS_WIN32 */
1867 /* Return a static string with the locale dir. */
1868 const gchar *get_locale_dir(void)
1870 static gchar *loc_dir;
1874 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1875 "\\share\\locale", NULL);
1878 loc_dir = LOCALEDIR;
1884 const gchar *get_home_dir(void)
1887 static char home_dir[MAX_PATH] = "";
1889 if (home_dir[0] == '\0') {
1890 if (w32_shgetfolderpath
1891 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1892 NULL, 0, home_dir) < 0)
1893 strcpy (home_dir, "C:\\Sylpheed");
1897 static const gchar *homeenv = NULL;
1902 if (!homeenv && g_getenv("HOME") != NULL)
1903 homeenv = g_strdup(g_getenv("HOME"));
1905 homeenv = g_get_home_dir();
1911 const gchar *get_rc_dir(void)
1913 static gchar *rc_dir = NULL;
1916 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1922 const gchar *get_mail_base_dir(void)
1925 static gchar *mail_base_dir = NULL;
1928 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1931 return mail_base_dir;
1933 return get_home_dir();
1938 const gchar *prefs_common_get_data_root(void);
1939 gchar *last_data_root = NULL;
1942 const gchar *get_news_cache_dir(void)
1944 static gchar *news_cache_dir = NULL;
1946 const gchar *data_root = prefs_common_get_data_root();
1947 if (strcmp2(data_root, last_data_root)) {
1948 g_free(news_cache_dir);
1949 news_cache_dir = NULL;
1952 if (!news_cache_dir)
1954 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1955 NEWS_CACHE_DIR, NULL);
1959 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1960 "Claws", G_DIR_SEPARATOR_S,
1961 g_get_user_name(), G_DIR_SEPARATOR_S,
1962 NEWS_CACHE_DIR, NULL);
1963 g_free(last_data_root);
1964 last_data_root = g_strdup(last_data_root);
1966 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 NEWS_CACHE_DIR, NULL);
1968 g_free(last_data_root);
1969 last_data_root = NULL;
1973 return news_cache_dir;
1976 const gchar *get_imap_cache_dir(void)
1978 static gchar *imap_cache_dir = NULL;
1980 const gchar *data_root = prefs_common_get_data_root();
1981 if (strcmp2(data_root, last_data_root)) {
1982 g_free(imap_cache_dir);
1983 imap_cache_dir = NULL;
1987 if (!imap_cache_dir)
1989 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1990 IMAP_CACHE_DIR, NULL);
1994 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1995 "Claws", G_DIR_SEPARATOR_S,
1996 g_get_user_name(), G_DIR_SEPARATOR_S,
1997 IMAP_CACHE_DIR, NULL);
1998 g_free(last_data_root);
1999 last_data_root = g_strdup(last_data_root);
2001 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2002 IMAP_CACHE_DIR, NULL);
2003 g_free(last_data_root);
2004 last_data_root = NULL;
2009 return imap_cache_dir;
2012 const gchar *get_mbox_cache_dir(void)
2014 static gchar *mbox_cache_dir = NULL;
2016 const gchar *data_root = prefs_common_get_data_root();
2017 if (strcmp2(data_root, last_data_root)) {
2018 g_free(mbox_cache_dir);
2019 mbox_cache_dir = NULL;
2022 if (!mbox_cache_dir)
2024 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2025 MBOX_CACHE_DIR, NULL);
2029 mbox_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2030 "Claws", G_DIR_SEPARATOR_S,
2031 g_get_user_name(), G_DIR_SEPARATOR_S,
2032 MBOX_CACHE_DIR, NULL);
2033 g_free(last_data_root);
2034 last_data_root = g_strdup(last_data_root);
2036 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2037 MBOX_CACHE_DIR, NULL);
2038 g_free(last_data_root);
2039 last_data_root = NULL;
2044 return mbox_cache_dir;
2047 const gchar *get_mime_tmp_dir(void)
2049 static gchar *mime_tmp_dir = NULL;
2052 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2053 MIME_TMP_DIR, NULL);
2055 return mime_tmp_dir;
2058 const gchar *get_template_dir(void)
2060 static gchar *template_dir = NULL;
2063 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2064 TEMPLATE_DIR, NULL);
2066 return template_dir;
2069 const gchar *get_header_cache_dir(void)
2071 static gchar *header_dir = NULL;
2074 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2075 HEADER_CACHE_DIR, NULL);
2080 /* Return the default directory for Plugins. */
2081 const gchar *get_plugin_dir(void)
2084 static gchar *plugin_dir = NULL;
2087 plugin_dir = g_strconcat(w32_get_module_dir(),
2088 "\\lib\\claws-mail\\plugins\\",
2092 if (is_dir_exist(PLUGINDIR))
2095 static gchar *plugin_dir = NULL;
2097 plugin_dir = g_strconcat(get_rc_dir(),
2098 G_DIR_SEPARATOR_S, "plugins",
2099 G_DIR_SEPARATOR_S, NULL);
2105 const gchar *get_tmp_dir(void)
2107 static gchar *tmp_dir = NULL;
2110 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2116 gchar *get_tmp_file(void)
2119 static guint32 id = 0;
2121 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2122 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2127 const gchar *get_domain_name(void)
2130 static gchar *domain_name = NULL;
2136 if (gethostname(hostname, sizeof(hostname)) != 0) {
2137 perror("gethostname");
2138 domain_name = "unknown";
2140 hostname[sizeof(hostname) - 1] = '\0';
2141 if ((hp = my_gethostbyname(hostname)) == NULL) {
2142 perror("gethostbyname");
2143 domain_name = g_strdup(hostname);
2145 domain_name = g_strdup(hp->h_name);
2148 debug_print("domain name = %s\n", domain_name);
2157 off_t get_file_size(const gchar *file)
2161 if (g_stat(file, &s) < 0) {
2162 FILE_OP_ERROR(file, "stat");
2169 time_t get_file_mtime(const gchar *file)
2173 if (g_stat(file, &s) < 0) {
2174 FILE_OP_ERROR(file, "stat");
2181 off_t get_file_size_as_crlf(const gchar *file)
2185 gchar buf[BUFFSIZE];
2187 if ((fp = g_fopen(file, "rb")) == NULL) {
2188 FILE_OP_ERROR(file, "fopen");
2192 while (fgets(buf, sizeof(buf), fp) != NULL) {
2194 size += strlen(buf) + 2;
2198 FILE_OP_ERROR(file, "fgets");
2207 off_t get_left_file_size(FILE *fp)
2213 if ((pos = ftell(fp)) < 0) {
2217 if (fseek(fp, 0L, SEEK_END) < 0) {
2221 if ((end = ftell(fp)) < 0) {
2226 if (fseek(fp, pos, SEEK_SET) < 0) {
2234 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2241 if (g_stat(file, &s) < 0) {
2242 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2246 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2253 /* Test on whether FILE is a relative file name. This is
2254 * straightforward for Unix but more complex for Windows. */
2255 gboolean is_relative_filename(const gchar *file)
2260 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2261 return FALSE; /* Prefixed with a hostname - this can't
2262 * be a relative name. */
2264 if ( ((*file >= 'a' && *file <= 'z')
2265 || (*file >= 'A' && *file <= 'Z'))
2267 file += 2; /* Skip drive letter. */
2269 return !(*file == '\\' || *file == '/');
2271 return !(*file == G_DIR_SEPARATOR);
2276 gboolean is_dir_exist(const gchar *dir)
2281 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2284 gboolean is_file_entry_exist(const gchar *file)
2289 return g_file_test(file, G_FILE_TEST_EXISTS);
2292 gboolean dirent_is_regular_file(struct dirent *d)
2294 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2295 if (d->d_type == DT_REG)
2297 else if (d->d_type != DT_UNKNOWN)
2301 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2304 gboolean dirent_is_directory(struct dirent *d)
2306 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2307 if (d->d_type == DT_DIR)
2309 else if (d->d_type != DT_UNKNOWN)
2313 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2316 gint change_dir(const gchar *dir)
2318 gchar *prevdir = NULL;
2321 prevdir = g_get_current_dir();
2323 if (g_chdir(dir) < 0) {
2324 FILE_OP_ERROR(dir, "chdir");
2325 if (debug_mode) g_free(prevdir);
2327 } else if (debug_mode) {
2330 cwd = g_get_current_dir();
2331 if (strcmp(prevdir, cwd) != 0)
2332 g_print("current dir: %s\n", cwd);
2340 gint make_dir(const gchar *dir)
2342 if (g_mkdir(dir, S_IRWXU) < 0) {
2343 FILE_OP_ERROR(dir, "mkdir");
2346 if (g_chmod(dir, S_IRWXU) < 0)
2347 FILE_OP_ERROR(dir, "chmod");
2352 gint make_dir_hier(const gchar *dir)
2357 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2358 parent_dir = g_strndup(dir, p - dir);
2359 if (*parent_dir != '\0') {
2360 if (!is_dir_exist(parent_dir)) {
2361 if (make_dir(parent_dir) < 0) {
2370 if (!is_dir_exist(dir)) {
2371 if (make_dir(dir) < 0)
2378 gint remove_all_files(const gchar *dir)
2381 const gchar *dir_name;
2384 prev_dir = g_get_current_dir();
2386 if (g_chdir(dir) < 0) {
2387 FILE_OP_ERROR(dir, "chdir");
2392 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2393 g_warning("failed to open directory: %s\n", dir);
2398 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2399 if (g_unlink(dir_name) < 0)
2400 FILE_OP_ERROR(dir_name, "unlink");
2405 if (g_chdir(prev_dir) < 0) {
2406 FILE_OP_ERROR(prev_dir, "chdir");
2416 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2419 const gchar *dir_name;
2423 prev_dir = g_get_current_dir();
2425 if (g_chdir(dir) < 0) {
2426 FILE_OP_ERROR(dir, "chdir");
2431 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2432 g_warning("failed to open directory: %s\n", dir);
2437 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2438 file_no = to_number(dir_name);
2439 if (file_no > 0 && first <= file_no && file_no <= last) {
2440 if (is_dir_exist(dir_name))
2442 if (g_unlink(dir_name) < 0)
2443 FILE_OP_ERROR(dir_name, "unlink");
2449 if (g_chdir(prev_dir) < 0) {
2450 FILE_OP_ERROR(prev_dir, "chdir");
2460 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2463 const gchar *dir_name;
2467 prev_dir = g_get_current_dir();
2469 if (g_chdir(dir) < 0) {
2470 FILE_OP_ERROR(dir, "chdir");
2475 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2476 FILE_OP_ERROR(dir, "opendir");
2481 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2482 file_no = to_number(dir_name);
2483 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2484 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2485 if (is_dir_exist(dir_name))
2487 if (g_unlink(dir_name) < 0)
2488 FILE_OP_ERROR(dir_name, "unlink");
2494 if (g_chdir(prev_dir) < 0) {
2495 FILE_OP_ERROR(prev_dir, "chdir");
2505 gint remove_all_numbered_files(const gchar *dir)
2507 return remove_numbered_files(dir, 0, UINT_MAX);
2510 gint remove_expired_files(const gchar *dir, guint hours)
2513 const gchar *dir_name;
2517 time_t mtime, now, expire_time;
2519 prev_dir = g_get_current_dir();
2521 if (g_chdir(dir) < 0) {
2522 FILE_OP_ERROR(dir, "chdir");
2527 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2528 g_warning("failed to open directory: %s\n", dir);
2534 expire_time = hours * 60 * 60;
2536 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2537 file_no = to_number(dir_name);
2539 if (g_stat(dir_name, &s) < 0) {
2540 FILE_OP_ERROR(dir_name, "stat");
2543 if (S_ISDIR(s.st_mode))
2545 mtime = MAX(s.st_mtime, s.st_atime);
2546 if (now - mtime > expire_time) {
2547 if (g_unlink(dir_name) < 0)
2548 FILE_OP_ERROR(dir_name, "unlink");
2555 if (g_chdir(prev_dir) < 0) {
2556 FILE_OP_ERROR(prev_dir, "chdir");
2566 gint remove_dir_recursive(const gchar *dir)
2570 const gchar *dir_name;
2573 if (g_stat(dir, &s) < 0) {
2574 FILE_OP_ERROR(dir, "stat");
2575 if (ENOENT == errno) return 0;
2579 if (!S_ISDIR(s.st_mode)) {
2580 if (g_unlink(dir) < 0) {
2581 FILE_OP_ERROR(dir, "unlink");
2588 prev_dir = g_get_current_dir();
2589 /* g_print("prev_dir = %s\n", prev_dir); */
2591 if (!path_cmp(prev_dir, dir)) {
2593 if (g_chdir("..") < 0) {
2594 FILE_OP_ERROR(dir, "chdir");
2597 prev_dir = g_get_current_dir();
2600 if (g_chdir(dir) < 0) {
2601 FILE_OP_ERROR(dir, "chdir");
2606 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2607 g_warning("failed to open directory: %s\n", dir);
2613 /* remove all files in the directory */
2614 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2615 /* g_print("removing %s\n", dir_name); */
2617 if (is_dir_exist(dir_name)) {
2618 if (remove_dir_recursive(dir_name) < 0) {
2619 g_warning("can't remove directory\n");
2623 if (g_unlink(dir_name) < 0)
2624 FILE_OP_ERROR(dir_name, "unlink");
2630 if (g_chdir(prev_dir) < 0) {
2631 FILE_OP_ERROR(prev_dir, "chdir");
2638 if (g_rmdir(dir) < 0) {
2639 FILE_OP_ERROR(dir, "rmdir");
2646 gint rename_force(const gchar *oldpath, const gchar *newpath)
2649 if (!is_file_entry_exist(oldpath)) {
2653 if (is_file_exist(newpath)) {
2654 if (g_unlink(newpath) < 0)
2655 FILE_OP_ERROR(newpath, "unlink");
2658 return g_rename(oldpath, newpath);
2662 * Append src file body to the tail of dest file.
2663 * Now keep_backup has no effects.
2665 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2667 FILE *src_fp, *dest_fp;
2671 gboolean err = FALSE;
2673 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2674 FILE_OP_ERROR(src, "fopen");
2678 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2679 FILE_OP_ERROR(dest, "fopen");
2684 if (change_file_mode_rw(dest_fp, dest) < 0) {
2685 FILE_OP_ERROR(dest, "chmod");
2686 g_warning("can't change file mode\n");
2689 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2690 if (n_read < sizeof(buf) && ferror(src_fp))
2692 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2693 g_warning("writing to %s failed.\n", dest);
2701 if (ferror(src_fp)) {
2702 FILE_OP_ERROR(src, "fread");
2706 if (fclose(dest_fp) == EOF) {
2707 FILE_OP_ERROR(dest, "fclose");
2719 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2721 FILE *src_fp, *dest_fp;
2724 gchar *dest_bak = NULL;
2725 gboolean err = FALSE;
2727 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2728 FILE_OP_ERROR(src, "fopen");
2731 if (is_file_exist(dest)) {
2732 dest_bak = g_strconcat(dest, ".bak", NULL);
2733 if (rename_force(dest, dest_bak) < 0) {
2734 FILE_OP_ERROR(dest, "rename");
2741 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2742 FILE_OP_ERROR(dest, "fopen");
2745 if (rename_force(dest_bak, dest) < 0)
2746 FILE_OP_ERROR(dest_bak, "rename");
2752 if (change_file_mode_rw(dest_fp, dest) < 0) {
2753 FILE_OP_ERROR(dest, "chmod");
2754 g_warning("can't change file mode\n");
2757 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2758 if (n_read < sizeof(buf) && ferror(src_fp))
2760 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2761 g_warning("writing to %s failed.\n", dest);
2766 if (rename_force(dest_bak, dest) < 0)
2767 FILE_OP_ERROR(dest_bak, "rename");
2774 if (ferror(src_fp)) {
2775 FILE_OP_ERROR(src, "fread");
2779 if (fclose(dest_fp) == EOF) {
2780 FILE_OP_ERROR(dest, "fclose");
2787 if (rename_force(dest_bak, dest) < 0)
2788 FILE_OP_ERROR(dest_bak, "rename");
2794 if (keep_backup == FALSE && dest_bak)
2802 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2804 if (overwrite == FALSE && is_file_exist(dest)) {
2805 g_warning("move_file(): file %s already exists.", dest);
2809 if (rename_force(src, dest) == 0) return 0;
2811 if (EXDEV != errno) {
2812 FILE_OP_ERROR(src, "rename");
2816 if (copy_file(src, dest, FALSE) < 0) return -1;
2823 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2826 gint bytes_left, to_read;
2829 if (fseek(fp, offset, SEEK_SET) < 0) {
2834 bytes_left = length;
2835 to_read = MIN(bytes_left, sizeof(buf));
2837 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2838 if (n_read < to_read && ferror(fp))
2840 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2843 bytes_left -= n_read;
2844 if (bytes_left == 0)
2846 to_read = MIN(bytes_left, sizeof(buf));
2857 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2860 gboolean err = FALSE;
2862 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2863 FILE_OP_ERROR(dest, "fopen");
2867 if (change_file_mode_rw(dest_fp, dest) < 0) {
2868 FILE_OP_ERROR(dest, "chmod");
2869 g_warning("can't change file mode\n");
2872 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2875 if (!err && fclose(dest_fp) == EOF) {
2876 FILE_OP_ERROR(dest, "fclose");
2881 g_warning("writing to %s failed.\n", dest);
2889 /* convert line endings into CRLF. If the last line doesn't end with
2890 * linebreak, add it.
2892 gchar *canonicalize_str(const gchar *str)
2898 for (p = str; *p != '\0'; ++p) {
2905 if (p == str || *(p - 1) != '\n')
2908 out = outp = g_malloc(new_len + 1);
2909 for (p = str; *p != '\0'; ++p) {
2916 if (p == str || *(p - 1) != '\n') {
2925 gint canonicalize_file(const gchar *src, const gchar *dest)
2927 FILE *src_fp, *dest_fp;
2928 gchar buf[BUFFSIZE];
2930 gboolean err = FALSE;
2931 gboolean last_linebreak = FALSE;
2933 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2934 FILE_OP_ERROR(src, "fopen");
2938 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2939 FILE_OP_ERROR(dest, "fopen");
2944 if (change_file_mode_rw(dest_fp, dest) < 0) {
2945 FILE_OP_ERROR(dest, "chmod");
2946 g_warning("can't change file mode\n");
2949 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2953 if (len == 0) break;
2954 last_linebreak = FALSE;
2956 if (buf[len - 1] != '\n') {
2957 last_linebreak = TRUE;
2958 r = fputs(buf, dest_fp);
2959 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2960 r = fputs(buf, dest_fp);
2963 r = fwrite(buf, 1, len - 1, dest_fp);
2968 r = fputs("\r\n", dest_fp);
2972 g_warning("writing to %s failed.\n", dest);
2980 if (last_linebreak == TRUE) {
2981 if (fputs("\r\n", dest_fp) == EOF)
2985 if (ferror(src_fp)) {
2986 FILE_OP_ERROR(src, "fgets");
2990 if (fclose(dest_fp) == EOF) {
2991 FILE_OP_ERROR(dest, "fclose");
3003 gint canonicalize_file_replace(const gchar *file)
3007 tmp_file = get_tmp_file();
3009 if (canonicalize_file(file, tmp_file) < 0) {
3014 if (move_file(tmp_file, file, TRUE) < 0) {
3015 g_warning("can't replace %s .\n", file);
3025 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3027 FILE *src_fp, *dest_fp;
3028 gchar buf[BUFFSIZE];
3029 gboolean err = FALSE;
3031 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3032 FILE_OP_ERROR(src, "fopen");
3036 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3037 FILE_OP_ERROR(dest, "fopen");
3042 if (change_file_mode_rw(dest_fp, dest) < 0) {
3043 FILE_OP_ERROR(dest, "chmod");
3044 g_warning("can't change file mode\n");
3047 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3049 if (fputs(buf, dest_fp) == EOF) {
3050 g_warning("writing to %s failed.\n", dest);
3058 if (ferror(src_fp)) {
3059 FILE_OP_ERROR(src, "fgets");
3063 if (fclose(dest_fp) == EOF) {
3064 FILE_OP_ERROR(dest, "fclose");
3076 gint uncanonicalize_file_replace(const gchar *file)
3080 tmp_file = get_tmp_file();
3082 if (uncanonicalize_file(file, tmp_file) < 0) {
3087 if (move_file(tmp_file, file, TRUE) < 0) {
3088 g_warning("can't replace %s .\n", file);
3098 gchar *normalize_newlines(const gchar *str)
3100 const gchar *p = str;
3103 out = outp = g_malloc(strlen(str) + 1);
3104 for (p = str; *p != '\0'; ++p) {
3106 if (*(p + 1) != '\n')
3117 gchar *get_outgoing_rfc2822_str(FILE *fp)
3119 gchar buf[BUFFSIZE];
3123 str = g_string_new(NULL);
3125 /* output header part */
3126 while (fgets(buf, sizeof(buf), fp) != NULL) {
3128 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3135 else if (next != ' ' && next != '\t') {
3139 if (fgets(buf, sizeof(buf), fp) == NULL)
3143 g_string_append(str, buf);
3144 g_string_append(str, "\r\n");
3150 /* output body part */
3151 while (fgets(buf, sizeof(buf), fp) != NULL) {
3154 g_string_append_c(str, '.');
3155 g_string_append(str, buf);
3156 g_string_append(str, "\r\n");
3160 g_string_free(str, FALSE);
3166 * Create a new boundary in a way that it is very unlikely that this
3167 * will occur in the following text. It would be easy to ensure
3168 * uniqueness if everything is either quoted-printable or base64
3169 * encoded (note that conversion is allowed), but because MIME bodies
3170 * may be nested, it may happen that the same boundary has already
3173 * boundary := 0*69<bchars> bcharsnospace
3174 * bchars := bcharsnospace / " "
3175 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3176 * "+" / "_" / "," / "-" / "." /
3177 * "/" / ":" / "=" / "?"
3179 * some special characters removed because of buggy MTAs
3182 gchar *generate_mime_boundary(const gchar *prefix)
3184 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3185 "abcdefghijklmnopqrstuvwxyz"
3190 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3191 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3194 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3198 gint change_file_mode_rw(FILE *fp, const gchar *file)
3201 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3203 return g_chmod(file, S_IRUSR|S_IWUSR);
3207 FILE *my_tmpfile(void)
3209 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3210 const gchar suffix[] = ".XXXXXX";
3211 const gchar *tmpdir;
3213 const gchar *progname;
3220 tmpdir = get_tmp_dir();
3221 tmplen = strlen(tmpdir);
3222 progname = g_get_prgname();
3223 if (progname == NULL)
3224 progname = "claws-mail";
3225 proglen = strlen(progname);
3226 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3229 memcpy(fname, tmpdir, tmplen);
3230 fname[tmplen] = G_DIR_SEPARATOR;
3231 memcpy(fname + tmplen + 1, progname, proglen);
3232 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3234 fd = mkstemp(fname);
3241 /* verify that we can write in the file after unlinking */
3242 if (write(fd, buf, 1) < 0) {
3249 fp = fdopen(fd, "w+b");
3257 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3262 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3266 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3267 dir, G_DIR_SEPARATOR);
3268 fd = mkstemp_name(template, filename);
3271 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3272 fd = mkstemp(*filename);
3274 return fdopen(fd, "w+");
3277 FILE *str_open_as_stream(const gchar *str)
3282 g_return_val_if_fail(str != NULL, NULL);
3286 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3291 if (len == 0) return fp;
3293 if (fwrite(str, 1, len, fp) != len) {
3294 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3303 gint str_write_to_file(const gchar *str, const gchar *file)
3308 g_return_val_if_fail(str != NULL, -1);
3309 g_return_val_if_fail(file != NULL, -1);
3311 if ((fp = g_fopen(file, "wb")) == NULL) {
3312 FILE_OP_ERROR(file, "fopen");
3322 if (fwrite(str, 1, len, fp) != len) {
3323 FILE_OP_ERROR(file, "fwrite");
3329 if (fclose(fp) == EOF) {
3330 FILE_OP_ERROR(file, "fclose");
3338 gchar *file_read_to_str(const gchar *file)
3343 g_return_val_if_fail(file != NULL, NULL);
3345 if ((fp = g_fopen(file, "rb")) == NULL) {
3346 FILE_OP_ERROR(file, "fopen");
3350 str = file_read_stream_to_str(fp);
3357 gchar *file_read_stream_to_str(FILE *fp)
3364 g_return_val_if_fail(fp != NULL, NULL);
3366 array = g_byte_array_new();
3368 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3369 if (n_read < sizeof(buf) && ferror(fp))
3371 g_byte_array_append(array, buf, n_read);
3375 FILE_OP_ERROR("file stream", "fread");
3376 g_byte_array_free(array, TRUE);
3381 g_byte_array_append(array, buf, 1);
3382 str = (gchar *)array->data;
3383 g_byte_array_free(array, FALSE);
3385 if (!g_utf8_validate(str, -1, NULL)) {
3386 const gchar *src_codeset, *dest_codeset;
3388 src_codeset = conv_get_locale_charset_str();
3389 dest_codeset = CS_UTF_8;
3390 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3399 char *fgets_crlf(char *buf, int size, FILE *stream)
3401 gboolean is_cr = FALSE;
3402 gboolean last_was_cr = FALSE;
3407 while (--size > 0 && (c = getc(stream)) != EOF)
3410 is_cr = (c == '\r');
3420 last_was_cr = is_cr;
3422 if (c == EOF && cs == buf)
3430 static gint execute_async(gchar *const argv[])
3432 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3434 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3435 NULL, NULL, NULL, FALSE) == FALSE) {
3436 g_warning("Couldn't execute command: %s\n", argv[0]);
3443 static gint execute_sync(gchar *const argv[])
3447 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3449 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3450 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3451 g_warning("Couldn't execute command: %s\n", argv[0]);
3456 if (WIFEXITED(status))
3457 return WEXITSTATUS(status);
3465 gint execute_command_line(const gchar *cmdline, gboolean async)
3470 debug_print("execute_command_line(): executing: %s\n", cmdline);
3472 argv = strsplit_with_quote(cmdline, " ", 0);
3475 ret = execute_async(argv);
3477 ret = execute_sync(argv);
3484 gchar *get_command_output(const gchar *cmdline)
3486 gchar *child_stdout;
3489 g_return_val_if_fail(cmdline != NULL, NULL);
3491 debug_print("get_command_output(): executing: %s\n", cmdline);
3493 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3495 g_warning("Couldn't execute command: %s\n", cmdline);
3499 return child_stdout;
3502 static gint is_unchanged_uri_char(char c)
3513 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3519 for(i = 0; i < strlen(uri) ; i++) {
3520 if (is_unchanged_uri_char(uri[i])) {
3521 if (k + 2 >= bufsize)
3523 encoded_uri[k++] = uri[i];
3526 char * hexa = "0123456789ABCDEF";
3528 if (k + 4 >= bufsize)
3530 encoded_uri[k++] = '%';
3531 encoded_uri[k++] = hexa[uri[i] / 16];
3532 encoded_uri[k++] = hexa[uri[i] % 16];
3538 gint open_uri(const gchar *uri, const gchar *cmdline)
3541 gchar buf[BUFFSIZE];
3543 gchar encoded_uri[BUFFSIZE];
3544 g_return_val_if_fail(uri != NULL, -1);
3546 /* an option to choose whether to use encode_uri or not ? */
3547 encode_uri(encoded_uri, BUFFSIZE, uri);
3550 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3551 !strchr(p + 2, '%'))
3552 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3555 g_warning("Open URI command line is invalid "
3556 "(there must be only one '%%s'): %s",
3558 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3561 execute_command_line(buf, TRUE);
3563 extern osso_context_t *get_osso_context(void);
3564 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3565 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3566 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3571 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3573 gchar buf[BUFFSIZE];
3576 g_return_val_if_fail(filepath != NULL, -1);
3579 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3580 !strchr(p + 2, '%'))
3581 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3584 g_warning("Open Text Editor command line is invalid "
3585 "(there must be only one '%%s'): %s",
3587 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3590 execute_command_line(buf, TRUE);
3595 time_t remote_tzoffset_sec(const gchar *zone)
3597 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3603 time_t remoteoffset;
3605 strncpy(zone3, zone, 3);
3609 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3610 (c == '+' || c == '-')) {
3611 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3613 remoteoffset = -remoteoffset;
3614 } else if (!strncmp(zone, "UT" , 2) ||
3615 !strncmp(zone, "GMT", 2)) {
3617 } else if (strlen(zone3) == 3) {
3618 for (p = ustzstr; *p != '\0'; p += 3) {
3619 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3620 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3621 remoteoffset = iustz * 3600;
3627 } else if (strlen(zone3) == 1) {
3629 case 'Z': remoteoffset = 0; break;
3630 case 'A': remoteoffset = -1; break;
3631 case 'B': remoteoffset = -2; break;
3632 case 'C': remoteoffset = -3; break;
3633 case 'D': remoteoffset = -4; break;
3634 case 'E': remoteoffset = -5; break;
3635 case 'F': remoteoffset = -6; break;
3636 case 'G': remoteoffset = -7; break;
3637 case 'H': remoteoffset = -8; break;
3638 case 'I': remoteoffset = -9; break;
3639 case 'K': remoteoffset = -10; break; /* J is not used */
3640 case 'L': remoteoffset = -11; break;
3641 case 'M': remoteoffset = -12; break;
3642 case 'N': remoteoffset = 1; break;
3643 case 'O': remoteoffset = 2; break;
3644 case 'P': remoteoffset = 3; break;
3645 case 'Q': remoteoffset = 4; break;
3646 case 'R': remoteoffset = 5; break;
3647 case 'S': remoteoffset = 6; break;
3648 case 'T': remoteoffset = 7; break;
3649 case 'U': remoteoffset = 8; break;
3650 case 'V': remoteoffset = 9; break;
3651 case 'W': remoteoffset = 10; break;
3652 case 'X': remoteoffset = 11; break;
3653 case 'Y': remoteoffset = 12; break;
3654 default: remoteoffset = 0; break;
3656 remoteoffset = remoteoffset * 3600;
3660 return remoteoffset;
3663 time_t tzoffset_sec(time_t *now)
3667 struct tm buf1, buf2;
3669 gmt = *gmtime_r(now, &buf1);
3670 lt = localtime_r(now, &buf2);
3672 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3674 if (lt->tm_year < gmt.tm_year)
3676 else if (lt->tm_year > gmt.tm_year)
3678 else if (lt->tm_yday < gmt.tm_yday)
3680 else if (lt->tm_yday > gmt.tm_yday)
3683 if (off >= 24 * 60) /* should be impossible */
3684 off = 23 * 60 + 59; /* if not, insert silly value */
3685 if (off <= -24 * 60)
3686 off = -(23 * 60 + 59);
3691 /* calculate timezone offset */
3692 gchar *tzoffset(time_t *now)
3694 static gchar offset_string[6];
3698 struct tm buf1, buf2;
3700 gmt = *gmtime_r(now, &buf1);
3701 lt = localtime_r(now, &buf2);
3703 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3705 if (lt->tm_year < gmt.tm_year)
3707 else if (lt->tm_year > gmt.tm_year)
3709 else if (lt->tm_yday < gmt.tm_yday)
3711 else if (lt->tm_yday > gmt.tm_yday)
3719 if (off >= 24 * 60) /* should be impossible */
3720 off = 23 * 60 + 59; /* if not, insert silly value */
3722 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3724 return offset_string;
3727 void get_rfc822_date(gchar *buf, gint len)
3731 gchar day[4], mon[4];
3732 gint dd, hh, mm, ss, yyyy;
3734 gchar buf2[BUFFSIZE];
3737 lt = localtime_r(&t, &buf1);
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 addr = g_strconcat("@", get_domain_name(), NULL);
3928 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3929 lt->tm_year + 1900, lt->tm_mon + 1,
3930 lt->tm_mday, lt->tm_hour,
3931 lt->tm_min, lt->tm_sec,
3932 (guint) rand(), addr);
3939 quote_cmd_argument()
3941 return a quoted string safely usable in argument of a command.
3943 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3946 gint quote_cmd_argument(gchar * result, guint size,
3956 for(p = path ; * p != '\0' ; p ++) {
3958 if (isalnum((guchar)*p) || (* p == '/')) {
3959 if (remaining > 0) {
3965 result[size - 1] = '\0';
3970 if (remaining >= 2) {
3978 result[size - 1] = '\0';
3983 if (remaining > 0) {
3987 result[size - 1] = '\0';
4001 static void g_node_map_recursive(GNode *node, gpointer data)
4003 GNodeMapData *mapdata = (GNodeMapData *) data;
4005 GNodeMapData newmapdata;
4008 newdata = mapdata->func(node->data, mapdata->data);
4009 if (newdata != NULL) {
4010 newnode = g_node_new(newdata);
4011 g_node_append(mapdata->parent, newnode);
4013 newmapdata.parent = newnode;
4014 newmapdata.func = mapdata->func;
4015 newmapdata.data = mapdata->data;
4017 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4021 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4024 GNodeMapData mapdata;
4026 g_return_val_if_fail(node != NULL, NULL);
4027 g_return_val_if_fail(func != NULL, NULL);
4029 root = g_node_new(func(node->data, data));
4031 mapdata.parent = root;
4032 mapdata.func = func;
4033 mapdata.data = data;
4035 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4040 #define HEX_TO_INT(val, hex) \
4044 if ('0' <= c && c <= '9') { \
4046 } else if ('a' <= c && c <= 'f') { \
4047 val = c - 'a' + 10; \
4048 } else if ('A' <= c && c <= 'F') { \
4049 val = c - 'A' + 10; \
4055 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4062 if (hi == -1 || lo == -1)
4065 *out = (hi << 4) + lo;
4069 #define INT_TO_HEX(hex, val) \
4072 hex = '0' + (val); \
4074 hex = 'A' + (val) - 10; \
4077 void get_hex_str(gchar *out, guchar ch)
4081 INT_TO_HEX(hex, ch >> 4);
4083 INT_TO_HEX(hex, ch & 0x0f);
4089 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4091 #define G_PRINT_REF g_print
4095 *\brief Register ref counted pointer. It is based on GBoxed, so should
4096 * work with anything that uses the GType system. The semantics
4097 * are similar to a C++ auto pointer, with the exception that
4098 * C doesn't have automatic closure (calling destructors) when
4099 * exiting a block scope.
4100 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4101 * function directly.
4103 *\return GType A GType type.
4105 GType g_auto_pointer_register(void)
4107 static GType auto_pointer_type;
4108 if (!auto_pointer_type)
4110 g_boxed_type_register_static
4111 ("G_TYPE_AUTO_POINTER",
4112 (GBoxedCopyFunc) g_auto_pointer_copy,
4113 (GBoxedFreeFunc) g_auto_pointer_free);
4114 return auto_pointer_type;
4118 *\brief Structure with g_new() allocated pointer guarded by the
4121 typedef struct AutoPointerRef {
4122 void (*free) (gpointer);
4128 *\brief The auto pointer opaque structure that references the
4129 * pointer guard block.
4131 typedef struct AutoPointer {
4132 AutoPointerRef *ref;
4133 gpointer ptr; /*!< access to protected pointer */
4137 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4141 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4142 * ... when assigning, copying and freeing storage elements
4144 * gtk_list_store_new(N_S_COLUMNS,
4145 * G_TYPE_AUTO_POINTER,
4149 * Template *precious_data = g_new0(Template, 1);
4150 * g_pointer protect = g_auto_pointer_new(precious_data);
4152 * gtk_list_store_set(container, &iter,
4156 * ... the gtk_list_store has copied the pointer and
4157 * ... incremented its reference count, we should free
4158 * ... the auto pointer (in C++ a destructor would do
4159 * ... this for us when leaving block scope)
4161 * g_auto_pointer_free(protect);
4163 * ... gtk_list_store_set() now manages the data. When
4164 * ... *explicitly* requesting a pointer from the list
4165 * ... store, don't forget you get a copy that should be
4166 * ... freed with g_auto_pointer_free() eventually.
4170 *\param pointer Pointer to be guarded.
4172 *\return GAuto * Pointer that should be used in containers with
4175 GAuto *g_auto_pointer_new(gpointer p)
4177 AutoPointerRef *ref;
4183 ref = g_new0(AutoPointerRef, 1);
4184 ptr = g_new0(AutoPointer, 1);
4194 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4200 *\brief Allocate an autopointer using the passed \a free function to
4201 * free the guarded pointer
4203 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4210 aptr = g_auto_pointer_new(p);
4211 aptr->ref->free = free_;
4215 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4217 if (auto_ptr == NULL)
4219 return ((AutoPointer *) auto_ptr)->ptr;
4223 *\brief Copies an auto pointer by. It's mostly not necessary
4224 * to call this function directly, unless you copy/assign
4225 * the guarded pointer.
4227 *\param auto_ptr Auto pointer returned by previous call to
4228 * g_auto_pointer_new_XXX()
4230 *\return gpointer An auto pointer
4232 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4235 AutoPointerRef *ref;
4238 if (auto_ptr == NULL)
4243 newp = g_new0(AutoPointer, 1);
4246 newp->ptr = ref->pointer;
4250 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4256 *\brief Free an auto pointer
4258 void g_auto_pointer_free(GAuto *auto_ptr)
4261 AutoPointerRef *ref;
4263 if (auto_ptr == NULL)
4269 if (--(ref->cnt) == 0) {
4271 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4273 ref->free(ref->pointer);
4278 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4283 void replace_returns(gchar *str)
4288 while (strstr(str, "\n")) {
4289 *strstr(str, "\n") = ' ';
4291 while (strstr(str, "\r")) {
4292 *strstr(str, "\r") = ' ';
4296 /* get_uri_part() - retrieves a URI starting from scanpos.
4297 Returns TRUE if succesful */
4298 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4299 const gchar **bp, const gchar **ep, gboolean hdr)
4302 gint parenthese_cnt = 0;
4304 g_return_val_if_fail(start != NULL, FALSE);
4305 g_return_val_if_fail(scanpos != NULL, FALSE);
4306 g_return_val_if_fail(bp != NULL, FALSE);
4307 g_return_val_if_fail(ep != NULL, FALSE);
4311 /* find end point of URI */
4312 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4313 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4314 !IS_ASCII(*(const guchar *)ep_) ||
4315 strchr("[]{}<>\"", *ep_)) {
4317 } else if (strchr("(", *ep_)) {
4319 } else if (strchr(")", *ep_)) {
4320 if (parenthese_cnt > 0)
4327 /* no punctuation at end of string */
4329 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4330 * should pass some URI type to this function and decide on that whether
4331 * to perform punctuation stripping */
4333 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4335 for (; ep_ - 1 > scanpos + 1 &&
4336 IS_REAL_PUNCT(*(ep_ - 1));
4340 #undef IS_REAL_PUNCT
4347 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4349 while (bp && *bp && g_ascii_isspace(*bp))
4351 return g_strndup(bp, ep - bp);
4354 /* valid mail address characters */
4355 #define IS_RFC822_CHAR(ch) \
4359 !g_ascii_isspace(ch) && \
4360 !strchr("(),;<>\"", (ch)))
4362 /* alphabet and number within 7bit ASCII */
4363 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4364 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4366 static GHashTable *create_domain_tab(void)
4368 static const gchar *toplvl_domains [] = {
4370 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4371 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4372 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4373 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4374 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4375 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4376 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4377 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4378 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4379 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4380 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4381 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4382 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4383 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4384 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4385 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4386 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4387 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4388 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4389 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4390 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4391 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4392 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4393 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4394 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4395 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4398 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4400 g_return_val_if_fail(htab, NULL);
4401 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4402 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4406 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4408 const gint MAX_LVL_DOM_NAME_LEN = 6;
4409 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4410 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4413 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4416 for (p = buf; p < m && first < last; *p++ = *first++)
4420 return g_hash_table_lookup(tab, buf) != NULL;
4423 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4424 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4425 const gchar **bp, const gchar **ep, gboolean hdr)
4427 /* more complex than the uri part because we need to scan back and forward starting from
4428 * the scan position. */
4429 gboolean result = FALSE;
4430 const gchar *bp_ = NULL;
4431 const gchar *ep_ = NULL;
4432 static GHashTable *dom_tab;
4433 const gchar *last_dot = NULL;
4434 const gchar *prelast_dot = NULL;
4435 const gchar *last_tld_char = NULL;
4437 /* the informative part of the email address (describing the name
4438 * of the email address owner) may contain quoted parts. the
4439 * closure stack stores the last encountered quotes. */
4440 gchar closure_stack[128];
4441 gchar *ptr = closure_stack;
4443 g_return_val_if_fail(start != NULL, FALSE);
4444 g_return_val_if_fail(scanpos != NULL, FALSE);
4445 g_return_val_if_fail(bp != NULL, FALSE);
4446 g_return_val_if_fail(ep != NULL, FALSE);
4449 const gchar *start_quote = NULL;
4450 const gchar *end_quote = NULL;
4452 /* go to the real start */
4453 if (start[0] == ',')
4455 if (start[0] == ';')
4457 while (start[0] == '\n' || start[0] == '\r')
4459 while (start[0] == ' ' || start[0] == '\t')
4464 /* check if there are quotes (to skip , in them) */
4465 if (*start == '"') {
4466 start_quote = start;
4468 end_quote = strstr(start, "\"");
4474 /* skip anything between quotes */
4475 if (start_quote && end_quote) {
4480 /* find end (either , or ; or end of line) */
4481 if (strstr(start, ",") && strstr(start, ";"))
4482 *ep = strstr(start,",") < strstr(start, ";")
4483 ? strstr(start, ",") : strstr(start, ";");
4484 else if (strstr(start, ","))
4485 *ep = strstr(start, ",");
4486 else if (strstr(start, ";"))
4487 *ep = strstr(start, ";");
4489 *ep = start+strlen(start);
4491 /* go back to real start */
4492 if (start_quote && end_quote) {
4493 start = start_quote;
4496 /* check there's still an @ in that, or search
4497 * further if possible */
4498 if (strstr(start, "@") && strstr(start, "@") < *ep)
4500 else if (*ep < start+strlen(start)) {
4503 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4511 dom_tab = create_domain_tab();
4512 g_return_val_if_fail(dom_tab, FALSE);
4514 /* scan start of address */
4515 for (bp_ = scanpos - 1;
4516 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4519 /* TODO: should start with an alnum? */
4521 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4524 if (bp_ != scanpos) {
4525 /* scan end of address */
4526 for (ep_ = scanpos + 1;
4527 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4529 prelast_dot = last_dot;
4531 if (*(last_dot + 1) == '.') {
4532 if (prelast_dot == NULL)
4534 last_dot = prelast_dot;
4539 /* TODO: really should terminate with an alnum? */
4540 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4545 if (last_dot == NULL)
4547 if (last_dot >= ep_)
4548 last_dot = prelast_dot;
4549 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4553 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4554 if (*last_tld_char == '?')
4557 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4564 if (!result) return FALSE;
4566 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4567 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4568 && IS_RFC822_CHAR(*(ep_ + 3))) {
4569 /* this informative part with an @ in it is
4570 * followed by the email address */
4573 /* go to matching '>' (or next non-rfc822 char, like \n) */
4574 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4577 /* include the bracket */
4578 if (*ep_ == '>') ep_++;
4580 /* include the leading quote */
4588 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4589 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4592 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4593 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4596 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4597 #define IN_STACK() (ptr > closure_stack)
4598 /* has underrun check */
4599 #define POP_STACK() if(IN_STACK()) --ptr
4600 /* has overrun check */
4601 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4602 /* has underrun check */
4603 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4607 /* scan for the informative part. */
4608 for (bp_ -= 2; bp_ >= start; bp_--) {
4609 /* if closure on the stack keep scanning */
4610 if (PEEK_STACK() == *bp_) {
4614 if (*bp_ == '\'' || *bp_ == '"') {
4619 /* if nothing in the closure stack, do the special conditions
4620 * the following if..else expression simply checks whether
4621 * a token is acceptable. if not acceptable, the clause
4622 * should terminate the loop with a 'break' */
4623 if (!PEEK_STACK()) {
4625 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4626 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4627 /* hyphens are allowed, but only in
4629 } else if (strchr(" \"'", *bp_)) {
4630 /* but anything not being a punctiation
4633 break; /* anything else is rejected */
4640 /* scan forward (should start with an alnum) */
4641 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4657 #undef IS_ASCII_ALNUM
4658 #undef IS_RFC822_CHAR
4660 gchar *make_email_string(const gchar *bp, const gchar *ep)
4662 /* returns a mailto: URI; mailto: is also used to detect the
4663 * uri type later on in the button_pressed signal handler */
4667 tmp = g_strndup(bp, ep - bp);
4668 result = g_strconcat("mailto:", tmp, NULL);
4674 gchar *make_http_string(const gchar *bp, const gchar *ep)
4676 /* returns an http: URI; */
4680 while (bp && *bp && g_ascii_isspace(*bp))
4682 tmp = g_strndup(bp, ep - bp);
4683 result = g_strconcat("http://", tmp, NULL);
4689 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4691 FILE *fp = fopen(path, "rb");
4692 gchar buf[BUFFSIZE];
4693 gchar *result = NULL;
4696 while (fgets(buf, sizeof (buf), fp) != NULL) {
4697 gchar **parts = g_strsplit(buf, ";", 3);
4698 gchar *trimmed = parts[0];
4699 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4701 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4702 trimmed[strlen(trimmed)-1] = '\0';
4704 if (!strcmp(trimmed, type)) {
4705 gboolean needsterminal = FALSE;
4706 if (parts[2] && strstr(parts[2], "needsterminal")) {
4707 needsterminal = TRUE;
4709 if (parts[2] && strstr(parts[2], "test=")) {
4710 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4711 gchar *testcmd = orig_testcmd;
4712 if (strstr(testcmd,";"))
4713 *(strstr(testcmd,";")) = '\0';
4714 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4716 while (testcmd[strlen(testcmd)-1] == '\n')
4717 testcmd[strlen(testcmd)-1] = '\0';
4718 while (testcmd[strlen(testcmd)-1] == '\r')
4719 testcmd[strlen(testcmd)-1] = '\0';
4720 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4721 testcmd[strlen(testcmd)-1] = '\0';
4723 if (strstr(testcmd, "%s")) {
4724 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4725 gint res = system(tmp);
4727 g_free(orig_testcmd);
4734 gint res = system(testcmd);
4735 g_free(orig_testcmd);
4745 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4747 while (trimmed[strlen(trimmed)-1] == '\n')
4748 trimmed[strlen(trimmed)-1] = '\0';
4749 while (trimmed[strlen(trimmed)-1] == '\r')
4750 trimmed[strlen(trimmed)-1] = '\0';
4751 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4752 trimmed[strlen(trimmed)-1] = '\0';
4753 result = g_strdup(trimmed);
4756 /* if there are no single quotes around %s, add them.
4757 * '.*%s.*' is ok, as in display 'png:%s'
4759 if (strstr(result, "%s")
4760 && !(strstr(result, "'") < strstr(result,"%s") &&
4761 strstr(strstr(result,"%s"), "'"))) {
4762 gchar *start = g_strdup(result);
4763 gchar *end = g_strdup(strstr(result, "%s")+2);
4765 *strstr(start, "%s") = '\0';
4766 tmp = g_strconcat(start,"'%s'",end, NULL);
4772 if (needsterminal) {
4773 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4784 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4786 gchar *result = NULL;
4790 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4791 result = mailcap_get_command_in_file(path, type, file_to_open);
4795 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4799 void mailcap_update_default(const gchar *type, const gchar *command)
4801 gchar *path = NULL, *outpath = NULL;
4802 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4803 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4804 FILE *fp = fopen(path, "rb");
4805 FILE *outfp = fopen(outpath, "wb");
4806 gchar buf[BUFFSIZE];
4814 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4815 gchar **parts = g_strsplit(buf, ";", 3);
4816 gchar *trimmed = parts[0];
4817 while (trimmed[0] == ' ')
4819 while (trimmed[strlen(trimmed)-1] == ' ')
4820 trimmed[strlen(trimmed)-1] = '\0';
4822 if (!strcmp(trimmed, type)) {
4831 fprintf(outfp, "%s; %s\n", type, command);
4837 g_rename(outpath, path);
4840 gint copy_dir(const gchar *src, const gchar *dst)
4845 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4846 g_warning("failed to open directory: %s\n", src);
4850 if (make_dir(dst) < 0)
4853 while ((name = g_dir_read_name(dir)) != NULL) {
4854 gchar *old_file, *new_file;
4855 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4856 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4857 debug_print("copying: %s -> %s\n", old_file, new_file);
4858 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4859 gint r = copy_file(old_file, new_file, TRUE);
4866 /* Windows has no symlinks. Or well, Vista seems to
4867 have something like this but the semantics might be
4868 different. Thus we don't use it under Windows. */
4869 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4872 gchar *target = g_file_read_link(old_file, &error);
4874 r = symlink(target, new_file);
4881 #endif /*G_OS_WIN32*/
4882 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4883 gint r = copy_dir(old_file, new_file);
4894 /* crude test to see if a file is an email. */
4895 gboolean file_is_email (const gchar *filename)
4901 if (filename == NULL)
4903 if ((fp = g_fopen(filename, "rb")) == NULL)
4905 while (i < 60 && score < 3
4906 && fgets(buffer, sizeof (buffer), fp) > 0) {
4907 if (!strncmp(buffer, "From:", strlen("From:")))
4909 if (!strncmp(buffer, "To:", strlen("To:")))
4911 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4916 return (score >= 3);
4919 gboolean sc_g_list_bigger(GList *list, gint max)
4923 while (cur && i <= max+1) {
4930 gboolean sc_g_slist_bigger(GSList *list, gint max)
4934 while (cur && i <= max+1) {
4941 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4942 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4943 NULL, NULL, NULL, NULL, NULL, NULL};
4944 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4945 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4946 NULL, NULL, NULL, NULL, NULL, NULL};
4947 const gchar *s_am_up = NULL;
4948 const gchar *s_pm_up = NULL;
4949 const gchar *s_am_low = NULL;
4950 const gchar *s_pm_low = NULL;
4951 const gchar *def_loc_format = NULL;
4952 const gchar *date_loc_format = NULL;
4953 const gchar *time_loc_format = NULL;
4954 const gchar *time_am_pm = NULL;
4956 static gboolean time_names_init_done = FALSE;
4958 static void init_time_names(void)
4960 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4961 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4962 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4963 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4964 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4965 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4966 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4968 monthnames[0] = Q_("Complete month name for use by strftime|January");
4969 monthnames[1] = Q_("Complete month name for use by strftime|February");
4970 monthnames[2] = Q_("Complete month name for use by strftime|March");
4971 monthnames[3] = Q_("Complete month name for use by strftime|April");
4972 monthnames[4] = Q_("Complete month name for use by strftime|May");
4973 monthnames[5] = Q_("Complete month name for use by strftime|June");
4974 monthnames[6] = Q_("Complete month name for use by strftime|July");
4975 monthnames[7] = Q_("Complete month name for use by strftime|August");
4976 monthnames[8] = Q_("Complete month name for use by strftime|September");
4977 monthnames[9] = Q_("Complete month name for use by strftime|October");
4978 monthnames[10] = Q_("Complete month name for use by strftime|November");
4979 monthnames[11] = Q_("Complete month name for use by strftime|December");
4981 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4982 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4983 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4984 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4985 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4986 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4987 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4989 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4990 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4991 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4992 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4993 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4994 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4995 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4996 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4997 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4998 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4999 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
5000 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
5002 s_am_up = Q_("For use by strftime (morning)|AM");
5003 s_pm_up = Q_("For use by strftime (afternoon)|PM");
5004 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5005 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5007 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5008 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5009 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5011 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5013 time_names_init_done = TRUE;
5016 #define CHECK_SIZE() { \
5017 total_done += len; \
5018 if (total_done >= buflen) { \
5019 buf[buflen-1] = '\0'; \
5024 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5026 gchar *curpos = buf;
5027 gint total_done = 0;
5028 gchar subbuf[64], subfmt[64];
5029 static time_t last_tzset = (time_t)0;
5031 if (!time_names_init_done)
5034 if (format == NULL || lt == NULL)
5037 if (last_tzset != time(NULL)) {
5039 last_tzset = time(NULL);
5042 if (*format == '%') {
5043 gint len = 0, tmp = 0;
5047 len = 1; CHECK_SIZE();
5051 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5052 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5055 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5056 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5060 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5061 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5064 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5065 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5068 fast_strftime(subbuf, 64, def_loc_format, lt);
5069 len = strlen(subbuf); CHECK_SIZE();
5070 strncpy2(curpos, subbuf, buflen - total_done);
5073 total_done += 2; CHECK_SIZE();
5074 tmp = (lt->tm_year + 1900)/100;
5075 *curpos++ = '0'+(tmp / 10);
5076 *curpos++ = '0'+(tmp % 10);
5079 total_done += 2; CHECK_SIZE();
5080 *curpos++ = '0'+(lt->tm_mday / 10);
5081 *curpos++ = '0'+(lt->tm_mday % 10);
5084 total_done += 8; CHECK_SIZE();
5085 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5086 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5088 *curpos++ = '0'+(lt->tm_mday / 10);
5089 *curpos++ = '0'+(lt->tm_mday % 10);
5091 tmp = lt->tm_year%100;
5092 *curpos++ = '0'+(tmp / 10);
5093 *curpos++ = '0'+(tmp % 10);
5096 len = 2; CHECK_SIZE();
5097 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5100 len = 10; CHECK_SIZE();
5101 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5102 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5105 total_done += 2; CHECK_SIZE();
5106 *curpos++ = '0'+(lt->tm_hour / 10);
5107 *curpos++ = '0'+(lt->tm_hour % 10);
5110 total_done += 2; CHECK_SIZE();
5116 *curpos++ = '0'+(tmp / 10);
5117 *curpos++ = '0'+(tmp % 10);
5120 len = 3; CHECK_SIZE();
5121 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5124 len = 2; CHECK_SIZE();
5125 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5128 len = 2; CHECK_SIZE();
5134 snprintf(curpos, buflen - total_done, "%2d", tmp);
5137 total_done += 2; CHECK_SIZE();
5138 tmp = lt->tm_mon + 1;
5139 *curpos++ = '0'+(tmp / 10);
5140 *curpos++ = '0'+(tmp % 10);
5143 total_done += 2; CHECK_SIZE();
5144 *curpos++ = '0'+(lt->tm_min / 10);
5145 *curpos++ = '0'+(lt->tm_min % 10);
5148 len = 1; CHECK_SIZE();
5152 if (lt->tm_hour >= 12) {
5153 len = strlen(s_pm_up); CHECK_SIZE();
5154 snprintf(curpos, buflen-total_done, s_pm_up);
5156 len = strlen(s_am_up); CHECK_SIZE();
5157 snprintf(curpos, buflen-total_done, s_am_up);
5161 if (lt->tm_hour >= 12) {
5162 len = strlen(s_pm_low); CHECK_SIZE();
5163 snprintf(curpos, buflen-total_done, s_pm_low);
5165 len = strlen(s_am_low); CHECK_SIZE();
5166 snprintf(curpos, buflen-total_done, s_am_low);
5170 fast_strftime(subbuf, 64, time_am_pm, lt);
5171 len = strlen(subbuf); CHECK_SIZE();
5172 strncpy2(curpos, subbuf, buflen - total_done);
5175 total_done += 5; CHECK_SIZE();
5176 *curpos++ = '0'+(lt->tm_hour / 10);
5177 *curpos++ = '0'+(lt->tm_hour % 10);
5179 *curpos++ = '0'+(lt->tm_min / 10);
5180 *curpos++ = '0'+(lt->tm_min % 10);
5183 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5184 len = strlen(subbuf); CHECK_SIZE();
5185 strncpy2(curpos, subbuf, buflen - total_done);
5188 total_done += 2; CHECK_SIZE();
5189 *curpos++ = '0'+(lt->tm_sec / 10);
5190 *curpos++ = '0'+(lt->tm_sec % 10);
5193 len = 1; CHECK_SIZE();
5197 total_done += 8; CHECK_SIZE();
5198 *curpos++ = '0'+(lt->tm_hour / 10);
5199 *curpos++ = '0'+(lt->tm_hour % 10);
5201 *curpos++ = '0'+(lt->tm_min / 10);
5202 *curpos++ = '0'+(lt->tm_min % 10);
5204 *curpos++ = '0'+(lt->tm_sec / 10);
5205 *curpos++ = '0'+(lt->tm_sec % 10);
5208 len = 1; CHECK_SIZE();
5209 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5212 len = 1; CHECK_SIZE();
5213 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5216 fast_strftime(subbuf, 64, date_loc_format, lt);
5217 len = strlen(subbuf); CHECK_SIZE();
5218 strncpy2(curpos, subbuf, buflen - total_done);
5221 fast_strftime(subbuf, 64, time_loc_format, lt);
5222 len = strlen(subbuf); CHECK_SIZE();
5223 strncpy2(curpos, subbuf, buflen - total_done);
5226 total_done += 2; CHECK_SIZE();
5227 tmp = lt->tm_year%100;
5228 *curpos++ = '0'+(tmp / 10);
5229 *curpos++ = '0'+(tmp % 10);
5232 len = 4; CHECK_SIZE();
5233 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5243 /* let these complicated ones be done with the libc */
5244 snprintf(subfmt, 64, "%%%c", *format);
5245 strftime(subbuf, 64, subfmt, lt);
5246 len = strlen(subbuf); CHECK_SIZE();
5247 strncpy2(curpos, subbuf, buflen - total_done);
5251 /* let these complicated modifiers be done with the libc */
5252 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5253 strftime(subbuf, 64, subfmt, lt);
5254 len = strlen(subbuf); CHECK_SIZE();
5255 strncpy2(curpos, subbuf, buflen - total_done);
5259 g_warning("format error (%c)", *format);
5266 int len = 1; CHECK_SIZE();
5267 *curpos++ = *format++;