Пример #1
0
    def test_restore_old_system(self):
        """
        This method test AutoContinue().restore() for the case that we run the
        most recent version but with data from the old system.
        """

        PyFunceble.CONFIGURATION["auto_continue"] = True
        File(self.file).delete()

        old_system = {
            PyFunceble.CONFIGURATION["file_to_test"]: {
                "number_of_up": 15,
                "number_of_down": 18,
                "number_of_invalid": 5,
                "number_of_tested": 38,
            }
        }

        Dict(old_system).to_json(self.file)
        AutoContinue().restore()

        expected = {"up": 15, "down": 18, "invalid": 5, "tested": 38}
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)

        self.set_counter(0)

        expected = {"up": 0, "down": 0, "invalid": 0, "tested": 0}
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)
        PyFunceble.CONFIGURATION["auto_continue"] = False
        File(self.file).delete()
Пример #2
0
    def test_backup(self):
        """
        Test AutoContinue().backup().
        """

        self.test_delete_file()
        PyFunceble.CONFIGURATION["auto_continue"] = True
        self.set_counter(to_set=25)

        AutoContinue().backup()

        expected = True
        actual = PyFunceble.path.isfile(self.file_to_work_with)

        self.assertEqual(expected, actual)

        expected = {
            PyFunceble.INTERN["file_to_test"]: {
                "up": 25,
                "down": 25,
                "invalid": 25,
                "tested": 25,
            }
        }
        actual = Dict().from_json(File(self.file_to_work_with).read())

        self.assertEqual(expected, actual)

        del PyFunceble.CONFIGURATION["auto_continue"]
        self.test_delete_file()
Пример #3
0
    def test_backup(self):
        """
        This function test AutoContinue().backup().
        """

        PyFunceble.CONFIGURATION["auto_continue"] = True

        File(self.file).delete()

        expected = False
        actual = PyFunceble.path.isfile(self.file)

        self.assertEqual(expected, actual)
        self.set_counter(to_set=25)

        AutoContinue().backup()

        expected = True
        actual = PyFunceble.path.isfile(self.file)

        self.assertEqual(expected, actual)

        expected = {
            PyFunceble.CONFIGURATION["file_to_test"]: {
                "up": 25, "down": 25, "invalid": 25, "tested": 25
            }
        }
        actual = Dict().from_json(File(self.file).read())

        self.assertEqual(expected, actual)
        PyFunceble.CONFIGURATION["auto_continue"] = False
        File(self.file).delete()
Пример #4
0
    def __init__(self, file, file_type="domain"):
        # We share the file we are working with.
        self.file = file
        # We share the file/test type.
        self.file_type = file_type

        # We construct the list of UP statuses.
        self.list_of_up_statuses = PyFunceble.STATUS["list"]["up"]
        self.list_of_up_statuses.extend(PyFunceble.STATUS["list"]["valid"])

        # We get/initiate the db.
        self.sqlite_db = SQLite()
        self.mysql_db = MySQL()

        # We get/initiate the preset class.
        self.preset = PyFunceble.Preset()
        # We get/initiate the autosave database/subsyste..
        self.autosave = AutoSave(start_time=PyFunceble.INTERN["start"])
        # We get/initiate the inactive database.
        self.inactive_db = InactiveDB(self.file,
                                      sqlite_db=self.sqlite_db,
                                      mysql_db=self.mysql_db)
        # We get/initiate the whois database.
        self.whois_db = WhoisDB(sqlite_db=self.sqlite_db,
                                mysql_db=self.mysql_db)
        # We get/initiate the mining subsystem.
        self.mining = Mining(self.file,
                             sqlite_db=self.sqlite_db,
                             mysql_db=self.mysql_db)
        # We get/initiate the autocontinue subsystem.
        self.autocontinue = AutoContinue(
            self.file,
            parent_process=True,
            sqlite_db=self.sqlite_db,
            mysql_db=self.mysql_db,
        )

        # We initiate a variable which will tell us when
        # we start testing for complements.
        self.complements_test_started = False

        # We download the file if it is a list.
        self.download_link()
Пример #5
0
    def _file_decision(self, current, last, status=None):
        """
        Manage the database, autosave and autocontinue systems for the case that we are reading
        a file.

        Arguments:
            - status: str
                The current status of current.
            - current: str
                The current domain or URL we are testing.
            - last: str
                The last domain or URL of the file we are testing.
        """

        if status:
            if not PyFunceble.CONFIGURATION[
                    "simple"] and PyFunceble.CONFIGURATION["file_to_test"]:
                if PyFunceble.CONFIGURATION["inactive_database"]:
                    if status.lower() in PyFunceble.STATUS["list"]["up"]:
                        Database().remove()
                    else:
                        Database().add()

                AutoContinue().backup()

                if current != last:
                    AutoSave()
                else:
                    ExecutionTime("stop")
                    Percentage().log()
                    self.reset_counters()
                    AutoContinue().backup()

                    self.colored_logo()

                    AutoSave(True)

        for index in ["http_code", "referer"]:
            if index in PyFunceble.CONFIGURATION:
                PyFunceble.CONFIGURATION[index] = ""
Пример #6
0
    def test_backup_not_activated(self):
        """
        This function test AutoContinue().backup() for the case that we did not
        activated the backup system.
        """

        PyFunceble.CONFIGURATION["auto_continue"] = False

        AutoContinue().backup()

        expected = False
        actual = PyFunceble.path.isfile(self.file)

        self.assertEqual(expected, actual)
Пример #7
0
    def file(self):
        """
        Manage the case that need to test each domain of a given file path.
        Note: 1 domain per line.
        """

        list_to_test = self._extract_domain_from_file()

        AutoContinue().restore()

        if PyFunceble.CONFIGURATION["adblock"]:
            list_to_test = self.adblock_decode(list_to_test)
        else:
            list_to_test = list(map(self._format_domain, list_to_test))

        PyFunceble.Clean(list_to_test)

        if PyFunceble.CONFIGURATION["inactive_database"]:
            Database().to_test()

            if PyFunceble.CONFIGURATION[
                    "file_to_test"] in PyFunceble.CONFIGURATION[
                        "inactive_db"] and "to_test" in PyFunceble.CONFIGURATION[
                            "inactive_db"][PyFunceble.CONFIGURATION[
                                "file_to_test"]] and PyFunceble.CONFIGURATION[
                                    "inactive_db"][PyFunceble.CONFIGURATION[
                                        "file_to_test"]]["to_test"]:
                list_to_test.extend(PyFunceble.CONFIGURATION["inactive_db"][
                    PyFunceble.CONFIGURATION["file_to_test"]]["to_test"])

        regex_delete = r"localhost$|localdomain$|local$|broadcasthost$|0\.0\.0\.0$|allhosts$|allnodes$|allrouters$|localnet$|loopback$|mcastprefix$"  # pylint: disable=line-too-long

        list_to_test = List(
            Regex(list_to_test, regex_delete).not_matching_list()).format()

        if PyFunceble.CONFIGURATION["filter"]:
            list_to_test = List(
                Regex(list_to_test,
                      PyFunceble.CONFIGURATION["filter"],
                      escape=True).matching_list()).format()

        list(
            map(
                self.domain,
                list_to_test[PyFunceble.
                             CONFIGURATION["counter"]["number"]["tested"]:],
                repeat(list_to_test[-1]),
            ))
Пример #8
0
    def test_backup_not_activated(self):
        """
        Test AutoContinue().backup() for the case that we did not
        activated the backup system.
        """

        self.test_delete_file()
        PyFunceble.CONFIGURATION["auto_continue"] = False

        AutoContinue().backup()

        expected = False
        actual = PyFunceble.path.isfile(self.file_to_work_with)

        self.assertEqual(expected, actual)

        del PyFunceble.CONFIGURATION["auto_continue"]
        self.test_delete_file()
Пример #9
0
    def test_restore(self):
        """
        Test AutoContinue().restore().
        """

        self.test_delete_file()
        PyFunceble.CONFIGURATION["auto_continue"] = True
        self.set_counter(12)

        expected = {"up": 12, "down": 12, "invalid": 12, "tested": 12}
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)

        saved = {
            PyFunceble.INTERN["file_to_test"]: {
                "up": 17,
                "down": 12,
                "invalid": 8,
                "tested": 37,
            }
        }

        Dict(saved).to_json(self.file_to_work_with)
        AutoContinue().restore()

        expected = saved[PyFunceble.INTERN["file_to_test"]]
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)

        self.set_counter(0)

        expected = {"up": 0, "down": 0, "invalid": 0, "tested": 0}
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)

        del PyFunceble.CONFIGURATION["auto_continue"]
        self.test_delete_file()
