Make Message-ID string generation less confusing.
[claws.git] / src / common / utils.c
index 2e41793fc0935c40d54fe5519a1463e530320461..d7d1b4cb9e5a7c448dacc3c38702216c6b5740aa 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2013 Hiroyuki Yamamoto & The Claws Mail Team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2016 Hiroyuki Yamamoto & The Claws Mail Team
  *
  * 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
  *
  * The code of the g_utf8_substring function below is owned by
  * Matthias Clasen <matthiasc@src.gnome.org>/<mclasen@redhat.com>
- * and is got from GLIB 2.30
- * 
+ * and is got from GLIB 2.30: https://git.gnome.org/browse/glib/commit/
+ *  ?h=glib-2-30&id=9eb65dd3ed5e1a9638595cbe10699c7606376511
+ *
+ * GLib 2.30 is licensed under GPL v2 or later and:
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * https://git.gnome.org/browse/glib/tree/glib/gutf8.c
+ *  ?h=glib-2-30&id=9eb65dd3ed5e1a9638595cbe10699c7606376511
  */
 
 #ifdef HAVE_CONFIG_H
@@ -42,6 +49,9 @@
 #include <ctype.h>
 #include <errno.h>
 #include <sys/param.h>
+#ifndef G_OS_WIN32
+#include <sys/socket.h>
+#endif
 
 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
 #  include <wchar.h>
@@ -74,6 +84,7 @@
 #include "utils.h"
 #include "socket.h"
 #include "../codeconv.h"
+#include "tlds.h"
 
 #define BUFFSIZE       8192
 
@@ -82,165 +93,34 @@ static gboolean debug_mode = FALSE;
 static GSList *tempfiles=NULL;
 #endif
 
-/* Return true if we are running as root.  This function should beused
-   instead of getuid () == 0.  */
-gboolean superuser_p (void)
+#if !GLIB_CHECK_VERSION(2, 26, 0)
+guchar *g_base64_decode_wa(const gchar *text, gsize *out_len)
 {
-#ifdef G_OS_WIN32
-  return w32_is_administrator ();
-#else
-  return !getuid();
-#endif  
-}
+       guchar *ret;
+       gsize input_length;
+       gint state = 0;
+       guint save = 0;
 
+       input_length = strlen(text);
 
+       ret = g_malloc0((input_length / 4) * 3 + 1);
 
-#if !defined(G_OS_UNIX)
-gint g_chdir(const gchar *path)
-{
-#ifdef G_OS_WIN32
-       if (G_WIN32_HAVE_WIDECHAR_API()) {
-               wchar_t *wpath;
-               gint retval;
-               gint save_errno;
-
-               wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
-               if (wpath == NULL) {
-                       errno = EINVAL;
-                       return -1;
-               }
-
-               retval = _wchdir(wpath);
-               save_errno = errno;
-
-               g_free(wpath);
-
-               errno = save_errno;
-               return retval;
-       } else {
-               gchar *cp_path;
-               gint retval;
-               gint save_errno;
-
-               cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
-               if (cp_path == NULL) {
-                       errno = EINVAL;
-                       return -1;
-               }
-
-               retval = chdir(cp_path);
-               save_errno = errno;
-
-               g_free(cp_path);
+       *out_len = g_base64_decode_step(text, input_length, ret, &state, &save);
 
-               errno = save_errno;
-               return retval;
-       }
-#else
-       return chdir(path);
-#endif
+       return ret;
 }
-
-gint g_chmod(const gchar *path, gint mode)
-{
-#ifdef G_OS_WIN32
-       if (G_WIN32_HAVE_WIDECHAR_API()) {
-               wchar_t *wpath;
-               gint retval;
-               gint save_errno;
-
-               wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
-               if (wpath == NULL) {
-                       errno = EINVAL;
-                       return -1;
-               }
-
-               retval = _wchmod(wpath, mode);
-               save_errno = errno;
-
-               g_free(wpath);
-
-               errno = save_errno;
-               return retval;
-       } else {
-               gchar *cp_path;
-               gint retval;
-               gint save_errno;
-
-               cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
-               if (cp_path == NULL) {
-                       errno = EINVAL;
-                       return -1;
-               }
-
-               retval = chmod(cp_path, mode);
-               save_errno = errno;
-
-               g_free(cp_path);
-
-               errno = save_errno;
-               return retval;
-       }
-#else
-       return chmod(path, mode);
 #endif
-}
 
