Skip to content

Commit 7d927e3

Browse files
committed
fixed vnc embedded
1 parent 13e73aa commit 7d927e3

3 files changed

Lines changed: 231 additions & 24 deletions

File tree

gtk3/PCM.py

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,8 @@ def _apri_rdp(self, nome: str, dati: dict):
10781078

10791079
def _apri_vnc(self, nome: str, dati: dict):
10801080
self._resolve_credentials(dati)
1081+
sftp_enabled = dati.get("sftp_browser", False)
1082+
10811083
if dati.get("vnc_internal", False):
10821084
# Viewer integrato
10831085
def _salva_password_vnc(pwd: str):
@@ -1095,16 +1097,81 @@ def _salva_password_vnc(pwd: str):
10951097
except Exception:
10961098
pass
10971099

1098-
widget = VncWebWidget(
1099-
host=dati.get("host",""),
1100-
port=dati.get("port","5900"),
1101-
password=dati.get("password",""),
1102-
color_depth=dati.get("vnc_color", 0),
1103-
quality=dati.get("vnc_quality", 2),
1104-
on_save_password=_salva_password_vnc,
1105-
)
1106-
widget.show_all()
1107-
container = self._sftp_browser_paned(widget, dati)
1100+
if sftp_enabled:
1101+
# Pannello SFTP differito: aperto solo dopo che VNC è connesso
1102+
# e solo se la porta SSH risponde.
1103+
ssh_host = dati.get("host", "")
1104+
ssh_port = int(dati.get("mon_ssh_port", 22))
1105+
sftp_dati = dict(dati)
1106+
sftp_dati["port"] = ssh_port
1107+
1108+
ph_lbl = Gtk.Label(label="In attesa della connessione VNC…")
1109+
ph_lbl.set_valign(Gtk.Align.CENTER)
1110+
ph_lbl.set_halign(Gtk.Align.CENTER)
1111+
ph_lbl.set_line_wrap(True)
1112+
ph_lbl.set_margin_start(8)
1113+
ph_lbl.set_margin_end(8)
1114+
placeholder = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
1115+
placeholder.set_size_request(220, -1)
1116+
placeholder.pack_start(ph_lbl, True, True, 0)
1117+
1118+
paned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
1119+
1120+
def _on_vnc_ready():
1121+
import socket as _sock
1122+
ph_lbl.set_text(f"Verifica SSH {ssh_host}:{ssh_port}…")
1123+
1124+
def _probe():
1125+
try:
1126+
s = _sock.create_connection((ssh_host, ssh_port), timeout=3)
1127+
s.close()
1128+
GLib.idle_add(_ssh_aperta)
1129+
except OSError:
1130+
GLib.idle_add(_ssh_chiusa)
1131+
1132+
def _ssh_aperta():
1133+
sftp = SftpBrowserWidget(
1134+
sftp_dati,
1135+
on_apri_editor=lambda c, p: self._apri_sftp_editor(c, p),
1136+
)
1137+
paned.remove(placeholder)
1138+
paned.pack2(sftp, False, False)
1139+
sftp.show_all()
1140+
1141+
def _ssh_chiusa():
1142+
ph_lbl.set_markup(
1143+
f"<small>Porta SSH {ssh_port}\nnon raggiungibile\nsu {ssh_host}</small>"
1144+
)
1145+
1146+
threading.Thread(target=_probe, daemon=True).start()
1147+
1148+
widget = VncWebWidget(
1149+
host=dati.get("host", ""),
1150+
port=dati.get("port", "5900"),
1151+
password=dati.get("password", ""),
1152+
color_depth=dati.get("vnc_color", 0),
1153+
quality=dati.get("vnc_quality", 2),
1154+
on_save_password=_salva_password_vnc,
1155+
on_vnc_ready=_on_vnc_ready,
1156+
)
1157+
widget.show_all()
1158+
paned.pack1(widget, True, True)
1159+
paned.pack2(placeholder, False, True)
1160+
paned.set_position(700)
1161+
paned.show_all()
1162+
container = paned
1163+
else:
1164+
widget = VncWebWidget(
1165+
host=dati.get("host", ""),
1166+
port=dati.get("port", "5900"),
1167+
password=dati.get("password", ""),
1168+
color_depth=dati.get("vnc_color", 0),
1169+
quality=dati.get("vnc_quality", 2),
1170+
on_save_password=_salva_password_vnc,
1171+
)
1172+
widget.show_all()
1173+
container = widget
1174+
11081175
container._pcm_dati = dati
11091176
self._append_tab(container, nome, lambda: self._chiudi_tab(container))
11101177
else:
@@ -1119,7 +1186,58 @@ def _salva_password_vnc(pwd: str):
11191186
widget = TerminalWidget.da_profilo(dati)
11201187
widget.comando_display = nome
11211188
widget.show_all()
1122-
container = self._sftp_browser_paned(widget, dati)
1189+
1190+
if sftp_enabled:
1191+
# Pannello SFTP differito: probe SSH in background senza attendere VNC
1192+
ssh_host = dati.get("host", "")
1193+
ssh_port = int(dati.get("mon_ssh_port", 22))
1194+
sftp_dati = dict(dati)
1195+
sftp_dati["port"] = ssh_port
1196+
1197+
ph_lbl = Gtk.Label(label=f"Verifica SSH {ssh_host}:{ssh_port}…")
1198+
ph_lbl.set_valign(Gtk.Align.CENTER)
1199+
ph_lbl.set_halign(Gtk.Align.CENTER)
1200+
ph_lbl.set_line_wrap(True)
1201+
ph_lbl.set_margin_start(8)
1202+
ph_lbl.set_margin_end(8)
1203+
placeholder = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
1204+
placeholder.set_size_request(220, -1)
1205+
placeholder.pack_start(ph_lbl, True, True, 0)
1206+
1207+
paned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
1208+
paned.pack1(widget, True, True)
1209+
paned.pack2(placeholder, False, True)
1210+
paned.set_position(700)
1211+
paned.show_all()
1212+
container = paned
1213+
1214+
def _ssh_aperta_ext():
1215+
sftp = SftpBrowserWidget(
1216+
sftp_dati,
1217+
on_apri_editor=lambda c, p: self._apri_sftp_editor(c, p),
1218+
)
1219+
paned.remove(placeholder)
1220+
paned.pack2(sftp, False, False)
1221+
sftp.show_all()
1222+
1223+
def _ssh_chiusa_ext():
1224+
ph_lbl.set_markup(
1225+
f"<small>Porta SSH {ssh_port}\nnon raggiungibile\nsu {ssh_host}</small>"
1226+
)
1227+
1228+
def _probe_ext():
1229+
import socket as _sock
1230+
try:
1231+
s = _sock.create_connection((ssh_host, ssh_port), timeout=3)
1232+
s.close()
1233+
GLib.idle_add(_ssh_aperta_ext)
1234+
except OSError:
1235+
GLib.idle_add(_ssh_chiusa_ext)
1236+
1237+
threading.Thread(target=_probe_ext, daemon=True).start()
1238+
else:
1239+
container = widget
1240+
11231241
container._pcm_dati = dati
11241242
self._append_tab(container, nome)
11251243
GLib.idle_add(widget.grab_focus)

