2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
29 #include <glib/gi18n.h>
39 #include <sys/param.h>
41 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
49 #include <sys/types.h>
51 # include <sys/wait.h>
58 #include <sys/utsname.h>
72 # include <tablet-browser-interface.h>
74 # include <osso-browser-interface.h>
80 #include "../codeconv.h"
84 static gboolean debug_mode = FALSE;
86 static GSList *tempfiles=NULL;
89 /* Return true if we are running as root. This function should beused
90 instead of getuid () == 0. */
91 gboolean superuser_p (void)
94 return w32_is_administrator ();
102 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
103 gint g_chdir(const gchar *path)
106 if (G_WIN32_HAVE_WIDECHAR_API()) {
111 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
117 retval = _wchdir(wpath);
129 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
130 if (cp_path == NULL) {
135 retval = chdir(cp_path);
148 gint g_chmod(const gchar *path, gint mode)
151 if (G_WIN32_HAVE_WIDECHAR_API()) {
156 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
162 retval = _wchmod(wpath, mode);
174 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
175 if (cp_path == NULL) {
180 retval = chmod(cp_path, mode);
189 return chmod(path, mode);
193 FILE* g_fopen(const gchar *filename, const gchar *mode)
196 char *name = g_win32_locale_filename_from_utf8(filename);
197 FILE* fp = fopen(name, mode);
201 return fopen(filename, mode);
204 int g_open(const gchar *filename, int flags, int mode)
207 char *name = g_win32_locale_filename_from_utf8(filename);
208 int fd = open(name, flags, mode);
212 return open(filename, flags, mode);
215 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
219 gint mkstemp_name(gchar *template, gchar **name_used)
221 static gulong count=0; /* W32-_mktemp only supports up to 27
225 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
226 tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
227 (S_IRUSR | S_IWUSR));
229 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
231 perror(g_strdup_printf("cant create %s",*name_used));
237 #endif /* G_OS_WIN32 */
240 gint mkstemp(gchar *template)
243 gint res = mkstemp_name(template, &dummyname);
247 #endif /* G_OS_WIN32 */
249 void list_free_strings(GList *list)
251 list = g_list_first(list);
253 while (list != NULL) {
259 void slist_free_strings(GSList *list)
261 while (list != NULL) {
267 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
272 void hash_free_strings(GHashTable *table)
274 g_hash_table_foreach(table, hash_free_strings_func, NULL);
277 gint str_case_equal(gconstpointer v, gconstpointer v2)
279 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
282 guint str_case_hash(gconstpointer key)
284 const gchar *p = key;
288 h = g_ascii_tolower(h);
289 for (p += 1; *p != '\0'; p++)
290 h = (h << 5) - h + g_ascii_tolower(*p);
296 void ptr_array_free_strings(GPtrArray *array)
301 cm_return_if_fail(array != NULL);
303 for (i = 0; i < array->len; i++) {
304 str = g_ptr_array_index(array, i);
309 gint to_number(const gchar *nstr)
311 register const gchar *p;
313 if (*nstr == '\0') return -1;
315 for (p = nstr; *p != '\0'; p++)
316 if (!g_ascii_isdigit(*p)) return -1;
321 /* convert integer into string,
322 nstr must be not lower than 11 characters length */
323 gchar *itos_buf(gchar *nstr, gint n)
325 g_snprintf(nstr, 11, "%d", n);
329 /* convert integer into string */
332 static gchar nstr[11];
334 return itos_buf(nstr, n);
337 #define divide(num,divisor,i,d) \
339 i = num >> divisor; \
340 d = num & ((1<<divisor)-1); \
341 d = (d*100) >> divisor; \
346 * \brief Convert a given size in bytes in a human-readable string
348 * \param size The size expressed in bytes to convert in string
349 * \return The string that respresents the size in an human-readable way
351 gchar *to_human_readable(goffset size)
353 static gchar str[14];
354 static gchar *b_format = NULL, *kb_format = NULL,
355 *mb_format = NULL, *gb_format = NULL;
356 register int t = 0, r = 0;
357 if (b_format == NULL) {
359 kb_format = _("%d.%02dKB");
360 mb_format = _("%d.%02dMB");
361 gb_format = _("%.2fGB");
364 if (size < (goffset)1024) {
365 g_snprintf(str, sizeof(str), b_format, (gint)size);
367 } else if (size >> 10 < (goffset)1024) {
368 divide(size, 10, t, r);
369 g_snprintf(str, sizeof(str), kb_format, t, r);
371 } else if (size >> 20 < (goffset)1024) {
372 divide(size, 20, t, r);
373 g_snprintf(str, sizeof(str), mb_format, t, r);
376 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
381 /* strcmp with NULL-checking */
382 gint strcmp2(const gchar *s1, const gchar *s2)
384 if (s1 == NULL || s2 == NULL)
387 return strcmp(s1, s2);
389 /* strstr with NULL-checking */
390 gchar *strstr2(const gchar *s1, const gchar *s2)
392 if (s1 == NULL || s2 == NULL)
395 return strstr(s1, s2);
398 gint path_cmp(const gchar *s1, const gchar *s2)
403 gchar *s1buf, *s2buf;
406 if (s1 == NULL || s2 == NULL) return -1;
407 if (*s1 == '\0' || *s2 == '\0') return -1;
410 s1buf = g_strdup (s1);
411 s2buf = g_strdup (s2);
412 subst_char (s1buf, '/', G_DIR_SEPARATOR);
413 subst_char (s2buf, '/', G_DIR_SEPARATOR);
416 #endif /* !G_OS_WIN32 */
421 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
422 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
424 rc = strncmp(s1, s2, MAX(len1, len2));
428 #endif /* !G_OS_WIN32 */
432 /* remove trailing return code */
433 gchar *strretchomp(gchar *str)
437 if (!*str) return str;
439 for (s = str + strlen(str) - 1;
440 s >= str && (*s == '\n' || *s == '\r');
447 /* remove trailing character */
448 gchar *strtailchomp(gchar *str, gchar tail_char)
452 if (!*str) return str;
453 if (tail_char == '\0') return str;
455 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
461 /* remove CR (carriage return) */
462 gchar *strcrchomp(gchar *str)
466 if (!*str) return str;
468 s = str + strlen(str) - 1;
469 if (*s == '\n' && s > str && *(s - 1) == '\r') {
477 gint file_strip_crs(const gchar *file)
479 FILE *fp = NULL, *outfp = NULL;
481 gchar *out = get_tmp_file();
485 fp = g_fopen(file, "rb");
489 outfp = g_fopen(out, "wb");
495 while (fgets(buf, sizeof (buf), fp) != NULL) {
497 if (fputs(buf, outfp) == EOF) {
505 if (fclose(outfp) == EOF) {
509 if (move_file(out, file, TRUE) < 0)
521 /* Similar to `strstr' but this function ignores the case of both strings. */
522 gchar *strcasestr(const gchar *haystack, const gchar *needle)
524 register size_t haystack_len, needle_len;
526 haystack_len = strlen(haystack);
527 needle_len = strlen(needle);
529 if (haystack_len < needle_len || needle_len == 0)
532 while (haystack_len >= needle_len) {
533 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
534 return (gchar *)haystack;
544 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
545 gconstpointer needle, size_t needlelen)
547 const gchar *haystack_ = (const gchar *)haystack;
548 const gchar *needle_ = (const gchar *)needle;
549 const gchar *haystack_cur = (const gchar *)haystack;
550 size_t haystack_left = haystacklen;
553 return memchr(haystack_, *needle_, haystacklen);
555 while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
557 if (haystacklen - (haystack_cur - haystack_) < needlelen)
559 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
560 return (gpointer)haystack_cur;
563 haystack_left = haystacklen - (haystack_cur - haystack_);
570 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
571 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
573 register const gchar *s = src;
574 register gchar *d = dest;
584 /* Examine if next block is non-ASCII string */
585 gboolean is_next_nonascii(const gchar *s)
589 /* skip head space */
590 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
592 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
593 if (*(guchar *)p > 127 || *(guchar *)p < 32)
600 gint get_next_word_len(const gchar *s)
604 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
610 static void trim_subject_for_compare(gchar *str)
614 eliminate_parenthesis(str, '[', ']');
615 eliminate_parenthesis(str, '(', ')');
618 srcp = str + subject_get_prefix_length(str);
620 memmove(str, srcp, strlen(srcp) + 1);
623 static void trim_subject_for_sort(gchar *str)
629 srcp = str + subject_get_prefix_length(str);
631 memmove(str, srcp, strlen(srcp) + 1);
634 /* compare subjects */
635 gint subject_compare(const gchar *s1, const gchar *s2)
639 if (!s1 || !s2) return -1;
640 if (!*s1 || !*s2) return -1;
642 Xstrdup_a(str1, s1, return -1);
643 Xstrdup_a(str2, s2, return -1);
645 trim_subject_for_compare(str1);
646 trim_subject_for_compare(str2);
648 if (!*str1 || !*str2) return -1;
650 return strcmp(str1, str2);
653 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
657 if (!s1 || !s2) return -1;
659 Xstrdup_a(str1, s1, return -1);
660 Xstrdup_a(str2, s2, return -1);
662 trim_subject_for_sort(str1);
663 trim_subject_for_sort(str2);
665 return g_utf8_collate(str1, str2);
668 void trim_subject(gchar *str)
670 register gchar *srcp;
676 srcp = str + subject_get_prefix_length(str);
681 } else if (*srcp == '(') {
693 else if (*srcp == cl)
700 while (g_ascii_isspace(*srcp)) srcp++;
701 memmove(str, srcp, strlen(srcp) + 1);
704 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
706 register gchar *srcp, *destp;
711 while ((destp = strchr(destp, op))) {
717 else if (*srcp == cl)
723 while (g_ascii_isspace(*srcp)) srcp++;
724 memmove(destp, srcp, strlen(srcp) + 1);
728 void extract_parenthesis(gchar *str, gchar op, gchar cl)
730 register gchar *srcp, *destp;
735 while ((srcp = strchr(destp, op))) {
738 memmove(destp, srcp + 1, strlen(srcp));
743 else if (*destp == cl)
755 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
758 register gchar *srcp, *destp;
760 gboolean in_quote = FALSE;
764 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
767 memmove(destp, srcp + 1, strlen(srcp));
770 if (*destp == op && !in_quote)
772 else if (*destp == cl && !in_quote)
774 else if (*destp == quote_chr)
786 void extract_quote(gchar *str, gchar quote_chr)
790 if ((str = strchr(str, quote_chr))) {
792 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
793 memmove(p - 1, p, strlen(p) + 1);
798 memmove(str, str + 1, p - str);
803 void eliminate_address_comment(gchar *str)
805 register gchar *srcp, *destp;
810 while ((destp = strchr(destp, '"'))) {
811 if ((srcp = strchr(destp + 1, '"'))) {
816 while (g_ascii_isspace(*srcp)) srcp++;
817 memmove(destp, srcp, strlen(srcp) + 1);
827 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
833 else if (*srcp == ')')
839 while (g_ascii_isspace(*srcp)) srcp++;
840 memmove(destp, srcp, strlen(srcp) + 1);
844 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
846 gboolean in_quote = FALSE;
849 if (*str == c && !in_quote)
851 if (*str == quote_chr)
859 void extract_address(gchar *str)
861 eliminate_address_comment(str);
862 if (strchr_with_skip_quote(str, '"', '<'))
863 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
867 void extract_list_id_str(gchar *str)
869 if (strchr_with_skip_quote(str, '"', '<'))
870 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
874 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
879 if (!str) return addr_list;
881 Xstrdup_a(work, str, return addr_list);
884 eliminate_address_comment(work);
887 while (workp && *workp) {
890 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
896 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
897 extract_parenthesis_with_skip_quote
898 (workp, '"', '<', '>');
902 addr_list = g_slist_append(addr_list, g_strdup(workp));
910 GSList *address_list_append(GSList *addr_list, const gchar *str)
912 return address_list_append_real(addr_list, str, TRUE);
915 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
917 return address_list_append_real(addr_list, str, FALSE);
920 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
924 if (!str) return msgid_list;
927 while (strp && *strp) {
928 const gchar *start, *end;
931 if ((start = strchr(strp, '<')) != NULL) {
932 end = strchr(start + 1, '>');
937 msgid = g_strndup(start + 1, end - start - 1);
940 msgid_list = g_slist_prepend(msgid_list, msgid);
950 GSList *references_list_append(GSList *msgid_list, const gchar *str)
954 list = references_list_prepend(NULL, str);
955 list = g_slist_reverse(list);
956 msgid_list = g_slist_concat(msgid_list, list);
961 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
966 if (!str) return group_list;
968 Xstrdup_a(work, str, return group_list);
972 while (workp && *workp) {
975 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
983 group_list = g_slist_append(group_list,
992 GList *add_history(GList *list, const gchar *str)
997 cm_return_val_if_fail(str != NULL, list);
999 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1002 list = g_list_remove(list, old->data);
1004 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1007 last = g_list_last(list);
1009 oldstr = last->data;
1010 list = g_list_remove(list, last->data);
1015 list = g_list_prepend(list, g_strdup(str));
1020 void remove_return(gchar *str)
1022 register gchar *p = str;
1025 if (*p == '\n' || *p == '\r')
1026 memmove(p, p + 1, strlen(p));
1032 void remove_space(gchar *str)
1034 register gchar *p = str;
1039 while (g_ascii_isspace(*(p + spc)))
1042 memmove(p, p + spc, strlen(p + spc) + 1);
1048 void unfold_line(gchar *str)
1050 register gchar *p = str;
1054 if (*p == '\n' || *p == '\r') {
1057 while (g_ascii_isspace(*(p + spc)))
1060 memmove(p, p + spc, strlen(p + spc) + 1);
1066 void subst_char(gchar *str, gchar orig, gchar subst)
1068 register gchar *p = str;
1077 void subst_chars(gchar *str, gchar *orig, gchar subst)
1079 register gchar *p = str;
1082 if (strchr(orig, *p) != NULL)
1088 void subst_for_filename(gchar *str)
1093 subst_chars(str, "\t\r\n\\/*:", '_');
1095 subst_chars(str, "\t\r\n\\/*", '_');
1099 void subst_for_shellsafe_filename(gchar *str)
1103 subst_for_filename(str);
1104 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1107 gboolean is_ascii_str(const gchar *str)
1109 const guchar *p = (const guchar *)str;
1111 while (*p != '\0') {
1112 if (*p != '\t' && *p != ' ' &&
1113 *p != '\r' && *p != '\n' &&
1114 (*p < 32 || *p >= 127))
1122 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1124 gchar * position = NULL;
1125 gchar * tmp_pos = NULL;
1128 if (quote_chars == NULL)
1131 for (i = 0; i < strlen(quote_chars); i++) {
1132 tmp_pos = strrchr (str, quote_chars[i]);
1134 || (tmp_pos != NULL && position <= tmp_pos) )
1140 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1142 const gchar *first_pos;
1143 const gchar *last_pos;
1144 const gchar *p = str;
1145 gint quote_level = -1;
1147 /* speed up line processing by only searching to the last '>' */
1148 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1149 /* skip a line if it contains a '<' before the initial '>' */
1150 if (memchr(str, '<', first_pos - str) != NULL)
1152 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1156 while (p <= last_pos) {
1157 while (p < last_pos) {
1158 if (g_ascii_isspace(*p))
1164 if (strchr(quote_chars, *p))
1166 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1167 /* any characters are allowed except '-','<' and space */
1168 while (*p != '-' && *p != '<'
1169 && !strchr(quote_chars, *p)
1170 && !g_ascii_isspace(*p)
1173 if (strchr(quote_chars, *p))
1185 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1187 const gchar *p = str, *q;
1188 gint cur_line = 0, len;
1190 while ((q = strchr(p, '\n')) != NULL) {
1192 if (len > max_chars) {
1202 if (len > max_chars) {
1211 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1213 gchar * position = NULL;
1214 gchar * tmp_pos = NULL;
1217 if (quote_chars == NULL)
1220 for (i = 0; i < strlen(quote_chars); i++) {
1221 tmp_pos = strchr (str, quote_chars[i]);
1223 || (tmp_pos != NULL && position >= tmp_pos) )
1229 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1231 register guint haystack_len, needle_len;
1232 gboolean in_squote = FALSE, in_dquote = FALSE;
1234 haystack_len = strlen(haystack);
1235 needle_len = strlen(needle);
1237 if (haystack_len < needle_len || needle_len == 0)
1240 while (haystack_len >= needle_len) {
1241 if (!in_squote && !in_dquote &&
1242 !strncmp(haystack, needle, needle_len))
1243 return (gchar *)haystack;
1245 /* 'foo"bar"' -> foo"bar"
1246 "foo'bar'" -> foo'bar' */
1247 if (*haystack == '\'') {
1250 else if (!in_dquote)
1252 } else if (*haystack == '\"') {
1255 else if (!in_squote)
1266 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1269 GSList *string_list = NULL, *slist;
1270 gchar **str_array, *s, *new_str;
1271 guint i, n = 1, len;
1273 cm_return_val_if_fail(str != NULL, NULL);
1274 cm_return_val_if_fail(delim != NULL, NULL);
1277 max_tokens = G_MAXINT;
1279 s = strstr_with_skip_quote(str, delim);
1281 guint delimiter_len = strlen(delim);
1285 new_str = g_strndup(str, len);
1287 if (new_str[0] == '\'' || new_str[0] == '\"') {
1288 if (new_str[len - 1] == new_str[0]) {
1289 new_str[len - 1] = '\0';
1290 memmove(new_str, new_str + 1, len - 1);
1293 string_list = g_slist_prepend(string_list, new_str);
1295 str = s + delimiter_len;
1296 s = strstr_with_skip_quote(str, delim);
1297 } while (--max_tokens && s);
1301 new_str = g_strdup(str);
1302 if (new_str[0] == '\'' || new_str[0] == '\"') {
1304 if (new_str[len - 1] == new_str[0]) {
1305 new_str[len - 1] = '\0';
1306 memmove(new_str, new_str + 1, len - 1);
1309 string_list = g_slist_prepend(string_list, new_str);
1313 str_array = g_new(gchar*, n);
1317 str_array[i--] = NULL;
1318 for (slist = string_list; slist; slist = slist->next)
1319 str_array[i--] = slist->data;
1321 g_slist_free(string_list);
1326 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1328 gchar *abbrev_group;
1330 const gchar *p = group;
1333 cm_return_val_if_fail(group != NULL, NULL);
1335 last = group + strlen(group);
1336 abbrev_group = ap = g_malloc(strlen(group) + 1);
1341 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1343 while (*p != '.') p++;
1346 return abbrev_group;
1351 return abbrev_group;
1354 gchar *trim_string(const gchar *str, gint len)
1356 const gchar *p = str;
1361 if (!str) return NULL;
1362 if (strlen(str) <= len)
1363 return g_strdup(str);
1364 if (g_utf8_validate(str, -1, NULL) == FALSE)
1365 return g_strdup(str);
1367 while (*p != '\0') {
1368 mb_len = g_utf8_skip[*(guchar *)p];
1371 else if (new_len + mb_len > len)
1378 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1379 return g_strconcat(new_str, "...", NULL);
1382 GList *uri_list_extract_filenames(const gchar *uri_list)
1384 GList *result = NULL;
1386 gchar *escaped_utf8uri;
1392 while (g_ascii_isspace(*p)) p++;
1393 if (!strncmp(p, "file:", 5)) {
1396 while (*q && *q != '\n' && *q != '\r') q++;
1399 gchar *file, *locale_file = NULL;
1401 while (q > p && g_ascii_isspace(*q))
1403 Xalloca(escaped_utf8uri, q - p + 2,
1405 Xalloca(file, q - p + 2,
1408 strncpy(escaped_utf8uri, p, q - p + 1);
1409 escaped_utf8uri[q - p + 1] = '\0';
1410 decode_uri(file, escaped_utf8uri);
1412 * g_filename_from_uri() rejects escaped/locale encoded uri
1413 * string which come from Nautilus.
1416 if (g_utf8_validate(file, -1, NULL))
1418 = conv_codeset_strdup(
1421 conv_get_locale_charset_str());
1423 locale_file = g_strdup(file + 5);
1425 locale_file = g_filename_from_uri(file, NULL, NULL);
1427 result = g_list_append(result, locale_file);
1431 p = strchr(p, '\n');
1438 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1441 static gint axtoi(const gchar *hexstr)
1443 gint hi, lo, result;
1446 if ('0' <= hi && hi <= '9') {
1449 if ('a' <= hi && hi <= 'f') {
1452 if ('A' <= hi && hi <= 'F') {
1457 if ('0' <= lo && lo <= '9') {
1460 if ('a' <= lo && lo <= 'f') {
1463 if ('A' <= lo && lo <= 'F') {
1466 result = lo + (16 * hi);
1470 gboolean is_uri_string(const gchar *str)
1472 while (str && *str && g_ascii_isspace(*str))
1474 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1475 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1476 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1477 g_ascii_strncasecmp(str, "www.", 4) == 0);
1480 gchar *get_uri_path(const gchar *uri)
1482 while (uri && *uri && g_ascii_isspace(*uri))
1484 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1485 return (gchar *)(uri + 7);
1486 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1487 return (gchar *)(uri + 8);
1488 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1489 return (gchar *)(uri + 6);
1491 return (gchar *)uri;
1494 gint get_uri_len(const gchar *str)
1498 if (is_uri_string(str)) {
1499 for (p = str; *p != '\0'; p++) {
1500 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1509 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1510 * plusses, and escape characters are used)
1512 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1514 gchar *dec = decoded_uri;
1515 const gchar *enc = encoded_uri;
1520 if (isxdigit((guchar)enc[0]) &&
1521 isxdigit((guchar)enc[1])) {
1527 if (with_plus && *enc == '+')
1539 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1541 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1544 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1546 gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1547 decode_uri_with_plus(buffer, encoded_uri, FALSE);
1551 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1552 gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1556 const gchar *forbidden_uris[] = { ".gnupg/",
1562 gint num_attach = 0;
1563 gchar **my_att = NULL;
1565 Xstrdup_a(tmp_mailto, mailto, return -1);
1567 if (!strncmp(tmp_mailto, "mailto:", 7))
1570 p = strchr(tmp_mailto, '?');
1577 *to = decode_uri_gdup(tmp_mailto);
1579 my_att = g_malloc(sizeof(char *));
1583 gchar *field, *value;
1600 if (*value == '\0') continue;
1602 if (from && !g_ascii_strcasecmp(field, "from")) {
1604 *from = decode_uri_gdup(value);
1606 gchar *tmp = decode_uri_gdup(value);
1607 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1611 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1613 *cc = decode_uri_gdup(value);
1615 gchar *tmp = decode_uri_gdup(value);
1616 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1620 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1622 *bcc = decode_uri_gdup(value);
1624 gchar *tmp = decode_uri_gdup(value);
1625 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1629 } else if (subject && !*subject &&
1630 !g_ascii_strcasecmp(field, "subject")) {
1631 *subject = decode_uri_gdup(value);
1632 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1633 *body = decode_uri_gdup(value);
1634 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1635 gchar *tmp = decode_uri_gdup(value);
1636 if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1637 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1641 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1643 gchar *tmp = decode_uri_gdup(value);
1644 for (; forbidden_uris[i]; i++) {
1645 if (strstr(tmp, forbidden_uris[i])) {
1646 g_print("Refusing to attach '%s', potential private data leak\n",
1654 /* attach is correct */
1656 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1657 my_att[num_attach-1] = tmp;
1658 my_att[num_attach] = NULL;
1660 } else if (inreplyto && !*inreplyto &&
1661 !g_ascii_strcasecmp(field, "in-reply-to")) {
1662 *inreplyto = decode_uri_gdup(value);
1673 #include <windows.h>
1674 #ifndef CSIDL_APPDATA
1675 #define CSIDL_APPDATA 0x001a
1677 #ifndef CSIDL_LOCAL_APPDATA
1678 #define CSIDL_LOCAL_APPDATA 0x001c
1680 #ifndef CSIDL_FLAG_CREATE
1681 #define CSIDL_FLAG_CREATE 0x8000
1683 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1687 w32_strerror (int w32_errno)
1689 static char strerr[256];
1690 int ec = (int)GetLastError ();
1694 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1695 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1696 strerr, DIM (strerr)-1, NULL);
1700 static __inline__ void *
1701 dlopen (const char * name, int flag)
1703 void * hd = LoadLibrary (name);
1707 static __inline__ void *
1708 dlsym (void * hd, const char * sym)
1712 void * fnc = GetProcAddress (hd, sym);
1721 static __inline__ const char *
1724 return w32_strerror (0);
1728 static __inline__ int
1740 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1742 static int initialized;
1743 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1747 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1753 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1755 handle = dlopen (dllnames[i], RTLD_LAZY);
1758 func = dlsym (handle, "SHGetFolderPathW");
1769 return func (a,b,c,d,e);
1774 /* Returns a static string with the directroy from which the module
1775 has been loaded. Returns an empty string on error. */
1776 static char *w32_get_module_dir(void)
1778 static char *moddir;
1781 char name[MAX_PATH+10];
1784 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1787 p = strrchr (name, '\\');
1793 moddir = g_strdup (name);
1797 #endif /* G_OS_WIN32 */
1799 /* Return a static string with the locale dir. */
1800 const gchar *get_locale_dir(void)
1802 static gchar *loc_dir;
1806 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1807 "\\share\\locale", NULL);
1810 loc_dir = LOCALEDIR;
1816 const gchar *get_home_dir(void)
1819 static char home_dir_utf16[MAX_PATH] = "";
1820 static gchar *home_dir_utf8 = NULL;
1821 if (home_dir_utf16[0] == '\0') {
1822 if (w32_shgetfolderpath
1823 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1824 NULL, 0, home_dir_utf16) < 0)
1825 strcpy (home_dir_utf16, "C:\\Sylpheed");
1826 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1828 return home_dir_utf8;
1830 static const gchar *homeenv = NULL;
1835 if (!homeenv && g_getenv("HOME") != NULL)
1836 homeenv = g_strdup(g_getenv("HOME"));
1838 homeenv = g_get_home_dir();
1844 static gchar *claws_rc_dir = NULL;
1845 static gboolean rc_dir_alt = FALSE;
1846 const gchar *get_rc_dir(void)
1850 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1853 return claws_rc_dir;
1856 void set_rc_dir(const gchar *dir)
1858 if (claws_rc_dir != NULL) {
1859 g_print("Error: rc_dir already set\n");
1862 if (g_path_is_absolute(dir))
1863 claws_rc_dir = g_strdup(dir);
1865 claws_rc_dir = g_strconcat(g_get_current_dir(),
1866 G_DIR_SEPARATOR_S, dir, NULL);
1868 debug_print("set rc_dir to %s\n", claws_rc_dir);
1869 if (!is_dir_exist(claws_rc_dir)) {
1870 if (make_dir_hier(claws_rc_dir) != 0) {
1871 g_print("Error: can't create %s\n",
1878 gboolean rc_dir_is_alt(void) {
1882 const gchar *get_mail_base_dir(void)
1884 return get_home_dir();
1888 const gchar *prefs_common_get_data_root(void);
1889 gchar *last_data_root = NULL;
1892 const gchar *get_news_cache_dir(void)
1894 static gchar *news_cache_dir = NULL;
1896 const gchar *data_root = prefs_common_get_data_root();
1897 if (strcmp2(data_root, last_data_root)) {
1898 g_free(news_cache_dir);
1899 news_cache_dir = NULL;
1902 if (!news_cache_dir)
1904 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1905 NEWS_CACHE_DIR, NULL);
1909 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1910 "Claws", G_DIR_SEPARATOR_S,
1911 g_get_user_name(), G_DIR_SEPARATOR_S,
1912 NEWS_CACHE_DIR, NULL);
1913 g_free(last_data_root);
1914 last_data_root = g_strdup(last_data_root);
1916 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1917 NEWS_CACHE_DIR, NULL);
1918 g_free(last_data_root);
1919 last_data_root = NULL;
1923 return news_cache_dir;
1926 const gchar *get_imap_cache_dir(void)
1928 static gchar *imap_cache_dir = NULL;
1930 const gchar *data_root = prefs_common_get_data_root();
1931 if (strcmp2(data_root, last_data_root)) {
1932 g_free(imap_cache_dir);
1933 imap_cache_dir = NULL;
1937 if (!imap_cache_dir)
1939 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1940 IMAP_CACHE_DIR, NULL);
1944 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1945 "Claws", G_DIR_SEPARATOR_S,
1946 g_get_user_name(), G_DIR_SEPARATOR_S,
1947 IMAP_CACHE_DIR, NULL);
1948 g_free(last_data_root);
1949 last_data_root = g_strdup(last_data_root);
1951 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1952 IMAP_CACHE_DIR, NULL);
1953 g_free(last_data_root);
1954 last_data_root = NULL;
1959 return imap_cache_dir;
1962 const gchar *get_mime_tmp_dir(void)
1964 static gchar *mime_tmp_dir = NULL;
1967 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1968 MIME_TMP_DIR, NULL);
1970 return mime_tmp_dir;
1973 const gchar *get_template_dir(void)
1975 static gchar *template_dir = NULL;
1978 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1979 TEMPLATE_DIR, NULL);
1981 return template_dir;
1985 const gchar *get_cert_file(void)
1987 const gchar *cert_file = NULL;
1989 cert_file = g_strconcat(w32_get_module_dir(),
1990 "\\share\\claws-mail\\",
1991 "ca-certificates.crt",
1997 /* Return the filepath of the claws-mail.desktop file */
1998 const gchar *get_desktop_file(void)
2000 #ifdef DESKTOPFILEPATH
2001 return DESKTOPFILEPATH;
2007 /* Return the default directory for Plugins. */
2008 const gchar *get_plugin_dir(void)
2011 static gchar *plugin_dir = NULL;
2014 plugin_dir = g_strconcat(w32_get_module_dir(),
2015 "\\lib\\claws-mail\\plugins\\",
2019 if (is_dir_exist(PLUGINDIR))
2022 static gchar *plugin_dir = NULL;
2024 plugin_dir = g_strconcat(get_rc_dir(),
2025 G_DIR_SEPARATOR_S, "plugins",
2026 G_DIR_SEPARATOR_S, NULL);
2034 /* Return the default directory for Themes. */
2035 const gchar *get_themes_dir(void)
2037 static gchar *themes_dir = NULL;
2040 themes_dir = g_strconcat(w32_get_module_dir(),
2041 "\\share\\claws-mail\\themes",
2047 const gchar *get_tmp_dir(void)
2049 static gchar *tmp_dir = NULL;
2052 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2058 gchar *get_tmp_file(void)
2061 static guint32 id = 0;
2063 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2064 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2069 const gchar *get_domain_name(void)
2072 static gchar *domain_name = NULL;
2078 if (gethostname(hostname, sizeof(hostname)) != 0) {
2079 perror("gethostname");
2080 domain_name = "unknown";
2082 hostname[sizeof(hostname) - 1] = '\0';
2083 if ((hp = my_gethostbyname(hostname)) == NULL) {
2084 perror("gethostbyname");
2085 domain_name = g_strdup(hostname);
2087 domain_name = g_strdup(hp->h_name);
2090 debug_print("domain name = %s\n", domain_name);
2099 off_t get_file_size(const gchar *file)
2103 if (g_stat(file, &s) < 0) {
2104 FILE_OP_ERROR(file, "stat");
2111 time_t get_file_mtime(const gchar *file)
2115 if (g_stat(file, &s) < 0) {
2116 FILE_OP_ERROR(file, "stat");
2123 off_t get_file_size_as_crlf(const gchar *file)
2127 gchar buf[BUFFSIZE];
2129 if ((fp = g_fopen(file, "rb")) == NULL) {
2130 FILE_OP_ERROR(file, "g_fopen");
2134 while (fgets(buf, sizeof(buf), fp) != NULL) {
2136 size += strlen(buf) + 2;
2140 FILE_OP_ERROR(file, "fgets");
2149 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2156 if (g_stat(file, &s) < 0) {
2157 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2161 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2168 /* Test on whether FILE is a relative file name. This is
2169 * straightforward for Unix but more complex for Windows. */
2170 gboolean is_relative_filename(const gchar *file)
2175 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2176 return FALSE; /* Prefixed with a hostname - this can't
2177 * be a relative name. */
2179 if ( ((*file >= 'a' && *file <= 'z')
2180 || (*file >= 'A' && *file <= 'Z'))
2182 file += 2; /* Skip drive letter. */
2184 return !(*file == '\\' || *file == '/');
2186 return !(*file == G_DIR_SEPARATOR);
2191 gboolean is_dir_exist(const gchar *dir)
2196 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2199 gboolean is_file_entry_exist(const gchar *file)
2204 return g_file_test(file, G_FILE_TEST_EXISTS);
2207 gboolean dirent_is_regular_file(struct dirent *d)
2209 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2210 if (d->d_type == DT_REG)
2212 else if (d->d_type != DT_UNKNOWN)
2216 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2219 gint change_dir(const gchar *dir)
2221 gchar *prevdir = NULL;
2224 prevdir = g_get_current_dir();
2226 if (g_chdir(dir) < 0) {
2227 FILE_OP_ERROR(dir, "chdir");
2228 if (debug_mode) g_free(prevdir);
2230 } else if (debug_mode) {
2233 cwd = g_get_current_dir();
2234 if (strcmp(prevdir, cwd) != 0)
2235 g_print("current dir: %s\n", cwd);
2243 gint make_dir(const gchar *dir)
2245 if (g_mkdir(dir, S_IRWXU) < 0) {
2246 FILE_OP_ERROR(dir, "mkdir");
2249 if (g_chmod(dir, S_IRWXU) < 0)
2250 FILE_OP_ERROR(dir, "chmod");
2255 gint make_dir_hier(const gchar *dir)
2260 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2261 parent_dir = g_strndup(dir, p - dir);
2262 if (*parent_dir != '\0') {
2263 if (!is_dir_exist(parent_dir)) {
2264 if (make_dir(parent_dir) < 0) {
2273 if (!is_dir_exist(dir)) {
2274 if (make_dir(dir) < 0)
2281 gint remove_all_files(const gchar *dir)
2284 const gchar *dir_name;
2287 prev_dir = g_get_current_dir();
2289 if (g_chdir(dir) < 0) {
2290 FILE_OP_ERROR(dir, "chdir");
2295 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2296 g_warning("failed to open directory: %s\n", dir);
2301 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2302 if (claws_unlink(dir_name) < 0)
2303 FILE_OP_ERROR(dir_name, "unlink");
2308 if (g_chdir(prev_dir) < 0) {
2309 FILE_OP_ERROR(prev_dir, "chdir");
2319 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2322 const gchar *dir_name;
2326 if (first == last) {
2327 /* Skip all the dir reading part. */
2328 gchar *filename = g_strdup_printf("%s%s%u", dir, G_DIR_SEPARATOR_S, first);
2329 if (claws_unlink(filename) < 0) {
2330 FILE_OP_ERROR(filename, "unlink");
2338 prev_dir = g_get_current_dir();
2340 if (g_chdir(dir) < 0) {
2341 FILE_OP_ERROR(dir, "chdir");
2346 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2347 g_warning("failed to open directory: %s\n", dir);
2352 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2353 file_no = to_number(dir_name);
2354 if (file_no > 0 && first <= file_no && file_no <= last) {
2355 if (is_dir_exist(dir_name))
2357 if (claws_unlink(dir_name) < 0)
2358 FILE_OP_ERROR(dir_name, "unlink");
2364 if (g_chdir(prev_dir) < 0) {
2365 FILE_OP_ERROR(prev_dir, "chdir");
2375 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2378 const gchar *dir_name;
2381 GHashTable *file_no_tbl;
2383 if (numberlist == NULL)
2386 prev_dir = g_get_current_dir();
2388 if (g_chdir(dir) < 0) {
2389 FILE_OP_ERROR(dir, "chdir");
2394 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2395 FILE_OP_ERROR(dir, "opendir");
2400 file_no_tbl = g_hash_table_new(g_direct_hash, g_direct_equal);
2401 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2402 file_no = to_number(dir_name);
2403 if (is_dir_exist(dir_name))
2406 g_hash_table_insert(file_no_tbl, GINT_TO_POINTER(file_no), GINT_TO_POINTER(1));
2410 if (g_hash_table_lookup(file_no_tbl, numberlist->data) == NULL) {
2411 debug_print("removing unwanted file %d from %s\n",
2412 GPOINTER_TO_INT(numberlist->data), dir);
2413 if (claws_unlink(dir_name) < 0)
2414 FILE_OP_ERROR(dir_name, "unlink");
2416 } while ((numberlist = g_slist_next(numberlist)));
2419 g_hash_table_destroy(file_no_tbl);
2421 if (g_chdir(prev_dir) < 0) {
2422 FILE_OP_ERROR(prev_dir, "chdir");
2432 gint remove_all_numbered_files(const gchar *dir)
2434 return remove_numbered_files(dir, 0, UINT_MAX);
2437 gint remove_dir_recursive(const gchar *dir)
2441 const gchar *dir_name;
2444 if (g_stat(dir, &s) < 0) {
2445 FILE_OP_ERROR(dir, "stat");
2446 if (ENOENT == errno) return 0;
2450 if (!S_ISDIR(s.st_mode)) {
2451 if (claws_unlink(dir) < 0) {
2452 FILE_OP_ERROR(dir, "unlink");
2459 prev_dir = g_get_current_dir();
2460 /* g_print("prev_dir = %s\n", prev_dir); */
2462 if (!path_cmp(prev_dir, dir)) {
2464 if (g_chdir("..") < 0) {
2465 FILE_OP_ERROR(dir, "chdir");
2468 prev_dir = g_get_current_dir();
2471 if (g_chdir(dir) < 0) {
2472 FILE_OP_ERROR(dir, "chdir");
2477 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2478 g_warning("failed to open directory: %s\n", dir);
2484 /* remove all files in the directory */
2485 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2486 /* g_print("removing %s\n", dir_name); */
2488 if (is_dir_exist(dir_name)) {
2489 if (remove_dir_recursive(dir_name) < 0) {
2490 g_warning("can't remove directory\n");
2494 if (claws_unlink(dir_name) < 0)
2495 FILE_OP_ERROR(dir_name, "unlink");
2501 if (g_chdir(prev_dir) < 0) {
2502 FILE_OP_ERROR(prev_dir, "chdir");
2509 if (g_rmdir(dir) < 0) {
2510 FILE_OP_ERROR(dir, "rmdir");
2517 gint rename_force(const gchar *oldpath, const gchar *newpath)
2520 if (!is_file_entry_exist(oldpath)) {
2524 if (is_file_exist(newpath)) {
2525 if (claws_unlink(newpath) < 0)
2526 FILE_OP_ERROR(newpath, "unlink");
2529 return g_rename(oldpath, newpath);
2533 * Append src file body to the tail of dest file.
2534 * Now keep_backup has no effects.
2536 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2538 FILE *src_fp, *dest_fp;
2542 gboolean err = FALSE;
2544 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2545 FILE_OP_ERROR(src, "g_fopen");
2549 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2550 FILE_OP_ERROR(dest, "g_fopen");
2555 if (change_file_mode_rw(dest_fp, dest) < 0) {
2556 FILE_OP_ERROR(dest, "chmod");
2557 g_warning("can't change file mode\n");
2560 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2561 if (n_read < sizeof(buf) && ferror(src_fp))
2563 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2564 g_warning("writing to %s failed.\n", dest);
2572 if (ferror(src_fp)) {
2573 FILE_OP_ERROR(src, "fread");
2577 if (fclose(dest_fp) == EOF) {
2578 FILE_OP_ERROR(dest, "fclose");
2590 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2592 FILE *src_fp, *dest_fp;
2595 gchar *dest_bak = NULL;
2596 gboolean err = FALSE;
2598 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2599 FILE_OP_ERROR(src, "g_fopen");
2602 if (is_file_exist(dest)) {
2603 dest_bak = g_strconcat(dest, ".bak", NULL);
2604 if (rename_force(dest, dest_bak) < 0) {
2605 FILE_OP_ERROR(dest, "rename");
2612 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2613 FILE_OP_ERROR(dest, "g_fopen");
2616 if (rename_force(dest_bak, dest) < 0)
2617 FILE_OP_ERROR(dest_bak, "rename");
2623 if (change_file_mode_rw(dest_fp, dest) < 0) {
2624 FILE_OP_ERROR(dest, "chmod");
2625 g_warning("can't change file mode\n");
2628 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2629 if (n_read < sizeof(buf) && ferror(src_fp))
2631 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2632 g_warning("writing to %s failed.\n", dest);
2637 if (rename_force(dest_bak, dest) < 0)
2638 FILE_OP_ERROR(dest_bak, "rename");
2645 if (ferror(src_fp)) {
2646 FILE_OP_ERROR(src, "fread");
2650 if (fclose(dest_fp) == EOF) {
2651 FILE_OP_ERROR(dest, "fclose");
2658 if (rename_force(dest_bak, dest) < 0)
2659 FILE_OP_ERROR(dest_bak, "rename");
2665 if (keep_backup == FALSE && dest_bak)
2666 claws_unlink(dest_bak);
2673 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2675 if (overwrite == FALSE && is_file_exist(dest)) {
2676 g_warning("move_file(): file %s already exists.", dest);
2680 if (rename_force(src, dest) == 0) return 0;
2682 if (EXDEV != errno) {
2683 FILE_OP_ERROR(src, "rename");
2687 if (copy_file(src, dest, FALSE) < 0) return -1;
2694 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2697 gint bytes_left, to_read;
2700 if (fseek(fp, offset, SEEK_SET) < 0) {
2705 bytes_left = length;
2706 to_read = MIN(bytes_left, sizeof(buf));
2708 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2709 if (n_read < to_read && ferror(fp))
2711 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2714 bytes_left -= n_read;
2715 if (bytes_left == 0)
2717 to_read = MIN(bytes_left, sizeof(buf));
2728 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2731 gboolean err = FALSE;
2733 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2734 FILE_OP_ERROR(dest, "g_fopen");
2738 if (change_file_mode_rw(dest_fp, dest) < 0) {
2739 FILE_OP_ERROR(dest, "chmod");
2740 g_warning("can't change file mode\n");
2743 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2746 if (!err && fclose(dest_fp) == EOF) {
2747 FILE_OP_ERROR(dest, "fclose");
2752 g_warning("writing to %s failed.\n", dest);
2760 /* convert line endings into CRLF. If the last line doesn't end with
2761 * linebreak, add it.
2763 gchar *canonicalize_str(const gchar *str)
2769 for (p = str; *p != '\0'; ++p) {
2776 if (p == str || *(p - 1) != '\n')
2779 out = outp = g_malloc(new_len + 1);
2780 for (p = str; *p != '\0'; ++p) {
2787 if (p == str || *(p - 1) != '\n') {
2796 gint canonicalize_file(const gchar *src, const gchar *dest)
2798 FILE *src_fp, *dest_fp;
2799 gchar buf[BUFFSIZE];
2801 gboolean err = FALSE;
2802 gboolean last_linebreak = FALSE;
2804 if (src == NULL || dest == NULL)
2807 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2808 FILE_OP_ERROR(src, "g_fopen");
2812 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2813 FILE_OP_ERROR(dest, "g_fopen");
2818 if (change_file_mode_rw(dest_fp, dest) < 0) {
2819 FILE_OP_ERROR(dest, "chmod");
2820 g_warning("can't change file mode\n");
2823 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2827 if (len == 0) break;
2828 last_linebreak = FALSE;
2830 if (buf[len - 1] != '\n') {
2831 last_linebreak = TRUE;
2832 r = fputs(buf, dest_fp);
2833 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2834 r = fputs(buf, dest_fp);
2837 r = fwrite(buf, 1, len - 1, dest_fp);
2842 r = fputs("\r\n", dest_fp);
2846 g_warning("writing to %s failed.\n", dest);
2854 if (last_linebreak == TRUE) {
2855 if (fputs("\r\n", dest_fp) == EOF)
2859 if (ferror(src_fp)) {
2860 FILE_OP_ERROR(src, "fgets");
2864 if (fclose(dest_fp) == EOF) {
2865 FILE_OP_ERROR(dest, "fclose");
2877 gint canonicalize_file_replace(const gchar *file)
2881 tmp_file = get_tmp_file();
2883 if (canonicalize_file(file, tmp_file) < 0) {
2888 if (move_file(tmp_file, file, TRUE) < 0) {
2889 g_warning("can't replace %s .\n", file);
2890 claws_unlink(tmp_file);
2899 gchar *normalize_newlines(const gchar *str)
2904 out = outp = g_malloc(strlen(str) + 1);
2905 for (p = str; *p != '\0'; ++p) {
2907 if (*(p + 1) != '\n')
2918 gchar *get_outgoing_rfc2822_str(FILE *fp)
2920 gchar buf[BUFFSIZE];
2924 str = g_string_new(NULL);
2926 /* output header part */
2927 while (fgets(buf, sizeof(buf), fp) != NULL) {
2929 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2936 else if (next != ' ' && next != '\t') {
2940 if (fgets(buf, sizeof(buf), fp) == NULL)
2944 g_string_append(str, buf);
2945 g_string_append(str, "\r\n");
2951 /* output body part */
2952 while (fgets(buf, sizeof(buf), fp) != NULL) {
2955 g_string_append_c(str, '.');
2956 g_string_append(str, buf);
2957 g_string_append(str, "\r\n");
2961 g_string_free(str, FALSE);
2967 * Create a new boundary in a way that it is very unlikely that this
2968 * will occur in the following text. It would be easy to ensure
2969 * uniqueness if everything is either quoted-printable or base64
2970 * encoded (note that conversion is allowed), but because MIME bodies
2971 * may be nested, it may happen that the same boundary has already
2974 * boundary := 0*69<bchars> bcharsnospace
2975 * bchars := bcharsnospace / " "
2976 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2977 * "+" / "_" / "," / "-" / "." /
2978 * "/" / ":" / "=" / "?"
2980 * some special characters removed because of buggy MTAs
2983 gchar *generate_mime_boundary(const gchar *prefix)
2985 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2986 "abcdefghijklmnopqrstuvwxyz"
2991 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2992 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2995 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2999 gint change_file_mode_rw(FILE *fp, const gchar *file)
3002 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3004 return g_chmod(file, S_IRUSR|S_IWUSR);
3008 FILE *my_tmpfile(void)
3010 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3011 const gchar suffix[] = ".XXXXXX";
3012 const gchar *tmpdir;
3014 const gchar *progname;
3023 tmpdir = get_tmp_dir();
3024 tmplen = strlen(tmpdir);
3025 progname = g_get_prgname();
3026 if (progname == NULL)
3027 progname = "claws-mail";
3028 proglen = strlen(progname);
3029 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3032 memcpy(fname, tmpdir, tmplen);
3033 fname[tmplen] = G_DIR_SEPARATOR;
3034 memcpy(fname + tmplen + 1, progname, proglen);
3035 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3037 fd = mkstemp(fname);
3042 claws_unlink(fname);
3044 /* verify that we can write in the file after unlinking */
3045 if (write(fd, buf, 1) < 0) {
3052 fp = fdopen(fd, "w+b");
3060 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3065 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3069 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3070 dir, G_DIR_SEPARATOR);
3071 fd = mkstemp_name(template, filename);
3074 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3075 fd = mkstemp(*filename);
3077 return fdopen(fd, "w+");
3080 FILE *str_open_as_stream(const gchar *str)
3085 cm_return_val_if_fail(str != NULL, NULL);
3089 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3094 if (len == 0) return fp;
3096 if (fwrite(str, 1, len, fp) != len) {
3097 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3106 gint str_write_to_file(const gchar *str, const gchar *file)
3111 cm_return_val_if_fail(str != NULL, -1);
3112 cm_return_val_if_fail(file != NULL, -1);
3114 if ((fp = g_fopen(file, "wb")) == NULL) {
3115 FILE_OP_ERROR(file, "g_fopen");
3125 if (fwrite(str, 1, len, fp) != len) {
3126 FILE_OP_ERROR(file, "fwrite");
3132 if (fclose(fp) == EOF) {
3133 FILE_OP_ERROR(file, "fclose");
3141 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3148 cm_return_val_if_fail(fp != NULL, NULL);
3150 array = g_byte_array_new();
3152 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3153 if (n_read < sizeof(buf) && ferror(fp))
3155 g_byte_array_append(array, buf, n_read);
3159 FILE_OP_ERROR("file stream", "fread");
3160 g_byte_array_free(array, TRUE);
3165 g_byte_array_append(array, buf, 1);
3166 str = (gchar *)array->data;
3167 g_byte_array_free(array, FALSE);
3169 if (recode && !g_utf8_validate(str, -1, NULL)) {
3170 const gchar *src_codeset, *dest_codeset;
3172 src_codeset = conv_get_locale_charset_str();
3173 dest_codeset = CS_UTF_8;
3174 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3182 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3189 struct timeval timeout = {1, 0};
3194 cm_return_val_if_fail(file != NULL, NULL);
3196 if (g_stat(file, &s) != 0) {
3197 FILE_OP_ERROR(file, "stat");
3200 if (S_ISDIR(s.st_mode)) {
3201 g_warning("%s: is a directory\n", file);
3206 fp = g_fopen (file, "rb");
3208 FILE_OP_ERROR(file, "open");
3212 /* test whether the file is readable without blocking */
3213 fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3215 FILE_OP_ERROR(file, "open");
3222 /* allow for one second */
3223 err = select(fd+1, &fds, NULL, NULL, &timeout);
3224 if (err <= 0 || !FD_ISSET(fd, &fds)) {
3226 FILE_OP_ERROR(file, "select");
3228 g_warning("%s: doesn't seem readable\n", file);
3234 /* Now clear O_NONBLOCK */
3235 if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3236 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3240 if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3241 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3246 /* get the FILE pointer */
3247 fp = fdopen(fd, "rb");
3250 FILE_OP_ERROR(file, "fdopen");
3251 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3256 str = file_read_stream_to_str_full(fp, recode);
3263 gchar *file_read_to_str(const gchar *file)
3265 return file_read_to_str_full(file, TRUE);
3267 gchar *file_read_stream_to_str(FILE *fp)
3269 return file_read_stream_to_str_full(fp, TRUE);
3272 gchar *file_read_to_str_no_recode(const gchar *file)
3274 return file_read_to_str_full(file, FALSE);
3276 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3278 return file_read_stream_to_str_full(fp, FALSE);
3281 char *fgets_crlf(char *buf, int size, FILE *stream)
3283 gboolean is_cr = FALSE;
3284 gboolean last_was_cr = FALSE;
3289 while (--size > 0 && (c = getc(stream)) != EOF)
3292 is_cr = (c == '\r');
3302 last_was_cr = is_cr;
3304 if (c == EOF && cs == buf)
3312 static gint execute_async(gchar *const argv[])
3314 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3316 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3317 NULL, NULL, NULL, FALSE) == FALSE) {
3318 g_warning("Couldn't execute command: %s\n", argv[0]);
3325 static gint execute_sync(gchar *const argv[])
3329 cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3332 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3333 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3334 g_warning("Couldn't execute command: %s\n", argv[0]);
3338 if (WIFEXITED(status))
3339 return WEXITSTATUS(status);
3343 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
3344 G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3345 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3346 g_warning("Couldn't execute command: %s\n", argv[0]);
3354 gint execute_command_line(const gchar *cmdline, gboolean async)
3359 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3361 argv = strsplit_with_quote(cmdline, " ", 0);
3364 ret = execute_async(argv);
3366 ret = execute_sync(argv);
3373 gchar *get_command_output(const gchar *cmdline)
3375 gchar *child_stdout;
3378 cm_return_val_if_fail(cmdline != NULL, NULL);
3380 debug_print("get_command_output(): executing: %s\n", cmdline);
3382 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3384 g_warning("Couldn't execute command: %s\n", cmdline);
3388 return child_stdout;
3391 static gint is_unchanged_uri_char(char c)
3402 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3408 for(i = 0; i < strlen(uri) ; i++) {
3409 if (is_unchanged_uri_char(uri[i])) {
3410 if (k + 2 >= bufsize)
3412 encoded_uri[k++] = uri[i];
3415 char * hexa = "0123456789ABCDEF";
3417 if (k + 4 >= bufsize)
3419 encoded_uri[k++] = '%';
3420 encoded_uri[k++] = hexa[uri[i] / 16];
3421 encoded_uri[k++] = hexa[uri[i] % 16];
3427 gint open_uri(const gchar *uri, const gchar *cmdline)
3431 gchar buf[BUFFSIZE];
3433 gchar encoded_uri[BUFFSIZE];
3434 cm_return_val_if_fail(uri != NULL, -1);
3436 /* an option to choose whether to use encode_uri or not ? */
3437 encode_uri(encoded_uri, BUFFSIZE, uri);
3440 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3441 !strchr(p + 2, '%'))
3442 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3445 g_warning("Open URI command-line is invalid "
3446 "(there must be only one '%%s'): %s",
3448 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3451 execute_command_line(buf, TRUE);
3453 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3456 extern osso_context_t *get_osso_context(void);
3457 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3458 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3459 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3464 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3466 gchar buf[BUFFSIZE];
3469 cm_return_val_if_fail(filepath != NULL, -1);
3472 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3473 !strchr(p + 2, '%'))
3474 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3477 g_warning("Open Text Editor command-line is invalid "
3478 "(there must be only one '%%s'): %s",
3480 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3483 execute_command_line(buf, TRUE);
3488 time_t remote_tzoffset_sec(const gchar *zone)
3490 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3496 time_t remoteoffset;
3498 strncpy(zone3, zone, 3);
3502 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3503 (c == '+' || c == '-')) {
3504 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3506 remoteoffset = -remoteoffset;
3507 } else if (!strncmp(zone, "UT" , 2) ||
3508 !strncmp(zone, "GMT", 2)) {
3510 } else if (strlen(zone3) == 3) {
3511 for (p = ustzstr; *p != '\0'; p += 3) {
3512 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3513 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3514 remoteoffset = iustz * 3600;
3520 } else if (strlen(zone3) == 1) {
3522 case 'Z': remoteoffset = 0; break;
3523 case 'A': remoteoffset = -1; break;
3524 case 'B': remoteoffset = -2; break;
3525 case 'C': remoteoffset = -3; break;
3526 case 'D': remoteoffset = -4; break;
3527 case 'E': remoteoffset = -5; break;
3528 case 'F': remoteoffset = -6; break;
3529 case 'G': remoteoffset = -7; break;
3530 case 'H': remoteoffset = -8; break;
3531 case 'I': remoteoffset = -9; break;
3532 case 'K': remoteoffset = -10; break; /* J is not used */
3533 case 'L': remoteoffset = -11; break;
3534 case 'M': remoteoffset = -12; break;
3535 case 'N': remoteoffset = 1; break;
3536 case 'O': remoteoffset = 2; break;
3537 case 'P': remoteoffset = 3; break;
3538 case 'Q': remoteoffset = 4; break;
3539 case 'R': remoteoffset = 5; break;
3540 case 'S': remoteoffset = 6; break;
3541 case 'T': remoteoffset = 7; break;
3542 case 'U': remoteoffset = 8; break;
3543 case 'V': remoteoffset = 9; break;
3544 case 'W': remoteoffset = 10; break;
3545 case 'X': remoteoffset = 11; break;
3546 case 'Y': remoteoffset = 12; break;
3547 default: remoteoffset = 0; break;
3549 remoteoffset = remoteoffset * 3600;
3553 return remoteoffset;
3556 time_t tzoffset_sec(time_t *now)
3560 struct tm buf1, buf2;
3562 if (now && *now < 0)
3565 gmt = *gmtime_r(now, &buf1);
3566 lt = localtime_r(now, &buf2);
3568 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3570 if (lt->tm_year < gmt.tm_year)
3572 else if (lt->tm_year > gmt.tm_year)
3574 else if (lt->tm_yday < gmt.tm_yday)
3576 else if (lt->tm_yday > gmt.tm_yday)
3579 if (off >= 24 * 60) /* should be impossible */
3580 off = 23 * 60 + 59; /* if not, insert silly value */
3581 if (off <= -24 * 60)
3582 off = -(23 * 60 + 59);
3587 /* calculate timezone offset */
3588 gchar *tzoffset(time_t *now)
3590 static gchar offset_string[6];
3594 struct tm buf1, buf2;
3596 if (now && *now < 0)
3599 gmt = *gmtime_r(now, &buf1);
3600 lt = localtime_r(now, &buf2);
3602 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3604 if (lt->tm_year < gmt.tm_year)
3606 else if (lt->tm_year > gmt.tm_year)
3608 else if (lt->tm_yday < gmt.tm_yday)
3610 else if (lt->tm_yday > gmt.tm_yday)
3618 if (off >= 24 * 60) /* should be impossible */
3619 off = 23 * 60 + 59; /* if not, insert silly value */
3621 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3623 return offset_string;
3626 void get_rfc822_date(gchar *buf, gint len)
3630 gchar day[4], mon[4];
3631 gint dd, hh, mm, ss, yyyy;
3633 gchar buf2[BUFFSIZE];
3636 lt = localtime_r(&t, &buf1);
3638 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3639 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3641 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3642 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3645 void debug_set_mode(gboolean mode)
3650 gboolean debug_get_mode(void)
3655 void debug_print_real(const gchar *format, ...)
3658 gchar buf[BUFFSIZE];
3660 if (!debug_mode) return;
3662 va_start(args, format);
3663 g_vsnprintf(buf, sizeof(buf), format, args);
3670 const char * debug_srcname(const char *file)
3672 const char *s = strrchr (file, '/');
3677 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3679 if (subject == NULL)
3682 subject += subject_get_prefix_length(subject);
3684 return g_hash_table_lookup(subject_table, subject);
3687 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3690 if (subject == NULL || *subject == 0)
3692 subject += subject_get_prefix_length(subject);
3693 g_hash_table_insert(subject_table, subject, data);
3696 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3698 if (subject == NULL)
3701 subject += subject_get_prefix_length(subject);
3702 g_hash_table_remove(subject_table, subject);
3706 static regex_t u_regex;
3707 static gboolean u_init_;
3710 void utils_free_regex(void)
3721 *\brief Check if a string is prefixed with known (combinations)
3722 * of prefixes. The function assumes that each prefix
3723 * is terminated by zero or exactly _one_ space.
3725 *\param str String to check for a prefixes
3727 *\return int Number of chars in the prefix that should be skipped
3728 * for a "clean" subject line. If no prefix was found, 0
3731 int subject_get_prefix_length(const gchar *subject)
3734 /*!< Array with allowable reply prefixes regexps. */
3735 static const gchar * const prefixes[] = {
3736 "Re\\:", /* "Re:" */
3737 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3738 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3739 "Aw\\:", /* "Aw:" (German) */
3740 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3741 "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
3742 "Fw\\:", /* "Fw:" Forward */
3743 "Fwd\\:", /* "Fwd:" Forward */
3744 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3745 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3746 "Rif\\:", /* "Rif:" (Italian Outlook) */
3747 "Sv\\:", /* "Sv" (Norwegian) */
3748 "Vs\\:", /* "Vs" (Norwegian) */
3749 "Ad\\:", /* "Ad" (Norwegian) */
3750 "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
3751 "R\303\251f\\. \\:", /* "R�f. :" (French Lotus Notes) */
3752 "Re \\:", /* "Re :" (French Yahoo Mail) */
3755 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3759 if (!subject) return 0;
3760 if (!*subject) return 0;
3763 GString *s = g_string_new("");
3765 for (n = 0; n < PREFIXES; n++)
3766 /* Terminate each prefix regexpression by a
3767 * "\ ?" (zero or ONE space), and OR them */
3768 g_string_append_printf(s, "(%s\\ ?)%s",
3773 g_string_prepend(s, "(");
3774 g_string_append(s, ")+"); /* match at least once */
3775 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3778 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3779 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3780 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3781 debug_print("Error compiling regexp %s\n", s->str);
3782 g_string_free(s, TRUE);
3786 g_string_free(s, TRUE);
3790 if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3795 /*!< Array with allowable reply prefixes regexps. */
3796 static const gchar * const prefixes[] = {
3798 "antw:", /* "Antw:" (Dutch / German Outlook) */
3799 "aw:", /* "Aw:" (German) */
3800 "antwort:", /* "Antwort:" (German Lotus Notes) */
3801 "res:", /* "Res:" (Spanish/Brazilian Outlook) */
3802 "fw:", /* "Fw:" Forward */
3803 "fwd:", /* "Fwd:" Forward */
3804 "enc:", /* "Enc:" Forward (Brazilian Outlook) */
3805 "odp:", /* "Odp:" Re (Polish Outlook) */
3806 "rif:", /* "Rif:" (Italian Outlook) */
3807 "sv:", /* "Sv" (Norwegian) */
3808 "vs:", /* "Vs" (Norwegian) */
3809 "ad:", /* "Ad" (Norwegian) */
3810 "R\303\251f. :", /* "R�f. :" (French Lotus Notes) */
3811 "Re :", /* "Re :" (French Yahoo Mail) */
3814 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3817 if (!subject) return 0;
3818 if (!*subject) return 0;
3820 for (n = 0; n < PREFIXES; n++) {
3821 int len = strlen(prefixes[n]);
3822 if (!strncasecmp(subject, prefixes[n], len)) {
3823 if (subject[len] == ' ')
3832 static guint g_stricase_hash(gconstpointer gptr)
3834 guint hash_result = 0;
3837 for (str = gptr; str && *str; str++) {
3838 hash_result += toupper(*str);
3844 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3846 const char *str1 = gptr1;
3847 const char *str2 = gptr2;
3849 return !strcasecmp(str1, str2);
3852 gint g_int_compare(gconstpointer a, gconstpointer b)
3854 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3857 gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
3865 lt = localtime_r(&t, &buft);
3867 if (user_addr != NULL)
3868 addr = g_strdup_printf(".%s", user_addr);
3869 else if (strlen(buf) != 0)
3870 addr = g_strdup_printf("@%s", buf);
3872 addr = g_strdup_printf("@%s", get_domain_name());
3874 /* Replace all @ but the last one in addr, with underscores.
3875 * RFC 2822 States that msg-id syntax only allows one @.
3877 while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
3878 *(strchr(addr, '@')) = '_';
3880 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3881 lt->tm_year + 1900, lt->tm_mon + 1,
3882 lt->tm_mday, lt->tm_hour,
3883 lt->tm_min, lt->tm_sec,
3884 (guint) rand(), addr);
3891 quote_cmd_argument()
3893 return a quoted string safely usable in argument of a command.
3895 code is extracted and adapted from etPan! project -- DINH V. Ho�.
3898 gint quote_cmd_argument(gchar * result, guint size,
3908 for(p = path ; * p != '\0' ; p ++) {
3910 if (isalnum((guchar)*p) || (* p == '/')) {
3911 if (remaining > 0) {
3917 result[size - 1] = '\0';
3922 if (remaining >= 2) {
3930 result[size - 1] = '\0';
3935 if (remaining > 0) {
3939 result[size - 1] = '\0';
3953 static void g_node_map_recursive(GNode *node, gpointer data)
3955 GNodeMapData *mapdata = (GNodeMapData *) data;
3957 GNodeMapData newmapdata;
3960 newdata = mapdata->func(node->data, mapdata->data);
3961 if (newdata != NULL) {
3962 newnode = g_node_new(newdata);
3963 g_node_append(mapdata->parent, newnode);
3965 newmapdata.parent = newnode;
3966 newmapdata.func = mapdata->func;
3967 newmapdata.data = mapdata->data;
3969 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3973 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3976 GNodeMapData mapdata;
3978 cm_return_val_if_fail(node != NULL, NULL);
3979 cm_return_val_if_fail(func != NULL, NULL);
3981 root = g_node_new(func(node->data, data));
3983 mapdata.parent = root;
3984 mapdata.func = func;
3985 mapdata.data = data;
3987 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3992 #define HEX_TO_INT(val, hex) \
3996 if ('0' <= c && c <= '9') { \
3998 } else if ('a' <= c && c <= 'f') { \
3999 val = c - 'a' + 10; \
4000 } else if ('A' <= c && c <= 'F') { \
4001 val = c - 'A' + 10; \
4007 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4014 if (hi == -1 || lo == -1)
4017 *out = (hi << 4) + lo;
4021 #define INT_TO_HEX(hex, val) \
4024 hex = '0' + (val); \
4026 hex = 'A' + (val) - 10; \
4029 void get_hex_str(gchar *out, guchar ch)
4033 INT_TO_HEX(hex, ch >> 4);
4035 INT_TO_HEX(hex, ch & 0x0f);
4041 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4043 #define G_PRINT_REF g_print
4047 *\brief Register ref counted pointer. It is based on GBoxed, so should
4048 * work with anything that uses the GType system. The semantics
4049 * are similar to a C++ auto pointer, with the exception that
4050 * C doesn't have automatic closure (calling destructors) when
4051 * exiting a block scope.
4052 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4053 * function directly.
4055 *\return GType A GType type.
4057 GType g_auto_pointer_register(void)
4059 static GType auto_pointer_type;
4060 if (!auto_pointer_type)
4062 g_boxed_type_register_static
4063 ("G_TYPE_AUTO_POINTER",
4064 (GBoxedCopyFunc) g_auto_pointer_copy,
4065 (GBoxedFreeFunc) g_auto_pointer_free);
4066 return auto_pointer_type;
4070 *\brief Structure with g_new() allocated pointer guarded by the
4073 typedef struct AutoPointerRef {
4074 void (*free) (gpointer);
4080 *\brief The auto pointer opaque structure that references the
4081 * pointer guard block.
4083 typedef struct AutoPointer {
4084 AutoPointerRef *ref;
4085 gpointer ptr; /*!< access to protected pointer */
4089 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4093 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4094 * ... when assigning, copying and freeing storage elements
4096 * gtk_list_store_new(N_S_COLUMNS,
4097 * G_TYPE_AUTO_POINTER,
4101 * Template *precious_data = g_new0(Template, 1);
4102 * g_pointer protect = g_auto_pointer_new(precious_data);
4104 * gtk_list_store_set(container, &iter,
4108 * ... the gtk_list_store has copied the pointer and
4109 * ... incremented its reference count, we should free
4110 * ... the auto pointer (in C++ a destructor would do
4111 * ... this for us when leaving block scope)
4113 * g_auto_pointer_free(protect);
4115 * ... gtk_list_store_set() now manages the data. When
4116 * ... *explicitly* requesting a pointer from the list
4117 * ... store, don't forget you get a copy that should be
4118 * ... freed with g_auto_pointer_free() eventually.
4122 *\param pointer Pointer to be guarded.
4124 *\return GAuto * Pointer that should be used in containers with
4127 GAuto *g_auto_pointer_new(gpointer p)
4129 AutoPointerRef *ref;
4135 ref = g_new0(AutoPointerRef, 1);
4136 ptr = g_new0(AutoPointer, 1);
4146 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4152 *\brief Allocate an autopointer using the passed \a free function to
4153 * free the guarded pointer
4155 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4162 aptr = g_auto_pointer_new(p);
4163 aptr->ref->free = free_;
4167 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4169 if (auto_ptr == NULL)
4171 return ((AutoPointer *) auto_ptr)->ptr;
4175 *\brief Copies an auto pointer by. It's mostly not necessary
4176 * to call this function directly, unless you copy/assign
4177 * the guarded pointer.
4179 *\param auto_ptr Auto pointer returned by previous call to
4180 * g_auto_pointer_new_XXX()
4182 *\return gpointer An auto pointer
4184 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4187 AutoPointerRef *ref;
4190 if (auto_ptr == NULL)
4195 newp = g_new0(AutoPointer, 1);
4198 newp->ptr = ref->pointer;
4202 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4208 *\brief Free an auto pointer
4210 void g_auto_pointer_free(GAuto *auto_ptr)
4213 AutoPointerRef *ref;
4215 if (auto_ptr == NULL)
4221 if (--(ref->cnt) == 0) {
4223 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4225 ref->free(ref->pointer);
4230 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4235 void replace_returns(gchar *str)
4240 while (strstr(str, "\n")) {
4241 *strstr(str, "\n") = ' ';
4243 while (strstr(str, "\r")) {
4244 *strstr(str, "\r") = ' ';
4248 /* get_uri_part() - retrieves a URI starting from scanpos.
4249 Returns TRUE if succesful */
4250 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4251 const gchar **bp, const gchar **ep, gboolean hdr)
4254 gint parenthese_cnt = 0;
4256 cm_return_val_if_fail(start != NULL, FALSE);
4257 cm_return_val_if_fail(scanpos != NULL, FALSE);
4258 cm_return_val_if_fail(bp != NULL, FALSE);
4259 cm_return_val_if_fail(ep != NULL, FALSE);
4263 /* find end point of URI */
4264 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4265 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4266 !IS_ASCII(*(const guchar *)ep_) ||
4267 strchr("[]{}<>\"", *ep_)) {
4269 } else if (strchr("(", *ep_)) {
4271 } else if (strchr(")", *ep_)) {
4272 if (parenthese_cnt > 0)
4279 /* no punctuation at end of string */
4281 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4282 * should pass some URI type to this function and decide on that whether
4283 * to perform punctuation stripping */
4285 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
4287 for (; ep_ - 1 > scanpos + 1 &&
4288 IS_REAL_PUNCT(*(ep_ - 1));
4292 #undef IS_REAL_PUNCT
4299 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4301 while (bp && *bp && g_ascii_isspace(*bp))
4303 return g_strndup(bp, ep - bp);
4306 /* valid mail address characters */
4307 #define IS_RFC822_CHAR(ch) \
4311 !g_ascii_isspace(ch) && \
4312 !strchr("(),;<>\"", (ch)))
4314 /* alphabet and number within 7bit ASCII */
4315 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4316 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4318 static GHashTable *create_domain_tab(void)
4320 static const gchar *toplvl_domains [] = {
4322 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4323 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4324 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4325 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4326 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4327 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4328 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4329 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4330 "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4331 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4332 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4333 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4334 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4335 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4336 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4337 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4338 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4339 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4340 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4341 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4342 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4343 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4344 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4345 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4346 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4347 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4350 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4352 cm_return_val_if_fail(htab, NULL);
4353 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4354 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4358 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4360 const gint MAX_LVL_DOM_NAME_LEN = 6;
4361 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4362 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4365 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4368 for (p = buf; p < m && first < last; *p++ = *first++)
4372 return g_hash_table_lookup(tab, buf) != NULL;
4375 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4376 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4377 const gchar **bp, const gchar **ep, gboolean hdr)
4379 /* more complex than the uri part because we need to scan back and forward starting from
4380 * the scan position. */
4381 gboolean result = FALSE;
4382 const gchar *bp_ = NULL;
4383 const gchar *ep_ = NULL;
4384 static GHashTable *dom_tab;
4385 const gchar *last_dot = NULL;
4386 const gchar *prelast_dot = NULL;
4387 const gchar *last_tld_char = NULL;
4389 /* the informative part of the email address (describing the name
4390 * of the email address owner) may contain quoted parts. the
4391 * closure stack stores the last encountered quotes. */
4392 gchar closure_stack[128];
4393 gchar *ptr = closure_stack;
4395 cm_return_val_if_fail(start != NULL, FALSE);
4396 cm_return_val_if_fail(scanpos != NULL, FALSE);
4397 cm_return_val_if_fail(bp != NULL, FALSE);
4398 cm_return_val_if_fail(ep != NULL, FALSE);
4401 const gchar *start_quote = NULL;
4402 const gchar *end_quote = NULL;
4404 /* go to the real start */
4405 if (start[0] == ',')
4407 if (start[0] == ';')
4409 while (start[0] == '\n' || start[0] == '\r')
4411 while (start[0] == ' ' || start[0] == '\t')
4416 /* check if there are quotes (to skip , in them) */
4417 if (*start == '"') {
4418 start_quote = start;
4420 end_quote = strstr(start, "\"");
4426 /* skip anything between quotes */
4427 if (start_quote && end_quote) {
4432 /* find end (either , or ; or end of line) */
4433 if (strstr(start, ",") && strstr(start, ";"))
4434 *ep = strstr(start,",") < strstr(start, ";")
4435 ? strstr(start, ",") : strstr(start, ";");
4436 else if (strstr(start, ","))
4437 *ep = strstr(start, ",");
4438 else if (strstr(start, ";"))
4439 *ep = strstr(start, ";");
4441 *ep = start+strlen(start);
4443 /* go back to real start */
4444 if (start_quote && end_quote) {
4445 start = start_quote;
4448 /* check there's still an @ in that, or search
4449 * further if possible */
4450 if (strstr(start, "@") && strstr(start, "@") < *ep)
4452 else if (*ep < start+strlen(start)) {
4455 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4463 dom_tab = create_domain_tab();
4464 cm_return_val_if_fail(dom_tab, FALSE);
4466 /* scan start of address */
4467 for (bp_ = scanpos - 1;
4468 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4471 /* TODO: should start with an alnum? */
4473 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4476 if (bp_ != scanpos) {
4477 /* scan end of address */
4478 for (ep_ = scanpos + 1;
4479 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4481 prelast_dot = last_dot;
4483 if (*(last_dot + 1) == '.') {
4484 if (prelast_dot == NULL)
4486 last_dot = prelast_dot;
4491 /* TODO: really should terminate with an alnum? */
4492 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4497 if (last_dot == NULL)
4499 if (last_dot >= ep_)
4500 last_dot = prelast_dot;
4501 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4505 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4506 if (*last_tld_char == '?')
4509 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4516 if (!result) return FALSE;
4518 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4519 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4520 && IS_RFC822_CHAR(*(ep_ + 3))) {
4521 /* this informative part with an @ in it is
4522 * followed by the email address */
4525 /* go to matching '>' (or next non-rfc822 char, like \n) */
4526 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4529 /* include the bracket */
4530 if (*ep_ == '>') ep_++;
4532 /* include the leading quote */
4540 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4541 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4544 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4545 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4548 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4549 #define IN_STACK() (ptr > closure_stack)
4550 /* has underrun check */
4551 #define POP_STACK() if(IN_STACK()) --ptr
4552 /* has overrun check */
4553 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4554 /* has underrun check */
4555 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4559 /* scan for the informative part. */
4560 for (bp_ -= 2; bp_ >= start; bp_--) {
4561 /* if closure on the stack keep scanning */
4562 if (PEEK_STACK() == *bp_) {
4566 if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
4571 /* if nothing in the closure stack, do the special conditions
4572 * the following if..else expression simply checks whether
4573 * a token is acceptable. if not acceptable, the clause
4574 * should terminate the loop with a 'break' */
4575 if (!PEEK_STACK()) {
4577 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4578 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4579 /* hyphens are allowed, but only in
4581 } else if (strchr(" \"'", *bp_)) {
4582 /* but anything not being a punctiation
4585 break; /* anything else is rejected */
4592 /* scan forward (should start with an alnum) */
4593 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4609 #undef IS_ASCII_ALNUM
4610 #undef IS_RFC822_CHAR
4612 gchar *make_email_string(const gchar *bp, const gchar *ep)
4614 /* returns a mailto: URI; mailto: is also used to detect the
4615 * uri type later on in the button_pressed signal handler */
4619 tmp = g_strndup(bp, ep - bp);
4620 result = g_strconcat("mailto:", tmp, NULL);
4626 gchar *make_http_string(const gchar *bp, const gchar *ep)
4628 /* returns an http: URI; */
4632 while (bp && *bp && g_ascii_isspace(*bp))
4634 tmp = g_strndup(bp, ep - bp);
4635 result = g_strconcat("http://", tmp, NULL);
4641 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4643 FILE *fp = g_fopen(path, "rb");
4644 gchar buf[BUFFSIZE];
4645 gchar *result = NULL;
4648 while (fgets(buf, sizeof (buf), fp) != NULL) {
4649 gchar **parts = g_strsplit(buf, ";", 3);
4650 gchar *trimmed = parts[0];
4651 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4653 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4654 trimmed[strlen(trimmed)-1] = '\0';
4656 if (!strcmp(trimmed, type)) {
4657 gboolean needsterminal = FALSE;
4658 if (parts[2] && strstr(parts[2], "needsterminal")) {
4659 needsterminal = TRUE;
4661 if (parts[2] && strstr(parts[2], "test=")) {
4662 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4663 gchar *testcmd = orig_testcmd;
4664 if (strstr(testcmd,";"))
4665 *(strstr(testcmd,";")) = '\0';
4666 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4668 while (testcmd[strlen(testcmd)-1] == '\n')
4669 testcmd[strlen(testcmd)-1] = '\0';
4670 while (testcmd[strlen(testcmd)-1] == '\r')
4671 testcmd[strlen(testcmd)-1] = '\0';
4672 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4673 testcmd[strlen(testcmd)-1] = '\0';
4675 if (strstr(testcmd, "%s")) {
4676 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4677 gint res = system(tmp);
4679 g_free(orig_testcmd);
4686 gint res = system(testcmd);
4687 g_free(orig_testcmd);
4697 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4699 while (trimmed[strlen(trimmed)-1] == '\n')
4700 trimmed[strlen(trimmed)-1] = '\0';
4701 while (trimmed[strlen(trimmed)-1] == '\r')
4702 trimmed[strlen(trimmed)-1] = '\0';
4703 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4704 trimmed[strlen(trimmed)-1] = '\0';
4705 result = g_strdup(trimmed);
4708 /* if there are no single quotes around %s, add them.
4709 * '.*%s.*' is ok, as in display 'png:%s'
4711 if (strstr(result, "%s")
4712 && !(strstr(result, "'") < strstr(result,"%s") &&
4713 strstr(strstr(result,"%s"), "'"))) {
4714 gchar *start = g_strdup(result);
4715 gchar *end = g_strdup(strstr(result, "%s")+2);
4717 *strstr(start, "%s") = '\0';
4718 tmp = g_strconcat(start,"'%s'",end, NULL);
4724 if (needsterminal) {
4725 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4736 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4738 gchar *result = NULL;
4742 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4743 result = mailcap_get_command_in_file(path, type, file_to_open);
4747 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4751 void mailcap_update_default(const gchar *type, const gchar *command)
4753 gchar *path = NULL, *outpath = NULL;
4754 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4755 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4756 FILE *fp = g_fopen(path, "rb");
4757 FILE *outfp = g_fopen(outpath, "wb");
4758 gchar buf[BUFFSIZE];
4759 gboolean err = FALSE;
4767 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4768 gchar **parts = g_strsplit(buf, ";", 3);
4769 gchar *trimmed = parts[0];
4770 while (trimmed[0] == ' ')
4772 while (trimmed[strlen(trimmed)-1] == ' ')
4773 trimmed[strlen(trimmed)-1] = '\0';
4775 if (!strcmp(trimmed, type)) {
4780 if(fputs(buf, outfp) == EOF) {
4787 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4793 if (fclose(outfp) == EOF)
4797 g_rename(outpath, path);
4803 gint copy_dir(const gchar *src, const gchar *dst)
4808 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4809 g_warning("failed to open directory: %s\n", src);
4813 if (make_dir(dst) < 0)
4816 while ((name = g_dir_read_name(dir)) != NULL) {
4817 gchar *old_file, *new_file;
4818 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4819 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4820 debug_print("copying: %s -> %s\n", old_file, new_file);
4821 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4822 gint r = copy_file(old_file, new_file, TRUE);
4829 /* Windows has no symlinks. Or well, Vista seems to
4830 have something like this but the semantics might be
4831 different. Thus we don't use it under Windows. */
4832 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4835 gchar *target = g_file_read_link(old_file, &error);
4837 r = symlink(target, new_file);
4844 #endif /*G_OS_WIN32*/
4845 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4846 gint r = copy_dir(old_file, new_file);
4857 /* crude test to see if a file is an email. */
4858 gboolean file_is_email (const gchar *filename)
4864 if (filename == NULL)
4866 if ((fp = g_fopen(filename, "rb")) == NULL)
4868 while (i < 60 && score < 3
4869 && fgets(buffer, sizeof (buffer), fp) > 0) {
4870 if (!strncmp(buffer, "From:", strlen("From:")))
4872 else if (!strncmp(buffer, "Date:", strlen("Date:")))
4874 else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
4876 else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4881 return (score >= 3);
4884 gboolean sc_g_list_bigger(GList *list, gint max)
4888 while (cur && i <= max+1) {
4895 gboolean sc_g_slist_bigger(GSList *list, gint max)
4899 while (cur && i <= max+1) {
4906 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4907 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4908 NULL, NULL, NULL, NULL, NULL, NULL};
4909 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4910 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4911 NULL, NULL, NULL, NULL, NULL, NULL};
4913 gint daynames_len[] = {0,0,0,0,0,0,0};
4914 gint monthnames_len[] = {0,0,0,0,0,0,
4916 gint s_daynames_len[] = {0,0,0,0,0,0,0};
4917 gint s_monthnames_len[] = {0,0,0,0,0,0,
4919 const gchar *s_am_up = NULL;
4920 const gchar *s_pm_up = NULL;
4921 const gchar *s_am_low = NULL;
4922 const gchar *s_pm_low = NULL;
4924 gint s_am_up_len = 0;
4925 gint s_pm_up_len = 0;
4926 gint s_am_low_len = 0;
4927 gint s_pm_low_len = 0;
4929 const gchar *def_loc_format = NULL;
4930 const gchar *date_loc_format = NULL;
4931 const gchar *time_loc_format = NULL;
4932 const gchar *time_am_pm = NULL;
4934 static gboolean time_names_init_done = FALSE;
4936 static void init_time_names(void)
4940 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4941 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4942 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4943 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4944 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4945 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4946 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4948 monthnames[0] = Q_("Complete month name for use by strftime|January");
4949 monthnames[1] = Q_("Complete month name for use by strftime|February");
4950 monthnames[2] = Q_("Complete month name for use by strftime|March");
4951 monthnames[3] = Q_("Complete month name for use by strftime|April");
4952 monthnames[4] = Q_("Complete month name for use by strftime|May");
4953 monthnames[5] = Q_("Complete month name for use by strftime|June");
4954 monthnames[6] = Q_("Complete month name for use by strftime|July");
4955 monthnames[7] = Q_("Complete month name for use by strftime|August");
4956 monthnames[8] = Q_("Complete month name for use by strftime|September");
4957 monthnames[9] = Q_("Complete month name for use by strftime|October");
4958 monthnames[10] = Q_("Complete month name for use by strftime|November");
4959 monthnames[11] = Q_("Complete month name for use by strftime|December");
4961 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4962 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4963 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4964 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4965 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4966 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4967 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4969 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4970 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4971 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4972 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4973 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4974 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4975 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4976 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4977 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4978 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4979 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4980 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4982 for (i = 0; i < 7; i++) {
4983 daynames_len[i] = strlen(daynames[i]);
4984 s_daynames_len[i] = strlen(s_daynames[i]);
4986 for (i = 0; i < 12; i++) {
4987 monthnames_len[i] = strlen(monthnames[i]);
4988 s_monthnames_len[i] = strlen(s_monthnames[i]);
4991 s_am_up = Q_("For use by strftime (morning)|AM");
4992 s_pm_up = Q_("For use by strftime (afternoon)|PM");
4993 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4994 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4996 s_am_up_len = strlen(s_am_up);
4997 s_pm_up_len = strlen(s_pm_up);
4998 s_am_low_len = strlen(s_am_low);
4999 s_pm_low_len = strlen(s_pm_low);
5001 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5002 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5003 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5005 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5007 time_names_init_done = TRUE;
5010 #define CHECK_SIZE() { \
5011 total_done += len; \
5012 if (total_done >= buflen) { \
5013 buf[buflen-1] = '\0'; \
5018 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5020 gchar *curpos = buf;
5021 gint total_done = 0;
5022 gchar subbuf[64], subfmt[64];
5023 static time_t last_tzset = (time_t)0;
5025 if (!time_names_init_done)
5028 if (format == NULL || lt == NULL)
5031 if (last_tzset != time(NULL)) {
5033 last_tzset = time(NULL);
5036 if (*format == '%') {
5037 gint len = 0, tmp = 0;
5041 len = 1; CHECK_SIZE();
5045 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
5046 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5049 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
5050 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5054 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
5055 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5058 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
5059 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5062 fast_strftime(subbuf, 64, def_loc_format, lt);
5063 len = strlen(subbuf); CHECK_SIZE();
5064 strncpy2(curpos, subbuf, buflen - total_done);
5067 total_done += 2; CHECK_SIZE();
5068 tmp = (lt->tm_year + 1900)/100;
5069 *curpos++ = '0'+(tmp / 10);
5070 *curpos++ = '0'+(tmp % 10);
5073 total_done += 2; CHECK_SIZE();
5074 *curpos++ = '0'+(lt->tm_mday / 10);
5075 *curpos++ = '0'+(lt->tm_mday % 10);
5078 total_done += 8; CHECK_SIZE();
5079 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5080 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5082 *curpos++ = '0'+(lt->tm_mday / 10);
5083 *curpos++ = '0'+(lt->tm_mday % 10);
5085 tmp = lt->tm_year%100;
5086 *curpos++ = '0'+(tmp / 10);
5087 *curpos++ = '0'+(tmp % 10);
5090 len = 2; CHECK_SIZE();
5091 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5094 len = 10; CHECK_SIZE();
5095 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5096 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5099 total_done += 2; CHECK_SIZE();
5100 *curpos++ = '0'+(lt->tm_hour / 10);
5101 *curpos++ = '0'+(lt->tm_hour % 10);
5104 total_done += 2; CHECK_SIZE();
5110 *curpos++ = '0'+(tmp / 10);
5111 *curpos++ = '0'+(tmp % 10);
5114 len = 3; CHECK_SIZE();
5115 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5118 len = 2; CHECK_SIZE();
5119 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5122 len = 2; CHECK_SIZE();
5128 snprintf(curpos, buflen - total_done, "%2d", tmp);
5131 total_done += 2; CHECK_SIZE();
5132 tmp = lt->tm_mon + 1;
5133 *curpos++ = '0'+(tmp / 10);
5134 *curpos++ = '0'+(tmp % 10);
5137 total_done += 2; CHECK_SIZE();
5138 *curpos++ = '0'+(lt->tm_min / 10);
5139 *curpos++ = '0'+(lt->tm_min % 10);
5142 len = 1; CHECK_SIZE();
5146 if (lt->tm_hour >= 12) {
5147 len = s_pm_up_len; CHECK_SIZE();
5148 snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5150 len = s_am_up_len; CHECK_SIZE();
5151 snprintf(curpos, buflen-total_done, "%s", s_am_up);
5155 if (lt->tm_hour >= 12) {
5156 len = s_pm_low_len; CHECK_SIZE();
5157 snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5159 len = s_am_low_len; CHECK_SIZE();
5160 snprintf(curpos, buflen-total_done, "%s", s_am_low);
5164 fast_strftime(subbuf, 64, time_am_pm, lt);
5165 len = strlen(subbuf); CHECK_SIZE();
5166 strncpy2(curpos, subbuf, buflen - total_done);
5169 total_done += 5; CHECK_SIZE();
5170 *curpos++ = '0'+(lt->tm_hour / 10);
5171 *curpos++ = '0'+(lt->tm_hour % 10);
5173 *curpos++ = '0'+(lt->tm_min / 10);
5174 *curpos++ = '0'+(lt->tm_min % 10);
5177 snprintf(subbuf, 64, "%ld", mktime(lt));
5178 len = strlen(subbuf); CHECK_SIZE();
5179 strncpy2(curpos, subbuf, buflen - total_done);
5182 total_done += 2; CHECK_SIZE();
5183 *curpos++ = '0'+(lt->tm_sec / 10);
5184 *curpos++ = '0'+(lt->tm_sec % 10);
5187 len = 1; CHECK_SIZE();
5191 total_done += 8; CHECK_SIZE();
5192 *curpos++ = '0'+(lt->tm_hour / 10);
5193 *curpos++ = '0'+(lt->tm_hour % 10);
5195 *curpos++ = '0'+(lt->tm_min / 10);
5196 *curpos++ = '0'+(lt->tm_min % 10);
5198 *curpos++ = '0'+(lt->tm_sec / 10);
5199 *curpos++ = '0'+(lt->tm_sec % 10);
5202 len = 1; CHECK_SIZE();
5203 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5206 len = 1; CHECK_SIZE();
5207 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5210 fast_strftime(subbuf, 64, date_loc_format, lt);
5211 len = strlen(subbuf); CHECK_SIZE();
5212 strncpy2(curpos, subbuf, buflen - total_done);
5215 fast_strftime(subbuf, 64, time_loc_format, lt);
5216 len = strlen(subbuf); CHECK_SIZE();
5217 strncpy2(curpos, subbuf, buflen - total_done);
5220 total_done += 2; CHECK_SIZE();
5221 tmp = lt->tm_year%100;
5222 *curpos++ = '0'+(tmp / 10);
5223 *curpos++ = '0'+(tmp % 10);
5226 len = 4; CHECK_SIZE();
5227 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5237 /* let these complicated ones be done with the libc */
5238 snprintf(subfmt, 64, "%%%c", *format);
5239 strftime(subbuf, 64, subfmt, lt);
5240 len = strlen(subbuf); CHECK_SIZE();
5241 strncpy2(curpos, subbuf, buflen - total_done);
5245 /* let these complicated modifiers be done with the libc */
5246 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5247 strftime(subbuf, 64, subfmt, lt);
5248 len = strlen(subbuf); CHECK_SIZE();
5249 strncpy2(curpos, subbuf, buflen - total_done);
5253 if (format && *format)
5254 g_warning("format error (%c)", *format);
5261 int len = 1; CHECK_SIZE();
5262 *curpos++ = *format++;
5269 gboolean prefs_common_get_use_shred(void);
5273 #define WEXITSTATUS(x) (x)
5276 int claws_unlink(const gchar *filename)
5279 static int found_shred = -1;
5280 static const gchar *args[4];
5282 if (filename == NULL)
5285 if (prefs_common_get_use_shred()) {
5286 if (found_shred == -1) {
5288 args[0] = g_find_program_in_path("shred");
5289 debug_print("found shred: %s\n", args[0]);
5290 found_shred = (args[0] != NULL) ? 1:0;
5294 if (found_shred == 1) {
5295 if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5296 if (s.st_nlink == 1) {
5299 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5300 NULL, NULL, NULL, NULL, &status, NULL);
5301 debug_print("%s %s exited with status %d\n",
5302 args[0], filename, WEXITSTATUS(status));
5303 if (truncate(filename, 0) < 0)
5304 g_warning("couln't truncate");
5309 return g_unlink(filename);
5312 GMutex *cm_mutex_new(void) {
5313 #if GLIB_CHECK_VERSION(2,32,0)
5315 m = g_new0(GMutex, 1);
5319 return g_mutex_new();
5323 void cm_mutex_free(GMutex *mutex) {
5324 #if GLIB_CHECK_VERSION(2,32,0)
5325 g_mutex_clear(mutex);
5328 g_mutex_free(mutex);