fix bug 4384, 'U+00AD (soft hyphen) changed to space in Subject'
[claws.git] / src / common / utils.c
index 0424ceb4c87b2317e281df255722bb1500dae45f..52a0eb3f561658af51f9eb7f535d50582259f7ab 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2015 Hiroyuki Yamamoto & The Claws Mail Team
+ * Copyright (C) 1999-2020 The Claws Mail Team and Hiroyuki Yamamoto
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <ctype.h>
 #include <errno.h>
 #include <sys/param.h>
+#ifdef G_OS_WIN32
+#  include <ws2tcpip.h>
+#else
+#  include <sys/socket.h>
+#endif
 
 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
 #  include <wchar.h>
 
 #include "utils.h"
 #include "socket.h"
-#include "../codeconv.h"
+#include "codeconv.h"
+#include "tlds.h"
+#include "timing.h"
+#include "file-utils.h"
 
 #define BUFFSIZE       8192
 
 static gboolean debug_mode = FALSE;
-#ifdef G_OS_WIN32
-static GSList *tempfiles=NULL;
-#endif
-
-#if !GLIB_CHECK_VERSION(2, 26, 0)
-guchar *g_base64_decode_wa(const gchar *text, gsize *out_len)
-{
-       guchar *ret;
-       gsize input_length;
-       gint state = 0;
-       guint save = 0;
-
-       input_length = strlen(text);
-
-       ret = g_malloc0((input_length / 4) * 3 + 1);
-
-       *out_len = g_base64_decode_step(text, input_length, ret, &state, &save);
-
-       return ret;
-}
-#endif
-
-/* Return true if we are running as root.  This function should beused
-   instead of getuid () == 0.  */
-gboolean superuser_p (void)
-{
-#ifdef G_OS_WIN32
-  return w32_is_administrator ();
-#else
-  return !getuid();
-#endif  
-}
 
 GSList *slist_copy_deep(GSList *list, GCopyFunc func)
 {
@@ -133,32 +109,14 @@ GSList *slist_copy_deep(GSList *list, GCopyFunc func)
 #endif
 }
 
-void list_free_strings(GList *list)
-{
-       list = g_list_first(list);
-
-       while (list != NULL) {
-               g_free(list->data);
-               list = list->next;
-       }
-}
-
-void slist_free_strings(GSList *list)
+void list_free_strings_full(GList *list)
 {
-       while (list != NULL) {
-               g_free(list->data);
-               list = list->next;
-       }
+       g_list_free_full(list, (GDestroyNotify)g_free);
 }
 
 void slist_free_strings_full(GSList *list)
 {
-#if GLIB_CHECK_VERSION(2,28,0)
        g_slist_free_full(list, (GDestroyNotify)g_free);
-#else
-       g_slist_foreach(list, (GFunc)g_free, NULL);
-       g_slist_free(list);
-#endif
 }
 
 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
@@ -190,19 +148,6 @@ guint str_case_hash(gconstpointer key)
        return h;
 }
 
