~bulldog98/kubuntu-packaging/kdeadmin

« back to all changes in this revision

Viewing changes to debian/patches/kubuntu_02_system_config_printer_trunk.diff

  • Committer: Jonathan Riddell
  • Date: 2011-02-28 17:09:58 UTC
  • Revision ID: jriddell@canonical.com-20110228170958-71ubt5mb58knwlgs
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
diff -urN kdeadmin-4.6.0/system-config-printer-kde/authconn.py kdeadmin/system-config-printer-kde/authconn.py
 
2
--- kdeadmin-4.6.0/system-config-printer-kde/authconn.py        1970-01-01 01:00:00.000000000 +0100
 
3
+++ kdeadmin/system-config-printer-kde/authconn.py      2011-02-23 16:22:52.300290533 +0000
 
4
@@ -0,0 +1,504 @@
 
5
+#!/usr/bin/env python
 
6
+
 
7
+## Copyright (C) 2007, 2008, 2009, 2010 Red Hat, Inc.
 
8
+## Author: Tim Waugh <twaugh@redhat.com>
 
9
+
 
10
+## This program is free software; you can redistribute it and/or modify
 
11
+## it under the terms of the GNU General Public License as published by
 
12
+## the Free Software Foundation; either version 2 of the License, or
 
13
+## (at your option) any later version.
 
14
+
 
15
+## This program is distributed in the hope that it will be useful,
 
16
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
18
+## GNU General Public License for more details.
 
19
+
 
20
+## You should have received a copy of the GNU General Public License
 
21
+## along with this program; if not, write to the Free Software
 
22
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
23
+
 
24
+# FIXME non UI bits work, dialogues needs porting from GTK to KDE
 
25
+
 
26
+import threading
 
27
+import cups
 
28
+#import cupspk not implemented, disabled in _connect()
 
29
+import gobject
 
30
+#import gtk
 
31
+import os
 
32
+#from errordialogs import *
 
33
+from debug import *
 
34
+from gettext import gettext as _
 
35
+N_ = lambda x: x
 
36
+
 
37
+"""
 
38
+class AuthDialog(gtk.Dialog):
 
39
+    AUTH_FIELD={'username': N_("Username:"),
 
40
+                'password': N_("Password:"),
 
41
+                'domain': N_("Domain:")}
 
42
+
 
43
+    def __init__ (self, title=None, parent=None,
 
44
+                  flags=gtk.DIALOG_MODAL | gtk.DIALOG_NO_SEPARATOR,
 
45
+                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
 
46
+                           gtk.STOCK_OK, gtk.RESPONSE_OK),
 
47
+                  auth_info_required=['username', 'password'],
 
48
+                  allow_remember=False):
 
49
+        if title == None:
 
50
+            title = _("Authentication")
 
51
+        gtk.Dialog.__init__ (self, title, parent, flags, buttons)
 
52
+        self.auth_info_required = auth_info_required
 
53
+        self.set_default_response (gtk.RESPONSE_OK)
 
54
+        self.set_border_width (6)
 
55
+        self.set_resizable (False)
 
56
+        hbox = gtk.HBox (False, 12)
 
57
+        hbox.set_border_width (6)
 
58
+        image = gtk.Image ()
 
59
+        image.set_from_stock (gtk.STOCK_DIALOG_AUTHENTICATION,
 
60
+                              gtk.ICON_SIZE_DIALOG)
 
61
+        image.set_alignment (0.0, 0.0)
 
62
+        hbox.pack_start (image, False, False, 0)
 
63
+        vbox = gtk.VBox (False, 12)
 
64
+        self.prompt_label = gtk.Label ()
 
65
+        vbox.pack_start (self.prompt_label, False, False, 0)
 
66
+
 
67
+        num_fields = len (auth_info_required)
 
68
+        table = gtk.Table (num_fields, 2)
 
69
+        table.set_row_spacings (6)
 
70
+        table.set_col_spacings (6)
 
71
+
 
72
+        self.field_entry = []
 
73
+        for i in range (num_fields):
 
74
+            field = auth_info_required[i]
 
75
+            label = gtk.Label (_(self.AUTH_FIELD.get (field, field)))
 
76
+            label.set_alignment (0, 0.5)
 
77
+            table.attach (label, 0, 1, i, i + 1)
 
78
+            entry = gtk.Entry ()
 
79
+            entry.set_visibility (field != 'password')
 
80
+            table.attach (entry, 1, 2, i, i + 1, 0, 0)
 
81
+            self.field_entry.append (entry)
 
82
+
 
83
+        self.field_entry[num_fields - 1].set_activates_default (True)
 
84
+        vbox.pack_start (table, False, False, 0)
 
85
+        hbox.pack_start (vbox, False, False, 0)
 
86
+        self.vbox.pack_start (hbox)
 
87
+
 
88
+        if allow_remember:
 
89
+            cb = gtk.CheckButton (_("Remember password"))
 
90
+            cb.set_active (False)
 
91
+            vbox.pack_start (cb)
 
92
+            self.remember_checkbox = cb
 
93
+
 
94
+        self.vbox.show_all ()
 
95
+
 
96
+    def set_prompt (self, prompt):
 
97
+        self.prompt_label.set_markup ('<span weight="bold" size="larger">' +
 
98
+                                      prompt + '</span>')
 
99
+        self.prompt_label.set_use_markup (True)
 
100
+        self.prompt_label.set_alignment (0, 0)
 
101
+        self.prompt_label.set_line_wrap (True)
 
102
+
 
103
+    def set_auth_info (self, auth_info):
 
104
+        for i in range (len (self.field_entry)):
 
105
+            self.field_entry[i].set_text (auth_info[i])
 
106
+
 
107
+    def get_auth_info (self):
 
108
+        return map (lambda x: x.get_text (), self.field_entry)
 
109
+
 
110
+    def get_remember_password (self):
 
111
+        try:
 
112
+            return self.remember_checkbox.get_active ()
 
113
+        except AttributeError:
 
114
+            return False
 
115
+
 
116
+    def field_grab_focus (self, field):
 
117
+        i = self.auth_info_required.index (field)
 
118
+        self.field_entry[i].grab_focus ()
 
119
+"""
 
120
+###
 
121
+### An auth-info cache.
 
122
+###
 
123
+class _AuthInfoCache:
 
124
+    def __init__ (self):
 
125
+        self.creds = dict() # by (host,port)
 
126
+
 
127
+    def cache_auth_info (self, data, host=None, port=None):
 
128
+        if port == None:
 
129
+            port = 631
 
130
+
 
131
+        self.creds[(host,port)] = data
 
132
+
 
133
+    def lookup_auth_info (self, host=None, port=None):
 
134
+        if port == None:
 
135
+            port = 631
 
136
+
 
137
+        try:
 
138
+            return self.creds[(host,port)]
 
139
+        except KeyError:
 
140
+            return None
 
141
+
 
142
+global_authinfocache = _AuthInfoCache ()
 
143
+
 
144
+class Connection:
 
145
+    def __init__ (self, parent=None, try_as_root=True, lock=False,
 
146
+                  host=None, port=None, encryption=None):
 
147
+        if host != None:
 
148
+            cups.setServer (host)
 
149
+        if port != None:
 
150
+            cups.setPort (port)
 
151
+        if encryption != None:
 
152
+            cups.setEncryption (encryption)
 
153
+
 
154
+        self._use_password = ''
 
155
+        self._parent = parent
 
156
+        self._try_as_root = try_as_root
 
157
+        self._use_user = cups.getUser ()
 
158
+        self._server = cups.getServer ()
 
159
+        self._port = cups.getPort()
 
160
+        self._encryption = cups.getEncryption ()
 
161
+        self._prompt_allowed = True
 
162
+        self._operation_stack = []
 
163
+        self._lock = lock
 
164
+        self._gui_event = threading.Event ()
 
165
+
 
166
+        creds = global_authinfocache.lookup_auth_info (host=host, port=port)
 
167
+        if creds != None:
 
168
+            if (creds[0] != 'root' or try_as_root):
 
169
+                (self._use_user, self._use_password) = creds
 
170
+            del creds
 
171
+
 
172
+        self._connect ()
 
173
+
 
174
+    def _begin_operation (self, operation):
 
175
+        debugprint ("%s: Operation += %s" % (self, repr (operation)))
 
176
+        self._operation_stack.append (operation)
 
177
+
 
178
+    def _end_operation (self):
 
179
+        debugprint ("%s: Operation ended" % self)
 
180
+        self._operation_stack.pop ()
 
181
+
 
182
+    def _get_prompt_allowed (self, ):
 
183
+        return self._prompt_allowed
 
184
+
 
185
+    def _set_prompt_allowed (self, allowed):
 
186
+        self._prompt_allowed = allowed
 
187
+
 
188
+    def _set_lock (self, whether):
 
189
+        self._lock = whether
 
190
+
 
191
+    def _connect (self, allow_pk=False):
 
192
+        cups.setUser (self._use_user)
 
193
+
 
194
+        self._use_pk = (allow_pk and
 
195
+                        (self._server[0] == '/' or self._server == 'localhost')
 
196
+                        and os.getuid () != 0)
 
197
+        if self._use_pk:
 
198
+            create_object = cupspk.Connection
 
199
+        else:
 
200
+            create_object = cups.Connection
 
201
+
 
202
+        self._connection = create_object (host=self._server,
 
203
+                                            port=self._port,
 
204
+                                            encryption=self._encryption)
 
205
+
 
206
+        if self._use_pk:
 
207
+            self._connection.set_parent(self._parent)
 
208
+
 
209
+        self._user = self._use_user
 
210
+        debugprint ("Connected as user %s" % self._user)
 
211
+        methodtype_lambda = type (self._connection.getPrinters)
 
212
+        methodtype_real = type (self._connection.addPrinter)
 
213
+        for fname in dir (self._connection):
 
214
+            if fname[0] == '_':
 
215
+                continue
 
216
+            fn = getattr (self._connection, fname)
 
217
+            if not type (fn) in [methodtype_lambda, methodtype_real]:
 
218
+                continue
 
219
+            setattr (self, fname, self._make_binding (fname, fn))
 
220
+
 
221
+    def _make_binding (self, fname, fn):
 
222
+        return lambda *args, **kwds: self._authloop (fname, fn, *args, **kwds)
 
223
+
 
224
+    def _authloop (self, fname, fn, *args, **kwds):
 
225
+        self._passes = 0
 
226
+        c = self._connection
 
227
+        retry = False
 
228
+        while True:
 
229
+            try:
 
230
+                if self._perform_authentication () == 0:
 
231
+                    break
 
232
+
 
233
+                if c != self._connection:
 
234
+                    # We have reconnected.
 
235
+                    fn = getattr (self._connection, fname)
 
236
+                    c = self._connection
 
237
+
 
238
+                cups.setUser (self._use_user)
 
239
+
 
240
+                result = fn.__call__ (*args, **kwds)
 
