// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 Eduardo Aguiar
//
// 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; either version 2, or (at your option) any later
// version.
//
// 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, see <http://www.gnu.org/licenses/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "hash.h"
#include <mobius/database/database.h>

namespace mobius
{
namespace forensics
{
namespace turing
{
mobius::database::database get_database ();

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
//! \param type hash type
//! \param value hash value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
hash::hash (std::int64_t uid)
  : uid_ (uid)
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief set password
//! \param password password
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
hash::set_password (const std::string& password)
{
  _load_data ();
  
  auto db = get_database ();

  auto stmt = db.new_statement (
          "UPDATE hash "
             "SET password = ?, "
                 "password_status = ?, "
                 "password_min_size = ?, "
                 "password_max_size = ? "
           "WHERE uid = ?");

  stmt.bind (1, password);
  stmt.bind (2, static_cast <int> (password_status::found));
  stmt.bind (3, static_cast <int> (password.length ()));
  stmt.bind (4, static_cast <int> (password.length ()));
  stmt.bind (5, uid_);
  stmt.execute ();
        
  password_ = password;
  password_status_ = password_status::found;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief load hash data on demand
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
hash::_load_data () const
{
  if (!data_loaded_)
    {
      auto db = get_database ();

      auto stmt = db.new_statement (
          "SELECT * "
            "FROM hash "
           "WHERE uid = ?");

      stmt.bind (1, uid_);
  
      if (stmt.fetch_row ())
        {
          type_ = stmt.get_column_string (1);
          value_ = stmt.get_column_string (2);
          password_ = stmt.get_column_string (3);
          password_min_size_ = stmt.get_column_int (4);
          
          if (stmt.is_column_null (5))
            password_max_size_ = -1;
          else
            password_max_size_ = stmt.get_column_int (5);

          password_status_ = hash::password_status (stmt.get_column_int (6));
        }

      data_loaded_ = true;
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief load hash arguments on demand
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
hash::_load_args () const
{
  if (!args_loaded_)
    {
      auto db = get_database ();

      auto stmt = db.new_statement (
          "SELECT * "
            "FROM hash_args "
           "WHERE uid_hash = ?");

      stmt.bind (1, uid_);
  
      while (stmt.fetch_row ())
        {
          auto name = stmt.get_column_string (2);
          auto value = stmt.get_column_string (3);
          args_[name] = value;
        }

      args_loaded_ = true;
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief check if argument exists
//! \param name argument name
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
hash::has_argument (const std::string& name) const
{
  _load_args ();
  return args_.find (name) != args_.end ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get argument value
//! \param name argument name
//! \return argument value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
hash::get_argument (const std::string& name) const
{
  _load_args ();
  std::string value;

  auto iter = args_.find (name);

  if (iter != args_.end ())
    value = iter->second;
  
  return value;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief set argument value
//! \param name argument name
//! \param value argument value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
hash::set_argument (const std::string& name, const std::string& value)
{
  _load_args ();
  
  auto db = get_database ();

  if (has_argument (name))
    {
      auto stmt = db.new_statement (
          "UPDATE hash_args "
             "SET value = ? "
           "WHERE uid_hash = ? "
             "AND name = ?");
      stmt.bind (1, value);
      stmt.bind (2, uid_);
      stmt.bind (3, name);
      stmt.execute ();
    }
        
  else
    {
      auto stmt = db.new_statement (
          "INSERT INTO hash_args "
               "VALUES (NULL, ?, ?, ?)");
      stmt.bind (1, uid_);
      stmt.bind (2, name);
      stmt.bind (3, value);
      stmt.execute ();
    }

  args_[name] = value;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief remove argument
//! \param name argument name
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
hash::remove_argument (const std::string& name)
{
  _load_args ();

  if (has_argument (name))
    {
      auto db = get_database ();

      auto stmt = db.new_statement (
          "DELETE FROM hash_args "
                "WHERE uid_hash = ? "
                  "AND name = ?");

      stmt.bind (1, uid_);
      stmt.bind (2, name);
      stmt.execute ();
          
      args_.erase (name);
    }
}

} // namespace turing
} // namespace forensics
} // namespace mobius
