znote 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,142 @@
1
+ import __main__
2
+ import impmagic
3
+
4
+
5
+ class Settings():
6
+ @impmagic.loader(
7
+ {'module': '__main__'},
8
+ {'module': 'os'},
9
+ {'module': 'pyotp'},
10
+ {'module': 'app.display','submodule': ['logs']},
11
+ {'module': 'Crypto','submodule': ['Random']},
12
+ {'module': 'Crypto.PublicKey','submodule': ['RSA']},
13
+ {'module': 'core.config','submodule': ['Config']},
14
+ {'module': 'core.client.token','submodule': ['get_token']}
15
+ )
16
+ def __init__(self, config_file):
17
+ self.config_dir = os.path.abspath(os.path.expanduser('~/.config/znote'))
18
+
19
+ self.token_file = os.path.join(self.config_dir, ".token")
20
+
21
+ if not config_file:
22
+ config_file = os.path.join(self.config_dir, "config.yml")
23
+
24
+ self.config = Config(f"{config_file}")
25
+
26
+ self.verbose = self.config.get('client.verbose', default=False, auto_set=True)
27
+
28
+ self.host = self.config.get('client.host', default="127.0.0.1", auto_set=True)
29
+ self.port = self.config.get('client.port', default=40017, auto_set=True)
30
+ self.key_size = self.config.get('client.key_size', default=2048, auto_set=True)
31
+ self.key_otp = self.config.get('client.key_otp', default='JBSWY3DPEHPK3PXP', auto_set=True)
32
+ self.totp = pyotp.TOTP(self.key_otp)
33
+
34
+ self.PubServ = None
35
+ self.keyC = None
36
+
37
+ self.token = get_token(self.token_file)
38
+ self.pubserv_set = None
39
+ self.authenticated = False
40
+
41
+
42
+ @impmagic.loader(
43
+ {'module': '__main__'},
44
+ {'module': 'os'},
45
+ {'module': 'hashlib'},
46
+ {'module': 'Crypto.PublicKey', 'submodule': ['RSA']},
47
+ {'module': 'Crypto', 'submodule': ['Random']},
48
+ {'module': 'app.display', 'submodule': ['logs']},
49
+ )
50
+ def load_key(self):
51
+ privkey_path = os.path.join(self.config_dir, "client.key")
52
+ pubkey_path = os.path.join(self.config_dir, "client.pem")
53
+
54
+ if not os.path.exists(privkey_path) or not os.path.exists(pubkey_path):
55
+ try:
56
+ #Génération de la paire de clé pour les échanges avec le client
57
+ logs("Création de la paire de clé", "info")
58
+ rng = Random.new().read
59
+ rsa_key = RSA.generate(self.key_size, rng)
60
+ rsa_priv = rsa_key.exportKey("PEM")
61
+
62
+ self.PrivKey = rsa_key.exportKey('PEM', pkcs=1)
63
+
64
+ with open(privkey_path, "wb") as f:
65
+ f.write(self.PrivKey)
66
+
67
+ key_pub = rsa_key.publickey()
68
+ self.PubKey = key_pub.exportKey("PEM")
69
+
70
+ with open(pubkey_path, "wb") as f:
71
+ f.write(self.PubKey)
72
+
73
+
74
+ logs("Paire de clé créée", "success")
75
+
76
+ except Exception as err:
77
+ logs(f"Erreur lors de la création de la paire de clé: {err}", "error")
78
+
79
+ else:
80
+ try:
81
+ logs("Chargement de la paire de clé", "info")
82
+
83
+ with open(privkey_path, "rb") as f:
84
+ self.PrivKey = f.read()
85
+
86
+ with open(pubkey_path, "rb") as f:
87
+ self.PubKey = f.read()
88
+
89
+ logs("Paire de clé chargée", "success")
90
+
91
+ except Exception as err:
92
+ logs(f"Erreur lors du chargement de la paire de clé: {err}", "error")
93
+
94
+ self.key = RSA.importKey(self.PrivKey)
95
+
96
+ #Calcul du hash de la clé du client
97
+ self.clienthash = hashlib.sha256(self.PubKey).hexdigest()
98
+
99
+
100
+ #Chargement de la clé serveur stocké
101
+ serverpub_path = os.path.join(self.config_dir, "server.pub")
102
+ if os.path.exists(serverpub_path):
103
+ try:
104
+ logs("Chargement de la clé du serveur", "info")
105
+
106
+ with open(serverpub_path, "rb") as f:
107
+ content = f.read()
108
+
109
+ self.pubserv_set = True
110
+ self.keyC = RSA.importKey(content)
111
+ #Calcul du hash de la clé du serveur
112
+ self.serverhash = hashlib.sha256(content).hexdigest()
113
+
114
+ except Exception as err:
115
+ logs(f"Erreur lors du chargement de la clé du serveur: {err}", "error")
116
+
117
+
118
+ @impmagic.loader(
119
+ {'module': 'zpp_color', 'submodule': ['fg', 'attr']},
120
+ )
121
+ def init_settings(self):
122
+ host = input(f"{fg('dark_gray')}host [{fg('cyan')}{self.host}{fg('dark_gray')}]: ")
123
+ port = input(f"{fg('dark_gray')}port [{fg('cyan')}{self.port}{fg('dark_gray')}]: ")
124
+ key_size = input(f"{fg('dark_gray')}key_size [{fg('cyan')}{self.key_size}{fg('dark_gray')}]: ")
125
+ key_otp = input(f"{fg('dark_gray')}key_otp [{fg('cyan')}{self.key_otp}{fg('dark_gray')}]: ")
126
+ verbose = input(f"{fg('dark_gray')}verbose [{fg('cyan')}{self.verbose}{fg('dark_gray')}]: ")
127
+
128
+ if host:
129
+ self.config.set('server.host', host)
130
+
131
+ if port and isinstance(port, int):
132
+ self.config.set('server.port', int(port))
133
+
134
+ if key_size and isinstance(key_size, int):
135
+ self.config.set('server.key_size', int(key_size))
136
+
137
+ if key_otp:
138
+ self.config.set('server.key_otp', key_otp)
139
+
140
+ if verbose:
141
+ self.config.set('server.verbose', verbose)
142
+
@@ -0,0 +1,17 @@
1
+ import __main__
2
+ import impmagic
3
+
4
+ def set_token(token):
5
+ with open(__main__.settings.token_file, 'w') as f:
6
+ f.write(token)
7
+
8
+
9
+ @impmagic.loader(
10
+ {'module': 'os'},
11
+ )
12
+ def get_token(token_file):
13
+ if os.path.exists(token_file):
14
+ with open(token_file, 'r') as f:
15
+ return f.read()
16
+ else:
17
+ return False
znote/core/config.py ADDED
@@ -0,0 +1,73 @@
1
+ import os
2
+ import yaml
3
+
4
+ class Config:
5
+ def __init__(self, path):
6
+ self.path = os.path.expanduser(path)
7
+ self.data = {}
8
+ self._load()
9
+
10
+ def _load(self):
11
+ if os.path.exists(self.path):
12
+ with open(self.path, "r", encoding="utf-8") as f:
13
+ self.data = yaml.safe_load(f) or {}
14
+ else:
15
+ self.data = {}
16
+
17
+ def _save(self):
18
+ directory = os.path.dirname(self.path)
19
+ if directory:
20
+ os.makedirs(directory, exist_ok=True)
21
+
22
+ with open(self.path, "w", encoding="utf-8") as f:
23
+ yaml.safe_dump(self.data, f, default_flow_style=False, sort_keys=False)
24
+
25
+ def _get_recursive(self, data, keys):
26
+ if len(keys) == 1:
27
+ return data.get(keys[0])
28
+ if keys[0] in data:
29
+ return self._get_recursive(data[keys[0]], keys[1:])
30
+ return None
31
+
32
+ def _set_recursive(self, data, keys, value):
33
+ if len(keys) == 1:
34
+ data[keys[0]] = value
35
+ return data
36
+ if keys[0] not in data or not isinstance(data[keys[0]], dict):
37
+ data[keys[0]] = {}
38
+ data[keys[0]] = self._set_recursive(data[keys[0]], keys[1:], value)
39
+ return data
40
+
41
+ def get(self, dotted_key, default=None, auto_set=False):
42
+ keys = dotted_key.split(".")
43
+ result = self._get_recursive(self.data, keys)
44
+
45
+ if result is not None:
46
+ return result
47
+
48
+ if auto_set:
49
+ self.set(dotted_key, default)
50
+ return default
51
+
52
+ return default
53
+
54
+ def set(self, dotted_key, value):
55
+ keys = dotted_key.split(".")
56
+ self.data = self._set_recursive(self.data, keys, value)
57
+ self._save()
58
+
59
+ def delete(self, dotted_key):
60
+ keys = dotted_key.split(".")
61
+ parent = self._get_recursive(self.data, keys[:-1])
62
+ if parent and keys[-1] in parent:
63
+ del parent[keys[-1]]
64
+ # Nettoie récursivement si parent vide :
65
+ while keys[:-1]:
66
+ keys = keys[:-1]
67
+ parent = self._get_recursive(self.data, keys[:-1])
68
+ if parent and not parent.get(keys[-1]):
69
+ del parent[keys[-1]]
70
+ self._save()
71
+
72
+ def has(self, dotted_key):
73
+ return self.get(dotted_key) is not None
znote/core/editor.py ADDED
@@ -0,0 +1,132 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import sys
4
+ import asyncio
5
+ import os # Import os for checking file existence
6
+
7
+ from prompt_toolkit import Application
8
+ from prompt_toolkit.layout import Layout, HSplit, Window
9
+ from prompt_toolkit.widgets import TextArea, Label
10
+ from prompt_toolkit.buffer import Buffer
11
+ from prompt_toolkit.key_binding import KeyBindings
12
+ from prompt_toolkit.filters import Condition
13
+
14
+ from prompt_toolkit.layout.containers import ConditionalContainer
15
+ from prompt_toolkit.shortcuts import message_dialog
16
+ from prompt_toolkit.layout.controls import BufferControl
17
+
18
+ from zpp_ManagedFile import ManagedFile
19
+
20
+ class Editor:
21
+ def __init__(self, filename, file):
22
+ # --- État de l'application ---
23
+ self.file = file # Default filename
24
+ self.filename = filename
25
+
26
+ # --- UI Elements ---
27
+ self.text_area = TextArea(
28
+ multiline=True,
29
+ wrap_lines=True,
30
+ scrollbar=True,
31
+ line_numbers=True,
32
+ )
33
+ # Initial status bar text will be set after loading
34
+ self.status_bar = Label(text="") # Start empty, will be set by load_file
35
+ # --- Layout ---
36
+ self.layout = Layout(
37
+ HSplit([
38
+ self.text_area,
39
+ self.status_bar,
40
+ ]),
41
+ )
42
+
43
+ # --- Key Bindings ---
44
+ self.kb = KeyBindings()
45
+
46
+ @self.kb.add("c-q")
47
+ def _(event):
48
+ """ Global shortcut to quit """
49
+ asyncio.get_event_loop().create_task(self.quit())
50
+
51
+
52
+ @self.kb.add("c-s") # Active only in editor mode
53
+ def _(event):
54
+ """ Ctrl+S shortcut to save """
55
+ # Schedule save_file as it's an async method
56
+ asyncio.get_event_loop().create_task(self.save_file())
57
+
58
+ # --- Application ---
59
+ self.app = Application(
60
+ layout=self.layout,
61
+ key_bindings=self.kb,
62
+ full_screen=True,
63
+ # style...
64
+ )
65
+
66
+ # --- Initial Actions ---
67
+ self.load_file(self.file) # Load file content on startup
68
+ self.app.layout.focus(self.text_area) # Set initial focus
69
+
70
+ # --- File Loading ---
71
+ def load_file(self, filename):
72
+ """ Loads content from a file into the text area on startup """
73
+ try:
74
+ if isinstance(self.file,ManagedFile) and hasattr(self.file,'read'):
75
+ self.file.seek(0)
76
+ self.text_area.text = self.file.read()
77
+ self.status_bar.text = f"Fichier chargé."
78
+ else:
79
+ if os.path.exists(filename):
80
+ with open(filename, "r", encoding='utf-8') as f:
81
+ content = f.read()
82
+ self.text_area.text = content
83
+ self.status_bar.text = f"Fichier '{filename}' chargé."
84
+ else:
85
+ self.status_bar.text = f"Nouveau fichier '{filename}'."
86
+ except Exception as e:
87
+ # Basic error handling for loading
88
+ self.status_bar.text = f"Erreur de chargement : {e}"
89
+ # Status messages set here are static until the first save/reset
90
+
91
+
92
+ # --- File Saving ---
93
+ # save_file must be async because it's awaited in save_and_quit
94
+ async def save_file(self):
95
+ """ Saves content to the default file """
96
+ filename = self.file # Use attribute
97
+ try:
98
+ if isinstance(self.file,ManagedFile) and hasattr(self.file,'read'):
99
+ self.file.seek(0)
100
+ self.file.truncate(0)
101
+ self.file.write(self.text_area.text)
102
+ else:
103
+ # Synchronous file writing
104
+ with open(self.file, "w", encoding='utf-8') as f:
105
+ f.write(self.text_area.text)
106
+
107
+ # Update status bar on success and schedule reset
108
+ self.status_bar.text = f"Fichier '{filename}' sauvegardé."
109
+ return True
110
+ except Exception as e:
111
+ # Update status bar on error and schedule reset
112
+ self.status_bar.text = f"Erreur de sauvegarde : {e}"
113
+ return False
114
+
115
+ # --- Quit ---
116
+ # async because it awaits the message_dialog
117
+ async def quit(self, event=None):
118
+ """ Asks for confirmation and quits the application """
119
+ # Use the message_dialog, which is awaited
120
+ self.app.exit()
121
+
122
+ # Entry point
123
+ if __name__ == '__main__':
124
+ try:
125
+ file = ManagedFile(mode='a', typefile='stringio')
126
+ editor = Editor(file)
127
+ editor.app.run()
128
+ file.seek(0)
129
+ print(file.read())
130
+ except Exception as e:
131
+ print(f"An unexpected error occurred: {e}")
132
+ sys.exit(1)
znote/core/gen.py ADDED
@@ -0,0 +1,21 @@
1
+ import impmagic
2
+
3
+
4
+ @impmagic.loader(
5
+ {'module': 'string'},
6
+ {'module': 'secrets'}
7
+ )
8
+ def gen_password(size):
9
+ alphabet = string.ascii_letters + string.digits + string.punctuation
10
+ pwd = ''
11
+ for i in range(size):
12
+ pwd += ''.join(secrets.choice(alphabet))
13
+
14
+ return pwd.encode()
15
+
16
+
17
+ @impmagic.loader(
18
+ {'module': 'uuid','submodule': ['uuid4']}
19
+ )
20
+ def UUID():
21
+ return str(uuid4()).replace("-","")
znote/core/pack.py ADDED
@@ -0,0 +1,72 @@
1
+ import impmagic
2
+
3
+
4
+ #Création de la signature d'une donnée
5
+ @impmagic.loader(
6
+ {'module': 'Crypto.Hash', 'submodule': ['SHA256']},
7
+ {'module': 'Crypto.Signature','submodule': ['PKCS1_v1_5']}
8
+ )
9
+ def sign(data, privKey):
10
+ if not isinstance(data, bytes):
11
+ data = data.encode()
12
+
13
+ digest = SHA256.new(data)
14
+
15
+ signature = PKCS1_v1_5.new(privKey).sign(digest)
16
+
17
+ return signature
18
+
19
+
20
+ #Vérification de la signature d'une donnée
21
+ @impmagic.loader(
22
+ {'module': 'Crypto.Hash', 'submodule': ['SHA256']},
23
+ {'module': 'Crypto.Signature','submodule': ['PKCS1_v1_5']}
24
+ )
25
+ def verify(data, signature, pubKey):
26
+ if not isinstance(data, bytes):
27
+ data = data.encode()
28
+
29
+ digest = SHA256.new(data)
30
+
31
+ verifier = PKCS1_v1_5.new(pubKey)
32
+ return verifier.verify(digest, signature)
33
+
34
+
35
+ #Création du package à envoyer
36
+ @impmagic.loader(
37
+ {'module': 'dill', 'submodule': ['dumps']}
38
+ )
39
+ def pack(data, privKey):
40
+ #Transformation d'un objet en binaire
41
+ data = dumps(data)
42
+
43
+ #Création de la signature de ce binaire
44
+ signature = sign(data, privKey)
45
+
46
+ return signature+data
47
+
48
+
49
+ #Transformation du package en objet
50
+ @impmagic.loader(
51
+ {'module': 'dill', 'submodule': ['loads']}
52
+ )
53
+ def unpack(data, pubKey):
54
+ if len(data)>256:
55
+ #Récupération de la signature dans le bloc de données
56
+ signature, data = data[0:256], data[256:]
57
+
58
+ #Vérifie si la signature est valide et provient du serveur
59
+ if verify(data, signature, pubKey):
60
+ try:
61
+ #Si la signature est valide, conversion du binaire en objet
62
+ return loads(data)
63
+ except:
64
+ print("Pickle invalid")
65
+ return
66
+ else:
67
+ print("Signature check: Bad return")
68
+ return
69
+ else:
70
+ print("Pickle invalid")
71
+ return
72
+
@@ -0,0 +1,20 @@
1
+ import __main__
2
+ import impmagic
3
+
4
+ @impmagic.loader(
5
+ {'module': 'pony.orm', 'submodule': ['db_session', 'select']}
6
+ )
7
+ def get_note(user_id, workspace=None):
8
+ notes_list = []
9
+
10
+ with db_session:
11
+ query = __main__.settings.orm.Note.select().where("n.creator_id == user_id", {"user_id": user_id})
12
+
13
+ if workspace:
14
+ query = query.where("n.workspace == workspace", {"workspace": workspace})
15
+
16
+ result = query[:]
17
+
18
+ return result
19
+
20
+ return notes_list
@@ -0,0 +1,148 @@
1
+ import impmagic
2
+
3
+ class Settings:
4
+ @impmagic.loader(
5
+ {'module': '__main__'},
6
+ {'module': 're'},
7
+ {'module': 'os'},
8
+ {'module': 'pyotp'},
9
+ {'module': 'app.display', 'submodule': ['logs']},
10
+ {'module': 'core.config', 'submodule': ['Config']},
11
+ )
12
+ def __init__(self, config_file=None):
13
+ self.config_dir = os.path.abspath(os.path.expanduser('~/.config/znote'))
14
+
15
+ if not os.path.exists(self.config_dir):
16
+ os.makedirs(self.config_dir, exist_ok=True)
17
+
18
+ if not config_file:
19
+ config_file = os.path.join(self.config_dir, "config.yml")
20
+ #config_file = "config.toml"
21
+
22
+ self.config = Config(config_file)
23
+
24
+ self.verbose = self.config.get('server.verbose', default=True, auto_set=True)
25
+
26
+ self.host = self.config.get('server.host', default="127.0.0.1", auto_set=True)
27
+ self.port = self.config.get('server.port', default=40017, auto_set=True)
28
+ self.key_size = self.config.get('server.key_size', default=2048, auto_set=True)
29
+ self.key_otp = self.config.get('server.key_otp', default='JBSWY3DPEHPK3PXP', auto_set=True)
30
+
31
+ #Initialisation de l'OTP pour la connexion initiale
32
+ if not re.match(r'^[A-Z0-9]{16}$', self.key_otp):
33
+ print("Clé OTP invalide. Passage sur la clé par défaut", "warning")
34
+ self.key_otp = 'JBSWY3DPEHPK3PXP'
35
+ self.totp = pyotp.TOTP(self.key_otp)
36
+
37
+ self.data_dir = self.config.get('server.data_dir', default=None, auto_set=False)
38
+
39
+ if not self.data_dir:
40
+ self.data_dir = os.path.join(self.config_dir, "znote.db")
41
+
42
+
43
+ @impmagic.loader(
44
+ {'module': '__main__'},
45
+ {'module': 'os'},
46
+ {'module': 'hashlib'},
47
+ {'module': 'Crypto.PublicKey', 'submodule': ['RSA']},
48
+ {'module': 'Crypto', 'submodule': ['Random']},
49
+ {'module': 'app.display', 'submodule': ['logs']},
50
+ )
51
+ def load_key(self):
52
+ privkey_path = os.path.join(self.config_dir, "server.key")
53
+ pubkey_path = os.path.join(self.config_dir, "server.pem")
54
+
55
+ if not os.path.exists(privkey_path) or not os.path.exists(pubkey_path):
56
+ try:
57
+ #Génération de la paire de clé pour les échanges avec le client
58
+ logs("Création de la paire de clé", "info")
59
+ rng = Random.new().read
60
+ rsa_key = RSA.generate(self.key_size, rng)
61
+ rsa_priv = rsa_key.exportKey("PEM")
62
+
63
+ self.PrivKey = rsa_key.exportKey('PEM', pkcs=1)
64
+
65
+ with open(privkey_path, "wb") as f:
66
+ f.write(self.PrivKey)
67
+
68
+ key_pub = rsa_key.publickey()
69
+ self.PubKey = key_pub.exportKey("PEM")
70
+
71
+ with open(pubkey_path, "wb") as f:
72
+ f.write(self.PubKey)
73
+
74
+ logs("Paire de clé créée", "success")
75
+
76
+ except Exception as err:
77
+ logs(f"Erreur lors de la création de la paire de clé: {err}", "error")
78
+ exit()
79
+
80
+ else:
81
+ try:
82
+ logs("Chargement de la paire de clé", "info")
83
+
84
+ with open(privkey_path, "rb") as f:
85
+ self.PrivKey = f.read()
86
+
87
+ with open(pubkey_path, "rb") as f:
88
+ self.PubKey = f.read()
89
+
90
+ logs("Paire de clé chargée", "success")
91
+
92
+ except Exception as err:
93
+ logs(f"Erreur lors du chargement de la paire de clé: {err}", "error")
94
+ exit()
95
+
96
+ #Calcul du hash de la clé du client
97
+ self.serverhash = hashlib.sha256(self.PubKey).hexdigest()
98
+
99
+
100
+ @impmagic.loader(
101
+ {'module': '__main__'},
102
+ {'module': 'os'},
103
+ {'module': 'model', 'submodule': ['models']},
104
+ {'module': 'pony.orm','submodule': ['Database']},
105
+ {'module': 'app.display', 'submodule': ['logs']},
106
+ )
107
+ def load_db(self):
108
+ try:
109
+ self.orm = Database()
110
+ models.Table(self.orm)
111
+ self.orm.bind(provider='sqlite', filename=self.data_dir, create_db=True)
112
+ self.orm.generate_mapping(create_tables=True)
113
+
114
+ logs("Base de données initialisée", "success")
115
+
116
+ except Exception as err:
117
+ logs(f"Erreur lors de l'initialisation de la base de données: {err}", "error")
118
+ exit()
119
+
120
+
121
+ @impmagic.loader(
122
+ {'module': 'zpp_color', 'submodule': ['fg', 'attr']},
123
+ )
124
+ def init_settings(self):
125
+ host = input(f"{fg('dark_gray')}host [{fg('cyan')}{self.host}{fg('dark_gray')}]: ")
126
+ port = input(f"{fg('dark_gray')}port [{fg('cyan')}{self.port}{fg('dark_gray')}]: ")
127
+ key_size = input(f"{fg('dark_gray')}key_size [{fg('cyan')}{self.key_size}{fg('dark_gray')}]: ")
128
+ key_otp = input(f"{fg('dark_gray')}key_otp [{fg('cyan')}{self.key_otp}{fg('dark_gray')}]: ")
129
+ data_dir = input(f"{fg('dark_gray')}data_dir [{fg('cyan')}{self.data_dir}{fg('dark_gray')}]: ")
130
+ verbose = input(f"{fg('dark_gray')}verbose [{fg('cyan')}{self.verbose}{fg('dark_gray')}]: ")
131
+
132
+ if host:
133
+ self.config.set('server.host', host)
134
+
135
+ if port and isinstance(port, int):
136
+ self.config.set('server.port', int(port))
137
+
138
+ if key_size and isinstance(key_size, int):
139
+ self.config.set('server.key_size', int(key_size))
140
+
141
+ if key_otp:
142
+ self.config.set('server.key_otp', key_otp)
143
+
144
+ if data_dir:
145
+ self.config.set('server.data_dir', data_dir)
146
+
147
+ if verbose:
148
+ self.config.set('server.verbose', verbose)