#!/usr/bin/python3 import sys, pathlib, re, subprocess import ressenger_initialisation, ressenger_client, ressenger_common, ressenger_cryptography, ressenger_exceptions, ressenger_server from PySide6.QtUiTools import QUiLoader from PySide6.QtWidgets import QApplication, QListWidget, QTextBrowser, QTextEdit, QPushButton, QFileDialog, QMessageBox, QDialog, QVBoxLayout, QLineEdit, QLabel from PySide6.QtCore import QFile, QIODevice, Qt _B32_RE = re.compile(r'^[a-z2-7]{52}\.b32\.i2p\.?$', re.IGNORECASE) def is_i2p_b32_address(s: str) -> bool: if not isinstance(s, str): return False s = s.strip() return bool(_B32_RE.fullmatch(s)) def parseArguments(): flag1=False args={} for i1 in range(len(sys.argv)): if flag1: if sys.argv[i1].startswith("--"): args[sys.argv[i1-1][2:]]=None else: args[sys.argv[i1-1][2:]]=sys.argv[i1] flag1=False elif sys.argv[i1].startswith("--"): flag1=True if flag1: args[sys.argv[-1][2:]]=None return args def message_box_2a(title, text): dlg = QMessageBox() dlg.setWindowTitle(title) dlg.setText(text) dlg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) dlg.setDefaultButton(QMessageBox.Ok) return dlg.exec() def message_box_2b(title, text): dlg = QMessageBox() dlg.setWindowTitle(title) dlg.setText(text) dlg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) dlg.setDefaultButton(QMessageBox.Yes) return dlg.exec() def message_box_1(title, text): dlg = QMessageBox() dlg.setWindowTitle(title) dlg.setText(text) dlg.setStandardButtons(QMessageBox.Ok) dlg.setDefaultButton(QMessageBox.Ok) return dlg.exec() def initialise_user(profile): ui_path = 'Initialise.ui' in_ui_file = QFile(ui_path) if not in_ui_file.open(QIODevice.ReadOnly): print(f"Cannot open {ui_path}: {in_ui_file.errorString()}") return None loader = QUiLoader() loaded_widget = loader.load(in_ui_file) in_ui_file.close() if not loaded_widget: print(loader.errorString()) return None app = QApplication.instance() if app is None: print("No QApplication instance.") return None if isinstance(loaded_widget, QDialog): if not hasattr(app, "_aux_windows"): app._aux_windows = [] app._aux_windows.append(loaded_widget) result = loaded_widget.exec() return loaded_widget container = QDialog() layout = QVBoxLayout(container) layout.addWidget(loaded_widget) container.setLayout(layout) container.setWindowTitle(loaded_widget.windowTitle() or "Initialise") if not hasattr(app, "_aux_windows"): app._aux_windows = [] app._aux_windows.append(container) btn_cancel = loaded_widget.findChild(QPushButton, "pushButton_11") btn_ok = loaded_widget.findChild(QPushButton, "pushButton_12") username = loaded_widget.findChild(QLabel, "label_27") nickname = loaded_widget.findChild(QLineEdit, "lineEdit_13") port = loaded_widget.findChild(QLineEdit, "lineEdit_16") b32addr = loaded_widget.findChild(QLineEdit, "lineEdit_15") passwd = loaded_widget.findChild(QLineEdit, "lineEdit_14") username.setText(profile) succeed=False def call_initialise(): nonlocal succeed try: ressenger_initialisation.initialise(password=passwd.text(), b32address=b32addr.text(), username=profile, port=int(port.text()), nick=nickname.text()) message_box_1('Suceess', 'Profile has been created successfully.') succeed=True return True except FileExistsError as e: if message_box_2b('Alert', f'{str(e)}\nDo you want to force the creation of the profile? Conflicting files will be overwritten.')==QMessageBox.Yes: ressenger_initialisation.initialise(password=passwd.text(), b32address=b32addr.text(), username=profile, port=int(port.text()), nick=nickname.text(), force=True) message_box_1('Suceess', 'Profile has been created successfully.') succeed=True return True return False def proceed(): if nickname.text()=='': message_box_1('Error', 'Nickname should not be empty!') elif not port.text().isdigit(): message_box_1('Error', 'Invalid port number!') elif not (int(port.text())<=49151 and int(port.text())>=1024): message_box_1('Error', 'Port number should be between 1024 and 49151!') elif not is_i2p_b32_address(b32addr.text()): message_box_1('Error', 'Invalid I2P Base32 address!') elif len(passwd.text())<8: message_box_1('Error', 'Password should be at least 8 characters long!') else: if len(passwd.text())<20: if message_box_2b('Alert', 'Password is suggested to be at least 20 characters long.\nDo you want to go back to set a longer password?')==QMessageBox.Yes: pass else: if call_initialise(): container.reject() else: if call_initialise(): container.reject() btn_cancel.clicked.connect(container.reject) btn_ok.clicked.connect(proceed) container.exec() if not succeed: exit() if __name__ == "__main__": args=parseArguments() if 'help' in args.keys(): print(f'''Usage: {sys.argv[0]} [OPTIONS] ... List of options: --profile [PROFILE] Start Ressenger with the profile [PROFILE]. --help Display this help and exit. ''') exit() profile='default' if 'profile' in args.keys(): profile=args['profile'] app = QApplication(sys.argv) if profile.startswith("cache_"): message_box_1('Error', 'Invalid profile name.') exit() if profile.startswith("lock_"): message_box_1('Error', 'Invalid profile name.') exit() user_path=pathlib.Path(f'~/.ressenger/{profile}').expanduser() if not user_path.exists(): if message_box_2a('Create a profile?',f'The Ressenger profile does not exist.\nWould you like to create one now?')==QMessageBox.Ok: initialise_user(profile) else: exit() ui_file_name = "MainWindow.ui" ui_file = QFile(ui_file_name) if not ui_file.open(QIODevice.ReadOnly): print(f"Cannot open {ui_file_name}: {ui_file.errorString()}") sys.exit(-1) loader = QUiLoader() window = loader.load(ui_file) ui_file.close() list_widget = window.findChild(QListWidget, "listWidget") text_browser = window.findChild(QTextBrowser, "textBrowser") text_edit = window.findChild(QTextEdit, "textEdit") btn_fingerprint = window.findChild(QPushButton, "pushButton") btn_file = window.findChild(QPushButton, "pushButton_2") btn_send = window.findChild(QPushButton, "pushButton_3") if not window: print(loader.errorString()) sys.exit(-1) window.show() sys.exit(app.exec())