-FILE* g_fopen(const gchar *filename, const gchar *mode)
-{
-#ifdef G_OS_WIN32
-       char *name = g_win32_locale_filename_from_utf8(filename);
-       FILE* fp = fopen(name, mode);
-       g_free(name);
-       return fp;
-#else
-       return fopen(filename, mode);
-#endif
-}
-int g_open(const gchar *filename, int flags, int mode)
+/* Return true if we are running as root.  This function should beused
+   instead of getuid () == 0.  */
+gboolean superuser_p (void)
 {
 #ifdef G_OS_WIN32
-       char *name = g_win32_locale_filename_from_utf8(filename);
-       int fd = open(name, flags, mode);
-       g_free(name);
-       return fp;
+  return w32_is_administrator ();
 #else
-       return open(filename, flags, mode);
-#endif
-}
-#endif /* G_OS_UNIX */
-
-
-#ifdef G_OS_WIN32
-gint mkstemp_name(gchar *template, gchar **name_used)
-{
-       static gulong count=0; /* W32-_mktemp only supports up to 27
-                                 tempfiles... */
-       int tmpfd;
-
-       *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
-       tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
-                                   (S_IRUSR | S_IWUSR));
-
-       tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
-       if (tmpfd<0) {
-               perror(g_strdup_printf("cant create %s",*name_used));
-               return -1;
-       }
-       else
-               return tmpfd;
-}
-#endif /* G_OS_WIN32 */
-
-#ifdef G_OS_WIN32
-gint mkstemp(gchar *template)
-{
-       gchar *dummyname;
-       gint res = mkstemp_name(template, &dummyname);
-       g_free(dummyname);
-       return res;
+  return !getuid();
+#endif  
 }