gtk3/sftp_browser.py

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,16 +166,49 @@ def _init_ui(self):
166166
# ------------------------------------------------------------------
167167

168168
def _connetti(self):
169-
try:
170-
host = self._profilo.get("host", "")
171-
port = int(self._profilo.get("port", 22))
169+
import socket as _socket
170+
171+
host = self._profilo.get("host", "")
172+
if not host:
173+
GLib.idle_add(self._set_status, "Host non configurato")
174+
return
175+
176+
port = int(self._profilo.get("port", 22))
177+
pkey = self._profilo.get("private_key", "")
178+
179+
# Per VNC/RDP la password del profilo è quella VNC/RDP, non SSH.
180+
proto = self._profilo.get("protocol", "ssh")
181+
if proto in ("vnc", "rdp"):
182+
user = ""
183+
pwd = ""
184+
else:
172185
user = self._profilo.get("user", "")
173186
pwd = self._profilo.get("password", "")
174-
pkey = self._profilo.get("private_key", "")
175187

188+
# Verifica raggiungibilità porta SSH prima di tentare l'auth.
189+
# Una semplice probe TCP non scatena fail2ban.
190+
GLib.idle_add(self._set_status, f"Verifica SSH {host}:{port}…")
191+
try:
192+
probe = _socket.create_connection((host, port), timeout=3)
193+
probe.close()
194+
except OSError:
195+
GLib.idle_add(self._set_status, f"SSH non disponibile su {host}:{port}")
196+
return
197+
198+
# Porta aperta ma credenziali SSH mancanti: chiedi all'utente.
199+
if not user:
200+
creds = [None]
201+
done = threading.Event()
202+
GLib.idle_add(self._mostra_dialog_credenziali_ssh, host, port, creds, done)
203+
done.wait(timeout=120)
204+
if not creds[0]:
205+
GLib.idle_add(self._set_status, "Connessione SSH annullata")
206+
return
207+
user, pwd = creds[0]
208+
209+
GLib.idle_add(self._set_status, f"Connessione SSH {user}@{host}:{port}…")
210+
try:
176211
self._ssh = paramiko.SSHClient()
177-
# RejectPolicy: rifiuta host sconosciuti invece di accettarli silenziosamente.
178-
# known_hosts dell'utente viene caricato automaticamente da load_system_host_keys().
179212
self._ssh.load_system_host_keys()
180213
if self._profilo.get("strict_host", False):
181214
self._ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
@@ -191,7 +224,6 @@ def _connetti(self):
191224
self._ssh.connect(**kwargs)
192225
self._sftp = self._ssh.open_sftp()
193226