241
+
 
242
+                if fname == 'adminGetServerSettings':
 
243
+                    # Special case for a rubbish bit of API.
 
244
+                    if result == {}:
 
245
+                        # Authentication failed, but we aren't told that.
 
246
+                        raise cups.IPPError (cups.IPP_NOT_AUTHORIZED, '')
 
247
+                break
 
248
+            except cups.IPPError, (e, m):
 
249
+                if self._use_pk and m == 'pkcancel':
 
250
+                    raise cups.IPPError (0, _("Operation canceled"))
 
251
+                if not self._cancel and (e == cups.IPP_NOT_AUTHORIZED or
 
252
+                                         e == cups.IPP_FORBIDDEN):
 
253
+                    self._failed (e == cups.IPP_FORBIDDEN)
 
254
+                elif not self._cancel and e == cups.IPP_SERVICE_UNAVAILABLE:
 
255
+                    if self._lock:
 
256
+                        self._gui_event.clear ()
 
257
+                        gobject.timeout_add (1, self._ask_retry_server_error, m)
 
258
+                        self._gui_event.wait ()
 
259
+                    else:
 
260
+                        self._ask_retry_server_error (m)
 
261
+
 
262
+                    if self._retry_response == gtk.RESPONSE_OK:
 
263
+                        debugprint ("retrying operation...")
 
264
+                        retry = True
 
265
+                        self._passes -= 1
 
266
+                        self._has_failed = True
 
267
+                    else:
 
268
+                        self._cancel = True
 
269
+                        raise
 
270
+                else:
 
271
+                    if self._cancel and not self._cannot_auth:
 
272
+                        raise cups.IPPError (0, _("Operation canceled"))
 
273
+
 
274
+                    raise
 
275
+            except cups.HTTPError, (s,):
 
276
+                if not self._cancel:
 
277
+                    self._failed (s == cups.HTTP_FORBIDDEN)
 
278
+                else:
 
279
+                    raise
 
280
+
 
281
+        return result
 
282
+
 
283
+    def _ask_retry_server_error (self, message):
 
284
+        if self._lock:
 
285
+            gtk.gdk.threads_enter ()
 
286
+
 
287
+        try:
 
288
+            msg = _("CUPS server error (%s)") % self._operation_stack[0]
 
289
+        except IndexError:
 
290
+            msg = _("CUPS server error")
 
291
+
 
292
+        d = gtk.MessageDialog (self._parent,
 
293
+                               gtk.DIALOG_MODAL |
 
294
+                               gtk.DIALOG_DESTROY_WITH_PARENT,
 
295
+                               gtk.MESSAGE_ERROR,
 
296
+                               gtk.BUTTONS_NONE,
 
297
+                               msg)
 
298
+                               
 
299
+        d.format_secondary_text (_("There was an error during the "
 
300
+                                   "CUPS operation: '%s'." % message))
 
301
+        d.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
 
302
+                       _("Retry"), gtk.RESPONSE_OK)
 
303
+        d.set_default_response (gtk.RESPONSE_OK)
 
304
+        if self._lock:
 
305
+            d.connect ("response", self._on_retry_server_error_response)
 
306
+            gtk.gdk.threads_leave ()
 
307
+        else:
 
308
+            self._retry_response = d.run ()
 
309
+            d.destroy ()
 
310
+
 
311
+    def _on_retry_server_error_response (self, dialog, response):
 
312
+        self._retry_response = response
 
313
+        dialog.destroy ()
 
314
+        self._gui_event.set ()
 
315
+
 
316
+    def _failed (self, forbidden=False):
 
317
+        self._has_failed = True
 
318
+        self._forbidden = forbidden
 
319
+
 
320
+    def _password_callback (self, prompt):
 
321
+        debugprint ("Got password callback")
 
322
+        if self._cancel or self._auth_called:
 
323
+            return ''
 
324
+
 
325
+        self._auth_called = True
 
326
+        self._prompt = prompt
 
327
+        return self._use_password
 
328
+
 
329
+    def _perform_authentication (self):
 
330
+        self._passes += 1
 
331
+
 
332
+        debugprint ("Authentication pass: %d" % self._passes)
 
333
+        if self._passes == 1:
 
334
+            # Haven't yet tried the operation.  Set the password
 
335
+            # callback and return > 0 so we try it for the first time.
 
336
+            self._has_failed = False
 
337
+            self._forbidden = False
 
338
+            self._auth_called = False
 
339
+            self._cancel = False
 
340
+            self._cannot_auth = False
 
341
+            self._dialog_shown = False
 
342
+            cups.setPasswordCB (self._password_callback)
 
343
+            debugprint ("Authentication: password callback set")
 
344
+            return 1
 
345
+
 
346
+        debugprint ("Forbidden: %s" % self._forbidden)
 
347
+        if not self._has_failed:
 
348
+            # Tried the operation and it worked.  Return 0 to signal to
 
349
+            # break out of the loop.
 
350
+            debugprint ("Authentication: Operation successful")
 
351
+            return 0
 
352
+
 
353
+        # Reset failure flag.
 
354
+        self._has_failed = False
 
355
+
 
356
+        if self._passes >= 2:
 
357
+            # Tried the operation without a password and it failed.
 
358
+            if (self._try_as_root and
 
359
+                self._user != 'root' and
 
360
+                (self._server[0] == '/' or self._forbidden)):
 
361
+                # This is a UNIX domain socket connection so we should
 
362
+                # not have needed a password (or it is not a UDS but
 
363
+                # we got an HTTP_FORBIDDEN response), and so the
 
364
+                # operation must not be something that the current
 
365
+                # user is authorised to do.  They need to try as root,
 
366
+                # and supply the password.  However, to get the right
 
367
+                # prompt, we need to try as root but with no password
 
368
+                # first.
 
369
+                debugprint ("Authentication: Try as root")
 
370
+                self._use_user = 'root'
 
371
+                self._auth_called = False
 
372
+                try:
 
373
+                    self._connect (allow_pk=False)
 
374
+                except RuntimeError:
 
375
+                    raise cups.IPPError (cups.IPP_SERVICE_UNAVAILABLE,
 
376
+                                         'server-error-service-unavailable')
 
377
+
 
378
+                return 1
 
379
+
 
380
+        if not self._prompt_allowed:
 
381
+            debugprint ("Authentication: prompting not allowed")
 
382
+            self._cancel = True
 
383
+            return 1
 
384
+
 
385
+        if not self._auth_called:
 
386
+            # We aren't even getting a chance to supply credentials.
 
387
+            debugprint ("Authentication: giving up")
 
388
+            self._cancel = True
 
389
+            self._cannot_auth = True
 
390
+            return 1
 
391
+
 
392
+        # Reset the flag indicating whether we were given an auth callback.
 
393
+        self._auth_called = False
 
394
+
 
395
+        # If we're previously prompted, explain why we're prompting again.
 
396
+        if self._dialog_shown:
 
397
+            if self._lock:
 
398
+                self._gui_event.clear ()
 
399
+                gobject.timeout_add (1, self._show_not_authorized_dialog)
 
400
+                self._gui_event.wait ()
 
401
+            else:
 
402
+                self._show_not_authorized_dialog ()
 
403
+
 
404
+        if self._lock:
 
405
+            self._gui_event.clear ()
 
406
+            gobject.timeout_add (1, self._perform_authentication_with_dialog)
 
407
+            self._gui_event.wait ()
 
408
+        else:
 
409
+            self._perform_authentication_with_dialog ()
 
410
+
 
411
+        if self._cancel:
 
412
+            debugprint ("cancelled")
 
413
+            return -1
 
414
+
 
415
+        cups.setUser (self._use_user)
 
416
+        debugprint ("Authentication: Reconnect")
 
417
+        try:
 
418
+            self._connect (allow_pk=False)
 
419
+        except RuntimeError:
 
420
+            raise cups.IPPError (cups.IPP_SERVICE_UNAVAILABLE,
 
421
+                                 'server-error-service-unavailable')
 
422
+
 
423
+        return 1
 
424
+
 
425
+    def _show_not_authorized_dialog (self):
 
426
+        if self._lock:
 
427
+            gtk.gdk.threads_enter ()
 
428
+        d = gtk.MessageDialog (self._parent,
 
429
+                               gtk.DIALOG_MODAL |
 
430
+                               gtk.DIALOG_DESTROY_WITH_PARENT,
 
431
+                               gtk.MESSAGE_ERROR,
 
432
+                               gtk.BUTTONS_CLOSE)
 
433
+        d.set_title (_("Not authorized"))
 
434
+        d.set_markup ('<span weight="bold" size="larger">' +
 
435
+                      _("Not authorized") + '</span>\n\n' +
 
436
+                      _("The password may be incorrect."))
 
437
+        if self._lock:
 
438
+            d.connect ("response", self._on_not_authorized_dialog_response)
 
439
+            d.show_all ()
 
440
+            d.show_now ()
 
441
+            gtk.gdk.threads_leave ()
 
442
+        else:
 
443
+            d.run ()
 
444
+            d.destroy ()
 
445
+
 
446
+    def _on_not_authorized_dialog_response (self, dialog, response):
 
447
+        self._gui_event.set ()
 
448
+        dialog.destroy ()
 
449
+
 
450
+    def _perform_authentication_with_dialog (self):
 
451
+        if self._lock:
 
452
+            gtk.gdk.threads_enter ()
 
453
+
 
454
+        # Prompt.
 
455
+        if len (self._operation_stack) > 0:
 
456
+            try:
 
457
+                title = _("Authentication (%s)") % self._operation_stack[0]
 
458
+            except IndexError:
 
459
+                title = _("Authentication")
 
460
+
 
461
+            debugprint("FIXME KDE AuthDialog not yet implemented")
 
462
+            d = AuthDialog (title=title,
 
463
+                            parent=self._parent)
 
464
+        else:
 
465
+            debugprint("FIXME KDE AuthDialog not yet implemented")
 
466
+            d = AuthDialog (parent=self._parent)
 
467
+
 
468
+        d.set_prompt (self._prompt)
 
469
+        d.set_auth_info ([self._use_user, ''])
 
470
+        d.field_grab_focus ('password')
 
471
+        d.set_keep_above (True)
 
472
+        d.show_all ()
 
473
+        d.show_now ()
 
474
+        self._dialog_shown = True
 
475
+        if self._lock:
 
476
+            d.connect ("response", self._on_authentication_response)
 
477
+            gtk.gdk.threads_leave ()
 
478
+        else:
 
479
+            response = d.run ()
 
480
+            self._on_authentication_response (d, response)
 
481
+
 
482
+    def _on_authentication_response (self, dialog, response):
 
483
+        (self._use_user,
 
484
+         self._use_password) = dialog.get_auth_info ()
 
485
+        global_authinfocache.cache_auth_info ((self._use_user,
 
486
+                                               self._use_password),
 
487
+                                              host=self._server,
 
488
+                                              port=self._port)
 