-#endif /* G_OS_WIN32 */
 
 GSList *slist_copy_deep(GSList *list, GCopyFunc func)
 {
@@ -1116,19 +996,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)) {
+                       /* 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;
+               }
        }
 }
 
@@ -1494,7 +1392,7 @@ GList *uri_list_extract_filenames(const gchar *uri_list)
                                        if (!locale_file)
                                                locale_file = g_strdup(file + 5);
 #else
-                                       locale_file = g_filename_from_uri(file, NULL, NULL);
+                                       locale_file = g_filename_from_uri(escaped_utf8uri, NULL, NULL);
 #endif
                                        result = g_list_append(result, locale_file);
                                }
@@ -1706,7 +1604,7 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
                        gchar *tmp = decode_uri_gdup(value);
                        if (!g_file_get_contents(tmp, body, NULL, NULL)) {
-                               g_warning("Error: couldn't set insert file '%s' in body\n", value);
+                               g_warning("couldn't set insert file '%s' in body", value);
                        }
                        g_free(tmp);
                        tmp = NULL;
@@ -1894,7 +1792,7 @@ 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");
+                               strcpy (home_dir_utf16, "C:\\Claws Mail");
                home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
        }
        return home_dir_utf8;
@@ -1937,7 +1835,7 @@ void set_rc_dir(const gchar *dir)
 
                if (err) {
                        g_print("Error looking for %s: %d(%s)\n",
-                               dir, -err, strerror(-err));
+                               dir, -err, g_strerror(-err));
                        exit(0);
                }
                rc_dir_alt = TRUE;
@@ -2012,7 +1910,7 @@ 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)
@@ -2062,7 +1960,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;
 
@@ -2100,21 +1998,28 @@ const gchar *get_domain_name(void)
 {
 #ifdef G_OS_UNIX
        static gchar *domain_name = NULL;
+       struct addrinfo hints, *res;
+       char hostname[256];
+       int s;
 
        if (!domain_name) {
-               struct hostent *hp;
-               char hostname[256];
-
                if (gethostname(hostname, sizeof(hostname)) != 0) {
                        perror("gethostname");
                        domain_name = "localhost";
                } else {
-                       hostname[sizeof(hostname) - 1] = '\0';
-                       if ((hp = my_gethostbyname(hostname)) == NULL) {
-                               perror("gethostbyname");
+                       memset(&hints, 0, sizeof(struct addrinfo));
+                       hints.ai_family = AF_UNSPEC;
+                       hints.ai_socktype = 0;
+                       hints.ai_flags = AI_CANONNAME;
+                       hints.ai_protocol = 0;
+
+                       s = getaddrinfo(hostname, NULL, &hints, &res);
+                       if (s != 0) {
+                               fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
                                domain_name = g_strdup(hostname);
                        } else {
-                               domain_name = g_strdup(hp->h_name);
+                               domain_name = g_strdup(res->ai_canonname);
+                               freeaddrinfo(res);
                        }
                }
                debug_print("domain name = %s\n", domain_name);
@@ -2128,7 +2033,7 @@ const gchar *get_domain_name(void)
 
 off_t get_file_size(const gchar *file)
 {
-       struct stat s;
+       GStatBuf s;
 
        if (g_stat(file, &s) < 0) {
                FILE_OP_ERROR(file, "stat");
@@ -2140,7 +2045,7 @@ off_t get_file_size(const gchar *file)
 
 time_t get_file_mtime(const gchar *file)
 {
-       struct stat s;
+       GStatBuf s;
 
        if (g_stat(file, &s) < 0) {
                FILE_OP_ERROR(file, "stat");
@@ -2150,35 +2055,9 @@ 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)
 {
-       struct stat s;
+       GStatBuf s;
 
        if (file == NULL)
                return FALSE;
@@ -2311,38 +2190,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) {
-               g_warning("failed to open directory: %s\n", dir);
-               g_free(prev_dir);
+       if ((dp = g_dir_open(dir, 0, NULL)) == NULL) {
+               g_warning("failed to open directory: %s", 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;
 }
 
@@ -2380,7 +2244,7 @@ gint remove_numbered_files(const gchar *dir, guint first, guint last)
        }
 
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
-               g_warning("failed to open directory: %s\n", dir);
+               g_warning("failed to open directory: %s", dir);
                g_free(prev_dir);
                return -1;
        }
@@ -2422,6 +2286,7 @@ gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
        gint file_no;
        GHashTable *wanted_files;
        GSList *cur;
+       GError *error = NULL;
 
        if (numberlist == NULL)
            return 0;
@@ -2434,8 +2299,10 @@ gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
                return -1;
        }
 
-       if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
-               FILE_OP_ERROR(dir, "opendir");
+       if ((dp = g_dir_open(".", 0, &error)) == NULL) {
+               g_message("Couldn't open current directory: %s (%d).\n",
+                               error->message, error->code);
+               g_error_free(error);
                g_free(prev_dir);
                return -1;
        }
@@ -2486,7 +2353,7 @@ gint remove_all_numbered_files(const gchar *dir)
 
 gint remove_dir_recursive(const gchar *dir)
 {
-       struct stat s;
+       GStatBuf s;
        GDir *dp;
        const gchar *dir_name;
        gchar *prev_dir;
@@ -2494,13 +2361,13 @@ gint remove_dir_recursive(const gchar *dir)
        if (g_stat(dir, &s) < 0) {
                FILE_OP_ERROR(dir, "stat");
                if (ENOENT == errno) return 0;
-               return -1;
+               return -(errno);
        }
 
        if (!S_ISDIR(s.st_mode)) {
                if (claws_unlink(dir) < 0) {
                        FILE_OP_ERROR(dir, "unlink");
-                       return -1;
+                       return -(errno);
                }
 
                return 0;
@@ -2513,7 +2380,7 @@ gint remove_dir_recursive(const gchar *dir)
                g_free(prev_dir);
                if (g_chdir("..") < 0) {
                        FILE_OP_ERROR(dir, "chdir");
-                       return -1;
+                       return -(errno);
                }
                prev_dir = g_get_current_dir();
        }
@@ -2521,14 +2388,14 @@ gint remove_dir_recursive(const gchar *dir)
        if (g_chdir(dir) < 0) {
                FILE_OP_ERROR(dir, "chdir");
                g_free(prev_dir);
-               return -1;
+               return -(errno);
        }
 
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
-               g_warning("failed to open directory: %s\n", dir);
+               g_warning("failed to open directory: %s", dir);
                g_chdir(prev_dir);
                g_free(prev_dir);
-               return -1;
+               return -(errno);
        }
 
        /* remove all files in the directory */
@@ -2536,9 +2403,11 @@ gint remove_dir_recursive(const gchar *dir)
                /* g_print("removing %s\n", dir_name); */
 
                if (is_dir_exist(dir_name)) {
-                       if (remove_dir_recursive(dir_name) < 0) {
-                               g_warning("can't remove directory\n");
-                               return -1;
+                       gint ret;
+
+                       if ((ret = remove_dir_recursive(dir_name)) < 0) {
+                               g_warning("can't remove directory: %s", dir_name);
+                               return ret;
                        }
                } else {
                        if (claws_unlink(dir_name) < 0)
@@ -2551,14 +2420,14 @@ gint remove_dir_recursive(const gchar *dir)
        if (g_chdir(prev_dir) < 0) {
                FILE_OP_ERROR(prev_dir, "chdir");
                g_free(prev_dir);
-               return -1;
+               return -(errno);
        }
 
        g_free(prev_dir);
 
        if (g_rmdir(dir) < 0) {
                FILE_OP_ERROR(dir, "rmdir");
-               return -1;
+               return -(errno);
        }
 
        return 0;
@@ -2604,14 +2473,14 @@ gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
 
        if (change_file_mode_rw(dest_fp, dest) < 0) {
                FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode\n");
+               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.\n", dest);
+                       g_warning("writing to %s failed.", dest);
                        fclose(dest_fp);
                        fclose(src_fp);
                        claws_unlink(dest);
@@ -2672,14 +2541,14 @@ gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
 
        if (change_file_mode_rw(dest_fp, dest) < 0) {
                FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode\n");
+               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.\n", dest);
+                       g_warning("writing to %s failed.", dest);
                        fclose(dest_fp);
                        fclose(src_fp);
                        claws_unlink(dest);
@@ -2787,7 +2656,7 @@ gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
 
        if (change_file_mode_rw(dest_fp, dest) < 0) {
                FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode\n");
+               g_warning("can't change file mode: %s", dest);
        }
 
        if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
@@ -2799,7 +2668,7 @@ gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
        }
 
        if (err) {
-               g_warning("writing to %s failed.\n", dest);
+               g_warning("writing to %s failed.", dest);
                claws_unlink(dest);
                return -1;
        }
@@ -2867,7 +2736,7 @@ gint canonicalize_file(const gchar *src, const gchar *dest)
 
        if (change_file_mode_rw(dest_fp, dest) < 0) {
                FILE_OP_ERROR(dest, "chmod");
-               g_warning("can't change file mode\n");
+               g_warning("can't change file mode: %s", dest);
        }
 
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
@@ -2893,7 +2762,7 @@ gint canonicalize_file(const gchar *src, const gchar *dest)
                }
 
                if (r == EOF) {
-                       g_warning("writing to %s failed.\n", dest);
+                       g_warning("writing to %s failed.", dest);
                        fclose(dest_fp);
                        fclose(src_fp);
                        claws_unlink(dest);
@@ -2936,7 +2805,7 @@ gint canonicalize_file_replace(const gchar *file)
        }
 
        if (move_file(tmp_file, file, TRUE) < 0) {
-               g_warning("can't replace %s .\n", file);
+               g_warning("can't replace file: %s", file);
                claws_unlink(tmp_file);
                g_free(tmp_file);
                return -1;
@@ -3057,7 +2926,6 @@ gint change_file_mode_rw(FILE *fp, const gchar *file)
 
 FILE *my_tmpfile(void)
 {
-#if HAVE_MKSTEMP || defined(G_OS_WIN32)
        const gchar suffix[] = ".XXXXXX";
        const gchar *tmpdir;
        guint tmplen;
@@ -3084,7 +2952,7 @@ FILE *my_tmpfile(void)
        memcpy(fname + tmplen + 1, progname, proglen);
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
 
-       fd = mkstemp(fname);
+       fd = g_mkstemp(fname);
        if (fd < 0)
                return tmpfile();
 
@@ -3107,25 +2975,16 @@ FILE *my_tmpfile(void)
                return fp;
        }
 
-#endif /* HAVE_MKSTEMP || G_OS_WIN32 */
-
        return tmpfile();
 }
 
 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
 {
        int fd;
-#ifdef G_OS_WIN32
-       char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
-                                         dir, G_DIR_SEPARATOR);
-       fd = mkstemp_name(template, filename);
-       g_free(template);
-#else
        *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
-       fd = mkstemp(*filename);
+       fd = g_mkstemp(*filename);
        if (fd < 0)
                return NULL;
-#endif
        return fdopen(fd, "w+");
 }
 
@@ -3235,7 +3094,7 @@ static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
 {
        FILE *fp;
        gchar *str;
-       struct stat s;
+       GStatBuf s;
 #ifndef G_OS_WIN32
        gint fd, err;
        struct timeval timeout = {1, 0};
@@ -3250,7 +3109,7 @@ static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
                return NULL;
        }
        if (S_ISDIR(s.st_mode)) {
-               g_warning("%s: is a directory\n", file);
+               g_warning("%s: is a directory", file);
                return NULL;
        }
 
@@ -3277,7 +3136,7 @@ static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
                if (err < 0) {
                        FILE_OP_ERROR(file, "select");
                } else {
-                       g_warning("%s: doesn't seem readable\n", file);
+                       g_warning("%s: doesn't seem readable", file);
                }
                close(fd);
                return NULL;
@@ -3361,29 +3220,29 @@ char *fgets_crlf(char *buf, int size, FILE *stream)
        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\n", argv[0]);
+               g_warning("couldn't execute command: %s", argv[0]);
                return -1;
        }
 
        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\n", argv[0]);
+               g_warning("couldn't execute command: %s", argv[0]);
                return -1;
        }
 
