/* attempts.c  --  Functions for processing openvpn_attempts records
 *
 *  GPLv2 only - Copyright (C) 2009 - 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/attempts.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2009-04-09
 *
 * @brief  Functions for processing attempts entries in the database
 *
 */

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

#include <libxml/tree.h>

#include <sqlite3.h>

#include <eurephia_nullsafe.h>
#include <eurephia_context.h>
#include <eurephia_log.h>
#include <eurephia_xml.h>
#include <eurephia_values.h>
#include <eurephiadb_session_struct.h>
#include <eurephiadb_mapping.h>
#include <passwd.h>

#include "../sqlite.h"

#define FMAP_OVPNATTEMPTS       /**< fieldmapping.h: Include declaration of tbl_sqlite_attempts */
#include "../fieldmapping.h"

/**
 * Internal function.  Retrieves a list of all registered entries in the attempts table
 *
 * @param ctx  eurephiaCTX
 * @param fmap eDBfieldMap containing the search query
 *
 * @return Returns a valid eurephia XML document on success, otherwise NULL
 */
xmlDoc *attempts_list(eurephiaCTX *ctx, eDBfieldMap *fmap) {
        dbresult *res = NULL;
        xmlDoc *doc = NULL;
        xmlNode *root_n = NULL, *uname_n = NULL, *cert_n = NULL, *remip_n = NULL;
        int i = 0;

        // Query the database for registered attempts
        res = sqlite_query_mapped(ctx, SQL_SELECT,
                                  "SELECT username, lower(digest), remoteip, attempts,"
                                  "       locdt(registered), locdt(last_attempt), atpid"
                                  "  FROM openvpn_attempts",
                                  NULL, fmap, "atpid");
        if( sqlite_query_status(res) != dbSUCCESS ) {
                eurephia_log(ctx, LOG_ERROR, 0, "Error querying the attempts log");
                sqlite_log_error(ctx, res);
                sqlite_free_results(res);
                return NULL;
        }

        eurephiaXML_CreateDoc(ctx, 1, "attemptslog", &doc, &root_n);
        xmlNewProp(root_n, (xmlChar *) "mode", (xmlChar *) "list");

        for( i = 0; i < sqlite_get_numtuples(res); i++ ) {
                xmlNode *atmpt_n = NULL;

                if( sqlite_get_value(res, i, 0) != NULL ) { // Username
                        if( uname_n == NULL ) {
                                uname_n = xmlNewChild(root_n, NULL, (xmlChar *) "username", NULL);
                                assert( uname_n != NULL );
                        }
                        atmpt_n = xmlNewChild(uname_n, NULL, (xmlChar *) "attempt", NULL);
                        sqlite_xml_value(atmpt_n, XML_NODE, "username", res, i, 0);
                } else if( sqlite_get_value(res, i, 1) != NULL ) { // Digest
                        if( cert_n == NULL ) {
                                cert_n  = xmlNewChild(root_n, NULL, (xmlChar *) "certificate", NULL);
                                assert( cert_n != NULL );
                        }
                        atmpt_n = xmlNewChild(cert_n, NULL, (xmlChar *) "attempt", NULL);
                        sqlite_xml_value(atmpt_n, XML_NODE, "certificate", res, i, 1);
                } else if( sqlite_get_value(res, i, 2) != NULL ) { // IP address
                        if( remip_n == NULL ) {
                                remip_n = xmlNewChild(root_n, NULL, (xmlChar *) "ipaddress", NULL);
                                assert( remip_n != NULL );
                        }
                        atmpt_n = xmlNewChild(remip_n, NULL, (xmlChar *) "attempt", NULL);
                        sqlite_xml_value(atmpt_n, XML_NODE, "ipaddress", res, i, 2);
                } else {
                        continue;
                }

                sqlite_xml_value(atmpt_n, XML_ATTR, "atpid", res, i, 6);
                sqlite_xml_value(atmpt_n, XML_ATTR, "attempts", res, i, 3);
                sqlite_xml_value(atmpt_n, XML_NODE, "registered", res, i, 4);
                sqlite_xml_value(atmpt_n, XML_NODE, "last_attempt", res, i, 5);
        }
        sqlite_free_results(res);
        return doc;
}



/**
 * Internal function.  Resets the attempts counter for one or more entries in the database
 *
 * @param ctx  eurephiaCTX
 * @param fmap eDBfieldMap defining which records to reset the attempts counter on
 *
 * @return Returns a valid eurephia XML document on success, otherwise NULL
 */
