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>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Update Translate Files"> <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" /> <change beforePath="$PROJECT_DIR$/wirepy.py" beforeDir="false" afterPath="$PROJECT_DIR$/wirepy.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
@ -43,28 +43,28 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent"><![CDATA[{
&quot;keyToString&quot;: { "keyToString": {
&quot;ASKED_ADD_EXTERNAL_FILES&quot;: &quot;true&quot;, "ASKED_ADD_EXTERNAL_FILES": "true",
&quot;Python.INSTALL.executor&quot;: &quot;Run&quot;, "Python.INSTALL.executor": "Run",
&quot;Python.cls_mth_fc.executor&quot;: &quot;Run&quot;, "Python.cls_mth_fc.executor": "Run",
&quot;Python.install.executor&quot;: &quot;Run&quot;, "Python.install.executor": "Run",
&quot;Python.main.executor&quot;: &quot;Run&quot;, "Python.main.executor": "Run",
&quot;Python.messagebox.executor&quot;: &quot;Run&quot;, "Python.messagebox.executor": "Run",
&quot;Python.start_wg.executor&quot;: &quot;Run&quot;, "Python.start_wg.executor": "Run",
&quot;Python.testtheme.executor&quot;: &quot;Run&quot;, "Python.testtheme.executor": "Run",
&quot;Python.wg_func.executor&quot;: &quot;Run&quot;, "Python.wg_func.executor": "Run",
&quot;Python.wg_main.executor&quot;: &quot;Run&quot;, "Python.wg_main.executor": "Run",
&quot;Python.wirepy.executor&quot;: &quot;Run&quot;, "Python.wirepy.executor": "Run",
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, "RunOnceActivity.ShowReadmeOnStart": "true",
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;, "RunOnceActivity.git.unshallow": "true",
&quot;Shell Script.install.executor&quot;: &quot;Run&quot;, "Shell Script.install.executor": "Run",
&quot;Shell Script.run_as.executor&quot;: &quot;Run&quot;, "Shell Script.run_as.executor": "Run",
&quot;git-widget-placeholder&quot;: &quot;21-04-2025-new-tooltip&quot;, "git-widget-placeholder": "28-04-2025-more-methods-and-optimize-methods",
&quot;last_opened_file_path&quot;: &quot;/home/punix/Pyapps/wire-py&quot;, "last_opened_file_path": "/home/punix/Pyapps/wire-py",
&quot;settings.editor.selected.configurable&quot;: &quot;ml.llm.LLMConfigurable&quot; "settings.editor.selected.configurable": "ml.llm.LLMConfigurable"
} }
}</component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/lx-icons" /> <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 - Fix ipv6 in Config File on import
- Wirepy run now as user - 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. - 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. - Tunnels are now read from the directory to view them in the list.
To display only own tunnels, and read errors are minimized. 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 pathlib import Path
from subprocess import check_call, CompletedProcess from subprocess import check_call, CompletedProcess
from tkinter import ttk, Toplevel from tkinter import ttk, Toplevel
from wp_app_config import AppConfig, Msg
import requests import requests
APP = "wirepy" # Translate
LOCALE_DIR = "/usr/share/locale/" _ = AppConfig.setup_translations()
locale.bindtextdomain(APP, LOCALE_DIR)
gettext.bindtextdomain(APP, LOCALE_DIR)
gettext.textdomain(APP)
_ = gettext.gettext
class Create: class Create:
""" """
@ -41,7 +36,7 @@ class Create:
pth: Path = Path.home() / ".config/wire_py" pth: Path = Path.home() / ".config/wire_py"
pth.mkdir(parents=True, exist_ok=True) pth.mkdir(parents=True, exist_ok=True)
sett: Path = Path.home() / ".config/wire_py/settings" sett: Path = Path.home() / ".config/wire_py/settings"
ks: Path = Path.home() / ".config/wire_py/keys" AppConfig.KEYS_FILE
if sett.exists(): if sett.exists():
pass pass
@ -50,11 +45,11 @@ class Create:
sett.touch() sett.touch()
sett.write_text("[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff\n") 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 pass
else: else:
ks.touch() AppConfig.KEYS_FILE.touch()
@staticmethod @staticmethod
def files_for_autostart() -> None: def files_for_autostart() -> None:
@ -80,11 +75,10 @@ class Create:
def make_dir() -> None: def make_dir() -> None:
"""Folder Name "tlecdewg" = Tunnel Encrypt Decrypt Wireguard""" """Folder Name "tlecdewg" = Tunnel Encrypt Decrypt Wireguard"""
folder_path: Path = Path("/tmp/tlecdcwg/") if AppConfig.TEMP_DIR.exists():
if folder_path.exists():
pass pass
else: else:
folder_path.mkdir() AppConfig.TEMP_DIR.mkdir()
@staticmethod @staticmethod
def decrypt() -> None: def decrypt() -> None:
@ -125,6 +119,31 @@ class LxTools(tk.Tk):
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) 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 @staticmethod
def uos() -> None: def uos() -> None:
""" """
@ -135,19 +154,18 @@ class LxTools(tk.Tk):
""" """
log_name: str = f"{Path.home()}"[6:] log_name: str = f"{Path.home()}"[6:]
file: Path = Path.home() / "/tmp/.log_user" file: Path = Path.home() / "/tmp/.log_user"
with open(file, "w", encoding="utf-8") as f: Path(file).write_text(log_name, encoding="utf-8")
f.write(log_name)
@staticmethod @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. method that can be added after need to delete a folder and a file when quitting.
Args: Args:
:param file: default None :param file: default None
:param folder_path: default None :param AppConfig.TEMP_DIR: default None
""" """
if folder_path is not None: if AppConfig.TEMP_DIR is not None:
shutil.rmtree(folder_path) shutil.rmtree(AppConfig.TEMP_DIR)
if file is not None: if file is not None:
Path.unlink(file) Path.unlink(file)
@ -156,14 +174,22 @@ class LxTools(tk.Tk):
""" """
method that writes in file whether tooltip is displayed or not method that writes in file whether tooltip is displayed or not
""" """
with open(path, "r", encoding="utf-8") as set_f2: lines = Path(path).read_text(encoding="utf-8")
lines2 = set_f2.readlines() if "False\n" in lines:
if "False\n" in lines2:
tip = False tip = False
else: else:
tip = True tip = True
return tip 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 @staticmethod
def msg_window(img_w: str, img_i: str, w_title: str, w_txt: str, txt2: Optional[str] = None, def msg_window(img_w: str, img_i: str, w_title: str, w_txt: str, txt2: Optional[str] = None,
com: Optional[str] = None) -> None: com: Optional[str] = None) -> None:
@ -394,8 +420,8 @@ class Tunnel:
""" """
Returns a list of Wireguard tunnel names Returns a list of Wireguard tunnel names
""" """
folder_path: Path = Path("/tmp/tlecdcwg/") AppConfig.TEMP_DIR: Path = Path("/tmp/tlecdcwg/")
wg_s: List[str] = os.listdir(folder_path) wg_s: List[str] = os.listdir(AppConfig.TEMP_DIR)
return wg_s return wg_s
@ -425,17 +451,15 @@ class Tunnel:
with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf: with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf:
if len(zf.namelist()) != 0: if len(zf.namelist()) != 0:
msg_t: str = _("Your zip file is in home directory") LxTools.msg_window(img_w, img_i, Msg.STR["exp_succ"], Msg.STR["exp_in_home"])
LxTools.msg_window(img_w, img_i, _("Export Successful"), msg_t)
else: else:
msg_t: str = _("Export failed! Please try again") LxTools.msg_window(img_w2, img_i2, Msg.STR["exp_err"], Msg.STR["exp_try"])
LxTools.msg_window(img_w2, img_i2, _("Export error"), msg_t)
else: 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: except TypeError:
pass pass
@ -485,6 +509,7 @@ class Tooltip:
label: tk.Label = tk.Label(tw, text=self.text, background="lightgreen", foreground="black", relief="solid", label: tk.Label = tk.Label(tw, text=self.text, background="lightgreen", foreground="black", relief="solid",
borderwidth=1, padx=5, pady=5) borderwidth=1, padx=5, pady=5)
label.grid() label.grid()
self.tooltip_window.after(2200, lambda: tw.destroy())
def hide_tooltip(self, event: Optional[Any] = None) -> None: def hide_tooltip(self, event: Optional[Any] = None) -> None:
""" """

