/* configuration.c   --  Functions for setting and deleting
 *                       configuration parameters in the database
 *
 *  GPLv2 only - Copyright (C) 2008 - 2012
 *               David Sommerseth <dazo@users.sourceforge.net>
 *
 *  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 Free Software Foundation; version 2
 *  of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

/**
 * @file   sqlite/administration/configuration.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2009-09-13
 *
 * @brief  Functions for setting and deleting configuration
 *         parameters in the database
 *
 */

#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sqlite3.h>

#include <eurephia_nullsafe.h>
#include <eurephia_context.h>
#include <eurephia_log.h>
#include <eurephia_values.h>
#include <eurephia_xml.h>

#include "../sqlite.h"

/**
 * Internal function.  Sets a configuration parameter in the database.  If the key already
 * exists, it will be replaced.  This operation will immediately update the in-memory copy
 * of the setting.
 *
 * @param ctx eurephiaCTX
 * @param key String (char *) containing the key name of the value
 * @param val String (char *) with the value to be stored
 *
 * @return Returns 1 on success, otherwise 0
 */
static int config_set(eurephiaCTX *ctx, const char *key, const char *val) {
        dbresult *res = NULL;
        int found = 0;

        DEBUG(ctx, 20, "Function call: eDBadminConfigSet(ctx, '%s', '%s')", key, val);
        assert((ctx != NULL) && (ctx->dbc != NULL));

        if( (ctx->context_type != ECTX_ADMIN_CONSOLE) && (ctx->context_type != ECTX_ADMIN_WEB) ) {
                eurephia_log(ctx, LOG_CRITICAL, 0,
                             "eurephia admin function call attempted with wrong context type");
                return 0;
        }

        res = sqlite_query(ctx, "SELECT count(*) FROM openvpn_config WHERE datakey = '%q'", key);
        if( sqlite_query_status(res) != dbSUCCESS ) {
                eurephia_log(ctx, LOG_ERROR, 0, "Could not query configuration table");
                sqlite_log_error(ctx, res);
                sqlite_free_results(res);
                return 0;
        }
        found = atoi_nullsafe(sqlite_get_value(res, 0, 0));
        sqlite_free_results(res);

        if( found == 0 ) {
                res = sqlite_query(ctx,
                                   "INSERT INTO openvpn_config (datakey, dataval) VALUES ('%q','%q')",
                                   key, val);
        } else {
                res = sqlite_query(ctx, "UPDATE openvpn_config SET dataval = '%q' WHERE datakey = '%q'",
                                   val, key);
        }

        if( sqlite_query_status(res) != dbSUCCESS ) {
                eurephia_log(ctx, LOG_ERROR, 0, "Could not register configuration entry (%s = '%s'", key, val);
                sqlite_log_error(ctx, res);
                sqlite_free_results(res);
                return 0;
        }
        sqlite_free_results(res);
        eAdd_value(ctx, ctx->dbc->config, key, val);
        return 1;
}


/**
 * Internal function.  Deletes a configuration parameter from the database.
 *
 * @param ctx eurephiaCTX
 * @param key String (char *) containing the key to be deleted.
 *
 * @return Returns 1 on success, otherwise 0
 */
static int config_delete(eurephiaCTX *ctx, const char *key) {
        dbresult *res = NULL;
        eurephiaVALUES *cfgptr = NULL;

        DEBUG(ctx, 20, "Function call: eDBadminConfigDelete(ctx, '%s') ", key);
        assert((ctx != NULL) && (ctx->dbc != NULL));

        if( (ctx->context_type != ECTX_ADMIN_CONSOLE) && (ctx->context_type != ECTX_ADMIN_WEB) ) {
                eurephia_log(ctx, LOG_CRITICAL, 0,
                             "eurephia admin function call attempted with wrong context type");
                return 0;
        }

        // Find the config parameter in the in-memory stack
        cfgptr = eGet_valuestruct(ctx->dbc->config, key);
        if( cfgptr == NULL ) {
                eurephia_log(ctx, LOG_WARNING, 0,
                             "Could not find the configuration parameter '%s'", key);
                return 0;
        }

        // Delete the config parameter from the database
        res = sqlite_query(ctx, "DELETE FROM openvpn_config WHERE datakey = '%q'", key);
        if( sqlite_query_status(res) != dbSUCCESS ) {
                eurephia_log(ctx, LOG_ERROR, 0, "Could not delete config configuration entry (%s)", key);
                sqlite_log_error(ctx, res);
                return 0;
        }
        sqlite_free_results(res);

        // Since the database call worked, remove it from memory as well
        ctx->dbc->config = eRemove_value(ctx, ctx->dbc->config, cfgptr->evgid, cfgptr->evid);

        return 1;
}


