Source code for PyFunceble

#!/usr/bin/env python3

# pylint:disable=line-too-long, too-many-lines
"""
The tool to check the availability or syntax of domains, IPv4 or URL.

::


    ██████╗ ██╗   ██╗███████╗██╗   ██╗███╗   ██╗ ██████╗███████╗██████╗ ██╗     ███████╗
    ██╔══██╗╚██╗ ██╔╝██╔════╝██║   ██║████╗  ██║██╔════╝██╔════╝██╔══██╗██║     ██╔════╝
    ██████╔╝ ╚████╔╝ █████╗  ██║   ██║██╔██╗ ██║██║     █████╗  ██████╔╝██║     █████╗
    ██╔═══╝   ╚██╔╝  ██╔══╝  ██║   ██║██║╚██╗██║██║     ██╔══╝  ██╔══██╗██║     ██╔══╝
    ██║        ██║   ██║     ╚██████╔╝██║ ╚████║╚██████╗███████╗██████╔╝███████╗███████╗
    ╚═╝        ╚═╝   ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚══════╝╚══════╝

This submodule is the main entry of PyFunceble.

Author:
    Nissar Chababy, @funilrys, contactTATAfunilrysTODTODcom

Special thanks:
    https://pyfunceble.readthedocs.io/en/master/special-thanks.html

Contributors:
    http://pyfunceble.readthedocs.io/en/master/special-thanks.html

Project link:
    https://github.com/funilrys/PyFunceble

Project documentation:
    https://pyfunceble.readthedocs.io/en/master/

Project homepage:
    https://funilrys.github.io/PyFunceble/

License:
::


    MIT License

    Copyright (c) 2017-2019 Nissar Chababy

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
"""
# pylint: enable=line-too-long
# pylint: disable=invalid-name,cyclic-import, bad-continuation
import argparse
import socket
from collections import OrderedDict
from inspect import getsourcefile
from os import environ, getcwd, mkdir, path, rename
from os import sep as directory_separator
from os import walk
from platform import system
from random import choice
from shutil import copy, rmtree
from time import mktime, strftime, strptime, time

import requests
from colorama import Back, Fore, Style
from colorama import init as initiate

from PyFunceble.check import Check
from PyFunceble.clean import Clean
from PyFunceble.config import Load, Merge, Version
from PyFunceble.core import Core
from PyFunceble.directory_structure import DirectoryStructure
from PyFunceble.iana import IANA
from PyFunceble.production import Production
from PyFunceble.publicsuffix import PublicSuffix

# We set our project name.
NAME = "PyFunceble"
# We set out project version.
VERSION = "1.15.1. (Blue Bontebok: Wasp)"

# We set the list of windows "platforms"
WINDOWS_PLATFORMS = ["windows", "cygwin", "cygwin_nt-10.0"]

if "PYFUNCEBLE_OUTPUT_DIR" in environ:  # pragma: no cover
    # We handle the case that the `PYFUNCEBLE_OUTPUT_DIR` environnement variable is set.
    CURRENT_DIRECTORY = environ["PYFUNCEBLE_OUTPUT_DIR"]
elif Version(True).is_cloned():  # pragma: no cover
    # We handle the case that we are in a cloned.
    CURRENT_DIRECTORY = getcwd() + directory_separator
elif "TRAVIS_BUILD_DIR" in environ:  # pragma: no cover
    # We handle the case that we are under Travis CI.
    CURRENT_DIRECTORY = getcwd() + directory_separator
