# -*- coding: utf-8 -*-
#
# (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
#
# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Author: Stan Dolson , Goutam Kodu
#

# Std Lib
import os
import os.path
import sys
import re
import time
import cStringIO
import ConfigParser
import shutil
import stat

# Local
from base.logger import *
from base.g import *
from base.codes import *
from base import utils, password,services
from installer import pluginhandler

# DBus
import dbus
import dbus.service
import gobject

import warnings
# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
warnings.simplefilter("ignore", DeprecationWarning)


class AccessDeniedException(dbus.DBusException):
    _dbus_error_name = 'com.hp.hplip.AccessDeniedException'

class UnsupportedException(dbus.DBusException):
    _dbus_error_name = 'com.hp.hplip.UnsupportedException'

class UsageError(dbus.DBusException):
    _dbus_error_name = 'com.hp.hplip.UsageError'


POLICY_KIT_ACTION = "com.hp.hplip"
INSTALL_PLUGIN_ACTION = "com.hp.hplip.installplugin"


def get_service_bus():
    return dbus.SystemBus()


def get_service(bus=None):
    if not bus:
        bus = get_service_bus()

    service = bus.get_object(BackendService.SERVICE_NAME, '/')
    service = dbus.Interface(service, BackendService.INTERFACE_NAME)
    return service


class PolicyKitAuthentication(object):
    def __init__(self):
        super(PolicyKitAuthentication, self).__init__()
        self.pkit = None
        self.auth = None


    def is_authorized(self, action_id, pid=None):
        if pid == None:
            pid = os.getpid()

        pid = dbus.UInt32(pid)

        authorized = self.policy_kit.IsProcessAuthorized(action_id, pid, False)
        log.debug("is_authorized(%s) = %r" % (action_id, authorized))

        return (authorized == 'yes')


    def obtain_authorization(self, action_id, widget=None):
        if self.is_authorized(action_id):
            return True

        xid = (widget and widget.get_toplevel().window.xid or 0)
        xid, pid = dbus.UInt32(xid), dbus.UInt32(os.getpid())

        granted = self.auth_agent.ObtainAuthorization(action_id, xid, pid)
        log.debug("obtain_authorization(%s) = %r" % (action_id, granted))

        return bool(granted)


    def get_policy_kit(self):
        if self.pkit:
            return self.pkit

        service = dbus.SystemBus().get_object('org.freedesktop.PolicyKit', '/')
        self.pkit = dbus.Interface(service, 'org.freedesktop.PolicyKit')
        return self.pkit

    policy_kit = property(get_policy_kit)


    def get_auth_agent(self):
        if self.auth:
            return self.auth

        self.auth = dbus.SessionBus().get_object(
            'org.freedesktop.PolicyKit.AuthenticationAgent', '/')
        return self.auth

    auth_agent = property(get_auth_agent)



class PolicyKitService(dbus.service.Object):
    def check_permission_v0(self, sender, action=POLICY_KIT_ACTION):
        if not sender:
            log.error("Session not authorized by PolicyKit")
            raise AccessDeniedException('Session not authorized by PolicyKit')

        try:
            policy_auth = PolicyKitAuthentication()
            bus = dbus.SystemBus()

            dbus_object = bus.get_object('org.freedesktop.DBus', '/')
            dbus_object = dbus.Interface(dbus_object, 'org.freedesktop.DBus')

            pid = dbus.UInt32(dbus_object.GetConnectionUnixProcessID(sender))

            granted = policy_auth.is_authorized(action, pid)
            if not granted:
                log.error("Process not authorized by PolicyKit")
                raise AccessDeniedException('Process not authorized by PolicyKit')

            granted = policy_auth.policy_kit.IsSystemBusNameAuthorized(action,
                                                                       sender,
                                                                       False)
            if granted != 'yes':
                log.error("Session not authorized by PolicyKit version 0")
                raise AccessDeniedException('Session not authorized by PolicyKit')

        except AccessDeniedException:
            log.warning("AccessDeniedException")
            raise

        except dbus.DBusException, ex:
            log.warning("AccessDeniedException %r", ex)
            raise AccessDeniedException(ex.message)


    def check_permission_v1(self, sender, connection, action=POLICY_KIT_ACTION):
        if not sender or not connection:
            log.error("Session not authorized by PolicyKit")
            raise AccessDeniedException('Session not authorized by PolicyKit')

        system_bus = dbus.SystemBus()
        obj = system_bus.get_object("org.freedesktop.PolicyKit1",
                                    "/org/freedesktop/PolicyKit1/Authority",
                                    "org.freedesktop.PolicyKit1.Authority")
        policy_kit = dbus.Interface(obj, "org.freedesktop.PolicyKit1.Authority")
        info = dbus.Interface(connection.get_object("org.freedesktop.DBus",
                                                    "/org/freedesktop/DBus/Bus",
                                                    False),
                              "org.freedesktop.DBus")
        pid = info.GetConnectionUnixProcessID(sender)
        
        subject = (
            'unix-process',
            { 'pid' : dbus.UInt32(pid, variant_level = 1) }
        )
        details = { '' : '' }
        flags = dbus.UInt32(1)         # AllowUserInteraction = 0x00000001
        cancel_id = ''

        (ok, notused, details) = \
            policy_kit.CheckAuthorization(subject,
                                          action,
                                          details,
                                          flags,
                                          cancel_id)
        if not ok:
            log.error("Session not authorized by PolicyKit version 1")

        return ok