194-
# Home directory
195227
_, stdout, _ = self._ssh.exec_command("echo $HOME")
196228
home = stdout.read().decode().strip() or "/"
197229
self._cwd = home
@@ -200,7 +232,6 @@ def _connetti(self):
200232
except paramiko.ssh_exception.SSHException as e:
201233
msg = str(e)
202234
if "not found in known_hosts" in msg or "Unknown server" in msg.lower():
203-
# Host non presente nei known_hosts: mostra istruzioni all'utente
204235
avviso = (
205236
f"Chiave host di '{host}' non trovata nei known_hosts.\n"
206237
f"Verifica l'impronta con il server e aggiungi manualmente:\n"
@@ -213,6 +244,53 @@ def _connetti(self):
213244
except Exception as e:
214245
GLib.idle_add(self._set_status, t("sftp.err_connect").format(e=e))
215246

247+
def _mostra_dialog_credenziali_ssh(self, host: str, port: int,
248+
creds: list, done: threading.Event):
249+
"""Dialog GTK per inserire username/password SSH. Chiamato sul main thread."""
250+
parent = self.get_toplevel()
251+
parent = parent if isinstance(parent, Gtk.Window) else None
252+
dlg = Gtk.Dialog(
253+
title=f"Credenziali SSH — {host}:{port}",
254+
transient_for=parent,
255+
modal=True,
256+
)
257+
dlg.add_buttons("Annulla", Gtk.ResponseType.CANCEL,
258+
"Connetti", Gtk.ResponseType.OK)
259+
dlg.set_default_response(Gtk.ResponseType.OK)
260+
261+
area = dlg.get_content_area()
262+
area.set_spacing(6)
263+
area.set_margin_start(12)
264+
area.set_margin_end(12)
265+
area.set_margin_top(8)
266+
area.set_margin_bottom(8)
267+
268+
grid = Gtk.Grid(row_spacing=6, column_spacing=8)
269+
lbl_u = Gtk.Label(label="Utente SSH:", xalign=1.0)
270+
ent_u = Gtk.Entry()
271+
ent_u.set_hexpand(True)
272+
ent_u.set_activates_default(True)
273+
lbl_p = Gtk.Label(label="Password SSH:", xalign=1.0)
274+
ent_p = Gtk.Entry()
275+
ent_p.set_visibility(False)
276+
ent_p.set_hexpand(True)
277+
ent_p.set_activates_default(True)
278+
grid.attach(lbl_u, 0, 0, 1, 1)
279+
grid.attach(ent_u, 1, 0, 1, 1)
280+
grid.attach(lbl_p, 0, 1, 1, 1)
281+
grid.attach(ent_p, 1, 1, 1, 1)
282+
area.pack_start(grid, False, False, 0)
283+
dlg.show_all()
284+
285+
resp = dlg.run()
286+
user = ent_u.get_text().strip()
287+
pwd = ent_p.get_text()
288+
dlg.destroy()
289+
290+
if resp == Gtk.ResponseType.OK and user:
291+
creds[0] = (user, pwd)
292+
done.set()
293+
216294
# ------------------------------------------------------------------
217295
# Navigazione
218296
# ------------------------------------------------------------------

gtk3/vnc_widget.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ def _find_vnc_client() -> str | None:
5656

5757
class _VncGtkVnc(Gtk.Box):
5858

59-
def __init__(self, host, port, password, color_depth=0, quality=2, on_save_password=None):
59+
def __init__(self, host, port, password, color_depth=0, quality=2,
60+
on_save_password=None, on_vnc_ready=None):
6061
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=0)
6162
self._host = host
6263
self._port = str(port)
6364
self._password = password
6465
self._on_save_password = on_save_password
66+
self._on_vnc_ready = on_vnc_ready
6567
self._closed = False
6668
self._scaling = True
6769
self._pointer_local = False
@@ -279,6 +281,8 @@ def _on_initialized(self, d):
279281
f"VNC — {self._host}:{self._port}"
280282
+ (f" [{nome}]" if nome else "")
281283
)
284+
if self._on_vnc_ready:
285+
self._on_vnc_ready()
282286