10
install
View File

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

View File

@ -5,14 +5,12 @@ import os
import shutil import shutil
from pathlib import Path from pathlib import Path
from subprocess import check_call from subprocess import check_call
from wp_app_config import AppConfig
uname: Path = Path("/tmp/.log_user") uname: Path = Path("/tmp/.log_user")
with open(uname, "r", encoding="utf-8") as f: log_name = Path(uname).read_text(encoding="utf-8")
log_name = f.readline()
# 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") keyfile: Path = Path(f"/home/{log_name}/.config/wire_py/pbwgk.pem")
PKEYFILE: Path = "/usr/local/etc/ssl/pwgk.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"]) check_call(["openssl", "rsa", "-in", PKEYFILE, "-out", keyfile, "-outform", "PEM", "-pubout"])
shutil.chown(keyfile, 1000, 1000) shutil.chown(keyfile, 1000, 1000)
folder_path2 = f"/home/{log_name}/.config/wire_py/" AppConfig.TEMP_DIR2 = f"/home/{log_name}/.config/wire_py/"
detl: list[str] = os.listdir(folder_path2) detl: list[str] = os.listdir(AppConfig.TEMP_DIR2)
os.chdir(folder_path2) os.chdir(AppConfig.TEMP_DIR2)
detl.remove("keys") detl.remove("keys")
detl.remove("settings") 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") detl.remove("pbwgk.pem")
for detunnels in detl: for detunnels in detl:
tlname2 = f"{detunnels[:-4]}.conf" tlname2 = f"{detunnels[:-4]}.conf"
extpath = f"{folder_path}/{tlname2}" extpath = f"{AppConfig.TEMP_DIR}/{tlname2}"
check_call(["openssl", "pkeyutl", "-decrypt", "-inkey", PKEYFILE, "-in", detunnels, "-out", extpath]) check_call(["openssl", "pkeyutl", "-decrypt", "-inkey", PKEYFILE, "-in", detunnels,
"-out", extpath])
shutil.chown(extpath, 1000, 1000) shutil.chown(extpath, 1000, 1000)