489
+        dialog.destroy ()
 
490
+
 
491
+        if (response == gtk.RESPONSE_CANCEL or
 
492
+            response == gtk.RESPONSE_DELETE_EVENT):
 
493
+            self._cancel = True
 
494
+
 
495
+        if self._lock:
 
496
+            self._gui_event.set ()
 
497
+
 
498
+if __name__ == '__main__':
 
499
+    # Test it out.
 
500
+    gtk.gdk.threads_init ()
 
501
+    from timedops import TimedOperation
 
502
+    set_debugging (True)
 
503
+    c = TimedOperation (Connection, args=(None,)).run ()
 
504
+    debugprint ("Connected")
 
505
+    c._set_lock (True)
 
506
+    print TimedOperation (c.getFile,
 
507
+                          args=('/admin/conf/cupsd.conf',
 
508
+                                '/dev/stdout')).run ()
 
509
diff -urN kdeadmin-4.6.0/system-config-printer-kde/CMakeLists.txt kdeadmin/system-config-printer-kde/CMakeLists.txt
 
510
--- kdeadmin-4.6.0/system-config-printer-kde/CMakeLists.txt     2011-01-19 22:09:10.000000000 +0000
 
511
+++ kdeadmin/system-config-printer-kde/CMakeLists.txt   2011-02-23 16:22:52.270290533 +0000
 
512
@@ -22,7 +22,7 @@
 
513
 
 
514
 macro_optional_find_package(SystemConfigPrinter)
 
515
 IF(NOT SYSTEMCONFIGPRINTER_FOUND)
 
516
-    macro_log_feature(SYSTEMCONFIGPRINTER_FOUND "system-config-printer" "system-config-printer was not found.  Some of its modules (cupshelpers modules, config.py, smburi.py and debug.py) are required by system-config-printer-kde." "http://cyberelk.net/tim/software/system-config-printer/" FALSE)
 
517
+    macro_log_feature(SYSTEMCONFIGPRINTER_FOUND "system-config-printer" "system-config-printer was not found.  Some of its modules (cupshelpers modules, config.py, smburi.py, debug.py and ppdippstr.py) are required by system-config-printer-kde." "http://cyberelk.net/tim/software/system-config-printer/" FALSE)
 
518
 ENDIF(NOT SYSTEMCONFIGPRINTER_FOUND)
 
519
 
 
520
 IF(PYQT4_FOUND AND PYKDE4_FOUND AND PYCUPS_FOUND AND SYSTEMCONFIGPRINTER_FOUND)
 
521
@@ -31,11 +31,13 @@
 
522
 
 
523
 IF(INSTALL_SYSTEM_CONFIG_PRINTER)
 
524
     install( FILES
 
525
+        authconn.py
 
526
         new-printer.ui
 
527
         system-config-printer.ui
 
528
         system-config-printer-kde.py
 
529
         options.py
 
530
         optionwidgets.py
 
531
+        pysmb.py
 
532
         ipp-browse-dialog.ui
 
533
         smb-browse-dialog.ui
 
534
         DESTINATION ${DATA_INSTALL_DIR}/system-config-printer-kde )
 
535
diff -urN kdeadmin-4.6.0/system-config-printer-kde/options.py kdeadmin/system-config-printer-kde/options.py
 
536
--- kdeadmin-4.6.0/system-config-printer-kde/options.py 2011-01-19 22:09:10.000000000 +0000
 
537
+++ kdeadmin/system-config-printer-kde/options.py       2011-02-23 16:22:52.300290533 +0000
 
538
@@ -30,6 +30,8 @@
 
539
 
 
540
 from PyQt4.QtCore import *
 
541
 from PyQt4.QtGui import *
 
542
+from PyKDE4.kdeui import *
 
543
+import ppdippstr
 
544
 
 
545
 """
 
546
 These classes are used in the Job Options tab of the printer config dialogue
 
547
@@ -126,41 +128,50 @@
 
548
         """Set the original value of the option and the supported choices.
 
549
         The special value None for original_value resets the option to the
 
