// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017 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 <mobius/tsk/stream.h>
#include <mobius/tsk/filesystem.h>
#include <mobius/tsk/diskimage.h>
#include <mobius/tsk/entry.h>
#include <mobius/core/application.h>
#include <mobius/string_functions.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdlib>
#include <unistd.h>

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief split a string into parts, given a separator
//! \param str string
//! \param sep separator
//! \param parts reference to a vector
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
string_split (const std::string& str, const std::string& sep, std::vector <std::string>& parts)
{
  std::string::size_type sep_size = sep.length ();
  std::string::size_type pos = 0;
  std::string::size_type idx = str.find (sep);

  while (idx != str.npos)
    {
      parts.push_back (str.substr (pos, idx - pos));
      pos = idx + sep_size;
      idx = str.find (sep, pos);
    }

  if (pos < str.length ())
    parts.push_back (str.substr (pos, str.length () - pos));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief check if entry is an active registry file
//! \param entry filesystem entry
//! \param out_filename reference to a string to contain the output filename
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
is_registry_file (const mobius::tsk::entry& entry, std::string& out_filename)
{
  if (entry.is_folder () || entry.is_deleted ())
    return false;

  // check if path points to a valid registry file
  std::string path = mobius::string_tolower (entry.get_path ());
  std::string filename = mobius::string_tolower (entry.get_name ());

  std::vector <std::string> dirnames;
  string_split (path, "/", dirnames);

  bool rc = false;

  if ((filename == "system" || filename == "software" || filename == "sam" ||
       filename == "security" || filename == "default") &&
      dirnames.size () == 5 &&
      dirnames[1] == "windows" && dirnames[2] == "system32" && dirnames[3] == "config")
    {
      out_filename = mobius::string_toupper (filename);
      rc = true;
    }

  else if (filename == "ntuser.dat" &&
           dirnames.size () == 4 &&
           (dirnames[1] == "documents and settings" || dirnames[1] == "user"))
    {
      out_filename = "NTUSER-" + mobius::string_toupper (dirnames[2]) + ".DAT";
      rc = true;
    }

  return rc;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief process an entry from filesystem, recursively
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
process_entry (const mobius::tsk::entry& entry)
{
  if (entry.get_name () == "." || entry.get_name () == "..")
    return;

  // retrieve entry if it is a registry file
  std::string out_filename;

  if (is_registry_file (entry, out_filename))
    {
      mobius::tsk::stream stream = entry.get_stream_by_type (0x80);
      std::cout << "    " << entry.get_path () << std::endl;

      std::ofstream out (out_filename.c_str ());
      char buffer[8192];
      size_t bytes;

      while (bytes = stream.read (buffer, 8192))
        out.write (buffer, bytes);
    }

  // navigate through entry's children
  mobius::tsk::entry_list children = entry.get_children ();
  std::size_t count = children.get_length ();

  for (std::size_t i = 0; i < count; i++)
    {
      mobius::tsk::entry child = children[i];
      child.set_path (entry.get_path () + "/" + child.get_name ());
      process_entry (child);
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief retrieve active registry files
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
retrieve_registry_files (const char *path, mobius::tsk::offset_t offset)
{
  mobius::tsk::diskimage imagefile (path);
  std::cout << "imagefile" << std::endl;
  std::cout << "    type: " << imagefile.get_type () << std::endl;
  std::cout << "    size: " << imagefile.get_size () << std::endl;
  std::cout << "    sector size: " << imagefile.get_sector_size () << std::endl;

  mobius::tsk::filesystem fs (imagefile, offset);
  std::cout << std::endl;
  std::cout << "filesystem" << std::endl;
  std::cout << "    offset: " << fs.get_offset () << std::endl;
  std::cout << "    inode count: " << fs.get_inode_count () << std::endl;
  std::cout << "    root_inum: " << fs.get_root_inode () << std::endl;
  std::cout << "    first_inum: " << fs.get_first_inode () << std::endl;
  std::cout << "    last_inum: " << fs.get_last_inode () << std::endl;

  std::cout << std::endl;
  std::cout << "registry files" << std::endl;

  mobius::tsk::entry entry = fs.get_root_entry ();
  entry.set_path ("C:");
  process_entry (entry);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief show usage text
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
usage ()
{
  std::cerr << std::endl;
  std::cerr << "use: registry_get [OPTIONS] <path>" << std::endl;
  std::cerr << "e.g: registry_get -o 63 disk.raw" << std::endl;
  std::cerr << std::endl;
  std::cerr << "options are:" << std::endl;
  std::cerr << "  -o offset\tstarting sector of the filesystem" << std::endl;
  std::cerr << std::endl;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief main function
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
int
main (int argc, char **argv)
{
  mobius::core::application app;
  std::cerr << app.get_name () << " v" << app.get_version () << std::endl;
  std::cerr << app.get_copyright () << std::endl;
  std::cerr << "Registry Get v1.1" << std::endl;
  std::cerr << "by Eduardo Aguiar" << std::endl;
  std::cerr << std::endl;

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // process command line
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  int opt;
  mobius::tsk::offset_t offset = 0;

  while ((opt = getopt (argc, argv, "o:")) != EOF)
    {
      if (opt == 'o')
        {
          offset = atol (optarg) * 512;
          break;
        }
      else
        {
          //std::cerr << "Error: Invalid option '-" << char (opt) << "'" << std::endl;
          usage ();
          exit (EXIT_FAILURE);
        }
    }

  if (optind == argc)
    {
      std::cerr << "Error: You must pass a valid path to an imagefile" << std::endl;
      usage ();
      exit (EXIT_FAILURE);
    }

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // search for registry files
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  try
    {
      retrieve_registry_files (argv[optind], offset);
    }
  catch (const std::exception& e)
    {
      std::cerr <<  "Error: " << e.what () << std::endl;
      exit (EXIT_FAILURE);
    }

  exit (EXIT_SUCCESS);
}
