Exemple #1
0
class Welcome(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Welcome")
        welcome = gtk.HBox ()
        welcome.set_spacing (12)
        welcome.set_border_width (12)
        image = gtk.Image ()
        image.set_alignment (0, 0)
        image.set_from_stock (gtk.STOCK_PRINT, gtk.ICON_SIZE_DIALOG)
        intro = gtk.Label ('<span weight="bold" size="larger">' +
                           _("Trouble-shooting Printing") +
                           '</span>\n\n' +
                           _("The next few screens will contain some "
                             "questions about your problem with printing. "
                             "Based on your answers a solution may be "
                             "suggested.") + '\n\n' +
                           _("Click 'Forward' to begin."))
        intro.set_alignment (0, 0)
        intro.set_use_markup (True)
        intro.set_line_wrap (True)
        welcome.pack_start (image, False, False, 0)
        welcome.pack_start (intro, True, True, 0)
        page = troubleshooter.new_page (welcome, self)

    def collect_answer (self):
        parent = self.troubleshooter.get_window ()
        # Store the authentication dialog instance in the answers.  This
        # allows the password to be cached.
        factory = AuthConnFactory (parent)
        self.op = TimedOperation (factory.get_connection, parent=parent)
        return {'_authenticated_connection_factory': factory,
                '_authenticated_connection': self.op.run () }

    def cancel_operation (self):
        self.op.cancel ()
Exemple #2
0
class Welcome(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Welcome")
        welcome = Gtk.HBox ()
        welcome.set_spacing (12)
        welcome.set_border_width (12)
        image = Gtk.Image ()
        image.set_alignment (0, 0)
        image.set_from_stock (Gtk.STOCK_PRINT, Gtk.IconSize.DIALOG)
        intro = Gtk.Label(label='<span weight="bold" size="larger">' +
                           _("Trouble-shooting Printing") +
                           '</span>\n\n' +
                           _("The next few screens will contain some "
                             "questions about your problem with printing. "
                             "Based on your answers a solution may be "
                             "suggested.") + '\n\n' +
                           _("Click 'Forward' to begin."))
        intro.set_alignment (0, 0)
        intro.set_use_markup (True)
        intro.set_line_wrap (True)
        welcome.pack_start (image, False, False, 0)
        welcome.pack_start (intro, True, True, 0)
        page = troubleshooter.new_page (welcome, self)

    def collect_answer (self):
        parent = self.troubleshooter.get_window ()
        # Store the authentication dialog instance in the answers.  This
        # allows the password to be cached.
        factory = AuthConnFactory (parent)
        self.op = TimedOperation (factory.get_connection, parent=parent)
        return {'_authenticated_connection_factory': factory,
                '_authenticated_connection': self.op.run () }

    def cancel_operation (self):
        self.op.cancel ()
Exemple #3
0
class PrinterFinder:
    def __init__ (self):
        self.quit = False

    def find (self, hostname, callback_fn):
        self.hostname = hostname
        self.callback_fn = callback_fn
        self.op = TimedOperation (self._do_find, callback=lambda x, y: None)

    def cancel (self):
        self.op.cancel ()
        self.quit = True

    def _do_find (self):
        self._cached_attributes = dict()
        for fn in [self._probe_jetdirect,
                   self._probe_ipp,
                   self._probe_snmp,
                   self._probe_lpd,
                   self._probe_hplip,
                   self._probe_smb]:
            if self.quit:
                return

            try:
                fn ()
            except Exception, e:
                nonfatalException ()

        # Signal that we've finished.
        if not self.quit:
            self.callback_fn (None)
Exemple #4
0
class NetworkCUPSPrinterShared(Question):
    def __init__(self, troubleshooter):
        Question.__init__(self, troubleshooter, "Queue not shared?")
        page = self.initial_vbox(
            _("Queue Not Shared"),
            _("The CUPS printer on the server is not "
              "shared."))
        troubleshooter.new_page(page, self)

    def display(self):
        self.answers = {}
        answers = self.troubleshooter.answers
        if (answers.has_key('remote_cups_queue_listed')
                and answers['remote_cups_queue_listed'] == False):
            return False

        parent = self.troubleshooter.get_window()
        if not answers.has_key('remote_cups_queue_attributes'):
            if not (answers.has_key('remote_server_try_connect')
                    and answers.has_key('remote_cups_queue')):
                return False

            try:
                host = answers['remote_server_try_connect']
                self.op = TimedOperation(cups.Connection,
                                         kwargs={"host": host},
                                         parent=parent)
                c = self.op.run()
                self.op = TimedOperation(c.getPrinterAttributes,
                                         args=(answers['remote_cups_queue'], ),
                                         parent=parent)
                attr = self.op.run()
            except RuntimeError:
                return False
            except cups.IPPError:
                return False

            self.answers['remote_cups_queue_attributes'] = attr
        else:
            attr = answers['remote_cups_queue_attributes']

        if attr.has_key('printer-is-shared'):
            # CUPS >= 1.2
            if not attr['printer-is-shared']:
                return True

        return False

    def can_click_forward(self):
        return False

    def collect_answer(self):
        return self.answers

    def cancel_operation(self):
        self.op.cancel()
class NetworkCUPSPrinterShared(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Queue not shared?")
        page = self.initial_vbox (_("Queue Not Shared"),
                                  _("The CUPS printer on the server is not "
                                    "shared."))
        troubleshooter.new_page (page, self)

    def display (self):
        self.answers = {}
        answers = self.troubleshooter.answers
        if ('remote_cups_queue_listed' in answers and
            answers['remote_cups_queue_listed'] == False):
            return False

        parent = self.troubleshooter.get_window ()
        if 'remote_cups_queue_attributes' not in answers:
            if not ('remote_server_try_connect' in answers and
                    'remote_cups_queue' in answers):
                return False

            try:
                host = answers['remote_server_try_connect']
                self.op = TimedOperation (cups.Connection,
                                          kwargs={"host": host},
                                          parent=parent)
                c = self.op.run ()
                self.op = TimedOperation (c.getPrinterAttributes,
                                          args=(answers['remote_cups_queue'],),
                                          parent=parent)
                attr = self.op.run ()
            except RuntimeError:
                return False
            except cups.IPPError:
                return False

            self.answers['remote_cups_queue_attributes'] = attr
        else:
            attr = answers['remote_cups_queue_attributes']

        if 'printer-is-shared' in attr:
            # CUPS >= 1.2
            if not attr['printer-is-shared']:
                return True

        return False

    def can_click_forward (self):
        return False

    def collect_answer (self):
        return self.answers

    def cancel_operation (self):
        self.op.cancel ()
class SchedulerNotRunning(Question):
    def __init__(self, troubleshooter):
        Question.__init__(self, troubleshooter, "Scheduler not running?")
        page = self.initial_vbox(
            _("CUPS Service Stopped"),
            _("The CUPS print spooler does not appear "
              "to be running.  To correct this, choose "
              "System->Administration->Services from "
              "the main menu and look for the 'cups' "
              "service."))
        troubleshooter.new_page(page, self)

    def display(self):
        self.answers = {}
        if self.troubleshooter.answers.get('cups_queue_listed', False):
            return False

        parent = self.troubleshooter.get_window()

        # Find out if CUPS is running.
        failure = False
        try:
            self.op = TimedOperation(cups.Connection, parent=parent)
            c = self.op.run()
        except RuntimeError:
            failure = True

        self.answers['cups_connection_failure'] = failure
        return failure

    def can_click_forward(self):
        return False

    def collect_answer(self):
        return self.answers

    def cancel_operation(self):
        self.op.cancel()
class SchedulerNotRunning(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Scheduler not running?")
        page = self.initial_vbox (_("CUPS Service Stopped"),
                                  _("The CUPS print spooler does not appear "
                                    "to be running.  To correct this, choose "
                                    "System->Administration->Services from "
                                    "the main menu and look for the 'cups' "
                                    "service."))
        troubleshooter.new_page (page, self)

    def display (self):
        self.answers = {}
        if self.troubleshooter.answers.get ('cups_queue_listed', False):
            return False

        parent = self.troubleshooter.get_window ()

        # Find out if CUPS is running.
        failure = False
        try:
            self.op = TimedOperation (cups.Connection,
                                      parent=parent)
            c = self.op.run ()
        except RuntimeError:
            failure = True

        self.answers['cups_connection_failure'] = failure
        return failure

    def can_click_forward (self):
        return False

    def collect_answer (self):
        return self.answers

    def cancel_operation (self):
        self.op.cancel ()
class ErrorLogFetch(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Error log fetch")
        troubleshooter.new_page (Gtk.Label (), self)
        self.persistent_answers = {}

    def display (self):
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window ()
        self.answers = {}
        checkpoint = answers.get ('error_log_checkpoint')
        cursor = answers.get ('error_log_cursor')

        if ('error_log' in self.persistent_answers or
            'journal' in self.persistent_answers):
            checkpoint = None
            cursor = None

        def fetch_log (c):
            prompt = c._get_prompt_allowed ()
            c._set_prompt_allowed (False)
            c._connect ()
            (tmpfd, tmpfname) = tempfile.mkstemp ()
            os.close (tmpfd)
            success = False
            try:
                c.getFile ('/admin/log/error_log', tmpfname)
                success = True
            except cups.HTTPError:
                try:
                    os.remove (tmpfname)
                except OSError:
                    pass

            c._set_prompt_allowed (prompt)
            if success:
                return tmpfname
            return None

        self.authconn = self.troubleshooter.answers['_authenticated_connection']
        if 'error_log_debug_logging_set' in answers:
            try:
                self.op = TimedOperation (self.authconn.adminGetServerSettings,
                                          parent=parent)
                settings = self.op.run ()
            except cups.IPPError:
                return False

            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
            orig_settings = answers['cups_server_settings']
            settings['MaxLogSize'] = orig_settings.get ('MaxLogSize', '2000000')
            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:

                self.op = TimedOperation (set_settings,
                                          (self.authconn, settings),
                                          parent=parent)
                self.op.run ()
                self.persistent_answers['error_log_debug_logging_unset'] = True
            except cups.IPPError:
                pass

        self.answers = {}
        if journal and cursor != None:
            def journal_format (x):
                try:
                    priority = "XACEWNIDd"[x['PRIORITY']]
                except (IndexError, TypeError):
                    priority = " "

                return (priority + " " +
                        x['__REALTIME_TIMESTAMP'].strftime("[%m/%b/%Y:%T]") +
                        " " + x['MESSAGE'])

            r = journal.Reader ()
            r.seek_cursor (cursor)
            r.add_match (_COMM="cupsd")
            self.answers['journal'] = [journal_format (x) for x in r]

        if checkpoint != None:
            self.op = TimedOperation (fetch_log,
                                      (self.authconn,),
                                      parent=parent)
            tmpfname = self.op.run ()
            if tmpfname != None:
                f = open (tmpfname)
                f.seek (checkpoint)
                lines = f.readlines ()
                os.remove (tmpfname)
                self.answers = { 'error_log': [x.strip () for x in lines] }

        return False

    def collect_answer (self):
        answers = self.persistent_answers.copy ()
        answers.update (self.answers)
        return answers

    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
class PrintTestPage(Question):
    STATE = { cups.IPP_JOB_PENDING: _("Pending"),
              cups.IPP_JOB_HELD: _("Held"),
              cups.IPP_JOB_PROCESSING: _("Processing"),
              cups.IPP_JOB_STOPPED: _("Stopped"),
              cups.IPP_JOB_CANCELED: _("Canceled"),
              cups.IPP_JOB_ABORTED: _("Aborted"),
              cups.IPP_JOB_COMPLETED: _("Completed") }

    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Print test page")
        page = Gtk.VBox ()
        page.set_spacing (12)
        page.set_border_width (12)

        label = Gtk.Label ()
        label.set_alignment (0, 0)
        label.set_use_markup (True)
        label.set_line_wrap (True)
        page.pack_start (label, False, False, 0)
        self.main_label = label
        self.main_label_text = ('<span weight="bold" size="larger">' +
                                _("Test Page") + '</span>\n\n' +
                                _("Now print a test page.  If you are having "
                                  "problems printing a specific document, "
                                  "print that document now and mark the print "
                                  "job below."))

        hbox = Gtk.HButtonBox ()
        hbox.set_border_width (0)
        hbox.set_spacing (3)
        hbox.set_layout (Gtk.ButtonBoxStyle.START)
        self.print_button = Gtk.Button.new_with_label (_("Print Test Page"))
        hbox.pack_start (self.print_button, False, False, 0)

        self.cancel_button = Gtk.Button.new_with_label (_("Cancel All Jobs"))
        hbox.pack_start (self.cancel_button, False, False, 0)
        page.pack_start (hbox, False, False, 0)

        tv = Gtk.TreeView ()
        test_cell = Gtk.CellRendererToggle ()
        test = Gtk.TreeViewColumn (_("Test"), test_cell, active=0)
        job = Gtk.TreeViewColumn (_("Job"), Gtk.CellRendererText (), text=1)
        printer_cell = Gtk.CellRendererText ()
        printer = Gtk.TreeViewColumn (_("Printer"), printer_cell, text=2)
        name_cell = Gtk.CellRendererText ()
        name = Gtk.TreeViewColumn (_("Document"), name_cell, text=3)
        status = Gtk.TreeViewColumn (_("Status"), Gtk.CellRendererText (),
                                     text=4)
        test_cell.set_radio (False)
        self.test_cell = test_cell
        printer.set_resizable (True)
        printer_cell.set_property ("ellipsize", Pango.EllipsizeMode.END)
        printer_cell.set_property ("width-chars", 20)
        name.set_resizable (True)
        name_cell.set_property ("ellipsize", Pango.EllipsizeMode.END)
        name_cell.set_property ("width-chars", 20)
        status.set_resizable (True)
        tv.append_column (test)
        tv.append_column (job)
        tv.append_column (printer)
        tv.append_column (name)
        tv.append_column (status)
        tv.set_rules_hint (True)
        sw = Gtk.ScrolledWindow ()
        sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type (Gtk.ShadowType.IN)
        sw.add (tv)
        self.treeview = tv
        page.pack_start (sw, False, False, 0)

        label = Gtk.Label(label=_("Did the marked print jobs print correctly?"))
        label.set_line_wrap (True)
        label.set_alignment (0, 0)
        page.pack_start (label, False, False, 0)

        vbox = Gtk.VBox ()
        vbox.set_spacing (6)
        self.yes = Gtk.RadioButton (label=_("Yes"))
        no = Gtk.RadioButton.new_with_label_from_widget (self.yes, _("No"))
        vbox.pack_start (self.yes, False, False, 0)
        vbox.pack_start (no, False, False, 0)
        page.pack_start (vbox, False, False, 0)
        self.persistent_answers = {}
        troubleshooter.new_page (page, self)

    def display (self):
        answers = self.troubleshooter.answers
        if 'cups_queue' not in answers:
            return False

        parent = self.troubleshooter.get_window ()
        self.authconn = answers['_authenticated_connection']
        mediatype = None
        defaults = answers.get ('cups_printer_ppd_defaults', {})
        for opts in defaults.values ():
            for opt, value in opts.items ():
                if opt == "MediaType":
                    mediatype = value
                    break

        if mediatype != None:
            mediatype_string = '\n\n' + (_("Remember to load paper of type "
                                           "'%s' into the printer first.") %
                                         mediatype)
        else:
            mediatype_string = ""

        label_text = self.main_label_text + mediatype_string
        self.main_label.set_markup (label_text)

        model = Gtk.ListStore (bool,
                               int,
                               str,
                               str,
                               str)
        self.treeview.set_model (model)
        self.job_to_iter = {}

        test_jobs = self.persistent_answers.get ('test_page_job_id', [])
        def get_jobs ():
            c = self.authconn
            try:
                r = ["job-id",
                     "job-name",
                     "job-state",
                     "job-printer-uri",
                     "printer-name"]
                jobs_dict = c.getJobs (which_jobs='not-completed',
                                       my_jobs=False,
                                       requested_attributes=r)
                completed_jobs_dict = c.getJobs (which_jobs='completed',
                                                 requested_attributes=r)
            except TypeError:
                # requested_attributes requires pycups 1.9.50
                jobs_dict = c.getJobs (which_jobs='not-completed',
                                       my_jobs=False)
                completed_jobs_dict = c.getJobs (which_jobs='completed')
            return (jobs_dict, completed_jobs_dict)

        self.op = TimedOperation (get_jobs, parent=parent)
        try:
            (jobs_dict, completed_jobs_dict) = self.op.run ()
        except (OperationCanceled, cups.IPPError):
            return False

        # We want to display the jobs in the queue for this printer...
        try:
            queue_uri_ending = "/" + self.troubleshooter.answers['cups_queue']
            jobs_on_this_printer = [x for x in jobs_dict.keys () if \
                                            jobs_dict[x]['job-printer-uri']. \
                                            endswith (queue_uri_ending)]
        except:
            jobs_on_this_printer = []

        # ...as well as any other jobs we've previous submitted as test pages.
        jobs = list (set(test_jobs).union (set (jobs_on_this_printer)))

        completed_jobs_dict = None
        for job in jobs:
            try:
                j = jobs_dict[job]
            except KeyError:
                try:
                    j = completed_jobs_dict[job]
                except KeyError:
                    continue

            iter = model.append (None)
            self.job_to_iter[job] = iter
            model.set_value (iter, 0, job in test_jobs)
            model.set_value (iter, 1, job)
            self.update_job (job, j)

        return True

    def connect_signals (self, handler):
        self.print_sigid = self.print_button.connect ("clicked",
                                                      self.print_clicked)
        self.cancel_sigid = self.cancel_button.connect ("clicked",
                                                        self.cancel_clicked)
        self.test_sigid = self.test_cell.connect ('toggled',
                                                  self.test_toggled)

        def create_subscription ():
            c = self.authconn
            sub_id = c.createSubscription ("/",
                                           events=["job-created",
                                                   "job-completed",
                                                   "job-stopped",
                                                   "job-progress",
                                                   "job-state-changed"])
            return sub_id

        parent = self.troubleshooter.get_window ()
        self.op = TimedOperation (create_subscription, parent=parent)
        try:
            self.sub_id = self.op.run ()
        except (OperationCanceled, cups.IPPError):
            pass

        try:
            bus = dbus.SystemBus ()
        except:
            bus = None

        self.bus = bus
        if bus:
            bus.add_signal_receiver (self.handle_dbus_signal,
                                     path=DBUS_PATH,
                                     dbus_interface=DBUS_IFACE)

        self.timer = GLib.timeout_add_seconds (1, self.update_jobs_list)

    def disconnect_signals (self):
        if self.bus:
            self.bus.remove_signal_receiver (self.handle_dbus_signal,
                                             path=DBUS_PATH,
                                             dbus_interface=DBUS_IFACE)
                                             
        self.print_button.disconnect (self.print_sigid)
        self.cancel_button.disconnect (self.cancel_sigid)
        self.test_cell.disconnect (self.test_sigid)

        def cancel_subscription (sub_id):
            c = self.authconn
            c.cancelSubscription (sub_id)

        parent = self.troubleshooter.get_window ()
        self.op = TimedOperation (cancel_subscription,
                                  (self.sub_id,),
                                  parent=parent)
        try:
            self.op.run ()
        except (OperationCanceled, cups.IPPError):
            pass

        try:
            del self.sub_seq
        except:
            pass

        GLib.source_remove (self.timer)

    def collect_answer (self):
        if not self.displayed:
            return {}

        self.answers = self.persistent_answers.copy ()
        parent = self.troubleshooter.get_window ()
        success = self.yes.get_active ()
        self.answers['test_page_successful'] = success

        class collect_jobs:
            def __init__ (self, model):
                self.jobs = []
                model.foreach (self.each, None)

            def each (self, model, path, iter, user_data):
                self.jobs.append (model.get (iter, 0, 1, 2, 3, 4))

        model = self.treeview.get_model ()
        jobs = collect_jobs (model).jobs
        def collect_attributes (jobs):
            job_attrs = None
            c = self.authconn
            with_attrs = []
            for (test, jobid, printer, doc, status) in jobs:
                attrs = None
                if test:
                    try:
                        attrs = c.getJobAttributes (jobid)
                    except AttributeError:
                        # getJobAttributes was introduced in pycups 1.9.35.
                        if job_attrs == None:
                            job_attrs = c.getJobs (which_jobs='all')

                        attrs = self.job_attrs[jobid]

                with_attrs.append ((test, jobid, printer, doc, status, attrs))

            return with_attrs

        self.op = TimedOperation (collect_attributes,
                                  (jobs,),
                                  parent=parent)
        try:
            with_attrs = self.op.run ()
            self.answers['test_page_job_status'] = with_attrs
        except (OperationCanceled, cups.IPPError):
            pass

        return self.answers

    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

    def handle_dbus_signal (self, *args):
        debugprint ("D-Bus signal caught: updating jobs list soon")
        GLib.source_remove (self.timer)
        self.timer = GLib.timeout_add (200, self.update_jobs_list)

    def update_job (self, jobid, job_dict):
        iter = self.job_to_iter[jobid]
        model = self.treeview.get_model ()
        try:
            printer_name = job_dict['printer-name']
        except KeyError:
            try:
                uri = job_dict['job-printer-uri']
                r = uri.rfind ('/')
                printer_name = uri[r + 1:]
            except KeyError:
                printer_name = None

        if printer_name != None:
            model.set_value (iter, 2, printer_name)

        model.set_value (iter, 3, job_dict['job-name'])
        model.set_value (iter, 4, self.STATE[job_dict['job-state']])

    def print_clicked (self, widget):
        now = time.time ()
        tt = time.localtime (now)
        when = time.strftime ("%d/%b/%Y:%T %z", tt)
        self.persistent_answers['test_page_attempted'] = when
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window ()

        def print_test_page (*args, **kwargs):
            factory = answers['_authenticated_connection_factory']
            c = factory.get_connection ()
            return c.printTestPage (*args, **kwargs)

        tmpfname = None
        mimetypes = [None, 'text/plain']
        for mimetype in mimetypes:
            try:
                if mimetype == None:
                    # Default test page.
                    self.op = TimedOperation (print_test_page,
                                              (answers['cups_queue'],),
                                              parent=parent)
                    jobid = self.op.run ()
                elif mimetype == 'text/plain':
                    (tmpfd, tmpfname) = tempfile.mkstemp ()
                    os.write (tmpfd, b"This is a test page.\n")
                    os.close (tmpfd)
                    self.op = TimedOperation (print_test_page,
                                              (answers['cups_queue'],),
                                              kwargs={'file': tmpfname,
                                                      'format': mimetype},
                                              parent=parent)
                    jobid = self.op.run ()
                    try:
                        os.unlink (tmpfname)
                    except OSError:
                        pass

                    tmpfname = None

                jobs = self.persistent_answers.get ('test_page_job_id', [])
                jobs.append (jobid)
                self.persistent_answers['test_page_job_id'] = jobs
                break
            except OperationCanceled:
                self.persistent_answers['test_page_submit_failure'] = 'cancel'
                break
            except RuntimeError:
                self.persistent_answers['test_page_submit_failure'] = 'connect'
                break
            except cups.IPPError as e:
                (e, s) = e.args
                if (e == cups.IPP_DOCUMENT_FORMAT and
                    mimetypes.index (mimetype) < (len (mimetypes) - 1)):
                    # Try next format.
                    if tmpfname != None:
                        os.unlink (tmpfname)
                        tmpfname = None
                    continue

                self.persistent_answers['test_page_submit_failure'] = (e, s)
                show_error_dialog (_("Error submitting test page"),
                                   _("There was an error during the CUPS "
                                     "operation: '%s'.") % s,
                                   self.troubleshooter.get_window ())
                break

    def cancel_clicked (self, widget):
        self.persistent_answers['test_page_jobs_cancelled'] = True
        jobids = []
        for jobid, iter in self.job_to_iter.items ():
            jobids.append (jobid)

        def cancel_jobs (jobids):
            c = self.authconn
            for jobid in jobids:
                try:
                    c.cancelJob (jobid)
                except cups.IPPError as e:
                    (e, s) = e.args
                    if e != cups.IPP_NOT_POSSIBLE:
                        self.persistent_answers['test_page_cancel_failure'] = (e, s)

        self.op = TimedOperation (cancel_jobs,
                                  (jobids,),
                                  parent=self.troubleshooter.get_window ())
        try:
            self.op.run ()
        except (OperationCanceled, cups.IPPError):
            pass

    def test_toggled (self, cell, path):
        model = self.treeview.get_model ()
        iter = model.get_iter (path)
        active = model.get_value (iter, 0)
        model.set_value (iter, 0, not active)

    def update_jobs_list (self):
        def get_notifications (self):
            c = self.authconn
            try:
                notifications = c.getNotifications ([self.sub_id],
                                                    [self.sub_seq + 1])
            except AttributeError:
                notifications = c.getNotifications ([self.sub_id])

            return notifications

        # Enter the GDK lock.  We need to do this because we were
        # called from a timeout.
        Gdk.threads_enter ()

        parent = self.troubleshooter.get_window ()
        self.op = TimedOperation (get_notifications,
                                  (self,),
                                  parent=parent)
        try:
            notifications = self.op.run ()
        except (OperationCanceled, cups.IPPError):
            Gdk.threads_leave ()
            return True

        answers = self.troubleshooter.answers
        model = self.treeview.get_model ()
        queue = answers['cups_queue']
        test_jobs = self.persistent_answers.get('test_page_job_id', [])
        for event in notifications['events']:
            seq = event['notify-sequence-number']
            try:
                if seq <= self.sub_seq:
                    # Work around a bug in pycups < 1.9.34
                    continue
            except AttributeError:
                pass
            self.sub_seq = seq
            job = event['notify-job-id']

            nse = event['notify-subscribed-event']
            if nse == 'job-created':
                if (job in test_jobs or
                    event['printer-name'] == queue):
                    iter = model.append (None)
                    self.job_to_iter[job] = iter
                    model.set_value (iter, 0, True)
                    model.set_value (iter, 1, job)
                else:
                    continue
            elif job not in self.job_to_iter:
                continue

            if (job in test_jobs and
                nse in ["job-stopped", "job-completed"]):
                comp = self.persistent_answers.get ('test_page_completions', [])
                comp.append ((job, event['notify-text']))
                self.persistent_answers['test_page_completions'] = comp

            self.update_job (job, event)

        # Update again when we're told to. (But we might update sooner if
        # there is a D-Bus signal.)
        GLib.source_remove (self.timer)
        self.timer = GLib.timeout_add_seconds (
            notifications['notify-get-interval'],
            self.update_jobs_list)
        debugprint ("Update again in %ds" %
                    notifications['notify-get-interval'])
        Gdk.threads_leave ()
        return False
class PrinterFinder:
    def __init__ (self):
        self.quit = False

    def find (self, hostname, callback_fn):
        self.hostname = hostname
        self.callback_fn = callback_fn
        self.op = TimedOperation (self._do_find, callback=lambda x, y: None)

    def cancel (self):
        self.op.cancel ()
        self.quit = True

    def _do_find (self):
        self._cached_attributes = dict()
        for fn in [self._probe_hplip,
                   self._probe_jetdirect,
                   self._probe_ipp,
                   self._probe_snmp,
                   self._probe_lpd,
                   self._probe_smb]:
            if self.quit:
                return

            try:
                fn ()
            except Exception:
                nonfatalException ()

        # Signal that we've finished.
        if not self.quit:
            self.callback_fn (None)

    def _new_device (self, uri, info, location = None):
        device_dict = { 'device-class': 'network',
                        'device-info': "%s" % info }
        if location:
            device_dict['device-location']=location
        device_dict.update (self._cached_attributes)
        new_device = cupshelpers.Device (uri, **device_dict)
        debugprint ("Device found: %s" % uri)
        self.callback_fn (new_device)

    def _probe_snmp (self):
        # Run the CUPS SNMP backend, pointing it at the host.
        try:
            debugprint ("snmp: trying")
            p = subprocess.Popen (args=["/usr/lib/cups/backend/snmp",
                                        self.hostname],
                                  close_fds=True,
                                  stdin=subprocess.DEVNULL,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.DEVNULL)
        except OSError as e:
            debugprint ("snmp: no good")
            if e.errno == errno.ENOENT:
                return

            raise

        (stdout, stderr) = p.communicate ()
        if p.returncode != 0:
            debugprint ("snmp: no good (return code %d)" % p.returncode)
            return

        if self.quit:
            debugprint ("snmp: no good")
            return

        for line in stdout.decode ().split ('\n'):
            words = wordsep (line)
            n = len (words)
            if n == 6:
                (device_class, uri, make_and_model,
                 info, device_id, device_location) = words
            elif n == 5:
                (device_class, uri, make_and_model, info, device_id) = words
            elif n == 4:
                (device_class, uri, make_and_model, info) = words
            else:
                continue

            device_dict = { 'device-class': device_class,
                            'device-make-and-model': make_and_model,
                            'device-info': info }
            if n == 5:
                debugprint ("snmp: Device ID found:\n%s" %
                            device_id)
                device_dict['device-id'] = device_id
            if n == 6:
                device_dict['device-location'] = device_location

            device = cupshelpers.Device (uri, **device_dict)
            debugprint ("Device found: %s" % uri)
            self.callback_fn (device)

            # Cache the make and model for use by other search methods
            # that are not able to determine it.
            self._cached_attributes['device-make-and-model'] = make_and_model
            self._cached_attributes['device_id'] = device_id

        debugprint ("snmp: done")

    def _probe_lpd (self):
        debugprint ("lpd: trying")
        lpd = LpdServer (self.hostname)
        for name in lpd.get_possible_queue_names ():
            if self.quit:
                debugprint ("lpd: no good")
                return

            found = lpd.probe_queue (name, [])
            if found is None:
                # Couldn't even connect.
                debugprint ("lpd: couldn't connect")
                break

            if found:
                uri = "lpd://%s/%s" % (self.hostname, name)
                self._new_device(uri, self.hostname)

            if not found and name.startswith ("pr"):
                break

            time.sleep(0.1) # avoid DOS and following counter measures 

        debugprint ("lpd: done")

    def _probe_hplip (self):
        if ('device-make-and-model' in self._cached_attributes and \
            self._cached_attributes['device-make-and-model'] != "Unknown" and \
            not self._cached_attributes['device-make-and-model'].lower ().startswith ("hp") and \
            not self._cached_attributes['device-make-and-model'].lower ().startswith ("hewlett")):
            debugprint ("hplip: no good (Non-HP printer: %s)" %
                        self._cached_attributes['device-make-and-model'])
            return

        try:
            debugprint ("hplip: trying")
            p = subprocess.Popen (args=["hp-makeuri", "-c", self.hostname],
                                  close_fds=True,
                                  stdin=subprocess.DEVNULL,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.DEVNULL)
        except OSError as e:
            if e.errno == errno.ENOENT:
                return

            raise

        (stdout, stderr) = p.communicate ()
        if p.returncode != 0:
            debugprint ("hplip: no good (return code %d)" % p.returncode)
            return

        if self.quit:
            debugprint ("hplip: no good")
            return

        uri = stdout.decode ().strip ()
        debugprint ("hplip: uri is %s" % uri)
        if uri.find (":") != -1:
            self._new_device(uri, uri)

        debugprint ("hplip: done")

    def _probe_smb (self):
        if not PYSMB_AVAILABLE:
            return

        smbc_auth = BackgroundSmbAuthContext ()
        debug = 0
        if get_debugging ():
            debug = 10
        ctx = pysmb.smbc.Context (debug=debug,
                                  auth_fn=smbc_auth.callback)
        entries = []
        uri = "smb://%s/" % self.hostname
        debugprint ("smb: trying")
        try:
            while smbc_auth.perform_authentication () > 0:
                if self.quit:
                    debugprint ("smb: no good")
                    return

                try:
                    entries = ctx.opendir (uri).getdents ()
                except Exception as e:
                    smbc_auth.failed (e)
        except RuntimeError as e:
            (e, s) = e.args
            if e not in [errno.ENOENT, errno.EACCES, errno.EPERM]:
                debugprint ("Runtime error: %s" % repr ((e, s)))
        except:
            nonfatalException ()

        if self.quit:
            debugprint ("smb: no good")
            return

        for entry in entries:
            if entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
                uri = "smb://%s/%s" % (smburi.urlquote (self.hostname),
                                       smburi.urlquote (entry.name))
                info = "SMB (%s)" % self.hostname
                self._new_device(uri, info)

        debugprint ("smb: done")

    def _probe_jetdirect (self):
        port = 9100    #jetdirect
        sock_address = (self.hostname, port)
        debugprint ("jetdirect: trying")
        s = open_socket(self.hostname, port)
        if not s:
            debugprint ("jetdirect: %s:%d CLOSED" % sock_address)
        else:
            # port is open so assume its a JetDirect device
            debugprint ("jetdirect %s:%d OPEN" % sock_address)
            uri = "socket://%s:%d" % sock_address
            info = "JetDirect (%s)" % self.hostname
            self._new_device(uri, info)
            s.close ()

        debugprint ("jetdirect: done")

    def _probe_ipp (self):
        debugprint ("ipp: trying")
        try:
            ai = socket.getaddrinfo(self.hostname, 631, socket.AF_UNSPEC,
                                    socket.SOCK_STREAM)
        except socket.gaierror:
            debugprint ("ipp: can't resolve %s" % self.hostname)
            debugprint ("ipp: no good")
            return
        for res in ai:
            af, socktype, proto, canonname, sa = res
            if (af == socket.AF_INET and sa[0] == '127.0.0.1' or
                af == socket.AF_INET6 and sa[0] == '::1'):
                debugprint ("ipp: do not probe local cups server")
                debugprint ("ipp: no good")
                return

        try:
            c = cups.Connection (host = self.hostname)
        except RuntimeError:
            debugprint ("ipp: can't connect to server/printer")
            debugprint ("ipp: no good")
            return

        try:
            printers = c.getPrinters ()
        except cups.IPPError:
            debugprint ("%s is probably not a cups server but IPP printer" %
                        self.hostname)
            uri = "ipp://%s:631/ipp" % (self.hostname)
            info = "IPP (%s)" % self.hostname
            self._new_device(uri, info)
            debugprint ("ipp: done")
            return

        for name, queue in printers.items ():
            uri = queue['printer-uri-supported']
            info = queue['printer-info']
            location = queue['printer-location']
            self._new_device(uri, info, location)

        debugprint ("ipp: done")
Exemple #11
0
class ChooseNetworkPrinter(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Choose network printer")
        page1 = self.initial_vbox (_("Choose Network Printer"),
                                   _("Please select the network printer you "
                                     "are trying to use from the list below. "
                                     "If it does not appear in the list, "
                                     "select 'Not listed'."))
        tv = Gtk.TreeView ()
        name = Gtk.TreeViewColumn (_("Name"),
                                   Gtk.CellRendererText (), text=0)
        location = Gtk.TreeViewColumn (_("Location"),
                                       Gtk.CellRendererText (), text=1)
        info = Gtk.TreeViewColumn (_("Information"),
                                   Gtk.CellRendererText (), text=2)
        name.set_property ("resizable", True)
        location.set_property ("resizable", True)
        info.set_property ("resizable", True)
        tv.append_column (name)
        tv.append_column (location)
        tv.append_column (info)
        tv.set_rules_hint (True)
        sw = Gtk.ScrolledWindow ()
        sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type (Gtk.ShadowType.IN)
        sw.add (tv)
        page1.pack_start (sw, True, True, 0)
        self.treeview = tv
        troubleshooter.new_page (page1, self)

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

        if not answers.get ('remote_server_cups', False):
            return False

        server = answers['remote_server_try_connect']

        model = Gtk.ListStore (str,
                               str,
                               str,
                               GObject.TYPE_PYOBJECT)
        self.model = model
        self.treeview.set_model (model)
        iter = model.append (None)
        model.set (iter, 0, _("Not listed"), 1, '', 2, '', 3, 0)

        parent = self.troubleshooter.get_window ()

        try:
            self.op = TimedOperation (cups.Connection, 
                                      kwargs={"host": server},
                                      parent=parent)
            c = self.op.run ()
            self.op = TimedOperation (c.getDests, parent=parent)
            dests = self.op.run ()
            printers = None
            dests_list = []
            for (name, instance), dest in dests.items ():
                if name is None:
                    continue

                if instance is not None:
                    queue = "%s/%s" % (name, instance)
                else:
                    queue = name

                if printers is None:
                    self.op = TimedOperation (c.getPrinters)
                    printers = self.op.run ()

                if name not in printers:
                    info = _("Unknown")
                    location = _("Unknown")
                else:
                    printer = printers[name]
                    info = printer.get('printer-info', _("Unknown"))
                    location = printer.get('printer-location', _("Unknown"))

                dests_list.append ((queue, location, info, dest))

            dests_list.sort (key=lambda x: x[0])
            for queue, location, info, dest in dests_list:
                iter = model.append (None)
                model.set (iter, 0, queue, 1, location, 2, info, 3, dest)

        except cups.HTTPError:
            pass
        except cups.IPPError:
            pass
        except RuntimeError:
            pass

        return True

    def connect_signals (self, handler):
        self.signal_id = self.treeview.connect ("cursor-changed", handler)

    def disconnect_signals (self):
        self.treeview.disconnect (self.signal_id)

    def can_click_forward (self):
        model, iter = self.treeview.get_selection ().get_selected ()
        if iter is None:
            return False
        return True

    def collect_answer (self):
        if not self.troubleshooter.answers.get ('remote_server_cups', False):
            return {}

        model, iter = self.treeview.get_selection ().get_selected ()
        if not model:
            return {}

        dest = model.get_value (iter, 3)
        if dest == 0:
            class enum_dests:
                def __init__ (self, model):
                    self.dests = []
                    model.foreach (self.each, None)

                def each (self, model, path, iter, user_data):
                    dest = model.get_value (iter, 3)
                    if dest:
                        self.dests.append ((dest.name, dest.instance))

            return { 'remote_cups_queue_listed': False,
                     'remote_cups_dests_available': enum_dests (model).dests }
        else:
            return { 'remote_cups_queue_listed': True,
                     'remote_cups_dest': dest,
                     'remote_cups_queue': dest.name,
                     'remote_cups_instance': dest.instance }

    def cancel_operation (self):
        self.op.cancel ()
Exemple #12
0
class ErrorLogFetch(Question):
    def __init__(self, troubleshooter):
        Question.__init__(self, troubleshooter, "Error log fetch")
        troubleshooter.new_page(Gtk.Label(), self)
        self.persistent_answers = {}

    def display(self):
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window()
        self.answers = {}
        checkpoint = answers.get('error_log_checkpoint')
        cursor = answers.get('error_log_cursor')

        if ('error_log' in self.persistent_answers
                or 'journal' in self.persistent_answers):
            checkpoint = None
            cursor = None

        def fetch_log(c):
            prompt = c._get_prompt_allowed()
            c._set_prompt_allowed(False)
            c._connect()
            (tmpfd, tmpfname) = tempfile.mkstemp()
            os.close(tmpfd)
            success = False
            try:
                c.getFile('/admin/log/error_log', tmpfname)
                success = True
            except cups.HTTPError:
                try:
                    os.remove(tmpfname)
                except OSError:
                    pass

            c._set_prompt_allowed(prompt)
            if success:
                return tmpfname
            return None

        self.authconn = self.troubleshooter.answers[
            '_authenticated_connection']
        if 'error_log_debug_logging_set' in answers:
            try:
                self.op = TimedOperation(self.authconn.adminGetServerSettings,
                                         parent=parent)
                settings = self.op.run()
            except cups.IPPError:
                return False

            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
            orig_settings = answers['cups_server_settings']
            settings['MaxLogSize'] = orig_settings.get('MaxLogSize', '2000000')
            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:

                self.op = TimedOperation(set_settings,
                                         (self.authconn, settings),
                                         parent=parent)
                self.op.run()
                self.persistent_answers['error_log_debug_logging_unset'] = True
            except cups.IPPError:
                pass

        self.answers = {}
        if journal and cursor != None:

            def journal_format(x):
                try:
                    priority = "XACEWNIDd"[x['PRIORITY']]
                except (IndexError, TypeError):
                    priority = " "

                return (priority + " " +
                        x['__REALTIME_TIMESTAMP'].strftime("[%m/%b/%Y:%T]") +
                        " " + x['MESSAGE'])

            r = journal.Reader()
            r.seek_cursor(cursor)
            r.add_match(_COMM="cupsd")
            self.answers['journal'] = [journal_format(x) for x in r]

        if checkpoint != None:
            self.op = TimedOperation(fetch_log, (self.authconn, ),
                                     parent=parent)
            tmpfname = self.op.run()
            if tmpfname != None:
                f = open(tmpfname)
                f.seek(checkpoint)
                lines = f.readlines()
                os.remove(tmpfname)
                self.answers = {'error_log': [x.strip() for x in lines]}

        return False

    def collect_answer(self):
        answers = self.persistent_answers.copy()
        answers.update(self.answers)
        return answers

    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
class PrinterStateReasons(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Printer state reasons")
        page = self.initial_vbox (_("Status Messages"),
                                  _("There are status messages associated with "
                                    "this queue."))
        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)

    def display (self):
        troubleshooter = self.troubleshooter
        try:
            queue = troubleshooter.answers['cups_queue']
        except KeyError:
            return False

        parent = self.troubleshooter.get_window ()
        cups.setServer ('')
        self.op = TimedOperation (cups.Connection, parent=parent)
        c = self.op.run ()
        self.op = TimedOperation (c.getPrinterAttributes,
                                  args=(queue,),
                                  parent=parent)
        dict = self.op.run ()

        the_ppdcache = ppdcache.PPDCache ()

        text = ''
        state_message = dict['printer-state-message']
        if state_message:
            text += _("The printer's state message is: '%s'.") % state_message
            text += '\n\n'

        state_reasons_list = dict['printer-state-reasons']
        if type (state_reasons_list) == unicode:
            state_reasons_list = [state_reasons_list]

        self.state_message = state_message
        self.state_reasons = state_reasons_list

        human_readable_errors = []
        human_readable_warnings = []
        for reason in state_reasons_list:
            if reason == "none":
                continue

            r = statereason.StateReason (queue, reason, the_ppdcache)
            (title, description) = r.get_description ()
            level = r.get_level ()
            if level == statereason.StateReason.ERROR:
                human_readable_errors.append (description)
            elif level == statereason.StateReason.WARNING:
                human_readable_warnings.append (description)

        if human_readable_errors:
            text += _("Errors are listed below:") + '\n'
            text += reduce (lambda x, y: x + "\n" + y, human_readable_errors)
            text += '\n\n'

        if human_readable_warnings:
            text += _("Warnings are listed below:") + '\n'
            text += reduce (lambda x, y: x + "\n" + y, human_readable_warnings)

        self.label.set_text (text)
        if (state_message == '' and
            len (human_readable_errors) == 0 and
            len (human_readable_warnings) == 0):
            return False

        # If this screen has been show before, don't show it again if
        # nothing changed.
        if troubleshooter.answers.has_key ('printer-state-message'):
            if (troubleshooter.answers['printer-state-message'] ==
                self.state_message and
                troubleshooter.answers['printer-state-reasons'] ==
                self.state_reasons):
                return False

        return True

    def collect_answer (self):
        if not self.displayed:
            return {}

        return { 'printer-state-message': self.state_message,
                 'printer-state-reasons': self.state_reasons }

    def cancel_operation (self):
        self.op.cancel ()
class PrinterStateReasons(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Printer state reasons")
        page = self.initial_vbox (_("Status Messages"),
                                  _("There are status messages associated with "
                                    "this queue."))
        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)

    def display (self):
        troubleshooter = self.troubleshooter
        try:
            queue = troubleshooter.answers['cups_queue']
        except KeyError:
            return False

        parent = self.troubleshooter.get_window ()
        cups.setServer ('')
        self.op = TimedOperation (cups.Connection, parent=parent)
        c = self.op.run ()
        self.op = TimedOperation (c.getPrinterAttributes,
                                  args=(queue,),
                                  parent=parent)
        dict = self.op.run ()

        the_ppdcache = ppdcache.PPDCache ()

        text = ''
        state_message = dict['printer-state-message']
        if state_message:
            text += _("The printer's state message is: '%s'.") % state_message
            text += '\n\n'

        state_reasons_list = dict['printer-state-reasons']
        if type (state_reasons_list) == str:
            state_reasons_list = [state_reasons_list]

        self.state_message = state_message
        self.state_reasons = state_reasons_list

        human_readable_errors = []
        human_readable_warnings = []
        for reason in state_reasons_list:
            if reason == "none":
                continue

            r = statereason.StateReason (queue, reason, the_ppdcache)
            (title, description) = r.get_description ()
            level = r.get_level ()
            if level == statereason.StateReason.ERROR:
                human_readable_errors.append (description)
            elif level == statereason.StateReason.WARNING:
                human_readable_warnings.append (description)

        if human_readable_errors:
            text += _("Errors are listed below:") + '\n'
            text += reduce (lambda x, y: x + "\n" + y, human_readable_errors)
            text += '\n\n'

        if human_readable_warnings:
            text += _("Warnings are listed below:") + '\n'
            text += reduce (lambda x, y: x + "\n" + y, human_readable_warnings)

        self.label.set_text (text)
        if (state_message == '' and
            len (human_readable_errors) == 0 and
            len (human_readable_warnings) == 0):
            return False

        # If this screen has been show before, don't show it again if
        # nothing changed.
        if 'printer-state-message' in troubleshooter.answers:
            if (troubleshooter.answers['printer-state-message'] ==
                self.state_message and
                troubleshooter.answers['printer-state-reasons'] ==
                self.state_reasons):
                return False

        return True

    def collect_answer (self):
        if not self.displayed:
            return {}

        return { 'printer-state-message': self.state_message,
                 'printer-state-reasons': self.state_reasons }

    def cancel_operation (self):
        self.op.cancel ()
class ErrorLogFetch(Question):
    def __init__(self, troubleshooter):
        Question.__init__(self, troubleshooter, "Error log fetch")
        troubleshooter.new_page(Gtk.Label(), self)
        self.persistent_answers = {}

    def display(self):
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window()
        self.answers = {}
        try:
            checkpoint = answers['error_log_checkpoint']
        except KeyError:
            checkpoint = None

        if self.persistent_answers.has_key('error_log'):
            checkpoint = None

        def fetch_log(c):
            prompt = c._get_prompt_allowed()
            c._set_prompt_allowed(False)
            c._connect()
            (tmpfd, tmpfname) = tempfile.mkstemp()
            os.close(tmpfd)
            success = False
            try:
                c.getFile('/admin/log/error_log', tmpfname)
                success = True
            except cups.HTTPError:
                try:
                    os.remove(tmpfname)
                except OSError:
                    pass

            c._set_prompt_allowed(prompt)
            if success:
                return tmpfname
            return None

        self.authconn = self.troubleshooter.answers[
            '_authenticated_connection']
        if answers.has_key('error_log_debug_logging_set'):
            try:
                self.op = TimedOperation(self.authconn.adminGetServerSettings,
                                         parent=parent)
                settings = self.op.run()
            except cups.IPPError:
                return False

            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
            orig_settings = answers['cups_server_settings']
            settings['MaxLogSize'] = orig_settings.get('MaxLogSize', '2000000')
            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:

                self.op = TimedOperation(set_settings,
                                         (self.authconn, settings),
                                         parent=parent)
                self.op.run()
                self.persistent_answers['error_log_debug_logging_unset'] = True
            except cups.IPPError:
                pass

        if checkpoint != None:
            self.op = TimedOperation(fetch_log, (self.authconn, ),
                                     parent=parent)
            tmpfname = self.op.run()
            if tmpfname != None:
                f = file(tmpfname)
                f.seek(checkpoint)
                lines = f.readlines()
                os.remove(tmpfname)
                self.answers = {'error_log': map(lambda x: x.strip(), lines)}

        return False

    def collect_answer(self):
        answers = self.persistent_answers.copy()
        answers.update(self.answers)
        return answers

    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
class PrintTestPage(Question):
    STATE = {
        cups.IPP_JOB_PENDING: _("Pending"),
        cups.IPP_JOB_HELD: _("Held"),
        cups.IPP_JOB_PROCESSING: _("Processing"),
        cups.IPP_JOB_STOPPED: _("Stopped"),
        cups.IPP_JOB_CANCELED: _("Canceled"),
        cups.IPP_JOB_ABORTED: _("Aborted"),
        cups.IPP_JOB_COMPLETED: _("Completed")
    }

    def __init__(self, troubleshooter):
        Question.__init__(self, troubleshooter, "Print test page")
        page = Gtk.VBox()
        page.set_spacing(12)
        page.set_border_width(12)

        label = Gtk.Label()
        label.set_alignment(0, 0)
        label.set_use_markup(True)
        label.set_line_wrap(True)
        page.pack_start(label, False, False, 0)
        self.main_label = label
        self.main_label_text = ('<span weight="bold" size="larger">' +
                                _("Test Page") + '</span>\n\n' +
                                _("Now print a test page.  If you are having "
                                  "problems printing a specific document, "
                                  "print that document now and mark the print "
                                  "job below."))

        hbox = Gtk.HButtonBox()
        hbox.set_border_width(0)
        hbox.set_spacing(3)
        hbox.set_layout(Gtk.ButtonBoxStyle.START)
        self.print_button = Gtk.Button.new_with_label(_("Print Test Page"))
        hbox.pack_start(self.print_button, False, False, 0)

        self.cancel_button = Gtk.Button.new_with_label(_("Cancel All Jobs"))
        hbox.pack_start(self.cancel_button, False, False, 0)
        page.pack_start(hbox, False, False, 0)

        tv = Gtk.TreeView()
        test_cell = Gtk.CellRendererToggle()
        test = Gtk.TreeViewColumn(_("Test"), test_cell, active=0)
        job = Gtk.TreeViewColumn(_("Job"), Gtk.CellRendererText(), text=1)
        printer_cell = Gtk.CellRendererText()
        printer = Gtk.TreeViewColumn(_("Printer"), printer_cell, text=2)
        name_cell = Gtk.CellRendererText()
        name = Gtk.TreeViewColumn(_("Document"), name_cell, text=3)
        status = Gtk.TreeViewColumn(_("Status"),
                                    Gtk.CellRendererText(),
                                    text=4)
        test_cell.set_radio(False)
        self.test_cell = test_cell
        printer.set_resizable(True)
        printer_cell.set_property("ellipsize", Pango.EllipsizeMode.END)
        printer_cell.set_property("width-chars", 20)
        name.set_resizable(True)
        name_cell.set_property("ellipsize", Pango.EllipsizeMode.END)
        name_cell.set_property("width-chars", 20)
        status.set_resizable(True)
        tv.append_column(test)
        tv.append_column(job)
        tv.append_column(printer)
        tv.append_column(name)
        tv.append_column(status)
        tv.set_rules_hint(True)
        sw = Gtk.ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.add(tv)
        self.treeview = tv
        page.pack_start(sw, False, False, 0)

        label = Gtk.Label(
            label=_("Did the marked print jobs print correctly?"))
        label.set_line_wrap(True)
        label.set_alignment(0, 0)
        page.pack_start(label, False, False, 0)

        vbox = Gtk.VBox()
        vbox.set_spacing(6)
        self.yes = Gtk.RadioButton(label=_("Yes"))
        no = Gtk.RadioButton.new_with_label_from_widget(self.yes, _("No"))
        vbox.pack_start(self.yes, False, False, 0)
        vbox.pack_start(no, False, False, 0)
        page.pack_start(vbox, False, False, 0)
        self.persistent_answers = {}
        troubleshooter.new_page(page, self)

    def display(self):
        answers = self.troubleshooter.answers
        if 'cups_queue' not in answers:
            return False

        parent = self.troubleshooter.get_window()
        self.authconn = answers['_authenticated_connection']
        mediatype = None
        defaults = answers.get('cups_printer_ppd_defaults', {})
        for opts in defaults.values():
            for opt, value in opts.items():
                if opt == "MediaType":
                    mediatype = value
                    break

        if mediatype != None:
            mediatype_string = '\n\n' + (_("Remember to load paper of type "
                                           "'%s' into the printer first.") %
                                         mediatype)
        else:
            mediatype_string = ""

        label_text = self.main_label_text + mediatype_string
        self.main_label.set_markup(label_text)

        model = Gtk.ListStore(bool, int, str, str, str)
        self.treeview.set_model(model)
        self.job_to_iter = {}

        test_jobs = self.persistent_answers.get('test_page_job_id', [])

        def get_jobs():
            c = self.authconn
            try:
                r = [
                    "job-id", "job-name", "job-state", "job-printer-uri",
                    "printer-name"
                ]
                jobs_dict = c.getJobs(which_jobs='not-completed',
                                      my_jobs=False,
                                      requested_attributes=r)
                completed_jobs_dict = c.getJobs(which_jobs='completed',
                                                requested_attributes=r)
            except TypeError:
                # requested_attributes requires pycups 1.9.50
                jobs_dict = c.getJobs(which_jobs='not-completed',
                                      my_jobs=False)
                completed_jobs_dict = c.getJobs(which_jobs='completed')
            return (jobs_dict, completed_jobs_dict)

        self.op = TimedOperation(get_jobs, parent=parent)
        try:
            (jobs_dict, completed_jobs_dict) = self.op.run()
        except (OperationCanceled, cups.IPPError):
            return False

        # We want to display the jobs in the queue for this printer...
        try:
            queue_uri_ending = "/" + self.troubleshooter.answers['cups_queue']
            jobs_on_this_printer = [x for x in jobs_dict.keys () if \
                                            jobs_dict[x]['job-printer-uri']. \
                                            endswith (queue_uri_ending)]
        except:
            jobs_on_this_printer = []

        # ...as well as any other jobs we've previous submitted as test pages.
        jobs = list(set(test_jobs).union(set(jobs_on_this_printer)))

        completed_jobs_dict = None
        for job in jobs:
            try:
                j = jobs_dict[job]
            except KeyError:
                try:
                    j = completed_jobs_dict[job]
                except KeyError:
                    continue

            iter = model.append(None)
            self.job_to_iter[job] = iter
            model.set_value(iter, 0, job in test_jobs)
            model.set_value(iter, 1, job)
            self.update_job(job, j)

        return True

    def connect_signals(self, handler):
        self.print_sigid = self.print_button.connect("clicked",
                                                     self.print_clicked)
        self.cancel_sigid = self.cancel_button.connect("clicked",
                                                       self.cancel_clicked)
        self.test_sigid = self.test_cell.connect('toggled', self.test_toggled)

        def create_subscription():
            c = self.authconn
            sub_id = c.createSubscription("/",
                                          events=[
                                              "job-created", "job-completed",
                                              "job-stopped", "job-progress",
                                              "job-state-changed"
                                          ])
            return sub_id

        parent = self.troubleshooter.get_window()
        self.op = TimedOperation(create_subscription, parent=parent)
        try:
            self.sub_id = self.op.run()
        except (OperationCanceled, cups.IPPError):
            pass

        try:
            bus = dbus.SystemBus()
        except:
            bus = None

        self.bus = bus
        if bus:
            bus.add_signal_receiver(self.handle_dbus_signal,
                                    path=DBUS_PATH,
                                    dbus_interface=DBUS_IFACE)

        self.timer = GLib.timeout_add_seconds(1, self.update_jobs_list)

    def disconnect_signals(self):
        if self.bus:
            self.bus.remove_signal_receiver(self.handle_dbus_signal,
                                            path=DBUS_PATH,
                                            dbus_interface=DBUS_IFACE)

        self.print_button.disconnect(self.print_sigid)
        self.cancel_button.disconnect(self.cancel_sigid)
        self.test_cell.disconnect(self.test_sigid)

        def cancel_subscription(sub_id):
            c = self.authconn
            c.cancelSubscription(sub_id)

        parent = self.troubleshooter.get_window()
        self.op = TimedOperation(cancel_subscription, (self.sub_id, ),
                                 parent=parent)
        try:
            self.op.run()
        except (OperationCanceled, cups.IPPError):
            pass

        try:
            del self.sub_seq
        except:
            pass

        GLib.source_remove(self.timer)

    def collect_answer(self):
        if not self.displayed:
            return {}

        self.answers = self.persistent_answers.copy()
        parent = self.troubleshooter.get_window()
        success = self.yes.get_active()
        self.answers['test_page_successful'] = success

        class collect_jobs:
            def __init__(self, model):
                self.jobs = []
                model.foreach(self.each, None)

            def each(self, model, path, iter, user_data):
                self.jobs.append(model.get(iter, 0, 1, 2, 3, 4))

        model = self.treeview.get_model()
        jobs = collect_jobs(model).jobs

        def collect_attributes(jobs):
            job_attrs = None
            c = self.authconn
            with_attrs = []
            for (test, jobid, printer, doc, status) in jobs:
                attrs = None
                if test:
                    try:
                        attrs = c.getJobAttributes(jobid)
                    except AttributeError:
                        # getJobAttributes was introduced in pycups 1.9.35.
                        if job_attrs == None:
                            job_attrs = c.getJobs(which_jobs='all')

                        attrs = self.job_attrs[jobid]

                with_attrs.append((test, jobid, printer, doc, status, attrs))

            return with_attrs

        self.op = TimedOperation(collect_attributes, (jobs, ), parent=parent)
        try:
            with_attrs = self.op.run()
            self.answers['test_page_job_status'] = with_attrs
        except (OperationCanceled, cups.IPPError):
            pass

        return self.answers

    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

    def handle_dbus_signal(self, *args):
        debugprint("D-Bus signal caught: updating jobs list soon")
        GLib.source_remove(self.timer)
        self.timer = GLib.timeout_add(200, self.update_jobs_list)

    def update_job(self, jobid, job_dict):
        iter = self.job_to_iter[jobid]
        model = self.treeview.get_model()
        try:
            printer_name = job_dict['printer-name']
        except KeyError:
            try:
                uri = job_dict['job-printer-uri']
                r = uri.rfind('/')
                printer_name = uri[r + 1:]
            except KeyError:
                printer_name = None

        if printer_name != None:
            model.set_value(iter, 2, printer_name)

        model.set_value(iter, 3, job_dict['job-name'])
        model.set_value(iter, 4, self.STATE[job_dict['job-state']])

    def print_clicked(self, widget):
        now = time.time()
        tt = time.localtime(now)
        when = time.strftime("%d/%b/%Y:%T %z", tt)
        self.persistent_answers['test_page_attempted'] = when
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window()

        def print_test_page(*args, **kwargs):
            factory = answers['_authenticated_connection_factory']
            c = factory.get_connection()
            return c.printTestPage(*args, **kwargs)

        tmpfname = None
        mimetypes = [None, 'text/plain']
        for mimetype in mimetypes:
            try:
                if mimetype == None:
                    # Default test page.
                    self.op = TimedOperation(print_test_page,
                                             (answers['cups_queue'], ),
                                             parent=parent)
                    jobid = self.op.run()
                elif mimetype == 'text/plain':
                    (tmpfd, tmpfname) = tempfile.mkstemp()
                    os.write(tmpfd, b"This is a test page.\n")
                    os.close(tmpfd)
                    self.op = TimedOperation(print_test_page,
                                             (answers['cups_queue'], ),
                                             kwargs={
                                                 'file': tmpfname,
                                                 'format': mimetype
                                             },
                                             parent=parent)
                    jobid = self.op.run()
                    try:
                        os.unlink(tmpfname)
                    except OSError:
                        pass

                    tmpfname = None

                jobs = self.persistent_answers.get('test_page_job_id', [])
                jobs.append(jobid)
                self.persistent_answers['test_page_job_id'] = jobs
                break
            except OperationCanceled:
                self.persistent_answers['test_page_submit_failure'] = 'cancel'
                break
            except RuntimeError:
                self.persistent_answers['test_page_submit_failure'] = 'connect'
                break
            except cups.IPPError as e:
                (e, s) = e.args
                if (e == cups.IPP_DOCUMENT_FORMAT
                        and mimetypes.index(mimetype) < (len(mimetypes) - 1)):
                    # Try next format.
                    if tmpfname != None:
                        os.unlink(tmpfname)
                        tmpfname = None
                    continue

                self.persistent_answers['test_page_submit_failure'] = (e, s)
                show_error_dialog(
                    _("Error submitting test page"),
                    _("There was an error during the CUPS "
                      "operation: '%s'.") % s,
                    self.troubleshooter.get_window())
                break

    def cancel_clicked(self, widget):
        self.persistent_answers['test_page_jobs_cancelled'] = True
        jobids = []
        for jobid, iter in self.job_to_iter.items():
            jobids.append(jobid)

        def cancel_jobs(jobids):
            c = self.authconn
            for jobid in jobids:
                try:
                    c.cancelJob(jobid)
                except cups.IPPError as e:
                    (e, s) = e.args
                    if e != cups.IPP_NOT_POSSIBLE:
                        self.persistent_answers['test_page_cancel_failure'] = (
                            e, s)

        self.op = TimedOperation(cancel_jobs, (jobids, ),
                                 parent=self.troubleshooter.get_window())
        try:
            self.op.run()
        except (OperationCanceled, cups.IPPError):
            pass

    def test_toggled(self, cell, path):
        model = self.treeview.get_model()
        iter = model.get_iter(path)
        active = model.get_value(iter, 0)
        model.set_value(iter, 0, not active)

    def update_jobs_list(self):
        def get_notifications(self):
            c = self.authconn
            try:
                notifications = c.getNotifications([self.sub_id],
                                                   [self.sub_seq + 1])
            except AttributeError:
                notifications = c.getNotifications([self.sub_id])

            return notifications

        # Enter the GDK lock.  We need to do this because we were
        # called from a timeout.
        Gdk.threads_enter()

        parent = self.troubleshooter.get_window()
        self.op = TimedOperation(get_notifications, (self, ), parent=parent)
        try:
            notifications = self.op.run()
        except (OperationCanceled, cups.IPPError):
            Gdk.threads_leave()
            return True

        answers = self.troubleshooter.answers
        model = self.treeview.get_model()
        queue = answers['cups_queue']
        test_jobs = self.persistent_answers.get('test_page_job_id', [])
        for event in notifications['events']:
            seq = event['notify-sequence-number']
            try:
                if seq <= self.sub_seq:
                    # Work around a bug in pycups < 1.9.34
                    continue
            except AttributeError:
                pass
            self.sub_seq = seq
            job = event['notify-job-id']

            nse = event['notify-subscribed-event']
            if nse == 'job-created':
                if (job in test_jobs or event['printer-name'] == queue):
                    iter = model.append(None)
                    self.job_to_iter[job] = iter
                    model.set_value(iter, 0, True)
                    model.set_value(iter, 1, job)
                else:
                    continue
            elif job not in self.job_to_iter:
                continue

            if (job in test_jobs and nse in ["job-stopped", "job-completed"]):
                comp = self.persistent_answers.get('test_page_completions', [])
                comp.append((job, event['notify-text']))
                self.persistent_answers['test_page_completions'] = comp

            self.update_job(job, event)

        # Update again when we're told to. (But we might update sooner if
        # there is a D-Bus signal.)
        GLib.source_remove(self.timer)
        self.timer = GLib.timeout_add_seconds(
            notifications['notify-get-interval'], self.update_jobs_list)
        debugprint("Update again in %ds" %
                   notifications['notify-get-interval'])
        Gdk.threads_leave()
        return False
class ErrorLogFetch(Question):
    def __init__(self, troubleshooter):
        Question.__init__(self, troubleshooter, "Error log fetch")
        page = self.initial_vbox(
            _("Retrieve Journal Entries"),
            _("No system journal entries were found. "
              "This may be because you are not an "
              "administrator. To fetch journal entries "
              "please run this command:"))
        self.entry = Gtk.Entry()
        self.entry.set_editable(False)
        page.pack_start(self.entry, False, False, 0)
        troubleshooter.new_page(page, self)
        self.persistent_answers = {}

    def display(self):
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window()
        self.answers = {}
        checkpoint = answers.get('error_log_checkpoint')
        cursor = answers.get('error_log_cursor')
        timestamp = answers.get('error_log_timestamp')

        if (self.persistent_answers.has_key('error_log')
                or self.persistent_answers.has_key('journal')):
            checkpoint = None
            cursor = None

        def fetch_log(c):
            prompt = c._get_prompt_allowed()
            c._set_prompt_allowed(False)
            c._connect()
            (tmpfd, tmpfname) = tempfile.mkstemp()
            os.close(tmpfd)
            success = False
            try:
                c.getFile('/admin/log/error_log', tmpfname)
                success = True
            except cups.HTTPError:
                try:
                    os.remove(tmpfname)
                except OSError:
                    pass

            c._set_prompt_allowed(prompt)
            if success:
                return tmpfname
            return None

        now = datetime.datetime.fromtimestamp(time.time()).strftime("%F %T")
        self.authconn = self.troubleshooter.answers[
            '_authenticated_connection']
        if answers.has_key('error_log_debug_logging_set'):
            try:
                self.op = TimedOperation(self.authconn.adminGetServerSettings,
                                         parent=parent)
                settings = self.op.run()
            except cups.IPPError:
                return False

            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
            orig_settings = answers['cups_server_settings']
            settings['MaxLogSize'] = orig_settings.get('MaxLogSize', '2000000')
            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:

                self.op = TimedOperation(set_settings,
                                         (self.authconn, settings),
                                         parent=parent)
                self.op.run()
                self.persistent_answers['error_log_debug_logging_unset'] = True
            except cups.IPPError:
                pass

        self.answers = {}
        if journal and cursor != None:

            def journal_format(x):
                try:
                    priority = "XACEWNIDd"[x['PRIORITY']]
                except (IndexError, TypeError):
                    priority = " "

                return (priority + " " +
                        x['__REALTIME_TIMESTAMP'].strftime("[%m/%b/%Y:%T]") +
                        " " + x['MESSAGE'])

            r = journal.Reader()
            r.seek_cursor(cursor)
            r.add_match(_COMM="cupsd")
            self.answers['journal'] = map(journal_format, r)

        if checkpoint != None:
            self.op = TimedOperation(fetch_log, (self.authconn, ),
                                     parent=parent)
            tmpfname = self.op.run()
            if tmpfname != None:
                f = file(tmpfname)
                f.seek(checkpoint)
                lines = f.readlines()
                os.remove(tmpfname)
                self.answers['error_log'] = map(lambda x: x.strip(), lines)

        if (len(self.answers.get('journal', [])) +
                len(self.answers.get('error_log', []))) == 0:
            cmd = ("su -c 'journalctl _COMM=cupsd "
                   "--since=\"%s\" --until=\"%s\"' > troubleshoot-logs.txt" %
                   (answers['error_log_timestamp'], now))
            self.entry.set_text(cmd)
            return True

        return False

    def collect_answer(self):
        answers = self.persistent_answers.copy()
        answers.update(self.answers)
        return answers

    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
class ErrorLogFetch(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Error log fetch")
        page = self.initial_vbox (_("Retrieve Journal Entries"),
                                  _("No system journal entries were found. "
                                    "This may be because you are not an "
                                    "administrator. To fetch journal entries "
                                    "please run this command:"))
        self.entry = Gtk.Entry ()
        self.entry.set_editable (False)
        page.pack_start (self.entry, False, False, 0)
        troubleshooter.new_page (page, self)
        self.persistent_answers = {}

    def display (self):
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window ()
        self.answers = {}
        checkpoint = answers.get ('error_log_checkpoint')
        cursor = answers.get ('error_log_cursor')
        timestamp = answers.get ('error_log_timestamp')

        if ('error_log' in self.persistent_answers or
            'journal' in self.persistent_answers):
            checkpoint = None
            cursor = None

        def fetch_log (c):
            prompt = c._get_prompt_allowed ()
            c._set_prompt_allowed (False)
            c._connect ()
            with tempfile.NamedTemporaryFile (delete=False) as tmpf:
                success = False
                try:
                    c.getFile ('/admin/log/error_log', tmpf.file)
                    success = True
                except cups.HTTPError:
                    try:
                        os.remove (tmpf.file)
                    except OSError:
                        pass

                c._set_prompt_allowed (prompt)
                if success:
                    return tmpf.file

            return None

        now = datetime.datetime.fromtimestamp (time.time ()).strftime ("%F %T")
        self.authconn = self.troubleshooter.answers['_authenticated_connection']
        if 'error_log_debug_logging_set' in answers:
            try:
                self.op = TimedOperation (self.authconn.adminGetServerSettings,
                                          parent=parent)
                settings = self.op.run ()
            except cups.IPPError:
                return False

            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
            orig_settings = answers['cups_server_settings']
            settings['MaxLogSize'] = orig_settings.get ('MaxLogSize', '2000000')
            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:

                self.op = TimedOperation (set_settings,
                                          (self.authconn, settings),
                                          parent=parent)
                self.op.run ()
                self.persistent_answers['error_log_debug_logging_unset'] = True
            except cups.IPPError:
                pass

        self.answers = {}
        if journal and cursor != None:
            def journal_format (x):
                try:
                    priority = "XACEWNIDd"[x['PRIORITY']]
                except (IndexError, TypeError):
                    priority = " "

                return (priority + " " +
                        x['__REALTIME_TIMESTAMP'].strftime("[%m/%b/%Y:%T]") +
                        " " + x['MESSAGE'])

            r = journal.Reader ()
            r.seek_cursor (cursor)
            r.add_match (_SYSTEMD_UNIT="cups.service")
            self.answers['journal'] = [journal_format (x) for x in r]

        if checkpoint != None:
            self.op = TimedOperation (fetch_log,
                                      (self.authconn,),
                                      parent=parent)
            tmpfname = self.op.run ()
            if tmpfname != None:
                f = open (tmpfname)
                f.seek (checkpoint)
                lines = f.readlines ()
                os.remove (tmpfname)
                self.answers = { 'error_log': [x.strip () for x in lines] }

        if (len (self.answers.get ('journal', [])) +
            len (self.answers.get ('error_log', []))) == 0:
            cmd = ("su -c 'journalctl -u cups.service "
                   "--since=\"%s\" --until=\"%s\"' > troubleshoot-logs.txt" %
                   (answers['error_log_timestamp'], now))
            self.entry.set_text (cmd)
            return True

        return False

    def collect_answer (self):
        answers = self.persistent_answers.copy ()
        answers.update (self.answers)
        return answers

    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
class PrinterFinder:
    def __init__(self):
        self.quit = False

    def find(self, hostname, callback_fn):
        self.hostname = hostname
        self.callback_fn = callback_fn
        self.op = TimedOperation(self._do_find, callback=lambda x, y: None)

    def cancel(self):
        self.op.cancel()
        self.quit = True

    def _do_find(self):
        self._cached_attributes = dict()
        for fn in [
                self._probe_jetdirect, self._probe_ipp, self._probe_snmp,
                self._probe_lpd, self._probe_hplip, self._probe_smb
        ]:
            if self.quit:
                return

            try:
                fn()
            except Exception:
                nonfatalException()

        # Signal that we've finished.
        if not self.quit:
            self.callback_fn(None)

    def _new_device(self, uri, info, location=None):
        device_dict = {'device-class': 'network', 'device-info': "%s" % info}
        if location:
            device_dict['device-location'] = location
        device_dict.update(self._cached_attributes)
        new_device = cupshelpers.Device(uri, **device_dict)
        debugprint("Device found: %s" % uri)
        self.callback_fn(new_device)

    def _probe_snmp(self):
        # Run the CUPS SNMP backend, pointing it at the host.
        null = file("/dev/null", "r+")
        try:
            debugprint("snmp: trying")
            p = subprocess.Popen(
                args=["/usr/lib/cups/backend/snmp", self.hostname],
                close_fds=True,
                stdin=null,
                stdout=subprocess.PIPE,
                stderr=null)
        except OSError as e:
            debugprint("snmp: no good")
            if e == errno.ENOENT:
                return

            raise

        (stdout, stderr) = p.communicate()
        if p.returncode != 0:
            debugprint("snmp: no good (return code %d)" % p.returncode)
            return

        if self.quit:
            debugprint("snmp: no good")
            return

        for line in stdout.split('\n'):
            words = wordsep(line)
            n = len(words)
            if n == 6:
                (device_class, uri, make_and_model, info, device_id,
                 device_location) = words
            elif n == 5:
                (device_class, uri, make_and_model, info, device_id) = words
            elif n == 4:
                (device_class, uri, make_and_model, info) = words
            else:
                continue

            device_dict = {
                'device-class': device_class,
                'device-make-and-model': make_and_model,
                'device-info': info
            }
            if n == 5:
                debugprint("snmp: Device ID found:\n%s" % device_id)
                device_dict['device-id'] = device_id
            if n == 6:
                device_dict['device-location'] = device_location

            device = cupshelpers.Device(uri, **device_dict)
            debugprint("Device found: %s" % uri)
            self.callback_fn(device)

            # Cache the make and model for use by other search methods
            # that are not able to determine it.
            self._cached_attributes['device-make-and-model'] = make_and_model
            self._cached_attributes['device_id'] = device_id

        debugprint("snmp: done")

    def _probe_lpd(self):
        debugprint("lpd: trying")
        lpd = LpdServer(self.hostname)
        for name in lpd.get_possible_queue_names():
            if self.quit:
                debugprint("lpd: no good")
                return

            found = lpd.probe_queue(name, [])
            if found == None:
                # Couldn't even connect.
                debugprint("lpd: couldn't connect")
                break

            if found:
                uri = "lpd://%s/%s" % (self.hostname, name)
                self._new_device(uri, self.hostname)

            if not found and name.startswith("pr"):
                break

            time.sleep(0.1)  # avoid DOS and following counter measures

        debugprint("lpd: done")

    def _probe_hplip(self):
        if (self._cached_attributes.has_key ('device-make-and-model') and \
            self._cached_attributes['device-make-and-model'] != "Unknown" and \
            not self._cached_attributes['device-make-and-model'].lower ().startswith ("hp") and \
            not self._cached_attributes['device-make-and-model'].lower ().startswith ("hewlett")):
            debugprint("hplip: no good (Non-HP printer: %s)" %
                       self._cached_attributes['device-make-and-model'])
            return

        null = file("/dev/null", "r+")
        try:
            debugprint("hplip: trying")
            p = subprocess.Popen(args=["hp-makeuri", "-c", self.hostname],
                                 close_fds=True,
                                 stdin=null,
                                 stdout=subprocess.PIPE,
                                 stderr=null)
        except OSError as e:
            if e == errno.ENOENT:
                return

            raise

        (stdout, stderr) = p.communicate()
        if p.returncode != 0:
            debugprint("hplip: no good (return code %d)" % p.returncode)
            return

        if self.quit:
            debugprint("hplip: no good")
            return

        uri = stdout.strip()
        debugprint("hplip: uri is %s" % uri)
        if uri.find(":") != -1:
            self._new_device(uri, uri)

        debugprint("hplip: done")

    def _probe_smb(self):
        if not PYSMB_AVAILABLE:
            return

        smbc_auth = BackgroundSmbAuthContext()
        debug = 0
        if get_debugging():
            debug = 10
        ctx = pysmb.smbc.Context(debug=debug, auth_fn=smbc_auth.callback)
        entries = []
        uri = "smb://%s/" % self.hostname
        debugprint("smb: trying")
        try:
            while smbc_auth.perform_authentication() > 0:
                if self.quit:
                    debugprint("smb: no good")
                    return

                try:
                    entries = ctx.opendir(uri).getdents()
                except Exception as e:
                    smbc_auth.failed(e)
        except RuntimeError as e:
            (e, s) = e.args
            if e not in [errno.ENOENT, errno.EACCES, errno.EPERM]:
                debugprint("Runtime error: %s" % repr((e, s)))
        except:
            nonfatalException()

        if self.quit:
            debugprint("smb: no good")
            return

        for entry in entries:
            if entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
                uri = "smb://%s/%s" % (smburi.urlquote(
                    self.hostname), smburi.urlquote(entry.name))
                info = "SMB (%s)" % self.hostname
                self._new_device(uri, info)

        debugprint("smb: done")

    def _probe_jetdirect(self):
        port = 9100  #jetdirect
        sock_address = (self.hostname, port)
        debugprint("jetdirect: trying")
        s = open_socket(self.hostname, port)
        if not s:
            debugprint("jetdirect: %s:%d CLOSED" % sock_address)
        else:
            # port is open so assume its a JetDirect device
            debugprint("jetdirect %s:%d OPEN" % sock_address)
            uri = "socket://%s:%d" % sock_address
            info = "JetDirect (%s)" % self.hostname
            self._new_device(uri, info)
            s.close()

        debugprint("jetdirect: done")

    def _probe_ipp(self):
        debugprint("ipp: trying")
        try:
            ai = socket.getaddrinfo(self.hostname, 631, socket.AF_UNSPEC,
                                    socket.SOCK_STREAM)
        except socket.gaierror:
            debugprint("ipp: can't resolve %s" % self.hostname)
            debugprint("ipp: no good")
            return
        for res in ai:
            af, socktype, proto, canonname, sa = res
            if (af == socket.AF_INET and sa[0] == '127.0.0.1'
                    or af == socket.AF_INET6 and sa[0] == '::1'):
                debugprint("ipp: do not probe local cups server")
                debugprint("ipp: no good")
                return

        try:
            c = cups.Connection(host=self.hostname)
        except RuntimeError:
            debugprint("ipp: can't connect to server/printer")
            debugprint("ipp: no good")
            return

        try:
            printers = c.getPrinters()
        except cups.IPPError:
            debugprint("%s is probably not a cups server but IPP printer" %
                       self.hostname)
            uri = "ipp://%s:631/ipp" % (self.hostname)
            info = "IPP (%s)" % self.hostname
            self._new_device(uri, info)
            debugprint("ipp: done")
            return

        for name, queue in printers.iteritems():
            uri = queue['printer-uri-supported']
            info = queue['printer-info']
            location = queue['printer-location']
            self._new_device(uri, info, location)

        debugprint("ipp: done")
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 (_("Enable Debugging"))
        buttonbox = gtk.HButtonBox ()
        buttonbox.set_border_width (0)
        buttonbox.set_layout (gtk.BUTTONBOX_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

        c = self.troubleshooter.answers['_authenticated_connection']
        c._set_lock (False)
        settings = c.adminGetServerSettings ()
        if len (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 (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 self.answers.has_key ('error_log_checkpoint'):
            return self.answers

        (tmpfd, tmpfname) = tempfile.mkstemp ()
        os.close (tmpfd)
        try:
            self.op = TimedOperation (self.authconn.getFile,
                                      args=('/admin/log/error_log', tmpfname),
                                      parent=parent)
            self.op.run ()
        except RuntimeError:
            try:
                os.remove (tmpfname)
            except OSError:
                pass

            return self.answers
        except cups.IPPError:
            try:
                os.remove (tmpfname)
            except OSError:
                pass

            return self.answers

        statbuf = os.stat (tmpfname)
        os.remove (tmpfname)
        self.answers['error_log_checkpoint'] = statbuf[6]
        self.persistent_answers['error_log_checkpoint'] = statbuf[6]
        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
class DeviceListed(Question):
    def __init__ (self, troubleshooter):
        # Is the device listed?
        Question.__init__ (self, troubleshooter, "Choose device")
        page1 = self.initial_vbox (_("Choose Device"),
                                   _("Please select the device you want "
                                     "to use from the list below. "
                                     "If it does not appear in the list, "
                                     "select 'Not listed'."))
        tv = Gtk.TreeView ()
        name = Gtk.TreeViewColumn (_("Name"),
                                   Gtk.CellRendererText (), text=0)
        info = Gtk.TreeViewColumn (_("Information"),
                                   Gtk.CellRendererText (), text=1)
        uri = Gtk.TreeViewColumn (_("Device URI"),
                                  Gtk.CellRendererText (), text=2)
        name.set_property ("resizable", True)
        info.set_property ("resizable", True)
        uri.set_property ("resizable", True)
        tv.append_column (name)
        tv.append_column (info)
        tv.append_column (uri)
        tv.set_rules_hint (True)
        sw = Gtk.ScrolledWindow ()
        sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type (Gtk.ShadowType.IN)
        sw.add (tv)
        page1.pack_start (sw, True, True, 0)
        self.treeview = tv
        troubleshooter.new_page (page1, self)

    def display (self):
        self.answers = {}
        answers = self.troubleshooter.answers
        if (answers['printer_is_remote'] or
            answers.get ('cups_printer_remote', False)):
            return False

        model = Gtk.ListStore (str,
                               str,
                               str,
                               GObject.TYPE_PYOBJECT)
        self.treeview.set_model (model)
        iter = model.append (None)
        model.set (iter, 0, _("Not listed"), 1, '', 2, '', 3, NotListed)

        devices = {}
        parent = self.troubleshooter.get_window ()
        # Skip device list if this page is hidden and we're skipping
        # backwards past it.
        if not (answers['cups_queue_listed'] and
                self.troubleshooter.is_moving_backwards ()):
            # Otherwise, fetch devices.
            self.authconn = answers['_authenticated_connection']
            try:
                self.op = TimedOperation (self.authconn.getDevices,
                                          parent=parent)
                devices = self.op.run ()
                devices_list = []
                for uri, device in devices.items ():
                    if uri.find (':') == -1:
                        continue

                    if device.get('device-class') != 'direct':
                        continue

                    name = device.get('device-info', _("Unknown"))
                    info = device.get('device-make-and-model', _("Unknown"))
                    devices_list.append ((name, info, uri, device))

                devices_list.sort (key=lambda x: x[0])
                for name, info, uri, device in devices_list:
                    iter = model.append (None)
                    model.set (iter, 0, name, 1, info, 2, uri, 3, device)

            except cups.HTTPError:
                pass
            except cups.IPPError:
                pass
            except RuntimeError:
                pass

        if answers['cups_queue_listed']:
            try:
                printer_dict = answers['cups_printer_dict']
                uri = printer_dict['device-uri']
                device = devices[uri]
                self.answers['cups_device_dict'] = device
            except KeyError:
                pass

            return False

        return True

    def connect_signals (self, handler):
        self.signal_id = self.treeview.connect ("cursor-changed", handler)

    def disconnect_signals (self):
        self.treeview.disconnect (self.signal_id)

    def can_click_forward (self):
        model, iter = self.treeview.get_selection ().get_selected ()
        if iter is None:
            return False
        return True

    def collect_answer (self):
        if not self.displayed:
            return self.answers

        model, iter = self.treeview.get_selection ().get_selected ()
        device = model.get_value (iter, 3)
        if device == NotListed:
            class enum_devices:
                def __init__ (self, model):
                    self.devices = {}
                    model.foreach (self.each, None)

                def each (self, model, path, iter, user_data):
                    uri = model.get_value (iter, 2)
                    device = model.get_value (iter, 3)
                    if device != NotListed:
                        self.devices[uri] = device

            self.answers['cups_device_listed'] = False
            avail = enum_devices (model).devices
            self.answers['cups_devices_available'] = avail
        else:
            uri = model.get_value (iter, 2)
            self.answers['cups_device_listed'] = True
            self.answers['cups_device_uri'] = uri
            self.answers['cups_device_attributes'] = device

        return self.answers

    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
class ErrorLogFetch(Question):
    def __init__ (self, troubleshooter):
        Question.__init__ (self, troubleshooter, "Error log fetch")
        troubleshooter.new_page (gtk.Label (), self)
        self.persistent_answers = {}

    def display (self):
        answers = self.troubleshooter.answers
        parent = self.troubleshooter.get_window ()
        self.answers = {}
        try:
            checkpoint = answers['error_log_checkpoint']
        except KeyError:
            checkpoint = None

        if self.persistent_answers.has_key ('error_log'):
            checkpoint = None

        def fetch_log (c):
            prompt = c._get_prompt_allowed ()
            c._set_prompt_allowed (False)
            c._connect ()
            (tmpfd, tmpfname) = tempfile.mkstemp ()
            os.close (tmpfd)
            success = False
            try:
                c.getFile ('/admin/log/error_log', tmpfname)
                success = True
            except cups.HTTPError:
                try:
                    os.remove (tmpfname)
                except OSError:
                    pass

            c._set_prompt_allowed (prompt)
            if success:
                return tmpfname
            return None

        self.authconn = self.troubleshooter.answers['_authenticated_connection']
        if answers.has_key ('error_log_debug_logging_set'):
            try:
                self.op = TimedOperation (self.authconn.adminGetServerSettings,
                                          parent=parent)
                settings = self.op.run ()
            except cups.IPPError:
                return False

            settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0'
            orig_settings = answers['cups_server_settings']
            settings['MaxLogSize'] = orig_settings.get ('MaxLogSize', '2000000')
            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:

                self.op = TimedOperation (set_settings,
                                          (self.authconn, settings),
                                          parent=parent)
                self.op.run ()
                self.persistent_answers['error_log_debug_logging_unset'] = True
            except cups.IPPError:
                pass

        if checkpoint != None:
            self.op = TimedOperation (fetch_log,
                                      (self.authconn,),
                                      parent=parent)
            tmpfname = self.op.run ()
            if tmpfname != None:
                f = file (tmpfname)
                f.seek (checkpoint)
                lines = f.readlines ()
                os.remove (tmpfname)
                self.answers = { 'error_log': map (lambda x: x.strip (),
                                                   lines) }

        return False

    def collect_answer (self):
        answers = self.persistent_answers.copy ()
        answers.update (self.answers)
        return answers

    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
Exemple #23
0
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

        (tmpfd, tmpfname) = tempfile.mkstemp()
        os.close(tmpfd)
        try:
            self.op = TimedOperation(self.authconn.getFile,
                                     args=('/admin/log/error_log', tmpfname),
                                     parent=parent)
            self.op.run()
        except (RuntimeError, cups.IPPError) as e:
            self.answers['error_log_checkpoint_exc'] = e
            try:
                os.remove(tmpfname)
            except OSError:
                pass
        except cups.HTTPError as e:
            self.answers['error_log_checkpoint_exc'] = e
            try:
                os.remove(tmpfname)
            except OSError:
                pass

            # 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(tmpfname)
            os.remove(tmpfname)
        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

        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