# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022 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/>.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import pymobius.registry
import pymobius.registry.main
import pymobius.vfs
import os.path
import mobius
import sys
import traceback
from . import model
from . import ares
from . import emuletorrent
from . import emule
from . import shareaza
from . import utorrent

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Constants
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
STATE_NO = 0
STATE_YES = 1
STATE_ALWAYS = 2
STATE_UNKNOWN = -1

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Add file to item's kff ignored file
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def set_handled (item, f):

  # create file reader
  if not f:
    return

  reader = f.new_reader ()
  if not reader:
    return

  # calculate hash sha2-512
  h = mobius.crypt.hash ('sha2_512')
  data = reader.read (65536)

  while data:
    h.update (data)
    data = reader.read (65536)

  # add to kff file
  case = item.case
  path = case.create_path (os.path.join ('hashset', '%04d-p2p.ignore' % item.uid))
  fp = open (path, 'a')
  fp.write ('%s\n' % h.get_hex_digest ())
  fp.close ()

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief generate unique list from list
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def unique (rows):
  old_key = None
  objs = []

  for row in rows:
    obj = row[0]
    key = row[1:]

    if key != old_key:
      objs.append (obj)
      old_key = key

  return objs

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief System user
# @author Eduardo Aguiar
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class User (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self, name):
    self.name = name
    self.appdata_dir = None
    self.local_appdata_dir = None

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief forensics: P2P
# @author Eduardo Aguiar
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Ant (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self, item):
    if item.datasource:
      self.__model = model.model (item)
      self.__item = item
      self.__is_data_available = self.__model.load ()
    else:
      self.__model = None
      self.__is_data_available = True

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief check if data is available
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def is_data_available (self):
    return self.__is_data_available

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief get data
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_data (self):
    if not self.__is_data_available:
      self.__retrieve_data ()

    return self.__model

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief retrieve data
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_data (self):
    vfs = pymobius.vfs.get_item_vfs (self.__item)
    if not vfs:
      raise Exception ('Datasource is not available')

    mobius.core.logf ('DBG 1')
    self.__model = model.model (self.__item)

    # Clear p2p ignored kff, if any
    mobius.core.logf ('DBG 2')
    case = self.__item.case
    path = case.get_path (os.path.join ('hashset', '%04d-p2p.ignore' % self.__item.uid))

    if os.path.exists (path):
      os.remove (path)

    # Retrieve activity data
    mobius.core.logf ('DBG 3.1')
    pymobius.p2p.ares.retrieve (self.__model)
    mobius.core.logf ('DBG 3.2')
    pymobius.p2p.emule.retrieve (self.__model)
    mobius.core.logf ('DBG 3.3')
    pymobius.p2p.emuletorrent.retrieve (self.__model)
    mobius.core.logf ('DBG 3.4')
    pymobius.p2p.shareaza.retrieve (self.__model)
    mobius.core.logf ('DBG 3.5')
    pymobius.p2p.utorrent.retrieve (self.__model)
    mobius.core.logf ('DBG 3.6')

    # Normalize data
    mobius.core.logf ('DBG 4')
    self.__calculate_totals ()
    self.__model.accounts = unique ((a, a.network, a.guid) for a in self.__model.accounts)

    # Save data model
    mobius.core.logf ('DBG 5')
    path = self.__item.create_data_path ('p2p.json')
    self.__model.save (path)
    self.__is_data_available = True
    mobius.core.logf ('DBG 6')

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Calculate totals by application
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __calculate_totals (self):
    application = dict ((a.id, a) for a in self.__model.applications)

    # Reset totals
    for app in application.values ():
      app.total_searches = 0
      app.total_local_files = 0
      app.total_remote_files = 0

    # Calculate totals
    for search in self.__model.searches:
      application[search.app_id].total_searches += 1

    for local_file in self.__model.local_files:
      application[local_file.app_id].total_local_files += 1

    for remote_file in self.__model.remote_files:
      application[remote_file.app_id].total_remote_files += 1
