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);
1987 /* Returns a static string with the directroy from which the module
1988 has been loaded. Returns an empty string on error. */
1989 static char *w32_get_module_dir(void)
1991 static char *moddir;
1994 char name[MAX_PATH+10];
1997 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
2000 p = strrchr (name, '\\');
2006 moddir = g_strdup (name);
2010 #endif /* G_OS_WIN32 */
2012 /* Return a static string with the locale dir. */
2013 const gchar *get_locale_dir(void)
2015 static gchar *loc_dir;
2019 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
2020 "\\share\\locale", NULL);
2023 loc_dir = LOCALEDIR;
2029 const gchar *get_home_dir(void)
2032 static char home_dir[MAX_PATH] = "";
2034 if (home_dir[0] == '\0') {
2035 if (w32_shgetfolderpath
2036 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
2037 NULL, 0, home_dir) < 0)
2038 strcpy (home_dir, "C:\\Sylpheed");
2042 static const gchar *homeenv = NULL;
2047 if (!homeenv && g_getenv("HOME") != NULL)
2048 homeenv = g_strdup(g_getenv("HOME"));
2050 homeenv = g_get_home_dir();
2056 const gchar *get_rc_dir(void)
2058 static gchar *rc_dir = NULL;
2061 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2067 const gchar *get_mail_base_dir(void)
2070 static gchar *mail_base_dir = NULL;
2073 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2076 return mail_base_dir;
2078 return get_home_dir();
2082 const gchar *get_news_cache_dir(void)
2084 static gchar *news_cache_dir = NULL;
2086 if (!news_cache_dir)
2087 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2088 NEWS_CACHE_DIR, NULL);
2090 return news_cache_dir;
2093 const gchar *get_imap_cache_dir(void)
2095 static gchar *imap_cache_dir = NULL;
2097 if (!imap_cache_dir)
2098 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2099 IMAP_CACHE_DIR, NULL);
2101 return imap_cache_dir;
2104 const gchar *get_mbox_cache_dir(void)
2106 static gchar *mbox_cache_dir = NULL;
2108 if (!mbox_cache_dir)
2109 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2110 MBOX_CACHE_DIR, NULL);
2112 return mbox_cache_dir;
2115 const gchar *get_mime_tmp_dir(void)
2117 static gchar *mime_tmp_dir = NULL;
2120 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2121 MIME_TMP_DIR, NULL);
2123 return mime_tmp_dir;
2126 const gchar *get_template_dir(void)
2128 static gchar *template_dir = NULL;
2131 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2132 TEMPLATE_DIR, NULL);
2134 return template_dir;
2137 const gchar *get_header_cache_dir(void)
2139 static gchar *header_dir = NULL;
2142 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2143 HEADER_CACHE_DIR, NULL);
2148 /* Return the default directory for Plugins. */
2149 const gchar *get_plugin_dir(void)
2152 static gchar *plugin_dir = NULL;
2155 plugin_dir = g_strconcat(w32_get_module_dir(),
2156 "\\lib\\sylpheed-claws\\plugins\\",
2160 if (is_dir_exist(PLUGINDIR))
2163 static gchar *plugin_dir = NULL;
2165 plugin_dir = g_strconcat(get_rc_dir(),
2166 G_DIR_SEPARATOR_S, "plugins",
2167 G_DIR_SEPARATOR_S, NULL);
2173 const gchar *get_tmp_dir(void)
2175 static gchar *tmp_dir = NULL;
2178 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2184 gchar *get_tmp_file(void)
2187 static guint32 id = 0;
2189 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2190 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2195 const gchar *get_domain_name(void)
2198 static gchar *domain_name = NULL;
2204 if (uname(&uts) < 0) {
2206 domain_name = "unknown";
2208 if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
2209 perror("gethostbyname");
2210 domain_name = g_strdup(uts.nodename);
2212 domain_name = g_strdup(hp->h_name);
2216 debug_print("domain name = %s\n", domain_name);
2225 off_t get_file_size(const gchar *file)
2229 if (g_stat(file, &s) < 0) {
2230 FILE_OP_ERROR(file, "stat");
2237 off_t get_file_size_as_crlf(const gchar *file)
2241 gchar buf[BUFFSIZE];
2243 if ((fp = g_fopen(file, "rb")) == NULL) {
2244 FILE_OP_ERROR(file, "fopen");
2248 while (fgets(buf, sizeof(buf), fp) != NULL) {
2250 size += strlen(buf) + 2;
2254 FILE_OP_ERROR(file, "fgets");
2263 off_t get_left_file_size(FILE *fp)
2269 if ((pos = ftell(fp)) < 0) {
2273 if (fseek(fp, 0L, SEEK_END) < 0) {
2277 if ((end = ftell(fp)) < 0) {
2282 if (fseek(fp, pos, SEEK_SET) < 0) {
2290 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2297 if (g_stat(file, &s) < 0) {
2298 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2302 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2309 /* Test on whether FILE is a relative file name. This is
2310 * straightforward for Unix but more complex for Windows. */
2311 gboolean is_relative_filename(const gchar *file)
2316 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2317 return FALSE; /* Prefixed with a hostname - this can't
2318 * be a relative name. */
2320 if ( ((*file >= 'a' && *file <= 'z')
2321 || (*file >= 'A' && *file <= 'Z'))
2323 file += 2; /* Skip drive letter. */
2325 return !(*file == '\\' || *file == '/');
2327 return !(*file == G_DIR_SEPARATOR);
2332 gboolean is_dir_exist(const gchar *dir)
2337 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2340 gboolean is_file_entry_exist(const gchar *file)
2345 return g_file_test(file, G_FILE_TEST_EXISTS);
2348 gboolean dirent_is_regular_file(struct dirent *d)
2350 #ifdef HAVE_DIRENT_D_TYPE
2351 if (d->d_type == DT_REG)
2353 else if (d->d_type != DT_UNKNOWN)
2357 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2360 gboolean dirent_is_directory(struct dirent *d)
2362 #ifdef HAVE_DIRENT_D_TYPE
2363 if (d->d_type == DT_DIR)
2365 else if (d->d_type != DT_UNKNOWN)
2369 return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2372 gint change_dir(const gchar *dir)
2374 gchar *prevdir = NULL;
2377 prevdir = g_get_current_dir();
2379 if (g_chdir(dir) < 0) {
2380 FILE_OP_ERROR(dir, "chdir");
2381 if (debug_mode) g_free(prevdir);
2383 } else if (debug_mode) {
2386 cwd = g_get_current_dir();
2387 if (strcmp(prevdir, cwd) != 0)
2388 g_print("current dir: %s\n", cwd);
2396 gint make_dir(const gchar *dir)
2398 if (g_mkdir(dir, S_IRWXU) < 0) {
2399 FILE_OP_ERROR(dir, "mkdir");
2402 if (g_chmod(dir, S_IRWXU) < 0)
2403 FILE_OP_ERROR(dir, "chmod");
2408 gint make_dir_hier(const gchar *dir)
2413 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2414 parent_dir = g_strndup(dir, p - dir);
2415 if (*parent_dir != '\0') {
2416 if (!is_dir_exist(parent_dir)) {
2417 if (make_dir(parent_dir) < 0) {
2426 if (!is_dir_exist(dir)) {
2427 if (make_dir(dir) < 0)
2434 gint remove_all_files(const gchar *dir)
2437 const gchar *dir_name;
2440 prev_dir = g_get_current_dir();
2442 if (g_chdir(dir) < 0) {
2443 FILE_OP_ERROR(dir, "chdir");
2448 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2449 g_warning("failed to open directory: %s\n", dir);
2454 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2455 if (g_unlink(dir_name) < 0)
2456 FILE_OP_ERROR(dir_name, "unlink");
2461 if (g_chdir(prev_dir) < 0) {
2462 FILE_OP_ERROR(prev_dir, "chdir");
2472 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2475 const gchar *dir_name;
2479 prev_dir = g_get_current_dir();
2481 if (g_chdir(dir) < 0) {
2482 FILE_OP_ERROR(dir, "chdir");
2487 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2488 g_warning("failed to open directory: %s\n", dir);
2493 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2494 file_no = to_number(dir_name);
2495 if (file_no > 0 && first <= file_no && file_no <= last) {
2496 if (is_dir_exist(dir_name))
2498 if (g_unlink(dir_name) < 0)
2499 FILE_OP_ERROR(dir_name, "unlink");
2505 if (g_chdir(prev_dir) < 0) {
2506 FILE_OP_ERROR(prev_dir, "chdir");
2516 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2519 const gchar *dir_name;
2523 prev_dir = g_get_current_dir();
2525 if (g_chdir(dir) < 0) {
2526 FILE_OP_ERROR(dir, "chdir");
2531 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2532 FILE_OP_ERROR(dir, "opendir");
2537 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2538 file_no = to_number(dir_name);
2539 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2540 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2541 if (is_dir_exist(dir_name))
2543 if (g_unlink(dir_name) < 0)
2544 FILE_OP_ERROR(dir_name, "unlink");
2550 if (g_chdir(prev_dir) < 0) {
2551 FILE_OP_ERROR(prev_dir, "chdir");
2561 gint remove_all_numbered_files(const gchar *dir)
2563 return remove_numbered_files(dir, 0, UINT_MAX);
2566 gint remove_expired_files(const gchar *dir, guint hours)
2569 const gchar *dir_name;
2573 time_t mtime, now, expire_time;
2575 prev_dir = g_get_current_dir();
2577 if (g_chdir(dir) < 0) {
2578 FILE_OP_ERROR(dir, "chdir");
2583 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2584 g_warning("failed to open directory: %s\n", dir);
2590 expire_time = hours * 60 * 60;
2592 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2593 file_no = to_number(dir_name);
2595 if (g_stat(dir_name, &s) < 0) {
2596 FILE_OP_ERROR(dir_name, "stat");
2599 if (S_ISDIR(s.st_mode))
2601 mtime = MAX(s.st_mtime, s.st_atime);
2602 if (now - mtime > expire_time) {
2603 if (g_unlink(dir_name) < 0)
2604 FILE_OP_ERROR(dir_name, "unlink");
2611 if (g_chdir(prev_dir) < 0) {
2612 FILE_OP_ERROR(prev_dir, "chdir");
2622 gint remove_dir_recursive(const gchar *dir)
2626 const gchar *dir_name;
2629 if (g_stat(dir, &s) < 0) {
2630 FILE_OP_ERROR(dir, "stat");
2631 if (ENOENT == errno) return 0;
2635 if (!S_ISDIR(s.st_mode)) {
2636 if (g_unlink(dir) < 0) {
2637 FILE_OP_ERROR(dir, "unlink");
2644 prev_dir = g_get_current_dir();
2645 /* g_print("prev_dir = %s\n", prev_dir); */
2647 if (!path_cmp(prev_dir, dir)) {
2649 if (g_chdir("..") < 0) {
2650 FILE_OP_ERROR(dir, "chdir");
2653 prev_dir = g_get_current_dir();
2656 if (g_chdir(dir) < 0) {
2657 FILE_OP_ERROR(dir, "chdir");
2662 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2663 g_warning("failed to open directory: %s\n", dir);
2669 /* remove all files in the directory */
2670 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2671 /* g_print("removing %s\n", dir_name); */
2673 if (is_dir_exist(dir_name)) {
2674 if (remove_dir_recursive(dir_name) < 0) {
2675 g_warning("can't remove directory\n");
2679 if (g_unlink(dir_name) < 0)
2680 FILE_OP_ERROR(dir_name, "unlink");
2686 if (g_chdir(prev_dir) < 0) {
2687 FILE_OP_ERROR(prev_dir, "chdir");
2694 if (g_rmdir(dir) < 0) {
2695 FILE_OP_ERROR(dir, "rmdir");
2702 gint rename_force(const gchar *oldpath, const gchar *newpath)
2705 if (!is_file_entry_exist(oldpath)) {
2709 if (is_file_exist(newpath)) {
2710 if (g_unlink(newpath) < 0)
2711 FILE_OP_ERROR(newpath, "unlink");
2714 return g_rename(oldpath, newpath);
2718 * Append src file body to the tail of dest file.
2719 * Now keep_backup has no effects.
2721 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2723 FILE *src_fp, *dest_fp;
2727 gboolean err = FALSE;
2729 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2730 FILE_OP_ERROR(src, "fopen");
2734 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2735 FILE_OP_ERROR(dest, "fopen");
2740 if (change_file_mode_rw(dest_fp, dest) < 0) {
2741 FILE_OP_ERROR(dest, "chmod");
2742 g_warning("can't change file mode\n");
2745 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2746 if (n_read < sizeof(buf) && ferror(src_fp))
2748 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2749 g_warning("writing to %s failed.\n", dest);
2757 if (ferror(src_fp)) {
2758 FILE_OP_ERROR(src, "fread");
2762 if (fclose(dest_fp) == EOF) {
2763 FILE_OP_ERROR(dest, "fclose");
2775 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2777 FILE *src_fp, *dest_fp;
2780 gchar *dest_bak = NULL;
2781 gboolean err = FALSE;
2783 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2784 FILE_OP_ERROR(src, "fopen");
2787 if (is_file_exist(dest)) {
2788 dest_bak = g_strconcat(dest, ".bak", NULL);
2789 if (rename_force(dest, dest_bak) < 0) {
2790 FILE_OP_ERROR(dest, "rename");
2797 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2798 FILE_OP_ERROR(dest, "fopen");
2801 if (rename_force(dest_bak, dest) < 0)
2802 FILE_OP_ERROR(dest_bak, "rename");
2808 if (change_file_mode_rw(dest_fp, dest) < 0) {
2809 FILE_OP_ERROR(dest, "chmod");
2810 g_warning("can't change file mode\n");
2813 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2814 if (n_read < sizeof(buf) && ferror(src_fp))
2816 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2817 g_warning("writing to %s failed.\n", dest);
2822 if (rename_force(dest_bak, dest) < 0)
2823 FILE_OP_ERROR(dest_bak, "rename");
2830 if (ferror(src_fp)) {
2831 FILE_OP_ERROR(src, "fread");
2835 if (fclose(dest_fp) == EOF) {
2836 FILE_OP_ERROR(dest, "fclose");
2843 if (rename_force(dest_bak, dest) < 0)
2844 FILE_OP_ERROR(dest_bak, "rename");
2850 if (keep_backup == FALSE && dest_bak)
2858 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2860 if (overwrite == FALSE && is_file_exist(dest)) {
2861 g_warning("move_file(): file %s already exists.", dest);
2865 if (rename_force(src, dest) == 0) return 0;
2867 if (EXDEV != errno) {
2868 FILE_OP_ERROR(src, "rename");
2872 if (copy_file(src, dest, FALSE) < 0) return -1;
2879 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2882 gint bytes_left, to_read;
2885 if (fseek(fp, offset, SEEK_SET) < 0) {
2890 bytes_left = length;
2891 to_read = MIN(bytes_left, sizeof(buf));
2893 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2894 if (n_read < to_read && ferror(fp))
2896 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2899 bytes_left -= n_read;
2900 if (bytes_left == 0)
2902 to_read = MIN(bytes_left, sizeof(buf));
2913 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2916 gboolean err = FALSE;
2918 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2919 FILE_OP_ERROR(dest, "fopen");
2923 if (change_file_mode_rw(dest_fp, dest) < 0) {
2924 FILE_OP_ERROR(dest, "chmod");
2925 g_warning("can't change file mode\n");
2928 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2931 if (!err && fclose(dest_fp) == EOF) {
2932 FILE_OP_ERROR(dest, "fclose");
2937 g_warning("writing to %s failed.\n", dest);
2945 /* convert line endings into CRLF. If the last line doesn't end with
2946 * linebreak, add it.
2948 gchar *canonicalize_str(const gchar *str)
2954 for (p = str; *p != '\0'; ++p) {
2961 if (p == str || *(p - 1) != '\n')
2964 out = outp = g_malloc(new_len + 1);
2965 for (p = str; *p != '\0'; ++p) {
2972 if (p == str || *(p - 1) != '\n') {
2981 gint canonicalize_file(const gchar *src, const gchar *dest)
2983 FILE *src_fp, *dest_fp;
2984 gchar buf[BUFFSIZE];
2986 gboolean err = FALSE;
2987 gboolean last_linebreak = FALSE;
2989 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2990 FILE_OP_ERROR(src, "fopen");
2994 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2995 FILE_OP_ERROR(dest, "fopen");
3000 if (change_file_mode_rw(dest_fp, dest) < 0) {
3001 FILE_OP_ERROR(dest, "chmod");
3002 g_warning("can't change file mode\n");
3005 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3009 if (len == 0) break;
3010 last_linebreak = FALSE;
3012 if (buf[len - 1] != '\n') {
3013 last_linebreak = TRUE;
3014 r = fputs(buf, dest_fp);
3015 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3016 r = fputs(buf, dest_fp);
3019 r = fwrite(buf, 1, len - 1, dest_fp);
3024 r = fputs("\r\n", dest_fp);
3028 g_warning("writing to %s failed.\n", dest);
3036 if (last_linebreak == TRUE) {
3037 if (fputs("\r\n", dest_fp) == EOF)
3041 if (ferror(src_fp)) {
3042 FILE_OP_ERROR(src, "fgets");
3046 if (fclose(dest_fp) == EOF) {
3047 FILE_OP_ERROR(dest, "fclose");
3059 gint canonicalize_file_replace(const gchar *file)
3063 tmp_file = get_tmp_file();
3065 if (canonicalize_file(file, tmp_file) < 0) {
3070 if (move_file(tmp_file, file, TRUE) < 0) {
3071 g_warning("can't replace %s .\n", file);
3081 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3083 FILE *src_fp, *dest_fp;
3084 gchar buf[BUFFSIZE];
3085 gboolean err = FALSE;
3087 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3088 FILE_OP_ERROR(src, "fopen");
3092 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3093 FILE_OP_ERROR(dest, "fopen");
3098 if (change_file_mode_rw(dest_fp, dest) < 0) {
3099 FILE_OP_ERROR(dest, "chmod");
3100 g_warning("can't change file mode\n");
3103 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3105 if (fputs(buf, dest_fp) == EOF) {
3106 g_warning("writing to %s failed.\n", dest);
3114 if (ferror(src_fp)) {
3115 FILE_OP_ERROR(src, "fgets");
3119 if (fclose(dest_fp) == EOF) {
3120 FILE_OP_ERROR(dest, "fclose");
3132 gint uncanonicalize_file_replace(const gchar *file)
3136 tmp_file = get_tmp_file();
3138 if (uncanonicalize_file(file, tmp_file) < 0) {
3143 if (move_file(tmp_file, file, TRUE) < 0) {
3144 g_warning("can't replace %s .\n", file);
3154 gchar *normalize_newlines(const gchar *str)
3156 const gchar *p = str;
3159 out = outp = g_malloc(strlen(str) + 1);
3160 for (p = str; *p != '\0'; ++p) {
3162 if (*(p + 1) != '\n')
3173 gchar *get_outgoing_rfc2822_str(FILE *fp)
3175 gchar buf[BUFFSIZE];
3179 str = g_string_new(NULL);
3181 /* output header part */
3182 while (fgets(buf, sizeof(buf), fp) != NULL) {
3184 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3191 else if (next != ' ' && next != '\t') {
3195 if (fgets(buf, sizeof(buf), fp) == NULL)
3199 g_string_append(str, buf);
3200 g_string_append(str, "\r\n");
3206 /* output body part */
3207 while (fgets(buf, sizeof(buf), fp) != NULL) {
3210 g_string_append_c(str, '.');
3211 g_string_append(str, buf);
3212 g_string_append(str, "\r\n");
3216 g_string_free(str, FALSE);
3222 * Create a new boundary in a way that it is very unlikely that this
3223 * will occur in the following text. It would be easy to ensure
3224 * uniqueness if everything is either quoted-printable or base64
3225 * encoded (note that conversion is allowed), but because MIME bodies
3226 * may be nested, it may happen that the same boundary has already
3229 * boundary := 0*69<bchars> bcharsnospace
3230 * bchars := bcharsnospace / " "
3231 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3232 * "+" / "_" / "," / "-" / "." /
3233 * "/" / ":" / "=" / "?"
3235 * some special characters removed because of buggy MTAs
3238 gchar *generate_mime_boundary(const gchar *prefix)
3240 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3241 "abcdefghijklmnopqrstuvwxyz"
3246 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3247 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3250 return g_strdup_printf("%s_%s", prefix ? prefix : "MP",
3254 gint change_file_mode_rw(FILE *fp, const gchar *file)
3257 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3259 return g_chmod(file, S_IRUSR|S_IWUSR);
3263 FILE *my_tmpfile(void)
3265 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3266 const gchar suffix[] = ".XXXXXX";
3267 const gchar *tmpdir;
3269 const gchar *progname;
3275 tmpdir = get_tmp_dir();
3276 tmplen = strlen(tmpdir);
3277 progname = g_get_prgname();
3278 if (progname == NULL)
3279 progname = "sylpheed-claws";
3280 proglen = strlen(progname);
3281 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3284 memcpy(fname, tmpdir, tmplen);
3285 fname[tmplen] = G_DIR_SEPARATOR;
3286 memcpy(fname + tmplen + 1, progname, proglen);
3287 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3289 fd = mkstemp(fname);
3297 fp = fdopen(fd, "w+b");
3302 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3307 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3311 char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
3312 dir, G_DIR_SEPARATOR);
3313 fd = mkstemp_name(template, filename);
3316 *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
3317 fd = mkstemp(*filename);
3319 return fdopen(fd, "w+");
3322 FILE *str_open_as_stream(const gchar *str)
3327 g_return_val_if_fail(str != NULL, NULL);
3331 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3336 if (len == 0) return fp;
3338 if (fwrite(str, 1, len, fp) != len) {
3339 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3348 gint str_write_to_file(const gchar *str, const gchar *file)
3353 g_return_val_if_fail(str != NULL, -1);
3354 g_return_val_if_fail(file != NULL, -1);
3356 if ((fp = g_fopen(file, "wb")) == NULL) {
3357 FILE_OP_ERROR(file, "fopen");
3367 if (fwrite(str, 1, len, fp) != len) {
3368 FILE_OP_ERROR(file, "fwrite");
3374 if (fclose(fp) == EOF) {
3375 FILE_OP_ERROR(file, "fclose");
3383 gchar *file_read_to_str(const gchar *file)
3388 g_return_val_if_fail(file != NULL, NULL);
3390 if ((fp = g_fopen(file, "rb")) == NULL) {
3391 FILE_OP_ERROR(file, "fopen");
3395 str = file_read_stream_to_str(fp);
3402 gchar *file_read_stream_to_str(FILE *fp)
3409 g_return_val_if_fail(fp != NULL, NULL);
3411 array = g_byte_array_new();
3413 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3414 if (n_read < sizeof(buf) && ferror(fp))
3416 g_byte_array_append(array, buf, n_read);
3420 FILE_OP_ERROR("file stream", "fread");
3421 g_byte_array_free(array, TRUE);
3426 g_byte_array_append(array, buf, 1);
3427 str = (gchar *)array->data;
3428 g_byte_array_free(array, FALSE);
3430 if (!g_utf8_validate(str, -1, NULL)) {
3431 const gchar *src_codeset, *dest_codeset;
3433 src_codeset = conv_get_locale_charset_str();
3434 dest_codeset = CS_UTF_8;
3435 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3443 gint execute_async(gchar *const argv[])
3445 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3447 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3448 NULL, NULL, NULL, FALSE) == FALSE) {
3449 g_warning("Can't execute command: %s\n", argv[0]);
3456 gint execute_sync(gchar *const argv[])
3460 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3462 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3463 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3464 g_warning("Can't execute command: %s\n", argv[0]);
3469 if (WIFEXITED(status))
3470 return WEXITSTATUS(status);
3478 gint execute_command_line(const gchar *cmdline, gboolean async)
3483 debug_print("execute_command_line(): executing: %s\n", cmdline);
3485 argv = strsplit_with_quote(cmdline, " ", 0);
3488 ret = execute_async(argv);
3490 ret = execute_sync(argv);
3497 gchar *get_command_output(const gchar *cmdline)
3499 gchar *child_stdout;
3502 g_return_val_if_fail(cmdline != NULL, NULL);
3504 debug_print("get_command_output(): executing: %s\n", cmdline);
3506 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3508 g_warning("Can't execute command: %s\n", cmdline);
3512 return child_stdout;
3515 static gint is_unchanged_uri_char(char c)
3527 void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3533 for(i = 0; i < strlen(uri) ; i++) {
3534 if (is_unchanged_uri_char(uri[i])) {
3535 if (k + 2 >= bufsize)
3537 encoded_uri[k++] = uri[i];
3540 char * hexa = "0123456789ABCDEF";
3542 if (k + 4 >= bufsize)
3544 encoded_uri[k++] = '%';
3545 encoded_uri[k++] = hexa[uri[i] / 16];
3546 encoded_uri[k++] = hexa[uri[i] % 16];
3552 gint open_uri(const gchar *uri, const gchar *cmdline)
3554 gchar buf[BUFFSIZE];
3556 gchar encoded_uri[BUFFSIZE];
3558 g_return_val_if_fail(uri != NULL, -1);
3560 /* an option to choose whether to use encode_uri or not ? */
3561 encode_uri(encoded_uri, BUFFSIZE, uri);
3564 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3565 !strchr(p + 2, '%'))
3566 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3569 g_warning("Open URI command line is invalid "
3570 "(there must be only one '%%s'): %s",
3572 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3575 execute_command_line(buf, TRUE);
3580 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3582 gchar buf[BUFFSIZE];
3585 g_return_val_if_fail(filepath != NULL, -1);
3588 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3589 !strchr(p + 2, '%'))
3590 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3593 g_warning("Open Text Editor command line is invalid "
3594 "(there must be only one '%%s'): %s",
3596 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3599 execute_command_line(buf, TRUE);
3604 time_t remote_tzoffset_sec(const gchar *zone)
3606 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3612 time_t remoteoffset;
3614 strncpy(zone3, zone, 3);
3618 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3619 (c == '+' || c == '-')) {
3620 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3622 remoteoffset = -remoteoffset;
3623 } else if (!strncmp(zone, "UT" , 2) ||
3624 !strncmp(zone, "GMT", 2)) {
3626 } else if (strlen(zone3) == 3) {
3627 for (p = ustzstr; *p != '\0'; p += 3) {
3628 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3629 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3630 remoteoffset = iustz * 3600;
3636 } else if (strlen(zone3) == 1) {
3638 case 'Z': remoteoffset = 0; break;
3639 case 'A': remoteoffset = -1; break;
3640 case 'B': remoteoffset = -2; break;
3641 case 'C': remoteoffset = -3; break;
3642 case 'D': remoteoffset = -4; break;
3643 case 'E': remoteoffset = -5; break;
3644 case 'F': remoteoffset = -6; break;
3645 case 'G': remoteoffset = -7; break;
3646 case 'H': remoteoffset = -8; break;
3647 case 'I': remoteoffset = -9; break;
3648 case 'K': remoteoffset = -10; break; /* J is not used */
3649 case 'L': remoteoffset = -11; break;
3650 case 'M': remoteoffset = -12; break;
3651 case 'N': remoteoffset = 1; break;
3652 case 'O': remoteoffset = 2; break;
3653 case 'P': remoteoffset = 3; break;
3654 case 'Q': remoteoffset = 4; break;
3655 case 'R': remoteoffset = 5; break;
3656 case 'S': remoteoffset = 6; break;
3657 case 'T': remoteoffset = 7; break;
3658 case 'U': remoteoffset = 8; break;
3659 case 'V': remoteoffset = 9; break;
3660 case 'W': remoteoffset = 10; break;
3661 case 'X': remoteoffset = 11; break;
3662 case 'Y': remoteoffset = 12; break;
3663 default: remoteoffset = 0; break;
3665 remoteoffset = remoteoffset * 3600;
3669 return remoteoffset;
3672 time_t tzoffset_sec(time_t *now)
3678 lt = localtime(now);
3680 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3682 if (lt->tm_year < gmt.tm_year)
3684 else if (lt->tm_year > gmt.tm_year)
3686 else if (lt->tm_yday < gmt.tm_yday)
3688 else if (lt->tm_yday > gmt.tm_yday)
3691 if (off >= 24 * 60) /* should be impossible */
3692 off = 23 * 60 + 59; /* if not, insert silly value */
3693 if (off <= -24 * 60)
3694 off = -(23 * 60 + 59);
3699 /* calculate timezone offset */
3700 gchar *tzoffset(time_t *now)
3702 static gchar offset_string[6];
3708 lt = localtime(now);
3710 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3712 if (lt->tm_year < gmt.tm_year)
3714 else if (lt->tm_year > gmt.tm_year)
3716 else if (lt->tm_yday < gmt.tm_yday)
3718 else if (lt->tm_yday > gmt.tm_yday)
3726 if (off >= 24 * 60) /* should be impossible */
3727 off = 23 * 60 + 59; /* if not, insert silly value */
3729 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3731 return offset_string;
3734 void get_rfc822_date(gchar *buf, gint len)
3738 gchar day[4], mon[4];
3739 gint dd, hh, mm, ss, yyyy;
3744 sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3745 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3746 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3747 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3750 /* just a wrapper to suppress the warning of gcc about %c */
3751 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3752 const struct tm *tm)
3754 return strftime(s, max, format, tm);
3757 void debug_set_mode(gboolean mode)
3762 gboolean debug_get_mode(void)
3767 void debug_print_real(const gchar *format, ...)
3770 gchar buf[BUFFSIZE];
3772 if (!debug_mode) return;
3774 va_start(args, format);
3775 g_vsnprintf(buf, sizeof(buf), format, args);
3782 const char * debug_srcname(const char *file)
3784 const char *s = strrchr (file, '/');
3789 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3791 if (subject == NULL)
3794 subject += subject_get_prefix_length(subject);
3796 return g_hash_table_lookup(subject_table, subject);
3799 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3802 if (subject == NULL || *subject == 0)
3804 subject += subject_get_prefix_length(subject);
3805 g_hash_table_insert(subject_table, subject, data);
3808 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3810 if (subject == NULL)
3813 subject += subject_get_prefix_length(subject);
3814 g_hash_table_remove(subject_table, subject);
3818 *\brief Check if a string is prefixed with known (combinations)
3819 * of prefixes. The function assumes that each prefix
3820 * is terminated by zero or exactly _one_ space.
3822 *\param str String to check for a prefixes
3824 *\return int Number of chars in the prefix that should be skipped
3825 * for a "clean" subject line. If no prefix was found, 0
3828 int subject_get_prefix_length(const gchar *subject)
3830 /*!< Array with allowable reply prefixes regexps. */
3831 static const gchar * const prefixes[] = {
3832 "Re\\:", /* "Re:" */
3833 "RE\\:", /* "RE:" (outlook) */
3834 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3835 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3836 "Aw\\:", /* "Aw:" (German) */
3837 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3838 "Res\\:", /* "Res:" (Brazilian Outlook) */
3839 "Fw\\:", /* "Fw:" Forward */
3840 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3841 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3842 "Rif\\:", /* "Rif:" (Italian Outlook) */
3843 "SV\\:", /* "SV" (Norwegian) */
3844 "Sv\\:", /* "Sv" (Norwegian) */
3845 "VS\\:", /* "VS" (Norwegian) */
3846 "Vs\\:", /* "Vs" (Norwegian) */
3847 "AD\\:", /* "AD" (Norwegian) */
3848 "Ad\\:" /* "Ad" (Norwegian) */
3851 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3854 static regex_t regex;
3855 static gboolean init_;
3857 if (!subject) return 0;
3858 if (!*subject) return 0;
3861 GString *s = g_string_new("");
3863 for (n = 0; n < PREFIXES; n++)
3864 /* Terminate each prefix regexpression by a
3865 * "\ ?" (zero or ONE space), and OR them */
3866 g_string_append_printf(s, "(%s\\ ?)%s",
3871 g_string_prepend(s, "(");
3872 g_string_append(s, ")+"); /* match at least once */
3873 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3876 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3877 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3878 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3879 debug_print("Error compiling regexp %s\n", s->str);
3880 g_string_free(s, TRUE);
3884 g_string_free(s, TRUE);
3888 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3894 guint g_stricase_hash(gconstpointer gptr)
3896 guint hash_result = 0;
3899 for (str = gptr; str && *str; str++) {
3900 hash_result += toupper(*str);
3906 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3908 const char *str1 = gptr1;
3909 const char *str2 = gptr2;
3911 return !strcasecmp(str1, str2);
3914 gint g_int_compare(gconstpointer a, gconstpointer b)
3916 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3919 gchar *generate_msgid(gchar *buf, gint len)
3928 addr = g_strconcat("@", get_domain_name(), NULL);
3930 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3931 lt->tm_year + 1900, lt->tm_mon + 1,
3932 lt->tm_mday, lt->tm_hour,
3933 lt->tm_min, lt->tm_sec,
3934 (guint) rand(), addr);
3941 quote_cmd_argument()
3943 return a quoted string safely usable in argument of a command.
3945 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3948 gint quote_cmd_argument(gchar * result, guint size,
3958 for(p = path ; * p != '\0' ; p ++) {
3960 if (isalnum((guchar)*p) || (* p == '/')) {
3961 if (remaining > 0) {
3967 result[size - 1] = '\0';
3972 if (remaining >= 2) {
3980 result[size - 1] = '\0';
3985 if (remaining > 0) {
3989 result[size - 1] = '\0';
4003 static void g_node_map_recursive(GNode *node, gpointer data)
4005 GNodeMapData *mapdata = (GNodeMapData *) data;
4007 GNodeMapData newmapdata;
4010 newdata = mapdata->func(node->data, mapdata->data);
4011 if (newdata != NULL) {
4012 newnode = g_node_new(newdata);
4013 g_node_append(mapdata->parent, newnode);
4015 newmapdata.parent = newnode;
4016 newmapdata.func = mapdata->func;
4017 newmapdata.data = mapdata->data;
4019 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4023 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4026 GNodeMapData mapdata;
4028 g_return_val_if_fail(node != NULL, NULL);
4029 g_return_val_if_fail(func != NULL, NULL);
4031 root = g_node_new(func(node->data, data));
4033 mapdata.parent = root;
4034 mapdata.func = func;
4035 mapdata.data = data;
4037 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4042 #define HEX_TO_INT(val, hex) \
4046 if ('0' <= c && c <= '9') { \
4048 } else if ('a' <= c && c <= 'f') { \
4049 val = c - 'a' + 10; \
4050 } else if ('A' <= c && c <= 'F') { \
4051 val = c - 'A' + 10; \
4057 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4064 if (hi == -1 || lo == -1)
4067 *out = (hi << 4) + lo;
4071 #define INT_TO_HEX(hex, val) \
4074 hex = '0' + (val); \
4076 hex = 'A' + (val) - 10; \
4079 void get_hex_str(gchar *out, guchar ch)
4083 INT_TO_HEX(hex, ch >> 4);
4085 INT_TO_HEX(hex, ch & 0x0f);
4091 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4093 #define G_PRINT_REF g_print
4097 *\brief Register ref counted pointer. It is based on GBoxed, so should
4098 * work with anything that uses the GType system. The semantics
4099 * are similar to a C++ auto pointer, with the exception that
4100 * C doesn't have automatic closure (calling destructors) when
4101 * exiting a block scope.
4102 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4103 * function directly.
4105 *\return GType A GType type.
4107 GType g_auto_pointer_register(void)
4109 static GType auto_pointer_type;
4110 if (!auto_pointer_type)
4112 g_boxed_type_register_static
4113 ("G_TYPE_AUTO_POINTER",
4114 (GBoxedCopyFunc) g_auto_pointer_copy,
4115 (GBoxedFreeFunc) g_auto_pointer_free);
4116 return auto_pointer_type;
4120 *\brief Structure with g_new() allocated pointer guarded by the
4123 typedef struct AutoPointerRef {
4124 void (*free) (gpointer);
4130 *\brief The auto pointer opaque structure that references the
4131 * pointer guard block.
4133 typedef struct AutoPointer {
4134 AutoPointerRef *ref;
4135 gpointer ptr; /*!< access to protected pointer */
4139 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4143 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4144 * ... when assigning, copying and freeing storage elements
4146 * gtk_list_store_new(N_S_COLUMNS,
4147 * G_TYPE_AUTO_POINTER,
4151 * Template *precious_data = g_new0(Template, 1);
4152 * g_pointer protect = g_auto_pointer_new(precious_data);
4154 * gtk_list_store_set(container, &iter,
4158 * ... the gtk_list_store has copied the pointer and
4159 * ... incremented its reference count, we should free
4160 * ... the auto pointer (in C++ a destructor would do
4161 * ... this for us when leaving block scope)
4163 * g_auto_pointer_free(protect);
4165 * ... gtk_list_store_set() now manages the data. When
4166 * ... *explicitly* requesting a pointer from the list
4167 * ... store, don't forget you get a copy that should be
4168 * ... freed with g_auto_pointer_free() eventually.
4172 *\param pointer Pointer to be guarded.
4174 *\return GAuto * Pointer that should be used in containers with
4177 GAuto *g_auto_pointer_new(gpointer p)
4179 AutoPointerRef *ref;
4185 ref = g_new0(AutoPointerRef, 1);
4186 ptr = g_new0(AutoPointer, 1);
4196 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4202 *\brief Allocate an autopointer using the passed \a free function to
4203 * free the guarded pointer
4205 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4212 aptr = g_auto_pointer_new(p);
4213 aptr->ref->free = free_;
4217 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4219 if (auto_ptr == NULL)
4221 return ((AutoPointer *) auto_ptr)->ptr;
4225 *\brief Copies an auto pointer by. It's mostly not necessary
4226 * to call this function directly, unless you copy/assign
4227 * the guarded pointer.
4229 *\param auto_ptr Auto pointer returned by previous call to
4230 * g_auto_pointer_new_XXX()
4232 *\return gpointer An auto pointer
4234 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4237 AutoPointerRef *ref;
4240 if (auto_ptr == NULL)
4245 newp = g_new0(AutoPointer, 1);
4248 newp->ptr = ref->pointer;
4252 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4258 *\brief Free an auto pointer
4260 void g_auto_pointer_free(GAuto *auto_ptr)
4263 AutoPointerRef *ref;
4265 if (auto_ptr == NULL)
4271 if (--(ref->cnt) == 0) {
4273 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4275 ref->free(ref->pointer);
4280 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4285 void replace_returns(gchar *str)
4290 while (strstr(str, "\n")) {
4291 *strstr(str, "\n") = ' ';
4293 while (strstr(str, "\r")) {
4294 *strstr(str, "\r") = ' ';
4298 /* get_uri_part() - retrieves a URI starting from scanpos.
4299 Returns TRUE if succesful */
4300 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4301 const gchar **bp, const gchar **ep, gboolean hdr)
4304 gint parenthese_cnt = 0;
4306 g_return_val_if_fail(start != NULL, FALSE);
4307 g_return_val_if_fail(scanpos != NULL, FALSE);
4308 g_return_val_if_fail(bp != NULL, FALSE);
4309 g_return_val_if_fail(ep != NULL, FALSE);
4313 /* find end point of URI */
4314 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4315 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4316 !IS_ASCII(*(const guchar *)ep_) ||
4317 strchr("[]{}<>\"'", *ep_)) {
4319 } else if (strchr("(", *ep_)) {
4321 } else if (strchr(")", *ep_)) {
4322 if (parenthese_cnt > 0)
4329 /* no punctuation at end of string */
4331 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4332 * should pass some URI type to this function and decide on that whether
4333 * to perform punctuation stripping */
4335 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4337 for (; ep_ - 1 > scanpos + 1 &&
4338 IS_REAL_PUNCT(*(ep_ - 1));
4342 #undef IS_REAL_PUNCT
4349 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4351 while (bp && *bp && g_ascii_isspace(*bp))
4353 return g_strndup(bp, ep - bp);
4356 /* valid mail address characters */
4357 #define IS_RFC822_CHAR(ch) \
4361 !g_ascii_isspace(ch) && \
4362 !strchr("(),;<>\"", (ch)))
4364 /* alphabet and number within 7bit ASCII */
4365 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4366 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4368 static GHashTable *create_domain_tab(void)
4370 static const gchar *toplvl_domains [] = {
4372 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4373 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4374 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4375 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4376 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4377 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4378 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4379 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4380 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4381 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4382 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4383 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4384 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4385 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4386 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4387 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4388 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4389 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4390 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4391 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4392 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4393 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4394 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4395 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4396 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4397 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4400 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4402 g_return_val_if_fail(htab, NULL);
4403 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4404 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4408 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4410 const gint MAX_LVL_DOM_NAME_LEN = 6;
4411 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4412 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4415 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4418 for (p = buf; p < m && first < last; *p++ = *first++)
4422 return g_hash_table_lookup(tab, buf) != NULL;
4425 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4426 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4427 const gchar **bp, const gchar **ep, gboolean hdr)
4429 /* more complex than the uri part because we need to scan back and forward starting from
4430 * the scan position. */
4431 gboolean result = FALSE;
4432 const gchar *bp_ = NULL;
4433 const gchar *ep_ = NULL;
4434 static GHashTable *dom_tab;
4435 const gchar *last_dot = NULL;
4436 const gchar *prelast_dot = NULL;
4437 const gchar *last_tld_char = NULL;
4439 /* the informative part of the email address (describing the name
4440 * of the email address owner) may contain quoted parts. the
4441 * closure stack stores the last encountered quotes. */
4442 gchar closure_stack[128];
4443 gchar *ptr = closure_stack;
4445 g_return_val_if_fail(start != NULL, FALSE);
4446 g_return_val_if_fail(scanpos != NULL, FALSE);
4447 g_return_val_if_fail(bp != NULL, FALSE);
4448 g_return_val_if_fail(ep != NULL, FALSE);
4451 const gchar *start_quote = NULL;
4452 const gchar *end_quote = NULL;
4454 /* go to the real start */
4455 if (start[0] == ',')
4457 if (start[0] == ';')
4459 while (start[0] == '\n' || start[0] == '\r')
4461 while (start[0] == ' ' || start[0] == '\t')
4466 /* check if there are quotes (to skip , in them) */
4467 if (*start == '"') {
4468 start_quote = start;
4470 end_quote = strstr(start, "\"");
4476 /* skip anything between quotes */
4477 if (start_quote && end_quote) {
4482 /* find end (either , or ; or end of line) */
4483 if (strstr(start, ",") && strstr(start, ";"))
4484 *ep = strstr(start,",") < strstr(start, ";")
4485 ? strstr(start, ",") : strstr(start, ";");
4486 else if (strstr(start, ","))
4487 *ep = strstr(start, ",");
4488 else if (strstr(start, ";"))
4489 *ep = strstr(start, ";");
4491 *ep = start+strlen(start);
4493 /* go back to real start */
4494 if (start_quote && end_quote) {
4495 start = start_quote;
4498 /* check there's still an @ in that, or search
4499 * further if possible */
4500 if (strstr(start, "@") && strstr(start, "@") < *ep)
4502 else if (*ep < start+strlen(start)) {
4505 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4513 dom_tab = create_domain_tab();
4514 g_return_val_if_fail(dom_tab, FALSE);
4516 /* scan start of address */
4517 for (bp_ = scanpos - 1;
4518 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4521 /* TODO: should start with an alnum? */
4523 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4526 if (bp_ != scanpos) {
4527 /* scan end of address */
4528 for (ep_ = scanpos + 1;
4529 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4531 prelast_dot = last_dot;
4533 if (*(last_dot + 1) == '.') {
4534 if (prelast_dot == NULL)
4536 last_dot = prelast_dot;
4541 /* TODO: really should terminate with an alnum? */
4542 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4547 if (last_dot == NULL)
4549 if (last_dot >= ep_)
4550 last_dot = prelast_dot;
4551 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4555 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4556 if (*last_tld_char == '?')
4559 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4566 if (!result) return FALSE;
4568 if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"'
4569 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4570 && IS_RFC822_CHAR(*(ep_ + 3))) {
4571 /* this informative part with an @ in it is
4572 * followed by the email address */
4575 /* go to matching '>' (or next non-rfc822 char, like \n) */
4576 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4579 /* include the bracket */
4580 if (*ep_ == '>') ep_++;
4582 /* include the leading quote */
4590 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4591 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4594 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4595 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4598 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4599 #define IN_STACK() (ptr > closure_stack)
4600 /* has underrun check */
4601 #define POP_STACK() if(IN_STACK()) --ptr
4602 /* has overrun check */
4603 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4604 /* has underrun check */
4605 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4609 /* scan for the informative part. */
4610 for (bp_ -= 2; bp_ >= start; bp_--) {
4611 /* if closure on the stack keep scanning */
4612 if (PEEK_STACK() == *bp_) {
4616 if (*bp_ == '\'' || *bp_ == '"') {
4621 /* if nothing in the closure stack, do the special conditions
4622 * the following if..else expression simply checks whether
4623 * a token is acceptable. if not acceptable, the clause
4624 * should terminate the loop with a 'break' */
4625 if (!PEEK_STACK()) {
4627 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4628 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4629 /* hyphens are allowed, but only in
4631 } else if (strchr(" \"'", *bp_)) {
4632 /* but anything not being a punctiation
4635 break; /* anything else is rejected */
4642 /* scan forward (should start with an alnum) */
4643 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4659 #undef IS_ASCII_ALNUM
4660 #undef IS_RFC822_CHAR
4662 gchar *make_email_string(const gchar *bp, const gchar *ep)
4664 /* returns a mailto: URI; mailto: is also used to detect the
4665 * uri type later on in the button_pressed signal handler */
4669 tmp = g_strndup(bp, ep - bp);
4670 result = g_strconcat("mailto:", tmp, NULL);
4676 gchar *make_http_string(const gchar *bp, const gchar *ep)
4678 /* returns an http: URI; */
4682 while (bp && *bp && g_ascii_isspace(*bp))
4684 tmp = g_strndup(bp, ep - bp);
4685 result = g_strconcat("http://", tmp, NULL);
4691 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type)
4693 FILE *fp = fopen(path, "rb");
4694 gchar buf[BUFFSIZE];
4695 gchar *result = NULL;
4698 while (fgets(buf, sizeof (buf), fp) != NULL) {
4699 gchar **parts = g_strsplit(buf, ";", -1);
4700 gchar *trimmed = parts[0];
4701 while (trimmed[0] == ' ')
4703 while (trimmed[strlen(trimmed)-1] == ' ')
4704 trimmed[strlen(trimmed)-1] = '\0';
4706 if (!strcmp(trimmed, type)) {
4708 while (trimmed[0] == ' ')
4710 while (trimmed[strlen(trimmed)-1] == ' ')
4711 trimmed[strlen(trimmed)-1] = '\0';
4712 while (trimmed[strlen(trimmed)-1] == '\n')
4713 trimmed[strlen(trimmed)-1] = '\0';
4714 while (trimmed[strlen(trimmed)-1] == '\r')
4715 trimmed[strlen(trimmed)-1] = '\0';
4716 result = g_strdup(trimmed);
4719 if (strstr(result, "%s") && !strstr(result, "'%s'")) {
4720 gchar *start = g_strdup(result);
4721 gchar *end = g_strdup(strstr(result, "%s")+2);
4723 *strstr(start, "%s") = '\0';
4724 tmp = g_strconcat(start,"'%s'",end, NULL);
4737 gchar *mailcap_get_command_for_type(const gchar *type)
4739 gchar *result = NULL;
4743 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4744 result = mailcap_get_command_in_file(path, type);
4748 result = mailcap_get_command_in_file("/etc/mailcap", type);
4752 gint copy_dir(const gchar *src, const gchar *dst)
4757 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4758 g_warning("failed to open directory: %s\n", src);
4762 if (make_dir(dst) < 0)
4765 while ((name = g_dir_read_name(dir)) != NULL) {
4766 gchar *old_file, *new_file;
4767 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4768 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4769 debug_print("copying: %s -> %s\n", old_file, new_file);
4770 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4771 gint r = copy_file(old_file, new_file, TRUE);
4774 } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4775 gint r = copy_dir(old_file, new_file);
4783 /* crude test to see if a file is an email. */
4784 gboolean file_is_email (const gchar *filename)
4790 if (filename == NULL)
4792 if ((fp = g_fopen(filename, "rb")) == NULL)
4794 while (i < 60 && score < 3
4795 && fgets(buffer, sizeof (buffer), fp) > 0) {
4796 if (!strncmp(buffer, "From:", strlen("From:")))
4798 if (!strncmp(buffer, "To:", strlen("To:")))
4800 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4805 return (score >= 3);