dimp 2.3.1__tar.gz → 2.3.3__tar.gz
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.
- {dimp-2.3.1 → dimp-2.3.3}/PKG-INFO +68 -47
- {dimp-2.3.1 → dimp-2.3.3}/README.md +67 -46
- {dimp-2.3.1 → dimp-2.3.3}/dimp/__init__.py +11 -9
- {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/keys.py +45 -21
- {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/pnf.py +12 -9
- {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/ted.py +16 -16
- {dimp-2.3.1 → dimp-2.3.3}/dimp/dkd/__init__.py +26 -22
- {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/docs.py +19 -15
- {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/document.py +8 -12
- {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/meta.py +7 -7
- {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/base.py +2 -2
- {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/envelope.py +19 -16
- {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/instant.py +8 -6
- {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/reliable.py +2 -2
- {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/secure.py +15 -9
- {dimp-2.3.1 → dimp-2.3.3}/dimp/plugins/__init__.py +2 -2
- {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/__init__.py +10 -8
- {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/base.py +141 -16
- {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/commands.py +127 -31
- {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/contents.py +170 -136
- dimp-2.3.3/dimp/protocol/customized.py +113 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/files.py +150 -9
- dimp-2.3.3/dimp/protocol/forward.py +253 -0
- dimp-2.3.3/dimp/protocol/groups.py +498 -0
- {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/money.py +93 -27
- {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/quote.py +51 -34
- {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/receipt.py +83 -11
- {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/PKG-INFO +68 -47
- {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/SOURCES.txt +3 -10
- dimp-2.3.3/dimp.egg-info/requires.txt +2 -0
- {dimp-2.3.1 → dimp-2.3.3}/setup.py +3 -3
- dimp-2.3.1/dimp/dkd/files.py +0 -181
- dimp-2.3.1/dimp/dkd/group_admins.py +0 -142
- dimp-2.3.1/dimp/dkd/groups.py +0 -234
- dimp-2.3.1/dimp/dkd/quote.py +0 -145
- dimp-2.3.1/dimp/plugins/cmd.py +0 -79
- dimp-2.3.1/dimp/protocol/commands.py +0 -247
- dimp-2.3.1/dimp/protocol/contents.py +0 -279
- dimp-2.3.1/dimp/protocol/groups.py +0 -286
- dimp-2.3.1/dimp/protocol/money.py +0 -124
- dimp-2.3.1/dimp/protocol/receipt.py +0 -130
- dimp-2.3.1/dimp.egg-info/requires.txt +0 -2
- {dimp-2.3.1 → dimp-2.3.3}/LICENSE +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/__init__.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/algorithms.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/__init__.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/__init__.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/docs.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/helpers.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/types.py +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/dependency_links.txt +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/top_level.txt +0 -0
- {dimp-2.3.1 → dimp-2.3.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dimp
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: Decentralized Instant Messaging Protocol
|
|
5
5
|
Home-page: https://github.com/dimchat/core-py
|
|
6
6
|
Author: Albert Moky
|
|
@@ -45,8 +45,9 @@ License-File: LICENSE
|
|
|
45
45
|
3. (S-C) handshake success
|
|
46
46
|
|
|
47
47
|
```python
|
|
48
|
+
from abc import ABC, abstractmethod
|
|
48
49
|
from enum import IntEnum
|
|
49
|
-
from typing import Optional,
|
|
50
|
+
from typing import Optional, Dict
|
|
50
51
|
|
|
51
52
|
from dimp import Command, BaseCommand
|
|
52
53
|
|
|
@@ -58,7 +59,20 @@ class HandshakeState(IntEnum):
|
|
|
58
59
|
Success = 3 # S -> C, handshake accepted
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
def handshake_state(title: str, session: str = None) -> HandshakeState:
|
|
63
|
+
# Server -> Client
|
|
64
|
+
if title == 'DIM!': # or title == 'OK!':
|
|
65
|
+
return HandshakeState.Success
|
|
66
|
+
if title == 'DIM?':
|
|
67
|
+
return HandshakeState.Again
|
|
68
|
+
# Client -> Server: "Hello world!"
|
|
69
|
+
if session is None or len(session) == 0:
|
|
70
|
+
return HandshakeState.Start
|
|
71
|
+
else:
|
|
72
|
+
return HandshakeState.Restart
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class HandshakeCommand(Command, ABC):
|
|
62
76
|
"""
|
|
63
77
|
Handshake Command
|
|
64
78
|
~~~~~~~~~~~~~~~~~
|
|
@@ -74,37 +88,24 @@ class HandshakeCommand(BaseCommand):
|
|
|
74
88
|
"""
|
|
75
89
|
HANDSHAKE = 'handshake'
|
|
76
90
|
|
|
77
|
-
def __init__(self, content: Dict[str, Any] = None, title: str = None, session: str = None):
|
|
78
|
-
if content is None:
|
|
79
|
-
# 1. new command with title & session key
|
|
80
|
-
assert title is not None, 'handshake command error: %s' % session
|
|
81
|
-
cmd = self.HANDSHAKE
|
|
82
|
-
super().__init__(cmd=cmd)
|
|
83
|
-
self['title'] = title
|
|
84
|
-
self['message'] = title # TODO: remove after all clients upgraded
|
|
85
|
-
if session is not None:
|
|
86
|
-
self['session'] = session
|
|
87
|
-
else:
|
|
88
|
-
# 2. command info from network
|
|
89
|
-
assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
|
|
90
|
-
super().__init__(content)
|
|
91
|
-
|
|
92
91
|
@property
|
|
92
|
+
@abstractmethod
|
|
93
93
|
def title(self) -> str:
|
|
94
|
-
|
|
95
|
-
text = self.get_str(key='title', default=None)
|
|
96
|
-
if text is None:
|
|
97
|
-
# compatible with v1.0
|
|
98
|
-
text = self.get_str(key='message', default='')
|
|
99
|
-
return text
|
|
94
|
+
raise NotImplemented
|
|
100
95
|
|
|
101
96
|
@property
|
|
97
|
+
@abstractmethod
|
|
102
98
|
def session(self) -> Optional[str]:
|
|
103
|
-
|
|
99
|
+
raise NotImplemented
|
|
104
100
|
|
|
105
101
|
@property
|
|
102
|
+
@abstractmethod
|
|
106
103
|
def state(self) -> HandshakeState:
|
|
107
|
-
|
|
104
|
+
raise NotImplemented
|
|
105
|
+
|
|
106
|
+
#
|
|
107
|
+
# Factories
|
|
108
|
+
#
|
|
108
109
|
|
|
109
110
|
@classmethod
|
|
110
111
|
def offer(cls, session: str = None) -> Command:
|
|
@@ -114,7 +115,7 @@ class HandshakeCommand(BaseCommand):
|
|
|
114
115
|
:param session: Old session key
|
|
115
116
|
:return: HandshakeCommand object
|
|
116
117
|
"""
|
|
117
|
-
return
|
|
118
|
+
return BaseHandshakeCommand(title='Hello world!', session=session)
|
|
118
119
|
|
|
119
120
|
@classmethod
|
|
120
121
|
def ask(cls, session: str) -> Command:
|
|
@@ -124,7 +125,7 @@ class HandshakeCommand(BaseCommand):
|
|
|
124
125
|
:param session: New session key
|
|
125
126
|
:return: HandshakeCommand object
|
|
126
127
|
"""
|
|
127
|
-
return
|
|
128
|
+
return BaseHandshakeCommand(title='DIM?', session=session)
|
|
128
129
|
|
|
129
130
|
@classmethod
|
|
130
131
|
def accepted(cls, session: str = None) -> Command:
|
|
@@ -133,7 +134,7 @@ class HandshakeCommand(BaseCommand):
|
|
|
133
134
|
|
|
134
135
|
:return: HandshakeCommand object
|
|
135
136
|
"""
|
|
136
|
-
return
|
|
137
|
+
return BaseHandshakeCommand(title='DIM!', session=session)
|
|
137
138
|
|
|
138
139
|
start = offer # (1. C->S) first handshake, without session
|
|
139
140
|
again = ask # (2. S->C) ask client to handshake with new session key
|
|
@@ -141,17 +142,34 @@ class HandshakeCommand(BaseCommand):
|
|
|
141
142
|
success = accepted # (4. S->C) notice the client that handshake accepted
|
|
142
143
|
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
class BaseHandshakeCommand(BaseCommand, HandshakeCommand):
|
|
146
|
+
|
|
147
|
+
def __init__(self, content: Dict = None, title: str = None, session: str = None):
|
|
148
|
+
if content is None:
|
|
149
|
+
# 1. new command with title & session key
|
|
150
|
+
assert title is not None, 'handshake command error: %s' % session
|
|
151
|
+
cmd = self.HANDSHAKE
|
|
152
|
+
super().__init__(cmd=cmd)
|
|
153
|
+
self['title'] = title
|
|
154
|
+
self['message'] = title # TODO: remove after all clients upgraded
|
|
155
|
+
if session is not None:
|
|
156
|
+
self['session'] = session
|
|
157
|
+
else:
|
|
158
|
+
# 2. command info from network
|
|
159
|
+
assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
|
|
160
|
+
super().__init__(content)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def title(self) -> str:
|
|
164
|
+
return self.get_str(key='title', default='')
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def session(self) -> Optional[str]:
|
|
168
|
+
return self.get_str(key='session')
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def state(self) -> HandshakeState:
|
|
172
|
+
return handshake_state(title=self.title, session=self.session)
|
|
155
173
|
```
|
|
156
174
|
|
|
157
175
|
### extends Content
|
|
@@ -165,13 +183,13 @@ from dimp import Content
|
|
|
165
183
|
from dimp import BaseContent
|
|
166
184
|
|
|
167
185
|
|
|
168
|
-
class
|
|
186
|
+
class AppContent(Content, ABC):
|
|
169
187
|
"""
|
|
170
188
|
Application Customized message
|
|
171
189
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
172
190
|
|
|
173
191
|
data format: {
|
|
174
|
-
type :
|
|
192
|
+
type : 0xA0,
|
|
175
193
|
sn : 123,
|
|
176
194
|
|
|
177
195
|
app : "{APP_ID}", // application (e.g.: "chat.dim.sechat")
|
|
@@ -199,22 +217,25 @@ class CustomizedContent(Content, ABC):
|
|
|
199
217
|
""" Action Name """
|
|
200
218
|
raise NotImplemented
|
|
201
219
|
|
|
220
|
+
#
|
|
221
|
+
# Factory
|
|
222
|
+
#
|
|
202
223
|
@classmethod
|
|
203
224
|
def create(cls, app: str, mod: str, act: str):
|
|
204
|
-
return
|
|
225
|
+
return ApplicationContent(app=app, mod=mod, act=act)
|
|
205
226
|
|
|
206
227
|
|
|
207
|
-
class
|
|
228
|
+
class ApplicationContent(BaseContent, AppContent):
|
|
208
229
|
|
|
209
230
|
def __init__(self, content: Dict[str, Any] = None,
|
|
210
|
-
msg_type:
|
|
231
|
+
msg_type: str = None,
|
|
211
232
|
app: str = None, mod: str = None, act: str = None):
|
|
212
233
|
if content is None:
|
|
213
234
|
# 1. new content with type, application, module & action
|
|
214
235
|
assert app is not None and mod is not None and act is not None, \
|
|
215
236
|
'customized content error: %s, %s, %s, %s' % (msg_type, app, mod, act)
|
|
216
237
|
if msg_type is None:
|
|
217
|
-
msg_type = ContentType.
|
|
238
|
+
msg_type = ContentType.APPLICATION
|
|
218
239
|
super().__init__(None, msg_type)
|
|
219
240
|
self['app'] = app
|
|
220
241
|
self['mod'] = mod
|
|
@@ -244,5 +265,5 @@ class AppCustomizedContent(BaseContent, CustomizedContent):
|
|
|
244
265
|
|
|
245
266
|
----
|
|
246
267
|
|
|
247
|
-
Copyright © 2018 Albert Moky
|
|
268
|
+
Copyright © 2018-2025 Albert Moky
|
|
248
269
|
[](https://github.com/moky?tab=followers)
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
3. (S-C) handshake success
|
|
32
32
|
|
|
33
33
|
```python
|
|
34
|
+
from abc import ABC, abstractmethod
|
|
34
35
|
from enum import IntEnum
|
|
35
|
-
from typing import Optional,
|
|
36
|
+
from typing import Optional, Dict
|
|
36
37
|
|
|
37
38
|
from dimp import Command, BaseCommand
|
|
38
39
|
|
|
@@ -44,7 +45,20 @@ class HandshakeState(IntEnum):
|
|
|
44
45
|
Success = 3 # S -> C, handshake accepted
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
def handshake_state(title: str, session: str = None) -> HandshakeState:
|
|
49
|
+
# Server -> Client
|
|
50
|
+
if title == 'DIM!': # or title == 'OK!':
|
|
51
|
+
return HandshakeState.Success
|
|
52
|
+
if title == 'DIM?':
|
|
53
|
+
return HandshakeState.Again
|
|
54
|
+
# Client -> Server: "Hello world!"
|
|
55
|
+
if session is None or len(session) == 0:
|
|
56
|
+
return HandshakeState.Start
|
|
57
|
+
else:
|
|
58
|
+
return HandshakeState.Restart
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class HandshakeCommand(Command, ABC):
|
|
48
62
|
"""
|
|
49
63
|
Handshake Command
|
|
50
64
|
~~~~~~~~~~~~~~~~~
|
|
@@ -60,37 +74,24 @@ class HandshakeCommand(BaseCommand):
|
|
|
60
74
|
"""
|
|
61
75
|
HANDSHAKE = 'handshake'
|
|
62
76
|
|
|
63
|
-
def __init__(self, content: Dict[str, Any] = None, title: str = None, session: str = None):
|
|
64
|
-
if content is None:
|
|
65
|
-
# 1. new command with title & session key
|
|
66
|
-
assert title is not None, 'handshake command error: %s' % session
|
|
67
|
-
cmd = self.HANDSHAKE
|
|
68
|
-
super().__init__(cmd=cmd)
|
|
69
|
-
self['title'] = title
|
|
70
|
-
self['message'] = title # TODO: remove after all clients upgraded
|
|
71
|
-
if session is not None:
|
|
72
|
-
self['session'] = session
|
|
73
|
-
else:
|
|
74
|
-
# 2. command info from network
|
|
75
|
-
assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
|
|
76
|
-
super().__init__(content)
|
|
77
|
-
|
|
78
77
|
@property
|
|
78
|
+
@abstractmethod
|
|
79
79
|
def title(self) -> str:
|
|
80
|
-
|
|
81
|
-
text = self.get_str(key='title', default=None)
|
|
82
|
-
if text is None:
|
|
83
|
-
# compatible with v1.0
|
|
84
|
-
text = self.get_str(key='message', default='')
|
|
85
|
-
return text
|
|
80
|
+
raise NotImplemented
|
|
86
81
|
|
|
87
82
|
@property
|
|
83
|
+
@abstractmethod
|
|
88
84
|
def session(self) -> Optional[str]:
|
|
89
|
-
|
|
85
|
+
raise NotImplemented
|
|
90
86
|
|
|
91
87
|
@property
|
|
88
|
+
@abstractmethod
|
|
92
89
|
def state(self) -> HandshakeState:
|
|
93
|
-
|
|
90
|
+
raise NotImplemented
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# Factories
|
|
94
|
+
#
|
|
94
95
|
|
|
95
96
|
@classmethod
|
|
96
97
|
def offer(cls, session: str = None) -> Command:
|
|
@@ -100,7 +101,7 @@ class HandshakeCommand(BaseCommand):
|
|
|
100
101
|
:param session: Old session key
|
|
101
102
|
:return: HandshakeCommand object
|
|
102
103
|
"""
|
|
103
|
-
return
|
|
104
|
+
return BaseHandshakeCommand(title='Hello world!', session=session)
|
|
104
105
|
|
|
105
106
|
@classmethod
|
|
106
107
|
def ask(cls, session: str) -> Command:
|
|
@@ -110,7 +111,7 @@ class HandshakeCommand(BaseCommand):
|
|
|
110
111
|
:param session: New session key
|
|
111
112
|
:return: HandshakeCommand object
|
|
112
113
|
"""
|
|
113
|
-
return
|
|
114
|
+
return BaseHandshakeCommand(title='DIM?', session=session)
|
|
114
115
|
|
|
115
116
|
@classmethod
|
|
116
117
|
def accepted(cls, session: str = None) -> Command:
|
|
@@ -119,7 +120,7 @@ class HandshakeCommand(BaseCommand):
|
|
|
119
120
|
|
|
120
121
|
:return: HandshakeCommand object
|
|
121
122
|
"""
|
|
122
|
-
return
|
|
123
|
+
return BaseHandshakeCommand(title='DIM!', session=session)
|
|
123
124
|
|
|
124
125
|
start = offer # (1. C->S) first handshake, without session
|
|
125
126
|
again = ask # (2. S->C) ask client to handshake with new session key
|
|
@@ -127,17 +128,34 @@ class HandshakeCommand(BaseCommand):
|
|
|
127
128
|
success = accepted # (4. S->C) notice the client that handshake accepted
|
|
128
129
|
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
class BaseHandshakeCommand(BaseCommand, HandshakeCommand):
|
|
132
|
+
|
|
133
|
+
def __init__(self, content: Dict = None, title: str = None, session: str = None):
|
|
134
|
+
if content is None:
|
|
135
|
+
# 1. new command with title & session key
|
|
136
|
+
assert title is not None, 'handshake command error: %s' % session
|
|
137
|
+
cmd = self.HANDSHAKE
|
|
138
|
+
super().__init__(cmd=cmd)
|
|
139
|
+
self['title'] = title
|
|
140
|
+
self['message'] = title # TODO: remove after all clients upgraded
|
|
141
|
+
if session is not None:
|
|
142
|
+
self['session'] = session
|
|
143
|
+
else:
|
|
144
|
+
# 2. command info from network
|
|
145
|
+
assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
|
|
146
|
+
super().__init__(content)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def title(self) -> str:
|
|
150
|
+
return self.get_str(key='title', default='')
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def session(self) -> Optional[str]:
|
|
154
|
+
return self.get_str(key='session')
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def state(self) -> HandshakeState:
|
|
158
|
+
return handshake_state(title=self.title, session=self.session)
|
|
141
159
|
```
|
|
142
160
|
|
|
143
161
|
### extends Content
|
|
@@ -151,13 +169,13 @@ from dimp import Content
|
|
|
151
169
|
from dimp import BaseContent
|
|
152
170
|
|
|
153
171
|
|
|
154
|
-
class
|
|
172
|
+
class AppContent(Content, ABC):
|
|
155
173
|
"""
|
|
156
174
|
Application Customized message
|
|
157
175
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
158
176
|
|
|
159
177
|
data format: {
|
|
160
|
-
type :
|
|
178
|
+
type : 0xA0,
|
|
161
179
|
sn : 123,
|
|
162
180
|
|
|
163
181
|
app : "{APP_ID}", // application (e.g.: "chat.dim.sechat")
|
|
@@ -185,22 +203,25 @@ class CustomizedContent(Content, ABC):
|
|
|
185
203
|
""" Action Name """
|
|
186
204
|
raise NotImplemented
|
|
187
205
|
|
|
206
|
+
#
|
|
207
|
+
# Factory
|
|
208
|
+
#
|
|
188
209
|
@classmethod
|
|
189
210
|
def create(cls, app: str, mod: str, act: str):
|
|
190
|
-
return
|
|
211
|
+
return ApplicationContent(app=app, mod=mod, act=act)
|
|
191
212
|
|
|
192
213
|
|
|
193
|
-
class
|
|
214
|
+
class ApplicationContent(BaseContent, AppContent):
|
|
194
215
|
|
|
195
216
|
def __init__(self, content: Dict[str, Any] = None,
|
|
196
|
-
msg_type:
|
|
217
|
+
msg_type: str = None,
|
|
197
218
|
app: str = None, mod: str = None, act: str = None):
|
|
198
219
|
if content is None:
|
|
199
220
|
# 1. new content with type, application, module & action
|
|
200
221
|
assert app is not None and mod is not None and act is not None, \
|
|
201
222
|
'customized content error: %s, %s, %s, %s' % (msg_type, app, mod, act)
|
|
202
223
|
if msg_type is None:
|
|
203
|
-
msg_type = ContentType.
|
|
224
|
+
msg_type = ContentType.APPLICATION
|
|
204
225
|
super().__init__(None, msg_type)
|
|
205
226
|
self['app'] = app
|
|
206
227
|
self['mod'] = mod
|
|
@@ -230,5 +251,5 @@ class AppCustomizedContent(BaseContent, CustomizedContent):
|
|
|
230
251
|
|
|
231
252
|
----
|
|
232
253
|
|
|
233
|
-
Copyright © 2018 Albert Moky
|
|
254
|
+
Copyright © 2018-2025 Albert Moky
|
|
234
255
|
[](https://github.com/moky?tab=followers)
|
|
@@ -34,7 +34,7 @@ from mkm.crypto import *
|
|
|
34
34
|
from mkm import *
|
|
35
35
|
from dkd import *
|
|
36
36
|
|
|
37
|
-
from .protocol.
|
|
37
|
+
from .protocol.base import CommandFactory
|
|
38
38
|
from .protocol import *
|
|
39
39
|
from .crypto import *
|
|
40
40
|
from .mkm import *
|
|
@@ -119,6 +119,8 @@ __all__ = [
|
|
|
119
119
|
'Meta',
|
|
120
120
|
'Document', 'Visa', 'Bulletin',
|
|
121
121
|
|
|
122
|
+
'MetaType', 'DocumentType',
|
|
123
|
+
|
|
122
124
|
'AddressFactory',
|
|
123
125
|
'IDFactory',
|
|
124
126
|
'MetaFactory',
|
|
@@ -132,8 +134,6 @@ __all__ = [
|
|
|
132
134
|
'BaseMeta',
|
|
133
135
|
'BaseDocument', 'BaseVisa', 'BaseBulletin',
|
|
134
136
|
|
|
135
|
-
'MetaType', 'DocumentType',
|
|
136
|
-
|
|
137
137
|
#
|
|
138
138
|
# DaoKeDao
|
|
139
139
|
#
|
|
@@ -145,11 +145,12 @@ __all__ = [
|
|
|
145
145
|
'InstantMessage', 'SecureMessage', 'ReliableMessage',
|
|
146
146
|
|
|
147
147
|
# contents
|
|
148
|
-
'TextContent', '
|
|
149
|
-
'
|
|
148
|
+
'TextContent', 'PageContent', 'NameCard',
|
|
149
|
+
'ForwardContent', 'CombineContent', 'ArrayContent',
|
|
150
150
|
'FileContent', 'ImageContent', 'AudioContent', 'VideoContent',
|
|
151
151
|
'MoneyContent', 'TransferContent',
|
|
152
|
-
'QuoteContent',
|
|
152
|
+
'QuoteContent',
|
|
153
|
+
'CustomizedContent',
|
|
153
154
|
|
|
154
155
|
# commands
|
|
155
156
|
'Command',
|
|
@@ -163,11 +164,12 @@ __all__ = [
|
|
|
163
164
|
|
|
164
165
|
# extend contents
|
|
165
166
|
'BaseContent',
|
|
166
|
-
'BaseTextContent', '
|
|
167
|
-
'
|
|
167
|
+
'BaseTextContent', 'WebPageContent', 'NameCardContent',
|
|
168
|
+
'SecretContent', 'CombineForwardContent', 'ListContent',
|
|
168
169
|
'BaseFileContent', 'ImageFileContent', 'AudioFileContent', 'VideoFileContent',
|
|
169
170
|
'BaseMoneyContent', 'TransferMoneyContent',
|
|
170
|
-
'BaseQuoteContent',
|
|
171
|
+
'BaseQuoteContent',
|
|
172
|
+
'AppCustomizedContent',
|
|
171
173
|
|
|
172
174
|
# extend commands
|
|
173
175
|
'BaseCommand',
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
# ==============================================================================
|
|
30
30
|
|
|
31
31
|
from abc import ABC
|
|
32
|
-
from typing import
|
|
32
|
+
from typing import Dict
|
|
33
33
|
|
|
34
|
-
from mkm.types import Dictionary
|
|
34
|
+
from mkm.types import Mapper, Dictionary
|
|
35
35
|
from mkm.crypto import CryptographyKey, EncryptKey, DecryptKey, SignKey, VerifyKey
|
|
36
36
|
from mkm.crypto import SymmetricKey, AsymmetricKey, PublicKey, PrivateKey
|
|
37
37
|
from mkm.plugins import SharedCryptoExtensions
|
|
@@ -46,7 +46,7 @@ from mkm.plugins import SharedCryptoExtensions
|
|
|
46
46
|
# noinspection PyAbstractClass
|
|
47
47
|
class BaseKey(Dictionary, CryptographyKey, ABC):
|
|
48
48
|
|
|
49
|
-
def __init__(self, key: Dict
|
|
49
|
+
def __init__(self, key: Dict):
|
|
50
50
|
super().__init__(dictionary=key)
|
|
51
51
|
|
|
52
52
|
@property # Override
|
|
@@ -58,9 +58,9 @@ class BaseKey(Dictionary, CryptographyKey, ABC):
|
|
|
58
58
|
#
|
|
59
59
|
|
|
60
60
|
@classmethod
|
|
61
|
-
def get_key_algorithm(cls, key: Dict
|
|
61
|
+
def get_key_algorithm(cls, key: Dict) -> str:
|
|
62
62
|
ext = SharedCryptoExtensions()
|
|
63
|
-
algorithm = ext.helper.get_key_algorithm(key=key
|
|
63
|
+
algorithm = ext.helper.get_key_algorithm(key=key)
|
|
64
64
|
return '' if algorithm is None else algorithm
|
|
65
65
|
|
|
66
66
|
@classmethod
|
|
@@ -97,20 +97,32 @@ class BaseKey(Dictionary, CryptographyKey, ABC):
|
|
|
97
97
|
# noinspection PyAbstractClass
|
|
98
98
|
class BaseSymmetricKey(Dictionary, SymmetricKey, ABC):
|
|
99
99
|
|
|
100
|
-
def __init__(self, key: Dict
|
|
100
|
+
def __init__(self, key: Dict):
|
|
101
101
|
super().__init__(dictionary=key)
|
|
102
102
|
|
|
103
103
|
# Override
|
|
104
104
|
def __eq__(self, other) -> bool:
|
|
105
|
-
if isinstance(other,
|
|
106
|
-
|
|
105
|
+
if isinstance(other, Mapper):
|
|
106
|
+
if self is other:
|
|
107
|
+
# same object
|
|
108
|
+
return True
|
|
109
|
+
elif isinstance(other, SymmetricKey):
|
|
110
|
+
return BaseKey.symmetric_keys_equal(other, self)
|
|
111
|
+
# compare with inner map
|
|
112
|
+
other = other.dictionary
|
|
113
|
+
return self.dictionary.__eq__(other)
|
|
107
114
|
|
|
108
115
|
# Override
|
|
109
116
|
def __ne__(self, other) -> bool:
|
|
110
|
-
if isinstance(other,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
if isinstance(other, Mapper):
|
|
118
|
+
if self is other:
|
|
119
|
+
# same object
|
|
120
|
+
return False
|
|
121
|
+
elif isinstance(other, SymmetricKey):
|
|
122
|
+
return not BaseKey.symmetric_keys_equal(other, self)
|
|
123
|
+
# compare with inner map
|
|
124
|
+
other = other.dictionary
|
|
125
|
+
return self.dictionary.__ne__(other)
|
|
114
126
|
|
|
115
127
|
@property # Override
|
|
116
128
|
def algorithm(self) -> str:
|
|
@@ -124,7 +136,7 @@ class BaseSymmetricKey(Dictionary, SymmetricKey, ABC):
|
|
|
124
136
|
# noinspection PyAbstractClass
|
|
125
137
|
class BaseAsymmetricKey(Dictionary, AsymmetricKey, ABC):
|
|
126
138
|
|
|
127
|
-
def __init__(self, key: Dict
|
|
139
|
+
def __init__(self, key: Dict):
|
|
128
140
|
super().__init__(dictionary=key)
|
|
129
141
|
|
|
130
142
|
@property # Override
|
|
@@ -135,7 +147,7 @@ class BaseAsymmetricKey(Dictionary, AsymmetricKey, ABC):
|
|
|
135
147
|
# noinspection PyAbstractClass
|
|
136
148
|
class BasePublicKey(Dictionary, PublicKey, ABC):
|
|
137
149
|
|
|
138
|
-
def __init__(self, key: Dict
|
|
150
|
+
def __init__(self, key: Dict):
|
|
139
151
|
super().__init__(dictionary=key)
|
|
140
152
|
|
|
141
153
|
@property # Override
|
|
@@ -150,20 +162,32 @@ class BasePublicKey(Dictionary, PublicKey, ABC):
|
|
|
150
162
|
# noinspection PyAbstractClass
|
|
151
163
|
class BasePrivateKey(Dictionary, PrivateKey, ABC):
|
|
152
164
|
|
|
153
|
-
def __init__(self, key: Dict
|
|
165
|
+
def __init__(self, key: Dict):
|
|
154
166
|
super().__init__(dictionary=key)
|
|
155
167
|
|
|
156
168
|
# Override
|
|
157
169
|
def __eq__(self, other) -> bool:
|
|
158
|
-
if isinstance(other,
|
|
159
|
-
|
|
170
|
+
if isinstance(other, Mapper):
|
|
171
|
+
if self is other:
|
|
172
|
+
# same object
|
|
173
|
+
return True
|
|
174
|
+
elif isinstance(other, PrivateKey):
|
|
175
|
+
return BaseKey.private_keys_equal(other, self)
|
|
176
|
+
# compare with inner map
|
|
177
|
+
other = other.dictionary
|
|
178
|
+
return self.dictionary.__eq__(other)
|
|
160
179
|
|
|
161
180
|
# Override
|
|
162
181
|
def __ne__(self, other) -> bool:
|
|
163
|
-
if isinstance(other,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
182
|
+
if isinstance(other, Mapper):
|
|
183
|
+
if self is other:
|
|
184
|
+
# same object
|
|
185
|
+
return False
|
|
186
|
+
elif isinstance(other, PrivateKey):
|
|
187
|
+
return not BaseKey.private_keys_equal(other, self)
|
|
188
|
+
# compare with inner map
|
|
189
|
+
other = other.dictionary
|
|
190
|
+
return self.dictionary.__ne__(other)
|
|
167
191
|
|
|
168
192
|
@property # Override
|
|
169
193
|
def algorithm(self) -> str:
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
# SOFTWARE.
|
|
29
29
|
# ==============================================================================
|
|
30
30
|
|
|
31
|
-
from typing import Optional,
|
|
31
|
+
from typing import Optional, Dict
|
|
32
32
|
|
|
33
33
|
from mkm.format import TransportableData
|
|
34
34
|
from mkm.types import URI
|
|
@@ -55,7 +55,7 @@ class BaseFileWrapper(Dictionary):
|
|
|
55
55
|
}
|
|
56
56
|
"""
|
|
57
57
|
|
|
58
|
-
def __init__(self, dictionary: Dict
|
|
58
|
+
def __init__(self, dictionary: Dict):
|
|
59
59
|
super().__init__(dictionary=dictionary)
|
|
60
60
|
# file data (not encrypted)
|
|
61
61
|
self.__attachment: Optional[TransportableData] = None
|
|
@@ -100,7 +100,7 @@ class BaseFileWrapper(Dictionary):
|
|
|
100
100
|
|
|
101
101
|
@property
|
|
102
102
|
def filename(self) -> Optional[str]:
|
|
103
|
-
return self.get_str(key='filename'
|
|
103
|
+
return self.get_str(key='filename')
|
|
104
104
|
|
|
105
105
|
@filename.setter
|
|
106
106
|
def filename(self, name: Optional[str]):
|
|
@@ -115,11 +115,12 @@ class BaseFileWrapper(Dictionary):
|
|
|
115
115
|
|
|
116
116
|
@property
|
|
117
117
|
def url(self) -> Optional[URI]:
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
remote = self.__url
|
|
119
|
+
if remote is None:
|
|
120
|
+
remote = self.get_str(key='URL')
|
|
120
121
|
# TODO: convert str to URI
|
|
121
122
|
self.__url = remote
|
|
122
|
-
return
|
|
123
|
+
return remote
|
|
123
124
|
|
|
124
125
|
@url.setter
|
|
125
126
|
def url(self, remote: Optional[URI]):
|
|
@@ -136,10 +137,12 @@ class BaseFileWrapper(Dictionary):
|
|
|
136
137
|
|
|
137
138
|
@property
|
|
138
139
|
def password(self) -> Optional[DecryptKey]:
|
|
139
|
-
|
|
140
|
+
key = self.__password
|
|
141
|
+
if key is None:
|
|
140
142
|
info = self.get('key')
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
key = SymmetricKey.parse(key=info)
|
|
144
|
+
self.__password = key
|
|
145
|
+
return key
|
|
143
146
|
|
|
144
147
|
@password.setter
|
|
145
148
|
def password(self, key: Optional[DecryptKey]):
|