2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
66 # include <tablet-browser-interface.h>
68 # include <osso-browser-interface.h>
74 #include "../codeconv.h"
78 static gboolean debug_mode = FALSE;
80 static GSList *tempfiles=NULL;
83 /* Return true if we are running as root. This function should beused
84 instead of getuid () == 0. */
85 gboolean superuser_p (void)
88 return w32_is_administrator ();
96 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
97 gint g_chdir(const gchar *path)
100 if (G_WIN32_HAVE_WIDECHAR_API()) {
105 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
111 retval = _wchdir(wpath);
123 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
124 if (cp_path == NULL) {
129 retval = chdir(cp_path);
142 gint g_chmod(const gchar *path, gint mode)
145 if (G_WIN32_HAVE_WIDECHAR_API()) {
150 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
156 retval = _wchmod(wpath, mode);
168 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
169 if (cp_path == NULL) {
174 retval = chmod(cp_path, mode);
183 return chmod(path, mode);
186 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
190 gint mkstemp_name(const gchar *template, gchar **name_used)
192 static gulong count=0; /* W32-_mktemp only supports up to 27
196 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
197 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
198 (S_IRUSR | S_IWUSR));
200 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
202 perror(g_strdup_printf("cant create %s",*name_used));
208 #endif /* G_OS_WIN32 */
211 gint mkstemp(const gchar *template)
214 gint res = mkstemp_name(template, &dummyname);
218 #endif /* G_OS_WIN32 */
220 void list_free_strings(GList *list)
222 list = g_list_first(list);
224 while (list != NULL) {
230 void slist_free_strings(GSList *list)
232 while (list != NULL) {
238 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
243 void hash_free_strings(GHashTable *table)
245 g_hash_table_foreach(table, hash_free_strings_func, NULL);
248 gint str_case_equal(gconstpointer v, gconstpointer v2)
250 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
253 guint str_case_hash(gconstpointer key)
255 const gchar *p = key;
259 h = g_ascii_tolower(h);
260 for (p += 1; *p != '\0'; p++)
261 h = (h << 5) - h + g_ascii_tolower(*p);
267 void ptr_array_free_strings(GPtrArray *array)
272 g_return_if_fail(array != NULL);
274 for (i = 0; i < array->len; i++) {
275 str = g_ptr_array_index(array, i);
280 gboolean str_find(const gchar *haystack, const gchar *needle)
282 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
285 gboolean str_case_find(const gchar *haystack, const gchar *needle)
287 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
290 gint to_number(const gchar *nstr)
292 register const gchar *p;
294 if (*nstr == '\0') return -1;
296 for (p = nstr; *p != '\0'; p++)
297 if (!g_ascii_isdigit(*p)) return -1;
302 /* convert integer into string,
303 nstr must be not lower than 11 characters length */
304 gchar *itos_buf(gchar *nstr, gint n)
306 g_snprintf(nstr, 11, "%d", n);
310 /* convert integer into string */
313 static gchar nstr[11];
315 return itos_buf(nstr, n);
318 #define divide(num,divisor,i,d) \
320 i = num >> divisor; \
321 d = num & ((1<<divisor)-1); \
322 d = (d*100) >> divisor; \
325 gchar *to_human_readable(off_t size)
327 static gchar str[14];
328 static gchar *b_format = NULL, *kb_format = NULL,
329 *mb_format = NULL, *gb_format = NULL;
330 register int t = 0, r = 0;
331 if (b_format == NULL) {
333 kb_format = _("%d.%02dKB");
334 mb_format = _("%d.%02dMB");
335 gb_format = _("%.2fGB");
338 if (size < (off_t)1024) {
339 g_snprintf(str, sizeof(str), b_format, (gint)size);
341 } else if (size >> 10 < (off_t)1024) {
342 divide(size, 10, t, r);
343 g_snprintf(str, sizeof(str), kb_format, t, r);
345 } else if (size >> 20 < (off_t)1024) {
346 divide(size, 20, t, r);
347 g_snprintf(str, sizeof(str), mb_format, t, r);
350 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
355 /* strcmp with NULL-checking */
356 gint strcmp2(const gchar *s1, const gchar *s2)
358 if (s1 == NULL || s2 == NULL)
361 return strcmp(s1, s2);
363 /* strstr with NULL-checking */
364 gchar *strstr2(const gchar *s1, const gchar *s2)
366 if (s1 == NULL || s2 == NULL)
369 return strstr(s1, s2);
372 gint path_cmp(const gchar *s1, const gchar *s2)
377 gchar *s1buf, *s2buf;
380 if (s1 == NULL || s2 == NULL) return -1;
381 if (*s1 == '\0' || *s2 == '\0') return -1;
384 s1buf = g_strdup (s1);
385 s2buf = g_strdup (s2);
386 subst_char (s1buf, '/', G_DIR_SEPARATOR);
387 subst_char (s2buf, '/', G_DIR_SEPARATOR);
390 #endif /* !G_OS_WIN32 */
395 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
396 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
398 rc = strncmp(s1, s2, MAX(len1, len2));
402 #endif /* !G_OS_WIN32 */
406 /* remove trailing return code */
407 gchar *strretchomp(gchar *str)
411 if (!*str) return str;
413 for (s = str + strlen(str) - 1;
414 s >= str && (*s == '\n' || *s == '\r');
421 /* remove trailing character */
422 gchar *strtailchomp(gchar *str, gchar tail_char)
426 if (!*str) return str;
427 if (tail_char == '\0') return str;
429 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
435 /* remove CR (carriage return) */
436 gchar *strcrchomp(gchar *str)
440 if (!*str) return str;
442 s = str + strlen(str) - 1;
443 if (*s == '\n' && s > str && *(s - 1) == '\r') {
451 gint file_strip_crs(const gchar *file)
453 FILE *fp = NULL, *outfp = NULL;
455 gchar *out = get_tmp_file();
459 fp = fopen(file, "rb");
463 outfp = fopen(out, "wb");
469 while (fgets(buf, sizeof (buf), fp) != NULL) {
471 if (fputs(buf, outfp) == EOF) {
479 if (fclose(outfp) == EOF) {
483 if (rename_force(out, file) < 0)
494 /* Similar to `strstr' but this function ignores the case of both strings. */
495 gchar *strcasestr(const gchar *haystack, const gchar *needle)
497 register size_t haystack_len, needle_len;
499 haystack_len = strlen(haystack);
500 needle_len = strlen(needle);
502 if (haystack_len < needle_len || needle_len == 0)
505 while (haystack_len >= needle_len) {
506 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
507 return (gchar *)haystack;
517 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
518 gconstpointer needle, size_t needlelen)
520 const gchar *haystack_ = (const gchar *)haystack;
521 const gchar *needle_ = (const gchar *)needle;
522 const gchar *haystack_cur = (const gchar *)haystack;
525 return memchr(haystack_, *needle_, haystacklen);
527 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
529 if (haystacklen - (haystack_cur - haystack_) < needlelen)
531 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
532 return (gpointer)haystack_cur;
540 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
541 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
543 register const gchar *s = src;
544 register gchar *d = dest;
554 /* Examine if next block is non-ASCII string */
555 gboolean is_next_nonascii(const gchar *s)
559 /* skip head space */
560 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
562 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
563 if (*(guchar *)p > 127 || *(guchar *)p < 32)
570 gint get_next_word_len(const gchar *s)
574 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
580 static void trim_subject_for_compare(gchar *str)
584 eliminate_parenthesis(str, '[', ']');
585 eliminate_parenthesis(str, '(', ')');
588 srcp = str + subject_get_prefix_length(str);
590 memmove(str, srcp, strlen(srcp) + 1);
593 static void trim_subject_for_sort(gchar *str)
599 srcp = str + subject_get_prefix_length(str);
601 memmove(str, srcp, strlen(srcp) + 1);
604 /* compare subjects */
605 gint subject_compare(const gchar *s1, const gchar *s2)
609 if (!s1 || !s2) return -1;
610 if (!*s1 || !*s2) return -1;
612 Xstrdup_a(str1, s1, return -1);
613 Xstrdup_a(str2, s2, return -1);
615 trim_subject_for_compare(str1);
616 trim_subject_for_compare(str2);
618 if (!*str1 || !*str2) return -1;
620 return strcmp(str1, str2);
623 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
627 if (!s1 || !s2) return -1;
629 Xstrdup_a(str1, s1, return -1);
630 Xstrdup_a(str2, s2, return -1);
632 trim_subject_for_sort(str1);
633 trim_subject_for_sort(str2);
635 return g_utf8_collate(str1, str2);
638 void trim_subject(gchar *str)
640 register gchar *srcp;
646 srcp = str + subject_get_prefix_length(str);
651 } else if (*srcp == '(') {
663 else if (*srcp == cl)
670 while (g_ascii_isspace(*srcp)) srcp++;
671 memmove(str, srcp, strlen(srcp) + 1);
674 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
676 register gchar *srcp, *destp;
681 while ((destp = strchr(destp, op))) {
687 else if (*srcp == cl)
693 while (g_ascii_isspace(*srcp)) srcp++;
694 memmove(destp, srcp, strlen(srcp) + 1);
698 void extract_parenthesis(gchar *str, gchar op, gchar cl)
700 register gchar *srcp, *destp;
705 while ((srcp = strchr(destp, op))) {
708 memmove(destp, srcp + 1, strlen(srcp));
713 else if (*destp == cl)
725 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
728 register gchar *srcp, *destp;
730 gboolean in_quote = FALSE;
734 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
737 memmove(destp, srcp + 1, strlen(srcp));
740 if (*destp == op && !in_quote)
742 else if (*destp == cl && !in_quote)
744 else if (*destp == quote_chr)
756 void extract_quote(gchar *str, gchar quote_chr)
760 if ((str = strchr(str, quote_chr))) {
762 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
763 memmove(p - 1, p, strlen(p) + 1);
768 memmove(str, str + 1, p - str);
773 void eliminate_address_comment(gchar *str)
775 register gchar *srcp, *destp;
780 while ((destp = strchr(destp, '"'))) {
781 if ((srcp = strchr(destp + 1, '"'))) {
786 while (g_ascii_isspace(*srcp)) srcp++;
787 memmove(destp, srcp, strlen(srcp) + 1);
797 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
803 else if (*srcp == ')')
809 while (g_ascii_isspace(*srcp)) srcp++;
810 memmove(destp, srcp, strlen(srcp) + 1);
814 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
816 gboolean in_quote = FALSE;
819 if (*str == c && !in_quote)
821 if (*str == quote_chr)
829 void extract_address(gchar *str)
831 eliminate_address_comment(str);
832 if (strchr_with_skip_quote(str, '"', '<'))
833 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
837 void extract_list_id_str(gchar *str)
839 if (strchr_with_skip_quote(str, '"', '<'))
840 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
844 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
849 if (!str) return addr_list;
851 Xstrdup_a(work, str, return addr_list);
854 eliminate_address_comment(work);
857 while (workp && *workp) {
860 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
866 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
867 extract_parenthesis_with_skip_quote
868 (workp, '"', '<', '>');
872 addr_list = g_slist_append(addr_list, g_strdup(workp));
880 GSList *address_list_append(GSList *addr_list, const gchar *str)
882 return address_list_append_real(addr_list, str, TRUE);
885 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
887 return address_list_append_real(addr_list, str, FALSE);
890 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
894 if (!str) return msgid_list;
897 while (strp && *strp) {
898 const gchar *start, *end;
901 if ((start = strchr(strp, '<')) != NULL) {
902 end = strchr(start + 1, '>');
907 msgid = g_strndup(start + 1, end - start - 1);
910 msgid_list = g_slist_prepend(msgid_list, msgid);
920 GSList *references_list_append(GSList *msgid_list, const gchar *str)
924 list = references_list_prepend(NULL, str);
925 list = g_slist_reverse(list);
926 msgid_list = g_slist_concat(msgid_list, list);
931 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
936 if (!str) return group_list;
938 Xstrdup_a(work, str, return group_list);
942 while (workp && *workp) {
945 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
953 group_list = g_slist_append(group_list,
962 GList *add_history(GList *list, const gchar *str)
966 g_return_val_if_fail(str != NULL, list);
968 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
971 list = g_list_remove(list, old->data);
972 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
975 last = g_list_last(list);
978 list = g_list_remove(list, last->data);
982 list = g_list_prepend(list, g_strdup(str));
987 void remove_return(gchar *str)
989 register gchar *p = str;
992 if (*p == '\n' || *p == '\r')
993 memmove(p, p + 1, strlen(p));
999 void remove_space(gchar *str)
1001 register gchar *p = str;
1006 while (g_ascii_isspace(*(p + spc)))
1009 memmove(p, p + spc, strlen(p + spc) + 1);
1015 void unfold_line(gchar *str)
1017 register gchar *p = str;
1021 if (*p == '\n' || *p == '\r') {
1024 while (g_ascii_isspace(*(p + spc)))
1027 memmove(p, p + spc, strlen(p + spc) + 1);
1033 void subst_char(gchar *str, gchar orig, gchar subst)
1035 register gchar *p = str;
1044 void subst_chars(gchar *str, gchar *orig, gchar subst)
1046 register gchar *p = str;
1049 if (strchr(orig, *p) != NULL)
1055 void subst_for_filename(gchar *str)
1060 subst_chars(str, "\t\r\n\\/*:", '_');
1062 subst_chars(str, "\t\r\n\\/*", '_');
1066 void subst_for_shellsafe_filename(gchar *str)
1070 subst_for_filename(str);
1071 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1074 gboolean is_ascii_str(const gchar *str)
1076 const guchar *p = (const guchar *)str;
1078 while (*p != '\0') {
1079 if (*p != '\t' && *p != ' ' &&
1080 *p != '\r' && *p != '\n' &&
1081 (*p < 32 || *p >= 127))
1089 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1091 gchar * position = NULL;
1092 gchar * tmp_pos = NULL;
1095 if (quote_chars == NULL)
1098 for (i = 0; i < strlen(quote_chars); i++) {
1099 tmp_pos = strrchr (str, quote_chars[i]);
1101 || (tmp_pos != NULL && position <= tmp_pos) )
1107 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1109 const gchar *first_pos;
1110 const gchar *last_pos;
1111 const gchar *p = str;
1112 gint quote_level = -1;
1114 /* speed up line processing by only searching to the last '>' */
1115 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1116 /* skip a line if it contains a '<' before the initial '>' */
1117 if (memchr(str, '<', first_pos - str) != NULL)
1119 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1123 while (p <= last_pos) {
1124 while (p < last_pos) {
1125 if (g_ascii_isspace(*p))
1131 if (strchr(quote_chars, *p))
1133 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1134 /* any characters are allowed except '-' and space */
1136 && !strchr(quote_chars, *p)
1137 && !g_ascii_isspace(*p)
1140 if (strchr(quote_chars, *p))
1152 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1154 const gchar *p = str, *q;
1155 gint cur_line = 0, len;
1157 while ((q = strchr(p, '\n')) != NULL) {
1159 if (len > max_chars) {
1169 if (len > max_chars) {
1178 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1180 gchar * position = NULL;
1181 gchar * tmp_pos = NULL;
1184 if (quote_chars == NULL)
1187 for (i = 0; i < strlen(quote_chars); i++) {
1188 tmp_pos = strchr (str, quote_chars[i]);
1190 || (tmp_pos != NULL && position >= tmp_pos) )
1196 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1198 register guint haystack_len, needle_len;
1199 gboolean in_squote = FALSE, in_dquote = FALSE;
1201 haystack_len = strlen(haystack);
1202 needle_len = strlen(needle);
1204 if (haystack_len < needle_len || needle_len == 0)
1207 while (haystack_len >= needle_len) {
1208 if (!in_squote && !in_dquote &&
1209 !strncmp(haystack, needle, needle_len))
1210 return (gchar *)haystack;
1212 /* 'foo"bar"' -> foo"bar"
1213 "foo'bar'" -> foo'bar' */
1214 if (*haystack == '\'') {
1217 else if (!in_dquote)
1219 } else if (*haystack == '\"') {
1222 else if (!in_squote)
1233 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1236 GSList *string_list = NULL, *slist;
1237 gchar **str_array, *s, *new_str;
1238 guint i, n = 1, len;
1240 g_return_val_if_fail(str != NULL, NULL);
1241 g_return_val_if_fail(delim != NULL, NULL);
1244 max_tokens = G_MAXINT;
1246 s = strstr_with_skip_quote(str, delim);
1248 guint delimiter_len = strlen(delim);
1252 new_str = g_strndup(str, len);
1254 if (new_str[0] == '\'' || new_str[0] == '\"') {
1255 if (new_str[len - 1] == new_str[0]) {
1256 new_str[len - 1] = '\0';
1257 memmove(new_str, new_str + 1, len - 1);
1260 string_list = g_slist_prepend(string_list, new_str);
1262 str = s + delimiter_len;
1263 s = strstr_with_skip_quote(str, delim);
1264 } while (--max_tokens && s);
1268 new_str = g_strdup(str);
1269 if (new_str[0] == '\'' || new_str[0] == '\"') {
1271 if (new_str[len - 1] == new_str[0]) {
1272 new_str[len - 1] = '\0';
1273 memmove(new_str, new_str + 1, len - 1);
1276 string_list = g_slist_prepend(string_list, new_str);
1280 str_array = g_new(gchar*, n);
1284 str_array[i--] = NULL;
1285 for (slist = string_list; slist; slist = slist->next)
1286 str_array[i--] = slist->data;
1288 g_slist_free(string_list);
1293 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1295 gchar *abbrev_group;
1297 const gchar *p = group;
1300 g_return_val_if_fail(group != NULL, NULL);
1302 last = group + strlen(group);
1303 abbrev_group = ap = g_malloc(strlen(group) + 1);
1308 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1310 while (*p != '.') p++;
1313 return abbrev_group;
1318 return abbrev_group;
1321 gchar *trim_string(const gchar *str, gint len)
1323 const gchar *p = str;
1328 if (!str) return NULL;
1329 if (strlen(str) <= len)
1330 return g_strdup(str);
1331 if (g_utf8_validate(str, -1, NULL) == FALSE)
1332 return g_strdup(str);
1334 while (*p != '\0') {
1335 mb_len = g_utf8_skip[*(guchar *)p];
1338 else if (new_len + mb_len > len)
1345 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1346 return g_strconcat(new_str, "...", NULL);
1349 GList *uri_list_extract_filenames(const gchar *uri_list)
1351 GList *result = NULL;
1353 gchar *escaped_utf8uri;
1359 while (g_ascii_isspace(*p)) p++;
1360 if (!strncmp(p, "file:", 5)) {
1363 while (*q && *q != '\n' && *q != '\r') q++;
1366 gchar *file, *locale_file = NULL;
1368 while (q > p && g_ascii_isspace(*q))
1370 Xalloca(escaped_utf8uri, q - p + 2,
1372 Xalloca(file, q - p + 2,
1375 strncpy(escaped_utf8uri, p, q - p + 1);
1376 escaped_utf8uri[q - p + 1] = '\0';
1377 decode_uri(file, escaped_utf8uri);
1379 * g_filename_from_uri() rejects escaped/locale encoded uri
1380 * string which come from Nautilus.
1382 if (g_utf8_validate(file, -1, NULL))
1384 = conv_codeset_strdup(
1387 conv_get_locale_charset_str());
1389 locale_file = g_strdup(file + 5);
1390 result = g_list_append(result, locale_file);
1394 p = strchr(p, '\n');
1401 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1404 static gint axtoi(const gchar *hexstr)
1406 gint hi, lo, result;
1409 if ('0' <= hi && hi <= '9') {
1412 if ('a' <= hi && hi <= 'f') {
1415 if ('A' <= hi && hi <= 'F') {
1420 if ('0' <= lo && lo <= '9') {
1423 if ('a' <= lo && lo <= 'f') {
1426 if ('A' <= lo && lo <= 'F') {
1429 result = lo + (16 * hi);
1433 gboolean is_uri_string(const gchar *str)
1435 while (str && *str && g_ascii_isspace(*str))
1437 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1438 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1439 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1440 g_ascii_strncasecmp(str, "www.", 4) == 0);
1443 gchar *get_uri_path(const gchar *uri)
1445 while (uri && *uri && g_ascii_isspace(*uri))
1447 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1448 return (gchar *)(uri + 7);
1449 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1450 return (gchar *)(uri + 8);
1451 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1452 return (gchar *)(uri + 6);
1454 return (gchar *)uri;
1457 gint get_uri_len(const gchar *str)
1461 if (is_uri_string(str)) {
1462 for (p = str; *p != '\0'; p++) {
1463 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1472 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1473 * plusses, and escape characters are used)
1475 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1477 gchar *dec = decoded_uri;
1478 const gchar *enc = encoded_uri;
1483 if (isxdigit((guchar)enc[0]) &&
1484 isxdigit((guchar)enc[1])) {
1490 if (with_plus && *enc == '+')
1502 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1504 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1507 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1509 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1510 decode_uri(buffer, encoded_uri);
1514 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1515 gchar **subject, gchar **body, gchar ***attach)
1519 const gchar *forbidden_uris[] = { ".gnupg/",
1523 gint num_attach = 0;
1524 gchar **my_att = NULL;
1526 Xstrdup_a(tmp_mailto, mailto, return -1);
1528 if (!strncmp(tmp_mailto, "mailto:", 7))
1531 p = strchr(tmp_mailto, '?');
1538 *to = decode_uri_gdup(tmp_mailto);
1540 my_att = g_malloc(sizeof(char *));
1544 gchar *field, *value;
1561 if (*value == '\0') continue;
1563 if (cc && !g_ascii_strcasecmp(field, "cc")) {
1565 *cc = decode_uri_gdup(value);
1567 gchar *tmp = decode_uri_gdup(value);
1568 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1572 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1574 *bcc = decode_uri_gdup(value);
1576 gchar *tmp = decode_uri_gdup(value);
1577 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1581 } else if (subject && !*subject &&
1582 !g_ascii_strcasecmp(field, "subject")) {
1583 *subject = decode_uri_gdup(value);
1584 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1585 *body = decode_uri_gdup(value);
1586 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1588 gchar *tmp = decode_uri_gdup(value);
1589 for (; forbidden_uris[i]; i++) {
1590 if (strstr(tmp, forbidden_uris[i])) {
1591 g_print("Refusing to attach '%s', potential private data leak\n",
1599 /* attach is correct */
1601 printf("realloc my_att %d\n", (num_attach+1));
1602 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1603 my_att[num_attach-1] = tmp;
1604 my_att[num_attach] = NULL;
1616 #include <windows.h>
1617 #ifndef CSIDL_APPDATA
1618 #define CSIDL_APPDATA 0x001a
1620 #ifndef CSIDL_LOCAL_APPDATA
1621 #define CSIDL_LOCAL_APPDATA 0x001c
1623 #ifndef CSIDL_FLAG_CREATE
1624 #define CSIDL_FLAG_CREATE 0x8000
1626 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1630 w32_strerror (int w32_errno)
1632 static char strerr[256];
1633 int ec = (int)GetLastError ();
1637 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1638 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1639 strerr, DIM (strerr)-1, NULL);
1643 static __inline__ void *
1644 dlopen (const char * name, int flag)
1646 void * hd = LoadLibrary (name);
1650 static __inline__ void *
1651 dlsym (void * hd, const char * sym)
1655 void * fnc = GetProcAddress (hd, sym);
1664 static __inline__ const char *
1667 return w32_strerror (0);
1671 static __inline__ int
1683 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1685 static int initialized;
1686 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1690 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1696 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1698 handle = dlopen (dllnames[i], RTLD_LAZY);
1701 func = dlsym (handle, "SHGetFolderPathA");
1712 return func (a,b,c,d,e);
1717 /* Returns a static string with the directroy from which the module
1718 has been loaded. Returns an empty string on error. */
1719 static char *w32_get_module_dir(void)
1721 static char *moddir;
1724 char name[MAX_PATH+10];
1727 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1730 p = strrchr (name, '\\');
1736 moddir = g_strdup (name);
1740 #endif /* G_OS_WIN32 */
1742 /* Return a static string with the locale dir. */
1743 const gchar *get_locale_dir(void)
1745 static gchar *loc_dir;
1749 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1750 "\\share\\locale", NULL);
1753 loc_dir = LOCALEDIR;
1759 const gchar *get_home_dir(void)
1762 static char home_dir[MAX_PATH] = "";
1764 if (home_dir[0] == '\0') {
1765 if (w32_shgetfolderpath
1766 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1767 NULL, 0, home_dir) < 0)
1768 strcpy (home_dir, "C:\\Sylpheed");
1772 static const gchar *homeenv = NULL;
1777 if (!homeenv && g_getenv("HOME") != NULL)
1778 homeenv = g_strdup(g_getenv("HOME"));
1780 homeenv = g_get_home_dir();
1786 static gchar *claws_rc_dir = NULL;
1787 static gboolean rc_dir_alt = FALSE;
1788 const gchar *get_rc_dir(void)
1792 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1795 return claws_rc_dir;
1798 void set_rc_dir(const gchar *dir)
1800 if (claws_rc_dir != NULL) {
1801 g_print("Error: rc_dir already set\n");
1804 if (g_path_is_absolute(dir))
1805 claws_rc_dir = g_strdup(dir);
1807 claws_rc_dir = g_strconcat(g_get_current_dir(),
1808 G_DIR_SEPARATOR_S, dir, NULL);
1810 debug_print("set rc_dir to %s\n", claws_rc_dir);
1811 if (!is_dir_exist(claws_rc_dir)) {
1812 if (make_dir_hier(claws_rc_dir) != 0) {
1813 g_print("Error: can't create %s\n",
1820 gboolean rc_dir_is_alt(void) {
1824 const gchar *get_mail_base_dir(void)
1827 static gchar *mail_base_dir = NULL;
1830 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1833 return mail_base_dir;
1835 return get_home_dir();
1840 const gchar *prefs_common_get_data_root(void);
1841 gchar *last_data_root = NULL;
1844 const gchar *get_news_cache_dir(void)
1846 static gchar *news_cache_dir = NULL;
1848 const gchar *data_root = prefs_common_get_data_root();
1849 if (strcmp2(data_root, last_data_root)) {
1850 g_free(news_cache_dir);
1851 news_cache_dir = NULL;
1854 if (!news_cache_dir)
1856 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1857 NEWS_CACHE_DIR, NULL);
1861 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1862 "Claws", G_DIR_SEPARATOR_S,
1863 g_get_user_name(), G_DIR_SEPARATOR_S,
1864 NEWS_CACHE_DIR, NULL);
1865 g_free(last_data_root);
1866 last_data_root = g_strdup(last_data_root);
1868 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1869 NEWS_CACHE_DIR, NULL);
1870 g_free(last_data_root);
1871 last_data_root = NULL;
1875 return news_cache_dir;
1878 const gchar *get_imap_cache_dir(void)
1880 static gchar *imap_cache_dir = NULL;
1882 const gchar *data_root = prefs_common_get_data_root();
1883 if (strcmp2(data_root, last_data_root)) {
1884 g_free(imap_cache_dir);
1885 imap_cache_dir = NULL;
1889 if (!imap_cache_dir)
1891 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1892 IMAP_CACHE_DIR, NULL);
1896 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1897 "Claws", G_DIR_SEPARATOR_S,
1898 g_get_user_name(), G_DIR_SEPARATOR_S,
1899 IMAP_CACHE_DIR, NULL);
1900 g_free(last_data_root);
1901 last_data_root = g_strdup(last_data_root);
1903 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904 IMAP_CACHE_DIR, NULL);
1905 g_free(last_data_root);
1906 last_data_root = NULL;
1911 return imap_cache_dir;
1914 const gchar *get_mime_tmp_dir(void)
1916 static gchar *mime_tmp_dir = NULL;
1919 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1920 MIME_TMP_DIR, NULL);
1922 return mime_tmp_dir;
1925 const gchar *get_template_dir(void)
1927 static gchar *template_dir = NULL;
1930 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1931 TEMPLATE_DIR, NULL);
1933 return template_dir;
1936 /* Return the default directory for Plugins. */
1937 const gchar *get_plugin_dir(void)
1940 static gchar *plugin_dir = NULL;
1943 plugin_dir = g_strconcat(w32_get_module_dir(),
1944 "\\lib\\claws-mail\\plugins\\",
1948 if (is_dir_exist(PLUGINDIR))
1951 static gchar *plugin_dir = NULL;
1953 plugin_dir = g_strconcat(get_rc_dir(),
1954 G_DIR_SEPARATOR_S, "plugins",
1955 G_DIR_SEPARATOR_S, NULL);
1961 const gchar *get_tmp_dir(void)
1963 static gchar *tmp_dir = NULL;
1966 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1972 gchar *get_tmp_file(void)
1975 static guint32 id = 0;
1977 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1978 get_tmp_dir(), G_DIR_SEPARATOR, id++);
1983 const gchar *get_domain_name(void)
1986 static gchar *domain_name = NULL;
1992 if (gethostname(hostname, sizeof(hostname)) != 0) {
1993 perror("gethostname");
1994 domain_name = "unknown";
1996 hostname[sizeof(hostname) - 1] = '\0';
1997 if ((hp = my_gethostbyname(hostname)) == NULL) {
1998 perror("gethostbyname");
1999 domain_name = g_strdup(hostname);
2001 domain_name = g_strdup(hp->h_name);
2004 debug_print("domain name = %s\n", domain_name);
2013 off_t get_file_size(const gchar *file)
2017 if (g_stat(file, &s) < 0) {
2018 FILE_OP_ERROR(file, "stat");
2025 time_t get_file_mtime(const gchar *file)
2029 if (g_stat(file, &s) < 0) {
2030 FILE_OP_ERROR(file, "stat");
2037 off_t get_file_size_as_crlf(const gchar *file)
2041 gchar buf[BUFFSIZE];
2043 if ((fp = g_fopen(file, "rb")) == NULL) {
2044 FILE_OP_ERROR(file, "fopen");
2048 while (fgets(buf, sizeof(buf), fp) != NULL) {
2050 size += strlen(buf) + 2;
2054 FILE_OP_ERROR(file, "fgets");
2063 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2070 if (g_stat(file, &s) < 0) {
2071 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2075 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2082 /* Test on whether FILE is a relative file name. This is
2083 * straightforward for Unix but more complex for Windows. */
2084 gboolean is_relative_filename(const gchar *file)
2089 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2090 return FALSE; /* Prefixed with a hostname - this can't
2091 * be a relative name. */
2093 if ( ((*file >= 'a' && *file <= 'z')
2094 || (*file >= 'A' && *file <= 'Z'))
2096 file += 2; /* Skip drive letter. */
2098 return !(*file == '\\' || *file == '/');
2100 return !(*file == G_DIR_SEPARATOR);
2105 gboolean is_dir_exist(const gchar *dir)
2110 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2113 gboolean is_file_entry_exist(const gchar *file)
2118 return g_file_test(file, G_FILE_TEST_EXISTS);
2121 gboolean dirent_is_regular_file(struct dirent *d)
2123 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2124 if (d->d_type == DT_REG)
2126 else if (d->d_type != DT_UNKNOWN)
2130 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2133 gint change_dir(const gchar *dir)
2135 gchar *prevdir = NULL;
2138 prevdir = g_get_current_dir();
2140 if (g_chdir(dir) < 0) {
2141 FILE_OP_ERROR(dir, "chdir");
2142 if (debug_mode) g_free(prevdir);
2144 } else if (debug_mode) {
2147 cwd = g_get_current_dir();
2148 if (strcmp(prevdir, cwd) != 0)
2149 g_print("current dir: %s\n", cwd);
2157 gint make_dir(const gchar *dir)
2159 if (g_mkdir(dir, S_IRWXU) < 0) {
2160 FILE_OP_ERROR(dir, "mkdir");
2163 if (g_chmod(dir, S_IRWXU) < 0)
2164 FILE_OP_ERROR(dir, "chmod");
2169 gint make_dir_hier(const gchar *dir)
2174 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2175 parent_dir = g_strndup(dir, p - dir);
2176 if (*parent_dir != '\0') {
2177 if (!is_dir_exist(parent_dir)) {
2178 if (make_dir(parent_dir) < 0) {
2187 if (!is_dir_exist(dir)) {
2188 if (make_dir(dir) < 0)
2195 gint remove_all_files(const gchar *dir)
2198 const gchar *dir_name;
2201 prev_dir = g_get_current_dir();
2203 if (g_chdir(dir) < 0) {
2204 FILE_OP_ERROR(dir, "chdir");
2209 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2210 g_warning("failed to open directory: %s\n", dir);
2215 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2216 if (g_unlink(dir_name) < 0)
2217 FILE_OP_ERROR(dir_name, "unlink");
2222 if (g_chdir(prev_dir) < 0) {
2223 FILE_OP_ERROR(prev_dir, "chdir");
2233 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2236 const gchar *dir_name;
2240 prev_dir = g_get_current_dir();
2242 if (g_chdir(dir) < 0) {
2243 FILE_OP_ERROR(dir, "chdir");
2248 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2249 g_warning("failed to open directory: %s\n", dir);
2254 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2255 file_no = to_number(dir_name);
2256 if (file_no > 0 && first <= file_no && file_no <= last) {
2257 if (is_dir_exist(dir_name))
2259 if (g_unlink(dir_name) < 0)
2260 FILE_OP_ERROR(dir_name, "unlink");
2266 if (g_chdir(prev_dir) < 0) {
2267 FILE_OP_ERROR(prev_dir, "chdir");
2277 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2280 const gchar *dir_name;
2284 prev_dir = g_get_current_dir();
2286 if (g_chdir(dir) < 0) {
2287 FILE_OP_ERROR(dir, "chdir");
2292 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2293 FILE_OP_ERROR(dir, "opendir");
2298 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2299 file_no = to_number(dir_name);
2300 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2301 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2302 if (is_dir_exist(dir_name))
2304 if (g_unlink(dir_name) < 0)
2305 FILE_OP_ERROR(dir_name, "unlink");
2311 if (g_chdir(prev_dir) < 0) {
2312 FILE_OP_ERROR(prev_dir, "chdir");
2322 gint remove_all_numbered_files(const gchar *dir)
2324 return remove_numbered_files(dir, 0, UINT_MAX);
2327 gint remove_dir_recursive(const gchar *dir)
2331 const gchar *dir_name;
2334 if (g_stat(dir, &s) < 0) {
2335 FILE_OP_ERROR(dir, "stat");
2336 if (ENOENT == errno) return 0;
2340 if (!S_ISDIR(s.st_mode)) {
2341 if (g_unlink(dir) < 0) {
2342 FILE_OP_ERROR(dir, "unlink");
2349 prev_dir = g_get_current_dir();
2350 /* g_print("prev_dir = %s\n", prev_dir); */
2352 if (!path_cmp(prev_dir, dir)) {
2354 if (g_chdir("..") < 0) {
2355 FILE_OP_ERROR(dir, "chdir");
2358 prev_dir = g_get_current_dir();
2361 if (g_chdir(dir) < 0) {
2362 FILE_OP_ERROR(dir, "chdir");
2367 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2368 g_warning("failed to open directory: %s\n", dir);
2374 /* remove all files in the directory */
2375 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2376 /* g_print("removing %s\n", dir_name); */
2378 if (is_dir_exist(dir_name)) {
2379 if (remove_dir_recursive(dir_name) < 0) {
2380 g_warning("can't remove directory\n");
2384 if (g_unlink(dir_name) < 0)
2385 FILE_OP_ERROR(dir_name, "unlink");
2391 if (g_chdir(prev_dir) < 0) {
2392 FILE_OP_ERROR(prev_dir, "chdir");
2399 if (g_rmdir(dir) < 0) {
2400 FILE_OP_ERROR(dir, "rmdir");
2407 gint rename_force(const gchar *oldpath, const gchar *newpath)
2410 if (!is_file_entry_exist(oldpath)) {
2414 if (is_file_exist(newpath)) {
2415 if (g_unlink(newpath) < 0)
2416 FILE_OP_ERROR(newpath, "unlink");
2419 return g_rename(oldpath, newpath);
2423 * Append src file body to the tail of dest file.
2424 * Now keep_backup has no effects.
2426 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2428 FILE *src_fp, *dest_fp;
2432 gboolean err = FALSE;
2434 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2435 FILE_OP_ERROR(src, "fopen");
2439 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2440 FILE_OP_ERROR(dest, "fopen");
2445 if (change_file_mode_rw(dest_fp, dest) < 0) {
2446 FILE_OP_ERROR(dest, "chmod");
2447 g_warning("can't change file mode\n");
2450 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2451 if (n_read < sizeof(buf) && ferror(src_fp))
2453 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2454 g_warning("writing to %s failed.\n", dest);
2462 if (ferror(src_fp)) {
2463 FILE_OP_ERROR(src, "fread");
2467 if (fclose(dest_fp) == EOF) {
2468 FILE_OP_ERROR(dest, "fclose");
2480 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2482 FILE *src_fp, *dest_fp;
2485 gchar *dest_bak = NULL;
2486 gboolean err = FALSE;
2488 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2489 FILE_OP_ERROR(src, "fopen");
2492 if (is_file_exist(dest)) {
2493 dest_bak = g_strconcat(dest, ".bak", NULL);
2494 if (rename_force(dest, dest_bak) < 0) {
2495 FILE_OP_ERROR(dest, "rename");
2502 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2503 FILE_OP_ERROR(dest, "fopen");
2506 if (rename_force(dest_bak, dest) < 0)
2507 FILE_OP_ERROR(dest_bak, "rename");
2513 if (change_file_mode_rw(dest_fp, dest) < 0) {
2514 FILE_OP_ERROR(dest, "chmod");
2515 g_warning("can't change file mode\n");
2518 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2519 if (n_read < sizeof(buf) && ferror(src_fp))
2521 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2522 g_warning("writing to %s failed.\n", dest);
2527 if (rename_force(dest_bak, dest) < 0)
2528 FILE_OP_ERROR(dest_bak, "rename");
2535 if (ferror(src_fp)) {
2536 FILE_OP_ERROR(src, "fread");
2540 if (fclose(dest_fp) == EOF) {
2541 FILE_OP_ERROR(dest, "fclose");
2548 if (rename_force(dest_bak, dest) < 0)
2549 FILE_OP_ERROR(dest_bak, "rename");
2555 if (keep_backup == FALSE && dest_bak)
2563 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2565 if (overwrite == FALSE && is_file_exist(dest)) {
2566 g_warning("move_file(): file %s already exists.", dest);
2570 if (rename_force(src, dest) == 0) return 0;
2572 if (EXDEV != errno) {
2573 FILE_OP_ERROR(src, "rename");
2577 if (copy_file(src, dest, FALSE) < 0) return -1;
2584 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2587 gint bytes_left, to_read;
2590 if (fseek(fp, offset, SEEK_SET) < 0) {
2595 bytes_left = length;
2596 to_read = MIN(bytes_left, sizeof(buf));
2598 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2599 if (n_read < to_read && ferror(fp))
2601 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2604 bytes_left -= n_read;
2605 if (bytes_left == 0)
2607 to_read = MIN(bytes_left, sizeof(buf));
2618 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2621 gboolean err = FALSE;
2623 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2624 FILE_OP_ERROR(dest, "fopen");
2628 if (change_file_mode_rw(dest_fp, dest) < 0) {
2629 FILE_OP_ERROR(dest, "chmod");
2630 g_warning("can't change file mode\n");
2633 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2636 if (!err && fclose(dest_fp) == EOF) {
2637 FILE_OP_ERROR(dest, "fclose");
2642 g_warning("writing to %s failed.\n", dest);
2650 /* convert line endings into CRLF. If the last line doesn't end with
2651 * linebreak, add it.
2653 gchar *canonicalize_str(const gchar *str)
2659 for (p = str; *p != '\0'; ++p) {
2666 if (p == str || *(p - 1) != '\n')
2669 out = outp = g_malloc(new_len + 1);
2670 for (p = str; *p != '\0'; ++p) {
2677 if (p == str || *(p - 1) != '\n') {
2686 gint canonicalize_file(const gchar *src, const gchar *dest)
2688 FILE *src_fp, *dest_fp;
2689 gchar buf[BUFFSIZE];
2691 gboolean err = FALSE;
2692 gboolean last_linebreak = FALSE;
2694 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2695 FILE_OP_ERROR(src, "fopen");
2699 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2700 FILE_OP_ERROR(dest, "fopen");
2705 if (change_file_mode_rw(dest_fp, dest) < 0) {
2706 FILE_OP_ERROR(dest, "chmod");
2707 g_warning("can't change file mode\n");
2710 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2714 if (len == 0) break;
2715 last_linebreak = FALSE;
2717 if (buf[len - 1] != '\n') {
2718 last_linebreak = TRUE;
2719 r = fputs(buf, dest_fp);
2720 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2721 r = fputs(buf, dest_fp);
2724 r = fwrite(buf, 1, len - 1, dest_fp);
2729 r = fputs("\r\n", dest_fp);
2733 g_warning("writing to %s failed.\n", dest);
2741 if (last_linebreak == TRUE) {
2742 if (fputs("\r\n", dest_fp) == EOF)
2746 if (ferror(src_fp)) {
2747 FILE_OP_ERROR(src, "fgets");
2751 if (fclose(dest_fp) == EOF) {
2752 FILE_OP_ERROR(dest, "fclose");
2764 gint canonicalize_file_replace(const gchar *file)
2768 tmp_file = get_tmp_file();
2770 if (canonicalize_file(file, tmp_file) < 0) {
2775 if (move_file(tmp_file, file, TRUE) < 0) {
2776 g_warning("can't replace %s .\n", file);
2786 gchar *normalize_newlines(const gchar *str)
2788 const gchar *p = str;
2791 out = outp = g_malloc(strlen(str) + 1);
2792 for (p = str; *p != '\0'; ++p) {
2794 if (*(p + 1) != '\n')
2805 gchar *get_outgoing_rfc2822_str(FILE *fp)
2807 gchar buf[BUFFSIZE];
2811 str = g_string_new(NULL);
2813 /* output header part */
2814 while (fgets(buf, sizeof(buf), fp) != NULL) {
2816 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2823 else if (next != ' ' && next != '\t') {
2827 if (fgets(buf, sizeof(buf), fp) == NULL)
2831 g_string_append(str, buf);
2832 g_string_append(str, "\r\n");
2838 /* output body part */
2839 while (fgets(buf, sizeof(buf), fp) != NULL) {
2842 g_string_append_c(str, '.');
2843 g_string_append(str, buf);
2844 g_string_append(str, "\r\n");
2848 g_string_free(str, FALSE);
2854 * Create a new boundary in a way that it is very unlikely that this
2855 * will occur in the following text. It would be easy to ensure
2856 * uniqueness if everything is either quoted-printable or base64
2857 * encoded (note that conversion is allowed), but because MIME bodies
2858 * may be nested, it may happen that the same boundary has already
2861 * boundary := 0*69<bchars> bcharsnospace
2862 * bchars := bcharsnospace / " "
2863 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2864 * "+" / "_" / "," / "-" / "." /
2865 * "/" / ":" / "=" / "?"
2867 * some special characters removed because of buggy MTAs
2870 gchar *generate_mime_boundary(const gchar *prefix)
2872 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2873 "abcdefghijklmnopqrstuvwxyz"
2878 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2879 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2882 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2886 gint change_file_mode_rw(FILE *fp, const gchar *file)
2889 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2891 return g_chmod(file, S_IRUSR|S_IWUSR);
2895 FILE *my_tmpfile(void)
2897 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2898 const gchar suffix[] = ".XXXXXX";
2899 const gchar *tmpdir;
2901 const gchar *progname;
2908 tmpdir = get_tmp_dir();
2909 tmplen = strlen(tmpdir);
2910 progname = g_get_prgname();
2911 if (progname == NULL)
2912 progname = "claws-mail";
2913 proglen = strlen(progname);
2914 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2917 memcpy(fname, tmpdir, tmplen);
2918 fname[tmplen] = G_DIR_SEPARATOR;
2919 memcpy(fname + tmplen + 1, progname, proglen);
2920 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2922 fd = mkstemp(fname);
2929 /* verify that we can write in the file after unlinking */
2930 if (write(fd, buf, 1) < 0) {
2937 fp = fdopen(fd, "w+b");
2945 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2950 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2954 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2955 dir, G_DIR_SEPARATOR);
2956 fd = mkstemp_name(template, filename);
2959 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2960 fd = mkstemp(*filename);
2962 return fdopen(fd, "w+");
2965 FILE *str_open_as_stream(const gchar *str)
2970 g_return_val_if_fail(str != NULL, NULL);
2974 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2979 if (len == 0) return fp;
2981 if (fwrite(str, 1, len, fp) != len) {
2982 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2991 gint str_write_to_file(const gchar *str, const gchar *file)
2996 g_return_val_if_fail(str != NULL, -1);
2997 g_return_val_if_fail(file != NULL, -1);
2999 if ((fp = g_fopen(file, "wb")) == NULL) {
3000 FILE_OP_ERROR(file, "fopen");
3010 if (fwrite(str, 1, len, fp) != len) {
3011 FILE_OP_ERROR(file, "fwrite");
3017 if (fclose(fp) == EOF) {
3018 FILE_OP_ERROR(file, "fclose");
3026 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3033 g_return_val_if_fail(fp != NULL, NULL);
3035 array = g_byte_array_new();
3037 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3038 if (n_read < sizeof(buf) && ferror(fp))
3040 g_byte_array_append(array, buf, n_read);
3044 FILE_OP_ERROR("file stream", "fread");
3045 g_byte_array_free(array, TRUE);
3050 g_byte_array_append(array, buf, 1);
3051 str = (gchar *)array->data;
3052 g_byte_array_free(array, FALSE);
3054 if (recode && !g_utf8_validate(str, -1, NULL)) {
3055 const gchar *src_codeset, *dest_codeset;
3057 src_codeset = conv_get_locale_charset_str();
3058 dest_codeset = CS_UTF_8;
3059 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3067 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3072 g_return_val_if_fail(file != NULL, NULL);
3074 if ((fp = g_fopen(file, "rb")) == NULL) {
3075 FILE_OP_ERROR(file, "fopen");
3079 str = file_read_stream_to_str_full(fp, recode);
3086 gchar *file_read_to_str(const gchar *file)
3088 file_read_to_str_full(file, TRUE);
3090 gchar *file_read_stream_to_str(FILE *fp)
3092 file_read_stream_to_str_full(fp, TRUE);
3095 gchar *file_read_to_str_no_recode(const gchar *file)
3097 file_read_to_str_full(file, FALSE);
3099 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3101 file_read_stream_to_str_full(fp, FALSE);
3104 char *fgets_crlf(char *buf, int size, FILE *stream)
3106 gboolean is_cr = FALSE;
3107 gboolean last_was_cr = FALSE;
3112 while (--size > 0 && (c = getc(stream)) != EOF)
3115 is_cr = (c == '\r');
3125 last_was_cr = is_cr;
3127 if (c == EOF && cs == buf)
3135 static gint execute_async(gchar *const argv[])
3137 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3139 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3140 NULL, NULL, NULL, FALSE) == FALSE) {
3141 g_warning("Couldn't execute command: %s\n", argv[0]);
3148 static gint execute_sync(gchar *const argv[])
3152 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3154 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3155 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3156 g_warning("Couldn't execute command: %s\n", argv[0]);
3161 if (WIFEXITED(status))
3162 return WEXITSTATUS(status);
3170 gint execute_command_line(const gchar *cmdline, gboolean async)
3175 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3177 argv = strsplit_with_quote(cmdline, " ", 0);
3180 ret = execute_async(argv);
3182 ret = execute_sync(argv);
3189 gchar *get_command_output(const gchar *cmdline)
3191 gchar *child_stdout;
3194 g_return_val_if_fail(cmdline != NULL, NULL);
3196 debug_print("get_command_output(): executing: %s\n", cmdline);
3198 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3200 g_warning("Couldn't execute command: %s\n", cmdline);
3204 return child_stdout;
3207 static gint is_unchanged_uri_char(char c)
3218 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3224 for(i = 0; i < strlen(uri) ; i++) {
3225 if (is_unchanged_uri_char(uri[i])) {
3226 if (k + 2 >= bufsize)
3228 encoded_uri[k++] = uri[i];
3231 char * hexa = "0123456789ABCDEF";
3233 if (k + 4 >= bufsize)
3235 encoded_uri[k++] = '%';
3236 encoded_uri[k++] = hexa[uri[i] / 16];
3237 encoded_uri[k++] = hexa[uri[i] % 16];
3243 gint open_uri(const gchar *uri, const gchar *cmdline)
3246 gchar buf[BUFFSIZE];
3248 gchar encoded_uri[BUFFSIZE];
3249 g_return_val_if_fail(uri != NULL, -1);
3251 /* an option to choose whether to use encode_uri or not ? */
3252 encode_uri(encoded_uri, BUFFSIZE, uri);
3255 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3256 !strchr(p + 2, '%'))
3257 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3260 g_warning("Open URI command line is invalid "
3261 "(there must be only one '%%s'): %s",
3263 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3266 execute_command_line(buf, TRUE);
3268 extern osso_context_t *get_osso_context(void);
3269 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3270 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3271 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3276 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3278 gchar buf[BUFFSIZE];
3281 g_return_val_if_fail(filepath != NULL, -1);
3284 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3285 !strchr(p + 2, '%'))
3286 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3289 g_warning("Open Text Editor command line is invalid "
3290 "(there must be only one '%%s'): %s",
3292 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3295 execute_command_line(buf, TRUE);
3300 time_t remote_tzoffset_sec(const gchar *zone)
3302 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3308 time_t remoteoffset;
3310 strncpy(zone3, zone, 3);
3314 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3315 (c == '+' || c == '-')) {
3316 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3318 remoteoffset = -remoteoffset;
3319 } else if (!strncmp(zone, "UT" , 2) ||
3320 !strncmp(zone, "GMT", 2)) {
3322 } else if (strlen(zone3) == 3) {
3323 for (p = ustzstr; *p != '\0'; p += 3) {
3324 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3325 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3326 remoteoffset = iustz * 3600;
3332 } else if (strlen(zone3) == 1) {
3334 case 'Z': remoteoffset = 0; break;
3335 case 'A': remoteoffset = -1; break;
3336 case 'B': remoteoffset = -2; break;
3337 case 'C': remoteoffset = -3; break;
3338 case 'D': remoteoffset = -4; break;
3339 case 'E': remoteoffset = -5; break;
3340 case 'F': remoteoffset = -6; break;
3341 case 'G': remoteoffset = -7; break;
3342 case 'H': remoteoffset = -8; break;
3343 case 'I': remoteoffset = -9; break;
3344 case 'K': remoteoffset = -10; break; /* J is not used */
3345 case 'L': remoteoffset = -11; break;
3346 case 'M': remoteoffset = -12; break;
3347 case 'N': remoteoffset = 1; break;
3348 case 'O': remoteoffset = 2; break;
3349 case 'P': remoteoffset = 3; break;
3350 case 'Q': remoteoffset = 4; break;
3351 case 'R': remoteoffset = 5; break;
3352 case 'S': remoteoffset = 6; break;
3353 case 'T': remoteoffset = 7; break;
3354 case 'U': remoteoffset = 8; break;
3355 case 'V': remoteoffset = 9; break;
3356 case 'W': remoteoffset = 10; break;
3357 case 'X': remoteoffset = 11; break;
3358 case 'Y': remoteoffset = 12; break;
3359 default: remoteoffset = 0; break;
3361 remoteoffset = remoteoffset * 3600;
3365 return remoteoffset;
3368 time_t tzoffset_sec(time_t *now)
3372 struct tm buf1, buf2;
3374 gmt = *gmtime_r(now, &buf1);
3375 lt = localtime_r(now, &buf2);
3377 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3379 if (lt->tm_year < gmt.tm_year)
3381 else if (lt->tm_year > gmt.tm_year)
3383 else if (lt->tm_yday < gmt.tm_yday)
3385 else if (lt->tm_yday > gmt.tm_yday)
3388 if (off >= 24 * 60) /* should be impossible */
3389 off = 23 * 60 + 59; /* if not, insert silly value */
3390 if (off <= -24 * 60)
3391 off = -(23 * 60 + 59);
3396 /* calculate timezone offset */
3397 gchar *tzoffset(time_t *now)
3399 static gchar offset_string[6];
3403 struct tm buf1, buf2;
3405 gmt = *gmtime_r(now, &buf1);
3406 lt = localtime_r(now, &buf2);
3408 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3410 if (lt->tm_year < gmt.tm_year)
3412 else if (lt->tm_year > gmt.tm_year)
3414 else if (lt->tm_yday < gmt.tm_yday)
3416 else if (lt->tm_yday > gmt.tm_yday)
3424 if (off >= 24 * 60) /* should be impossible */
3425 off = 23 * 60 + 59; /* if not, insert silly value */
3427 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3429 return offset_string;
3432 void get_rfc822_date(gchar *buf, gint len)
3436 gchar day[4], mon[4];
3437 gint dd, hh, mm, ss, yyyy;
3439 gchar buf2[BUFFSIZE];
3442 lt = localtime_r(&t, &buf1);
3444 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3445 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3447 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3448 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3451 void debug_set_mode(gboolean mode)
3456 gboolean debug_get_mode(void)
3461 void debug_print_real(const gchar *format, ...)
3464 gchar buf[BUFFSIZE];
3466 if (!debug_mode) return;
3468 va_start(args, format);
3469 g_vsnprintf(buf, sizeof(buf), format, args);
3476 const char * debug_srcname(const char *file)
3478 const char *s = strrchr (file, '/');
3483 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3485 if (subject == NULL)
3488 subject += subject_get_prefix_length(subject);
3490 return g_hash_table_lookup(subject_table, subject);
3493 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3496 if (subject == NULL || *subject == 0)
3498 subject += subject_get_prefix_length(subject);
3499 g_hash_table_insert(subject_table, subject, data);
3502 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3504 if (subject == NULL)
3507 subject += subject_get_prefix_length(subject);
3508 g_hash_table_remove(subject_table, subject);
3512 *\brief Check if a string is prefixed with known (combinations)
3513 * of prefixes. The function assumes that each prefix
3514 * is terminated by zero or exactly _one_ space.
3516 *\param str String to check for a prefixes
3518 *\return int Number of chars in the prefix that should be skipped
3519 * for a "clean" subject line. If no prefix was found, 0
3522 int subject_get_prefix_length(const gchar *subject)
3524 /*!< Array with allowable reply prefixes regexps. */
3525 static const gchar * const prefixes[] = {
3526 "Re\\:", /* "Re:" */
3527 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3528 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3529 "Aw\\:", /* "Aw:" (German) */
3530 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3531 "Res\\:", /* "Res:" (Brazilian Outlook) */
3532 "Fw\\:", /* "Fw:" Forward */
3533 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3534 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3535 "Rif\\:", /* "Rif:" (Italian Outlook) */
3536 "Sv\\:", /* "Sv" (Norwegian) */
3537 "Vs\\:", /* "Vs" (Norwegian) */
3538 "Ad\\:", /* "Ad" (Norwegian) */
3539 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3542 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3545 static regex_t regex;
3546 static gboolean init_;
3548 if (!subject) return 0;
3549 if (!*subject) return 0;
3552 GString *s = g_string_new("");
3554 for (n = 0; n < PREFIXES; n++)
3555 /* Terminate each prefix regexpression by a
3556 * "\ ?" (zero or ONE space), and OR them */
3557 g_string_append_printf(s, "(%s\\ ?)%s",
3562 g_string_prepend(s, "(");
3563 g_string_append(s, ")+"); /* match at least once */
3564 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3567 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3568 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3569 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3570 debug_print("Error compiling regexp %s\n", s->str);
3571 g_string_free(s, TRUE);
3575 g_string_free(s, TRUE);
3579 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3585 static guint g_stricase_hash(gconstpointer gptr)
3587 guint hash_result = 0;
3590 for (str = gptr; str && *str; str++) {
3591 hash_result += toupper(*str);
3597 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3599 const char *str1 = gptr1;
3600 const char *str2 = gptr2;
3602 return !strcasecmp(str1, str2);
3605 gint g_int_compare(gconstpointer a, gconstpointer b)
3607 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3610 gchar *generate_msgid(gchar *buf, gint len)
3618 lt = localtime_r(&t, &buft);
3620 if (strcmp(buf, "") == 0) {
3621 addr = g_strconcat("@", get_domain_name(), NULL);
3624 addr = g_strconcat("@", buf, NULL);
3627 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3628 lt->tm_year + 1900, lt->tm_mon + 1,
3629 lt->tm_mday, lt->tm_hour,
3630 lt->tm_min, lt->tm_sec,
3631 (guint) rand(), addr);
3638 quote_cmd_argument()
3640 return a quoted string safely usable in argument of a command.
3642 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3645 gint quote_cmd_argument(gchar * result, guint size,
3655 for(p = path ; * p != '\0' ; p ++) {
3657 if (isalnum((guchar)*p) || (* p == '/')) {
3658 if (remaining > 0) {
3664 result[size - 1] = '\0';
3669 if (remaining >= 2) {
3677 result[size - 1] = '\0';
3682 if (remaining > 0) {
3686 result[size - 1] = '\0';
3700 static void g_node_map_recursive(GNode *node, gpointer data)
3702 GNodeMapData *mapdata = (GNodeMapData *) data;
3704 GNodeMapData newmapdata;
3707 newdata = mapdata->func(node->data, mapdata->data);
3708 if (newdata != NULL) {
3709 newnode = g_node_new(newdata);
3710 g_node_append(mapdata->parent, newnode);
3712 newmapdata.parent = newnode;
3713 newmapdata.func = mapdata->func;
3714 newmapdata.data = mapdata->data;
3716 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3720 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3723 GNodeMapData mapdata;
3725 g_return_val_if_fail(node != NULL, NULL);
3726 g_return_val_if_fail(func != NULL, NULL);
3728 root = g_node_new(func(node->data, data));
3730 mapdata.parent = root;
3731 mapdata.func = func;
3732 mapdata.data = data;
3734 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3739 #define HEX_TO_INT(val, hex) \
3743 if ('0' <= c && c <= '9') { \
3745 } else if ('a' <= c && c <= 'f') { \
3746 val = c - 'a' + 10; \
3747 } else if ('A' <= c && c <= 'F') { \
3748 val = c - 'A' + 10; \
3754 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3761 if (hi == -1 || lo == -1)
3764 *out = (hi << 4) + lo;
3768 #define INT_TO_HEX(hex, val) \
3771 hex = '0' + (val); \
3773 hex = 'A' + (val) - 10; \
3776 void get_hex_str(gchar *out, guchar ch)
3780 INT_TO_HEX(hex, ch >> 4);
3782 INT_TO_HEX(hex, ch & 0x0f);
3788 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3790 #define G_PRINT_REF g_print
3794 *\brief Register ref counted pointer. It is based on GBoxed, so should
3795 * work with anything that uses the GType system. The semantics
3796 * are similar to a C++ auto pointer, with the exception that
3797 * C doesn't have automatic closure (calling destructors) when
3798 * exiting a block scope.
3799 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3800 * function directly.
3802 *\return GType A GType type.
3804 GType g_auto_pointer_register(void)
3806 static GType auto_pointer_type;
3807 if (!auto_pointer_type)
3809 g_boxed_type_register_static
3810 ("G_TYPE_AUTO_POINTER",
3811 (GBoxedCopyFunc) g_auto_pointer_copy,
3812 (GBoxedFreeFunc) g_auto_pointer_free);
3813 return auto_pointer_type;
3817 *\brief Structure with g_new() allocated pointer guarded by the
3820 typedef struct AutoPointerRef {
3821 void (*free) (gpointer);
3827 *\brief The auto pointer opaque structure that references the
3828 * pointer guard block.
3830 typedef struct AutoPointer {
3831 AutoPointerRef *ref;
3832 gpointer ptr; /*!< access to protected pointer */
3836 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
3840 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3841 * ... when assigning, copying and freeing storage elements
3843 * gtk_list_store_new(N_S_COLUMNS,
3844 * G_TYPE_AUTO_POINTER,
3848 * Template *precious_data = g_new0(Template, 1);
3849 * g_pointer protect = g_auto_pointer_new(precious_data);
3851 * gtk_list_store_set(container, &iter,
3855 * ... the gtk_list_store has copied the pointer and
3856 * ... incremented its reference count, we should free
3857 * ... the auto pointer (in C++ a destructor would do
3858 * ... this for us when leaving block scope)
3860 * g_auto_pointer_free(protect);
3862 * ... gtk_list_store_set() now manages the data. When
3863 * ... *explicitly* requesting a pointer from the list
3864 * ... store, don't forget you get a copy that should be
3865 * ... freed with g_auto_pointer_free() eventually.
3869 *\param pointer Pointer to be guarded.
3871 *\return GAuto * Pointer that should be used in containers with
3874 GAuto *g_auto_pointer_new(gpointer p)
3876 AutoPointerRef *ref;
3882 ref = g_new0(AutoPointerRef, 1);
3883 ptr = g_new0(AutoPointer, 1);
3893 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3899 *\brief Allocate an autopointer using the passed \a free function to
3900 * free the guarded pointer
3902 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3909 aptr = g_auto_pointer_new(p);
3910 aptr->ref->free = free_;
3914 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3916 if (auto_ptr == NULL)
3918 return ((AutoPointer *) auto_ptr)->ptr;
3922 *\brief Copies an auto pointer by. It's mostly not necessary
3923 * to call this function directly, unless you copy/assign
3924 * the guarded pointer.
3926 *\param auto_ptr Auto pointer returned by previous call to
3927 * g_auto_pointer_new_XXX()
3929 *\return gpointer An auto pointer
3931 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3934 AutoPointerRef *ref;
3937 if (auto_ptr == NULL)
3942 newp = g_new0(AutoPointer, 1);
3945 newp->ptr = ref->pointer;
3949 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3955 *\brief Free an auto pointer
3957 void g_auto_pointer_free(GAuto *auto_ptr)
3960 AutoPointerRef *ref;
3962 if (auto_ptr == NULL)
3968 if (--(ref->cnt) == 0) {
3970 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3972 ref->free(ref->pointer);
3977 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3982 void replace_returns(gchar *str)
3987 while (strstr(str, "\n")) {
3988 *strstr(str, "\n") = ' ';
3990 while (strstr(str, "\r")) {
3991 *strstr(str, "\r") = ' ';
3995 /* get_uri_part() - retrieves a URI starting from scanpos.
3996 Returns TRUE if succesful */
3997 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
3998 const gchar **bp, const gchar **ep, gboolean hdr)
4001 gint parenthese_cnt = 0;
4003 g_return_val_if_fail(start != NULL, FALSE);
4004 g_return_val_if_fail(scanpos != NULL, FALSE);
4005 g_return_val_if_fail(bp != NULL, FALSE);
4006 g_return_val_if_fail(ep != NULL, FALSE);
4010 /* find end point of URI */
4011 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4012 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4013 !IS_ASCII(*(const guchar *)ep_) ||
4014 strchr("[]{}<>\"", *ep_)) {
4016 } else if (strchr("(", *ep_)) {
4018 } else if (strchr(")", *ep_)) {
4019 if (parenthese_cnt > 0)
4026 /* no punctuation at end of string */
4028 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4029 * should pass some URI type to this function and decide on that whether
4030 * to perform punctuation stripping */
4032 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4034 for (; ep_ - 1 > scanpos + 1 &&
4035 IS_REAL_PUNCT(*(ep_ - 1));
4039 #undef IS_REAL_PUNCT
4046 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4048 while (bp && *bp && g_ascii_isspace(*bp))
4050 return g_strndup(bp, ep - bp);
4053 /* valid mail address characters */
4054 #define IS_RFC822_CHAR(ch) \
4058 !g_ascii_isspace(ch) && \
4059 !strchr("(),;<>\"", (ch)))
4061 /* alphabet and number within 7bit ASCII */
4062 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4063 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4065 static GHashTable *create_domain_tab(void)
4067 static const gchar *toplvl_domains [] = {
4069 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4070 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4071 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4072 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4073 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4074 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4075 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4076 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4077 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4078 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4079 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4080 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4081 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4082 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4083 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4084 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4085 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4086 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4087 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4088 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4089 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4090 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4091 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4092 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4093 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4094 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4097 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4099 g_return_val_if_fail(htab, NULL);
4100 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4101 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4105 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4107 const gint MAX_LVL_DOM_NAME_LEN = 6;
4108 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4109 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4112 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4115 for (p = buf; p < m && first < last; *p++ = *first++)
4119 return g_hash_table_lookup(tab, buf) != NULL;
4122 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4123 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4124 const gchar **bp, const gchar **ep, gboolean hdr)
4126 /* more complex than the uri part because we need to scan back and forward starting from
4127 * the scan position. */
4128 gboolean result = FALSE;
4129 const gchar *bp_ = NULL;
4130 const gchar *ep_ = NULL;
4131 static GHashTable *dom_tab;
4132 const gchar *last_dot = NULL;
4133 const gchar *prelast_dot = NULL;
4134 const gchar *last_tld_char = NULL;
4136 /* the informative part of the email address (describing the name
4137 * of the email address owner) may contain quoted parts. the
4138 * closure stack stores the last encountered quotes. */
4139 gchar closure_stack[128];
4140 gchar *ptr = closure_stack;
4142 g_return_val_if_fail(start != NULL, FALSE);
4143 g_return_val_if_fail(scanpos != NULL, FALSE);
4144 g_return_val_if_fail(bp != NULL, FALSE);
4145 g_return_val_if_fail(ep != NULL, FALSE);
4148 const gchar *start_quote = NULL;
4149 const gchar *end_quote = NULL;
4151 /* go to the real start */
4152 if (start[0] == ',')
4154 if (start[0] == ';')
4156 while (start[0] == '\n' || start[0] == '\r')
4158 while (start[0] == ' ' || start[0] == '\t')
4163 /* check if there are quotes (to skip , in them) */
4164 if (*start == '"') {
4165 start_quote = start;
4167 end_quote = strstr(start, "\"");
4173 /* skip anything between quotes */
4174 if (start_quote && end_quote) {
4179 /* find end (either , or ; or end of line) */
4180 if (strstr(start, ",") && strstr(start, ";"))
4181 *ep = strstr(start,",") < strstr(start, ";")
4182 ? strstr(start, ",") : strstr(start, ";");
4183 else if (strstr(start, ","))
4184 *ep = strstr(start, ",");
4185 else if (strstr(start, ";"))
4186 *ep = strstr(start, ";");
4188 *ep = start+strlen(start);
4190 /* go back to real start */
4191 if (start_quote && end_quote) {
4192 start = start_quote;
4195 /* check there's still an @ in that, or search
4196 * further if possible */
4197 if (strstr(start, "@") && strstr(start, "@") < *ep)
4199 else if (*ep < start+strlen(start)) {
4202 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4210 dom_tab = create_domain_tab();
4211 g_return_val_if_fail(dom_tab, FALSE);
4213 /* scan start of address */
4214 for (bp_ = scanpos - 1;
4215 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4218 /* TODO: should start with an alnum? */
4220 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4223 if (bp_ != scanpos) {
4224 /* scan end of address */
4225 for (ep_ = scanpos + 1;
4226 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4228 prelast_dot = last_dot;