Handle password store config_version update without the global gint variable.
[claws.git] / src / passwordstore.c
index dfc96a06be5e57df3359f07da1cab4646e48f29c..2f1d6c10a62da0a4635599f929aab6887ff6bf1d 100644 (file)
@@ -34,7 +34,9 @@
 #include "common/utils.h"
 #include "passwordstore.h"
 #include "password.h"
+#include "prefs_common.h"
 #include "prefs_gtk.h"
+#include "prefs_migration.h"
 
 static GSList *_password_store;
 
@@ -45,8 +47,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 +73,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 +98,7 @@ static PasswordBlock *_new_block(PasswordBlockType block_type,
        return block;
 }
 
-///////////////////////////////////////////////////////////////
+/*************************************************************/
 
 /* Stores a password. */
 gboolean passwd_store_set(PasswordBlockType block_type,
@@ -107,24 +107,30 @@ gboolean passwd_store_set(PasswordBlockType block_type,
                const gchar *password,
                gboolean encrypted)
 {
+       const gchar *p;
        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);
 
+       /* Empty password string equals null password for us. */
+       if (password == NULL || strlen(password) == 0)
+               p = NULL;
+       else
+               p = password;
+
        debug_print("%s password '%s' in block (%d/%s)%s\n",
-                       (password == NULL ? "Deleting" : "Storing"),
+                       (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. */
-               if (password == NULL)
+               if (p == NULL)
                        return TRUE;
 
                if ((block = _new_block(block_type, block_name)) == NULL) {
@@ -134,7 +140,7 @@ gboolean passwd_store_set(PasswordBlockType block_type,
                }
        }
 
-       if (password == NULL) {
+       if (p == NULL) {
                /* NULL password was passed to us, so delete the entry with
                 * corresponding id */
                g_hash_table_remove(block->entries, password_id);
@@ -142,17 +148,17 @@ gboolean passwd_store_set(PasswordBlockType block_type,
                if (!encrypted) {
                        /* encrypt password before saving it */
                        if ((encrypted_password =
-                                               password_encrypt(password, NULL)) == NULL) {
+                                               password_encrypt(p, NULL)) == NULL) {
                                debug_print("Could not encrypt password '%s' for block (%d/%s).\n",
                                                password_id, block_type, block_name);
                                return FALSE;
                        }
                } else {
                        /* password is already in encrypted form already */
-                       encrypted_password = g_strdup(password);
+                       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);
@@ -169,21 +175,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",
@@ -191,7 +196,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",
@@ -199,10 +204,31 @@ gchar *passwd_store_get(PasswordBlockType block_type,
                return NULL;
        }
 
-       // return decrypted password
+       /* return decrypted password */
        return password;
 }
 
+gboolean passwd_store_delete_block(PasswordBlockType block_type,
+               const gchar *block_name)
+{
+       PasswordBlock *block;
+
+       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 */
+       if ((block = _get_block(block_type, block_name)) == NULL) {
+               debug_print("Block (%d/%s) not found.\n", block_type, block_name);
+               return FALSE;
+       }
+
+       g_hash_table_destroy(block->entries);
+       block->entries = NULL;
+       return TRUE;
+}
+
 gboolean passwd_store_set_account(gint account_id,
                const gchar *password_id,
                const gchar *password,
@@ -284,6 +310,22 @@ 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 (fputs(line, fp) == EOF) {
+               FILE_OP_ERROR("password store, config_version", "fputs");
+               g_free(line);
+               return -1;
+       }
+       g_free(line);
+
+       /* Add a newline if needed */
+       if (_password_store != NULL && fputs("\n", fp) == EOF) {
+               FILE_OP_ERROR("password store", "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)
@@ -328,6 +370,7 @@ static gint _write_to_file(FILE *fp)
                }
                g_list_free(keys);
 
+               /* Add a separating new line if there is another block remaining */
                if (item->next != NULL && fputs("\n", fp) == EOF) {
                        FILE_OP_ERROR("password store", "fputs");
                        return -1;
@@ -364,15 +407,17 @@ 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();
+       /* TODO: passwd_store_clear(); */
 
        debug_print("Reading password store from file...\n");
 
@@ -380,15 +425,17 @@ void passwd_store_read_config(void)
                        PASSWORD_STORE_RC, NULL);
 
        if (!g_file_get_contents(rcpath, &contents, NULL, &error)) {
-               g_warning("couldn't read password store from file: %s\n", error->message);
+               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 */
@@ -405,17 +452,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("Uknown password block type: '%s'\n", typestr);
+                                       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);
@@ -435,4 +496,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);
 }