/**
 * Internal function.  Validates if key or value strings are NULL or not.
 *
 * @param ctx    eurephiaCTX
 * @param key    char pointer to the key string
 * @param value  char pointer to the value string
 *
 * @return Returns NULL if key and value is not NULL.  Otherwise an eurephia XML document with an error
 *         description will be returned.
 */
xmlDoc *validate_key_value(eurephiaCTX *ctx, const char *key, const char *value) {
        int k_null = 0, v_null = 0;

        k_null = (key == NULL ? 1 : 0);
        v_null = (value == NULL ? 1 : 0);

        if( k_null || v_null ) {
                return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "%s%s%s",
                                             (k_null ? "The key attribute was not set" : ""),
                                             (k_null && v_null ? " and " : ""),
                                             (v_null ? "The value tag was not set" : "")
                                             );
        }
        return NULL;
}

/**
 * @copydoc eDBadminConfiguration()
 */
xmlDoc *eDBadminConfiguration(eurephiaCTX *ctx, xmlDoc *cfgxml) {
        xmlDoc *resxml = NULL;
        xmlNode *root_n = NULL, *cfg_n = NULL;
        char *key = NULL, *value = NULL;

        DEBUG(ctx, 20, "Function call: eDBadminConfiguration(ctx, {xmlDoc})");
        assert( (ctx != NULL) && (cfgxml != NULL) );

        if( (ctx->context_type != ECTX_ADMIN_CONSOLE) && (ctx->context_type != ECTX_ADMIN_WEB) ) {
                eurephia_log(ctx, LOG_CRITICAL, 0,
                             "eurephia admin function call attempted with wrong context type");
                return NULL;
        }

        root_n = eurephiaXML_getRoot(ctx, cfgxml, "configuration", 1);
        if( root_n == NULL ) {
                eurephia_log(ctx, LOG_CRITICAL, 0, "Invalid XML input.");
                return NULL;
        }

        // Look if we recevied a <set> tag
        cfg_n = xmlFindNode(root_n, "set");
        if( cfg_n != NULL ) {
                key = xmlGetAttrValue(cfg_n->properties, "key");
                value = xmlExtractContent(cfg_n);

                resxml = validate_key_value(ctx, key, value);
                if( resxml ) {
                        // Input data was not approved, and we have an error message
                        return resxml;
                }

                if( config_set(ctx, key, value) ) {
                        resxml = eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL,
                                                       "Configuration key '%s' was set to '%s'",
                                                       key, value);
                } else {
                        resxml = eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
                                                       "Failed to set the key '%s' to '%s'",
                                                       key, value);
                }
                return resxml;
        };

        // If not, look if we recevied a <delete> tag
        cfg_n = xmlFindNode(root_n, "delete");
        if( cfg_n != NULL ) {
                key = xmlGetAttrValue(cfg_n->properties, "key");

                resxml = validate_key_value(ctx, key, ""); // Do not use NULL for value
                if( resxml ) {
                        // Input data was not approved, and we have an error message
                        return resxml;
                }

                if( config_delete(ctx, key) ) {
                        resxml = eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL,
                                                       "Configuration key '%s' was deleted", key);
                } else {
                        resxml = eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
                                                       "Failed to delete the key '%s'", key);
                }
                return resxml;
        }

        // If not, it's an invalid input
        return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Unknown XML tag received");
}