550
         system default."""
 
551
+        self.widget.blockSignals(True)
 
552
         if (supported != None and
 
553
             self.use_supported):
 
554
-            if (type(self.widget) == QComboBox and
 
555
+            if (isinstance(self.widget, QComboBox) and
 
556
                 self.ipp_type == str):
 
557
                 self.widget.clear()
 
558
-                if type(supported) == list:
 
559
-                    for each in supported:
 
560
-                        self.widget.addItem(unicode(each))
 
561
-                else:
 
562
-                    self.widget.addItem(self.combobox_dict[supported])
 
563
-            elif (type(self.widget) == QComboBox and
 
564
+                translations = ppdippstr.job_options.get (self.name)
 
565
+                if translations:
 
566
+                    self.combobox_map = []
 
567
+                    self.combobox_dict = dict()
 
568
+                    i = 0
 
569
+
 
570
+                for each in supported:
 
571
+                    if translations:
 
572
+                        self.combobox_map.append (each)
 
573
+                        text = translations.get (each)
 
574
+                        self.combobox_dict[each] = text
 
575
+                        i += 1
 
576
+                    else:
 
577
+                        text = each
 
578
+
 
579
+                    self.widget.addItem(unicode(each))
 
580
+            elif (isinstance(self.widget, QComboBox) and
 
581
                   self.ipp_type == int and
 
582
                   self.combobox_map != None):
 
583
                 self.widget.clear()
 
584
-                if type(supported) == list:
 
585
-                    for each in supported:
 
586
-                        self.widget.addItem(unicode(each))
 
587
-                else:
 
588
-                    self.widget.addItem(supported)
 
589
+                for each in supported:
 
590
+                    self.widget.addItem(unicode(self.combobox_dict[each]))
 
591
+
 
592
         if original_value != None:
 
593
             self.original_value = self.ipp_type (original_value)
 
594
             self.set_widget_value (self.original_value)
 
595
-            if original_value != self.get_widget_value():
 
596
-                self.button.setEnabled(True)
 
597
+            self.button.setEnabled (True)
 
598
         else:
 
599
             self.original_value = None
 
600
             self.set_widget_value (self.system_default)
 
601
-            self.button.setEnabled(False)
 
602
+            self.button.setEnabled (False)
 
603
         self.state = self.STATE_UNCHANGED
 
604
+        self.widget.blockSignals(False)
 
605
 
 
606
     def set_widget_value(self, ipp_value):
 
607
-        t = type(self.widget)
 
608
-        if t == QSpinBox:
 
609
+        if isinstance(self.widget, QSpinBox):
 
610
             return self.widget.setValue(ipp_value)
 
611
-        elif t == QComboBox:
 
612
+        elif isinstance(self.widget, QComboBox):
 
613
             if self.ipp_type == str and self.combobox_map == None:
 
614
                 index = self.widget.findText(ipp_value)
 
615
                 if index != -1:
 
616
@@ -172,14 +183,13 @@
 
617
                 else:
 
618
                     index = ipp_value
 
619
                 return self.widget.setCurrentIndex(index)
 
620
-        elif t == QCheckBox:
 
621
+        elif isinstance(self.widget, QCheckBox):
 
622
             return self.widget.setChecked(ipp_value)
 
623
         else:
 
624
             return NotImplemented
 
625
 
 
626
     def get_widget_value(self):
 
627
-        t = type(self.widget)
 
628
-        if t == QSpinBox:
 
629
+        if isinstance(self.widget, QSpinBox):
 
630
             # Ideally we would use self.widget.get_value() here, but
 
631
             # it doesn't work if the value has been typed in and then
 
632
             # the Apply button immediately clicked.  To handle this,
 
633
@@ -191,13 +201,13 @@
 
634
             except ValueError:
 
635
                 # Can't convert result of get_text() to ipp_type.
 
636
                 return self.ipp_type (self.widget.value ())
 
637
-        elif t == QComboBox:
 
638
+        elif isinstance(self.widget, QComboBox):
 
639
             if self.combobox_map:
 
640
                 return self.combobox_map[self.widget.currentIndex()]
 
641
             if self.ipp_type == str:
 
642
                 return self.widget.currentText()
 
643
             return self.ipp_type (unicode(self.widget.currentText()))
 
644
-        elif t == QCheckBox:
 
645
+        elif isinstance(self.widget, QCheckBox):
 
646
             return self.ipp_type (self.widget.isChecked())
 
647
 
 
648
         return NotImplemented
 
649
@@ -398,7 +408,7 @@
 
650
         Option.__init__(self, name, value, supported, on_change)
 
651
 
 
652
         self.selector = QLineEdit()
 
653
-        self.selector.setText(value)
 
654
+        self.selector.setText(unicode(value))
 
655
         self.selector.connect(self.selector, SIGNAL("textChanged(QString)"), self.changed)
 
656
 
 
657
     def get_current_value(self):
 
658
diff -urN kdeadmin-4.6.0/system-config-printer-kde/pysmb.py kdeadmin/system-config-printer-kde/pysmb.py
 
659
--- kdeadmin-4.6.0/system-config-printer-kde/pysmb.py   1970-01-01 01:00:00.000000000 +0100
 
660
+++ kdeadmin/system-config-printer-kde/pysmb.py 2011-02-23 16:22:52.300290533 +0000
 
661
@@ -0,0 +1,200 @@
 
662
+#!/usr/bin/python
 
663
+
 
664
+## system-config-printer
 
665
+## CUPS backend
 
666
 
667
+## Copyright (C) 2002, 2003, 2006, 2007, 2008, 2010 Red Hat, Inc.
 
668
+## Authors:
 
669
+##  Tim Waugh <twaugh@redhat.com>
 
670
 
671
+## This program is free software; you can redistribute it and/or modify
 
672
+## it under the terms of the GNU General Public License as published by
 
673
+## the Free Software Foundation; either version 2 of the License, or
 
674
+## (at your option) any later version.
 
675
 
676
+## This program is distributed in the hope that it will be useful,
 
677
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
 
678
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
679
+## GNU General Public License for more details.
 
680
 
681
+## You should have received a copy of the GNU General Public License
 
682
+## along with this program; if not, write to the Free Software
 
683
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
684
+
 
685
+import errno
 
686
+from gettext import gettext as _
 
687
+# import gobject
 
688
+# import gtk TODO port to KDE
 
689
+import os
 
690
+import pwd
 
691
+import smbc
 
692
+from debug import *
 
693
+
 
694
+class _None(RuntimeError):
 
695
+    pass
 
696
+
 
697
+try:
 
698
+    NoEntryError = smbc.NoEntryError
 
699
+    PermissionError = smbc.PermissionError
 
700
+    ExistsError = smbc.ExistsError
 
701
+    NotEmptyError = smbc.NotEmptyError
 
702
+    TimedOutError = smbc.TimedOutError
 
703
+    NoSpaceError = smbc.NoSpaceError
 
704
+except AttributeError:
 
705
+    NoEntryError = PermissionError = ExistsError = _None
 
706
+    NotEmptyError = TimedOutError = NoSpaceError = _None
 
707
+
 
708
+class AuthContext:
 
709
+    def __init__ (self, parent=None, workgroup='', user='', passwd=''):
 
710
+        self.passes = 0
 
711
+        self.has_failed = False
 
712
+        self.auth_called = False
 
713
+        self.tried_guest = False
 
714
+        self.cancel = False
 
715
+        self.use_user = user
 
716
+        self.use_password = passwd
 
717
+        self.use_workgroup = workgroup
 
718
+        self.dialog_shown = False
 
719
+        self.parent = parent
 
720
+
 
721
+    def perform_authentication (self):
 
722
+        self.passes += 1
 
723
+        if self.passes == 1:
 
724
+            return 1
 
725
+
 
726
+        if not self.has_failed:
 
727
+            return 0
 
728
+
 
729
+        debugprint ("pysmb: authentication pass: %d" % self.passes)
 
730
+        if not self.auth_called:
 
731
+            debugprint ("pysmb: auth callback not called?!")
 
732
+            self.cancel = True
 
733
+            return 0
 
734
+
 
735
+        self.has_failed = False
 
736
+        if self.auth_called and not self.tried_guest:
 
737
+            self.use_user = 'guest'
 
738
+            self.use_password = ''
 
739
+            self.tried_guest = True
 
740
+            debugprint ("pysmb: try auth as guest")
 
741
+            return 1
 
742
+
 
743
+        self.auth_called = False
 
744
+
 
745
+        if self.dialog_shown:
 
746
+            d = gtk.MessageDialog (self.parent,
 
747
+                                   gtk.DIALOG_MODAL |
 
748
+                                   gtk.DIALOG_DESTROY_WITH_PARENT,
 
749
+                                   gtk.MESSAGE_ERROR,
 
750
+                                   gtk.BUTTONS_CLOSE)
 
751
+            d.set_title (_("Not authorized"))
 
752
+            d.set_markup ('<span weight="bold" size="larger">' +
 
753
+                          _("Not authorized") + '</span>\n\n' +
 
754
+                          _("The password may be incorrect."))
 
755
+            d.run ()
 
756
+            d.destroy ()
 
757
+
 
758
+        # After that, prompt
 
759
+        d = gtk.Dialog (_("Authentication"), self.parent,
 
760
+                        gtk.DIALOG_MODAL | gtk.DIALOG_NO_SEPARATOR,
 
761
+                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
 
762
+                         gtk.STOCK_OK, gtk.RESPONSE_OK))
 
763
+        d.set_default_response (gtk.RESPONSE_OK)
 
764
+        d.set_border_width (6)
 
765
+        d.set_resizable (False)
 
766
+        hbox = gtk.HBox (False, 12)
 
767
+        hbox.set_border_width (6)
 
768
+        image = gtk.Image ()
 
769
+        image.set_from_stock (gtk.STOCK_DIALOG_AUTHENTICATION,
 
770
+                              gtk.ICON_SIZE_DIALOG)
 
771
+        hbox.pack_start (image, False, False, 0)
 
772
+        vbox = gtk.VBox (False, 12)
 
773
+        label = gtk.Label ('<span weight="bold" size="larger">' +
 
774
+                           _("You must log in to access %s.") % self.for_server +
 
775
+                           '</span>')
 
776
+        label.set_use_markup (True)
 
777
+        label.set_alignment (0, 0)
 
778
+        label.set_line_wrap (True)
 
779
+        vbox.pack_start (label, False, False, 0)
 
780
+
 
781
+        table = gtk.Table (3, 2)
 
782
+        table.set_row_spacings (6)
 
783
+        table.set_col_spacings (6)
 
784
+        table.attach (gtk.Label (_("Username:")), 0, 1, 0, 1, 0, 0)
 
785
+        username_entry = gtk.Entry ()
 
786
+        table.attach (username_entry, 1, 2, 0, 1, 0, 0)
 
787
+        table.attach (gtk.Label (_("Domain:")), 0, 1, 1, 2, 0, 0)
 
788
+        domain_entry = gtk.Entry ()
 
789
+        table.attach (domain_entry, 1, 2, 1, 2, 0, 0)
 
790
+        table.attach (gtk.Label (_("Password:")), 0, 1, 2, 3, 0, 0)
 
791
+        password_entry = gtk.Entry ()
 
792
+        password_entry.set_activates_default (True)
 
793
+        password_entry.set_visibility (False)
 
794
+        table.attach (password_entry, 1, 2, 2, 3, 0, 0)
 
795
+        vbox.pack_start (table, False, False, 0)
 
796
+        hbox.pack_start (vbox, False, False, 0)
 
797
+        d.vbox.pack_start (hbox)
 
798
+        self.dialog_shown = True
 
799
+        d.show_all ()
 
800
+        d.show_now ()
 
801
+
 
802
+        if self.use_user == 'guest':
 
803
+            self.use_user = pwd.getpwuid (os.getuid ())[0]
 
804
+            debugprint ("pysmb: try as %s" % self.use_user)
 
805
+        username_entry.set_text (self.use_user)
 
806
+        domain_entry.set_text (self.use_workgroup)
 
807
+
 
808
+        d.set_keep_above (True)
 
809
+        gtk.gdk.pointer_grab (d.window, True)
 
810
+        gtk.gdk.keyboard_grab (d.window, True)
 
811
+        response = d.run ()
 
812
+        gtk.gdk.keyboard_ungrab ()
 
813
+        gtk.gdk.pointer_ungrab ()
 
814
+
 
815
+        if response == gtk.RESPONSE_CANCEL:
 
816
+            self.cancel = True
 
817
+            d.destroy ()
 
818
+            return -1
 
819
+
 
820
+        self.use_user = username_entry.get_text ()
 
821
+        self.use_password = password_entry.get_text ()
 
822
+        self.use_workgroup = domain_entry.get_text ()
 
823
+        d.destroy ()
 
824
+        return 1
 
825
+
 
826
+    def initial_authentication (self):
 
827
+        try:
 
828
+            context = smbc.Context ()
 
829
+            self.use_workgroup = context.workgroup
 
830
+        except:
 
831
+            pass
 
832
+
 
833
+    def failed (self, exc=None):
 
834
+        self.has_failed = True
 
835
+        debugprint ("pysmb: operation failed: %s" % repr (exc))
 
836
+
 
837
+        if exc:
 
838
+            if (self.cancel or
 
839
+                (type (exc) in [NoEntryError, ExistsError, NotEmptyError,
 
840
+                                TimedOutError, NoSpaceError]) or
 
841
+                (type (exc) == RuntimeError and
 
842
+                 not (exc.args[0] in [errno.EACCES, errno.EPERM]))):
 
843
+                    raise exc
 
844
+
 
845
+    def callback (self, server, share, workgroup, user, password):
 
846
+        debugprint ("pysmb: got password callback")
 
847
+        self.auth_called = True
 
848
+        self.for_server = server
 
849
+        self.for_share = share
 
850
+        if self.passes == 1:
 
851
+            self.initial_authentication ()
 
852
+
 
853
+        if self.use_user:
 
854
+            if self.use_workgroup:
 
855
+                workgroup = self.use_workgroup
 
856
+
 
857
+            return (workgroup, self.use_user, self.use_password)
 
858
+
 
859
+        user = ''
 
860
+        password = ''
 
861
+        return (workgroup, user, password)
 
862
diff -urN kdeadmin-4.6.0/system-config-printer-kde/README kdeadmin/system-config-printer-kde/README
 
863
--- kdeadmin-4.6.0/system-config-printer-kde/README     2011-01-19 22:09:10.000000000 +0000
 
864
+++ kdeadmin/system-config-printer-kde/README   2011-02-23 16:22:52.300290533 +0000
 
865
@@ -2,7 +2,7 @@
 
866
 
 
867
 It currently misses quite a few features compared to the Gnome version but should be usable.
 
868
 
 
869
-It needs some files from the Gnome version config.py, cupshelpers.py, ppds.py, smburi.py, debug.py 
 
870
+It needs some files from the Gnome version config.py, cupshelpers.py, ppds.py, smburi.py, debug.py, ppdippstr
 
871
 (in Ubuntu we package these into system-config-printer-common)
 
872
 
 
873
 Gnome version homepage:
 
874
diff -urN kdeadmin-4.6.0/system-config-printer-kde/system-config-printer-kde.py kdeadmin/system-config-printer-kde/system-config-printer-kde.py
 
875
--- kdeadmin-4.6.0/system-config-printer-kde/system-config-printer-kde.py       2011-02-23 16:20:55.000000000 +0000
 
876
+++ kdeadmin/system-config-printer-kde/system-config-printer-kde.py     2011-02-23 16:22:52.270290533 +0000
 
877
@@ -31,7 +31,7 @@
 
878
 MIN_REFRESH_INTERVAL = 1 # seconds
 
879
 import locale
 
880
 
 
881
-import sys, os, time, traceback, re, tempfile, httplib, thread
 
882
+import sys, os, time, traceback, re, tempfile, httplib, thread, string
 
883
 
 
884
 #load modules from system-config-printer-common (debug, smburi), change path here if you have it installed elsewhere
 
885
 SYSTEM_CONFIG_PRINTER_DIR = "/usr/share/system-config-printer"
 
886
@@ -65,12 +65,19 @@
 
887
 import cups
 
888
 cups.require ("1.9.27")
 
889
 
 
890
+try:
 
891
+    import pysmb
 
892
+    PYSMB_AVAILABLE=True
 
893
+except:
 
894
+    PYSMB_AVAILABLE=False
 
895
+
 
896
 # These come from system-config-printer
 
897
 import config
 
898
 import cupshelpers, options
 
899
 from optionwidgets import OptionWidget
 
900
 from smburi import SMBURI
 
901
 from debug import *
 
902
+import authconn
 
903
 
 
904
 import dbus
 
905
 import dbus.mainloop.qt
 
906
@@ -154,10 +161,17 @@
 
907
 
 
908
         self.servers = set((self.connect_server,))
 
909
 
 
910
-        try:
 
911
-            self.cups = cups.Connection()
 
912
-        except RuntimeError:
 
913
-            self.cups = None
 
914
+        host = None #FIXME should be loaded from somewhere
 
915
+        encryption = None #FIXME should be loaded from somewhere
 
916
+        if not host:
 
917
+            host = cups.getServer()
 
918
+        if not encryption:
 
919
+            encryption = cups.getEncryption ()
 
920
+
 
921
+        c = authconn.Connection (parent=self.ui,
 
922
+                                 host=host,
 
923
+                                 encryption=encryption)
 
924
+        self.cups = c
 
925
 
 
926
         # New Printer Dialog
 
927
         self.newPrinterGUI = np = NewPrinterGUI(self)
 
928
@@ -185,7 +199,7 @@
 
929
 
 
930
         self.ui.connect(self.ui.entPDescription, SIGNAL("textEdited(const QString&)"), self.on_printer_changed)
 
931
         self.ui.connect(self.ui.entPLocation, SIGNAL("textEdited(const QString&)"), self.on_printer_changed)
 
932
-        self.ui.connect(self.ui.chkPMakeDefault, SIGNAL("stateChanged(int)"), self.chkPMakeDefault_stateChanged)
 
933
+        self.ui.connect(self.ui.chkPMakeDefault, SIGNAL("stateChanged(int)"), self.on_default_printer_changed)
 
934
         self.ui.connect(self.ui.btnDelete, SIGNAL("clicked()"), self.btnDelete_clicked)
 
935
         self.ui.connect(self.ui.entPDevice, SIGNAL("textEdited(const QString&)"), self.on_printer_changed)
 
936
         self.ui.connect(self.ui.chkPEnabled, SIGNAL("stateChanged(int)"),self.on_printer_changed)
 
937
@@ -371,6 +385,8 @@
 
938
         self.ui.newPrinterSpecialLabel.hide()
 
939
         self.ui.entPName.hide()
 
940
         self.ui.lblPName.hide()
 
941
+        self.ui.btnNewPrinterNetwork.setText(i18n("New Printer"))
 
942
+        self.ui.newPrinterNetworkLabel.setText(i18n("Add a new printer"))
 
943
 
 
944
         #catch the currentItemChanged signal. This should stop the
 
945
         # 'Do you want to save settings' pop-up at startup
 
946
@@ -649,6 +665,24 @@
 
947
     #TODO
 
948
     # Connect to Server
 
949
 
 
950
+
 
951
+    def default_printer_state_changed(self):
 
952
+        """returns true if default printer tickbox now in a different state than current setting"""
 
953
+        default = self.cups.getDefault()
 
954
+        if self.printer.name == default and not self.ui.chkPMakeDefault.isChecked() or self.printer.name != default and self.ui.chkPMakeDefault.isChecked():
 
955
+            return True
 
956
+        else:
 
957
+            return False
 
958
+
 
959
+    def on_default_printer_changed(self, text):
 
960
+        """default printer tickbox has been ticked"""
 
961
+        widget = self.sender()        
 
962
+        if self.default_printer_state_changed():
 
963
+            self.changed.add(widget)
 
964
+        else:
 
965
+            self.changed.discard(widget)
 
966
+        self.setDataButtonState()
 
967
+
 
968
     def on_printer_changed(self, text):
 
969
         widget = self.sender()
 
970
         if not widget:  #method called as a method not a slot
 
971
@@ -696,20 +730,52 @@
 
972
 
 
973
     # set buttons sensitivity
 
974
     def setDataButtonState(self):
 
975
-        try: # Might not be a printer selected
 
976
-            possible = (self.ppd and
 
977
-                        not bool (self.changed) and
 
978
-                        self.printer.enabled and
 
979
-                        not self.printer.rejecting)
 
980
-
 
981
-            self.btnPrintTestPage.setEnabled(possible)
 
982
-
 
983
-            commands = (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0
 
984
-            self.btnSelfTest.setEnabled(commands and possible)
 
985
-            self.btnCleanHeads.setEnabled(commands and possible)
 
986
+        try:
 
987
+            printable = (self.ppd != None and
 
988
+                         not bool (self.changed) and
 
989
+                         self.printer.enabled and
 
990
+                         not self.printer.rejecting)
 
991
+
 
992
+            self.ui.btnPrintTestPage.setEnabled (printable)
 
993
+            adjustable = not (self.printer.discovered or bool (self.changed))
 
994
+            for button in [self.ui.btnChangePPD]:
 
995
+                           #self.ui.btnSelectDevice]:
 
996
+                button.setEnabled (adjustable)
 
997
+
 
998
+            selftest = False
 
999
+            cleanheads = False
 
1000
+            if (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0:
 
1001
+                attrs = self.printer.other_attributes
 
1002
+                formats = attrs.get('document-format-supported', [])
 
1003
+                try:
 
1004
+                    # Is the command format supported?
 
1005
+                    formats.index ('application/vnd.cups-command')
 
1006
+
 
1007
+                    # Yes...
 
1008
+                    commands = attrs.get('printer-commands', [])
 
1009
+                    for command in commands:
 
1010
+                        if command == "PrintSelfTestPage":
 
1011
+                            selftest = True
 
1012
+                            if cleanheads:
 
1013
+                                break
 
1014
+
 
1015
+                        elif command == "Clean":
 
1016
+                            cleanheads = True
 
1017
+                            if selftest:
 
1018
+                                break
 
1019
+                except ValueError:
 
1020
+                    # Command format not supported.
 
1021
+                    pass
 
1022
+
 
1023
+            for cond, button in [(selftest, self.ui.btnSelfTest),
 
1024
+                                 (cleanheads, self.ui.btnCleanHeads)]:
 
1025
+                if cond:
 
1026
+                    button.show ()
 
1027
+                else:
 
1028
+                    button.hide ()
 
1029
         except:
 
1030
             debugprint ("exception in setDataButtonState")
 
1031
-            pass
 
1032
+            nonfatalException()
 
1033
 
 
1034
         installablebold = False
 
1035
         optionsbold = False
 
1036
@@ -752,23 +818,30 @@
 
1037
         #self.ui.btnPrinterPropertiesApply.setEnabled(len (self.changed) > 0)
 
1038
         #self.ui.btnRevert.setEnabled(len (self.changed) > 0)
 
1039
 
 
1040
-    def save_printer(self, printer, saveall=False):
 
1041
+    def save_printer(self, printer, saveall=False, parent=None):
 
1042
+        if parent == None:
 
1043
+            parent = self.ui
 
1044
         class_deleted = False
 
1045
         name = printer.name
 
1046
 
 
1047
+        if printer.is_class:
 
1048
+            self.cups._begin_operation (_("modifying class %s") % name)
 
1049
+        else:
 
1050
+            self.cups._begin_operation (_("modifying printer %s") % name)
 
1051
+
 
1052
         try:
 
1053
-            if not printer.is_class and self.ppd: 
 
1054
+            if not printer.is_class and self.ppd:
 
1055
                 self.getPrinterSettings()
 
1056
                 if self.ppd.nondefaultsMarked() or saveall:
 
1057
-                    self.passwd_retry = False # use cached Passwd 
 
1058
                     self.cups.addPrinter(name, ppd=self.ppd)
 
1059
 
 
1060
             if printer.is_class:
 
1061
                 # update member list
 
1062
-                new_members = self.getCurrentClassMembers(self.ui.tvClassMembers)
 
1063
+                new_members = self.getCurrentClassMembers(self.ui.tvClassMembers) #FIXME newprinter in gnome UI
 
1064
                 if not new_members:
 
1065
                     result = KMessageBox.warningContinueCancel(self.ui, i18n("Class now has no members. Proceed anyway?"), i18n("Class Empty"))
 
1066
                     if result == KMessageBox.Continue:
 
1067
+                        self.cups._end_operation ()
 
1068
                         return True
 
1069
                     class_deleted = True
 
1070
 
 
1071
@@ -776,45 +849,41 @@
 
1072
                 old_members = printer.class_members[:]
 
1073
 
 
1074
                 for member in new_members:
 
1075
-                    member = unicode(member)
 
1076
                     if member in old_members:
 
1077
                         old_members.remove(member)
 
1078
                     else:
 
1079
                         self.cups.addPrinterToClass(member, name)
 
1080
                 for member in old_members:
 
1081
-                    self.cups.deletePrinterFromClass(member, name)    
 
1082
+                    self.cups.deletePrinterFromClass(member, name)
 
1083
 
 
1084
             location = unicode(self.ui.entPLocation.text())
 
1085
             info = unicode(self.ui.entPDescription.text())
 
1086
             device_uri = unicode(self.ui.entPDevice.text())
 
1087
-            if device_uri.find (ellipsis) != -1:
 
1088
-                # The URI is sanitized and not editable.
 
1089
-                device_uri = printer.device_uri
 
1090
 
 
1091
             enabled = self.ui.chkPEnabled.isChecked()
 
1092
             accepting = self.ui.chkPAccepting.isChecked()
 
1093
             shared = self.ui.chkPShared.isChecked()
 
1094
 
 
1095
             if info!=printer.info or saveall:
 
1096
-                self.passwd_retry = False # use cached Passwd 
 
1097
                 self.cups.setPrinterInfo(name, info)
 
1098
             if location!=printer.location or saveall:
 
1099
-                self.passwd_retry = False # use cached Passwd 
 
1100
                 self.cups.setPrinterLocation(name, location)
 
1101
             if (not printer.is_class and
 
1102
                 (device_uri!=printer.device_uri or saveall)):
 
1103
-                self.passwd_retry = False # use cached Passwd 
 
1104
                 self.cups.setPrinterDevice(name, device_uri)
 
1105
 
 
1106
             if enabled != printer.enabled or saveall:
 
1107
-                self.passwd_retry = False # use cached Passwd 
 
1108
                 self.printer.setEnabled(enabled)
 
1109
             if accepting == printer.rejecting or saveall:
 
1110
-                self.passwd_retry = False # use cached Passwd 
 
1111
                 self.printer.setAccepting(accepting)
 
1112
             if shared != printer.is_shared or saveall:
 
1113
-                self.passwd_retry = False # use cached Passwd 
 
1114
                 self.printer.setShared(shared)
 
1115
+            """
 
