# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 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.p2p
import mobius
import collections
from . import decoder_arestra
from . import decoder_phashidx_dat
from . import decoder_shareh_dat
from . import decoder_sharel_dat
from . import decoder_tempdl_phash_dat
from . import decoder_tempdl_pbthash_dat
from . import decoder_torrenth_dat

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

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Local file
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class local_file (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self):
    self.name = None
    self.path = None
    self.size = None
    self.title = None
    self.artist = None
    self.album = None
    self.category = None
    self.year = None
    self.vidinfo = None
    self.language = None
    self.url = None
    self.comment = None
    self.filetype = None
    self.hash_sha1 = None
    self.flag_shared = STATE_UNKNOWN
    self.flag_uploaded = STATE_UNKNOWN
    self.flag_downloaded = STATE_UNKNOWN
    self.flag_corrupted = STATE_UNKNOWN
    self.flag_completed = STATE_UNKNOWN
    self.download_completed_time = None
    self.metadata = {}

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Remote file
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class remote_file (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self):
    self.name = None
    self.size = None
    self.metadata = {}

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief User profile model
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class profile (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self, item):
    self.__shareh = collections.OrderedDict ()
    self.__sharel = collections.OrderedDict ()
    self.__arestra = collections.OrderedDict ()
    self.__phashidx = collections.OrderedDict ()
    self.__pbthash = collections.OrderedDict ()
    self.__phash = collections.OrderedDict ()
    self.__torrenth = []
    self.__item = item
    self.__local_files = None
    self.__remote_files = None
    self.__is_normalized = False

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get local files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_local_files (self):
    if not self.__is_normalized:
      self.__normalize ()

    return self.__local_files

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get remote files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_remote_files (self):
    if not self.__is_normalized:
      self.__normalize ()

    return self.__remote_files

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Data folder
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def retrieve_data_folder (self, folder):
    if not folder:
      return

    for f in folder.get_children ():
      fname = f.name.lower ()

      # Ignore reallocated entries
      if f.is_reallocated ():
        pass

      # ShareH.dat
      elif fname == 'shareh.dat':
        entries = decoder_shareh_dat.decode (f)

        if entries != None:
          self.__shareh.update ((e.hash_sha1, e) for e in entries)
          pymobius.p2p.set_handled (self.__item, f)

      # ShareL.dat
      elif fname == 'sharel.dat':
        entries = decoder_sharel_dat.decode (f)

        if entries != None:
          self.__sharel.update ((e.hash_sha1, e) for e in entries)
          pymobius.p2p.set_handled (self.__item, f)

      # TorrentH.dat
      elif fname == 'torrenth.dat':
        entries = decoder_torrenth_dat.decode (f)

        if entries != None:
          self.__torrenth += entries
          pymobius.p2p.set_handled (self.__item, f)

      # Default.m3u
      elif fname == 'default.m3u':
        pymobius.p2p.set_handled (self.__item, f)

      # DHTnodes.dat
      elif fname == 'dhtnodes.dat':
        pymobius.p2p.set_handled (self.__item, f)

      # MDHTnodes.dat
      elif fname == 'mdhtnodes.dat':
        pymobius.p2p.set_handled (self.__item, f)

      # ChatroomIPs.dat
      elif fname == 'chatroomips.dat':
        pymobius.p2p.set_handled (self.__item, f)

      # SNodes.dat
      elif fname == 'snodes.dat':
        pymobius.p2p.set_handled (self.__item, f)

      # FailedSNodes.dat
      elif fname == 'failedsnodes.dat':
        pymobius.p2p.set_handled (self.__item, f)

      # PHashIdx.dat
      elif fname in ('phashidx.dat', 'phashidxtemp.dat', 'tempphash.dat'):
        entries = decoder_phashidx_dat.decode (f)

        if entries != None:
          self.__phashidx.update ((e.hash_sha1, e) for e in entries)
          pymobius.p2p.set_handled (self.__item, f)

      # TempDL
      elif fname == 'tempdl' and f.is_folder ():
        self.__retrieve_tempdl_folder (f)

      else:
        t = 'file' if f.is_file () else 'folder'
        mobius.core.logf ('p2p.ares: Unhandled Data/%s %s' % (f.name, t))

    # set is_normalized false
    self.__is_normalized = False

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Data/TempDL folder
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_tempdl_folder (self, folder):
    idx = 1

    for f in folder.get_children ():
      fname = f.name.lower ()

      if f.is_reallocated ():
        pass

      # PHash_XXXX.dat
      elif fname.startswith ('phash_') and fname.endswith ('.dat'):
        entry = decoder_tempdl_phash_dat.decode (f)

        if entry:
          self.__phash[entry.hash_sha1] = entry
          pymobius.p2p.set_handled (self.__item, f)

      # PBTHash_XXXX.dat
      elif fname.startswith ('pbthash_') and fname.endswith ('.dat'):
        entry = decoder_tempdl_pbthash_dat.decode (f)

        if entry:
          for ef in entry.files:
            self.__pbthash[(ef.path.lower (), ef.size)] = (entry, ef)
          pymobius.p2p.set_handled (self.__item, f)

      # META_XXXX.dat
      # @see tBittorrentTransfer.initFrom_ut_Meta - btcore.pas
      #elif fname.startswith ('meta_') and fname.endswith ('.dat'):
      #  pass # @todo handle

      else:
        mobius.core.logf ('p2p.ares: Unhandled Data/TempDL/%s file' % f.name)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from My Shared Folder
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def retrieve_my_shared_folder (self, folder):
    try:
      children = folder.get_children ()
    except:
      children = []

    # scan entries
    for child in children:
      fname = child.name.lower ()

      if child.is_reallocated ():
        pass

      elif child.is_folder ():
        self.retrieve_my_shared_folder (child)

      elif fname.startswith ('___arestra___'):
        entry = decoder_arestra.decode (child)

        if entry:
          self.__arestra[entry.hash_sha1] = entry

    # set is_normalized false
    self.__is_normalized = False

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Normalize data
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __normalize (self):
    self.__generate_remote_files ()
    self.__generate_local_files ()

    # release old data
    self.__sharel = None
    self.__shareh = None
    self.__arestra = None
    self.__phash = None
    self.__pbthash = None
    self.__torrenth = None

    # set is_normalized flag
    self.__is_normalized = True

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Generate remote files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __generate_remote_files (self):
    self.__remote_files = []

    # add remote files from ___ARESTRA___
    for entry in self.__arestra.values ():
      for source in entry.sources:
        f = remote_file ()
        f.name = entry.name
        f.size = entry.size
        f.timestamp = entry.creation_time
        f.hash_sha1 = entry.hash_sha1
        f.filetype = entry.filetype
        f.title = entry.title
        f.artist = entry.artist
        f.album = entry.album
        f.category = entry.category
        f.year = entry.year
        f.language = entry.language
        f.url = entry.url
        f.comment = entry.comment

        f.peer_ip = source.ip
        f.peer_port = source.port

        f.metadata['local_timestamp'] = entry.begin_time
        self.__remote_files.append (f)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Generate local files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __generate_local_files (self):
    self.__local_files = []
    self.__add_local_files_from_sharel ()
    self.__add_info_from_shareh ()
    self.__add_local_files_from_shareh ()
    self.__add_info_from_arestra ()
    self.__add_local_files_from_arestra ()
    self.__add_info_from_phashidx ()
    self.__add_info_from_phash ()
    self.__add_info_from_pbthash ()
    self.__add_local_files_from_pbthash ()
    self.__add_local_files_from_torrenth ()

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add local files from sharel
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_local_files_from_sharel (self):

    for entry in self.__sharel.values ():
      f = local_file ()
      f.size = entry.size
      f.hash_sha1 = entry.hash_sha1
      f.filetype = entry.filetype
      f.name = entry.name
      f.path = entry.path
      f.title = entry.title
      f.artist = entry.artist
      f.album = entry.album
      f.category = entry.category
      f.year = entry.year
      f.vidinfo = entry.vidinfo
      f.language = entry.language
      f.url = entry.url
      f.comment = entry.comment
      f.download_completed_time = entry.download_completed_time
      f.flag_corrupted = STATE_YES if entry.flag_corrupted else STATE_NO
      f.flag_shared = STATE_YES
      f.flag_completed = STATE_YES
      f.flag_downloaded = STATE_YES if entry.flag_downloaded else STATE_UNKNOWN

      f.metadata['sharel_path'] = entry.sharel_path
      f.metadata['sharel_creation_time'] = entry.sharel_creation_time
      f.metadata['sharel_last_modification_time'] = entry.sharel_last_modification_time

      self.__local_files.append (f)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add info from shareh
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_info_from_shareh (self):

    for f in self.__local_files:
      shareh_entry = self.__shareh.pop (f.hash_sha1, None)

      if shareh_entry:
        f.title = f.title or shareh_entry.title
        f.artist = f.artist or shareh_entry.artist
        f.album = f.album or shareh_entry.album
        f.category = f.category or shareh_entry.category
        f.year = f.year or shareh_entry.year
        f.language = f.language or shareh_entry.language
        f.url = f.url or shareh_entry.url
        f.comment = f.comment or shareh_entry.comment
        f.download_completed_time = f.download_completed_time or shareh_entry.download_completed_time

        f.flag_shared = STATE_YES if shareh_entry.flag_shared else STATE_NO

        if shareh_entry.download_completed_time:
          f.flag_downloaded = STATE_YES

        f.metadata['shareh_path'] = shareh_entry.shareh_path
        f.metadata['shareh_creation_time'] = shareh_entry.shareh_creation_time
        f.metadata['shareh_last_modification_time'] = shareh_entry.shareh_last_modification_time

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add local files from remaining sharel
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_local_files_from_shareh (self):

    for entry in self.__shareh.values ():
      f = local_file ()
      f.size = entry.size
      f.hash_sha1 = entry.hash_sha1
      f.filetype = entry.filetype
      f.title = entry.title
      f.artist = entry.artist
      f.album = entry.album
      f.category = entry.category
      f.year = entry.year
      f.language = entry.language
      f.url = entry.url
      f.comment = entry.comment
      f.download_completed_time = entry.download_completed_time

      f.flag_shared = STATE_YES if entry.flag_shared else STATE_NO
      f.flag_corrupted = STATE_YES if entry.flag_corrupted else STATE_NO
      f.flag_completed = STATE_YES
      f.flag_downloaded = STATE_YES if entry.download_completed_time else STATE_UNKNOWN

      f.metadata['shareh_path'] = entry.shareh_path
      f.metadata['shareh_creation_time'] = entry.shareh_creation_time
      f.metadata['shareh_last_modification_time'] = entry.shareh_last_modification_time

      self.__local_files.append (f)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add info from ___ARESTRA___ files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_info_from_arestra (self):

    for f in self.__local_files:
      arestra_entry = self.__arestra.get (f.hash_sha1, None)

      if arestra_entry:
        f.path = f.path or arestra_entry.path
        f.name = f.name or arestra_entry.name
        f.title = f.title or arestra_entry.title
        f.artist = f.artist or arestra_entry.artist
        f.album = f.album or arestra_entry.album
        f.category = f.category or arestra_entry.category
        f.language = f.language or arestra_entry.language
        f.url = f.url or arestra_entry.url
        f.comment = f.comment or arestra_entry.comment
        f.filetype = f.filetype or arestra_entry.filetype

        if f.size == None:
          f.size = arestra_entry.size

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add local files from ___ARESTRA___ files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_local_files_from_arestra (self):
    for entry in self.__arestra.values ():

      if entry.size > 0:
        pct_progress = float (entry.progress) * 100.0 / float (entry.size)
      else:
        pct_progress = 0.0

      f = local_file ()
      f.size = entry.size
      f.name = entry.name
      f.path = entry.path
      f.hash_sha1 = entry.hash_sha1
      f.filetype = entry.filetype
      f.title = entry.title
      f.artist = entry.artist
      f.album = entry.album
      f.category = entry.category
      f.year = entry.year
      f.language = entry.language
      f.url = entry.url
      f.comment = entry.comment
      f.flag_shared = STATE_NO		# @see thread_share.pas (line 1065)
      f.flag_downloaded = STATE_YES if entry.progress > 0 else STATE_NO
      f.flag_completed = STATE_YES if entry.size == entry.progress else STATE_NO
      f.flag_corrupted = STATE_NO if entry.phash_verified >= entry.progress else STATE_UNKNOWN

      f.metadata['bytes_downloaded'] = '%d bytes (%2.f%%)' % (entry.progress, pct_progress)
      f.metadata['arestra_subfolder'] = entry.subfolder
      f.metadata['download_start_time'] = entry.begin_time
      f.metadata['arestra_creation_time'] = entry.creation_time
      f.metadata['arestra_last_modification_time'] = entry.last_modification_time
      f.metadata['flag_paused'] = entry.flag_paused
      f.metadata['param1'] = entry.param1
      f.metadata['param2'] = entry.param2
      f.metadata['param3'] = entry.param3

      self.__local_files.append (f)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add info from PHashIdx.dat file
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_info_from_phashidx (self):

    for f in self.__local_files:
      entry = self.__phashidx.pop (f.hash_sha1, None)

      if entry:
        f.flag_downloaded = STATE_YES
        f.flag_completed = STATE_YES

        if f.size != -1:
          f.metadata['bytes_downloaded'] = f.size

        f.metadata['pieces'] = len (entry.hashes)
        f.metadata['pieces_downloaded'] = len (entry.hashes)
        f.metadata['pieces_to_download'] = 0
        f.metadata['phashidx_path'] = entry.phashidx_path
        f.metadata['phashidx_creation_time'] = entry.phashidx_creation_time
        f.metadata['phashidx_last_modification_time'] = entry.phashidx_last_modification_time

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add info from PHash_xxx.dat files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_info_from_phash (self):

    for f in self.__local_files:
      entry = self.__phash.pop (f.hash_sha1, None)

      if entry:
        progress = sum (p.progress for p in entry.pieces)
        pieces_done = [p for p in entry.pieces if p.done]

        if f.flag_downloaded != STATE_YES and progress > 0:
          f.flag_downloaded = STATE_YES

        f.metadata['pieces'] = len (entry.pieces)

        if pieces_done:
          f.metadata['piece_size'] = '%d bytes' % max (p.progress for p in pieces_done)

        f.metadata['pieces_downloaded'] = len (pieces_done)
        f.metadata['pieces_to_download'] = len (entry.pieces) - len (pieces_done)
        f.metadata['phash_path'] = entry.phash_path
        f.metadata['phash_creation_time'] = entry.phash_creation_time
        f.metadata['phash_last_modification_time'] = entry.phash_last_modification_time

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add info from PBTHash_xxx.dat files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_info_from_pbthash (self):
    files = [ f for f in self.__local_files if f.path and f.size ]

    for f in files:
      entry, ef = self.__pbthash.pop ((f.path.lower (), f.size), (None, None))

      if entry:
        f.comment = f.comment or entry.comment

        if all (p for p in entry.pieces if p.flag_checked):
          f.flag_corrupted = STATE_NO

        # flag_downloaded
        if entry.bytes_downloaded > 0:
          f.flag_downloaded = STATE_YES

        elif f.flag_downloaded == STATE_UNKNOWN:
          f.flag_downloaded = STATE_NO

        # flag_uploaded
        if f.flag_uploaded == STATE_UNKNOWN and entry.bytes_uploaded == 0:
          f.flag_uploaded = STATE_NO

        elif entry.bytes_uploaded > 0 and entry.num_files == 1:
          f.flag_uploaded = STATE_YES

        # flag_completed
        if entry.bytes_downloaded == entry.size:
          f.flag_completed = STATE_YES

        elif f.flag_completed == STATE_UNKNOWN:
          f.flag_completed = STATE_NO

        # bytes downloaded and uploaded
        if entry.num_files == 1:
          f.metadata['bytes_downloaded'] = entry.bytes_downloaded
          f.metadata['bytes_uploaded'] = entry.bytes_uploaded

        elif entry.bytes_downloaded == entry.size:
          f.metadata['bytes_downloaded'] = ef.size

        f.metadata['torrent_hash_sha1'] = entry.hash_sha1.upper ()
        f.metadata['torrent_piece_length'] = '%d bytes' % entry.piece_length
        f.metadata['torrent_pieces'] = entry.num_pieces
        f.metadata['torrent_files'] = entry.num_files
        f.metadata['file_idx'] = ef.idx + 1
        f.metadata['db_time'] = entry.db_time
        f.metadata['download_start_time'] = entry.start_time
        f.metadata['pbthash_path'] = entry.pbthash_path
        f.metadata['pbthash_creation_time'] = entry.pbthash_creation_time
        f.metadata['pbthash_last_modification_time'] = entry.pbthash_last_modification_time

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add local files from PBTHash_xxx.dat files
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_local_files_from_pbthash (self):

    for entry, ef in self.__pbthash.values ():
      f = local_file ()
      f.app_id = 'ares'
      f.app = 'Ares Galaxy'
      f.size = ef.size
      f.name = ef.name
      f.path = ef.path

      # flags
      flag_checked = all (p for p in entry.pieces if p.flag_checked)

      f.flag_shared = STATE_YES if entry.flag_seeding else STATE_NO
      f.flag_completed = STATE_YES if entry.bytes_downloaded == entry.size else STATE_UNKNOWN
      f.flag_corrupted = STATE_NO if flag_checked else STATE_UNKNOWN

      # flag_downloaded
      if entry.bytes_downloaded == 0:
        f.flag_downloaded = STATE_NO

      elif entry.bytes_downloaded == entry.size:
        f.flag_downloaded = STATE_YES

      elif entry.bytes_downloaded > 0 and entry.num_files == 1:
        f.flag_downloaded = STATE_YES

      # flag_uploaded
      if entry.bytes_uploaded == 0:
        f.flag_uploaded = STATE_NO

      elif entry.bytes_uploaded > 0 and entry.num_files == 1:
        f.flag_uploaded = STATE_YES

      # metadata
      f.metadata['torrent_hash_sha1'] = entry.hash_sha1.upper ()
      f.metadata['torrent_piece_length'] = '%d bytes' % entry.piece_length
      f.metadata['torrent_pieces'] = entry.num_pieces
      f.metadata['torrent_files'] = entry.num_files
      f.metadata['file_idx'] = ef.idx + 1
      f.metadata['db_time'] = entry.db_time
      f.metadata['download_start_time'] = entry.start_time
      f.metadata['pbthash_path'] = entry.pbthash_path
      f.metadata['pbthash_creation_time'] = entry.pbthash_creation_time
      f.metadata['pbthash_last_modification_time'] = entry.pbthash_last_modification_time

      self.__local_files.append (f)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add local files from TorrentH.dat file
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __add_local_files_from_torrenth (self):

    for entry in self.__torrenth:
      f = local_file ()
      f.app_id = 'ares'
      f.app = 'Ares Galaxy'
      f.path = entry.path
      f.size = entry.size
      f.name = entry.name
      f.title = entry.title
      f.comment = entry.comment
      f.filetype = entry.filetype
      f.url = entry.url

      # flags
      f.flag_shared = STATE_YES			# @see DHT/thread_dht.pas (line 412)
      f.flag_downloaded = STATE_YES		# @see DHT/dhtkeywords.pas (line 355)
      # @todo f.flag_uploaded
      f.flag_completed = STATE_YES		# @see DHT/dhtkeywords.pas (line 355)
      f.flag_corrupted = STATE_NO		# @see DHT/dhtkeywords.pas (line 355)

      # metadata
      f.metadata['dht_added_time'] = entry.timestamp
      f.metadata['dht_hash_sha1'] = entry.hash_sha1
      f.metadata['dht_verified'] = 'yes' if entry.flag_verified else 'no'
      f.metadata['seeds'] = entry.seeds
      f.metadata['torrenth_path'] = entry.torrenth_path
      f.metadata['torrenth_creation_time'] = entry.torrenth_creation_time
      f.metadata['torrenth_last_modification_time'] = entry.torrenth_last_modification_time

      # add to model
      self.__local_files.append (f)
