2012-05-11 [colin] 3.8.0cvs40
[claws.git] / src / common / utils.c
index 55634b7a9a0d58239e7ee1ae0847e97b10f779e9..3b82ed01ff7c1aaea29bd9c62d909853e73d1f1a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2009 Hiroyuki Yamamoto & The Claws Mail Team
+ * Copyright (C) 1999-2011 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
 
 #include <glib/gi18n.h>
 
+#ifdef USE_PTHREAD
+#include <pthread.h>
+#endif
+
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
@@ -293,7 +297,7 @@ void ptr_array_free_strings(GPtrArray *array)
        gint i;
        gchar *str;
 
-       g_return_if_fail(array != NULL);
+       cm_return_if_fail(array != NULL);
 
        for (i = 0; i < array->len; i++) {
                str = g_ptr_array_index(array, i);
@@ -301,16 +305,6 @@ void ptr_array_free_strings(GPtrArray *array)
        }
 }
 
-gboolean str_find(const gchar *haystack, const gchar *needle)
-{
-       return strstr(haystack, needle) != NULL ? TRUE : FALSE;
-}
-
-gboolean str_case_find(const gchar *haystack, const gchar *needle)
-{
-       return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
-}
-
 gint to_number(const gchar *nstr)
 {
        register const gchar *p;
@@ -346,6 +340,13 @@ gchar *itos(gint n)
        d = (d*100) >> divisor;         \
 }
 
+
+/*!
+ * \brief Convert a given size in bytes in a human-readable string
+ *
+ * \param size  The size expressed in bytes to convert in string
+ * \return      The string that respresents the size in an human-readable way
+ */
 gchar *to_human_readable(goffset size)
 {
        static gchar str[14];
@@ -504,7 +505,7 @@ gint file_strip_crs(const gchar *file)
                goto unlinkout;
        }
        
-       if (rename_force(out, file) < 0)
+       if (move_file(out, file, TRUE) < 0)
                goto unlinkout;
        
        g_free(out);
@@ -990,20 +991,23 @@ GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
 GList *add_history(GList *list, const gchar *str)
 {
        GList *old;
+       gchar *oldstr;
 
-       g_return_val_if_fail(str != NULL, list);
+       cm_return_val_if_fail(str != NULL, list);
 
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
        if (old) {
-               g_free(old->data);
+               oldstr = old->data;
                list = g_list_remove(list, old->data);
+               g_free(oldstr);
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
                GList *last;
 
                last = g_list_last(list);
                if (last) {
-                       g_free(last->data);
+                       oldstr = last->data;
                        list = g_list_remove(list, last->data);
+                       g_free(oldstr);
                }
        }
 
@@ -1121,7 +1125,7 @@ static const gchar * line_has_quote_char_last(const gchar * str, const gchar *qu
        int i;
 
        if (quote_chars == NULL)
-               return FALSE;
+               return NULL;
 
        for (i = 0; i < strlen(quote_chars); i++) {
                tmp_pos = strrchr (str, quote_chars[i]);
@@ -1159,8 +1163,8 @@ gint get_quote_level(const gchar *str, const gchar *quote_chars)
                if (strchr(quote_chars, *p))
                        quote_level++;
                else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
-                       /* any characters are allowed except '-' and space */
-                       while (*p != '-'
+                       /* any characters are allowed except '-','<' and space */
+                       while (*p != '-' && *p != '<'
                               && !strchr(quote_chars, *p)
                               && !g_ascii_isspace(*p)
                               && p < last_pos)
@@ -1265,8 +1269,8 @@ gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
        gchar **str_array, *s, *new_str;
        guint i, n = 1, len;
 
-       g_return_val_if_fail(str != NULL, NULL);
-       g_return_val_if_fail(delim != NULL, NULL);
+       cm_return_val_if_fail(str != NULL, NULL);
+       cm_return_val_if_fail(delim != NULL, NULL);
 
        if (max_tokens < 1)
                max_tokens = G_MAXINT;
@@ -1325,7 +1329,7 @@ gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
        const gchar *p = group;
        const gchar *last;
 
-       g_return_val_if_fail(group != NULL, NULL);
+       cm_return_val_if_fail(group != NULL, NULL);
 
        last = group + strlen(group);
        abbrev_group = ap = g_malloc(strlen(group) + 1);
@@ -1544,7 +1548,7 @@ static gchar *decode_uri_gdup(const gchar *encoded_uri)
 }
 
 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
-                    gchar **subject, gchar **body, gchar ***attach)
+                    gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
 {
        gchar *tmp_mailto;
        gchar *p;
@@ -1628,8 +1632,8 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                        *body = decode_uri_gdup(value);
                } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
                        gchar *tmp = decode_uri_gdup(value);
-                       if (!g_file_get_contents(value, body, NULL, NULL)) {
-                               g_error("Error: couldn't set insert file '%s' in body\n", value);
+                       if (!g_file_get_contents(tmp, body, NULL, NULL)) {
+                               g_warning("Error: couldn't set insert file '%s' in body\n", value);
                        }
                        g_free(tmp);
                        tmp = NULL;
@@ -1652,6 +1656,9 @@ gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc,
                                my_att[num_attach-1] = tmp;
                                my_att[num_attach] = NULL;
                        }
+               } else if (inreplyto && !*inreplyto &&
+                          !g_ascii_strcasecmp(field, "in-reply-to")) {
+                       *inreplyto = decode_uri_gdup(value);
                }
        }
 
