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 return g_get_home_dir();
2006 const gchar *get_rc_dir(void)
2008 static gchar *rc_dir = NULL;
2011 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2017 const gchar *get_mail_base_dir(void)
2020 static gchar *mail_base_dir = NULL;
2023 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2026 return mail_base_dir;
2028 return get_home_dir();
2032 const gchar *get_news_cache_dir(void)
2034 static gchar *news_cache_dir = NULL;
2036 if (!news_cache_dir)
2037 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2038 NEWS_CACHE_DIR, NULL);
2040 return news_cache_dir;
2043 const gchar *get_imap_cache_dir(void)
2045 static gchar *imap_cache_dir = NULL;
2047 if (!imap_cache_dir)
2048 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2049 IMAP_CACHE_DIR, NULL);
2051 return imap_cache_dir;
2054 const gchar *get_mbox_cache_dir(void)
2056 static gchar *mbox_cache_dir = NULL;
2058 if (!mbox_cache_dir)
2059 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2060 MBOX_CACHE_DIR, NULL);
2062 return mbox_cache_dir;
2065 const gchar *get_mime_tmp_dir(void)
2067 static gchar *mime_tmp_dir = NULL;
2070 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2071 MIME_TMP_DIR, NULL);
2073 return mime_tmp_dir;
2076 const gchar *get_template_dir(void)
2078 static gchar *template_dir = NULL;
2081 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2082 TEMPLATE_DIR, NULL);
2084 return template_dir;
2087 const gchar *get_header_cache_dir(void)
2089 static gchar *header_dir = NULL;
2092 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2093 HEADER_CACHE_DIR, NULL);
2098 /* Return the default directory for Plugins. */
2099 const gchar *get_plugin_dir(void)
2102 static gchar *plugin_dir = NULL;
2105 plugin_dir = g_strconcat(sylpheed_get_startup_dir(),
2106 "\\lib\\sylpheed-claws\\plugins\\",
2114 const gchar *get_tmp_dir(void)
2116 static gchar *tmp_dir = NULL;
2119 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2125 gchar *get_tmp_file(void)
2128 static guint32 id = 0;
2130 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2131 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2136 const gchar *get_domain_name(void)
2139 static gchar *domain_name = NULL;
2145 if (uname(&uts) < 0) {
2147 domain_name = "unknown";
2149 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2150 perror("gethostbyname");
2151 domain_name = g_strdup(uts.nodename);
2153 domain_name = g_strdup(hp->h_name);
2157 debug_print("domain name = %s\n", domain_name);
2166 off_t get_file_size(const gchar *file)
2170 if (g_stat(file, &s) < 0) {
2171 FILE_OP_ERROR(file, "stat");
2178 off_t get_file_size_as_crlf(const gchar *file)
2182 gchar buf[BUFFSIZE];
2184 if ((fp = g_fopen(file, "rb")) == NULL) {
2185 FILE_OP_ERROR(file, "fopen");
2189 while (fgets(buf, sizeof(buf), fp) != NULL) {
2191 size += strlen(buf) + 2;
2195 FILE_OP_ERROR(file, "fgets");
2204 off_t get_left_file_size(FILE *fp)
2210 if ((pos = ftell(fp)) < 0) {
2214 if (fseek(fp, 0L, SEEK_END) < 0) {
2218 if ((end = ftell(fp)) < 0) {
2223 if (fseek(fp, pos, SEEK_SET) < 0) {
2231 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2238 if (g_stat(file, &s) < 0) {
2239 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2243 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2250 /* Test on whether FILE is a relative file name. This is
2251 * straightforward for Unix but more complex for Windows. */
2252 gboolean is_relative_filename(const gchar *file)
2257 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2258 return FALSE; /* Prefixed with a hostname - this can't
2259 * be a relative name. */
2261 if ( ((*file >= 'a' && *file <= 'z')
2262 || (*file >= 'A' && *file <= 'Z'))
2264 file += 2; /* Skip drive letter. */
2266 return !(*file == '\\' || *file == '/');
2268 return !(*file == G_DIR_SEPARATOR);
2273 gboolean is_dir_exist(const gchar *dir)
2278 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2281 gboolean is_file_entry_exist(const gchar *file)
2286 return g_file_test(file, G_FILE_TEST_EXISTS);
2289 gboolean dirent_is_regular_file(struct dirent *d)
2291 #ifdef HAVE_DIRENT_D_TYPE
2292 if (d->d_type == DT_REG)
2294 else if (d->d_type != DT_UNKNOWN)
2298 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2301 gboolean dirent_is_directory(struct dirent *d)
2303 #ifdef HAVE_DIRENT_D_TYPE
2304 if (d->d_type == DT_DIR)
2306 else if (d->d_type != DT_UNKNOWN)
2310 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2313 gint change_dir(const gchar *dir)
2315 gchar *prevdir = NULL;
2318 prevdir = g_get_current_dir();
2320 if (g_chdir(dir) < 0) {
2321 FILE_OP_ERROR(dir, "chdir");
2322 if (debug_mode) g_free(prevdir);
2324 } else if (debug_mode) {
2327 cwd = g_get_current_dir();
2328 if (strcmp(prevdir, cwd) != 0)
2329 g_print("current dir: %s\n", cwd);
2337 gint make_dir(const gchar *dir)
2339 if (g_mkdir(dir, S_IRWXU) < 0) {
2340 FILE_OP_ERROR(dir, "mkdir");
2343 if (g_chmod(dir, S_IRWXU) < 0)
2344 FILE_OP_ERROR(dir, "chmod");
2349 gint make_dir_hier(const gchar *dir)
2354 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2355 parent_dir = g_strndup(dir, p - dir);
2356 if (*parent_dir != '\0') {
2357 if (!is_dir_exist(parent_dir)) {
2358 if (make_dir(parent_dir) < 0) {
2367 if (!is_dir_exist(dir)) {
2368 if (make_dir(dir) < 0)
2375 gint remove_all_files(const gchar *dir)
2378 const gchar *dir_name;
2381 prev_dir = g_get_current_dir();
2383 if (g_chdir(dir) < 0) {
2384 FILE_OP_ERROR(dir, "chdir");
2389 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2390 g_warning("failed to open directory: %s\n", dir);
2395 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2396 if (g_unlink(dir_name) < 0)
2397 FILE_OP_ERROR(dir_name, "unlink");
2402 if (g_chdir(prev_dir) < 0) {
2403 FILE_OP_ERROR(prev_dir, "chdir");
2413 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2416 const gchar *dir_name;
2420 prev_dir = g_get_current_dir();
2422 if (g_chdir(dir) < 0) {
2423 FILE_OP_ERROR(dir, "chdir");
2428 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2429 g_warning("failed to open directory: %s\n", dir);
2434 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2435 file_no = to_number(dir_name);
2436 if (file_no > 0 && first <= file_no && file_no <= last) {
2437 if (is_dir_exist(dir_name))
2439 if (g_unlink(dir_name) < 0)
2440 FILE_OP_ERROR(dir_name, "unlink");
2446 if (g_chdir(prev_dir) < 0) {
2447 FILE_OP_ERROR(prev_dir, "chdir");
2457 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2460 const gchar *dir_name;
2464 prev_dir = g_get_current_dir();
2466 if (g_chdir(dir) < 0) {
2467 FILE_OP_ERROR(dir, "chdir");
2472 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2473 FILE_OP_ERROR(dir, "opendir");
2478 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2479 file_no = to_number(dir_name);
2480 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2481 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2482 if (is_dir_exist(dir_name))
2484 if (g_unlink(dir_name) < 0)
2485 FILE_OP_ERROR(dir_name, "unlink");
2491 if (g_chdir(prev_dir) < 0) {
2492 FILE_OP_ERROR(prev_dir, "chdir");
2502 gint remove_all_numbered_files(const gchar *dir)
2504 return remove_numbered_files(dir, 0, UINT_MAX);
2507 gint remove_expired_files(const gchar *dir, guint hours)
2510 const gchar *dir_name;
2514 time_t mtime, now, expire_time;
2516 prev_dir = g_get_current_dir();
2518 if (g_chdir(dir) < 0) {
2519 FILE_OP_ERROR(dir, "chdir");
2524 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2525 g_warning("failed to open directory: %s\n", dir);
2531 expire_time = hours * 60 * 60;
2533 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2534 file_no = to_number(dir_name);
2536 if (g_stat(dir_name, &s) < 0) {
2537 FILE_OP_ERROR(dir_name, "stat");
2540 if (S_ISDIR(s.st_mode))
2542 mtime = MAX(s.st_mtime, s.st_atime);
2543 if (now - mtime > expire_time) {
2544 if (g_unlink(dir_name) < 0)
2545 FILE_OP_ERROR(dir_name, "unlink");
2552 if (g_chdir(prev_dir) < 0) {
2553 FILE_OP_ERROR(prev_dir, "chdir");
2563 gint remove_dir_recursive(const gchar *dir)
2567 const gchar *dir_name;
2570 if (g_stat(dir, &s) < 0) {
2571 FILE_OP_ERROR(dir, "stat");
2572 if (ENOENT == errno) return 0;
2576 if (!S_ISDIR(s.st_mode)) {
2577 if (g_unlink(dir) < 0) {
2578 FILE_OP_ERROR(dir, "unlink");
2585 prev_dir = g_get_current_dir();
2586 /* g_print("prev_dir = %s\n", prev_dir); */
2588 if (!path_cmp(prev_dir, dir)) {
2590 if (g_chdir("..") < 0) {
2591 FILE_OP_ERROR(dir, "chdir");
2594 prev_dir = g_get_current_dir();
2597 if (g_chdir(dir) < 0) {
2598 FILE_OP_ERROR(dir, "chdir");
2603 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2604 g_warning("failed to open directory: %s\n", dir);
2610 /* remove all files in the directory */
2611 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2612 /* g_print("removing %s\n", dir_name); */
2614 if (is_dir_exist(dir_name)) {
2615 if (remove_dir_recursive(dir_name) < 0) {
2616 g_warning("can't remove directory\n");
2620 if (g_unlink(dir_name) < 0)
2621 FILE_OP_ERROR(dir_name, "unlink");
2627 if (g_chdir(prev_dir) < 0) {
2628 FILE_OP_ERROR(prev_dir, "chdir");
2635 if (g_rmdir(dir) < 0) {
2636 FILE_OP_ERROR(dir, "rmdir");
2643 gint rename_force(const gchar *oldpath, const gchar *newpath)
2646 if (!is_file_entry_exist(oldpath)) {
2650 if (is_file_exist(newpath)) {
2651 if (g_unlink(newpath) < 0)
2652 FILE_OP_ERROR(newpath, "unlink");
2655 return g_rename(oldpath, newpath);
2659 * Append src file body to the tail of dest file.
2660 * Now keep_backup has no effects.
2662 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2664 FILE *src_fp, *dest_fp;
2668 gboolean err = FALSE;
2670 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2671 FILE_OP_ERROR(src, "fopen");
2675 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2676 FILE_OP_ERROR(dest, "fopen");
2681 if (change_file_mode_rw(dest_fp, dest) < 0) {
2682 FILE_OP_ERROR(dest, "chmod");
2683 g_warning("can't change file mode\n");
2686 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2687 if (n_read < sizeof(buf) && ferror(src_fp))
2689 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2690 g_warning("writing to %s failed.\n", dest);
2698 if (ferror(src_fp)) {
2699 FILE_OP_ERROR(src, "fread");
2703 if (fclose(dest_fp) == EOF) {
2704 FILE_OP_ERROR(dest, "fclose");
2716 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2718 FILE *src_fp, *dest_fp;
2721 gchar *dest_bak = NULL;
2722 gboolean err = FALSE;
2724 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2725 FILE_OP_ERROR(src, "fopen");
2728 if (is_file_exist(dest)) {
2729 dest_bak = g_strconcat(dest, ".bak", NULL);
2730 if (rename_force(dest, dest_bak) < 0) {
2731 FILE_OP_ERROR(dest, "rename");
2738 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2739 FILE_OP_ERROR(dest, "fopen");
2742 if (rename_force(dest_bak, dest) < 0)
2743 FILE_OP_ERROR(dest_bak, "rename");
2749 if (change_file_mode_rw(dest_fp, dest) < 0) {
2750 FILE_OP_ERROR(dest, "chmod");
2751 g_warning("can't change file mode\n");
2754 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2755 if (n_read < sizeof(buf) && ferror(src_fp))
2757 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2758 g_warning("writing to %s failed.\n", dest);
2763 if (rename_force(dest_bak, dest) < 0)
2764 FILE_OP_ERROR(dest_bak, "rename");
2771 if (ferror(src_fp)) {
2772 FILE_OP_ERROR(src, "fread");
2776 if (fclose(dest_fp) == EOF) {
2777 FILE_OP_ERROR(dest, "fclose");
2784 if (rename_force(dest_bak, dest) < 0)
2785 FILE_OP_ERROR(dest_bak, "rename");
2791 if (keep_backup == FALSE && dest_bak)
2799 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2801 if (overwrite == FALSE && is_file_exist(dest)) {
2802 g_warning("move_file(): file %s already exists.", dest);
2806 if (rename_force(src, dest) == 0) return 0;
2808 if (EXDEV != errno) {
2809 FILE_OP_ERROR(src, "rename");
2813 if (copy_file(src, dest, FALSE) < 0) return -1;
2820 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2823 gint bytes_left, to_read;
2826 if (fseek(fp, offset, SEEK_SET) < 0) {
2831 bytes_left = length;
2832 to_read = MIN(bytes_left, sizeof(buf));
2834 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2835 if (n_read < to_read && ferror(fp))
2837 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2840 bytes_left -= n_read;
2841 if (bytes_left == 0)
2843 to_read = MIN(bytes_left, sizeof(buf));
2854 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2857 gboolean err = FALSE;
2859 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2860 FILE_OP_ERROR(dest, "fopen");
2864 if (change_file_mode_rw(dest_fp, dest) < 0) {
2865 FILE_OP_ERROR(dest, "chmod");
2866 g_warning("can't change file mode\n");
2869 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2872 if (!err && fclose(dest_fp) == EOF) {
2873 FILE_OP_ERROR(dest, "fclose");
2878 g_warning("writing to %s failed.\n", dest);
2886 /* convert line endings into CRLF. If the last line doesn't end with
2887 * linebreak, add it.
2889 gchar *canonicalize_str(const gchar *str)
2895 for (p = str; *p != '\0'; ++p) {
2902 if (p == str || *(p - 1) != '\n')
2905 out = outp = g_malloc(new_len + 1);
2906 for (p = str; *p != '\0'; ++p) {
2913 if (p == str || *(p - 1) != '\n') {
2922 gint canonicalize_file(const gchar *src, const gchar *dest)
2924 FILE *src_fp, *dest_fp;
2925 gchar buf[BUFFSIZE];
2927 gboolean err = FALSE;
2928 gboolean last_linebreak = FALSE;
2930 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2931 FILE_OP_ERROR(src, "fopen");
2935 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2936 FILE_OP_ERROR(dest, "fopen");
2941 if (change_file_mode_rw(dest_fp, dest) < 0) {
2942 FILE_OP_ERROR(dest, "chmod");
2943 g_warning("can't change file mode\n");
2946 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2950 if (len == 0) break;
2951 last_linebreak = FALSE;
2953 if (buf[len - 1] != '\n') {
2954 last_linebreak = TRUE;
2955 r = fputs(buf, dest_fp);
2956 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2957 r = fputs(buf, dest_fp);
2960 r = fwrite(buf, 1, len - 1, dest_fp);
2965 r = fputs("\r\n", dest_fp);
2969 g_warning("writing to %s failed.\n", dest);
2977 if (last_linebreak == TRUE) {
2978 if (fputs("\r\n", dest_fp) == EOF)
2982 if (ferror(src_fp)) {
2983 FILE_OP_ERROR(src, "fgets");
2987 if (fclose(dest_fp) == EOF) {
2988 FILE_OP_ERROR(dest, "fclose");
3000 gint canonicalize_file_replace(const gchar *file)
3004 tmp_file = get_tmp_file();
3006 if (canonicalize_file(file, tmp_file) < 0) {
3011 if (move_file(tmp_file, file, TRUE) < 0) {
3012 g_warning("can't replace %s .\n", file);
3022 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3024 FILE *src_fp, *dest_fp;
3025 gchar buf[BUFFSIZE];
3026 gboolean err = FALSE;
3028 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3029 FILE_OP_ERROR(src, "fopen");
3033 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3034 FILE_OP_ERROR(dest, "fopen");
3039 if (change_file_mode_rw(dest_fp, dest) < 0) {
3040 FILE_OP_ERROR(dest, "chmod");
3041 g_warning("can't change file mode\n");
3044 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3046 if (fputs(buf, dest_fp) == EOF) {
3047 g_warning("writing to %s failed.\n", dest);
3055 if (ferror(src_fp)) {
3056 FILE_OP_ERROR(src, "fgets");
3060 if (fclose(dest_fp) == EOF) {
3061 FILE_OP_ERROR(dest, "fclose");
3073 gint uncanonicalize_file_replace(const gchar *file)
3077 tmp_file = get_tmp_file();
3079 if (uncanonicalize_file(file, tmp_file) < 0) {
3084 if (move_file(tmp_file, file, TRUE) < 0) {
3085 g_warning("can't replace %s .\n", file);
3095 gchar *normalize_newlines(const gchar *str)
3097 const gchar *p = str;
3100 out = outp = g_malloc(strlen(str) + 1);
3101 for (p = str; *p != '\0'; ++p) {
3103 if (*(p + 1) != '\n')
3114 gchar *get_outgoing_rfc2822_str(FILE *fp)
3116 gchar buf[BUFFSIZE];
3120 str = g_string_new(NULL);
3122 /* output header part */
3123 while (fgets(buf, sizeof(buf), fp) != NULL) {
3125 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3132 else if (next != ' ' && next != '\t') {
3136 if (fgets(buf, sizeof(buf), fp) == NULL)
3140 g_string_append(str, buf);
3141 g_string_append(str, "\r\n");
3147 /* output body part */
3148 while (fgets(buf, sizeof(buf), fp) != NULL) {
3151 g_string_append_c(str, '.');
3152 g_string_append(str, buf);
3153 g_string_append(str, "\r\n");
3157 g_string_free(str, FALSE);
3163 * Create a new boundary in a way that it is very unlikely that this
3164 * will occur in the following text. It would be easy to ensure
3165 * uniqueness if everything is either quoted-printable or base64
3166 * encoded (note that conversion is allowed), but because MIME bodies
3167 * may be nested, it may happen that the same boundary has already
3170 * boundary := 0*69<bchars> bcharsnospace
3171 * bchars := bcharsnospace / " "
3172 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3173 * "+" / "_" / "," / "-" / "." /
3174 * "/" / ":" / "=" / "?"
3176 * some special characters removed because of buggy MTAs
3179 gchar *generate_mime_boundary(const gchar *prefix)
3181 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3182 "abcdefghijklmnopqrstuvwxyz"
3187 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3188 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3191 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3195 gint change_file_mode_rw(FILE *fp, const gchar *file)
3198 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3200 return g_chmod(file, S_IRUSR|S_IWUSR);
3204 FILE *my_tmpfile(void)
3206 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3207 const gchar suffix[] = ".XXXXXX";
3208 const gchar *tmpdir;
3210 const gchar *progname;
3216 tmpdir = get_tmp_dir();
3217 tmplen = strlen(tmpdir);
3218 progname = g_get_prgname();
3219 if (progname == NULL)
3220 progname = "sylpheed-claws";
3221 proglen = strlen(progname);
3222 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3225 memcpy(fname, tmpdir, tmplen);
3226 fname[tmplen] = G_DIR_SEPARATOR;
3227 memcpy(fname + tmplen + 1, progname, proglen);
3228 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3230 fd = mkstemp(fname);
3238 fp = fdopen(fd, "w+b");
3243 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3248 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3252 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3253 dir, G_DIR_SEPARATOR);
3254 fd = mkstemp_name(template, filename);
3257 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3258 fd = mkstemp(*filename);
3260 return fdopen(fd, "w+");
3263 FILE *str_open_as_stream(const gchar *str)
3268 g_return_val_if_fail(str != NULL, NULL);
3272 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3277 if (len == 0) return fp;
3279 if (fwrite(str, 1, len, fp) != len) {
3280 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3289 gint str_write_to_file(const gchar *str, const gchar *file)
3294 g_return_val_if_fail(str != NULL, -1);
3295 g_return_val_if_fail(file != NULL, -1);
3297 if ((fp = g_fopen(file, "wb")) == NULL) {
3298 FILE_OP_ERROR(file, "fopen");
3308 if (fwrite(str, 1, len, fp) != len) {
3309 FILE_OP_ERROR(file, "fwrite");
3315 if (fclose(fp) == EOF) {
3316 FILE_OP_ERROR(file, "fclose");
3324 gchar *file_read_to_str(const gchar *file)
3329 g_return_val_if_fail(file != NULL, NULL);
3331 if ((fp = g_fopen(file, "rb")) == NULL) {
3332 FILE_OP_ERROR(file, "fopen");
3336 str = file_read_stream_to_str(fp);
3343 gchar *file_read_stream_to_str(FILE *fp)
3350 g_return_val_if_fail(fp != NULL, NULL);
3352 array = g_byte_array_new();
3354 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3355 if (n_read < sizeof(buf) && ferror(fp))
3357 g_byte_array_append(array, buf, n_read);
3361 FILE_OP_ERROR("file stream", "fread");
3362 g_byte_array_free(array, TRUE);
3367 g_byte_array_append(array, buf, 1);
3368 str = (gchar *)array->data;
3369 g_byte_array_free(array, FALSE);
3371 if (!g_utf8_validate(str, -1, NULL)) {
3372 const gchar *src_codeset, *dest_codeset;
3374 src_codeset = conv_get_locale_charset_str();
3375 dest_codeset = CS_UTF_8;
3376 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3384 gint execute_async(gchar *const argv[])
3386 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3388 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3389 NULL, NULL, NULL, FALSE) == FALSE) {
3390 g_warning("Can't execute command: %s\n", argv[0]);
3397 gint execute_sync(gchar *const argv[])
3401 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3403 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3404 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3405 g_warning("Can't execute command: %s\n", argv[0]);
3410 if (WIFEXITED(status))
3411 return WEXITSTATUS(status);
3419 gint execute_command_line(const gchar *cmdline, gboolean async)
3424 debug_print("execute_command_line(): executing: %s\n", cmdline);
3426 argv = strsplit_with_quote(cmdline, " ", 0);
3429 ret = execute_async(argv);
3431 ret = execute_sync(argv);
3438 gchar *get_command_output(const gchar *cmdline)
3440 gchar *child_stdout;
3443 g_return_val_if_fail(cmdline != NULL, NULL);
3445 debug_print("get_command_output(): executing: %s\n", cmdline);
3447 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3449 g_warning("Can't execute command: %s\n", cmdline);
3453 return child_stdout;
3456 static gint is_unchanged_uri_char(char c)
3468 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3474 for(i = 0; i < strlen(uri) ; i++) {
3475 if (is_unchanged_uri_char(uri[i])) {
3476 if (k + 2 >= bufsize)
3478 encoded_uri[k++] = uri[i];
3481 char * hexa = "0123456789ABCDEF";
3483 if (k + 4 >= bufsize)
3485 encoded_uri[k++] = '%';
3486 encoded_uri[k++] = hexa[uri[i] / 16];
3487 encoded_uri[k++] = hexa[uri[i] % 16];
3493 gint open_uri(const gchar *uri, const gchar *cmdline)
3495 gchar buf[BUFFSIZE];
3497 gchar encoded_uri[BUFFSIZE];
3499 g_return_val_if_fail(uri != NULL, -1);
3501 /* an option to choose whether to use encode_uri or not ? */
3502 encode_uri(encoded_uri, BUFFSIZE, uri);
3505 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3506 !strchr(p + 2, '%'))
3507 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3510 g_warning("Open URI command line is invalid "
3511 "(there must be only one '%%s'): %s",
3513 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3516 execute_command_line(buf, TRUE);
3521 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3523 gchar buf[BUFFSIZE];
3526 g_return_val_if_fail(filepath != NULL, -1);
3529 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3530 !strchr(p + 2, '%'))
3531 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3534 g_warning("Open Text Editor command line is invalid "
3535 "(there must be only one '%%s'): %s",
3537 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3540 execute_command_line(buf, TRUE);
3545 time_t remote_tzoffset_sec(const gchar *zone)
3547 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3553 time_t remoteoffset;
3555 strncpy(zone3, zone, 3);
3559 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3560 (c == '+' || c == '-')) {
3561 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3563 remoteoffset = -remoteoffset;
3564 } else if (!strncmp(zone, "UT" , 2) ||
3565 !strncmp(zone, "GMT", 2)) {
3567 } else if (strlen(zone3) == 3) {
3568 for (p = ustzstr; *p != '\0'; p += 3) {
3569 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3570 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3571 remoteoffset = iustz * 3600;
3577 } else if (strlen(zone3) == 1) {
3579 case 'Z': remoteoffset = 0; break;
3580 case 'A': remoteoffset = -1; break;
3581 case 'B': remoteoffset = -2; break;
3582 case 'C': remoteoffset = -3; break;
3583 case 'D': remoteoffset = -4; break;
3584 case 'E': remoteoffset = -5; break;
3585 case 'F': remoteoffset = -6; break;
3586 case 'G': remoteoffset = -7; break;
3587 case 'H': remoteoffset = -8; break;
3588 case 'I': remoteoffset = -9; break;
3589 case 'K': remoteoffset = -10; break; /* J is not used */
3590 case 'L': remoteoffset = -11; break;
3591 case 'M': remoteoffset = -12; break;
3592 case 'N': remoteoffset = 1; break;
3593 case 'O': remoteoffset = 2; break;
3594 case 'P': remoteoffset = 3; break;
3595 case 'Q': remoteoffset = 4; break;
3596 case 'R': remoteoffset = 5; break;
3597 case 'S': remoteoffset = 6; break;
3598 case 'T': remoteoffset = 7; break;
3599 case 'U': remoteoffset = 8; break;
3600 case 'V': remoteoffset = 9; break;
3601 case 'W': remoteoffset = 10; break;
3602 case 'X': remoteoffset = 11; break;
3603 case 'Y': remoteoffset = 12; break;
3604 default: remoteoffset = 0; break;
3606 remoteoffset = remoteoffset * 3600;
3610 return remoteoffset;
3613 time_t tzoffset_sec(time_t *now)
3619 lt = localtime(now);
3621 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3623 if (lt->tm_year < gmt.tm_year)
3625 else if (lt->tm_year > gmt.tm_year)
3627 else if (lt->tm_yday < gmt.tm_yday)
3629 else if (lt->tm_yday > gmt.tm_yday)
3632 if (off >= 24 * 60) /* should be impossible */
3633 off = 23 * 60 + 59; /* if not, insert silly value */
3634 if (off <= -24 * 60)
3635 off = -(23 * 60 + 59);
3640 /* calculate timezone offset */
3641 gchar *tzoffset(time_t *now)
3643 static gchar offset_string[6];
3649 lt = localtime(now);
3651 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3653 if (lt->tm_year < gmt.tm_year)
3655 else if (lt->tm_year > gmt.tm_year)
3657 else if (lt->tm_yday < gmt.tm_yday)
3659 else if (lt->tm_yday > gmt.tm_yday)
3667 if (off >= 24 * 60) /* should be impossible */
3668 off = 23 * 60 + 59; /* if not, insert silly value */
3670 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3672 return offset_string;
3675 void get_rfc822_date(gchar *buf, gint len)
3679 gchar day[4], mon[4];
3680 gint dd, hh, mm, ss, yyyy;
3685 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3686 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3687 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3688 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3691 /* just a wrapper to suppress the warning of gcc about %c */
3692 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3693 const struct tm *tm)
3695 return strftime(s, max, format, tm);
3698 void debug_set_mode(gboolean mode)
3703 gboolean debug_get_mode(void)
3708 void debug_print_real(const gchar *format, ...)
3711 gchar buf[BUFFSIZE];
3713 if (!debug_mode) return;
3715 va_start(args, format);
3716 g_vsnprintf(buf, sizeof(buf), format, args);
3723 const char * debug_srcname(const char *file)
3725 const char *s = strrchr (file, '/');
3730 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3732 if (subject == NULL)
3735 subject += subject_get_prefix_length(subject);
3737 return g_hash_table_lookup(subject_table, subject);
3740 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3743 if (subject == NULL || *subject == 0)
3745 subject += subject_get_prefix_length(subject);
3746 g_hash_table_insert(subject_table, subject, data);
3749 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3751 if (subject == NULL)
3754 subject += subject_get_prefix_length(subject);
3755 g_hash_table_remove(subject_table, subject);
3759 *\brief Check if a string is prefixed with known (combinations)
3760 * of prefixes. The function assumes that each prefix
3761 * is terminated by zero or exactly _one_ space.
3763 *\param str String to check for a prefixes
3765 *\return int Number of chars in the prefix that should be skipped
3766 * for a "clean" subject line. If no prefix was found, 0
3769 int subject_get_prefix_length(const gchar *subject)
3771 /*!< Array with allowable reply prefixes regexps. */
3772 static const gchar * const prefixes[] = {
3773 "Re\\:", /* "Re:" */
3774 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3775 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3776 "Aw\\:", /* "Aw:" (German) */
3777 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3778 "Res\\:", /* "Res:" (Brazilian Outlook) */
3779 "Fw\\:", /* "Fw:" Forward */
3780 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3781 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3782 "Rif\\:" /* "Rif:" (Italian Outlook) */
3785 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3788 static regex_t regex;
3789 static gboolean init_;
3791 if (!subject) return 0;
3792 if (!*subject) return 0;
3795 GString *s = g_string_new("");
3797 for (n = 0; n < PREFIXES; n++)
3798 /* Terminate each prefix regexpression by a
3799 * "\ ?" (zero or ONE space), and OR them */
3800 g_string_append_printf(s, "(%s\\ ?)%s",
3805 g_string_prepend(s, "(");
3806 g_string_append(s, ")+"); /* match at least once */
3807 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3810 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3811 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3812 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3813 debug_print("Error compiling regexp %s\n", s->str);
3814 g_string_free(s, TRUE);
3818 g_string_free(s, TRUE);
3822 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3828 guint g_stricase_hash(gconstpointer gptr)
3830 guint hash_result = 0;
3833 for (str = gptr; str && *str; str++) {
3834 if (isupper((guchar)*str)) hash_result += (*str + ' ');
3835 else hash_result += *str;
3841 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3843 const char *str1 = gptr1;
3844 const char *str2 = gptr2;
3846 return !g_utf8_collate(str1, str2);
3849 gint g_int_compare(gconstpointer a, gconstpointer b)
3851 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3854 gchar *generate_msgid(gchar *buf, gint len)
3863 addr = g_strconcat("@", get_domain_name(), NULL);
3865 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3866 lt->tm_year + 1900, lt->tm_mon + 1,
3867 lt->tm_mday, lt->tm_hour,
3868 lt->tm_min, lt->tm_sec,
3869 (guint) rand(), addr);
3876 quote_cmd_argument()
3878 return a quoted string safely usable in argument of a command.
3880 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3883 gint quote_cmd_argument(gchar * result, guint size,
3893 for(p = path ; * p != '\0' ; p ++) {
3895 if (isalnum((guchar)*p) || (* p == '/')) {
3896 if (remaining > 0) {
3902 result[size - 1] = '\0';
3907 if (remaining >= 2) {
3915 result[size - 1] = '\0';
3920 if (remaining > 0) {
3924 result[size - 1] = '\0';
3938 static void g_node_map_recursive(GNode *node, gpointer data)
3940 GNodeMapData *mapdata = (GNodeMapData *) data;
3942 GNodeMapData newmapdata;
3945 newdata = mapdata->func(node->data, mapdata->data);
3946 if (newdata != NULL) {
3947 newnode = g_node_new(newdata);
3948 g_node_append(mapdata->parent, newnode);
3950 newmapdata.parent = newnode;
3951 newmapdata.func = mapdata->func;
3952 newmapdata.data = mapdata->data;
3954 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3958 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3961 GNodeMapData mapdata;
3963 g_return_val_if_fail(node != NULL, NULL);
3964 g_return_val_if_fail(func != NULL, NULL);
3966 root = g_node_new(func(node->data, data));
3968 mapdata.parent = root;
3969 mapdata.func = func;
3970 mapdata.data = data;
3972 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3977 #define HEX_TO_INT(val, hex) \
3981 if ('0' <= c && c <= '9') { \
3983 } else if ('a' <= c && c <= 'f') { \
3984 val = c - 'a' + 10; \
3985 } else if ('A' <= c && c <= 'F') { \
3986 val = c - 'A' + 10; \
3992 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3999 if (hi == -1 || lo == -1)
4002 *out = (hi << 4) + lo;
4006 #define INT_TO_HEX(hex, val) \
4009 hex = '0' + (val); \
4011 hex = 'A' + (val) - 10; \
4014 void get_hex_str(gchar *out, guchar ch)
4018 INT_TO_HEX(hex, ch >> 4);
4020 INT_TO_HEX(hex, ch & 0x0f);
4026 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4028 #define G_PRINT_REF g_print
4032 *\brief Register ref counted pointer. It is based on GBoxed, so should
4033 * work with anything that uses the GType system. The semantics
4034 * are similar to a C++ auto pointer, with the exception that
4035 * C doesn't have automatic closure (calling destructors) when
4036 * exiting a block scope.
4037 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4038 * function directly.
4040 *\return GType A GType type.
4042 GType g_auto_pointer_register(void)
4044 static GType auto_pointer_type;
4045 if (!auto_pointer_type)
4047 g_boxed_type_register_static
4048 ("G_TYPE_AUTO_POINTER",
4049 (GBoxedCopyFunc) g_auto_pointer_copy,
4050 (GBoxedFreeFunc) g_auto_pointer_free);
4051 return auto_pointer_type;
4055 *\brief Structure with g_new() allocated pointer guarded by the
4058 typedef struct AutoPointerRef {
4059 void (*free) (gpointer);
4065 *\brief The auto pointer opaque structure that references the
4066 * pointer guard block.
4068 typedef struct AutoPointer {
4069 AutoPointerRef *ref;
4070 gpointer ptr; /*!< access to protected pointer */
4074 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4078 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4079 * ... when assigning, copying and freeing storage elements
4081 * gtk_list_store_new(N_S_COLUMNS,
4082 * G_TYPE_AUTO_POINTER,
4086 * Template *precious_data = g_new0(Template, 1);
4087 * g_pointer protect = g_auto_pointer_new(precious_data);
4089 * gtk_list_store_set(container, &iter,
4093 * ... the gtk_list_store has copied the pointer and
4094 * ... incremented its reference count, we should free
4095 * ... the auto pointer (in C++ a destructor would do
4096 * ... this for us when leaving block scope)
4098 * g_auto_pointer_free(protect);
4100 * ... gtk_list_store_set() now manages the data. When
4101 * ... *explicitly* requesting a pointer from the list
4102 * ... store, don't forget you get a copy that should be
4103 * ... freed with g_auto_pointer_free() eventually.
4107 *\param pointer Pointer to be guarded.
4109 *\return GAuto * Pointer that should be used in containers with
4112 GAuto *g_auto_pointer_new(gpointer p)
4114 AutoPointerRef *ref;
4120 ref = g_new0(AutoPointerRef, 1);
4121 ptr = g_new0(AutoPointer, 1);
4131 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4137 *\brief Allocate an autopointer using the passed \a free function to
4138 * free the guarded pointer
4140 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4147 aptr = g_auto_pointer_new(p);
4148 aptr->ref->free = free_;
4152 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4154 if (auto_ptr == NULL)
4156 return ((AutoPointer *) auto_ptr)->ptr;
4160 *\brief Copies an auto pointer by. It's mostly not necessary
4161 * to call this function directly, unless you copy/assign
4162 * the guarded pointer.
4164 *\param auto_ptr Auto pointer returned by previous call to
4165 * g_auto_pointer_new_XXX()
4167 *\return gpointer An auto pointer
4169 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4172 AutoPointerRef *ref;
4175 if (auto_ptr == NULL)
4180 newp = g_new0(AutoPointer, 1);
4183 newp->ptr = ref->pointer;
4187 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4193 *\brief Free an auto pointer
4195 void g_auto_pointer_free(GAuto *auto_ptr)
4198 AutoPointerRef *ref;
4200 if (auto_ptr == NULL)
4206 if (--(ref->cnt) == 0) {
4208 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4210 ref->free(ref->pointer);
4215 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4220 void replace_returns(gchar *str)
4225 while (strstr(str, "\n")) {
4226 *strstr(str, "\n") = ' ';
4228 while (strstr(str, "\r")) {
4229 *strstr(str, "\r") = ' ';
4233 /* get_uri_part() - retrieves a URI starting from scanpos.
4234 Returns TRUE if succesful */
4235 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4236 const gchar **bp, const gchar **ep, gboolean hdr)
4239 gint parenthese_cnt = 0;
4241 g_return_val_if_fail(start != NULL, FALSE);
4242 g_return_val_if_fail(scanpos != NULL, FALSE);
4243 g_return_val_if_fail(bp != NULL, FALSE);
4244 g_return_val_if_fail(ep != NULL, FALSE);
4248 /* find end point of URI */
4249 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4250 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4251 !IS_ASCII(*(const guchar *)ep_) ||
4252 strchr("[]{}<>\"", *ep_)) {
4254 } else if (strchr("(", *ep_)) {
4256 } else if (strchr(")", *ep_)) {
4257 if (parenthese_cnt > 0)
4264 /* no punctuation at end of string */
4266 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4267 * should pass some URI type to this function and decide on that whether
4268 * to perform punctuation stripping */
4270 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4272 for (; ep_ - 1 > scanpos + 1 &&
4273 IS_REAL_PUNCT(*(ep_ - 1));
4277 #undef IS_REAL_PUNCT
4284 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4286 while (bp && *bp && g_ascii_isspace(*bp))
4288 return g_strndup(bp, ep - bp);
4291 /* valid mail address characters */
4292 #define IS_RFC822_CHAR(ch) \
4296 !g_ascii_isspace(ch) && \
4297 !strchr("(),;<>\"", (ch)))
4299 /* alphabet and number within 7bit ASCII */
4300 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4301 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4303 static GHashTable *create_domain_tab(void)
4305 static const gchar *toplvl_domains [] = {
4307 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4308 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4309 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4310 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4311 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4312 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4313 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4314 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4315 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4316 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4317 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4318 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4319 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4320 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4321 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4322 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4323 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4324 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4325 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4326 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4327 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4328 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4329 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4330 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4331 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4332 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4335 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4337 g_return_val_if_fail(htab, NULL);
4338 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4339 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4343 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4345 const gint MAX_LVL_DOM_NAME_LEN = 6;
4346 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4347 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4350 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4353 for (p = buf; p < m && first < last; *p++ = *first++)
4357 return g_hash_table_lookup(tab, buf) != NULL;
4360 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4361 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4362 const gchar **bp, const gchar **ep, gboolean hdr)
4364 /* more complex than the uri part because we need to scan back and forward starting from
4365 * the scan position. */
4366 gboolean result = FALSE;
4367 const gchar *bp_ = NULL;
4368 const gchar *ep_ = NULL;
4369 static GHashTable *dom_tab;
4370 const gchar *last_dot = NULL;
4371 const gchar *prelast_dot = NULL;
4372 const gchar *last_tld_char = NULL;
4374 /* the informative part of the email address (describing the name
4375 * of the email address owner) may contain quoted parts. the
4376 * closure stack stores the last encountered quotes. */
4377 gchar closure_stack[128];
4378 gchar *ptr = closure_stack;
4380 g_return_val_if_fail(start != NULL, FALSE);
4381 g_return_val_if_fail(scanpos != NULL, FALSE);
4382 g_return_val_if_fail(bp != NULL, FALSE);
4383 g_return_val_if_fail(ep != NULL, FALSE);
4386 const gchar *start_quote = NULL;
4387 const gchar *end_quote = NULL;
4389 /* go to the real start */
4390 if (start[0] == ',')
4392 if (start[0] == ';')
4394 while (start[0] == '\n' || start[0] == '\r')
4396 while (start[0] == ' ' || start[0] == '\t')
4401 /* check if there are quotes (to skip , in them) */
4402 if (*start == '"') {
4403 start_quote = start;
4405 end_quote = strstr(start, "\"");
4411 /* skip anything between quotes */
4412 if (start_quote && end_quote) {
4417 /* find end (either , or ; or end of line) */
4418 if (strstr(start, ",") && strstr(start, ";"))
4419 *ep = strstr(start,",") < strstr(start, ";")
4420 ? strstr(start, ",") : strstr(start, ";");
4421 else if (strstr(start, ","))
4422 *ep = strstr(start, ",");
4423 else if (strstr(start, ";"))
4424 *ep = strstr(start, ";");
4426 *ep = start+strlen(start);
4428 /* go back to real start */
4429 if (start_quote && end_quote) {
4430 start = start_quote;
4433 /* check there's still an @ in that, or search
4434 * further if possible */
4435 if (strstr(start, "@") && strstr(start, "@") < *ep)
4437 else if (*ep < start+strlen(start)) {
4440 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4448 dom_tab = create_domain_tab();
4449 g_return_val_if_fail(dom_tab, FALSE);
4451 /* scan start of address */
4452 for (bp_ = scanpos - 1;
4453 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4456 /* TODO: should start with an alnum? */
4458 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4461 if (bp_ != scanpos) {
4462 /* scan end of address */
4463 for (ep_ = scanpos + 1;
4464 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4466 prelast_dot = last_dot;
4468 if (*(last_dot + 1) == '.') {
4469 if (prelast_dot == NULL)
4471 last_dot = prelast_dot;
4476 /* TODO: really should terminate with an alnum? */
4477 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4482 if (last_dot == NULL)
4484 if (last_dot >= ep_)
4485 last_dot = prelast_dot;
4486 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4490 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4491 if (*last_tld_char == '?')
4494 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4501 if (!result) return FALSE;
4503 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4504 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4505 && IS_RFC822_CHAR(*(ep_ + 3))) {
4506 /* this informative part with an @ in it is
4507 * followed by the email address */
4510 /* go to matching '>' (or next non-rfc822 char, like \n) */
4511 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4514 /* include the bracket */
4515 if (*ep_ == '>') ep_++;
4517 /* include the leading quote */
4525 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4526 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4529 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4530 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4533 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4534 #define IN_STACK() (ptr > closure_stack)
4535 /* has underrun check */
4536 #define POP_STACK() if(IN_STACK()) --ptr
4537 /* has overrun check */
4538 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4539 /* has underrun check */
4540 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4544 /* scan for the informative part. */
4545 for (bp_ -= 2; bp_ >= start; bp_--) {
4546 /* if closure on the stack keep scanning */
4547 if (PEEK_STACK() == *bp_) {
4551 if (*bp_ == '\'' || *bp_ == '"') {
4556 /* if nothing in the closure stack, do the special conditions
4557 * the following if..else expression simply checks whether
4558 * a token is acceptable. if not acceptable, the clause
4559 * should terminate the loop with a 'break' */
4560 if (!PEEK_STACK()) {
4562 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4563 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4564 /* hyphens are allowed, but only in
4566 } else if (strchr(" \"'", *bp_)) {
4567 /* but anything not being a punctiation
4570 break; /* anything else is rejected */
4577 /* scan forward (should start with an alnum) */
4578 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4594 #undef IS_ASCII_ALNUM
4595 #undef IS_RFC822_CHAR
4597 gchar *make_email_string(const gchar *bp, const gchar *ep)
4599 /* returns a mailto: URI; mailto: is also used to detect the
4600 * uri type later on in the button_pressed signal handler */
4604 tmp = g_strndup(bp, ep - bp);
4605 result = g_strconcat("mailto:", tmp, NULL);
4611 gchar *make_http_string(const gchar *bp, const gchar *ep)
4613 /* returns an http: URI; */
4617 while (bp && *bp && g_ascii_isspace(*bp))
4619 tmp = g_strndup(bp, ep - bp);
4620 result = g_strconcat("http://", tmp, NULL);
4626 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4628 FILE *fp = fopen(path, "rb");
4629 gchar buf[BUFFSIZE];
4630 gchar *result = NULL;
4633 while (fgets(buf, sizeof (buf), fp) != NULL) {
4634 gchar **parts = g_strsplit(buf, ";", -1);
4635 gchar *trimmed = parts[0];
4636 while (trimmed[0] == ' ')
4638 while (trimmed[strlen(trimmed)-1] == ' ')
4639 trimmed[strlen(trimmed)-1] = '\0';
4641 if (!strcmp(trimmed, type)) {
4643 while (trimmed[0] == ' ')
4645 while (trimmed[strlen(trimmed)-1] == ' ')
4646 trimmed[strlen(trimmed)-1] = '\0';
4647 while (trimmed[strlen(trimmed)-1] == '\n')
4648 trimmed[strlen(trimmed)-1] = '\0';
4649 while (trimmed[strlen(trimmed)-1] == '\r')
4650 trimmed[strlen(trimmed)-1] = '\0';
4651 result = g_strdup(trimmed);
4654 if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4655 gchar *start = g_strdup(result);
4656 gchar *end = g_strdup(strstr(result, "%s")+2);
4658 *strstr(start, "%s") = '\0';
4659 tmp = g_strconcat(start,"'%s'",end, NULL);
4672 gchar *mailcap_get_command_for_type(const gchar *type)
4674 gchar *result = NULL;
4676 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4677 result = mailcap_get_command_in_file(path, type);
4681 result = mailcap_get_command_in_file("/etc/mailcap", type);
4685 gint copy_dir(const gchar *src, const gchar *dst)
4690 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4691 g_warning("failed to open directory: %s\n", src);
4695 if (make_dir(dst) < 0)
4698 while ((name = g_dir_read_name(dir)) != NULL) {
4699 gchar *old_file, *new_file;
4700 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4701 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4702 debug_print("copying: %s -> %s\n", old_file, new_file);
4703 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4704 gint r = copy_file(old_file, new_file, TRUE);
4707 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4708 gint r = copy_dir(old_file, new_file);
4716 /* crude test to see if a file is an email. */
4717 gboolean file_is_email (const gchar *filename)
4723 if (filename == NULL)
4725 if ((fp = g_fopen(filename, "rb")) == NULL)
4727 while (i < 60 && score < 3
4728 && fgets(buffer, sizeof (buffer), fp) > 0) {
4729 if (!strncmp(buffer, "From:", strlen("From:")))
4731 if (!strncmp(buffer, "To:", strlen("To:")))
4733 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4738 return (score >= 3);