@@ -3392,10 +3251,12 @@ 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\n", argv[0]);
+               g_warning("couldn't execute command: %s", argv[0]);
                return -1;
        }
 
@@ -3403,7 +3264,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;
@@ -3413,9 +3275,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);
 
@@ -3433,7 +3295,7 @@ gchar *get_command_output(const gchar *cmdline)
 
        if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
                                      NULL) == FALSE) {
-               g_warning("Couldn't execute command: %s\n", cmdline);
+               g_warning("couldn't execute command: %s", cmdline);
                return NULL;
        }
 
@@ -3500,7 +3362,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
@@ -3526,7 +3388,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;
 }
@@ -3551,7 +3413,7 @@ time_t remote_tzoffset_sec(const gchar *zone)
                if (c == '-')
                        remoteoffset = -remoteoffset;
        } else if (!strncmp(zone, "UT" , 2) ||
-                  !strncmp(zone, "GMT", 2)) {
+                  !strncmp(zone, "GMT", 3)) {
                remoteoffset = 0;
        } else if (strlen(zone3) == 3) {
                for (p = ustzstr; *p != '\0'; p += 3) {
@@ -3669,7 +3531,7 @@ 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;
@@ -3685,7 +3547,17 @@ void get_rfc822_date(gchar *buf, gint len)
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
 
        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)
@@ -3748,19 +3620,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
 }
 
 /*!
@@ -3776,7 +3644,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:" */
@@ -3837,44 +3704,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;
@@ -3900,39 +3731,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()
 
