* src/imap.c
[claws.git] / src / utils.c
index c072f7436c133f92bf5c3c3d4a55de54d20ed4a6..ff10b1d117724fe0748de3b73a80145756f2a182 100644 (file)
@@ -28,7 +28,6 @@
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
-#include <netdb.h>
 
 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
 #  include <wchar.h>
@@ -45,6 +44,7 @@
 
 #include "intl.h"
 #include "utils.h"
+#include "socket.h"
 #include "statusbar.h"
 #include "logwindow.h"
 
@@ -1349,6 +1349,63 @@ GList *uri_list_extract_filenames(const gchar *uri_list)
        } \
 }
 
+gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
+                    gchar **subject, gchar **body)
+{
+       gchar *tmp_mailto;
+       gchar *p;
+
+       Xstrdup_a(tmp_mailto, mailto, return -1);
+
+       if (!strncmp(tmp_mailto, "mailto:", 7))
+               tmp_mailto += 7;
+
+       p = strchr(tmp_mailto, '?');
+       if (p) {
+               *p = '\0';
+               p++;
+       }
+
+       if (to && !*to)
+               *to = g_strdup(tmp_mailto);
+
+       while (p) {
+               gchar *field, *value;
+
+               field = p;
+
+               p = strchr(p, '=');
+               if (!p) break;
+               *p = '\0';
+               p++;
+
+               value = p;
+
+               p = strchr(p, '&');
+               if (p) {
+                       *p = '\0';
+                       p++;
+               }
+
+               if (*value == '\0') continue;
+
+               if (cc && !*cc && !g_strcasecmp(field, "cc")) {
+                       *cc = g_strdup(value);
+               } else if (bcc && !*bcc && !g_strcasecmp(field, "bcc")) {
+                       *bcc = g_strdup(value);
+               } else if (subject && !*subject &&
+                          !g_strcasecmp(field, "subject")) {
+                       *subject = g_malloc(strlen(value) + 1);
+                       decode_uri(*subject, value);
+               } else if (body && !*body && !g_strcasecmp(field, "body")) {
+                       *body = g_malloc(strlen(value) + 1);
+                       decode_uri(*body, value);
+               }
+       }
+
+       return 0;
+}
+
 /*
  * We need this wrapper around g_get_home_dir(), so that
  * we can fix some Windoze things here.  Should be done in glibc of course
@@ -1457,13 +1514,24 @@ gchar *get_header_cache_dir(void)
        return header_dir;
 }
 
+gchar *get_tmp_dir(void)
+{
+       static gchar *tmp_dir = NULL;
+
+       if (!tmp_dir)
+               tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                                     TMP_DIR, NULL);
+
+       return tmp_dir;
+}
+
 gchar *get_tmp_file(void)
 {
-       static gchar *tmp_file = NULL;
+       gchar *tmp_file;
+       static guint32 id = 0;
 
-       if (!tmp_file)
-               tmp_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
-                                      "tmpfile", NULL);
+       tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
+                                  get_tmp_dir(), G_DIR_SEPARATOR, id++);
 
        return tmp_file;
 }
@@ -1471,26 +1539,25 @@ gchar *get_tmp_file(void)
 gchar *get_domain_name(void)
 {
        static gchar *domain_name = NULL;
-        struct hostent *myfqdn = NULL;
 
        if (!domain_name) {
-               gchar buf[BUFFSIZE] = "";
+               gchar buf[128] = "";
+               struct hostent *hp;
 
                if (gethostname(buf, sizeof(buf)) < 0) {
                        perror("gethostname");
-                       strcpy(buf, "unknown");
-               }  else  {
-                myfqdn = gethostbyname(buf);
-                if (myfqdn != NULL)  {
-                  memset(buf, '\0', strlen(buf));
-                  strcpy(buf, myfqdn->h_name);
-                  }  else  {
-                  perror("gethostbyname");
-                  strcpy(buf, "unknown");
-                  }
-                }
+                       domain_name = "unknown";
+               } else {
+                       buf[sizeof(buf) - 1] = '\0';
+                       if ((hp = my_gethostbyname(buf)) == NULL) {
+                               perror("gethostbyname");
+                               domain_name = g_strdup(buf);
+                       } else {
+                               domain_name = g_strdup(hp->h_name);
+                       }
+               }
 
-               domain_name = g_strdup(buf);
+               debug_print("domain name = %s\n", domain_name);
        }
 
        return domain_name;
@@ -1565,6 +1632,9 @@ gboolean file_exist(const gchar *file, gboolean allow_fifo)
 {
        struct stat s;
 
+       if (file == NULL)
+               return FALSE;
+
        if (stat(file, &s) < 0) {
                if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
                return FALSE;
@@ -1580,6 +1650,9 @@ gboolean is_dir_exist(const gchar *dir)
 {
        struct stat s;
 
+       if (dir == NULL)
+               return FALSE;
+
        if (stat(dir, &s) < 0) {
                if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
                return FALSE;
@@ -1595,6 +1668,9 @@ gboolean is_file_entry_exist(const gchar *file)
 {
        struct stat s;
 
+       if (file == NULL)
+               return FALSE;
+
        if (stat(file, &s) < 0) {
                if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
                return FALSE;
@@ -1675,11 +1751,13 @@ gint remove_all_files(const gchar *dir)
 
        if (chdir(dir) < 0) {
                FILE_OP_ERROR(dir, "chdir");
+               g_free(prev_dir);
                return -1;
        }
 
        if ((dp = opendir(".")) == NULL) {
                FILE_OP_ERROR(dir, "opendir");
+               g_free(prev_dir);
                return -1;
        }
 
@@ -1716,11 +1794,13 @@ gint remove_numbered_files(const gchar *dir, guint first, guint last)
 
        if (chdir(dir) < 0) {
                FILE_OP_ERROR(dir, "chdir");
+               g_free(prev_dir);
                return -1;
        }
 
        if ((dp = opendir(".")) == NULL) {
                FILE_OP_ERROR(dir, "opendir");
+               g_free(prev_dir);
                return -1;
        }
 
@@ -1747,6 +1827,51 @@ gint remove_numbered_files(const gchar *dir, guint first, guint last)
        return 0;
 }
 
+gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
+{
+       DIR *dp;
+       struct dirent *d;
+       gchar *prev_dir;
+       gint fileno;
+
+       prev_dir = g_get_current_dir();
+
+       if (chdir(dir) < 0) {
+               FILE_OP_ERROR(dir, "chdir");
+               g_free(prev_dir);
+               return -1;
+       }
+
+       if ((dp = opendir(".")) == NULL) {
+               FILE_OP_ERROR(dir, "opendir");
+               g_free(prev_dir);
+               return -1;
+       }
+
+       while ((d = readdir(dp)) != NULL) {
+               fileno = to_number(d->d_name);
+               if (fileno >= 0 && (g_slist_find(numberlist, GINT_TO_POINTER(fileno)) == NULL)) {
+                       debug_print("removing unwanted file %d from %s\n", fileno, dir);
+                       if (is_dir_exist(d->d_name))
+                               continue;
+                       if (unlink(d->d_name) < 0)
+                               FILE_OP_ERROR(d->d_name, "unlink");
+               }
+       }
+
+       closedir(dp);
+
+       if (chdir(prev_dir) < 0) {
+               FILE_OP_ERROR(prev_dir, "chdir");
+               g_free(prev_dir);
+               return -1;
+       }
+
+       g_free(prev_dir);
+
+       return 0;
+}
+
 gint remove_all_numbered_files(const gchar *dir)
 {
        return remove_numbered_files(dir, 0, UINT_MAX);
@@ -1765,11 +1890,13 @@ gint remove_expired_files(const gchar *dir, guint hours)
 
        if (chdir(dir) < 0) {
                FILE_OP_ERROR(dir, "chdir");
+               g_free(prev_dir);
                return -1;
        }
 
        if ((dp = opendir(".")) == NULL) {
                FILE_OP_ERROR(dir, "opendir");
+               g_free(prev_dir);
                return -1;
        }
 
@@ -1975,7 +2102,66 @@ gint copy_file(const gchar *src, const gchar *dest)
 }
 #endif
 
-gint copy_file(const gchar *src, const gchar *dest)
+
+/*
+ * Append src file body to the tail of dest file.
+ * Now keep_backup has no effects.
+ */
+gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
+{
+       FILE *src_fp, *dest_fp;
+       gint n_read;
+       gchar buf[BUFSIZ];
+
+       gboolean err = FALSE;
+
+       if ((src_fp = fopen(src, "rb")) == NULL) {
+               FILE_OP_ERROR(src, "fopen");
+               return -1;
+       }
+       
+       if ((dest_fp = fopen(dest, "ab")) == NULL) {
+               FILE_OP_ERROR(dest, "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\n"));
+       }
+
+       while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
+               if (n_read < sizeof(buf) && ferror(src_fp))
+                       break;
+               if (fwrite(buf, n_read, 1, dest_fp) < 1) {
+                       g_warning(_("writing to %s failed.\n"), dest);
+                       fclose(dest_fp);
+                       fclose(src_fp);
+                       unlink(dest);
+                       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;
+       }
+
+       if (err) {
+               unlink(dest);
+               return -1;
+       }
+
+       return 0;
+}
+
+gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
 {
        FILE *src_fp, *dest_fp;
        gint n_read;
@@ -2050,15 +2236,18 @@ gint copy_file(const gchar *src, const gchar *dest)
                return -1;
        }
 