1116
+            def get_combo_value (cmb):
 
1117
+                model = cmb.get_model ()
 
1118
+                iter = cmb.get_active_iter ()
 
1119
+                return model.get_value (iter, 1)
 
1120
+            """
 
1121
 
 
1122
             job_sheet_start = unicode(self.ui.cmbPStartBanner.currentText())
 
1123
             job_sheet_end = unicode(self.ui.cmbPEndBanner.currentText())
 
1124
@@ -824,23 +893,18 @@
 
1125
 
 
1126
             if (job_sheet_start != printer.job_sheet_start or
 
1127
                 job_sheet_end != printer.job_sheet_end) or saveall:
 
1128
-                self.passwd_retry = False # use cached Passwd
 
1129
                 printer.setJobSheets(job_sheet_start, job_sheet_end)
 
1130
             #FIXME Not implemented in current UI
 
1131
             #if error_policy != printer.error_policy or saveall:
 
1132
-                #self.passwd_retry = False # use cached Passwd
 
1133
-                #printer.setErrorPolicy(error_policy)
 
1134
+            #   printer.setErrorPolicy(error_policy)
 
1135
             #if op_policy != printer.op_policy or saveall:
 
1136
-                #self.passwd_retry = False # use cached Passwd
 
1137
-                #printer.setOperationPolicy(op_policy)
 
1138
+            #   printer.setOperationPolicy(op_policy)
 
1139
 
 
1140
-            #Access Control
 
1141
             default_allow = self.ui.rbtnPAllow.isChecked()
 
1142
             except_users = self.getPUsers()
 
1143
 
 
1144
             if (default_allow != printer.default_allow or
 
1145
                 except_users != printer.except_users) or saveall:
 
1146
-                self.passwd_retry = False # use cached Passwd
 
1147
                 printer.setAccess(default_allow, except_users)
 
1148
 
 
1149
             for option in printer.attributes:
 
1150
@@ -848,36 +912,44 @@
 
1151
                     printer.unsetOption(option)
 
1152
             for option in self.server_side_options.itervalues():
 
1153
                 if (option.is_changed() or
 
1154
-                    saveall and
 
1155
-                    option.get_current_value () != option.system_default):
 
1156
-                    printer.setOption(unicode(option.name), unicode(option.get_current_value()) )
 
1157
+                    (saveall and
 
1158
+                     option.get_current_value () != option.get_default())):
 
1159
+                    printer.setOption(option.name, option.get_current_value())
 
1160
+
 
1161
+            if self.default_printer_state_changed():
 
1162
+                if self.ui.chkPMakeDefault.isChecked():
 
1163
+                    printer.setAsDefault ()
 
1164
 
 
1165
         except cups.IPPError, (e, s):
 
1166
-            self.show_IPP_Error(e, s)
 
1167
+            debugprint("except in save_printer")
 
1168
+            show_IPP_Error(e, s, parent)
 
1169
+            self.cups._end_operation ()
 
1170
             return True
 
1171
+        self.cups._end_operation ()
 
1172
         self.changed = set() # of options
 
1173
-        
 
1174
-        if not self.__dict__.has_key ("server_settings"):
 
1175
+
 
1176
+        if not self.cups._use_pk and not self.__dict__.has_key ("server_settings"):
 
1177
             # We can authenticate with the server correctly at this point,
 
1178
             # but we have never fetched the server settings to see whether
 
1179
             # the server is publishing shared printers.  Fetch the settings
 
1180
             # now so that we can update the "not published" label if necessary.
 
1181
+            self.cups._begin_operation (_("fetching server settings"))
 
1182
             try:
 
1183
                 self.server_settings = self.cups.adminGetServerSettings()
 
1184
             except:
 
1185
                 nonfatalException()
 
1186
 
 
1187
-        if class_deleted:
 
1188
-            self.populateList ()
 
1189
-        else:
 
1190
+            self.cups._end_operation ()
 
1191
+
 
1192
+        if not class_deleted:
 
1193
             # Update our copy of the printer's settings.
 
1194
             try:
 
1195
-                printers = cupshelpers.getPrinters (self.cups)
 
1196
-                this_printer = { name: printers[name] }
 
1197
-                self.printers.update (this_printer)
 
1198
-            except cups.IPPError, (e, s):
 
1199
-                show_IPP_Error(e, s, self.PrinterPropertiesDialog)
 
1200
+                self.printer.getAttributes ()
 
1201
+                self.updatePrinterProperties ()
 
1202
+            except cups.IPPError:
 
1203
+                pass
 
1204
 
 
1205
+        #self._monitor.update () FIXME
 
1206
         return False
 
1207
 
 
1208
     def getPrinterSettings(self):
 
1209
@@ -1070,6 +1142,7 @@
 
1210
             #catch the stateChanged signal to prevent a signal loop
 
1211
             self.ui.chkPMakeDefault.blockSignals(True) 
 
1212
             self.ui.chkPMakeDefault.setChecked(True) #set checked
 
1213
+            self.ui.chkPMakeDefault.setEnabled(False) #can't unset being the default
 
1214
             #unblock the signal
 
1215
             self.ui.chkPMakeDefault.blockSignals(False)
 
1216
             self.ui.chkPMakeDefault.setText(i18n("This is the default printer"))
 
1217
@@ -1077,12 +1150,13 @@
 
1218
             #catch the stateChanged signal to prevent a signal loop
 
1219
             self.ui.chkPMakeDefault.blockSignals(True)
 
1220
             self.ui.chkPMakeDefault.setChecked(False) #set unchecked
 
1221
+            self.ui.chkPMakeDefault.setEnabled(True)
 
1222
             #unblock the signal
 
1223
             self.ui.chkPMakeDefault.blockSignals(False)
 
1224
             if default is not None:
 
1225
-                self.ui.chkPMakeDefault.setText(i18n('Make Default. (The current default is %1)', default))
 
1226
+                self.ui.chkPMakeDefault.setText(i18n('Make default. (The current default is %1.)', default))
 
1227
             else:
 
1228
-                self.ui.chkPMakeDefault.setText(i18n('Make Default. (There is no current default)'))
 
1229
+                self.ui.chkPMakeDefault.setText(i18n('Make default. (There is no current default.)'))
 
1230
 
 
1231
         #self.setTestButton (printer)
 
1232
 
 
1233
@@ -1203,7 +1277,7 @@
 
1234
         if tab_nr != -1:
 
1235
             self.ui.ntbkPrinter.removeTab(tab_nr)
 
1236
         policiesTabNo = self.ui.ntbkPrinter.indexOf(self.policiesTabWidget)
 
1237
-        self.ui.ntbkPrinter.insertTab(policiesTabNo+1, self.optionsTabWidget, i18n("Options"))
 
1238
+        self.ui.ntbkPrinter.insertTab(policiesTabNo+1, self.optionsTabWidget, i18n("Printer Options"))
 
1239
 
 
1240
         """
 
