Skip to content

Commit 51bb62f

Browse files
committed
Add web interface support for network printers and update related UI elements
1 parent 780bd93 commit 51bb62f

2 files changed

Lines changed: 189 additions & 32 deletions

File tree

cupshelpers/cupshelpers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ def __init__(self, uri, **kw):
526526
self.make_and_model = kw.get('device-make-and-model', '')
527527
self.id = kw.get('device-id', '')
528528
self.location = kw.get('device-location', '')
529+
self.other_attributes = kw.copy ()
529530

530531
uri_pieces = uri.split(":")
531532
self.type = uri_pieces[0]

newprinter.py

Lines changed: 188 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ def __init__(self):
239239
self.recommended_model_selected = False
240240
self._searchdialog = None
241241
self._installdialog = None
242+
self.web_interface_device = None
242243

243244
self.getWidgets({"NewPrinterWindow":
244245
["NewPrinterWindow",
@@ -464,7 +465,7 @@ def protect_toggle (toggle_widget):
464465
self.expNPDeviceURIs.connect ("notify::expanded",
465466
self.on_expNPDeviceURIs_expanded)
466467
self.expNPDeviceURIs.set_expanded(1)
467-
self.btnNPOpenWebInterface.set_sensitive (False)
468+
# self.btnNPOpenWebInterface.set_sensitive (False)
468469

469470
# SMB browser
470471
self.smb_store = Gtk.TreeStore (GObject.TYPE_PYOBJECT)
@@ -2249,33 +2250,33 @@ def fillDeviceTab(self, current_uri=None):
22492250
network_iter = model.append (None, row=[_("Network Printer"),
22502251
None,
22512252
False])
2253+
network_dict = { 'device-class': 'network',
2254+
'device-info': _("Find Network Printer") }
2255+
network = cupshelpers.Device ('network', **network_dict)
2256+
find_nw_iter = model.append (network_iter,
2257+
row=[self._manual_network_device_label (network),
2258+
PhysicalDevice (network), False])
22522259
model.append (network_iter, row=['', None, True])
22532260
ipp_group_iter = model.append (network_iter,
22542261
row=[_("IPP Destinations"),
22552262
None,
22562263
False])
22572264
model.append (network_iter, row=['', None, True])
2258-
queue_group_iter = model.append (network_iter,
2259-
row=[_("Queues & Others"),
2260-
None,
2261-
False])
2262-
network_dict = { 'device-class': 'network',
2263-
'device-info': _("Find Network Printer") }
2264-
network = cupshelpers.Device ('network', **network_dict)
2265-
find_nw_iter = model.append (queue_group_iter,
2266-
row=[self._manual_network_device_label (network),
2267-
PhysicalDevice (network), False])
2265+
legacy_group_iter = model.append (network_iter,
2266+
row=[_("Legacy Protocols"),
2267+
None,
2268+
False])
22682269
smbdev_dict = { 'device-class': 'network',
22692270
'device-info': _("Windows Printer via SAMBA") }
22702271
smbdev = cupshelpers.Device ('smb', **smbdev_dict)
2271-
model.append (queue_group_iter,
2272+
model.append (legacy_group_iter,
22722273
row=[self._manual_network_device_label (smbdev),
22732274
PhysicalDevice (smbdev), False])
22742275
self.devices_uri_iter = uri_iter
22752276
self.devices_find_nw_iter = find_nw_iter
22762277
self.devices_network_iter = network_iter
22772278
self.devices_network_ipp_group_iter = ipp_group_iter
2278-
self.devices_network_queue_group_iter = queue_group_iter
2279+
self.devices_network_legacy_group_iter = legacy_group_iter
22792280
self.devices_network_fetched = False
22802281
self.tvNPDevices.set_model (model)
22812282
self.entNPTDevice.set_text ('')
@@ -2978,8 +2979,10 @@ def device_uri_select_function (self, selection, model, path, *UNUSED):
29782979
return model.get_value (iter, 2) == "device"
29792980

29802981
def _classify_connection_device (self, device):
2981-
if device.type == "ipp":
2982+
2983+
if device.type in ["ipp", "ipps", "https"]:
29822984
parsed = urllib.parse.urlparse (device.uri)
2985+
print(device.type,"dtttt",parsed.path.startswith ("/printers/"))
29832986
if parsed.path.startswith ("/printers/"):
29842987
return "queue"
29852988
return "ipp"
@@ -3029,7 +3032,7 @@ def _set_default_connection_selection (self, model):
30293032

30303033
iter = model.iter_next (iter)
30313034

3032-
self.btnNPOpenWebInterface.set_sensitive (False)
3035+
# self.btnNPOpenWebInterface.set_sensitive (False)
30333036

30343037
def _get_connection_path_for_uri (self, uri):
30353038
model = self.tvNPDeviceURIs.get_model ()
@@ -3051,7 +3054,7 @@ def _network_group_for_manual_device (self, device):
30513054
if device.type in ["ipp", "ipps", "https"]:
30523055
return self.devices_network_ipp_group_iter
30533056

3054-
return self.devices_network_queue_group_iter
3057+
return self.devices_network_legacy_group_iter
30553058

30563059
def _manual_network_device_label (self, device):
30573060
labels = {
@@ -3066,6 +3069,144 @@ def _manual_network_device_label (self, device):
30663069
}
30673070
return labels.get (device.type,
30683071
getattr (device, "info", None) or device.uri)
3072+
3073+
def _get_adminurl_from_avahi(self, device):
3074+
import subprocess
3075+
import re
3076+
3077+
try:
3078+
output = subprocess.check_output(
3079+
["avahi-browse", "-rt", "_ipps._tcp"],
3080+
text=True
3081+
)
3082+
3083+
matches = re.findall(r'adminurl=([^\s"]+)', output)
3084+
3085+
if matches:
3086+
return matches[0].replace(".local./", ".local/")
3087+
3088+
except Exception as e:
3089+
print("Avahi error:", e)
3090+
3091+
return None
3092+
def _get_device_web_interface_url(self, device, physicaldevice=None):
3093+
import urllib.parse
3094+
3095+
attrs = getattr(device, "other_attributes", {})
3096+
3097+
for key in ["printer-more-info", "device-more-info", "adminurl"]:
3098+
url = attrs.get(key)
3099+
if isinstance(url, list):
3100+
url = url[0] if url else None
3101+
if url:
3102+
return url.replace(".local./", ".local/")
3103+
3104+
if physicaldevice:
3105+
txt = getattr(physicaldevice, "txt", None) or \
3106+
getattr(physicaldevice, "dnssd_txt", None)
3107+
3108+
if txt:
3109+
for entry in txt:
3110+
if isinstance(entry, bytes):
3111+
entry = entry.decode(errors="ignore")
3112+
3113+
if isinstance(entry, str) and entry.startswith("adminurl="):
3114+
url = entry.split("=", 1)[1]
3115+
return url.replace(".local./", ".local/")
3116+
3117+
parsed = urllib.parse.urlparse(device.uri)
3118+
raw_host = parsed.hostname or ""
3119+
3120+
if "._tcp" in raw_host:
3121+
url = self._get_adminurl_from_avahi(device)
3122+
print("ggg",url)
3123+
if url:
3124+
return url
3125+
3126+
host = raw_host
3127+
3128+
if not host or "._tcp" in host:
3129+
host = (
3130+
attrs.get("hostname") or
3131+
attrs.get("host") or
3132+
attrs.get("address") or
3133+
attrs.get("ip-address")
3134+
)
3135+
3136+
if not host and physicaldevice:
3137+
host = (
3138+
getattr(physicaldevice, "dnssd_hostname", None) or
3139+
getattr(physicaldevice, "_network_host", None) or
3140+
getattr(physicaldevice, "address", None)
3141+
)
3142+
3143+
if not host:
3144+
return None
3145+
3146+
host = urllib.parse.unquote(host).rstrip(".")
3147+
3148+
scheme = "https" if parsed.scheme in ["ipps", "https"] else "http"
3149+
3150+
port = parsed.port
3151+
if port and port not in [80, 443]:
3152+
return f"{scheme}://{host}:{port}/"
3153+
3154+
return f"{scheme}://{host}/"
3155+
3156+
def _get_preferred_ipp_device (self, physicaldevice):
3157+
for device in physicaldevice.get_devices ():
3158+
if (self._classify_connection_device (device) == "ipp" and
3159+
self._get_device_web_interface_url (device,
3160+
physicaldevice=physicaldevice) is not None):
3161+
return device
3162+
3163+
return None
3164+
3165+
def _get_selected_physical_device (self):
3166+
path, column = self.tvNPDevices.get_cursor ()
3167+
if path is None:
3168+
return None
3169+
3170+
model = self.tvNPDevices.get_model ()
3171+
if model is None:
3172+
return None
3173+
3174+
iter = model.get_iter (path)
3175+
if iter is None:
3176+
return None
3177+
3178+
return model.get_value (iter, 1)
3179+
3180+
def _get_selected_connection_device (self):
3181+
path, column = self.tvNPDeviceURIs.get_cursor ()
3182+
if path is None:
3183+
return None
3184+
3185+
model = self.tvNPDeviceURIs.get_model ()
3186+
if model is None:
3187+
return None
3188+
3189+
iter = model.get_iter (path)
3190+
if iter is None:
3191+
return None
3192+
3193+
return model.get_value (iter, 1)
3194+
3195+
def _update_web_interface_button (self, device=None, physicaldevice=None):
3196+
if device is None:
3197+
device = self._get_selected_connection_device ()
3198+
if (device is None or
3199+
self._classify_connection_device (device) != "ipp" or
3200+
self._get_device_web_interface_url (device,
3201+
physicaldevice=physicaldevice) is None):
3202+
if physicaldevice is None:
3203+
physicaldevice = self._get_selected_physical_device ()
3204+
if physicaldevice is not None:
3205+
device = self._get_preferred_ipp_device (physicaldevice)
3206+
else:
3207+
device = None
3208+
self.web_interface_device = device
3209+
self.btnNPOpenWebInterface.set_sensitive (device is not None)
30693210

30703211
def device_row_separator_fn (self, model, iter, data):
30713212
return model.get_value (iter, 2)
@@ -3152,14 +3293,12 @@ def on_tvNPDevices_cursor_changed(self, widget):
31523293
self.device_selected += 1
31533294
path, column = widget.get_cursor ()
31543295
if path is None:
3155-
self.btnNPOpenWebInterface.set_sensitive (False)
31563296
return
31573297

31583298
model = widget.get_model ()
31593299
iter = model.get_iter (path)
31603300
physicaldevice = model.get_value (iter, 1)
31613301
if physicaldevice is None:
3162-
self.btnNPOpenWebInterface.set_sensitive (False)
31633302
return
31643303
show_uris = True
31653304
for device in physicaldevice.get_devices ():
@@ -3345,28 +3484,27 @@ def on_tvNPDevices_cursor_changed(self, widget):
33453484
model, self._connection_group_title (group),
33463485
grouped_devices[group])
33473486
self._set_default_connection_selection (model)
3487+
# Keep main selection in sync with the default connection row.
3488+
self.device = self._get_selected_connection_device ()
3489+
self._update_web_interface_button (physicaldevice=physicaldevice)
33483490
if show_uris:
33493491
self.expNPDeviceURIs.show_all ()
33503492
else:
33513493
self.expNPDeviceURIs.hide ()
3352-
self.btnNPOpenWebInterface.set_sensitive (False)
33533494

33543495
def on_tvNPDeviceURIs_cursor_changed(self, widget):
33553496
path, column = widget.get_cursor ()
33563497
if path is None:
3357-
self.btnNPOpenWebInterface.set_sensitive (False)
33583498
return
33593499

33603500
model = widget.get_model ()
33613501
iter = model.get_iter (path)
33623502
device = model.get_value(iter, 1)
33633503
if device is None:
3364-
self.btnNPOpenWebInterface.set_sensitive (False)
33653504
return
33663505

33673506
self.device = device
3368-
self.btnNPOpenWebInterface.set_sensitive (
3369-
self._classify_connection_device (device) == "ipp")
3507+
self._update_web_interface_button (device=device)
33703508
self.lblNPDeviceDescription.set_text ('')
33713509
page = self.new_printer_device_tabs.get (device.type, self.PAGE_SELECT_DEVICE)
33723510
self.ntbkNPType.set_current_page(page)
@@ -3523,16 +3661,32 @@ def on_tvNPDeviceURIs_cursor_changed(self, widget):
35233661

35243662
self.setNPButtons()
35253663

3526-
def on_btnNPOpenWebInterface_clicked (self, button):
3527-
try:
3528-
Gtk.show_uri_on_window (self.NewPrinterWindow,
3529-
"http://localhost:8000",
3530-
Gdk.CURRENT_TIME)
3531-
except GLib.GError as e:
3532-
show_error_dialog (_("Unable to Open Web Interface"),
3533-
str (e),
3534-
parent=self.NewPrinterWindow)
3664+
def on_btnNPOpenWebInterface_clicked(self, button):
3665+
import subprocess
3666+
3667+
url = self._get_device_web_interface_url(
3668+
self.web_interface_device,
3669+
physicaldevice=self._get_selected_physical_device()
3670+
)
35353671

3672+
print(url)
3673+
3674+
if not url:
3675+
show_error_dialog(
3676+
_("Unable to Open Web Interface"),
3677+
_("No web interface URL was provided by the printer."),
3678+
parent=self.NewPrinterWindow
3679+
)
3680+
return
3681+
3682+
try:
3683+
subprocess.Popen(["xdg-open", url])
3684+
except Exception as e:
3685+
show_error_dialog(
3686+
_("Unable to Open Web Interface"),
3687+
str(e),
3688+
parent=self.NewPrinterWindow
3689+
)
35363690
def on_entNPTLpdHost_changed(self, ent):
35373691
hostname = ent.get_text()
35383692
self.btnNPTLpdProbe.set_sensitive (len (hostname) > 0)
@@ -3664,6 +3818,8 @@ def found_network_printer_callback (self, new_device):
36643818
###
36653819

36663820
def getDeviceURI(self):
3821+
if self.device is None:
3822+
raise AttributeError
36673823
if self.dialog_mode in ['printer_with_uri', 'ppd']:
36683824
return self.device.uri
36693825

0 commit comments

Comments
 (0)