+       if (keep_backup == FALSE && dest_bak)
+               unlink(dest_bak);
+
        g_free(dest_bak);
 
        return 0;
 }
 
-gint move_file(const gchar *src, const gchar *dest)
+gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
 {
-       if (is_file_exist(dest)) {
-               g_warning(_("move_file(): file %s already exists."), dest);
+       if (overwrite == FALSE && is_file_exist(dest)) {
+               g_warning("move_file(): file %s already exists.", dest);
                return -1;
        }
 
@@ -2069,13 +2258,71 @@ gint move_file(const gchar *src, const gchar *dest)
                return -1;
        }
 
-       if (copy_file(src, dest) < 0) return -1;
+       if (copy_file(src, dest, FALSE) < 0) return -1;
 
        unlink(src);
 
        return 0;
 }
 
+gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
+{
+       FILE *dest_fp;
+       gint n_read;
+       gint bytes_left, to_read;
+       gchar buf[BUFSIZ];
+       gboolean err = FALSE;
+
+       if (fseek(fp, offset, SEEK_SET) < 0) {
+               perror("fseek");
+               return -1;
+       }
+
+       if ((dest_fp = fopen(dest, "wb")) == NULL) {
+               FILE_OP_ERROR(dest, "fopen");
+               return -1;
+       }
+
+       if (change_file_mode_rw(dest_fp, dest) < 0) {
+               FILE_OP_ERROR(dest, "chmod");
+               g_warning("can't change file mode\n");
+       }
+
+       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, n_read, 1, dest_fp) < 1) {
+                       g_warning(_("writing to %s failed.\n"), dest);
+                       fclose(dest_fp);
+                       unlink(dest);
+                       return -1;
+               }
+               bytes_left -= n_read;
+               if (bytes_left == 0)
+                       break;
+               to_read = MIN(bytes_left, sizeof(buf));
+       }
+
+       if (ferror(fp)) {
+               perror("fread");
+               err = TRUE;
+       }
+       if (fclose(dest_fp) == EOF) {
+               FILE_OP_ERROR(dest, "fclose");
+               err = TRUE;
+       }
+
+       if (err) {
+               unlink(dest);
+               return -1;
+       }
+
+       return 0;
+}
+
 /* convert line endings into CRLF. If the last line doesn't end with
  * linebreak, add it.
  */
