diff --git a/ca.crt b/ca.crt index af19e89..8f69c12 100644 --- a/ca.crt +++ b/ca.crt @@ -1,68 +1,68 @@ -----BEGIN CERTIFICATE----- -MIIGATCCA+mgAwIBAgIUKx8oo/wc8VCjZI4ueEY4vlp3KKQwDQYJKoZIhvcNAQEL +MIIF9TCCA92gAwIBAgIUQaJLS561XqG19oy1TnVL7da4P8YwDQYJKoZIhvcNAQEL BQAwcTELMAkGA1UEBhMCQkUxDjAMBgNVBAgMBUxJRUdFMRMwEQYDVQQKDApJTGlr ZVN3aWxhMRYwFAYDVQQLDA1DeWJlclNlY3VyaXR5MSUwIwYDVQQDDBxUcnVzdFN3 -aWxhIENlcnRpZmljYXQgUmFjaW5lMB4XDTIyMDIwMTE5MzU0NVoXDTMxMTIxMTE5 -MzU0NVowgYYxCzAJBgNVBAYTAkJFMQ4wDAYDVQQIDAVMSUVHRTEOMAwGA1UEBwwF +aWxhIENlcnRpZmljYXQgUmFjaW5lMB4XDTIyMDIwMjE0NDc0MloXDTMxMTIxMjE0 +NDc0MlowgZIxCzAJBgNVBAYTAkJFMQ4wDAYDVQQIDAVMSUVHRTEOMAwGA1UEBwwF TElFR0UxEzARBgNVBAoMCklMaWtlU3dpbGExFjAUBgNVBAsMDUN5YmVyU2VjdXJp -dHkxKjAoBgNVBAMMIUxhYm8yMDIyIENlcnRpZmljYXQgSW50ZXJtZWRpYWlyZTCC -AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeG9SlV1fhXjfj9sVcnzO8q -HqYt/2+j9qQSA7479Y/ZMd+3bZ3V7KAwgrwrrnOeAynCplbcHaWfPwSYrAixZ3Lz -7Kj/Opy5C1P3fOuFrrSFjqRPn+ssvhvgtQzQgqN6lYFPzW0q4gIvHcQaaTSyWtmP -8zdFbJmzklPaGugZ9WLeOTMtetg3Nf0vTcIoC8mlfSwXUJUxxXXwlHyO/9TuRtya -2aFiuGHuk4Oh2aqthkXTz309xTzGAcjRDzlzjAi7Z5GtyD0Npcdr3GVNTXzIneLc -u1FtVC0dsLINg9r/SiphAIBVFoc1psbSVW9Tuy9vaIlSkbJlFXRvEcIh/uzKP6Wy -2or1pXg3IoJkDG6jLUid4Nlfwfwopm0vrFbefN/7flbICm4ADcrlgXC9fwRrgUzK -fa1dGOVe12s6oHJ/RuPbE7QdaJtgAu+i1VWLSmr/MxXWJGMQDljmy91FAZmjk9V8 -tlDL1/qKoH6j6iA31l4IZ23buDRuaFcY1iS8L+QNNcRB+qFmU8zlmVCjeHMUoPfD -EVh25WagiHYcV2Pt1lqWJYjl53HYZI2coYo+hngwVAVFX90Sp7Ah5cDUp2AAeIU/ -nHB31GAciQ5xflz8ApNTw3G8wBXYVHL7f98K1SKrdnhHE+Rzc58r1b7LRvJhiHIE -5x1DdhNYruRU7D3DyVF/AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN -BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSNGyiz -5FCsuBl98u51IuTlrx+DwzAfBgNVHSMEGDAWgBQOFchEMlX2QrcgELQGbqwxgIy6 -czANBgkqhkiG9w0BAQsFAAOCAgEAWuUVIm4lDD0wvKA0eYCwQf+iknt4/XsA3nId -06P9DMsFvVE9CGVFUbHRftiL45e6cVM2+KrwPOjpeu7DES+xtDSH9TBpYP8WsW6k -YeJiw/C3Lhl8YdMEjHV927UMGPXaj4foFeh5ls8XPeEkruG7zriYLm2qBxY8XQR0 -8sIHvyg9Aucp+d+trR8pcT7A9AszIn4OncUymPNwfN1qSpAQyDye3caqOQyKXAUB -1RUnaUm06tPAvFGvSKVwIhTNgFEsvGujnNuBrCOT7soXJtuv5GgHS4A9nD6Y5DdV -ihKYRAC6XIsQwgRHUeKQYNtNPkvdE8PqSx3jW/UKhlDBDKy7KN7FXiMPoIxxKlOl -IRSYvNXZHnkspgOxYDs+YtC+0IUtdormSodQekbjY4jPjW685AWRDN5zVjK2FUaY -ZHYFc9HGOuS7okMODjjl+9QIZAlhc5WlWwsrVnsKAvN7yNemF943LZaOQCZIDKVS -KpRtjgB5pt6uh0FkLanKWzGP1xyqAlQnEUJB8WzqCHgEkTA5eapfyw1VR9CBvr55 -6LXd9gX0RrIE3h3WUvTSEVjAymmIkfRBuVoGPUgZT5VRvMqKeQAXovE0K8m9tRtP -cGjz8ce8WERiTXB5dQuq6G6DUQulMatw5qbOjOCxw3bKUKN+rbHLSTie2iQ8bnr3 -JBVmbAQ= +dHkxNjA0BgNVBAMMLUxhYm8yMDIyIERldmVsb3BtZW50IENlcnRpZmljYXQgSW50 +ZXJtZWRpYWlyZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOIkDI64 +cT6RN2hwTh81+Di5CqPJevY9ulR3nyR/bdI/UEvZ1KBZ6SDZjO2gWwyTdSaeTHGO +xKDpd6IYuqnqPU1XzCM2i4nqm9Fdyk3gKjlJxearIa52Nz2hTiM5D48UN9wfzAZH +8qjijRDfZ6+6Jzj+FloWWfuGUSPZlEufghrGxB4QccHT2YWYDoNNaY6qUXH+rRrN +kQtq8X080IIHj5At0wfd35ZPV7qIet7WawrcSeNTzVRwFw++2GkjWeICZvazQwYO +oo9QoTd844cGYMvfIEK4ZUjiaBux5QBYfUC12HZSVP4aKIUP6swDj02ec4QeB5A1 +kzTDQffmPhYKh7BlUuf/WWtRVvoSUVsjvjnRquVTTQMU4WTE/nXIyjqHsZxn7xzM +jFWkdMuV6BYycWCJ+smTPqIRYZsFM/UrMz9+PgDSOWUfhU4e8kaQuuSGP/SuOUq3 +PsFhlaop+b5+kpGD9J4CwM+uHJ2GlNj8P6bqtzdk5f35/IghagEIjagiCjZsOAuA +VdDUxQ6aqxKDr9VFy4C4U7lu8qWht7eXjEoa511YSDsgs0PmpjutjEvS9qky9lUq +3T9FkHsDLfH2zjp/7KykTRCmGr7IIcSblTDJaBh/LjMMdbY3PKP9McDusanHg0/U +bgWrguqJEjSoTRUKRPMcyXr77xKQhfMGaUTfAgMBAAGjYzBhMB0GA1UdDgQWBBRt +CVdNN96jLKZC5o6Ww+t6FqL74jAfBgNVHSMEGDAWgBSgLzmYK4ju6ocXHm5J3MlH +lN+4YDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B +AQsFAAOCAgEAT7WOIyY3Kdd2QK5h0/bs8xvGU1HJ7dtwAhSlgHdJ3OC6Zc2o4998 +y6el4QrpCd8pNMzBbZcQiNLsywP8Xe7MiVuzUjNB9Y75+Ml9qMxsACyQlO6GQ1sN +pyE8mZitEYeW+YvTK864rWUYWMCVKHM0hWymK2QVlA17g07ZhaJ6OPsK8Fa/OkNL +9MXghgqitpGY8aXy8sd0uipz0NcgNzEvmgYv1S1zL6RxEuZ2hdknm+vlW8cN9Efj +lvZXBGGg6TrFLkY5g5sCgf2bxFeiULOLMGSF/wkj4XPXhqt6BH635jLA3UUVdapq +fhgVIp/qWmz7ouJN7E+jVfVK9AfJAqb33CjJSn5n0plJSO+OfwARlhZPRpH2kXOe +6Sl0NXLSBOHk8ruufQrTLl10c9SYxjbfDSHQCVJ8VTv9/HtE4+nG/I3KNAIo/WMK +8hbskzDe31tRz5V0uv0J/plrW8pNbqRqXTbvT4NQc5WYBKZSJ5RaTPA+P6ZEWoWn +TOYKq3MiRqTRE8eeD52LKMnw8/du8jWqgG/bc6ti7DpZf+aUNF6ru9DmzEJYksOc +c+n7Sz2ijCB2G4sSchnL+ueXIzqWSbrPiTVoWjjiBux1ryqMVg6rtFPJ7gvVgOda +4++FIs2P9AYceoyBuRHlGZGNuGNfxu2NH/pjLvqqaKxUMyCBkcONIxI= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIFwzCCA6ugAwIBAgIUKx8oo/wc8VCjZI4ueEY4vlp3KKMwDQYJKoZIhvcNAQEL +MIIF0zCCA7ugAwIBAgIUQaJLS561XqG19oy1TnVL7da4P8UwDQYJKoZIhvcNAQEL BQAwcTELMAkGA1UEBhMCQkUxDjAMBgNVBAgMBUxJRUdFMRMwEQYDVQQKDApJTGlr ZVN3aWxhMRYwFAYDVQQLDA1DeWJlclNlY3VyaXR5MSUwIwYDVQQDDBxUcnVzdFN3 -aWxhIENlcnRpZmljYXQgUmFjaW5lMB4XDTIyMDIwMTE5MzI0NloXDTI1MDEzMTE5 -MzI0NlowcTELMAkGA1UEBhMCQkUxDjAMBgNVBAgMBUxJRUdFMRMwEQYDVQQKDApJ +aWxhIENlcnRpZmljYXQgUmFjaW5lMB4XDTIyMDIwMjE0NDQzN1oXDTI1MDIwMTE0 +NDQzN1owcTELMAkGA1UEBhMCQkUxDjAMBgNVBAgMBUxJRUdFMRMwEQYDVQQKDApJ TGlrZVN3aWxhMRYwFAYDVQQLDA1DeWJlclNlY3VyaXR5MSUwIwYDVQQDDBxUcnVz dFN3aWxhIENlcnRpZmljYXQgUmFjaW5lMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAqNTJ9/KsR+LyOGgxE67sx1SGqo7obEH80oj+q6Vcair4/xI2sb0V -kQaQxrAwvg4LwsZ+jpJmUt+2sCfxIF2PwHNAnBuNYa/fW4Y/LFS9N4OIdOiLvjJ6 -Meuwjkv3M5n9DZ0cf0UukhhCBkOYn6BZ8rXy4udYM/5uJhfDgJbymrzc6h/83bFI -1v+vR08HSnjwnkAs6GGdPvJD5HgZZcHM+RCahlSJ+bRuu8hQNIa8oLiIdhN+3rzi -qO+FwGfZO671Ad3sgeK9NKrWR3ciN76sS7u4pn8mkb33E5vD5dkI3x1fyTJbtVzB -f7jakMJUVmU/eIoIT+8b4OVOEQG4Hkqp6spbzOAyj39tD12I2nVvQ1mWzAuMG3uI -GiRiEXOhIhCGSQIIpA0yMHed4wqLxVsCoN9BwRh7Q9jgo9nubvQlv071ttZMh2fC -CBL5ID1bSWYMWkchnhjpCJCbN4QMWgKVHwKAA2jnYmvBPBkLFR0ptLJLRdlxnIVw -iXv/LUwvj78W3RbJmVz6F9CFdS9NXAjLdDi+eL57//EmIwFCyIv7j3pOk0PvQS5p -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 +UHNJ9snS7g== -----END CERTIFICATE----- \ No newline at end of file diff --git a/controller/AppController.py b/controller/AppController.py index b858286..7b3376a 100644 --- a/controller/AppController.py +++ b/controller/AppController.py @@ -1,16 +1,135 @@ -from controller.IAppController import * +from net.UnicastConnection import * +from model.Protocol import * +import traceback +import os.path -class AppController(IAppController): +class AppController: def __init__(self, parser): self.parser = parser + self.connection = UnicastConnection() self.window_controller = None + def ssl_ca_path(self, ca_path): + self.connection.set_ca_path(ca_path) + def set_window_controller(self, window_controller): self.window_controller = window_controller + def on_sign(self, host, port, login, passw, tls, register=False): + print("[AppController::on_sign]") + try: + if not self.connection.is_connected: + print("[AppController::on_sign] connecting ... calling UnicastConnection::connect") + self.connect(host, port, tls) + if not register: + message = self.parser.build_SIGNIN(login, passw) + else: + message = self.parser.build_SIGNUP(login, passw) + self.connection.send_message(message) + line = self.connection.read_line() + message_id = self.parser.parse(line, False) + if message_id == Protocol.PARSE_SIGNOK: + self.window_controller.switch_connected_mode() + self.show_message("Connection done", False) + elif message_id == Protocol.PARSE_SIGNERROR: + self.show_message("Error during SIGN_IN / SIGN_UP", True) + self.on_filelist() + except Exception as ex: + print(ex) + traceback.print_exc() + + def get_filename_size_from_item(self, item): + tokens = item.split('!') + if len(tokens) == 2: + return (tokens[0],tokens[1]) + else: + return None + + def on_filelist(self): + print("[AppController::on_filelist]") + try: + if self.connection.is_connected: + message = self.parser.build_FILELIST() + self.connection.send_message(message) + line = self.connection.read_line() + message_id = self.parser.parse(line, False) + if message_id == Protocol.PARSE_FILES: + tokens = self.parser.parse_FILES(line) + if len(tokens) == 1: + self.window_controller.clear_all_file_on_window() + allfiles = tokens[0].split() + for f in allfiles: + (filename, filesize) = self.get_filename_size_from_item(f) + self.window_controller.add_file_on_window([filename, filesize]) + except Exception as ex: + print(ex) + traceback.print_exc() + def on_quit(self): - pass -# if self.connection.is_connected: -# self.connection.send_message(self.parser.buildEXIT()) -# if self.client_thread != None: -# self.client_thread.stop_loop() \ No newline at end of file + if self.connection.is_connected: + self.connection.send_message(self.parser.build_SIGNOUT()) + + def on_fileupload(self, filepath): + if os.path.isfile(filepath): + size = os.path.getsize(filepath) + print(f"[AppController::on_fileupload] file={filepath}; size={size}") + message = self.parser.build_SAVEFILE(os.path.basename(filepath), size) + self.connection.send_message(message) + self.connection.send_file(filepath) + line = self.connection.read_line() + message_id = self.parser.parse(line, False) + if message_id == Protocol.PARSE_SAVEFILEOK: + self.show_message("File saved", False) + self.on_filelist() + elif message_id == Protocol.PARSE_SAVEFILEERROR: + self.show_message("Error during saving file !", True) + else: + self.show_message(f"Error: file {filepath} not found !", True) + + def on_filedownload(self, filename, savepath): + if os.path.isdir(savepath): + print(f"[AppController::on_filedownload] file={filename}; path={savepath}") + message = self.parser.build_GETFILE(filename) + self.connection.send_message(message) + line = self.connection.read_line() + message_id = self.parser.parse(line, False) + if message_id == Protocol.PARSE_GETFILEOK: + tokens = self.parser.parse_GETFILEOK(line) + if len(tokens) == 2: + self.connection.receive_file(tokens[0], savepath, int(tokens[1])) + self.show_message("File downloaded !", False) + else: + self.show_message(f"Bad file information\n{tokens}") + elif message_id == Protocol.PARSE_GETFILEERROR: + self.show_message(f"Cannot download the file", True) + else: + self.show_message(f"Error: directory {savepath} not found !", True) + + def on_filedelete(self, filename): + print(f"[AppController::on_filedelete] file={filename}") + message = self.parser.build_REMOVEFILE(filename) + self.connection.send_message(message) + line = self.connection.read_line() + message_id = self.parser.parse(line, False) + if message_id == Protocol.PARSE_REMOVEFILEOK: + self.show_message("File deleted successfully !", False) + self.on_filelist() + elif message_id == Protocol.PARSE_REMOVEFILEERROR: + self.show_message("Error while erasing file.", True) + + def show_message(self,message, is_error): + self.window_controller.show_message(message, is_error) + + def connect(self, host, port, tls): + try: + self.connection.connect(host, port, tls) + if tls and UnicastConnection.STRICT_SSL_VALIDATION: + self.show_message(self.connection.ssl_info, False) + elif tls and not UnicastConnection.STRICT_SSL_VALIDATION: + self.show_message("SSL/TLS enabled, but certificate NOT checked !", False) + else: + self.show_message("No SSL connection", False) + + except Exception as ex: + self.show_message(f"Error during connection !\n{ex}", True) + self.connection = UnicastConnection() diff --git a/controller/IAppController.py b/controller/IAppController.py deleted file mode 100644 index 25cf96a..0000000 --- a/controller/IAppController.py +++ /dev/null @@ -1,2 +0,0 @@ -class IAppController: - pass \ No newline at end of file diff --git a/controller/IMainWindowController.py b/controller/IMainWindowController.py deleted file mode 100644 index 224a8c8..0000000 --- a/controller/IMainWindowController.py +++ /dev/null @@ -1,2 +0,0 @@ -class IEventHandler: - pass \ No newline at end of file diff --git a/controller/MainWindowController.py b/controller/MainWindowController.py index c88fca8..908addd 100644 --- a/controller/MainWindowController.py +++ b/controller/MainWindowController.py @@ -1,10 +1,45 @@ -from controller.IMainWindowController import * - -class MainWindowController(IEventHandler): +class MainWindowController: def __init__(self, parser, app_controller): self.parser = parser self.app_controller = app_controller + self.app_controller.set_window_controller(self) + self.window = None def on_quit(self): self.app_controller.on_quit() - pass \ No newline at end of file + pass + + def on_signin(self, host, port, login, passw, tls): + print("[MainWindowController::on_signin] calling AppController::on_sign") + self.app_controller.on_sign(host, port, login, passw, tls) + + def on_signup(self, host, port, login, passw, tls): + self.app_controller.on_sign(host, port, login, passw, tls, True) + + def on_refreshlist(self): + self.app_controller.on_filelist() + + def on_fileupload(self, filepath): + self.app_controller.on_fileupload(filepath) + + def on_filedownload(self, filename, savepath): + self.app_controller.on_filedownload(filename, savepath) + + def on_filedelete(self, filename): + self.app_controller.on_filedelete(filename) + + def show_message(self, message, is_error): + self.window.show_message(message, is_error) + + def switch_connected_mode(self): + self.window.connected_mode() + + def register_window(self, window): + self.window = window + + def clear_all_file_on_window(self): + self.window.remove_all_from_treeview() + + def add_file_on_window(self, fileinfo): + self.window.add_file_in_treeview(fileinfo) + diff --git a/gui/IMainWindow.py b/gui/IMainWindow.py deleted file mode 100644 index d286fbf..0000000 --- a/gui/IMainWindow.py +++ /dev/null @@ -1,2 +0,0 @@ -class IMainWindow: - pass \ No newline at end of file diff --git a/gui/MainWindow.py b/gui/MainWindow.py index b880a47..16dfef2 100644 --- a/gui/MainWindow.py +++ b/gui/MainWindow.py @@ -1,11 +1,12 @@ import tkinter as tk from tkinter import ttk from tkinter import messagebox -from gui.IMainWindow import * +from tkinter import filedialog -class MainWindow(IMainWindow): +class MainWindow: def __init__(self, controller): self.controller = controller + self.controller.register_window(self) self.root = tk.Tk() self.root.title("SecCon - © Louis SWINNEN 2022") self.root.config(bd=5) @@ -81,9 +82,9 @@ class MainWindow(IMainWindow): bottom_pane = ttk.Frame(self.root, padding=(5, 0, 5, 0)) bottom_pane.columnconfigure(1, weight=1) - self.bt_filelist = ttk.Button(bottom_pane, text="Refresh list", command=self.file_list) + self.bt_filelist = ttk.Button(bottom_pane, text="Refresh list", command=self.refresh_list) self.bt_filelist.grid(row=0, column=0, padx=10, pady=2,sticky=tk.E) - self.bt_savefile = ttk.Button(bottom_pane, text="Upload file", command=self.save_file) + self.bt_savefile = ttk.Button(bottom_pane, text="Upload file", command=self.upload_file) self.bt_savefile.grid(row=0, column=1, padx=10, pady=2,sticky=tk.E) self.bt_getfile = ttk.Button(bottom_pane, text="Download file", command=self.get_file) self.bt_getfile.grid(row=0, column=2, padx=10, pady=2,sticky=tk.E) @@ -120,25 +121,84 @@ class MainWindow(IMainWindow): self.bt_removefile.state(["!disabled"]) def sign_in(self): - pass - def sign_up(self): - pass + if self.check_before_signin(): + print("[MainWindow] Calling MainWindowController::on_signin()") + self.controller.on_signin(self.host.get(), int(self.port.get()), self.login.get(), self.passw.get(), self.tls.get()) + else: + self.show_message("All fields are mandatory!\nPort is numeric (1025-65535)", True) - def save_file(self): + def sign_up(self): + if self.check_before_signin(): + print("[MainWindow] Calling MainWindowController::on_signup()") + self.controller.on_signup(self.host.get(), int(self.port.get()), self.login.get(), self.passw.get(), self.tls.get()) + else: + self.show_message("All fields are mandatory!\nPort is numeric (1025-65535)", True) + + def check_before_signin(self): + try: + host = self.host.get() + port = int(self.port.get()) + login = self.login.get() + passw = self.passw.get() + tls = self.tls.get() + if(not(host and host.strip())) or port <= 1024 or port >= 65536 or not (login and login.strip()) or not(passw and passw.strip()): + return False + else: + return True + except ValueError: + return False + + def show_message(self, message, is_error): + if is_error: + messagebox.showerror("Error", message) + else: + messagebox.showinfo("Information", message) + + def show_select_file_dialog(self): + filepath = filedialog.askopenfilename() + return filepath + + def show_save_directory_dialog(self): + filedir = filedialog.askdirectory() + return filedir + + def upload_file(self): + path = self.show_select_file_dialog() + self.controller.on_fileupload(path) pass def get_file(self): - pass + current_item = self.tv_files.focus() + selected_file = self.tv_files.item(current_item)['values'] + if selected_file is None or selected_file == '': + self.show_message("No file selected !", True) + else: + path = self.show_save_directory_dialog() + if path is None or path == '': + self.show_message("No directory selected !", True) + else: + self.controller.on_filedownload(selected_file[0], path) def remove_file(self): - pass + current_item = self.tv_files.focus() + selected_file = self.tv_files.item(current_item)['values'] + if selected_file is None or selected_file == '': + self.show_message("No file selected !", True) + else: + self.controller.on_filedelete(selected_file[0]) - def file_list(self): - pass + def refresh_list(self): + self.controller.on_refreshlist() def about(self): pass def quit(self): self.root.destroy() - self.controller.on_quit() \ No newline at end of file + self.controller.on_quit() + + def remove_all_from_treeview(self): + self.tv_files.delete(*self.tv_files.get_children()) + + def add_file_in_treeview(self, fileinfo): + self.tv_files.insert('', tk.END, values = fileinfo) \ No newline at end of file diff --git a/main.py b/main.py index 9ca96c2..5f2d84b 100644 --- a/main.py +++ b/main.py @@ -2,10 +2,25 @@ from gui.MainWindow import * from controller.MainWindowController import * from controller.AppController import * from model.Protocol import * +import sys +import os.path if __name__ == '__main__': parser = Protocol() app_controller = AppController(parser) - event_handler = MainWindowController(parser, app_controller) - window = MainWindow(event_handler) + window_controller = MainWindowController(parser, app_controller) + window = MainWindow(window_controller) + if os.path.isfile('./ca.crt'): + print("[main] Found ca.crt") + app_controller.ssl_ca_path('./ca.crt') + if len(sys.argv) > 1: + window.host.set(sys.argv[1]) + if len(sys.argv) > 2: + window.port.set(int(sys.argv[2])) + if len(sys.argv) > 3: + window.login.set(sys.argv[3]) + if len(sys.argv) > 4: + window.passw.set(sys.argv[4]) + if len(sys.argv) > 5: + window.tls.set(sys.argv[5] == 'tls') window.start_main_loop() diff --git a/model/IProtocol.py b/model/IProtocol.py deleted file mode 100644 index de63ada..0000000 --- a/model/IProtocol.py +++ /dev/null @@ -1,30 +0,0 @@ -class IProtocol: - def build_SIGNIN(self, login, password): - pass - - def build_SIGNUP(self, login, password): - pass - - def build_FILELIST(self): - pass - - def build_SAVEFILE(self, filename, size): - pass - - def build_GETFILE(self, filename): - pass - - def build_REMOVEFILE(self, filename): - pass - - def build_SIGNOUT(self): - pass - - def parse(self, line, debug_enabled): - pass - - def parse_FILES(self, line): - pass - - def parse_GETFILEOK(self, line): - pass \ No newline at end of file diff --git a/model/Protocol.py b/model/Protocol.py index 48ea503..6f20481 100644 --- a/model/Protocol.py +++ b/model/Protocol.py @@ -1,7 +1,6 @@ -from model.IProtocol import * import re -class Protocol(IProtocol): +class Protocol: RX_DIGIT = r"[0-9]" RX_SIZE = r"(" + RX_DIGIT + "{1,10})" RX_LINE = r"(\\x0d\\x0a){0,1}" diff --git a/net/UnicastConnection.py b/net/UnicastConnection.py new file mode 100644 index 0000000..a11ea9a --- /dev/null +++ b/net/UnicastConnection.py @@ -0,0 +1,111 @@ +import socket +import ssl +import time + +class UnicastConnection: + STRICT_SSL_VALIDATION = True + BUFFER_SIZE=8000 + + def __init__(self, sock=None): + if sock is None: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + else: + self.sock = sock + self.is_connected = False + self.is_tls = False + self.ssock = None + self.ca_path = None + self.ssl_info = None + + def set_ca_path(self, ca_path): + self.ca_path = ca_path + + def connect(self, host, port, tls): + print("[UnicastConnection::connect]") + self.is_tls = tls + if self.is_tls: + print("[UnicastConnection::connect] Attempting SSL/TLS connection") + context = ssl.create_default_context() + if UnicastConnection.STRICT_SSL_VALIDATION: + if self.ca_path: + print("[UnicastConnection::connect] loading CA.CRT") + context.load_verify_locations(self.ca_path) + else: + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + + self.ssock = context.wrap_socket(self.sock, server_hostname=host) + self.ssock.connect((host,port)) + certinfo = self.ssock.getpeercert() + if certinfo: + cert_subject = dict(x[0] for x in certinfo['subject']) + cert_issuer = dict(x[0] for x in certinfo['issuer']) + print(cert_subject['commonName']) + print(cert_issuer['commonName']) + self.ssl_info = f"Certificate Informatinon:\nCN={cert_subject['commonName']}\nIssuer={cert_issuer['commonName']}\nTLS version: {self.ssock.version()}" + self.is_connected = True + else: + print("[UnicastConnection::connect] Attemping unencypted connection") + self.sock.connect((host, port)) + self.is_connected = True + def read_line(self): + print("[UnicastConnection::read_line] begin") + if self.is_tls: + line = self.ssock.recv(1000) + else: + line = self.sock.recv(1000) + message = line.decode('utf-8') + print(f"[UnicastConnection::read_line] Received: '{message}'") + return message + + def send_message(self, message): + print(f"[UnicastConnection::send_message] sending message '{message}'") + if self.is_tls: + self.ssock.sendall(message.encode('utf-8')) + else: + self.sock.sendall(message.encode('utf-8')) + + def send_file(self, filepath): + time.sleep(1) + self.nextrun = True + f = open(filepath, "rb") + while self.nextrun: + l = f.read(self.BUFFER_SIZE) + while (l): + if self.is_tls: + self.ssock.send(l) + else: + self.sock.send(l) + l = f.read(self.BUFFER_SIZE) + if not l: + f.close() + self.nextrun = True + break + + def receive_file(self, filename, savepath, filesize): + self.nextrun = True + remaining = filesize + with open(f"{savepath}/{filename}", 'wb') as f: + while remaining > 0 and self.nextrun: + if self.is_tls: + data = self.ssock.recv(self.BUFFER_SIZE) + else: + data = self.sock.recv(self.BUFFER_SIZE) + if not data: + f.close() + self.nextrun = False + break + f.write(data) + remaining = remaining - len(data) + print(f"Received = {len(data)} - Remaining = {remaining} / {filesize}") + time.sleep(1) + + + + def get_certificate_info(self): + return self.ssl_info + + def close(self): + self.sock.close() + +