13 Commits

Author SHA1 Message Date
2311661735 part one more optimization with app_config file 2025-04-30 23:24:00 +02:00
c10667ec21 bug fix in rename 2025-04-30 10:43:51 +02:00
08bef8fe6e add_wp_app_config.py for central configuration 2025-04-30 09:49:57 +02:00
2e94a324a6 add wp_app_config.py for central configuration 2025-04-30 09:48:40 +02:00
18ed97bf20 delete empty row 2025-04-29 16:20:07 +02:00
5dcfc91621 replace more "with open" 2025-04-29 15:09:56 +02:00
5fb4e68867 methods optimized-13:24 2025-04-29 13:25:03 +02:00
19d413ea97 optimized remove with open 2025-04-29 12:32:23 +02:00
213f772f40 methods optimized in wirepy and cls_mth_fc 2025-04-29 11:39:05 +02:00
6f02724daa with opening reduced 2025-04-29 09:02:06 +02:00
53f66ea76d method info and update to staticmethod 2025-04-28 21:16:21 +02:00
3039dbecb0 now only methodologies within the class 2025-04-28 19:55:44 +02:00
eadc2a06bf methods optimized 2025-04-28 12:35:19 +02:00
11 changed files with 683 additions and 511 deletions

44
.idea/workspace.xml generated
View File

@ -5,7 +5,7 @@
</component>
<component name="ChangeListManager">
<list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Update Translate Files">
<change beforePath="$PROJECT_DIR$/cls_mth_fc.py" beforeDir="false" afterPath="$PROJECT_DIR$/cls_mth_fc.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wirepy.py" beforeDir="false" afterPath="$PROJECT_DIR$/wirepy.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@ -43,28 +43,28 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ASKED_ADD_EXTERNAL_FILES&quot;: &quot;true&quot;,
&quot;Python.INSTALL.executor&quot;: &quot;Run&quot;,
&quot;Python.cls_mth_fc.executor&quot;: &quot;Run&quot;,
&quot;Python.install.executor&quot;: &quot;Run&quot;,
&quot;Python.main.executor&quot;: &quot;Run&quot;,
&quot;Python.messagebox.executor&quot;: &quot;Run&quot;,
&quot;Python.start_wg.executor&quot;: &quot;Run&quot;,
&quot;Python.testtheme.executor&quot;: &quot;Run&quot;,
&quot;Python.wg_func.executor&quot;: &quot;Run&quot;,
&quot;Python.wg_main.executor&quot;: &quot;Run&quot;,
&quot;Python.wirepy.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;Shell Script.install.executor&quot;: &quot;Run&quot;,
&quot;Shell Script.run_as.executor&quot;: &quot;Run&quot;,
&quot;git-widget-placeholder&quot;: &quot;21-04-2025-new-tooltip&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/punix/Pyapps/wire-py&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;ml.llm.LLMConfigurable&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ASKED_ADD_EXTERNAL_FILES": "true",
"Python.INSTALL.executor": "Run",
"Python.cls_mth_fc.executor": "Run",
"Python.install.executor": "Run",
"Python.main.executor": "Run",
"Python.messagebox.executor": "Run",
"Python.start_wg.executor": "Run",
"Python.testtheme.executor": "Run",
"Python.wg_func.executor": "Run",
"Python.wg_main.executor": "Run",
"Python.wirepy.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"Shell Script.install.executor": "Run",
"Shell Script.run_as.executor": "Run",
"git-widget-placeholder": "28-04-2025-more-methods-and-optimize-methods",
"last_opened_file_path": "/home/punix/Pyapps/wire-py",
"settings.editor.selected.configurable": "ml.llm.LLMConfigurable"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/lx-icons" />

View File

@ -41,7 +41,7 @@ My standard System: Linux Mint 22 Cinnamon
- Fix ipv6 in Config File on import
- Wirepy run now as user
- settings, keys and Config Files now in ~/.config/wire_py
- settings, AppConfig.KEYS_FILE and Config Files now in ~/.config/wire_py
- For new users, the required files are created and autostart service is started.
- Tunnels are now read from the directory to view them in the list.
To display only own tunnels, and read errors are minimized.

Binary file not shown.

View File

@ -14,16 +14,11 @@ from datetime import datetime
from pathlib import Path
from subprocess import check_call, CompletedProcess
from tkinter import ttk, Toplevel
from wp_app_config import AppConfig, Msg
import requests
APP = "wirepy"
LOCALE_DIR = "/usr/share/locale/"
locale.bindtextdomain(APP, LOCALE_DIR)
gettext.bindtextdomain(APP, LOCALE_DIR)
gettext.textdomain(APP)
_ = gettext.gettext
# Translate
_ = AppConfig.setup_translations()
class Create:
"""
@ -41,7 +36,7 @@ class Create:
pth: Path = Path.home() / ".config/wire_py"
pth.mkdir(parents=True, exist_ok=True)
sett: Path = Path.home() / ".config/wire_py/settings"
ks: Path = Path.home() / ".config/wire_py/keys"
AppConfig.KEYS_FILE
if sett.exists():
pass
@ -50,11 +45,11 @@ class Create:
sett.touch()
sett.write_text("[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff\n")
if ks.exists():
if AppConfig.KEYS_FILE.exists():
pass
else:
ks.touch()
AppConfig.KEYS_FILE.touch()
@staticmethod
def files_for_autostart() -> None:
@ -80,11 +75,10 @@ class Create:
def make_dir() -> None:
"""Folder Name "tlecdewg" = Tunnel Encrypt Decrypt Wireguard"""
folder_path: Path = Path("/tmp/tlecdcwg/")
if folder_path.exists():
if AppConfig.TEMP_DIR.exists():
pass
else:
folder_path.mkdir()
AppConfig.TEMP_DIR.mkdir()
@staticmethod
def decrypt() -> None:
@ -125,6 +119,31 @@ class LxTools(tk.Tk):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
@staticmethod
def get_file_name(path: Path, i: int = 5) -> List[str]:
"""
Recursively searches the specified path for files and returns a list of filenames,
with the last 'i' characters of each filename removed.
This method is useful for obtaining filenames without specific file extensions,
e.g., to remove '.conf' from Wireguard configuration files.
Args:
path (Path): The directory path to search
i (int, optional): Number of characters to remove from the end of each filename.
Default is 5, which typically corresponds to the length of '.conf'.
Returns:
List[str]: A list of filenames without the last 'i' characters
Example:
If path contains files like 'tunnel1.conf', 'tunnel2.conf' and i=5,
the method returns ['tunnel1', 'tunnel2'].
"""
lists_file = list(path.rglob("*"))
lists_file = [conf_file.name[:-i] for conf_file in lists_file]
return lists_file
@staticmethod
def uos() -> None:
"""
@ -135,19 +154,18 @@ class LxTools(tk.Tk):
"""
log_name: str = f"{Path.home()}"[6:]
file: Path = Path.home() / "/tmp/.log_user"
with open(file, "w", encoding="utf-8") as f:
f.write(log_name)
Path(file).write_text(log_name, encoding="utf-8")
@staticmethod
def clean_files(folder_path: Path = None, file: Path = None) -> None:
def clean_files(TEMP_DIR: Path = None, file: Path = None) -> None:
"""
method that can be added after need to delete a folder and a file when quitting.
Args:
:param file: default None
:param folder_path: default None
:param AppConfig.TEMP_DIR: default None
"""
if folder_path is not None:
shutil.rmtree(folder_path)
if AppConfig.TEMP_DIR is not None:
shutil.rmtree(AppConfig.TEMP_DIR)
if file is not None:
Path.unlink(file)
@ -156,14 +174,22 @@ class LxTools(tk.Tk):
"""
method that writes in file whether tooltip is displayed or not
"""
with open(path, "r", encoding="utf-8") as set_f2:
lines2 = set_f2.readlines()
if "False\n" in lines2:
lines = Path(path).read_text(encoding="utf-8")
if "False\n" in lines:
tip = False
else:
tip = True
return tip
def theme_change(self) -> None:
lines = AppConfig.SETTINGS_FILE.read_text()
if "light\n" in lines:
self.tk.call("set_theme", "light")
else:
self.tk.call("set_theme", "dark")
@staticmethod
def msg_window(img_w: str, img_i: str, w_title: str, w_txt: str, txt2: Optional[str] = None,
com: Optional[str] = None) -> None:
@ -394,8 +420,8 @@ class Tunnel:
"""
Returns a list of Wireguard tunnel names
"""
folder_path: Path = Path("/tmp/tlecdcwg/")
wg_s: List[str] = os.listdir(folder_path)
AppConfig.TEMP_DIR: Path = Path("/tmp/tlecdcwg/")
wg_s: List[str] = os.listdir(AppConfig.TEMP_DIR)
return wg_s
@ -425,17 +451,15 @@ class Tunnel:
with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf:
if len(zf.namelist()) != 0:
msg_t: str = _("Your zip file is in home directory")
LxTools.msg_window(img_w, img_i, _("Export Successful"), msg_t)
LxTools.msg_window(img_w, img_i, Msg.STR["exp_succ"], Msg.STR["exp_in_home"])
else:
msg_t: str = _("Export failed! Please try again")
LxTools.msg_window(img_w2, img_i2, _("Export error"), msg_t)
LxTools.msg_window(img_w2, img_i2, Msg.STR["exp_err"], Msg.STR["exp_try"])
else:
LxTools.msg_window(img_w, img_i2, sl, pfit)
LxTools.msg_window(img_w, img_i2, Msg.STR["sel_tl"], Msg.STR["tl_first"])
except TypeError:
pass
@ -485,6 +509,7 @@ class Tooltip:
label: tk.Label = tk.Label(tw, text=self.text, background="lightgreen", foreground="black", relief="solid",
borderwidth=1, padx=5, pady=5)
label.grid()
self.tooltip_window.after(2200, lambda: tw.destroy())
def hide_tooltip(self, event: Optional[Any] = None) -> None:
"""