Пример #10
0
    def url_file(self):
        """
        Manage the case that we have to test a file
        Note: 1 URL per line.
        """

        list_to_test = self._extract_domain_from_file()

        AutoContinue().restore()

        PyFunceble.Clean(list_to_test)

        if PyFunceble.CONFIGURATION["inactive_database"]:
            Database().to_test()

            if PyFunceble.CONFIGURATION[
                    "file_to_test"] in PyFunceble.CONFIGURATION[
                        "inactive_db"] and "to_test" in PyFunceble.CONFIGURATION[
                            "inactive_db"][PyFunceble.CONFIGURATION[
                                "file_to_test"]] and PyFunceble.CONFIGURATION[
                                    "inactive_db"][PyFunceble.CONFIGURATION[
                                        "file_to_test"]]["to_test"]:
                list_to_test.extend(PyFunceble.CONFIGURATION["inactive_db"][
                    PyFunceble.CONFIGURATION["file_to_test"]]["to_test"])

        if PyFunceble.CONFIGURATION["filter"]:
            list_to_test = List(
                Regex(list_to_test,
                      PyFunceble.CONFIGURATION["filter"],
                      escape=True).matching_list()).format()

        list(
            map(
                self.url,
                list_to_test[PyFunceble.
                             CONFIGURATION["counter"]["number"]["tested"]:],
                repeat(list_to_test[-1]),
            ))
Пример #11
0
    def test_restore(self):
        """
        This method test AutoContinue().restore().
        """

        PyFunceble.CONFIGURATION["auto_continue"] = True
        File(self.file).delete()

        self.set_counter(12)

        expected = {"up": 12, "down": 12, "invalid": 12, "tested": 12}
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)

        saved = {
            PyFunceble.CONFIGURATION["file_to_test"]: {
                "up": 17, "down": 12, "invalid": 8, "tested": 37
            }
        }

        Dict(saved).to_json(self.file)
        AutoContinue().restore()

        expected = saved[PyFunceble.CONFIGURATION["file_to_test"]]
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)

        self.set_counter(0)

        expected = {"up": 0, "down": 0, "invalid": 0, "tested": 0}
        actual = PyFunceble.CONFIGURATION["counter"]["number"]

        self.assertEqual(expected, actual)
        PyFunceble.CONFIGURATION["auto_continue"] = False
        File(self.file).delete()