-void ptr_array_free_strings(GPtrArray *array)
-{
-       gint i;
-       gchar *str;
-
-       cm_return_if_fail(array != NULL);
-
-       for (i = 0; i < array->len; i++) {
-               str = g_ptr_array_index(array, i);
-               g_free(str);
-       }
-}
-
 gint to_number(const gchar *nstr)
 {
        register const gchar *p;
@@ -248,7 +193,7 @@ gchar *itos(gint n)
 gchar *to_human_readable(goffset size)
 {
        static gchar str[14];
-       static gchar *b_format = NULL, *kb_format = NULL, 
+       static gchar *b_format = NULL, *kb_format = NULL,
                     *mb_format = NULL, *gb_format = NULL;
        register int t = 0, r = 0;
        if (b_format == NULL) {
@@ -257,7 +202,7 @@ gchar *to_human_readable(goffset size)
                mb_format = _("%d.%02dMB");
                gb_format = _("%.2fGB");
        }
-       
+
        if (size < (goffset)1024) {
                g_snprintf(str, sizeof(str), b_format, (gint)size);
                return str;
@@ -275,22 +220,6 @@ gchar *to_human_readable(goffset size)
        }
 }
 
-/* strcmp with NULL-checking */
-gint strcmp2(const gchar *s1, const gchar *s2)
-{
-       if (s1 == NULL || s2 == NULL)
-               return -1;
-       else
-               return strcmp(s1, s2);
-}
-/* strstr with NULL-checking */
-gchar *strstr2(const gchar *s1, const gchar *s2)
-{
-       if (s1 == NULL || s2 == NULL)
-               return NULL;
-       else
-               return strstr(s1, s2);
-}
 /* compare paths */
 gint path_cmp(const gchar *s1, const gchar *s2)
 {
@@ -371,50 +300,7 @@ gchar *strcrchomp(gchar *str)
        return str;
 }
 
-gint file_strip_crs(const gchar *file)
-{
-       FILE *fp = NULL, *outfp = NULL;
-       gchar buf[4096];
-       gchar *out = get_tmp_file();
-       if (file == NULL)
-               goto freeout;
-
-       fp = g_fopen(file, "rb");
-       if (!fp)
-               goto freeout;
-
-       outfp = g_fopen(out, "wb");
-       if (!outfp) {
-               fclose(fp);
-               goto freeout;
-       }
-
-       while (fgets(buf, sizeof (buf), fp) != NULL) {
-               strcrchomp(buf);
-               if (fputs(buf, outfp) == EOF) {
-                       fclose(fp);
-                       fclose(outfp);
-                       goto unlinkout;
-               }
-       }
-
-       fclose(fp);
-       if (fclose(outfp) == EOF) {
-               goto unlinkout;
-       }
-       
-       if (move_file(out, file, TRUE) < 0)
-               goto unlinkout;
-       
-       g_free(out);
-       return 0;
-unlinkout:
-       claws_unlink(out);
-freeout:
-       g_free(out);
-       return -1;
-}
-
+#ifndef HAVE_STRCASESTR
 /* Similar to `strstr' but this function ignores the case of both strings.  */
 gchar *strcasestr(const gchar *haystack, const gchar *needle)
 {
@@ -422,6 +308,7 @@ gchar *strcasestr(const gchar *haystack, const gchar *needle)
 
        return strncasestr(haystack, haystack_len, needle);
 }
+#endif /* HAVE_STRCASESTR */
 
 gchar *strncasestr(const gchar *haystack, gint haystack_len, const gchar *needle)
 {
@@ -941,7 +828,7 @@ GList *add_history(GList *list, const gchar *str)
 
        cm_return_val_if_fail(str != NULL, list);
 
-       old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
+       old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)g_strcmp0);
        if (old) {
                oldstr = old->data;
                list = g_list_remove(list, old->data);
@@ -992,19 +879,37 @@ void remove_space(gchar *str)
 
 void unfold_line(gchar *str)
 {
-       register gchar *p = str;
-       register gint spc;
+       register gchar *ch;
+       register gunichar c;
+       register gint len;
 
-       while (*p) {
-               if (*p == '\n' || *p == '\r') {
-                       *p++ = ' ';
-                       spc = 0;
-                       while (g_ascii_isspace(*(p + spc)))
-                               spc++;
-                       if (spc)
-                               memmove(p, p + spc, strlen(p + spc) + 1);
-               } else
-                       p++;
+       ch = str; /* iterator for source string */
+
+       while (*ch != 0) {
+               c = g_utf8_get_char_validated(ch, -1);
+
+               if (c == (gunichar)-1 || c == (gunichar)-2) {
+                       /* non-unicode byte, move past it */
+                       ch++;
+                       continue;
+               }
+
+               len = g_unichar_to_utf8(c, NULL);
+
+               if ((!g_unichar_isdefined(c) || !g_unichar_isprint(c) ||
+                               g_unichar_isspace(c)) && c != 173) {
+                       /* replace anything bad or whitespacey with a single space */
+                       *ch = ' ';
+                       ch++;
+                       if (len > 1) {
+                               /* move rest of the string forwards, since we just replaced
+                                * a multi-byte sequence with one byte */
+                               memmove(ch, ch + len-1, strlen(ch + len-1) + 1);
+                       }
+               } else {
+                       /* A valid unicode character, copy it. */
+                       ch += len;
+               }
        }
 }
 
@@ -1035,7 +940,7 @@ void subst_for_filename(gchar *str)
        if (!str)
                return;
 #ifdef G_OS_WIN32
-       subst_chars(str, "\t\r\n\\/*:", '_');
+       subst_chars(str, "\t\r\n\\/*?:", '_');
 #else
        subst_chars(str, "\t\r\n\\/*", '_');
 #endif
@@ -1070,13 +975,13 @@ static const gchar * line_has_quote_char_last(const gchar * str, const gchar *qu
        gchar * tmp_pos = NULL;
        int i;
 
-       if (quote_chars == NULL)
+       if (str == NULL || quote_chars == NULL)
                return NULL;
 
        for (i = 0; i < strlen(quote_chars); i++) {
-               tmp_pos = strrchr (str, quote_chars[i]);
+               tmp_pos = strrchr (str, quote_chars[i]);
                if(position == NULL
-                  || (tmp_pos != NULL && position <= tmp_pos) )
+                               || (tmp_pos != NULL && position <= tmp_pos) )
                        position = tmp_pos;
        }
        return position;
@@ -1159,13 +1064,13 @@ const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
        gchar * tmp_pos = NULL;
        int i;
 
-       if (quote_chars == NULL)
-               return FALSE;
+       if (str == NULL || quote_chars == NULL)
+               return NULL;
 
        for (i = 0; i < strlen(quote_chars); i++) {
-               tmp_pos = strchr (str,  quote_chars[i]);
+               tmp_pos = strchr (str, quote_chars[i]);
                if(position == NULL
-                  || (tmp_pos != NULL && position >= tmp_pos) )
+                               || (tmp_pos != NULL && position >= tmp_pos) )
                        position = tmp_pos;
        }
        return position;
@@ -1355,7 +1260,7 @@ GList *uri_list_extract_filenames(const gchar *uri_list)
                                        *file = '\0';
                                        strncpy(escaped_utf8uri, p, q - p + 1);
                                        escaped_utf8uri[q - p + 1] = '\0';
-                                       decode_uri(file, escaped_utf8uri);
+                                       decode_uri_with_plus(file, escaped_utf8uri, FALSE);
                    /*
                     * g_filename_from_uri() rejects escaped/locale encoded uri
                     * string which come from Nautilus.
@@ -1508,7 +1413,8 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                                          "../",
                                          NULL };
        gint num_attach = 0;
-       gchar **my_att = NULL;
+
+       cm_return_val_if_fail(mailto != NULL, -1);
 
        Xstrdup_a(tmp_mailto, mailto, return -1);
 
@@ -1524,9 +1430,6 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
        if (to && !*to)
                *to = decode_uri_gdup(tmp_mailto);
 
-       my_att = g_malloc(sizeof(char *));
-       my_att[0] = NULL;
-
        while (p) {
                gchar *field, *value;
 
@@ -1553,6 +1456,7 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                        } else {
                                gchar *tmp = decode_uri_gdup(value);
                                gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
+                               g_free(tmp);
                                g_free(*from);
                                *from = new_from;
                        }
@@ -1562,6 +1466,7 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                        } else {
                                gchar *tmp = decode_uri_gdup(value);
                                gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
+                               g_free(tmp);
                                g_free(*cc);
                                *cc = new_cc;
                        }
@@ -1571,6 +1476,7 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                        } else {
                                gchar *tmp = decode_uri_gdup(value);
                                gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
+                               g_free(tmp);
                                g_free(*bcc);
                                *bcc = new_bcc;
                        }
@@ -1580,20 +1486,41 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
                        *body = decode_uri_gdup(value);
                } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
+                       int i = 0;
                        gchar *tmp = decode_uri_gdup(value);
-                       if (!g_file_get_contents(tmp, body, NULL, NULL)) {
-                               g_warning("couldn't set insert file '%s' in body", value);
+
+                       for (; forbidden_uris[i]; i++) {
+                               if (strstr(tmp, forbidden_uris[i])) {
+                                       g_print("Refusing to insert '%s', potential private data leak\n",
+                                                       tmp);
+                                       g_free(tmp);
+                                       tmp = NULL;
+                                       break;
+                               }
+                       }
+
+                       if (tmp) {
+                               if (!is_file_entry_regular(tmp)) {
+                                       g_warning("Refusing to insert '%s', not a regular file\n", tmp);
+                               } else if (!g_file_get_contents(tmp, body, NULL, NULL)) {
+                                       g_warning("couldn't set insert file '%s' in body", value);
+                               }
+
+                               g_free(tmp);
                        }
-                       g_free(tmp);
-                       tmp = NULL;
                } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
                        int i = 0;
                        gchar *tmp = decode_uri_gdup(value);
+                       gchar **my_att = g_malloc(sizeof(char *));
+
+                       my_att[0] = NULL;
+
                        for (; forbidden_uris[i]; i++) {
                                if (strstr(tmp, forbidden_uris[i])) {
                                        g_print("Refusing to attach '%s', potential private data leak\n",
                                                        tmp);
                                        g_free(tmp);
+                                       g_free(my_att);
                                        tmp = NULL;
                                        break;
                                }
@@ -1604,6 +1531,7 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                                my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
                                my_att[num_attach-1] = tmp;
                                my_att[num_attach] = NULL;
+                               *attach = my_att;
                        }
                } else if (inreplyto && !*inreplyto &&
                           !g_ascii_strcasecmp(field, "in-reply-to")) {
@@ -1611,8 +1539,6 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                }
        }
 
-       if (attach)
-               *attach = my_att;
        return 0;
 }
 
@@ -1756,7 +1682,7 @@ const gchar *get_locale_dir(void)
 #endif
        if (!loc_dir)
                loc_dir = LOCALEDIR;
-       
+
        return loc_dir;
 }
 
@@ -1770,8 +1696,8 @@ const gchar *get_home_dir(void)
                if (w32_shgetfolderpath
                            (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
                             NULL, 0, home_dir_utf16) < 0)
-                               strcpy (home_dir_utf16, "C:\\Sylpheed");
-               home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
+                               strcpy (home_dir_utf16, "C:\\Claws Mail");
+               home_dir_utf8 = g_utf16_to_utf8 ((const gunichar2 *)home_dir_utf16, -1, NULL, NULL, NULL);
        }
        return home_dir_utf8;
 #else
@@ -1819,11 +1745,11 @@ void set_rc_dir(const gchar *dir)
                rc_dir_alt = TRUE;
 
                claws_rc_dir = canonical_dir;
-               
+
                len = strlen(claws_rc_dir);
                if (claws_rc_dir[len - 1] == G_DIR_SEPARATOR)
                        claws_rc_dir[len - 1] = '\0';
-               
+
                debug_print("set rc_dir to %s\n", claws_rc_dir);
                if (!is_dir_exist(claws_rc_dir)) {
                        if (make_dir_hier(claws_rc_dir) != 0) {
@@ -1888,14 +1814,14 @@ const gchar *get_template_dir(void)
 }
 
 #ifdef G_OS_WIN32
-const gchar *get_cert_file(void)
+const gchar *w32_get_cert_file(void)
 {
        const gchar *cert_file = NULL;
        if (!cert_file)
                cert_file = g_strconcat(w32_get_module_dir(),
                                 "\\share\\claws-mail\\",
                                "ca-certificates.crt",
-                               NULL);  
+                               NULL);
        return cert_file;
 }
 #endif
@@ -1927,10 +1853,10 @@ const gchar *get_plugin_dir(void)
        else {
                static gchar *plugin_dir = NULL;
                if (!plugin_dir)
-                       plugin_dir = g_strconcat(get_rc_dir(), 
-                               G_DIR_SEPARATOR_S, "plugins", 
+                       plugin_dir = g_strconcat(get_rc_dir(),
+                               G_DIR_SEPARATOR_S, "plugins",
                                G_DIR_SEPARATOR_S, NULL);
-               return plugin_dir;                      
+               return plugin_dir;
        }
 #endif
 }
@@ -1938,7 +1864,7 @@ const gchar *get_plugin_dir(void)
 
 #ifdef G_OS_WIN32
 /* Return the default directory for Themes. */
-const gchar *get_themes_dir(void)
+const gchar *w32_get_themes_dir(void)
 {
        static gchar *themes_dir = NULL;
 
@@ -2009,8 +1935,52 @@ const gchar *get_domain_name(void)
 #endif
 }
 
+/* Tells whether the given host address string is a valid representation of a
+ * numerical IP (v4 or, if supported, v6) address.
+ */
+gboolean is_numeric_host_address(const gchar *hostaddress)
+{
+       struct addrinfo hints, *res;
+       int err;
+
+       /* See what getaddrinfo makes of the string when told that it is a
+        * numeric IP address representation. */
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = 0;
+       hints.ai_flags = AI_NUMERICHOST;
+       hints.ai_protocol = 0;
+
+       err = getaddrinfo(hostaddress, NULL, &hints, &res);
+       if (err == 0)
+               freeaddrinfo(res);
+
+       return (err == 0);
+}
+
 off_t get_file_size(const gchar *file)
 {
+#ifdef G_OS_WIN32
+       GFile *f;
+       GFileInfo *fi;
+       GError *error = NULL;
+       goffset size;
+
+       f = g_file_new_for_path(file);
+       fi = g_file_query_info(f, "standard::size",
+                       G_FILE_QUERY_INFO_NONE, NULL, &error);
+       if (error != NULL) {
+               debug_print("get_file_size error: %s\n", error->message);
+               g_error_free(error);
+               g_object_unref(f);
+               return -1;
+       }
+       size = g_file_info_get_size(fi);
+       g_object_unref(fi);
+       g_object_unref(f);
+       return size;
+
+#else
        GStatBuf s;
 
        if (g_stat(file, &s) < 0) {
@@ -2019,6 +1989,7 @@ off_t get_file_size(const gchar *file)
        }
 
        return s.st_size;
+#endif
 }
 
 time_t get_file_mtime(const gchar *file)
@@ -2033,32 +2004,6 @@ time_t get_file_mtime(const gchar *file)
        return s.st_mtime;
 }
 