if utils.to_bool(sys_conf.get('configure', 'policy-kit')):
    class BackendService(PolicyKitService):
        INTERFACE_NAME = 'com.hp.hplip'
        SERVICE_NAME   = 'com.hp.hplip'
        LOGFILE_NAME   = '/tmp/hp-pkservice.log'

        def __init__(self, connection=None, path='/', logfile=LOGFILE_NAME):
            if connection is None:
                connection = get_service_bus()

            super(BackendService, self).__init__(connection, path)

            self.name = dbus.service.BusName(self.SERVICE_NAME, connection)
            self.loop = gobject.MainLoop()
            self.version = 0

            log.set_logfile("%s.%d" % (logfile, os.getpid()))
            log.set_level("debug")

        def run(self, version=None):
            if version is None:
                version = policykit_version()
                if version is None:
                    log.error("Unable to determine installed PolicyKit version")
                    return

            self.version = version
            log.set_where(Logger.LOG_TO_CONSOLE_AND_FILE)
            log.debug("Starting back-end service loop (version %d)" % version)

            self.loop.run()


        @dbus.service.method(dbus_interface=INTERFACE_NAME,
                                in_signature='s', out_signature='b',
                                sender_keyword='sender',
                                connection_keyword='connection')
        def installPlugin(self, src_dir, sender=None, connection=None):
            if self.version == 0:
                try:
                    self.check_permission_v0(sender, INSTALL_PLUGIN_ACTION)
                except AccessDeniedException, e:
                    return False

            elif self.version == 1:
                if not self.check_permission_v1(sender,
                                                connection,
                                                INSTALL_PLUGIN_ACTION):
                    return False

            else:
                log.error("installPlugin: invalid PolicyKit version %d" % self.version)
                return False

            log.debug("installPlugin: installing from '%s'" % src_dir)
            try:
                from installer import pluginhandler
            except ImportError,e:
                log.error("Failed to Import pluginhandler")
                return False

            pluginObj = pluginhandler.PluginHandle()
            if not pluginObj.copyFiles(src_dir):
                log.error("Plugin installation failed")
                return False

            return True


        @dbus.service.method(dbus_interface=INTERFACE_NAME,
                                in_signature='s', out_signature='b',
                                sender_keyword='sender',
                                connection_keyword='connection')
        def shutdown(self, arg, sender=None, connection=None):
            log.debug("Stopping backend service")
            self.loop.quit()

            return True



class PolicyKit(object):
    def __init__(self, version=None):
        if version is None:
            version = policykit_version()
            if version is None:
                log.debug("Unable to determine installed PolicyKit version")
                return

        self.bus = dbus.SystemBus()
        self.obj = self.bus.get_object(POLICY_KIT_ACTION, "/")
        self.iface = dbus.Interface(self.obj, dbus_interface=POLICY_KIT_ACTION)
        self.version = version

    def installPlugin(self, src_dir):
        if self.version == 0:
            auth = PolicyKitAuthentication()
            if not auth.is_authorized(INSTALL_PLUGIN_ACTION):
                if not auth.obtain_authorization(INSTALL_PLUGIN_ACTION):
                    return None

        try:
            ok = self.iface.installPlugin(src_dir)
            return ok
        except dbus.DBusException, e:
            log.debug("installPlugin: %s" % str(e))
            return False


    def shutdown(self):
        if self.version == 0:
            auth = PolicyKitAuthentication()
            if not auth.is_authorized(INSTALL_PLUGIN_ACTION):
                if not auth.obtain_authorization(INSTALL_PLUGIN_ACTION):
                    return None

        try:
            ok = self.iface.shutdown("")
            return ok
        except dbus.DBusException, e:
            log.debug("shutdown: %s" % str(e))
            return False





def run_plugin_command(required=True, plugin_reason=PLUGIN_REASON_NONE, Mode = GUI_MODE):
    su_sudo = None
    need_sudo = True
    name = None
    version = None

    if utils.to_bool(sys_conf.get('configure', 'policy-kit')):
        try:
            obj = PolicyKit()
            su_sudo = "%s"
            need_sudo = False
            log.debug("Using PolicyKit for authentication")
        except dbus.DBusException, ex:
            log.error("PolicyKit NOT installed when configured for use")

    if os.geteuid() == 0:
        su_sudo = "%s"
        need_sudo = False
        
    passwordObj = password.Password(Mode)
    if need_sudo:
        su_sudo = passwordObj.getAuthType()

    if su_sudo is None:
        log.error("Unable to find a suitable sudo command to run 'hp-plugin'")
        return (False, False)

    req = '--required'
    if not required:
        req = '--optional'

    if utils.which("hp-plugin"):
        p_path="hp-plugin"
    else:
        p_path="python2 ./plugin.py"

    if 'gksu' in su_sudo:
        cmd = passwordObj.getAuthCmd() % ("%s -u %s --reason %s" % (p_path, req, plugin_reason))
        cmd +=" -m" 
        cmd += (" \"hp-plugin:- HP Device requires to install HP proprietary plugin. Please enter root password to continue\"")
    else:
        cmd = passwordObj.getAuthCmd() % ("%s -u %s --reason %s To_install_plugin_for_HP_Device" % (p_path, req, plugin_reason))

        
    log.debug("%s" % cmd)
    status, output = utils.run(cmd, passwordObj)

    return (status == 0, True)


def policykit_version():
    if os.path.isdir("/usr/share/polkit-1"):
        return 1
    elif os.path.isdir("/usr/share/PolicyKit"):
        return 0
    else:
        return None
