#!/usr/bin/python3.7

## Printing troubleshooter

## Copyright (C) 2008, 2009, 2010, 2011, 2014 Red Hat, Inc.
## Authors:
##  Tim Waugh <twaugh@redhat.com>

## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

from gi.repository import Gtk

import cups
import os
import smburi
import socket
import subprocess
from timedops import TimedSubprocess, TimedOperation
from .base import *

try:
    import smbc
except:
    pass

class CheckNetworkServerSanity(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Check network server sanity")
        troubleshooter.new_page (Gtk.Label (), self)

    def display (self):
        # Collect useful information.

        self.answers = {}
        answers = self.troubleshooter.answers
        if ('remote_server_name' not in answers and
            'remote_server_ip_address' not in answers):
            return False

        parent = self.troubleshooter.get_window ()

        server_name = answers['remote_server_name']
        server_port = answers.get('remote_server_port', 631)
        try_connect = False
        if server_name:
            # Try resolving the hostname.
            try:
                ai = socket.getaddrinfo (server_name, server_port)
                resolves = [family_socktype_proto_canonname_sockaddr[4][0] for
                             family_socktype_proto_canonname_sockaddr in ai]
                try_connect = True
            except socket.gaierror:
                resolves = False

            self.answers['remote_server_name_resolves'] = resolves

            ipaddr = answers.get ('remote_server_ip_address', '')
            if resolves:
                if ipaddr:
                    try:
                        resolves.index (ipaddr)
                    except ValueError:
                        # The IP address given doesn't match the server name.
                        # Use the IP address instead of the name.
                        server_name = ipaddr
                        try_connect = True
            elif ipaddr:
                server_name = ipaddr
                try_connect = True
        else:
            server_name = answers['remote_server_ip_address']
            # Validate it.
            try:
                ai = socket.getaddrinfo (server_name, server_port)
                resolves = [family_socktype_proto_canonname_sockaddr1[4][0] for
                             family_socktype_proto_canonname_sockaddr1 in ai]
            except socket.gaierror:
                resolves = False

            self.answers['remote_server_name_resolves'] = resolves
            try_connect = True

        self.answers['remote_server_try_connect'] = server_name

        if (try_connect and
            answers.get ('cups_device_uri_scheme', 'ipp') in ['ipp',
                                                              'http',
                                                              'https']):
            if answers.get ('cups_device_uri_scheme') == 'https':
                encryption = cups.HTTP_ENCRYPT_REQUIRED
            else:
                encryption = cups.HTTP_ENCRYPT_IF_REQUESTED

            try:
                self.op = TimedOperation (cups.Connection,
                                          kwargs={"host": server_name,
                                                  "port": server_port,
                                                  "encryption": encryption},
                                          parent=parent)
                c = self.op.run ()
                ipp_connect = True
            except RuntimeError:
                ipp_connect = False

            self.answers['remote_server_connect_ipp'] = ipp_connect

            if ipp_connect:
                try:
                    self.op = TimedOperation (c.getPrinters, parent=parent)
                    self.op.run ()
                    cups_server = True
                except:
                    cups_server = False

                self.answers['remote_server_cups'] = cups_server

                if cups_server:
                    cups_printer_dict = answers.get ('cups_printer_dict', {})
                    uri = cups_printer_dict.get ('device-uri', None)
                    if uri:
                        try:
                            self.op = TimedOperation (c.getPrinterAttributes,
                                                      kwargs={"uri": uri},
                                                      parent=parent)
                            attr = self.op.run ()
                            self.answers['remote_cups_queue_attributes'] = attr
                        except:
                            pass

        if try_connect:
            # Try to see if we can connect using smbc.
            context = None
            try:
                context = smbc.Context ()
                name = self.answers['remote_server_try_connect']
                self.op = TimedOperation (context.opendir,
                                          args=("smb://%s/" % name,),
                                          parent=parent)
                dir = self.op.run ()
                self.op = TimedOperation (dir.getdents, parent=parent)
                shares = self.op.run ()
                self.answers['remote_server_smb'] = True
                self.answers['remote_server_smb_shares'] = shares
            except NameError:
                # No smbc support
                pass
            except RuntimeError as e:
                (e, s) = e.args
                self.answers['remote_server_smb_shares'] = (e, s)

            if context is not None and 'cups_printer_dict' in answers:
                uri = answers['cups_printer_dict'].get ('device-uri', '')
                u = smburi.SMBURI (uri)
                (group, host, share, user, password) = u.separate ()
                accessible = False
                try:
                    self.op = TimedOperation (context.open,
                                              args=("smb://%s/%s" % (host,
                                                                     share),
                                                    os.O_RDWR,
                                                    0o777),
                                              parent=parent)
                    f  = self.op.run ()
                    accessible = True
                except RuntimeError as e:
                    (e, s) = e.args
                    accessible = (e, s)

                self.answers['remote_server_smb_share_anon_access'] = accessible

        # Try traceroute if we haven't already.
        if (try_connect and
            'remote_server_traceroute' not in answers):
            try:
                self.op = TimedSubprocess (parent=parent, close_fds=True,
                                           args=['traceroute', '-w', '1',
                                                 server_name],
                                           stdin=subprocess.DEVNULL,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE)
                self.answers['remote_server_traceroute'] = self.op.run ()
            except:
                # Problem executing command.
                pass

        return False

    def collect_answer (self):
        return self.answers

    def cancel_operation (self):
        self.op.cancel ()