283287
def _on_disconnected(self, d):
284288
if not self._closed:
@@ -450,11 +454,12 @@ class _VncSocket(Gtk.Box):
450454
"xvnc4viewer": ("-Parent {}", "-passwd {}"),
451455
}
452456

453-
def __init__(self, host, port, password):
457+
def __init__(self, host, port, password, on_vnc_ready=None):
454458
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=0)
455459
self._host = host
456460
self._port = str(port)
457461
self._password = password
462+
self._on_vnc_ready = on_vnc_ready
458463
self._client = _find_vnc_client()
459464
self._proc = None
460465
self._closed = False
@@ -563,6 +568,8 @@ def _write_passwd_file(password):
563568

564569
def _on_plug_added(self, s):
565570
self._lbl.set_text(f"VNC → {self._host}:{self._port}")
571+
if self._on_vnc_ready:
572+
self._on_vnc_ready()
566573

567574
def _on_plug_removed(self, s):
568575
if not self._closed:
@@ -628,7 +635,8 @@ def chiudi_processo(self):
628635
# ---------------------------------------------------------------------------
629636

630637
def VncWebWidget(host: str, port: str = "5900", password: str = "",
631-
color_depth: int = 0, quality: int = 2, on_save_password=None):
638+
color_depth: int = 0, quality: int = 2,
639+
on_save_password=None, on_vnc_ready=None):
632640
"""
633641
Restituisce il miglior widget VNC disponibile:
634642
1. gtk-vnc nativo (gir1.2-gtk-vnc-2.0) — toolbar completa
@@ -637,11 +645,14 @@ def VncWebWidget(host: str, port: str = "5900", password: str = "",
637645
638646
color_depth: 0=32bpp, 1=16bpp, 2=8bpp
639647
quality: 0=best, 1=good, 2=fast
648+
on_vnc_ready: callable() invocato sul main thread quando VNC è connesso
640649
"""
641650
if _GTKV_OK:
642651
return _VncGtkVnc(host=host, port=port, password=password,
643652
color_depth=color_depth, quality=quality,
644-
on_save_password=on_save_password)
653+
on_save_password=on_save_password,
654+
on_vnc_ready=on_vnc_ready)
645655
if _find_vnc_client():
646-
return _VncSocket(host=host, port=port, password=password)
656+
return _VncSocket(host=host, port=port, password=password,
657+
on_vnc_ready=on_vnc_ready)
647658
return _VncNoDriver(host=host, port=port)

0 commit comments

Comments
 (0)