!C99Shell v. 2.0 [PHP 7 Update] [25.02.2019]!

Software: Apache/2.2.16 (Debian). PHP/5.3.3-7+squeeze19 

uname -a: Linux mail.tri-specialutilitydistrict.com 2.6.32-5-amd64 #1 SMP Tue May 13 16:34:35 UTC
2014 x86_64
 

uid=33(www-data) gid=33(www-data) groups=33(www-data) 

Safe-mode: OFF (not secure)

/usr/share/system-config-printer/   drwxr-xr-x
Free 130.02 GB of 142.11 GB (91.49%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     jobviewer.py (85.83 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |

## Copyright (C) 2007, 2008, 2009, 2010 Red Hat, Inc.
## Authors:
##  Tim Waugh <twaugh@redhat.com>
##  Jiri Popelka <jpopelka@redhat.com>

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

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

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import asyncconn
import authconn
import cups
import dbus
import dbus.glib
import dbus.service
import pynotify
import gettext
import glib
import gobject
import gtk
import gtk.gdk
from gui import GtkGUI
import monitor
import os, shutil
import pango
import pwd
import smburi
import subprocess
import sys
import time
import urllib
from xml.sax import saxutils

from debug import *
import config
import statereason
import errordialogs

try:
    import gnomekeyring
    USE_KEYRING=True
except ImportError:
    USE_KEYRING=False

from gettext import gettext as _
DOMAIN="system-config-printer"
gettext.textdomain (DOMAIN)
from statereason import StateReason
statereason.set_gettext_function (_)
errordialogs.set_gettext_function (_)

pkgdata = config.pkgdatadir
ICON="printer"
SEARCHING_ICON="document-print-preview"

# We need to call pynotify.init before we can check the server for caps
pynotify.init('System Config Printer Notification')

class PrinterURIIndex:
    def __init__ (self, names=[]):
        self.printer = {}
        self.names = names
        c = cups.Connection ()
        for name in names:
            self.add_printer (name, connection=c)

    def add_printer (self, printer, connection=None):
        try:
            self._map_printer (name=printer, connection=connection)
        except KeyError:
            return

    def update_from_attrs (self, printer, attrs):
        uris = []
        if attrs.has_key ('printer-uri-supported'):
            uri_supported = attrs['printer-uri-supported']
            if type (uri_supported) != list:
                uri_supported = [uri_supported]
            uris.extend (uri_supported)
        if attrs.has_key ('notify-printer-uri'):
            uris.append (attrs['notify-printer-uri'])
        if attrs.has_key ('printer-more-info'):
            uris.append (attrs['printer-more-info'])

        for uri in uris:
            self.printer[uri] = printer

    def remove_printer (self, printer):
        # Remove references to this printer in the URI map.
        uris = self.printer.keys ()
        for uri in uris:
            if self.printer[uri] == printer:
                del self.printer[uri]

    def lookup (self, uri, connection=None):
        try:
            return self.printer[uri]
        except KeyError:
            return self._map_printer (uri=uri, connection=connection)

    def all_printer_names (self):
        return set (self.printer.values ())

    def lookup_cached_by_name (self, name):
        for uri, printer in self.printer.iteritems ():
            if printer == name:
                return uri

        raise KeyError

    def _map_printer (self, uri=None, name=None, connection=None):
        if connection == None:
            connection = cups.Connection ()

        r = ['printer-name', 'printer-uri-supported', 'printer-more-info']
        try:
            if uri != None:
                attrs = connection.getPrinterAttributes (uri=uri,
                                                         requested_attributes=r)
            else:
                attrs = connection.getPrinterAttributes (name,
                                                         requested_attributes=r)
        except cups.IPPError:
            # URI not known.
            raise KeyError

        name = attrs['printer-name']
        self.update_from_attrs (name, attrs)
        if uri != None:
            self.printer[uri] = name
        return name


class CancelJobsOperation:
    def __init__ (self, jobviewer, jobids, purge_job):
        self.jobviewer = jobviewer
        self.jobids = list (jobids)
        self.purge_job = purge_job
        if purge_job:
            if len(self.jobids) > 1:
                dialog_title = _("Delete Jobs")
                dialog_label = _("Do you really want to delete these jobs?")
            else:
                dialog_title = _("Delete Job")
                dialog_label = _("Do you really want to delete this job?")
        else:
            if len(self.jobids) > 1:
                dialog_title = _("Cancel Jobs")
                dialog_label = _("Do you really want to cancel these jobs?")
            else:
                dialog_title = _("Cancel Job")
                dialog_label = _("Do you really want to cancel this job?")

        dialog = gtk.Dialog (dialog_title, jobviewer.JobsWindow,
                             gtk.DIALOG_MODAL |
                             gtk.DIALOG_DESTROY_WITH_PARENT |
                             gtk.DIALOG_NO_SEPARATOR,
                             (_("Keep Printing"), gtk.RESPONSE_NO,
                              dialog_title, gtk.RESPONSE_YES))
        dialog.set_default_response (gtk.RESPONSE_NO)
        dialog.set_border_width (6)
        dialog.set_resizable (False)
        hbox = gtk.HBox (False, 12)
        image = gtk.Image ()
        image.set_from_stock (gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
        image.set_alignment (0.0, 0.0)
        hbox.pack_start (image, False, False, 0)
        label = gtk.Label (dialog_label)
        label.set_line_wrap (True)
        label.set_alignment (0.0, 0.0)
        hbox.pack_start (label, False, False, 0)
        dialog.vbox.pack_start (hbox, False, False, 0)
        dialog.set_data ('job-ids', self.jobids)
        dialog.connect ("response", self.on_job_cancel_prompt_response)
        dialog.connect ("delete-event", self.on_job_cancel_prompt_delete)
        dialog.show_all ()
        self.dialog = dialog
        self.connection = None
        debugprint ("+%s" % self)

    def __del__ (self):
        debugprint ("-%s" % self)

    def destroy (self):
        self.jobviewer = None
        self.connection = None
        if self.dialog:
            self.dialog.destroy ()
            self.dialog = None

        debugprint ("DESTROY: %s" % self)

    def on_job_cancel_prompt_delete (self, dialog, event):
        self.on_job_cancel_prompt_response (dialog, gtk.RESPONSE_NO)

    def on_job_cancel_prompt_response (self, dialog, response):
        dialog.destroy ()
        self.dialog = None

        if response != gtk.RESPONSE_YES:
            return

        c = asyncconn.Connection (host=self.jobviewer.host,
                                  port=self.jobviewer.port,
                                  encryption=self.jobviewer.encryption)
        self.connection = c

        if self.purge_job:
            operation = _("deleting job")
        else:
            operation = _("canceling job")

        self.connection._begin_operation (operation)
        self.connection.cancelJob (self.jobids[0], self.purge_job,
                                   reply_handler=self.cancelJob_finish,
                                   error_handler=self.cancelJob_error)

    def cancelJob_error (self, connection, exc):
        debugprint ("cancelJob_error %s:%s" % (connection, repr (exc)))
        if self.jobviewer == None:
            return

        if type (exc) == cups.IPPError:
            (e, m) = exc.args
            if (e != cups.IPP_NOT_POSSIBLE and
                e != cups.IPP_NOT_FOUND):
                self.jobviewer.show_IPP_Error (e, m)
            self.cancelJob_finish(connection, None)
        else:
            self.connection._end_operation ()
            self.connection.destroy ()
            self.jobviewer.update_monitor ()
            raise exc

    def cancelJob_finish (self, connection, result):
        debugprint ("cancelJob_finish %s:%s" % (connection, repr (result)))
        if self.jobviewer == None:
            return

        del self.jobids[0]
        if not self.jobids:
            # Last job canceled.
            self.connection._end_operation ()
            self.connection.destroy ()
            self.jobviewer.update_monitor ()
        else:
            # there are other jobs to cancel/delete
            connection.cancelJob (self.jobids[0], self.purge_job,
                                  reply_handler=self.cancelJob_finish,
                                  error_handler=self.cancelJob_error)

class JobViewer (GtkGUI, monitor.Watcher):
    required_job_attributes = set(['job-k-octets',
                                   'job-name',
                                   'job-originating-user-name',
                                   'job-printer-uri',
                                   'job-state',
                                   'time-at-creation'])

    def __init__(self, bus=None, loop=None,
                 trayicon=False, suppress_icon_hide=False,
                 my_jobs=True, specific_dests=None, exit_handler=None,
                 parent=None):
        self.loop = loop
        self.trayicon = trayicon
        self.suppress_icon_hide = suppress_icon_hide
        self.my_jobs = my_jobs
        self.specific_dests = specific_dests
        self.exit_handler = exit_handler

        self.jobs = {}
        self.jobiters = {}
        self.jobids = []
        self.jobs_attrs = {} # dict of jobid->(GtkListStore, page_index)
        self.active_jobs = set() # of job IDs
        self.stopped_job_prompts = set() # of job IDs
        self.printer_state_reasons = {}
        self.num_jobs_when_hidden = 0
        self.connecting_to_device = {} # dict of printer->time first seen
        self.state_reason_notifications = {}
        self.auth_info_dialogs = {} # by job ID
        self.job_creation_times_timer = None
        self.special_status_icon = False
        self.new_printer_notifications = {}
        self.completed_job_notifications = {}
        self.authenticated_jobs = set() # of job IDs
        self.ops = []

        self.getWidgets ({"JobsWindow":
                              ["JobsWindow",
                               "treeview",
                               "statusbar",
                               "toolbar"],
                          "statusicon_popupmenu":
                              ["statusicon_popupmenu"]},

                         domain=DOMAIN)

        job_action_group = gtk.ActionGroup ("JobActionGroup")
        job_action_group.add_actions ([
                ("cancel-job", gtk.STOCK_CANCEL, _("_Cancel"), None,
                 _("Cancel selected jobs"), self.on_job_cancel_activate),
                ("delete-job", gtk.STOCK_DELETE, _("_Delete"), None,
                 _("Delete selected jobs"), self.on_job_delete_activate),
                ("hold-job", gtk.STOCK_MEDIA_PAUSE, _("_Hold"), None,
                 _("Hold selected jobs"), self.on_job_hold_activate),
                ("release-job", gtk.STOCK_MEDIA_PLAY, _("_Release"), None,
                 _("Release selected jobs"), self.on_job_release_activate),
                ("reprint-job", gtk.STOCK_REDO, _("Re_print"), None,
                 _("Reprint selected jobs"), self.on_job_reprint_activate),
                ("retrieve-job", gtk.STOCK_SAVE_AS, _("Re_trieve"), None,
                 _("Retrieve selected jobs"), self.on_job_retrieve_activate),
                ("move-job", None, _("_Move To"), None, None, None),
                ("authenticate-job", None, _("_Authenticate"), None, None,
                 self.on_job_authenticate_activate),
                ("job-attributes", None, _("_View Attributes"), None, None,
                 self.on_job_attributes_activate),
                ("close", gtk.STOCK_CLOSE, None, "<ctrl>w",
                 _("Close this window"), self.on_delete_event)
                ])
        self.job_ui_manager = gtk.UIManager ()
        self.job_ui_manager.insert_action_group (job_action_group, -1)
        self.job_ui_manager.add_ui_from_string (
"""
<ui>
 <accelerator action="cancel-job"/>
 <accelerator action="delete-job"/>
 <accelerator action="hold-job"/>
 <accelerator action="release-job"/>
 <accelerator action="reprint-job"/>
 <accelerator action="retrieve-job"/>
 <accelerator action="move-job"/>
 <accelerator action="authenticate-job"/>
 <accelerator action="job-attributes"/>
 <accelerator action="close"/>
</ui>
"""
)
        self.job_ui_manager.ensure_update ()
        self.JobsWindow.add_accel_group (self.job_ui_manager.get_accel_group ())
        self.job_context_menu = gtk.Menu ()
        for action_name in ["cancel-job",
                            "delete-job",
                            "hold-job",
                            "release-job",
                            "reprint-job",
                            "retrieve-job",
                            "move-job",
                            None,
                            "authenticate-job",
                            "job-attributes"]:
            if not action_name:
                item = gtk.SeparatorMenuItem ()
            else:
                action = job_action_group.get_action (action_name)
                action.set_sensitive (False)
                item = action.create_menu_item ()

                if action_name == 'move-job':
                    self.move_job_menuitem = item
                    printers = gtk.Menu ()
                    item.set_submenu (printers)

            item.show ()
            self.job_context_menu.append (item)

        for action_name in ["cancel-job",
                            "delete-job",
                            "hold-job",
                            "release-job",
                            "reprint-job",
                            "retrieve-job",
                            "close"]:
            action = job_action_group.get_action (action_name)
            action.set_sensitive (action_name == "close")
            action.set_is_important (action_name == "close")
            item = action.create_tool_item ()
            item.show ()
            self.toolbar.insert (item, -1)

        for skip, ellipsize, name, setter in \
                [(False, False, _("Job"), self._set_job_job_number_text),
                 (True, False, _("User"), self._set_job_user_text),
                 (False, True, _("Document"), self._set_job_document_text),
                 (False, True, _("Printer"), self._set_job_printer_text),
                 (False, False, _("Size"), self._set_job_size_text)]:
            if trayicon and skip:
                # Skip the user column for the trayicon.
                continue

            cell = gtk.CellRendererText()
            if ellipsize:
                # Ellipsize the 'Document' and 'Printer' columns.
                cell.set_property ("ellipsize", pango.ELLIPSIZE_END)
                cell.set_property ("width-chars", 20)
            column = gtk.TreeViewColumn(name, cell)
            column.set_cell_data_func (cell, setter)
            column.set_resizable(True)
            self.treeview.append_column(column)

        cell = gtk.CellRendererText ()
        column = gtk.TreeViewColumn (_("Time submitted"), cell, text=1)
        column.set_resizable (True)
        self.treeview.append_column (column)

        column = gtk.TreeViewColumn (_("Status"))
        icon = gtk.CellRendererPixbuf ()
        column.pack_start (icon, False)
        text = gtk.CellRendererText ()
        text.set_property ("ellipsize", pango.ELLIPSIZE_END)
        text.set_property ("width-chars", 20)
        column.pack_start (text, True)
        column.set_cell_data_func (icon, self._set_job_status_icon)
        column.set_cell_data_func (text, self._set_job_status_text)
        self.treeview.append_column (column)

        self.store = gtk.TreeStore(int, str)
        self.store.set_sort_column_id (0, gtk.SORT_DESCENDING)
        self.treeview.set_model(self.store)
        self.treeview.set_rules_hint (True)
        self.selection = self.treeview.get_selection()
        self.selection.set_mode(gtk.SELECTION_MULTIPLE)
        self.selection.connect('changed', self.on_selection_changed)
        self.treeview.connect ('button_release_event',
                               self.on_treeview_button_release_event)
        self.treeview.connect ('popup-menu', self.on_treeview_popup_menu)
        self.treeview.set_has_tooltip (True)
        self.treeview.connect ('query-tooltip', self._on_treeview_query_tooltip)

        self.JobsWindow.set_icon_name (ICON)
        self.JobsWindow.hide ()

        if specific_dests:
            the_dests = reduce (lambda x, y: x + ", " + y, specific_dests)

        if my_jobs:
            if specific_dests:
                title = _("my jobs on %s") % the_dests
            else:
                title = _("my jobs")
        else:
            if specific_dests:
                title = "%s" % the_dests
            else:
                title = _("all jobs")
        self.JobsWindow.set_title (_("Document Print Status (%s)") % title)

        if parent:
            self.JobsWindow.set_transient_for (parent)

        self.statusbar_set = False

        theme = gtk.icon_theme_get_default ()
        self.icon_jobs = theme.load_icon (ICON, 22, 0)
        self.icon_jobs_processing = theme.load_icon ("printer-printing",
                                                     22, 0)
        self.icon_no_jobs = self.icon_jobs.copy ()
        self.icon_no_jobs.fill (0)
        self.icon_jobs.composite (self.icon_no_jobs,
                                  0, 0,
                                  self.icon_no_jobs.get_width(),
                                  self.icon_no_jobs.get_height(),
                                  0, 0,
                                  1.0, 1.0,
                                  gtk.gdk.INTERP_BILINEAR,
                                  127)
        if self.trayicon:
            self.statusicon = gtk.StatusIcon ()
            pixbuf = theme.load_icon (ICON, 22, 0)
            self.statusicon.set_from_pixbuf (pixbuf)    
            self.set_statusicon_from_pixbuf (self.icon_no_jobs)
            self.statusicon.connect ('activate', self.toggle_window_display)
            self.statusicon.connect ('popup-menu', self.on_icon_popupmenu)
            self.statusicon.set_visible (False)

        # D-Bus
        if bus == None:
            bus = dbus.SystemBus ()

        self.set_process_pending (True)
        self.host = cups.getServer ()
        self.port = cups.getPort ()
        self.encryption = cups.getEncryption ()
        self.monitor = monitor.Monitor (self, bus=bus, my_jobs=my_jobs,
                                        host=self.host, port=self.port,
                                        encryption=self.encryption)

        if not self.trayicon:
            self.JobsWindow.show ()

        self.JobsAttributesWindow = gtk.Window()
        self.JobsAttributesWindow.set_title (_("Job attributes"))
        self.JobsAttributesWindow.set_position(gtk.WIN_POS_MOUSE)
        self.JobsAttributesWindow.set_default_size(600, 600)
        self.JobsAttributesWindow.set_transient_for (self.JobsWindow)
        self.JobsAttributesWindow.connect("delete_event",
                                          self.job_attributes_on_delete_event)
        self.JobsAttributesWindow.add_accel_group (self.job_ui_manager.get_accel_group ())
        attrs_action_group = gtk.ActionGroup ("AttrsActionGroup")
        attrs_action_group.add_actions ([
                ("close", gtk.STOCK_CLOSE, None, "<ctrl>w",
                 _("Close this window"), self.job_attributes_on_delete_event)
                ])
        self.attrs_ui_manager = gtk.UIManager ()
        self.attrs_ui_manager.insert_action_group (attrs_action_group, -1)
        self.attrs_ui_manager.add_ui_from_string (
"""
<ui>
 <accelerator action="close"/>
</ui>
"""
)
        self.attrs_ui_manager.ensure_update ()
        self.JobsAttributesWindow.add_accel_group (self.attrs_ui_manager.get_accel_group ())
        vbox = gtk.VBox ()
        self.JobsAttributesWindow.add (vbox)
        toolbar = gtk.Toolbar ()
        action = self.attrs_ui_manager.get_action ("/close")
        item = action.create_tool_item ()
        item.set_is_important (True)
        toolbar.insert (item, 0)
        vbox.pack_start (toolbar, False, False, 0)
        self.notebook = gtk.Notebook()
        vbox.pack_start (self.notebook)

    def cleanup (self):
        self.monitor.cleanup ()

        # Close any open notifications.
        for l in [self.new_printer_notifications.values (),
                  self.state_reason_notifications.values ()]:
            for notification in l:
                if notification.get_data ('closed') != True:
                    try:
                        notification.close ()
                    except glib.GError:
                        # Can fail if the notification wasn't even shown
                        # yet (as in bug #571603).
                        pass
                    notification.set_data ('closed', True)

        if self.job_creation_times_timer != None:
            gobject.source_remove (self.job_creation_times_timer)
            self.job_creation_times_timer = None

        for op in self.ops:
            op.destroy ()

        if self.exit_handler:
            self.exit_handler (self)

    def set_process_pending (self, whether):
        self.process_pending_events = whether

    # Handle "special" status icon
    def set_special_statusicon (self, iconname, tooltip=None):
        self.special_status_icon = True
        self.statusicon.set_from_icon_name (iconname)
        self.set_statusicon_visibility ()
        if tooltip != None:
            self.set_statusicon_tooltip (tooltip=tooltip)

    def unset_special_statusicon (self):
        self.special_status_icon = False
        self.statusicon.set_from_pixbuf (self.saved_statusicon_pixbuf)
        self.set_statusicon_visibility ()
        self.set_statusicon_tooltip ()

    def notify_new_printer (self, printer, notification):
        self.new_printer_notifications[printer] = notification
        notification.set_data ('printer-name', printer)
        notification.connect ('closed', self.on_new_printer_notification_closed)
        self.set_statusicon_visibility ()
        notification.attach_to_status_icon (self.statusicon)
        try:
            notification.show ()
        except gobject.GError:
            nonfatalException ()

    def on_new_printer_notification_closed (self, notification, reason=None):
        printer = notification.get_data ('printer-name')
        del self.new_printer_notifications[printer]
        self.set_statusicon_visibility ()

    def set_statusicon_from_pixbuf (self, pb):
        self.saved_statusicon_pixbuf = pb
        if not self.special_status_icon:
            self.statusicon.set_from_pixbuf (pb)

    def on_delete_event(self, *args):
        if self.trayicon or not self.loop:
            self.JobsWindow.hide ()
            self.JobsWindow.set_data ('visible', False)
            if not self.loop:
                # Being run from main app, not applet
                self.cleanup ()
        else:
            self.loop.quit ()
        return True

    def job_attributes_on_delete_event(self, widget, event=None):
        for page in range(self.notebook.get_n_pages()):
            self.notebook.remove_page(-1)
        self.jobs_attrs = {}
        self.JobsAttributesWindow.hide_all()
        return True

    def show_IPP_Error(self, exception, message):
        return errordialogs.show_IPP_Error (exception, message, self.JobsWindow)

    def toggle_window_display(self, icon, force_show=False):
        visible = self.JobsWindow.get_data('visible')
        if force_show:
            visible = False

        if visible:
            w = self.JobsWindow.window
            aw = self.JobsAttributesWindow.window
            (s, area, o) = self.statusicon.get_geometry ()
            w.set_skip_taskbar_hint (True)
            if aw != None:
                aw.set_skip_taskbar_hint (True)

            w.property_change ("_NET_WM_ICON_GEOMETRY",
                               "CARDINAL", 32,
                               gtk.gdk.PROP_MODE_REPLACE,
                               list (area))
            self.JobsWindow.iconify ()
        else:
            self.JobsWindow.present ()
            self.JobsWindow.window.set_skip_taskbar_hint (False)
            aw = self.JobsAttributesWindow.window
            if aw != None:
                aw.set_skip_taskbar_hint (False)

        self.JobsWindow.set_data ('visible', not visible)

    def on_show_completed_jobs_clicked(self, toggletoolbutton):
        if toggletoolbutton.get_active():
            which_jobs = "all"
        else:
            which_jobs = "not-completed"
        self.monitor.refresh(which_jobs=which_jobs, refresh_all=False)


    def update_job_creation_times(self):
        now = time.time ()
        need_update = False
        for job, data in self.jobs.iteritems():
            t = _("Unknown")
            if data.has_key ('time-at-creation'):
                created = data['time-at-creation']
                ago = now - created
                need_update = True
                if ago < 2 * 60:
                    t = _("a minute ago")
                elif ago < 60 * 60:
                    mins = int (ago / 60)
                    t = _("%d minutes ago") % mins
                elif ago < 24 * 60 * 60:
                    hours = int (ago / (60 * 60))
                    if hours == 1:
                        t = _("an hour ago")
                    else:
                        t = _("%d hours ago") % hours
                elif ago < 7 * 24 * 60 * 60:
                    days = int (ago / (24 * 60 * 60))
                    if days == 1:
                        t = _("yesterday")
                    else:
                        t = _("%d days ago") % days
                elif ago < 6 * 7 * 24 * 60 * 60:
                    weeks = int (ago / (7 * 24 * 60 * 60))
                    if weeks == 1:
                        t = _("last week")
                    else:
                        t = _("%d weeks ago") % weeks
                else:
                    need_update = False
                    t = time.strftime ("%B %Y", time.localtime (created))

            if self.jobiters.has_key (job):
                iter = self.jobiters[job]
                self.store.set_value (iter, 1, t)

        if need_update and not self.job_creation_times_timer:
            def update_times_with_locking ():
                gtk.gdk.threads_enter ()
                ret = self.update_job_creation_times ()
                gtk.gdk.threads_leave ()
                return ret

            t = gobject.timeout_add_seconds (60, update_times_with_locking)
            self.job_creation_times_timer = t

        if not need_update:
            if self.job_creation_times_timer:
                gobject.source_remove (self.job_creation_times_timer)
                self.job_creation_times_timer = None

        # Return code controls whether the timeout will recur.
        return need_update

    def print_error_dialog_response(self, dialog, response, jobid):
        dialog.hide ()
        dialog.destroy ()
        self.stopped_job_prompts.remove (jobid)
        if response == gtk.RESPONSE_NO:
            # Diagnose
            if not self.__dict__.has_key ('troubleshooter'):
                import troubleshoot
                troubleshooter = troubleshoot.run (self.on_troubleshoot_quit)
                self.troubleshooter = troubleshooter

    def on_troubleshoot_quit(self, troubleshooter):
        del self.troubleshooter

    def add_job (self, job, data, connection=None):
        self.update_job (job, data, connection=connection)

        store = self.store
        iter = self.store.append (None)
        store.set_value (iter, 0, job)
        debugprint ("Job %d added" % job)
        self.jobiters[job] = iter

        range = self.treeview.get_visible_range ()
        if range != None:
            (start, end) = range
            if (self.store.get_sort_column_id () == (0,
                                                     gtk.SORT_DESCENDING) and
                start == (1,)):
                # This job was added job above the visible range, and
                # we are sorting by descending job ID.  Scroll to it.
                self.treeview.scroll_to_cell ((0,), None, False, 0.0, 0.0)

        if not self.job_creation_times_timer:
            def start_updating_job_creation_times():
                gtk.gdk.threads_enter ()
                self.update_job_creation_times ()
                gtk.gdk.threads_leave ()
                return False

            gobject.timeout_add (500, start_updating_job_creation_times)

    def update_monitor (self):
        self.monitor.update ()

    def update_job (self, job, data, connection=None):
        # Fetch required attributes for this job if they are missing.
        r = self.required_job_attributes - set (data.keys ())

        # If we are showing attributes of this job at this moment, update them.
        if job in self.jobs_attrs:
            self.update_job_attributes_viewer(job)

        if r:
            attrs = None
            try:
                if connection == None:
                    connection = cups.Connection (host=self.host,
                                                  port=self.port,
                                                  encryption=self.encryption)

                debugprint ("requesting %s" % r)
                r = list (r)
                attrs = connection.getJobAttributes (job,
                                                     requested_attributes=r)
            except RuntimeError:
                pass
            except AttributeError:
                pass
            except cups.IPPError:
                # someone else may have purged the job
                return

            if attrs:
                data.update (attrs)

        self.jobs[job] = data

        job_requires_auth = False
        try:
            jstate = data.get ('job-state', cups.IPP_JOB_PROCESSING)
            s = int (jstate)

            if s in [cups.IPP_JOB_HELD, cups.IPP_JOB_STOPPED]:
                jattrs = ['job-state', 'job-hold-until']
                pattrs = ['auth-info-required', 'device-uri']
                uri = data.get ('job-printer-uri')
                c = authconn.Connection (self.JobsWindow,
                                         host=self.host,
                                         port=self.port,
                                         encryption=self.encryption)
                attrs = c.getPrinterAttributes (uri = uri,
                                                requested_attributes=pattrs)

                try:
                    auth_info_required = attrs['auth-info-required']
                except KeyError:
                    debugprint ("No auth-info-required attribute; "
                                "guessing instead")
                    auth_info_required = ['username', 'password']

                if not isinstance (auth_info_required, list):
                    auth_info_required = [auth_info_required]
                    attrs['auth-info-required'] = auth_info_required

                data.update (attrs)

                attrs = c.getJobAttributes (job,
                                            requested_attributes=jattrs)
                data.update (attrs)
                jstate = data.get ('job-state', cups.IPP_JOB_PROCESSING)
                s = int (jstate)
        except ValueError:
            pass
        except RuntimeError:
            pass
        except cups.IPPError, (e, m):
            pass

        # Invalidate the cached status description and redraw the treeview.
        try:
            del data['_status_text']
        except KeyError:
            pass
        self.treeview.queue_draw ()

        # Check whether authentication is required.
        if self.trayicon:
            job_requires_auth = (s == cups.IPP_JOB_HELD and
                                 data.get ('job-hold-until', 'none') ==
                                 'auth-info-required')

            if (job_requires_auth and
                not self.auth_info_dialogs.has_key (job)):
                try:
                    cups.require ("1.9.37")
                except:
                    debugprint ("Authentication required but "
                                "authenticateJob() not available")
                    return

                # Find out which auth-info is required.
                try_keyring = USE_KEYRING
                keyring_attrs = dict()
                auth_info = None
                if try_keyring and 'password' in auth_info_required:
                    auth_info_required = data.get ('auth-info-required', [])
                    device_uri = data.get ("device-uri")
                    (scheme, rest) = urllib.splittype (device_uri)
                    if scheme == 'smb':
                        uri = smburi.SMBURI (uri=device_uri)
                        (group, server, share,
                         user, password) = uri.separate ()
                        keyring_attrs["domain"] = str (group)
                    else:
                        (serverport, rest) = urllib.splithost (rest)
                        (server, port) = urllib.splitnport (serverport)
                    keyring_attrs.update ({ "server": str (server.lower ()),
                                            "protocol": str (scheme)})

                if job in self.authenticated_jobs:
                    # We've already tried to authenticate this job before.
                    try_keyring = False

                if try_keyring and 'password' in auth_info_required:
                    type = gnomekeyring.ITEM_NETWORK_PASSWORD
                    try:
                        items = gnomekeyring.find_items_sync (type,
                                                              keyring_attrs)
                        auth_info = map (lambda x: '', auth_info_required)
                        ind = auth_info_required.index ('username')
                        auth_info[ind] = items[0].attributes.get ('user', '')
                        ind = auth_info_required.index ('password')
                        auth_info[ind] = items[0].secret
                    except gnomekeyring.NoMatchError:
                        debugprint ("gnomekeyring: no match for %s" %
                                    keyring_attrs)
                    except gnomekeyring.DeniedError:
                        debugprint ("gnomekeyring: denied for %s" %
                                    keyring_attrs)

                if try_keyring and c == None:
                    try:
                        c = authconn.Connection (self.JobsWindow,
                                                 host=self.host,
                                                 port=self.port,
                                                 encryption=self.encryption)
                    except RuntimeError:
                        try_keyring = False

                if try_keyring and auth_info != None:
                    try:
                        c._begin_operation (_("authenticating job"))
                        c.authenticateJob (job, auth_info)
                        c._end_operation ()
                        self.monitor.update ()
                        debugprint ("Automatically authenticated job %d" % job)
                        self.authenticated_jobs.add (job)
                        return
                    except cups.IPPError, (e, m):
                        c._end_operation ()
                        nonfatalException ()
                        return
                    except:
                        c._end_operation ()
                        nonfatalException ()

                username = pwd.getpwuid (os.getuid ())[0]
                keyring_attrs["user"] = str (username)
                self.display_auth_info_dialog (job, keyring_attrs)
        self.update_sensitivity ()

    def display_auth_info_dialog (self, job, keyring_attrs=None):
        data = self.jobs[job]
        auth_info_required = data['auth-info-required']
        dialog = authconn.AuthDialog (auth_info_required=auth_info_required,
                                      allow_remember=USE_KEYRING)
        dialog.set_data ('keyring-attrs', keyring_attrs)
        dialog.set_data ('auth-info-required', auth_info_required)
        dialog.set_position (gtk.WIN_POS_CENTER)

        # Pre-fill 'username' field.
        auth_info = map (lambda x: '', auth_info_required)
        username = pwd.getpwuid (os.getuid ())[0]
        if 'username' in auth_info_required:
            try:
                ind = auth_info_required.index ('username')
                auth_info[ind] = username
                dialog.set_auth_info (auth_info)
            except:
                nonfatalException ()

        # Focus on the first empty field.
        index = 0
        for field in auth_info_required:
            if auth_info[index] == '':
                dialog.field_grab_focus (field)
                break
            index += 1

        dialog.set_prompt (_("Authentication required for "
                             "printing document `%s' (job %d)") %
                           (data.get('job-name', _("Unknown")), job))
        self.auth_info_dialogs[job] = dialog
        dialog.connect ('response', self.auth_info_dialog_response)
        dialog.connect ('delete-event', self.auth_info_dialog_delete)
        dialog.set_data ('job-id', job)
        dialog.show_all ()
        dialog.set_keep_above (True)
        dialog.show_now ()

    def auth_info_dialog_delete (self, dialog, event):
        self.auth_info_dialog_response (dialog, gtk.RESPONSE_CANCEL)

    def auth_info_dialog_response (self, dialog, response):
        jobid = dialog.get_data ('job-id')
        del self.auth_info_dialogs[jobid]

        if response != gtk.RESPONSE_OK:
            dialog.destroy ()
            return

        auth_info = dialog.get_auth_info ()
        try:
            c = authconn.Connection (self.JobsWindow,
                                     host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
        except RuntimeError:
            debugprint ("Error connecting to CUPS for authentication")
            return

        remember = False
        c._begin_operation (_("authenticating job"))
        try:
            c.authenticateJob (jobid, auth_info)
            remember = dialog.get_remember_password ()
            self.authenticated_jobs.add (jobid)
            self.monitor.update ()
        except cups.IPPError, (e, m):
            self.show_IPP_Error (e, m)

        c._end_operation ()

        if remember:
            try:
                keyring = gnomekeyring.get_default_keyring_sync ()
                type = gnomekeyring.ITEM_NETWORK_PASSWORD
                attrs = dialog.get_data ("keyring-attrs")
                auth_info_required = dialog.get_data ('auth-info-required')
                if attrs != None and auth_info_required != None:
                    try:
                        ind = auth_info_required.index ('username')
                        attrs['user'] = auth_info[ind]
                    except IndexError:
                        pass

                    name = "%s@%s (%s)" % (attrs.get ("user"),
                                           attrs.get ("server"),
                                           attrs.get ("protocol"))
                    ind = auth_info_required.index ('password')
                    secret = auth_info[ind]
                    id = gnomekeyring.item_create_sync (keyring, type, name,
                                                        attrs, secret, True)
                    debugprint ("keyring: created id %d for %s" % (id, name))
            except:
                nonfatalException ()

        dialog.destroy ()

    def set_statusicon_visibility (self):
        if not self.trayicon:
            return

        if self.suppress_icon_hide:
            # Avoid hiding the icon if we've been woken up to notify
            # about a new printer.
            self.suppress_icon_hide = False
            return

        open_notifications = len (self.new_printer_notifications.keys ())
        open_notifications += len (self.completed_job_notifications.keys ())
        for reason, notification in self.state_reason_notifications.iteritems():
            if notification.get_data ('closed') != True:
                open_notifications += 1
        num_jobs = len (self.active_jobs)

        debugprint ("open notifications: %d" % open_notifications)
        debugprint ("num_jobs: %d" % num_jobs)
        debugprint ("num_jobs_when_hidden: %d" % self.num_jobs_when_hidden)

        self.statusicon.set_visible (self.special_status_icon or
                                     open_notifications > 0 or
                                     num_jobs > self.num_jobs_when_hidden)

        # Let the icon show/hide itself before continuing.
        while self.process_pending_events and gtk.events_pending ():
            gtk.main_iteration ()

    def on_treeview_popup_menu (self, treeview):
        event = gtk.gdk.Event (gtk.gdk.NOTHING)
        self.show_treeview_popup_menu (treeview, event, 0)

    def on_treeview_button_release_event(self, treeview, event):
        if event.button == 3:
            self.show_treeview_popup_menu (treeview, event, event.button)

    def update_sensitivity (self, selection = None):
        if (selection is None):
            selection = self.treeview.get_selection () 
        (model, pathlist) = selection.get_selected_rows()
        cancel = self.job_ui_manager.get_action ("/cancel-job")
        delete = self.job_ui_manager.get_action ("/delete-job")
        hold = self.job_ui_manager.get_action ("/hold-job")
        release = self.job_ui_manager.get_action ("/release-job")
        reprint = self.job_ui_manager.get_action ("/reprint-job")
        retrieve = self.job_ui_manager.get_action ("/retrieve-job")
        authenticate = self.job_ui_manager.get_action ("/authenticate-job")
        attributes = self.job_ui_manager.get_action ("/job-attributes")
        move = self.job_ui_manager.get_action ("/move-job")
        if len (pathlist) == 0:
            for widget in [cancel, delete, hold, release, reprint, retrieve,
                           move, authenticate, attributes]:
                widget.set_sensitive (False)
            return

        cancel_sensitive = True
        hold_sensitive = True
        release_sensitive = True
        reprint_sensitive = True
        authenticate_sensitive = True
        move_sensitive = False
        other_printers = self.printer_uri_index.all_printer_names ()
        job_printers = dict()

        self.jobids = []
        for path in pathlist:
            iter = self.store.get_iter (path)
            jobid = self.store.get_value (iter, 0)
            self.jobids.append(jobid)
            job = self.jobs[jobid]

            if job.has_key ('job-state'):
                s = job['job-state']
                if s >= cups.IPP_JOB_CANCELED:
                    cancel_sensitive = False
                if s != cups.IPP_JOB_PENDING:
                    hold_sensitive = False
                if s != cups.IPP_JOB_HELD:
                    release_sensitive = False
                if (not job.get('job-preserved', False)):
                    reprint_sensitive = False

            if (job.get ('job-state',
                         cups.IPP_JOB_CANCELED) != cups.IPP_JOB_HELD or
                job.get ('job-hold-until', 'none') != 'auth-info-required'):
                authenticate_sensitive = False

            uri = job.get ('job-printer-uri', None)
            if uri:
                printer = self.printer_uri_index.lookup (uri)
                job_printers[printer] = uri

        if len (job_printers.keys ()) == 1:
            try:
                other_printers.remove (job_printers.keys ()[0])
            except KeyError:
                pass

        if len (other_printers) > 0:
            printers_menu = gtk.Menu ()
            other_printers = list (other_printers)
            other_printers.sort ()
            for printer in other_printers:
                uri = self.printer_uri_index.lookup_cached_by_name (printer)
                menuitem = gtk.MenuItem (printer, False)
                menuitem.set_sensitive (True)
                menuitem.show ()
                menuitem.connect ('activate', self.on_job_move_activate, uri)
                printers_menu.append (menuitem)

            self.move_job_menuitem.set_submenu (printers_menu)
            move_sensitive = True

        cancel.set_sensitive(cancel_sensitive)
        delete.set_sensitive(True)
        hold.set_sensitive(hold_sensitive)
        release.set_sensitive(release_sensitive)
        reprint.set_sensitive(reprint_sensitive)
        retrieve.set_sensitive(reprint_sensitive)
        move.set_sensitive (move_sensitive)
        authenticate.set_sensitive(authenticate_sensitive)
        attributes.set_sensitive(True)

    def on_selection_changed (self, selection):
        self.update_sensitivity (selection)

    def show_treeview_popup_menu (self, treeview, event, event_button):
        # Right-clicked.
        self.job_context_menu.popup (None, None, None, event_button,
                                     event.get_time ())

    def on_icon_popupmenu(self, icon, button, time):
        self.statusicon_popupmenu.popup (None, None, None, button, time)

    def on_icon_hide_activate(self, menuitem):
        self.num_jobs_when_hidden = len (self.jobs.keys ())
        self.set_statusicon_visibility ()

    def on_icon_configure_printers_activate(self, menuitem):
        if self.loop:
            env = {}
            for name, value in os.environ.iteritems ():
                if name == "SYSTEM_CONFIG_PRINTER_UI":
                    continue
                env[name] = value
            p = subprocess.Popen ([ "system-config-printer" ],
                                  close_fds=True, env=env)
            gobject.timeout_add_seconds (10, self.poll_subprocess, p)

    def poll_subprocess(self, process):
        returncode = process.poll ()
        return returncode == None

    def on_icon_quit_activate (self, menuitem):
        if self.loop:
            self.loop.quit ()

    def on_job_cancel_activate(self, menuitem):
        self.on_job_cancel_activate2(False)

    def on_job_delete_activate(self, menuitem):
        self.on_job_cancel_activate2(True)

    def on_job_cancel_activate2(self, purge_job):
        if self.jobids:
            op = CancelJobsOperation (self, self.jobids, purge_job)
            self.ops.append (op)

    def on_job_hold_activate(self, menuitem):
        try:
            c = authconn.Connection (self.JobsWindow,
                                     host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
        except RuntimeError:
            return

        for jobid in self.jobids:
            c._begin_operation (_("holding job"))
            try:
                c.setJobHoldUntil (jobid, "indefinite")
            except cups.IPPError, (e, m):
                if (e != cups.IPP_NOT_POSSIBLE and
                    e != cups.IPP_NOT_FOUND):
                    self.show_IPP_Error (e, m)
                self.monitor.update ()
                c._end_operation ()
                return
            c._end_operation ()

        del c
        self.monitor.update ()

    def on_job_release_activate(self, menuitem):
        try:
            c = authconn.Connection (self.JobsWindow,
                                     host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
        except RuntimeError:
            return

        for jobid in self.jobids:
            c._begin_operation (_("releasing job"))
            try:
                c.setJobHoldUntil (jobid, "no-hold")
            except cups.IPPError, (e, m):
                if (e != cups.IPP_NOT_POSSIBLE and
                    e != cups.IPP_NOT_FOUND):
                    self.show_IPP_Error (e, m)
                self.monitor.update ()
                c._end_operation ()
                return
            c._end_operation ()

        del c
        self.monitor.update ()

    def on_job_reprint_activate(self, menuitem):
        try:
            c = authconn.Connection (self.JobsWindow,
                                     host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
            for jobid in self.jobids:
                c.restartJob (jobid)
            del c
        except cups.IPPError, (e, m):
            self.show_IPP_Error (e, m)
            self.monitor.update ()
            return
        except RuntimeError:
            return

        self.monitor.update ()

    def on_job_retrieve_activate(self, menuitem):
        try:
            c = authconn.Connection (self.JobsWindow,
                                     host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
        except RuntimeError:
            return

        for jobid in self.jobids:
            try:
                attrs=c.getJobAttributes(jobid)
                printer_uri=attrs['job-printer-uri']
                document_count=attrs['document-count']
                for document_number in range(1, document_count+1):
                    document=c.getDocument(printer_uri, jobid, document_number)
                    tempfile = document.get('file')
                    name = document.get('document-name')
                    format = document.get('document-format', '')

                    # if there's no document-name retrieved
                    if name == None:
                        # give the default filename some meaningful name
                        name = _("retrieved")+str(document_number)
                        # add extension according to format
                        if format == 'application/postscript':
                            name = name + ".ps"
                        elif format.find('application/vnd.') != -1:
                            name = name + format.replace('application/vnd', '')
                        elif format.find('application/') != -1:
                            name = name + format.replace('application/', '.')

                    if tempfile != None:
                        dialog = gtk.FileChooserDialog (_("Save File"),
                                                        self.JobsWindow,
                                                  gtk.FILE_CHOOSER_ACTION_SAVE,
                                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                         gtk.STOCK_SAVE, gtk.RESPONSE_OK))
                        dialog.set_current_name(name)
                        dialog.set_do_overwrite_confirmation(True)

                        response = dialog.run()
                        if response == gtk.RESPONSE_OK:
                            file_to_save = dialog.get_filename()
                            try:
                                shutil.copyfile(tempfile, file_to_save)
                            except (IOError, shutil.Error):
                                debugprint("Unable to save file "+file_to_save)
                        elif response == gtk.RESPONSE_CANCEL:
                            pass
                        dialog.destroy()
                        os.unlink(tempfile)
                    else:
                        debugprint("Unable to retrieve file from job file")
                        return

            except cups.IPPError, (e, m):
                self.show_IPP_Error (e, m)
                self.monitor.update ()
                return

        del c
        self.monitor.update ()

    def on_job_move_activate(self, menuitem, job_printer_uri):
        try:
            c = authconn.Connection (self.JobsWindow,
                                     host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
            for jobid in self.jobids:
                c.moveJob (job_id=jobid, job_printer_uri=job_printer_uri)
            del c
        except cups.IPPError, (e, m):
            self.show_IPP_Error (e, m)
            self.monitor.update ()
            return
        except RuntimeError:
            return
        except AttributeError:
            # Requires pycups >= 1.9.47
            debugprint ("Move requires pycups >= 1.9.47")
            return

        self.monitor.update ()

    def on_job_authenticate_activate(self, menuitem):
        for jobid in self.jobids:
            self.display_auth_info_dialog (jobid)

    def on_refresh_clicked(self, toolbutton):
        self.monitor.refresh ()
        self.update_job_creation_times ()

    def on_job_attributes_activate(self, menuitem):
        """ For every selected job create notebook page with attributes. """
        try:
            c = cups.Connection (host=self.host,
                                 port=self.port,
                                 encryption=self.encryption)
        except RuntimeError:
            self.monitor.watcher.cups_connection_error (self)
            return False

        for jobid in self.jobids:
            if jobid not in self.jobs_attrs:
                # add new notebook page with scrollable treeview
                scrolledwindow = gtk.ScrolledWindow()
                label = gtk.Label(jobid) # notebook page has label with jobid
                page_index = self.notebook.append_page(scrolledwindow, label)
                attr_treeview = gtk.TreeView()
                scrolledwindow.add(attr_treeview)
                cell = gtk.CellRendererText ()
                attr_treeview.insert_column_with_attributes(0, _("Name"),
                                                            cell, text=0)
                cell = gtk.CellRendererText ()
                attr_treeview.insert_column_with_attributes(1, _("Value"),
                                                            cell, text=1)
                attr_store = gtk.ListStore(gobject.TYPE_STRING,
                                           gobject.TYPE_STRING)
                attr_treeview.set_model(attr_store)
                attr_treeview.get_selection().set_mode(gtk.SELECTION_NONE)
                attr_store.set_sort_column_id (0, gtk.SORT_ASCENDING)
                self.jobs_attrs[jobid] = (attr_store, page_index)
                self.update_job_attributes_viewer (jobid, conn=c)

        self.JobsAttributesWindow.show_all ()

    def update_job_attributes_viewer(self, jobid, conn=None):
        """ Update attributes store with new values. """
        if conn != None:
            c = conn
        else:
            try:
                c = cups.Connection (host=self.host,
                                     port=self.port,
                                     encryption=self.encryption)
            except RuntimeError:
                self.monitor.watcher.cups_connection_error (self)
                return False

        if jobid in self.jobs_attrs:
            (attr_store, page) = self.jobs_attrs[jobid]
            try:
                attrs = c.getJobAttributes(jobid)       # new attributes
            except AttributeError:
                return
            except cups.IPPError:
                # someone else may have purged the job,
                # remove jobs notebook page
                self.notebook.remove_page(page)
                del self.jobs_attrs[jobid]
                return

            attr_store.clear()                          # remove old attributes
            for name, value in attrs.iteritems():
                if name in ['job-id', 'job-printer-up-time']:
                    continue
                attr_store.append([name, value])

    def job_is_active (self, jobdata):
        state = jobdata.get ('job-state', cups.IPP_JOB_CANCELED)
        if state >= cups.IPP_JOB_CANCELED:
            return False

        return True

    ## Icon manipulation
    def add_state_reason_emblem (self, pixbuf, printer=None):
        worst_reason = None
        if printer == None and self.worst_reason != None:
            # Check that it's valid.
            printer = self.worst_reason.get_printer ()
            found = False
            for reason in self.printer_state_reasons.get (printer, []):
                if reason == self.worst_reason:
                    worst_reason = self.worst_reason
                    break
            if worst_reason == None:
                self.worst_reason = None

        if printer != None:
            for reason in self.printer_state_reasons.get (printer, []):
                if worst_reason == None:
                    worst_reason = reason
                elif reason > worst_reason:
                    worst_reason = reason

        if worst_reason != None:
            level = worst_reason.get_level ()
            if level > StateReason.REPORT:
                # Add an emblem to the icon.
                icon = StateReason.LEVEL_ICON[level]
                pixbuf = pixbuf.copy ()
                try:
                    theme = gtk.icon_theme_get_default ()
                    emblem = theme.load_icon (icon, 22, 0)
                    emblem.composite (pixbuf,
                                      pixbuf.get_width () / 2,
                                      pixbuf.get_height () / 2,
                                      emblem.get_width () / 2,
                                      emblem.get_height () / 2,
                                      pixbuf.get_width () / 2,
                                      pixbuf.get_height () / 2,
                                      0.5, 0.5,
                                      gtk.gdk.INTERP_BILINEAR, 255)
                except gobject.GError:
                    debugprint ("No %s icon available" % icon)

        return pixbuf

    def get_icon_pixbuf (self, have_jobs=None):
        if not self.trayicon:
            return

        if have_jobs == None:
            have_jobs = len (self.jobs.keys ()) > 0

        if have_jobs:
            pixbuf = self.icon_jobs
            for jobid, jobdata in self.jobs.iteritems ():
                jstate = jobdata.get ('job-state', cups.IPP_JOB_PENDING)
                if jstate == cups.IPP_JOB_PROCESSING:
                    pixbuf = self.icon_jobs_processing
                    break
        else:
            pixbuf = self.icon_no_jobs

        try:
            pixbuf = self.add_state_reason_emblem (pixbuf)
        except:
            nonfatalException ()

        return pixbuf

    def set_statusicon_tooltip (self, tooltip=None):
        if not self.trayicon:
            return

        if tooltip == None:
            num_jobs = len (self.jobs)
            if num_jobs == 0:
                tooltip = _("No documents queued")
            elif num_jobs == 1:
                tooltip = _("1 document queued")
            else:
                tooltip = _("%d documents queued") % num_jobs

        self.statusicon.set_tooltip (tooltip)

    def update_status (self, have_jobs=None):
        # Found out which printer state reasons apply to our active jobs.
        upset_printers = set()
        for printer, reasons in self.printer_state_reasons.iteritems ():
            if len (reasons) > 0:
                upset_printers.add (printer)
        debugprint ("Upset printers: %s" % upset_printers)

        my_upset_printers = set()
        if len (upset_printers):
            my_upset_printers = set()
            for jobid in self.active_jobs:
                # 'job-printer-name' is set by job_added/job_event
                printer = self.jobs[jobid]['job-printer-name']
                if printer in upset_printers:
                    my_upset_printers.add (printer)
            debugprint ("My upset printers: %s" % my_upset_printers)

        my_reasons = []
        for printer in my_upset_printers:
            my_reasons.extend (self.printer_state_reasons[printer])

        # Find out which is the most problematic.
        self.worst_reason = None
        if len (my_reasons) > 0:
            worst_reason = my_reasons[0]
            for reason in my_reasons:
                if reason > worst_reason:
                    worst_reason = reason
            self.worst_reason = worst_reason
            debugprint ("Worst reason: %s" % worst_reason)

        if self.worst_reason != None:
            (title, tooltip) = self.worst_reason.get_description ()
            if self.statusbar_set:
                self.statusbar.pop (0)
            self.statusbar.push (0, tooltip)
            self.statusbar_set = True
        else:
            tooltip = None
            if self.statusbar_set:
                self.statusbar.pop (0)
                self.statusbar_set = False

        if self.trayicon:
            pixbuf = self.get_icon_pixbuf (have_jobs=have_jobs)
            self.set_statusicon_from_pixbuf (pixbuf)
            self.set_statusicon_visibility ()
            self.set_statusicon_tooltip (tooltip=tooltip)

    ## Notifications
    def notify_printer_state_reason_if_important (self, reason):
        level = reason.get_level ()
        if level < StateReason.WARNING:
            # Not important enough to justify a notification.
            return

        self.notify_printer_state_reason (reason)

    def notify_printer_state_reason (self, reason):
        tuple = reason.get_tuple ()
        if self.state_reason_notifications.has_key (tuple):
            debugprint ("Already sent notification for %s" % repr (reason))
            return

        if reason.get_reason () == "com.apple.print.recoverable":
            return

        level = reason.get_level ()
        if (level == StateReason.ERROR or
            reason.get_reason () == "connecting-to-device"):
            urgency = pynotify.URGENCY_NORMAL
        else:
            urgency = pynotify.URGENCY_LOW

        (title, text) = reason.get_description ()
        notification = pynotify.Notification (title, text, 'printer')
        reason.user_notified = True
        notification.set_urgency (urgency)
        if "actions" in pynotify.get_server_caps():
            notification.set_timeout (pynotify.EXPIRES_NEVER)
        notification.connect ('closed',
                              self.on_state_reason_notification_closed)
        self.state_reason_notifications[reason.get_tuple ()] = notification
        self.set_statusicon_visibility ()
        notification.attach_to_status_icon (self.statusicon)
        try:
            notification.show ()
        except gobject.GError:
            nonfatalException ()

    def on_state_reason_notification_closed (self, notification, reason=None):
        debugprint ("Notification %s closed" % repr (notification))
        notification.set_data ('closed', True)
        self.set_statusicon_visibility ()
        return

    def notify_completed_job (self, jobid):
        job = self.jobs.get (jobid, {})
        document = job.get ('job-name', _("Unknown"))
        printer_uri = job.get ('job-printer-uri')
        if printer_uri != None:
            # Determine if this printer is remote.  There's no need to
            # show a notification if the printer is connected to this
            # machine.

            # Find out the device URI.  We might already have
            # determined this if authentication was required.
            device_uri = job.get ('device-uri')

            if device_uri == None:
                pattrs = ['device-uri']
                c = authconn.Connection (self.JobsWindow,
                                         host=self.host,
                                         port=self.port,
                                         encryption=self.encryption)
                try:
                    attrs = c.getPrinterAttributes (uri=printer_uri,
                                                    requested_attributes=pattrs)
                except cups.IPPError:
                    return

                device_uri = attrs.get ('device-uri')

            if device_uri != None:
                (scheme, rest) = urllib.splittype (device_uri)
                if scheme not in ['socket', 'ipp', 'http', 'smb']:
                    return

        printer = job.get ('job-printer-name', _("Unknown"))
        notification = pynotify.Notification (_("Document printed"),
                                              _("Document `%s' has been sent "
                                                "to `%s' for printing.") %
                                              (document, printer),
                                              'printer')
        notification.set_urgency (pynotify.URGENCY_LOW)
        notification.connect ('closed',
                              self.on_completed_job_notification_closed)
        notification.set_data ('jobid', jobid)
        self.completed_job_notifications[jobid] = notification
        self.set_statusicon_visibility ()
        notification.attach_to_status_icon (self.statusicon)
        try:
            notification.show ()
        except gobject.GError:
            nonfatalException ()

    def on_completed_job_notification_closed (self, notification, reason=None):
        jobid = notification.get_data ('jobid')
        del self.completed_job_notifications[jobid]
        self.set_statusicon_visibility ()

    ## monitor.Watcher interface
    def current_printers_and_jobs (self, mon, printers, jobs):
        monitor.Watcher.current_printers_and_jobs (self, mon, printers, jobs)
        self.set_process_pending (False)
        self.store.clear ()
        self.jobs = {}
        self.jobiters = {}
        self.printer_uri_index = PrinterURIIndex (names=printers)
        connection = None
        for jobid, jobdata in jobs.iteritems ():
            uri = jobdata.get ('job-printer-uri', '')
            try:
                printer = self.printer_uri_index.lookup (uri,
                                                         connection=connection)
            except KeyError:
                printer = uri

            if self.specific_dests and printer not in self.specific_dests:
                continue

            jobdata['job-printer-name'] = printer

            self.add_job (jobid, jobdata, connection=connection)

        self.jobs = jobs
        self.active_jobs = set()
        for jobid, jobdata in jobs.iteritems ():
            if (self.job_is_active (jobdata) and
                jobdata.has_key('job-printer-name')):
                self.active_jobs.add (jobid)

        self.set_process_pending (True)
        self.update_status ()

    def job_added (self, mon, jobid, eventname, event, jobdata):
        monitor.Watcher.job_added (self, mon, jobid, eventname, event, jobdata)

        uri = jobdata.get ('job-printer-uri', '')
        try:
            printer = self.printer_uri_index.lookup (uri)
        except KeyError:
            printer = uri

        if self.specific_dests and printer not in self.specific_dests:
            return

        jobdata['job-printer-name'] = printer

        # We may be showing this job already, perhaps because we are showing
        # completed jobs and one was reprinted.
        if not self.jobiters.has_key (jobid):
            self.add_job (jobid, jobdata)

        if self.job_is_active (jobdata):
            self.active_jobs.add (jobid)
        elif jobid in self.active_jobs:
            self.active_jobs.remove (jobid)

        self.update_status (have_jobs=True)
        if self.trayicon:
            if not self.job_is_active (jobdata):
                return

            for reason in self.printer_state_reasons.get (printer, []):
                if not reason.user_notified:
                    self.notify_printer_state_reason_if_important (reason)

    def job_event (self, mon, jobid, eventname, event, jobdata):
        monitor.Watcher.job_event (self, mon, jobid, eventname, event, jobdata)

        uri = jobdata.get ('job-printer-uri', '')
        try:
            printer = self.printer_uri_index.lookup (uri)
        except KeyError:
            printer = uri
        jobdata['job-printer-name'] = printer

        if self.job_is_active (jobdata):
            self.active_jobs.add (jobid)
        elif jobid in self.active_jobs:
            self.active_jobs.remove (jobid)

        self.update_job (jobid, jobdata)
        self.update_status ()
        jobdata = self.jobs[jobid]

        # If the job has finished, let the user know.
        if self.trayicon and (eventname == 'job-completed' or
                              (eventname == 'job-state-changed' and
                               event['job-state'] == cups.IPP_JOB_COMPLETED)):
            reasons = event['job-state-reasons']
            if type (reasons) != list:
                reasons = [reasons]

            canceled = False
            for reason in reasons:
                if reason.startswith ("job-canceled"):
                    canceled = True
                    break

            if not canceled:
                self.notify_completed_job (jobid)

        # Look out for stopped jobs.
        if (self.trayicon and
            (eventname == 'job-stopped' or
             (eventname == 'job-state-changed' and
              event['job-state'] in [cups.IPP_JOB_STOPPED,
                                     cups.IPP_JOB_PENDING])) and
            not jobid in self.stopped_job_prompts):
            # Why has the job stopped?  It might be due to a job error
            # of some sort, or it might be that the backend requires
            # authentication.  If the latter, the job will be held not
            # stopped, and the job-hold-until attribute will be
            # 'auth-info-required'.  This was already checked for in
            # update_job.
            may_be_problem = True
            jstate = jobdata['job-state']
            if (jstate == cups.IPP_JOB_PROCESSING or
                (jstate == cups.IPP_JOB_HELD and
                 jobdata['job-hold-until'] == 'auth-info-required')):
                # update_job already dealt with this.
                may_be_problem = False
            else:
                # Other than that, unfortunately the only
                # clue we get is the notify-text, which is not
                # translated into our native language.  We'd better
                # try parsing it.  In CUPS-1.3.6 the possible strings
                # are:
                #
                # "Job stopped due to filter errors; please consult
                # the error_log file for details."
                #
                # "Job stopped due to backend errors; please consult
                # the error_log file for details."
                #
                # "Job held due to backend errors; please consult the
                # error_log file for details."
                #
                # "Authentication is required for job %d."
                # [This case is handled in the update_job method.]
                #
                # "Job stopped due to printer being paused"
                # [This should be ignored, as the job was doing just
                # fine until the printer was stopped for other reasons.]
                notify_text = event['notify-text']
                document = jobdata['job-name']
                if notify_text.find ("backend errors") != -1:
                    message = _("There was a problem sending document `%s' "
                                "(job %d) to the printer.") % (document, jobid)
                elif notify_text.find ("filter errors") != -1:
                    message = _("There was a problem processing document `%s' "
                                "(job %d).") % (document, jobid)
                elif (notify_text.find ("being paused") != -1 or
                      jstate != cups.IPP_JOB_STOPPED):
                    may_be_problem = False
                else:
                    # Give up and use the provided message untranslated.
                    message = _("There was a problem printing document `%s' "
                                "(job %d): `%s'.") % (document, jobid,
                                                      notify_text)

            if may_be_problem:
                debugprint ("Problem detected")
                self.toggle_window_display (self.statusicon, force_show=True)
                dialog = gtk.Dialog (_("Print Error"), self.JobsWindow, 0,
                                     (_("_Diagnose"), gtk.RESPONSE_NO,
                                        gtk.STOCK_OK, gtk.RESPONSE_OK))
                dialog.set_default_response (gtk.RESPONSE_OK)
                dialog.set_border_width (6)
                dialog.set_resizable (False)
                dialog.set_icon_name (ICON)
                hbox = gtk.HBox (False, 12)
                hbox.set_border_width (6)
                image = gtk.Image ()
                image.set_from_stock (gtk.STOCK_DIALOG_ERROR,
                                      gtk.ICON_SIZE_DIALOG)
                hbox.pack_start (image, False, False, 0)
                vbox = gtk.VBox (False, 12)

                markup = ('<span weight="bold" size="larger">' +
                          _("Print Error") + '</span>\n\n' +
                          saxutils.escape (message))
                try:
                    if event['printer-state'] == cups.IPP_PRINTER_STOPPED:
                        name = event['printer-name']
                        markup += ' '
                        markup += (_("The printer called `%s' has "
                                     "been disabled.") % name)
                except KeyError:
                    pass

                label = gtk.Label (markup)
                label.set_use_markup (True)
                label.set_line_wrap (True)
                label.set_alignment (0, 0)
                vbox.pack_start (label, False, False, 0)
                hbox.pack_start (vbox, False, False, 0)
                dialog.vbox.pack_start (hbox)
                dialog.connect ('response',
                                self.print_error_dialog_response, jobid)
                self.stopped_job_prompts.add (jobid)
                dialog.show_all ()

    def job_removed (self, mon, jobid, eventname, event):
        monitor.Watcher.job_removed (self, mon, jobid, eventname, event)

        # If the job has finished, let the user know.
        if self.trayicon and (eventname == 'job-completed' or
                              (eventname == 'job-state-changed' and
                               event['job-state'] == cups.IPP_JOB_COMPLETED)):
            reasons = event['job-state-reasons']
            debugprint (reasons)
            if type (reasons) != list:
                reasons = [reasons]

            canceled = False
            for reason in reasons:
                if reason.startswith ("job-canceled"):
                    canceled = True
                    break

            if not canceled:
                self.notify_completed_job (jobid)

        if self.jobiters.has_key (jobid):
            self.store.remove (self.jobiters[jobid])
            del self.jobiters[jobid]
            del self.jobs[jobid]

        if jobid in self.active_jobs:
            self.active_jobs.remove (jobid)

        if self.jobs_attrs.has_key (jobid):
            del self.jobs_attrs[jobid]

        self.update_status ()

    def state_reason_added (self, mon, reason):
        monitor.Watcher.state_reason_added (self, mon, reason)

        (title, text) = reason.get_description ()
        printer = reason.get_printer ()

        try:
            l = self.printer_state_reasons[printer]
        except KeyError:
            l = []
            self.printer_state_reasons[printer] = l

        reason.user_notified = False
        l.append (reason)
        self.update_status ()
        self.treeview.queue_draw ()

        if not self.trayicon:
            return

        # Find out if the user has jobs queued for that printer.
        for job, data in self.jobs.iteritems ():
            if not self.job_is_active (data):
                continue
            if data['job-printer-name'] == printer:
                # Yes!  Notify them of the state reason, if necessary.
                self.notify_printer_state_reason_if_important (reason)
                break

    def state_reason_removed (self, mon, reason):
        monitor.Watcher.state_reason_removed (self, mon, reason)

        printer = reason.get_printer ()
        try:
            reasons = self.printer_state_reasons[printer]
        except KeyError:
            debugprint ("Printer not found")
            return

        try:
            i = reasons.index (reason)
        except IndexError:
            debugprint ("Reason not found")
            return

        del reasons[i]

        self.update_status ()
        self.treeview.queue_draw ()

        if not self.trayicon:
            return

        tuple = reason.get_tuple ()
        try:
            notification = self.state_reason_notifications[tuple]
            if notification.get_data ('closed') != True:
                try:
                    notification.close ()
                except glib.GError:
                    # Can fail if the notification wasn't even shown
                    # yet (as in bug #545733).
                    pass

            del self.state_reason_notifications[tuple]
            self.set_statusicon_visibility ()
        except KeyError:
            pass

    def still_connecting (self, mon, reason):
        monitor.Watcher.still_connecting (self, mon, reason)
        if not self.trayicon:
            return

        self.notify_printer_state_reason (reason)

    def now_connected (self, mon, printer):
        monitor.Watcher.now_connected (self, mon, printer)

        if not self.trayicon:
            return

        # Find the connecting-to-device state reason.
        try:
            reasons = self.printer_state_reasons[printer]
            reason = None
            for r in reasons:
                if r.get_reason () == "connecting-to-device":
                    reason = r
                    break
        except KeyError:
            debugprint ("Couldn't find state reason (no reasons)!")

        if reason != None:
            tuple = reason.get_tuple ()
        else:
            debugprint ("Couldn't find state reason in list!")
            for (level,
                 p,
                 r) in self.state_reason_notifications.keys ():
                if p == printer and r == "connecting-to-device":
                    debugprint ("Found from notifications list")
                    tuple = (level, p, r)
                    break

        try:
            notification = self.state_reason_notifications[tuple]
        except KeyError:
            debugprint ("Unexpected now_connected signal")
            return

        if notification.get_data ('closed') != True:
            try:
                notification.close ()
            except glib.GError:
                # Can fail if the notification wasn't even shown
                pass
            notification.set_data ('closed', True)

    def printer_added (self, mon, printer):
        monitor.Watcher.printer_added (self, mon, printer)
        self.printer_uri_index.add_printer (printer)

    def printer_event (self, mon, printer, eventname, event):
        monitor.Watcher.printer_event (self, mon, printer, eventname, event)
        self.printer_uri_index.update_from_attrs (printer, event)

    def printer_removed (self, mon, printer):
        monitor.Watcher.printer_removed (self, mon, printer)
        self.printer_uri_index.remove_printer (printer)

    ### Cell data functions
    def _set_job_job_number_text (self, column, cell, model, iter, *data):
        cell.set_property("text", str (model.get_value (iter, 0)))

    def _set_job_user_text (self, column, cell, model, iter, *data):
        jobid = model.get_value (iter, 0)
        job = self.jobs[jobid]
        cell.set_property("text", job.get ('job-originating-user-name',
                                           _("Unknown")))

    def _set_job_document_text (self, column, cell, model, iter, *data):
        jobid = model.get_value (iter, 0)
        job = self.jobs[jobid]
        cell.set_property("text", job.get('job-name', _("Unknown")))

    def _set_job_printer_text (self, column, cell, model, iter, *data):
        jobid = model.get_value (iter, 0)
        reasons = self.jobs[jobid].get('job-state-reasons')
        if reasons == 'printer-stopped':
            reason = ' - ' + _("disabled")
        else:
            reason = ''
        cell.set_property("text", self.jobs[jobid]['job-printer-name']+reason)

    def _set_job_size_text (self, column, cell, model, iter, *data):
        jobid = model.get_value (iter, 0)
        job = self.jobs[jobid]
        size = _("Unknown")
        if job.has_key ('job-k-octets'):
            size = str (job['job-k-octets']) + 'k'
        cell.set_property("text", size)

    def _find_job_state_text (self, job):
        data = self.jobs[job]
        jstate = data.get ('job-state', cups.IPP_JOB_PROCESSING)
        s = int (jstate)
        job_requires_auth = (s == cups.IPP_JOB_HELD and
                             data.get ('job-hold-until', 'none') ==
                             'auth-info-required')
        state = None
        if job_requires_auth:
            state = _("Held for authentication")
        elif s == cups.IPP_JOB_HELD:
            state = _("Held")
            until = data.get ('job-hold-until')
            if until != None:
                try:
                    colon1 = until.find (':')
                    if colon1 != -1:
                        now = time.gmtime ()
                        hh = int (until[:colon1])
                        colon2 = until[colon1 + 1:].find (':')
                        if colon2 != -1:
                            colon2 += colon1 + 1
                            mm = int (until[colon1 + 1:colon2])
                            ss = int (until[colon2 + 1:])
                        else:
                            mm = int (until[colon1 + 1:])
                            ss = 0

                        day = now.tm_mday
                        if (hh < now.tm_hour or
                            (hh == now.tm_hour and
                             (mm < now.tm_min or
                              (mm == now.tm_min and ss < now.tm_sec)))):
                            day += 1

                        hold = (now.tm_year, now.tm_mon, day,
                                hh, mm, ss, 0, 0, -1)
                        old_tz = os.environ.get("TZ")
                        os.environ["TZ"] = "UTC"
                        simpletime = time.mktime (hold)

                        if old_tz == None:
                            del os.environ["TZ"]
                        else:
                            os.environ["TZ"] = old_tz

                        local = time.localtime (simpletime)
                        state = _("Held until %s") % time.strftime ("%X", local)
                except ValueError:
                    pass
            if until == "day-time":
                state = _("Held until day-time")
            elif until == "evening":
                state = _("Held until evening")
            elif until == "night":
                state = _("Held until night-time")
            elif until == "second-shift":
                state = _("Held until second shift")
            elif until == "third-shift":
                state = _("Held until third shift")
            elif until == "weekend":
                state = _("Held until weekend")
        else:
            try:
                state = { cups.IPP_JOB_PENDING: _("Pending"),
                          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") }[s]
            except IndexError:
                pass

        if state == None:
            state = _("Unknown")

        return state

    def _set_job_status_icon (self, column, cell, model, iter, *data):
        jobid = model.get_value (iter, 0)
        data = self.jobs[jobid]
        jstate = data.get ('job-state', cups.IPP_JOB_PROCESSING)
        s = int (jstate)
        if s == cups.IPP_JOB_PROCESSING:
            icon = self.icon_jobs_processing
        else:
            icon = self.icon_jobs

        if s == cups.IPP_JOB_HELD:
            theme = gtk.icon_theme_get_default ()
            emblem = theme.load_icon (gtk.STOCK_MEDIA_PAUSE, 22 / 2, 0)
            copy = icon.copy ()
            emblem.composite (copy, 0, 0,
                              copy.get_width (),
                              copy.get_height (),
                              copy.get_width () / 2 - 1,
                              copy.get_height () / 2 - 1,
                              1.0, 1.0,
                              gtk.gdk.INTERP_NEAREST, 255)
            icon = copy
        else:
            # Check state reasons.
            printer = data['job-printer-name']
            icon = self.add_state_reason_emblem (icon, printer=printer)

        cell.set_property ("pixbuf", icon)

    def _set_job_status_text (self, column, cell, model, iter, *data):
        jobid = model.get_value (iter, 0)
        data = self.jobs[jobid]
        try:
            text = data['_status_text']
        except KeyError:
            text = self._find_job_state_text (jobid)
            data['_status_text'] = text

        printer = data['job-printer-name']
        reasons = self.printer_state_reasons.get (printer, [])
        if len (reasons) > 0:
            worst_reason = reasons[0]
            for reason in reasons[1:]:
                if reason > worst_reason:
                    worst_reason = reason
            (title, unused) = worst_reason.get_description ()
            text += " - " + title

        cell.set_property ("text", text)

    def _on_treeview_query_tooltip (self, tv, x, y, keyboard_mode, tooltip):
        if keyboard_mode:
            (path, column) = tv.get_cursor ()
            if path is None:
                return False
        else:
            bin_x, bin_y = tv.convert_widget_to_bin_window_coords (x, y)
            ret = tv.get_path_at_pos (bin_x, bin_y)
            if ret is None:
                return False
            path = ret[0]
            column = ret[1]

        cells = column.get_cell_renderers ()
        for cell in cells:
            if type (cell) == gtk.CellRendererText:
                tooltip.set_markup (cell.get_property ("text"))
                self.treeview.set_tooltip_cell (tooltip, path, column, cell)
                break

        return True

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 2.0 [PHP 7 Update] [25.02.2019] maintained by KaizenLouie | C99Shell Github | Generation time: 0.0277 ]--