#!/usr/bin/env python
#
# This file is a part of DNSViz, a tool suite for DNS/DNSSEC monitoring,
# analysis, and visualization.
# Created by Casey Deccio (casey@deccio.net)
#
# Copyright 2015-2016 VeriSign, Inc.
#
# Copyright 2016-2021 Casey Deccio
#
# DNSViz is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# DNSViz is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with DNSViz.  If not, see <http://www.gnu.org/licenses/>.
#

from __future__ import unicode_literals

import getopt
import importlib
import logging
import os
import sys

CMD_DIR1 = 'dnsviz'
CMD_DIR2 = 'commands'

logging.basicConfig(level=logging.WARNING, format='%(message)s')
logger = logging.getLogger()

def check_deps():
    # check dnspython dependency
    try:
        import dns.name
    except ImportError:
        sys.stderr.write('Error: dnspython does not appear to be installed\n')
        sys.exit(1)

def usage(err=None):
    if err is not None:
        err += '\n\n'
    else:
        err = ''
    sys.stderr.write('''%sUsage: dnsviz [options] <command> [args]
Options:
    -p <path>      - Add path to the python path.
Commands:
    probe          - Issue diagnostic DNS queries.
    grok           - Assess diagnostic DNS queries.
    graph          - Graph the assessment of diagnostic DNS queries.
    print          - Process diagnostic DNS queries to textual output.
    query          - Assess a DNS query.
    help [<command>]
                   - Show usage for a command.
''' % (err))

def main():
    check_deps()

    try:
        opts, args = getopt.getopt(sys.argv[1:], 'p:')
    except getopt.GetoptError as e:
        usage(str(e) + '\n')
        sys.exit(1)

    opts = dict(opts)

    if len(args) < 1:
        usage()
        sys.exit(0)

    if args[0] == 'help':
        if len(args) < 2:
            usage()
            sys.exit(0)

        command = args[1]
    else:
        command = args[0]

    if '-p' in opts:
        sys.path.insert(0, opts['-p'])

    # first try importing just the commands module to make sure
    # dnsviz is properly reachable with the current path
    importlib.import_module('%s.%s' % (CMD_DIR1, CMD_DIR2))

    # now try importing the module for the actual command
    try:
        mod = importlib.import_module('%s.%s.%s' % (CMD_DIR1, CMD_DIR2, command))
    except ImportError:
        # See if the filename corresponding to the module we were trying to
        # import was found in the stack.  If so, then the error is with
        # importing something from inside that file.  Otherwise, it was that we
        # couldn't find the file corresponding to the command, so it was thus
        # an illegitmate command.
        exc_frame = sys.exc_info()[2]
        frame1 = exc_frame.tb_next.tb_next

        found_file = False
        while frame1 is not None:
            filename = frame1.tb_frame.f_code.co_filename
            cmd_dir2, filename = os.path.split(filename)
            if filename.endswith('.py'):
                filename = filename[:-3]
            cmd_dir1, cmd_dir2 = os.path.split(cmd_dir2)
            cmd_dir0, cmd_dir1 = os.path.split(cmd_dir1)
            if cmd_dir1 == CMD_DIR1 and \
                    cmd_dir2 == CMD_DIR2 and \
                    filename == command:
                found_file = True
                break
            frame1 = frame1.tb_next

        if found_file:
            raise
        else:
            sys.stderr.write('Invalid command: %s\n' % command)
            sys.exit(1)

    if args[0] == 'help':
        helper = mod.build_helper(logger, sys.argv[0], command)
        helper.parser.print_help()
    else:
        mod.main(args)

if __name__ == "__main__":
    main()
