- Include other fixes from upstream including:

- OpenPrinting API change (trac #74).
- libnotify API change for 'closed' signal.
- Notification for job authentication (trac #91).
- Glade delete-event fixes (trac #88).
- Pre-fill username in job authentication dialog (trac #87).
This commit is contained in:
Tim Waugh 2008-08-14 15:26:32 +00:00
parent c35b1e7a33
commit da5a905204
3 changed files with 468 additions and 88 deletions

View File

@ -1,85 +0,0 @@
diff -up system-config-printer-1.0.5/authconn.py.forbidden system-config-printer-1.0.5/authconn.py
--- system-config-printer-1.0.5/authconn.py.forbidden 2008-08-11 14:33:09.000000000 +0100
+++ system-config-printer-1.0.5/authconn.py 2008-08-13 22:21:03.000000000 +0100
@@ -130,7 +130,13 @@ class Connection:
def _authloop (self, fname, fn, *args, **kwds):
self._passes = 0
+ c = self._connection
while self._perform_authentication () != 0:
+ if c != self._connection:
+ # We have reconnected.
+ fn = getattr (self._connection, fname)
+ c = self._connection
+
try:
result = fn.__call__ (*args, **kwds)
@@ -146,15 +152,17 @@ class Connection:
else:
raise
except cups.HTTPError, (s,):
- if not self._cancel and s == cups.HTTP_UNAUTHORIZED:
- self._failed ()
+ if not self._cancel and (s == cups.HTTP_UNAUTHORIZED or
+ s == cups.HTTP_FORBIDDEN):
+ self._failed (s == cups.HTTP_FORBIDDEN)
else:
raise
return result
- def _failed (self):
+ def _failed (self, forbidden=False):
self._has_failed = True
+ self._forbidden = forbidden
def _password_callback (self, prompt):
debugprint ("Got password callback")
@@ -173,6 +181,7 @@ class Connection:
# Haven't yet tried the operation. Set the password
# callback and return > 0 so we try it for the first time.
self._has_failed = False
+ self._forbidden = False
self._auth_called = False
self._cancel = False
cups.setPasswordCB (self._password_callback)
@@ -192,16 +201,17 @@ class Connection:
# Tried the operation without a password and it failed.
if (self._try_as_root and
self._user != 'root' and
- self._server[0] == '/'):
+ (self._server[0] == '/' or self._forbidden)):
# This is a UNIX domain socket connection so we should
- # not have needed a password, and so the operation must
- # not be something that the current user is authorised to
- # do. They need to try as root, and supply the password.
- # However, to get the right prompt, we need to try as
- # root but with no password first.
+ # not have needed a password (or it is not a UDS but
+ # we got an HTTP_FORBIDDEN response), and so the
+ # operation must not be something that the current
+ # user is authorised to do. They need to try as root,
+ # and supply the password. However, to get the right
+ # prompt, we need to try as root but with no password
+ # first.
debugprint ("Authentication: Try as root")
self._use_user = 'root'
- cups.setUser (self._use_user)
self._auth_called = False
self._connect ()
return 1
diff -U0 system-config-printer-1.0.5/ChangeLog.forbidden system-config-printer-1.0.5/ChangeLog
--- system-config-printer-1.0.5/ChangeLog.forbidden 2008-08-11 14:51:07.000000000 +0100
+++ system-config-printer-1.0.5/ChangeLog 2008-08-13 22:21:03.000000000 +0100
@@ -0,0 +1,10 @@
+2008-08-13 Tim Waugh <twaugh@redhat.com>
+
+ * authconn.py (Connection._authloop): Re-bind to the named method
+ on reconnection. Handle HTTP_FORBIDDEN (trac #89).
+ (Connection._failed): New optional parameter forbidden. Remember
+ whether we saw HTTP_FORBIDDEN.
+ (Connection._perform_authentication): Initialise self._forbidden.
+ Use it to decide whether to try as root. Don't call setUser()
+ here; _connect() will do that.
+

View File

@ -0,0 +1,457 @@
diff -up system-config-printer-1.0.5/authconn.py.git system-config-printer-1.0.5/authconn.py
--- system-config-printer-1.0.5/authconn.py.git 2008-08-11 14:33:09.000000000 +0100
+++ system-config-printer-1.0.5/authconn.py 2008-08-14 16:23:49.000000000 +0100
@@ -130,7 +130,13 @@ class Connection:
def _authloop (self, fname, fn, *args, **kwds):
self._passes = 0
+ c = self._connection
while self._perform_authentication () != 0:
+ if c != self._connection:
+ # We have reconnected.
+ fn = getattr (self._connection, fname)
+ c = self._connection
+
try:
result = fn.__call__ (*args, **kwds)
@@ -146,15 +152,17 @@ class Connection:
else:
raise
except cups.HTTPError, (s,):
- if not self._cancel and s == cups.HTTP_UNAUTHORIZED:
- self._failed ()
+ if not self._cancel and (s == cups.HTTP_UNAUTHORIZED or
+ s == cups.HTTP_FORBIDDEN):
+ self._failed (s == cups.HTTP_FORBIDDEN)
else:
raise
return result
- def _failed (self):
+ def _failed (self, forbidden=False):
self._has_failed = True
+ self._forbidden = forbidden
def _password_callback (self, prompt):
debugprint ("Got password callback")
@@ -173,6 +181,7 @@ class Connection:
# Haven't yet tried the operation. Set the password
# callback and return > 0 so we try it for the first time.
self._has_failed = False
+ self._forbidden = False
self._auth_called = False
self._cancel = False
cups.setPasswordCB (self._password_callback)
@@ -192,16 +201,17 @@ class Connection:
# Tried the operation without a password and it failed.
if (self._try_as_root and
self._user != 'root' and
- self._server[0] == '/'):
+ (self._server[0] == '/' or self._forbidden)):
# This is a UNIX domain socket connection so we should
- # not have needed a password, and so the operation must
- # not be something that the current user is authorised to
- # do. They need to try as root, and supply the password.
- # However, to get the right prompt, we need to try as
- # root but with no password first.
+ # not have needed a password (or it is not a UDS but
+ # we got an HTTP_FORBIDDEN response), and so the
+ # operation must not be something that the current
+ # user is authorised to do. They need to try as root,
+ # and supply the password. However, to get the right
+ # prompt, we need to try as root but with no password
+ # first.
debugprint ("Authentication: Try as root")
self._use_user = 'root'
- cups.setUser (self._use_user)
self._auth_called = False
self._connect ()
return 1
diff -U0 system-config-printer-1.0.5/ChangeLog.git system-config-printer-1.0.5/ChangeLog
--- system-config-printer-1.0.5/ChangeLog.git 2008-08-11 14:51:07.000000000 +0100
+++ system-config-printer-1.0.5/ChangeLog 2008-08-14 16:23:49.000000000 +0100
@@ -0,0 +1,67 @@
+2008-08-14 Tim Waugh <twaugh@redhat.com>
+
+ * cupshelpers/openprinting.py
+ (OpenPrinting.listDrivers.parse_result): OpenPrinting API change:
+ freesoftware -> nonfreesoftware (trac #74).
+ * system-config-printer.py
+ (NewPrinterGUI.on_tvNPDownloadableDrivers_cursor_changed):
+ Likewise.
+
+2008-08-14 Tim Waugh <twaugh@redhat.com>
+
+ * jobviewer.py (JobViewer.on_new_printer_notification_closed):
+ Optional reason parameter to handle newer libnotify signal
+ interface.
+ (JobViewer.on_state_reason_notification_closed): Likewise.
+ (JobViewer.on_auth_notification_closed): Likewise.
+
+2008-08-14 Tim Waugh <twaugh@redhat.com>
+
+ * jobviewer.py (JobViewer.update_job): Display a notification when
+ a job requires authentication (trac #91).
+ (JobViewer.on_auth_notification_authenticate): New method.
+ Display an authentication dialog when the notification's
+ authenticate action is triggered.
+ (JobViewer.on_auth_notification_closed): New method. Handle an
+ auth notification being closed.
+
+2008-08-14 Tim Waugh <twaugh@redhat.com>
+
+ * jobviewer.py (JobViewer.update_job): Moved code for displaying
+ authentication dialog...
+ (JobViewer.display_auth_info_dialog): ...here. New method.
+
+2008-08-14 Rui Tiago Cação Matos <tiagomatos@gmail.com>
+
+ * system-config-printer.py (GUI.__init__):
+ * system-config-printer.py (NewPrinterGUI.__init__): Since some
+ dialogs are reused we can't let the delete-event's default handler
+ destroy them.
+ * system-config-printer.py (on_delete_just_hide): This method is
+ connected to those dialog's delete-event (trac #88).
+
+2008-08-14 Tim Waugh <twaugh@redhat.com>
+
+ * jobviewer.py (JobViewer.update_job): When a job is held for
+ authentication, say so in the status column.
+
+2008-08-14 Tim Waugh <twaugh@redhat.com>
+
+ * jobviewer.py (JobViewer.update_job): Show authentication dialog
+ in the middle of the screen (trac #90).
+
+2008-08-13 Tim Waugh <twaugh@redhat.com>
+
+ * authconn.py (Connection._authloop): Re-bind to the named method
+ on reconnection. Handle HTTP_FORBIDDEN (trac #89).
+ (Connection._failed): New optional parameter forbidden. Remember
+ whether we saw HTTP_FORBIDDEN.
+ (Connection._perform_authentication): Initialise self._forbidden.
+ Use it to decide whether to try as root. Don't call setUser()
+ here; _connect() will do that.
+
+2008-08-12 Tim Waugh <twaugh@redhat.com>
+
+ * jobviewer.py (JobViewer.update_job): Pre-fill the username field
+ when asking for authentication for the job (trac #87).
+
diff -up system-config-printer-1.0.5/cupshelpers/openprinting.py.git system-config-printer-1.0.5/cupshelpers/openprinting.py
--- system-config-printer-1.0.5/cupshelpers/openprinting.py.git 2008-08-11 10:05:02.000000000 +0100
+++ system-config-printer-1.0.5/cupshelpers/openprinting.py 2008-08-14 16:23:49.000000000 +0100
@@ -217,7 +217,7 @@ class OpenPrinting:
# 'supplier': supplier,
# 'license': short license string e.g. GPLv2,
# 'licensetext': license text (HTML),
- # 'freesoftware': Boolean,
+ # 'nonfreesoftware': Boolean,
# 'patents': Boolean,
# 'shortdescription': short description,
# 'recommended': Boolean,
@@ -260,10 +260,15 @@ class OpenPrinting:
if element != None:
dict['licensetext'] = element.text
- for boolean in ['freesoftware', 'recommended',
+ for boolean in ['nonfreesoftware', 'recommended',
'patents']:
dict[boolean] = driver.find (boolean) != None
+ # Make a 'freesoftware' tag for compatibility with
+ # how the OpenPrinting API used to work (see trac
+ # #74).
+ dict['freesoftware'] = not dict['nonfreesoftware']
+
if not dict.has_key ('name') or not dict.has_key ('url'):
continue
diff -up system-config-printer-1.0.5/jobviewer.py.git system-config-printer-1.0.5/jobviewer.py
--- system-config-printer-1.0.5/jobviewer.py.git 2008-08-11 14:51:07.000000000 +0100
+++ system-config-printer-1.0.5/jobviewer.py 2008-08-14 16:23:49.000000000 +0100
@@ -31,6 +31,7 @@ import gtk.glade
import monitor
import os
import pango
+import pwd
import sys
import time
@@ -160,6 +161,7 @@ class JobViewer (monitor.Watcher):
self.num_jobs_when_hidden = 0
self.connecting_to_device = {} # dict of printer->time first seen
self.state_reason_notifications = {}
+ self.auth_notifications = {}
self.job_creation_times_timer = None
self.special_status_icon = False
self.new_printer_notifications = {}
@@ -320,7 +322,7 @@ class JobViewer (monitor.Watcher):
notification.attach_to_status_icon (self.statusicon)
notification.show ()
- def on_new_printer_notification_closed (self, notification):
+ 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 ()
@@ -471,17 +473,24 @@ class JobViewer (monitor.Watcher):
store.set_value (iter, 4, size)
state = None
+ job_requires_auth = False
if data.has_key ('job-state'):
try:
jstate = data['job-state']
s = int (jstate)
- 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") }[s]
+ job_requires_auth = (jstate == cups.IPP_JOB_HELD and
+ data.get ('job-hold-until', 'none') ==
+ 'auth-info-required')
+ if job_requires_auth:
+ state = _("Held for authentication")
+ else:
+ 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") }[s]
except ValueError:
pass
except IndexError:
@@ -492,64 +501,115 @@ class JobViewer (monitor.Watcher):
store.set_value (iter, 6, state)
# Check whether authentication is required.
- if (self.trayicon and
- data['job-state'] == cups.IPP_JOB_HELD and
- data.get ('job-hold-until', 'none') == 'auth-info-required' and
- not self.auth_info_dialog):
+ if self.trayicon:
+ if (job_requires_auth and
+ not self.auth_notifications.has_key (job) and
+ not self.auth_info_dialog):
+ try:
+ cups.require ("1.9.37")
+ except:
+ debugprint ("Authentication required but "
+ "authenticateJob() not available")
+ return
+
+ title = _("Authentication Required")
+ text = _("Job requires authentication to proceed.")
+ notification = pynotify.Notification (title, text, 'printer')
+ notification.set_data ('job-id', job)
+ notification.set_urgency (pynotify.URGENCY_NORMAL)
+ notification.set_timeout (pynotify.EXPIRES_NEVER)
+ notification.connect ('closed',
+ self.on_auth_notification_closed)
+ self.set_statusicon_visibility ()
+ notification.attach_to_status_icon (self.statusicon)
+ notification.add_action ("authenticate", _("Authenticate"),
+ self.on_auth_notification_authenticate)
+ notification.show ()
+ self.auth_notifications[job] = notification
+ elif (not job_requires_auth and
+ self.auth_notifications.has_key (job)):
+ self.auth_notifications[job].close ()
+
+ def on_auth_notification_closed (self, notification, reason=None):
+ job = notification.get_data ('job-id')
+ debugprint ("auth notification closed for job %s" % job)
+ del self.auth_notifications[job]
+
+ def on_auth_notification_authenticate (self, notification, action):
+ job = notification.get_data ('job-id')
+ debugprint ("auth notification authenticate for job %s" % job)
+ self.display_auth_info_dialog (job)
+
+ def display_auth_info_dialog (self, job):
+ data = self.jobs[job]
+ # Find out which auth-info is required.
+ try:
+ c = authconn.Connection (self.MainWindow)
try:
- cups.require ("1.9.37")
- except:
- debugprint ("Authentication required but "
- "authenticateJob() not available")
- return
+ uri = data['job-printer-uri']
+ attributes = c.getPrinterAttributes (uri = uri)
+ except TypeError: # uri keyword introduced in pycups-1.9.32
+ debugprint ("Fetching printer attributes by name")
+ attributes = c.getPrinterAttributes (printer)
+ except cups.IPPError, (e, m):
+ self.show_IPP_Error (e, m)
+ return
+ except RuntimeError:
+ debugprint ("Failed to connect when fetching printer attrs")
+ return
- # Find out which auth-info is required.
+ try:
+ auth_info_required = attributes['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]
+
+ if auth_info_required == ['negotiate']:
+ # Try Kerberos authentication.
try:
- c = authconn.Connection (self.MainWindow)
- try:
- uri = data['job-printer-uri']
- attributes = c.getPrinterAttributes (uri = uri)
- except TypeError: # uri keyword introduced in pycups-1.9.32
- debugprint ("Fetching printer attributes by name")
- attributes = c.getPrinterAttributes (printer)
+ debugprint ("Trying Kerberos auth for job %d" % jobid)
+ c.authenticateJob (jobid)
+ except TypeError:
+ # Requires pycups-1.9.39 for optional auth parameter.
+ # Treat this as a normal job error.
+ debugprint ("... need newer pycups for that")
+ return
except cups.IPPError, (e, m):
self.show_IPP_Error (e, m)
return
- except RuntimeError:
- debugprint ("Failed to connect when fetching printer attrs")
- return
- try:
- auth_info_required = attributes['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]
+ dialog = authconn.AuthDialog (auth_info_required=auth_info_required)
+ dialog.set_position (gtk.WIN_POS_CENTER)
- if auth_info_required == ['negotiate']:
- # Try Kerberos authentication.
- try:
- debugprint ("Trying Kerberos auth for job %d" % jobid)
- c.authenticateJob (jobid)
- except TypeError:
- # Requires pycups-1.9.39 for optional auth parameter.
- # Treat this as a normal job error.
- debugprint ("... need newer pycups for that")
- return
- except cups.IPPError, (e, m):
- self.show_IPP_Error (e, m)
- return
+ # Pre-fill 'username' field.
+ if 'username' in auth_info_required:
+ try:
+ auth_info = map (lambda x: '', auth_info_required)
+ username = pwd.getpwuid (os.getuid ())[0]
+ ind = auth_info_required.index ('username')
+ auth_info[ind] = username
+ dialog.set_auth_info (auth_info)
+
+ index = 0
+ for field in auth_info_required:
+ if auth_info[index] == '':
+ # Focus on the first empty field.
+ dialog.field_grab_focus (field)
+ break
+ index += 1
+ except:
+ nonfatalException ()
- dialog = authconn.AuthDialog (auth_info_required=auth_info_required)
- dialog.set_prompt (_("Authentication required for "
- "printing document `%s' (job %d)") %
- (data.get('job-name', _("Unknown")), job))
- self.auth_info_dialog = dialog
- dialog.connect ('response', self.auth_info_dialog_response)
- dialog.set_data ('job-id', job)
- dialog.show_all ()
+ dialog.set_prompt (_("Authentication required for "
+ "printing document `%s' (job %d)") %
+ (data.get('job-name', _("Unknown")), job))
+ self.auth_info_dialog = dialog
+ dialog.connect ('response', self.auth_info_dialog_response)
+ dialog.set_data ('job-id', job)
+ dialog.show_all ()
def auth_info_dialog_response (self, dialog, response):
dialog.hide ()
@@ -862,7 +922,7 @@ class JobViewer (monitor.Watcher):
notification.attach_to_status_icon (self.statusicon)
notification.show ()
- def on_state_reason_notification_closed (self, notification):
+ def on_state_reason_notification_closed (self, notification, reason=None):
debugprint ("Notification %s closed" % repr (notification))
reason = notification.get_data ('printer-state-reason')
tuple = reason.get_tuple ()
diff -up system-config-printer-1.0.5/system-config-printer.py.git system-config-printer-1.0.5/system-config-printer.py
--- system-config-printer-1.0.5/system-config-printer.py.git 2008-08-11 14:51:07.000000000 +0100
+++ system-config-printer-1.0.5/system-config-printer.py 2008-08-14 16:23:49.000000000 +0100
@@ -152,6 +152,10 @@ class GtkGUI:
result.sort()
return result
+def on_delete_just_hide (widget, event):
+ widget.hide ()
+ return True # stop other handlers
+
class GUI(GtkGUI, monitor.Watcher):
printer_states = { cups.IPP_PRINTER_IDLE: _("Idle"),
@@ -271,6 +275,14 @@ class GUI(GtkGUI, monitor.Watcher):
"AboutDialog",
"WaitWindow", "lblWait",
)
+
+ # Since some dialogs are reused we can't let the delete-event's
+ # default handler destroy them
+ for dialog in [self.PrinterPropertiesDialog,
+ self.ServerSettingsDialog,
+ self.ConnectingDialog]:
+ dialog.connect ("delete-event", on_delete_just_hide)
+
self.tooltips = gtk.Tooltips()
self.tooltips.enable()
gtk.window_set_default_icon_name ('printer')
@@ -2597,6 +2609,13 @@ class NewPrinterGUI(GtkGUI):
"rbtnNPDownloadLicenseNo",
"NewPrinterName", "entCopyName", "btnCopyOk",
"InstallDialog", "lblInstall")
+
+ # Since some dialogs are reused we can't let the delete-event's
+ # default handler destroy them
+ for dialog in [self.IPPBrowseDialog,
+ self.SMBBrowseDialog]:
+ dialog.connect ("delete-event", on_delete_just_hide)
+
# share with mainapp
self.WaitWindow = mainapp.WaitWindow
self.lblWait = mainapp.lblWait
@@ -4722,7 +4741,7 @@ class NewPrinterGUI(GtkGUI):
self.lblNPDownloadableDriverLicense.set_text (license)
description = driver.get('shortdescription', _("None"))
self.lblNPDownloadableDriverDescription.set_text (description)
- if driver['freesoftware'] and not driver['patents']:
+ if not driver['nonfreesoftware'] and not driver['patents']:
self.rbtnNPDownloadLicenseYes.set_active (True)
self.frmNPDownloadableDriverLicenseTerms.hide ()
else:

View File

@ -7,7 +7,7 @@
Summary: A printer administration tool
Name: system-config-printer
Version: 1.0.5
Release: 2%{?dist}
Release: 3%{?dist}
License: GPLv2+
URL: http://cyberelk.net/tim/software/system-config-printer/
Group: System Environment/Base
@ -15,7 +15,7 @@ Source0: http://cyberelk.net/tim/data/system-config-printer/1.0.x/system-config-
Source1: http://cyberelk.net/tim/data/pycups/pycups-%{pycups_version}.tar.bz2
Source2: http://cyberelk.net/tim/data/pysmbc/pysmbc-%{pysmbc_version}.tar.bz2
Patch0: system-config-printer-forbidden.patch
Patch0: system-config-printer-git.patch
Patch1: pysmbc-debug.patch
BuildRequires: cups-devel >= 1.2
@ -64,7 +64,7 @@ the configuration tool.
%prep
%setup -q -a 1 -a 2
%patch0 -p1 -b .forbidden
%patch0 -p1 -b .git
pushd pysmbc-%{pysmbc_version}
%patch1 -p1 -b .debug
@ -157,6 +157,14 @@ rm -rf %buildroot
exit 0
%changelog
* Thu Aug 14 2008 Tim Waugh <twaugh@redhat.com> 1.0.5-3
- Include other fixes from upstream including:
- OpenPrinting API change (trac #74).
- libnotify API change for 'closed' signal.
- Notification for job authentication (trac #91).
- Glade delete-event fixes (trac #88).
- Pre-fill username in job authentication dialog (trac #87).
* Wed Aug 13 2008 Tim Waugh <twaugh@redhat.com> 1.0.5-2
- Handle HTTP_FORBIDDEN.