-off_t get_file_size_as_crlf(const gchar *file)
-{
-       FILE *fp;
-       off_t size = 0;
-       gchar buf[BUFFSIZE];
-
-       if ((fp = g_fopen(file, "rb")) == NULL) {
-               FILE_OP_ERROR(file, "g_fopen");
-               return -1;
-       }
-
-       while (fgets(buf, sizeof(buf), fp) != NULL) {
-               strretchomp(buf);
-               size += strlen(buf) + 2;
-       }
-
-       if (ferror(fp)) {
-               FILE_OP_ERROR(file, "fgets");
-               size = -1;
-       }
-
-       fclose(fp);
-
-       return size;
-}
-
 gboolean file_exist(const gchar *file, gboolean allow_fifo)
 {
        GStatBuf s;
@@ -2117,6 +2062,14 @@ gboolean is_file_entry_exist(const gchar *file)
        return g_file_test(file, G_FILE_TEST_EXISTS);
 }
 
+gboolean is_file_entry_regular(const gchar *file)
+{
+       if (file == NULL)
+               return FALSE;
+
+       return g_file_test(file, G_FILE_TEST_IS_REGULAR);
+}
+
 gboolean dirent_is_regular_file(struct dirent *d)
 {
 #if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE)
@@ -2194,38 +2147,23 @@ gint make_dir_hier(const gchar *dir)
 gint remove_all_files(const gchar *dir)
 {
        GDir *dp;
-       const gchar *dir_name;
-       gchar *prev_dir;
-
-       prev_dir = g_get_current_dir();
-
-       if (g_chdir(dir) < 0) {
-               FILE_OP_ERROR(dir, "chdir");
-               g_free(prev_dir);
-               return -1;
-       }
+       const gchar *file_name;
+       gchar *tmp;
 
-       if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
+       if ((dp = g_dir_open(dir, 0, NULL)) == NULL) {
                g_warning("failed to open directory: %s", dir);
-               g_free(prev_dir);
                return -1;
        }
 
-       while ((dir_name = g_dir_read_name(dp)) != NULL) {
-               if (claws_unlink(dir_name) < 0)
-                       FILE_OP_ERROR(dir_name, "unlink");
+       while ((file_name = g_dir_read_name(dp)) != NULL) {
+               tmp = g_strconcat(dir, G_DIR_SEPARATOR_S, file_name, NULL);
+               if (claws_unlink(tmp) < 0)
+                       FILE_OP_ERROR(tmp, "unlink");
+               g_free(tmp);
        }
 
        g_dir_close(dp);
 
-       if (g_chdir(prev_dir) < 0) {
-               FILE_OP_ERROR(prev_dir, "chdir");
-               g_free(prev_dir);
-               return -1;
-       }
-
-       g_free(prev_dir);
-
        return 0;
 }
 
@@ -2452,417 +2390,71 @@ gint remove_dir_recursive(const gchar *dir)
        return 0;
 }
 
-gint rename_force(const gchar *oldpath, const gchar *newpath)
-{
-#ifndef G_OS_UNIX
-       if (!is_file_entry_exist(oldpath)) {
-               errno = ENOENT;
-               return -1;
-       }
-       if (is_file_exist(newpath)) {
-               if (claws_unlink(newpath) < 0)
-                       FILE_OP_ERROR(newpath, "unlink");
-       }
-#endif
-       return g_rename(oldpath, newpath);
-}
-
-/*
- * Append src file body to the tail of dest file.
- * Now keep_backup has no effects.
+/* convert line endings into CRLF. If the last line doesn't end with
+ * linebreak, add it.
  */
-gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
+gchar *canonicalize_str(const gchar *str)
 {
-       FILE *src_fp, *dest_fp;
-       gint n_read;
-       gchar buf[BUFSIZ];
-
-       gboolean err = FALSE;
-
-       if ((src_fp = g_fopen(src, "rb")) == NULL) {
-               FILE_OP_ERROR(src, "g_fopen");
-               return -1;
-       }
-
-       if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
-               FILE_OP_ERROR(dest, "g_fopen");
-               fclose(src_fp);
-               return -1;
-       }
-
-       if (change_file_mode_rw(dest_fp, dest) < 0) {
-               FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode: %s", dest);
-       }
+       const gchar *p;
+       guint new_len = 0;
+       gchar *out, *outp;
 
-       while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
-               if (n_read < sizeof(buf) && ferror(src_fp))
-                       break;
-               if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
-                       g_warning("writing to %s failed.", dest);
-                       fclose(dest_fp);
-                       fclose(src_fp);
-                       claws_unlink(dest);
-                       return -1;
+       for (p = str; *p != '\0'; ++p) {
+               if (*p != '\r') {
+                       ++new_len;
+                       if (*p == '\n')
+                               ++new_len;
                }
        }
+       if (p == str || *(p - 1) != '\n')
+               new_len += 2;
 
-       if (ferror(src_fp)) {
-               FILE_OP_ERROR(src, "fread");
-               err = TRUE;
-       }
-       fclose(src_fp);
-       if (fclose(dest_fp) == EOF) {
-               FILE_OP_ERROR(dest, "fclose");
-               err = TRUE;
+       out = outp = g_malloc(new_len + 1);
+       for (p = str; *p != '\0'; ++p) {
+               if (*p != '\r') {
+                       if (*p == '\n')
+                               *outp++ = '\r';
+                       *outp++ = *p;
+               }
        }
-
-       if (err) {
-               claws_unlink(dest);
-               return -1;
+       if (p == str || *(p - 1) != '\n') {
+               *outp++ = '\r';
+               *outp++ = '\n';
        }
+       *outp = '\0';
 
-       return 0;
+       return out;
 }
 
-gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
+gchar *normalize_newlines(const gchar *str)
 {
-       FILE *src_fp, *dest_fp;
-       gint n_read;
-       gchar buf[BUFSIZ];
-       gchar *dest_bak = NULL;
-       gboolean err = FALSE;
-
-       if ((src_fp = g_fopen(src, "rb")) == NULL) {
-               FILE_OP_ERROR(src, "g_fopen");
-               return -1;
-       }
-       if (is_file_exist(dest)) {
-               dest_bak = g_strconcat(dest, ".bak", NULL);
-               if (rename_force(dest, dest_bak) < 0) {
-                       FILE_OP_ERROR(dest, "rename");
-                       fclose(src_fp);
-                       g_free(dest_bak);
-                       return -1;
-               }
-       }
-
-       if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
-               FILE_OP_ERROR(dest, "g_fopen");
-               fclose(src_fp);
-               if (dest_bak) {
-                       if (rename_force(dest_bak, dest) < 0)
-                               FILE_OP_ERROR(dest_bak, "rename");
-                       g_free(dest_bak);
-               }
-               return -1;
-       }
-
-       if (change_file_mode_rw(dest_fp, dest) < 0) {
-               FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode: %s", dest);
-       }
-
-       while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
-               if (n_read < sizeof(buf) && ferror(src_fp))
-                       break;
-               if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
-                       g_warning("writing to %s failed.", dest);
-                       fclose(dest_fp);
-                       fclose(src_fp);
-                       claws_unlink(dest);
-                       if (dest_bak) {
-                               if (rename_force(dest_bak, dest) < 0)
-                                       FILE_OP_ERROR(dest_bak, "rename");
-                               g_free(dest_bak);
-                       }
-                       return -1;
-               }
-       }
-
-       if (ferror(src_fp)) {
-               FILE_OP_ERROR(src, "fread");
-               err = TRUE;
-       }
-       fclose(src_fp);
-       if (fclose(dest_fp) == EOF) {
-               FILE_OP_ERROR(dest, "fclose");
-               err = TRUE;
-       }
+       const gchar *p;
+       gchar *out, *outp;
 
-       if (err) {
-               claws_unlink(dest);
-               if (dest_bak) {
-                       if (rename_force(dest_bak, dest) < 0)
-                               FILE_OP_ERROR(dest_bak, "rename");
-                       g_free(dest_bak);
-               }
-               return -1;
+       out = outp = g_malloc(strlen(str) + 1);
+       for (p = str; *p != '\0'; ++p) {
+               if (*p == '\r') {
+                       if (*(p + 1) != '\n')
+                               *outp++ = '\n';
+               } else
+                       *outp++ = *p;
        }
 
-       if (keep_backup == FALSE && dest_bak)
-               claws_unlink(dest_bak);
-
-       g_free(dest_bak);
+       *outp = '\0';
 
-       return 0;
+       return out;
 }
 
-gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
+gchar *get_outgoing_rfc2822_str(FILE *fp)
 {
-       if (overwrite == FALSE && is_file_exist(dest)) {
-               g_warning("move_file(): file %s already exists.", dest);
-               return -1;
-       }
+       gchar buf[BUFFSIZE];
+       GString *str;
+       gchar *ret;
 
-       if (rename_force(src, dest) == 0) return 0;
-
-       if (EXDEV != errno) {
-               FILE_OP_ERROR(src, "rename");
-               return -1;
-       }
-
-       if (copy_file(src, dest, FALSE) < 0) return -1;
-
-       claws_unlink(src);
-
-       return 0;
-}
-
-gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
-{
-       gint n_read;
-       gint bytes_left, to_read;
-       gchar buf[BUFSIZ];
-
-       if (fseek(fp, offset, SEEK_SET) < 0) {
-               perror("fseek");
-               return -1;
-       }
-
-       bytes_left = length;
-       to_read = MIN(bytes_left, sizeof(buf));
-
-       while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
-               if (n_read < to_read && ferror(fp))
-                       break;
-               if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
-                       return -1;
-               }
-               bytes_left -= n_read;
-               if (bytes_left == 0)
-                       break;
-               to_read = MIN(bytes_left, sizeof(buf));
-       }
-
-       if (ferror(fp)) {
-               perror("fread");
-               return -1;
-       }
-
-       return 0;
-}
-
-gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
-{
-       FILE *dest_fp;
-       gboolean err = FALSE;
-
-       if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
-               FILE_OP_ERROR(dest, "g_fopen");
-               return -1;
-       }
-
-       if (change_file_mode_rw(dest_fp, dest) < 0) {
-               FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode: %s", dest);
-       }
-
-       if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
-               err = TRUE;
-
-       if (!err && fclose(dest_fp) == EOF) {
-               FILE_OP_ERROR(dest, "fclose");
-               err = TRUE;
-       }
-
-       if (err) {
-               g_warning("writing to %s failed.", dest);
-               claws_unlink(dest);
-               return -1;
-       }
-
-       return 0;
-}
-
-/* convert line endings into CRLF. If the last line doesn't end with
- * linebreak, add it.
- */
-gchar *canonicalize_str(const gchar *str)
-{
-       const gchar *p;
-       guint new_len = 0;
-       gchar *out, *outp;
-
-       for (p = str; *p != '\0'; ++p) {
-               if (*p != '\r') {
-                       ++new_len;
-                       if (*p == '\n')
-                               ++new_len;
-               }
-       }
-       if (p == str || *(p - 1) != '\n')
-               new_len += 2;
-
-       out = outp = g_malloc(new_len + 1);
-       for (p = str; *p != '\0'; ++p) {
-               if (*p != '\r') {
-                       if (*p == '\n')
-                               *outp++ = '\r';
-                       *outp++ = *p;
-               }
-       }
-       if (p == str || *(p - 1) != '\n') {
-               *outp++ = '\r';
-               *outp++ = '\n';
-       }
-       *outp = '\0';
-
-       return out;
-}
-
-gint canonicalize_file(const gchar *src, const gchar *dest)
-{
-       FILE *src_fp, *dest_fp;
-       gchar buf[BUFFSIZE];
-       gint len;
-       gboolean err = FALSE;
-       gboolean last_linebreak = FALSE;
-
-       if (src == NULL || dest == NULL)
-               return -1;
-
-       if ((src_fp = g_fopen(src, "rb")) == NULL) {
-               FILE_OP_ERROR(src, "g_fopen");
-               return -1;
-       }
-
-       if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
-               FILE_OP_ERROR(dest, "g_fopen");
-               fclose(src_fp);
-               return -1;
-       }
-
-       if (change_file_mode_rw(dest_fp, dest) < 0) {
-               FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode: %s", dest);
-       }
-
-       while (fgets(buf, sizeof(buf), src_fp) != NULL) {
-               gint r = 0;
-
-               len = strlen(buf);
-               if (len == 0) break;
-               last_linebreak = FALSE;
-
-               if (buf[len - 1] != '\n') {
-                       last_linebreak = TRUE;
-                       r = fputs(buf, dest_fp);
-               } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
-                       r = fputs(buf, dest_fp);
-               } else {
-                       if (len > 1) {
-                               r = fwrite(buf, 1, len - 1, dest_fp);
-                               if (r != (len -1))
-                                       r = EOF;
-                       }
-                       if (r != EOF)
-                               r = fputs("\r\n", dest_fp);
-               }
-
-               if (r == EOF) {
-                       g_warning("writing to %s failed.", dest);
-                       fclose(dest_fp);
-                       fclose(src_fp);
-                       claws_unlink(dest);
-                       return -1;
-               }
-       }
-
-       if (last_linebreak == TRUE) {
-               if (fputs("\r\n", dest_fp) == EOF)
-                       err = TRUE;
-       }
-
-       if (ferror(src_fp)) {
-               FILE_OP_ERROR(src, "fgets");
-               err = TRUE;
-       }
-       fclose(src_fp);
-       if (fclose(dest_fp) == EOF) {
-               FILE_OP_ERROR(dest, "fclose");
-               err = TRUE;
-       }
-
-       if (err) {
-               claws_unlink(dest);
-               return -1;
-       }
-
-       return 0;
-}
-
-gint canonicalize_file_replace(const gchar *file)
-{
-       gchar *tmp_file;
-
-       tmp_file = get_tmp_file();
-
-       if (canonicalize_file(file, tmp_file) < 0) {
-               g_free(tmp_file);
-               return -1;
-       }
-
-       if (move_file(tmp_file, file, TRUE) < 0) {
-               g_warning("can't replace file: %s", file);
-               claws_unlink(tmp_file);
-               g_free(tmp_file);
-               return -1;
-       }
-
-       g_free(tmp_file);
-       return 0;
-}
-
-gchar *normalize_newlines(const gchar *str)
-{
-       const gchar *p;
-       gchar *out, *outp;
-
-       out = outp = g_malloc(strlen(str) + 1);
-       for (p = str; *p != '\0'; ++p) {
-               if (*p == '\r') {
-                       if (*(p + 1) != '\n')
-                               *outp++ = '\n';
-               } else
-                       *outp++ = *p;
-       }
-
-       *outp = '\0';
-
-       return out;
-}
-
-gchar *get_outgoing_rfc2822_str(FILE *fp)
-{
-       gchar buf[BUFFSIZE];
-       GString *str;
-       gchar *ret;
-
-       str = g_string_new(NULL);
+       str = g_string_new(NULL);
 
        /* output header part */
-       while (fgets(buf, sizeof(buf), fp) != NULL) {
+       while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
                strretchomp(buf);
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
                        gint next;
@@ -2875,7 +2467,7 @@ gchar *get_outgoing_rfc2822_str(FILE *fp)
                                        ungetc(next, fp);
                                        break;
                                }
-                               if (fgets(buf, sizeof(buf), fp) == NULL)
+                               if (claws_fgets(buf, sizeof(buf), fp) == NULL)
                                        break;
                        }
                } else {
@@ -2887,7 +2479,7 @@ gchar *get_outgoing_rfc2822_str(FILE *fp)
        }
 
        /* output body part */
-       while (fgets(buf, sizeof(buf), fp) != NULL) {
+       while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
                strretchomp(buf);
                if (buf[0] == '.')
                        g_string_append_c(str, '.');
@@ -2934,280 +2526,6 @@ gchar *generate_mime_boundary(const gchar *prefix)
                               buf_uniq);
 }
 
