#!/usr/local/bin/python3.11

## Printing troubleshooter

## Copyright (C) 2008, 2009, 2014 Red Hat, Inc.
## Author: 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
from tempfile import NamedTemporaryFile
import datetime
import time
from timedops import TimedOperation, OperationCanceled
from .base import *

try:
    from systemd import journal
except:
    journal = False

class ErrorLogCheckpoint(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Error log checkpoint")
        page = self.initial_vbox (_("Debugging"),
                                  _("This step will enable debugging output "
                                    "from the CUPS scheduler.  This may "
                                    "cause the scheduler to restart.  Click "
                                    "the button below to enable debugging."))
        button = Gtk.Button.new_with_label (_("Enable Debugging"))
        buttonbox = Gtk.HButtonBox ()
        buttonbox.set_border_width (0)
        buttonbox.set_layout (Gtk.ButtonBoxStyle.START)
        buttonbox.pack_start (button, False, False, 0)
        self.button = button
        page.pack_start (buttonbox, False, False, 0)
        self.label = Gtk.Label ()
        self.label.set_alignment (0, 0)
        self.label.set_line_wrap (True)
        page.pack_start (self.label, False, False, 0)
        troubleshooter.new_page (page, self)
        self.persistent_answers = {}

    def __del__ (self):
        if not self.persistent_answers.get ('error_log_debug_logging_set',
                                            False):
            return

        f = self.troubleshooter.answers['_authenticated_connection_factory']
        c = f.get_connection ()
        c._set_lock (False)
        settings = c.adminGetServerSettings ()
        if len (list(settings.keys ())) == 0:
            return

        settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
        answers = self.troubleshooter.answers
        orig_settings = self.persistent_answers['cups_server_settings']
        settings['MaxLogSize'] = orig_settings.get ('MaxLogSize', '2000000')
        c.adminSetServerSettings (settings)

    def display (self):
        self.answers = {}
        answers = self.troubleshooter.answers
        if not answers['cups_queue_listed']:
            return False

        self.authconn = answers['_authenticated_connection']
        parent = self.troubleshooter.get_window ()

        def getServerSettings ():
            # Fail if auth required.
            cups.setPasswordCB (lambda x: '')
            cups.setServer ('')
            c = cups.Connection ()
            return c.adminGetServerSettings ()

        try:
            self.op = TimedOperation (getServerSettings, parent=parent)
            settings = self.op.run ()
        except RuntimeError:
            return False
        except cups.IPPError:
            settings = {}

        self.forward_allowed = False
        self.label.set_text ('')
        if len (list(settings.keys ())) == 0:
            # Requires root
            return True
        else:
            self.persistent_answers['cups_server_settings'] = settings

        try:
            if int (settings[cups.CUPS_SERVER_DEBUG_LOGGING]) != 0:
                # Already enabled
                return False
        except KeyError:
            pass
        except ValueError:
            pass

        return True

    def connect_signals (self, handler):
        self.button_sigid = self.button.connect ('clicked', self.enable_clicked,
                                                 handler)

    def disconnect_signals (self):
        self.button.disconnect (self.button_sigid)

    def collect_answer (self):
        answers = self.troubleshooter.answers
        if not answers['cups_queue_listed']:
            return {}

        parent = self.troubleshooter.get_window ()
        self.answers.update (self.persistent_answers)
        if 'error_log_checkpoint' in self.answers:
            return self.answers

        tmpf = NamedTemporaryFile()
        try:
            self.op = TimedOperation (self.authconn.getFile,
                                        args=["/admin/log/error_log"],
                                        kwargs={'file': tmpf},
                                        parent=parent)
            self.op.run ()
        except (RuntimeError, cups.IPPError) as e:
            self.answers['error_log_checkpoint_exc'] = e
        except cups.HTTPError as e:
            self.answers['error_log_checkpoint_exc'] = e

            # Abandon the CUPS connection and make another.
            answers = self.troubleshooter.answers
            factory = answers['_authenticated_connection_factory']
            self.authconn = factory.get_connection ()
            self.answers['_authenticated_connection'] = self.authconn

        try:
            statbuf = os.stat (tmpf.name)
        except OSError:
            statbuf = [0, 0, 0, 0, 0, 0, 0]

        self.answers['error_log_checkpoint'] = statbuf[6]
        self.persistent_answers['error_log_checkpoint'] = statbuf[6]

        if journal:
            j = journal.Reader ()
            j.seek_tail ()
            cursor = j.get_previous ()['__CURSOR']
            self.answers['error_log_cursor'] = cursor
            self.persistent_answers['error_log_cursor'] = cursor
            now = datetime.datetime.fromtimestamp (time.time ())
            timestamp = now.strftime ("%F %T")
            self.answers['error_log_timestamp'] = timestamp
            self.persistent_answers['error_log_timestamp'] = timestamp

        return self.answers

    def can_click_forward (self):
        return self.forward_allowed

    def enable_clicked (self, button, handler):
        parent = self.troubleshooter.get_window ()
        self.troubleshooter.busy ()
        try:
            self.op = TimedOperation (self.authconn.adminGetServerSettings,
                                      parent=parent)
            settings = self.op.run ()
        except (cups.IPPError, OperationCanceled):
            self.troubleshooter.ready ()
            self.forward_allowed = True
            handler (button)
            return

        self.persistent_answers['cups_server_settings'] = settings.copy ()
        MAXLOGSIZE='MaxLogSize'
        try:
            prev_debug = int (settings[cups.CUPS_SERVER_DEBUG_LOGGING])
        except KeyError:
            prev_debug = 0
        try:
            prev_logsize = int (settings[MAXLOGSIZE])
        except (KeyError, ValueError):
            prev_logsize = -1

        if prev_debug == 0 or prev_logsize != '0':
            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '1'
            settings[MAXLOGSIZE] = '0'
            success = False

            def set_settings (connection, settings):
                connection.adminSetServerSettings (settings)

                # Now reconnect.
                attempt = 1
                while attempt <= 5:
                    try:
                        time.sleep (1)
                        connection._connect ()
                        break
                    except RuntimeError:
                        # Connection failed
                        attempt += 1

            try:
                debugprint ("Settings to set: " + repr (settings))
                self.op = TimedOperation (set_settings,
                                          args=(self.authconn, settings,),
                                          parent=parent)
                self.op.run ()
                success = True
            except cups.IPPError:
                pass
            except RuntimeError:
                pass

            if success:
                self.persistent_answers['error_log_debug_logging_set'] = True
                self.label.set_text (_("Debug logging enabled."))
        else:
            self.label.set_text (_("Debug logging was already enabled."))

        self.forward_allowed = True
        self.troubleshooter.ready ()
        handler (button)

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

        # Abandon the CUPS connection and make another.
        answers = self.troubleshooter.answers
        factory = answers['_authenticated_connection_factory']
        self.authconn = factory.get_connection ()
        self.answers['_authenticated_connection'] = self.authconn