10
install
View File

@ -17,7 +17,7 @@ install_file_with(){
exit 0
else
sudo apt install python3-tk && \
sudo cp -fv wirepy.py start_wg.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
@ -43,7 +43,7 @@ install_arch_d(){
exit 0
else
sudo pacman -S --noconfirm tk python3 python-requests && \
sudo cp -fv wirepy.py start_wg.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
@ -120,7 +120,7 @@ install(){
exit 0
else
sudo dnf install python3-tkinter -y
sudo cp -fv wirepy.py start_wg.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
@ -145,7 +145,7 @@ install(){
rm -r ~/.config/wire_py && rm -r ~/.config/systemd
exit 0
else
sudo cp -fv wirepy.py start_wg.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
@ -181,7 +181,7 @@ install(){
remove(){
sudo rm -f /usr/local/bin/wirepy /usr/local/bin/wirepy.py /usr/local/bin/start_wg.py \
/usr/local/bin/cls_mth_fc.py /usr/local/bin/ssl_encrypt.py /usr/local/bin/ssl_decrypt.py
/usr/local/bin/wp_app_config.py cls_mth_fc.py /usr/local/bin/ssl_encrypt.py /usr/local/bin/ssl_decrypt.py
if [ $? -ne 0 ]
then
exit 0

View File

@ -5,14 +5,12 @@ import os
import shutil
from pathlib import Path
from subprocess import check_call
from wp_app_config import AppConfig
uname: Path = Path("/tmp/.log_user")
with open(uname, "r", encoding="utf-8") as f:
log_name = f.readline()
log_name = Path(uname).read_text(encoding="utf-8")
# Folder Name "tlecdewg" = Tunnel Encrypt Decrypt Wireguard
folder_path: Path = Path("/tmp/tlecdcwg/")
keyfile: Path = Path(f"/home/{log_name}/.config/wire_py/pbwgk.pem")
PKEYFILE: Path = "/usr/local/etc/ssl/pwgk.pem"
@ -21,15 +19,16 @@ if not keyfile.is_file():
check_call(["openssl", "rsa", "-in", PKEYFILE, "-out", keyfile, "-outform", "PEM", "-pubout"])
shutil.chown(keyfile, 1000, 1000)
folder_path2 = f"/home/{log_name}/.config/wire_py/"
detl: list[str] = os.listdir(folder_path2)
os.chdir(folder_path2)
AppConfig.TEMP_DIR2 = f"/home/{log_name}/.config/wire_py/"
detl: list[str] = os.listdir(AppConfig.TEMP_DIR2)
os.chdir(AppConfig.TEMP_DIR2)
detl.remove("keys")
detl.remove("settings")
if os.path.exists(f"{folder_path2}pbwgk.pem"):
if os.path.exists(f"{AppConfig.TEMP_DIR2}pbwgk.pem"):
detl.remove("pbwgk.pem")
for detunnels in detl:
tlname2 = f"{detunnels[:-4]}.conf"
extpath = f"{folder_path}/{tlname2}"
check_call(["openssl", "pkeyutl", "-decrypt", "-inkey", PKEYFILE, "-in", detunnels, "-out", extpath])
extpath = f"{AppConfig.TEMP_DIR}/{tlname2}"
check_call(["openssl", "pkeyutl", "-decrypt", "-inkey", PKEYFILE, "-in", detunnels,
"-out", extpath])
shutil.chown(extpath, 1000, 1000)

View File

@ -6,13 +6,13 @@ import shutil
from pathlib import Path
from subprocess import check_call
from wp_app_config import AppConfig
uname: Path = Path("/tmp/.log_user")
with open(uname, "r", encoding="utf-8") as f:
log_name: str = f.readline()
log_name = Path(uname).read_text(encoding="utf-8")
keyfile: Path = Path(f"/home/{log_name}/.config/wire_py/pbwgk.pem")
folder_path: Path = Path("/tmp/tlecdcwg/")
PKEYFILE = "/usr/local/etc/ssl/pwgk.pem"
if not keyfile.is_file():
@ -20,28 +20,28 @@ if not keyfile.is_file():
check_call(["openssl", "rsa", "-in", PKEYFILE, "-out", keyfile, "-outform", "PEM", "-pubout"])
shutil.chown(keyfile, 1000, 1000)
if folder_path.exists():
tl = os.listdir(f"{folder_path}")
if AppConfig.TEMP_DIR.exists():
tl = os.listdir(f"{AppConfig.TEMP_DIR}")
CPTH: str = f"{keyfile}"
CRYPTFILES: str = CPTH[:-9]
if keyfile.exists() and len(tl) != 0:
for tunnels in tl:
sourcetl: str = f"{folder_path}/{tunnels}"
sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}"
tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat"
check_call(["openssl", "pkeyutl", "-encrypt", "-inkey", keyfile, "-pubin", "-in", sourcetl, "-out",
tlname,])
else:
if folder_path.exists():
tl: list[str] = os.listdir(f"{folder_path}")
if AppConfig.TEMP_DIR.exists():
tl: list[str] = os.listdir(f"{AppConfig.TEMP_DIR}")
CPTH: str = f"{keyfile}"
CRYPTFILES: str = CPTH[:-9]
if keyfile.exists() and len(tl) != 0:
for tunnels in tl:
sourcetl: str = f"{folder_path}/{tunnels}"
sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}"
tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat"
check_call(["openssl", "pkeyutl", "-encrypt", "-inkey", keyfile, "-pubin", "-in", sourcetl, "-out",
tlname])

View File

@ -7,11 +7,8 @@ from subprocess import check_call
path_to_file = Path(Path.home() / ".config/wire_py/settings")
with open(path_to_file, "r", encoding="utf-8") as a_con:
# This function is for the independent autostarted of the previously selected tunnel
lines = a_con.readlines()
a_con = lines[7].strip()
a_con = Path(path_to_file).read_text(encoding="utf-8").splitlines(keepends=True)
a_con = a_con[7].strip()
if a_con != "off":
check_call(["nmcli", "connection", "up", a_con])
else:

670
wirepy.py
View File

@ -15,73 +15,53 @@ from subprocess import check_call
from tkinter import TclError, filedialog, ttk
from cls_mth_fc import (Create, GiteaUpdate, Tooltip, Tunnel, LxTools)
from wp_app_config import AppConfig, Msg
LxTools.uos()
Create.dir_and_files()
Create.make_dir()
Create.decrypt()
tcl_path: Path = Path("/usr/share/TK-Themes")
set_file: Path = Path(Path.home() / ".config/wire_py/settings")
tips = LxTools.if_tip(set_file)
folder_path: Path = Path("/tmp/tlecdcwg/")
user_file = Path("/tmp/.log_user")
tips = LxTools.if_tip(AppConfig.SETTINGS_FILE)
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
VERSION: str = "v. 2.04.1725"
res = GiteaUpdate.api_down("https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases", VERSION, set_file)
res = GiteaUpdate.api_down("https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases", VERSION, AppConfig.SETTINGS_FILE)
# Translate
APP = "wirepy"
LOCALE_DIR = "/usr/share/locale/"
locale.bindtextdomain(APP, LOCALE_DIR)
gettext.bindtextdomain(APP, LOCALE_DIR)
gettext.textdomain(APP)
_ = gettext.gettext
_ = AppConfig.setup_translations()
img_w: str = r"/usr/share/icons/lx-icons/64/info.png"
img_i: str = r"/usr/share/icons/lx-icons/48/wg_vpn.png"
img_w2: str = r"/usr/share/icons/lx-icons/64/error.png"
img_i2: str = r"/usr/share/icons/lx-icons/48/wg_msg.png"
sl: str = _("Select tunnel")
rnp: str = _("Renaming not possible")
ie:str = _("Import Error")
pfit: str = _("Please first import tunnel")
pstl: str = _("Please select a tunnel from the list")
LxTools.sigi(folder_path, user_file)
LxTools.sigi(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
class Wirepy(tk.Tk):
"""
Class Wirepy this is the Main Window of wirepy
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.my_tool_tip = None
self.x_width = 600
self.y_height = 383
self.x_width = AppConfig.UI_CONFIG["window_size"][0]
self.y_height = AppConfig.UI_CONFIG["window_size"][1]
self.monitor_center_x = int(self.winfo_screenwidth() / 2 - (self.x_width / 2))
self.monitor_center_y = int(self.winfo_screenheight() / 2 - (self.y_height / 2))
self.resizable(width=False, height=False)
self.title("Wire-Py")
self.resizable(AppConfig.UI_CONFIG["resizable_window"][0], AppConfig.UI_CONFIG["resizable_window"][1])
self.title(AppConfig.UI_CONFIG["window_title"])
self.geometry(f"{self.x_width}x{self.y_height}+{self.monitor_center_x}+{self.monitor_center_y}")
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.style = ttk.Style(self)
self.tk.call("source", f"{tcl_path}/water.tcl")
with open(set_file, "r", encoding="utf-8") as read_file:
lines = read_file.readlines()
if "light\n" in lines:
self.tk.call("set_theme", "light")
else:
self.tk.call("set_theme", "dark")
self.tk.call("source", f"{AppConfig.SYSTEM_PATHS["tcl_path"]}/water.tcl")
LxTools.theme_change(self)
# Load the image file from the disk
self.wg_icon = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_vpn.png")
self.wg_icon = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"])
# Set it as the window icon
self.iconphoto(True, self.wg_icon)
@ -93,7 +73,6 @@ class FrameWidgets(ttk.Frame):
"""
ttk frame class for better structure
"""
def __init__(self, container, **kwargs):
super().__init__(container, **kwargs)
@ -103,85 +82,12 @@ class FrameWidgets(ttk.Frame):
self.dns = None
self.address = None
self.auto_con = None
self.wg_vpn_start = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_vpn-start.png")
self.wg_vpn_stop = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_vpn-stop.png")
self.imp_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_import.png")
self.tr_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_trash.png")
self.exp_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_export.png")
self.warning_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/64/error.png")
def update():
"""
Set on or off in file
"""
if set_update.get() == 1:
with open(set_file, "r", encoding="utf-8") as set_f2:
lines2 = set_f2.readlines()
lines2[1] = "off\n"
with open(set_file, "w", encoding="utf-8") as set_f2:
set_f2.writelines(lines2)
if set_update.get() == 0:
with open(set_file, "r", encoding="utf-8") as set_f2:
lines2 = set_f2.readlines()
lines2[1] = "on\n"
with open(set_file, "w", encoding="utf-8") as set_f2:
set_f2.writelines(lines2)
def theme_change_light():
"""
Set a light theme
"""
if self.tk.call("ttk::style", "theme", "use") == "water-dark":
self.tk.call("set_theme", "light")
with open(set_file, "r", encoding="utf-8") as theme_set2:
lines3 = theme_set2.readlines()
lines3[3] = "light\n"
with open(set_file, "w", encoding="utf-8") as theme_set2:
theme_set2.writelines(lines3)
self.color_label()
def theme_change_dark():
"""
Set a dark theme
"""
if not self.tk.call("ttk::style", "theme", "use") == "water-dark":
self.tk.call("set_theme", "dark")
with open(set_file, "r", encoding="utf-8") as theme_set2:
lines4 = theme_set2.readlines()
lines4[3] = "dark\n"
with open(set_file, "w", encoding="utf-8") as theme_set2:
theme_set2.writelines(lines4)
self.color_label()
def tooltip():
"""
Set True or False in a file
"""
if set_tip.get():
with open(set_file, "r", encoding="utf-8") as set_f2:
lines2 = set_f2.readlines()
lines2[5] = "False\n"
with open(set_file, "w", encoding="utf-8") as set_f2:
set_f2.writelines(lines2)
else:
with open(set_file, "r", encoding="utf-8") as set_f2:
lines2 = set_f2.readlines()
lines2[5] = "True\n"
with open(set_file, "w", encoding="utf-8") as set_f2:
set_f2.writelines(lines2)
def info():
def link_btn():
webbrowser.open("https://git.ilunix.de/punix/Wire-Py")
msg_t = _("Wire-Py a simple Wireguard Gui for Linux systems.\n\n"
"Wire-Py is open source software written in Python.\n\n"
"Email: polunga40@unity-mail.de also likes for donation.\n\n"
"Use without warranty!\n")
LxTools.msg_window(img_i, img_i, _("Info"), msg_t, _("Go to Wire-Py git"), link_btn)
self.wg_vpn_start = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_start"])
self.wg_vpn_stop = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_stop"])
self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"])
self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"])
self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"])
self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"])
# Frame for Menu
self.menu_frame = ttk.Frame(self)
@ -204,15 +110,16 @@ class FrameWidgets(ttk.Frame):
set_tip = tk.BooleanVar()
self.settings = tk.Menu(self, relief="flat")
self.options_btn.configure(menu=self.settings, style="Toolbutton")
self.settings.add_checkbutton(label=_("Disable Updates"), command=update, variable=set_update)
self.settings.add_checkbutton(label=_("Disable Tooltips"), command=tooltip, variable=set_tip)
self.settings.add_command(label=_("Light"), command=theme_change_light)
self.settings.add_command(label=_("Dark"), command=theme_change_dark)
self.settings.add_checkbutton(label=_("Disable Updates"),
command=lambda: self.update_setting(set_update.get()), variable=set_update)
self.settings.add_checkbutton(label=_("Disable Tooltips"),
command=lambda: self.tooltip(set_tip.get()), variable=set_tip)
self.settings.add_command(label=_("Light"), command=self.theme_change_light)
self.settings.add_command(label=_("Dark"), command=self.theme_change_dark)
# About BTN Menu / Label
self.about_btn = ttk.Button(
self.menu_frame, text=_("About"), style="Toolbutton", command=info
)
self.menu_frame, text=_("About"), style="Toolbutton", command=self.about)
self.about_btn.grid(column=2, columnspan=2, row=0)
self.readme = tk.Menu(self)
@ -306,23 +213,12 @@ class FrameWidgets(ttk.Frame):
self.peer.config(font=("Ubuntu", 9))
self.peer.grid(column=0, row=4, sticky="we", padx=130)
def enable_check_box(_):
"""
checkbox for enable autostart Tunnel
"""
Create.files_for_autostart()
if self.l_box.size() != 0:
self.wg_autostart.configure(state="normal")
self.lb_rename.config(state="normal")
self.lb_rename.delete(0, tk.END)
self.btn_rename.config(state="normal")
# Listbox with Scrollbar
self.l_box = tk.Listbox(self.lb_frame_btn_lbox, selectmode="single")
self.l_box.config(relief="ridge", font=("Ubuntu", 12, "bold"))
self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns")
self.l_box.event_add("<<ClickEvent>>", "<Button-1>")
self.l_box.bind("<<ClickEvent>>", enable_check_box)
self.l_box.bind("<<ClickEvent>>", self.enable_check_box)
self.scrollbar = ttk.Scrollbar(self.lb_frame_btn_lbox, orient="vertical", command=self.l_box.yview)
self.scrollbar.grid(column=1, rowspan=4, row=0, sticky="nse")
self.l_box.configure(yscrollcommand=self.scrollbar.set)
@ -336,13 +232,7 @@ class FrameWidgets(ttk.Frame):
# Button Vpn
if self.a != "":
self.stop()
wg_read = f"/tmp/tlecdcwg/{self.a}.conf"
with open(wg_read, "r", encoding="utf-8") as file:
data = Tunnel.con_to_dict(file)
# Address Label
self.init_and_report(data)
self.show_data()
data = self.handle_tunnel_data(self.a)
else:
self.start()
@ -350,7 +240,7 @@ class FrameWidgets(ttk.Frame):
self.add = tk.StringVar()
self.DNS = tk.StringVar()
self.enp = tk.StringVar()
self.label_empty()
self.reset_fields()
self.show_data()
# Button Import
@ -359,7 +249,151 @@ class FrameWidgets(ttk.Frame):
Tooltip(self.btn_i, _("Click to import a Wireguard Tunnel"), tips)
def delete():
# Button Trash
self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=self.delete, padding=0,
style="CButton.TButton")
self.btn_tr.grid(column=0, row=2, padx=15, pady=8)
if self.l_box.size() == 0:
Tooltip(self.btn_tr, _("No tunnels to delete in the list"), tips)
else:
Tooltip(self.btn_tr, _("Click to delete a Wireguard Tunnel\nSelect from the list!"), tips)
# Button Export
self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic,
command=lambda: Tunnel.export(img_w, img_i, img_w2, img_i2,
Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0)
self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
if self.l_box.size() == 0:
Tooltip(self.btn_exp, _("No Tunnels in List for Export"), tips)
else:
Tooltip(self.btn_exp, _("Click to export all\nWireguard Tunnel to Zipfile"), tips)
# Label Entry
self.lb_rename = ttk.Entry(self.lb_frame4, width=20)
self.lb_rename.grid(column=2, row=0, padx=8, pady=10, sticky="ne")
self.lb_rename.insert(0, _("Max. 12 characters!"))
self.lb_rename.config(state="disable")
if self.l_box.size() != 0:
Tooltip(self.lb_rename, _("To rename a tunnel, you need to\nselect a tunnel from the list"), tips)
else:
Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), tips)
# Button Rename
self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=self.tl_rename,
padding=4, style="RnButton.TButton")
self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne")
# Check Buttons
self.selected_option = tk.IntVar()
self.autoconnect_var = tk.StringVar()
self.autoconnect_var.set(f"{self.auto_con}")
# Frame for Labels, Entry and Button
self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, width=15)
self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
self.wg_autostart = ttk.Checkbutton(self.lb_frame3, text=_("Autoconnect on:"), variable=self.selected_option,
command=self.box_set)
self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw")
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], tips)
if self.l_box.size() == 0:
Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"], tips)
else:
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], tips)
self.on_off()
@staticmethod
def about() -> None:
"""
a tk.Toplevel window
"""
def link_btn() -> str | None:
webbrowser.open("https://git.ilunix.de/punix/Wire-Py")
msg_t = _("Wire-Py a simple Wireguard Gui for Linux systems.\n\n"
"Wire-Py is open source software written in Python.\n\n"
"Email: polunga40@unity-mail.de also likes for donation.\n\n"
"Use without warranty!\n")
LxTools.msg_window(img_i, img_i, _("Info"), msg_t, _("Go to Wire-Py git"), link_btn)
def theme_change_light(self) -> None:
"""
Set a light theme
"""
if self.tk.call("ttk::style", "theme", "use") == "water-dark":
self.tk.call("set_theme", "light")
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) # (keepends=True) = not changed
lines[3] = 'light\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
self.color_label()
def theme_change_dark(self) -> None:
"""
Set a dark theme
"""
if not self.tk.call("ttk::style", "theme", "use") == "water-dark":
self.tk.call("set_theme", "dark")
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[3] = 'dark\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
self.color_label()
@staticmethod
def update_setting(update_res) -> None:
"""
write off or on in file
Args:
update_res (int): argument that is passed contains 0 or 1
"""
if update_res == 1:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[1] = 'off\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
else:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[1] = 'on\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
@staticmethod
def tooltip(tip) -> None:
"""
write True or False in a file
Args:
tip (bool): argument that is passed contains True or False
"""
if tip:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[5] = 'False\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
else:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[5] = 'True\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
def enable_check_box(self, _) -> None:
"""
checkbox for enable autostart Tunnel
"""
Create.files_for_autostart()
if self.l_box.size() != 0:
self.wg_autostart.configure(state="normal")
self.lb_rename.config(state="normal")
self.lb_rename.delete(0, tk.END)
self.btn_rename.config(state="normal")
def delete(self) -> None:
"""
delete Wireguard Tunnel
"""
@ -371,14 +405,12 @@ class FrameWidgets(ttk.Frame):
pre_key = key[3]
check_call(["nmcli", "connection", "delete", select_tl])
self.l_box.delete(self.select_tunnel[0])
with open(set_file, "r", encoding="utf-8") as set_f6:
with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f6:
lines6 = set_f6.readlines()
if (
select_tl == lines6[7].strip()
and "off\n" not in lines6[7].strip()
):
if (select_tl == lines6[7].strip()
and "off\n" not in lines6[7].strip()):
lines6[7] = "off\n"
with open(set_file, "w", encoding="utf-8") as set_f7:
with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as set_f7:
set_f7.writelines(lines6)
self.selected_option.set(0)
self.autoconnect_var.set(_("no Autoconnect"))
@ -386,7 +418,7 @@ class FrameWidgets(ttk.Frame):
if is_encrypt.is_file():
Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat")
Path.unlink(f"/tmp/tlecdcwg/{select_tl}.conf")
with open(f"{Path.home()}/.config/wire_py/keys", "r", encoding="utf-8") as readfile:
with open(AppConfig.KEYS_FILE, "r", encoding="utf-8") as readfile:
with open(f"{Path.home()}/.config/wire_py/keys2", "w", encoding="utf-8") as writefile:
for line in readfile:
if pre_key not in line.strip("\n"):
@ -412,69 +444,40 @@ class FrameWidgets(ttk.Frame):
self.str_var.set(value="")
self.start()
self.l_box.update()
# Address Label
self.add.set("")
self.DNS.set("")
self.enp.set("")
self.reset_fields()
except IndexError:
if self.l_box.size() != 0:
LxTools.msg_window(img_w, img_i2, sl, pstl)
LxTools.msg_window(img_w, img_i2, Msg.STR["sel_tl"], Msg.STR["sel_list"])
else:
LxTools.msg_window(img_w, img_i2, sl, pfit)
# Button Trash
self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=delete, padding=0,
style="CButton.TButton")
self.btn_tr.grid(column=0, row=2, padx=15, pady=8)
if self.l_box.size() == 0:
Tooltip(self.btn_tr, _("No tunnels to delete in the list"), tips)
else:
Tooltip(self.btn_tr, _("Click to delete a Wireguard Tunnel\nSelect from the list!"), tips)
# Button Export
self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic,
command=lambda: Tunnel.export(img_w, img_i, img_w2, img_i2, sl, pfit), padding=0)
self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
if self.l_box.size() == 0:
Tooltip(self.btn_exp, _("No Tunnels in List for Export"), tips)
else:
Tooltip(self.btn_exp, _("Click to export all\nWireguard Tunnel to Zipfile"), tips)
# Label Entry
self.lb_rename = ttk.Entry(self.lb_frame4, width=20)
self.lb_rename.grid(column=2, row=0, padx=8, pady=10, sticky="ne")
self.lb_rename.insert(0, _("Max. 12 characters!"))
self.lb_rename.config(state="disable")
if self.l_box.size() != 0:
Tooltip(self.lb_rename, _("To rename a tunnel, you need to\nselect a tunnel from the list"), tips)
else:
Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), tips)
def tl_rename():
LxTools.msg_window(img_w, img_i2, Msg.STR["sel_tl"], Msg.STR["tl_first"])
def tl_rename(self) -> None:
"""
method to rename a tunnel
"""
name_of_file = LxTools.get_file_name(AppConfig.TEMP_DIR)
special_characters = ["\\", "/", "{", "}", " "]
if len(self.lb_rename.get()) > 12:
LxTools.msg_window(img_w, img_i2, rnp, _("The new name may contain only 12 characters"))
LxTools.msg_window(img_w, img_i2, Msg.STR["ren_err"], Msg.STR["sign_len"])
elif len(self.lb_rename.get()) == 0:
LxTools.msg_window(img_w, img_i2, rnp, _("At least one character must be entered"))
LxTools.msg_window(img_w, img_i2, Msg.STR["ren_err"], Msg.STR["zero_signs"])
elif any(ch in special_characters for ch in self.lb_rename.get()):
msg_t = _("No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n")
LxTools.msg_window(img_w, img_i2, rnp, msg_t)
LxTools.msg_window(img_w, img_i2, Msg.STR["ren_err"], Msg.STR["false_signs"])
elif self.lb_rename.get() in name_of_file:
LxTools.msg_window(img_w, img_i2, Msg.STR["ren_err"], Msg.STR["is_in_use"])
else:
@ -483,7 +486,7 @@ class FrameWidgets(ttk.Frame):
select_tl = self.l_box.get(self.select_tunnel[0])
# nmcli connection modify old connection.id iphone
check_call(["nmcli", "connection", "modify", select_tl, "connection.id", self.lb_rename.get()])
subprocess.check_output(["nmcli", "connection", "modify", select_tl, "connection.id", self.lb_rename.get()], text=True)
source = Path(f"/tmp/tlecdcwg/{select_tl}.conf")
destination = source.with_name(f"{self.lb_rename.get()}.conf")
source.replace(destination)
@ -494,68 +497,31 @@ class FrameWidgets(ttk.Frame):
self.l_box.update()
new_a_connect = self.lb_rename.get()
self.lb_rename.delete(0, tk.END)
if self.a != "" and self.a == select_tl:
self.a = Tunnel.active()
self.str_var.set(value=self.a)
with open(set_file, "r", encoding="utf-8") as set_f5:
with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f5:
lines5 = set_f5.readlines()
if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip():
lines5[7] = new_a_connect
with open(set_file, "w", encoding="utf-8") as theme_set5:
with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as theme_set5:
theme_set5.writelines(lines5)
self.autoconnect_var.set(value=new_a_connect)
self.update_connection_display()
Create.encrypt()
except IndexError:
LxTools.msg_window(img_w, img_i2, rnp, pstl)
LxTools.msg_window(img_w, img_i2, Msg.STR["ren_err"], Msg.STR["sel_list"])
# Button Rename
self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=tl_rename, padding=4,
style="RnButton.TButton")
self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne")
except subprocess.CalledProcessError:
pass
# Check Buttons
self.selected_option = tk.IntVar()
self.autoconnect_var = tk.StringVar()
self.autoconnect_var.set(self.auto_con)
except EOFError as e:
print(e)
# Frame for Labels, Entry and Button
self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, width=15)
self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
self.wg_autostart = ttk.Checkbutton(self.lb_frame3, text=_("Autoconnect on:"), variable=self.selected_option,
command=self.box_set)
self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw")
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
Tooltip(self.wg_autostart, _("To use the autostart, enable this Checkbox"), tips)
if self.l_box.size() == 0:
Tooltip(self.wg_autostart, _("You must have at least one\ntunnel in the list,to use the autostart"), tips)
else:
Tooltip(self.wg_autostart, _("To use the autostart, a tunnel must be selected from the list"), tips)
self.on_off()
def import_sl(self):
def import_sl(self) -> None:
"""
Import Methode for Wireguard config Files.
Before importing, it is checked whether PrivateKey and PublicKey are in the file.
If True, then it is checked whether the PreSharedKey is already in the key file
to avoid an import error so that no double wgconf are imported.
Thus, tunnels can be renamed without the problems arising.
If False, then the key is written into the file.
Furthermore, it is checked whether the name is longer than 12 characters.
If True, then the name is automatically shortened to 12 characters
and then imported.
If in each case false comes out, a corresponding window comes to
inform the user that something is wrong.
validity check of wireguard config files
"""
Create.dir_and_files()
try:
@ -566,32 +532,29 @@ class FrameWidgets(ttk.Frame):
read = file.read()
path_split = filepath.split("/")
path_split1 = path_split[-1]
self.a = Tunnel.active()
if "PrivateKey = " in read and "PublicKey = " in read and "Endpoint =" in read:
with open(filepath, "r", encoding="utf-8") as file:
key = Tunnel.con_to_dict(file)
pre_key = key[3]
if len(pre_key) != 0:
with open(f"{Path.home()}/.config/wire_py/keys", "r", encoding="utf-8") as readfile:
p_key = readfile.readlines()
p_key = AppConfig.KEYS_FILE.read_text(encoding="utf-8")
if pre_key in p_key or f"{pre_key}\n" in p_key:
msg_t = _("Tunnel already available!\nPlease use another file for import")
LxTools.msg_window(img_w2, img_i2, ie, msg_t)
LxTools.msg_window(img_w2, img_i2, Msg.STR["imp_err"], Msg.STR["tl_exist"])
else:
with open(f"{Path.home()}/.config/wire_py/keys", "a", encoding="utf-8") as keyfile:
with open(AppConfig.KEYS_FILE, "a", encoding="utf-8") as keyfile:
keyfile.write(f"{pre_key}\r")
if len(path_split1) > 17:
p1 = shutil.copy(filepath, "/tmp/tlecdcwg/")
p1 = shutil.copy(filepath, AppConfig.TEMP_DIR)
path_split = path_split1[len(path_split1) - 17:]
os.rename(p1, f"/tmp/tlecdcwg/{path_split}")
new_conf = f"/tmp/tlecdcwg/{path_split}"
os.rename(p1, f"{AppConfig.TEMP_DIR}/{path_split}")
new_conf = f"{AppConfig.TEMP_DIR}/{path_split}"
if self.a != "":
check_call(["nmcli", "connection", "down", Tunnel.active()])
self.label_empty()
check_call(["nmcli", "connection", "down", self.a])
self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type",
"wireguard", "file", new_conf], text=True)
@ -599,10 +562,10 @@ class FrameWidgets(ttk.Frame):
Create.encrypt()
else:
shutil.copy(filepath, "/tmp/tlecdcwg/")
shutil.copy(filepath, f"{AppConfig.TEMP_DIR}/")
if self.a != "":
check_call(["nmcli", "connection", "down", Tunnel.active()])
self.label_empty()
check_call(["nmcli", "connection", "down", self.a])
self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type",
"wireguard", "file", filepath], text=True)
@ -617,40 +580,27 @@ class FrameWidgets(ttk.Frame):
self.l_box.update()
self.l_box.selection_set(0)
Tooltip(self.wg_autostart, _("To use the autostart, enable this Checkbox"), tips)
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], tips)
# Tooltip(self.l_box, _("List of available tunnels"))
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], tips)
Tooltip(self.btn_tr, _("Click to delete a Wireguard Tunnel\nSelect from the list!")
, tips,)
Tooltip(self.btn_exp, Msg.TTIP["export_tl"], tips)
Tooltip(self.btn_exp, _(" Click to export all\nWireguard Tunnel to Zipfile")
, tips)
Tooltip(self.btn_rename, _("To rename a tunnel, you need to\nselect a tunnel from"
" the list"), tips)
Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], tips)
self.lb_rename.insert(0, "Max. 12 characters!")
self.str_var = tk.StringVar()
self.str_var.set(self.a)
self.color_label()
self.stop()
wg_read = f"/tmp/tlecdcwg/{self.a}.conf"
with open(wg_read, "r", encoding="utf-8") as file_for_key:
data = Tunnel.con_to_dict(file_for_key)
# Address Label
self.init_and_report(data)
self.show_data()
data = self.handle_tunnel_data(self.a)
check_call(["nmcli", "con", "mod", self.a, "connection.autoconnect", "no"])
Path.chmod(wg_read, 0o600)
if ("PrivateKey = " in read) and ("Endpoint = " in read):
pass
else:
msg_t = _("Oh... no valid Wireguard File!\nPlease select a valid Wireguard File")
LxTools.msg_window(img_w2, img_i2, ie, msg_t)
LxTools.msg_window(img_w2, img_i2, Msg.STR["imp_err"], Msg.STR["no_valid_file"])
except EOFError as e:
print(e)
@ -661,9 +611,25 @@ class FrameWidgets(ttk.Frame):
except subprocess.CalledProcessError:
print("Tunnel exist!")
def box_set(self):
def handle_tunnel_data(self, tunnel_name: str) -> tuple[str, str, str, str | None]:
"""_summary_
Args:
tunnel_name (str): name of a tunnel
Returns:
tuple[str, str]: tuple with tunnel data
"""
This Method will display the autostart label which
wg_read = f"/tmp/tlecdcwg/{tunnel_name}.conf"
with open(wg_read, "r", encoding="utf-8") as file:
data = Tunnel.con_to_dict(file)
self.init_and_report(data)
self.show_data()
return data
def box_set(self) -> None:
"""
This Method will display the autostarted label which
Tunnel is automatically started regardless of the active tunnel.
The selected tunnel is written into a file to read it after the start of the system.
"""
@ -672,11 +638,9 @@ class FrameWidgets(ttk.Frame):
select_tl = self.l_box.get(select_tunnel[0])
if self.selected_option.get() == 0:
with open(set_file, "r", encoding="utf-8") as set_f3:
lines3 = set_f3.readlines()
lines3[7] = "off\n"
with open(set_file, "w", encoding="utf-8") as set_f3:
set_f3.writelines(lines3)
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[7] = 'off\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
tl = Tunnel.list()
@ -684,31 +648,28 @@ class FrameWidgets(ttk.Frame):
self.wg_autostart.configure(state="disabled")
if self.selected_option.get() >= 1:
with open(set_file, "r", encoding="utf-8") as set_f3:
lines3 = set_f3.readlines()
lines3[7] = select_tl
with open(set_file, "w", encoding="utf-8") as set_f3:
set_f3.writelines(lines3)
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[7] = select_tl
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
except IndexError:
self.selected_option.set(1)
self.on_off()
def on_off(self):
def on_off(self) -> None:
"""
Here it is checked whether the path to the file is there, if not, it is created.
Set (on), the selected tunnel is displayed in the label.
At (off) the label is first emptied then filled with No Autoconnect
"""
with open(set_file, "r", encoding="utf-8") as set_f4:
lines4 = set_f4.readlines()
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
if lines4[7] != "off\n":
print(f"{lines4[7]} starts automatically when the system starts.")
if lines[7] != "off\n":
print(f"{lines[7]} starts automatically when the system starts.")
self.selected_option.set(1)
self.autoconnect_var.set("")
self.auto_con = lines4[7]
self.auto_con = lines[7]
else:
self.selected_option.set(0)
@ -722,12 +683,11 @@ class FrameWidgets(ttk.Frame):
self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
def init_and_report(self, data=None):
def init_and_report(self, data=None) -> None:
"""
Displays the value address, DNS and peer in the labels
or empty it again
"""
# Address Label
self.add = tk.StringVar()
self.add.set(f"{_("Address: ")}{data[0]}")
@ -736,19 +696,18 @@ class FrameWidgets(ttk.Frame):
self.enp = tk.StringVar()
self.enp.set(f"{_("Endpoint: ")}{data[2]}")
def label_empty(self):
def reset_fields(self) -> None:
"""
empty labels
reset data from labels
"""
self.add.set("")
self.DNS.set("")
self.enp.set("")
fields = [self.add, self.DNS, self.enp]
for field in fields:
field.set("")
def show_data(self):
def show_data(self) -> None:
"""
shows data in the label
"""
# Address Label
self.address = ttk.Label(self.lb_frame, textvariable=self.add, foreground="#0071ff")
self.address.grid(column=0, row=5, sticky="w", padx=10, pady=6)
@ -764,35 +723,35 @@ class FrameWidgets(ttk.Frame):
self.endpoint.grid(column=0, row=8, sticky="w", padx=10, pady=20)
self.endpoint.config(font=("Ubuntu", 9))
def stop(self):
def stop(self) -> None:
"""
Stop Button
"""
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop, command=self.wg_switch, padding=0)
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop,
command=lambda: self.wg_switch("stop"), padding=0)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
Tooltip(self.btn_stst, _("Click to stop selected Wireguard Tunnel"), tips)
Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], tips)
def start(self):
def start(self) -> None:
"""
Start Button
"""
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start, command=self.wg_switch, padding=0)
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start,
command=lambda: self.wg_switch("start"), padding=0)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
tl = Tunnel.list()
if len(tl) == 0:
Tooltip(self.btn_stst, _("No tunnels to start in the list"), tips)
Tooltip(self.btn_stst, Msg.TTIP["empty_list"], tips)
else:
Tooltip(self.btn_stst, _("Click to start selected Wireguard Tunnel"), tips)
Tooltip(self.btn_stst, Msg.TTIP["start_tl"], tips)
def color_label(self):
def color_label(self) -> None:
"""
View activ Tunnel in the color green or yellow
"""
with open(set_file, "r", encoding="utf-8") as read_file:
lines = read_file.readlines()
lines = AppConfig.SETTINGS_FILE.read_text()
if "light\n" in lines:
self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green")
@ -802,60 +761,75 @@ class FrameWidgets(ttk.Frame):
self.lb_tunnel.config(font=("Ubuntu", 11, "bold"))
self.lb_tunnel.grid(column=2, padx=10, row=1)
def wg_switch(self):
def wg_switch(self, event=None) -> None:
"""
switch Tunnel method change from labels and buttons
Deals with switching the VPN connection
"""
self.a = Tunnel.active()
try:
if self.a == "":
self.start()
self.select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0])
check_call(["nmcli", "connection", "up", select_tl])
wg_read = f"/tmp/tlecdcwg/{select_tl}.conf"
with open(wg_read, "r", encoding="utf-8") as file:
data = Tunnel.con_to_dict(file)
self.handle_connection_state("start", select_tl)
# Address Label
self.init_and_report(data)
self.show_data()
else:
# Button Start/Stop
self.stop()
self.a = Tunnel.active()
self.str_var = tk.StringVar()
self.str_var.set(self.a)
self.color_label()
data = self.handle_tunnel_data(self.a)
if data:
elif self.a != "":
# Button Start/Stop
self.stop()
check_call(["nmcli", "connection", "down", self.a])
# Button Start/Stop
self.start()
self.a = Tunnel.active()
self.str_var.set("")
self.color_label()
# Address Label
self.add.set("")
self.DNS.set("")
self.enp.set("")
self.show_data()
self.handle_connection_state("stop")
except IndexError:
if self.l_box.size() != 0:
LxTools.msg_window(img_w, img_i2, sl, pstl)
LxTools.msg_window(img_w, img_i2, Msg.STR["sel_tl"], Msg.STR["sel_list"])
else:
LxTools.msg_window(img_w, img_i2, sl, pfit)
LxTools.msg_window(img_w, img_i2, Msg.STR["sel_tl"], Msg.STR["tl_first"])
def handle_connection_state(self, action: str, tunnel_name: str = None) -> None:
"""
central management for connection states
Args:
action (str): "start", "stop" or "toggle"
tunnel_name (str, optional): name of a tunnel for a start-option. defaults to None.
"""
if action == "stop":
if self.a:
check_call(["nmcli", "connection", "down", self.a])
self.update_connection_display()
self.reset_fields()
self.start()
elif action == "start":
if tunnel_name or self.a:
target_tunnel = tunnel_name or self.a
check_call(["nmcli", "connection", "up", target_tunnel])
self.update_connection_display()
data = self.handle_tunnel_data(self.a)
self.init_and_report(data)
self.show_data()
self.color_label()
self.stop()
elif action == "toggle":
if self.a:
self.handle_connection_state("stop")
else:
self.handle_connection_state("start")
def update_connection_display(self) -> None:
"""
Updated the display after connection changes
"""
self.a = Tunnel.active()
if not hasattr(self, "str_var"):
self.str_var = tk.StringVar()
self.str_var.set(self.a)
self.color_label()
self.show_data()
if __name__ == "__main__":
@ -871,5 +845,5 @@ if __name__ == "__main__":
window.tk.call("set", "::tk::dialog::file::showHiddenVar", "0")
window.mainloop()
LxTools.clean_files(folder_path, user_file)
LxTools.clean_files(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
sys.exit(0)

177
wp_app_config.py Normal file
View File

@ -0,0 +1,177 @@
#!/usr/bin/python3
"""App configuration for Wire-Py"""
import gettext
import locale
from pathlib import Path
from typing import Dict, Any
class AppConfig:
"""Central configuration class for Wire-Py application"""
# Localization
APP_NAME: str = "wirepy"
LOCALE_DIR: Path = Path("/usr/share/locale/")
# Base paths
BASE_DIR: Path = Path.home()
CONFIG_DIR: Path = BASE_DIR / ".config/wire_py"
TEMP_DIR: Path = Path("/tmp/tlecdcwg")
USER_FILE: Path = Path("/tmp/.log_user")
# Configuration files
SETTINGS_FILE: Path = CONFIG_DIR / "settings"
KEYS_FILE: Path = CONFIG_DIR / "keys"
AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service"
# Default settings
DEFAULT_SETTINGS: Dict[str, Any] = {
"updates": "on",
"theme": "light",
"tooltip": True,
"autostart": "off"
}
# UI configuration
UI_CONFIG: Dict[str, Any] = {
"window_title": "Wire-Py",
"window_size": (600, 383),
"font_family": "Ubuntu",
"font_size": 11,
"resizable_window": (False, False)
}
# System-dependent paths
SYSTEM_PATHS: Dict[str, str]= {
"ssl_decrypt": "/usr/local/bin/ssl_decrypt.py",
"ssl_encrypt": "/usr/local/bin/ssl_encrypt.py",
"tcl_path": "/usr/share/TK-Themes",
}
# Images and icons paths
IMAGE_PATHS: Dict[str, str] = {
"icon_vpn": "/usr/share/icons/lx-icons/48/wg_vpn.png",
"icon_msg": "/usr/share/icons/lx-icons/48/wg_msg.png",
"icon_import": "/usr/share/icons/lx-icons/48/wg_import.png",
"icon_export": "/usr/share/icons/lx-icons/48/wg_export.png",
"icon_trash": "/usr/share/icons/lx-icons/48/wg_trash.png",
"icon_start": "/usr/share/icons/lx-icons/48/wg_vpn-start.png",
"icon_stop": "/usr/share/icons/lx-icons/48/wg_vpn-stop.png",
"icon_info": "/usr/share/icons/lx-icons/64/info.png",
"icon_error": "/usr/share/icons/lx-icons/64/error.png"
}
@staticmethod
def setup_translations() -> gettext.gettext:
"""
Initialize translations and set the translation function
Special method for translating strings in this file
Returns:
The gettext translation function
"""
locale.bindtextdomain(AppConfig.APP_NAME, AppConfig.LOCALE_DIR)
gettext.bindtextdomain(AppConfig.APP_NAME, AppConfig.LOCALE_DIR)
gettext.textdomain(AppConfig.APP_NAME)
return gettext.gettext
@classmethod
def ensure_directories(cls) -> None:
"""Ensures that all required directories exist"""
cls.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
cls.TEMP_DIR.mkdir(parents=True, exist_ok=True)
@classmethod
def create_default_settings(cls) -> None:
"""Creates default settings if they don't exist"""
if not cls.SETTINGS_FILE.exists():
content = "\n".join(f"[{k.upper()}]\n{v}" for k, v in cls.DEFAULT_SETTINGS.items())
cls.SETTINGS_FILE.write_text(content)
@classmethod
def get_image_paths(cls) -> Dict[str, Path]:
"""Returns paths to UI images"""
return {
"main_icon": cls.SYSTEM_PATHS["image_path"] / "48/wg_vpn.png",
"warning": cls.CONFIG_DIR / "images/warning.png",
"success": cls.CONFIG_DIR / "images/success.png",
"error": cls.CONFIG_DIR / "images/error.png"
}
@classmethod
def get_autostart_content(cls) -> str:
"""Returns the content for the autostart service file"""
return """[Unit]
Description=Automatic Tunnel Start
After=network-online.target
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 5
ExecStart=/usr/local/bin/start_wg.py
[Install]
WantedBy=default.target"""
# here is inizialize the class for translate strrings
_ = AppConfig.setup_translations()
class Msg:
"""
A utility class that provides centralized access to translated message strings.
This class contains a dictionary of message strings used throughout the Wire-Py application.
All strings are prepared for translation using gettext. The short key names make the code
more concise while maintaining readability.
Attributes:
STR (dict): A dictionary mapping short keys to translated message strings.
Keys are abbreviated for brevity but remain descriptive.
Usage:
Import this class and access messages using the dictionary:
`Msg.STR["sel_tl"]` returns the translated "Select tunnel" message.
Note:
Ensure that gettext translation is properly initialized before
accessing these strings to ensure correct localization.
"""
STR: Dict[str, str] = {
# Strings for messages
"sel_tl": _("Select tunnel"),
"ren_err": _("Renaming not possible"),
"exp_succ": _("Export successful"),
"exp_in_home": _("Your zip file is in home directory"),
"imp_err": _("Import Error"),
"exp_err": _("Export Error"),
"exp_try": _("Export failed! Please try again"),
"tl_first": _("Please first import tunnel"),
"sel_list": _("Please select a tunnel from the list"),
"sign_len": _("The new name may contain only 12 characters"),
"zero_signs": _("At least one character must be entered"),
"false signs": _("No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n"),
"is_in_use": _("The tunnel is already in use"),
"no_valid_file": _("Oh... no valid Wireguard File!\nPlease select a valid Wireguard File"),
"tl_exist": _("Tunnel already available!\nPlease use another file for import")
}
TTIP: Dict[str, str] = {
#Strings for Tooltips
"start_tl": _("Click to start selected Wireguard Tunnel"),
"empty_list": _("No tunnels to start in the list"),
"stop_tl": _("Click to stop selected Wireguard Tunnel"),
"del_tl": _("Click to delete selected Wireguard Tunnel"),
"rename_tl": _("To rename a tunnel, you need to\nselect a tunnel from the list"),
"export_tl": _(" Click to export all\nWireguard Tunnel to Zipfile"),
"trash_tl": _("Click to delete a Wireguard Tunnel\nSelect from the list!"),
"autostart": _("To use the autostart, enable this Checkbox"),
"autostart_info": _("You must have at least one\ntunnel in the list,to use the autostart"),
"export_tl_info": _("No Tunnels in List for Export"),
"start_tl_info": _("Click to start selected Wireguard Tunnel"),
"rename_tl_info": _("To rename a tunnel, at least one must be in the list"),
"trash_tl_info": _("No tunnels to delete in the list"),
"list_auto_info": _("To use the autostart, a tunnel must be selected from the list")
}