2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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>
67 # include <tablet-browser-interface.h>
69 # include <osso-browser-interface.h>
75 #include "../codeconv.h"
79 static gboolean debug_mode = FALSE;
81 static GSList *tempfiles=NULL;
84 /* Return true if we are running as root. This function should beused
85 instead of getuid () == 0. */
86 gboolean superuser_p (void)
89 return w32_is_administrator ();
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
101 if (G_WIN32_HAVE_WIDECHAR_API()) {
106 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
112 retval = _wchdir(wpath);
124 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125 if (cp_path == NULL) {
130 retval = chdir(cp_path);
143 gint g_chmod(const gchar *path, gint mode)
146 if (G_WIN32_HAVE_WIDECHAR_API()) {
151 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
157 retval = _wchmod(wpath, mode);
169 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170 if (cp_path == NULL) {
175 retval = chmod(cp_path, mode);
184 return chmod(path, mode);
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
191 char *name = g_win32_locale_filename_from_utf8(filename);
192 FILE* fp = fopen(name, mode);
196 return fopen(filename, mode);
199 int g_open(const gchar *filename, int flags, int mode)
202 char *name = g_win32_locale_filename_from_utf8(filename);
203 int fd = open(name, flags, mode);
207 return open(filename, flags, mode);
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
214 gint mkstemp_name(gchar *template, gchar **name_used)
216 static gulong count=0; /* W32-_mktemp only supports up to 27
220 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222 (S_IRUSR | S_IWUSR));
224 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
226 perror(g_strdup_printf("cant create %s",*name_used));
232 #endif /* G_OS_WIN32 */
235 gint mkstemp(gchar *template)
238 gint res = mkstemp_name(template, &dummyname);
242 #endif /* G_OS_WIN32 */
244 void list_free_strings(GList *list)
246 list = g_list_first(list);
248 while (list != NULL) {
254 void slist_free_strings(GSList *list)
256 while (list != NULL) {
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
267 void hash_free_strings(GHashTable *table)
269 g_hash_table_foreach(table, hash_free_strings_func, NULL);
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
274 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
277 guint str_case_hash(gconstpointer key)
279 const gchar *p = key;
283 h = g_ascii_tolower(h);
284 for (p += 1; *p != '\0'; p++)
285 h = (h << 5) - h + g_ascii_tolower(*p);
291 void ptr_array_free_strings(GPtrArray *array)
296 cm_return_if_fail(array != NULL);
298 for (i = 0; i < array->len; i++) {
299 str = g_ptr_array_index(array, i);
304 gboolean str_find(const gchar *haystack, const gchar *needle)
306 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
311 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
314 gint to_number(const gchar *nstr)
316 register const gchar *p;
318 if (*nstr == '\0') return -1;
320 for (p = nstr; *p != '\0'; p++)
321 if (!g_ascii_isdigit(*p)) return -1;
326 /* convert integer into string,
327 nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
330 g_snprintf(nstr, 11, "%d", n);
334 /* convert integer into string */
337 static gchar nstr[11];
339 return itos_buf(nstr, n);
342 #define divide(num,divisor,i,d) \
344 i = num >> divisor; \
345 d = num & ((1<<divisor)-1); \
346 d = (d*100) >> divisor; \
351 * \brief Convert a given size in bytes in a human-readable string
353 * \param size The size expressed in bytes to convert in string
354 * \return The string that respresents the size in an human-readable way
356 gchar *to_human_readable(goffset size)
358 static gchar str[14];
359 static gchar *b_format = NULL, *kb_format = NULL,
360 *mb_format = NULL, *gb_format = NULL;
361 register int t = 0, r = 0;
362 if (b_format == NULL) {
364 kb_format = _("%d.%02dKB");
365 mb_format = _("%d.%02dMB");
366 gb_format = _("%.2fGB");
369 if (size < (goffset)1024) {
370 g_snprintf(str, sizeof(str), b_format, (gint)size);
372 } else if (size >> 10 < (goffset)1024) {
373 divide(size, 10, t, r);
374 g_snprintf(str, sizeof(str), kb_format, t, r);
376 } else if (size >> 20 < (goffset)1024) {
377 divide(size, 20, t, r);
378 g_snprintf(str, sizeof(str), mb_format, t, r);
381 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
386 /* strcmp with NULL-checking */
387 gint strcmp2(const gchar *s1, const gchar *s2)
389 if (s1 == NULL || s2 == NULL)
392 return strcmp(s1, s2);
394 /* strstr with NULL-checking */
395 gchar *strstr2(const gchar *s1, const gchar *s2)
397 if (s1 == NULL || s2 == NULL)
400 return strstr(s1, s2);
403 gint path_cmp(const gchar *s1, const gchar *s2)
408 gchar *s1buf, *s2buf;
411 if (s1 == NULL || s2 == NULL) return -1;
412 if (*s1 == '\0' || *s2 == '\0') return -1;
415 s1buf = g_strdup (s1);
416 s2buf = g_strdup (s2);
417 subst_char (s1buf, '/', G_DIR_SEPARATOR);
418 subst_char (s2buf, '/', G_DIR_SEPARATOR);
421 #endif /* !G_OS_WIN32 */
426 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
427 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
429 rc = strncmp(s1, s2, MAX(len1, len2));
433 #endif /* !G_OS_WIN32 */
437 /* remove trailing return code */
438 gchar *strretchomp(gchar *str)
442 if (!*str) return str;
444 for (s = str + strlen(str) - 1;
445 s >= str && (*s == '\n' || *s == '\r');
452 /* remove trailing character */
453 gchar *strtailchomp(gchar *str, gchar tail_char)
457 if (!*str) return str;
458 if (tail_char == '\0') return str;
460 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
466 /* remove CR (carriage return) */
467 gchar *strcrchomp(gchar *str)
471 if (!*str) return str;
473 s = str + strlen(str) - 1;
474 if (*s == '\n' && s > str && *(s - 1) == '\r') {
482 gint file_strip_crs(const gchar *file)
484 FILE *fp = NULL, *outfp = NULL;
486 gchar *out = get_tmp_file();
490 fp = g_fopen(file, "rb");
494 outfp = g_fopen(out, "wb");
500 while (fgets(buf, sizeof (buf), fp) != NULL) {
502 if (fputs(buf, outfp) == EOF) {
510 if (fclose(outfp) == EOF) {
514 if (move_file(out, file, TRUE) < 0)
526 /* Similar to `strstr' but this function ignores the case of both strings. */
527 gchar *strcasestr(const gchar *haystack, const gchar *needle)
529 register size_t haystack_len, needle_len;
531 haystack_len = strlen(haystack);
532 needle_len = strlen(needle);
534 if (haystack_len < needle_len || needle_len == 0)
537 while (haystack_len >= needle_len) {
538 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
539 return (gchar *)haystack;
549 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
550 gconstpointer needle, size_t needlelen)
552 const gchar *haystack_ = (const gchar *)haystack;
553 const gchar *needle_ = (const gchar *)needle;
554 const gchar *haystack_cur = (const gchar *)haystack;
555 size_t haystack_left = haystacklen;
558 return memchr(haystack_, *needle_, haystacklen);
560 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
562 if (haystacklen - (haystack_cur - haystack_) < needlelen)
564 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
565 return (gpointer)haystack_cur;
568 haystack_left = haystacklen - (haystack_cur - haystack_);
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 static void trim_subject_for_compare(gchar *str)
619 eliminate_parenthesis(str, '[', ']');
620 eliminate_parenthesis(str, '(', ')');
623 srcp = str + subject_get_prefix_length(str);
625 memmove(str, srcp, strlen(srcp) + 1);
628 static void trim_subject_for_sort(gchar *str)
634 srcp = str + subject_get_prefix_length(str);
636 memmove(str, srcp, strlen(srcp) + 1);
639 /* compare subjects */
640 gint subject_compare(const gchar *s1, const gchar *s2)
644 if (!s1 || !s2) return -1;
645 if (!*s1 || !*s2) return -1;
647 Xstrdup_a(str1, s1, return -1);
648 Xstrdup_a(str2, s2, return -1);
650 trim_subject_for_compare(str1);
651 trim_subject_for_compare(str2);
653 if (!*str1 || !*str2) return -1;
655 return strcmp(str1, str2);
658 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
662 if (!s1 || !s2) return -1;
664 Xstrdup_a(str1, s1, return -1);
665 Xstrdup_a(str2, s2, return -1);
667 trim_subject_for_sort(str1);
668 trim_subject_for_sort(str2);
670 return g_utf8_collate(str1, str2);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void extract_quote(gchar *str, gchar quote_chr)
795 if ((str = strchr(str, quote_chr))) {
797 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798 memmove(p - 1, p, strlen(p) + 1);
803 memmove(str, str + 1, p - str);
808 void eliminate_address_comment(gchar *str)
810 register gchar *srcp, *destp;
815 while ((destp = strchr(destp, '"'))) {
816 if ((srcp = strchr(destp + 1, '"'))) {
821 while (g_ascii_isspace(*srcp)) srcp++;
822 memmove(destp, srcp, strlen(srcp) + 1);
832 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
838 else if (*srcp == ')')
844 while (g_ascii_isspace(*srcp)) srcp++;
845 memmove(destp, srcp, strlen(srcp) + 1);
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
851 gboolean in_quote = FALSE;
854 if (*str == c && !in_quote)
856 if (*str == quote_chr)
864 void extract_address(gchar *str)
866 eliminate_address_comment(str);
867 if (strchr_with_skip_quote(str, '"', '<'))
868 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
872 void extract_list_id_str(gchar *str)
874 if (strchr_with_skip_quote(str, '"', '<'))
875 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
879 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
884 if (!str) return addr_list;
886 Xstrdup_a(work, str, return addr_list);
889 eliminate_address_comment(work);
892 while (workp && *workp) {
895 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
901 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
902 extract_parenthesis_with_skip_quote
903 (workp, '"', '<', '>');
907 addr_list = g_slist_append(addr_list, g_strdup(workp));
915 GSList *address_list_append(GSList *addr_list, const gchar *str)
917 return address_list_append_real(addr_list, str, TRUE);
920 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
922 return address_list_append_real(addr_list, str, FALSE);
925 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
929 if (!str) return msgid_list;
932 while (strp && *strp) {
933 const gchar *start, *end;
936 if ((start = strchr(strp, '<')) != NULL) {
937 end = strchr(start + 1, '>');
942 msgid = g_strndup(start + 1, end - start - 1);
945 msgid_list = g_slist_prepend(msgid_list, msgid);
955 GSList *references_list_append(GSList *msgid_list, const gchar *str)
959 list = references_list_prepend(NULL, str);
960 list = g_slist_reverse(list);
961 msgid_list = g_slist_concat(msgid_list, list);
966 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
971 if (!str) return group_list;
973 Xstrdup_a(work, str, return group_list);
977 while (workp && *workp) {
980 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
988 group_list = g_slist_append(group_list,
997 GList *add_history(GList *list, const gchar *str)
1001 cm_return_val_if_fail(str != NULL, list);
1003 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1006 list = g_list_remove(list, old->data);
1007 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1010 last = g_list_last(list);
1013 list = g_list_remove(list, last->data);
1017 list = g_list_prepend(list, g_strdup(str));
1022 void remove_return(gchar *str)
1024 register gchar *p = str;
1027 if (*p == '\n' || *p == '\r')
1028 memmove(p, p + 1, strlen(p));
1034 void remove_space(gchar *str)
1036 register gchar *p = str;
1041 while (g_ascii_isspace(*(p + spc)))
1044 memmove(p, p + spc, strlen(p + spc) + 1);
1050 void unfold_line(gchar *str)
1052 register gchar *p = str;
1056 if (*p == '\n' || *p == '\r') {
1059 while (g_ascii_isspace(*(p + spc)))
1062 memmove(p, p + spc, strlen(p + spc) + 1);
1068 void subst_char(gchar *str, gchar orig, gchar subst)
1070 register gchar *p = str;
1079 void subst_chars(gchar *str, gchar *orig, gchar subst)
1081 register gchar *p = str;
1084 if (strchr(orig, *p) != NULL)
1090 void subst_for_filename(gchar *str)
1095 subst_chars(str, "\t\r\n\\/*:", '_');
1097 subst_chars(str, "\t\r\n\\/*", '_');
1101 void subst_for_shellsafe_filename(gchar *str)
1105 subst_for_filename(str);
1106 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1109 gboolean is_ascii_str(const gchar *str)
1111 const guchar *p = (const guchar *)str;
1113 while (*p != '\0') {
1114 if (*p != '\t' && *p != ' ' &&
1115 *p != '\r' && *p != '\n' &&
1116 (*p < 32 || *p >= 127))
1124 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1126 gchar * position = NULL;
1127 gchar * tmp_pos = NULL;
1130 if (quote_chars == NULL)
1133 for (i = 0; i < strlen(quote_chars); i++) {
1134 tmp_pos = strrchr (str, quote_chars[i]);
1136 || (tmp_pos != NULL && position <= tmp_pos) )
1142 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1144 const gchar *first_pos;
1145 const gchar *last_pos;
1146 const gchar *p = str;
1147 gint quote_level = -1;
1149 /* speed up line processing by only searching to the last '>' */
1150 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1151 /* skip a line if it contains a '<' before the initial '>' */
1152 if (memchr(str, '<', first_pos - str) != NULL)
1154 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1158 while (p <= last_pos) {
1159 while (p < last_pos) {
1160 if (g_ascii_isspace(*p))
1166 if (strchr(quote_chars, *p))
1168 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1169 /* any characters are allowed except '-','<' and space */
1170 while (*p != '-' && *p != '<'
1171 && !strchr(quote_chars, *p)
1172 && !g_ascii_isspace(*p)
1175 if (strchr(quote_chars, *p))
1187 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1189 const gchar *p = str, *q;
1190 gint cur_line = 0, len;
1192 while ((q = strchr(p, '\n')) != NULL) {
1194 if (len > max_chars) {
1204 if (len > max_chars) {
1213 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1215 gchar * position = NULL;
1216 gchar * tmp_pos = NULL;
1219 if (quote_chars == NULL)
1222 for (i = 0; i < strlen(quote_chars); i++) {
1223 tmp_pos = strchr (str, quote_chars[i]);
1225 || (tmp_pos != NULL && position >= tmp_pos) )
1231 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1233 register guint haystack_len, needle_len;
1234 gboolean in_squote = FALSE, in_dquote = FALSE;
1236 haystack_len = strlen(haystack);
1237 needle_len = strlen(needle);
1239 if (haystack_len < needle_len || needle_len == 0)
1242 while (haystack_len >= needle_len) {
1243 if (!in_squote && !in_dquote &&
1244 !strncmp(haystack, needle, needle_len))
1245 return (gchar *)haystack;
1247 /* 'foo"bar"' -> foo"bar"
1248 "foo'bar'" -> foo'bar' */
1249 if (*haystack == '\'') {
1252 else if (!in_dquote)
1254 } else if (*haystack == '\"') {
1257 else if (!in_squote)
1268 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1271 GSList *string_list = NULL, *slist;
1272 gchar **str_array, *s, *new_str;
1273 guint i, n = 1, len;
1275 cm_return_val_if_fail(str != NULL, NULL);
1276 cm_return_val_if_fail(delim != NULL, NULL);
1279 max_tokens = G_MAXINT;
1281 s = strstr_with_skip_quote(str, delim);
1283 guint delimiter_len = strlen(delim);
1287 new_str = g_strndup(str, len);
1289 if (new_str[0] == '\'' || new_str[0] == '\"') {
1290 if (new_str[len - 1] == new_str[0]) {
1291 new_str[len - 1] = '\0';
1292 memmove(new_str, new_str + 1, len - 1);
1295 string_list = g_slist_prepend(string_list, new_str);
1297 str = s + delimiter_len;
1298 s = strstr_with_skip_quote(str, delim);
1299 } while (--max_tokens && s);
1303 new_str = g_strdup(str);
1304 if (new_str[0] == '\'' || new_str[0] == '\"') {
1306 if (new_str[len - 1] == new_str[0]) {
1307 new_str[len - 1] = '\0';
1308 memmove(new_str, new_str + 1, len - 1);
1311 string_list = g_slist_prepend(string_list, new_str);
1315 str_array = g_new(gchar*, n);
1319 str_array[i--] = NULL;
1320 for (slist = string_list; slist; slist = slist->next)
1321 str_array[i--] = slist->data;
1323 g_slist_free(string_list);
1328 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1330 gchar *abbrev_group;
1332 const gchar *p = group;
1335 cm_return_val_if_fail(group != NULL, NULL);
1337 last = group + strlen(group);
1338 abbrev_group = ap = g_malloc(strlen(group) + 1);
1343 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1345 while (*p != '.') p++;
1348 return abbrev_group;
1353 return abbrev_group;
1356 gchar *trim_string(const gchar *str, gint len)
1358 const gchar *p = str;
1363 if (!str) return NULL;
1364 if (strlen(str) <= len)
1365 return g_strdup(str);
1366 if (g_utf8_validate(str, -1, NULL) == FALSE)
1367 return g_strdup(str);
1369 while (*p != '\0') {
1370 mb_len = g_utf8_skip[*(guchar *)p];
1373 else if (new_len + mb_len > len)
1380 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1381 return g_strconcat(new_str, "...", NULL);
1384 GList *uri_list_extract_filenames(const gchar *uri_list)
1386 GList *result = NULL;
1388 gchar *escaped_utf8uri;
1394 while (g_ascii_isspace(*p)) p++;
1395 if (!strncmp(p, "file:", 5)) {
1398 while (*q && *q != '\n' && *q != '\r') q++;
1401 gchar *file, *locale_file = NULL;
1403 while (q > p && g_ascii_isspace(*q))
1405 Xalloca(escaped_utf8uri, q - p + 2,
1407 Xalloca(file, q - p + 2,
1410 strncpy(escaped_utf8uri, p, q - p + 1);
1411 escaped_utf8uri[q - p + 1] = '\0';
1412 decode_uri(file, escaped_utf8uri);
1414 * g_filename_from_uri() rejects escaped/locale encoded uri
1415 * string which come from Nautilus.
1418 if (g_utf8_validate(file, -1, NULL))
1420 = conv_codeset_strdup(
1423 conv_get_locale_charset_str());
1425 locale_file = g_strdup(file + 5);
1427 locale_file = g_filename_from_uri(file, NULL, NULL);
1429 result = g_list_append(result, locale_file);
1433 p = strchr(p, '\n');
1440 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1443 static gint axtoi(const gchar *hexstr)
1445 gint hi, lo, result;
1448 if ('0' <= hi && hi <= '9') {
1451 if ('a' <= hi && hi <= 'f') {
1454 if ('A' <= hi && hi <= 'F') {
1459 if ('0' <= lo && lo <= '9') {
1462 if ('a' <= lo && lo <= 'f') {
1465 if ('A' <= lo && lo <= 'F') {
1468 result = lo + (16 * hi);
1472 gboolean is_uri_string(const gchar *str)
1474 while (str && *str && g_ascii_isspace(*str))
1476 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1477 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1478 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1479 g_ascii_strncasecmp(str, "www.", 4) == 0);
1482 gchar *get_uri_path(const gchar *uri)
1484 while (uri && *uri && g_ascii_isspace(*uri))
1486 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1487 return (gchar *)(uri + 7);
1488 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1489 return (gchar *)(uri + 8);
1490 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1491 return (gchar *)(uri + 6);
1493 return (gchar *)uri;
1496 gint get_uri_len(const gchar *str)
1500 if (is_uri_string(str)) {
1501 for (p = str; *p != '\0'; p++) {
1502 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1511 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1512 * plusses, and escape characters are used)
1514 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1516 gchar *dec = decoded_uri;
1517 const gchar *enc = encoded_uri;
1522 if (isxdigit((guchar)enc[0]) &&
1523 isxdigit((guchar)enc[1])) {
1529 if (with_plus && *enc == '+')
1541 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1543 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1546 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1548 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1549 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1553 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1554 gchar **subject, gchar **body, gchar ***attach)
1558 const gchar *forbidden_uris[] = { ".gnupg/",
1564 gint num_attach = 0;
1565 gchar **my_att = NULL;
1567 Xstrdup_a(tmp_mailto, mailto, return -1);
1569 if (!strncmp(tmp_mailto, "mailto:", 7))
1572 p = strchr(tmp_mailto, '?');
1579 *to = decode_uri_gdup(tmp_mailto);
1581 my_att = g_malloc(sizeof(char *));
1585 gchar *field, *value;
1602 if (*value == '\0') continue;
1604 if (from && !g_ascii_strcasecmp(field, "from")) {
1606 *from = decode_uri_gdup(value);
1608 gchar *tmp = decode_uri_gdup(value);
1609 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1613 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1615 *cc = decode_uri_gdup(value);
1617 gchar *tmp = decode_uri_gdup(value);
1618 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1622 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1624 *bcc = decode_uri_gdup(value);
1626 gchar *tmp = decode_uri_gdup(value);
1627 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1631 } else if (subject && !*subject &&
1632 !g_ascii_strcasecmp(field, "subject")) {
1633 *subject = decode_uri_gdup(value);
1634 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1635 *body = decode_uri_gdup(value);
1636 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1637 gchar *tmp = decode_uri_gdup(value);
1638 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1639 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1643 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1645 gchar *tmp = decode_uri_gdup(value);
1646 for (; forbidden_uris[i]; i++) {
1647 if (strstr(tmp, forbidden_uris[i])) {
1648 g_print("Refusing to attach '%s', potential private data leak\n",
1656 /* attach is correct */
1658 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1659 my_att[num_attach-1] = tmp;
1660 my_att[num_attach] = NULL;
1672 #include <windows.h>
1673 #ifndef CSIDL_APPDATA
1674 #define CSIDL_APPDATA 0x001a
1676 #ifndef CSIDL_LOCAL_APPDATA
1677 #define CSIDL_LOCAL_APPDATA 0x001c
1679 #ifndef CSIDL_FLAG_CREATE
1680 #define CSIDL_FLAG_CREATE 0x8000
1682 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1686 w32_strerror (int w32_errno)
1688 static char strerr[256];
1689 int ec = (int)GetLastError ();
1693 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1694 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1695 strerr, DIM (strerr)-1, NULL);
1699 static __inline__ void *
1700 dlopen (const char * name, int flag)
1702 void * hd = LoadLibrary (name);
1706 static __inline__ void *
1707 dlsym (void * hd, const char * sym)
1711 void * fnc = GetProcAddress (hd, sym);
1720 static __inline__ const char *
1723 return w32_strerror (0);
1727 static __inline__ int
1739 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1741 static int initialized;
1742 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1746 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1752 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1754 handle = dlopen (dllnames[i], RTLD_LAZY);
1757 func = dlsym (handle, "SHGetFolderPathW");
1768 return func (a,b,c,d,e);
1773 /* Returns a static string with the directroy from which the module
1774 has been loaded. Returns an empty string on error. */
1775 static char *w32_get_module_dir(void)
1777 static char *moddir;
1780 char name[MAX_PATH+10];
1783 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1786 p = strrchr (name, '\\');
1792 moddir = g_strdup (name);
1796 #endif /* G_OS_WIN32 */
1798 /* Return a static string with the locale dir. */
1799 const gchar *get_locale_dir(void)
1801 static gchar *loc_dir;
1805 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1806 "\\share\\locale", NULL);
1809 loc_dir = LOCALEDIR;
1815 const gchar *get_home_dir(void)
1818 static char home_dir_utf16[MAX_PATH] = "";
1819 static gchar *home_dir_utf8 = NULL;
1820 if (home_dir_utf16[0] == '\0') {
1821 if (w32_shgetfolderpath
1822 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1823 NULL, 0, home_dir_utf16) < 0)
1824 strcpy (home_dir_utf16, "C:\\Sylpheed");
1825 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1827 return home_dir_utf8;
1829 static const gchar *homeenv = NULL;
1834 if (!homeenv && g_getenv("HOME") != NULL)
1835 homeenv = g_strdup(g_getenv("HOME"));
1837 homeenv = g_get_home_dir();
1843 static gchar *claws_rc_dir = NULL;
1844 static gboolean rc_dir_alt = FALSE;
1845 const gchar *get_rc_dir(void)
1849 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1852 return claws_rc_dir;
1855 void set_rc_dir(const gchar *dir)
1857 if (claws_rc_dir != NULL) {
1858 g_print("Error: rc_dir already set\n");
1861 if (g_path_is_absolute(dir))
1862 claws_rc_dir = g_strdup(dir);
1864 claws_rc_dir = g_strconcat(g_get_current_dir(),
1865 G_DIR_SEPARATOR_S, dir, NULL);
1867 debug_print("set rc_dir to %s\n", claws_rc_dir);
1868 if (!is_dir_exist(claws_rc_dir)) {
1869 if (make_dir_hier(claws_rc_dir) != 0) {
1870 g_print("Error: can't create %s\n",
1877 gboolean rc_dir_is_alt(void) {
1881 const gchar *get_mail_base_dir(void)
1883 return get_home_dir();
1887 const gchar *prefs_common_get_data_root(void);
1888 gchar *last_data_root = NULL;
1891 const gchar *get_news_cache_dir(void)
1893 static gchar *news_cache_dir = NULL;
1895 const gchar *data_root = prefs_common_get_data_root();
1896 if (strcmp2(data_root, last_data_root)) {
1897 g_free(news_cache_dir);
1898 news_cache_dir = NULL;
1901 if (!news_cache_dir)
1903 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904 NEWS_CACHE_DIR, NULL);
1908 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1909 "Claws", G_DIR_SEPARATOR_S,
1910 g_get_user_name(), G_DIR_SEPARATOR_S,
1911 NEWS_CACHE_DIR, NULL);
1912 g_free(last_data_root);
1913 last_data_root = g_strdup(last_data_root);
1915 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1916 NEWS_CACHE_DIR, NULL);
1917 g_free(last_data_root);
1918 last_data_root = NULL;
1922 return news_cache_dir;
1925 const gchar *get_imap_cache_dir(void)
1927 static gchar *imap_cache_dir = NULL;
1929 const gchar *data_root = prefs_common_get_data_root();
1930 if (strcmp2(data_root, last_data_root)) {
1931 g_free(imap_cache_dir);
1932 imap_cache_dir = NULL;
1936 if (!imap_cache_dir)
1938 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1939 IMAP_CACHE_DIR, NULL);
1943 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1944 "Claws", G_DIR_SEPARATOR_S,
1945 g_get_user_name(), G_DIR_SEPARATOR_S,
1946 IMAP_CACHE_DIR, NULL);
1947 g_free(last_data_root);
1948 last_data_root = g_strdup(last_data_root);
1950 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1951 IMAP_CACHE_DIR, NULL);
1952 g_free(last_data_root);
1953 last_data_root = NULL;
1958 return imap_cache_dir;
1961 const gchar *get_mime_tmp_dir(void)
1963 static gchar *mime_tmp_dir = NULL;
1966 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967 MIME_TMP_DIR, NULL);
1969 return mime_tmp_dir;
1972 const gchar *get_template_dir(void)
1974 static gchar *template_dir = NULL;
1977 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1978 TEMPLATE_DIR, NULL);
1980 return template_dir;
1984 const gchar *get_cert_file(void)
1986 const gchar *cert_file = NULL;
1988 cert_file = g_strconcat(w32_get_module_dir(),
1989 "\\share\\claws-mail\\",
1990 "ca-certificates.crt",
1996 /* Return the default directory for Plugins. */
1997 const gchar *get_plugin_dir(void)
2000 static gchar *plugin_dir = NULL;
2003 plugin_dir = g_strconcat(w32_get_module_dir(),
2004 "\\lib\\claws-mail\\plugins\\",
2008 if (is_dir_exist(PLUGINDIR))
2011 static gchar *plugin_dir = NULL;
2013 plugin_dir = g_strconcat(get_rc_dir(),
2014 G_DIR_SEPARATOR_S, "plugins",
2015 G_DIR_SEPARATOR_S, NULL);
2023 /* Return the default directory for Themes. */
2024 const gchar *get_themes_dir(void)
2026 static gchar *themes_dir = NULL;
2029 themes_dir = g_strconcat(w32_get_module_dir(),
2030 "\\share\\claws-mail\\themes",
2036 const gchar *get_tmp_dir(void)
2038 static gchar *tmp_dir = NULL;
2041 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2047 gchar *get_tmp_file(void)
2050 static guint32 id = 0;
2052 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2053 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2058 const gchar *get_domain_name(void)
2061 static gchar *domain_name = NULL;
2067 if (gethostname(hostname, sizeof(hostname)) != 0) {
2068 perror("gethostname");
2069 domain_name = "unknown";
2071 hostname[sizeof(hostname) - 1] = '\0';
2072 if ((hp = my_gethostbyname(hostname)) == NULL) {
2073 perror("gethostbyname");
2074 domain_name = g_strdup(hostname);
2076 domain_name = g_strdup(hp->h_name);
2079 debug_print("domain name = %s\n", domain_name);
2088 off_t get_file_size(const gchar *file)
2092 if (g_stat(file, &s) < 0) {
2093 FILE_OP_ERROR(file, "stat");
2100 time_t get_file_mtime(const gchar *file)
2104 if (g_stat(file, &s) < 0) {
2105 FILE_OP_ERROR(file, "stat");
2112 off_t get_file_size_as_crlf(const gchar *file)
2116 gchar buf[BUFFSIZE];
2118 if ((fp = g_fopen(file, "rb")) == NULL) {
2119 FILE_OP_ERROR(file, "g_fopen");
2123 while (fgets(buf, sizeof(buf), fp) != NULL) {
2125 size += strlen(buf) + 2;
2129 FILE_OP_ERROR(file, "fgets");
2138 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2145 if (g_stat(file, &s) < 0) {
2146 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2150 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2157 /* Test on whether FILE is a relative file name. This is
2158 * straightforward for Unix but more complex for Windows. */
2159 gboolean is_relative_filename(const gchar *file)
2164 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2165 return FALSE; /* Prefixed with a hostname - this can't
2166 * be a relative name. */
2168 if ( ((*file >= 'a' && *file <= 'z')
2169 || (*file >= 'A' && *file <= 'Z'))
2171 file += 2; /* Skip drive letter. */
2173 return !(*file == '\\' || *file == '/');
2175 return !(*file == G_DIR_SEPARATOR);
2180 gboolean is_dir_exist(const gchar *dir)
2185 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2188 gboolean is_file_entry_exist(const gchar *file)
2193 return g_file_test(file, G_FILE_TEST_EXISTS);
2196 gboolean dirent_is_regular_file(struct dirent *d)
2198 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2199 if (d->d_type == DT_REG)
2201 else if (d->d_type != DT_UNKNOWN)
2205 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2208 gint change_dir(const gchar *dir)
2210 gchar *prevdir = NULL;
2213 prevdir = g_get_current_dir();
2215 if (g_chdir(dir) < 0) {
2216 FILE_OP_ERROR(dir, "chdir");
2217 if (debug_mode) g_free(prevdir);
2219 } else if (debug_mode) {
2222 cwd = g_get_current_dir();
2223 if (strcmp(prevdir, cwd) != 0)
2224 g_print("current dir: %s\n", cwd);
2232 gint make_dir(const gchar *dir)
2234 if (g_mkdir(dir, S_IRWXU) < 0) {
2235 FILE_OP_ERROR(dir, "mkdir");
2238 if (g_chmod(dir, S_IRWXU) < 0)
2239 FILE_OP_ERROR(dir, "chmod");
2244 gint make_dir_hier(const gchar *dir)
2249 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2250 parent_dir = g_strndup(dir, p - dir);
2251 if (*parent_dir != '\0') {
2252 if (!is_dir_exist(parent_dir)) {
2253 if (make_dir(parent_dir) < 0) {
2262 if (!is_dir_exist(dir)) {
2263 if (make_dir(dir) < 0)
2270 gint remove_all_files(const gchar *dir)
2273 const gchar *dir_name;
2276 prev_dir = g_get_current_dir();
2278 if (g_chdir(dir) < 0) {
2279 FILE_OP_ERROR(dir, "chdir");
2284 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2285 g_warning("failed to open directory: %s\n", dir);
2290 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2291 if (claws_unlink(dir_name) < 0)
2292 FILE_OP_ERROR(dir_name, "unlink");
2297 if (g_chdir(prev_dir) < 0) {
2298 FILE_OP_ERROR(prev_dir, "chdir");
2308 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2311 const gchar *dir_name;
2315 prev_dir = g_get_current_dir();
2317 if (g_chdir(dir) < 0) {
2318 FILE_OP_ERROR(dir, "chdir");
2323 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2324 g_warning("failed to open directory: %s\n", dir);
2329 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2330 file_no = to_number(dir_name);
2331 if (file_no > 0 && first <= file_no && file_no <= last) {
2332 if (is_dir_exist(dir_name))
2334 if (claws_unlink(dir_name) < 0)
2335 FILE_OP_ERROR(dir_name, "unlink");
2341 if (g_chdir(prev_dir) < 0) {
2342 FILE_OP_ERROR(prev_dir, "chdir");
2352 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2355 const gchar *dir_name;
2359 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 FILE_OP_ERROR(dir, "opendir");
2373 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2374 file_no = to_number(dir_name);
2375 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2376 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2377 if (is_dir_exist(dir_name))
2379 if (claws_unlink(dir_name) < 0)
2380 FILE_OP_ERROR(dir_name, "unlink");
2386 if (g_chdir(prev_dir) < 0) {
2387 FILE_OP_ERROR(prev_dir, "chdir");
2397 gint remove_all_numbered_files(const gchar *dir)
2399 return remove_numbered_files(dir, 0, UINT_MAX);
2402 gint remove_dir_recursive(const gchar *dir)
2406 const gchar *dir_name;
2409 if (g_stat(dir, &s) < 0) {
2410 FILE_OP_ERROR(dir, "stat");
2411 if (ENOENT == errno) return 0;
2415 if (!S_ISDIR(s.st_mode)) {
2416 if (claws_unlink(dir) < 0) {
2417 FILE_OP_ERROR(dir, "unlink");
2424 prev_dir = g_get_current_dir();
2425 /* g_print("prev_dir = %s\n", prev_dir); */
2427 if (!path_cmp(prev_dir, dir)) {
2429 if (g_chdir("..") < 0) {
2430 FILE_OP_ERROR(dir, "chdir");
2433 prev_dir = g_get_current_dir();
2436 if (g_chdir(dir) < 0) {
2437 FILE_OP_ERROR(dir, "chdir");
2442 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2443 g_warning("failed to open directory: %s\n", dir);
2449 /* remove all files in the directory */
2450 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2451 /* g_print("removing %s\n", dir_name); */
2453 if (is_dir_exist(dir_name)) {
2454 if (remove_dir_recursive(dir_name) < 0) {
2455 g_warning("can't remove directory\n");
2459 if (claws_unlink(dir_name) < 0)
2460 FILE_OP_ERROR(dir_name, "unlink");
2466 if (g_chdir(prev_dir) < 0) {
2467 FILE_OP_ERROR(prev_dir, "chdir");
2474 if (g_rmdir(dir) < 0) {
2475 FILE_OP_ERROR(dir, "rmdir");
2482 gint rename_force(const gchar *oldpath, const gchar *newpath)
2485 if (!is_file_entry_exist(oldpath)) {
2489 if (is_file_exist(newpath)) {
2490 if (claws_unlink(newpath) < 0)
2491 FILE_OP_ERROR(newpath, "unlink");
2494 return g_rename(oldpath, newpath);
2498 * Append src file body to the tail of dest file.
2499 * Now keep_backup has no effects.
2501 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2503 FILE *src_fp, *dest_fp;
2507 gboolean err = FALSE;
2509 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2510 FILE_OP_ERROR(src, "g_fopen");
2514 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2515 FILE_OP_ERROR(dest, "g_fopen");
2520 if (change_file_mode_rw(dest_fp, dest) < 0) {
2521 FILE_OP_ERROR(dest, "chmod");
2522 g_warning("can't change file mode\n");
2525 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2526 if (n_read < sizeof(buf) && ferror(src_fp))
2528 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2529 g_warning("writing to %s failed.\n", dest);
2537 if (ferror(src_fp)) {
2538 FILE_OP_ERROR(src, "fread");
2542 if (fclose(dest_fp) == EOF) {
2543 FILE_OP_ERROR(dest, "fclose");
2555 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2557 FILE *src_fp, *dest_fp;
2560 gchar *dest_bak = NULL;
2561 gboolean err = FALSE;
2563 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2564 FILE_OP_ERROR(src, "g_fopen");
2567 if (is_file_exist(dest)) {
2568 dest_bak = g_strconcat(dest, ".bak", NULL);
2569 if (rename_force(dest, dest_bak) < 0) {
2570 FILE_OP_ERROR(dest, "rename");
2577 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2578 FILE_OP_ERROR(dest, "g_fopen");
2581 if (rename_force(dest_bak, dest) < 0)
2582 FILE_OP_ERROR(dest_bak, "rename");
2588 if (change_file_mode_rw(dest_fp, dest) < 0) {
2589 FILE_OP_ERROR(dest, "chmod");
2590 g_warning("can't change file mode\n");
2593 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2594 if (n_read < sizeof(buf) && ferror(src_fp))
2596 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2597 g_warning("writing to %s failed.\n", dest);
2602 if (rename_force(dest_bak, dest) < 0)
2603 FILE_OP_ERROR(dest_bak, "rename");
2610 if (ferror(src_fp)) {
2611 FILE_OP_ERROR(src, "fread");
2615 if (fclose(dest_fp) == EOF) {
2616 FILE_OP_ERROR(dest, "fclose");
2623 if (rename_force(dest_bak, dest) < 0)
2624 FILE_OP_ERROR(dest_bak, "rename");
2630 if (keep_backup == FALSE && dest_bak)
2631 claws_unlink(dest_bak);
2638 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2640 if (overwrite == FALSE && is_file_exist(dest)) {
2641 g_warning("move_file(): file %s already exists.", dest);
2645 if (rename_force(src, dest) == 0) return 0;
2647 if (EXDEV != errno) {
2648 FILE_OP_ERROR(src, "rename");
2652 if (copy_file(src, dest, FALSE) < 0) return -1;
2659 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2662 gint bytes_left, to_read;
2665 if (fseek(fp, offset, SEEK_SET) < 0) {
2670 bytes_left = length;
2671 to_read = MIN(bytes_left, sizeof(buf));
2673 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2674 if (n_read < to_read && ferror(fp))
2676 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2679 bytes_left -= n_read;
2680 if (bytes_left == 0)
2682 to_read = MIN(bytes_left, sizeof(buf));
2693 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2696 gboolean err = FALSE;
2698 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2699 FILE_OP_ERROR(dest, "g_fopen");
2703 if (change_file_mode_rw(dest_fp, dest) < 0) {
2704 FILE_OP_ERROR(dest, "chmod");
2705 g_warning("can't change file mode\n");
2708 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2711 if (!err && fclose(dest_fp) == EOF) {
2712 FILE_OP_ERROR(dest, "fclose");
2717 g_warning("writing to %s failed.\n", dest);
2725 /* convert line endings into CRLF. If the last line doesn't end with
2726 * linebreak, add it.
2728 gchar *canonicalize_str(const gchar *str)
2734 for (p = str; *p != '\0'; ++p) {
2741 if (p == str || *(p - 1) != '\n')
2744 out = outp = g_malloc(new_len + 1);
2745 for (p = str; *p != '\0'; ++p) {
2752 if (p == str || *(p - 1) != '\n') {
2761 gint canonicalize_file(const gchar *src, const gchar *dest)
2763 FILE *src_fp, *dest_fp;
2764 gchar buf[BUFFSIZE];
2766 gboolean err = FALSE;
2767 gboolean last_linebreak = FALSE;
2769 if (src == NULL || dest == NULL)
2772 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2773 FILE_OP_ERROR(src, "g_fopen");
2777 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2778 FILE_OP_ERROR(dest, "g_fopen");
2783 if (change_file_mode_rw(dest_fp, dest) < 0) {
2784 FILE_OP_ERROR(dest, "chmod");
2785 g_warning("can't change file mode\n");
2788 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2792 if (len == 0) break;
2793 last_linebreak = FALSE;
2795 if (buf[len - 1] != '\n') {
2796 last_linebreak = TRUE;
2797 r = fputs(buf, dest_fp);
2798 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2799 r = fputs(buf, dest_fp);
2802 r = fwrite(buf, 1, len - 1, dest_fp);
2807 r = fputs("\r\n", dest_fp);
2811 g_warning("writing to %s failed.\n", dest);
2819 if (last_linebreak == TRUE) {
2820 if (fputs("\r\n", dest_fp) == EOF)
2824 if (ferror(src_fp)) {
2825 FILE_OP_ERROR(src, "fgets");
2829 if (fclose(dest_fp) == EOF) {
2830 FILE_OP_ERROR(dest, "fclose");
2842 gint canonicalize_file_replace(const gchar *file)
2846 tmp_file = get_tmp_file();
2848 if (canonicalize_file(file, tmp_file) < 0) {
2853 if (move_file(tmp_file, file, TRUE) < 0) {
2854 g_warning("can't replace %s .\n", file);
2855 claws_unlink(tmp_file);
2864 gchar *normalize_newlines(const gchar *str)
2866 const gchar *p = str;
2869 out = outp = g_malloc(strlen(str) + 1);
2870 for (p = str; *p != '\0'; ++p) {
2872 if (*(p + 1) != '\n')
2883 gchar *get_outgoing_rfc2822_str(FILE *fp)
2885 gchar buf[BUFFSIZE];
2889 str = g_string_new(NULL);
2891 /* output header part */
2892 while (fgets(buf, sizeof(buf), fp) != NULL) {
2894 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2901 else if (next != ' ' && next != '\t') {
2905 if (fgets(buf, sizeof(buf), fp) == NULL)
2909 g_string_append(str, buf);
2910 g_string_append(str, "\r\n");
2916 /* output body part */
2917 while (fgets(buf, sizeof(buf), fp) != NULL) {
2920 g_string_append_c(str, '.');
2921 g_string_append(str, buf);
2922 g_string_append(str, "\r\n");
2926 g_string_free(str, FALSE);
2932 * Create a new boundary in a way that it is very unlikely that this
2933 * will occur in the following text. It would be easy to ensure
2934 * uniqueness if everything is either quoted-printable or base64
2935 * encoded (note that conversion is allowed), but because MIME bodies
2936 * may be nested, it may happen that the same boundary has already
2939 * boundary := 0*69<bchars> bcharsnospace
2940 * bchars := bcharsnospace / " "
2941 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2942 * "+" / "_" / "," / "-" / "." /
2943 * "/" / ":" / "=" / "?"
2945 * some special characters removed because of buggy MTAs
2948 gchar *generate_mime_boundary(const gchar *prefix)
2950 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2951 "abcdefghijklmnopqrstuvwxyz"
2956 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2957 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2960 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2964 gint change_file_mode_rw(FILE *fp, const gchar *file)
2967 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2969 return g_chmod(file, S_IRUSR|S_IWUSR);
2973 FILE *my_tmpfile(void)
2975 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2976 const gchar suffix[] = ".XXXXXX";
2977 const gchar *tmpdir;
2979 const gchar *progname;
2988 tmpdir = get_tmp_dir();
2989 tmplen = strlen(tmpdir);
2990 progname = g_get_prgname();
2991 if (progname == NULL)
2992 progname = "claws-mail";
2993 proglen = strlen(progname);
2994 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2997 memcpy(fname, tmpdir, tmplen);
2998 fname[tmplen] = G_DIR_SEPARATOR;
2999 memcpy(fname + tmplen + 1, progname, proglen);
3000 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3002 fd = mkstemp(fname);
3007 claws_unlink(fname);
3009 /* verify that we can write in the file after unlinking */
3010 if (write(fd, buf, 1) < 0) {
3017 fp = fdopen(fd, "w+b");
3025 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3030 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3034 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3035 dir, G_DIR_SEPARATOR);
3036 fd = mkstemp_name(template, filename);
3039 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3040 fd = mkstemp(*filename);
3042 return fdopen(fd, "w+");
3045 FILE *str_open_as_stream(const gchar *str)
3050 cm_return_val_if_fail(str != NULL, NULL);
3054 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3059 if (len == 0) return fp;
3061 if (fwrite(str, 1, len, fp) != len) {
3062 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3071 gint str_write_to_file(const gchar *str, const gchar *file)
3076 cm_return_val_if_fail(str != NULL, -1);
3077 cm_return_val_if_fail(file != NULL, -1);
3079 if ((fp = g_fopen(file, "wb")) == NULL) {
3080 FILE_OP_ERROR(file, "g_fopen");
3090 if (fwrite(str, 1, len, fp) != len) {
3091 FILE_OP_ERROR(file, "fwrite");
3097 if (fclose(fp) == EOF) {
3098 FILE_OP_ERROR(file, "fclose");
3106 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3113 cm_return_val_if_fail(fp != NULL, NULL);
3115 array = g_byte_array_new();
3117 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3118 if (n_read < sizeof(buf) && ferror(fp))
3120 g_byte_array_append(array, buf, n_read);
3124 FILE_OP_ERROR("file stream", "fread");
3125 g_byte_array_free(array, TRUE);
3130 g_byte_array_append(array, buf, 1);
3131 str = (gchar *)array->data;
3132 g_byte_array_free(array, FALSE);
3134 if (recode && !g_utf8_validate(str, -1, NULL)) {
3135 const gchar *src_codeset, *dest_codeset;
3137 src_codeset = conv_get_locale_charset_str();
3138 dest_codeset = CS_UTF_8;
3139 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3147 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3154 struct timeval timeout = {1, 0};
3159 cm_return_val_if_fail(file != NULL, NULL);
3161 if (g_stat(file, &s) != 0) {
3162 FILE_OP_ERROR(file, "stat");
3165 if (S_ISDIR(s.st_mode)) {
3166 g_warning("%s: is a directory\n", file);
3171 fp = g_fopen (file, "rb");
3173 FILE_OP_ERROR(file, "open");
3177 /* test whether the file is readable without blocking */
3178 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3180 FILE_OP_ERROR(file, "open");
3187 /* allow for one second */
3188 err = select(fd+1, &fds, NULL, NULL, &timeout);
3189 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3191 FILE_OP_ERROR(file, "select");
3193 g_warning("%s: doesn't seem readable\n", file);
3199 /* Now clear O_NONBLOCK */
3200 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3201 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3205 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3206 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3211 /* get the FILE pointer */
3212 fp = fdopen(fd, "rb");
3215 FILE_OP_ERROR(file, "fdopen");
3216 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3221 str = file_read_stream_to_str_full(fp, recode);
3228 gchar *file_read_to_str(const gchar *file)
3230 return file_read_to_str_full(file, TRUE);
3232 gchar *file_read_stream_to_str(FILE *fp)
3234 return file_read_stream_to_str_full(fp, TRUE);
3237 gchar *file_read_to_str_no_recode(const gchar *file)
3239 return file_read_to_str_full(file, FALSE);
3241 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3243 return file_read_stream_to_str_full(fp, FALSE);
3246 char *fgets_crlf(char *buf, int size, FILE *stream)
3248 gboolean is_cr = FALSE;
3249 gboolean last_was_cr = FALSE;
3254 while (--size > 0 && (c = getc(stream)) != EOF)
3257 is_cr = (c == '\r');
3267 last_was_cr = is_cr;
3269 if (c == EOF && cs == buf)
3277 static gint execute_async(gchar *const argv[])
3279 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3281 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3282 NULL, NULL, NULL, FALSE) == FALSE) {
3283 g_warning("Couldn't execute command: %s\n", argv[0]);
3290 static gint execute_sync(gchar *const argv[])
3294 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3297 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3298 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3299 g_warning("Couldn't execute command: %s\n", argv[0]);
3303 if (WIFEXITED(status))
3304 return WEXITSTATUS(status);
3308 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3309 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3310 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3311 g_warning("Couldn't execute command: %s\n", argv[0]);
3319 gint execute_command_line(const gchar *cmdline, gboolean async)
3324 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3326 argv = strsplit_with_quote(cmdline, " ", 0);
3329 ret = execute_async(argv);
3331 ret = execute_sync(argv);
3338 gchar *get_command_output(const gchar *cmdline)
3340 gchar *child_stdout;
3343 cm_return_val_if_fail(cmdline != NULL, NULL);
3345 debug_print("get_command_output(): executing: %s\n", cmdline);
3347 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3349 g_warning("Couldn't execute command: %s\n", cmdline);
3353 return child_stdout;
3356 static gint is_unchanged_uri_char(char c)
3367 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3373 for(i = 0; i < strlen(uri) ; i++) {
3374 if (is_unchanged_uri_char(uri[i])) {
3375 if (k + 2 >= bufsize)
3377 encoded_uri[k++] = uri[i];
3380 char * hexa = "0123456789ABCDEF";
3382 if (k + 4 >= bufsize)
3384 encoded_uri[k++] = '%';
3385 encoded_uri[k++] = hexa[uri[i] / 16];
3386 encoded_uri[k++] = hexa[uri[i] % 16];
3392 gint open_uri(const gchar *uri, const gchar *cmdline)
3396 gchar buf[BUFFSIZE];
3398 gchar encoded_uri[BUFFSIZE];
3399 cm_return_val_if_fail(uri != NULL, -1);
3401 /* an option to choose whether to use encode_uri or not ? */
3402 encode_uri(encoded_uri, BUFFSIZE, uri);
3405 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3406 !strchr(p + 2, '%'))
3407 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3410 g_warning("Open URI command-line is invalid "
3411 "(there must be only one '%%s'): %s",
3413 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3416 execute_command_line(buf, TRUE);
3418 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3421 extern osso_context_t *get_osso_context(void);
3422 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3423 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3424 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3429 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3431 gchar buf[BUFFSIZE];
3434 cm_return_val_if_fail(filepath != NULL, -1);
3437 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3438 !strchr(p + 2, '%'))
3439 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3442 g_warning("Open Text Editor command-line is invalid "
3443 "(there must be only one '%%s'): %s",
3445 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3448 execute_command_line(buf, TRUE);
3453 time_t remote_tzoffset_sec(const gchar *zone)
3455 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3461 time_t remoteoffset;
3463 strncpy(zone3, zone, 3);
3467 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3468 (c == '+' || c == '-')) {
3469 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3471 remoteoffset = -remoteoffset;
3472 } else if (!strncmp(zone, "UT" , 2) ||
3473 !strncmp(zone, "GMT", 2)) {
3475 } else if (strlen(zone3) == 3) {
3476 for (p = ustzstr; *p != '\0'; p += 3) {
3477 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3478 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3479 remoteoffset = iustz * 3600;
3485 } else if (strlen(zone3) == 1) {
3487 case 'Z': remoteoffset = 0; break;
3488 case 'A': remoteoffset = -1; break;
3489 case 'B': remoteoffset = -2; break;
3490 case 'C': remoteoffset = -3; break;
3491 case 'D': remoteoffset = -4; break;
3492 case 'E': remoteoffset = -5; break;
3493 case 'F': remoteoffset = -6; break;
3494 case 'G': remoteoffset = -7; break;
3495 case 'H': remoteoffset = -8; break;
3496 case 'I': remoteoffset = -9; break;
3497 case 'K': remoteoffset = -10; break; /* J is not used */
3498 case 'L': remoteoffset = -11; break;
3499 case 'M': remoteoffset = -12; break;
3500 case 'N': remoteoffset = 1; break;
3501 case 'O': remoteoffset = 2; break;
3502 case 'P': remoteoffset = 3; break;
3503 case 'Q': remoteoffset = 4; break;
3504 case 'R': remoteoffset = 5; break;
3505 case 'S': remoteoffset = 6; break;
3506 case 'T': remoteoffset = 7; break;
3507 case 'U': remoteoffset = 8; break;
3508 case 'V': remoteoffset = 9; break;
3509 case 'W': remoteoffset = 10; break;
3510 case 'X': remoteoffset = 11; break;
3511 case 'Y': remoteoffset = 12; break;
3512 default: remoteoffset = 0; break;
3514 remoteoffset = remoteoffset * 3600;
3518 return remoteoffset;
3521 time_t tzoffset_sec(time_t *now)
3526 struct tm buf1, buf2;
3529 if (now && *now < 0)
3532 gmt = *gmtime_r(now, &buf1);
3533 lt = localtime_r(now, &buf2);
3535 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3537 if (lt->tm_year < gmt.tm_year)
3539 else if (lt->tm_year > gmt.tm_year)
3541 else if (lt->tm_yday < gmt.tm_yday)
3543 else if (lt->tm_yday > gmt.tm_yday)
3546 if (off >= 24 * 60) /* should be impossible */
3547 off = 23 * 60 + 59; /* if not, insert silly value */
3548 if (off <= -24 * 60)
3549 off = -(23 * 60 + 59);
3554 /* calculate timezone offset */
3555 gchar *tzoffset(time_t *now)
3557 static gchar offset_string[6];
3562 struct tm buf1, buf2;
3565 if (now && *now < 0)
3568 gmt = *gmtime_r(now, &buf1);
3569 lt = localtime_r(now, &buf2);
3571 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3573 if (lt->tm_year < gmt.tm_year)
3575 else if (lt->tm_year > gmt.tm_year)
3577 else if (lt->tm_yday < gmt.tm_yday)
3579 else if (lt->tm_yday > gmt.tm_yday)
3587 if (off >= 24 * 60) /* should be impossible */
3588 off = 23 * 60 + 59; /* if not, insert silly value */
3590 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3592 return offset_string;
3595 void get_rfc822_date(gchar *buf, gint len)
3599 gchar day[4], mon[4];
3600 gint dd, hh, mm, ss, yyyy;
3603 gchar buf2[BUFFSIZE];
3607 lt = localtime_r(&t, &buf1);
3609 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3610 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3612 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3613 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3616 void debug_set_mode(gboolean mode)
3621 gboolean debug_get_mode(void)
3626 void debug_print_real(const gchar *format, ...)
3629 gchar buf[BUFFSIZE];
3631 if (!debug_mode) return;
3633 va_start(args, format);
3634 g_vsnprintf(buf, sizeof(buf), format, args);
3641 const char * debug_srcname(const char *file)
3643 const char *s = strrchr (file, '/');
3648 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3650 if (subject == NULL)
3653 subject += subject_get_prefix_length(subject);
3655 return g_hash_table_lookup(subject_table, subject);
3658 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3661 if (subject == NULL || *subject == 0)
3663 subject += subject_get_prefix_length(subject);
3664 g_hash_table_insert(subject_table, subject, data);
3667 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3669 if (subject == NULL)
3672 subject += subject_get_prefix_length(subject);
3673 g_hash_table_remove(subject_table, subject);
3677 static regex_t u_regex;
3678 static gboolean u_init_;
3681 void utils_free_regex(void)
3692 *\brief Check if a string is prefixed with known (combinations)
3693 * of prefixes. The function assumes that each prefix
3694 * is terminated by zero or exactly _one_ space.
3696 *\param str String to check for a prefixes
3698 *\return int Number of chars in the prefix that should be skipped
3699 * for a "clean" subject line. If no prefix was found, 0
3702 int subject_get_prefix_length(const gchar *subject)
3705 /*!< Array with allowable reply prefixes regexps. */
3706 static const gchar * const prefixes[] = {
3707 "Re\\:", /* "Re:" */
3708 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3709 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3710 "Aw\\:", /* "Aw:" (German) */
3711 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3712 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3713 "Fw\\:", /* "Fw:" Forward */
3714 "Fwd\\:", /* "Fwd:" Forward */
3715 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3716 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3717 "Rif\\:", /* "Rif:" (Italian Outlook) */
3718 "Sv\\:", /* "Sv" (Norwegian) */
3719 "Vs\\:", /* "Vs" (Norwegian) */
3720 "Ad\\:", /* "Ad" (Norwegian) */
3721 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3722 "R\303\251f\\. \\:", /* "Réf. :" (French Lotus Notes) */
3723 "Re \\:", /* "Re :" (French Yahoo Mail) */
3726 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3730 if (!subject) return 0;
3731 if (!*subject) return 0;
3734 GString *s = g_string_new("");
3736 for (n = 0; n < PREFIXES; n++)
3737 /* Terminate each prefix regexpression by a
3738 * "\ ?" (zero or ONE space), and OR them */
3739 g_string_append_printf(s, "(%s\\ ?)%s",
3744 g_string_prepend(s, "(");
3745 g_string_append(s, ")+"); /* match at least once */
3746 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3749 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3750 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3751 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3752 debug_print("Error compiling regexp %s\n", s->str);
3753 g_string_free(s, TRUE);
3757 g_string_free(s, TRUE);
3761 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3766 /*!< Array with allowable reply prefixes regexps. */
3767 static const gchar * const prefixes[] = {
3769 "antw:", /* "Antw:" (Dutch / German Outlook) */
3770 "aw:", /* "Aw:" (German) */
3771 "antwort:", /* "Antwort:" (German Lotus Notes) */
3772 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3773 "fw:", /* "Fw:" Forward */
3774 "fwd:", /* "Fwd:" Forward */
3775 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3776 "odp:", /* "Odp:" Re (Polish Outlook) */
3777 "rif:", /* "Rif:" (Italian Outlook) */
3778 "sv:", /* "Sv" (Norwegian) */
3779 "vs:", /* "Vs" (Norwegian) */
3780 "ad:", /* "Ad" (Norwegian) */
3781 "R\303\251f. :", /* "Réf. :" (French Lotus Notes) */
3782 "Re :", /* "Re :" (French Yahoo Mail) */
3785 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3788 if (!subject) return 0;
3789 if (!*subject) return 0;
3791 for (n = 0; n < PREFIXES; n++) {
3792 int len = strlen(prefixes[n]);
3793 if (!strncasecmp(subject, prefixes[n], len)) {
3794 if (subject[len] == ' ')
3803 static guint g_stricase_hash(gconstpointer gptr)
3805 guint hash_result = 0;
3808 for (str = gptr; str && *str; str++) {
3809 hash_result += toupper(*str);
3815 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3817 const char *str1 = gptr1;
3818 const char *str2 = gptr2;
3820 return !strcasecmp(str1, str2);
3823 gint g_int_compare(gconstpointer a, gconstpointer b)
3825 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3828 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3838 lt = localtime_r(&t, &buft);
3840 if (strcmp(buf, "") == 0) {
3841 if (user_addr != NULL) {
3842 addr = g_strconcat(user_addr, "@", get_domain_name(), NULL);
3845 addr = g_strconcat("@", get_domain_name(), NULL);
3849 if (user_addr != NULL) {
3850 addr = g_strconcat(user_addr, "@", buf, NULL);
3853 addr = g_strconcat("@", buf, NULL);
3857 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3858 lt->tm_year + 1900, lt->tm_mon + 1,
3859 lt->tm_mday, lt->tm_hour,
3860 lt->tm_min, lt->tm_sec,
3861 (guint) rand(), addr);
3868 quote_cmd_argument()
3870 return a quoted string safely usable in argument of a command.
3872 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3875 gint quote_cmd_argument(gchar * result, guint size,
3885 for(p = path ; * p != '\0' ; p ++) {
3887 if (isalnum((guchar)*p) || (* p == '/')) {
3888 if (remaining > 0) {
3894 result[size - 1] = '\0';
3899 if (remaining >= 2) {
3907 result[size - 1] = '\0';
3912 if (remaining > 0) {
3916 result[size - 1] = '\0';
3930 static void g_node_map_recursive(GNode *node, gpointer data)
3932 GNodeMapData *mapdata = (GNodeMapData *) data;
3934 GNodeMapData newmapdata;
3937 newdata = mapdata->func(node->data, mapdata->data);
3938 if (newdata != NULL) {
3939 newnode = g_node_new(newdata);
3940 g_node_append(mapdata->parent, newnode);
3942 newmapdata.parent = newnode;
3943 newmapdata.func = mapdata->func;
3944 newmapdata.data = mapdata->data;
3946 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3950 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3953 GNodeMapData mapdata;
3955 cm_return_val_if_fail(node != NULL, NULL);
3956 cm_return_val_if_fail(func != NULL, NULL);
3958 root = g_node_new(func(node->data, data));
3960 mapdata.parent = root;
3961 mapdata.func = func;
3962 mapdata.data = data;
3964 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3969 #define HEX_TO_INT(val, hex) \
3973 if ('0' <= c && c <= '9') { \
3975 } else if ('a' <= c && c <= 'f') { \
3976 val = c - 'a' + 10; \
3977 } else if ('A' <= c && c <= 'F') { \
3978 val = c - 'A' + 10; \
3984 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3991 if (hi == -1 || lo == -1)
3994 *out = (hi << 4) + lo;
3998 #define INT_TO_HEX(hex, val) \
4001 hex = '0' + (val); \
4003 hex = 'A' + (val) - 10; \
4006 void get_hex_str(gchar *out, guchar ch)
4010 INT_TO_HEX(hex, ch >> 4);
4012 INT_TO_HEX(hex, ch & 0x0f);
4018 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4020 #define G_PRINT_REF g_print
4024 *\brief Register ref counted pointer. It is based on GBoxed, so should
4025 * work with anything that uses the GType system. The semantics
4026 * are similar to a C++ auto pointer, with the exception that
4027 * C doesn't have automatic closure (calling destructors) when
4028 * exiting a block scope.
4029 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4030 * function directly.
4032 *\return GType A GType type.
4034 GType g_auto_pointer_register(void)
4036 static GType auto_pointer_type;
4037 if (!auto_pointer_type)
4039 g_boxed_type_register_static
4040 ("G_TYPE_AUTO_POINTER",
4041 (GBoxedCopyFunc) g_auto_pointer_copy,
4042 (GBoxedFreeFunc) g_auto_pointer_free);
4043 return auto_pointer_type;
4047 *\brief Structure with g_new() allocated pointer guarded by the
4050 typedef struct AutoPointerRef {
4051 void (*free) (gpointer);
4057 *\brief The auto pointer opaque structure that references the
4058 * pointer guard block.
4060 typedef struct AutoPointer {
4061 AutoPointerRef *ref;
4062 gpointer ptr; /*!< access to protected pointer */
4066 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4070 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4071 * ... when assigning, copying and freeing storage elements
4073 * gtk_list_store_new(N_S_COLUMNS,
4074 * G_TYPE_AUTO_POINTER,
4078 * Template *precious_data = g_new0(Template, 1);
4079 * g_pointer protect = g_auto_pointer_new(precious_data);
4081 * gtk_list_store_set(container, &iter,
4085 * ... the gtk_list_store has copied the pointer and
4086 * ... incremented its reference count, we should free
4087 * ... the auto pointer (in C++ a destructor would do
4088 * ... this for us when leaving block scope)
4090 * g_auto_pointer_free(protect);
4092 * ... gtk_list_store_set() now manages the data. When
4093 * ... *explicitly* requesting a pointer from the list
4094 * ... store, don't forget you get a copy that should be
4095 * ... freed with g_auto_pointer_free() eventually.
4099 *\param pointer Pointer to be guarded.
4101 *\return GAuto * Pointer that should be used in containers with
4104 GAuto *g_auto_pointer_new(gpointer p)
4106 AutoPointerRef *ref;
4112 ref = g_new0(AutoPointerRef, 1);
4113 ptr = g_new0(AutoPointer, 1);
4123 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4129 *\brief Allocate an autopointer using the passed \a free function to
4130 * free the guarded pointer
4132 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4139 aptr = g_auto_pointer_new(p);
4140 aptr->ref->free = free_;
4144 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4146 if (auto_ptr == NULL)
4148 return ((AutoPointer *) auto_ptr)->ptr;
4152 *\brief Copies an auto pointer by. It's mostly not necessary
4153 * to call this function directly, unless you copy/assign
4154 * the guarded pointer.
4156 *\param auto_ptr Auto pointer returned by previous call to
4157 * g_auto_pointer_new_XXX()
4159 *\return gpointer An auto pointer
4161 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4164 AutoPointerRef *ref;
4167 if (auto_ptr == NULL)
4172 newp = g_new0(AutoPointer, 1);
4175 newp->ptr = ref->pointer;
4179 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4185 *\brief Free an auto pointer
4187 void g_auto_pointer_free(GAuto *auto_ptr)
4190 AutoPointerRef *ref;
4192 if (auto_ptr == NULL)
4198 if (--(ref->cnt) == 0) {
4200 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4202 ref->free(ref->pointer);
4207 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4212 void replace_returns(gchar *str)
4217 while (strstr(str, "\n")) {
4218 *strstr(str, "\n") = ' ';
4220 while (strstr(str, "\r")) {
4221 *strstr(str, "\r") = ' ';
4225 /* get_uri_part() - retrieves a URI starting from scanpos.
4226 Returns TRUE if succesful */
4227 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4228 const gchar **bp, const gchar **ep, gboolean hdr)
4231 gint parenthese_cnt = 0;
4233 cm_return_val_if_fail(start != NULL, FALSE);
4234 cm_return_val_if_fail(scanpos != NULL, FALSE);
4235 cm_return_val_if_fail(bp != NULL, FALSE);
4236 cm_return_val_if_fail(ep != NULL, FALSE);
4240 /* find end point of URI */
4241 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4242 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4243 !IS_ASCII(*(const guchar *)ep_) ||
4244 strchr("[]{}<>\"", *ep_)) {
4246 } else if (strchr("(", *ep_)) {
4248 } else if (strchr(")", *ep_)) {
4249 if (parenthese_cnt > 0)
4256 /* no punctuation at end of string */
4258 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4259 * should pass some URI type to this function and decide on that whether
4260 * to perform punctuation stripping */
4262 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4264 for (; ep_ - 1 > scanpos + 1 &&
4265 IS_REAL_PUNCT(*(ep_ - 1));
4269 #undef IS_REAL_PUNCT
4276 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4278 while (bp && *bp && g_ascii_isspace(*bp))
4280 return g_strndup(bp, ep - bp);
4283 /* valid mail address characters */
4284 #define IS_RFC822_CHAR(ch) \
4288 !g_ascii_isspace(ch) && \
4289 !strchr("(),;<>\"", (ch)))
4291 /* alphabet and number within 7bit ASCII */
4292 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4293 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4295 static GHashTable *create_domain_tab(void)
4297 static const gchar *toplvl_domains [] = {
4299 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4300 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4301 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4302 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4303 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4304 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4305 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4306 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4307 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4308 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4309 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4310 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4311 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4312 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4313 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4314 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4315 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4316 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4317 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4318 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4319 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4320 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4321 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4322 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4323 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4324 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4327 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4329 cm_return_val_if_fail(htab, NULL);
4330 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4331 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4335 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4337 const gint MAX_LVL_DOM_NAME_LEN = 6;
4338 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4339 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4342 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4345 for (p = buf; p < m && first < last; *p++ = *first++)
4349 return g_hash_table_lookup(tab, buf) != NULL;
4352 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4353 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4354 const gchar **bp, const gchar **ep, gboolean hdr)
4356 /* more complex than the uri part because we need to scan back and forward starting from
4357 * the scan position. */
4358 gboolean result = FALSE;
4359 const gchar *bp_ = NULL;
4360 const gchar *ep_ = NULL;
4361 static GHashTable *dom_tab;
4362 const gchar *last_dot = NULL;
4363 const gchar *prelast_dot = NULL;
4364 const gchar *last_tld_char = NULL;
4366 /* the informative part of the email address (describing the name
4367 * of the email address owner) may contain quoted parts. the
4368 * closure stack stores the last encountered quotes. */
4369 gchar closure_stack[128];
4370 gchar *ptr = closure_stack;
4372 cm_return_val_if_fail(start != NULL, FALSE);
4373 cm_return_val_if_fail(scanpos != NULL, FALSE);
4374 cm_return_val_if_fail(bp != NULL, FALSE);
4375 cm_return_val_if_fail(ep != NULL, FALSE);
4378 const gchar *start_quote = NULL;
4379 const gchar *end_quote = NULL;
4381 /* go to the real start */
4382 if (start[0] == ',')
4384 if (start[0] == ';')
4386 while (start[0] == '\n' || start[0] == '\r')
4388 while (start[0] == ' ' || start[0] == '\t')
4393 /* check if there are quotes (to skip , in them) */
4394 if (*start == '"') {
4395 start_quote = start;
4397 end_quote = strstr(start, "\"");
4403 /* skip anything between quotes */
4404 if (start_quote && end_quote) {
4409 /* find end (either , or ; or end of line) */
4410 if (strstr(start, ",") && strstr(start, ";"))
4411 *ep = strstr(start,",") < strstr(start, ";")
4412 ? strstr(start, ",") : strstr(start, ";");
4413 else if (strstr(start, ","))
4414 *ep = strstr(start, ",");
4415 else if (strstr(start, ";"))
4416 *ep = strstr(start, ";");
4418 *ep = start+strlen(start);
4420 /* go back to real start */
4421 if (start_quote && end_quote) {
4422 start = start_quote;
4425 /* check there's still an @ in that, or search
4426 * further if possible */
4427 if (strstr(start, "@") && strstr(start, "@") < *ep)
4429 else if (*ep < start+strlen(start)) {
4432 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4440 dom_tab = create_domain_tab();
4441 cm_return_val_if_fail(dom_tab, FALSE);
4443 /* scan start of address */
4444 for (bp_ = scanpos - 1;
4445 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4448 /* TODO: should start with an alnum? */
4450 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4453 if (bp_ != scanpos) {
4454 /* scan end of address */
4455 for (ep_ = scanpos + 1;
4456 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4458 prelast_dot = last_dot;
4460 if (*(last_dot + 1) == '.') {
4461 if (prelast_dot == NULL)
4463 last_dot = prelast_dot;
4468 /* TODO: really should terminate with an alnum? */
4469 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4474 if (last_dot == NULL)
4476 if (last_dot >= ep_)
4477 last_dot = prelast_dot;
4478 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4482 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4483 if (*last_tld_char == '?')
4486 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4493 if (!result) return FALSE;
4495 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4496 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4497 && IS_RFC822_CHAR(*(ep_ + 3))) {
4498 /* this informative part with an @ in it is
4499 * followed by the email address */
4502 /* go to matching '>' (or next non-rfc822 char, like \n) */
4503 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4506 /* include the bracket */
4507 if (*ep_ == '>') ep_++;
4509 /* include the leading quote */
4517 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4518 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4521 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4522 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4525 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4526 #define IN_STACK() (ptr > closure_stack)
4527 /* has underrun check */
4528 #define POP_STACK() if(IN_STACK()) --ptr
4529 /* has overrun check */
4530 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4531 /* has underrun check */
4532 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4536 /* scan for the informative part. */
4537 for (bp_ -= 2; bp_ >= start; bp_--) {
4538 /* if closure on the stack keep scanning */
4539 if (PEEK_STACK() == *bp_) {
4543 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4548 /* if nothing in the closure stack, do the special conditions
4549 * the following if..else expression simply checks whether
4550 * a token is acceptable. if not acceptable, the clause
4551 * should terminate the loop with a 'break' */
4552 if (!PEEK_STACK()) {
4554 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4555 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4556 /* hyphens are allowed, but only in
4558 } else if (strchr(" \"'", *bp_)) {
4559 /* but anything not being a punctiation
4562 break; /* anything else is rejected */
4569 /* scan forward (should start with an alnum) */
4570 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4586 #undef IS_ASCII_ALNUM
4587 #undef IS_RFC822_CHAR
4589 gchar *make_email_string(const gchar *bp, const gchar *ep)
4591 /* returns a mailto: URI; mailto: is also used to detect the
4592 * uri type later on in the button_pressed signal handler */
4596 tmp = g_strndup(bp, ep - bp);
4597 result = g_strconcat("mailto:", tmp, NULL);
4603 gchar *make_http_string(const gchar *bp, const gchar *ep)
4605 /* returns an http: URI; */
4609 while (bp && *bp && g_ascii_isspace(*bp))
4611 tmp = g_strndup(bp, ep - bp);
4612 result = g_strconcat("http://", tmp, NULL);
4618 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4620 FILE *fp = g_fopen(path, "rb");
4621 gchar buf[BUFFSIZE];
4622 gchar *result = NULL;
4625 while (fgets(buf, sizeof (buf), fp) != NULL) {
4626 gchar **parts = g_strsplit(buf, ";", 3);
4627 gchar *trimmed = parts[0];
4628 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4630 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4631 trimmed[strlen(trimmed)-1] = '\0';
4633 if (!strcmp(trimmed, type)) {
4634 gboolean needsterminal = FALSE;
4635 if (parts[2] && strstr(parts[2], "needsterminal")) {
4636 needsterminal = TRUE;
4638 if (parts[2] && strstr(parts[2], "test=")) {
4639 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4640 gchar *testcmd = orig_testcmd;
4641 if (strstr(testcmd,";"))
4642 *(strstr(testcmd,";")) = '\0';
4643 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4645 while (testcmd[strlen(testcmd)-1] == '\n')
4646 testcmd[strlen(testcmd)-1] = '\0';
4647 while (testcmd[strlen(testcmd)-1] == '\r')
4648 testcmd[strlen(testcmd)-1] = '\0';
4649 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4650 testcmd[strlen(testcmd)-1] = '\0';
4652 if (strstr(testcmd, "%s")) {
4653 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4654 gint res = system(tmp);
4656 g_free(orig_testcmd);
4663 gint res = system(testcmd);
4664 g_free(orig_testcmd);
4674 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4676 while (trimmed[strlen(trimmed)-1] == '\n')
4677 trimmed[strlen(trimmed)-1] = '\0';
4678 while (trimmed[strlen(trimmed)-1] == '\r')
4679 trimmed[strlen(trimmed)-1] = '\0';
4680 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4681 trimmed[strlen(trimmed)-1] = '\0';
4682 result = g_strdup(trimmed);
4685 /* if there are no single quotes around %s, add them.
4686 * '.*%s.*' is ok, as in display 'png:%s'
4688 if (strstr(result, "%s")
4689 && !(strstr(result, "'") < strstr(result,"%s") &&
4690 strstr(strstr(result,"%s"), "'"))) {
4691 gchar *start = g_strdup(result);
4692 gchar *end = g_strdup(strstr(result, "%s")+2);
4694 *strstr(start, "%s") = '\0';
4695 tmp = g_strconcat(start,"'%s'",end, NULL);
4701 if (needsterminal) {
4702 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4713 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4715 gchar *result = NULL;
4719 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4720 result = mailcap_get_command_in_file(path, type, file_to_open);
4724 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4728 void mailcap_update_default(const gchar *type, const gchar *command)
4730 gchar *path = NULL, *outpath = NULL;
4731 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4732 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4733 FILE *fp = g_fopen(path, "rb");
4734 FILE *outfp = g_fopen(outpath, "wb");
4735 gchar buf[BUFFSIZE];
4736 gboolean err = FALSE;
4744 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4745 gchar **parts = g_strsplit(buf, ";", 3);
4746 gchar *trimmed = parts[0];
4747 while (trimmed[0] == ' ')
4749 while (trimmed[strlen(trimmed)-1] == ' ')
4750 trimmed[strlen(trimmed)-1] = '\0';
4752 if (!strcmp(trimmed, type)) {
4757 if(fputs(buf, outfp) == EOF) {
4764 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4770 if (fclose(outfp) == EOF)
4774 g_rename(outpath, path);
4780 gint copy_dir(const gchar *src, const gchar *dst)
4785 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4786 g_warning("failed to open directory: %s\n", src);
4790 if (make_dir(dst) < 0)
4793 while ((name = g_dir_read_name(dir)) != NULL) {
4794 gchar *old_file, *new_file;
4795 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4796 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4797 debug_print("copying: %s -> %s\n", old_file, new_file);
4798 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4799 gint r = copy_file(old_file, new_file, TRUE);
4806 /* Windows has no symlinks. Or well, Vista seems to
4807 have something like this but the semantics might be
4808 different. Thus we don't use it under Windows. */
4809 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4812 gchar *target = g_file_read_link(old_file, &error);
4814 r = symlink(target, new_file);
4821 #endif /*G_OS_WIN32*/
4822 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4823 gint r = copy_dir(old_file, new_file);
4834 /* crude test to see if a file is an email. */
4835 gboolean file_is_email (const gchar *filename)
4841 if (filename == NULL)
4843 if ((fp = g_fopen(filename, "rb")) == NULL)
4845 while (i < 60 && score < 3
4846 && fgets(buffer, sizeof (buffer), fp) > 0) {
4847 if (!strncmp(buffer, "From:", strlen("From:")))
4849 if (!strncmp(buffer, "To:", strlen("To:")))
4851 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4856 return (score >= 3);
4859 gboolean sc_g_list_bigger(GList *list, gint max)
4863 while (cur && i <= max+1) {
4870 gboolean sc_g_slist_bigger(GSList *list, gint max)
4874 while (cur && i <= max+1) {
4881 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4882 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4883 NULL, NULL, NULL, NULL, NULL, NULL};
4884 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4885 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4886 NULL, NULL, NULL, NULL, NULL, NULL};
4888 gint daynames_len[] = {0,0,0,0,0,0,0};
4889 gint monthnames_len[] = {0,0,0,0,0,0,
4891 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4892 gint s_monthnames_len[] = {0,0,0,0,0,0,
4894 const gchar *s_am_up = NULL;
4895 const gchar *s_pm_up = NULL;
4896 const gchar *s_am_low = NULL;
4897 const gchar *s_pm_low = NULL;
4899 gint s_am_up_len = 0;
4900 gint s_pm_up_len = 0;
4901 gint s_am_low_len = 0;
4902 gint s_pm_low_len = 0;
4904 const gchar *def_loc_format = NULL;
4905 const gchar *date_loc_format = NULL;
4906 const gchar *time_loc_format = NULL;
4907 const gchar *time_am_pm = NULL;
4909 static gboolean time_names_init_done = FALSE;
4911 static void init_time_names(void)
4915 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4916 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4917 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4918 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4919 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4920 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4921 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4923 monthnames[0] = Q_("Complete month name for use by strftime|January");
4924 monthnames[1] = Q_("Complete month name for use by strftime|February");
4925 monthnames[2] = Q_("Complete month name for use by strftime|March");
4926 monthnames[3] = Q_("Complete month name for use by strftime|April");
4927 monthnames[4] = Q_("Complete month name for use by strftime|May");
4928 monthnames[5] = Q_("Complete month name for use by strftime|June");
4929 monthnames[6] = Q_("Complete month name for use by strftime|July");
4930 monthnames[7] = Q_("Complete month name for use by strftime|August");
4931 monthnames[8] = Q_("Complete month name for use by strftime|September");
4932 monthnames[9] = Q_("Complete month name for use by strftime|October");
4933 monthnames[10] = Q_("Complete month name for use by strftime|November");
4934 monthnames[11] = Q_("Complete month name for use by strftime|December");
4936 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4937 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4938 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4939 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4940 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4941 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4942 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4944 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4945 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4946 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4947 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4948 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4949 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4950 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4951 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4952 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4953 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4954 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4955 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4957 for (i = 0; i < 7; i++) {
4958 daynames_len[i] = strlen(daynames[i]);
4959 s_daynames_len[i] = strlen(s_daynames[i]);
4961 for (i = 0; i < 12; i++) {
4962 monthnames_len[i] = strlen(monthnames[i]);
4963 s_monthnames_len[i] = strlen(s_monthnames[i]);
4966 s_am_up = Q_("For use by strftime (morning)|AM");
4967 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4968 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4969 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4971 s_am_up_len = strlen(s_am_up);
4972 s_pm_up_len = strlen(s_pm_up);
4973 s_am_low_len = strlen(s_am_low);
4974 s_pm_low_len = strlen(s_pm_low);
4976 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4977 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4978 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4980 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4982 time_names_init_done = TRUE;
4985 #define CHECK_SIZE() { \
4986 total_done += len; \
4987 if (total_done >= buflen) { \
4988 buf[buflen-1] = '\0'; \
4993 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4995 gchar *curpos = buf;
4996 gint total_done = 0;
4997 gchar subbuf[64], subfmt[64];
4998 static time_t last_tzset = (time_t)0;
5000 if (!time_names_init_done)
5003 if (format == NULL || lt == NULL)
5006 if (last_tzset != time(NULL)) {
5008 last_tzset = time(NULL);
5011 if (*format == '%') {
5012 gint len = 0, tmp = 0;
5016 len = 1; CHECK_SIZE();
5020 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5021 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5024 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5025 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5029 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5030 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5033 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5034 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5037 fast_strftime(subbuf, 64, def_loc_format, lt);
5038 len = strlen(subbuf); CHECK_SIZE();
5039 strncpy2(curpos, subbuf, buflen - total_done);
5042 total_done += 2; CHECK_SIZE();
5043 tmp = (lt->tm_year + 1900)/100;
5044 *curpos++ = '0'+(tmp / 10);
5045 *curpos++ = '0'+(tmp % 10);
5048 total_done += 2; CHECK_SIZE();
5049 *curpos++ = '0'+(lt->tm_mday / 10);
5050 *curpos++ = '0'+(lt->tm_mday % 10);
5053 total_done += 8; CHECK_SIZE();
5054 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5055 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5057 *curpos++ = '0'+(lt->tm_mday / 10);
5058 *curpos++ = '0'+(lt->tm_mday % 10);
5060 tmp = lt->tm_year%100;
5061 *curpos++ = '0'+(tmp / 10);
5062 *curpos++ = '0'+(tmp % 10);
5065 len = 2; CHECK_SIZE();
5066 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5069 len = 10; CHECK_SIZE();
5070 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5071 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5074 total_done += 2; CHECK_SIZE();
5075 *curpos++ = '0'+(lt->tm_hour / 10);
5076 *curpos++ = '0'+(lt->tm_hour % 10);
5079 total_done += 2; CHECK_SIZE();
5085 *curpos++ = '0'+(tmp / 10);
5086 *curpos++ = '0'+(tmp % 10);
5089 len = 3; CHECK_SIZE();
5090 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5093 len = 2; CHECK_SIZE();
5094 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5097 len = 2; CHECK_SIZE();
5103 snprintf(curpos, buflen - total_done, "%2d", tmp);
5106 total_done += 2; CHECK_SIZE();
5107 tmp = lt->tm_mon + 1;
5108 *curpos++ = '0'+(tmp / 10);
5109 *curpos++ = '0'+(tmp % 10);
5112 total_done += 2; CHECK_SIZE();
5113 *curpos++ = '0'+(lt->tm_min / 10);
5114 *curpos++ = '0'+(lt->tm_min % 10);
5117 len = 1; CHECK_SIZE();
5121 if (lt->tm_hour >= 12) {
5122 len = s_pm_up_len; CHECK_SIZE();
5123 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5125 len = s_am_up_len; CHECK_SIZE();
5126 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5130 if (lt->tm_hour >= 12) {
5131 len = s_pm_low_len; CHECK_SIZE();
5132 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5134 len = s_am_low_len; CHECK_SIZE();
5135 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5139 fast_strftime(subbuf, 64, time_am_pm, lt);
5140 len = strlen(subbuf); CHECK_SIZE();
5141 strncpy2(curpos, subbuf, buflen - total_done);
5144 total_done += 5; CHECK_SIZE();
5145 *curpos++ = '0'+(lt->tm_hour / 10);
5146 *curpos++ = '0'+(lt->tm_hour % 10);
5148 *curpos++ = '0'+(lt->tm_min / 10);
5149 *curpos++ = '0'+(lt->tm_min % 10);
5152 snprintf(subbuf, 64, "%ld", mktime(lt));
5153 len = strlen(subbuf); CHECK_SIZE();
5154 strncpy2(curpos, subbuf, buflen - total_done);
5157 total_done += 2; CHECK_SIZE();
5158 *curpos++ = '0'+(lt->tm_sec / 10);
5159 *curpos++ = '0'+(lt->tm_sec % 10);
5162 len = 1; CHECK_SIZE();
5166 total_done += 8; CHECK_SIZE();
5167 *curpos++ = '0'+(lt->tm_hour / 10);
5168 *curpos++ = '0'+(lt->tm_hour % 10);
5170 *curpos++ = '0'+(lt->tm_min / 10);
5171 *curpos++ = '0'+(lt->tm_min % 10);
5173 *curpos++ = '0'+(lt->tm_sec / 10);
5174 *curpos++ = '0'+(lt->tm_sec % 10);
5177 len = 1; CHECK_SIZE();
5178 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5181 len = 1; CHECK_SIZE();
5182 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5185 fast_strftime(subbuf, 64, date_loc_format, lt);
5186 len = strlen(subbuf); CHECK_SIZE();
5187 strncpy2(curpos, subbuf, buflen - total_done);
5190 fast_strftime(subbuf, 64, time_loc_format, lt);
5191 len = strlen(subbuf); CHECK_SIZE();
5192 strncpy2(curpos, subbuf, buflen - total_done);
5195 total_done += 2; CHECK_SIZE();
5196 tmp = lt->tm_year%100;
5197 *curpos++ = '0'+(tmp / 10);
5198 *curpos++ = '0'+(tmp % 10);
5201 len = 4; CHECK_SIZE();
5202 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5212 /* let these complicated ones be done with the libc */
5213 snprintf(subfmt, 64, "%%%c", *format);
5214 strftime(subbuf, 64, subfmt, lt);
5215 len = strlen(subbuf); CHECK_SIZE();
5216 strncpy2(curpos, subbuf, buflen - total_done);
5220 /* let these complicated modifiers be done with the libc */
5221 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5222 strftime(subbuf, 64, subfmt, lt);
5223 len = strlen(subbuf); CHECK_SIZE();
5224 strncpy2(curpos, subbuf, buflen - total_done);
5228 if (format && *format)
5229 g_warning("format error (%c)", *format);
5236 int len = 1; CHECK_SIZE();
5237 *curpos++ = *format++;
5244 gboolean prefs_common_get_use_shred(void);
5248 #define WEXITSTATUS(x) (x)
5251 int claws_unlink(const gchar *filename)
5254 static int found_shred = -1;
5255 static const gchar *args[4];
5257 if (filename == NULL)
5260 if (prefs_common_get_use_shred()) {
5261 if (found_shred == -1) {
5263 args[0] = g_find_program_in_path("shred");
5264 debug_print("found shred: %s\n", args[0]);
5265 found_shred = (args[0] != NULL) ? 1:0;
5269 if (found_shred == 1) {
5270 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5271 if (s.st_nlink == 1) {
5274 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5275 NULL, NULL, NULL, NULL, &status, NULL);
5276 debug_print("%s %s exited with status %d\n",
5277 args[0], filename, WEXITSTATUS(status));
5278 if (truncate(filename, 0) < 0)
5279 g_warning("couln't truncate");
5284 return g_unlink(filename);