2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto & The Sylpheed-Claws 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <glib/gi18n.h>
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
46 #include <sys/types.h>
48 # include <sys/wait.h>
55 #include <sys/utsname.h>
66 #include "../codeconv.h"
70 static gboolean debug_mode = FALSE;
72 static GSList *tempfiles=NULL;
76 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
77 gint g_chdir(const gchar *path)
80 if (G_WIN32_HAVE_WIDECHAR_API()) {
85 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
91 retval = _wchdir(wpath);
103 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
104 if (cp_path == NULL) {
109 retval = chdir(cp_path);
122 gint g_chmod(const gchar *path, gint mode)
125 if (G_WIN32_HAVE_WIDECHAR_API()) {
130 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
136 retval = _wchmod(wpath, mode);
148 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
149 if (cp_path == NULL) {
154 retval = chmod(cp_path, mode);
163 return chmod(path, mode);
166 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
170 gint mkstemp_name(const gchar *template, gchar **name_used)
172 static gulong count=0; /* W32-_mktemp only supports up to 27
176 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
177 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
178 (S_IRUSR | S_IWUSR));
180 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
182 perror(g_strdup_printf("cant create %s",*name_used));
188 #endif /* G_OS_WIN32 */
191 gint mkstemp(const gchar *template)
194 gint res = mkstemp_name(template, &dummyname);
198 #endif /* G_OS_WIN32 */
200 void list_free_strings(GList *list)
202 list = g_list_first(list);
204 while (list != NULL) {
210 void slist_free_strings(GSList *list)
212 while (list != NULL) {
218 GSList *slist_concat_unique (GSList *first, GSList *second)
226 } else if (second == NULL)
229 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
230 if (g_slist_find(ret, tmp->data) == NULL)
231 ret = g_slist_prepend(ret, tmp->data);
236 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
241 void hash_free_strings(GHashTable *table)
243 g_hash_table_foreach(table, hash_free_strings_func, NULL);
246 static void hash_free_value_mem_func(gpointer key, gpointer value,
252 void hash_free_value_mem(GHashTable *table)
254 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
257 gint str_case_equal(gconstpointer v, gconstpointer v2)
259 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
262 guint str_case_hash(gconstpointer key)
264 const gchar *p = key;
268 h = g_ascii_tolower(h);
269 for (p += 1; *p != '\0'; p++)
270 h = (h << 5) - h + g_ascii_tolower(*p);
276 void ptr_array_free_strings(GPtrArray *array)
281 g_return_if_fail(array != NULL);
283 for (i = 0; i < array->len; i++) {
284 str = g_ptr_array_index(array, i);
289 gboolean str_find(const gchar *haystack, const gchar *needle)
291 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
294 gboolean str_case_find(const gchar *haystack, const gchar *needle)
296 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
299 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
301 return strcmp(haystack, needle) == 0;
304 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
306 return g_ascii_strcasecmp(haystack, needle) == 0;
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 gchar *to_human_readable(off_t size)
339 static gchar str[14];
342 g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
343 else if (size >> 10 < 1024)
344 g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
345 else if (size >> 20 < 1024)
346 g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
348 g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
353 /* strcmp with NULL-checking */
354 gint strcmp2(const gchar *s1, const gchar *s2)
356 if (s1 == NULL || s2 == NULL)
359 return strcmp(s1, s2);
361 /* strstr with NULL-checking */
362 gchar *strstr2(const gchar *s1, const gchar *s2)
364 if (s1 == NULL || s2 == NULL)
367 return strstr(s1, s2);
370 gint path_cmp(const gchar *s1, const gchar *s2)
375 gchar *s1buf, *s2buf;
378 if (s1 == NULL || s2 == NULL) return -1;
379 if (*s1 == '\0' || *s2 == '\0') return -1;
382 s1buf = g_strdup (s1);
383 s2buf = g_strdup (s2);
384 subst_char (s1buf, '/', G_DIR_SEPARATOR);
385 subst_char (s2buf, '/', G_DIR_SEPARATOR);
388 #endif /* !G_OS_WIN32 */
393 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
394 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
396 rc = strncmp(s1, s2, MAX(len1, len2));
400 #endif /* !G_OS_WIN32 */
404 /* remove trailing return code */
405 gchar *strretchomp(gchar *str)
409 if (!*str) return str;
411 for (s = str + strlen(str) - 1;
412 s >= str && (*s == '\n' || *s == '\r');
419 /* remove trailing character */
420 gchar *strtailchomp(gchar *str, gchar tail_char)
424 if (!*str) return str;
425 if (tail_char == '\0') return str;
427 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
433 /* remove CR (carriage return) */
434 gchar *strcrchomp(gchar *str)
438 if (!*str) return str;
440 s = str + strlen(str) - 1;
441 if (*s == '\n' && s > str && *(s - 1) == '\r') {
449 void file_strip_crs(const gchar *file)
451 FILE *fp = NULL, *outfp = NULL;
453 gchar *out = get_tmp_file();
457 fp = fopen(file, "rb");
461 outfp = fopen(out, "wb");
467 while (fgets(buf, sizeof (buf), fp) != NULL) {
474 rename_force(out, file);
479 /* Similar to `strstr' but this function ignores the case of both strings. */
480 gchar *strcasestr(const gchar *haystack, const gchar *needle)
482 register size_t haystack_len, needle_len;
484 haystack_len = strlen(haystack);
485 needle_len = strlen(needle);
487 if (haystack_len < needle_len || needle_len == 0)
490 while (haystack_len >= needle_len) {
491 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
492 return (gchar *)haystack;
502 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
503 gconstpointer needle, size_t needlelen)
505 const gchar *haystack_ = (const gchar *)haystack;
506 const gchar *needle_ = (const gchar *)needle;
507 const gchar *haystack_cur = (const gchar *)haystack;
510 return memchr(haystack_, *needle_, haystacklen);
512 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
514 if (haystacklen - (haystack_cur - haystack_) < needlelen)
516 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
517 return (gpointer)haystack_cur;
525 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
526 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
528 register const gchar *s = src;
529 register gchar *d = dest;
539 int iswalnum(wint_t wc)
541 return g_ascii_isalnum((int)wc);
546 int iswspace(wint_t wc)
548 return g_ascii_isspace((int)wc);
553 wint_t towlower(wint_t wc)
555 if (wc >= L'A' && wc <= L'Z')
556 return wc + L'a' - L'A';
563 size_t wcslen(const wchar_t *s)
575 /* Copy SRC to DEST. */
576 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
584 } while (c != L'\0');
591 /* Copy no more than N wide-characters of SRC to DEST. */
592 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
602 } while (c != L'\0');
613 /* Duplicate S, returning an identical malloc'd string. */
614 wchar_t *wcsdup(const wchar_t *s)
619 new_str = g_new(wchar_t, wcslen(s) + 1);
627 /* Duplicate no more than N wide-characters of S,
628 returning an identical malloc'd string. */
629 wchar_t *wcsndup(const wchar_t *s, size_t n)
634 new_str = g_new(wchar_t, n + 1);
635 wcsncpy(new_str, s, n);
636 new_str[n] = (wchar_t)0;
643 wchar_t *strdup_mbstowcs(const gchar *s)
648 new_str = g_new(wchar_t, strlen(s) + 1);
649 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
653 new_str = g_realloc(new_str,
654 sizeof(wchar_t) * (wcslen(new_str) + 1));
661 gchar *strdup_wcstombs(const wchar_t *s)
667 len = wcslen(s) * MB_CUR_MAX + 1;
668 new_str = g_new(gchar, len);
669 if (wcstombs(new_str, s, len) < 0) {
673 new_str = g_realloc(new_str, strlen(new_str) + 1);
680 /* Compare S1 and S2, ignoring case. */
681 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
687 c1 = towlower(*s1++);
688 c2 = towlower(*s2++);
691 else if (c1 == 0 && c2 == 0)
698 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */
699 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
701 register size_t haystack_len, needle_len;
703 haystack_len = wcslen(haystack);
704 needle_len = wcslen(needle);
706 if (haystack_len < needle_len || needle_len == 0)
709 while (haystack_len >= needle_len) {
710 if (!wcsncasecmp(haystack, needle, needle_len))
711 return (wchar_t *)haystack;
721 gint get_mbs_len(const gchar *s)
731 mb_len = g_utf8_skip[*(guchar *)p];
743 /* Examine if next block is non-ASCII string */
744 gboolean is_next_nonascii(const gchar *s)
748 /* skip head space */
749 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
751 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
752 if (*(guchar *)p > 127 || *(guchar *)p < 32)
759 gint get_next_word_len(const gchar *s)
763 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
769 /* compare subjects */
770 gint subject_compare(const gchar *s1, const gchar *s2)
774 if (!s1 || !s2) return -1;
775 if (!*s1 || !*s2) return -1;
777 Xstrdup_a(str1, s1, return -1);
778 Xstrdup_a(str2, s2, return -1);
780 trim_subject_for_compare(str1);
781 trim_subject_for_compare(str2);
783 if (!*str1 || !*str2) return -1;
785 return strcmp(str1, str2);
788 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
792 if (!s1 || !s2) return -1;
794 Xstrdup_a(str1, s1, return -1);
795 Xstrdup_a(str2, s2, return -1);
797 trim_subject_for_sort(str1);
798 trim_subject_for_sort(str2);
800 return g_utf8_collate(str1, str2);
803 void trim_subject_for_compare(gchar *str)
807 eliminate_parenthesis(str, '[', ']');
808 eliminate_parenthesis(str, '(', ')');
811 srcp = str + subject_get_prefix_length(str);
813 memmove(str, srcp, strlen(srcp) + 1);
816 void trim_subject_for_sort(gchar *str)
822 srcp = str + subject_get_prefix_length(str);
824 memmove(str, srcp, strlen(srcp) + 1);
827 void trim_subject(gchar *str)
829 register gchar *srcp;
835 srcp = str + subject_get_prefix_length(str);
840 } else if (*srcp == '(') {
852 else if (*srcp == cl)
859 while (g_ascii_isspace(*srcp)) srcp++;
860 memmove(str, srcp, strlen(srcp) + 1);
863 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
865 register gchar *srcp, *destp;
870 while ((destp = strchr(destp, op))) {
876 else if (*srcp == cl)
882 while (g_ascii_isspace(*srcp)) srcp++;
883 memmove(destp, srcp, strlen(srcp) + 1);
887 void extract_parenthesis(gchar *str, gchar op, gchar cl)
889 register gchar *srcp, *destp;
894 while ((srcp = strchr(destp, op))) {
897 memmove(destp, srcp + 1, strlen(srcp));
902 else if (*destp == cl)
914 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
917 register gchar *srcp, *destp;
919 gboolean in_quote = FALSE;
923 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
926 memmove(destp, srcp + 1, strlen(srcp));
929 if (*destp == op && !in_quote)
931 else if (*destp == cl && !in_quote)
933 else if (*destp == quote_chr)
945 void eliminate_quote(gchar *str, gchar quote_chr)
947 register gchar *srcp, *destp;
951 while ((destp = strchr(destp, quote_chr))) {
952 if ((srcp = strchr(destp + 1, quote_chr))) {
954 while (g_ascii_isspace(*srcp)) srcp++;
955 memmove(destp, srcp, strlen(srcp) + 1);
963 void extract_quote(gchar *str, gchar quote_chr)
967 if ((str = strchr(str, quote_chr))) {
969 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
970 memmove(p - 1, p, strlen(p) + 1);
975 memmove(str, str + 1, p - str);
980 void eliminate_address_comment(gchar *str)
982 register gchar *srcp, *destp;
987 while ((destp = strchr(destp, '"'))) {
988 if ((srcp = strchr(destp + 1, '"'))) {
993 while (g_ascii_isspace(*srcp)) srcp++;
994 memmove(destp, srcp, strlen(srcp) + 1);
1004 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
1010 else if (*srcp == ')')
1016 while (g_ascii_isspace(*srcp)) srcp++;
1017 memmove(destp, srcp, strlen(srcp) + 1);
1021 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1023 gboolean in_quote = FALSE;
1026 if (*str == c && !in_quote)
1027 return (gchar *)str;
1028 if (*str == quote_chr)
1036 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
1038 gboolean in_quote = FALSE;
1041 p = str + strlen(str) - 1;
1043 if (*p == c && !in_quote)
1045 if (*p == quote_chr)
1053 void extract_address(gchar *str)
1055 eliminate_address_comment(str);
1056 if (strchr_with_skip_quote(str, '"', '<'))
1057 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1061 void extract_list_id_str(gchar *str)
1063 if (strchr_with_skip_quote(str, '"', '<'))
1064 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
1068 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
1073 if (!str) return addr_list;
1075 Xstrdup_a(work, str, return addr_list);
1078 eliminate_address_comment(work);
1081 while (workp && *workp) {
1084 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1090 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
1091 extract_parenthesis_with_skip_quote
1092 (workp, '"', '<', '>');
1096 addr_list = g_slist_append(addr_list, g_strdup(workp));
1104 GSList *address_list_append(GSList *addr_list, const gchar *str)
1106 return address_list_append_real(addr_list, str, TRUE);
1109 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
1111 return address_list_append_real(addr_list, str, FALSE);
1114 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1118 if (!str) return msgid_list;
1121 while (strp && *strp) {
1122 const gchar *start, *end;
1125 if ((start = strchr(strp, '<')) != NULL) {
1126 end = strchr(start + 1, '>');
1131 msgid = g_strndup(start + 1, end - start - 1);
1134 msgid_list = g_slist_prepend(msgid_list, msgid);
1144 GSList *references_list_append(GSList *msgid_list, const gchar *str)
1148 list = references_list_prepend(NULL, str);
1149 list = g_slist_reverse(list);
1150 msgid_list = g_slist_concat(msgid_list, list);
1155 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1160 if (!str) return group_list;
1162 Xstrdup_a(work, str, return group_list);
1166 while (workp && *workp) {
1169 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1177 group_list = g_slist_append(group_list,
1186 GList *add_history(GList *list, const gchar *str)
1190 g_return_val_if_fail(str != NULL, list);
1192 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1195 list = g_list_remove(list, old->data);
1196 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1199 last = g_list_last(list);
1202 list = g_list_remove(list, last->data);
1206 list = g_list_prepend(list, g_strdup(str));
1211 void remove_return(gchar *str)
1213 register gchar *p = str;
1216 if (*p == '\n' || *p == '\r')
1217 memmove(p, p + 1, strlen(p));
1223 void remove_space(gchar *str)
1225 register gchar *p = str;
1230 while (g_ascii_isspace(*(p + spc)))
1233 memmove(p, p + spc, strlen(p + spc) + 1);
1239 void unfold_line(gchar *str)
1241 register gchar *p = str;
1245 if (*p == '\n' || *p == '\r') {
1248 while (g_ascii_isspace(*(p + spc)))
1251 memmove(p, p + spc, strlen(p + spc) + 1);
1257 void subst_char(gchar *str, gchar orig, gchar subst)
1259 register gchar *p = str;
1268 void subst_chars(gchar *str, gchar *orig, gchar subst)
1270 register gchar *p = str;
1273 if (strchr(orig, *p) != NULL)
1279 void subst_for_filename(gchar *str)
1284 subst_chars(str, "\t\r\n\\/*:", '_');
1286 subst_chars(str, "\t\r\n\\/*", '_');
1290 void subst_for_shellsafe_filename(gchar *str)
1294 subst_for_filename(str);
1295 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1298 gboolean is_header_line(const gchar *str)
1300 if (str[0] == ':') return FALSE;
1302 while (*str != '\0' && *str != ' ') {
1311 gboolean is_ascii_str(const gchar *str)
1313 const guchar *p = (const guchar *)str;
1315 while (*p != '\0') {
1316 if (*p != '\t' && *p != ' ' &&
1317 *p != '\r' && *p != '\n' &&
1318 (*p < 32 || *p >= 127))
1326 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1328 const gchar *first_pos;
1329 const gchar *last_pos;
1330 const gchar *p = str;
1331 gint quote_level = -1;
1333 /* speed up line processing by only searching to the last '>' */
1334 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1335 /* skip a line if it contains a '<' before the initial '>' */
1336 if (memchr(str, '<', first_pos - str) != NULL)
1338 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1342 while (p <= last_pos) {
1343 while (p < last_pos) {
1344 if (g_ascii_isspace(*p))
1350 if (strchr(quote_chars, *p))
1352 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1353 /* any characters are allowed except '-' and space */
1355 && !strchr(quote_chars, *p)
1356 && !g_ascii_isspace(*p)
1359 if (strchr(quote_chars, *p))
1371 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1373 const gchar *p = str, *q;
1374 gint cur_line = 0, len;
1376 while ((q = strchr(p, '\n')) != NULL) {
1378 if (len > max_chars) {
1388 if (len > max_chars) {
1397 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1399 gchar * position = NULL;
1400 gchar * tmp_pos = NULL;
1403 if (quote_chars == NULL)
1406 for (i = 0; i < strlen(quote_chars); i++) {
1407 tmp_pos = strchr (str, quote_chars[i]);
1409 || (tmp_pos != NULL && position >= tmp_pos) )
1415 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1417 gchar * position = NULL;
1418 gchar * tmp_pos = NULL;
1421 if (quote_chars == NULL)
1424 for (i = 0; i < strlen(quote_chars); i++) {
1425 tmp_pos = strrchr (str, quote_chars[i]);
1427 || (tmp_pos != NULL && position <= tmp_pos) )
1433 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1435 register guint haystack_len, needle_len;
1436 gboolean in_squote = FALSE, in_dquote = FALSE;
1438 haystack_len = strlen(haystack);
1439 needle_len = strlen(needle);
1441 if (haystack_len < needle_len || needle_len == 0)
1444 while (haystack_len >= needle_len) {
1445 if (!in_squote && !in_dquote &&
1446 !strncmp(haystack, needle, needle_len))
1447 return (gchar *)haystack;
1449 /* 'foo"bar"' -> foo"bar"
1450 "foo'bar'" -> foo'bar' */
1451 if (*haystack == '\'') {
1454 else if (!in_dquote)
1456 } else if (*haystack == '\"') {
1459 else if (!in_squote)
1470 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1473 gchar quote_chr = '"';
1475 gboolean in_quote = FALSE;
1479 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1483 if (*p == op && !in_quote)
1485 else if (*p == cl && !in_quote)
1487 else if (*p == quote_chr)
1500 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1503 GSList *string_list = NULL, *slist;
1505 const gchar *s_op, *s_cl;
1508 g_return_val_if_fail(str != NULL, NULL);
1511 max_tokens = G_MAXINT;
1513 s_op = strchr_with_skip_quote(str, '"', op);
1514 if (!s_op) return NULL;
1516 s_cl = strchr_parenthesis_close(str, op, cl);
1524 new_string = g_new(gchar, len + 1);
1525 strncpy(new_string, str, len);
1526 new_string[len] = 0;
1527 string_list = g_slist_prepend(string_list, new_string);
1531 while (*str && g_ascii_isspace(*str)) str++;
1533 string_list = g_slist_prepend(string_list,
1536 s_op = strchr_with_skip_quote(str, '"', op);
1537 if (!--max_tokens || !s_op) break;
1541 s_cl = strchr_parenthesis_close(str, op, cl);
1542 } while (--max_tokens && s_cl);
1545 str_array = g_new(gchar*, n);
1549 str_array[i--] = NULL;
1550 for (slist = string_list; slist; slist = slist->next)
1551 str_array[i--] = slist->data;
1553 g_slist_free(string_list);
1558 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1561 GSList *string_list = NULL, *slist;
1562 gchar **str_array, *s, *new_str;
1563 guint i, n = 1, len;
1565 g_return_val_if_fail(str != NULL, NULL);
1566 g_return_val_if_fail(delim != NULL, NULL);
1569 max_tokens = G_MAXINT;
1571 s = strstr_with_skip_quote(str, delim);
1573 guint delimiter_len = strlen(delim);
1577 new_str = g_strndup(str, len);
1579 if (new_str[0] == '\'' || new_str[0] == '\"') {
1580 if (new_str[len - 1] == new_str[0]) {
1581 new_str[len - 1] = '\0';
1582 memmove(new_str, new_str + 1, len - 1);
1585 string_list = g_slist_prepend(string_list, new_str);
1587 str = s + delimiter_len;
1588 s = strstr_with_skip_quote(str, delim);
1589 } while (--max_tokens && s);
1593 new_str = g_strdup(str);
1594 if (new_str[0] == '\'' || new_str[0] == '\"') {
1596 if (new_str[len - 1] == new_str[0]) {
1597 new_str[len - 1] = '\0';
1598 memmove(new_str, new_str + 1, len - 1);
1601 string_list = g_slist_prepend(string_list, new_str);
1605 str_array = g_new(gchar*, n);
1609 str_array[i--] = NULL;
1610 for (slist = string_list; slist; slist = slist->next)
1611 str_array[i--] = slist->data;
1613 g_slist_free(string_list);
1618 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1620 gchar *abbrev_group;
1622 const gchar *p = group;
1625 g_return_val_if_fail(group != NULL, NULL);
1627 last = group + strlen(group);
1628 abbrev_group = ap = g_malloc(strlen(group) + 1);
1633 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1635 while (*p != '.') p++;
1638 return abbrev_group;
1643 return abbrev_group;
1646 gchar *trim_string(const gchar *str, gint len)
1648 const gchar *p = str;
1653 if (!str) return NULL;
1654 if (strlen(str) <= len)
1655 return g_strdup(str);
1656 if (g_utf8_validate(str, -1, NULL) == FALSE)
1657 return g_strdup(str);
1659 while (*p != '\0') {
1660 mb_len = g_utf8_skip[*(guchar *)p];
1663 else if (new_len + mb_len > len)
1670 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1671 return g_strconcat(new_str, "...", NULL);
1674 GList *uri_list_extract_filenames(const gchar *uri_list)
1676 GList *result = NULL;
1678 gchar *escaped_utf8uri;
1684 while (g_ascii_isspace(*p)) p++;
1685 if (!strncmp(p, "file:", 5)) {
1688 while (*q && *q != '\n' && *q != '\r') q++;
1691 gchar *file, *locale_file = NULL;
1693 while (q > p && g_ascii_isspace(*q))
1695 Xalloca(escaped_utf8uri, q - p + 2,
1697 Xalloca(file, q - p + 2,
1700 strncpy(escaped_utf8uri, p, q - p + 1);
1701 escaped_utf8uri[q - p + 1] = '\0';
1702 decode_uri(file, escaped_utf8uri);
1704 * g_filename_from_uri() rejects escaped/locale encoded uri
1705 * string which come from Nautilus.
1707 if (g_utf8_validate(file, -1, NULL))
1709 = conv_codeset_strdup(
1712 conv_get_locale_charset_str());
1714 locale_file = g_strdup(file + 5);
1715 result = g_list_append(result, locale_file);
1719 p = strchr(p, '\n');
1726 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1729 static gint axtoi(const gchar *hexstr)
1731 gint hi, lo, result;
1734 if ('0' <= hi && hi <= '9') {
1737 if ('a' <= hi && hi <= 'f') {
1740 if ('A' <= hi && hi <= 'F') {
1745 if ('0' <= lo && lo <= '9') {
1748 if ('a' <= lo && lo <= 'f') {
1751 if ('A' <= lo && lo <= 'F') {
1754 result = lo + (16 * hi);
1758 gboolean is_uri_string(const gchar *str)
1760 while (str && *str && g_ascii_isspace(*str))
1762 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1763 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1764 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1765 g_ascii_strncasecmp(str, "www.", 4) == 0);
1768 gchar *get_uri_path(const gchar *uri)
1770 while (uri && *uri && g_ascii_isspace(*uri))
1772 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1773 return (gchar *)(uri + 7);
1774 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1775 return (gchar *)(uri + 8);
1776 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1777 return (gchar *)(uri + 6);
1779 return (gchar *)uri;
1782 gint get_uri_len(const gchar *str)
1786 if (is_uri_string(str)) {
1787 for (p = str; *p != '\0'; p++) {
1788 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1797 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1798 * plusses, and escape characters are used)
1800 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1802 gchar *dec = decoded_uri;
1803 const gchar *enc = encoded_uri;
1808 if (isxdigit((guchar)enc[0]) &&
1809 isxdigit((guchar)enc[1])) {
1827 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1828 gchar **subject, gchar **body)
1833 Xstrdup_a(tmp_mailto, mailto, return -1);
1835 if (!strncmp(tmp_mailto, "mailto:", 7))
1838 p = strchr(tmp_mailto, '?');
1845 *to = g_strdup(tmp_mailto);
1848 gchar *field, *value;
1865 if (*value == '\0') continue;
1867 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1868 *cc = g_strdup(value);
1869 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1870 *bcc = g_strdup(value);
1871 } else if (subject && !*subject &&
1872 !g_ascii_strcasecmp(field, "subject")) {
1873 *subject = g_malloc(strlen(value) + 1);
1874 decode_uri(*subject, value);
1875 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1876 *body = g_malloc(strlen(value) + 1);
1877 decode_uri(*body, value);
1886 #include <windows.h>
1887 #ifndef CSIDL_APPDATA
1888 #define CSIDL_APPDATA 0x001a
1890 #ifndef CSIDL_LOCAL_APPDATA
1891 #define CSIDL_LOCAL_APPDATA 0x001c
1893 #ifndef CSIDL_FLAG_CREATE
1894 #define CSIDL_FLAG_CREATE 0x8000
1896 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1900 w32_strerror (int w32_errno)
1902 static char strerr[256];
1903 int ec = (int)GetLastError ();
1907 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1908 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1909 strerr, DIM (strerr)-1, NULL);
1913 static __inline__ void *
1914 dlopen (const char * name, int flag)
1916 void * hd = LoadLibrary (name);
1920 static __inline__ void *
1921 dlsym (void * hd, const char * sym)
1925 void * fnc = GetProcAddress (hd, sym);
1934 static __inline__ const char *
1937 return w32_strerror (0);
1941 static __inline__ int
1953 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1955 static int initialized;
1956 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1960 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1966 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1968 handle = dlopen (dllnames[i], RTLD_LAZY);
1971 func = dlsym (handle, "SHGetFolderPathA");
1982 return func (a,b,c,d,e);
1988 const gchar *get_home_dir(void)
1991 static char home_dir[MAX_PATH] = "";
1993 if (home_dir[0] == '\0')
1995 if (w32_shgetfolderpath
1996 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1997 NULL, 0, home_dir) < 0)
1998 strcpy (home_dir, "C:\\Sylpheed");
2002 const gchar *homeenv = NULL;
2007 if (!homeenv && g_getenv("HOME") != NULL)
2008 homeenv = g_strdup(g_getenv("HOME"));
2010 homeenv = g_get_home_dir();
2016 const gchar *get_rc_dir(void)
2018 static gchar *rc_dir = NULL;
2021 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2027 const gchar *get_mail_base_dir(void)
2030 static gchar *mail_base_dir = NULL;
2033 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2036 return mail_base_dir;
2038 return get_home_dir();
2042 const gchar *get_news_cache_dir(void)
2044 static gchar *news_cache_dir = NULL;
2046 if (!news_cache_dir)
2047 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2048 NEWS_CACHE_DIR, NULL);
2050 return news_cache_dir;
2053 const gchar *get_imap_cache_dir(void)
2055 static gchar *imap_cache_dir = NULL;
2057 if (!imap_cache_dir)
2058 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2059 IMAP_CACHE_DIR, NULL);
2061 return imap_cache_dir;
2064 const gchar *get_mbox_cache_dir(void)
2066 static gchar *mbox_cache_dir = NULL;
2068 if (!mbox_cache_dir)
2069 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2070 MBOX_CACHE_DIR, NULL);
2072 return mbox_cache_dir;
2075 const gchar *get_mime_tmp_dir(void)
2077 static gchar *mime_tmp_dir = NULL;
2080 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2081 MIME_TMP_DIR, NULL);
2083 return mime_tmp_dir;
2086 const gchar *get_template_dir(void)
2088 static gchar *template_dir = NULL;
2091 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2092 TEMPLATE_DIR, NULL);
2094 return template_dir;
2097 const gchar *get_header_cache_dir(void)
2099 static gchar *header_dir = NULL;
2102 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2103 HEADER_CACHE_DIR, NULL);
2108 /* Return the default directory for Plugins. */
2109 const gchar *get_plugin_dir(void)
2112 static gchar *plugin_dir = NULL;
2115 plugin_dir = g_strconcat(sylpheed_get_startup_dir(),
2116 "\\lib\\sylpheed-claws\\plugins\\",
2124 const gchar *get_tmp_dir(void)
2126 static gchar *tmp_dir = NULL;
2129 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2135 gchar *get_tmp_file(void)
2138 static guint32 id = 0;
2140 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2141 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2146 const gchar *get_domain_name(void)
2149 static gchar *domain_name = NULL;
2155 if (uname(&uts) < 0) {
2157 domain_name = "unknown";
2159 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2160 perror("gethostbyname");
2161 domain_name = g_strdup(uts.nodename);
2163 domain_name = g_strdup(hp->h_name);
2167 debug_print("domain name = %s\n", domain_name);
2176 off_t get_file_size(const gchar *file)
2180 if (g_stat(file, &s) < 0) {
2181 FILE_OP_ERROR(file, "stat");
2188 off_t get_file_size_as_crlf(const gchar *file)
2192 gchar buf[BUFFSIZE];
2194 if ((fp = g_fopen(file, "rb")) == NULL) {
2195 FILE_OP_ERROR(file, "fopen");
2199 while (fgets(buf, sizeof(buf), fp) != NULL) {
2201 size += strlen(buf) + 2;
2205 FILE_OP_ERROR(file, "fgets");
2214 off_t get_left_file_size(FILE *fp)
2220 if ((pos = ftell(fp)) < 0) {
2224 if (fseek(fp, 0L, SEEK_END) < 0) {
2228 if ((end = ftell(fp)) < 0) {
2233 if (fseek(fp, pos, SEEK_SET) < 0) {
2241 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2248 if (g_stat(file, &s) < 0) {
2249 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2253 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2260 /* Test on whether FILE is a relative file name. This is
2261 * straightforward for Unix but more complex for Windows. */
2262 gboolean is_relative_filename(const gchar *file)
2267 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2268 return FALSE; /* Prefixed with a hostname - this can't
2269 * be a relative name. */
2271 if ( ((*file >= 'a' && *file <= 'z')
2272 || (*file >= 'A' && *file <= 'Z'))
2274 file += 2; /* Skip drive letter. */
2276 return !(*file == '\\' || *file == '/');
2278 return !(*file == G_DIR_SEPARATOR);
2283 gboolean is_dir_exist(const gchar *dir)
2288 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2291 gboolean is_file_entry_exist(const gchar *file)
2296 return g_file_test(file, G_FILE_TEST_EXISTS);
2299 gboolean dirent_is_regular_file(struct dirent *d)
2301 #ifdef HAVE_DIRENT_D_TYPE
2302 if (d->d_type == DT_REG)
2304 else if (d->d_type != DT_UNKNOWN)
2308 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2311 gboolean dirent_is_directory(struct dirent *d)
2313 #ifdef HAVE_DIRENT_D_TYPE
2314 if (d->d_type == DT_DIR)
2316 else if (d->d_type != DT_UNKNOWN)
2320 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2323 gint change_dir(const gchar *dir)
2325 gchar *prevdir = NULL;
2328 prevdir = g_get_current_dir();
2330 if (g_chdir(dir) < 0) {
2331 FILE_OP_ERROR(dir, "chdir");
2332 if (debug_mode) g_free(prevdir);
2334 } else if (debug_mode) {
2337 cwd = g_get_current_dir();
2338 if (strcmp(prevdir, cwd) != 0)
2339 g_print("current dir: %s\n", cwd);
2347 gint make_dir(const gchar *dir)
2349 if (g_mkdir(dir, S_IRWXU) < 0) {
2350 FILE_OP_ERROR(dir, "mkdir");
2353 if (g_chmod(dir, S_IRWXU) < 0)
2354 FILE_OP_ERROR(dir, "chmod");
2359 gint make_dir_hier(const gchar *dir)
2364 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2365 parent_dir = g_strndup(dir, p - dir);
2366 if (*parent_dir != '\0') {
2367 if (!is_dir_exist(parent_dir)) {
2368 if (make_dir(parent_dir) < 0) {
2377 if (!is_dir_exist(dir)) {
2378 if (make_dir(dir) < 0)
2385 gint remove_all_files(const gchar *dir)
2388 const gchar *dir_name;
2391 prev_dir = g_get_current_dir();
2393 if (g_chdir(dir) < 0) {
2394 FILE_OP_ERROR(dir, "chdir");
2399 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2400 g_warning("failed to open directory: %s\n", dir);
2405 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2406 if (g_unlink(dir_name) < 0)
2407 FILE_OP_ERROR(dir_name, "unlink");
2412 if (g_chdir(prev_dir) < 0) {
2413 FILE_OP_ERROR(prev_dir, "chdir");
2423 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2426 const gchar *dir_name;
2430 prev_dir = g_get_current_dir();
2432 if (g_chdir(dir) < 0) {
2433 FILE_OP_ERROR(dir, "chdir");
2438 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2439 g_warning("failed to open directory: %s\n", dir);
2444 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2445 file_no = to_number(dir_name);
2446 if (file_no > 0 && first <= file_no && file_no <= last) {
2447 if (is_dir_exist(dir_name))
2449 if (g_unlink(dir_name) < 0)
2450 FILE_OP_ERROR(dir_name, "unlink");
2456 if (g_chdir(prev_dir) < 0) {
2457 FILE_OP_ERROR(prev_dir, "chdir");
2467 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2470 const gchar *dir_name;
2474 prev_dir = g_get_current_dir();
2476 if (g_chdir(dir) < 0) {
2477 FILE_OP_ERROR(dir, "chdir");
2482 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2483 FILE_OP_ERROR(dir, "opendir");
2488 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2489 file_no = to_number(dir_name);
2490 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2491 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2492 if (is_dir_exist(dir_name))
2494 if (g_unlink(dir_name) < 0)
2495 FILE_OP_ERROR(dir_name, "unlink");
2501 if (g_chdir(prev_dir) < 0) {
2502 FILE_OP_ERROR(prev_dir, "chdir");
2512 gint remove_all_numbered_files(const gchar *dir)
2514 return remove_numbered_files(dir, 0, UINT_MAX);
2517 gint remove_expired_files(const gchar *dir, guint hours)
2520 const gchar *dir_name;
2524 time_t mtime, now, expire_time;
2526 prev_dir = g_get_current_dir();
2528 if (g_chdir(dir) < 0) {
2529 FILE_OP_ERROR(dir, "chdir");
2534 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2535 g_warning("failed to open directory: %s\n", dir);
2541 expire_time = hours * 60 * 60;
2543 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2544 file_no = to_number(dir_name);
2546 if (g_stat(dir_name, &s) < 0) {
2547 FILE_OP_ERROR(dir_name, "stat");
2550 if (S_ISDIR(s.st_mode))
2552 mtime = MAX(s.st_mtime, s.st_atime);
2553 if (now - mtime > expire_time) {
2554 if (g_unlink(dir_name) < 0)
2555 FILE_OP_ERROR(dir_name, "unlink");
2562 if (g_chdir(prev_dir) < 0) {
2563 FILE_OP_ERROR(prev_dir, "chdir");
2573 gint remove_dir_recursive(const gchar *dir)
2577 const gchar *dir_name;
2580 if (g_stat(dir, &s) < 0) {
2581 FILE_OP_ERROR(dir, "stat");
2582 if (ENOENT == errno) return 0;
2586 if (!S_ISDIR(s.st_mode)) {
2587 if (g_unlink(dir) < 0) {
2588 FILE_OP_ERROR(dir, "unlink");
2595 prev_dir = g_get_current_dir();
2596 /* g_print("prev_dir = %s\n", prev_dir); */
2598 if (!path_cmp(prev_dir, dir)) {
2600 if (g_chdir("..") < 0) {
2601 FILE_OP_ERROR(dir, "chdir");
2604 prev_dir = g_get_current_dir();
2607 if (g_chdir(dir) < 0) {
2608 FILE_OP_ERROR(dir, "chdir");
2613 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2614 g_warning("failed to open directory: %s\n", dir);
2620 /* remove all files in the directory */
2621 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2622 /* g_print("removing %s\n", dir_name); */
2624 if (is_dir_exist(dir_name)) {
2625 if (remove_dir_recursive(dir_name) < 0) {
2626 g_warning("can't remove directory\n");
2630 if (g_unlink(dir_name) < 0)
2631 FILE_OP_ERROR(dir_name, "unlink");
2637 if (g_chdir(prev_dir) < 0) {
2638 FILE_OP_ERROR(prev_dir, "chdir");
2645 if (g_rmdir(dir) < 0) {
2646 FILE_OP_ERROR(dir, "rmdir");
2653 gint rename_force(const gchar *oldpath, const gchar *newpath)
2656 if (!is_file_entry_exist(oldpath)) {
2660 if (is_file_exist(newpath)) {
2661 if (g_unlink(newpath) < 0)
2662 FILE_OP_ERROR(newpath, "unlink");
2665 return g_rename(oldpath, newpath);
2669 * Append src file body to the tail of dest file.
2670 * Now keep_backup has no effects.
2672 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2674 FILE *src_fp, *dest_fp;
2678 gboolean err = FALSE;
2680 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2681 FILE_OP_ERROR(src, "fopen");
2685 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2686 FILE_OP_ERROR(dest, "fopen");
2691 if (change_file_mode_rw(dest_fp, dest) < 0) {
2692 FILE_OP_ERROR(dest, "chmod");
2693 g_warning("can't change file mode\n");
2696 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2697 if (n_read < sizeof(buf) && ferror(src_fp))
2699 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2700 g_warning("writing to %s failed.\n", dest);
2708 if (ferror(src_fp)) {
2709 FILE_OP_ERROR(src, "fread");
2713 if (fclose(dest_fp) == EOF) {
2714 FILE_OP_ERROR(dest, "fclose");
2726 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2728 FILE *src_fp, *dest_fp;
2731 gchar *dest_bak = NULL;
2732 gboolean err = FALSE;
2734 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2735 FILE_OP_ERROR(src, "fopen");
2738 if (is_file_exist(dest)) {
2739 dest_bak = g_strconcat(dest, ".bak", NULL);
2740 if (rename_force(dest, dest_bak) < 0) {
2741 FILE_OP_ERROR(dest, "rename");
2748 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2749 FILE_OP_ERROR(dest, "fopen");
2752 if (rename_force(dest_bak, dest) < 0)
2753 FILE_OP_ERROR(dest_bak, "rename");
2759 if (change_file_mode_rw(dest_fp, dest) < 0) {
2760 FILE_OP_ERROR(dest, "chmod");
2761 g_warning("can't change file mode\n");
2764 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2765 if (n_read < sizeof(buf) && ferror(src_fp))
2767 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2768 g_warning("writing to %s failed.\n", dest);
2773 if (rename_force(dest_bak, dest) < 0)
2774 FILE_OP_ERROR(dest_bak, "rename");
2781 if (ferror(src_fp)) {
2782 FILE_OP_ERROR(src, "fread");
2786 if (fclose(dest_fp) == EOF) {
2787 FILE_OP_ERROR(dest, "fclose");
2794 if (rename_force(dest_bak, dest) < 0)
2795 FILE_OP_ERROR(dest_bak, "rename");
2801 if (keep_backup == FALSE && dest_bak)
2809 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2811 if (overwrite == FALSE && is_file_exist(dest)) {
2812 g_warning("move_file(): file %s already exists.", dest);
2816 if (rename_force(src, dest) == 0) return 0;
2818 if (EXDEV != errno) {
2819 FILE_OP_ERROR(src, "rename");
2823 if (copy_file(src, dest, FALSE) < 0) return -1;
2830 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2833 gint bytes_left, to_read;
2836 if (fseek(fp, offset, SEEK_SET) < 0) {
2841 bytes_left = length;
2842 to_read = MIN(bytes_left, sizeof(buf));
2844 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2845 if (n_read < to_read && ferror(fp))
2847 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2850 bytes_left -= n_read;
2851 if (bytes_left == 0)
2853 to_read = MIN(bytes_left, sizeof(buf));
2864 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2867 gboolean err = FALSE;
2869 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2870 FILE_OP_ERROR(dest, "fopen");
2874 if (change_file_mode_rw(dest_fp, dest) < 0) {
2875 FILE_OP_ERROR(dest, "chmod");
2876 g_warning("can't change file mode\n");
2879 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2882 if (!err && fclose(dest_fp) == EOF) {
2883 FILE_OP_ERROR(dest, "fclose");
2888 g_warning("writing to %s failed.\n", dest);
2896 /* convert line endings into CRLF. If the last line doesn't end with
2897 * linebreak, add it.
2899 gchar *canonicalize_str(const gchar *str)
2905 for (p = str; *p != '\0'; ++p) {
2912 if (p == str || *(p - 1) != '\n')
2915 out = outp = g_malloc(new_len + 1);
2916 for (p = str; *p != '\0'; ++p) {
2923 if (p == str || *(p - 1) != '\n') {
2932 gint canonicalize_file(const gchar *src, const gchar *dest)
2934 FILE *src_fp, *dest_fp;
2935 gchar buf[BUFFSIZE];
2937 gboolean err = FALSE;
2938 gboolean last_linebreak = FALSE;
2940 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2941 FILE_OP_ERROR(src, "fopen");
2945 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2946 FILE_OP_ERROR(dest, "fopen");
2951 if (change_file_mode_rw(dest_fp, dest) < 0) {
2952 FILE_OP_ERROR(dest, "chmod");
2953 g_warning("can't change file mode\n");
2956 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2960 if (len == 0) break;
2961 last_linebreak = FALSE;
2963 if (buf[len - 1] != '\n') {
2964 last_linebreak = TRUE;
2965 r = fputs(buf, dest_fp);
2966 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2967 r = fputs(buf, dest_fp);
2970 r = fwrite(buf, 1, len - 1, dest_fp);
2975 r = fputs("\r\n", dest_fp);
2979 g_warning("writing to %s failed.\n", dest);
2987 if (last_linebreak == TRUE) {
2988 if (fputs("\r\n", dest_fp) == EOF)
2992 if (ferror(src_fp)) {
2993 FILE_OP_ERROR(src, "fgets");
2997 if (fclose(dest_fp) == EOF) {
2998 FILE_OP_ERROR(dest, "fclose");
3010 gint canonicalize_file_replace(const gchar *file)
3014 tmp_file = get_tmp_file();
3016 if (canonicalize_file(file, tmp_file) < 0) {
3021 if (move_file(tmp_file, file, TRUE) < 0) {
3022 g_warning("can't replace %s .\n", file);
3032 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3034 FILE *src_fp, *dest_fp;
3035 gchar buf[BUFFSIZE];
3036 gboolean err = FALSE;
3038 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3039 FILE_OP_ERROR(src, "fopen");
3043 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3044 FILE_OP_ERROR(dest, "fopen");
3049 if (change_file_mode_rw(dest_fp, dest) < 0) {
3050 FILE_OP_ERROR(dest, "chmod");
3051 g_warning("can't change file mode\n");
3054 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3056 if (fputs(buf, dest_fp) == EOF) {
3057 g_warning("writing to %s failed.\n", dest);
3065 if (ferror(src_fp)) {
3066 FILE_OP_ERROR(src, "fgets");
3070 if (fclose(dest_fp) == EOF) {
3071 FILE_OP_ERROR(dest, "fclose");
3083 gint uncanonicalize_file_replace(const gchar *file)
3087 tmp_file = get_tmp_file();
3089 if (uncanonicalize_file(file, tmp_file) < 0) {
3094 if (move_file(tmp_file, file, TRUE) < 0) {
3095 g_warning("can't replace %s .\n", file);
3105 gchar *normalize_newlines(const gchar *str)
3107 const gchar *p = str;
3110 out = outp = g_malloc(strlen(str) + 1);
3111 for (p = str; *p != '\0'; ++p) {
3113 if (*(p + 1) != '\n')
3124 gchar *get_outgoing_rfc2822_str(FILE *fp)
3126 gchar buf[BUFFSIZE];
3130 str = g_string_new(NULL);
3132 /* output header part */
3133 while (fgets(buf, sizeof(buf), fp) != NULL) {
3135 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3142 else if (next != ' ' && next != '\t') {
3146 if (fgets(buf, sizeof(buf), fp) == NULL)
3150 g_string_append(str, buf);
3151 g_string_append(str, "\r\n");
3157 /* output body part */
3158 while (fgets(buf, sizeof(buf), fp) != NULL) {
3161 g_string_append_c(str, '.');
3162 g_string_append(str, buf);
3163 g_string_append(str, "\r\n");
3167 g_string_free(str, FALSE);
3173 * Create a new boundary in a way that it is very unlikely that this
3174 * will occur in the following text. It would be easy to ensure
3175 * uniqueness if everything is either quoted-printable or base64
3176 * encoded (note that conversion is allowed), but because MIME bodies
3177 * may be nested, it may happen that the same boundary has already
3180 * boundary := 0*69<bchars> bcharsnospace
3181 * bchars := bcharsnospace / " "
3182 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3183 * "+" / "_" / "," / "-" / "." /
3184 * "/" / ":" / "=" / "?"
3186 * some special characters removed because of buggy MTAs
3189 gchar *generate_mime_boundary(const gchar *prefix)
3191 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3192 "abcdefghijklmnopqrstuvwxyz"
3197 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3198 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3201 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3205 gint change_file_mode_rw(FILE *fp, const gchar *file)
3208 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3210 return g_chmod(file, S_IRUSR|S_IWUSR);
3214 FILE *my_tmpfile(void)
3216 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3217 const gchar suffix[] = ".XXXXXX";
3218 const gchar *tmpdir;
3220 const gchar *progname;
3226 tmpdir = get_tmp_dir();
3227 tmplen = strlen(tmpdir);
3228 progname = g_get_prgname();
3229 if (progname == NULL)
3230 progname = "sylpheed-claws";
3231 proglen = strlen(progname);
3232 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3235 memcpy(fname, tmpdir, tmplen);
3236 fname[tmplen] = G_DIR_SEPARATOR;
3237 memcpy(fname + tmplen + 1, progname, proglen);
3238 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3240 fd = mkstemp(fname);
3248 fp = fdopen(fd, "w+b");
3253 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3258 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3262 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3263 dir, G_DIR_SEPARATOR);
3264 fd = mkstemp_name(template, filename);
3267 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3268 fd = mkstemp(*filename);
3270 return fdopen(fd, "w+");
3273 FILE *str_open_as_stream(const gchar *str)
3278 g_return_val_if_fail(str != NULL, NULL);
3282 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3287 if (len == 0) return fp;
3289 if (fwrite(str, 1, len, fp) != len) {
3290 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3299 gint str_write_to_file(const gchar *str, const gchar *file)
3304 g_return_val_if_fail(str != NULL, -1);
3305 g_return_val_if_fail(file != NULL, -1);
3307 if ((fp = g_fopen(file, "wb")) == NULL) {
3308 FILE_OP_ERROR(file, "fopen");
3318 if (fwrite(str, 1, len, fp) != len) {
3319 FILE_OP_ERROR(file, "fwrite");
3325 if (fclose(fp) == EOF) {
3326 FILE_OP_ERROR(file, "fclose");
3334 gchar *file_read_to_str(const gchar *file)
3339 g_return_val_if_fail(file != NULL, NULL);
3341 if ((fp = g_fopen(file, "rb")) == NULL) {
3342 FILE_OP_ERROR(file, "fopen");
3346 str = file_read_stream_to_str(fp);
3353 gchar *file_read_stream_to_str(FILE *fp)
3360 g_return_val_if_fail(fp != NULL, NULL);
3362 array = g_byte_array_new();
3364 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3365 if (n_read < sizeof(buf) && ferror(fp))
3367 g_byte_array_append(array, buf, n_read);
3371 FILE_OP_ERROR("file stream", "fread");
3372 g_byte_array_free(array, TRUE);
3377 g_byte_array_append(array, buf, 1);
3378 str = (gchar *)array->data;
3379 g_byte_array_free(array, FALSE);
3381 if (!g_utf8_validate(str, -1, NULL)) {
3382 const gchar *src_codeset, *dest_codeset;
3384 src_codeset = conv_get_locale_charset_str();
3385 dest_codeset = CS_UTF_8;
3386 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3394 gint execute_async(gchar *const argv[])
3396 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3398 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3399 NULL, NULL, NULL, FALSE) == FALSE) {
3400 g_warning("Can't execute command: %s\n", argv[0]);
3407 gint execute_sync(gchar *const argv[])
3411 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3413 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3414 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3415 g_warning("Can't execute command: %s\n", argv[0]);
3420 if (WIFEXITED(status))
3421 return WEXITSTATUS(status);
3429 gint execute_command_line(const gchar *cmdline, gboolean async)
3434 debug_print("execute_command_line(): executing: %s\n", cmdline);
3436 argv = strsplit_with_quote(cmdline, " ", 0);
3439 ret = execute_async(argv);
3441 ret = execute_sync(argv);
3448 gchar *get_command_output(const gchar *cmdline)
3450 gchar *child_stdout;
3453 g_return_val_if_fail(cmdline != NULL, NULL);
3455 debug_print("get_command_output(): executing: %s\n", cmdline);
3457 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3459 g_warning("Can't execute command: %s\n", cmdline);
3463 return child_stdout;
3466 static gint is_unchanged_uri_char(char c)
3478 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3484 for(i = 0; i < strlen(uri) ; i++) {
3485 if (is_unchanged_uri_char(uri[i])) {
3486 if (k + 2 >= bufsize)
3488 encoded_uri[k++] = uri[i];
3491 char * hexa = "0123456789ABCDEF";
3493 if (k + 4 >= bufsize)
3495 encoded_uri[k++] = '%';
3496 encoded_uri[k++] = hexa[uri[i] / 16];
3497 encoded_uri[k++] = hexa[uri[i] % 16];
3503 gint open_uri(const gchar *uri, const gchar *cmdline)
3505 gchar buf[BUFFSIZE];
3507 gchar encoded_uri[BUFFSIZE];
3509 g_return_val_if_fail(uri != NULL, -1);
3511 /* an option to choose whether to use encode_uri or not ? */
3512 encode_uri(encoded_uri, BUFFSIZE, uri);
3515 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3516 !strchr(p + 2, '%'))
3517 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3520 g_warning("Open URI command line is invalid "
3521 "(there must be only one '%%s'): %s",
3523 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3526 execute_command_line(buf, TRUE);
3531 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3533 gchar buf[BUFFSIZE];
3536 g_return_val_if_fail(filepath != NULL, -1);
3539 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3540 !strchr(p + 2, '%'))
3541 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3544 g_warning("Open Text Editor command line is invalid "
3545 "(there must be only one '%%s'): %s",
3547 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3550 execute_command_line(buf, TRUE);
3555 time_t remote_tzoffset_sec(const gchar *zone)
3557 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3563 time_t remoteoffset;
3565 strncpy(zone3, zone, 3);
3569 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3570 (c == '+' || c == '-')) {
3571 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3573 remoteoffset = -remoteoffset;
3574 } else if (!strncmp(zone, "UT" , 2) ||
3575 !strncmp(zone, "GMT", 2)) {
3577 } else if (strlen(zone3) == 3) {
3578 for (p = ustzstr; *p != '\0'; p += 3) {
3579 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3580 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3581 remoteoffset = iustz * 3600;
3587 } else if (strlen(zone3) == 1) {
3589 case 'Z': remoteoffset = 0; break;
3590 case 'A': remoteoffset = -1; break;
3591 case 'B': remoteoffset = -2; break;
3592 case 'C': remoteoffset = -3; break;
3593 case 'D': remoteoffset = -4; break;
3594 case 'E': remoteoffset = -5; break;
3595 case 'F': remoteoffset = -6; break;
3596 case 'G': remoteoffset = -7; break;
3597 case 'H': remoteoffset = -8; break;
3598 case 'I': remoteoffset = -9; break;
3599 case 'K': remoteoffset = -10; break; /* J is not used */
3600 case 'L': remoteoffset = -11; break;
3601 case 'M': remoteoffset = -12; break;
3602 case 'N': remoteoffset = 1; break;
3603 case 'O': remoteoffset = 2; break;
3604 case 'P': remoteoffset = 3; break;
3605 case 'Q': remoteoffset = 4; break;
3606 case 'R': remoteoffset = 5; break;
3607 case 'S': remoteoffset = 6; break;
3608 case 'T': remoteoffset = 7; break;
3609 case 'U': remoteoffset = 8; break;
3610 case 'V': remoteoffset = 9; break;
3611 case 'W': remoteoffset = 10; break;
3612 case 'X': remoteoffset = 11; break;
3613 case 'Y': remoteoffset = 12; break;
3614 default: remoteoffset = 0; break;
3616 remoteoffset = remoteoffset * 3600;
3620 return remoteoffset;
3623 time_t tzoffset_sec(time_t *now)
3629 lt = localtime(now);
3631 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3633 if (lt->tm_year < gmt.tm_year)
3635 else if (lt->tm_year > gmt.tm_year)
3637 else if (lt->tm_yday < gmt.tm_yday)
3639 else if (lt->tm_yday > gmt.tm_yday)
3642 if (off >= 24 * 60) /* should be impossible */
3643 off = 23 * 60 + 59; /* if not, insert silly value */
3644 if (off <= -24 * 60)
3645 off = -(23 * 60 + 59);
3650 /* calculate timezone offset */
3651 gchar *tzoffset(time_t *now)
3653 static gchar offset_string[6];
3659 lt = localtime(now);
3661 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3663 if (lt->tm_year < gmt.tm_year)
3665 else if (lt->tm_year > gmt.tm_year)
3667 else if (lt->tm_yday < gmt.tm_yday)
3669 else if (lt->tm_yday > gmt.tm_yday)
3677 if (off >= 24 * 60) /* should be impossible */
3678 off = 23 * 60 + 59; /* if not, insert silly value */
3680 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3682 return offset_string;
3685 void get_rfc822_date(gchar *buf, gint len)
3689 gchar day[4], mon[4];
3690 gint dd, hh, mm, ss, yyyy;
3695 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3696 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3697 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3698 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3701 /* just a wrapper to suppress the warning of gcc about %c */
3702 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3703 const struct tm *tm)
3705 return strftime(s, max, format, tm);
3708 void debug_set_mode(gboolean mode)
3713 gboolean debug_get_mode(void)
3718 void debug_print_real(const gchar *format, ...)
3721 gchar buf[BUFFSIZE];
3723 if (!debug_mode) return;
3725 va_start(args, format);
3726 g_vsnprintf(buf, sizeof(buf), format, args);
3733 const char * debug_srcname(const char *file)
3735 const char *s = strrchr (file, '/');
3740 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3742 if (subject == NULL)
3745 subject += subject_get_prefix_length(subject);
3747 return g_hash_table_lookup(subject_table, subject);
3750 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3753 if (subject == NULL || *subject == 0)
3755 subject += subject_get_prefix_length(subject);
3756 g_hash_table_insert(subject_table, subject, data);
3759 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3761 if (subject == NULL)
3764 subject += subject_get_prefix_length(subject);
3765 g_hash_table_remove(subject_table, subject);
3769 *\brief Check if a string is prefixed with known (combinations)
3770 * of prefixes. The function assumes that each prefix
3771 * is terminated by zero or exactly _one_ space.
3773 *\param str String to check for a prefixes
3775 *\return int Number of chars in the prefix that should be skipped
3776 * for a "clean" subject line. If no prefix was found, 0
3779 int subject_get_prefix_length(const gchar *subject)
3781 /*!< Array with allowable reply prefixes regexps. */
3782 static const gchar * const prefixes[] = {
3783 "Re\\:", /* "Re:" */
3784 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3785 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3786 "Aw\\:", /* "Aw:" (German) */
3787 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3788 "Res\\:", /* "Res:" (Brazilian Outlook) */
3789 "Fw\\:", /* "Fw:" Forward */
3790 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3791 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3792 "Rif\\:" /* "Rif:" (Italian Outlook) */
3795 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3798 static regex_t regex;
3799 static gboolean init_;
3801 if (!subject) return 0;
3802 if (!*subject) return 0;
3805 GString *s = g_string_new("");
3807 for (n = 0; n < PREFIXES; n++)
3808 /* Terminate each prefix regexpression by a
3809 * "\ ?" (zero or ONE space), and OR them */
3810 g_string_append_printf(s, "(%s\\ ?)%s",
3815 g_string_prepend(s, "(");
3816 g_string_append(s, ")+"); /* match at least once */
3817 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3820 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3821 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3822 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3823 debug_print("Error compiling regexp %s\n", s->str);
3824 g_string_free(s, TRUE);
3828 g_string_free(s, TRUE);
3832 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3838 guint g_stricase_hash(gconstpointer gptr)
3840 guint hash_result = 0;
3843 for (str = gptr; str && *str; str++) {
3844 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3845 else hash_result += *str;
3851 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3853 const char *str1 = gptr1;
3854 const char *str2 = gptr2;
3856 return !g_utf8_collate(str1, str2);
3859 gint g_int_compare(gconstpointer a, gconstpointer b)
3861 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3864 gchar *generate_msgid(gchar *buf, gint len)
3873 addr = g_strconcat("@", get_domain_name(), NULL);
3875 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3876 lt->tm_year + 1900, lt->tm_mon + 1,
3877 lt->tm_mday, lt->tm_hour,
3878 lt->tm_min, lt->tm_sec,
3879 (guint) rand(), addr);
3886 quote_cmd_argument()
3888 return a quoted string safely usable in argument of a command.
3890 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3893 gint quote_cmd_argument(gchar * result, guint size,
3903 for(p = path ; * p != '\0' ; p ++) {
3905 if (isalnum((guchar)*p) || (* p == '/')) {
3906 if (remaining > 0) {
3912 result[size - 1] = '\0';
3917 if (remaining >= 2) {
3925 result[size - 1] = '\0';
3930 if (remaining > 0) {
3934 result[size - 1] = '\0';
3948 static void g_node_map_recursive(GNode *node, gpointer data)
3950 GNodeMapData *mapdata = (GNodeMapData *) data;
3952 GNodeMapData newmapdata;
3955 newdata = mapdata->func(node->data, mapdata->data);
3956 if (newdata != NULL) {
3957 newnode = g_node_new(newdata);
3958 g_node_append(mapdata->parent, newnode);
3960 newmapdata.parent = newnode;
3961 newmapdata.func = mapdata->func;
3962 newmapdata.data = mapdata->data;
3964 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3968 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3971 GNodeMapData mapdata;
3973 g_return_val_if_fail(node != NULL, NULL);
3974 g_return_val_if_fail(func != NULL, NULL);
3976 root = g_node_new(func(node->data, data));
3978 mapdata.parent = root;
3979 mapdata.func = func;
3980 mapdata.data = data;
3982 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3987 #define HEX_TO_INT(val, hex) \
3991 if ('0' <= c && c <= '9') { \
3993 } else if ('a' <= c && c <= 'f') { \
3994 val = c - 'a' + 10; \
3995 } else if ('A' <= c && c <= 'F') { \
3996 val = c - 'A' + 10; \
4002 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4009 if (hi == -1 || lo == -1)
4012 *out = (hi << 4) + lo;
4016 #define INT_TO_HEX(hex, val) \
4019 hex = '0' + (val); \
4021 hex = 'A' + (val) - 10; \
4024 void get_hex_str(gchar *out, guchar ch)
4028 INT_TO_HEX(hex, ch >> 4);
4030 INT_TO_HEX(hex, ch & 0x0f);
4036 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4038 #define G_PRINT_REF g_print
4042 *\brief Register ref counted pointer. It is based on GBoxed, so should
4043 * work with anything that uses the GType system. The semantics
4044 * are similar to a C++ auto pointer, with the exception that
4045 * C doesn't have automatic closure (calling destructors) when
4046 * exiting a block scope.
4047 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4048 * function directly.
4050 *\return GType A GType type.
4052 GType g_auto_pointer_register(void)
4054 static GType auto_pointer_type;
4055 if (!auto_pointer_type)
4057 g_boxed_type_register_static
4058 ("G_TYPE_AUTO_POINTER",
4059 (GBoxedCopyFunc) g_auto_pointer_copy,
4060 (GBoxedFreeFunc) g_auto_pointer_free);
4061 return auto_pointer_type;
4065 *\brief Structure with g_new() allocated pointer guarded by the
4068 typedef struct AutoPointerRef {
4069 void (*free) (gpointer);
4075 *\brief The auto pointer opaque structure that references the
4076 * pointer guard block.
4078 typedef struct AutoPointer {
4079 AutoPointerRef *ref;
4080 gpointer ptr; /*!< access to protected pointer */
4084 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4088 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4089 * ... when assigning, copying and freeing storage elements
4091 * gtk_list_store_new(N_S_COLUMNS,
4092 * G_TYPE_AUTO_POINTER,
4096 * Template *precious_data = g_new0(Template, 1);
4097 * g_pointer protect = g_auto_pointer_new(precious_data);
4099 * gtk_list_store_set(container, &iter,
4103 * ... the gtk_list_store has copied the pointer and
4104 * ... incremented its reference count, we should free
4105 * ... the auto pointer (in C++ a destructor would do
4106 * ... this for us when leaving block scope)
4108 * g_auto_pointer_free(protect);
4110 * ... gtk_list_store_set() now manages the data. When
4111 * ... *explicitly* requesting a pointer from the list
4112 * ... store, don't forget you get a copy that should be
4113 * ... freed with g_auto_pointer_free() eventually.
4117 *\param pointer Pointer to be guarded.
4119 *\return GAuto * Pointer that should be used in containers with
4122 GAuto *g_auto_pointer_new(gpointer p)
4124 AutoPointerRef *ref;
4130 ref = g_new0(AutoPointerRef, 1);
4131 ptr = g_new0(AutoPointer, 1);
4141 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4147 *\brief Allocate an autopointer using the passed \a free function to
4148 * free the guarded pointer
4150 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4157 aptr = g_auto_pointer_new(p);
4158 aptr->ref->free = free_;
4162 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4164 if (auto_ptr == NULL)
4166 return ((AutoPointer *) auto_ptr)->ptr;
4170 *\brief Copies an auto pointer by. It's mostly not necessary
4171 * to call this function directly, unless you copy/assign
4172 * the guarded pointer.
4174 *\param auto_ptr Auto pointer returned by previous call to
4175 * g_auto_pointer_new_XXX()
4177 *\return gpointer An auto pointer
4179 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4182 AutoPointerRef *ref;
4185 if (auto_ptr == NULL)
4190 newp = g_new0(AutoPointer, 1);
4193 newp->ptr = ref->pointer;
4197 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4203 *\brief Free an auto pointer
4205 void g_auto_pointer_free(GAuto *auto_ptr)
4208 AutoPointerRef *ref;
4210 if (auto_ptr == NULL)
4216 if (--(ref->cnt) == 0) {
4218 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4220 ref->free(ref->pointer);
4225 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4230 void replace_returns(gchar *str)
4235 while (strstr(str, "\n")) {
4236 *strstr(str, "\n") = ' ';
4238 while (strstr(str, "\r")) {
4239 *strstr(str, "\r") = ' ';
4243 /* get_uri_part() - retrieves a URI starting from scanpos.
4244 Returns TRUE if succesful */
4245 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4246 const gchar **bp, const gchar **ep, gboolean hdr)
4249 gint parenthese_cnt = 0;
4251 g_return_val_if_fail(start != NULL, FALSE);
4252 g_return_val_if_fail(scanpos != NULL, FALSE);
4253 g_return_val_if_fail(bp != NULL, FALSE);
4254 g_return_val_if_fail(ep != NULL, FALSE);
4258 /* find end point of URI */
4259 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4260 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4261 !IS_ASCII(*(const guchar *)ep_) ||
4262 strchr("[]{}<>\"", *ep_)) {
4264 } else if (strchr("(", *ep_)) {
4266 } else if (strchr(")", *ep_)) {
4267 if (parenthese_cnt > 0)
4274 /* no punctuation at end of string */
4276 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4277 * should pass some URI type to this function and decide on that whether
4278 * to perform punctuation stripping */
4280 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4282 for (; ep_ - 1 > scanpos + 1 &&
4283 IS_REAL_PUNCT(*(ep_ - 1));
4287 #undef IS_REAL_PUNCT
4294 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4296 while (bp && *bp && g_ascii_isspace(*bp))
4298 return g_strndup(bp, ep - bp);
4301 /* valid mail address characters */
4302 #define IS_RFC822_CHAR(ch) \
4306 !g_ascii_isspace(ch) && \
4307 !strchr("(),;<>\"", (ch)))
4309 /* alphabet and number within 7bit ASCII */
4310 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4311 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4313 static GHashTable *create_domain_tab(void)
4315 static const gchar *toplvl_domains [] = {
4317 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4318 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4319 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4320 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4321 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4322 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4323 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4324 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4325 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4326 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4327 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4328 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4329 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4330 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4331 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4332 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4333 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4334 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4335 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4336 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4337 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4338 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4339 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4340 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4341 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4342 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4345 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4347 g_return_val_if_fail(htab, NULL);
4348 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4349 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4353 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4355 const gint MAX_LVL_DOM_NAME_LEN = 6;
4356 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4357 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4360 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4363 for (p = buf; p < m && first < last; *p++ = *first++)
4367 return g_hash_table_lookup(tab, buf) != NULL;
4370 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4371 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4372 const gchar **bp, const gchar **ep, gboolean hdr)
4374 /* more complex than the uri part because we need to scan back and forward starting from
4375 * the scan position. */
4376 gboolean result = FALSE;
4377 const gchar *bp_ = NULL;
4378 const gchar *ep_ = NULL;
4379 static GHashTable *dom_tab;
4380 const gchar *last_dot = NULL;
4381 const gchar *prelast_dot = NULL;
4382 const gchar *last_tld_char = NULL;
4384 /* the informative part of the email address (describing the name
4385 * of the email address owner) may contain quoted parts. the
4386 * closure stack stores the last encountered quotes. */
4387 gchar closure_stack[128];
4388 gchar *ptr = closure_stack;
4390 g_return_val_if_fail(start != NULL, FALSE);
4391 g_return_val_if_fail(scanpos != NULL, FALSE);
4392 g_return_val_if_fail(bp != NULL, FALSE);
4393 g_return_val_if_fail(ep != NULL, FALSE);
4396 const gchar *start_quote = NULL;
4397 const gchar *end_quote = NULL;
4399 /* go to the real start */
4400 if (start[0] == ',')
4402 if (start[0] == ';')
4404 while (start[0] == '\n' || start[0] == '\r')
4406 while (start[0] == ' ' || start[0] == '\t')
4411 /* check if there are quotes (to skip , in them) */
4412 if (*start == '"') {
4413 start_quote = start;
4415 end_quote = strstr(start, "\"");
4421 /* skip anything between quotes */
4422 if (start_quote && end_quote) {
4427 /* find end (either , or ; or end of line) */
4428 if (strstr(start, ",") && strstr(start, ";"))
4429 *ep = strstr(start,",") < strstr(start, ";")
4430 ? strstr(start, ",") : strstr(start, ";");
4431 else if (strstr(start, ","))
4432 *ep = strstr(start, ",");
4433 else if (strstr(start, ";"))
4434 *ep = strstr(start, ";");
4436 *ep = start+strlen(start);
4438 /* go back to real start */
4439 if (start_quote && end_quote) {
4440 start = start_quote;
4443 /* check there's still an @ in that, or search
4444 * further if possible */
4445 if (strstr(start, "@") && strstr(start, "@") < *ep)
4447 else if (*ep < start+strlen(start)) {
4450 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4458 dom_tab = create_domain_tab();
4459 g_return_val_if_fail(dom_tab, FALSE);
4461 /* scan start of address */
4462 for (bp_ = scanpos - 1;
4463 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4466 /* TODO: should start with an alnum? */
4468 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4471 if (bp_ != scanpos) {
4472 /* scan end of address */
4473 for (ep_ = scanpos + 1;
4474 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4476 prelast_dot = last_dot;
4478 if (*(last_dot + 1) == '.') {
4479 if (prelast_dot == NULL)
4481 last_dot = prelast_dot;
4486 /* TODO: really should terminate with an alnum? */
4487 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4492 if (last_dot == NULL)
4494 if (last_dot >= ep_)
4495 last_dot = prelast_dot;
4496 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4500 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4501 if (*last_tld_char == '?')
4504 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4511 if (!result) return FALSE;
4513 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4514 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4515 && IS_RFC822_CHAR(*(ep_ + 3))) {
4516 /* this informative part with an @ in it is
4517 * followed by the email address */
4520 /* go to matching '>' (or next non-rfc822 char, like \n) */
4521 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4524 /* include the bracket */
4525 if (*ep_ == '>') ep_++;
4527 /* include the leading quote */
4535 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4536 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4539 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4540 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4543 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4544 #define IN_STACK() (ptr > closure_stack)
4545 /* has underrun check */
4546 #define POP_STACK() if(IN_STACK()) --ptr
4547 /* has overrun check */
4548 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4549 /* has underrun check */
4550 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4554 /* scan for the informative part. */
4555 for (bp_ -= 2; bp_ >= start; bp_--) {
4556 /* if closure on the stack keep scanning */
4557 if (PEEK_STACK() == *bp_) {
4561 if (*bp_ == '\'' || *bp_ == '"') {
4566 /* if nothing in the closure stack, do the special conditions
4567 * the following if..else expression simply checks whether
4568 * a token is acceptable. if not acceptable, the clause
4569 * should terminate the loop with a 'break' */
4570 if (!PEEK_STACK()) {
4572 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4573 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4574 /* hyphens are allowed, but only in
4576 } else if (strchr(" \"'", *bp_)) {
4577 /* but anything not being a punctiation
4580 break; /* anything else is rejected */
4587 /* scan forward (should start with an alnum) */
4588 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4604 #undef IS_ASCII_ALNUM
4605 #undef IS_RFC822_CHAR
4607 gchar *make_email_string(const gchar *bp, const gchar *ep)
4609 /* returns a mailto: URI; mailto: is also used to detect the
4610 * uri type later on in the button_pressed signal handler */
4614 tmp = g_strndup(bp, ep - bp);
4615 result = g_strconcat("mailto:", tmp, NULL);
4621 gchar *make_http_string(const gchar *bp, const gchar *ep)
4623 /* returns an http: URI; */
4627 while (bp && *bp && g_ascii_isspace(*bp))
4629 tmp = g_strndup(bp, ep - bp);
4630 result = g_strconcat("http://", tmp, NULL);
4636 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4638 FILE *fp = fopen(path, "rb");
4639 gchar buf[BUFFSIZE];
4640 gchar *result = NULL;
4643 while (fgets(buf, sizeof (buf), fp) != NULL) {
4644 gchar **parts = g_strsplit(buf, ";", -1);
4645 gchar *trimmed = parts[0];
4646 while (trimmed[0] == ' ')
4648 while (trimmed[strlen(trimmed)-1] == ' ')
4649 trimmed[strlen(trimmed)-1] = '\0';
4651 if (!strcmp(trimmed, type)) {
4653 while (trimmed[0] == ' ')
4655 while (trimmed[strlen(trimmed)-1] == ' ')
4656 trimmed[strlen(trimmed)-1] = '\0';
4657 while (trimmed[strlen(trimmed)-1] == '\n')
4658 trimmed[strlen(trimmed)-1] = '\0';
4659 while (trimmed[strlen(trimmed)-1] == '\r')
4660 trimmed[strlen(trimmed)-1] = '\0';
4661 result = g_strdup(trimmed);
4664 if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4665 gchar *start = g_strdup(result);
4666 gchar *end = g_strdup(strstr(result, "%s")+2);
4668 *strstr(start, "%s") = '\0';
4669 tmp = g_strconcat(start,"'%s'",end, NULL);
4682 gchar *mailcap_get_command_for_type(const gchar *type)
4684 gchar *result = NULL;
4686 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4687 result = mailcap_get_command_in_file(path, type);
4691 result = mailcap_get_command_in_file("/etc/mailcap", type);
4695 gint copy_dir(const gchar *src, const gchar *dst)
4700 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4701 g_warning("failed to open directory: %s\n", src);
4705 if (make_dir(dst) < 0)
4708 while ((name = g_dir_read_name(dir)) != NULL) {
4709 gchar *old_file, *new_file;
4710 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4711 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4712 debug_print("copying: %s -> %s\n", old_file, new_file);
4713 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4714 gint r = copy_file(old_file, new_file, TRUE);
4717 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4718 gint r = copy_dir(old_file, new_file);
4726 /* crude test to see if a file is an email. */
4727 gboolean file_is_email (const gchar *filename)
4733 if (filename == NULL)
4735 if ((fp = g_fopen(filename, "rb")) == NULL)
4737 while (i < 60 && score < 3
4738 && fgets(buffer, sizeof (buffer), fp) > 0) {
4739 if (!strncmp(buffer, "From:", strlen("From:")))
4741 if (!strncmp(buffer, "To:", strlen("To:")))
4743 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4748 return (score >= 3);