-gint change_file_mode_rw(FILE *fp, const gchar *file)
-{
-#if HAVE_FCHMOD
-       return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
-#else
-       return g_chmod(file, S_IRUSR|S_IWUSR);
-#endif
-}
-
-FILE *my_tmpfile(void)
-{
-       const gchar suffix[] = ".XXXXXX";
-       const gchar *tmpdir;
-       guint tmplen;
-       const gchar *progname;
-       guint proglen;
-       gchar *fname;
-       gint fd;
-       FILE *fp;
-#ifndef G_OS_WIN32
-       gchar buf[2]="\0";
-#endif
-
-       tmpdir = get_tmp_dir();
-       tmplen = strlen(tmpdir);
-       progname = g_get_prgname();
-       if (progname == NULL)
-               progname = "claws-mail";
-       proglen = strlen(progname);
-       Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
-               return tmpfile());
-
-       memcpy(fname, tmpdir, tmplen);
-       fname[tmplen] = G_DIR_SEPARATOR;
-       memcpy(fname + tmplen + 1, progname, proglen);
-       memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
-
-       fd = g_mkstemp(fname);
-       if (fd < 0)
-               return tmpfile();
-
-#ifndef G_OS_WIN32
-       claws_unlink(fname);
-       
-       /* verify that we can write in the file after unlinking */
-       if (write(fd, buf, 1) < 0) {
-               close(fd);
-               return tmpfile();
-       }
-       
-#endif
-
-       fp = fdopen(fd, "w+b");
-       if (!fp)
-               close(fd);
-       else {
-               rewind(fp);
-               return fp;
-       }
-
-       return tmpfile();
-}
-
-FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
-{
-       int fd;
-       *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
-       fd = g_mkstemp(*filename);
-       if (fd < 0)
-               return NULL;
-       return fdopen(fd, "w+");
-}
-
-FILE *str_open_as_stream(const gchar *str)
-{
-       FILE *fp;
-       size_t len;
-
-       cm_return_val_if_fail(str != NULL, NULL);
-
-       fp = my_tmpfile();
-       if (!fp) {
-               FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
-               return NULL;
-       }
-
-       len = strlen(str);
-       if (len == 0) return fp;
-
-       if (fwrite(str, 1, len, fp) != len) {
-               FILE_OP_ERROR("str_open_as_stream", "fwrite");
-               fclose(fp);
-               return NULL;
-       }
-
-       rewind(fp);
-       return fp;
-}
-
-gint str_write_to_file(const gchar *str, const gchar *file)
-{
-       FILE *fp;
-       size_t len;
-
-       cm_return_val_if_fail(str != NULL, -1);
-       cm_return_val_if_fail(file != NULL, -1);
-
-       if ((fp = g_fopen(file, "wb")) == NULL) {
-               FILE_OP_ERROR(file, "g_fopen");
-               return -1;
-       }
-
-       len = strlen(str);
-       if (len == 0) {
-               fclose(fp);
-               return 0;
-       }
-
-       if (fwrite(str, 1, len, fp) != len) {
-               FILE_OP_ERROR(file, "fwrite");
-               fclose(fp);
-               claws_unlink(file);
-               return -1;
-       }
-
-       if (fclose(fp) == EOF) {
-               FILE_OP_ERROR(file, "fclose");
-               claws_unlink(file);
-               return -1;
-       }
-
-       return 0;
-}
-
-static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
-{
-       GByteArray *array;
-       guchar buf[BUFSIZ];
-       gint n_read;
-       gchar *str;
-
-       cm_return_val_if_fail(fp != NULL, NULL);
-
-       array = g_byte_array_new();
-
-       while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
-               if (n_read < sizeof(buf) && ferror(fp))
-                       break;
-               g_byte_array_append(array, buf, n_read);
-       }
-
-       if (ferror(fp)) {
-               FILE_OP_ERROR("file stream", "fread");
-               g_byte_array_free(array, TRUE);
-               return NULL;
-       }
-
-       buf[0] = '\0';
-       g_byte_array_append(array, buf, 1);
-       str = (gchar *)array->data;
-       g_byte_array_free(array, FALSE);
-
-       if (recode && !g_utf8_validate(str, -1, NULL)) {
-               const gchar *src_codeset, *dest_codeset;
-               gchar *tmp = NULL;
-               src_codeset = conv_get_locale_charset_str();
-               dest_codeset = CS_UTF_8;
-               tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
-               g_free(str);
-               str = tmp;
-       }
-
-       return str;
-}
-
-static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
-{
-       FILE *fp;
-       gchar *str;
-       GStatBuf s;
-#ifndef G_OS_WIN32
-       gint fd, err;
-       struct timeval timeout = {1, 0};
-       fd_set fds;
-       int fflags = 0;
-#endif
-
-       cm_return_val_if_fail(file != NULL, NULL);
-
-       if (g_stat(file, &s) != 0) {
-               FILE_OP_ERROR(file, "stat");
-               return NULL;
-       }
-       if (S_ISDIR(s.st_mode)) {
-               g_warning("%s: is a directory", file);
-               return NULL;
-       }
-
-#ifdef G_OS_WIN32
-       fp = g_fopen (file, "rb");
-       if (fp == NULL) {
-               FILE_OP_ERROR(file, "open");
-               return NULL;
-       }
-#else    
-       /* test whether the file is readable without blocking */
-       fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
-       if (fd == -1) {
-               FILE_OP_ERROR(file, "open");
-               return NULL;
-       }
-
-       FD_ZERO(&fds);
-       FD_SET(fd, &fds);
-
-       /* allow for one second */
-       err = select(fd+1, &fds, NULL, NULL, &timeout);
-       if (err <= 0 || !FD_ISSET(fd, &fds)) {
-               if (err < 0) {
-                       FILE_OP_ERROR(file, "select");
-               } else {
-                       g_warning("%s: doesn't seem readable", file);
-               }
-               close(fd);
-               return NULL;
-       }
-       
-       /* Now clear O_NONBLOCK */
-       if ((fflags = fcntl(fd, F_GETFL)) < 0) {
-               FILE_OP_ERROR(file, "fcntl (F_GETFL)");
-               close(fd);
-               return NULL;
-       }
-       if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
-               FILE_OP_ERROR(file, "fcntl (F_SETFL)");
-               close(fd);
-               return NULL;
-       }
-       
-       /* get the FILE pointer */
-       fp = fdopen(fd, "rb");
-
-       if (fp == NULL) {
-               FILE_OP_ERROR(file, "fdopen");
-               close(fd); /* if fp isn't NULL, we'll use fclose instead! */
-               return NULL;
-       }
-#endif
-
-       str = file_read_stream_to_str_full(fp, recode);
-
-       fclose(fp);
-
-       return str;
-}
-
-gchar *file_read_to_str(const gchar *file)
-{
-       return file_read_to_str_full(file, TRUE);
-}
-gchar *file_read_stream_to_str(FILE *fp)
-{
-       return file_read_stream_to_str_full(fp, TRUE);
-}
-
-gchar *file_read_to_str_no_recode(const gchar *file)
-{
-       return file_read_to_str_full(file, FALSE);
-}
-gchar *file_read_stream_to_str_no_recode(FILE *fp)
-{
-       return file_read_stream_to_str_full(fp, FALSE);
-}
-
 char *fgets_crlf(char *buf, int size, FILE *stream)
 {
        gboolean is_cr = FALSE;
@@ -3236,14 +2554,14 @@ char *fgets_crlf(char *buf, int size, FILE *stream)
 
        *cs = '\0';
 
-       return buf;     
+       return buf;
 }
 
-static gint execute_async(gchar *const argv[])
+static gint execute_async(gchar *const argv[], const gchar *working_directory)
 {
        cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
 
-       if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
+       if (g_spawn_async(working_directory, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
                          NULL, NULL, NULL, FALSE) == FALSE) {
                g_warning("couldn't execute command: %s", argv[0]);
                return -1;
@@ -3252,14 +2570,14 @@ static gint execute_async(gchar *const argv[])
        return 0;
 }
 
-static gint execute_sync(gchar *const argv[])
+static gint execute_sync(gchar *const argv[], const gchar *working_directory)
 {
        gint status;
 
        cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
 
 #ifdef G_OS_UNIX
-       if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
+       if (g_spawn_sync(working_directory, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
                g_warning("couldn't execute command: %s", argv[0]);
                return -1;
@@ -3270,8 +2588,10 @@ static gint execute_sync(gchar *const argv[])
        else
                return -1;
 #else
-       if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH| 
-                        G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
+       if (g_spawn_sync(working_directory, (gchar **)argv, NULL,
+                               G_SPAWN_SEARCH_PATH|
+                               G_SPAWN_CHILD_INHERITS_STDIN|
+                               G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
                g_warning("couldn't execute command: %s", argv[0]);
                return -1;
@@ -3281,7 +2601,8 @@ static gint execute_sync(gchar *const argv[])
 #endif
 }
 
-gint execute_command_line(const gchar *cmdline, gboolean async)
+gint execute_command_line(const gchar *cmdline, gboolean async,
+               const gchar *working_directory)
 {
        gchar **argv;
        gint ret;
@@ -3291,9 +2612,9 @@ gint execute_command_line(const gchar *cmdline, gboolean async)
        argv = strsplit_with_quote(cmdline, " ", 0);
 
        if (async)
-               ret = execute_async(argv);
+               ret = execute_async(argv, working_directory);
        else
-               ret = execute_sync(argv);
+               ret = execute_sync(argv, working_directory);
 
        g_strfreev(argv);
 
@@ -3378,7 +2699,7 @@ gint open_uri(const gchar *uri, const gchar *cmdline)
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
        }
 
-       execute_command_line(buf, TRUE);
+       execute_command_line(buf, TRUE, NULL);
 #else
        ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
 #endif
@@ -3404,7 +2725,7 @@ gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
                g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
        }
 
-       execute_command_line(buf, TRUE);
+       execute_command_line(buf, TRUE, NULL);
 
        return 0;
 }
@@ -3485,7 +2806,7 @@ time_t tzoffset_sec(time_t *now)
 #ifdef G_OS_WIN32
        if (now && *now < 0)
                return 0;
-#endif 
+#endif
        gmt = *gmtime_r(now, &buf1);
        lt = localtime_r(now, &buf2);
 
@@ -3547,23 +2868,37 @@ gchar *tzoffset(time_t *now)
        return offset_string;
 }
 
-void get_rfc822_date(gchar *buf, gint len)
+static void _get_rfc822_date(gchar *buf, gint len, gboolean hidetz)
 {
        struct tm *lt;
        time_t t;
        gchar day[4], mon[4];
        gint dd, hh, mm, ss, yyyy;
        struct tm buf1;
-       gchar buf2[BUFFSIZE];
+       gchar buf2[RFC822_DATE_BUFFSIZE];
 
        t = time(NULL);
-       lt = localtime_r(&t, &buf1);
+       if (hidetz)
+               lt = gmtime_r(&t, &buf1);
+       else
+               lt = localtime_r(&t, &buf1);
 
-       sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
-              day, mon, &dd, &hh, &mm, &ss, &yyyy);
+       if (sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
+              day, mon, &dd, &hh, &mm, &ss, &yyyy) != 7)
+               g_warning("failed reading date/time");
 
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
-                  day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
+                  day, dd, mon, yyyy, hh, mm, ss, (hidetz? "-0000": tzoffset(&t)));
+}
+
+void get_rfc822_date(gchar *buf, gint len)
+{
+       _get_rfc822_date(buf, len, FALSE);
+}
+
+void get_rfc822_date_hide_tz(gchar *buf, gint len)
+{
+       _get_rfc822_date(buf, len, TRUE);
 }
 
 void debug_set_mode(gboolean mode)
