# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2022 The MVT Project Authors.
# Use of this software is governed by the MVT License 1.1 that can be found at
#   https://license.mvt.re/1.1/

import logging
import os

import pkg_resources

from mvt.android.lookups.koodous import koodous_lookup
from mvt.android.lookups.virustotal import virustotal_lookup

from .base import AndroidExtraction

log = logging.getLogger(__name__)


class Packages(AndroidExtraction):
    """This module extracts the list of installed packages."""

    def __init__(self, file_path=None, base_folder=None, output_folder=None,
                 serial=None, fast_mode=False, log=None, results=[]):
        super().__init__(file_path=file_path, base_folder=base_folder,
                         output_folder=output_folder, fast_mode=fast_mode,
                         log=log, results=results)

    def serialize(self, record):
        records = []

        timestamps = [
            {"event": "package_install", "timestamp": record["timestamp"]},
            {"event": "package_first_install", "timestamp": record["first_install_time"]},
            {"event": "package_last_update", "timestamp": record["last_update_time"]},
        ]

        for ts in timestamps:
            records.append({
                "timestamp": ts["timestamp"],
                "module": self.__class__.__name__,
                "event": ts["event"],
                "data": f"{record['package_name']} (system: {record['system']}, third party: {record['third_party']})",
            })

        return records

    def check_indicators(self):
        root_packages_path = os.path.join("..", "..", "data", "root_packages.txt")
        root_packages_string = pkg_resources.resource_string(__name__, root_packages_path)
        root_packages = root_packages_string.decode("utf-8").split("\n")
        root_packages = [rp.strip() for rp in root_packages]

        for result in self.results:
            if result["package_name"] in root_packages:
                self.log.warning("Found an installed package related to rooting/jailbreaking: \"%s\"",
                                 result["package_name"])
                self.detected.append(result)
                continue

            ioc = self.indicators.check_app_id(result.get("package_name"))
            if ioc:
                result["matched_indicator"] = ioc
                self.detected.append(result)
                continue

            for package_file in result["files"]:
                ioc = self.indicators.check_file_hash(package_file["sha256"])
                if ioc:
                    result["matched_indicator"] = ioc
                    self.detected.append(result)

    def _get_files_for_package(self, package_name):
        output = self._adb_command(f"pm path {package_name}")
        output = output.strip().replace("package:", "")
        if not output:
            return []

        package_files = []
        for file_path in output.split("\n"):
            file_path = file_path.strip()

            md5 = self._adb_command(f"md5sum {file_path}").split(" ")[0]
            sha1 = self._adb_command(f"sha1sum {file_path}").split(" ")[0]
            sha256 = self._adb_command(f"sha256sum {file_path}").split(" ")[0]
            sha512 = self._adb_command(f"sha512sum {file_path}").split(" ")[0]

            package_files.append({
                "path": file_path,
                "md5": md5,
                "sha1": sha1,
                "sha256": sha256,
                "sha512": sha512,
            })

        return package_files

    def run(self):
        self._adb_connect()

        packages = self._adb_command("pm list packages -U -u -i -f")
        if packages.strip() == "Error: Unknown option: -U":
            packages = self._adb_command("pm list packages -u -i -f")

        for line in packages.split("\n"):
            line = line.strip()
            if not line.startswith("package:"):
                continue

            fields = line.split()
            file_name, package_name = fields[0].split(":")[1].rsplit("=", 1)

            try:
                installer = fields[1].split("=")[1].strip()
            except IndexError:
                installer = None
            else:
                if installer == "null":
                    installer = None

            try:
                uid = fields[2].split(":")[1].strip()
            except IndexError:
                uid = None

            dumpsys = self._adb_command(f"dumpsys package {package_name} | grep -A2 timeStamp").split("\n")
            timestamp = dumpsys[0].split("=")[1].strip()
            first_install = dumpsys[1].split("=")[1].strip()
            last_update = dumpsys[2].split("=")[1].strip()

            package_files = self._get_files_for_package(package_name)

            self.results.append({
                "package_name": package_name,
                "file_name": file_name,
                "installer": installer,
                "timestamp": timestamp,
                "first_install_time": first_install,
                "last_update_time": last_update,
                "uid": uid,
                "disabled": False,
                "system": False,
                "third_party": False,
                "files": package_files,
            })

        cmds = [
            {"field": "disabled", "arg": "-d"},
            {"field": "system", "arg": "-s"},
            {"field": "third_party", "arg": "-3"},
        ]
        for cmd in cmds:
            output = self._adb_command(f"pm list packages {cmd['arg']}")
            for line in output.split("\n"):
                line = line.strip()
                if not line.startswith("package:"):
                    continue

                package_name = line.split(":", 1)[1]
                for i, result in enumerate(self.results):
                    if result["package_name"] == package_name:
                        self.results[i][cmd["field"]] = True

        packages_to_lookup = []
        for result in self.results:
            if result["system"]:
                continue

            packages_to_lookup.append(result)
            self.log.info("Found non-system package with name \"%s\" installed by \"%s\" on %s",
                          result["package_name"], result["installer"], result["timestamp"])

        if not self.fast_mode:
            virustotal_lookup(packages_to_lookup)
            koodous_lookup(packages_to_lookup)

        self.log.info("Extracted at total of %d installed package names",
                      len(self.results))

        self._adb_disconnect()
