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 gint 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) {
506 if (fputs(buf, outfp) == EOF) {
514 if (fclose(outfp) == EOF) {
518 if (rename_force(out, file) < 0)
529 /* Similar to `strstr' but this function ignores the case of both strings. */
530 gchar *strcasestr(const gchar *haystack, const gchar *needle)
532 register size_t haystack_len, needle_len;
534 haystack_len = strlen(haystack);
535 needle_len = strlen(needle);
537 if (haystack_len < needle_len || needle_len == 0)
540 while (haystack_len >= needle_len) {
541 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
542 return (gchar *)haystack;
552 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
553 gconstpointer needle, size_t needlelen)
555 const gchar *haystack_ = (const gchar *)haystack;
556 const gchar *needle_ = (const gchar *)needle;
557 const gchar *haystack_cur = (const gchar *)haystack;
560 return memchr(haystack_, *needle_, haystacklen);
562 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
564 if (haystacklen - (haystack_cur - haystack_) < needlelen)
566 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
567 return (gpointer)haystack_cur;
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 /* compare subjects */
616 gint subject_compare(const gchar *s1, const gchar *s2)
620 if (!s1 || !s2) return -1;
621 if (!*s1 || !*s2) return -1;
623 Xstrdup_a(str1, s1, return -1);
624 Xstrdup_a(str2, s2, return -1);
626 trim_subject_for_compare(str1);
627 trim_subject_for_compare(str2);
629 if (!*str1 || !*str2) return -1;
631 return strcmp(str1, str2);
634 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
638 if (!s1 || !s2) return -1;
640 Xstrdup_a(str1, s1, return -1);
641 Xstrdup_a(str2, s2, return -1);
643 trim_subject_for_sort(str1);
644 trim_subject_for_sort(str2);
646 return g_utf8_collate(str1, str2);
649 void trim_subject_for_compare(gchar *str)
653 eliminate_parenthesis(str, '[', ']');
654 eliminate_parenthesis(str, '(', ')');
657 srcp = str + subject_get_prefix_length(str);
659 memmove(str, srcp, strlen(srcp) + 1);
662 void trim_subject_for_sort(gchar *str)
668 srcp = str + subject_get_prefix_length(str);
670 memmove(str, srcp, strlen(srcp) + 1);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void eliminate_quote(gchar *str, gchar quote_chr)
793 register gchar *srcp, *destp;
797 while ((destp = strchr(destp, quote_chr))) {
798 if ((srcp = strchr(destp + 1, quote_chr))) {
800 while (g_ascii_isspace(*srcp)) srcp++;
801 memmove(destp, srcp, strlen(srcp) + 1);
809 void extract_quote(gchar *str, gchar quote_chr)
813 if ((str = strchr(str, quote_chr))) {
815 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
816 memmove(p - 1, p, strlen(p) + 1);
821 memmove(str, str + 1, p - str);
826 void eliminate_address_comment(gchar *str)
828 register gchar *srcp, *destp;
833 while ((destp = strchr(destp, '"'))) {
834 if ((srcp = strchr(destp + 1, '"'))) {
839 while (g_ascii_isspace(*srcp)) srcp++;
840 memmove(destp, srcp, strlen(srcp) + 1);
850 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
856 else if (*srcp == ')')
862 while (g_ascii_isspace(*srcp)) srcp++;
863 memmove(destp, srcp, strlen(srcp) + 1);
867 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
869 gboolean in_quote = FALSE;
872 if (*str == c && !in_quote)
874 if (*str == quote_chr)
882 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
884 gboolean in_quote = FALSE;
887 p = str + strlen(str) - 1;
889 if (*p == c && !in_quote)
899 void extract_address(gchar *str)
901 eliminate_address_comment(str);
902 if (strchr_with_skip_quote(str, '"', '<'))
903 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
907 void extract_list_id_str(gchar *str)
909 if (strchr_with_skip_quote(str, '"', '<'))
910 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
914 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
919 if (!str) return addr_list;
921 Xstrdup_a(work, str, return addr_list);
924 eliminate_address_comment(work);
927 while (workp && *workp) {
930 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
936 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
937 extract_parenthesis_with_skip_quote
938 (workp, '"', '<', '>');
942 addr_list = g_slist_append(addr_list, g_strdup(workp));
950 GSList *address_list_append(GSList *addr_list, const gchar *str)
952 return address_list_append_real(addr_list, str, TRUE);
955 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
957 return address_list_append_real(addr_list, str, FALSE);
960 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
964 if (!str) return msgid_list;
967 while (strp && *strp) {
968 const gchar *start, *end;
971 if ((start = strchr(strp, '<')) != NULL) {
972 end = strchr(start + 1, '>');
977 msgid = g_strndup(start + 1, end - start - 1);
980 msgid_list = g_slist_prepend(msgid_list, msgid);
990 GSList *references_list_append(GSList *msgid_list, const gchar *str)
994 list = references_list_prepend(NULL, str);
995 list = g_slist_reverse(list);
996 msgid_list = g_slist_concat(msgid_list, list);
1001 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1006 if (!str) return group_list;
1008 Xstrdup_a(work, str, return group_list);
1012 while (workp && *workp) {
1015 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1023 group_list = g_slist_append(group_list,
1032 GList *add_history(GList *list, const gchar *str)
1036 g_return_val_if_fail(str != NULL, list);
1038 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1041 list = g_list_remove(list, old->data);
1042 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1045 last = g_list_last(list);
1048 list = g_list_remove(list, last->data);
1052 list = g_list_prepend(list, g_strdup(str));
1057 void remove_return(gchar *str)
1059 register gchar *p = str;
1062 if (*p == '\n' || *p == '\r')
1063 memmove(p, p + 1, strlen(p));
1069 void remove_space(gchar *str)
1071 register gchar *p = str;
1076 while (g_ascii_isspace(*(p + spc)))
1079 memmove(p, p + spc, strlen(p + spc) + 1);
1085 void unfold_line(gchar *str)
1087 register gchar *p = str;
1091 if (*p == '\n' || *p == '\r') {
1094 while (g_ascii_isspace(*(p + spc)))
1097 memmove(p, p + spc, strlen(p + spc) + 1);
1103 void subst_char(gchar *str, gchar orig, gchar subst)
1105 register gchar *p = str;
1114 void subst_chars(gchar *str, gchar *orig, gchar subst)
1116 register gchar *p = str;
1119 if (strchr(orig, *p) != NULL)
1125 void subst_for_filename(gchar *str)
1130 subst_chars(str, "\t\r\n\\/*:", '_');
1132 subst_chars(str, "\t\r\n\\/*", '_');
1136 void subst_for_shellsafe_filename(gchar *str)
1140 subst_for_filename(str);
1141 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1144 gboolean is_header_line(const gchar *str)
1146 if (str[0] == ':') return FALSE;
1148 while (*str != '\0' && *str != ' ') {
1157 gboolean is_ascii_str(const gchar *str)
1159 const guchar *p = (const guchar *)str;
1161 while (*p != '\0') {
1162 if (*p != '\t' && *p != ' ' &&
1163 *p != '\r' && *p != '\n' &&
1164 (*p < 32 || *p >= 127))
1172 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1174 const gchar *first_pos;
1175 const gchar *last_pos;
1176 const gchar *p = str;
1177 gint quote_level = -1;
1179 /* speed up line processing by only searching to the last '>' */
1180 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1181 /* skip a line if it contains a '<' before the initial '>' */
1182 if (memchr(str, '<', first_pos - str) != NULL)
1184 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1188 while (p <= last_pos) {
1189 while (p < last_pos) {
1190 if (g_ascii_isspace(*p))
1196 if (strchr(quote_chars, *p))
1198 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1199 /* any characters are allowed except '-' and space */
1201 && !strchr(quote_chars, *p)
1202 && !g_ascii_isspace(*p)
1205 if (strchr(quote_chars, *p))
1217 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1219 const gchar *p = str, *q;
1220 gint cur_line = 0, len;
1222 while ((q = strchr(p, '\n')) != NULL) {
1224 if (len > max_chars) {
1234 if (len > max_chars) {
1243 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1245 gchar * position = NULL;
1246 gchar * tmp_pos = NULL;
1249 if (quote_chars == NULL)
1252 for (i = 0; i < strlen(quote_chars); i++) {
1253 tmp_pos = strchr (str, quote_chars[i]);
1255 || (tmp_pos != NULL && position >= tmp_pos) )
1261 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1263 gchar * position = NULL;
1264 gchar * tmp_pos = NULL;
1267 if (quote_chars == NULL)
1270 for (i = 0; i < strlen(quote_chars); i++) {
1271 tmp_pos = strrchr (str, quote_chars[i]);
1273 || (tmp_pos != NULL && position <= tmp_pos) )
1279 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1281 register guint haystack_len, needle_len;
1282 gboolean in_squote = FALSE, in_dquote = FALSE;
1284 haystack_len = strlen(haystack);
1285 needle_len = strlen(needle);
1287 if (haystack_len < needle_len || needle_len == 0)
1290 while (haystack_len >= needle_len) {
1291 if (!in_squote && !in_dquote &&
1292 !strncmp(haystack, needle, needle_len))
1293 return (gchar *)haystack;
1295 /* 'foo"bar"' -> foo"bar"
1296 "foo'bar'" -> foo'bar' */
1297 if (*haystack == '\'') {
1300 else if (!in_dquote)
1302 } else if (*haystack == '\"') {
1305 else if (!in_squote)
1316 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1319 gchar quote_chr = '"';
1321 gboolean in_quote = FALSE;
1325 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1329 if (*p == op && !in_quote)
1331 else if (*p == cl && !in_quote)
1333 else if (*p == quote_chr)
1346 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1349 GSList *string_list = NULL, *slist;
1351 const gchar *s_op, *s_cl;
1354 g_return_val_if_fail(str != NULL, NULL);
1357 max_tokens = G_MAXINT;
1359 s_op = strchr_with_skip_quote(str, '"', op);
1360 if (!s_op) return NULL;
1362 s_cl = strchr_parenthesis_close(str, op, cl);
1370 new_string = g_new(gchar, len + 1);
1371 strncpy(new_string, str, len);
1372 new_string[len] = 0;
1373 string_list = g_slist_prepend(string_list, new_string);
1377 while (*str && g_ascii_isspace(*str)) str++;
1379 string_list = g_slist_prepend(string_list,
1382 s_op = strchr_with_skip_quote(str, '"', op);
1383 if (!--max_tokens || !s_op) break;
1387 s_cl = strchr_parenthesis_close(str, op, cl);
1388 } while (--max_tokens && s_cl);
1391 str_array = g_new(gchar*, n);
1395 str_array[i--] = NULL;
1396 for (slist = string_list; slist; slist = slist->next)
1397 str_array[i--] = slist->data;
1399 g_slist_free(string_list);
1404 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1407 GSList *string_list = NULL, *slist;
1408 gchar **str_array, *s, *new_str;
1409 guint i, n = 1, len;
1411 g_return_val_if_fail(str != NULL, NULL);
1412 g_return_val_if_fail(delim != NULL, NULL);
1415 max_tokens = G_MAXINT;
1417 s = strstr_with_skip_quote(str, delim);
1419 guint delimiter_len = strlen(delim);
1423 new_str = g_strndup(str, len);
1425 if (new_str[0] == '\'' || new_str[0] == '\"') {
1426 if (new_str[len - 1] == new_str[0]) {
1427 new_str[len - 1] = '\0';
1428 memmove(new_str, new_str + 1, len - 1);
1431 string_list = g_slist_prepend(string_list, new_str);
1433 str = s + delimiter_len;
1434 s = strstr_with_skip_quote(str, delim);
1435 } while (--max_tokens && s);
1439 new_str = g_strdup(str);
1440 if (new_str[0] == '\'' || new_str[0] == '\"') {
1442 if (new_str[len - 1] == new_str[0]) {
1443 new_str[len - 1] = '\0';
1444 memmove(new_str, new_str + 1, len - 1);
1447 string_list = g_slist_prepend(string_list, new_str);
1451 str_array = g_new(gchar*, n);
1455 str_array[i--] = NULL;
1456 for (slist = string_list; slist; slist = slist->next)
1457 str_array[i--] = slist->data;
1459 g_slist_free(string_list);
1464 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1466 gchar *abbrev_group;
1468 const gchar *p = group;
1471 g_return_val_if_fail(group != NULL, NULL);
1473 last = group + strlen(group);
1474 abbrev_group = ap = g_malloc(strlen(group) + 1);
1479 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1481 while (*p != '.') p++;
1484 return abbrev_group;
1489 return abbrev_group;
1492 gchar *trim_string(const gchar *str, gint len)
1494 const gchar *p = str;
1499 if (!str) return NULL;
1500 if (strlen(str) <= len)
1501 return g_strdup(str);
1502 if (g_utf8_validate(str, -1, NULL) == FALSE)
1503 return g_strdup(str);
1505 while (*p != '\0') {
1506 mb_len = g_utf8_skip[*(guchar *)p];
1509 else if (new_len + mb_len > len)
1516 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1517 return g_strconcat(new_str, "...", NULL);
1520 GList *uri_list_extract_filenames(const gchar *uri_list)
1522 GList *result = NULL;
1524 gchar *escaped_utf8uri;
1530 while (g_ascii_isspace(*p)) p++;
1531 if (!strncmp(p, "file:", 5)) {
1534 while (*q && *q != '\n' && *q != '\r') q++;
1537 gchar *file, *locale_file = NULL;
1539 while (q > p && g_ascii_isspace(*q))
1541 Xalloca(escaped_utf8uri, q - p + 2,
1543 Xalloca(file, q - p + 2,
1546 strncpy(escaped_utf8uri, p, q - p + 1);
1547 escaped_utf8uri[q - p + 1] = '\0';
1548 decode_uri(file, escaped_utf8uri);
1550 * g_filename_from_uri() rejects escaped/locale encoded uri
1551 * string which come from Nautilus.
1553 if (g_utf8_validate(file, -1, NULL))
1555 = conv_codeset_strdup(
1558 conv_get_locale_charset_str());
1560 locale_file = g_strdup(file + 5);
1561 result = g_list_append(result, locale_file);
1565 p = strchr(p, '\n');
1572 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1575 static gint axtoi(const gchar *hexstr)
1577 gint hi, lo, result;
1580 if ('0' <= hi && hi <= '9') {
1583 if ('a' <= hi && hi <= 'f') {
1586 if ('A' <= hi && hi <= 'F') {
1591 if ('0' <= lo && lo <= '9') {
1594 if ('a' <= lo && lo <= 'f') {
1597 if ('A' <= lo && lo <= 'F') {
1600 result = lo + (16 * hi);
1604 gboolean is_uri_string(const gchar *str)
1606 while (str && *str && g_ascii_isspace(*str))
1608 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1609 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1610 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1611 g_ascii_strncasecmp(str, "www.", 4) == 0);
1614 gchar *get_uri_path(const gchar *uri)
1616 while (uri && *uri && g_ascii_isspace(*uri))
1618 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1619 return (gchar *)(uri + 7);
1620 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1621 return (gchar *)(uri + 8);
1622 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1623 return (gchar *)(uri + 6);
1625 return (gchar *)uri;
1628 gint get_uri_len(const gchar *str)
1632 if (is_uri_string(str)) {
1633 for (p = str; *p != '\0'; p++) {
1634 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1643 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1644 * plusses, and escape characters are used)
1646 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1648 gchar *dec = decoded_uri;
1649 const gchar *enc = encoded_uri;
1654 if (isxdigit((guchar)enc[0]) &&
1655 isxdigit((guchar)enc[1])) {
1661 if (with_plus && *enc == '+')
1673 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1675 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1678 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1679 gchar **subject, gchar **body, gchar **attach)
1683 const gchar *forbidden_uris[] = { ".gnupg/",
1688 Xstrdup_a(tmp_mailto, mailto, return -1);
1690 if (!strncmp(tmp_mailto, "mailto:", 7))
1693 p = strchr(tmp_mailto, '?');
1700 *to = g_strdup(tmp_mailto);
1703 gchar *field, *value;
1720 if (*value == '\0') continue;
1722 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1723 *cc = g_strdup(value);
1724 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1725 *bcc = g_strdup(value);
1726 } else if (subject && !*subject &&
1727 !g_ascii_strcasecmp(field, "subject")) {
1728 *subject = g_malloc(strlen(value) + 1);
1729 decode_uri(*subject, value);
1730 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1731 *body = g_malloc(strlen(value) + 1);
1732 decode_uri(*body, value);
1733 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1735 *attach = g_malloc(strlen(value) + 1);
1736 decode_uri(*attach, value);
1737 for (; forbidden_uris[i]; i++) {
1738 if (strstr(*attach, forbidden_uris[i])) {
1739 g_print("Refusing to attach '%s', potential private data leak\n",
1754 #include <windows.h>
1755 #ifndef CSIDL_APPDATA
1756 #define CSIDL_APPDATA 0x001a
1758 #ifndef CSIDL_LOCAL_APPDATA
1759 #define CSIDL_LOCAL_APPDATA 0x001c
1761 #ifndef CSIDL_FLAG_CREATE
1762 #define CSIDL_FLAG_CREATE 0x8000
1764 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1768 w32_strerror (int w32_errno)
1770 static char strerr[256];
1771 int ec = (int)GetLastError ();
1775 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1776 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1777 strerr, DIM (strerr)-1, NULL);
1781 static __inline__ void *
1782 dlopen (const char * name, int flag)
1784 void * hd = LoadLibrary (name);
1788 static __inline__ void *
1789 dlsym (void * hd, const char * sym)
1793 void * fnc = GetProcAddress (hd, sym);
1802 static __inline__ const char *
1805 return w32_strerror (0);
1809 static __inline__ int
1821 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1823 static int initialized;
1824 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1828 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1834 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1836 handle = dlopen (dllnames[i], RTLD_LAZY);
1839 func = dlsym (handle, "SHGetFolderPathA");
1850 return func (a,b,c,d,e);
1855 /* Returns a static string with the directroy from which the module
1856 has been loaded. Returns an empty string on error. */
1857 static char *w32_get_module_dir(void)
1859 static char *moddir;
1862 char name[MAX_PATH+10];
1865 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1868 p = strrchr (name, '\\');
1874 moddir = g_strdup (name);
1878 #endif /* G_OS_WIN32 */
1880 /* Return a static string with the locale dir. */
1881 const gchar *get_locale_dir(void)
1883 static gchar *loc_dir;
1887 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1888 "\\share\\locale", NULL);
1891 loc_dir = LOCALEDIR;
1897 const gchar *get_home_dir(void)
1900 static char home_dir[MAX_PATH] = "";
1902 if (home_dir[0] == '\0') {
1903 if (w32_shgetfolderpath
1904 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1905 NULL, 0, home_dir) < 0)
1906 strcpy (home_dir, "C:\\Sylpheed");
1910 static const gchar *homeenv = NULL;
1915 if (!homeenv && g_getenv("HOME") != NULL)
1916 homeenv = g_strdup(g_getenv("HOME"));
1918 homeenv = g_get_home_dir();
1924 const gchar *get_rc_dir(void)
1926 static gchar *rc_dir = NULL;
1929 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1935 const gchar *get_mail_base_dir(void)
1938 static gchar *mail_base_dir = NULL;
1941 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1944 return mail_base_dir;
1946 return get_home_dir();
1951 const gchar *prefs_common_get_data_root(void);
1952 gchar *last_data_root = NULL;
1955 const gchar *get_news_cache_dir(void)
1957 static gchar *news_cache_dir = NULL;
1959 const gchar *data_root = prefs_common_get_data_root();
1960 if (strcmp2(data_root, last_data_root)) {
1961 g_free(news_cache_dir);
1962 news_cache_dir = NULL;
1965 if (!news_cache_dir)
1967 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1968 NEWS_CACHE_DIR, NULL);
1972 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1973 "Claws", G_DIR_SEPARATOR_S,
1974 g_get_user_name(), G_DIR_SEPARATOR_S,
1975 NEWS_CACHE_DIR, NULL);
1976 g_free(last_data_root);
1977 last_data_root = g_strdup(last_data_root);
1979 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1980 NEWS_CACHE_DIR, NULL);
1981 g_free(last_data_root);
1982 last_data_root = NULL;
1986 return news_cache_dir;
1989 const gchar *get_imap_cache_dir(void)
1991 static gchar *imap_cache_dir = NULL;
1993 const gchar *data_root = prefs_common_get_data_root();
1994 if (strcmp2(data_root, last_data_root)) {
1995 g_free(imap_cache_dir);
1996 imap_cache_dir = NULL;
2000 if (!imap_cache_dir)
2002 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2003 IMAP_CACHE_DIR, NULL);
2007 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2008 "Claws", G_DIR_SEPARATOR_S,
2009 g_get_user_name(), G_DIR_SEPARATOR_S,
2010 IMAP_CACHE_DIR, NULL);
2011 g_free(last_data_root);
2012 last_data_root = g_strdup(last_data_root);
2014 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2015 IMAP_CACHE_DIR, NULL);
2016 g_free(last_data_root);
2017 last_data_root = NULL;
2022 return imap_cache_dir;
2025 const gchar *get_mbox_cache_dir(void)
2027 static gchar *mbox_cache_dir = NULL;
2029 const gchar *data_root = prefs_common_get_data_root();
2030 if (strcmp2(data_root, last_data_root)) {
2031 g_free(mbox_cache_dir);
2032 mbox_cache_dir = NULL;
2035 if (!mbox_cache_dir)
2037 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2038 MBOX_CACHE_DIR, NULL);
2042 mbox_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2043 "Claws", G_DIR_SEPARATOR_S,
2044 g_get_user_name(), G_DIR_SEPARATOR_S,
2045 MBOX_CACHE_DIR, NULL);
2046 g_free(last_data_root);
2047 last_data_root = g_strdup(last_data_root);
2049 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2050 MBOX_CACHE_DIR, NULL);
2051 g_free(last_data_root);
2052 last_data_root = NULL;
2057 return mbox_cache_dir;
2060 const gchar *get_mime_tmp_dir(void)
2062 static gchar *mime_tmp_dir = NULL;
2065 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2066 MIME_TMP_DIR, NULL);
2068 return mime_tmp_dir;
2071 const gchar *get_template_dir(void)
2073 static gchar *template_dir = NULL;
2076 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2077 TEMPLATE_DIR, NULL);
2079 return template_dir;
2082 const gchar *get_header_cache_dir(void)
2084 static gchar *header_dir = NULL;
2087 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2088 HEADER_CACHE_DIR, NULL);
2093 /* Return the default directory for Plugins. */
2094 const gchar *get_plugin_dir(void)
2097 static gchar *plugin_dir = NULL;
2100 plugin_dir = g_strconcat(w32_get_module_dir(),
2101 "\\lib\\claws-mail\\plugins\\",
2105 if (is_dir_exist(PLUGINDIR))
2108 static gchar *plugin_dir = NULL;
2110 plugin_dir = g_strconcat(get_rc_dir(),
2111 G_DIR_SEPARATOR_S, "plugins",
2112 G_DIR_SEPARATOR_S, NULL);
2118 const gchar *get_tmp_dir(void)
2120 static gchar *tmp_dir = NULL;
2123 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2129 gchar *get_tmp_file(void)
2132 static guint32 id = 0;
2134 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2135 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2140 const gchar *get_domain_name(void)
2143 static gchar *domain_name = NULL;
2149 if (gethostname(hostname, sizeof(hostname)) != 0) {
2150 perror("gethostname");
2151 domain_name = "unknown";
2153 hostname[sizeof(hostname) - 1] = '\0';
2154 if ((hp = my_gethostbyname(hostname)) == NULL) {
2155 perror("gethostbyname");
2156 domain_name = g_strdup(hostname);
2158 domain_name = g_strdup(hp->h_name);
2161 debug_print("domain name = %s\n", domain_name);
2170 off_t get_file_size(const gchar *file)
2174 if (g_stat(file, &s) < 0) {
2175 FILE_OP_ERROR(file, "stat");
2182 time_t get_file_mtime(const gchar *file)
2186 if (g_stat(file, &s) < 0) {
2187 FILE_OP_ERROR(file, "stat");
2194 off_t get_file_size_as_crlf(const gchar *file)
2198 gchar buf[BUFFSIZE];
2200 if ((fp = g_fopen(file, "rb")) == NULL) {
2201 FILE_OP_ERROR(file, "fopen");
2205 while (fgets(buf, sizeof(buf), fp) != NULL) {
2207 size += strlen(buf) + 2;
2211 FILE_OP_ERROR(file, "fgets");
2220 off_t get_left_file_size(FILE *fp)
2226 if ((pos = ftell(fp)) < 0) {
2230 if (fseek(fp, 0L, SEEK_END) < 0) {
2234 if ((end = ftell(fp)) < 0) {
2239 if (fseek(fp, pos, SEEK_SET) < 0) {
2247 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2254 if (g_stat(file, &s) < 0) {
2255 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2259 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2266 /* Test on whether FILE is a relative file name. This is
2267 * straightforward for Unix but more complex for Windows. */
2268 gboolean is_relative_filename(const gchar *file)
2273 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2274 return FALSE; /* Prefixed with a hostname - this can't
2275 * be a relative name. */
2277 if ( ((*file >= 'a' && *file <= 'z')
2278 || (*file >= 'A' && *file <= 'Z'))
2280 file += 2; /* Skip drive letter. */
2282 return !(*file == '\\' || *file == '/');
2284 return !(*file == G_DIR_SEPARATOR);
2289 gboolean is_dir_exist(const gchar *dir)
2294 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2297 gboolean is_file_entry_exist(const gchar *file)
2302 return g_file_test(file, G_FILE_TEST_EXISTS);
2305 gboolean dirent_is_regular_file(struct dirent *d)
2307 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2308 if (d->d_type == DT_REG)
2310 else if (d->d_type != DT_UNKNOWN)
2314 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2317 gboolean dirent_is_directory(struct dirent *d)
2319 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2320 if (d->d_type == DT_DIR)
2322 else if (d->d_type != DT_UNKNOWN)
2326 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2329 gint change_dir(const gchar *dir)
2331 gchar *prevdir = NULL;
2334 prevdir = g_get_current_dir();
2336 if (g_chdir(dir) < 0) {
2337 FILE_OP_ERROR(dir, "chdir");
2338 if (debug_mode) g_free(prevdir);
2340 } else if (debug_mode) {
2343 cwd = g_get_current_dir();
2344 if (strcmp(prevdir, cwd) != 0)
2345 g_print("current dir: %s\n", cwd);
2353 gint make_dir(const gchar *dir)
2355 if (g_mkdir(dir, S_IRWXU) < 0) {
2356 FILE_OP_ERROR(dir, "mkdir");
2359 if (g_chmod(dir, S_IRWXU) < 0)
2360 FILE_OP_ERROR(dir, "chmod");
2365 gint make_dir_hier(const gchar *dir)
2370 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2371 parent_dir = g_strndup(dir, p - dir);
2372 if (*parent_dir != '\0') {
2373 if (!is_dir_exist(parent_dir)) {
2374 if (make_dir(parent_dir) < 0) {
2383 if (!is_dir_exist(dir)) {
2384 if (make_dir(dir) < 0)
2391 gint remove_all_files(const gchar *dir)
2394 const gchar *dir_name;
2397 prev_dir = g_get_current_dir();
2399 if (g_chdir(dir) < 0) {
2400 FILE_OP_ERROR(dir, "chdir");
2405 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2406 g_warning("failed to open directory: %s\n", dir);
2411 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2412 if (g_unlink(dir_name) < 0)
2413 FILE_OP_ERROR(dir_name, "unlink");
2418 if (g_chdir(prev_dir) < 0) {
2419 FILE_OP_ERROR(prev_dir, "chdir");
2429 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2432 const gchar *dir_name;
2436 prev_dir = g_get_current_dir();
2438 if (g_chdir(dir) < 0) {
2439 FILE_OP_ERROR(dir, "chdir");
2444 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2445 g_warning("failed to open directory: %s\n", dir);
2450 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2451 file_no = to_number(dir_name);
2452 if (file_no > 0 && first <= file_no && file_no <= last) {
2453 if (is_dir_exist(dir_name))
2455 if (g_unlink(dir_name) < 0)
2456 FILE_OP_ERROR(dir_name, "unlink");
2462 if (g_chdir(prev_dir) < 0) {
2463 FILE_OP_ERROR(prev_dir, "chdir");
2473 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2476 const gchar *dir_name;
2480 prev_dir = g_get_current_dir();
2482 if (g_chdir(dir) < 0) {
2483 FILE_OP_ERROR(dir, "chdir");
2488 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2489 FILE_OP_ERROR(dir, "opendir");
2494 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2495 file_no = to_number(dir_name);
2496 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2497 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2498 if (is_dir_exist(dir_name))
2500 if (g_unlink(dir_name) < 0)
2501 FILE_OP_ERROR(dir_name, "unlink");
2507 if (g_chdir(prev_dir) < 0) {
2508 FILE_OP_ERROR(prev_dir, "chdir");
2518 gint remove_all_numbered_files(const gchar *dir)
2520 return remove_numbered_files(dir, 0, UINT_MAX);
2523 gint remove_expired_files(const gchar *dir, guint hours)
2526 const gchar *dir_name;
2530 time_t mtime, now, expire_time;
2532 prev_dir = g_get_current_dir();
2534 if (g_chdir(dir) < 0) {
2535 FILE_OP_ERROR(dir, "chdir");
2540 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2541 g_warning("failed to open directory: %s\n", dir);
2547 expire_time = hours * 60 * 60;
2549 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2550 file_no = to_number(dir_name);
2552 if (g_stat(dir_name, &s) < 0) {
2553 FILE_OP_ERROR(dir_name, "stat");
2556 if (S_ISDIR(s.st_mode))
2558 mtime = MAX(s.st_mtime, s.st_atime);
2559 if (now - mtime > expire_time) {
2560 if (g_unlink(dir_name) < 0)
2561 FILE_OP_ERROR(dir_name, "unlink");
2568 if (g_chdir(prev_dir) < 0) {
2569 FILE_OP_ERROR(prev_dir, "chdir");
2579 gint remove_dir_recursive(const gchar *dir)
2583 const gchar *dir_name;
2586 if (g_stat(dir, &s) < 0) {
2587 FILE_OP_ERROR(dir, "stat");
2588 if (ENOENT == errno) return 0;
2592 if (!S_ISDIR(s.st_mode)) {
2593 if (g_unlink(dir) < 0) {
2594 FILE_OP_ERROR(dir, "unlink");
2601 prev_dir = g_get_current_dir();
2602 /* g_print("prev_dir = %s\n", prev_dir); */
2604 if (!path_cmp(prev_dir, dir)) {
2606 if (g_chdir("..") < 0) {
2607 FILE_OP_ERROR(dir, "chdir");
2610 prev_dir = g_get_current_dir();
2613 if (g_chdir(dir) < 0) {
2614 FILE_OP_ERROR(dir, "chdir");
2619 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2620 g_warning("failed to open directory: %s\n", dir);
2626 /* remove all files in the directory */
2627 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2628 /* g_print("removing %s\n", dir_name); */
2630 if (is_dir_exist(dir_name)) {
2631 if (remove_dir_recursive(dir_name) < 0) {
2632 g_warning("can't remove directory\n");
2636 if (g_unlink(dir_name) < 0)
2637 FILE_OP_ERROR(dir_name, "unlink");
2643 if (g_chdir(prev_dir) < 0) {
2644 FILE_OP_ERROR(prev_dir, "chdir");
2651 if (g_rmdir(dir) < 0) {
2652 FILE_OP_ERROR(dir, "rmdir");
2659 gint rename_force(const gchar *oldpath, const gchar *newpath)
2662 if (!is_file_entry_exist(oldpath)) {
2666 if (is_file_exist(newpath)) {
2667 if (g_unlink(newpath) < 0)
2668 FILE_OP_ERROR(newpath, "unlink");
2671 return g_rename(oldpath, newpath);
2675 * Append src file body to the tail of dest file.
2676 * Now keep_backup has no effects.
2678 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2680 FILE *src_fp, *dest_fp;
2684 gboolean err = FALSE;
2686 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2687 FILE_OP_ERROR(src, "fopen");
2691 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2692 FILE_OP_ERROR(dest, "fopen");
2697 if (change_file_mode_rw(dest_fp, dest) < 0) {
2698 FILE_OP_ERROR(dest, "chmod");
2699 g_warning("can't change file mode\n");
2702 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2703 if (n_read < sizeof(buf) && ferror(src_fp))
2705 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2706 g_warning("writing to %s failed.\n", dest);
2714 if (ferror(src_fp)) {
2715 FILE_OP_ERROR(src, "fread");
2719 if (fclose(dest_fp) == EOF) {
2720 FILE_OP_ERROR(dest, "fclose");
2732 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2734 FILE *src_fp, *dest_fp;
2737 gchar *dest_bak = NULL;
2738 gboolean err = FALSE;
2740 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2741 FILE_OP_ERROR(src, "fopen");
2744 if (is_file_exist(dest)) {
2745 dest_bak = g_strconcat(dest, ".bak", NULL);
2746 if (rename_force(dest, dest_bak) < 0) {
2747 FILE_OP_ERROR(dest, "rename");
2754 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2755 FILE_OP_ERROR(dest, "fopen");
2758 if (rename_force(dest_bak, dest) < 0)
2759 FILE_OP_ERROR(dest_bak, "rename");
2765 if (change_file_mode_rw(dest_fp, dest) < 0) {
2766 FILE_OP_ERROR(dest, "chmod");
2767 g_warning("can't change file mode\n");
2770 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2771 if (n_read < sizeof(buf) && ferror(src_fp))
2773 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2774 g_warning("writing to %s failed.\n", dest);
2779 if (rename_force(dest_bak, dest) < 0)
2780 FILE_OP_ERROR(dest_bak, "rename");
2787 if (ferror(src_fp)) {
2788 FILE_OP_ERROR(src, "fread");
2792 if (fclose(dest_fp) == EOF) {
2793 FILE_OP_ERROR(dest, "fclose");
2800 if (rename_force(dest_bak, dest) < 0)
2801 FILE_OP_ERROR(dest_bak, "rename");
2807 if (keep_backup == FALSE && dest_bak)
2815 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2817 if (overwrite == FALSE && is_file_exist(dest)) {
2818 g_warning("move_file(): file %s already exists.", dest);
2822 if (rename_force(src, dest) == 0) return 0;
2824 if (EXDEV != errno) {
2825 FILE_OP_ERROR(src, "rename");
2829 if (copy_file(src, dest, FALSE) < 0) return -1;
2836 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2839 gint bytes_left, to_read;
2842 if (fseek(fp, offset, SEEK_SET) < 0) {
2847 bytes_left = length;
2848 to_read = MIN(bytes_left, sizeof(buf));
2850 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2851 if (n_read < to_read && ferror(fp))
2853 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2856 bytes_left -= n_read;
2857 if (bytes_left == 0)
2859 to_read = MIN(bytes_left, sizeof(buf));
2870 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2873 gboolean err = FALSE;
2875 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2876 FILE_OP_ERROR(dest, "fopen");
2880 if (change_file_mode_rw(dest_fp, dest) < 0) {
2881 FILE_OP_ERROR(dest, "chmod");
2882 g_warning("can't change file mode\n");
2885 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2888 if (!err && fclose(dest_fp) == EOF) {
2889 FILE_OP_ERROR(dest, "fclose");
2894 g_warning("writing to %s failed.\n", dest);
2902 /* convert line endings into CRLF. If the last line doesn't end with
2903 * linebreak, add it.
2905 gchar *canonicalize_str(const gchar *str)
2911 for (p = str; *p != '\0'; ++p) {
2918 if (p == str || *(p - 1) != '\n')
2921 out = outp = g_malloc(new_len + 1);
2922 for (p = str; *p != '\0'; ++p) {
2929 if (p == str || *(p - 1) != '\n') {
2938 gint canonicalize_file(const gchar *src, const gchar *dest)
2940 FILE *src_fp, *dest_fp;
2941 gchar buf[BUFFSIZE];
2943 gboolean err = FALSE;
2944 gboolean last_linebreak = FALSE;
2946 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2947 FILE_OP_ERROR(src, "fopen");
2951 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2952 FILE_OP_ERROR(dest, "fopen");
2957 if (change_file_mode_rw(dest_fp, dest) < 0) {
2958 FILE_OP_ERROR(dest, "chmod");
2959 g_warning("can't change file mode\n");
2962 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2966 if (len == 0) break;
2967 last_linebreak = FALSE;
2969 if (buf[len - 1] != '\n') {
2970 last_linebreak = TRUE;
2971 r = fputs(buf, dest_fp);
2972 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2973 r = fputs(buf, dest_fp);
2976 r = fwrite(buf, 1, len - 1, dest_fp);
2981 r = fputs("\r\n", dest_fp);
2985 g_warning("writing to %s failed.\n", dest);
2993 if (last_linebreak == TRUE) {
2994 if (fputs("\r\n", dest_fp) == EOF)
2998 if (ferror(src_fp)) {
2999 FILE_OP_ERROR(src, "fgets");
3003 if (fclose(dest_fp) == EOF) {
3004 FILE_OP_ERROR(dest, "fclose");
3016 gint canonicalize_file_replace(const gchar *file)
3020 tmp_file = get_tmp_file();
3022 if (canonicalize_file(file, tmp_file) < 0) {
3027 if (move_file(tmp_file, file, TRUE) < 0) {
3028 g_warning("can't replace %s .\n", file);
3038 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3040 FILE *src_fp, *dest_fp;
3041 gchar buf[BUFFSIZE];
3042 gboolean err = FALSE;
3044 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3045 FILE_OP_ERROR(src, "fopen");
3049 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3050 FILE_OP_ERROR(dest, "fopen");
3055 if (change_file_mode_rw(dest_fp, dest) < 0) {
3056 FILE_OP_ERROR(dest, "chmod");
3057 g_warning("can't change file mode\n");
3060 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3062 if (fputs(buf, dest_fp) == EOF) {
3063 g_warning("writing to %s failed.\n", dest);
3071 if (ferror(src_fp)) {
3072 FILE_OP_ERROR(src, "fgets");
3076 if (fclose(dest_fp) == EOF) {
3077 FILE_OP_ERROR(dest, "fclose");
3089 gint uncanonicalize_file_replace(const gchar *file)
3093 tmp_file = get_tmp_file();
3095 if (uncanonicalize_file(file, tmp_file) < 0) {
3100 if (move_file(tmp_file, file, TRUE) < 0) {
3101 g_warning("can't replace %s .\n", file);
3111 gchar *normalize_newlines(const gchar *str)
3113 const gchar *p = str;
3116 out = outp = g_malloc(strlen(str) + 1);
3117 for (p = str; *p != '\0'; ++p) {
3119 if (*(p + 1) != '\n')
3130 gchar *get_outgoing_rfc2822_str(FILE *fp)
3132 gchar buf[BUFFSIZE];
3136 str = g_string_new(NULL);
3138 /* output header part */
3139 while (fgets(buf, sizeof(buf), fp) != NULL) {
3141 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3148 else if (next != ' ' && next != '\t') {
3152 if (fgets(buf, sizeof(buf), fp) == NULL)
3156 g_string_append(str, buf);
3157 g_string_append(str, "\r\n");
3163 /* output body part */
3164 while (fgets(buf, sizeof(buf), fp) != NULL) {
3167 g_string_append_c(str, '.');
3168 g_string_append(str, buf);
3169 g_string_append(str, "\r\n");
3173 g_string_free(str, FALSE);
3179 * Create a new boundary in a way that it is very unlikely that this
3180 * will occur in the following text. It would be easy to ensure
3181 * uniqueness if everything is either quoted-printable or base64
3182 * encoded (note that conversion is allowed), but because MIME bodies
3183 * may be nested, it may happen that the same boundary has already
3186 * boundary := 0*69<bchars> bcharsnospace
3187 * bchars := bcharsnospace / " "
3188 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3189 * "+" / "_" / "," / "-" / "." /
3190 * "/" / ":" / "=" / "?"
3192 * some special characters removed because of buggy MTAs
3195 gchar *generate_mime_boundary(const gchar *prefix)
3197 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3198 "abcdefghijklmnopqrstuvwxyz"
3203 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3204 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3207 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
3211 gint change_file_mode_rw(FILE *fp, const gchar *file)
3214 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3216 return g_chmod(file, S_IRUSR|S_IWUSR);
3220 FILE *my_tmpfile(void)
3222 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3223 const gchar suffix[] = ".XXXXXX";
3224 const gchar *tmpdir;
3226 const gchar *progname;
3233 tmpdir = get_tmp_dir();
3234 tmplen = strlen(tmpdir);
3235 progname = g_get_prgname();
3236 if (progname == NULL)
3237 progname = "claws-mail";
3238 proglen = strlen(progname);
3239 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3242 memcpy(fname, tmpdir, tmplen);
3243 fname[tmplen] = G_DIR_SEPARATOR;
3244 memcpy(fname + tmplen + 1, progname, proglen);
3245 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3247 fd = mkstemp(fname);
3254 /* verify that we can write in the file after unlinking */
3255 if (write(fd, buf, 1) < 0) {
3262 fp = fdopen(fd, "w+b");
3270 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3275 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3279 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3280 dir, G_DIR_SEPARATOR);
3281 fd = mkstemp_name(template, filename);
3284 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3285 fd = mkstemp(*filename);
3287 return fdopen(fd, "w+");
3290 FILE *str_open_as_stream(const gchar *str)
3295 g_return_val_if_fail(str != NULL, NULL);
3299 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3304 if (len == 0) return fp;
3306 if (fwrite(str, 1, len, fp) != len) {
3307 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3316 gint str_write_to_file(const gchar *str, const gchar *file)
3321 g_return_val_if_fail(str != NULL, -1);
3322 g_return_val_if_fail(file != NULL, -1);
3324 if ((fp = g_fopen(file, "wb")) == NULL) {
3325 FILE_OP_ERROR(file, "fopen");
3335 if (fwrite(str, 1, len, fp) != len) {
3336 FILE_OP_ERROR(file, "fwrite");
3342 if (fclose(fp) == EOF) {
3343 FILE_OP_ERROR(file, "fclose");
3351 gchar *file_read_to_str(const gchar *file)
3356 g_return_val_if_fail(file != NULL, NULL);
3358 if ((fp = g_fopen(file, "rb")) == NULL) {
3359 FILE_OP_ERROR(file, "fopen");
3363 str = file_read_stream_to_str(fp);
3370 gchar *file_read_stream_to_str(FILE *fp)
3377 g_return_val_if_fail(fp != NULL, NULL);
3379 array = g_byte_array_new();
3381 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3382 if (n_read < sizeof(buf) && ferror(fp))
3384 g_byte_array_append(array, buf, n_read);
3388 FILE_OP_ERROR("file stream", "fread");
3389 g_byte_array_free(array, TRUE);
3394 g_byte_array_append(array, buf, 1);
3395 str = (gchar *)array->data;
3396 g_byte_array_free(array, FALSE);
3398 if (!g_utf8_validate(str, -1, NULL)) {
3399 const gchar *src_codeset, *dest_codeset;
3401 src_codeset = conv_get_locale_charset_str();
3402 dest_codeset = CS_UTF_8;
3403 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3412 char *fgets_crlf(char *buf, int size, FILE *stream)
3414 gboolean is_cr = FALSE;
3415 gboolean last_was_cr = FALSE;
3420 while (--size > 0 && (c = getc(stream)) != EOF)
3423 is_cr = (c == '\r');
3433 last_was_cr = is_cr;
3435 if (c == EOF && cs == buf)
3443 static gint execute_async(gchar *const argv[])
3445 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3447 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3448 NULL, NULL, NULL, FALSE) == FALSE) {
3449 g_warning("Couldn't execute command: %s\n", argv[0]);
3456 static gint execute_sync(gchar *const argv[])
3460 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3462 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3463 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3464 g_warning("Couldn't execute command: %s\n", argv[0]);
3469 if (WIFEXITED(status))
3470 return WEXITSTATUS(status);
3478 gint execute_command_line(const gchar *cmdline, gboolean async)
3483 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3485 argv = strsplit_with_quote(cmdline, " ", 0);
3488 ret = execute_async(argv);
3490 ret = execute_sync(argv);
3497 gchar *get_command_output(const gchar *cmdline)
3499 gchar *child_stdout;
3502 g_return_val_if_fail(cmdline != NULL, NULL);
3504 debug_print("get_command_output(): executing: %s\n", cmdline);
3506 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3508 g_warning("Couldn't execute command: %s\n", cmdline);
3512 return child_stdout;
3515 static gint is_unchanged_uri_char(char c)
3526 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3532 for(i = 0; i < strlen(uri) ; i++) {
3533 if (is_unchanged_uri_char(uri[i])) {
3534 if (k + 2 >= bufsize)
3536 encoded_uri[k++] = uri[i];
3539 char * hexa = "0123456789ABCDEF";
3541 if (k + 4 >= bufsize)
3543 encoded_uri[k++] = '%';
3544 encoded_uri[k++] = hexa[uri[i] / 16];
3545 encoded_uri[k++] = hexa[uri[i] % 16];
3551 gint open_uri(const gchar *uri, const gchar *cmdline)
3554 gchar buf[BUFFSIZE];
3556 gchar encoded_uri[BUFFSIZE];
3557 g_return_val_if_fail(uri != NULL, -1);
3559 /* an option to choose whether to use encode_uri or not ? */
3560 encode_uri(encoded_uri, BUFFSIZE, uri);
3563 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3564 !strchr(p + 2, '%'))
3565 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3568 g_warning("Open URI command line is invalid "
3569 "(there must be only one '%%s'): %s",
3571 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3574 execute_command_line(buf, TRUE);
3576 extern osso_context_t *get_osso_context(void);
3577 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3578 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3579 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3584 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3586 gchar buf[BUFFSIZE];
3589 g_return_val_if_fail(filepath != NULL, -1);
3592 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3593 !strchr(p + 2, '%'))
3594 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3597 g_warning("Open Text Editor command line is invalid "
3598 "(there must be only one '%%s'): %s",
3600 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3603 execute_command_line(buf, TRUE);
3608 time_t remote_tzoffset_sec(const gchar *zone)
3610 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3616 time_t remoteoffset;
3618 strncpy(zone3, zone, 3);
3622 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3623 (c == '+' || c == '-')) {
3624 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3626 remoteoffset = -remoteoffset;
3627 } else if (!strncmp(zone, "UT" , 2) ||
3628 !strncmp(zone, "GMT", 2)) {
3630 } else if (strlen(zone3) == 3) {
3631 for (p = ustzstr; *p != '\0'; p += 3) {
3632 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3633 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3634 remoteoffset = iustz * 3600;
3640 } else if (strlen(zone3) == 1) {
3642 case 'Z': remoteoffset = 0; break;
3643 case 'A': remoteoffset = -1; break;
3644 case 'B': remoteoffset = -2; break;
3645 case 'C': remoteoffset = -3; break;
3646 case 'D': remoteoffset = -4; break;
3647 case 'E': remoteoffset = -5; break;
3648 case 'F': remoteoffset = -6; break;
3649 case 'G': remoteoffset = -7; break;
3650 case 'H': remoteoffset = -8; break;
3651 case 'I': remoteoffset = -9; break;
3652 case 'K': remoteoffset = -10; break; /* J is not used */
3653 case 'L': remoteoffset = -11; break;
3654 case 'M': remoteoffset = -12; break;
3655 case 'N': remoteoffset = 1; break;
3656 case 'O': remoteoffset = 2; break;
3657 case 'P': remoteoffset = 3; break;
3658 case 'Q': remoteoffset = 4; break;
3659 case 'R': remoteoffset = 5; break;
3660 case 'S': remoteoffset = 6; break;
3661 case 'T': remoteoffset = 7; break;
3662 case 'U': remoteoffset = 8; break;
3663 case 'V': remoteoffset = 9; break;
3664 case 'W': remoteoffset = 10; break;
3665 case 'X': remoteoffset = 11; break;
3666 case 'Y': remoteoffset = 12; break;
3667 default: remoteoffset = 0; break;
3669 remoteoffset = remoteoffset * 3600;
3673 return remoteoffset;
3676 time_t tzoffset_sec(time_t *now)
3680 struct tm buf1, buf2;
3682 gmt = *gmtime_r(now, &buf1);
3683 lt = localtime_r(now, &buf2);
3685 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3687 if (lt->tm_year < gmt.tm_year)
3689 else if (lt->tm_year > gmt.tm_year)
3691 else if (lt->tm_yday < gmt.tm_yday)
3693 else if (lt->tm_yday > gmt.tm_yday)
3696 if (off >= 24 * 60) /* should be impossible */
3697 off = 23 * 60 + 59; /* if not, insert silly value */
3698 if (off <= -24 * 60)
3699 off = -(23 * 60 + 59);
3704 /* calculate timezone offset */
3705 gchar *tzoffset(time_t *now)
3707 static gchar offset_string[6];
3711 struct tm buf1, buf2;
3713 gmt = *gmtime_r(now, &buf1);
3714 lt = localtime_r(now, &buf2);
3716 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3718 if (lt->tm_year < gmt.tm_year)
3720 else if (lt->tm_year > gmt.tm_year)
3722 else if (lt->tm_yday < gmt.tm_yday)
3724 else if (lt->tm_yday > gmt.tm_yday)
3732 if (off >= 24 * 60) /* should be impossible */
3733 off = 23 * 60 + 59; /* if not, insert silly value */
3735 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3737 return offset_string;
3740 void get_rfc822_date(gchar *buf, gint len)
3744 gchar day[4], mon[4];
3745 gint dd, hh, mm, ss, yyyy;
3747 gchar buf2[BUFFSIZE];
3750 lt = localtime_r(&t, &buf1);
3753 sscanf(asctime_r(lt, buf2, sizeof(buf2)), "%3s %3s %d %d:%d:%d %d\n",
3754 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3756 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3757 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3759 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3760 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3763 /* just a wrapper to suppress the warning of gcc about %c */
3764 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3765 const struct tm *tm)
3767 return strftime(s, max, format, tm);
3770 void debug_set_mode(gboolean mode)
3775 gboolean debug_get_mode(void)
3780 void debug_print_real(const gchar *format, ...)
3783 gchar buf[BUFFSIZE];
3785 if (!debug_mode) return;
3787 va_start(args, format);
3788 g_vsnprintf(buf, sizeof(buf), format, args);
3795 const char * debug_srcname(const char *file)
3797 const char *s = strrchr (file, '/');
3802 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3804 if (subject == NULL)
3807 subject += subject_get_prefix_length(subject);
3809 return g_hash_table_lookup(subject_table, subject);
3812 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3815 if (subject == NULL || *subject == 0)
3817 subject += subject_get_prefix_length(subject);
3818 g_hash_table_insert(subject_table, subject, data);
3821 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3823 if (subject == NULL)
3826 subject += subject_get_prefix_length(subject);
3827 g_hash_table_remove(subject_table, subject);
3831 *\brief Check if a string is prefixed with known (combinations)
3832 * of prefixes. The function assumes that each prefix
3833 * is terminated by zero or exactly _one_ space.
3835 *\param str String to check for a prefixes
3837 *\return int Number of chars in the prefix that should be skipped
3838 * for a "clean" subject line. If no prefix was found, 0
3841 int subject_get_prefix_length(const gchar *subject)
3843 /*!< Array with allowable reply prefixes regexps. */
3844 static const gchar * const prefixes[] = {
3845 "Re\\:", /* "Re:" */
3846 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3847 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3848 "Aw\\:", /* "Aw:" (German) */
3849 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3850 "Res\\:", /* "Res:" (Brazilian Outlook) */
3851 "Fw\\:", /* "Fw:" Forward */
3852 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3853 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3854 "Rif\\:", /* "Rif:" (Italian Outlook) */
3855 "Sv\\:", /* "Sv" (Norwegian) */
3856 "Vs\\:", /* "Vs" (Norwegian) */
3857 "Ad\\:", /* "Ad" (Norwegian) */
3858 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3861 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3864 static regex_t regex;
3865 static gboolean init_;
3867 if (!subject) return 0;
3868 if (!*subject) return 0;
3871 GString *s = g_string_new("");
3873 for (n = 0; n < PREFIXES; n++)
3874 /* Terminate each prefix regexpression by a
3875 * "\ ?" (zero or ONE space), and OR them */
3876 g_string_append_printf(s, "(%s\\ ?)%s",
3881 g_string_prepend(s, "(");
3882 g_string_append(s, ")+"); /* match at least once */
3883 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3886 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3887 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3888 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3889 debug_print("Error compiling regexp %s\n", s->str);
3890 g_string_free(s, TRUE);
3894 g_string_free(s, TRUE);
3898 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3904 guint g_stricase_hash(gconstpointer gptr)
3906 guint hash_result = 0;
3909 for (str = gptr; str && *str; str++) {
3910 hash_result += toupper(*str);
3916 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3918 const char *str1 = gptr1;
3919 const char *str2 = gptr2;
3921 return !strcasecmp(str1, str2);
3924 gint g_int_compare(gconstpointer a, gconstpointer b)
3926 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3929 gchar *generate_msgid(gchar *buf, gint len)
3937 lt = localtime_r(&t, &buft);
3939 if (strcmp(buf, "") == 0) {
3940 addr = g_strconcat("@", get_domain_name(), NULL);
3943 addr = g_strconcat("@", buf, NULL);
3946 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3947 lt->tm_year + 1900, lt->tm_mon + 1,
3948 lt->tm_mday, lt->tm_hour,
3949 lt->tm_min, lt->tm_sec,
3950 (guint) rand(), addr);
3957 quote_cmd_argument()
3959 return a quoted string safely usable in argument of a command.
3961 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3964 gint quote_cmd_argument(gchar * result, guint size,
3974 for(p = path ; * p != '\0' ; p ++) {
3976 if (isalnum((guchar)*p) || (* p == '/')) {
3977 if (remaining > 0) {
3983 result[size - 1] = '\0';
3988 if (remaining >= 2) {
3996 result[size - 1] = '\0';
4001 if (remaining > 0) {
4005 result[size - 1] = '\0';
4019 static void g_node_map_recursive(GNode *node, gpointer data)
4021 GNodeMapData *mapdata = (GNodeMapData *) data;
4023 GNodeMapData newmapdata;
4026 newdata = mapdata->func(node->data, mapdata->data);
4027 if (newdata != NULL) {
4028 newnode = g_node_new(newdata);
4029 g_node_append(mapdata->parent, newnode);
4031 newmapdata.parent = newnode;
4032 newmapdata.func = mapdata->func;
4033 newmapdata.data = mapdata->data;
4035 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4039 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4042 GNodeMapData mapdata;
4044 g_return_val_if_fail(node != NULL, NULL);
4045 g_return_val_if_fail(func != NULL, NULL);
4047 root = g_node_new(func(node->data, data));
4049 mapdata.parent = root;
4050 mapdata.func = func;
4051 mapdata.data = data;
4053 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4058 #define HEX_TO_INT(val, hex) \
4062 if ('0' <= c && c <= '9') { \
4064 } else if ('a' <= c && c <= 'f') { \
4065 val = c - 'a' + 10; \
4066 } else if ('A' <= c && c <= 'F') { \
4067 val = c - 'A' + 10; \
4073 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4080 if (hi == -1 || lo == -1)
4083 *out = (hi << 4) + lo;
4087 #define INT_TO_HEX(hex, val) \
4090 hex = '0' + (val); \
4092 hex = 'A' + (val) - 10; \
4095 void get_hex_str(gchar *out, guchar ch)
4099 INT_TO_HEX(hex, ch >> 4);
4101 INT_TO_HEX(hex, ch & 0x0f);
4107 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4109 #define G_PRINT_REF g_print
4113 *\brief Register ref counted pointer. It is based on GBoxed, so should
4114 * work with anything that uses the GType system. The semantics
4115 * are similar to a C++ auto pointer, with the exception that
4116 * C doesn't have automatic closure (calling destructors) when
4117 * exiting a block scope.
4118 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4119 * function directly.
4121 *\return GType A GType type.
4123 GType g_auto_pointer_register(void)
4125 static GType auto_pointer_type;
4126 if (!auto_pointer_type)
4128 g_boxed_type_register_static
4129 ("G_TYPE_AUTO_POINTER",
4130 (GBoxedCopyFunc) g_auto_pointer_copy,
4131 (GBoxedFreeFunc) g_auto_pointer_free);
4132 return auto_pointer_type;
4136 *\brief Structure with g_new() allocated pointer guarded by the
4139 typedef struct AutoPointerRef {
4140 void (*free) (gpointer);
4146 *\brief The auto pointer opaque structure that references the
4147 * pointer guard block.
4149 typedef struct AutoPointer {
4150 AutoPointerRef *ref;
4151 gpointer ptr; /*!< access to protected pointer */
4155 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4159 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4160 * ... when assigning, copying and freeing storage elements
4162 * gtk_list_store_new(N_S_COLUMNS,
4163 * G_TYPE_AUTO_POINTER,
4167 * Template *precious_data = g_new0(Template, 1);
4168 * g_pointer protect = g_auto_pointer_new(precious_data);
4170 * gtk_list_store_set(container, &iter,
4174 * ... the gtk_list_store has copied the pointer and
4175 * ... incremented its reference count, we should free
4176 * ... the auto pointer (in C++ a destructor would do
4177 * ... this for us when leaving block scope)
4179 * g_auto_pointer_free(protect);
4181 * ... gtk_list_store_set() now manages the data. When
4182 * ... *explicitly* requesting a pointer from the list
4183 * ... store, don't forget you get a copy that should be
4184 * ... freed with g_auto_pointer_free() eventually.
4188 *\param pointer Pointer to be guarded.
4190 *\return GAuto * Pointer that should be used in containers with
4193 GAuto *g_auto_pointer_new(gpointer p)
4195 AutoPointerRef *ref;
4201 ref = g_new0(AutoPointerRef, 1);
4202 ptr = g_new0(AutoPointer, 1);
4212 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4218 *\brief Allocate an autopointer using the passed \a free function to
4219 * free the guarded pointer
4221 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4228 aptr = g_auto_pointer_new(p);
4229 aptr->ref->free = free_;
4233 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4235 if (auto_ptr == NULL)
4237 return ((AutoPointer *) auto_ptr)->ptr;
4241 *\brief Copies an auto pointer by. It's mostly not necessary
4242 * to call this function directly, unless you copy/assign
4243 * the guarded pointer.
4245 *\param auto_ptr Auto pointer returned by previous call to
4246 * g_auto_pointer_new_XXX()
4248 *\return gpointer An auto pointer
4250 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4253 AutoPointerRef *ref;
4256 if (auto_ptr == NULL)
4261 newp = g_new0(AutoPointer, 1);
4264 newp->ptr = ref->pointer;
4268 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4274 *\brief Free an auto pointer
4276 void g_auto_pointer_free(GAuto *auto_ptr)
4279 AutoPointerRef *ref;
4281 if (auto_ptr == NULL)
4287 if (--(ref->cnt) == 0) {
4289 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4291 ref->free(ref->pointer);
4296 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4301 void replace_returns(gchar *str)
4306 while (strstr(str, "\n")) {
4307 *strstr(str, "\n") = ' ';
4309 while (strstr(str, "\r")) {
4310 *strstr(str, "\r") = ' ';
4314 /* get_uri_part() - retrieves a URI starting from scanpos.
4315 Returns TRUE if succesful */
4316 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4317 const gchar **bp, const gchar **ep, gboolean hdr)
4320 gint parenthese_cnt = 0;
4322 g_return_val_if_fail(start != NULL, FALSE);
4323 g_return_val_if_fail(scanpos != NULL, FALSE);
4324 g_return_val_if_fail(bp != NULL, FALSE);
4325 g_return_val_if_fail(ep != NULL, FALSE);
4329 /* find end point of URI */
4330 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4331 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4332 !IS_ASCII(*(const guchar *)ep_) ||
4333 strchr("[]{}<>\"", *ep_)) {
4335 } else if (strchr("(", *ep_)) {
4337 } else if (strchr(")", *ep_)) {
4338 if (parenthese_cnt > 0)
4345 /* no punctuation at end of string */
4347 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4348 * should pass some URI type to this function and decide on that whether
4349 * to perform punctuation stripping */
4351 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4353 for (; ep_ - 1 > scanpos + 1 &&
4354 IS_REAL_PUNCT(*(ep_ - 1));
4358 #undef IS_REAL_PUNCT
4365 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4367 while (bp && *bp && g_ascii_isspace(*bp))
4369 return g_strndup(bp, ep - bp);
4372 /* valid mail address characters */
4373 #define IS_RFC822_CHAR(ch) \
4377 !g_ascii_isspace(ch) && \
4378 !strchr("(),;<>\"", (ch)))
4380 /* alphabet and number within 7bit ASCII */
4381 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4382 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4384 static GHashTable *create_domain_tab(void)
4386 static const gchar *toplvl_domains [] = {
4388 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4389 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4390 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4391 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4392 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4393 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4394 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4395 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4396 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4397 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4398 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4399 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4400 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4401 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4402 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4403 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4404 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4405 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4406 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4407 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4408 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4409 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4410 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4411 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4412 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4413 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4416 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4418 g_return_val_if_fail(htab, NULL);
4419 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4420 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4424 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4426 const gint MAX_LVL_DOM_NAME_LEN = 6;
4427 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4428 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4431 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4434 for (p = buf; p < m && first < last; *p++ = *first++)
4438 return g_hash_table_lookup(tab, buf) != NULL;
4441 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4442 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4443 const gchar **bp, const gchar **ep, gboolean hdr)
4445 /* more complex than the uri part because we need to scan back and forward starting from
4446 * the scan position. */
4447 gboolean result = FALSE;
4448 const gchar *bp_ = NULL;
4449 const gchar *ep_ = NULL;
4450 static GHashTable *dom_tab;
4451 const gchar *last_dot = NULL;
4452 const gchar *prelast_dot = NULL;
4453 const gchar *last_tld_char = NULL;
4455 /* the informative part of the email address (describing the name
4456 * of the email address owner) may contain quoted parts. the
4457 * closure stack stores the last encountered quotes. */
4458 gchar closure_stack[128];
4459 gchar *ptr = closure_stack;
4461 g_return_val_if_fail(start != NULL, FALSE);
4462 g_return_val_if_fail(scanpos != NULL, FALSE);
4463 g_return_val_if_fail(bp != NULL, FALSE);
4464 g_return_val_if_fail(ep != NULL, FALSE);
4467 const gchar *start_quote = NULL;
4468 const gchar *end_quote = NULL;
4470 /* go to the real start */
4471 if (start[0] == ',')
4473 if (start[0] == ';')
4475 while (start[0] == '\n' || start[0] == '\r')
4477 while (start[0] == ' ' || start[0] == '\t')
4482 /* check if there are quotes (to skip , in them) */
4483 if (*start == '"') {
4484 start_quote = start;
4486 end_quote = strstr(start, "\"");
4492 /* skip anything between quotes */
4493 if (start_quote && end_quote) {
4498 /* find end (either , or ; or end of line) */
4499 if (strstr(start, ",") && strstr(start, ";"))
4500 *ep = strstr(start,",") < strstr(start, ";")
4501 ? strstr(start, ",") : strstr(start, ";");
4502 else if (strstr(start, ","))
4503 *ep = strstr(start, ",");
4504 else if (strstr(start, ";"))
4505 *ep = strstr(start, ";");
4507 *ep = start+strlen(start);
4509 /* go back to real start */
4510 if (start_quote && end_quote) {
4511 start = start_quote;
4514 /* check there's still an @ in that, or search
4515 * further if possible */
4516 if (strstr(start, "@") && strstr(start, "@") < *ep)
4518 else if (*ep < start+strlen(start)) {
4521 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4529 dom_tab = create_domain_tab();
4530 g_return_val_if_fail(dom_tab, FALSE);
4532 /* scan start of address */
4533 for (bp_ = scanpos - 1;
4534 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4537 /* TODO: should start with an alnum? */
4539 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4542 if (bp_ != scanpos) {
4543 /* scan end of address */
4544 for (ep_ = scanpos + 1;
4545 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4547 prelast_dot = last_dot;
4549 if (*(last_dot + 1) == '.') {
4550 if (prelast_dot == NULL)
4552 last_dot = prelast_dot;
4557 /* TODO: really should terminate with an alnum? */
4558 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4563 if (last_dot == NULL)
4565 if (last_dot >= ep_)
4566 last_dot = prelast_dot;
4567 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4571 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4572 if (*last_tld_char == '?')
4575 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4582 if (!result) return FALSE;
4584 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4585 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4586 && IS_RFC822_CHAR(*(ep_ + 3))) {
4587 /* this informative part with an @ in it is
4588 * followed by the email address */
4591 /* go to matching '>' (or next non-rfc822 char, like \n) */
4592 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4595 /* include the bracket */
4596 if (*ep_ == '>') ep_++;
4598 /* include the leading quote */
4606 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4607 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4610 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4611 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4614 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4615 #define IN_STACK() (ptr > closure_stack)
4616 /* has underrun check */
4617 #define POP_STACK() if(IN_STACK()) --ptr
4618 /* has overrun check */
4619 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4620 /* has underrun check */
4621 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4625 /* scan for the informative part. */
4626 for (bp_ -= 2; bp_ >= start; bp_--) {
4627 /* if closure on the stack keep scanning */
4628 if (PEEK_STACK() == *bp_) {
4632 if (*bp_ == '\'' || *bp_ == '"') {
4637 /* if nothing in the closure stack, do the special conditions
4638 * the following if..else expression simply checks whether
4639 * a token is acceptable. if not acceptable, the clause
4640 * should terminate the loop with a 'break' */
4641 if (!PEEK_STACK()) {
4643 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4644 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4645 /* hyphens are allowed, but only in
4647 } else if (strchr(" \"'", *bp_)) {
4648 /* but anything not being a punctiation
4651 break; /* anything else is rejected */
4658 /* scan forward (should start with an alnum) */
4659 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4675 #undef IS_ASCII_ALNUM
4676 #undef IS_RFC822_CHAR
4678 gchar *make_email_string(const gchar *bp, const gchar *ep)
4680 /* returns a mailto: URI; mailto: is also used to detect the
4681 * uri type later on in the button_pressed signal handler */
4685 tmp = g_strndup(bp, ep - bp);
4686 result = g_strconcat("mailto:", tmp, NULL);
4692 gchar *make_http_string(const gchar *bp, const gchar *ep)
4694 /* returns an http: URI; */
4698 while (bp && *bp && g_ascii_isspace(*bp))
4700 tmp = g_strndup(bp, ep - bp);
4701 result = g_strconcat("http://", tmp, NULL);
4707 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4709 FILE *fp = fopen(path, "rb");
4710 gchar buf[BUFFSIZE];
4711 gchar *result = NULL;
4714 while (fgets(buf, sizeof (buf), fp) != NULL) {
4715 gchar **parts = g_strsplit(buf, ";", 3);
4716 gchar *trimmed = parts[0];
4717 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4719 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4720 trimmed[strlen(trimmed)-1] = '\0';
4722 if (!strcmp(trimmed, type)) {
4723 gboolean needsterminal = FALSE;
4724 if (parts[2] && strstr(parts[2], "needsterminal")) {
4725 needsterminal = TRUE;
4727 if (parts[2] && strstr(parts[2], "test=")) {
4728 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4729 gchar *testcmd = orig_testcmd;
4730 if (strstr(testcmd,";"))
4731 *(strstr(testcmd,";")) = '\0';
4732 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4734 while (testcmd[strlen(testcmd)-1] == '\n')
4735 testcmd[strlen(testcmd)-1] = '\0';
4736 while (testcmd[strlen(testcmd)-1] == '\r')
4737 testcmd[strlen(testcmd)-1] = '\0';
4738 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4739 testcmd[strlen(testcmd)-1] = '\0';
4741 if (strstr(testcmd, "%s")) {
4742 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4743 gint res = system(tmp);
4745 g_free(orig_testcmd);
4752 gint res = system(testcmd);
4753 g_free(orig_testcmd);
4763 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4765 while (trimmed[strlen(trimmed)-1] == '\n')
4766 trimmed[strlen(trimmed)-1] = '\0';
4767 while (trimmed[strlen(trimmed)-1] == '\r')
4768 trimmed[strlen(trimmed)-1] = '\0';
4769 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4770 trimmed[strlen(trimmed)-1] = '\0';
4771 result = g_strdup(trimmed);
4774 /* if there are no single quotes around %s, add them.
4775 * '.*%s.*' is ok, as in display 'png:%s'
4777 if (strstr(result, "%s")
4778 && !(strstr(result, "'") < strstr(result,"%s") &&
4779 strstr(strstr(result,"%s"), "'"))) {
4780 gchar *start = g_strdup(result);
4781 gchar *end = g_strdup(strstr(result, "%s")+2);
4783 *strstr(start, "%s") = '\0';
4784 tmp = g_strconcat(start,"'%s'",end, NULL);
4790 if (needsterminal) {
4791 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4802 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4804 gchar *result = NULL;
4808 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4809 result = mailcap_get_command_in_file(path, type, file_to_open);
4813 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4817 void mailcap_update_default(const gchar *type, const gchar *command)
4819 gchar *path = NULL, *outpath = NULL;
4820 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4821 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4822 FILE *fp = fopen(path, "rb");
4823 FILE *outfp = fopen(outpath, "wb");
4824 gchar buf[BUFFSIZE];
4832 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4833 gchar **parts = g_strsplit(buf, ";", 3);
4834 gchar *trimmed = parts[0];
4835 while (trimmed[0] == ' ')
4837 while (trimmed[strlen(trimmed)-1] == ' ')
4838 trimmed[strlen(trimmed)-1] = '\0';
4840 if (!strcmp(trimmed, type)) {
4849 fprintf(outfp, "%s; %s\n", type, command);
4855 g_rename(outpath, path);
4858 gint copy_dir(const gchar *src, const gchar *dst)
4863 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4864 g_warning("failed to open directory: %s\n", src);
4868 if (make_dir(dst) < 0)
4871 while ((name = g_dir_read_name(dir)) != NULL) {
4872 gchar *old_file, *new_file;
4873 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4874 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4875 debug_print("copying: %s -> %s\n", old_file, new_file);
4876 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4877 gint r = copy_file(old_file, new_file, TRUE);
4884 /* Windows has no symlinks. Or well, Vista seems to
4885 have something like this but the semantics might be
4886 different. Thus we don't use it under Windows. */
4887 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4890 gchar *target = g_file_read_link(old_file, &error);
4892 r = symlink(target, new_file);
4899 #endif /*G_OS_WIN32*/
4900 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4901 gint r = copy_dir(old_file, new_file);
4912 /* crude test to see if a file is an email. */
4913 gboolean file_is_email (const gchar *filename)
4919 if (filename == NULL)
4921 if ((fp = g_fopen(filename, "rb")) == NULL)
4923 while (i < 60 && score < 3
4924 && fgets(buffer, sizeof (buffer), fp) > 0) {
4925 if (!strncmp(buffer, "From:", strlen("From:")))
4927 if (!strncmp(buffer, "To:", strlen("To:")))
4929 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4934 return (score >= 3);
4937 gboolean sc_g_list_bigger(GList *list, gint max)
4941 while (cur && i <= max+1) {
4948 gboolean sc_g_slist_bigger(GSList *list, gint max)
4952 while (cur && i <= max+1) {
4959 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4960 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4961 NULL, NULL, NULL, NULL, NULL, NULL};
4962 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4963 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4964 NULL, NULL, NULL, NULL, NULL, NULL};
4965 const gchar *s_am_up = NULL;
4966 const gchar *s_pm_up = NULL;
4967 const gchar *s_am_low = NULL;
4968 const gchar *s_pm_low = NULL;
4969 const gchar *def_loc_format = NULL;
4970 const gchar *date_loc_format = NULL;
4971 const gchar *time_loc_format = NULL;
4972 const gchar *time_am_pm = NULL;
4974 static gboolean time_names_init_done = FALSE;
4976 static void init_time_names(void)
4978 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4979 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4980 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4981 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4982 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4983 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4984 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4986 monthnames[0] = Q_("Complete month name for use by strftime|January");
4987 monthnames[1] = Q_("Complete month name for use by strftime|February");
4988 monthnames[2] = Q_("Complete month name for use by strftime|March");
4989 monthnames[3] = Q_("Complete month name for use by strftime|April");
4990 monthnames[4] = Q_("Complete month name for use by strftime|May");
4991 monthnames[5] = Q_("Complete month name for use by strftime|June");
4992 monthnames[6] = Q_("Complete month name for use by strftime|July");
4993 monthnames[7] = Q_("Complete month name for use by strftime|August");
4994 monthnames[8] = Q_("Complete month name for use by strftime|September");
4995 monthnames[9] = Q_("Complete month name for use by strftime|October");
4996 monthnames[10] = Q_("Complete month name for use by strftime|November");
4997 monthnames[11] = Q_("Complete month name for use by strftime|December");
4999 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
5000 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
5001 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
5002 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
5003 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
5004 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
5005 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
5007 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
5008 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
5009 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
5010 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
5011 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
5012 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
5013 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
5014 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
5015 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
5016 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
5017 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
5018 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
5020 s_am_up = Q_("For use by strftime (morning)|AM");
5021 s_pm_up = Q_("For use by strftime (afternoon)|PM");
5022 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5023 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5025 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5026 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5027 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5029 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5031 time_names_init_done = TRUE;
5034 #define CHECK_SIZE() { \
5035 total_done += len; \
5036 if (total_done >= buflen) { \
5037 buf[buflen-1] = '\0'; \
5042 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5044 gchar *curpos = buf;
5045 gint total_done = 0;
5046 gchar subbuf[64], subfmt[64];
5047 static time_t last_tzset = (time_t)0;
5049 if (!time_names_init_done)
5052 if (format == NULL || lt == NULL)
5055 if (last_tzset != time(NULL)) {
5057 last_tzset = time(NULL);
5060 if (*format == '%') {
5061 gint len = 0, tmp = 0;
5065 len = 1; CHECK_SIZE();
5069 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5070 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5073 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5074 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5078 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5079 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5082 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5083 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5086 fast_strftime(subbuf, 64, def_loc_format, lt);
5087 len = strlen(subbuf); CHECK_SIZE();
5088 strncpy2(curpos, subbuf, buflen - total_done);
5091 total_done += 2; CHECK_SIZE();
5092 tmp = (lt->tm_year + 1900)/100;
5093 *curpos++ = '0'+(tmp / 10);
5094 *curpos++ = '0'+(tmp % 10);
5097 total_done += 2; CHECK_SIZE();
5098 *curpos++ = '0'+(lt->tm_mday / 10);
5099 *curpos++ = '0'+(lt->tm_mday % 10);
5102 total_done += 8; CHECK_SIZE();
5103 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5104 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5106 *curpos++ = '0'+(lt->tm_mday / 10);
5107 *curpos++ = '0'+(lt->tm_mday % 10);
5109 tmp = lt->tm_year%100;
5110 *curpos++ = '0'+(tmp / 10);
5111 *curpos++ = '0'+(tmp % 10);
5114 len = 2; CHECK_SIZE();
5115 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5118 len = 10; CHECK_SIZE();
5119 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5120 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5123 total_done += 2; CHECK_SIZE();
5124 *curpos++ = '0'+(lt->tm_hour / 10);
5125 *curpos++ = '0'+(lt->tm_hour % 10);
5128 total_done += 2; CHECK_SIZE();
5134 *curpos++ = '0'+(tmp / 10);
5135 *curpos++ = '0'+(tmp % 10);
5138 len = 3; CHECK_SIZE();
5139 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5142 len = 2; CHECK_SIZE();
5143 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5146 len = 2; CHECK_SIZE();
5152 snprintf(curpos, buflen - total_done, "%2d", tmp);
5155 total_done += 2; CHECK_SIZE();
5156 tmp = lt->tm_mon + 1;
5157 *curpos++ = '0'+(tmp / 10);
5158 *curpos++ = '0'+(tmp % 10);
5161 total_done += 2; CHECK_SIZE();
5162 *curpos++ = '0'+(lt->tm_min / 10);
5163 *curpos++ = '0'+(lt->tm_min % 10);
5166 len = 1; CHECK_SIZE();
5170 if (lt->tm_hour >= 12) {
5171 len = strlen(s_pm_up); CHECK_SIZE();
5172 snprintf(curpos, buflen-total_done, s_pm_up);
5174 len = strlen(s_am_up); CHECK_SIZE();
5175 snprintf(curpos, buflen-total_done, s_am_up);
5179 if (lt->tm_hour >= 12) {
5180 len = strlen(s_pm_low); CHECK_SIZE();
5181 snprintf(curpos, buflen-total_done, s_pm_low);
5183 len = strlen(s_am_low); CHECK_SIZE();
5184 snprintf(curpos, buflen-total_done, s_am_low);
5188 fast_strftime(subbuf, 64, time_am_pm, lt);
5189 len = strlen(subbuf); CHECK_SIZE();
5190 strncpy2(curpos, subbuf, buflen - total_done);
5193 total_done += 5; CHECK_SIZE();
5194 *curpos++ = '0'+(lt->tm_hour / 10);
5195 *curpos++ = '0'+(lt->tm_hour % 10);
5197 *curpos++ = '0'+(lt->tm_min / 10);
5198 *curpos++ = '0'+(lt->tm_min % 10);
5201 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5202 len = strlen(subbuf); CHECK_SIZE();
5203 strncpy2(curpos, subbuf, buflen - total_done);
5206 total_done += 2; CHECK_SIZE();
5207 *curpos++ = '0'+(lt->tm_sec / 10);
5208 *curpos++ = '0'+(lt->tm_sec % 10);
5211 len = 1; CHECK_SIZE();
5215 total_done += 8; CHECK_SIZE();
5216 *curpos++ = '0'+(lt->tm_hour / 10);
5217 *curpos++ = '0'+(lt->tm_hour % 10);
5219 *curpos++ = '0'+(lt->tm_min / 10);
5220 *curpos++ = '0'+(lt->tm_min % 10);
5222 *curpos++ = '0'+(lt->tm_sec / 10);
5223 *curpos++ = '0'+(lt->tm_sec % 10);
5226 len = 1; CHECK_SIZE();
5227 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5230 len = 1; CHECK_SIZE();
5231 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5234 fast_strftime(subbuf, 64, date_loc_format, lt);
5235 len = strlen(subbuf); CHECK_SIZE();
5236 strncpy2(curpos, subbuf, buflen - total_done);
5239 fast_strftime(subbuf, 64, time_loc_format, lt);
5240 len = strlen(subbuf); CHECK_SIZE();
5241 strncpy2(curpos, subbuf, buflen - total_done);
5244 total_done += 2; CHECK_SIZE();
5245 tmp = lt->tm_year%100;
5246 *curpos++ = '0'+(tmp / 10);
5247 *curpos++ = '0'+(tmp % 10);
5250 len = 4; CHECK_SIZE();
5251 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5261 /* let these complicated ones be done with the libc */
5262 snprintf(subfmt, 64, "%%%c", *format);
5263 strftime(subbuf, 64, subfmt, lt);
5264 len = strlen(subbuf); CHECK_SIZE();
5265 strncpy2(curpos, subbuf, buflen - total_done);
5269 /* let these complicated modifiers be done with the libc */
5270 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5271 strftime(subbuf, 64, subfmt, lt);
5272 len = strlen(subbuf); CHECK_SIZE();
5273 strncpy2(curpos, subbuf, buflen - total_done);
5277 g_warning("format error (%c)", *format);
5284 int len = 1; CHECK_SIZE();
5285 *curpos++ = *format++;