2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib/gi18n.h>
34 #include <sys/param.h>
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
44 #include <sys/types.h>
46 # include <sys/wait.h>
53 #include <sys/utsname.h>
65 #include <osso-browser-interface.h>
70 #include "../codeconv.h"
74 static gboolean debug_mode = FALSE;
76 static GSList *tempfiles=NULL;
79 /* Return true if we are running as root. This function should beused
80 instead of getuid () == 0. */
81 gboolean superuser_p (void)
84 return w32_is_administrator ();
92 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
93 gint g_chdir(const gchar *path)
96 if (G_WIN32_HAVE_WIDECHAR_API()) {
101 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
107 retval = _wchdir(wpath);
119 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
120 if (cp_path == NULL) {
125 retval = chdir(cp_path);
138 gint g_chmod(const gchar *path, gint mode)
141 if (G_WIN32_HAVE_WIDECHAR_API()) {
146 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
152 retval = _wchmod(wpath, mode);
164 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
165 if (cp_path == NULL) {
170 retval = chmod(cp_path, mode);
179 return chmod(path, mode);
182 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
186 gint mkstemp_name(const gchar *template, gchar **name_used)
188 static gulong count=0; /* W32-_mktemp only supports up to 27
192 *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
193 tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
194 (S_IRUSR | S_IWUSR));
196 tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
198 perror(g_strdup_printf("cant create %s",*name_used));
204 #endif /* G_OS_WIN32 */
207 gint mkstemp(const gchar *template)
210 gint res = mkstemp_name(template, &dummyname);
214 #endif /* G_OS_WIN32 */
216 void list_free_strings(GList *list)
218 list = g_list_first(list);
220 while (list != NULL) {
226 void slist_free_strings(GSList *list)
228 while (list != NULL) {
234 GSList *slist_concat_unique (GSList *first, GSList *second)
242 } else if (second == NULL)
245 for (tmp = second; tmp != NULL; tmp = g_slist_next(tmp)) {
246 if (g_slist_find(ret, tmp->data) == NULL)
247 ret = g_slist_prepend(ret, tmp->data);
252 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
257 void hash_free_strings(GHashTable *table)
259 g_hash_table_foreach(table, hash_free_strings_func, NULL);
262 static void hash_free_value_mem_func(gpointer key, gpointer value,
268 void hash_free_value_mem(GHashTable *table)
270 g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
273 gint str_case_equal(gconstpointer v, gconstpointer v2)
275 return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
278 guint str_case_hash(gconstpointer key)
280 const gchar *p = key;
284 h = g_ascii_tolower(h);
285 for (p += 1; *p != '\0'; p++)
286 h = (h << 5) - h + g_ascii_tolower(*p);
292 void ptr_array_free_strings(GPtrArray *array)
297 g_return_if_fail(array != NULL);
299 for (i = 0; i < array->len; i++) {
300 str = g_ptr_array_index(array, i);
305 gboolean str_find(const gchar *haystack, const gchar *needle)
307 return strstr(haystack, needle) != NULL ? TRUE : FALSE;
310 gboolean str_case_find(const gchar *haystack, const gchar *needle)
312 return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
315 gboolean str_find_equal(const gchar *haystack, const gchar *needle)
317 return strcmp(haystack, needle) == 0;
320 gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
322 return g_ascii_strcasecmp(haystack, needle) == 0;
325 gint to_number(const gchar *nstr)
327 register const gchar *p;
329 if (*nstr == '\0') return -1;
331 for (p = nstr; *p != '\0'; p++)
332 if (!g_ascii_isdigit(*p)) return -1;
337 /* convert integer into string,
338 nstr must be not lower than 11 characters length */
339 gchar *itos_buf(gchar *nstr, gint n)
341 g_snprintf(nstr, 11, "%d", n);
345 /* convert integer into string */
348 static gchar nstr[11];
350 return itos_buf(nstr, n);
353 #define divide(num,divisor,i,d) \
355 i = num >> divisor; \
356 d = num & ((1<<divisor)-1); \
357 d = (d*100) >> divisor; \
360 gchar *to_human_readable(off_t size)
362 static gchar str[14];
363 static gchar *b_format = NULL, *kb_format = NULL,
364 *mb_format = NULL, *gb_format = NULL;
365 register int t = 0, r = 0;
366 if (b_format == NULL) {
368 kb_format = _("%d.%02dKB");
369 mb_format = _("%d.%02dMB");
370 gb_format = _("%.2fGB");
373 if (size < (off_t)1024) {
374 g_snprintf(str, sizeof(str), b_format, (gint)size);
376 } else if (size >> 10 < (off_t)1024) {
377 divide(size, 10, t, r);
378 g_snprintf(str, sizeof(str), kb_format, t, r);
380 } else if (size >> 20 < (off_t)1024) {
381 divide(size, 20, t, r);
382 g_snprintf(str, sizeof(str), mb_format, t, r);
385 g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
390 /* strcmp with NULL-checking */
391 gint strcmp2(const gchar *s1, const gchar *s2)
393 if (s1 == NULL || s2 == NULL)
396 return strcmp(s1, s2);
398 /* strstr with NULL-checking */
399 gchar *strstr2(const gchar *s1, const gchar *s2)
401 if (s1 == NULL || s2 == NULL)
404 return strstr(s1, s2);
407 gint path_cmp(const gchar *s1, const gchar *s2)
412 gchar *s1buf, *s2buf;
415 if (s1 == NULL || s2 == NULL) return -1;
416 if (*s1 == '\0' || *s2 == '\0') return -1;
419 s1buf = g_strdup (s1);
420 s2buf = g_strdup (s2);
421 subst_char (s1buf, '/', G_DIR_SEPARATOR);
422 subst_char (s2buf, '/', G_DIR_SEPARATOR);
425 #endif /* !G_OS_WIN32 */
430 if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
431 if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
433 rc = strncmp(s1, s2, MAX(len1, len2));
437 #endif /* !G_OS_WIN32 */
441 /* remove trailing return code */
442 gchar *strretchomp(gchar *str)
446 if (!*str) return str;
448 for (s = str + strlen(str) - 1;
449 s >= str && (*s == '\n' || *s == '\r');
456 /* remove trailing character */
457 gchar *strtailchomp(gchar *str, gchar tail_char)
461 if (!*str) return str;
462 if (tail_char == '\0') return str;
464 for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
470 /* remove CR (carriage return) */
471 gchar *strcrchomp(gchar *str)
475 if (!*str) return str;
477 s = str + strlen(str) - 1;
478 if (*s == '\n' && s > str && *(s - 1) == '\r') {
486 gint file_strip_crs(const gchar *file)
488 FILE *fp = NULL, *outfp = NULL;
490 gchar *out = get_tmp_file();
494 fp = fopen(file, "rb");
498 outfp = fopen(out, "wb");
504 while (fgets(buf, sizeof (buf), fp) != NULL) {
506 if (fputs(buf, outfp) == EOF) {
514 if (fclose(outfp) == EOF) {
518 if (rename_force(out, file) < 0)
529 /* Similar to `strstr' but this function ignores the case of both strings. */
530 gchar *strcasestr(const gchar *haystack, const gchar *needle)
532 register size_t haystack_len, needle_len;
534 haystack_len = strlen(haystack);
535 needle_len = strlen(needle);
537 if (haystack_len < needle_len || needle_len == 0)
540 while (haystack_len >= needle_len) {
541 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
542 return (gchar *)haystack;
552 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
553 gconstpointer needle, size_t needlelen)
555 const gchar *haystack_ = (const gchar *)haystack;
556 const gchar *needle_ = (const gchar *)needle;
557 const gchar *haystack_cur = (const gchar *)haystack;
560 return memchr(haystack_, *needle_, haystacklen);
562 while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
564 if (haystacklen - (haystack_cur - haystack_) < needlelen)
566 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
567 return (gpointer)haystack_cur;
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
578 register const gchar *s = src;
579 register gchar *d = dest;
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
594 /* skip head space */
595 for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
597 for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598 if (*(guchar *)p > 127 || *(guchar *)p < 32)
605 gint get_next_word_len(const gchar *s)
609 for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
615 /* compare subjects */
616 gint subject_compare(const gchar *s1, const gchar *s2)
620 if (!s1 || !s2) return -1;
621 if (!*s1 || !*s2) return -1;
623 Xstrdup_a(str1, s1, return -1);
624 Xstrdup_a(str2, s2, return -1);
626 trim_subject_for_compare(str1);
627 trim_subject_for_compare(str2);
629 if (!*str1 || !*str2) return -1;
631 return strcmp(str1, str2);
634 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
638 if (!s1 || !s2) return -1;
640 Xstrdup_a(str1, s1, return -1);
641 Xstrdup_a(str2, s2, return -1);
643 trim_subject_for_sort(str1);
644 trim_subject_for_sort(str2);
646 return g_utf8_collate(str1, str2);
649 void trim_subject_for_compare(gchar *str)
653 eliminate_parenthesis(str, '[', ']');
654 eliminate_parenthesis(str, '(', ')');
657 srcp = str + subject_get_prefix_length(str);
659 memmove(str, srcp, strlen(srcp) + 1);
662 void trim_subject_for_sort(gchar *str)
668 srcp = str + subject_get_prefix_length(str);
670 memmove(str, srcp, strlen(srcp) + 1);
673 void trim_subject(gchar *str)
675 register gchar *srcp;
681 srcp = str + subject_get_prefix_length(str);
686 } else if (*srcp == '(') {
698 else if (*srcp == cl)
705 while (g_ascii_isspace(*srcp)) srcp++;
706 memmove(str, srcp, strlen(srcp) + 1);
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
711 register gchar *srcp, *destp;
716 while ((destp = strchr(destp, op))) {
722 else if (*srcp == cl)
728 while (g_ascii_isspace(*srcp)) srcp++;
729 memmove(destp, srcp, strlen(srcp) + 1);
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
735 register gchar *srcp, *destp;
740 while ((srcp = strchr(destp, op))) {
743 memmove(destp, srcp + 1, strlen(srcp));
748 else if (*destp == cl)
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
763 register gchar *srcp, *destp;
765 gboolean in_quote = FALSE;
769 while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
772 memmove(destp, srcp + 1, strlen(srcp));
775 if (*destp == op && !in_quote)
777 else if (*destp == cl && !in_quote)
779 else if (*destp == quote_chr)
791 void extract_quote(gchar *str, gchar quote_chr)
795 if ((str = strchr(str, quote_chr))) {
797 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798 memmove(p - 1, p, strlen(p) + 1);
803 memmove(str, str + 1, p - str);
808 void eliminate_address_comment(gchar *str)
810 register gchar *srcp, *destp;
815 while ((destp = strchr(destp, '"'))) {
816 if ((srcp = strchr(destp + 1, '"'))) {
821 while (g_ascii_isspace(*srcp)) srcp++;
822 memmove(destp, srcp, strlen(srcp) + 1);
832 while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
838 else if (*srcp == ')')
844 while (g_ascii_isspace(*srcp)) srcp++;
845 memmove(destp, srcp, strlen(srcp) + 1);
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
851 gboolean in_quote = FALSE;
854 if (*str == c && !in_quote)
856 if (*str == quote_chr)
864 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
866 gboolean in_quote = FALSE;
869 p = str + strlen(str) - 1;
871 if (*p == c && !in_quote)
881 void extract_address(gchar *str)
883 eliminate_address_comment(str);
884 if (strchr_with_skip_quote(str, '"', '<'))
885 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
889 void extract_list_id_str(gchar *str)
891 if (strchr_with_skip_quote(str, '"', '<'))
892 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
896 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
901 if (!str) return addr_list;
903 Xstrdup_a(work, str, return addr_list);
906 eliminate_address_comment(work);
909 while (workp && *workp) {
912 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
918 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
919 extract_parenthesis_with_skip_quote
920 (workp, '"', '<', '>');
924 addr_list = g_slist_append(addr_list, g_strdup(workp));
932 GSList *address_list_append(GSList *addr_list, const gchar *str)
934 return address_list_append_real(addr_list, str, TRUE);
937 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
939 return address_list_append_real(addr_list, str, FALSE);
942 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
946 if (!str) return msgid_list;
949 while (strp && *strp) {
950 const gchar *start, *end;
953 if ((start = strchr(strp, '<')) != NULL) {
954 end = strchr(start + 1, '>');
959 msgid = g_strndup(start + 1, end - start - 1);
962 msgid_list = g_slist_prepend(msgid_list, msgid);
972 GSList *references_list_append(GSList *msgid_list, const gchar *str)
976 list = references_list_prepend(NULL, str);
977 list = g_slist_reverse(list);
978 msgid_list = g_slist_concat(msgid_list, list);
983 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
988 if (!str) return group_list;
990 Xstrdup_a(work, str, return group_list);
994 while (workp && *workp) {
997 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1005 group_list = g_slist_append(group_list,
1014 GList *add_history(GList *list, const gchar *str)
1018 g_return_val_if_fail(str != NULL, list);
1020 old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1023 list = g_list_remove(list, old->data);
1024 } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1027 last = g_list_last(list);
1030 list = g_list_remove(list, last->data);
1034 list = g_list_prepend(list, g_strdup(str));
1039 void remove_return(gchar *str)
1041 register gchar *p = str;
1044 if (*p == '\n' || *p == '\r')
1045 memmove(p, p + 1, strlen(p));
1051 void remove_space(gchar *str)
1053 register gchar *p = str;
1058 while (g_ascii_isspace(*(p + spc)))
1061 memmove(p, p + spc, strlen(p + spc) + 1);
1067 void unfold_line(gchar *str)
1069 register gchar *p = str;
1073 if (*p == '\n' || *p == '\r') {
1076 while (g_ascii_isspace(*(p + spc)))
1079 memmove(p, p + spc, strlen(p + spc) + 1);
1085 void subst_char(gchar *str, gchar orig, gchar subst)
1087 register gchar *p = str;
1096 void subst_chars(gchar *str, gchar *orig, gchar subst)
1098 register gchar *p = str;
1101 if (strchr(orig, *p) != NULL)
1107 void subst_for_filename(gchar *str)
1112 subst_chars(str, "\t\r\n\\/*:", '_');
1114 subst_chars(str, "\t\r\n\\/*", '_');
1118 void subst_for_shellsafe_filename(gchar *str)
1122 subst_for_filename(str);
1123 subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1126 gboolean is_header_line(const gchar *str)
1128 if (str[0] == ':') return FALSE;
1130 while (*str != '\0' && *str != ' ') {
1139 gboolean is_ascii_str(const gchar *str)
1141 const guchar *p = (const guchar *)str;
1143 while (*p != '\0') {
1144 if (*p != '\t' && *p != ' ' &&
1145 *p != '\r' && *p != '\n' &&
1146 (*p < 32 || *p >= 127))
1154 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1156 const gchar *first_pos;
1157 const gchar *last_pos;
1158 const gchar *p = str;
1159 gint quote_level = -1;
1161 /* speed up line processing by only searching to the last '>' */
1162 if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1163 /* skip a line if it contains a '<' before the initial '>' */
1164 if (memchr(str, '<', first_pos - str) != NULL)
1166 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1170 while (p <= last_pos) {
1171 while (p < last_pos) {
1172 if (g_ascii_isspace(*p))
1178 if (strchr(quote_chars, *p))
1180 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1181 /* any characters are allowed except '-' and space */
1183 && !strchr(quote_chars, *p)
1184 && !g_ascii_isspace(*p)
1187 if (strchr(quote_chars, *p))
1199 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1201 const gchar *p = str, *q;
1202 gint cur_line = 0, len;
1204 while ((q = strchr(p, '\n')) != NULL) {
1206 if (len > max_chars) {
1216 if (len > max_chars) {
1225 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1227 gchar * position = NULL;
1228 gchar * tmp_pos = NULL;
1231 if (quote_chars == NULL)
1234 for (i = 0; i < strlen(quote_chars); i++) {
1235 tmp_pos = strchr (str, quote_chars[i]);
1237 || (tmp_pos != NULL && position >= tmp_pos) )
1243 const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1245 gchar * position = NULL;
1246 gchar * tmp_pos = NULL;
1249 if (quote_chars == NULL)
1252 for (i = 0; i < strlen(quote_chars); i++) {
1253 tmp_pos = strrchr (str, quote_chars[i]);
1255 || (tmp_pos != NULL && position <= tmp_pos) )
1261 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1263 register guint haystack_len, needle_len;
1264 gboolean in_squote = FALSE, in_dquote = FALSE;
1266 haystack_len = strlen(haystack);
1267 needle_len = strlen(needle);
1269 if (haystack_len < needle_len || needle_len == 0)
1272 while (haystack_len >= needle_len) {
1273 if (!in_squote && !in_dquote &&
1274 !strncmp(haystack, needle, needle_len))
1275 return (gchar *)haystack;
1277 /* 'foo"bar"' -> foo"bar"
1278 "foo'bar'" -> foo'bar' */
1279 if (*haystack == '\'') {
1282 else if (!in_dquote)
1284 } else if (*haystack == '\"') {
1287 else if (!in_squote)
1298 gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1301 gchar quote_chr = '"';
1303 gboolean in_quote = FALSE;
1307 if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1311 if (*p == op && !in_quote)
1313 else if (*p == cl && !in_quote)
1315 else if (*p == quote_chr)
1328 gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1331 GSList *string_list = NULL, *slist;
1333 const gchar *s_op, *s_cl;
1336 g_return_val_if_fail(str != NULL, NULL);
1339 max_tokens = G_MAXINT;
1341 s_op = strchr_with_skip_quote(str, '"', op);
1342 if (!s_op) return NULL;
1344 s_cl = strchr_parenthesis_close(str, op, cl);
1352 new_string = g_new(gchar, len + 1);
1353 strncpy(new_string, str, len);
1354 new_string[len] = 0;
1355 string_list = g_slist_prepend(string_list, new_string);
1359 while (*str && g_ascii_isspace(*str)) str++;
1361 string_list = g_slist_prepend(string_list,
1364 s_op = strchr_with_skip_quote(str, '"', op);
1365 if (!--max_tokens || !s_op) break;
1369 s_cl = strchr_parenthesis_close(str, op, cl);
1370 } while (--max_tokens && s_cl);
1373 str_array = g_new(gchar*, n);
1377 str_array[i--] = NULL;
1378 for (slist = string_list; slist; slist = slist->next)
1379 str_array[i--] = slist->data;
1381 g_slist_free(string_list);
1386 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1389 GSList *string_list = NULL, *slist;
1390 gchar **str_array, *s, *new_str;
1391 guint i, n = 1, len;
1393 g_return_val_if_fail(str != NULL, NULL);
1394 g_return_val_if_fail(delim != NULL, NULL);
1397 max_tokens = G_MAXINT;
1399 s = strstr_with_skip_quote(str, delim);
1401 guint delimiter_len = strlen(delim);
1405 new_str = g_strndup(str, len);
1407 if (new_str[0] == '\'' || new_str[0] == '\"') {
1408 if (new_str[len - 1] == new_str[0]) {
1409 new_str[len - 1] = '\0';
1410 memmove(new_str, new_str + 1, len - 1);
1413 string_list = g_slist_prepend(string_list, new_str);
1415 str = s + delimiter_len;
1416 s = strstr_with_skip_quote(str, delim);
1417 } while (--max_tokens && s);
1421 new_str = g_strdup(str);
1422 if (new_str[0] == '\'' || new_str[0] == '\"') {
1424 if (new_str[len - 1] == new_str[0]) {
1425 new_str[len - 1] = '\0';
1426 memmove(new_str, new_str + 1, len - 1);
1429 string_list = g_slist_prepend(string_list, new_str);
1433 str_array = g_new(gchar*, n);
1437 str_array[i--] = NULL;
1438 for (slist = string_list; slist; slist = slist->next)
1439 str_array[i--] = slist->data;
1441 g_slist_free(string_list);
1446 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1448 gchar *abbrev_group;
1450 const gchar *p = group;
1453 g_return_val_if_fail(group != NULL, NULL);
1455 last = group + strlen(group);
1456 abbrev_group = ap = g_malloc(strlen(group) + 1);
1461 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1463 while (*p != '.') p++;
1466 return abbrev_group;
1471 return abbrev_group;
1474 gchar *trim_string(const gchar *str, gint len)
1476 const gchar *p = str;
1481 if (!str) return NULL;
1482 if (strlen(str) <= len)
1483 return g_strdup(str);
1484 if (g_utf8_validate(str, -1, NULL) == FALSE)
1485 return g_strdup(str);
1487 while (*p != '\0') {
1488 mb_len = g_utf8_skip[*(guchar *)p];
1491 else if (new_len + mb_len > len)
1498 Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1499 return g_strconcat(new_str, "...", NULL);
1502 GList *uri_list_extract_filenames(const gchar *uri_list)
1504 GList *result = NULL;
1506 gchar *escaped_utf8uri;
1512 while (g_ascii_isspace(*p)) p++;
1513 if (!strncmp(p, "file:", 5)) {
1516 while (*q && *q != '\n' && *q != '\r') q++;
1519 gchar *file, *locale_file = NULL;
1521 while (q > p && g_ascii_isspace(*q))
1523 Xalloca(escaped_utf8uri, q - p + 2,
1525 Xalloca(file, q - p + 2,
1528 strncpy(escaped_utf8uri, p, q - p + 1);
1529 escaped_utf8uri[q - p + 1] = '\0';
1530 decode_uri(file, escaped_utf8uri);
1532 * g_filename_from_uri() rejects escaped/locale encoded uri
1533 * string which come from Nautilus.
1535 if (g_utf8_validate(file, -1, NULL))
1537 = conv_codeset_strdup(
1540 conv_get_locale_charset_str());
1542 locale_file = g_strdup(file + 5);
1543 result = g_list_append(result, locale_file);
1547 p = strchr(p, '\n');
1554 /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1557 static gint axtoi(const gchar *hexstr)
1559 gint hi, lo, result;
1562 if ('0' <= hi && hi <= '9') {
1565 if ('a' <= hi && hi <= 'f') {
1568 if ('A' <= hi && hi <= 'F') {
1573 if ('0' <= lo && lo <= '9') {
1576 if ('a' <= lo && lo <= 'f') {
1579 if ('A' <= lo && lo <= 'F') {
1582 result = lo + (16 * hi);
1586 gboolean is_uri_string(const gchar *str)
1588 while (str && *str && g_ascii_isspace(*str))
1590 return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1591 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1592 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1593 g_ascii_strncasecmp(str, "www.", 4) == 0);
1596 gchar *get_uri_path(const gchar *uri)
1598 while (uri && *uri && g_ascii_isspace(*uri))
1600 if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1601 return (gchar *)(uri + 7);
1602 else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1603 return (gchar *)(uri + 8);
1604 else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1605 return (gchar *)(uri + 6);
1607 return (gchar *)uri;
1610 gint get_uri_len(const gchar *str)
1614 if (is_uri_string(str)) {
1615 for (p = str; *p != '\0'; p++) {
1616 if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1625 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1626 * plusses, and escape characters are used)
1628 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1630 gchar *dec = decoded_uri;
1631 const gchar *enc = encoded_uri;
1636 if (isxdigit((guchar)enc[0]) &&
1637 isxdigit((guchar)enc[1])) {
1643 if (with_plus && *enc == '+')
1655 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1657 decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1660 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1661 gchar **subject, gchar **body, gchar **attach)
1665 const gchar *forbidden_uris[] = { ".gnupg/",
1670 Xstrdup_a(tmp_mailto, mailto, return -1);
1672 if (!strncmp(tmp_mailto, "mailto:", 7))
1675 p = strchr(tmp_mailto, '?');
1682 *to = g_strdup(tmp_mailto);
1685 gchar *field, *value;
1702 if (*value == '\0') continue;
1704 if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1705 *cc = g_strdup(value);
1706 } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1707 *bcc = g_strdup(value);
1708 } else if (subject && !*subject &&
1709 !g_ascii_strcasecmp(field, "subject")) {
1710 *subject = g_malloc(strlen(value) + 1);
1711 decode_uri(*subject, value);
1712 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1713 *body = g_malloc(strlen(value) + 1);
1714 decode_uri(*body, value);
1715 } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
1717 *attach = g_malloc(strlen(value) + 1);
1718 decode_uri(*attach, value);
1719 for (; forbidden_uris[i]; i++) {
1720 if (strstr(*attach, forbidden_uris[i])) {
1721 g_print("Refusing to attach '%s', potential private data leak\n",
1736 #include <windows.h>
1737 #ifndef CSIDL_APPDATA
1738 #define CSIDL_APPDATA 0x001a
1740 #ifndef CSIDL_LOCAL_APPDATA
1741 #define CSIDL_LOCAL_APPDATA 0x001c
1743 #ifndef CSIDL_FLAG_CREATE
1744 #define CSIDL_FLAG_CREATE 0x8000
1746 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
1750 w32_strerror (int w32_errno)
1752 static char strerr[256];
1753 int ec = (int)GetLastError ();
1757 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1758 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1759 strerr, DIM (strerr)-1, NULL);
1763 static __inline__ void *
1764 dlopen (const char * name, int flag)
1766 void * hd = LoadLibrary (name);
1770 static __inline__ void *
1771 dlsym (void * hd, const char * sym)
1775 void * fnc = GetProcAddress (hd, sym);
1784 static __inline__ const char *
1787 return w32_strerror (0);
1791 static __inline__ int
1803 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1805 static int initialized;
1806 static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1810 static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1816 for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1818 handle = dlopen (dllnames[i], RTLD_LAZY);
1821 func = dlsym (handle, "SHGetFolderPathA");
1832 return func (a,b,c,d,e);
1837 /* Returns a static string with the directroy from which the module
1838 has been loaded. Returns an empty string on error. */
1839 static char *w32_get_module_dir(void)
1841 static char *moddir;
1844 char name[MAX_PATH+10];
1847 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1850 p = strrchr (name, '\\');
1856 moddir = g_strdup (name);
1860 #endif /* G_OS_WIN32 */
1862 /* Return a static string with the locale dir. */
1863 const gchar *get_locale_dir(void)
1865 static gchar *loc_dir;
1869 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1870 "\\share\\locale", NULL);
1873 loc_dir = LOCALEDIR;
1879 const gchar *get_home_dir(void)
1882 static char home_dir[MAX_PATH] = "";
1884 if (home_dir[0] == '\0') {
1885 if (w32_shgetfolderpath
1886 (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1887 NULL, 0, home_dir) < 0)
1888 strcpy (home_dir, "C:\\Sylpheed");
1892 static const gchar *homeenv = NULL;
1897 if (!homeenv && g_getenv("HOME") != NULL)
1898 homeenv = g_strdup(g_getenv("HOME"));
1900 homeenv = g_get_home_dir();
1906 static gchar *claws_rc_dir = NULL;
1907 static gboolean rc_dir_alt = FALSE;
1908 const gchar *get_rc_dir(void)
1912 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1915 return claws_rc_dir;
1918 void set_rc_dir(const gchar *dir)
1920 if (claws_rc_dir != NULL) {
1921 g_print("Error: rc_dir already set\n");
1924 if (g_path_is_absolute(dir))
1925 claws_rc_dir = g_strdup(dir);
1927 claws_rc_dir = g_strconcat(g_get_current_dir(),
1928 G_DIR_SEPARATOR_S, dir, NULL);
1930 debug_print("set rc_dir to %s\n", claws_rc_dir);
1931 if (!is_dir_exist(claws_rc_dir)) {
1932 if (make_dir_hier(claws_rc_dir) != 0) {
1933 g_print("Error: can't create %s\n",
1940 gboolean rc_dir_is_alt(void) {
1944 const gchar *get_mail_base_dir(void)
1947 static gchar *mail_base_dir = NULL;
1950 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1953 return mail_base_dir;
1955 return get_home_dir();
1960 const gchar *prefs_common_get_data_root(void);
1961 gchar *last_data_root = NULL;
1964 const gchar *get_news_cache_dir(void)
1966 static gchar *news_cache_dir = NULL;
1968 const gchar *data_root = prefs_common_get_data_root();
1969 if (strcmp2(data_root, last_data_root)) {
1970 g_free(news_cache_dir);
1971 news_cache_dir = NULL;
1974 if (!news_cache_dir)
1976 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1977 NEWS_CACHE_DIR, NULL);
1981 news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1982 "Claws", G_DIR_SEPARATOR_S,
1983 g_get_user_name(), G_DIR_SEPARATOR_S,
1984 NEWS_CACHE_DIR, NULL);
1985 g_free(last_data_root);
1986 last_data_root = g_strdup(last_data_root);
1988 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1989 NEWS_CACHE_DIR, NULL);
1990 g_free(last_data_root);
1991 last_data_root = NULL;
1995 return news_cache_dir;
1998 const gchar *get_imap_cache_dir(void)
2000 static gchar *imap_cache_dir = NULL;
2002 const gchar *data_root = prefs_common_get_data_root();
2003 if (strcmp2(data_root, last_data_root)) {
2004 g_free(imap_cache_dir);
2005 imap_cache_dir = NULL;
2009 if (!imap_cache_dir)
2011 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2012 IMAP_CACHE_DIR, NULL);
2016 imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2017 "Claws", G_DIR_SEPARATOR_S,
2018 g_get_user_name(), G_DIR_SEPARATOR_S,
2019 IMAP_CACHE_DIR, NULL);
2020 g_free(last_data_root);
2021 last_data_root = g_strdup(last_data_root);
2023 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2024 IMAP_CACHE_DIR, NULL);
2025 g_free(last_data_root);
2026 last_data_root = NULL;
2031 return imap_cache_dir;
2034 const gchar *get_mbox_cache_dir(void)
2036 static gchar *mbox_cache_dir = NULL;
2038 const gchar *data_root = prefs_common_get_data_root();
2039 if (strcmp2(data_root, last_data_root)) {
2040 g_free(mbox_cache_dir);
2041 mbox_cache_dir = NULL;
2044 if (!mbox_cache_dir)
2046 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2047 MBOX_CACHE_DIR, NULL);
2051 mbox_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
2052 "Claws", G_DIR_SEPARATOR_S,
2053 g_get_user_name(), G_DIR_SEPARATOR_S,
2054 MBOX_CACHE_DIR, NULL);
2055 g_free(last_data_root);
2056 last_data_root = g_strdup(last_data_root);
2058 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2059 MBOX_CACHE_DIR, NULL);
2060 g_free(last_data_root);
2061 last_data_root = NULL;
2066 return mbox_cache_dir;
2069 const gchar *get_mime_tmp_dir(void)
2071 static gchar *mime_tmp_dir = NULL;
2074 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2075 MIME_TMP_DIR, NULL);
2077 return mime_tmp_dir;
2080 const gchar *get_template_dir(void)
2082 static gchar *template_dir = NULL;
2085 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2086 TEMPLATE_DIR, NULL);
2088 return template_dir;
2091 const gchar *get_header_cache_dir(void)
2093 static gchar *header_dir = NULL;
2096 header_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2097 HEADER_CACHE_DIR, NULL);
2102 /* Return the default directory for Plugins. */
2103 const gchar *get_plugin_dir(void)
2106 static gchar *plugin_dir = NULL;
2109 plugin_dir = g_strconcat(w32_get_module_dir(),
2110 "\\lib\\claws-mail\\plugins\\",
2114 if (is_dir_exist(PLUGINDIR))
2117 static gchar *plugin_dir = NULL;
2119 plugin_dir = g_strconcat(get_rc_dir(),
2120 G_DIR_SEPARATOR_S, "plugins",
2121 G_DIR_SEPARATOR_S, NULL);
2127 const gchar *get_tmp_dir(void)
2129 static gchar *tmp_dir = NULL;
2132 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2138 gchar *get_tmp_file(void)
2141 static guint32 id = 0;
2143 tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2144 get_tmp_dir(), G_DIR_SEPARATOR, id++);
2149 const gchar *get_domain_name(void)
2152 static gchar *domain_name = NULL;
2158 if (gethostname(hostname, sizeof(hostname)) != 0) {
2159 perror("gethostname");
2160 domain_name = "unknown";
2162 hostname[sizeof(hostname) - 1] = '\0';
2163 if ((hp = my_gethostbyname(hostname)) == NULL) {
2164 perror("gethostbyname");
2165 domain_name = g_strdup(hostname);
2167 domain_name = g_strdup(hp->h_name);
2170 debug_print("domain name = %s\n", domain_name);
2179 off_t get_file_size(const gchar *file)
2183 if (g_stat(file, &s) < 0) {
2184 FILE_OP_ERROR(file, "stat");
2191 time_t get_file_mtime(const gchar *file)
2195 if (g_stat(file, &s) < 0) {
2196 FILE_OP_ERROR(file, "stat");
2203 off_t get_file_size_as_crlf(const gchar *file)
2207 gchar buf[BUFFSIZE];
2209 if ((fp = g_fopen(file, "rb")) == NULL) {
2210 FILE_OP_ERROR(file, "fopen");
2214 while (fgets(buf, sizeof(buf), fp) != NULL) {
2216 size += strlen(buf) + 2;
2220 FILE_OP_ERROR(file, "fgets");
2229 off_t get_left_file_size(FILE *fp)
2235 if ((pos = ftell(fp)) < 0) {
2239 if (fseek(fp, 0L, SEEK_END) < 0) {
2243 if ((end = ftell(fp)) < 0) {
2248 if (fseek(fp, pos, SEEK_SET) < 0) {
2256 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2263 if (g_stat(file, &s) < 0) {
2264 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2268 if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2275 /* Test on whether FILE is a relative file name. This is
2276 * straightforward for Unix but more complex for Windows. */
2277 gboolean is_relative_filename(const gchar *file)
2282 if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2283 return FALSE; /* Prefixed with a hostname - this can't
2284 * be a relative name. */
2286 if ( ((*file >= 'a' && *file <= 'z')
2287 || (*file >= 'A' && *file <= 'Z'))
2289 file += 2; /* Skip drive letter. */
2291 return !(*file == '\\' || *file == '/');
2293 return !(*file == G_DIR_SEPARATOR);
2298 gboolean is_dir_exist(const gchar *dir)
2303 return g_file_test(dir, G_FILE_TEST_IS_DIR);
2306 gboolean is_file_entry_exist(const gchar *file)
2311 return g_file_test(file, G_FILE_TEST_EXISTS);
2314 gboolean dirent_is_regular_file(struct dirent *d)
2316 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2317 if (d->d_type == DT_REG)
2319 else if (d->d_type != DT_UNKNOWN)
2323 return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2326 gint change_dir(const gchar *dir)
2328 gchar *prevdir = NULL;
2331 prevdir = g_get_current_dir();
2333 if (g_chdir(dir) < 0) {
2334 FILE_OP_ERROR(dir, "chdir");
2335 if (debug_mode) g_free(prevdir);
2337 } else if (debug_mode) {
2340 cwd = g_get_current_dir();
2341 if (strcmp(prevdir, cwd) != 0)
2342 g_print("current dir: %s\n", cwd);
2350 gint make_dir(const gchar *dir)
2352 if (g_mkdir(dir, S_IRWXU) < 0) {
2353 FILE_OP_ERROR(dir, "mkdir");
2356 if (g_chmod(dir, S_IRWXU) < 0)
2357 FILE_OP_ERROR(dir, "chmod");
2362 gint make_dir_hier(const gchar *dir)
2367 for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2368 parent_dir = g_strndup(dir, p - dir);
2369 if (*parent_dir != '\0') {
2370 if (!is_dir_exist(parent_dir)) {
2371 if (make_dir(parent_dir) < 0) {
2380 if (!is_dir_exist(dir)) {
2381 if (make_dir(dir) < 0)
2388 gint remove_all_files(const gchar *dir)
2391 const gchar *dir_name;
2394 prev_dir = g_get_current_dir();
2396 if (g_chdir(dir) < 0) {
2397 FILE_OP_ERROR(dir, "chdir");
2402 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2403 g_warning("failed to open directory: %s\n", dir);
2408 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2409 if (g_unlink(dir_name) < 0)
2410 FILE_OP_ERROR(dir_name, "unlink");
2415 if (g_chdir(prev_dir) < 0) {
2416 FILE_OP_ERROR(prev_dir, "chdir");
2426 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2429 const gchar *dir_name;
2433 prev_dir = g_get_current_dir();
2435 if (g_chdir(dir) < 0) {
2436 FILE_OP_ERROR(dir, "chdir");
2441 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2442 g_warning("failed to open directory: %s\n", dir);
2447 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2448 file_no = to_number(dir_name);
2449 if (file_no > 0 && first <= file_no && file_no <= last) {
2450 if (is_dir_exist(dir_name))
2452 if (g_unlink(dir_name) < 0)
2453 FILE_OP_ERROR(dir_name, "unlink");
2459 if (g_chdir(prev_dir) < 0) {
2460 FILE_OP_ERROR(prev_dir, "chdir");
2470 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2473 const gchar *dir_name;
2477 prev_dir = g_get_current_dir();
2479 if (g_chdir(dir) < 0) {
2480 FILE_OP_ERROR(dir, "chdir");
2485 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2486 FILE_OP_ERROR(dir, "opendir");
2491 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2492 file_no = to_number(dir_name);
2493 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2494 debug_print("removing unwanted file %d from %s\n", file_no, dir);
2495 if (is_dir_exist(dir_name))
2497 if (g_unlink(dir_name) < 0)
2498 FILE_OP_ERROR(dir_name, "unlink");
2504 if (g_chdir(prev_dir) < 0) {
2505 FILE_OP_ERROR(prev_dir, "chdir");
2515 gint remove_all_numbered_files(const gchar *dir)
2517 return remove_numbered_files(dir, 0, UINT_MAX);
2520 gint remove_expired_files(const gchar *dir, guint hours)
2523 const gchar *dir_name;
2527 time_t mtime, now, expire_time;
2529 prev_dir = g_get_current_dir();
2531 if (g_chdir(dir) < 0) {
2532 FILE_OP_ERROR(dir, "chdir");
2537 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2538 g_warning("failed to open directory: %s\n", dir);
2544 expire_time = hours * 60 * 60;
2546 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2547 file_no = to_number(dir_name);
2549 if (g_stat(dir_name, &s) < 0) {
2550 FILE_OP_ERROR(dir_name, "stat");
2553 if (S_ISDIR(s.st_mode))
2555 mtime = MAX(s.st_mtime, s.st_atime);
2556 if (now - mtime > expire_time) {
2557 if (g_unlink(dir_name) < 0)
2558 FILE_OP_ERROR(dir_name, "unlink");
2565 if (g_chdir(prev_dir) < 0) {
2566 FILE_OP_ERROR(prev_dir, "chdir");
2576 gint remove_dir_recursive(const gchar *dir)
2580 const gchar *dir_name;
2583 if (g_stat(dir, &s) < 0) {
2584 FILE_OP_ERROR(dir, "stat");
2585 if (ENOENT == errno) return 0;
2589 if (!S_ISDIR(s.st_mode)) {
2590 if (g_unlink(dir) < 0) {
2591 FILE_OP_ERROR(dir, "unlink");
2598 prev_dir = g_get_current_dir();
2599 /* g_print("prev_dir = %s\n", prev_dir); */
2601 if (!path_cmp(prev_dir, dir)) {
2603 if (g_chdir("..") < 0) {
2604 FILE_OP_ERROR(dir, "chdir");
2607 prev_dir = g_get_current_dir();
2610 if (g_chdir(dir) < 0) {
2611 FILE_OP_ERROR(dir, "chdir");
2616 if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2617 g_warning("failed to open directory: %s\n", dir);
2623 /* remove all files in the directory */
2624 while ((dir_name = g_dir_read_name(dp)) != NULL) {
2625 /* g_print("removing %s\n", dir_name); */
2627 if (is_dir_exist(dir_name)) {
2628 if (remove_dir_recursive(dir_name) < 0) {
2629 g_warning("can't remove directory\n");
2633 if (g_unlink(dir_name) < 0)
2634 FILE_OP_ERROR(dir_name, "unlink");
2640 if (g_chdir(prev_dir) < 0) {
2641 FILE_OP_ERROR(prev_dir, "chdir");
2648 if (g_rmdir(dir) < 0) {
2649 FILE_OP_ERROR(dir, "rmdir");
2656 gint rename_force(const gchar *oldpath, const gchar *newpath)
2659 if (!is_file_entry_exist(oldpath)) {
2663 if (is_file_exist(newpath)) {
2664 if (g_unlink(newpath) < 0)
2665 FILE_OP_ERROR(newpath, "unlink");
2668 return g_rename(oldpath, newpath);
2672 * Append src file body to the tail of dest file.
2673 * Now keep_backup has no effects.
2675 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2677 FILE *src_fp, *dest_fp;
2681 gboolean err = FALSE;
2683 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2684 FILE_OP_ERROR(src, "fopen");
2688 if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2689 FILE_OP_ERROR(dest, "fopen");
2694 if (change_file_mode_rw(dest_fp, dest) < 0) {
2695 FILE_OP_ERROR(dest, "chmod");
2696 g_warning("can't change file mode\n");
2699 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2700 if (n_read < sizeof(buf) && ferror(src_fp))
2702 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2703 g_warning("writing to %s failed.\n", dest);
2711 if (ferror(src_fp)) {
2712 FILE_OP_ERROR(src, "fread");
2716 if (fclose(dest_fp) == EOF) {
2717 FILE_OP_ERROR(dest, "fclose");
2729 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2731 FILE *src_fp, *dest_fp;
2734 gchar *dest_bak = NULL;
2735 gboolean err = FALSE;
2737 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2738 FILE_OP_ERROR(src, "fopen");
2741 if (is_file_exist(dest)) {
2742 dest_bak = g_strconcat(dest, ".bak", NULL);
2743 if (rename_force(dest, dest_bak) < 0) {
2744 FILE_OP_ERROR(dest, "rename");
2751 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2752 FILE_OP_ERROR(dest, "fopen");
2755 if (rename_force(dest_bak, dest) < 0)
2756 FILE_OP_ERROR(dest_bak, "rename");
2762 if (change_file_mode_rw(dest_fp, dest) < 0) {
2763 FILE_OP_ERROR(dest, "chmod");
2764 g_warning("can't change file mode\n");
2767 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2768 if (n_read < sizeof(buf) && ferror(src_fp))
2770 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2771 g_warning("writing to %s failed.\n", dest);
2776 if (rename_force(dest_bak, dest) < 0)
2777 FILE_OP_ERROR(dest_bak, "rename");
2784 if (ferror(src_fp)) {
2785 FILE_OP_ERROR(src, "fread");
2789 if (fclose(dest_fp) == EOF) {
2790 FILE_OP_ERROR(dest, "fclose");
2797 if (rename_force(dest_bak, dest) < 0)
2798 FILE_OP_ERROR(dest_bak, "rename");
2804 if (keep_backup == FALSE && dest_bak)
2812 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2814 if (overwrite == FALSE && is_file_exist(dest)) {
2815 g_warning("move_file(): file %s already exists.", dest);
2819 if (rename_force(src, dest) == 0) return 0;
2821 if (EXDEV != errno) {
2822 FILE_OP_ERROR(src, "rename");
2826 if (copy_file(src, dest, FALSE) < 0) return -1;
2833 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2836 gint bytes_left, to_read;
2839 if (fseek(fp, offset, SEEK_SET) < 0) {
2844 bytes_left = length;
2845 to_read = MIN(bytes_left, sizeof(buf));
2847 while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2848 if (n_read < to_read && ferror(fp))
2850 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2853 bytes_left -= n_read;
2854 if (bytes_left == 0)
2856 to_read = MIN(bytes_left, sizeof(buf));
2867 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2870 gboolean err = FALSE;
2872 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2873 FILE_OP_ERROR(dest, "fopen");
2877 if (change_file_mode_rw(dest_fp, dest) < 0) {
2878 FILE_OP_ERROR(dest, "chmod");
2879 g_warning("can't change file mode\n");
2882 if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2885 if (!err && fclose(dest_fp) == EOF) {
2886 FILE_OP_ERROR(dest, "fclose");
2891 g_warning("writing to %s failed.\n", dest);
2899 /* convert line endings into CRLF. If the last line doesn't end with
2900 * linebreak, add it.
2902 gchar *canonicalize_str(const gchar *str)
2908 for (p = str; *p != '\0'; ++p) {
2915 if (p == str || *(p - 1) != '\n')
2918 out = outp = g_malloc(new_len + 1);
2919 for (p = str; *p != '\0'; ++p) {
2926 if (p == str || *(p - 1) != '\n') {
2935 gint canonicalize_file(const gchar *src, const gchar *dest)
2937 FILE *src_fp, *dest_fp;
2938 gchar buf[BUFFSIZE];
2940 gboolean err = FALSE;
2941 gboolean last_linebreak = FALSE;
2943 if ((src_fp = g_fopen(src, "rb")) == NULL) {
2944 FILE_OP_ERROR(src, "fopen");
2948 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2949 FILE_OP_ERROR(dest, "fopen");
2954 if (change_file_mode_rw(dest_fp, dest) < 0) {
2955 FILE_OP_ERROR(dest, "chmod");
2956 g_warning("can't change file mode\n");
2959 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2963 if (len == 0) break;
2964 last_linebreak = FALSE;
2966 if (buf[len - 1] != '\n') {
2967 last_linebreak = TRUE;
2968 r = fputs(buf, dest_fp);
2969 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2970 r = fputs(buf, dest_fp);
2973 r = fwrite(buf, 1, len - 1, dest_fp);
2978 r = fputs("\r\n", dest_fp);
2982 g_warning("writing to %s failed.\n", dest);
2990 if (last_linebreak == TRUE) {
2991 if (fputs("\r\n", dest_fp) == EOF)
2995 if (ferror(src_fp)) {
2996 FILE_OP_ERROR(src, "fgets");
3000 if (fclose(dest_fp) == EOF) {
3001 FILE_OP_ERROR(dest, "fclose");
3013 gint canonicalize_file_replace(const gchar *file)
3017 tmp_file = get_tmp_file();
3019 if (canonicalize_file(file, tmp_file) < 0) {
3024 if (move_file(tmp_file, file, TRUE) < 0) {
3025 g_warning("can't replace %s .\n", file);
3035 gint uncanonicalize_file(const gchar *src, const gchar *dest)
3037 FILE *src_fp, *dest_fp;
3038 gchar buf[BUFFSIZE];
3039 gboolean err = FALSE;
3041 if ((src_fp = g_fopen(src, "rb")) == NULL) {
3042 FILE_OP_ERROR(src, "fopen");
3046 if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3047 FILE_OP_ERROR(dest, "fopen");
3052 if (change_file_mode_rw(dest_fp, dest) < 0) {
3053 FILE_OP_ERROR(dest, "chmod");
3054 g_warning("can't change file mode\n");
3057 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3059 if (fputs(buf, dest_fp) == EOF) {
3060 g_warning("writing to %s failed.\n", dest);
3068 if (ferror(src_fp)) {
3069 FILE_OP_ERROR(src, "fgets");
3073 if (fclose(dest_fp) == EOF) {
3074 FILE_OP_ERROR(dest, "fclose");
3086 gint uncanonicalize_file_replace(const gchar *file)
3090 tmp_file = get_tmp_file();
3092 if (uncanonicalize_file(file, tmp_file) < 0) {
3097 if (move_file(tmp_file, file, TRUE) < 0) {
3098 g_warning("can't replace %s .\n", file);
3108 gchar *normalize_newlines(const gchar *str)
3110 const gchar *p = str;
3113 out = outp = g_malloc(strlen(str) + 1);
3114 for (p = str; *p != '\0'; ++p) {
3116 if (*(p + 1) != '\n')
3127 gchar *get_outgoing_rfc2822_str(FILE *fp)
3129 gchar buf[BUFFSIZE];
3133 str = g_string_new(NULL);
3135 /* output header part */
3136 while (fgets(buf, sizeof(buf), fp) != NULL) {
3138 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3145 else if (next != ' ' && next != '\t') {
3149 if (fgets(buf, sizeof(buf), fp) == NULL)
3153 g_string_append(str, buf);
3154 g_string_append(str, "\r\n");
3160 /* output body part */
3161 while (fgets(buf, sizeof(buf), fp) != NULL) {
3164 g_string_append_c(str, '.');
3165 g_string_append(str, buf);
3166 g_string_append(str, "\r\n");
3170 g_string_free(str, FALSE);
3176 * Create a new boundary in a way that it is very unlikely that this
3177 * will occur in the following text. It would be easy to ensure
3178 * uniqueness if everything is either quoted-printable or base64
3179 * encoded (note that conversion is allowed), but because MIME bodies
3180 * may be nested, it may happen that the same boundary has already
3183 * boundary := 0*69<bchars> bcharsnospace
3184 * bchars := bcharsnospace / " "
3185 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3186 * "+" / "_" / "," / "-" / "." /
3187 * "/" / ":" / "=" / "?"
3189 * some special characters removed because of buggy MTAs
3192 gchar *generate_mime_boundary(const gchar *prefix)
3194 static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3195 "abcdefghijklmnopqrstuvwxyz"
3200 for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3201 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3204 return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
3208 gint change_file_mode_rw(FILE *fp, const gchar *file)
3211 return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3213 return g_chmod(file, S_IRUSR|S_IWUSR);
3217 FILE *my_tmpfile(void)
3219 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
3220 const gchar suffix[] = ".XXXXXX";
3221 const gchar *tmpdir;
3223 const gchar *progname;
3230 tmpdir = get_tmp_dir();
3231 tmplen = strlen(tmpdir);
3232 progname = g_get_prgname();
3233 if (progname == NULL)
3234 progname = "claws-mail";
3235 proglen = strlen(progname);
3236 Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3239 memcpy(fname, tmpdir, tmplen);
3240 fname[tmplen] = G_DIR_SEPARATOR;
3241 memcpy(fname + tmplen + 1, progname, proglen);
3242 memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3244 fd = mkstemp(fname);
3251 /* verify that we can write in the file after unlinking */
3252 if (write(fd, buf, 1) < 0) {
3259 fp = fdopen(fd, "w+b");
3267 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3272 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3276 char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3277 dir, G_DIR_SEPARATOR);
3278 fd = mkstemp_name(template, filename);
3281 *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3282 fd = mkstemp(*filename);
3284 return fdopen(fd, "w+");
3287 FILE *str_open_as_stream(const gchar *str)
3292 g_return_val_if_fail(str != NULL, NULL);
3296 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3301 if (len == 0) return fp;
3303 if (fwrite(str, 1, len, fp) != len) {
3304 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3313 gint str_write_to_file(const gchar *str, const gchar *file)
3318 g_return_val_if_fail(str != NULL, -1);
3319 g_return_val_if_fail(file != NULL, -1);
3321 if ((fp = g_fopen(file, "wb")) == NULL) {
3322 FILE_OP_ERROR(file, "fopen");
3332 if (fwrite(str, 1, len, fp) != len) {
3333 FILE_OP_ERROR(file, "fwrite");
3339 if (fclose(fp) == EOF) {
3340 FILE_OP_ERROR(file, "fclose");
3348 gchar *file_read_to_str(const gchar *file)
3353 g_return_val_if_fail(file != NULL, NULL);
3355 if ((fp = g_fopen(file, "rb")) == NULL) {
3356 FILE_OP_ERROR(file, "fopen");
3360 str = file_read_stream_to_str(fp);
3367 gchar *file_read_stream_to_str(FILE *fp)
3374 g_return_val_if_fail(fp != NULL, NULL);
3376 array = g_byte_array_new();
3378 while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3379 if (n_read < sizeof(buf) && ferror(fp))
3381 g_byte_array_append(array, buf, n_read);
3385 FILE_OP_ERROR("file stream", "fread");
3386 g_byte_array_free(array, TRUE);
3391 g_byte_array_append(array, buf, 1);
3392 str = (gchar *)array->data;
3393 g_byte_array_free(array, FALSE);
3395 if (!g_utf8_validate(str, -1, NULL)) {
3396 const gchar *src_codeset, *dest_codeset;
3398 src_codeset = conv_get_locale_charset_str();
3399 dest_codeset = CS_UTF_8;
3400 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3409 char *fgets_crlf(char *buf, int size, FILE *stream)
3411 gboolean is_cr = FALSE;
3412 gboolean last_was_cr = FALSE;
3417 while (--size > 0 && (c = getc(stream)) != EOF)
3420 is_cr = (c == '\r');
3430 last_was_cr = is_cr;
3432 if (c == EOF && cs == buf)
3440 static gint execute_async(gchar *const argv[])
3442 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3444 if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3445 NULL, NULL, NULL, FALSE) == FALSE) {
3446 g_warning("Couldn't execute command: %s\n", argv[0]);
3453 static gint execute_sync(gchar *const argv[])
3457 g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3459 if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3460 NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3461 g_warning("Couldn't execute command: %s\n", argv[0]);
3466 if (WIFEXITED(status))
3467 return WEXITSTATUS(status);
3475 gint execute_command_line(const gchar *cmdline, gboolean async)
3480 debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3482 argv = strsplit_with_quote(cmdline, " ", 0);
3485 ret = execute_async(argv);
3487 ret = execute_sync(argv);
3494 gchar *get_command_output(const gchar *cmdline)
3496 gchar *child_stdout;
3499 g_return_val_if_fail(cmdline != NULL, NULL);
3501 debug_print("get_command_output(): executing: %s\n", cmdline);
3503 if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3505 g_warning("Couldn't execute command: %s\n", cmdline);
3509 return child_stdout;
3512 static gint is_unchanged_uri_char(char c)
3523 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3529 for(i = 0; i < strlen(uri) ; i++) {
3530 if (is_unchanged_uri_char(uri[i])) {
3531 if (k + 2 >= bufsize)
3533 encoded_uri[k++] = uri[i];
3536 char * hexa = "0123456789ABCDEF";
3538 if (k + 4 >= bufsize)
3540 encoded_uri[k++] = '%';
3541 encoded_uri[k++] = hexa[uri[i] / 16];
3542 encoded_uri[k++] = hexa[uri[i] % 16];
3548 gint open_uri(const gchar *uri, const gchar *cmdline)
3551 gchar buf[BUFFSIZE];
3553 gchar encoded_uri[BUFFSIZE];
3554 g_return_val_if_fail(uri != NULL, -1);
3556 /* an option to choose whether to use encode_uri or not ? */
3557 encode_uri(encoded_uri, BUFFSIZE, uri);
3560 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3561 !strchr(p + 2, '%'))
3562 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3565 g_warning("Open URI command line is invalid "
3566 "(there must be only one '%%s'): %s",
3568 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3571 execute_command_line(buf, TRUE);
3573 extern osso_context_t *get_osso_context(void);
3574 osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3575 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
3576 DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3581 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3583 gchar buf[BUFFSIZE];
3586 g_return_val_if_fail(filepath != NULL, -1);
3589 (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3590 !strchr(p + 2, '%'))
3591 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3594 g_warning("Open Text Editor command line is invalid "
3595 "(there must be only one '%%s'): %s",
3597 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3600 execute_command_line(buf, TRUE);
3605 time_t remote_tzoffset_sec(const gchar *zone)
3607 static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3613 time_t remoteoffset;
3615 strncpy(zone3, zone, 3);
3619 if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3620 (c == '+' || c == '-')) {
3621 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3623 remoteoffset = -remoteoffset;
3624 } else if (!strncmp(zone, "UT" , 2) ||
3625 !strncmp(zone, "GMT", 2)) {
3627 } else if (strlen(zone3) == 3) {
3628 for (p = ustzstr; *p != '\0'; p += 3) {
3629 if (!g_ascii_strncasecmp(p, zone3, 3)) {
3630 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3631 remoteoffset = iustz * 3600;
3637 } else if (strlen(zone3) == 1) {
3639 case 'Z': remoteoffset = 0; break;
3640 case 'A': remoteoffset = -1; break;
3641 case 'B': remoteoffset = -2; break;
3642 case 'C': remoteoffset = -3; break;
3643 case 'D': remoteoffset = -4; break;
3644 case 'E': remoteoffset = -5; break;
3645 case 'F': remoteoffset = -6; break;
3646 case 'G': remoteoffset = -7; break;
3647 case 'H': remoteoffset = -8; break;
3648 case 'I': remoteoffset = -9; break;
3649 case 'K': remoteoffset = -10; break; /* J is not used */
3650 case 'L': remoteoffset = -11; break;
3651 case 'M': remoteoffset = -12; break;
3652 case 'N': remoteoffset = 1; break;
3653 case 'O': remoteoffset = 2; break;
3654 case 'P': remoteoffset = 3; break;
3655 case 'Q': remoteoffset = 4; break;
3656 case 'R': remoteoffset = 5; break;
3657 case 'S': remoteoffset = 6; break;
3658 case 'T': remoteoffset = 7; break;
3659 case 'U': remoteoffset = 8; break;
3660 case 'V': remoteoffset = 9; break;
3661 case 'W': remoteoffset = 10; break;
3662 case 'X': remoteoffset = 11; break;
3663 case 'Y': remoteoffset = 12; break;
3664 default: remoteoffset = 0; break;
3666 remoteoffset = remoteoffset * 3600;
3670 return remoteoffset;
3673 time_t tzoffset_sec(time_t *now)
3677 struct tm buf1, buf2;
3679 gmt = *gmtime_r(now, &buf1);
3680 lt = localtime_r(now, &buf2);
3682 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3684 if (lt->tm_year < gmt.tm_year)
3686 else if (lt->tm_year > gmt.tm_year)
3688 else if (lt->tm_yday < gmt.tm_yday)
3690 else if (lt->tm_yday > gmt.tm_yday)
3693 if (off >= 24 * 60) /* should be impossible */
3694 off = 23 * 60 + 59; /* if not, insert silly value */
3695 if (off <= -24 * 60)
3696 off = -(23 * 60 + 59);
3701 /* calculate timezone offset */
3702 gchar *tzoffset(time_t *now)
3704 static gchar offset_string[6];
3708 struct tm buf1, buf2;
3710 gmt = *gmtime_r(now, &buf1);
3711 lt = localtime_r(now, &buf2);
3713 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3715 if (lt->tm_year < gmt.tm_year)
3717 else if (lt->tm_year > gmt.tm_year)
3719 else if (lt->tm_yday < gmt.tm_yday)
3721 else if (lt->tm_yday > gmt.tm_yday)
3729 if (off >= 24 * 60) /* should be impossible */
3730 off = 23 * 60 + 59; /* if not, insert silly value */
3732 sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3734 return offset_string;
3737 void get_rfc822_date(gchar *buf, gint len)
3741 gchar day[4], mon[4];
3742 gint dd, hh, mm, ss, yyyy;
3744 gchar buf2[BUFFSIZE];
3747 lt = localtime_r(&t, &buf1);
3749 sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3750 day, mon, &dd, &hh, &mm, &ss, &yyyy);
3752 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3753 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3756 /* just a wrapper to suppress the warning of gcc about %c */
3757 size_t my_strftime(gchar *s, size_t max, const gchar *format,
3758 const struct tm *tm)
3760 return strftime(s, max, format, tm);
3763 void debug_set_mode(gboolean mode)
3768 gboolean debug_get_mode(void)
3773 void debug_print_real(const gchar *format, ...)
3776 gchar buf[BUFFSIZE];
3778 if (!debug_mode) return;
3780 va_start(args, format);
3781 g_vsnprintf(buf, sizeof(buf), format, args);
3788 const char * debug_srcname(const char *file)
3790 const char *s = strrchr (file, '/');
3795 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3797 if (subject == NULL)
3800 subject += subject_get_prefix_length(subject);
3802 return g_hash_table_lookup(subject_table, subject);
3805 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3808 if (subject == NULL || *subject == 0)
3810 subject += subject_get_prefix_length(subject);
3811 g_hash_table_insert(subject_table, subject, data);
3814 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3816 if (subject == NULL)
3819 subject += subject_get_prefix_length(subject);
3820 g_hash_table_remove(subject_table, subject);
3824 *\brief Check if a string is prefixed with known (combinations)
3825 * of prefixes. The function assumes that each prefix
3826 * is terminated by zero or exactly _one_ space.
3828 *\param str String to check for a prefixes
3830 *\return int Number of chars in the prefix that should be skipped
3831 * for a "clean" subject line. If no prefix was found, 0
3834 int subject_get_prefix_length(const gchar *subject)
3836 /*!< Array with allowable reply prefixes regexps. */
3837 static const gchar * const prefixes[] = {
3838 "Re\\:", /* "Re:" */
3839 "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
3840 "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
3841 "Aw\\:", /* "Aw:" (German) */
3842 "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
3843 "Res\\:", /* "Res:" (Brazilian Outlook) */
3844 "Fw\\:", /* "Fw:" Forward */
3845 "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
3846 "Odp\\:", /* "Odp:" Re (Polish Outlook) */
3847 "Rif\\:", /* "Rif:" (Italian Outlook) */
3848 "Sv\\:", /* "Sv" (Norwegian) */
3849 "Vs\\:", /* "Vs" (Norwegian) */
3850 "Ad\\:", /* "Ad" (Norwegian) */
3851 "\347\255\224\345\244\215\\:" /* "Re" (Chinese, UTF-8) */
3854 const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3857 static regex_t regex;
3858 static gboolean init_;
3860 if (!subject) return 0;
3861 if (!*subject) return 0;
3864 GString *s = g_string_new("");
3866 for (n = 0; n < PREFIXES; n++)
3867 /* Terminate each prefix regexpression by a
3868 * "\ ?" (zero or ONE space), and OR them */
3869 g_string_append_printf(s, "(%s\\ ?)%s",
3874 g_string_prepend(s, "(");
3875 g_string_append(s, ")+"); /* match at least once */
3876 g_string_prepend(s, "^\\ *"); /* from beginning of line */
3879 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3880 * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3881 if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
3882 debug_print("Error compiling regexp %s\n", s->str);
3883 g_string_free(s, TRUE);
3887 g_string_free(s, TRUE);
3891 if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
3897 guint g_stricase_hash(gconstpointer gptr)
3899 guint hash_result = 0;
3902 for (str = gptr; str && *str; str++) {
3903 hash_result += toupper(*str);
3909 gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3911 const char *str1 = gptr1;
3912 const char *str2 = gptr2;
3914 return !strcasecmp(str1, str2);
3917 gint g_int_compare(gconstpointer a, gconstpointer b)
3919 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3922 gchar *generate_msgid(gchar *buf, gint len)
3930 lt = localtime_r(&t, &buft);
3932 if (strcmp(buf, "") == 0) {
3933 addr = g_strconcat("@", get_domain_name(), NULL);
3936 addr = g_strconcat("@", buf, NULL);
3939 g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3940 lt->tm_year + 1900, lt->tm_mon + 1,
3941 lt->tm_mday, lt->tm_hour,
3942 lt->tm_min, lt->tm_sec,
3943 (guint) rand(), addr);
3950 quote_cmd_argument()
3952 return a quoted string safely usable in argument of a command.
3954 code is extracted and adapted from etPan! project -- DINH V. Hoà .
3957 gint quote_cmd_argument(gchar * result, guint size,
3967 for(p = path ; * p != '\0' ; p ++) {
3969 if (isalnum((guchar)*p) || (* p == '/')) {
3970 if (remaining > 0) {
3976 result[size - 1] = '\0';
3981 if (remaining >= 2) {
3989 result[size - 1] = '\0';
3994 if (remaining > 0) {
3998 result[size - 1] = '\0';
4012 static void g_node_map_recursive(GNode *node, gpointer data)
4014 GNodeMapData *mapdata = (GNodeMapData *) data;
4016 GNodeMapData newmapdata;
4019 newdata = mapdata->func(node->data, mapdata->data);
4020 if (newdata != NULL) {
4021 newnode = g_node_new(newdata);
4022 g_node_append(mapdata->parent, newnode);
4024 newmapdata.parent = newnode;
4025 newmapdata.func = mapdata->func;
4026 newmapdata.data = mapdata->data;
4028 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
4032 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
4035 GNodeMapData mapdata;
4037 g_return_val_if_fail(node != NULL, NULL);
4038 g_return_val_if_fail(func != NULL, NULL);
4040 root = g_node_new(func(node->data, data));
4042 mapdata.parent = root;
4043 mapdata.func = func;
4044 mapdata.data = data;
4046 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
4051 #define HEX_TO_INT(val, hex) \
4055 if ('0' <= c && c <= '9') { \
4057 } else if ('a' <= c && c <= 'f') { \
4058 val = c - 'a' + 10; \
4059 } else if ('A' <= c && c <= 'F') { \
4060 val = c - 'A' + 10; \
4066 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
4073 if (hi == -1 || lo == -1)
4076 *out = (hi << 4) + lo;
4080 #define INT_TO_HEX(hex, val) \
4083 hex = '0' + (val); \
4085 hex = 'A' + (val) - 10; \
4088 void get_hex_str(gchar *out, guchar ch)
4092 INT_TO_HEX(hex, ch >> 4);
4094 INT_TO_HEX(hex, ch & 0x0f);
4100 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
4102 #define G_PRINT_REF g_print
4106 *\brief Register ref counted pointer. It is based on GBoxed, so should
4107 * work with anything that uses the GType system. The semantics
4108 * are similar to a C++ auto pointer, with the exception that
4109 * C doesn't have automatic closure (calling destructors) when
4110 * exiting a block scope.
4111 * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
4112 * function directly.
4114 *\return GType A GType type.
4116 GType g_auto_pointer_register(void)
4118 static GType auto_pointer_type;
4119 if (!auto_pointer_type)
4121 g_boxed_type_register_static
4122 ("G_TYPE_AUTO_POINTER",
4123 (GBoxedCopyFunc) g_auto_pointer_copy,
4124 (GBoxedFreeFunc) g_auto_pointer_free);
4125 return auto_pointer_type;
4129 *\brief Structure with g_new() allocated pointer guarded by the
4132 typedef struct AutoPointerRef {
4133 void (*free) (gpointer);
4139 *\brief The auto pointer opaque structure that references the
4140 * pointer guard block.
4142 typedef struct AutoPointer {
4143 AutoPointerRef *ref;
4144 gpointer ptr; /*!< access to protected pointer */
4148 *\brief Creates an auto pointer for a g_new()ed pointer. Example:
4152 * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
4153 * ... when assigning, copying and freeing storage elements
4155 * gtk_list_store_new(N_S_COLUMNS,
4156 * G_TYPE_AUTO_POINTER,
4160 * Template *precious_data = g_new0(Template, 1);
4161 * g_pointer protect = g_auto_pointer_new(precious_data);
4163 * gtk_list_store_set(container, &iter,
4167 * ... the gtk_list_store has copied the pointer and
4168 * ... incremented its reference count, we should free
4169 * ... the auto pointer (in C++ a destructor would do
4170 * ... this for us when leaving block scope)
4172 * g_auto_pointer_free(protect);
4174 * ... gtk_list_store_set() now manages the data. When
4175 * ... *explicitly* requesting a pointer from the list
4176 * ... store, don't forget you get a copy that should be
4177 * ... freed with g_auto_pointer_free() eventually.
4181 *\param pointer Pointer to be guarded.
4183 *\return GAuto * Pointer that should be used in containers with
4186 GAuto *g_auto_pointer_new(gpointer p)
4188 AutoPointerRef *ref;
4194 ref = g_new0(AutoPointerRef, 1);
4195 ptr = g_new0(AutoPointer, 1);
4205 G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4211 *\brief Allocate an autopointer using the passed \a free function to
4212 * free the guarded pointer
4214 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4221 aptr = g_auto_pointer_new(p);
4222 aptr->ref->free = free_;
4226 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4228 if (auto_ptr == NULL)
4230 return ((AutoPointer *) auto_ptr)->ptr;
4234 *\brief Copies an auto pointer by. It's mostly not necessary
4235 * to call this function directly, unless you copy/assign
4236 * the guarded pointer.
4238 *\param auto_ptr Auto pointer returned by previous call to
4239 * g_auto_pointer_new_XXX()
4241 *\return gpointer An auto pointer
4243 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4246 AutoPointerRef *ref;
4249 if (auto_ptr == NULL)
4254 newp = g_new0(AutoPointer, 1);
4257 newp->ptr = ref->pointer;
4261 G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4267 *\brief Free an auto pointer
4269 void g_auto_pointer_free(GAuto *auto_ptr)
4272 AutoPointerRef *ref;
4274 if (auto_ptr == NULL)
4280 if (--(ref->cnt) == 0) {
4282 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4284 ref->free(ref->pointer);
4289 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4294 void replace_returns(gchar *str)
4299 while (strstr(str, "\n")) {
4300 *strstr(str, "\n") = ' ';
4302 while (strstr(str, "\r")) {
4303 *strstr(str, "\r") = ' ';
4307 /* get_uri_part() - retrieves a URI starting from scanpos.
4308 Returns TRUE if succesful */
4309 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4310 const gchar **bp, const gchar **ep, gboolean hdr)
4313 gint parenthese_cnt = 0;
4315 g_return_val_if_fail(start != NULL, FALSE);
4316 g_return_val_if_fail(scanpos != NULL, FALSE);
4317 g_return_val_if_fail(bp != NULL, FALSE);
4318 g_return_val_if_fail(ep != NULL, FALSE);
4322 /* find end point of URI */
4323 for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4324 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4325 !IS_ASCII(*(const guchar *)ep_) ||
4326 strchr("[]{}<>\"", *ep_)) {
4328 } else if (strchr("(", *ep_)) {
4330 } else if (strchr(")", *ep_)) {
4331 if (parenthese_cnt > 0)
4338 /* no punctuation at end of string */
4340 /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4341 * should pass some URI type to this function and decide on that whether
4342 * to perform punctuation stripping */
4344 #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4346 for (; ep_ - 1 > scanpos + 1 &&
4347 IS_REAL_PUNCT(*(ep_ - 1));
4351 #undef IS_REAL_PUNCT
4358 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4360 while (bp && *bp && g_ascii_isspace(*bp))
4362 return g_strndup(bp, ep - bp);
4365 /* valid mail address characters */
4366 #define IS_RFC822_CHAR(ch) \
4370 !g_ascii_isspace(ch) && \
4371 !strchr("(),;<>\"", (ch)))
4373 /* alphabet and number within 7bit ASCII */
4374 #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
4375 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4377 static GHashTable *create_domain_tab(void)
4379 static const gchar *toplvl_domains [] = {
4381 "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4382 "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4383 "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4384 "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4385 "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4386 "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4387 "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4388 "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4389 "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4390 "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4391 "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4392 "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4393 "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4394 "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4395 "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4396 "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4397 "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4398 "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4399 "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4400 "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4401 "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4402 "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4403 "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4404 "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4405 "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4406 "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4409 GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4411 g_return_val_if_fail(htab, NULL);
4412 for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4413 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4417 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4419 const gint MAX_LVL_DOM_NAME_LEN = 6;
4420 gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4421 const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4424 if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4427 for (p = buf; p < m && first < last; *p++ = *first++)
4431 return g_hash_table_lookup(tab, buf) != NULL;
4434 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4435 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4436 const gchar **bp, const gchar **ep, gboolean hdr)
4438 /* more complex than the uri part because we need to scan back and forward starting from
4439 * the scan position. */
4440 gboolean result = FALSE;
4441 const gchar *bp_ = NULL;
4442 const gchar *ep_ = NULL;
4443 static GHashTable *dom_tab;
4444 const gchar *last_dot = NULL;
4445 const gchar *prelast_dot = NULL;
4446 const gchar *last_tld_char = NULL;
4448 /* the informative part of the email address (describing the name
4449 * of the email address owner) may contain quoted parts. the
4450 * closure stack stores the last encountered quotes. */
4451 gchar closure_stack[128];
4452 gchar *ptr = closure_stack;
4454 g_return_val_if_fail(start != NULL, FALSE);
4455 g_return_val_if_fail(scanpos != NULL, FALSE);
4456 g_return_val_if_fail(bp != NULL, FALSE);
4457 g_return_val_if_fail(ep != NULL, FALSE);
4460 const gchar *start_quote = NULL;
4461 const gchar *end_quote = NULL;
4463 /* go to the real start */
4464 if (start[0] == ',')
4466 if (start[0] == ';')
4468 while (start[0] == '\n' || start[0] == '\r')
4470 while (start[0] == ' ' || start[0] == '\t')
4475 /* check if there are quotes (to skip , in them) */
4476 if (*start == '"') {
4477 start_quote = start;
4479 end_quote = strstr(start, "\"");
4485 /* skip anything between quotes */
4486 if (start_quote && end_quote) {
4491 /* find end (either , or ; or end of line) */
4492 if (strstr(start, ",") && strstr(start, ";"))
4493 *ep = strstr(start,",") < strstr(start, ";")
4494 ? strstr(start, ",") : strstr(start, ";");
4495 else if (strstr(start, ","))
4496 *ep = strstr(start, ",");
4497 else if (strstr(start, ";"))
4498 *ep = strstr(start, ";");
4500 *ep = start+strlen(start);
4502 /* go back to real start */
4503 if (start_quote && end_quote) {
4504 start = start_quote;
4507 /* check there's still an @ in that, or search
4508 * further if possible */
4509 if (strstr(start, "@") && strstr(start, "@") < *ep)
4511 else if (*ep < start+strlen(start)) {
4514 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4522 dom_tab = create_domain_tab();
4523 g_return_val_if_fail(dom_tab, FALSE);
4525 /* scan start of address */
4526 for (bp_ = scanpos - 1;
4527 bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4530 /* TODO: should start with an alnum? */
4532 for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4535 if (bp_ != scanpos) {
4536 /* scan end of address */
4537 for (ep_ = scanpos + 1;
4538 *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4540 prelast_dot = last_dot;
4542 if (*(last_dot + 1) == '.') {
4543 if (prelast_dot == NULL)
4545 last_dot = prelast_dot;
4550 /* TODO: really should terminate with an alnum? */
4551 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4556 if (last_dot == NULL)
4558 if (last_dot >= ep_)
4559 last_dot = prelast_dot;
4560 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4564 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4565 if (*last_tld_char == '?')
4568 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4575 if (!result) return FALSE;
4577 if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4578 && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4579 && IS_RFC822_CHAR(*(ep_ + 3))) {
4580 /* this informative part with an @ in it is
4581 * followed by the email address */
4584 /* go to matching '>' (or next non-rfc822 char, like \n) */
4585 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4588 /* include the bracket */
4589 if (*ep_ == '>') ep_++;
4591 /* include the leading quote */
4599 /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4600 if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4603 /* see if this is <bracketed>; in this case we also scan for the informative part. */
4604 if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4607 #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4608 #define IN_STACK() (ptr > closure_stack)
4609 /* has underrun check */
4610 #define POP_STACK() if(IN_STACK()) --ptr
4611 /* has overrun check */
4612 #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4613 /* has underrun check */
4614 #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
4618 /* scan for the informative part. */
4619 for (bp_ -= 2; bp_ >= start; bp_--) {
4620 /* if closure on the stack keep scanning */
4621 if (PEEK_STACK() == *bp_) {
4625 if (*bp_ == '\'' || *bp_ == '"') {
4630 /* if nothing in the closure stack, do the special conditions
4631 * the following if..else expression simply checks whether
4632 * a token is acceptable. if not acceptable, the clause
4633 * should terminate the loop with a 'break' */
4634 if (!PEEK_STACK()) {
4636 && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4637 && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
4638 /* hyphens are allowed, but only in
4640 } else if (strchr(" \"'", *bp_)) {
4641 /* but anything not being a punctiation
4644 break; /* anything else is rejected */
4651 /* scan forward (should start with an alnum) */
4652 for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4668 #undef IS_ASCII_ALNUM
4669 #undef IS_RFC822_CHAR
4671 gchar *make_email_string(const gchar *bp, const gchar *ep)
4673 /* returns a mailto: URI; mailto: is also used to detect the
4674 * uri type later on in the button_pressed signal handler */
4678 tmp = g_strndup(bp, ep - bp);
4679 result = g_strconcat("mailto:", tmp, NULL);
4685 gchar *make_http_string(const gchar *bp, const gchar *ep)
4687 /* returns an http: URI; */
4691 while (bp && *bp && g_ascii_isspace(*bp))
4693 tmp = g_strndup(bp, ep - bp);
4694 result = g_strconcat("http://", tmp, NULL);
4700 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4702 FILE *fp = fopen(path, "rb");
4703 gchar buf[BUFFSIZE];
4704 gchar *result = NULL;
4707 while (fgets(buf, sizeof (buf), fp) != NULL) {
4708 gchar **parts = g_strsplit(buf, ";", 3);
4709 gchar *trimmed = parts[0];
4710 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4712 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4713 trimmed[strlen(trimmed)-1] = '\0';
4715 if (!strcmp(trimmed, type)) {
4716 gboolean needsterminal = FALSE;
4717 if (parts[2] && strstr(parts[2], "needsterminal")) {
4718 needsterminal = TRUE;
4720 if (parts[2] && strstr(parts[2], "test=")) {
4721 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4722 gchar *testcmd = orig_testcmd;
4723 if (strstr(testcmd,";"))
4724 *(strstr(testcmd,";")) = '\0';
4725 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4727 while (testcmd[strlen(testcmd)-1] == '\n')
4728 testcmd[strlen(testcmd)-1] = '\0';
4729 while (testcmd[strlen(testcmd)-1] == '\r')
4730 testcmd[strlen(testcmd)-1] = '\0';
4731 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4732 testcmd[strlen(testcmd)-1] = '\0';
4734 if (strstr(testcmd, "%s")) {
4735 gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4736 gint res = system(tmp);
4738 g_free(orig_testcmd);
4745 gint res = system(testcmd);
4746 g_free(orig_testcmd);
4756 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4758 while (trimmed[strlen(trimmed)-1] == '\n')
4759 trimmed[strlen(trimmed)-1] = '\0';
4760 while (trimmed[strlen(trimmed)-1] == '\r')
4761 trimmed[strlen(trimmed)-1] = '\0';
4762 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4763 trimmed[strlen(trimmed)-1] = '\0';
4764 result = g_strdup(trimmed);
4767 /* if there are no single quotes around %s, add them.
4768 * '.*%s.*' is ok, as in display 'png:%s'
4770 if (strstr(result, "%s")
4771 && !(strstr(result, "'") < strstr(result,"%s") &&
4772 strstr(strstr(result,"%s"), "'"))) {
4773 gchar *start = g_strdup(result);
4774 gchar *end = g_strdup(strstr(result, "%s")+2);
4776 *strstr(start, "%s") = '\0';
4777 tmp = g_strconcat(start,"'%s'",end, NULL);
4783 if (needsterminal) {
4784 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4795 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4797 gchar *result = NULL;
4801 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4802 result = mailcap_get_command_in_file(path, type, file_to_open);
4806 result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4810 void mailcap_update_default(const gchar *type, const gchar *command)
4812 gchar *path = NULL, *outpath = NULL;
4813 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4814 outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4815 FILE *fp = fopen(path, "rb");
4816 FILE *outfp = fopen(outpath, "wb");
4817 gchar buf[BUFFSIZE];
4818 gboolean err = FALSE;
4826 while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4827 gchar **parts = g_strsplit(buf, ";", 3);
4828 gchar *trimmed = parts[0];
4829 while (trimmed[0] == ' ')
4831 while (trimmed[strlen(trimmed)-1] == ' ')
4832 trimmed[strlen(trimmed)-1] = '\0';
4834 if (!strcmp(trimmed, type)) {
4839 if(fputs(buf, outfp) == EOF) {
4846 if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4852 if (fclose(outfp) == EOF)
4856 g_rename(outpath, path);
4862 gint copy_dir(const gchar *src, const gchar *dst)
4867 if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4868 g_warning("failed to open directory: %s\n", src);
4872 if (make_dir(dst) < 0)
4875 while ((name = g_dir_read_name(dir)) != NULL) {
4876 gchar *old_file, *new_file;
4877 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4878 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4879 debug_print("copying: %s -> %s\n", old_file, new_file);
4880 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4881 gint r = copy_file(old_file, new_file, TRUE);
4888 /* Windows has no symlinks. Or well, Vista seems to
4889 have something like this but the semantics might be
4890 different. Thus we don't use it under Windows. */
4891 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4894 gchar *target = g_file_read_link(old_file, &error);
4896 r = symlink(target, new_file);
4903 #endif /*G_OS_WIN32*/
4904 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4905 gint r = copy_dir(old_file, new_file);
4916 /* crude test to see if a file is an email. */
4917 gboolean file_is_email (const gchar *filename)
4923 if (filename == NULL)
4925 if ((fp = g_fopen(filename, "rb")) == NULL)
4927 while (i < 60 && score < 3
4928 && fgets(buffer, sizeof (buffer), fp) > 0) {
4929 if (!strncmp(buffer, "From:", strlen("From:")))
4931 if (!strncmp(buffer, "To:", strlen("To:")))
4933 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4938 return (score >= 3);
4941 gboolean sc_g_list_bigger(GList *list, gint max)
4945 while (cur && i <= max+1) {
4952 gboolean sc_g_slist_bigger(GSList *list, gint max)
4956 while (cur && i <= max+1) {
4963 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4964 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4965 NULL, NULL, NULL, NULL, NULL, NULL};
4966 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4967 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
4968 NULL, NULL, NULL, NULL, NULL, NULL};
4969 const gchar *s_am_up = NULL;
4970 const gchar *s_pm_up = NULL;
4971 const gchar *s_am_low = NULL;
4972 const gchar *s_pm_low = NULL;
4973 const gchar *def_loc_format = NULL;
4974 const gchar *date_loc_format = NULL;
4975 const gchar *time_loc_format = NULL;
4976 const gchar *time_am_pm = NULL;
4978 static gboolean time_names_init_done = FALSE;
4980 static void init_time_names(void)
4982 daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4983 daynames[1] = Q_("Complete day name for use by strftime|Monday");
4984 daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4985 daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4986 daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4987 daynames[5] = Q_("Complete day name for use by strftime|Friday");
4988 daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4990 monthnames[0] = Q_("Complete month name for use by strftime|January");
4991 monthnames[1] = Q_("Complete month name for use by strftime|February");
4992 monthnames[2] = Q_("Complete month name for use by strftime|March");
4993 monthnames[3] = Q_("Complete month name for use by strftime|April");
4994 monthnames[4] = Q_("Complete month name for use by strftime|May");
4995 monthnames[5] = Q_("Complete month name for use by strftime|June");
4996 monthnames[6] = Q_("Complete month name for use by strftime|July");
4997 monthnames[7] = Q_("Complete month name for use by strftime|August");
4998 monthnames[8] = Q_("Complete month name for use by strftime|September");
4999 monthnames[9] = Q_("Complete month name for use by strftime|October");
5000 monthnames[10] = Q_("Complete month name for use by strftime|November");
5001 monthnames[11] = Q_("Complete month name for use by strftime|December");
5003 s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
5004 s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
5005 s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
5006 s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
5007 s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
5008 s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
5009 s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
5011 s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
5012 s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
5013 s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
5014 s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
5015 s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
5016 s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
5017 s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
5018 s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
5019 s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
5020 s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
5021 s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
5022 s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
5024 s_am_up = Q_("For use by strftime (morning)|AM");
5025 s_pm_up = Q_("For use by strftime (afternoon)|PM");
5026 s_am_low = Q_("For use by strftime (morning, lowercase)|am");
5027 s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
5029 def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
5030 date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
5031 time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
5033 time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
5035 time_names_init_done = TRUE;
5038 #define CHECK_SIZE() { \
5039 total_done += len; \
5040 if (total_done >= buflen) { \
5041 buf[buflen-1] = '\0'; \
5046 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
5048 gchar *curpos = buf;
5049 gint total_done = 0;
5050 gchar subbuf[64], subfmt[64];
5051 static time_t last_tzset = (time_t)0;
5053 if (!time_names_init_done)
5056 if (format == NULL || lt == NULL)
5059 if (last_tzset != time(NULL)) {
5061 last_tzset = time(NULL);
5064 if (*format == '%') {
5065 gint len = 0, tmp = 0;
5069 len = 1; CHECK_SIZE();
5073 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
5074 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
5077 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
5078 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
5082 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
5083 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
5086 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
5087 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
5090 fast_strftime(subbuf, 64, def_loc_format, lt);
5091 len = strlen(subbuf); CHECK_SIZE();
5092 strncpy2(curpos, subbuf, buflen - total_done);
5095 total_done += 2; CHECK_SIZE();
5096 tmp = (lt->tm_year + 1900)/100;
5097 *curpos++ = '0'+(tmp / 10);
5098 *curpos++ = '0'+(tmp % 10);
5101 total_done += 2; CHECK_SIZE();
5102 *curpos++ = '0'+(lt->tm_mday / 10);
5103 *curpos++ = '0'+(lt->tm_mday % 10);
5106 total_done += 8; CHECK_SIZE();
5107 *curpos++ = '0'+((lt->tm_mon+1) / 10);
5108 *curpos++ = '0'+((lt->tm_mon+1) % 10);
5110 *curpos++ = '0'+(lt->tm_mday / 10);
5111 *curpos++ = '0'+(lt->tm_mday % 10);
5113 tmp = lt->tm_year%100;
5114 *curpos++ = '0'+(tmp / 10);
5115 *curpos++ = '0'+(tmp % 10);
5118 len = 2; CHECK_SIZE();
5119 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
5122 len = 10; CHECK_SIZE();
5123 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
5124 lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
5127 total_done += 2; CHECK_SIZE();
5128 *curpos++ = '0'+(lt->tm_hour / 10);
5129 *curpos++ = '0'+(lt->tm_hour % 10);
5132 total_done += 2; CHECK_SIZE();
5138 *curpos++ = '0'+(tmp / 10);
5139 *curpos++ = '0'+(tmp % 10);
5142 len = 3; CHECK_SIZE();
5143 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5146 len = 2; CHECK_SIZE();
5147 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5150 len = 2; CHECK_SIZE();
5156 snprintf(curpos, buflen - total_done, "%2d", tmp);
5159 total_done += 2; CHECK_SIZE();
5160 tmp = lt->tm_mon + 1;
5161 *curpos++ = '0'+(tmp / 10);
5162 *curpos++ = '0'+(tmp % 10);
5165 total_done += 2; CHECK_SIZE();
5166 *curpos++ = '0'+(lt->tm_min / 10);
5167 *curpos++ = '0'+(lt->tm_min % 10);
5170 len = 1; CHECK_SIZE();
5174 if (lt->tm_hour >= 12) {
5175 len = strlen(s_pm_up); CHECK_SIZE();
5176 snprintf(curpos, buflen-total_done, s_pm_up);
5178 len = strlen(s_am_up); CHECK_SIZE();
5179 snprintf(curpos, buflen-total_done, s_am_up);
5183 if (lt->tm_hour >= 12) {
5184 len = strlen(s_pm_low); CHECK_SIZE();
5185 snprintf(curpos, buflen-total_done, s_pm_low);
5187 len = strlen(s_am_low); CHECK_SIZE();
5188 snprintf(curpos, buflen-total_done, s_am_low);
5192 fast_strftime(subbuf, 64, time_am_pm, lt);
5193 len = strlen(subbuf); CHECK_SIZE();
5194 strncpy2(curpos, subbuf, buflen - total_done);
5197 total_done += 5; CHECK_SIZE();
5198 *curpos++ = '0'+(lt->tm_hour / 10);
5199 *curpos++ = '0'+(lt->tm_hour % 10);
5201 *curpos++ = '0'+(lt->tm_min / 10);
5202 *curpos++ = '0'+(lt->tm_min % 10);
5205 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
5206 len = strlen(subbuf); CHECK_SIZE();
5207 strncpy2(curpos, subbuf, buflen - total_done);
5210 total_done += 2; CHECK_SIZE();
5211 *curpos++ = '0'+(lt->tm_sec / 10);
5212 *curpos++ = '0'+(lt->tm_sec % 10);
5215 len = 1; CHECK_SIZE();
5219 total_done += 8; CHECK_SIZE();
5220 *curpos++ = '0'+(lt->tm_hour / 10);
5221 *curpos++ = '0'+(lt->tm_hour % 10);
5223 *curpos++ = '0'+(lt->tm_min / 10);
5224 *curpos++ = '0'+(lt->tm_min % 10);
5226 *curpos++ = '0'+(lt->tm_sec / 10);
5227 *curpos++ = '0'+(lt->tm_sec % 10);
5230 len = 1; CHECK_SIZE();
5231 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5234 len = 1; CHECK_SIZE();
5235 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5238 fast_strftime(subbuf, 64, date_loc_format, lt);
5239 len = strlen(subbuf); CHECK_SIZE();
5240 strncpy2(curpos, subbuf, buflen - total_done);
5243 fast_strftime(subbuf, 64, time_loc_format, lt);
5244 len = strlen(subbuf); CHECK_SIZE();
5245 strncpy2(curpos, subbuf, buflen - total_done);
5248 total_done += 2; CHECK_SIZE();
5249 tmp = lt->tm_year%100;
5250 *curpos++ = '0'+(tmp / 10);
5251 *curpos++ = '0'+(tmp % 10);
5254 len = 4; CHECK_SIZE();
5255 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5265 /* let these complicated ones be done with the libc */
5266 snprintf(subfmt, 64, "%%%c", *format);
5267 strftime(subbuf, 64, subfmt, lt);
5268 len = strlen(subbuf); CHECK_SIZE();
5269 strncpy2(curpos, subbuf, buflen - total_done);
5273 /* let these complicated modifiers be done with the libc */
5274 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5275 strftime(subbuf, 64, subfmt, lt);
5276 len = strlen(subbuf); CHECK_SIZE();
5277 strncpy2(curpos, subbuf, buflen - total_done);
5281 g_warning("format error (%c)", *format);
5288 int len = 1; CHECK_SIZE();
5289 *curpos++ = *format++;