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.
@@ -1 +1 @@
1
- __version__ = "0.2.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
@@ -0,0 +1 @@
1
+ from .smtp import SmtpSSL
@@ -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.set_content(data["content"])
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
- # print(emsg)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-plugins
3
- Version: 0.2.2
3
+ Version: 0.2.5
4
4
  Summary: A collection of Python functions and classes.
5
5
  Project-URL: Documentation, https://python-plugins.readthedocs.io
6
6
  Project-URL: Source, https://github.com/ojso/python-plugins
@@ -1,4 +1,4 @@
1
- python_plugins/__about__.py,sha256=m6kyaNpwBcP1XYcqrelX2oS3PJuOnElOcRdBa9pEb8c,22
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=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- python_plugins/email/smtp.py,sha256=weSfLVPzROFqwlxDaVhjem522NVEp31lc6PFkbZE1ok,854
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.2.dist-info/METADATA,sha256=lXLtCipynBJOeJmj8mBv0GgiPzm1kpQaegfOPr2074k,2888
43
- python_plugins-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
- python_plugins-0.2.2.dist-info/licenses/LICENSE.rst,sha256=X5eLIsAn1yAKd88LWTYkXUe0PmVK_Z5SD7_5NheGR-s,1104
45
- python_plugins-0.2.2.dist-info/RECORD,,
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,,