Пример #12
0
class FileCore:  # pylint: disable=too-many-instance-attributes
    """
    Brain of PyFunceble for file testing.

    :param str file: The file we are testing.

    :param str file_type:
        The file type.
        Should be one of the following.

            - :code:`domain`

            - :code:`url`
    """

    # We set a regex of element to delete.
    # Understand with this variable that we don't want to test those.
    regex_ignore = r"localhost$|localdomain$|local$|broadcasthost$|0\.0\.0\.0$|allhosts$|allnodes$|allrouters$|localnet$|loopback$|mcastprefix$|ip6-mcastprefix$|ip6-localhost$|ip6-loopback$|ip6-allnodes$|ip6-allrouters$|ip6-localnet$"  # pylint: disable=line-too-long

    def __init__(self, file, file_type="domain"):
        # We share the file we are working with.
        self.file = file
        # We share the file/test type.
        self.file_type = file_type

        # We construct the list of UP statuses.
        self.list_of_up_statuses = PyFunceble.STATUS["list"]["up"]
        self.list_of_up_statuses.extend(PyFunceble.STATUS["list"]["valid"])

        # We get/initiate the db.
        self.sqlite_db = SQLite()
        self.mysql_db = MySQL()

        # We get/initiate the preset class.
        self.preset = PyFunceble.Preset()
        # We get/initiate the autosave database/subsyste..
        self.autosave = AutoSave(start_time=PyFunceble.INTERN["start"])
        # We get/initiate the inactive database.
        self.inactive_db = InactiveDB(self.file,
                                      sqlite_db=self.sqlite_db,
                                      mysql_db=self.mysql_db)
        # We get/initiate the whois database.
        self.whois_db = WhoisDB(sqlite_db=self.sqlite_db,
                                mysql_db=self.mysql_db)
        # We get/initiate the mining subsystem.
        self.mining = Mining(self.file,
                             sqlite_db=self.sqlite_db,
                             mysql_db=self.mysql_db)
        # We get/initiate the autocontinue subsystem.
        self.autocontinue = AutoContinue(
            self.file,
            parent_process=True,
            sqlite_db=self.sqlite_db,
            mysql_db=self.mysql_db,
        )

        # We initiate a variable which will tell us when
        # we start testing for complements.
        self.complements_test_started = False

        # We download the file if it is a list.
        self.download_link()

    @classmethod
    def get_simple_coloration(cls, status):
        """
        Given a status we give the coloration for the simple mode.

        :param str status: An official status output.
        """

        if status in [
                PyFunceble.STATUS["official"]["up"],
                PyFunceble.STATUS["official"]["valid"],
        ]:
            # The status is in the list of UP status.

            # We return the green coloration.
            return PyFunceble.Fore.GREEN + PyFunceble.Style.BRIGHT

        if status == PyFunceble.STATUS["official"]["down"]:
            # The status is in the list of DOWN status.

            # We return the red coloration.
            return PyFunceble.Fore.RED + PyFunceble.Style.BRIGHT

        # The status is not in the list of UP nor DOWN status.

        # We return the cyam coloration.
        return PyFunceble.Fore.CYAN + PyFunceble.Style.BRIGHT

    def download_link(self):  # pragma: no cover
        """
        Download the file if it is an URL.
        """

        if PyFunceble.Check(self.file).is_url():
            # We get the destination.
            destination = self.file.split("/")[-1]

            if self.file and self.autocontinue.is_empty():
                # The given file is an URL.

                if (not PyFunceble.path.isfile(destination) or
                        PyFunceble.INTERN["counter"]["number"]["tested"] == 0):
                    # The filename does not exist in the current directory
                    # or the currently number of tested is equal to 0.

                    # We download the content of the link.
                    Download(self.file, destination).text()

            # We update the global file with the destination.
            self.file = destination

    def domain(self, subject):  # pragma: no cover
        """
        Handle the test of a single domain.

        :param str subject: The subject we are testing.
        """

        if subject:
            # The given subject is not empty nor None.

            if PyFunceble.CONFIGURATION["syntax"]:
                # The syntax mode is activated.

                # We get the status from SyntaxStatus.
                status = SyntaxStatus(subject,
                                      subject_type="file_domain",
                                      filename=self.file).get()["status"]
            else:
                # We test and get the status of the domain.
                status = Status(
                    subject,
                    subject_type="file_domain",
                    filename=self.file,
                    whois_db=self.whois_db,
                    inactive_db=self.inactive_db,
                ).get()["status"]

            if PyFunceble.CONFIGURATION["simple"]:
                # The simple mode is activated.

                # We print the domain and the status.
                print("{0} {1}".format(
                    self.get_simple_coloration(status) + subject, status))

            if self.complements_test_started:
                # We started to test the complements.

                # We generate the complement file(s).
                Generate(subject, "file_domain", status).complements_file()

            # We return the status.
            return status

        # We return None, there is nothing to test.
        return None

    def url(self, subject):  # pragma: no cover
        """
        Handle the simple URL testing.

        :param str subject: The subject we are testing.
        """

        if subject:
            # The given subject is not empty nor None.

            if PyFunceble.CONFIGURATION["syntax"]:
                # The syntax mode is activated.

                # We get the status from SyntaxStatus.
                status = SyntaxStatus(subject,
                                      subject_type="file_url",
                                      filename=self.file).get()["status"]
            else:
                # We test and get the status of the domain.
                status = URLStatus(
                    subject,
                    subject_type="file_url",
                    filename=self.file,
                    inactive_db=self.inactive_db,
                ).get()["status"]

            if PyFunceble.CONFIGURATION["simple"]:
                # The simple mode is activated.

                # We print the domain and the status.
                print("{0} {1}".format(
                    self.get_simple_coloration(status) + subject, status))

            if self.complements_test_started:
                # We started to test the complements.

                # We generate the complement file(s).
                Generate(subject, "file_url", status).complements_file()

            # We retunr the status.
            return status

        # We return None, there is nothing to test.
        return None

    @classmethod
    def _format_line(cls, line):
        """
        Format the extracted line before passing it to the system.

        :param str line: The extracted line.

        :return: The formatted line with only the element to test.
        :rtype: str

        .. note:
            Understand by formating the fact that we get rid
            of all the noises around the element we want to test.
        """

        line = line.strip()

        if line and not line.startswith("#"):
            # The line is not a commented line.

            if "#" in line:
                # There is a comment at the end of the line.

                # We delete the comment from the line.
                line = line[:line.find("#")].strip()

            if " " in line or "\t" in line:
                # A space or a tabs is in the line.

                # We remove all whitestring from the extracted line.
                splited_line = line.split()

                # As there was a space or a tab in the string, we consider
                # that we are working with the hosts file format which means
                # that the domain we have to test is after the first string.
                # So we set the index to 1.
                index = 1

                while index < len(splited_line):
                    # We loop until the index is greater than the length of
                    #  the splited line.

                    if splited_line[index]:
                        # The element at the current index is not an empty string.

                        # We break the loop.
                        break

                    # The element at the current index is an empty string.

                    # We increase the index number.
                    index += 1  # pragma: no cover

                # We return the last read element.
                return splited_line[index]

            # We return the extracted line.
            return line

        # The extracted line is a comment line.

        # We return an empty string as we do not want to work with commented line.
        return ""

    def __process_test(self, subject):  # pragma: no cover
        """
        Given a subject, we perform its test.

        :param str subject: The subjet we have to test.
        """

        if self.file_type == "domain":
            # We are testing for domains.

            if PyFunceble.CONFIGURATION["idna_conversion"]:
                # We have to convert to IDNA:

                # We get and return the status of the IDNA
                # domain.
                return self.domain(domain2idna(subject))

            # We get and return the status of the domain.
            return self.domain(subject)

        if self.file_type == "url":
            # We are testing for urls.

            # We get and return the status of the URL.
            return self.url(subject)

        # We raise an exception, we could not understand the
        # given file type.
        raise Exception("Unknown file type.")

    def get_complements(self):  # pragma: no cover
        """
        Generate a list of complements to test.
        """

        # We initiate an empty list of complements.
        complements = []

        if (PyFunceble.CONFIGURATION["generate_complements"]
                and self.autocontinue.authorized):
            # * The user want us to generate and test the list
            # of all complements.
            # and
            # * The autocontinue subsystem is activated.

            # We inform all subsystem that we are testing for complements.
            self.complements_test_started = True

            # We get/generate the complements.
            complements = self.autocontinue.get_or_generate_complements()

        return complements

    def _test_line(self, line, manager_data=None):  # pylint: disable=too-many-branches  # pragma: no cover
        """
        Given a line, we test it.

        :param str line: A line to work with.
        :param multiprocessing.Manager.list manager_data: A Server process.
        """

        if PyFunceble.CONFIGURATION[
                "db_type"] == "json" and manager_data is not None:
            autocontinue = AutoContinue(self.file, parent_process=False)
            inactive_db = InactiveDB(self.file)
            mining = Mining(self.file)
        else:
            # We use the previously initiated autocontinue instance.
            autocontinue = self.autocontinue

            # We use the previously initiated inactive database instance.
            inactive_db = self.inactive_db

            # We use the previously initiated mining instance.
            mining = self.mining

        # We remove cariage from the given line.
        line = line.strip()

        if not line or line[0] == "#":
            # We line is a comment line.

            # We return None, there is nothing to test.
            return None

        if Regex(line, self.regex_ignore, escape=False,
                 return_data=False).match():
            # The line match our list of elemenet
            # to ignore.

            # We return None, there is nothing to test.
            return None

        # We format the line, it's the last
        # rush before starting to filter and test.
        subject = self._format_line(line)

        if (not PyFunceble.CONFIGURATION["local"]
                and PyFunceble.Check(subject).is_reserved_ipv4()):
            # * We are not testing for local components.
            # and
            # * The subject is a reserved IPv4.

            # We return None, there is nothing to test.
            return None

        if PyFunceble.CONFIGURATION["filter"]:
            # We have to filter.

            if Regex(subject,
                     PyFunceble.CONFIGURATION["filter"],
                     return_data=False).match():
                # The line match the given filter.

                # We get the status of the current line.
                status = self.__process_test(subject)
            else:
                # The line does not match the given filter.

                # We return None.
                return None
        else:
            # We do not have to filter.

            # We get the status of the current line.
            status = self.__process_test(subject)

        # We add the line into the auto continue database.
        autocontinue.add(subject, status)

        if status.lower() in self.list_of_up_statuses:
            # The status is in the list of UP status.

            # We mine if necessary.
            mining.mine(subject, self.file_type)

            if subject in inactive_db:
                # The subject is in the inactive database.

                # We generate the suspicous file.
                Generate(subject, "file_domain", PyFunceble.STATUS["official"]
                         ["up"]).analytic_file("suspicious")

                # And we remove the current subject from
                # the inactive database.
                inactive_db.remove(subject)
        else:
            # The status is not in the list of UP status.

            # We add the current subject into the
            # inactive database.
            inactive_db.add(subject, status)

        if (self.complements_test_started
                and PyFunceble.CONFIGURATION["db_type"] == "json"):
            # We started the test of the complements.

            if "complements" in autocontinue.database:
                # The complement index is present.

                while subject in autocontinue.database["complements"]:
                    # We loop untill the line is not present into the
                    # database.

                    # We remove the currently tested element.
                    autocontinue.database["complements"].remove(subject)

                    # We save the current state.
                    autocontinue.save()

        if manager_data is None:
            # We are not in a multiprocess environment.

            # We update the counters
            autocontinue.update_counters()

            # We process the autosaving if it is necessary.
            self.autosave.process(test_completed=False)
        elif PyFunceble.CONFIGURATION["db_type"] == "json":
            # We are in a multiprocess environment.

            # We save everything we initiated into the server process
            manager_data.append({
                "autocontinue": autocontinue.database,
                "inactive_db": inactive_db.database,
                "mining": mining.database,
            })

        # We return None.
        return None

    def _get_list_to_of_subjects_to_test_from_file(
            self, file_object):  # pragma: no cover
        """
        Give a file object, we construct/get the list of subject to test.
        """

        to_retest_inactive_db = self.inactive_db.get_to_retest()

        if PyFunceble.CONFIGURATION["multiprocess"]:
            with Pool(PyFunceble.CONFIGURATION["maximal_processes"]) as pool:
                if not PyFunceble.CONFIGURATION["adblock"]:
                    formatted_subjects = set(
                        pool.map(self._format_line, file_object))
                else:
                    formatted_subjects = {
                        x
                        for x in AdBlock(file_object).decode()
                    }
        else:
            if not PyFunceble.CONFIGURATION["adblock"]:
                formatted_subjects = {
                    self._format_line(x)
                    for x in file_object
                }
            else:
                formatted_subjects = {x for x in AdBlock(file_object).decode()}

        subjects_to_test = (formatted_subjects -
                            self.autocontinue.get_already_tested() -
                            self.inactive_db.get_already_tested() -
                            to_retest_inactive_db)

        if not subjects_to_test:
            subjects_to_test = list(formatted_subjects)
        else:
            subjects_to_test = list(subjects_to_test)

        if not PyFunceble.CONFIGURATION["multiprocess"]:
            if not PyFunceble.CONFIGURATION["hierarchical_sorting"]:
                subjects_to_test = List(subjects_to_test).custom_format(
                    Sort.standard)
            else:
                subjects_to_test = List(subjects_to_test).custom_format(
                    Sort.hierarchical)

        return chain(subjects_to_test, to_retest_inactive_db)

    def read_and_test_file_content(self):  # pragma: no cover
        """
        Read a file block by block and test its content.
        """

        # We print the CLI header.
        PyFunceble.CLICore.print_header()

        with open(self.file, "r", encoding="utf-8") as file:
            # We open the file we have to test.

            for line in self._get_list_to_of_subjects_to_test_from_file(file):
                # We loop through the file decoded file
                # content.

                # We test the line.
                self._test_line(line)

        for index, line in self.mining.list_of_mined():
            # We loop through the list of mined domains
            # (if the mining subystem is activated.)

            # We test the line.
            self._test_line(line)
            # and remove the currently tested line
            # from the mining database.
            self.mining.remove(index, line)

        for subject in self.get_complements():
            # We loop through the list of complements.

            # We test the complement.
            self._test_line(subject)

        # We inform all subsystem that we are not testing for complements anymore.
        self.complements_test_started = False

        # We update the counters
        self.autocontinue.update_counters()
        # We clean the autocontinue subsystem, we finished
        # the test.
        self.autocontinue.clean()
        # We process the autosaving if necessary.
        self.autosave.process(test_completed=True)
        # We close the database connection
        if self.sqlite_db.authorized:
            self.sqlite_db.connection.close()
        if self.mysql_db.authorized:
            self.mysql_db.get_connection().close()
