...and one more forgotten NULL check.
[claws.git] / src / passwordstore.c
index b53ea77e21ab4be8e3b33d59e60cfa3aa2b9ef0e..0564d473b0ea982944d2de8ae4788b21ecfb8a19 100644 (file)
 #include "common/utils.h"
 #include "passwordstore.h"
 #include "password.h"
+#include "prefs_common.h"
 #include "prefs_gtk.h"
+#include "prefs_migration.h"
+#include "file-utils.h"
 
 static GSList *_password_store;
 
@@ -45,8 +48,7 @@ static PasswordBlock *_get_block(PasswordBlockType block_type,
        GSList *item;
        PasswordBlock *block;
 
-       g_return_val_if_fail(block_type >= 0 && block_type < NUM_PWS_TYPES,
-                       NULL);
+       g_return_val_if_fail(block_type < NUM_PWS_TYPES, NULL);
        g_return_val_if_fail(block_name != NULL, NULL);
 
        for (item = _password_store; item != NULL; item = item->next) {
@@ -72,8 +74,7 @@ static PasswordBlock *_new_block(PasswordBlockType block_type,
 {
        PasswordBlock *block;
 
-       g_return_val_if_fail(block_type >= 0 && block_type < NUM_PWS_TYPES,
-                       NULL);
+       g_return_val_if_fail(block_type < NUM_PWS_TYPES, NULL);
        g_return_val_if_fail(block_name != NULL, NULL);
 
        /* First check to see if the block doesn't already exist. */
@@ -98,7 +99,7 @@ static PasswordBlock *_new_block(PasswordBlockType block_type,
        return block;
 }
 
-///////////////////////////////////////////////////////////////
+/*************************************************************/
 
 /* Stores a password. */
 gboolean passwd_store_set(PasswordBlockType block_type,
@@ -111,8 +112,7 @@ gboolean passwd_store_set(PasswordBlockType block_type,
        PasswordBlock *block;
        gchar *encrypted_password;
 
-       g_return_val_if_fail(block_type >= 0 && block_type < NUM_PWS_TYPES,
-                       FALSE);
+       g_return_val_if_fail(block_type < NUM_PWS_TYPES, FALSE);
        g_return_val_if_fail(block_name != NULL, FALSE);
        g_return_val_if_fail(password_id != NULL, FALSE);
 
@@ -122,12 +122,7 @@ gboolean passwd_store_set(PasswordBlockType block_type,
        else
                p = password;
 
-       debug_print("%s password '%s' in block (%d/%s)%s\n",
-                       (p == NULL ? "Deleting" : "Storing"),
-                       password_id, block_type, block_name,
-                       (encrypted ? ", already encrypted" : "") );
-
-       // find correct block (create if needed)
+       /* find correct block (create if needed) */
        if ((block = _get_block(block_type, block_name)) == NULL) {
                /* If caller wants to delete a password, and even its block
                 * doesn't exist, we're done. */
@@ -141,6 +136,11 @@ gboolean passwd_store_set(PasswordBlockType block_type,
                }
        }
 
+       debug_print("%s password for '%s' in block (%d/%s)%s\n",
+                       (p == NULL ? "Deleting" : "Storing"),
+                       password_id, block_type, block_name,
+                       (encrypted ? ", already encrypted" : "") );
+
        if (p == NULL) {
                /* NULL password was passed to us, so delete the entry with
                 * corresponding id */
@@ -159,7 +159,7 @@ gboolean passwd_store_set(PasswordBlockType block_type,
                        encrypted_password = g_strdup(p);
                }
 
-               // add encrypted password to the block
+               /* add encrypted password to the block */
                g_hash_table_insert(block->entries,
                                g_strdup(password_id),
                                encrypted_password);
@@ -176,21 +176,20 @@ gchar *passwd_store_get(PasswordBlockType block_type,
        PasswordBlock *block;
        gchar *encrypted_password, *password;
 
-       g_return_val_if_fail(block_type >= 0 && block_type < NUM_PWS_TYPES,
-                       NULL);
+       g_return_val_if_fail(block_type < NUM_PWS_TYPES, NULL);
        g_return_val_if_fail(block_name != NULL, NULL);
        g_return_val_if_fail(password_id != NULL, NULL);
 
        debug_print("Getting password '%s' from block (%d/%s)\n",
                        password_id, block_type, block_name);
 
-       // find correct block
+       /* find correct block */
        if ((block = _get_block(block_type, block_name)) == NULL) {
                debug_print("Block (%d/%s) not found.\n", block_type, block_name);
                return NULL;
        }
 
-       // grab pointer to encrypted password
+       /* grab pointer to encrypted password */
        if ((encrypted_password =
                                g_hash_table_lookup(block->entries, password_id)) == NULL) {
                debug_print("Password '%s' in block (%d/%s) not found.\n",
@@ -198,7 +197,7 @@ gchar *passwd_store_get(PasswordBlockType block_type,
                return NULL;
        }
 
-       // decrypt password
+       /* decrypt password */
        if ((password =
                                password_decrypt(encrypted_password, NULL)) == NULL) {
                debug_print("Could not decrypt password '%s' for block (%d/%s).\n",
@@ -206,22 +205,48 @@ gchar *passwd_store_get(PasswordBlockType block_type,
                return NULL;
        }
 
-       // return decrypted password
+       /* return decrypted password */
        return password;
 }
 
+/* Checks if a password exists in the password store.
+ * No decryption happens. */
+gboolean passwd_store_has_password(PasswordBlockType block_type,
+               const gchar *block_name,
+               const gchar *password_id)
+{
+       PasswordBlock *block;
+
+       g_return_val_if_fail(block_type < NUM_PWS_TYPES, FALSE);
+       g_return_val_if_fail(block_name != NULL, FALSE);
+       g_return_val_if_fail(password_id != NULL, FALSE);
+
+       /* find correct block */
+       if ((block = _get_block(block_type, block_name)) == NULL) {
+               debug_print("Block (%d/%s) not found.\n", block_type, block_name);
+               return FALSE;
+       }
+
+       /* do we have specified password in this block? */
+       if (g_hash_table_lookup(block->entries, password_id) != NULL) {
+               return TRUE; /* yes */
+       }
+
+       return FALSE; /* no */
+}
+
+
 gboolean passwd_store_delete_block(PasswordBlockType block_type,
                const gchar *block_name)
 {
        PasswordBlock *block;
 
-       g_return_val_if_fail(block_type >= 0 && block_type < NUM_PWS_TYPES,
-                       FALSE);
+       g_return_val_if_fail(block_type < NUM_PWS_TYPES, FALSE);
        g_return_val_if_fail(block_name != NULL, FALSE);
 
        debug_print("Deleting block (%d/%s)\n", block_type, block_name);
 
-       // find correct block
+       /* find correct block */
        if ((block = _get_block(block_type, block_name)) == NULL) {
                debug_print("Block (%d/%s) not found.\n", block_type, block_name);
                return FALSE;
@@ -253,6 +278,15 @@ gchar *passwd_store_get_account(gint account_id,
        return ret;
 }
 
+gboolean passwd_store_has_password_account(gint account_id,
+               const gchar *password_id)
+{
+       gchar *uid = g_strdup_printf("%d", account_id);
+       gboolean ret = passwd_store_has_password(PWS_ACCOUNT, uid, password_id);
+       g_free(uid);
+       return ret;
+}
+
 /* Reencrypts all stored passwords. */
 void passwd_store_reencrypt_all(const gchar *old_mpwd,
                const gchar *new_mpwd)
@@ -273,7 +307,7 @@ void passwd_store_reencrypt_all(const gchar *old_mpwd,
                debug_print("Reencrypting passwords in block (%d/%s).\n",
                                block->block_type, block->block_name);
 
-               if (g_hash_table_size(block->entries) == 0)
+               if (block->entries == NULL || g_hash_table_size(block->entries) == 0)
                        continue;
 
                keys = g_hash_table_get_keys(block->entries);
@@ -313,13 +347,29 @@ static gint _write_to_file(FILE *fp)
        GList *keys, *eitem;
        gchar *typestr, *line, *key, *pwd;
 
+       /* Write out the config_version */
+       line = g_strdup_printf("[config_version:%d]\n", CLAWS_CONFIG_VERSION);
+       if (claws_fputs(line, fp) == EOF) {
+               FILE_OP_ERROR("password store, config_version", "claws_fputs");
+               g_free(line);
+               return -1;
+       }
+       g_free(line);
+
+       /* Add a newline if needed */
+       if (_password_store != NULL && claws_fputs("\n", fp) == EOF) {
+               FILE_OP_ERROR("password store", "claws_fputs");
+               return -1;
+       }
+
+       /* Write out each password store block */
        for (item = _password_store; item != NULL; item = item->next) {
                block = (PasswordBlock*)item->data;
                if (block == NULL)
                        continue; /* Just in case. */
 
                /* Do not save empty blocks. */
-               if (g_hash_table_size(block->entries) == 0)
+               if (block->entries == NULL || g_hash_table_size(block->entries) == 0)
                        continue;
 
                /* Prepare the section header string and write it out. */
@@ -333,8 +383,8 @@ static gint _write_to_file(FILE *fp)
                }
                line = g_strdup_printf("[%s:%s]\n", typestr, block->block_name);
 
-               if (fputs(line, fp) == EOF) {
-                       FILE_OP_ERROR("password store", "fputs");
+               if (claws_fputs(line, fp) == EOF) {
+                       FILE_OP_ERROR("password store", "claws_fputs");
                        g_free(line);
                        return -1;
                }
@@ -348,8 +398,8 @@ static gint _write_to_file(FILE *fp)
                                continue;
 
                        line = g_strdup_printf("%s %s\n", key, pwd);
-                       if (fputs(line, fp) == EOF) {
-                               FILE_OP_ERROR("password store", "fputs");
+                       if (claws_fputs(line, fp) == EOF) {
+                               FILE_OP_ERROR("password store", "claws_fputs");
                                g_free(line);
                                return -1;
                        }
@@ -357,8 +407,9 @@ static gint _write_to_file(FILE *fp)
                }
                g_list_free(keys);
 
-               if (item->next != NULL && fputs("\n", fp) == EOF) {
-                       FILE_OP_ERROR("password store", "fputs");
+               /* Add a separating new line if there is another block remaining */
+               if (item->next != NULL && claws_fputs("\n", fp) == EOF) {
+                       FILE_OP_ERROR("password store", "claws_fputs");
                        return -1;
                }
 
@@ -393,31 +444,41 @@ void passwd_store_write_config(void)
        }
 }
 
-void passwd_store_read_config(void)
+int passwd_store_read_config(void)
 {
        gchar *rcpath, *contents, **lines, **line, *typestr, *name;
        GError *error = NULL;
        guint i = 0;
        PasswordBlock *block = NULL;
        PasswordBlockType type;
+       gboolean reading_config_version = FALSE;
+       gint config_version = -1;
 
-       // TODO: passwd_store_clear();
-
-       debug_print("Reading password store from file...\n");
+       /* TODO: passwd_store_clear(); */
 
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
                        PASSWORD_STORE_RC, NULL);
 
+       debug_print("Reading password store from file '%s'\n", rcpath);
+
+       if (!g_file_test(rcpath, G_FILE_TEST_EXISTS)) {
+               debug_print("File does not exist, looks like a new configuration.\n");
+               g_free(rcpath);
+               return 0;
+       }
+
        if (!g_file_get_contents(rcpath, &contents, NULL, &error)) {
                g_warning("couldn't read password store from file: %s", error->message);
                g_error_free(error);
                g_free(rcpath);
-               return;
+               return -1;
        }
        g_free(rcpath);
 
        lines = g_strsplit(contents, "\n", -1);
 
+       g_free(contents);
+
        while (lines[i] != NULL) {
                if (*lines[i] == '[') {
                        /* Beginning of a new block */
@@ -434,17 +495,31 @@ void passwd_store_read_config(void)
                                        type = PWS_ACCOUNT;
                                } else if (!strcmp(typestr, "plugin")) {
                                        type = PWS_PLUGIN;
+                               } else if (!strcmp(typestr, "config_version")) {
+                                       reading_config_version = TRUE;
+                                       config_version = atoi(name);
                                } else {
                                        debug_print("Unknown password block type: '%s'\n", typestr);
                                        g_strfreev(line);
                                        i++; continue;
                                }
 
-                               if ((block = _new_block(type, name)) == NULL) {
-                                       debug_print("Duplicate password block, ignoring: (%d/%s)\n",
-                                                       type, name);
-                                       g_strfreev(line);
-                                       i++; continue;
+                               if (reading_config_version) {
+                                       if (config_version < 0) {
+                                               debug_print("config_version:%d looks invalid, ignoring it\n",
+                                                               config_version);
+                                               config_version = -1; /* set to default value if missing */
+                                               i++; continue;
+                                       }
+                                       debug_print("config_version in file is %d\n", config_version);
+                                       reading_config_version = FALSE;
+                               } else {
+                                       if ((block = _new_block(type, name)) == NULL) {
+                                               debug_print("Duplicate password block, ignoring: (%d/%s)\n",
+                                                               type, name);
+                                               g_strfreev(line);
+                                               i++; continue;
+                                       }
                                }
                        }
                        g_strfreev(line);
@@ -464,4 +539,11 @@ void passwd_store_read_config(void)
                i++;
        }
        g_strfreev(lines);
+
+       if (prefs_update_config_version_password_store(config_version) < 0) {
+               debug_print("Password store configuration file version upgrade failed\n");
+               return -2;
+       }
+
+       return g_slist_length(_password_store);
 }