else:  # pragma: no cover
    # We handle all other case and distributions specific cases.

    if system().lower() == "linux" or system().lower() == "darwin":
        # We are under a Linux distribution.

        # We set the default configuration location path.
        config_dir_path = (
            path.expanduser("~" + directory_separator + ".config") + directory_separator
        )

        if path.isdir(config_dir_path):
            # Everything went right:
            #   * `~/.config` exists.
            # We set our configuration location path as the directory we are working with.
            CURRENT_DIRECTORY = config_dir_path
        elif path.isdir(path.expanduser("~")):
            # Something went wrong:
            #   * `~/.config` does not exists.
            #   * `~` exists.
            # We set `~/` as the directory we are working with.
            #
            # Note: The `.` at the end is because we want to hide the directory we are
            # going to create.
            CURRENT_DIRECTORY = (
                path.expanduser("~") + directory_separator + "."
            )  # pylint: disable=line-too-long
        else:
            # Everything went wrong:
            #   * `~/.config` does not exists.
            #   * `~` soes not exists.
            # We set the current directory as the directory we are working with.
            CURRENT_DIRECTORY = getcwd() + directory_separator
    elif system().lower() in WINDOWS_PLATFORMS:
        # We are under Windows or CygWin.

        if "APPDATA" in environ:
            # Everything went right:
            #   * `APPDATA` is into the environnement variables.
            # We set it as the directory we are working with.
            CURRENT_DIRECTORY = environ["APPDATA"]
        else:
            # Everything went wrong:
            #   * `APPDATA` is not into the environnement variables.
            # We set the current directory as the directory we are working with.
            CURRENT_DIRECTORY = getcwd() + directory_separator

    if not CURRENT_DIRECTORY.endswith(directory_separator):
        # If the directory we are working with does not ends with the directory
        # separator, we append it to the end.
        CURRENT_DIRECTORY += directory_separator

    # We append the name of the project to the directory we are working with.
    CURRENT_DIRECTORY += NAME + directory_separator

    if not path.isdir(CURRENT_DIRECTORY):
        # If the directory does not exist we create it.
        mkdir(CURRENT_DIRECTORY)

if not CURRENT_DIRECTORY.endswith(directory_separator):  # pragma: no cover
    # Again for safety, if the directory we are working with does not ends with
    # the directory separator, we append it to the end.
    CURRENT_DIRECTORY += directory_separator

# We set the location of the `output` directory which should always be in the current
# directory.
OUTPUT_DIRECTORY = getcwd() + directory_separator

# We set the filename of the default configuration file.
DEFAULT_CONFIGURATION_FILENAME = ".PyFunceble_production.yaml"
# We set the filename of the configuration file we are actually using.
CONFIGURATION_FILENAME = ".PyFunceble.yaml"

# We set the current time (return the current time) in a specific format.
CURRENT_TIME = strftime("%a %d %b %H:%m:%S %Z %Y")

# We initiate the location where we are going to save our whole configuration content.
CONFIGURATION = {}
# We initiate the location where we are going to get all statuses.
STATUS = {}
# We initiate the location where we are going to get all outputs.
OUTPUTS = {}
# We initiate the location where we are going to get the map of the classification
# of each status codes for the analytic part.
HTTP_CODE = {}
# We initiate the location where we are going to get all links.
LINKS = {}
# We initiate a location which will have all internal data.
INTERN = {}

# We initiate the CLI logo of PyFunceble.
ASCII_PYFUNCEBLE = """
██████╗ ██╗   ██╗███████╗██╗   ██╗███╗   ██╗ ██████╗███████╗██████╗ ██╗     ███████╗
██╔══██╗╚██╗ ██╔╝██╔════╝██║   ██║████╗  ██║██╔════╝██╔════╝██╔══██╗██║     ██╔════╝
██████╔╝ ╚████╔╝ █████╗  ██║   ██║██╔██╗ ██║██║     █████╗  ██████╔╝██║     █████╗
██╔═══╝   ╚██╔╝  ██╔══╝  ██║   ██║██║╚██╗██║██║     ██╔══╝  ██╔══██╗██║     ██╔══╝
██║        ██║   ██║     ╚██████╔╝██║ ╚████║╚██████╗███████╗██████╔╝███████╗███████╗
╚═╝        ╚═╝   ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚══════╝╚══════╝
"""