@@ -3626,19 +2961,15 @@ void subject_table_remove(GHashTable *subject_table, gchar * subject)
        g_hash_table_remove(subject_table, subject);
 }
 
-#ifndef G_OS_WIN32
 static regex_t u_regex;
 static gboolean u_init_;
-#endif
 
 void utils_free_regex(void)
 {
-#ifndef G_OS_WIN32
        if (u_init_) {
                regfree(&u_regex);
                u_init_ = FALSE;
        }
-#endif
 }
 
 /*!
@@ -3654,7 +2985,6 @@ void utils_free_regex(void)
  */
 int subject_get_prefix_length(const gchar *subject)
 {
-#ifndef G_OS_WIN32
        /*!< Array with allowable reply prefixes regexps. */
        static const gchar * const prefixes[] = {
                "Re\\:",                        /* "Re:" */
@@ -3715,44 +3045,8 @@ int subject_get_prefix_length(const gchar *subject)
                return pos.rm_eo;
        else
                return 0;
-#else
-       /*!< Array with allowable reply prefixes regexps. */
-       static const gchar * const prefixes[] = {
-               "re:",                  /* "Re:" */
-               "antw:",                        /* "Antw:" (Dutch / German Outlook) */
-               "aw:",                  /* "Aw:"   (German) */
-               "antwort:",                     /* "Antwort:" (German Lotus Notes) */
-               "res:",                 /* "Res:" (Spanish/Brazilian Outlook) */
-               "fw:",                  /* "Fw:" Forward */
-               "fwd:",                 /* "Fwd:" Forward */
-               "enc:",                 /* "Enc:" Forward (Brazilian Outlook) */
-               "odp:",                 /* "Odp:" Re (Polish Outlook) */
-               "rif:",                 /* "Rif:" (Italian Outlook) */
-               "sv:",                  /* "Sv" (Norwegian) */
-               "vs:",                  /* "Vs" (Norwegian) */
-               "ad:",                  /* "Ad" (Norwegian) */
-               "R\303\251f. :",        /* "R�f. :" (French Lotus Notes) */
-               "Re :",                 /* "Re :" (French Yahoo Mail) */
-               /* add more */
-       };
-       const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
-       int n;
-
-       if (!subject) return 0;
-       if (!*subject) return 0;
-
-       for (n = 0; n < PREFIXES; n++) {
-               int len = strlen(prefixes[n]);
-               if (!strncasecmp(subject, prefixes[n], len)) {
-                       if (subject[len] == ' ')
-                               return len+1;
-                       else
-                               return len;
-               }
-       }
-       return 0;
-#endif
 }
+
 static guint g_stricase_hash(gconstpointer gptr)
 {
        guint hash_result = 0;
@@ -3778,39 +3072,6 @@ gint g_int_compare(gconstpointer a, gconstpointer b)
        return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
 }
 
-gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
-{
-       struct tm *lt;
-       time_t t;
-       gchar *addr;
-       struct tm buft;
-
-       t = time(NULL);
-       lt = localtime_r(&t, &buft);
-
-       if (user_addr != NULL)
-             addr = g_strdup_printf(".%s", user_addr);
-       else if (strlen(buf) != 0)
-             addr = g_strdup_printf("@%s", buf);
-       else
-             addr = g_strdup_printf("@%s", get_domain_name());
-
-       /* Replace all @ but the last one in addr, with underscores.
-        * RFC 2822 States that msg-id syntax only allows one @.
-        */
-       while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
-               *(strchr(addr, '@')) = '_';
-
-       g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
-                  lt->tm_year + 1900, lt->tm_mon + 1,
-                  lt->tm_mday, lt->tm_hour,
-                  lt->tm_min, lt->tm_sec,
-                  (guint) rand(), addr);
-
-       g_free(addr);
-       return buf;
-}
-
 /*
    quote_cmd_argument()
 
@@ -4170,7 +3431,7 @@ void replace_returns(gchar *str)
 }
 
 /* get_uri_part() - retrieves a URI starting from scanpos.
-                   Returns TRUE if succesful */
+                   Returns TRUE if successful */
 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
                             const gchar **bp, const gchar **ep, gboolean hdr)
 {
@@ -4185,9 +3446,10 @@ gboolean get_uri_part(const gchar *start, const gchar *scanpos,
        *bp = scanpos;
 
        /* find end point of URI */
-       for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
-               if (!g_ascii_isgraph(*(const guchar *)ep_) ||
-                   !IS_ASCII(*(const guchar *)ep_) ||
+       for (ep_ = scanpos; *ep_ != '\0'; ep_ = g_utf8_next_char(ep_)) {
+               gunichar u = g_utf8_get_char_validated(ep_, -1);
+               if (!g_unichar_isgraph(u) ||
+                   u == (gunichar)-1 ||
                    strchr("[]{}<>\"", *ep_)) {
                        break;
                } else if (strchr("(", *ep_)) {
@@ -4206,7 +3468,7 @@ gboolean get_uri_part(const gchar *start, const gchar *scanpos,
         * should pass some URI type to this function and decide on that whether
         * to perform punctuation stripping */
 
-#define IS_REAL_PUNCT(ch)      (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
+#define IS_REAL_PUNCT(ch)      (g_ascii_ispunct(ch) && !strchr("/?=-_~)", ch))
 
        for (; ep_ - 1 > scanpos + 1 &&
               IS_REAL_PUNCT(*(ep_ - 1));
@@ -4241,35 +3503,6 @@ gchar *make_uri_string(const gchar *bp, const gchar *ep)
 
 static GHashTable *create_domain_tab(void)
 {
-       static const gchar *toplvl_domains [] = {
-           "museum", "aero",
-           "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
-           "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
-           "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
-           "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
-           "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
-           "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
-           "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
-           "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
-           "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
-           "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
-           "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
-           "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
-           "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
-           "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
-           "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
-           "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
-           "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
-           "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
-           "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
-           "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
-           "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
-           "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
-           "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
-           "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
-           "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
-           "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
-       };
        gint n;
        GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
 
@@ -4281,12 +3514,11 @@ static GHashTable *create_domain_tab(void)
 
 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
 {
-       const gint MAX_LVL_DOM_NAME_LEN = 6;
-       gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
-       const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
+       gchar buf[BUFFSIZE + 1];
+       const gchar *m = buf + BUFFSIZE + 1;
        register gchar *p;
 
-       if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
+       if (last - first > BUFFSIZE || first > last)
                return FALSE;
 
        for (p = buf; p < m &&  first < last; *p++ = *first++)
@@ -4296,7 +3528,7 @@ static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gcha
        return g_hash_table_lookup(tab, buf) != NULL;
 }
 
-/* get_email_part() - retrieves an email address. Returns TRUE if succesful */
+/* get_email_part() - retrieves an email address. Returns TRUE if successful */
 gboolean get_email_part(const gchar *start, const gchar *scanpos,
                               const gchar **bp, const gchar **ep, gboolean hdr)
 {
@@ -4336,7 +3568,7 @@ search_again:
                        start++;
 
                *bp = start;
-               
+
                /* check if there are quotes (to skip , in them) */
                if (*start == '"') {
                        start_quote = start;
@@ -4346,12 +3578,12 @@ search_again:
                        start_quote = NULL;
                        end_quote = NULL;
                }
-               
+
                /* skip anything between quotes */
                if (start_quote && end_quote) {
                        start = end_quote;
-                       
-               } 
+
+               }
 
                /* find end (either , or ; or end of line) */
                if (strstr(start, ",") && strstr(start, ";"))
@@ -4447,7 +3679,7 @@ search_again:
                ep_ += 3;
 
                /* go to matching '>' (or next non-rfc822 char, like \n) */
-               for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
+               for (; *ep_ != '>' && *ep_ != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
                        ;
 
                /* include the bracket */
@@ -4539,10 +3771,21 @@ gchar *make_email_string(const gchar *bp, const gchar *ep)
         * uri type later on in the button_pressed signal handler */
        gchar *tmp;
        gchar *result;
+       gchar *colon, *at;
 
        tmp = g_strndup(bp, ep - bp);
-       result = g_strconcat("mailto:", tmp, NULL);
-       g_free(tmp);
+
+       /* If there is a colon in the username part of the address,
+        * we're dealing with an URI for some other protocol - do
+        * not prefix with mailto: in such case. */
+       colon = strchr(tmp, ':');
+       at = strchr(tmp, '@');
+       if (colon != NULL && at != NULL && colon < at) {
+               result = tmp;
+       } else {
+               result = g_strconcat("mailto:", tmp, NULL);
+               g_free(tmp);
+       }
 
        return result;
 }
@@ -4564,12 +3807,12 @@ gchar *make_http_string(const gchar *bp, const gchar *ep)
 
 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
 {
-       FILE *fp = g_fopen(path, "rb");
+       FILE *fp = claws_fopen(path, "rb");
        gchar buf[BUFFSIZE];
        gchar *result = NULL;
        if (!fp)
                return NULL;
-       while (fgets(buf, sizeof (buf), fp) != NULL) {
+       while (claws_fgets(buf, sizeof (buf), fp) != NULL) {
                gchar **parts = g_strsplit(buf, ";", 3);
                gchar *trimmed = parts[0];
                while (trimmed[0] == ' ' || trimmed[0] == '\t')
@@ -4595,13 +3838,13 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                                        testcmd[strlen(testcmd)-1] = '\0';
                                while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
                                        testcmd[strlen(testcmd)-1] = '\0';
-                                       
+
                                if (strstr(testcmd, "%s")) {
                                        gchar *tmp = g_strdup_printf(testcmd, file_to_open);
                                        gint res = system(tmp);
                                        g_free(tmp);
                                        g_free(orig_testcmd);
-                                       
+
                                        if (res != 0) {
                                                g_strfreev(parts);
                                                continue;
@@ -4609,14 +3852,14 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                                } else {
                                        gint res = system(testcmd);
                                        g_free(orig_testcmd);
-                                       
+
                                        if (res != 0) {
                                                g_strfreev(parts);
                                                continue;
                                        }
                                }
                        }
-                       
+
                        trimmed = parts[1];
                        while (trimmed[0] == ' ' || trimmed[0] == '\t')
                                trimmed++;
@@ -4628,23 +3871,7 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                                trimmed[strlen(trimmed)-1] = '\0';
                        result = g_strdup(trimmed);
                        g_strfreev(parts);
-                       fclose(fp);
-                       /* if there are no single quotes around %s, add them.
-                        * '.*%s.*' is ok, as in display 'png:%s'
-                        */
-                       if (strstr(result, "%s") 
-                       && !(strstr(result, "'") < strstr(result,"%s") &&
-                            strstr(strstr(result,"%s"), "'"))) {
-                               gchar *start = g_strdup(result);
-                               gchar *end = g_strdup(strstr(result, "%s")+2);
-                               gchar *tmp;
-                               *strstr(start, "%s") = '\0';
-                               tmp = g_strconcat(start,"'%s'",end, NULL);
-                               g_free(start);
-                               g_free(end);
-                               g_free(result);
-                               result = tmp;
-                       }
+                       claws_fclose(fp);
                        if (needsterminal) {
                                gchar *tmp = g_strdup_printf("xterm -e %s", result);
                                g_free(result);
@@ -4654,7 +3881,7 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                }
                g_strfreev(parts);
        }
-       fclose(fp);
+       claws_fclose(fp);
        return NULL;
 }
 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
@@ -4677,13 +3904,13 @@ void mailcap_update_default(const gchar *type, const gchar *command)
        gchar *path = NULL, *outpath = NULL;
        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
        outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
-       FILE *fp = g_fopen(path, "rb");
+       FILE *fp = claws_fopen(path, "rb");
        FILE *outfp = NULL;
        gchar buf[BUFFSIZE];
        gboolean err = FALSE;
 
        if (!fp) {
-               fp = g_fopen(path, "a");
+               fp = claws_fopen(path, "a");
                if (!fp) {
                        g_warning("failed to create file %s", path);
                        g_free(path);
@@ -4699,15 +3926,15 @@ void mailcap_update_default(const gchar *type, const gchar *command)
                }
        }
 
-       outfp = g_fopen(outpath, "wb");
+       outfp = claws_fopen(outpath, "wb");
        if (!outfp) {
                g_warning("failed to create file %s", outpath);
                g_free(path);
                g_free(outpath);
-               fclose(fp);
+               claws_fclose(fp);
                return;
        }
-       while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
+       while (fp && claws_fgets(buf, sizeof (buf), fp) != NULL) {
                gchar **parts = g_strsplit(buf, ";", 3);
                gchar *trimmed = parts[0];
                while (trimmed[0] == ' ')
@@ -4720,7 +3947,7 @@ void mailcap_update_default(const gchar *type, const gchar *command)
                        continue;
                }
                else {
-                       if(fputs(buf, outfp) == EOF) {
+                       if(claws_fputs(buf, outfp) == EOF) {
                                err = TRUE;
                                break;
                        }
@@ -4731,11 +3958,11 @@ void mailcap_update_default(const gchar *type, const gchar *command)
                err = TRUE;
 
        if (fp)
-               fclose(fp);
+               claws_fclose(fp);
 
-       if (fclose(outfp) == EOF)
+       if (claws_safe_fclose(outfp) == EOF)
                err = TRUE;
-               
+
        if (!err)
                g_rename(outpath, path);
 
@@ -4743,60 +3970,6 @@ void mailcap_update_default(const gchar *type, const gchar *command)
        g_free(outpath);
 }
 
-gint copy_dir(const gchar *src, const gchar *dst)
-{
-       GDir *dir;
-       const gchar *name;
-
-       if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
-               g_warning("failed to open directory: %s", src);
-               return -1;
-       }
-
-       if (make_dir(dst) < 0)
-               return -1;
-
-       while ((name = g_dir_read_name(dir)) != NULL) {
-               gchar *old_file, *new_file;
-               old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
-               new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
-               debug_print("copying: %s -> %s\n", old_file, new_file);
-               if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
-                       gint r = copy_file(old_file, new_file, TRUE);
-                       if (r < 0) {
-                               g_dir_close(dir);
-                               return r;
-                       }
-                }
-#ifndef G_OS_WIN32
-                /* Windows has no symlinks.  Or well, Vista seems to
-                   have something like this but the semantics might be
-                   different.  Thus we don't use it under Windows. */
-                else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
-                       GError *error = NULL;
-                       gint r = 0;
-                       gchar *target = g_file_read_link(old_file, &error);
-                       if (target)
-                               r = symlink(target, new_file);
-                       g_free(target);
-                       if (r < 0) {
-                               g_dir_close(dir);
-                               return r;
-                       }
-                 }
-#endif /*G_OS_WIN32*/
-               else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
-                       gint r = copy_dir(old_file, new_file);
-                       if (r < 0) {
-                               g_dir_close(dir);
-                               return r;
-                       }
-               }
-       }
-       g_dir_close(dir);
-       return 0;
-}
-
 /* crude test to see if a file is an email. */
 gboolean file_is_email (const gchar *filename)
 {
@@ -4806,10 +3979,10 @@ gboolean file_is_email (const gchar *filename)
        gint score = 0;
        if (filename == NULL)
                return FALSE;
-       if ((fp = g_fopen(filename, "rb")) == NULL)
+       if ((fp = claws_fopen(filename, "rb")) == NULL)
                return FALSE;
        while (i < 60 && score < 3
-              && fgets(buffer, sizeof (buffer), fp) > 0) {
+              && claws_fgets(buffer, sizeof (buffer), fp) != NULL) {
                if (!strncmp(buffer, "From:", strlen("From:")))
                        score++;
                else if (!strncmp(buffer, "Date:", strlen("Date:")))
@@ -4820,7 +3993,7 @@ gboolean file_is_email (const gchar *filename)
                        score++;
                i++;
        }
-       fclose(fp);
+       claws_fclose(fp);
        return (score >= 3);
 }
 
@@ -4847,10 +4020,10 @@ gboolean sc_g_slist_bigger(GSList *list, gint max)
 }
 
 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
-const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
+const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL};
 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
-const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
+const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL};
 
 gint daynames_len[] =     {0,0,0,0,0,0,0};
@@ -4903,7 +4076,7 @@ static void init_time_names(void)
        s_daynames[4] = C_("Abbr. day name for use by strftime", "Thu");
        s_daynames[5] = C_("Abbr. day name for use by strftime", "Fri");
        s_daynames[6] = C_("Abbr. day name for use by strftime", "Sat");
-       
+
        s_monthnames[0] = C_("Abbr. month name for use by strftime", "Jan");
        s_monthnames[1] = C_("Abbr. month name for use by strftime", "Feb");
        s_monthnames[2] = C_("Abbr. month name for use by strftime", "Mar");
@@ -4930,7 +4103,7 @@ static void init_time_names(void)
        s_pm_up = C_("For use by strftime (afternoon)", "PM");
        s_am_low = C_("For use by strftime (morning, lowercase)", "am");
        s_pm_low = C_("For use by strftime (afternoon, lowercase)", "pm");
-       
+
        s_am_up_len = strlen(s_am_up);
        s_pm_up_len = strlen(s_pm_up);
        s_am_low_len = strlen(s_am_low);
@@ -4953,13 +4126,13 @@ size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt
        gint total_done = 0;
        gchar subbuf[64], subfmt[64];
        static time_t last_tzset = (time_t)0;
-       
+
        if (!time_names_init_done)
                init_time_names();
-       
+
        if (format == NULL || lt == NULL)
                return 0;
-               
+
        if (last_tzset != time(NULL)) {
                tzset();
                last_tzset = time(NULL);
@@ -5024,7 +4197,7 @@ size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt
                                break;
                        case 'F':
                                len = 10; CHECK_SIZE();
-                               snprintf(curpos, buflen - total_done, "%4d-%02d-%02d", 
+                               snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
                                        lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
                                break;
                        case 'H':
@@ -5093,7 +4266,11 @@ size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt
                                }
                                break;
                        case 'r':
+#ifdef G_OS_WIN32
+                               strftime(subbuf, 64, "%I:%M:%S %p", lt);
+#else
                                strftime(subbuf, 64, "%r", lt);
+#endif
                                len = strlen(subbuf); CHECK_SIZE();
                                strncpy2(curpos, subbuf, buflen - total_done);
                                break;
@@ -5106,7 +4283,7 @@ size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt
                                *curpos++ = '0'+(lt->tm_min % 10);
                                break;
                        case 's':
-                               snprintf(subbuf, 64, "%ld", mktime(lt));
+                               snprintf(subbuf, 64, "%lld", (long long)mktime(lt));
                                len = strlen(subbuf); CHECK_SIZE();
                                strncpy2(curpos, subbuf, buflen - total_done);
                                break;
@@ -5190,56 +4367,17 @@ size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt
                        format++;
                } else {
                        int len = 1; CHECK_SIZE();
-                       *curpos++ = *format++; 
+                       *curpos++ = *format++;
                }
        }
        *curpos = '\0';
        return total_done;
 }
 
-gboolean prefs_common_get_use_shred(void);
-
-
 #ifdef G_OS_WIN32
 #define WEXITSTATUS(x) (x)
 #endif
 
-int claws_unlink(const gchar *filename) 
-{
-       GStatBuf s;
-       static int found_shred = -1;
-       static const gchar *args[4];
-
-       if (filename == NULL)
-               return 0;
-
-       if (prefs_common_get_use_shred()) {
-               if (found_shred == -1) {
-                       /* init */
-                       args[0] = g_find_program_in_path("shred");
-                       debug_print("found shred: %s\n", args[0]);
-                       found_shred = (args[0] != NULL) ? 1:0;
-                       args[1] = "-f";
-                       args[3] = NULL;
-               }
-               if (found_shred == 1) {
-                       if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
-                               if (s.st_nlink == 1) {
-                                       gint status=0;
-                                       args[2] = filename;
-                                       g_spawn_sync(NULL, (gchar **)args, NULL, 0,
-                                        NULL, NULL, NULL, NULL, &status, NULL);
-                                       debug_print("%s %s exited with status %d\n",
-                                               args[0], filename, WEXITSTATUS(status));
-                                       if (truncate(filename, 0) < 0)
-                                               g_warning("couln't truncate: %s", filename);
-                               }
-                       }
-               }
-       }
-       return g_unlink(filename);
-}
-
 GMutex *cm_mutex_new(void) {
 #if GLIB_CHECK_VERSION(2,32,0)
        GMutex *m = g_new0(GMutex, 1);
@@ -5299,7 +4437,9 @@ static GSList *cm_split_path(const gchar *filename, int depth)
        GSList *canonical_parts = NULL;
        GStatBuf st;
        int i;
+#ifndef G_OS_WIN32
        gboolean follow_symlinks = TRUE;
+#endif
 
        if (depth > 32) {
 #ifndef G_OS_WIN32
@@ -5328,7 +4468,7 @@ static GSList *cm_split_path(const gchar *filename, int depth)
                                return NULL;
                        }
                        else /* Remove the last inserted element */
-                               canonical_parts = 
+                               canonical_parts =
                                        g_slist_delete_link(canonical_parts,
                                                            canonical_parts);
                } else {
@@ -5342,7 +4482,9 @@ static GSList *cm_split_path(const gchar *filename, int depth)
                        if(g_stat(tmp_path, &st) < 0) {
                                if (errno == ENOENT) {
                                        errno = 0;
+#ifndef G_OS_WIN32
                                        follow_symlinks = FALSE;
+#endif
                                }
                                if (errno != 0) {
                                        g_free(tmp_path);
@@ -5359,7 +4501,7 @@ static GSList *cm_split_path(const gchar *filename, int depth)
 
                                if (!g_path_is_absolute(target)) {
                                        /* remove the last inserted element */
-                                       canonical_parts = 
+                                       canonical_parts =
                                                g_slist_delete_link(canonical_parts,
                                                            canonical_parts);
                                        /* add the target */
@@ -5413,7 +4555,7 @@ int cm_canonicalize_filename(const gchar *filename, gchar **canonical_name) {
                gchar *cur = g_get_current_dir();
                gchar *absolute_filename = g_strconcat(cur, G_DIR_SEPARATOR_S,
                                                       filename, NULL);
-               
+
                canonical_parts = cm_split_path(absolute_filename, 0);
                g_free(absolute_filename);
                g_free(cur);
@@ -5437,7 +4579,7 @@ guchar *g_base64_decode_zero(const gchar *text, gsize *out_len)
        g_free(tmp);
 
        if (strlen(out) != *out_len) {
-               g_warning ("strlen(out) %zd != *out_len %" G_GSIZE_FORMAT, strlen(out), *out_len);
+               g_warning ("strlen(out) %"G_GSIZE_FORMAT" != *out_len %"G_GSIZE_FORMAT, strlen(out), *out_len);
        }
 
        return out;
@@ -5476,3 +4618,131 @@ g_utf8_substring (const gchar *str,
   return out;
 }
 #endif
+
+/* Attempts to read count bytes from a PRNG into memory area starting at buf.
+ * It is up to the caller to make sure there is at least count bytes
+ * available at buf. */
+gboolean
+get_random_bytes(void *buf, size_t count)
+{
+       /* Open our prng source. */
+#if defined G_OS_WIN32
+       HCRYPTPROV rnd;
+
+       if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
+                       !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
+               debug_print("Could not acquire a CSP handle.\n");
+               return FALSE;
+       }
+#else
+       int rnd;
+       ssize_t ret;
+
+       rnd = open("/dev/urandom", O_RDONLY);
+       if (rnd == -1) {
+               FILE_OP_ERROR("/dev/urandom", "open");
+               debug_print("Could not open /dev/urandom.\n");
+               return FALSE;
+       }
+#endif
+
+       /* Read data from the source into buf. */
+#if defined G_OS_WIN32
+       if (!CryptGenRandom(rnd, count, buf)) {
+               debug_print("Could not read %"G_GSIZE_FORMAT" random bytes.\n", count);
+               CryptReleaseContext(rnd, 0);
+               return FALSE;
+       }
+#else
+       ret = read(rnd, buf, count);
+       if (ret != count) {
+               FILE_OP_ERROR("/dev/urandom", "read");
+               debug_print("Could not read enough data from /dev/urandom, read only %ld of %lu bytes.\n", ret, count);
+               close(rnd);
+               return FALSE;
+       }
+#endif
+
+       /* Close the prng source. */
+#if defined G_OS_WIN32
+       CryptReleaseContext(rnd, 0);
+#else
+       close(rnd);
+#endif
+
+       return TRUE;
+}
+
+/* returns FALSE if parsing failed, otherwise returns TRUE and sets *server, *port
+   and eventually *fp from filename (if not NULL, they must be free'd by caller after
+   user.
+   filenames we expect: 'host.name.port.cert' or 'host.name.port.f:i:n:g:e:r:p:r:i:n:t.cert' */
+gboolean get_serverportfp_from_filename(const gchar *str, gchar **server, gchar **port, gchar **fp)
+{
+       const gchar *pos, *dotport_pos = NULL, *dotcert_pos = NULL, *dotfp_pos = NULL;
+
+       g_return_val_if_fail(str != NULL, FALSE);
+
+       pos = str + strlen(str) - 1;
+       while ((pos > str) && !dotport_pos) {
+               if (*pos == '.') {
+                       if (!dotcert_pos) {
+                               /* match the .cert suffix */
+                               if (strcmp(pos, ".cert") == 0) {
+                                       dotcert_pos = pos;
+                               }
+                       } else {
+                               if (!dotfp_pos) {
+                                       /* match an eventual fingerprint */
+                                       /* or the port number */
+                                       if (strncmp(pos + 3, ":", 1) == 0) {
+                                               dotfp_pos = pos;
+                                       } else {
+                                               dotport_pos = pos;
+                                       }
+                               } else {
+                                       /* match the port number */
+                                       dotport_pos = pos;
+                               }
+                       }
+               }
+               pos--;
+       }
+       if (!dotport_pos || !dotcert_pos) {
+               g_warning("could not parse filename %s", str);
+               return FALSE;
+       }
+
+       if (server != NULL)
+               *server = g_strndup(str, dotport_pos - str);
+       if (dotfp_pos) {
+               if (port != NULL)
+                       *port = g_strndup(dotport_pos + 1, dotfp_pos - dotport_pos - 1);
+               if (fp != NULL)
+                       *fp = g_strndup(dotfp_pos + 1, dotcert_pos - dotfp_pos - 1);
+       } else {
+               if (port != NULL)
+                       *port = g_strndup(dotport_pos + 1, dotcert_pos - dotport_pos - 1);
+               if (fp != NULL)
+                       *fp = NULL;
+       }
+
+       debug_print("filename='%s' => server='%s' port='%s' fp='%s'\n",
+                       str,
+                       (server ? *server : "(n/a)"),
+                       (port ? *port : "(n/a)"),
+                       (fp ? *fp : "(n/a)"));
+
+       if (!(server && *server) || !(port && *port))
+               return FALSE;
+       else
+               return TRUE;
+}
+
+#ifdef G_OS_WIN32
+gchar *win32_debug_log_path(void)
+{
+       return g_strconcat(g_get_tmp_dir(), G_DIR_SEPARATOR_S,
+                       "claws-win32.log", NULL);
+}
+#endif