View File

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

View File

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

670
wirepy.py
View File

@ -15,73 +15,53 @@ from subprocess import check_call
from tkinter import TclError, filedialog, ttk from tkinter import TclError, filedialog, ttk
from cls_mth_fc import (Create, GiteaUpdate, Tooltip, Tunnel, LxTools) from cls_mth_fc import (Create, GiteaUpdate, Tooltip, Tunnel, LxTools)
from wp_app_config import AppConfig, Msg
LxTools.uos() LxTools.uos()
Create.dir_and_files() Create.dir_and_files()
Create.make_dir() Create.make_dir()
Create.decrypt() Create.decrypt()
tcl_path: Path = Path("/usr/share/TK-Themes") tips = LxTools.if_tip(AppConfig.SETTINGS_FILE)
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")
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year # 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
VERSION: str = "v. 2.04.1725" 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 # Translate
APP = "wirepy" _ = AppConfig.setup_translations()
LOCALE_DIR = "/usr/share/locale/"
locale.bindtextdomain(APP, LOCALE_DIR)
gettext.bindtextdomain(APP, LOCALE_DIR)
gettext.textdomain(APP)
_ = gettext.gettext
img_w: str = r"/usr/share/icons/lx-icons/64/info.png" 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_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_w2: str = r"/usr/share/icons/lx-icons/64/error.png"
img_i2: str = r"/usr/share/icons/lx-icons/48/wg_msg.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(tk.Tk):
""" """
Class Wirepy this is the Main Window of wirepy Class Wirepy this is the Main Window of wirepy
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.my_tool_tip = None self.my_tool_tip = None
self.x_width = 600 self.x_width = AppConfig.UI_CONFIG["window_size"][0]
self.y_height = 383 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_x = int(self.winfo_screenwidth() / 2 - (self.x_width / 2))
self.monitor_center_y = int(self.winfo_screenheight() / 2 - (self.y_height / 2)) self.monitor_center_y = int(self.winfo_screenheight() / 2 - (self.y_height / 2))
self.resizable(width=False, height=False) self.resizable(AppConfig.UI_CONFIG["resizable_window"][0], AppConfig.UI_CONFIG["resizable_window"][1])
self.title("Wire-Py") 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.geometry(f"{self.x_width}x{self.y_height}+{self.monitor_center_x}+{self.monitor_center_y}")
self.columnconfigure(0, weight=1) self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1) self.rowconfigure(0, weight=1)
self.style = ttk.Style(self) self.style = ttk.Style(self)
self.tk.call("source", f"{tcl_path}/water.tcl") self.tk.call("source", f"{AppConfig.SYSTEM_PATHS["tcl_path"]}/water.tcl")
with open(set_file, "r", encoding="utf-8") as read_file: LxTools.theme_change(self)
lines = read_file.readlines()
if "light\n" in lines:
self.tk.call("set_theme", "light")
else:
self.tk.call("set_theme", "dark")
# Load the image file from the disk # 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 # Set it as the window icon
self.iconphoto(True, self.wg_icon) self.iconphoto(True, self.wg_icon)
@ -93,7 +73,6 @@ class FrameWidgets(ttk.Frame):
""" """
ttk frame class for better structure ttk frame class for better structure
""" """
def __init__(self, container, **kwargs): def __init__(self, container, **kwargs):
super().__init__(container, **kwargs) super().__init__(container, **kwargs)
@ -103,85 +82,12 @@ class FrameWidgets(ttk.Frame):
self.dns = None self.dns = None
self.address = None self.address = None
self.auto_con = 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_start = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_start"])
self.wg_vpn_stop = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_vpn-stop.png") self.wg_vpn_stop = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_stop"])
self.imp_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_import.png") self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"])
self.tr_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_trash.png") self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"])
self.exp_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/48/wg_export.png") self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"])
self.warning_pic = tk.PhotoImage(file=r"/usr/share/icons/lx-icons/64/error.png") self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"])
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)
# Frame for Menu # Frame for Menu
self.menu_frame = ttk.Frame(self) self.menu_frame = ttk.Frame(self)
@ -204,15 +110,16 @@ class FrameWidgets(ttk.Frame):
set_tip = tk.BooleanVar() set_tip = tk.BooleanVar()
self.settings = tk.Menu(self, relief="flat") self.settings = tk.Menu(self, relief="flat")
self.options_btn.configure(menu=self.settings, style="Toolbutton") 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 Updates"),
self.settings.add_checkbutton(label=_("Disable Tooltips"), command=tooltip, variable=set_tip) command=lambda: self.update_setting(set_update.get()), variable=set_update)
self.settings.add_command(label=_("Light"), command=theme_change_light) self.settings.add_checkbutton(label=_("Disable Tooltips"),
self.settings.add_command(label=_("Dark"), command=theme_change_dark) 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 # About BTN Menu / Label
self.about_btn = ttk.Button( 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.about_btn.grid(column=2, columnspan=2, row=0)
self.readme = tk.Menu(self) self.readme = tk.Menu(self)
@ -306,23 +213,12 @@ class FrameWidgets(ttk.Frame):
self.peer.config(font=("Ubuntu", 9)) self.peer.config(font=("Ubuntu", 9))
self.peer.grid(column=0, row=4, sticky="we", padx=130) 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 # Listbox with Scrollbar
self.l_box = tk.Listbox(self.lb_frame_btn_lbox, selectmode="single") 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.config(relief="ridge", font=("Ubuntu", 12, "bold"))
self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns") self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns")
self.l_box.event_add("<<ClickEvent>>", "<Button-1>") 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 = 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.scrollbar.grid(column=1, rowspan=4, row=0, sticky="nse")
self.l_box.configure(yscrollcommand=self.scrollbar.set) self.l_box.configure(yscrollcommand=self.scrollbar.set)
@ -336,13 +232,7 @@ class FrameWidgets(ttk.Frame):
# Button Vpn # Button Vpn
if self.a != "": if self.a != "":
self.stop() self.stop()
wg_read = f"/tmp/tlecdcwg/{self.a}.conf" data = self.handle_tunnel_data(self.a)
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()
else: else:
self.start() self.start()
@ -350,7 +240,7 @@ class FrameWidgets(ttk.Frame):
self.add = tk.StringVar() self.add = tk.StringVar()
self.DNS = tk.StringVar() self.DNS = tk.StringVar()
self.enp = tk.StringVar() self.enp = tk.StringVar()
self.label_empty() self.reset_fields()
self.show_data() self.show_data()
# Button Import # Button Import
@ -359,7 +249,151 @@ class FrameWidgets(ttk.Frame):
Tooltip(self.btn_i, _("Click to import a Wireguard Tunnel"), tips) 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 delete Wireguard Tunnel
""" """
@ -371,14 +405,12 @@ class FrameWidgets(ttk.Frame):
pre_key = key[3] pre_key = key[3]
check_call(["nmcli", "connection", "delete", select_tl]) check_call(["nmcli", "connection", "delete", select_tl])
self.l_box.delete(self.select_tunnel[0]) 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() lines6 = set_f6.readlines()
if ( if (select_tl == lines6[7].strip()
select_tl == lines6[7].strip() and "off\n" not in lines6[7].strip()):
and "off\n" not in lines6[7].strip()
):
lines6[7] = "off\n" 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) set_f7.writelines(lines6)
self.selected_option.set(0) self.selected_option.set(0)
self.autoconnect_var.set(_("no Autoconnect")) self.autoconnect_var.set(_("no Autoconnect"))
@ -386,7 +418,7 @@ class FrameWidgets(ttk.Frame):
if is_encrypt.is_file(): if is_encrypt.is_file():
Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat") Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat")
Path.unlink(f"/tmp/tlecdcwg/{select_tl}.conf") 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: with open(f"{Path.home()}/.config/wire_py/keys2", "w", encoding="utf-8") as writefile:
for line in readfile: for line in readfile:
if pre_key not in line.strip("\n"): if pre_key not in line.strip("\n"):
@ -412,69 +444,40 @@ class FrameWidgets(ttk.Frame):
self.str_var.set(value="") self.str_var.set(value="")
self.start() self.start()
self.l_box.update() self.l_box.update()
self.reset_fields()
# Address Label
self.add.set("")
self.DNS.set("")
self.enp.set("")
except IndexError: except IndexError:
if self.l_box.size() != 0: 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: 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"])
# 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():
def tl_rename(self) -> None:
"""
method to rename a tunnel
"""
name_of_file = LxTools.get_file_name(AppConfig.TEMP_DIR)
special_characters = ["\\", "/", "{", "}", " "] special_characters = ["\\", "/", "{", "}", " "]
if len(self.lb_rename.get()) > 12: 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: 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()): 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, Msg.STR["ren_err"], Msg.STR["false_signs"])
LxTools.msg_window(img_w, img_i2, rnp, msg_t)
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: else:
@ -483,7 +486,7 @@ class FrameWidgets(ttk.Frame):
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.l_box.get(self.select_tunnel[0])
# nmcli connection modify old connection.id iphone # 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") source = Path(f"/tmp/tlecdcwg/{select_tl}.conf")
destination = source.with_name(f"{self.lb_rename.get()}.conf") destination = source.with_name(f"{self.lb_rename.get()}.conf")
source.replace(destination) source.replace(destination)
@ -494,68 +497,31 @@ class FrameWidgets(ttk.Frame):
self.l_box.update() self.l_box.update()
new_a_connect = self.lb_rename.get() new_a_connect = self.lb_rename.get()
self.lb_rename.delete(0, tk.END) self.lb_rename.delete(0, tk.END)
if self.a != "" and self.a == select_tl:
self.a = Tunnel.active() with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f5:
self.str_var.set(value=self.a)
with open(set_file, "r", encoding="utf-8") as set_f5:
lines5 = set_f5.readlines() lines5 = set_f5.readlines()
if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip(): if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip():
lines5[7] = new_a_connect 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) theme_set5.writelines(lines5)
self.autoconnect_var.set(value=new_a_connect) self.autoconnect_var.set(value=new_a_connect)
self.update_connection_display()
Create.encrypt() Create.encrypt()
except IndexError: 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 except subprocess.CalledProcessError:
self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=tl_rename, padding=4, pass
style="RnButton.TButton")
self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne")
# Check Buttons except EOFError as e:
self.selected_option = tk.IntVar() print(e)
self.autoconnect_var = tk.StringVar()
self.autoconnect_var.set(self.auto_con)
# Frame for Labels, Entry and Button def import_sl(self) -> None:
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):
""" """
Import Methode for Wireguard config Files. validity check of 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.
""" """
Create.dir_and_files() Create.dir_and_files()
try: try:
@ -566,32 +532,29 @@ class FrameWidgets(ttk.Frame):
read = file.read() read = file.read()
path_split = filepath.split("/") path_split = filepath.split("/")
path_split1 = path_split[-1] path_split1 = path_split[-1]
self.a = Tunnel.active()
if "PrivateKey = " in read and "PublicKey = " in read and "Endpoint =" in read: if "PrivateKey = " in read and "PublicKey = " in read and "Endpoint =" in read:
with open(filepath, "r", encoding="utf-8") as file: with open(filepath, "r", encoding="utf-8") as file:
key = Tunnel.con_to_dict(file) key = Tunnel.con_to_dict(file)
pre_key = key[3] pre_key = key[3]
if len(pre_key) != 0: if len(pre_key) != 0:
with open(f"{Path.home()}/.config/wire_py/keys", "r", encoding="utf-8") as readfile: p_key = AppConfig.KEYS_FILE.read_text(encoding="utf-8")
p_key = readfile.readlines()
if pre_key in p_key or f"{pre_key}\n" in p_key: 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, Msg.STR["imp_err"], Msg.STR["tl_exist"])
LxTools.msg_window(img_w2, img_i2, ie, msg_t)
else: 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") keyfile.write(f"{pre_key}\r")
if len(path_split1) > 17: 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:] path_split = path_split1[len(path_split1) - 17:]
os.rename(p1, f"/tmp/tlecdcwg/{path_split}") os.rename(p1, f"{AppConfig.TEMP_DIR}/{path_split}")
new_conf = f"/tmp/tlecdcwg/{path_split}" new_conf = f"{AppConfig.TEMP_DIR}/{path_split}"
if self.a != "": if self.a != "":
check_call(["nmcli", "connection", "down", Tunnel.active()]) check_call(["nmcli", "connection", "down", self.a])
self.label_empty() self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type", subprocess.check_output(["nmcli", "connection", "import", "type",
"wireguard", "file", new_conf], text=True) "wireguard", "file", new_conf], text=True)
@ -599,10 +562,10 @@ class FrameWidgets(ttk.Frame):
Create.encrypt() Create.encrypt()
else: else:
shutil.copy(filepath, "/tmp/tlecdcwg/") shutil.copy(filepath, f"{AppConfig.TEMP_DIR}/")
if self.a != "": if self.a != "":
check_call(["nmcli", "connection", "down", Tunnel.active()]) check_call(["nmcli", "connection", "down", self.a])
self.label_empty() self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type", subprocess.check_output(["nmcli", "connection", "import", "type",
"wireguard", "file", filepath], text=True) "wireguard", "file", filepath], text=True)
@ -617,40 +580,27 @@ class FrameWidgets(ttk.Frame):
self.l_box.update() self.l_box.update()
self.l_box.selection_set(0) 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!") Tooltip(self.btn_exp, Msg.TTIP["export_tl"], tips)
, tips,)
Tooltip(self.btn_exp, _(" Click to export all\nWireguard Tunnel to Zipfile") Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], tips)
, tips)
Tooltip(self.btn_rename, _("To rename a tunnel, you need to\nselect a tunnel from"
" the list"), tips)
self.lb_rename.insert(0, "Max. 12 characters!") self.lb_rename.insert(0, "Max. 12 characters!")
self.str_var = tk.StringVar() self.str_var = tk.StringVar()
self.str_var.set(self.a) self.str_var.set(self.a)
self.color_label() self.color_label()
self.stop() self.stop()
wg_read = f"/tmp/tlecdcwg/{self.a}.conf" data = self.handle_tunnel_data(self.a)
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()
check_call(["nmcli", "con", "mod", self.a, "connection.autoconnect", "no"]) check_call(["nmcli", "con", "mod", self.a, "connection.autoconnect", "no"])
Path.chmod(wg_read, 0o600)
if ("PrivateKey = " in read) and ("Endpoint = " in read): if ("PrivateKey = " in read) and ("Endpoint = " in read):
pass pass
else: else:
msg_t = _("Oh... no valid Wireguard File!\nPlease select a valid Wireguard File") LxTools.msg_window(img_w2, img_i2, Msg.STR["imp_err"], Msg.STR["no_valid_file"])
LxTools.msg_window(img_w2, img_i2, ie, msg_t)
except EOFError as e: except EOFError as e:
print(e) print(e)
@ -661,9 +611,25 @@ class FrameWidgets(ttk.Frame):
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print("Tunnel exist!") 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. 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. 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]) select_tl = self.l_box.get(select_tunnel[0])
if self.selected_option.get() == 0: if self.selected_option.get() == 0:
with open(set_file, "r", encoding="utf-8") as set_f3: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines3 = set_f3.readlines() lines[7] = 'off\n'
lines3[7] = "off\n" Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
with open(set_file, "w", encoding="utf-8") as set_f3:
set_f3.writelines(lines3)
tl = Tunnel.list() tl = Tunnel.list()
@ -684,31 +648,28 @@ class FrameWidgets(ttk.Frame):
self.wg_autostart.configure(state="disabled") self.wg_autostart.configure(state="disabled")
if self.selected_option.get() >= 1: if self.selected_option.get() >= 1:
with open(set_file, "r", encoding="utf-8") as set_f3: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines3 = set_f3.readlines() lines[7] = select_tl
lines3[7] = select_tl Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
with open(set_file, "w", encoding="utf-8") as set_f3:
set_f3.writelines(lines3)
except IndexError: except IndexError:
self.selected_option.set(1) self.selected_option.set(1)
self.on_off() 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. 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. Set (on), the selected tunnel is displayed in the label.
At (off) the label is first emptied then filled with No Autoconnect At (off) the label is first emptied then filled with No Autoconnect
""" """
with open(set_file, "r", encoding="utf-8") as set_f4: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines4 = set_f4.readlines()
if lines4[7] != "off\n": if lines[7] != "off\n":
print(f"{lines4[7]} starts automatically when the system starts.") print(f"{lines[7]} starts automatically when the system starts.")
self.selected_option.set(1) self.selected_option.set(1)
self.autoconnect_var.set("") self.autoconnect_var.set("")
self.auto_con = lines4[7] self.auto_con = lines[7]
else: else:
self.selected_option.set(0) self.selected_option.set(0)
@ -722,12 +683,11 @@ class FrameWidgets(ttk.Frame):
self.autoconnect.config(font=("Ubuntu", 11)) self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19) 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 Displays the value address, DNS and peer in the labels
or empty it again or empty it again
""" """
# Address Label # Address Label
self.add = tk.StringVar() self.add = tk.StringVar()
self.add.set(f"{_("Address: ")}{data[0]}") self.add.set(f"{_("Address: ")}{data[0]}")
@ -736,19 +696,18 @@ class FrameWidgets(ttk.Frame):
self.enp = tk.StringVar() self.enp = tk.StringVar()
self.enp.set(f"{_("Endpoint: ")}{data[2]}") 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("") fields = [self.add, self.DNS, self.enp]
self.DNS.set("") for field in fields:
self.enp.set("") field.set("")
def show_data(self): def show_data(self) -> None:
""" """
shows data in the label shows data in the label
""" """
# Address Label # Address Label
self.address = ttk.Label(self.lb_frame, textvariable=self.add, foreground="#0071ff") 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) 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.grid(column=0, row=8, sticky="w", padx=10, pady=20)
self.endpoint.config(font=("Ubuntu", 9)) self.endpoint.config(font=("Ubuntu", 9))
def stop(self): def stop(self) -> None:
""" """
Stop Button 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) 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 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) self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
tl = Tunnel.list() tl = Tunnel.list()
if len(tl) == 0: 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: 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 View activ Tunnel in the color green or yellow
""" """
lines = AppConfig.SETTINGS_FILE.read_text()
with open(set_file, "r", encoding="utf-8") as read_file:
lines = read_file.readlines()
if "light\n" in lines: if "light\n" in lines:
self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green") 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.config(font=("Ubuntu", 11, "bold"))
self.lb_tunnel.grid(column=2, padx=10, row=1) 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: try:
if self.a == "": if self.a == "":
self.start()
self.select_tunnel = self.l_box.curselection() self.select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.l_box.get(self.select_tunnel[0])
check_call(["nmcli", "connection", "up", select_tl]) self.handle_connection_state("start", 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)
# Address Label else:
self.init_and_report(data)
self.show_data()
# Button Start/Stop data = self.handle_tunnel_data(self.a)
self.stop() if data:
self.a = Tunnel.active()
self.str_var = tk.StringVar()
self.str_var.set(self.a)
self.color_label()
elif self.a != "": self.handle_connection_state("stop")
# 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()
except IndexError: except IndexError:
if self.l_box.size() != 0: 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: 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__": if __name__ == "__main__":
@ -871,5 +845,5 @@ if __name__ == "__main__":
window.tk.call("set", "::tk::dialog::file::showHiddenVar", "0") window.tk.call("set", "::tk::dialog::file::showHiddenVar", "0")
window.mainloop() window.mainloop()
LxTools.clean_files(folder_path, user_file) LxTools.clean_files(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
sys.exit(0) 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")
}