Пример #13
0
    def _test_line(self, line, manager_data=None):  # pylint: disable=too-many-branches  # pragma: no cover
        """
        Given a line, we test it.

        :param str line: A line to work with.
        :param multiprocessing.Manager.list manager_data: A Server process.
        """

        if PyFunceble.CONFIGURATION[
                "db_type"] == "json" and manager_data is not None:
            autocontinue = AutoContinue(self.file, parent_process=False)
            inactive_db = InactiveDB(self.file)
            mining = Mining(self.file)
        else:
            # We use the previously initiated autocontinue instance.
            autocontinue = self.autocontinue

            # We use the previously initiated inactive database instance.
            inactive_db = self.inactive_db

            # We use the previously initiated mining instance.
            mining = self.mining

        # We remove cariage from the given line.
        line = line.strip()

        if not line or line[0] == "#":
            # We line is a comment line.

            # We return None, there is nothing to test.
            return None

        if Regex(line, self.regex_ignore, escape=False,
                 return_data=False).match():
            # The line match our list of elemenet
            # to ignore.

            # We return None, there is nothing to test.
            return None

        # We format the line, it's the last
        # rush before starting to filter and test.
        subject = self._format_line(line)

        if (not PyFunceble.CONFIGURATION["local"]
                and PyFunceble.Check(subject).is_reserved_ipv4()):
            # * We are not testing for local components.
            # and
            # * The subject is a reserved IPv4.

            # We return None, there is nothing to test.
            return None

        if PyFunceble.CONFIGURATION["filter"]:
            # We have to filter.

            if Regex(subject,
                     PyFunceble.CONFIGURATION["filter"],
                     return_data=False).match():
                # The line match the given filter.

                # We get the status of the current line.
                status = self.__process_test(subject)
            else:
                # The line does not match the given filter.

                # We return None.
                return None
        else:
            # We do not have to filter.

            # We get the status of the current line.
            status = self.__process_test(subject)

        # We add the line into the auto continue database.
        autocontinue.add(subject, status)

        if status.lower() in self.list_of_up_statuses:
            # The status is in the list of UP status.

            # We mine if necessary.
            mining.mine(subject, self.file_type)

            if subject in inactive_db:
                # The subject is in the inactive database.

                # We generate the suspicous file.
                Generate(subject, "file_domain", PyFunceble.STATUS["official"]
                         ["up"]).analytic_file("suspicious")

                # And we remove the current subject from
                # the inactive database.
                inactive_db.remove(subject)
        else:
            # The status is not in the list of UP status.

            # We add the current subject into the
            # inactive database.
            inactive_db.add(subject, status)

        if (self.complements_test_started
                and PyFunceble.CONFIGURATION["db_type"] == "json"):
            # We started the test of the complements.

            if "complements" in autocontinue.database:
                # The complement index is present.

                while subject in autocontinue.database["complements"]:
                    # We loop untill the line is not present into the
                    # database.

                    # We remove the currently tested element.
                    autocontinue.database["complements"].remove(subject)

                    # We save the current state.
                    autocontinue.save()

        if manager_data is None:
            # We are not in a multiprocess environment.

            # We update the counters
            autocontinue.update_counters()

            # We process the autosaving if it is necessary.
            self.autosave.process(test_completed=False)
        elif PyFunceble.CONFIGURATION["db_type"] == "json":
            # We are in a multiprocess environment.

            # We save everything we initiated into the server process
            manager_data.append({
                "autocontinue": autocontinue.database,
                "inactive_db": inactive_db.database,
                "mining": mining.database,
            })

        # We return None.
        return None
Пример #14
0
    def _file_list_to_test_filtering(self):
        """
        Unify the way we work before testing file contents.
        """

        # We get the list to test from the file we have to test.
        list_to_test = self._extract_domain_from_file()

        # We save the original list to test globally.
        PyFunceble.INTERN["extracted_list_to_test"] = list_to_test

        # We get the list of mined.
        mined_list = Mining().list_of_mined()

        if mined_list:
            list_to_test.extend(mined_list)

        # We generate the directory structure.
        DirectoryStructure()

        # We restore the data from the last session if it does exist.
        AutoContinue().restore()

        if PyFunceble.CONFIGURATION["adblock"]:
            # The adblock decoder is activated.

            # We get the list of domain to test (decoded).
            list_to_test = AdBlock(list_to_test).decode()
        else:
            # The adblock decoder is not activated.

            # We get the formatted list of domain to test.
            list_to_test = list(map(self._format_domain, list_to_test))

        # We clean the output directory if it is needed.
        PyFunceble.Clean(list_to_test)

        # We set the start time.
        ExecutionTime("start")

        # We get the list we have to test in the current session (from the database).
        Inactive().to_test()

        if (
            PyFunceble.CONFIGURATION["inactive_database"]
            and PyFunceble.INTERN["file_to_test"] in PyFunceble.INTERN["inactive_db"]
            and "to_test"
            in PyFunceble.INTERN["inactive_db"][PyFunceble.INTERN["file_to_test"]]
            and PyFunceble.INTERN["inactive_db"][PyFunceble.INTERN["file_to_test"]][
                "to_test"
            ]
        ):
            # * The current file to test in into the database.
            # and
            # * The `to_test` index is present into the database
            #   related to the file we are testing.
            # and
            # * The `to_test` index content is not empty.

            # We extend our list to test with the content of the `to_test` index
            # of the current file database.
            list_to_test.extend(
                PyFunceble.INTERN["inactive_db"][PyFunceble.INTERN["file_to_test"]][
                    "to_test"
                ]
            )

        # We set a regex of element to delete.
        # Understand with this variable that we don't want to test those.
        regex_delete = r"localhost$|localdomain$|local$|broadcasthost$|0\.0\.0\.0$|allhosts$|allnodes$|allrouters$|localnet$|loopback$|mcastprefix$|ip6-mcastprefix$|ip6-localhost$|ip6-loopback$|ip6-allnodes$|ip6-allrouters$|ip6-localnet$"  # pylint: disable=line-too-long

        # We load the flatten version of the database.
        PyFunceble.INTERN.update({"flatten_inactive_db": Inactive().content()})

        # We initiate a local variable which will save the current state of the list.
        not_filtered = list_to_test

        try:
            # We remove the element which are in the database from the
            # current list to test.
            list_to_test = List(
                list(
                    set(Regex(list_to_test, regex_delete).not_matching_list())
                    - set(PyFunceble.INTERN["flatten_inactive_db"])
                )
            ).format()
            _ = list_to_test[-1]
        except IndexError:
            # We test without the database removing.
            list_to_test = List(
                Regex(not_filtered, regex_delete).not_matching_list()
            ).format()

            # We delete the not_filtered variable.
            del not_filtered

        if PyFunceble.CONFIGURATION["filter"]:
            # The filter is not empty.

            # We get update our list to test. Indeed we only keep the elements which
            # matches the given filter.
            list_to_test = List(
                Regex(
                    list_to_test, PyFunceble.CONFIGURATION["filter"], escape=False
                ).matching_list()
            ).format()

        list_to_test = List(list(list_to_test)).custom_format(Sort.standard)

        if PyFunceble.CONFIGURATION["hierarchical_sorting"]:
            # The hierarchical sorting is desired by the user.

            # We format the list.
            list_to_test = List(list(list_to_test)).custom_format(Sort.hierarchical)

        # We return the final list to test.
        return list_to_test