@@ -4363,35 +4161,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);
 
@@ -4661,10 +4430,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;
 }
@@ -4751,22 +4531,6 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                        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;
-                       }
                        if (needsterminal) {
                                gchar *tmp = g_strdup_printf("xterm -e %s", result);
                                g_free(result);
@@ -4807,14 +4571,14 @@ void mailcap_update_default(const gchar *type, const gchar *command)
        if (!fp) {
                fp = g_fopen(path, "a");
                if (!fp) {
-                       g_warning("failed to create file %s\n", path);
+                       g_warning("failed to create file %s", path);
                        g_free(path);
                        g_free(outpath);
                        return;
                }
                fp = g_freopen(path, "rb", fp);
                if (!fp) {
-                       g_warning("failed to reopen file %s\n", path);
+                       g_warning("failed to reopen file %s", path);
                        g_free(path);
                        g_free(outpath);
                        return;
@@ -4823,7 +4587,7 @@ void mailcap_update_default(const gchar *type, const gchar *command)
 
        outfp = g_fopen(outpath, "wb");
        if (!outfp) {
-               g_warning("failed to create file %s\n", outpath);
+               g_warning("failed to create file %s", outpath);
                g_free(path);
                g_free(outpath);
                fclose(fp);
@@ -4871,7 +4635,7 @@ gint copy_dir(const gchar *src, const gchar *dst)
        const gchar *name;
 
        if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
-               g_warning("failed to open directory: %s\n", src);
+               g_warning("failed to open directory: %s", src);
                return -1;
        }
 
@@ -4895,7 +4659,7 @@ gint copy_dir(const gchar *src, const gchar *dst)
                    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;
+                       GError *error = NULL;
                        gint r = 0;
                        gchar *target = g_file_read_link(old_file, &error);
                        if (target)
@@ -4931,7 +4695,7 @@ gboolean file_is_email (const gchar *filename)
        if ((fp = g_fopen(filename, "rb")) == NULL)
                return FALSE;
        while (i < 60 && score < 3
-              && fgets(buffer, sizeof (buffer), fp) > 0) {
+              && fgets(buffer, sizeof (buffer), fp) != NULL) {
                if (!strncmp(buffer, "From:", strlen("From:")))
                        score++;
                else if (!strncmp(buffer, "Date:", strlen("Date:")))
@@ -5228,7 +4992,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;
@@ -5328,7 +5092,7 @@ gboolean prefs_common_get_use_shred(void);
 
 int claws_unlink(const gchar *filename) 
 {
-       struct stat s;
+       GStatBuf s;
        static int found_shred = -1;
        static const gchar *args[4];
 
@@ -5354,7 +5118,7 @@ int claws_unlink(const gchar *filename)
                                        debug_print("%s %s exited with status %d\n",
                                                args[0], filename, WEXITSTATUS(status));
                                        if (truncate(filename, 0) < 0)
-                                               g_warning("couln't truncate");
+                                               g_warning("couln't truncate: %s", filename);
                                }
                        }
                }
@@ -5419,7 +5183,7 @@ static GSList *cm_split_path(const gchar *filename, int depth)
 {
        gchar **path_parts;
        GSList *canonical_parts = NULL;
-       struct stat st;
+       GStatBuf st;
        int i;
        gboolean follow_symlinks = TRUE;
 
@@ -5550,6 +5314,21 @@ int cm_canonicalize_filename(const gchar *filename, gchar **canonical_name) {
        return 0;
 }
 
+/* Returns a decoded base64 string, guaranteed to be null-terminated. */
+guchar *g_base64_decode_zero(const gchar *text, gsize *out_len)
+{
+       gchar *tmp = g_base64_decode(text, out_len);
+       gchar *out = g_strndup(tmp, *out_len);
+
+       g_free(tmp);
+
+       if (strlen(out) != *out_len) {
+               g_warning ("strlen(out) %zd != *out_len %" G_GSIZE_FORMAT, strlen(out), *out_len);
+       }
+
+       return out;
+}
+
 #if !GLIB_CHECK_VERSION(2, 30, 0)
 /**
  * g_utf8_substring:
@@ -5583,3 +5362,57 @@ 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) {
+               perror("open on /dev/urandom");
+               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 %d random bytes.\n", count);
+               CryptReleaseContext(rnd, 0);
+               return FALSE;
+       }
+#else
+       ret = read(rnd, buf, count);
+       if (ret != count) {
+               perror("read from /dev/urandom");
+               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;
+}