// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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/tsk/exception.h>
#include <mobius/core/application.h>
#include <iostream>
#include <string>

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief show information about a filesystem entry
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
show_entry (const mobius::tsk::entry& entry, int indent = 0)
{
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // show entry metadata
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  std::string space (indent * 3, ' ');
  std::cout << space << "[" << entry.get_inode () << "] ";

  if (entry.is_deleted ())
    std::cout << "<DEL> ";

  std::cout << "(size=" << entry.get_size () << ") "
            << entry.get_name () << ' '
            << "(atime=" << entry.get_atime () << ") "
            << "(mtime=" << entry.get_mtime () << ") "
            << "(ctime=" << entry.get_ctime () << ") "
            << "(crtime=" << entry.get_crtime () << ") "
            << "(dtime=" << entry.get_dtime () << ") - "
            << entry.get_path ()
            << std::endl;

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // show entry streams
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  std::size_t count = entry.get_stream_count ();

  for (std::size_t i = 0; i < count; i++)
    {
      mobius::tsk::stream stream = entry.get_stream_by_idx (i);
      std::cout << space << "   stream " << i << ": " << stream.get_type ()
                << ", size: " << stream.get_size () << ", name: " << stream.get_name ()
                << std::endl;

      try
        {
          char buffer[16];
          std::size_t bytes = stream.read (buffer, 16);
          std::cout << space << "   ";
          for (std::size_t j = 0; j < bytes; j++)
            printf ("%02x ", (unsigned char) buffer[j]);
          std::cout << std::endl;
        }
      catch (const mobius::tsk::exception& e)
        {
          std::cerr << "warning: " << e.what () << std::endl;
        }
    }

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // show entry's children, if possible
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  if (entry.is_reallocated ())
    return;

  mobius::tsk::entry_list children = entry.get_children ();
  count = children.get_length ();

  for (std::size_t i = 0; i < count; i++)
    {
      mobius::tsk::entry child = children[i];
      if (child.get_name () != "." && child.get_name () != "..")
        {
          try
            {
              show_entry (child, indent + 1);
            }
          catch (std::exception& e)
            {
              std::cout << "warning: " << e.what () << std::endl;
            }
        }
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief show usage text
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
usage ()
{
  std::cerr << std::endl;
  std::cerr << "use: dirtsk [OPTIONS] <path>" << std::endl;
  std::cerr << "e.g: dirtsk -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 << "Dirtsk v1.1" << std::endl;
  std::cerr << "by Eduardo Aguiar" << std::endl;
  std::cerr << std::endl;

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // parse 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
        {
          usage ();
          exit (EXIT_FAILURE);
        }
    }

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

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // show filesystem entries
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  try
    {
      mobius::tsk::diskimage imagefile (argv[optind]);
      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 << "    id: " << fs.get_id () << std::endl;

      mobius::tsk::entry entry = fs.get_root_entry ();
      entry.set_path ("C:");
      show_entry (entry);
    }
  catch (const std::exception& e)
    {
      std::cerr << e.what () << std::endl;
      exit (EXIT_FAILURE);
    }

  exit (EXIT_SUCCESS);
}