xmlDoc *attempts_reset(eurephiaCTX *ctx, eDBfieldMap *fmap) {
        dbresult *res = NULL;
        xmlDoc *ret = NULL;
        int fields = 0;
        eDBfieldMap update_vals[] = {
                {TABLE_ATTEMPTS, NULL, FIELD_ATTEMPTS, ft_INT, flt_NOTSET, "attempts",    "0", NULL},
                {0, NULL, 0, ft_UNDEF, flt_NOTSET, NULL, NULL, NULL}
        };

        fields = eDBmappingFieldsPresent(fmap);
        if( (fields & (FIELD_UNAME | FIELD_CERTDIGEST | FIELD_REMOTEIP | FIELD_RECID)) == 0 ) {
                return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
                                             "Missing username, IP address, certificate digest or atpid");
        }

        res = sqlite_query_mapped(ctx, SQL_UPDATE, "UPDATE openvpn_attempts", update_vals, fmap, NULL);
        if( sqlite_query_status(res) == dbSUCCESS ) {
                ret = eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL, "Attempts count reset");
        } else {
                xmlNode *err_n = NULL;

                eurephia_log(ctx, LOG_FATAL, 0, "Could not reset the attempts count");
                err_n = sqlite_log_error_xml(ctx, res);
                ret = eurephiaXML_ResultMsg(ctx, exmlERROR, err_n, "Could not reset the attempts count");
                xmlFreeNode(err_n);
        }
        sqlite_free_results(res);
        return ret;
}


/**
 * Internal function.  Deletes an entry from the attempts log
 *
 * @param ctx  eurephiaCTX
 * @param fmap eDBfieldMap containing the entry/entries to delete
 *
 * @return Returns a valid eurephia XML document on success, otherwise NULL
 */
xmlDoc *attempts_delete(eurephiaCTX *ctx, eDBfieldMap *fmap) {
        dbresult *res = NULL;
        xmlDoc *ret = NULL;
        int fields;

        fields = eDBmappingFieldsPresent(fmap);
        if( (fields & (FIELD_UNAME | FIELD_CERTDIGEST | FIELD_REMOTEIP | FIELD_RECID)) == 0 ) {
                return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
                                             "Missing username, IP address, certificate digest or atpid");
        }

        res = sqlite_query_mapped(ctx, SQL_DELETE, "DELETE FROM openvpn_attempts", NULL, fmap, NULL);
        if( sqlite_query_status(res) == dbSUCCESS ) {
                ret = eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL, "Attempts record removed");
        } else {
                xmlNode *err_n = NULL;

                eurephia_log(ctx, LOG_FATAL, 0, "Could not remove attempts record");
                err_n = sqlite_log_error_xml(ctx, res);
                ret = eurephiaXML_ResultMsg(ctx, exmlERROR, err_n, "Could not delete the attempts record");
                xmlFreeNode(err_n);
        }
        sqlite_free_results(res);
        return ret;
}


/**
 * @copydoc eDBadminAttemptsLog()
 */
xmlDoc *eDBadminAttemptsLog(eurephiaCTX *ctx, xmlDoc *qryxml) {
        eDBfieldMap *fmap = NULL;
        char *mode = NULL;
        xmlDoc *resxml = NULL;
        xmlNode *root_n = NULL, *fieldmap_n = NULL;

        DEBUG(ctx, 20, "Function call: eDBadminAttemptsLog(ctx, {xmlDoc})");
        assert( (ctx != NULL) && (qryxml != 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, qryxml, "attemptslog", 1);
        if( root_n == NULL ) {
                eurephia_log(ctx, LOG_CRITICAL, 0, "Invalid XML input.");
                return NULL;
        }
        mode = xmlGetAttrValue(root_n->properties, "mode");
        if( mode == NULL ) {
                eurephia_log(ctx, LOG_ERROR, 0, "Missing mode attribute");
                return NULL;
        }

        fieldmap_n = xmlFindNode(root_n, "fieldMapping");
        if( fieldmap_n == NULL ) {
                eurephia_log(ctx, LOG_ERROR, 0, "Missing fieldMapping");
        }
        fmap = eDBxmlMapping(ctx, tbl_sqlite_attempts, NULL, fieldmap_n);

        if( strcmp(mode, "list") == 0 ) {
                resxml = attempts_list(ctx, fmap);
        } else if( strcmp(mode, "reset") == 0 ) {
                resxml = attempts_reset(ctx, fmap);
        } else if( strcmp(mode, "delete") == 0 ) {
                resxml = attempts_delete(ctx, fmap);
        } else {
                eurephia_log(ctx, LOG_ERROR, 0, "Attempts - Unknown mode: '%s'", mode);
                resxml = eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Unknown mode '%s'", mode);
        }
        eDBfreeMapping(fmap);
        return resxml;
}