1241
         # clean Installable Options Tab
 
1242
@@ -1545,7 +1619,7 @@
 
1243
             while it: #iterate over the tree
 
1244
                 try:
 
1245
                     item = it.next()
 
1246
-                    name = item.text(0)
 
1247
+                    name = unicode(item.text(0))
 
1248
                     result.append(name)
 
1249
                 except StopIteration:
 
1250
                     break
 
1251
@@ -1856,7 +1930,8 @@
 
1252
         if exception == cups.IPP_NOT_AUTHORIZED:
 
1253
             KMessageBox.error(self, i18n('The password may be incorrect.'), i18n('Not authorized'))
 
1254
         else:
 
1255
-            KMessageBox.error(self, i18n("There was an error during the CUPS " "operation: '%1'.", message),
 
1256
+            debugprint("show_IPP_Error() exception: " + str(exception))
 
1257
+            KMessageBox.error(self, i18n("There was an error during the CUPS operation: '%1'.", str(message)),
 
1258
                                     i18n('CUPS server error'))
 
1259
 
 
1260
     def show_HTTP_Error(self, status):
 
1261
@@ -1938,9 +2013,9 @@
 
1262
             debugprint ("no changes yet: full printer properties update")
 
1263
 
 
1264
             # State
 
1265
-            self.ui.chkPEnabled.setEnabled(printer.enabled)
 
1266
-            self.ui.chkPAccepting.setEnabled(not printer.rejecting)
 
1267
-            self.ui.chkPShared.setEnabled(printer.is_shared)
 
1268
+            self.ui.chkPEnabled.setChecked(printer.enabled)
 
1269
+            self.ui.chkPAccepting.setChecked(not printer.rejecting)
 
1270
+            self.ui.chkPShared.setChecked(printer.is_shared)
 
1271
 
 
1272
             # Job sheets
 
1273
             self.fillComboBox(self.ui.cmbPStartBanner,
 
1274
@@ -2030,8 +2105,8 @@
 
1275
         "ipp" : 3,
 
1276
         "http" : 3,
 
1277
         "lpd" : 4,
 
1278
-        "scsi" : 5,
 
1279
-        "serial" : 6,
 
1280
+        # "scsi" : 5, #not implemented yet, defaults to URL page
 
1281
+        #"serial" : 6,
 
1282
         "smb" : 7,
 
1283
         }
 
1284
 
 
1285
@@ -2068,7 +2143,9 @@
 
1286
         self.connect(self.entNPTDevice, SIGNAL("textEdited(const QString&)"), self.on_entNPTDevice_changed)
 
1287
 #        self.connect(self.entNPTIPPHostname, SIGNAL("textEdited(const QString&)"), self.on_entNPTIPPHostname_changed)
 
1288
 #        self.connect(self.entNPTIPPQueuename, SIGNAL("textEdited(const QString&)"), self.on_entNPTIPPQueuename_changed)
 
1289
-        self.connect(self.entSMBURI, SIGNAL("textEdited(const QString&)"), self.on_entSMBURI_changed)
 
1290
+        self.connect(self.entSMBURI, SIGNAL("textChanged(const QString&)"), self.on_entSMBURI_changed)
 
1291
+        self.rbtnSMBAuthPrompt.setChecked(True)
 
1292
+        self.connect(self.btnSMBBrowse, SIGNAL("clicked()"), self.on_btnSMBBrowse_clicked)
 
1293
         self.rbtnSMBAuthPrompt.setChecked(True)
 
1294
         self.on_rbtnSMBAuthSet_toggled(False)
 
1295
         self.connect(self.rbtnSMBAuthSet, SIGNAL("toggled(bool)"), self.on_rbtnSMBAuthSet_toggled)
 
1296
@@ -2104,30 +2181,23 @@
 
1297
         cell = gtk.CellRendererText()
 
1298
         combobox.pack_start (cell, True)
 
1299
         combobox.add_attribute(cell, 'text', 0)
 
1300
+        """
 
1301
 
 
1302
         # SMB browser
 
1303
-        self.smb_store = gtk.TreeStore (str, # host or share
 
1304
-                                        str, # comment
 
1305
-                                        gobject.TYPE_PYOBJECT, # domain dict
 
1306
-                                        gobject.TYPE_PYOBJECT) # host dict
 
1307
-        self.tvSMBBrowser.set_model (self.smb_store)
 
1308
-        self.smb_store.set_sort_column_id (0, gtk.SORT_ASCENDING)
 
1309
-
 
1310
-        # SMB list columns
 
1311
-        col = gtk.TreeViewColumn (_("Share"), gtk.CellRendererText (),
 
1312
-                                  text=0)
 
1313
-        col.set_resizable (True)
 
1314
-        col.set_sort_column_id (0)
 
1315
-        self.tvSMBBrowser.append_column (col)
 
1316
-
 
1317
-        col = gtk.TreeViewColumn (_("Comment"), gtk.CellRendererText (),
 
1318
-                                  text=1)
 
1319
-        self.tvSMBBrowser.append_column (col)
 
1320
-        slct = self.tvSMBBrowser.get_selection ()
 
1321
-        slct.set_select_function (self.smb_select_function)
 
1322
-
 
1323
-        self.SMBBrowseDialog.set_transient_for(self.NewPrinterWindow)
 
1324
-
 
1325
+        self.SMBBrowseDialog = KDialog(self)
 
1326
+        self.tvSMBBrowser = QTreeWidget(self.SMBBrowseDialog)
 
1327
+        self.SMBBrowseDialog.setMainWidget(self.tvSMBBrowser)
 
1328
+        self.SMBBrowseDialog.setButtons(KDialog.ButtonCode(KDialog.Ok | KDialog.User1 | KDialog.Cancel))
 
1329
+        self.SMBBrowseDialog.setButtonText(KDialog.User1, i18n("Refresh"))
 
1330
+        self.SMBBrowseDialog.setButtonIcon(KDialog.User1, KIcon("view-refresh"))
 
1331
+        self.SMBBrowseDialog.setCaption(i18n("SMB Browser"))
 
1332
+        self.tvSMBBrowser.setHeaderLabels([i18n("Share"), i18n("Comment")])
 
1333
+        self.connect(self.tvSMBBrowser, SIGNAL("itemExpanded(QTreeWidgetItem *)"), self.on_tvSMBBrowser_row_expanded)
 
1334
+        self.connect(self.tvSMBBrowser, SIGNAL("itemSelectionChanged()"), self.on_tvSMBBrowser_cursor_changed)
 
1335
+        self.connect(self.SMBBrowseDialog, SIGNAL("okClicked()"), self.on_btnSMBBrowseOk_clicked)
 
1336
+        self.connect(self.SMBBrowseDialog, SIGNAL("user1Clicked()"), self.on_btnSMBBrowseRefresh_clicked)
 
1337
+        self.smb_store = {}
 
1338
+        """
 
1339
         # IPP browser
 
1340
         self.ipp_store = gtk.TreeStore (str, # queue
 
1341
                                         str, # location
 
1342
@@ -2167,8 +2237,7 @@
 
1343
         """
 
1344
 
 
1345
         #FIXME hide bits which are not yet implemented
 
1346
-        self.btnSMBBrowse.hide()
 
1347
-        self.btnSMBVerify.hide()
 
1348
+        #self.btnSMBVerify.hide()
 
1349
         self.btnNPTLpdProbe.hide()
 
1350
 
 
1351
     def option_changed(self, option):
 
1352
@@ -3064,13 +3133,211 @@
 
1353
         self.tvNPDevices.setCurrentRow(0)
 
1354
         self.on_tvNPDevices_cursor_changed()
 
1355
 
 
1356
+    def entry_changed(self, entry, allowed_chars):
 
1357
+        "Remove all chars from entry's text that are not in allowed_chars."
 
1358
+        try:
 
1359
+            allowed_chars = unicode (allowed_chars, locale.getpreferredencoding())
 
1360
+        except UnicodeDecodeError:
 
1361
+            allowed_chars = unicode (allowed_chars)
 
1362
+        origtext = unicode (entry.text())
 
1363
+        new_text = origtext
 
1364
+        for char in origtext:
 
1365
+            if char not in allowed_chars:
 
1366
+                new_text = new_text.replace(char, "")
 
1367
+                debugprint ("removed disallowed character %s" % char)
 
1368
+        if origtext!=new_text:
 
1369
+            entry.setText(new_text)
 
1370
+
 
1371
     def on_entNPTDevice_changed(self, entry):
 
1372
         self.setNPButtons()
 
1373
 
 
1374
-    #TODO
 
1375
+
 
1376
     ## SMB browsing
 
1377
+
 
1378
+    def browse_smb_hosts(self):
 
1379
+        """Initialise the SMB tree store."""
 
1380
+        self.tvSMBBrowser.clear()
 
1381
+        self.busy(self.SMBBrowseDialog)
 
1382
+        self.tvSMBBrowser.addTopLevelItem(QTreeWidgetItem([i18n("Scanning...")]))
 
1383
+        store = self.smb_store = {}
 
1384
+        KApplication.processEvents()
 
1385
+
 
1386
+        debug = 0
 
1387
+        if get_debugging ():
 
1388
+            debug = 10
 
1389
+        smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
 
1390
+        ctx = pysmb.smbc.Context (debug=debug,
 
1391
+                                  auth_fn=smbc_auth.callback)
 
1392
+        entries = None
 
1393
+        try:
 
1394
+            while smbc_auth.perform_authentication () > 0:
 
1395
+                try:
 
1396
+                    entries = ctx.opendir ("smb://").getdents ()
 
1397
+                except Exception, e:
 
1398
+                    smbc_auth.failed (e)
 
1399
+        except RuntimeError, (e, s):
 
1400
+            if e != errno.ENOENT:
 
1401
+                debugprint ("Runtime error: %s" % repr ((e, s)))
 
1402
+        except:
 
1403
+            nonfatalException ()
 
1404
+
 
1405
+        self.tvSMBBrowser.clear()
 
1406
+        if entries:
 
1407
+            for entry in entries:
 
1408
+                if entry.smbc_type in [pysmb.smbc.WORKGROUP,
 
1409
+                                       pysmb.smbc.SERVER]:
 
1410
+                    item = QTreeWidgetItem([self.smbbrowser_cell_share(entry), self.smbbrowser_cell_comment(entry)])
 
1411
+                    self.tvSMBBrowser.addTopLevelItem(item)
 
1412
+                    item.setChildIndicatorPolicy(QTreeWidgetItem.ShowIndicator)
 
1413
+                    store[item] = entry
 
1414
+
 
1415
+        """TODO
 
1416
+        specified_uri = SMBURI (uri=self.entSMBURI.get_text ())
 
1417
+        (group, host, share, user, password) = specified_uri.separate ()
 
1418
+        if len (host) > 0:
 
1419
+            # The user has specified a server before clicking Browse.
 
1420
+            # Append the server as a top-level entry.
 
1421
+            class FakeEntry:
 
1422
+                pass
 
1423
+            toplevel = FakeEntry ()
 
1424
+            toplevel.smbc_type = pysmb.smbc.SERVER
 
1425
+            toplevel.name = host
 
1426
+            toplevel.comment = ''
 
1427
+            iter = store.append (None, [toplevel])
 
1428
+            i = store.append (iter)
 
1429
+
 
1430
+            # Now expand it.
 
1431
+            path = store.get_path (iter)
 
1432
+            self.tvSMBBrowser.expand_row (path, 0)
 
1433
+
 
1434
+        """
 
1435
+        self.ready(self.SMBBrowseDialog)
 
1436
+
 
1437
+        if len(store) == 0:
 
1438
+            self.SMBBrowseDialog.hide ()
 
1439
+            KMessageBox.error(self, 
 
1440
+                              i18n("There were no SMB print shares found.  "
 
1441
+                                "Please check that the Samba service is running and "
 
1442
+                                "marked as trusted in your firewall "
 
1443
+                                "configuration."), i18n("No SMB Print Shares"))
 
1444
+
 
1445
+    def smb_select_function (self, path):
 
1446
+        """Don't allow this path to be selected unless it is a leaf."""
 
1447
+        iter = self.smb_store.get_iter (path)
 
1448
+        return not self.smb_store.iter_has_child (iter)
 
1449
+
 
1450
+    def smbbrowser_cell_share (self, entry):
 
1451
+        share = ''
 
1452
+        if entry != None:
 
1453
+            share = entry.name
 
1454
+        return share
 
1455
+
 
1456
+    def smbbrowser_cell_comment (self, entry):
 
1457
+        comment = ''
 
1458
+        if entry != None:
 
1459
+            comment = entry.comment
 
1460
+        return comment
 
1461
+
 
1462
+    def on_tvSMBBrowser_row_expanded (self, item):
 
1463
+        """Handler for expanding a row in the SMB tree view."""
 
1464
+        entry = self.smb_store[item]
 
1465
+        if entry == None:
 
1466
+            return
 
1467
+
 
1468
+        if entry.smbc_type == pysmb.smbc.WORKGROUP:
 
1469
+            # Workgroup
 
1470
+            # Be careful though: if there is a server with the
 
1471
+            # same name as the workgroup we will get a list of its
 
1472
+            # shares, not the workgroup's servers.
 
1473
+            try:
 
1474
+                if self.expanding_row:
 
1475
+                    return
 
1476
+            except:
 
1477
+                self.expanding_row = 1
 
1478
+
 
1479
+            self.busy (self.SMBBrowseDialog)
 
1480
+            uri = "smb://%s/" % entry.name
 
1481
+            debug = 0
 
1482
+            if get_debugging ():
 
1483
+                debug = 10
 
1484
+            smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
 
1485
+            ctx = pysmb.smbc.Context (debug=debug,
 
1486
+                                      auth_fn=smbc_auth.callback)
 
1487
+            entries = []
 
1488
+            try:
 
1489
+                while smbc_auth.perform_authentication () > 0:
 
1490
+                    try:
 
1491
+                        entries = ctx.opendir (uri).getdents ()
 
1492
+                    except Exception, e:
 
1493
+                        smbc_auth.failed (e)
 
1494
+            except RuntimeError, (e, s):
 
1495
+                if e != errno.ENOENT:
 
1496
+                    debugprint ("Runtime error: %s" % repr ((e, s)))
 
1497
+            except:
 
1498
+                nonfatalException()
 
1499
+
 
1500
+            item.takeChildren()
 
1501
+
 
1502
+            for entry in entries:
 
1503
+                if entry.smbc_type in [pysmb.smbc.SERVER,
 
1504
+                                       pysmb.smbc.PRINTER_SHARE]:
 
1505
+                    pass
 
1506
+                    #i = model.append (iter, [entry])
 
1507
+                if entry.smbc_type == pysmb.smbc.SERVER:
 
1508
+                    newItem = QTreeWidgetItem([self.smbbrowser_cell_share(entry), self.smbbrowser_cell_comment(entry)])
 
1509
+                    item.addChild(newItem)
 
1510
+                    newItem.setChildIndicatorPolicy(QTreeWidgetItem.ShowIndicator)
 
1511
+                    self.smb_store[newItem] = entry
 
1512
+
 
1513
+            del self.expanding_row
 
1514
+            self.ready (self.SMBBrowseDialog)
 
1515
+
 
1516
+        elif entry.smbc_type == pysmb.smbc.SERVER:
 
1517
+            # Server
 
1518
+            try:
 
1519
+                if self.expanding_row:
 
1520
+                    return
 
1521
+            except:
 
1522
+                self.expanding_row = 1
 
1523
+
 
1524
+            self.busy (self.SMBBrowseDialog)
 
1525
+            uri = "smb://%s/" % entry.name
 
1526
+            debug = 0
 
1527
+            if get_debugging ():
 
1528
+                debug = 10
 
1529
+            smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
 
1530
+            ctx = pysmb.smbc.Context (debug=debug,
 
1531
+                                      auth_fn=smbc_auth.callback)
 
1532
+            shares = []
 
1533
+            try:
 
1534
+                while smbc_auth.perform_authentication () > 0:
 
1535
+                    try:
 
1536
+                        shares = ctx.opendir (uri).getdents ()
 
1537
+                    except Exception, e:
 
1538
+                        smbc_auth.failed (e)
 
1539
+            except RuntimeError, (e, s):
 
1540
+                if e != errno.EACCES and e != errno.EPERM:
 
1541
+                    debugprint ("Runtime error: %s" % repr ((e, s)))
 
1542
+            except:
 
1543
+                nonfatalException()
 
1544
+
 
1545
+            item.takeChildren()
 
1546
+
 
1547
+            for share in shares:
 
1548
+                if share.smbc_type == pysmb.smbc.PRINTER_SHARE:
 
1549
+                    newItem = QTreeWidgetItem([self.smbbrowser_cell_share(share), self.smbbrowser_cell_comment(share)])
 
1550
+                    item.addChild(newItem)
 
1551
+                    self.smb_store[newItem] = share
 
1552
+                    debugprint (repr (share))
 
1553
+
 
1554
+            del self.expanding_row
 
1555
+            self.ready (self.SMBBrowseDialog)
 
1556
+
 
1557
     def on_entSMBURI_changed (self, text):
 
1558
-        uri = unicode(text)
 
1559
+        ent = self.sender()
 
1560
+        allowed_chars = string.letters+string.digits+'_-./:@'
 
1561
+        self.entry_changed(ent, allowed_chars)
 
1562
+        uri = unicode(ent.text())
 
1563
         (group, host, share, user, password) = SMBURI (uri=uri).separate ()
 
1564
         if user:
 
1565
             self.entSMBUsername.setText(user)
 
1566
@@ -3078,20 +3345,140 @@
 
1567
             self.entSMBPassword.setText(password)
 
1568
         if user or password:
 
1569
             uri = SMBURI (group=group, host=host, share=share).get_uri ()
 
1570
-            self.entSMBURI.setText(uri)
 
1571
+            ent.setText(uri)
 
1572
             self.rbtnSMBAuthSet.setChecked(True)
 
1573
-        elif unicode(self.entSMBUsername.text()) == '':
 
1574
+        elif self.entSMBUsername.text() == '':
 
1575
             self.rbtnSMBAuthPrompt.setChecked(True)
 
1576
 
 
1577
         self.btnSMBVerify.setEnabled(bool(uri))
 
1578
+        self.setNPButtons ()
 
1579
 
 
1580
-    def on_rbtnSMBAuthSet_toggled(self, ticked):
 
1581
-        self.tblSMBAuth.setEnabled(ticked)
 
1582
+    def on_tvSMBBrowser_cursor_changed(self):
 
1583
+        store = self.smb_store
 
1584
+        item = self.tvSMBBrowser.currentItem()
 
1585
+        entry = store[item]
 
1586
+        self.SMBBrowseDialog.enableButton(KDialog.Ok, entry and entry.smbc_type == pysmb.smbc.PRINTER_SHARE)
 
1587
+
 
1588
+    def on_btnSMBBrowse_clicked(self):
 
1589
+        self.SMBBrowseDialog.enableButton(KDialog.Ok, False)
 
1590
+
 
1591
+        """ Firewall check is Fedora specific
 
1592
+        try:
 
1593
+            # Note: we do the browsing from *this* machine, regardless
 
1594
+            # of which CUPS server we are connected to.
 
1595
+            f = firewall.Firewall ()
 
1596
+            allowed = f.check_samba_client_allowed ()
 
1597
+        except:
 
1598
+            allowed = False
 
1599
+
 
1600
+        if not allowed:
 
1601
+            show_info_dialog (_("Review Firewall"),
 
1602
+                              _("You may need to adjust the firewall "
 
1603
+                                "to allow network printer discovery on this "
 
1604
+                                "computer.") + '\n\n' +
 
1605
+                              TEXT_start_firewall_tool,
 
1606
+                              parent=self.NewPrinterWindow)
 
1607
+        """
 
1608
+        self.SMBBrowseDialog.show()
 
1609
+        self.browse_smb_hosts()
 
1610
+
 
1611
+    def on_btnSMBBrowseOk_clicked(self):
 
1612
+        store = self.smb_store
 
1613
+        item = self.tvSMBBrowser.currentItem()
 
1614
+        is_share = False
 
1615
+        if item:
 
1616
+            entry = store[item]
 
1617
+            if entry:
 
1618
+                is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
 
1619
+
 
1620
+        if not entry or not is_share:
 
1621
+            self.SMBBrowseDialog.hide()
 
1622
+            return
 
1623
+
 
1624
+        share = item.text(0)
 
1625
+        host = item.parent().text(0)
 
1626
+        if item.parent().parent():
 
1627
+            group = item.parent().parent().text(0)
 
1628
+        else:
 
1629
+            group = ''
 
1630
+        uri = SMBURI (group=unicode(group),
 
1631
+                      host=unicode(host),
 
1632
+                      share=unicode(share)).get_uri ()
 
1633
+
 
1634
+        self.entSMBUsername.setText('')
 
1635
+        self.entSMBPassword.setText('')
 
1636
+        self.entSMBURI.setText(uri)
 
1637
+
 
1638
+        self.SMBBrowseDialog.hide()
 
1639
+
 
1640
+    def on_btnSMBBrowseRefresh_clicked(self):
 
1641
+        self.browse_smb_hosts()
 
1642
+
 
1643
+    def on_rbtnSMBAuthSet_toggled(self, active):
 
1644
+        self.tblSMBAuth.setEnabled(active)
 
1645
+
 
1646
+    def on_btnSMBVerify_clicked(self):
 
1647
+        uri = unicode(self.entSMBURI.text())
 
1648
+        (group, host, share, u, p) = SMBURI (uri=uri).separate ()
 
1649
+        user = ''
 
1650
+        passwd = ''
 
1651
+        reason = None
 
1652
+        auth_set = self.rbtnSMBAuthSet.isChecked()
 
1653
+        if auth_set:
 
1654
+            user = unicode(self.entSMBUsername.text())
 
1655
+            passwd = unicode(self.entSMBPassword.text())
 
1656
+
 
1657
+        accessible = False
 
1658
+        canceled = False
 
1659
+        self.busy ()
 
1660
+        try:
 
1661
+            debug = 0
 
1662
+            if get_debugging ():
 
1663
+                debug = 10
 
1664
+
 
1665
+            if auth_set:
 
1666
+                # No prompting.
 
1667
+                def do_auth (svr, shr, wg, un, pw):
 
1668
+                    return (group, user, passwd)
 
1669
+                ctx = pysmb.smbc.Context (debug=debug, auth_fn=do_auth)
 
1670
+                f = ctx.open ("smb://%s/%s" % (host, share),
 
1671
+                              os.O_RDWR, 0777)
 
1672
+                accessible = True
 
1673
+            else:
 
1674
+                # May need to prompt.
 
1675
+                smbc_auth = pysmb.AuthContext (self,
 
1676
+                                               workgroup=group,
 
1677
+                                               user=user,
 
1678
+                                               passwd=passwd)
 
1679
+                ctx = pysmb.smbc.Context (debug=debug,
 
1680
+                                          auth_fn=smbc_auth.callback)
 
1681
+                while smbc_auth.perform_authentication () > 0:
 
1682
+                    try:
 
1683
+                        f = ctx.open ("smb://%s/%s" % (host, share),
 
1684
+                                      os.O_RDWR, 0777)
 
1685
+                        accessible = True
 
1686
+                    except Exception, e:
 
1687
+                        smbc_auth.failed (e)
 
1688
+
 
1689
+                if not accessible:
 
1690
+                    canceled = True
 
1691
+        except RuntimeError, (e, s):
 
1692
+            debugprint ("Error accessing share: %s" % repr ((e, s)))
 
1693
+            reason = s
 
1694
+        except:
 
1695
+            nonfatalException()
 
1696
+        self.ready ()
 
1697
+
 
1698
+        if accessible:
 
1699
+            KMessageBox.information (self, i18n("This print share is accessible."),
 
1700
+                                    i18n("Print Share Verified"))
 
1701
+            return
 
1702
 
 
1703
-    def on_entNPTIPPHostname_textChanged(self):
 
1704
-        valid = len (self.entNPTIPPHostname.text ()) > 0
 
1705
-        self.btnIPPFindQueue.setEnabled(valid)
 
1706
-        self.update_IPP_URI_label ()
 
1707
+        if not canceled:
 
1708
+            text = i18n("This print share is not accessible.")
 
1709
+            if reason:
 
1710
+                text = reason
 
1711
+            KMessageBox.error (self, text, i18n("Inaccessible"))
 
1712
 
 
1713
     ### IPP Browsing
 
1714
     def update_IPP_URI_label(self):
 
1715
diff -urN kdeadmin-4.6.0/system-config-printer-kde/system-config-printer.ui kdeadmin/system-config-printer-kde/system-config-printer.ui
 
1716
--- kdeadmin-4.6.0/system-config-printer-kde/system-config-printer.ui   2011-01-19 22:09:10.000000000 +0000
 
1717
+++ kdeadmin/system-config-printer-kde/system-config-printer.ui 2011-02-23 16:22:52.300290533 +0000
 
1718
@@ -527,21 +527,21 @@
 
1719
                <item>
 
1720
                 <widget class="QCheckBox" name="chkPEnabled">
 
1721
                  <property name="text">
 
1722
-                  <string>enable</string>
 
1723
+                  <string>Enable</string>
 
1724
                  </property>
 
1725
                 </widget>
 
1726
                </item>
 
1727
                <item>
 
1728
                 <widget class="QCheckBox" name="chkPAccepting">
 
1729
                  <property name="text">
 
1730
-                  <string>accepting</string>
 
1731
+                  <string>Accepting</string>
 
1732
                  </property>
 
1733
                 </widget>
 
1734
                </item>
 
1735
                <item>
 
1736
                 <widget class="QCheckBox" name="chkPShared">
 
1737
                  <property name="text">
 
1738
-                  <string>sharing</string>
 
1739
+                  <string>Sharing</string>
 
1740
                  </property>
 
1741
                 </widget>
 
1742
                </item>
 
1743
@@ -861,7 +861,7 @@
 
1744
            </widget>
 
1745
            <widget class="QWidget" name="tab_2">
 
1746
             <attribute name="title">
 
1747
-             <string>Options</string>
 
1748
+             <string>Printer Options</string>
 
1749
             </attribute>
 
1750
             <layout class="QVBoxLayout" name="verticalLayout_3">
 
1751
              <item>
 
1752
@@ -878,7 +878,7 @@
 
1753
                   <x>0</x>
 
1754
                   <y>0</y>
 
1755
                   <width>532</width>
 
1756
-                  <height>426</height>
 
1757
+                  <height>427</height>
 
1758
                  </rect>
 
1759
                 </property>
 
1760
                 <layout class="QGridLayout" name="gridLayout_5">
 
1761
@@ -912,7 +912,7 @@
 
1762
                  <rect>
 
1763
                   <x>0</x>
 
1764
                   <y>0</y>
 
1765
-                  <width>514</width>
 
1766
+                  <width>515</width>
 
1767
                   <height>1026</height>
 
1768
                  </rect>
 
1769
                 </property>