@@ -2116,8 +2363,11 @@ gint canonicalize_file(const gchar *src, const gchar *dest)
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
                        r = fputs(buf, dest_fp);
                } else {
-                       if (len > 1)
+                       if (len > 1) {
                                r = fwrite(buf, len - 1, 1, dest_fp);
+                               if (r != 1)
+                                       r = EOF;
+                       }
                        if (r != EOF)
                                r = fputs("\r\n", dest_fp);
                }
@@ -2160,15 +2410,19 @@ gint canonicalize_file_replace(const gchar *file)
 
        tmp_file = get_tmp_file();
 
-       if (canonicalize_file(file, tmp_file) < 0)
+       if (canonicalize_file(file, tmp_file) < 0) {
+               g_free(tmp_file);
                return -1;
+       }
 
-       if (rename(tmp_file, file) < 0) {
-               FILE_OP_ERROR(file, "rename");
+       if (move_file(tmp_file, file, TRUE) < 0) {
+               g_warning("can't replace %s .\n", file);
                unlink(tmp_file);
+               g_free(tmp_file);
                return -1;
        }
 
+       g_free(tmp_file);
        return 0;
 }
 
@@ -2193,7 +2447,7 @@ FILE *my_tmpfile(void)
        gint fd;
        FILE *fp;
 
-       tmpdir = g_get_tmp_dir();
+       tmpdir = get_tmp_dir();
        tmplen = strlen(tmpdir);
        progname = g_get_prgname();
        proglen = strlen(progname);
@@ -2247,6 +2501,81 @@ FILE *str_open_as_stream(const gchar *str)
        return fp;
 }
 
+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);
+
+       if ((fp = fopen(file, "wb")) == NULL) {
+               FILE_OP_ERROR(file, "fopen");
+               return -1;
+       }
+
+       len = strlen(str);
+       if (len == 0) {
+               fclose(fp);
+               return 0;
+       }
+
+       if (fwrite(str, len, 1, fp) != 1) {
+               FILE_OP_ERROR(file, "fwrite");
+               fclose(fp);
+               unlink(file);
+               return -1;
+       }
+
+       if (fclose(fp) == EOF) {
+               FILE_OP_ERROR(file, "fclose");
+               unlink(file);
+               return -1;
+       }
+
+       return 0;
+}
+
+gchar *file_read_to_str(const gchar *file)
+{
+       GByteArray *array;
+       FILE *fp;
+       gchar buf[BUFSIZ];
+       gint n_read;
+       gchar *str;
+
+       g_return_val_if_fail(file != NULL, NULL);
+
+       if ((fp = fopen(file, "rb")) == NULL) {
+               FILE_OP_ERROR(file, "fopen");
+               return 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, "fread");
+               fclose(fp);
+               g_byte_array_free(array, TRUE);
+               return NULL;
+       }
+
+       fclose(fp);
+
+       buf[0] = '\0';
+       g_byte_array_append(array, buf, 1);
+       str = (gchar *)array->data;
+       g_byte_array_free(array, FALSE);
+
+       return str;
+}
+
 gint execute_async(gchar *const argv[])
 {
        pid_t pid;
@@ -2782,3 +3111,13 @@ gboolean subject_is_reply(const gchar *subject)
        if (subject == NULL) return FALSE;
        else return 0 == g_strncasecmp(subject, "Re: ", 4);
 }
+
+FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
+{
+       int fd;
+       
+       *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
+       fd = mkstemp(*filename);
+
+       return fdopen(fd, "w+");
+}