Пример #15
0
    def _file_decision(self, current, last, status=None):
        """
        Manage the database, autosave and autocontinue systems for the case that we are reading
        a file.

        :param current: The currently tested element.
        :type current: str

        :param last: The last element of the list.
        :type last: str

        :param status: The status of the currently tested element.
        :type status: str
        """

        if (
            status
            and not PyFunceble.CONFIGURATION["simple"]
            and PyFunceble.INTERN["file_to_test"]
        ):
            # * The status is given.
            # and
            # * The simple mode is deactivated.
            # and
            # * A file to test is set.

            # We run the mining logic.
            Mining().process()

            # We delete the currently tested element from the mining
            # database.
            # Indeed, as it is tested, it is already in our
            # testing process which means that we don't need it into
            # the mining database.
            Mining().remove()

            if (
                status.lower() in PyFunceble.STATUS["list"]["up"]
                or status.lower() in PyFunceble.STATUS["list"]["valid"]
            ):
                # The status is in the list of up status.

                if Inactive().is_present():
                    # The currently tested element is in the database.

                    # We generate the suspicious file(s).
                    Generate("strange").analytic_file(
                        "suspicious", PyFunceble.STATUS["official"]["up"]
                    )

                    # We remove the currently tested element from the
                    # database.
                    Inactive().remove()

            else:
                # The status is not in the list of up status.

                # We add the currently tested element to the
                # database.
                Inactive().add()

            # We backup the current state of the file reading
            # for the case that we need to continue later.
            AutoContinue().backup()

            if current != last:
                # The current element is not the last one.

                # We run the autosave logic.
                AutoSave()
            else:
                # The current element is the last one.

                # We stop and log the execution time.
                ExecutionTime("stop", True)

                # We show/log the percentage.
                Percentage().log()

                # We reset the counters as we end the process.
                self.reset_counters()

                # We backup the current state of the file reading
                # for the case that we need to continue later.
                AutoContinue().backup()

                # We show the colored logo.
                self.colorify_logo()

                # We save and stop the script if we are under
                # Travis CI.
                AutoSave(True)

        for index in ["http_code", "referer"]:
            # We loop through some configuration index we have to empty.

            if index in PyFunceble.INTERN:
                # The index is in the configuration.

                # We empty the configuration index.
                PyFunceble.INTERN[index] = ""