[docs]def test(domain, complete=False, config=None): # pragma: no cover """ Test the availability of the given domain or IP. :param domain: The domain or IP to test. :type domain: str :param complete: Activate the return of a dict with some significant data from the test. :type complete: bool :param config: A dict with the configuration index (from .PyFunceble.yaml) to update. :type config: dict :return: The status or the informations of the domain. :rtype: str|dict .. warning:: If an empty or a non-string :code:`domain` is given, we return :code:`None`. .. warning:: If :code:`config` is given, the given :code:`dict` overwrite the last value of the given indexes. .. note:: If :code:`complete` is set to :code:`True`, we return the following indexes. :: { "_status_source": None, "_status": None, "domain_syntax_validation": None, "expiration_date": None, "http_status_code": None, "ip4_syntax_validation": None, "nslookup": [], "status_source": None, "status": None, "tested": None, "url_syntax_validation": None, "whois_record": None, "whois_server": None, } """ if domain and isinstance(domain, str): # * The given domain is not empty nor None. # and # * The given domain is a string. # We silently load the configuration. load_config(True) if config and isinstance(config, dict): # The given configuration is not None or empty. # and # It is a dict. # We update the configuration index. CONFIGURATION.update(config) # And we return the status of the given domain. return Core(domain_or_ip_to_test=domain, modulo_test=True).test(complete) # We return None, there is nothing to test. return None
[docs]def syntax_check(domain): # pragma: no cover """ Check the syntax of the given domain. :param domain: The domain to check the syntax for. :type domain: str :return: The syntax validity. :rtype: bool .. warning:: If an empty or a non-string :code:`domain` is given, we return :code:`None`. """ if domain and isinstance(domain, str): # * The given domain is not empty nor None. # and # * The given domain is a string. # We silently load the configuration. load_config(True) return Check(domain).is_domain_valid() # We return None, there is nothing to check. return None
[docs]def is_subdomain(domain): # pragma: no cover """ Check if the given domain is a subdomain. :param domain: The domain we are checking. :type domain: str :return: The subdomain state. :rtype: bool .. warning:: If an empty or a non-string :code:`domain` is given, we return :code:`None`. """ if domain and isinstance(domain, str): # * The given domain is not empty nor None. # and # * The given domain is a string. # We silently load the configuration. load_config(True) return Check(domain).is_subdomain() # We return None, there is nothing to check. return None
[docs]def ipv4_syntax_check(ip): # pragma: no cover """ Check the syntax of the given IPv4. :param ip: The IPv4 to check the syntax for. :type ip: str :return: The syntax validity. :rtype: bool .. warning:: If an empty or a non-string :code:`ip` is given, we return :code:`None`. """ if ip and isinstance(ip, str): # The given IP is not empty nor None. # and # * The given IP is a string. # We silently load the configuration. load_config(True) return Check(ip).is_ip_valid() # We return None, there is nothing to check. return None
[docs]def is_ipv4_range(ip): # pragma: no cover """ Check if the given IP is an IP range. :param ip: The IP we are checking. :type ip: str :return: The IPv4 range state. :rtype: bool .. warning:: If an empty or a non-string :code:`ip` is given, we return :code:`None`. """ if ip and isinstance(ip, str): # The given IP is not empty nor None. # and # * The given IP is a string. # We silently load the configuration. load_config(True) return Check(ip).is_ip_range() # We return None, there is nothing to check. return None
[docs]def url_syntax_check(url): # pragma: no cover """ Check the syntax of the given URL. :param url: The URL to check the syntax for. :type url: str :return: The syntax validity. :rtype: bool .. warning:: If an empty or a non-string :code:`url` is given, we return :code:`None`. """ if url and isinstance(url, str): # The given URL is not empty nor None. # and # * The given URL is a string. # We silently load the configuration. load_config(True) return Check(url).is_url_valid() # We return None, there is nothing to check. return None
[docs]def url_test(url, complete=False, config=None): # pragma: no covere """ Test the availability of the given URL. :param url: The URL to test. :type url: str :param complete: Activate the return of a dict with some significant data from the test. :type complete: bool :param config: A dict with the configuration index (from .PyFunceble.yaml) to update. :type config: dict :return: The status or the informations of the URL. :rtype: str|dict .. warning:: If an empty or a non-string :code:`url` is given, we return :code:`None`. .. warning:: If :code:`config` is given, the given :code:`dict` overwrite the last value of the given indexes. """ if url and isinstance(url, str): # The given URL is not empty nor None. # and # * The given URL is a string. # We silently load the configuration. load_config(True) if config and isinstance(config, dict): # The given configuration is not None or empty. # and # It is a dict. # We update the configuration index. CONFIGURATION.update(config) # And we return the status of the given URL. return Core(url_to_test=url, modulo_test=True).test(complete) # We return None, there is nothing to test. return None
[docs]def load_config(under_test=False, custom=None): # pragma: no cover """ Load the configuration. :param under_test: Tell us if we only have to load the configuration file (True) or load the configuration file and initate the output directory if it does not exist (False). :type under_test: bool :param custom: A dict with the configuration index (from .PyFunceble.yaml) to update. :type custom: dict .. warning:: If :code:`custom` is given, the given :code:`dict` overwrite the last value of the given configuration indexes. """ if "config_loaded" not in INTERN: # The configuration was not already loaded. # We load and download the different configuration file if they are non # existant. Load(CURRENT_DIRECTORY) if not under_test: # If we are not under test which means that we want to save informations, # we initiate the directory structure. DirectoryStructure() # We save that the configuration was loaded. INTERN.update({"config_loaded": True}) if custom and isinstance(custom, dict): # The given configuration is not None or empty. # and # It is a dict. # We update the configuration index. CONFIGURATION.update(custom)
def stay_safe(): # pragma: no cover """ Print a friendly message. """ random = int(choice(str(int(time())))) if not CONFIGURATION["quiet"] and random % 3 == 0: print("\n" + Fore.GREEN + Style.BRIGHT + "Thanks for using PyFunceble!") print( Fore.YELLOW + Style.BRIGHT + "Share your experience on " + Fore.CYAN + "Twitter" + Fore.YELLOW + " with " + Fore.CYAN + "#PyFunceble" + Fore.YELLOW + "!" ) print( Fore.GREEN + Style.BRIGHT + "Have a feedback, an issue or an improvement idea ?" ) print( Fore.YELLOW + Style.BRIGHT + "Let us know on " + Fore.CYAN + "GitHub" + Fore.YELLOW + "!" ) def _command_line(): # pragma: no cover pylint: disable=too-many-branches,too-many-statements """ Provide the command line interface. """ if __name__ == "PyFunceble": # We initiate the end of the coloration at the end of each line. initiate(autoreset=True) # We load the configuration and the directory structure. load_config(True) try: # The following handle the command line argument. try: PARSER = argparse.ArgumentParser( epilog="Crafted with %s by %s" % ( Fore.RED + "♥" + Fore.RESET, Style.BRIGHT + Fore.CYAN + "Nissar Chababy (Funilrys) " + Style.RESET_ALL + "with the help of " + Style.BRIGHT + Fore.GREEN + "https://pyfunceble.rtfd.io/en/master/contributors.html " + Style.RESET_ALL + "&& " + Style.BRIGHT + Fore.GREEN + "https://pyfunceble.rtfd.io/en/master/special-thanks.html", ), add_help=False, ) CURRENT_VALUE_FORMAT = ( Fore.YELLOW + Style.BRIGHT + "Configured value: " + Fore.BLUE ) PARSER.add_argument( "-ad", "--adblock", action="store_true", help="Switch the decoding of the adblock format. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["adblock"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-a", "--all", action="store_false", help="Output all available information on the screen. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["less"]) + Style.RESET_ALL ), ) PARSER.add_argument( "" "-c", "--auto-continue", "--continue", action="store_true", help="Switch the value of the auto continue mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["auto_continue"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--autosave-minutes", type=int, help="Update the minimum of minutes before we start " "committing to upstream under Travis CI. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["travis_autosave_minutes"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--clean", action="store_true", help="Clean all files under output." ) PARSER.add_argument( "--clean-all", action="store_true", help="Clean all files under output and all file generated by PyFunceble.", ) PARSER.add_argument( "--cmd", type=str, help="Pass a command to run before each commit " "(except the final one) under the Travis mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["command_before_end"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--cmd-before-end", type=str, help="Pass a command to run before the results " "(final) commit under the Travis mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["command_before_end"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--commit-autosave-message", type=str, help="Replace the default autosave commit message. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["travis_autosave_commit"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--commit-results-message", type=str, help="Replace the default results (final) commit message. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["travis_autosave_final_commit"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-d", "--domain", type=str, help="Set and test the given domain." ) PARSER.add_argument( "-db", "--database", action="store_true", help="Switch the value of the usage of a database to store " "inactive domains of the currently tested list. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["inactive_database"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-dbr", "--days-between-db-retest", type=int, help="Set the numbers of days between each retest of domains present " "into inactive-db.json. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["days_between_db_retest"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--debug", action="store_true", help="Switch the value of the debug mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["debug"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--directory-structure", action="store_true", help="Generate the directory and files that are needed and which does " "not exist in the current directory.", ) PARSER.add_argument( "-ex", "--execution", action="store_true", help="Switch the default value of the execution time showing. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["show_execution_time"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-f", "--file", type=str, help="Read the given file and test all domains inside it. " "If a URL is given we download and test the content of the given URL.", # pylint: disable=line-too-long ) PARSER.add_argument( "--filter", type=str, help="Domain to filter (regex)." ) PARSER.add_argument( "--help", action="help", default=argparse.SUPPRESS, help="Show this help message and exit.", ) PARSER.add_argument( "--hierarchical", action="store_true", help="Switch the value of the hierarchical sorting of the tested file. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["hierarchical_sorting"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-h", "--host", action="store_true", help="Switch the value of the generation of hosts file. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["generate_hosts"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--http", action="store_true", help="Switch the value of the usage of HTTP code. %s" % ( CURRENT_VALUE_FORMAT + repr(HTTP_CODE["active"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--iana", action="store_true", help="Update/Generate `iana-domains-db.json`.", ) PARSER.add_argument( "--idna", action="store_true", help="Switch the value of the IDNA conversion. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["idna_conversion"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-ip", type=str, help="Change the IP to print in the hosts files with the given one. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["custom_ip"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--json", action="store_true", help="Switch the value of the generation " "of the JSON formatted list of domains. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["generate_json"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--less", action="store_true", help="Output less informations on screen. %s" % ( CURRENT_VALUE_FORMAT + repr(Core.switch("less")) + Style.RESET_ALL ), ) PARSER.add_argument( "--local", action="store_true", help="Switch the value of the local network testing. %s" % ( CURRENT_VALUE_FORMAT + repr(Core.switch("local")) + Style.RESET_ALL ), ) PARSER.add_argument( "--link", type=str, help="Download and test the given file." ) PARSER.add_argument( "-m", "--mining", action="store_true", help="Switch the value of the mining subsystem usage. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["mining"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-n", "--no-files", action="store_true", help="Switch the value of the production of output files. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["no_files"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-nl", "--no-logs", action="store_true", help="Switch the value of the production of logs files " "in the case we encounter some errors. %s" % ( CURRENT_VALUE_FORMAT + repr(not CONFIGURATION["logs"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-ns", "--no-special", action="store_true", help="Switch the value of the usage of the SPECIAL rules. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["no_special"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-nu", "--no-unified", action="store_true", help="Switch the value of the production unified logs " "under the output directory. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["unified"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-nw", "--no-whois", action="store_true", help="Switch the value the usage of whois to test domain's status. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["no_whois"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-p", "--percentage", action="store_true", help="Switch the value of the percentage output mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["show_percentage"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--plain", action="store_true", help="Switch the value of the generation " "of the plain list of domains. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["plain_list_domain"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--production", action="store_true", help="Prepare the repository for production.", ) PARSER.add_argument( "-psl", "--public-suffix", action="store_true", help="Update/Generate `public-suffix.json`.", ) PARSER.add_argument( "-q", "--quiet", action="store_true", help="Run the script in quiet mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["quiet"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--share-logs", action="store_true", help="Switch the value of the sharing of logs. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["share_logs"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-s", "--simple", action="store_true", help="Switch the value of the simple output mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["simple"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--split", action="store_true", help="Switch the value of the split of the generated output files. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["inactive_database"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--syntax", action="store_true", help="Switch the value of the syntax test mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["syntax"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-t", "--timeout", type=int, default=3, help="Switch the value of the timeout. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["seconds_before_http_timeout"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--travis", action="store_true", help="Switch the value of the Travis mode. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["travis"]) + Style.RESET_ALL ), ) PARSER.add_argument( "--travis-branch", type=str, default="master", help="Switch the branch name where we are going to push. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["travis_branch"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-u", "--url", type=str, help="Analyze the given URL." ) PARSER.add_argument( "-uf", "--url-file", type=str, help="Read and test the list of URL of the given file. " "If a URL is given we download and test the content of the given URL.", # pylint: disable=line-too-long ) PARSER.add_argument( "-ua", "--user-agent", type=str, help="Set the user-agent to use and set every time we " "interact with everything which is not our logs sharing system.", # pylint: disable=line-too-long ) PARSER.add_argument( "-v", "--version", help="Show the version of PyFunceble and exit.", action="version", version="%(prog)s " + VERSION, ) PARSER.add_argument( "-vsc", "--verify-ssl-certificate", action="store_true", help="Switch the value of the verification of the " "SSL/TLS certificate when testing for URL. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["verify_ssl_certificate"]) + Style.RESET_ALL ), ) PARSER.add_argument( "-wdb", "--whois-database", action="store_true", help="Switch the value of the usage of a database to store " "whois data in order to avoid whois servers rate limit. %s" % ( CURRENT_VALUE_FORMAT + repr(CONFIGURATION["whois_database"]) + Style.RESET_ALL ), ) ARGS = PARSER.parse_args() if ARGS.less: CONFIGURATION.update({"less": ARGS.less}) elif not ARGS.all: CONFIGURATION.update({"less": ARGS.all}) if ARGS.adblock: CONFIGURATION.update({"adblock": Core.switch("adblock")}) if ARGS.auto_continue: CONFIGURATION.update( {"auto_continue": Core.switch("auto_continue")} ) if ARGS.autosave_minutes: CONFIGURATION.update( {"travis_autosave_minutes": ARGS.autosave_minutes} ) if ARGS.clean: Clean(None) if ARGS.clean_all: Clean(None, ARGS.clean_all) if ARGS.cmd: CONFIGURATION.update({"command": ARGS.cmd}) if ARGS.cmd_before_end: CONFIGURATION.update({"command_before_end": ARGS.cmd_before_end}) if ARGS.commit_autosave_message: CONFIGURATION.update( {"travis_autosave_commit": ARGS.commit_autosave_message} ) if ARGS.commit_results_message: CONFIGURATION.update( {"travis_autosave_final_commit": ARGS.commit_results_message} ) if ARGS.database: CONFIGURATION.update( {"inactive_database": Core.switch("inactive_database")} ) if ARGS.days_between_db_retest: CONFIGURATION.update( {"days_between_db_retest": ARGS.days_between_db_retest} ) if ARGS.debug: CONFIGURATION.update({"debug": Core.switch("debug")}) if ARGS.directory_structure: DirectoryStructure() if ARGS.execution: CONFIGURATION.update( {"show_execution_time": Core.switch("show_execution_time")} ) if ARGS.filter: CONFIGURATION.update({"filter": ARGS.filter}) if ARGS.hierarchical: CONFIGURATION.update( {"hierarchical_sorting": Core.switch("hierarchical_sorting")} ) if ARGS.host: CONFIGURATION.update( {"generate_hosts": Core.switch("generate_hosts")} ) if ARGS.http: HTTP_CODE.update({"active": Core.switch(HTTP_CODE["active"], True)}) if ARGS.iana: IANA().update() if ARGS.idna: CONFIGURATION.update( {"idna_conversion": Core.switch("idna_conversion")} ) if ARGS.ip: CONFIGURATION.update({"custom_ip": ARGS.ip}) if ARGS.json: CONFIGURATION.update( {"generate_json": Core.switch("generate_json")} ) if ARGS.local: CONFIGURATION.update({"local": Core.switch("local")}) if ARGS.mining: CONFIGURATION.update({"mining": Core.switch("mining")}) if ARGS.no_files: CONFIGURATION.update({"no_files": Core.switch("no_files")}) if ARGS.no_logs: CONFIGURATION.update({"logs": Core.switch("logs")}) if ARGS.no_special: CONFIGURATION.update({"no_special": Core.switch("no_special")}) if ARGS.no_unified: CONFIGURATION.update({"unified": Core.switch("unified")}) if ARGS.no_whois: CONFIGURATION.update({"no_whois": Core.switch("no_whois")}) if ARGS.percentage: CONFIGURATION.update( {"show_percentage": Core.switch("show_percentage")} ) if ARGS.plain: CONFIGURATION.update( {"plain_list_domain": Core.switch("plain_list_domain")} ) if ARGS.production: Production() if ARGS.public_suffix: PublicSuffix().update() if ARGS.quiet: CONFIGURATION.update({"quiet": Core.switch("quiet")}) if ARGS.share_logs: CONFIGURATION.update({"share_logs": Core.switch("share_logs")}) if ARGS.simple: CONFIGURATION.update( {"simple": Core.switch("simple"), "quiet": Core.switch("quiet")} ) if ARGS.split: CONFIGURATION.update({"split": Core.switch("split")}) if ARGS.syntax: CONFIGURATION.update({"syntax": Core.switch("syntax")}) if ARGS.timeout and ARGS.timeout % 3 == 0: CONFIGURATION.update({"seconds_before_http_timeout": ARGS.timeout}) if ARGS.travis: CONFIGURATION.update({"travis": Core.switch("travis")}) if ARGS.travis_branch: CONFIGURATION.update({"travis_branch": ARGS.travis_branch}) if ARGS.user_agent: CONFIGURATION.update({"user_agent": ARGS.user_agent}) if ARGS.verify_ssl_certificate: CONFIGURATION.update( {"verify_ssl_certificate": ARGS.verify_ssl_certificate} ) if ARGS.whois_database: CONFIGURATION.update( {"whois_database": Core.switch("whois_database")} ) if not CONFIGURATION["quiet"]: Core.colorify_logo(home=True) # We compare the versions (upstream and local) and in between. Version().compare() # We call our Core which will handle all case depending of the configuration or # the used command line arguments. Core( domain_or_ip_to_test=ARGS.domain, file_path=ARGS.file, url_to_test=ARGS.url, url_file=ARGS.url_file, link_to_test=ARGS.link, ) except KeyError as e: if not Version(True).is_cloned(): # We are not into the cloned version. # We merge the local with the upstream configuration. Merge(CURRENT_DIRECTORY) else: # We are in the cloned version. # We raise the exception. # # Note: The purpose of this is to avoid having # to search for a mistake while developing. raise e except KeyboardInterrupt: stay_safe()