@@ -1986,6 +1993,16 @@ const gchar *get_cert_file(void)
 }
 #endif
 
+/* Return the filepath of the claws-mail.desktop file */
+const gchar *get_desktop_file(void)
+{
+#ifdef DESKTOPFILEPATH
+  return DESKTOPFILEPATH;
+#else
+  return NULL;
+#endif
+}
+
 /* Return the default directory for Plugins. */
 const gchar *get_plugin_dir(void)
 {
@@ -2759,6 +2776,9 @@ gint canonicalize_file(const gchar *src, const gchar *dest)
        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;
@@ -3037,7 +3057,7 @@ FILE *str_open_as_stream(const gchar *str)
        FILE *fp;
        size_t len;
 
-       g_return_val_if_fail(str != NULL, NULL);
+       cm_return_val_if_fail(str != NULL, NULL);
 
        fp = my_tmpfile();
        if (!fp) {
@@ -3063,8 +3083,8 @@ gint str_write_to_file(const gchar *str, const gchar *file)
        FILE *fp;
        size_t len;
 
-       g_return_val_if_fail(str != NULL, -1);
-       g_return_val_if_fail(file != NULL, -1);
+       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");
@@ -3100,7 +3120,7 @@ static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
        gint n_read;
        gchar *str;
 
-       g_return_val_if_fail(fp != NULL, NULL);
+       cm_return_val_if_fail(fp != NULL, NULL);
 
        array = g_byte_array_new();
 
@@ -3146,7 +3166,7 @@ static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
        int fflags = 0;
 #endif
 
-       g_return_val_if_fail(file != NULL, NULL);
+       cm_return_val_if_fail(file != NULL, NULL);
 
        if (g_stat(file, &s) != 0) {
                FILE_OP_ERROR(file, "stat");
@@ -3165,7 +3185,7 @@ static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
        }
 #else    
        /* test whether the file is readable without blocking */
-       fd = g_open(file, O_RDONLY | O_NONBLOCK);
+       fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
        if (fd == -1) {
                FILE_OP_ERROR(file, "open");
                return NULL;
@@ -3266,7 +3286,7 @@ char *fgets_crlf(char *buf, int size, FILE *stream)
 
 static gint execute_async(gchar *const argv[])
 {
-       g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
+       cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
 
        if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
                          NULL, NULL, NULL, FALSE) == FALSE) {
@@ -3281,20 +3301,27 @@ static gint execute_sync(gchar *const argv[])
 {
        gint status;
 
-       g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
+       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,
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
                g_warning("Couldn't execute command: %s\n", argv[0]);
                return -1;
        }
 
-#ifdef G_OS_UNIX
        if (WIFEXITED(status))
                return WEXITSTATUS(status);
        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,
+                        NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
+               g_warning("Couldn't execute command: %s\n", argv[0]);
+               return -1;
+       }
+
        return status;
 #endif
 }
@@ -3323,7 +3350,7 @@ gchar *get_command_output(const gchar *cmdline)
        gchar *child_stdout;
        gint status;
 
-       g_return_val_if_fail(cmdline != NULL, NULL);
+       cm_return_val_if_fail(cmdline != NULL, NULL);
 
        debug_print("get_command_output(): executing: %s\n", cmdline);
 
@@ -3379,7 +3406,7 @@ gint open_uri(const gchar *uri, const gchar *cmdline)
        gchar buf[BUFFSIZE];
        gchar *p;
        gchar encoded_uri[BUFFSIZE];
-       g_return_val_if_fail(uri != NULL, -1);
+       cm_return_val_if_fail(uri != NULL, -1);
 
        /* an option to choose whether to use encode_uri or not ? */
        encode_uri(encoded_uri, BUFFSIZE, uri);
@@ -3414,7 +3441,7 @@ gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
        gchar buf[BUFFSIZE];
        gchar *p;
 
-       g_return_val_if_fail(filepath != NULL, -1);
+       cm_return_val_if_fail(filepath != NULL, -1);
 
        if (cmdline &&
            (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
@@ -3505,9 +3532,7 @@ time_t tzoffset_sec(time_t *now)
 {
        struct tm gmt, *lt;
        gint off;
-#ifndef G_OS_WIN32
        struct tm buf1, buf2;
-#endif
 #ifdef G_OS_WIN32
        if (now && *now < 0)
                return 0;
@@ -3541,9 +3566,7 @@ gchar *tzoffset(time_t *now)
        struct tm gmt, *lt;
        gint off;
        gchar sign = '+';
-#ifndef G_OS_WIN32
        struct tm buf1, buf2;
-#endif
 #ifdef G_OS_WIN32
        if (now && *now < 0)
                return 0;
@@ -3581,10 +3604,8 @@ void get_rfc822_date(gchar *buf, gint len)
        time_t t;
        gchar day[4], mon[4];
        gint dd, hh, mm, ss, yyyy;
-#ifndef G_OS_WIN32
        struct tm buf1;
        gchar buf2[BUFFSIZE];
-#endif
 
        t = time(NULL);
        lt = localtime_r(&t, &buf1);
@@ -3656,18 +3677,6 @@ void subject_table_remove(GHashTable *subject_table, gchar * subject)
        g_hash_table_remove(subject_table, subject);
 }
 
-/*!
- *\brief       Check if a string is prefixed with known (combinations)
- *             of prefixes. The function assumes that each prefix
- *             is terminated by zero or exactly _one_ space.
- *
- *\param       str String to check for a prefixes
- *
- *\return      int Number of chars in the prefix that should be skipped
- *             for a "clean" subject line. If no prefix was found, 0
- *             is returned.
- */
 #ifndef G_OS_WIN32
 static regex_t u_regex;
 static gboolean u_init_;
@@ -3683,6 +3692,17 @@ void utils_free_regex(void)
 #endif
 }
 
+/*!
+ *\brief       Check if a string is prefixed with known (combinations)
+ *             of prefixes. The function assumes that each prefix
+ *             is terminated by zero or exactly _one_ space.
+ *
+ *\param       str String to check for a prefixes
+ *
+ *\return      int Number of chars in the prefix that should be skipped
+ *             for a "clean" subject line. If no prefix was found, 0
+ *             is returned.
+ */
 int subject_get_prefix_length(const gchar *subject)
 {
 #ifndef G_OS_WIN32
@@ -3702,7 +3722,9 @@ int subject_get_prefix_length(const gchar *subject)
                "Sv\\:",                        /* "Sv" (Norwegian) */
                "Vs\\:",                        /* "Vs" (Norwegian) */
                "Ad\\:",                        /* "Ad" (Norwegian) */
-               "\347\255\224\345\244\215\\:"   /* "Re" (Chinese, UTF-8) */
+               "\347\255\224\345\244\215\\:",  /* "Re" (Chinese, UTF-8) */
+               "R\303\251f\\. \\:",            /* "Réf. :" (French Lotus Notes) */
+               "Re \\:",                       /* "Re :" (French Yahoo Mail) */
                /* add more */
        };
        const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
@@ -3760,6 +3782,8 @@ int subject_get_prefix_length(const gchar *subject)
                "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];
@@ -3770,8 +3794,12 @@ int subject_get_prefix_length(const gchar *subject)
 
        for (n = 0; n < PREFIXES; n++) {
                int len = strlen(prefixes[n]);
-               if (!strncasecmp(subject, prefixes[n], len))
-                       return len;
+               if (!strncasecmp(subject, prefixes[n], len)) {
+                       if (subject[len] == ' ')
+                               return len+1;
+                       else
+                               return len;
+               }
        }
        return 0;
 #endif
@@ -3801,24 +3829,28 @@ 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 *generate_msgid(gchar *buf, gint len, gchar *user_addr)
 {
        struct tm *lt;
        time_t t;
        gchar *addr;
-#ifndef G_OS_WIN32
        struct tm buft;
-#endif
 
        t = time(NULL);
        lt = localtime_r(&t, &buft);
 
-       if (strcmp(buf, "") == 0) {
-               addr = g_strconcat("@", get_domain_name(), NULL);
-       }
-       else {
-               addr = g_strconcat("@", buf, NULL);
-       }
+       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,
@@ -3918,8 +3950,8 @@ GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
        GNode *root;
        GNodeMapData mapdata;
 
-       g_return_val_if_fail(node != NULL, NULL);
-       g_return_val_if_fail(func != NULL, NULL);
+       cm_return_val_if_fail(node != NULL, NULL);
+       cm_return_val_if_fail(func != NULL, NULL);
 
        root = g_node_new(func(node->data, data));
 
@@ -4196,10 +4228,10 @@ gboolean get_uri_part(const gchar *start, const gchar *scanpos,
        const gchar *ep_;
        gint parenthese_cnt = 0;
 
-       g_return_val_if_fail(start != NULL, FALSE);
-       g_return_val_if_fail(scanpos != NULL, FALSE);
-       g_return_val_if_fail(bp != NULL, FALSE);
-       g_return_val_if_fail(ep != NULL, FALSE);
+       cm_return_val_if_fail(start != NULL, FALSE);
+       cm_return_val_if_fail(scanpos != NULL, FALSE);
+       cm_return_val_if_fail(bp != NULL, FALSE);
+       cm_return_val_if_fail(ep != NULL, FALSE);
 
        *bp = scanpos;
 
@@ -4225,7 +4257,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));
@@ -4292,7 +4324,7 @@ static GHashTable *create_domain_tab(void)
        gint n;
        GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
 
-       g_return_val_if_fail(htab, NULL);
+       cm_return_val_if_fail(htab, NULL);
        for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
                g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
        return htab;
@@ -4335,10 +4367,10 @@ gboolean get_email_part(const gchar *start, const gchar *scanpos,
        gchar closure_stack[128];
        gchar *ptr = closure_stack;
 
-       g_return_val_if_fail(start != NULL, FALSE);
-       g_return_val_if_fail(scanpos != NULL, FALSE);
-       g_return_val_if_fail(bp != NULL, FALSE);
-       g_return_val_if_fail(ep != NULL, FALSE);
+       cm_return_val_if_fail(start != NULL, FALSE);
+       cm_return_val_if_fail(scanpos != NULL, FALSE);
+       cm_return_val_if_fail(bp != NULL, FALSE);
+       cm_return_val_if_fail(ep != NULL, FALSE);
 
        if (hdr) {
                const gchar *start_quote = NULL;
@@ -4404,7 +4436,7 @@ search_again:
 
        if (!dom_tab)
                dom_tab = create_domain_tab();
-       g_return_val_if_fail(dom_tab, FALSE);
+       cm_return_val_if_fail(dom_tab, FALSE);
 
        /* scan start of address */
        for (bp_ = scanpos - 1;
@@ -4812,9 +4844,11 @@ gboolean file_is_email (const gchar *filename)
               && fgets(buffer, sizeof (buffer), fp) > 0) {
                if (!strncmp(buffer, "From:", strlen("From:")))
                        score++;
-               if (!strncmp(buffer, "To:", strlen("To:")))
+               else if (!strncmp(buffer, "Date:", strlen("Date:")))
+                       score++;
+               else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
                        score++;
-               if (!strncmp(buffer, "Subject:", strlen("Subject:")))
+               else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
                        score++;
                i++;
        }