python-plugins 0.2.2__py3-none-any.whl → 0.2.5__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.
- python_plugins/__about__.py +1 -1
- python_plugins/crypto/zipmix.py +264 -0
- python_plugins/email/__init__.py +1 -0
- python_plugins/email/smtp.py +23 -6
- {python_plugins-0.2.2.dist-info → python_plugins-0.2.5.dist-info}/METADATA +1 -1
- {python_plugins-0.2.2.dist-info → python_plugins-0.2.5.dist-info}/RECORD +8 -7
- {python_plugins-0.2.2.dist-info → python_plugins-0.2.5.dist-info}/WHEEL +0 -0
- {python_plugins-0.2.2.dist-info → python_plugins-0.2.5.dist-info}/licenses/LICENSE.rst +0 -0
python_plugins/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.2.
|
|
1
|
+
__version__ = "0.2.5"
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import random
|
|
3
|
+
import hashlib
|
|
4
|
+
import tempfile
|
|
5
|
+
import filecmp
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ZipMix:
|
|
11
|
+
def __init__(self, zipcmd="7z"):
|
|
12
|
+
self.zipcmd = zipcmd
|
|
13
|
+
self.z7_available = self.check_7z_available()
|
|
14
|
+
|
|
15
|
+
def check_7z_available(self) -> bool:
|
|
16
|
+
try:
|
|
17
|
+
subprocess.run([self.zipcmd], capture_output=True, check=True)
|
|
18
|
+
return True
|
|
19
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
def create_mix_flag(self, pos: int, nlen: int) -> bytes:
|
|
23
|
+
"""m{}i{}x{}{nlen}{}"""
|
|
24
|
+
start_bytes = pos.to_bytes(4, byteorder="big")
|
|
25
|
+
return b"m%si%sx%s%s%s" % (
|
|
26
|
+
start_bytes[0:1],
|
|
27
|
+
start_bytes[1:2],
|
|
28
|
+
start_bytes[2:3],
|
|
29
|
+
nlen.to_bytes(1, byteorder="big"),
|
|
30
|
+
start_bytes[3:4],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def int_to_bytes(self, pos: int):
|
|
34
|
+
return pos.to_bytes(4, byteorder="big")
|
|
35
|
+
|
|
36
|
+
def int_from_bytes(self, intbs):
|
|
37
|
+
return int.from_bytes(intbs, byteorder="big")
|
|
38
|
+
|
|
39
|
+
def extract_from_mix_flag(self, mix_flag):
|
|
40
|
+
fix_flag = mix_flag[0:1] + mix_flag[2:3] + mix_flag[4:5]
|
|
41
|
+
if fix_flag != b"mix":
|
|
42
|
+
raise ValueError("not match mix.")
|
|
43
|
+
nlen = self.int_from_bytes(mix_flag[6:7])
|
|
44
|
+
pos = self.int_from_bytes(
|
|
45
|
+
mix_flag[1:2] + mix_flag[3:4] + mix_flag[5:6] + mix_flag[7:8]
|
|
46
|
+
)
|
|
47
|
+
return (pos, nlen)
|
|
48
|
+
|
|
49
|
+
def insert_string_to_binary(self, data, insert_bytes):
|
|
50
|
+
n_data = len(data)
|
|
51
|
+
n_ibytes = len(insert_bytes)
|
|
52
|
+
pos_list = sorted(random.sample(range(min(n_data, 0xFFFFFFFF)), n_ibytes))
|
|
53
|
+
|
|
54
|
+
parts = []
|
|
55
|
+
pre_pos = 0
|
|
56
|
+
|
|
57
|
+
for i, curr_pos in enumerate(pos_list):
|
|
58
|
+
if i == 0:
|
|
59
|
+
end_flag = self.create_mix_flag(curr_pos, n_ibytes)
|
|
60
|
+
else:
|
|
61
|
+
parts.append(self.int_to_bytes(curr_pos))
|
|
62
|
+
|
|
63
|
+
parts.append(data[pre_pos:curr_pos])
|
|
64
|
+
parts.append(insert_bytes[i : i + 1])
|
|
65
|
+
pre_pos = curr_pos
|
|
66
|
+
|
|
67
|
+
parts.append(data[pre_pos:])
|
|
68
|
+
parts.append(end_flag)
|
|
69
|
+
return b"".join(parts)
|
|
70
|
+
|
|
71
|
+
def extract_string_from_binary(self, data):
|
|
72
|
+
start_pos, n_insert_bytes = self.extract_from_mix_flag(data[-8:])
|
|
73
|
+
parts = []
|
|
74
|
+
insert_parts = []
|
|
75
|
+
pos_list = [start_pos]
|
|
76
|
+
data_pre_pos = 0
|
|
77
|
+
curr_pos = start_pos
|
|
78
|
+
|
|
79
|
+
for k in range(n_insert_bytes):
|
|
80
|
+
data_curr_pos = curr_pos + 5 * k
|
|
81
|
+
parts.append(data[data_pre_pos:data_curr_pos])
|
|
82
|
+
insert_parts.append(data[data_curr_pos : data_curr_pos + 1])
|
|
83
|
+
if k < n_insert_bytes - 1:
|
|
84
|
+
curr_pos = self.int_from_bytes(
|
|
85
|
+
data[data_curr_pos + 1 : data_curr_pos + 5]
|
|
86
|
+
)
|
|
87
|
+
pos_list.append(curr_pos)
|
|
88
|
+
data_pre_pos = data_curr_pos + 5
|
|
89
|
+
else:
|
|
90
|
+
curr_pos = None
|
|
91
|
+
data_pre_pos = data_curr_pos + 1
|
|
92
|
+
|
|
93
|
+
parts.append(data[data_pre_pos:-8])
|
|
94
|
+
return b"".join(insert_parts), b"".join(parts)
|
|
95
|
+
|
|
96
|
+
def _generate_password(self) -> str:
|
|
97
|
+
return hashlib.sha256(os.urandom(1000)).hexdigest()[:32]
|
|
98
|
+
|
|
99
|
+
def zip7(self, file_or_dir, archive_path=None, pwd=None, force_pwd=False,silent = True):
|
|
100
|
+
path_obj = Path(file_or_dir)
|
|
101
|
+
if not path_obj.exists():
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
if archive_path is None:
|
|
105
|
+
archive_path = file_or_dir + ".7z"
|
|
106
|
+
|
|
107
|
+
if pwd is None:
|
|
108
|
+
if force_pwd:
|
|
109
|
+
pwd = self._generate_password()
|
|
110
|
+
else:
|
|
111
|
+
pwd = pwd[:32]
|
|
112
|
+
|
|
113
|
+
cmd = [self.zipcmd, "a"]
|
|
114
|
+
if silent:
|
|
115
|
+
cmd.append("-bso0")
|
|
116
|
+
if pwd:
|
|
117
|
+
cmd.extend([f"-p{pwd}", "-mhe=on"])
|
|
118
|
+
cmd.extend([archive_path, file_or_dir])
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
subprocess.run(cmd, check=True)
|
|
122
|
+
if pwd is not None:
|
|
123
|
+
return ("ok", archive_path, pwd)
|
|
124
|
+
else:
|
|
125
|
+
return ("ok", archive_path)
|
|
126
|
+
except subprocess.CalledProcessError as e:
|
|
127
|
+
return ("fail", f"{e}")
|
|
128
|
+
|
|
129
|
+
def unzip7_list_archive(self, archive_path, pwd=None,silent = True):
|
|
130
|
+
cmd = [self.zipcmd, "l"]
|
|
131
|
+
if silent:
|
|
132
|
+
cmd.append("-bso0")
|
|
133
|
+
if pwd:
|
|
134
|
+
cmd.append(f"-p{pwd}")
|
|
135
|
+
cmd.append(archive_path)
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
subprocess.run(cmd, check=True)
|
|
139
|
+
return ("ok", archive_path)
|
|
140
|
+
except subprocess.CalledProcessError as e:
|
|
141
|
+
return ("fail", f"{e}")
|
|
142
|
+
|
|
143
|
+
def unzip7(self, archive_path, output_path=None, pwd=None, overwrite: bool = True,silent = True):
|
|
144
|
+
archive_obj = Path(archive_path)
|
|
145
|
+
|
|
146
|
+
if output_path is None:
|
|
147
|
+
output_dir = archive_obj.parent
|
|
148
|
+
else:
|
|
149
|
+
output_dir = Path(output_path)
|
|
150
|
+
|
|
151
|
+
if not output_dir.exists():
|
|
152
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
|
|
154
|
+
if not output_dir.is_dir():
|
|
155
|
+
return ("fail", f"{output_dir} is not dir")
|
|
156
|
+
|
|
157
|
+
cmd = [self.zipcmd, "x", "-y"]
|
|
158
|
+
if silent:
|
|
159
|
+
cmd.append("-bso0")
|
|
160
|
+
if not overwrite:
|
|
161
|
+
cmd.append("-aos")
|
|
162
|
+
if pwd:
|
|
163
|
+
cmd.append(f"-p{pwd}")
|
|
164
|
+
cmd.extend([archive_path, f"-o{output_dir}"])
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
subprocess.run(cmd, check=True)
|
|
168
|
+
return ("ok", f"{output_dir}")
|
|
169
|
+
except subprocess.CalledProcessError as e:
|
|
170
|
+
return ("fail", f"{e}")
|
|
171
|
+
|
|
172
|
+
def zip7mix(self, file_or_dir, archive_path=None,silent = True):
|
|
173
|
+
path_obj = Path(file_or_dir)
|
|
174
|
+
if not path_obj.exists():
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
if archive_path is None:
|
|
178
|
+
archive_path = file_or_dir + ".7z"
|
|
179
|
+
|
|
180
|
+
tmp_archive_path = archive_path+".tmp"
|
|
181
|
+
|
|
182
|
+
tmp_obj = Path(tmp_archive_path)
|
|
183
|
+
if tmp_obj.exists():
|
|
184
|
+
tmp_obj.unlink()
|
|
185
|
+
|
|
186
|
+
pwd = self._generate_password()
|
|
187
|
+
|
|
188
|
+
cmd = [self.zipcmd, "a"]
|
|
189
|
+
if silent:
|
|
190
|
+
cmd.append("-bso0")
|
|
191
|
+
cmd.extend([f"-p{pwd}", "-mhe=on"])
|
|
192
|
+
cmd.extend([tmp_archive_path, file_or_dir])
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
subprocess.run(cmd, check=True)
|
|
196
|
+
except subprocess.CalledProcessError as e:
|
|
197
|
+
return ("fail", f"{e}")
|
|
198
|
+
|
|
199
|
+
with open(tmp_archive_path,"rb") as f1:
|
|
200
|
+
data = f1.read()
|
|
201
|
+
|
|
202
|
+
# remove tmp file
|
|
203
|
+
tmp_obj.unlink()
|
|
204
|
+
|
|
205
|
+
new_data = self.insert_string_to_binary(data,pwd.encode('utf-8'))
|
|
206
|
+
|
|
207
|
+
# append fake 7z tail
|
|
208
|
+
new_data += data[-100:]
|
|
209
|
+
|
|
210
|
+
with open(archive_path,"wb") as f2:
|
|
211
|
+
f2.write(new_data)
|
|
212
|
+
|
|
213
|
+
return ("ok", archive_path)
|
|
214
|
+
|
|
215
|
+
def unzip7mix(self, archive_path, output_path=None, overwrite: bool = True,silent = True):
|
|
216
|
+
archive_obj = Path(archive_path)
|
|
217
|
+
|
|
218
|
+
tmp_archive_path = archive_path+".tmp"
|
|
219
|
+
|
|
220
|
+
tmp_obj = Path(tmp_archive_path)
|
|
221
|
+
if tmp_obj.exists():
|
|
222
|
+
tmp_obj.unlink()
|
|
223
|
+
|
|
224
|
+
if output_path is None:
|
|
225
|
+
output_dir = archive_obj.parent
|
|
226
|
+
else:
|
|
227
|
+
output_dir = Path(output_path)
|
|
228
|
+
|
|
229
|
+
if not output_dir.exists():
|
|
230
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
|
|
232
|
+
if not output_dir.is_dir():
|
|
233
|
+
return ("fail", f"{output_dir} is not dir")
|
|
234
|
+
|
|
235
|
+
with open(archive_path,"rb") as f1:
|
|
236
|
+
data = f1.read()
|
|
237
|
+
# remove fake 7z tail
|
|
238
|
+
data = data[:-100]
|
|
239
|
+
|
|
240
|
+
pwdbts,new_data = self.extract_string_from_binary(data)
|
|
241
|
+
|
|
242
|
+
pwd = pwdbts.decode("utf-8")
|
|
243
|
+
|
|
244
|
+
with open(tmp_archive_path,"wb") as f2:
|
|
245
|
+
f2.write(new_data)
|
|
246
|
+
|
|
247
|
+
cmd = [self.zipcmd, "x", "-y"]
|
|
248
|
+
if silent:
|
|
249
|
+
cmd.append("-bso0")
|
|
250
|
+
if not overwrite:
|
|
251
|
+
cmd.append("-aos")
|
|
252
|
+
cmd.append(f"-p{pwd}")
|
|
253
|
+
cmd.extend([tmp_archive_path, f"-o{output_dir}"])
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
subprocess.run(cmd, check=True)
|
|
257
|
+
tmp_obj.unlink()
|
|
258
|
+
return ("ok", f"{output_dir}")
|
|
259
|
+
except subprocess.CalledProcessError as e:
|
|
260
|
+
return ("fail", f"{e}")
|
|
261
|
+
|
|
262
|
+
def filecmp(self,f1,f2):
|
|
263
|
+
result = filecmp.cmp(f1, f2, shallow=False)
|
|
264
|
+
return result
|
python_plugins/email/__init__.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .smtp import SmtpSSL
|
python_plugins/email/smtp.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import smtplib
|
|
2
2
|
from email.message import EmailMessage
|
|
3
|
+
import mimetypes
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class SmtpSSL:
|
|
@@ -12,11 +13,7 @@ class SmtpSSL:
|
|
|
12
13
|
def send_emsg(self, data):
|
|
13
14
|
emsg = EmailMessage()
|
|
14
15
|
emsg["Subject"] = data["subject"]
|
|
15
|
-
emsg.
|
|
16
|
-
|
|
17
|
-
if data.get("From") is None:
|
|
18
|
-
emsg["From"] = self.user
|
|
19
|
-
|
|
16
|
+
emsg["From"] = data.get("From", self.user)
|
|
20
17
|
emsg["To"] = data["to"]
|
|
21
18
|
|
|
22
19
|
if "cc" in data:
|
|
@@ -25,7 +22,27 @@ class SmtpSSL:
|
|
|
25
22
|
if "bcc" in data:
|
|
26
23
|
emsg["Bcc"] = data["bcc"]
|
|
27
24
|
|
|
28
|
-
#
|
|
25
|
+
# Check if HTML content is provided
|
|
26
|
+
if "html_content" in data:
|
|
27
|
+
emsg.add_alternative(data["html_content"], subtype="html")
|
|
28
|
+
else:
|
|
29
|
+
emsg.set_content(data["content"])
|
|
30
|
+
|
|
31
|
+
# Add attachments if provided
|
|
32
|
+
if "attachments" in data:
|
|
33
|
+
for file_path in data["attachments"]:
|
|
34
|
+
ctype, encoding = mimetypes.guess_type(file_path)
|
|
35
|
+
if ctype is None or encoding is not None:
|
|
36
|
+
ctype = "application/octet-stream"
|
|
37
|
+
maintype, subtype = ctype.split("/", 1)
|
|
38
|
+
|
|
39
|
+
with open(file_path, "rb") as f:
|
|
40
|
+
emsg.add_attachment(
|
|
41
|
+
f.read(),
|
|
42
|
+
maintype=maintype,
|
|
43
|
+
subtype=subtype,
|
|
44
|
+
filename=file_path.split("\\")[-1],
|
|
45
|
+
)
|
|
29
46
|
|
|
30
47
|
with smtplib.SMTP_SSL(self.host, self.port) as smtp:
|
|
31
48
|
# smtp.set_debuglevel(1)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
python_plugins/__about__.py,sha256=
|
|
1
|
+
python_plugins/__about__.py,sha256=Xsa3ayOMVkhUWm4t06YeyHE0apjpZefxLH4ylp0CDtU,22
|
|
2
2
|
python_plugins/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
3
3
|
python_plugins/convert/__init__.py,sha256=mQeBTbhjuTJkyG4DhXkoX9CNP1TDEgPBFIJ2TQNl2NM,119
|
|
4
4
|
python_plugins/convert/datetime_str.py,sha256=bmC9d1W7XKA1ED9XO9reUiUyfQcM_BqGoGyOF21RW9A,282
|
|
@@ -8,10 +8,11 @@ python_plugins/crypto/__init__.py,sha256=2Snt9AIwVsQ-qLNno8qGWs8IyL-AvTImvqkRFO8
|
|
|
8
8
|
python_plugins/crypto/fernet.py,sha256=UgEAHJGBtmH9AacD-IOyr9dR8T5PKTWL8kH5yj2wKqI,1191
|
|
9
9
|
python_plugins/crypto/file_to_file.py,sha256=mS9llG3j0emhCB4NY6IlKew6wpoLyVW-008ZXN1v3iw,1764
|
|
10
10
|
python_plugins/crypto/str_to_list.py,sha256=T2I4p6bcZuiCsEve4XsOb1q9hFxeVPITstjPyTVoATE,2514
|
|
11
|
+
python_plugins/crypto/zipmix.py,sha256=_pB-Oi7Kl4BEp8S2hynz41_Zblfpf5Z7HzWpaarHRak,8046
|
|
11
12
|
python_plugins/dumps/__init__.py,sha256=TLPuPLIAuULFkiIbFfTCsuhH0CQhPRMUjBmcsr0mtqA,83
|
|
12
13
|
python_plugins/dumps/postgresql_dump.py,sha256=zN5aLYLxpon2y9_5gKizRjlrRUlkAO-dG-BuKpJ4F3U,1030
|
|
13
|
-
python_plugins/email/__init__.py,sha256=
|
|
14
|
-
python_plugins/email/smtp.py,sha256=
|
|
14
|
+
python_plugins/email/__init__.py,sha256=HubZT2h3m5WEjic3NZ0H7gYp-9tUgZaDkAE9O-Tm7hM,26
|
|
15
|
+
python_plugins/email/smtp.py,sha256=CY1fWxdBDqqGZ22R8yqEvFZvMeEsMnqTb4h5IZ8WAVU,1641
|
|
15
16
|
python_plugins/examples/higher_order_functions.py,sha256=cBewE3cDkzaNHR6CQzdfWny0r_V4PgcUeZpHEdCp-E0,633
|
|
16
17
|
python_plugins/hashes/__init__.py,sha256=PQbx8f4klFMg5Q8kehSHz1itj_djWV5hkBT4AMNNUn0,39
|
|
17
18
|
python_plugins/hashes/hash.py,sha256=uSF3ohZRD7b2VAIvjjpTWC0SlY7QsFhsFZUejINrRGU,640
|
|
@@ -39,7 +40,7 @@ python_plugins/weixin/format_response.py,sha256=Eml9R1jYYOCtfXORyEWfHbCL8gauWeg2
|
|
|
39
40
|
python_plugins/weixin/wechat.py,sha256=D6KHZSNj_19vCs5rxLSrHt5XkjkM4ZoD2z6VWD0a_b4,4296
|
|
40
41
|
python_plugins/weixin/wechat_crypt.py,sha256=YOCy4F5ycfZFOJYtKSzhiFSfNb_MsQnN2Qy9u_FfZSU,4717
|
|
41
42
|
python_plugins/weixin/weixin_api.py,sha256=xtnWBovJKNMdm-jXmIM7QHnMye-fZN4a4U_efejRarE,3286
|
|
42
|
-
python_plugins-0.2.
|
|
43
|
-
python_plugins-0.2.
|
|
44
|
-
python_plugins-0.2.
|
|
45
|
-
python_plugins-0.2.
|
|
43
|
+
python_plugins-0.2.5.dist-info/METADATA,sha256=sZM0pfD-ulzFSNGGFZUQ7ghF4zZTl9C3SsCu78f3YQU,2888
|
|
44
|
+
python_plugins-0.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
45
|
+
python_plugins-0.2.5.dist-info/licenses/LICENSE.rst,sha256=X5eLIsAn1yAKd88LWTYkXUe0PmVK_Z5SD7_5NheGR-s,1104
|
|
46
|
+
python_plugins-0.2.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|