rdp-client/files/rdp-profile-manager.py

447 lines
19 KiB
Python
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
HackerSoft RDP Profile Manager - GUI für RDP Thin Client
Speichere diese Datei als: files/rdp-profile-manager.py
Version: 1.1
"""
import tkinter as tk
from tkinter import ttk, messagebox
import configparser
import subprocess
from pathlib import Path
import os
CONFIG_DIR = Path.home() / ".config" / "rdp-profiles"
CONFIG_FILE = CONFIG_DIR / "profiles.ini"
VERSION = "1.1"
# Firmeninformationen
COMPANY_NAME = "Hacker-Net Telekommunikation"
COMPANY_ADDRESS = "Am Wunderburgpark 5b\n26135 Oldenburg"
COMPANY_PHONE = "Tel.: +49 441 35065316"
COMPANY_EMAIL = "E-Mail: info@hacker-net.de"
# Prüfe ob Emoji-Fonts verfügbar sind
def check_emoji_support():
"""Prüft ob Emoji-Fonts installiert sind"""
emoji_fonts = [
'/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf',
'/usr/share/fonts/opentype/noto/NotoColorEmoji.ttf',
'/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf'
]
return any(Path(font).exists() for font in emoji_fonts)
# Emoji-Support erkennen
USE_EMOJIS = check_emoji_support()
# Button-Texte (mit oder ohne Emojis)
if USE_EMOJIS:
BTN_NEW = " Neues Profil"
BTN_EDIT = "✏️ Bearbeiten"
BTN_DELETE = "🗑️ Löschen"
BTN_CONNECT = "🔌 Verbinden"
BTN_BLUETOOTH = "🎧 Bluetooth"
BTN_INFO = " Info"
BTN_AUTOSTART = "⭐ Autostart"
BTN_SAVE = "💾 Speichern"
BTN_CANCEL = "❌ Abbrechen"
LBL_AUDIO = "🔊 Audio umleiten"
LBL_MIC = "🎤 Mikrofon umleiten"
LBL_USB = "💾 USB-Geräte umleiten"
LBL_SMARTCARD = "💳 Smart Cards umleiten"
LBL_PRINTER = "🖨️ Drucker umleiten"
else:
BTN_NEW = "+ Neues Profil"
BTN_EDIT = "Bearbeiten"
BTN_DELETE = "Löschen"
BTN_CONNECT = "Verbinden"
BTN_BLUETOOTH = "Bluetooth"
BTN_INFO = "Info"
BTN_AUTOSTART = "* Autostart"
BTN_SAVE = "Speichern"
BTN_CANCEL = "Abbrechen"
LBL_AUDIO = "Audio umleiten"
LBL_MIC = "Mikrofon umleiten"
LBL_USB = "USB-Geräte umleiten"
LBL_SMARTCARD = "Smart Cards umleiten"
LBL_PRINTER = "Drucker umleiten"
class RDPProfileManager:
def __init__(self, root):
self.root = root
self.root.title("HackerSoft - RDP Profile Manager")
self.root.geometry("900x600")
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
if not CONFIG_FILE.exists():
CONFIG_FILE.touch()
self.config = configparser.ConfigParser()
self.load_profiles()
self.create_widgets()
self.refresh_profile_list()
def load_profiles(self):
self.config.read(CONFIG_FILE)
def save_profiles(self):
with open(CONFIG_FILE, 'w') as f:
self.config.write(f)
def create_widgets(self):
# Top Frame - Buttons
top_frame = ttk.Frame(self.root, padding="10")
top_frame.pack(fill=tk.X)
ttk.Button(top_frame, text=BTN_NEW, command=self.new_profile).pack(side=tk.LEFT, padx=5)
ttk.Button(top_frame, text=BTN_EDIT, command=self.edit_profile).pack(side=tk.LEFT, padx=5)
ttk.Button(top_frame, text=BTN_DELETE, command=self.delete_profile).pack(side=tk.LEFT, padx=5)
ttk.Button(top_frame, text=BTN_CONNECT, command=self.connect_profile).pack(side=tk.LEFT, padx=5)
ttk.Button(top_frame, text=BTN_AUTOSTART, command=self.toggle_autostart).pack(side=tk.LEFT, padx=5)
# Rechte Seite
ttk.Button(top_frame, text=BTN_INFO, command=self.show_info).pack(side=tk.RIGHT, padx=5)
ttk.Button(top_frame, text=BTN_BLUETOOTH, command=self.open_bluetooth).pack(side=tk.RIGHT, padx=5)
# Profile List
list_frame = ttk.Frame(self.root, padding="10")
list_frame.pack(fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(list_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
columns = ('Autostart', 'Server', 'Benutzer', 'Auflösung', 'Audio', 'USB')
self.tree = ttk.Treeview(list_frame, columns=columns, show='tree headings', yscrollcommand=scrollbar.set)
scrollbar.config(command=self.tree.yview)
self.tree.heading('#0', text='Profil')
self.tree.heading('Autostart', text='Auto')
self.tree.heading('Server', text='Server')
self.tree.heading('Benutzer', text='Benutzer')
self.tree.heading('Auflösung', text='Auflösung')
self.tree.heading('Audio', text='Audio')
self.tree.heading('USB', text='USB')
self.tree.column('#0', width=150)
self.tree.column('Autostart', width=50)
self.tree.column('Server', width=180)
self.tree.column('Benutzer', width=120)
self.tree.column('Auflösung', width=120)
self.tree.column('Audio', width=80)
self.tree.column('USB', width=80)
self.tree.pack(fill=tk.BOTH, expand=True)
self.tree.bind('<Double-1>', lambda e: self.connect_profile())
def show_info(self):
"""Zeigt Info-Dialog mit Firmendaten"""
info_window = tk.Toplevel(self.root)
info_window.title("Info")
info_window.geometry("450x350")
info_window.transient(self.root)
info_window.grab_set()
# Logo/Header
header_frame = ttk.Frame(info_window, padding="20")
header_frame.pack(fill=tk.X)
ttk.Label(header_frame, text="HackerSoft", font=('Arial', 24, 'bold')).pack()
ttk.Label(header_frame, text="RDP Profile Manager", font=('Arial', 12)).pack()
ttk.Label(header_frame, text=f"Version {VERSION}", font=('Arial', 10), foreground='gray').pack()
# Separator
ttk.Separator(info_window, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=20, pady=10)
# Firmeninfo
info_frame = ttk.Frame(info_window, padding="20")
info_frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(info_frame, text=COMPANY_NAME, font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0,5))
ttk.Label(info_frame, text=COMPANY_ADDRESS, font=('Arial', 10)).pack(anchor=tk.W, pady=2)
ttk.Label(info_frame, text=COMPANY_PHONE, font=('Arial', 10)).pack(anchor=tk.W, pady=2)
ttk.Label(info_frame, text=COMPANY_EMAIL, font=('Arial', 10), foreground='blue').pack(anchor=tk.W, pady=2)
# Button
button_frame = ttk.Frame(info_window, padding="10")
button_frame.pack(fill=tk.X, side=tk.BOTTOM)
ttk.Button(button_frame, text="OK", command=info_window.destroy).pack()
def toggle_autostart(self):
"""Setzt/entfernt Autostart für ausgewähltes Profil"""
selected = self.tree.selection()
if not selected:
messagebox.showwarning("Warnung", "Bitte wählen Sie ein Profil aus!")
return
profile_name = self.tree.item(selected[0])['text']
profile = self.config[profile_name]
# Aktueller Autostart-Status
current_autostart = profile.getboolean('autostart', False)
if current_autostart:
# Deaktivieren
self.config[profile_name]['autostart'] = 'False'
messagebox.showinfo("Autostart", f"Autostart für '{profile_name}' wurde deaktiviert!")
else:
# Aktivieren - alle anderen deaktivieren
for section in self.config.sections():
self.config[section]['autostart'] = 'False'
self.config[profile_name]['autostart'] = 'True'
messagebox.showinfo("Autostart", f"'{profile_name}' wird jetzt automatisch gestartet!")
self.save_profiles()
self.refresh_profile_list()
def refresh_profile_list(self):
self.tree.delete(*self.tree.get_children())
for profile_name in self.config.sections():
profile = self.config[profile_name]
autostart_icon = '' if profile.getboolean('autostart', False) else ''
username_display = profile.get('username', '-') or '-'
self.tree.insert('', tk.END, text=profile_name, values=(
autostart_icon,
profile.get('server', ''),
username_display,
profile.get('resolution', 'Client'),
'' if profile.getboolean('redirect_audio', True) else '',
'' if profile.getboolean('redirect_usb', True) else ''
))
def new_profile(self):
dialog = ProfileDialog(self.root, "Neues Profil")
self.root.wait_window(dialog.top)
if dialog.result:
profile_name = dialog.result['name']
if profile_name in self.config:
messagebox.showerror("Fehler", f"Profil '{profile_name}' existiert bereits!")
return
self.config[profile_name] = {
'server': dialog.result['server'],
'username': dialog.result['username'],
'domain': dialog.result['domain'],
'resolution': dialog.result['resolution'],
'multimon': str(dialog.result['multimon']),
'redirect_audio': str(dialog.result['redirect_audio']),
'redirect_microphone': str(dialog.result['redirect_microphone']),
'redirect_usb': str(dialog.result['redirect_usb']),
'redirect_smartcard': str(dialog.result['redirect_smartcard']),
'redirect_printers': str(dialog.result['redirect_printers']),
'exit_hotkey': dialog.result['exit_hotkey'],
'autostart': 'False'
}
self.save_profiles()
self.refresh_profile_list()
messagebox.showinfo("Erfolg", f"Profil '{profile_name}' wurde erstellt!")
def edit_profile(self):
selected = self.tree.selection()
if not selected:
messagebox.showwarning("Warnung", "Bitte wählen Sie ein Profil aus!")
return
profile_name = self.tree.item(selected[0])['text']
profile = self.config[profile_name]
dialog = ProfileDialog(self.root, f"Profil bearbeiten: {profile_name}", profile_name, profile)
self.root.wait_window(dialog.top)
if dialog.result:
new_name = dialog.result['name']
if new_name != profile_name:
self.config.remove_section(profile_name)
self.config[new_name] = {
'server': dialog.result['server'],
'username': dialog.result['username'],
'domain': dialog.result['domain'],
'resolution': dialog.result['resolution'],
'multimon': str(dialog.result['multimon']),
'redirect_audio': str(dialog.result['redirect_audio']),
'redirect_microphone': str(dialog.result['redirect_microphone']),
'redirect_usb': str(dialog.result['redirect_usb']),
'redirect_smartcard': str(dialog.result['redirect_smartcard']),
'redirect_printers': str(dialog.result['redirect_printers']),
'exit_hotkey': dialog.result['exit_hotkey'],
'autostart': profile.get('autostart', 'False')
}
self.save_profiles()
self.refresh_profile_list()
messagebox.showinfo("Erfolg", f"Profil '{new_name}' wurde aktualisiert!")
def delete_profile(self):
selected = self.tree.selection()
if not selected:
messagebox.showwarning("Warnung", "Bitte wählen Sie ein Profil aus!")
return
profile_name = self.tree.item(selected[0])['text']
if messagebox.askyesno("Bestätigung", f"Profil '{profile_name}' wirklich löschen?"):
self.config.remove_section(profile_name)
self.save_profiles()
self.refresh_profile_list()
messagebox.showinfo("Erfolg", f"Profil '{profile_name}' wurde gelöscht!")
def connect_profile(self):
selected = self.tree.selection()
if not selected:
messagebox.showwarning("Warnung", "Bitte wählen Sie ein Profil aus!")
return
profile_name = self.tree.item(selected[0])['text']
try:
subprocess.Popen(['/usr/local/bin/rdp-launcher.sh', profile_name])
self.root.withdraw()
except Exception as e:
messagebox.showerror("Fehler", f"Verbindung fehlgeschlagen: {str(e)}")
def open_bluetooth(self):
try:
subprocess.Popen(['blueman-manager'])
except Exception as e:
messagebox.showerror("Fehler", f"Bluetooth Manager konnte nicht gestartet werden: {str(e)}")
class ProfileDialog:
def __init__(self, parent, title, profile_name=None, profile_data=None):
self.result = None
self.top = tk.Toplevel(parent)
self.top.title(title)
self.top.geometry("550x700")
self.top.transient(parent)
self.top.grab_set()
main_frame = ttk.Frame(self.top, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
row = 0
# Profile Name
ttk.Label(main_frame, text="Profilname:", font=('', 10, 'bold')).grid(row=row, column=0, sticky=tk.W, pady=5)
self.name_var = tk.StringVar(value=profile_name or "")
ttk.Entry(main_frame, textvariable=self.name_var, width=40).grid(row=row, column=1, pady=5)
row += 1
ttk.Separator(main_frame, orient=tk.HORIZONTAL).grid(row=row, column=0, columnspan=2, sticky='ew', pady=10)
row += 1
# Server
ttk.Label(main_frame, text="Server:").grid(row=row, column=0, sticky=tk.W, pady=5)
self.server_var = tk.StringVar(value=profile_data.get('server', '') if profile_data else '')
ttk.Entry(main_frame, textvariable=self.server_var, width=40).grid(row=row, column=1, pady=5)
row += 1
# Username
ttk.Label(main_frame, text="Benutzername (optional):").grid(row=row, column=0, sticky=tk.W, pady=5)
self.username_var = tk.StringVar(value=profile_data.get('username', '') if profile_data else '')
ttk.Entry(main_frame, textvariable=self.username_var, width=40).grid(row=row, column=1, pady=5)
row += 1
# Domain
ttk.Label(main_frame, text="Domäne (optional):").grid(row=row, column=0, sticky=tk.W, pady=5)
self.domain_var = tk.StringVar(value=profile_data.get('domain', '') if profile_data else '')
ttk.Entry(main_frame, textvariable=self.domain_var, width=40).grid(row=row, column=1, pady=5)
row += 1
# Resolution
ttk.Label(main_frame, text="Auflösung:").grid(row=row, column=0, sticky=tk.W, pady=5)
self.resolution_var = tk.StringVar(value=profile_data.get('resolution', 'client') if profile_data else 'client')
resolution_combo = ttk.Combobox(main_frame, textvariable=self.resolution_var, width=37, state='readonly')
resolution_combo['values'] = ('client', '1920x1080', '1680x1050', '1600x900', '1440x900', '1366x768', '1280x1024', '1024x768')
resolution_combo.grid(row=row, column=1, pady=5)
row += 1
# Multi-Monitor
self.multimon_var = tk.BooleanVar(value=profile_data.getboolean('multimon', False) if profile_data else False)
ttk.Checkbutton(main_frame, text="Alle Monitore nutzen (Multi-Monitor)", variable=self.multimon_var).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=5)
row += 1
ttk.Separator(main_frame, orient=tk.HORIZONTAL).grid(row=row, column=0, columnspan=2, sticky='ew', pady=15)
row += 1
ttk.Label(main_frame, text="Umleitungen:", font=('', 10, 'bold')).grid(row=row, column=0, sticky=tk.W, pady=5)
row += 1
# Redirects
self.audio_var = tk.BooleanVar(value=profile_data.getboolean('redirect_audio', True) if profile_data else True)
ttk.Checkbutton(main_frame, text=LBL_AUDIO, variable=self.audio_var).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=3)
row += 1
self.microphone_var = tk.BooleanVar(value=profile_data.getboolean('redirect_microphone', True) if profile_data else True)
ttk.Checkbutton(main_frame, text=LBL_MIC, variable=self.microphone_var).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=3)
row += 1
self.usb_var = tk.BooleanVar(value=profile_data.getboolean('redirect_usb', True) if profile_data else True)
ttk.Checkbutton(main_frame, text=LBL_USB, variable=self.usb_var).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=3)
row += 1
self.smartcard_var = tk.BooleanVar(value=profile_data.getboolean('redirect_smartcard', True) if profile_data else True)
ttk.Checkbutton(main_frame, text=LBL_SMARTCARD, variable=self.smartcard_var).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=3)
row += 1
self.printers_var = tk.BooleanVar(value=profile_data.getboolean('redirect_printers', False) if profile_data else False)
ttk.Checkbutton(main_frame, text=LBL_PRINTER, variable=self.printers_var).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=3)
row += 1
ttk.Separator(main_frame, orient=tk.HORIZONTAL).grid(row=row, column=0, columnspan=2, sticky='ew', pady=15)
row += 1
# Exit Hotkey
ttk.Label(main_frame, text="Tastenkombination:").grid(row=row, column=0, sticky=tk.W, pady=5)
self.hotkey_var = tk.StringVar(value=profile_data.get('exit_hotkey', 'Control+Alt+q') if profile_data else 'Control+Alt+q')
ttk.Entry(main_frame, textvariable=self.hotkey_var, width=40).grid(row=row, column=1, pady=5)
row += 1
ttk.Label(main_frame, text="Format: Control+Alt+q", font=('', 8), foreground='gray').grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=2)
row += 1
# Buttons
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=row, column=0, columnspan=2, pady=20)
ttk.Button(button_frame, text=BTN_SAVE, command=self.ok).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text=BTN_CANCEL, command=self.cancel).pack(side=tk.LEFT, padx=5)
def ok(self):
name = self.name_var.get().strip()
server = self.server_var.get().strip()
username = self.username_var.get().strip()
if not name or not server:
messagebox.showerror("Fehler", "Bitte füllen Sie mindestens Profilname und Server aus!")
return
self.result = {
'name': name,
'server': server,
'username': username,
'domain': self.domain_var.get().strip(),
'resolution': self.resolution_var.get(),
'multimon': self.multimon_var.get(),
'redirect_audio': self.audio_var.get(),
'redirect_microphone': self.microphone_var.get(),
'redirect_usb': self.usb_var.get(),
'redirect_smartcard': self.smartcard_var.get(),
'redirect_printers': self.printers_var.get(),
'exit_hotkey': self.hotkey_var.get().strip()
}
self.top.destroy()
def cancel(self):
self.top.destroy()
if __name__ == '__main__':
root = tk.Tk()
app = RDPProfileManager(root)
root.mainloop()