dimp 2.2.2__tar.gz → 2.3.0__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.0/LICENSE +21 -0
- dimp-2.3.0/PKG-INFO +248 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/__init__.py +8 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/crypto/__init__.py +9 -0
- dimp-2.3.0/dimp/crypto/algorithms.py +57 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/crypto/pnf.py +3 -1
- {dimp-2.2.2 → dimp-2.3.0}/dimp/crypto/ted.py +11 -10
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/base.py +9 -8
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/commands.py +24 -25
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/contents.py +19 -18
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/files.py +6 -7
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/group_admins.py +8 -8
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/groups.py +7 -7
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/money.py +7 -7
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/quote.py +8 -7
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/receipt.py +1 -1
- {dimp-2.2.2 → dimp-2.3.0}/dimp/mkm/__init__.py +3 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/mkm/docs.py +5 -5
- {dimp-2.2.2 → dimp-2.3.0}/dimp/mkm/document.py +14 -13
- {dimp-2.2.2 → dimp-2.3.0}/dimp/mkm/meta.py +9 -9
- {dimp-2.2.2 → dimp-2.3.0}/dimp/msg/base.py +1 -1
- {dimp-2.2.2 → dimp-2.3.0}/dimp/msg/envelope.py +3 -3
- {dimp-2.2.2 → dimp-2.3.0}/dimp/msg/instant.py +1 -1
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/__init__.py +3 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/commands.py +14 -14
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/contents.py +8 -44
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/files.py +11 -10
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/groups.py +8 -7
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/money.py +6 -5
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/quote.py +3 -21
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/receipt.py +1 -1
- dimp-2.3.0/dimp/protocol/types.py +177 -0
- dimp-2.3.0/dimp.egg-info/PKG-INFO +248 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp.egg-info/SOURCES.txt +4 -1
- dimp-2.3.0/dimp.egg-info/requires.txt +2 -0
- {dimp-2.2.2 → dimp-2.3.0}/setup.py +3 -3
- dimp-2.2.2/PKG-INFO +0 -248
- dimp-2.2.2/dimp.egg-info/PKG-INFO +0 -248
- dimp-2.2.2/dimp.egg-info/requires.txt +0 -2
- {dimp-2.2.2 → dimp-2.3.0}/README.md +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/crypto/keys.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/dkd/__init__.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/msg/__init__.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/msg/reliable.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/msg/secure.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/plugins/__init__.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/plugins/cmd.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/docs.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp/protocol/helpers.py +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp.egg-info/dependency_links.txt +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/dimp.egg-info/top_level.txt +0 -0
- {dimp-2.2.2 → dimp-2.3.0}/setup.cfg +0 -0
dimp-2.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Albert Moky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dimp-2.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dimp
|
|
3
|
+
Version: 2.3.0
|
|
4
|
+
Summary: Decentralized Instant Messaging Protocol
|
|
5
|
+
Home-page: https://github.com/dimchat/core-py
|
|
6
|
+
Author: Albert Moky
|
|
7
|
+
Author-email: albert.moky@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
|
|
15
|
+
# Decentralized Instant Messaging Protocol (Python)
|
|
16
|
+
|
|
17
|
+
[](https://github.com/dimchat/core-py/blob/master/LICENSE)
|
|
18
|
+
[](https://github.com/dimchat/core-py/pulls)
|
|
19
|
+
[](https://github.com/dimchat/core-py/wiki)
|
|
20
|
+
[](https://github.com/dimchat/core-py/issues)
|
|
21
|
+
[](https://github.com/dimchat/core-py/archive/refs/heads/main.zip)
|
|
22
|
+
[](https://github.com/dimchat/core-py/tags)
|
|
23
|
+
[](https://pypi.org/project/dimp)
|
|
24
|
+
|
|
25
|
+
[](https://github.com/dimchat/core-py/watchers)
|
|
26
|
+
[](https://github.com/dimchat/core-py/forks)
|
|
27
|
+
[](https://github.com/dimchat/core-py/stargazers)
|
|
28
|
+
[](https://github.com/orgs/dimchat/followers)
|
|
29
|
+
|
|
30
|
+
## Dependencies
|
|
31
|
+
|
|
32
|
+
| Name | Version | Description |
|
|
33
|
+
|------|---------|-------------|
|
|
34
|
+
| [Ming Ke Ming (名可名)](https://github.com/dimchat/mkm-py) | [](https://pypi.org/project/mkm) | Decentralized User Identity Authentication |
|
|
35
|
+
| [Dao Ke Dao (道可道)](https://github.com/dimchat/dkd-py) | [](https://pypi.org/project/dkd) | Universal Message Module |
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
### extends Command
|
|
40
|
+
|
|
41
|
+
* _Handshake Command Protocol_
|
|
42
|
+
0. (C-S) handshake start
|
|
43
|
+
1. (S-C) handshake again with new session
|
|
44
|
+
2. (C-S) handshake restart with new session
|
|
45
|
+
3. (S-C) handshake success
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from enum import IntEnum
|
|
49
|
+
from typing import Optional, Any, Dict
|
|
50
|
+
|
|
51
|
+
from dimp import Command, BaseCommand
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class HandshakeState(IntEnum):
|
|
55
|
+
Start = 0 # C -> S, without session key(or session expired)
|
|
56
|
+
Again = 1 # S -> C, with new session key
|
|
57
|
+
Restart = 2 # C -> S, with new session key
|
|
58
|
+
Success = 3 # S -> C, handshake accepted
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class HandshakeCommand(BaseCommand):
|
|
62
|
+
"""
|
|
63
|
+
Handshake Command
|
|
64
|
+
~~~~~~~~~~~~~~~~~
|
|
65
|
+
|
|
66
|
+
data format: {
|
|
67
|
+
type : 0x88,
|
|
68
|
+
sn : 123,
|
|
69
|
+
|
|
70
|
+
command : "handshake", // command name
|
|
71
|
+
title : "Hello world!", // "DIM?", "DIM!"
|
|
72
|
+
session : "{SESSION_ID}", // session key
|
|
73
|
+
}
|
|
74
|
+
"""
|
|
75
|
+
HANDSHAKE = 'handshake'
|
|
76
|
+
|
|
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
|
+
@property
|
|
93
|
+
def title(self) -> str:
|
|
94
|
+
# TODO: modify after all clients upgraded
|
|
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
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def session(self) -> Optional[str]:
|
|
103
|
+
return self.get_str(key='session', default=None)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def state(self) -> HandshakeState:
|
|
107
|
+
return handshake_state(title=self.title, session=self.session)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def offer(cls, session: str = None) -> Command:
|
|
111
|
+
"""
|
|
112
|
+
Create client-station handshake offer
|
|
113
|
+
|
|
114
|
+
:param session: Old session key
|
|
115
|
+
:return: HandshakeCommand object
|
|
116
|
+
"""
|
|
117
|
+
return HandshakeCommand(title='Hello world!', session=session)
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def ask(cls, session: str) -> Command:
|
|
121
|
+
"""
|
|
122
|
+
Create station-client handshake again with new session
|
|
123
|
+
|
|
124
|
+
:param session: New session key
|
|
125
|
+
:return: HandshakeCommand object
|
|
126
|
+
"""
|
|
127
|
+
return HandshakeCommand(title='DIM?', session=session)
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def accepted(cls, session: str = None) -> Command:
|
|
131
|
+
"""
|
|
132
|
+
Create station-client handshake success notice
|
|
133
|
+
|
|
134
|
+
:return: HandshakeCommand object
|
|
135
|
+
"""
|
|
136
|
+
return HandshakeCommand(title='DIM!', session=session)
|
|
137
|
+
|
|
138
|
+
start = offer # (1. C->S) first handshake, without session
|
|
139
|
+
again = ask # (2. S->C) ask client to handshake with new session key
|
|
140
|
+
restart = offer # (3. C->S) handshake with new session key
|
|
141
|
+
success = accepted # (4. S->C) notice the client that handshake accepted
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def handshake_state(title: str, session: str = None) -> HandshakeState:
|
|
145
|
+
# Server -> Client
|
|
146
|
+
if title == 'DIM!': # or title == 'OK!':
|
|
147
|
+
return HandshakeState.Success
|
|
148
|
+
if title == 'DIM?':
|
|
149
|
+
return HandshakeState.Again
|
|
150
|
+
# Client -> Server: "Hello world!"
|
|
151
|
+
if session is None or len(session) == 0:
|
|
152
|
+
return HandshakeState.Start
|
|
153
|
+
else:
|
|
154
|
+
return HandshakeState.Restart
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### extends Content
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from abc import ABC, abstractmethod
|
|
161
|
+
from typing import Any, Dict
|
|
162
|
+
|
|
163
|
+
from dimp import ContentType
|
|
164
|
+
from dimp import Content
|
|
165
|
+
from dimp import BaseContent
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class CustomizedContent(Content, ABC):
|
|
169
|
+
"""
|
|
170
|
+
Application Customized message
|
|
171
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
172
|
+
|
|
173
|
+
data format: {
|
|
174
|
+
type : 0xCC,
|
|
175
|
+
sn : 123,
|
|
176
|
+
|
|
177
|
+
app : "{APP_ID}", // application (e.g.: "chat.dim.sechat")
|
|
178
|
+
mod : "{MODULE}", // module name (e.g.: "drift_bottle")
|
|
179
|
+
act : "{ACTION}", // action name (e.g.: "throw")
|
|
180
|
+
extra : info // action parameters
|
|
181
|
+
}
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
@abstractmethod
|
|
186
|
+
def application(self) -> str:
|
|
187
|
+
""" App ID """
|
|
188
|
+
raise NotImplemented
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
@abstractmethod
|
|
192
|
+
def module(self) -> str:
|
|
193
|
+
""" Module Name """
|
|
194
|
+
raise NotImplemented
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def action(self) -> str:
|
|
199
|
+
""" Action Name """
|
|
200
|
+
raise NotImplemented
|
|
201
|
+
|
|
202
|
+
@classmethod
|
|
203
|
+
def create(cls, app: str, mod: str, act: str):
|
|
204
|
+
return AppCustomizedContent(app=app, mod=mod, act=act)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class AppCustomizedContent(BaseContent, CustomizedContent):
|
|
208
|
+
|
|
209
|
+
def __init__(self, content: Dict[str, Any] = None,
|
|
210
|
+
msg_type: int = None,
|
|
211
|
+
app: str = None, mod: str = None, act: str = None):
|
|
212
|
+
if content is None:
|
|
213
|
+
# 1. new content with type, application, module & action
|
|
214
|
+
assert app is not None and mod is not None and act is not None, \
|
|
215
|
+
'customized content error: %s, %s, %s, %s' % (msg_type, app, mod, act)
|
|
216
|
+
if msg_type is None:
|
|
217
|
+
msg_type = ContentType.CUSTOMIZED.value
|
|
218
|
+
super().__init__(None, msg_type)
|
|
219
|
+
self['app'] = app
|
|
220
|
+
self['mod'] = mod
|
|
221
|
+
self['act'] = act
|
|
222
|
+
else:
|
|
223
|
+
# 2. content info from network
|
|
224
|
+
assert msg_type is None and app is None and mod is None and act is None, \
|
|
225
|
+
'params error: %s, %s, %s, %s, %s' % (content, msg_type, app, mod, act)
|
|
226
|
+
super().__init__(content)
|
|
227
|
+
|
|
228
|
+
@property # Override
|
|
229
|
+
def application(self) -> str:
|
|
230
|
+
return self.get_str(key='app', default='')
|
|
231
|
+
|
|
232
|
+
@property # Override
|
|
233
|
+
def module(self) -> str:
|
|
234
|
+
return self.get_str(key='mod', default='')
|
|
235
|
+
|
|
236
|
+
@property # Override
|
|
237
|
+
def action(self) -> str:
|
|
238
|
+
return self.get_str(key='act', default='')
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### extends ID Address
|
|
242
|
+
|
|
243
|
+
* Examples in [dim plugins](https://pypi.org/project/dimplugins)
|
|
244
|
+
|
|
245
|
+
----
|
|
246
|
+
|
|
247
|
+
Copyright © 2018 Albert Moky
|
|
248
|
+
[](https://github.com/moky?tab=followers)
|
|
@@ -102,6 +102,12 @@ __all__ = [
|
|
|
102
102
|
'BaseDataWrapper',
|
|
103
103
|
'BaseFileWrapper',
|
|
104
104
|
|
|
105
|
+
#
|
|
106
|
+
# Algorithm
|
|
107
|
+
#
|
|
108
|
+
'AsymmetricAlgorithms', 'SymmetricAlgorithms',
|
|
109
|
+
'EncodeAlgorithms',
|
|
110
|
+
|
|
105
111
|
#
|
|
106
112
|
# MingKeMing
|
|
107
113
|
#
|
|
@@ -125,6 +131,8 @@ __all__ = [
|
|
|
125
131
|
'BaseMeta',
|
|
126
132
|
'BaseDocument', 'BaseVisa', 'BaseBulletin',
|
|
127
133
|
|
|
134
|
+
'MetaType', 'DocumentType',
|
|
135
|
+
|
|
128
136
|
#
|
|
129
137
|
# DaoKeDao
|
|
130
138
|
#
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
|
|
31
31
|
from mkm.crypto import *
|
|
32
32
|
|
|
33
|
+
from .algorithms import AsymmetricAlgorithms, SymmetricAlgorithms
|
|
34
|
+
from .algorithms import EncodeAlgorithms
|
|
35
|
+
|
|
33
36
|
from .keys import BaseKey, BaseSymmetricKey
|
|
34
37
|
from .keys import BaseAsymmetricKey, BasePublicKey, BasePrivateKey
|
|
35
38
|
|
|
@@ -57,6 +60,12 @@ __all__ = [
|
|
|
57
60
|
|
|
58
61
|
'SymmetricKeyFactory', 'PrivateKeyFactory', 'PublicKeyFactory',
|
|
59
62
|
|
|
63
|
+
#
|
|
64
|
+
# Algorithm
|
|
65
|
+
#
|
|
66
|
+
'AsymmetricAlgorithms', 'SymmetricAlgorithms',
|
|
67
|
+
'EncodeAlgorithms',
|
|
68
|
+
|
|
60
69
|
#
|
|
61
70
|
# Base Keys
|
|
62
71
|
#
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# DIMP : Decentralized Instant Messaging Protocol
|
|
4
|
+
#
|
|
5
|
+
# Written in 2025 by Moky <albert.moky@gmail.com>
|
|
6
|
+
#
|
|
7
|
+
# ==============================================================================
|
|
8
|
+
# MIT License
|
|
9
|
+
#
|
|
10
|
+
# Copyright (c) 2025 Albert Moky
|
|
11
|
+
#
|
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
# in the Software without restriction, including without limitation the rights
|
|
15
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
# furnished to do so, subject to the following conditions:
|
|
18
|
+
#
|
|
19
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
# copies or substantial portions of the Software.
|
|
21
|
+
#
|
|
22
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
# SOFTWARE.
|
|
29
|
+
# ==============================================================================
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AsymmetricAlgorithms:
|
|
33
|
+
""" Algorithms for Asymmetric Key """
|
|
34
|
+
|
|
35
|
+
RSA = 'RSA' # -- "RSA/ECB/PKCS1Padding", "SHA256withRSA"
|
|
36
|
+
ECC = 'ECC'
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SymmetricAlgorithms:
|
|
40
|
+
""" Algorithms for Symmetric Key """
|
|
41
|
+
|
|
42
|
+
AES = 'AES' # -- "AES/CBC/PKCS7Padding"
|
|
43
|
+
DES = 'DES'
|
|
44
|
+
|
|
45
|
+
# Symmetric key algorithm for broadcast message,
|
|
46
|
+
# which will do nothing when en/decoding message data
|
|
47
|
+
PLAIN = 'PLAIN'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class EncodeAlgorithms:
|
|
51
|
+
""" Algorithms for Encoding Data """
|
|
52
|
+
|
|
53
|
+
DEFAULT = 'base64'
|
|
54
|
+
|
|
55
|
+
BASE_64 = 'base64'
|
|
56
|
+
BASE_58 = 'base58'
|
|
57
|
+
HEX = 'hex'
|
|
@@ -35,6 +35,8 @@ from mkm.types import URI
|
|
|
35
35
|
from mkm.types import Dictionary
|
|
36
36
|
from mkm.crypto import DecryptKey, SymmetricKey
|
|
37
37
|
|
|
38
|
+
from .algorithms import EncodeAlgorithms
|
|
39
|
+
|
|
38
40
|
|
|
39
41
|
class BaseFileWrapper(Dictionary):
|
|
40
42
|
"""
|
|
@@ -90,7 +92,7 @@ class BaseFileWrapper(Dictionary):
|
|
|
90
92
|
ted = None
|
|
91
93
|
self.pop('data', None)
|
|
92
94
|
else:
|
|
93
|
-
ted = TransportableData.create(data=binary)
|
|
95
|
+
ted = TransportableData.create(data=binary, algorithm=EncodeAlgorithms.DEFAULT)
|
|
94
96
|
self['data'] = ted.object
|
|
95
97
|
self.__attachment = ted
|
|
96
98
|
|
|
@@ -33,9 +33,10 @@ from typing import Optional, Any, Dict
|
|
|
33
33
|
from mkm.format import base64_encode, base64_decode
|
|
34
34
|
from mkm.format import base58_encode, base58_decode
|
|
35
35
|
from mkm.format import hex_encode, hex_decode
|
|
36
|
-
from mkm.format import TransportableData
|
|
37
36
|
from mkm.types import Dictionary
|
|
38
37
|
|
|
38
|
+
from .algorithms import EncodeAlgorithms
|
|
39
|
+
|
|
39
40
|
|
|
40
41
|
class BaseDataWrapper(Dictionary):
|
|
41
42
|
"""
|
|
@@ -72,7 +73,7 @@ class BaseDataWrapper(Dictionary):
|
|
|
72
73
|
if len(text) == 0:
|
|
73
74
|
return text
|
|
74
75
|
alg = self.get_str(key='algorithm', default='')
|
|
75
|
-
if alg ==
|
|
76
|
+
if alg == EncodeAlgorithms.DEFAULT:
|
|
76
77
|
alg = ''
|
|
77
78
|
if len(alg) == 0:
|
|
78
79
|
# 0. "{BASE64_ENCODE}"
|
|
@@ -99,12 +100,12 @@ class BaseDataWrapper(Dictionary):
|
|
|
99
100
|
def algorithm(self) -> str:
|
|
100
101
|
alg = self.get_str(key='algorithm', default='')
|
|
101
102
|
if len(alg) == 0:
|
|
102
|
-
alg =
|
|
103
|
+
alg = EncodeAlgorithms.DEFAULT
|
|
103
104
|
return alg
|
|
104
105
|
|
|
105
106
|
@algorithm.setter
|
|
106
107
|
def algorithm(self, name: str):
|
|
107
|
-
if name is None: # or name ==
|
|
108
|
+
if name is None: # or name == EncodeAlgorithms.DEFAULT:
|
|
108
109
|
self.pop('algorithm', None)
|
|
109
110
|
else:
|
|
110
111
|
self['algorithm'] = name
|
|
@@ -120,11 +121,11 @@ class BaseDataWrapper(Dictionary):
|
|
|
120
121
|
text = self.get_str(key='data', default='')
|
|
121
122
|
if len(text) > 0:
|
|
122
123
|
alg = self.algorithm
|
|
123
|
-
if alg ==
|
|
124
|
+
if alg == EncodeAlgorithms.BASE_64:
|
|
124
125
|
binary = base64_decode(string=text)
|
|
125
|
-
elif alg ==
|
|
126
|
+
elif alg == EncodeAlgorithms.BASE_58:
|
|
126
127
|
binary = base58_decode(string=text)
|
|
127
|
-
elif alg ==
|
|
128
|
+
elif alg == EncodeAlgorithms.HEX:
|
|
128
129
|
binary = hex_decode(string=text)
|
|
129
130
|
else:
|
|
130
131
|
assert False, 'data algorithm not support: %s' % alg
|
|
@@ -137,11 +138,11 @@ class BaseDataWrapper(Dictionary):
|
|
|
137
138
|
self.pop('data', None)
|
|
138
139
|
else:
|
|
139
140
|
alg = self.algorithm
|
|
140
|
-
if alg ==
|
|
141
|
+
if alg == EncodeAlgorithms.BASE_64:
|
|
141
142
|
text = base64_encode(data=binary)
|
|
142
|
-
elif alg ==
|
|
143
|
+
elif alg == EncodeAlgorithms.BASE_58:
|
|
143
144
|
text = base58_encode(data=binary)
|
|
144
|
-
elif alg ==
|
|
145
|
+
elif alg == EncodeAlgorithms.HEX:
|
|
145
146
|
text = hex_encode(data=binary)
|
|
146
147
|
else:
|
|
147
148
|
assert False, 'data algorithm not support: %s' % alg
|
|
@@ -33,10 +33,11 @@ from typing import Optional, Any, Dict
|
|
|
33
33
|
from mkm.types import DateTime
|
|
34
34
|
from mkm.types import Dictionary
|
|
35
35
|
from mkm import ID
|
|
36
|
-
from dkd import
|
|
36
|
+
from dkd import Content
|
|
37
37
|
from dkd import InstantMessage
|
|
38
38
|
from dkd.plugins import SharedMessageExtensions
|
|
39
39
|
|
|
40
|
+
from ..protocol import ContentType
|
|
40
41
|
from ..protocol import Command
|
|
41
42
|
|
|
42
43
|
from ..plugins import SharedCommandExtensions
|
|
@@ -52,11 +53,11 @@ from ..plugins import SharedCommandExtensions
|
|
|
52
53
|
|
|
53
54
|
class BaseContent(Dictionary, Content):
|
|
54
55
|
|
|
55
|
-
def __init__(self, content: Dict[str, Any] = None, msg_type:
|
|
56
|
+
def __init__(self, content: Dict[str, Any] = None, msg_type: str = None):
|
|
56
57
|
# check parameters
|
|
57
58
|
if content is None:
|
|
58
59
|
# 1. new content with type
|
|
59
|
-
assert msg_type is not None and msg_type > 0, 'content type error: %s' % msg_type
|
|
60
|
+
assert msg_type is not None and len(msg_type) > 0, 'content type error: %s' % msg_type
|
|
60
61
|
time = DateTime.now()
|
|
61
62
|
sn = InstantMessage.generate_serial_number(msg_type, time)
|
|
62
63
|
content = {
|
|
@@ -77,11 +78,11 @@ class BaseContent(Dictionary, Content):
|
|
|
77
78
|
self.__time = time
|
|
78
79
|
|
|
79
80
|
@property # Override
|
|
80
|
-
def type(self) ->
|
|
81
|
+
def type(self) -> str:
|
|
81
82
|
""" message content type: text, image, ... """
|
|
82
83
|
if self.__type is None:
|
|
83
84
|
ext = SharedMessageExtensions()
|
|
84
|
-
self.__type = ext.helper.get_content_type(content=self.dictionary, default=
|
|
85
|
+
self.__type = ext.helper.get_content_type(content=self.dictionary, default='')
|
|
85
86
|
# self.__type = self.get_int(key='type', default=0)
|
|
86
87
|
return self.__type
|
|
87
88
|
|
|
@@ -114,18 +115,18 @@ class BaseContent(Dictionary, Content):
|
|
|
114
115
|
|
|
115
116
|
class BaseCommand(BaseContent, Command):
|
|
116
117
|
|
|
117
|
-
def __init__(self, content: Dict[str, Any] = None, msg_type:
|
|
118
|
+
def __init__(self, content: Dict[str, Any] = None, msg_type: str = None, cmd: str = None):
|
|
118
119
|
# check parameters
|
|
119
120
|
if content is None:
|
|
120
121
|
# 1. new command with type & name
|
|
121
122
|
if msg_type is None:
|
|
122
|
-
msg_type = ContentType.COMMAND
|
|
123
|
+
msg_type = ContentType.COMMAND
|
|
123
124
|
assert cmd is not None and len(cmd) > 0, 'command name should not empty'
|
|
124
125
|
super().__init__(None, msg_type)
|
|
125
126
|
self['command'] = cmd
|
|
126
127
|
else:
|
|
127
128
|
# 2. command info from network
|
|
128
|
-
assert msg_type is None and cmd is None, 'params error: %
|
|
129
|
+
assert msg_type is None and cmd is None, 'params error: %s, %s' % (msg_type, cmd)
|
|
129
130
|
super().__init__(content)
|
|
130
131
|
|
|
131
132
|
@property # Override
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
Meta Command Protocol
|
|
33
33
|
~~~~~~~~~~~~~~~~~~~~~
|
|
34
34
|
|
|
35
|
-
1. contains '
|
|
35
|
+
1. contains 'did' only, means query meta for ID
|
|
36
36
|
2. contains 'meta' (must match), means reply
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
from typing import Optional, Any, Dict
|
|
39
|
+
from typing import Optional, Any, List, Dict
|
|
40
40
|
|
|
41
41
|
from mkm.types import DateTime
|
|
42
42
|
from mkm import ID, Meta, Document
|
|
@@ -52,11 +52,11 @@ class BaseMetaCommand(BaseCommand, MetaCommand):
|
|
|
52
52
|
~~~~~~~~~~~~
|
|
53
53
|
|
|
54
54
|
data format: {
|
|
55
|
-
type : 0x88,
|
|
55
|
+
type : i2s(0x88),
|
|
56
56
|
sn : 123,
|
|
57
57
|
|
|
58
58
|
command : "meta", // command name
|
|
59
|
-
|
|
59
|
+
did : "{ID}", // contact's ID
|
|
60
60
|
meta : {...} // When meta is empty, means query meta for ID
|
|
61
61
|
}
|
|
62
62
|
"""
|
|
@@ -71,7 +71,7 @@ class BaseMetaCommand(BaseCommand, MetaCommand):
|
|
|
71
71
|
if cmd is None:
|
|
72
72
|
cmd = Command.META
|
|
73
73
|
super().__init__(cmd=cmd)
|
|
74
|
-
self.set_string(key='
|
|
74
|
+
self.set_string(key='did', value=identifier)
|
|
75
75
|
if meta is not None:
|
|
76
76
|
self.set_map(key='meta', value=meta)
|
|
77
77
|
else:
|
|
@@ -89,7 +89,7 @@ class BaseMetaCommand(BaseCommand, MetaCommand):
|
|
|
89
89
|
@property # Override
|
|
90
90
|
def identifier(self) -> ID:
|
|
91
91
|
if self.__id is None:
|
|
92
|
-
self.__id = ID.parse(identifier=self.get('
|
|
92
|
+
self.__id = ID.parse(identifier=self.get('did'))
|
|
93
93
|
return self.__id
|
|
94
94
|
|
|
95
95
|
#
|
|
@@ -108,14 +108,14 @@ class BaseDocumentCommand(BaseMetaCommand, DocumentCommand):
|
|
|
108
108
|
~~~~~~~~~~~~~~~~
|
|
109
109
|
|
|
110
110
|
data format: {
|
|
111
|
-
type : 0x88,
|
|
111
|
+
type : i2s(0x88),
|
|
112
112
|
sn : 123,
|
|
113
113
|
|
|
114
114
|
command : "document", // command name
|
|
115
|
-
|
|
115
|
+
did : "{ID}", // entity ID
|
|
116
116
|
meta : {...}, // only for handshaking with new friend
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
documents : [...], // when document is empty, means query for ID
|
|
118
|
+
last_time : 12345 // old document time for querying
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
"""
|
|
@@ -123,38 +123,37 @@ class BaseDocumentCommand(BaseMetaCommand, DocumentCommand):
|
|
|
123
123
|
def __init__(self, content: Dict[str, Any] = None,
|
|
124
124
|
identifier: ID = None,
|
|
125
125
|
meta: Optional[Meta] = None,
|
|
126
|
-
|
|
126
|
+
documents: List[Document] = None,
|
|
127
127
|
last_time: Optional[DateTime] = None):
|
|
128
128
|
if content is None:
|
|
129
129
|
# 1. new command with ID, meta, document & signature
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
identifier = document.identifier
|
|
133
|
-
assert identifier is not None, 'document command error: %s, %s, %s' % (meta, document, last_time)
|
|
134
|
-
cmd = Command.DOCUMENT
|
|
130
|
+
assert identifier is not None, 'document command error: %s, %s, %s' % (meta, documents, last_time)
|
|
131
|
+
cmd = Command.DOCUMENTS
|
|
135
132
|
super().__init__(cmd=cmd, identifier=identifier, meta=meta)
|
|
136
133
|
# respond with document info
|
|
137
|
-
if
|
|
138
|
-
self['
|
|
134
|
+
if documents is not None:
|
|
135
|
+
self['documents'] = Document.revert(documents=documents)
|
|
139
136
|
# query with last document time
|
|
140
137
|
if last_time is not None:
|
|
141
138
|
self.set_datetime(key='last_time', value=last_time)
|
|
142
139
|
else:
|
|
143
140
|
# 2. command info from network
|
|
144
|
-
assert identifier is None and meta is None and
|
|
145
|
-
'params error: %s, %s, %s, %s, %s' % (content, identifier, meta,
|
|
141
|
+
assert identifier is None and meta is None and documents is None and last_time is None, \
|
|
142
|
+
'params error: %s, %s, %s, %s, %s' % (content, identifier, meta, documents, last_time)
|
|
146
143
|
super().__init__(content)
|
|
147
144
|
# lazy load
|
|
148
|
-
self.
|
|
145
|
+
self.__docs = documents
|
|
149
146
|
|
|
150
147
|
#
|
|
151
148
|
# document
|
|
152
149
|
#
|
|
153
150
|
@property # Override
|
|
154
|
-
def
|
|
155
|
-
if self.
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
def documents(self) -> Optional[List[Document]]:
|
|
152
|
+
if self.__docs is None:
|
|
153
|
+
docs = self.get('documents')
|
|
154
|
+
if docs is not None:
|
|
155
|
+
self.__docs = Document.convert(array=docs)
|
|
156
|
+
return self.__docs
|
|
158
157
|
|
|
159
158
|
@property # Override
|
|
160
159
|
def last_time(self) -> Optional[DateTime]:
|