Пример #16
0
class Core:  # pragma: no cover
    """
    Main entry to PyFunceble. Brain of the program. Also known as "put everything
    together to make the system works".

    :param domain_or_ip_to_test: A domain or IP to test.
    :type domain_or_ip_to_test: str

    :param file_path: A path to a file to read and test.
    :type file_path: str

    :param url_to_test: A URL to test.
    :type url_to_test: str

    :param url_file: A path to a file which contains URL to test.
    :type url_file: str

    :param link_to_test: A link to a file to download and test.
    :type link_to_test: str

    :param modulo_test:
        If set to True, it will tell the system that we are working as an
        exported module.
    :param modulo_test: bool
    """

    def __init__(self, **args):
        # We initiate our list of optional arguments with their default values.
        optional_arguments = {
            "domain_or_ip_to_test": None,
            "file_path": None,
            "url_to_test": None,
            "url_file": None,
            "modulo_test": False,
            "link_to_test": None,
        }

        # We initiate our optional_arguments in order to be usable all over the
        # class.
        for (arg, default) in optional_arguments.items():
            setattr(self, arg, args.get(arg, default))

        # We initiate a variable in order to avoid having to recall/declare
        # Status() over and over.
        self.status = Status()
        # We initiate a variable in order to avoid having to recall/declare
        # Check() over and over.
        self.checker = Check()
        # We initiate a variable in order to avoid having to recall/declare
        # Percentage() over and over.
        self.percentage = Percentage()
        # We initiate a variable in order to avoid having to recall/declare
        # URL() over and over.
        self.url_status = URL()
        # We initiate a variable in order to avoid having to recall/declare
        # Mining() over and over.
        self.mining = Mining()
        # We initiate a variable in order to avoid having to recall/declare
        # AutoContinue() over and over.
        self.auto_continue = None
        # We initiate a variable in order to avoid having to recall/declare
        # Syntax() over and over.
        self.syntax_status = Syntax()
        # We initiate a variable in order to avoid having to recall/declare
        # Inactive() over and over.
        self.inactive_database = Inactive()

        # We manage the entries.
        self._entry_management()

    def _entry_management_url_download(self, passed):
        """
        Check if the given information is a URL.
        If it is the case, it download and update the location of file to test.

        :param passed: The url passed to the system.
        :type passed: str

        :return: The state of the check.
        :rtype: bool
        """

        if passed and self.checker.is_url_valid(passed):
            # The passed string is an URL.

            # We get the file name based on the URL.
            # We actually just get the  string after the last `/` in the URL.
            file_to_test = passed.split("/")[-1]

            if (
                not PyFunceble.path.isfile(file_to_test)
                or PyFunceble.INTERN["counter"]["number"]["tested"] == 0
            ):
                # The filename does not exist in the current directory
                # or the currently number of tested is equal to 0.

                # We download the content of the link.
                Download(passed, file_to_test).text()

            # The files does exist or the currently number of tested is greater than
            # 0.

            # We initiate the file we have to test.
            PyFunceble.INTERN["file_to_test"] = file_to_test

            # We return true to say that everything goes right.
            return True

        # The passed string is not an URL.

        # We do not need to do anything else.
        return False

    def _entry_management_url(self):
        """
        Manage the loading of the url system.
        """

        if (
            self.url_file  # pylint: disable=no-member
            and not self._entry_management_url_download(
                self.url_file  # pylint: disable=no-member
            )
        ):  # pylint: disable=no-member
            # The current url_file is not a URL.

            # We initiate the filename as the file we have to test.
            PyFunceble.INTERN[
                "file_to_test"
            ] = self.url_file  # pylint: disable=no-member

    def _entry_management(self):  # pylint: disable=too-many-branches
        """
        Avoid to have 1 millions line into self.__init__()
        """

        if not self.modulo_test:  # pylint: disable=no-member
            # We are not in a module usage.

            # We set the file_path as the file we have to test.
            PyFunceble.INTERN[
                "file_to_test"
            ] = self.file_path  # pylint: disable=no-member

            # We check if the given file_path is an url.
            # If it is an URL we update the file to test and download
            # the given URL.
            self._entry_management_url()

            # We fix the environnement permissions.
            AutoSave().travis_permissions()

            # We check if we need to bypass the execution of PyFunceble.
            self.bypass()

            # We set the start time.
            ExecutionTime("start")

            if PyFunceble.CONFIGURATION["syntax"]:
                # We are checking for syntax.

                # We deactivate the http status code.
                PyFunceble.HTTP_CODE["active"] = False

            if self.domain_or_ip_to_test:  # pylint: disable=no-member
                # The given domain is not empty or None.

                # We initiate a variable which will tell the system the type
                # of the tested element.
                PyFunceble.INTERN["to_test_type"] = "domain"

                # We set the start time.
                ExecutionTime("start")

                # We deactivate the showing of percentage as we are in a single
                # test run.
                PyFunceble.CONFIGURATION["show_percentage"] = False

                # We deactivate the whois database as it is not needed.
                PyFunceble.CONFIGURATION["whois_database"] = False

                if PyFunceble.CONFIGURATION["idna_conversion"]:
                    domain_or_ip_to_test = domain2idna(
                        self.domain_or_ip_to_test.lower()  # pylint: disable=no-member
                    )
                else:
                    domain_or_ip_to_test = (
                        self.domain_or_ip_to_test.lower()  # pylint: disable=no-member
                    )  # pylint: disable=no-member

                # We test the domain after converting it to lower case.
                self.domain(domain_or_ip_to_test)
            elif self.url_to_test and not self.file_path:  # pylint: disable=no-member
                # An url to test is given and the file path is empty.

                # We initiate a variable which will tell the system the type
                # of the tested element.
                PyFunceble.INTERN["to_test_type"] = "url"

                # We set the start time.
                ExecutionTime("start")

                # We deactivate the showing of percentage as we are in a single
                # test run.
                PyFunceble.CONFIGURATION["show_percentage"] = False

                # We test the url to test after converting it if needed (IDNA).
                self.url(
                    self.checker.is_url_valid(
                        self.url_to_test,  # pylint: disable=no-member
                        return_formatted=True,
                    )
                )
            elif (
                self._entry_management_url_download(
                    self.url_file  # pylint: disable=no-member
                )
                or self.url_file  # pylint: disable=no-member
            ):
                # * A file full of URL is given.
                # or
                # * the given file full of URL is a URL.

                # * We deactivate the whois subsystem as it is not needed for url testing.
                # * We activate the generation of plain list element.
                # * We activate the generation of splited data instead of unified data.
                PyFunceble.CONFIGURATION["no_whois"] = PyFunceble.CONFIGURATION[
                    "plain_list_domain"
                ] = PyFunceble.CONFIGURATION["split"] = True

                # We deactivate the generation of hosts file as it is not relevant for
                # url testing.
                PyFunceble.CONFIGURATION["generate_hosts"] = False

                # We initiate a variable which will tell the system the type
                # of the tested element.
                PyFunceble.INTERN["to_test_type"] = "url"

                # And we test the given or the downloaded file.
                self.file_url()
            elif (
                self._entry_management_url_download(
                    self.link_to_test  # pylint: disable=no-member
                )
                or self._entry_management_url_download(
                    self.file_path  # pylint: disable=no-member
                )  # pylint: disable=no-member
                or self.file_path  # pylint: disable=no-member
            ):
                # * A file path is given.
                # or
                # * The given file path is an URL.
                # or
                # * A link to test is given.

                # We initiate a variable which will tell the system the type
                # of the tested element.
                PyFunceble.INTERN["to_test_type"] = "domain"

                # We test the given or the downloaded file.
                self.file()
            else:
                # No file, domain, single url or file or url is given.

                # We print a message on screen.
                print(
                    PyFunceble.Fore.CYAN + PyFunceble.Style.BRIGHT + "Nothing to test."
                )

            if (
                self.domain_or_ip_to_test  # pylint: disable=no-member
                or self.url_to_test  # pylint: disable=no-member
            ):
                # We are testing a domain.

                # We stop and log the execution time.
                ExecutionTime("stop", last=True)

                # We log the current percentage state.
                self.percentage.log()

                # We show the colored logo.
                self.colorify_logo()

            # We print our friendly message :)
            PyFunceble.stay_safe()
        else:
            # We are used as an imported module.

            # * We activate the simple mode as the table or any full
            # details on screen are irrelevant.
            # * We activate the quiet mode.
            # And we deactivate the generation of files.
            PyFunceble.CONFIGURATION["simple"] = PyFunceble.CONFIGURATION[
                "quiet"
            ] = PyFunceble.CONFIGURATION["no_files"] = True

            # * We deactivate the whois database as it is not needed.
            # * We deactivate the database as it is not needed.
            # * We deactivate the autocontinue subsystem as it is not needed.
            # * We deactivate the execution time subsystem as it is not needed.
            PyFunceble.CONFIGURATION["whois_database"] = PyFunceble.CONFIGURATION[
                "inactive_database"
            ] = PyFunceble.CONFIGURATION["auto_continue"] = PyFunceble.CONFIGURATION[
                "show_execution_time"
            ] = False

            if self.domain_or_ip_to_test:  # pylint: disable=no-member
                # A domain is given.

                # We initiate a variable which will tell the system the type
                # of the tested element.
                PyFunceble.INTERN["to_test_type"] = "domain"

                # We set the domain to test.
                PyFunceble.INTERN[
                    "to_test"
                ] = self.domain_or_ip_to_test.lower()  # pylint: disable=no-member
            elif self.url_to_test:  # pylint: disable=no-member
                # A url is given,

                # We initiate a variable which will tell the system the type
                # of the tested element.
                PyFunceble.INTERN["to_test_type"] = "url"

                # We set the url to test.
                PyFunceble.INTERN[
                    "to_test"
                ] = self.url_to_test  # pylint: disable=no-member

    def test_with_complete_information(self):
        """
        Run a test and return all available informations.

        .. note::
            The following are the indexes which we return.
            Please report to the advanced usage documentation for a
            description of each indexes.

            ::

                {
                    "whois_server": None,
                    "whois_record": None,
                    "url_syntax_validation": None,
                    "tested": None,
                    "status": None,
                    "status_source": None,
                    "nslookup": [],
                    "ip4_syntax_validation": None,
                    "http_status_code": None,
                    "expiration_date": None,
                    "domain_syntax_validation": None,
                    "_status": None,
                    "_status_source": None,
                }
        """

        # We initiate the location and the information we have to return.
        PyFunceble.INTERN["current_test_data"] = {
            "whois_server": None,
            "whois_record": None,
            "url_syntax_validation": None,
            "tested": None,
            "status": None,
            "status_source": None,
            "nslookup": [],
            "ip4_syntax_validation": None,
            "http_status_code": None,
            "expiration_date": None,
            "domain_syntax_validation": None,
            "_status": None,
            "_status_source": None,
        }

        if "to_test" in PyFunceble.INTERN and PyFunceble.INTERN["to_test"]:
            # We are testing something.

            # We update the tested index.
            PyFunceble.INTERN["current_test_data"]["tested"] = PyFunceble.INTERN[
                "to_test"
            ]

            if PyFunceble.INTERN["to_test_type"] == "domain":
                # We are testing a domain.

                # We get the status and the source of the domain.
                PyFunceble.INTERN["current_test_data"]["status"], PyFunceble.INTERN[
                    "current_test_data"
                ]["status_source"] = self.status.get()
            elif PyFunceble.INTERN["to_test_type"] == "url":
                # We are testing a url.

                # We get the status of the url.
                PyFunceble.INTERN["current_test_data"]["status"] = self.url_status.get()
            else:
                raise Exception("Unknow test type.")

        if "http_code" in PyFunceble.INTERN:
            # The http status code exist into the configuration.

            # We update the tested index.
            PyFunceble.INTERN["current_test_data"][
                "http_status_code"
            ] = PyFunceble.INTERN["http_code"]

        if "referer" in PyFunceble.INTERN:
            # The referer exist into the internal memory.

            # We update the related index.
            PyFunceble.INTERN["current_test_data"]["whois_server"] = PyFunceble.INTERN[
                "referer"
            ]

        return PyFunceble.INTERN["current_test_data"]

    def test(self, complete=False):
        """
        Avoid confusion between self.domain which is called into
        __main__ and test() which should be called out of PyFunceble's scope.

        :param complete:
            Activate the return of a dictionnary with signigican data about
            the test.
        :type complete: bool

        :return: ACTIVE INACTIVE or INVALID.
        :rtype: str|list

        :raises:
            :code:`Exception`
                When this method is called under
                :code:`__name__ == '__main__'`

        .. note::
            This method should never be called in a
            :code:`__name__ == '__main__'` context.
        """

        if not self.modulo_test:  # pylint: disable=no-member
            # We are not used as an imported module.

            # We inform the user that they should not use this method.
            raise Exception(
                "You should not use this method. Please prefer self.domain()"
            )

        # We are used as an imported module.

        if complete:
            # We have to return much more information into our result.

            # We finaly return our dataset.
            return self.test_with_complete_information()

        if PyFunceble.INTERN["to_test_type"] == "domain":
            # We are testing a domain.

            # We get the status of the domain we are trying to test.
            status, _ = self.status.get()

            # We return the catched status of the domains.
            return status

        if PyFunceble.INTERN["to_test_type"] == "url":
            # We are testing a url.

            # We return the status of the parsed url.
            return self.url_status.get()

        # We raise an exception because that means that something wrong
        # happened because of the developer not the user.
        raise Exception("Unknown to_test_type. Please report issue.")

    @classmethod
    def bypass(cls):
        """
        Exit the script if :code:`[PyFunceble skip]` is matched into the latest
        commit message.
        """

        # We set the regex to match in order to bypass the execution of
        # PyFunceble.
        regex_bypass = r"\[PyFunceble\sskip\]"

        if (
            PyFunceble.CONFIGURATION["travis"]
            and Regex(
                Command("git log -1").execute(), regex_bypass, return_data=False
            ).match()
        ):
            # * We are under Travis CI.
            # and
            # * The bypass marker is matched into the latest commit.

            # We save everything and stop PyFunceble.
            AutoSave(True, is_bypass=True)

    @classmethod
    def _print_header(cls):
        """
        Decide if we print or not the header.
        """

        if (
            not PyFunceble.CONFIGURATION["quiet"]
            and not PyFunceble.CONFIGURATION["header_printed"]
        ):
            # * The quiet mode is not activated.
            # and
            # * The header has not been already printed.

            # We print a new line.
            print("\n")

            if PyFunceble.CONFIGURATION["less"]:
                # We have to show less informations on screen.

                # We print the `Less` header.
                Prints(None, "Less").header()
            else:
                # We have to show every informations on screen.

                # We print the `Generic` header.
                Prints(None, "Generic").header()

            # The header was printed.

            # We initiate the variable which say that the header has been printed to True.
            PyFunceble.CONFIGURATION["header_printed"] = True

    def _file_decision(self, current, last, status=None):
        """
        Manage the database, autosave and autocontinue systems for the case that we are reading
        a file.

        :param current: The currently tested element.
        :type current: str

        :param last: The last element of the list.
        :type last: str

        :param status: The status of the currently tested element.
        :type status: str
        """

        if (
            status
            and not PyFunceble.CONFIGURATION["simple"]
            and PyFunceble.INTERN["file_to_test"]
        ):
            # * The status is given.
            # and
            # * The simple mode is deactivated.
            # and
            # * A file to test is set.

            # We run the mining logic.
            self.mining.process()

            # We delete the currently tested element from the mining
            # database.
            # Indeed, as it is tested, it is already in our
            # testing process which means that we don't need it into
            # the mining database.
            self.mining.remove()

            if (
                status.lower() in PyFunceble.STATUS["list"]["up"]
                or status.lower() in PyFunceble.STATUS["list"]["valid"]
            ):
                # The status is in the list of up status.

                if self.inactive_database.is_present():
                    # The currently tested element is in the database.

                    # We generate the suspicious file(s).
                    Generate(PyFunceble.STATUS["official"]["up"]).analytic_file(
                        "suspicious"
                    )

                    # We remove the currently tested element from the
                    # database.
                    self.inactive_database.remove()

            else:
                # The status is not in the list of up status.

                # We add the currently tested element to the
                # database.
                self.inactive_database.add()

            # We backup the current state of the file reading
            # for the case that we need to continue later.
            self.auto_continue.backup()

            if current != last:
                # The current element is not the last one.

                # We run the autosave logic.
                AutoSave()
            else:
                # The current element is the last one.

                # We stop and log the execution time.
                ExecutionTime("stop", last=True)

                # We show/log the percentage.
                self.percentage.log()

                # We reset the counters as we end the process.
                self.reset_counters()

                # We backup the current state of the file reading
                # for the case that we need to continue later.
                self.auto_continue.backup()

                # We show the colored logo.
                self.colorify_logo()

                # We save and stop the script if we are under
                # Travis CI.
                AutoSave(True)

        for index in ["http_code", "referer"]:
            # We loop through some configuration index we have to empty.

            if index in PyFunceble.INTERN:
                # The index is in the configuration.

                # We empty the configuration index.
                PyFunceble.INTERN[index] = ""

    def domain(self, domain=None, last_domain=None):
        """
        Manage the case that we want to test only a domain.

        :param domain: The domain or IP to test.
        :type domain: str

        :param last_domain:
            The last domain to test if we are testing a file.
        :type last_domain: str

        :param return_status: Tell us if we need to return the status.
        :type return_status: bool
        """

        # We print the header.
        self._print_header()

        if domain:
            # A domain is given.

            # We format and set the domain we are testing and treating.
            PyFunceble.INTERN["to_test"] = self._format_domain(domain)
        else:
            # A domain is not given.

            # We set the domain we are testing and treating to None.
            PyFunceble.INTERN["to_test"] = None

        if PyFunceble.INTERN["to_test"]:
            # The domain is given (Not None).

            if PyFunceble.CONFIGURATION["syntax"]:
                # The syntax mode is activated.

                # We get the status from Syntax.
                status = self.syntax_status.get()
            else:
                # We test and get the status of the domain.
                status, _ = self.status.get()

            # We run the file decision logic.
            self._file_decision(PyFunceble.INTERN["to_test"], last_domain, status)

            if PyFunceble.CONFIGURATION["simple"]:
                # The simple mode is activated.

                # We print the domain and the status.
                print(PyFunceble.INTERN["to_test"], status)

            # We return the tested domain and its status.
            return PyFunceble.INTERN["to_test"], status

        # We return None, there is nothing to test.
        return None

    def url(self, url_to_test=None, last_url=None):
        """
        Manage the case that we want to test only a given url.

        :param url_to_test: The url to test.
        :type url_to_test: str

        :param last_url:
            The last url of the file we are testing
            (if exist)
        :type last_url: str
        """

        # We print the header.
        self._print_header()

        if url_to_test:
            # An url to test is given.

            # We set the url we are going to test.
            PyFunceble.INTERN["to_test"] = url_to_test
        else:
            # An URL to test is not given.

            # We set the url we are going to test to None.
            PyFunceble.INTERN["to_test"] = None

        if PyFunceble.INTERN["to_test"]:
            # An URL to test is given.

            if PyFunceble.CONFIGURATION["syntax"]:
                # The syntax mode is activated.

                # We get the status from Syntax.
                status = self.syntax_status.get()
            else:
                # The syntax mode is not activated.

                # We get the status from URL.
                status = self.url_status.get()

            # We run the file decision logic.
            self._file_decision(PyFunceble.INTERN["to_test"], last_url, status)

            if PyFunceble.CONFIGURATION["simple"]:
                # The simple mode is activated.

                # We print the URL informations.
                print(PyFunceble.INTERN["to_test"], status)

            # We return the URL we tested and its status.
            return PyFunceble.INTERN["to_test"], status

        # We return None, there is nothing to test.
        return None

    @classmethod
    def reset_counters(cls):
        """
        Reset the counters when needed.
        """

        for string in ["up", "down", "invalid", "tested"]:
            # We loop through to the index of the autoContinue subsystem.

            # And we set their counter to 0.
            PyFunceble.INTERN["counter"]["number"].update({string: 0})

    @classmethod
    def colorify_logo(cls, home=False):
        """
        Print the colored logo based on global results.

        :param home: Tell us if we have to print the initial coloration.
        :type home: bool
        """

        if not PyFunceble.CONFIGURATION["quiet"]:
            # The quiet mode is not activated.

            to_print = []

            if home:
                # We have to print the initial logo.

                for line in PyFunceble.ASCII_PYFUNCEBLE.split("\n"):
                    # We loop through each lines of the ASCII representation
                    # of PyFunceble.

                    # And we append to the data to print the currently read
                    # line with the right coloration.
                    to_print.append(
                        PyFunceble.Fore.YELLOW + line + PyFunceble.Fore.RESET
                    )

            elif PyFunceble.INTERN["counter"]["percentage"]["up"] >= 50:
                # The percentage of up is greater or equal to 50%.

                for line in PyFunceble.ASCII_PYFUNCEBLE.split("\n"):
                    # We loop through each lines of the ASCII representation
                    # of PyFunceble.

                    # And we append to the data to print the currently read
                    # line with the right coloration.
                    to_print.append(
                        PyFunceble.Fore.GREEN + line + PyFunceble.Fore.RESET
                    )
            else:
                # The percentage of up is less than 50%.

                for line in PyFunceble.ASCII_PYFUNCEBLE.split("\n"):
                    # We loop through each lines of the ASCII representation
                    # of PyFunceble.

                    # And we append to the data to print the currently read
                    # line with the right coloration.
                    to_print.append(PyFunceble.Fore.RED + line + PyFunceble.Fore.RESET)

            print("\n".join(to_print))

    @classmethod
    def _format_domain(cls, extracted_domain):
        """
        Format the extracted domain before passing it to the system.

        :param extracted_domain: The extracted domain.
        :type extracted_domain: str

        :return: The formatted domain or IP to test.
        :rtype: str

        .. note:
            Understand by formating the fact that we get rid
            of all the noises around the domain we want to test.
        """

        if not extracted_domain.startswith("#"):
            # The line is not a commented line.

            if "#" in extracted_domain:
                # There is a comment at the end of the line.

                # We delete the comment from the line.
                extracted_domain = extracted_domain[
                    : extracted_domain.find("#")
                ].strip()

            if " " in extracted_domain or "\t" in extracted_domain:
                # A space or a tabs is in the line.

                # We remove all whitestring from the extracted line.
                splited_line = extracted_domain.split()

                # As there was a space or a tab in the string, we consider
                # that we are working with the hosts file format which means
                # that the domain we have to test is after the first string.
                # So we set the index to 1.
                index = 1

                while index < len(splited_line):
                    # We loop until the index is greater than the length of
                    #  the splited line.

                    if splited_line[index]:
                        # The element at the current index is not an empty string.

                        # We break the loop.
                        break

                    # The element at the current index is an empty string.

                    # We increase the index number.
                    index += 1

                # We return the last read element.
                return splited_line[index]

            # We return the extracted line.
            return extracted_domain

        # The extracted line is a comment line.

        # We return an empty string as we do not want to work with commented line.
        return ""

    @classmethod
    def _extract_domain_from_file(cls):
        """
        Extract all non commented lines from the file we are testing.

        :return: The elements to test.
        :rtype: list
        """

        # We initiate the variable which will save what we are going to return.
        result = []

        if PyFunceble.path.isfile(PyFunceble.INTERN["file_to_test"]):
            # The give file to test exist.

            try:
                with open(PyFunceble.INTERN["file_to_test"]) as file:
                    # We open and read the file.

                    for line in file:
                        # We loop through each lines.

                        if not line.startswith("#"):
                            # The currently read line is not a commented line.

                            # We append the current read line to the result.
                            result.append(line.rstrip("\n").strip())
            except UnicodeDecodeError:
                with open(PyFunceble.INTERN["file_to_test"], encoding="utf-8") as file:
                    # We open and read the file.

                    for line in file:
                        # We loop through each lines.

                        if not line.startswith("#"):
                            # The currently read line is not a commented line.

                            # We append the current read line to the result.
                            result.append(line.rstrip("\n").strip())

        else:
            # The given file to test does not exist.

            # We raise a FileNotFoundError exception.
            raise FileNotFoundError(PyFunceble.INTERN["file_to_test"])

        # We return the result.
        return result

    def _file_list_to_test_filtering(self):
        """
        Unify the way we work before testing file contents.
        """

        # We get the list to test from the file we have to test.
        list_to_test = self._extract_domain_from_file()

        # We save the original list to test globally.
        PyFunceble.INTERN["extracted_list_to_test"] = list_to_test

        # We get the list of mined.
        mined_list = self.mining.list_of_mined()

        if mined_list:
            list_to_test.extend(mined_list)

        # We generate the directory structure.
        PyFunceble.DirectoryStructure()

        # We update the auto continue variable.
        self.auto_continue = AutoContinue()

        # We restore the data from the last session if it does exist.
        self.auto_continue.restore()

        if PyFunceble.CONFIGURATION["adblock"]:
            # The adblock decoder is activated.

            # We get the list of domain to test (decoded).
            list_to_test = AdBlock(list_to_test).decode()
        else:
            # The adblock decoder is not activated.

            # We get the formatted list of domain to test.
            list_to_test = [self._format_domain(x) for x in list_to_test]

        # We clean the output directory if it is needed.
        PyFunceble.Clean(list_to_test)

        # We set the start time.
        ExecutionTime("start")

        # We get the list we have to test in the current session (from the database).
        self.inactive_database.to_test()

        if (
            PyFunceble.CONFIGURATION["inactive_database"]
            and PyFunceble.INTERN["file_to_test"] in PyFunceble.INTERN["inactive_db"]
            and "to_test"
            in PyFunceble.INTERN["inactive_db"][PyFunceble.INTERN["file_to_test"]]
            and PyFunceble.INTERN["inactive_db"][PyFunceble.INTERN["file_to_test"]][
                "to_test"
            ]
        ):
            # * The current file to test in into the database.
            # and
            # * The `to_test` index is present into the database
            #   related to the file we are testing.
            # and
            # * The `to_test` index content is not empty.

            # We extend our list to test with the content of the `to_test` index
            # of the current file database.
            list_to_test.extend(
                PyFunceble.INTERN["inactive_db"][PyFunceble.INTERN["file_to_test"]][
                    "to_test"
                ]
            )

        # We set a regex of element to delete.
        # Understand with this variable that we don't want to test those.
        regex_delete = r"localhost$|localdomain$|local$|broadcasthost$|0\.0\.0\.0$|allhosts$|allnodes$|allrouters$|localnet$|loopback$|mcastprefix$|ip6-mcastprefix$|ip6-localhost$|ip6-loopback$|ip6-allnodes$|ip6-allrouters$|ip6-localnet$"  # pylint: disable=line-too-long

        # We load the flatten version of the database.
        PyFunceble.INTERN.update(
            {"flatten_inactive_db": self.inactive_database.content()}
        )

        # We remove the element which are in the database from the
        # current list to test.
        list_to_test = List(
            Regex(list_to_test, regex_delete).not_matching_list()
        ).format()
        _ = list_to_test[-1]

        if PyFunceble.CONFIGURATION["filter"]:
            # The filter is not empty.

            # We get update our list to test. Indeed we only keep the elements which
            # matches the given filter.
            list_to_test = List(
                Regex(
                    list_to_test, PyFunceble.CONFIGURATION["filter"], escape=False
                ).matching_list()
            ).format()

        list_to_test = List(list(list_to_test)).custom_format(Sort.standard)

        # We return the final list to test.
        return list_to_test

    def file(self):
        """
        Manage the case that need to test each domain of a given file path.

        .. note::
            1 domain per line.
        """

        # We get, format, filter, clean the list to test.
        list_to_test = self._file_list_to_test_filtering()

        if PyFunceble.CONFIGURATION["idna_conversion"]:
            # We have to convert domains to idna.

            # We convert if we need to convert.
            list_to_test = domain2idna(list_to_test)

            if PyFunceble.CONFIGURATION["hierarchical_sorting"]:
                # The hierarchical sorting is desired by the user.

                # We format the list.
                list_to_test = List(list_to_test).custom_format(Sort.hierarchical)
            else:
                # The hierarchical sorting is not desired by the user.

                # We format the list.
                list_to_test = List(list_to_test).custom_format(Sort.standard)

        # We initiate a local variable which will save the current state of the list.
        not_filtered = list_to_test

        try:
            # We remove the element which are in the database from the
            # current list to test.
            list_to_test = List(
                list(
                    set(
                        list_to_test[PyFunceble.INTERN["counter"]["number"]["tested"] :]
                    )
                    - set(PyFunceble.INTERN["flatten_inactive_db"])
                )
            ).format()
            _ = list_to_test[-1]
        except IndexError:
            # Our list to test is the one with the element from the database.
            list_to_test = not_filtered[
                PyFunceble.INTERN["counter"]["number"]["tested"] :
            ]

            # We delete the undesired variable.
            del not_filtered

        if PyFunceble.CONFIGURATION["hierarchical_sorting"]:
            # The hierarchical sorting is desired by the user.

            # We format the list.
            list_to_test = List(list(list_to_test)).custom_format(Sort.hierarchical)

        try:
            # We test each element of the list to test.
            return [self.domain(x, list_to_test[-1]) for x in list_to_test if x]
        except IndexError:
            # We print a message on screen.
            print(PyFunceble.Fore.CYAN + PyFunceble.Style.BRIGHT + "Nothing to test.")

    def file_url(self):
        """
        Manage the case that we have to test a file

        .. note::
            1 URL per line.
        """

        # We get, format, clean the list of URL to test.
        list_to_test = self._file_list_to_test_filtering()

        # We initiate a local variable which will save the current state of the list.
        not_filtered = list_to_test

        try:
            # We remove the element which are in the database from the
            # current list to test.
            list_to_test = List(
                list(
                    set(
                        list_to_test[PyFunceble.INTERN["counter"]["number"]["tested"] :]
                    )
                    - set(PyFunceble.INTERN["flatten_inactive_db"])
                )
            ).format()
            _ = list_to_test[-1]
        except IndexError:
            # Our list to test is the one with the element from the database.
            list_to_test = not_filtered[
                PyFunceble.INTERN["counter"]["number"]["tested"] :
            ]

            # We delete the undesired variable.
            del not_filtered

        if PyFunceble.CONFIGURATION["hierarchical_sorting"]:
            # The hierarchical sorting is desired by the user.

            # We format the list.
            list_to_test = List(list(list_to_test)).custom_format(Sort.hierarchical)

        try:
            # We test each URL from the list to test.
            return [self.url(x, list_to_test[-1]) for x in list_to_test if x]
        except IndexError:
            # We print a message on screen.
            print(PyFunceble.Fore.CYAN + PyFunceble.Style.BRIGHT + "Nothing to test.")

    @classmethod
    def switch(
        cls, variable, custom=False
    ):  # pylint: disable=inconsistent-return-statements
        """
        Switch PyFunceble.CONFIGURATION variables to their opposite.

        :param variable:
            The variable name to switch.
            The variable should be an index our configuration system.
            If we want to switch a bool variable, we should parse
            it here.
        :type variable: str|bool

        :param custom:
            Let us know if have to switch the parsed variable instead
            of our configuration index.
        :type custom: bool

        :return:
            The opposite of the configuration index or the given variable.
        :rtype: bool

        :raises:
            :code:`Exception`
                When the configuration is not valid. In other words,
                if the PyFunceble.CONFIGURATION[variable_name] is not a bool.
        """

        if not custom:
            # We are not working with custom variable which is not into
            # the configuration.

            # We get the current state.
            current_state = dict.get(PyFunceble.CONFIGURATION, variable)
        else:
            # We are working with a custom variable which is not into the
            # configuration
            current_state = variable

        if isinstance(current_state, bool):
            # The current state is a boolean.

            if current_state:
                # The current state is equal to True.

                # We return False.
                return False

            # The current state is equal to False.

            # We return True.
            return True

        # The current state is not a boolean.

        # We set the message to raise.
        to_print = "Impossible to switch %s. Please post an issue to %s"

        # We raise an exception inviting the user to report an issue.
        raise Exception(
            to_print % (repr(variable), PyFunceble.LINKS["repo"] + "/issues.")
        )