atomicshop 2.16.44__py3-none-any.whl → 2.16.45__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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.16.44'
4
+ __version__ = '2.16.45'
@@ -65,8 +65,9 @@ def _handle_callback_matching(
65
65
  callback_result = callback(archived_file_bytes)
66
66
  if callback_result:
67
67
  # Initialize key for callback function name if not present
68
- if callback.__name__ not in results:
69
- results[callback.__name__] = []
68
+ if _get_callback_name(callback) not in results:
69
+ results[_get_callback_name(callback)]: dict = {}
70
+ results[_get_callback_name(callback)]['files']: list = []
70
71
 
71
72
  if archive_type == 'zip':
72
73
  file_info = {
@@ -82,7 +83,8 @@ def _handle_callback_matching(
82
83
  'size': item.uncompressed,
83
84
  'modified_time': item.creationtime
84
85
  }
85
- results[callback.__name__].append(file_info)
86
+ results[_get_callback_name(callback)]['files'].append(file_info)
87
+ results[_get_callback_name(callback)]['callable_result'] = callback_result
86
88
  if return_first_only:
87
89
  found_set.add(item.filename)
88
90
  return True
@@ -149,11 +151,42 @@ def _search_in_archive(
149
151
  break # All files found, stop searching
150
152
 
151
153
 
152
- def _initialize_results(callback_functions):
153
- if callback_functions:
154
- return {callback.__name__: [] for callback in callback_functions}
154
+ def _get_callback_name(callback: callable) -> str:
155
+ """
156
+ Get the name of the callback function.
157
+ :param callback: callable.
158
+ :return: string, the name of the callback function. If the function is part of the class, the name will be
159
+ 'class_name.function_name', if not the only the function name will be returned.
160
+ """
161
+ try:
162
+ class_name = callback.__self__.__class__.__name__
163
+ except AttributeError:
164
+ class_name = None
165
+
166
+ function_name = callback.__name__
167
+
168
+ if class_name:
169
+ return f"{class_name}.{function_name}"
155
170
  else:
156
- return {}
171
+ return function_name
172
+
173
+
174
+ # def _initialize_results(callback_functions: list[callable]) -> dict:
175
+ # """
176
+ # Initialize the results dictionary.
177
+ # :param callback_functions:
178
+ # :return:
179
+ # """
180
+ # if callback_functions:
181
+ # result_dict: dict = {}
182
+ # for callback in callback_functions:
183
+ # result_dict[_get_callback_name(callback)]: dict = {}
184
+ # result_dict[_get_callback_name(callback)]['files']: list = []
185
+ # result_dict[_get_callback_name(callback)]['callable_result']: any = None
186
+ #
187
+ # return result_dict
188
+ # else:
189
+ # return {}
157
190
 
158
191
 
159
192
  def _get_archive_type(file_object) -> Union[str, None]:
@@ -212,7 +245,7 @@ def search_file_in_archive(
212
245
  recursive: bool = False,
213
246
  callback_functions: list = None,
214
247
  extract_file_to_path: str = None
215
- ) -> dict[str, list[bytes]]:
248
+ ) -> dict[list[bytes], str]:
216
249
  """
217
250
  Function searches for the file names inside the zip file and returns a dictionary where the keys are the
218
251
  names of the callback functions and the values are lists of found file bytes.
@@ -236,7 +269,8 @@ def search_file_in_archive(
236
269
  raise ValueError("Either file_names_to_search or callback_functions must be provided.")
237
270
 
238
271
  # Initialize results dictionary.
239
- results = _initialize_results(callback_functions)
272
+ # results = _initialize_results(callback_functions)
273
+ results: dict[list[bytes], str] = {}
240
274
  found_set = set()
241
275
 
242
276
  _search_archive_content(
@@ -1,8 +1,10 @@
1
1
  import os
2
2
  import sys
3
3
  import ast
4
- import importlib
5
4
  from pathlib import Path
5
+ import pkgutil
6
+ import importlib
7
+ import inspect
6
8
 
7
9
  from ..file_io.file_io import read_file
8
10
 
@@ -26,6 +28,63 @@ class ParserParent:
26
28
  """
27
29
 
28
30
 
31
+ def get_list_of_classes_in_module(
32
+ imported_package,
33
+ imported_base_class
34
+ ) -> list:
35
+ """
36
+ Function that returns a list of classes that are subclasses of the imported_base_class from the imported_package.
37
+
38
+ Example:
39
+ # Package structure:
40
+ # unpackers
41
+ # ├── __init__.py
42
+ # ├── unpacker_base.py
43
+ # ├── unpacker_1.py
44
+ # ├── unpacker_2.py
45
+ # ├── unpacker_3.py
46
+ # └── ... (other unpacker modules)
47
+
48
+ # unpacker_base.py:
49
+ from abc import abstractmethod
50
+ class Unpacker:
51
+ @abstractmethod
52
+ def unpack(self, file_path):
53
+ pass
54
+
55
+ # unpacker_1.py:
56
+ from unpackers.unpacker_base import Unpacker
57
+ class Unpacker1(Unpacker):
58
+ def unpack(self, file_path):
59
+ print(f"Unpacking file with Unpacker1: {file_path}")
60
+
61
+ # main_script.py:
62
+ # Import the base class
63
+ from unpackers.unpacker_base import Unpacker
64
+ # Import the package
65
+ import unpackers
66
+ # Get the list of classes
67
+ unpacker_classes = get_list_of_classes_in_module(imported_package=unpackers, imported_base_class=Unpacker)
68
+
69
+ :param imported_package:
70
+ :param imported_base_class:
71
+ :return:
72
+ """
73
+ unpacker_classes = []
74
+
75
+ # Iterate over all modules in the 'imported_package' package
76
+ for loader, module_name, is_pkg in pkgutil.iter_modules(imported_package.__path__):
77
+ # Import the module
78
+ module = importlib.import_module(f"{imported_package.__name__}.{module_name}")
79
+
80
+ # Inspect module members to find Unpacker subclasses
81
+ for name, obj in inspect.getmembers(module, inspect.isclass):
82
+ if issubclass(obj, imported_base_class) and obj is not imported_base_class:
83
+ unpacker_classes.append(obj)
84
+
85
+ return unpacker_classes
86
+
87
+
29
88
  def create_empty_class():
30
89
  """
31
90
  Function creates empty class, you can add parameters to it dynamically.
@@ -1,6 +1,7 @@
1
1
  from datetime import datetime
2
2
 
3
3
  from ..wrappers.socketw import receiver, sender, socket_client, base
4
+ from .. import websocket_parse
4
5
  from ..http_parse import HTTPRequestParse, HTTPResponseParse
5
6
  from ..basics import threads, tracebacks
6
7
  from ..print_api import print_api
@@ -65,6 +66,7 @@ def thread_worker_main(
65
66
  raise ValueError(f"Error in statistics error list. Values: {statistics_error_list}")
66
67
 
67
68
  statistics_writer.write_row(
69
+ thread_id=str(thread_id),
68
70
  host=client_message.server_name,
69
71
  tls_type=tls_type,
70
72
  tls_version=tls_version,
@@ -92,12 +94,15 @@ def thread_worker_main(
92
94
 
93
95
  def parse_http():
94
96
  nonlocal error_message
97
+ nonlocal protocol
95
98
  # Parsing the raw bytes as HTTP.
96
99
  request_decoded, is_http_request, request_parsing_info, request_parsing_error = (
97
100
  HTTPRequestParse(client_message.request_raw_bytes).parse())
98
101
 
99
102
  if is_http_request:
100
- client_message.protocol = 'HTTP'
103
+ if protocol == '':
104
+ protocol = 'HTTP'
105
+
101
106
  client_message.request_raw_decoded = request_decoded
102
107
  print_api(request_parsing_info, logger=network_logger, logger_method='info')
103
108
  network_logger.info(f"Method: {request_decoded.command} | Path: {request_decoded.path}")
@@ -107,6 +112,29 @@ def thread_worker_main(
107
112
  # statistics_error_list.append(error_message)
108
113
  print_api(request_parsing_error, logger=network_logger, logger_method='error', color='yellow')
109
114
 
115
+ is_http_request_a_websocket()
116
+
117
+ def is_http_request_a_websocket():
118
+ nonlocal protocol
119
+
120
+ if protocol == 'HTTP':
121
+ if (client_message.request_raw_decoded and
122
+ hasattr(client_message.request_raw_decoded, 'headers') and
123
+ 'Upgrade' in client_message.request_raw_decoded.headers):
124
+ if client_message.request_raw_decoded.headers['Upgrade'] == 'websocket':
125
+ protocol = 'Websocket'
126
+
127
+ network_logger.info(f'Protocol upgraded to Websocket')
128
+
129
+ def parse_websocket(raw_bytes):
130
+ is_deflated = websocket_parse.is_frame_deflated(raw_bytes)
131
+ request_decoded = websocket_frame_parser.parse_frame_bytes(raw_bytes)
132
+
133
+ return {
134
+ 'is_deflated': is_deflated,
135
+ 'frame': request_decoded
136
+ }
137
+
110
138
  def finish_thread():
111
139
  # At this stage there could be several times that the same socket was used to the service server - we need to
112
140
  # close this socket as well if it still opened.
@@ -141,6 +169,13 @@ def thread_worker_main(
141
169
  thread_id = threads.current_thread_id()
142
170
  client_message.thread_id = thread_id
143
171
 
172
+ protocol: str = str()
173
+ # # This is Client Masked Frame Parser.
174
+ # websocket_masked_frame_parser = websocket_parse.WebsocketFrameParser()
175
+ # # This is Server UnMasked Frame Parser.
176
+ # websocket_unmasked_frame_parser = websocket_parse.WebsocketFrameParser()
177
+ websocket_frame_parser = websocket_parse.WebsocketFrameParser()
178
+
144
179
  # Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
145
180
  # These should be outside any loop and initialized only once entering the thread.
146
181
  parser, responder, recorder = assign_class_by_domain(
@@ -201,6 +236,12 @@ def thread_worker_main(
201
236
  client_message.request_raw_bytes = client_received_raw_data
202
237
 
203
238
  parse_http()
239
+ if protocol != '':
240
+ client_message.protocol = protocol
241
+
242
+ # Parse websocket frames only if it is not the first protocol upgrade request.
243
+ if protocol == 'Websocket' and cycle_count != 0:
244
+ client_message.request_raw_decoded = parse_websocket(client_message.request_raw_bytes)
204
245
 
205
246
  # Custom parser, should parse HTTP body or the whole message if not HTTP.
206
247
  parser_instance = parser(client_message)
@@ -219,6 +260,12 @@ def thread_worker_main(
219
260
  # Re-initiate the 'client_message.response_list_of_raw_bytes' list, since we'll be appending
220
261
  # new entries for empty list.
221
262
  client_message.response_list_of_raw_bytes = list()
263
+
264
+ # If it's the first cycle and the protocol is Websocket, then we'll create the HTTP Handshake
265
+ # response automatically.
266
+ if protocol == 'Websocket' and cycle_count == 0:
267
+ client_message.response_list_of_raw_bytes.append(
268
+ websocket_parse.create_byte_http_response(client_message.request_raw_bytes))
222
269
  # Creating response for parsed message and printing
223
270
  responder.create_response(client_message)
224
271
 
@@ -283,9 +330,14 @@ def thread_worker_main(
283
330
 
284
331
  if is_http_response:
285
332
  client_message.response_list_of_raw_decoded.append(response_raw_decoded)
333
+ elif protocol == 'Websocket' and cycle_count != 0:
334
+ response_decoded = parse_websocket(response_raw_bytes)
335
+ client_message.response_list_of_raw_decoded.append(response_decoded)
286
336
  else:
287
337
  client_message.response_list_of_raw_decoded.append(None)
288
338
 
339
+
340
+
289
341
  # So if the socket was closed and there was an error we can break the loop
290
342
  if not service_ssl_socket:
291
343
  record_and_statistics_write()
@@ -47,9 +47,9 @@ class RecorderParent:
47
47
  self.build_record_path_to_engine()
48
48
 
49
49
  # If HTTP Path is not defined, 'http_path' will be empty, and it will not interfere with file name.
50
- self.record_file_path: str = \
51
- self.engine_record_path + os.sep + \
52
- day_time_format + "_" + self.class_client_message.server_name + self.file_extension
50
+ self.record_file_path: str = (
51
+ f"{self.engine_record_path}{os.sep}{day_time_format}_"
52
+ f"{self.class_client_message.server_name}{self.file_extension}")
53
53
 
54
54
  def convert_messages(self):
55
55
  """
@@ -59,78 +59,106 @@ def create_byte_http_response(
59
59
  raise ValueError("The event is not a Request object.")
60
60
 
61
61
 
62
- def parse_frame_bytes(data_bytes: bytes):
63
- # Define the read_exact function
64
- def read_exact(n: int) -> Generator[None, None, bytes]:
65
- return reader.read_exact(n)
66
-
67
- # Helper function to run generator-based coroutines
68
- def run_coroutine(coroutine):
69
- try:
70
- while True:
71
- next(coroutine)
72
- except StopIteration as e:
73
- return e.value
74
- except Exception as e:
75
- raise e # Re-raise exceptions to be handled by the caller
76
-
77
- # Function to parse frames
78
- def parse_frame(mask: bool):
79
- try:
80
- # Use Frame.parse to parse the frame
81
- frame_parser = Frame.parse(
82
- read_exact,
83
- mask=mask, # Client frames are masked
84
- max_size=None,
85
- extensions=[permessage_deflate], # Include the extension unconditionally
86
- )
87
- current_frame = run_coroutine(frame_parser)
88
- except EOFError as e:
89
- # Not enough data to parse a complete frame
90
- raise e
91
- except (ProtocolError, PayloadTooBig) as e:
92
- print("Error parsing frame:", e)
93
- raise e
94
- except Exception as e:
95
- print("Error parsing frame:", e)
96
- raise e
97
- return current_frame
98
-
99
- def process_frame(current_frame):
100
- if current_frame.opcode == Opcode.TEXT:
101
- message = current_frame.data.decode('utf-8', errors='replace')
102
- return message
103
- elif current_frame.opcode == Opcode.BINARY:
104
- return current_frame.data
105
- elif current_frame.opcode == Opcode.CLOSE:
106
- print("Received close frame")
107
- elif current_frame.opcode == Opcode.PING:
108
- print("Received ping")
109
- elif current_frame.opcode == Opcode.PONG:
110
- print("Received pong")
111
- else:
112
- raise WebsocketParseWrongOpcode("Received unknown frame with opcode:", current_frame.opcode)
113
-
114
- # Create the StreamReader instance
115
- reader = StreamReader()
116
-
117
- # Instantiate the permessage-deflate extension
118
- permessage_deflate = PerMessageDeflate(
119
- remote_no_context_takeover=False,
120
- local_no_context_takeover=False,
121
- remote_max_window_bits=15,
122
- local_max_window_bits=15,
123
- )
124
-
125
- masked = is_frame_masked(data_bytes)
62
+ class WebsocketFrameParser:
63
+ def __init__(self):
64
+ # Instantiate the permessage-deflate extension.
65
+ # If a frame uses 'deflate', then the 'permessage_deflate' should be the same object during parsing of
66
+ # several message on the same socket. Each time 'PerMessageDeflate' is initiated, the context changes
67
+ # and more than one message can't be parsed.
68
+ self.permessage_deflate_masked = PerMessageDeflate(
69
+ remote_no_context_takeover=False,
70
+ local_no_context_takeover=False,
71
+ remote_max_window_bits=15,
72
+ local_max_window_bits=15,
73
+ )
126
74
 
127
- # Feed the data into the reader
128
- reader.feed_data(data_bytes)
75
+ # We need separate instances for masked (frames from client) and unmasked (frames from server).
76
+ self.permessage_deflate_unmasked = PerMessageDeflate(
77
+ remote_no_context_takeover=False,
78
+ local_no_context_takeover=False,
79
+ remote_max_window_bits=15,
80
+ local_max_window_bits=15,
81
+ )
129
82
 
130
- # Parse and process frames
131
- frame = parse_frame(masked)
132
- result = process_frame(frame)
133
- return result
83
+ def parse_frame_bytes(
84
+ self,
85
+ data_bytes: bytes
86
+ ):
87
+ # Define the read_exact function
88
+ def read_exact(n: int) -> Generator[None, None, bytes]:
89
+ return reader.read_exact(n)
90
+
91
+ # Helper function to run generator-based coroutines
92
+ def run_coroutine(coroutine):
93
+ try:
94
+ while True:
95
+ next(coroutine)
96
+ except StopIteration as e:
97
+ return e.value
98
+ except Exception as e:
99
+ raise e # Re-raise exceptions to be handled by the caller
100
+
101
+ # Function to parse frames
102
+ def parse_frame(mask: bool, deflate: bool):
103
+ try:
104
+ if mask:
105
+ # Decide whether to include permessage-deflate extension
106
+ extensions = [self.permessage_deflate_masked] if deflate else []
107
+ else:
108
+ extensions = [self.permessage_deflate_unmasked] if deflate else []
109
+
110
+ # Use Frame.parse to parse the frame
111
+ frame_parser = Frame.parse(
112
+ read_exact,
113
+ mask=mask, # Client frames are masked
114
+ max_size=None,
115
+ extensions=extensions
116
+ )
117
+ current_frame = run_coroutine(frame_parser)
118
+ except EOFError as e:
119
+ # Not enough data to parse a complete frame
120
+ raise e
121
+ except (ProtocolError, PayloadTooBig) as e:
122
+ print("Error parsing frame:", e)
123
+ raise e
124
+ except Exception as e:
125
+ print("Error parsing frame:", e)
126
+ raise e
127
+ return current_frame
128
+
129
+ def process_frame(current_frame):
130
+ if current_frame.opcode == Opcode.TEXT:
131
+ message = current_frame.data.decode('utf-8', errors='replace')
132
+ return message
133
+ elif current_frame.opcode == Opcode.BINARY:
134
+ return current_frame.data
135
+ elif current_frame.opcode == Opcode.CLOSE:
136
+ print("Received close frame")
137
+ elif current_frame.opcode == Opcode.PING:
138
+ print("Received ping")
139
+ elif current_frame.opcode == Opcode.PONG:
140
+ print("Received pong")
141
+ else:
142
+ raise WebsocketParseWrongOpcode("Received unknown frame with opcode:", current_frame.opcode)
143
+
144
+ # Create the StreamReader instance
145
+ reader = StreamReader()
146
+
147
+ masked = is_frame_masked(data_bytes)
148
+ deflated = is_frame_deflated(data_bytes)
149
+
150
+ # Feed the data into the reader
151
+ reader.feed_data(data_bytes)
152
+
153
+ # Parse and process frames
154
+ frame = parse_frame(masked, deflated)
155
+ result = process_frame(frame)
156
+
157
+ # This is basically not needed since we restart the 'reader = StreamReader()' each function execution.
158
+ # # After processing, reset the reader's buffer
159
+ # reader.buffer = b''
160
+
161
+ return result
134
162
 
135
163
 
136
164
  def create_websocket_frame(
@@ -6,7 +6,7 @@ from ..loggingw import loggingw
6
6
 
7
7
  LOGGER_NAME: str = 'statistics'
8
8
  STATISTICS_HEADER: str = (
9
- 'request_time_sent,tls,protocol,host,path,command,status_code,request_size_bytes,response_size_bytes,file_path,'
9
+ 'request_time_sent,thread_id,tls,protocol,host,path,command,status_code,request_size_bytes,response_size_bytes,file_path,'
10
10
  'process_cmd,error')
11
11
 
12
12
 
@@ -30,6 +30,7 @@ class StatisticsCSVWriter:
30
30
 
31
31
  def write_row(
32
32
  self,
33
+ thread_id: str,
33
34
  host: str,
34
35
  tls_type: str,
35
36
  tls_version: str,
@@ -54,6 +55,7 @@ class StatisticsCSVWriter:
54
55
 
55
56
  escaped_line_string: str = csvs.escape_csv_line_to_string([
56
57
  request_time_sent,
58
+ thread_id,
57
59
  tls_info,
58
60
  protocol,
59
61
  host,
@@ -73,7 +75,8 @@ class StatisticsCSVWriter:
73
75
  self,
74
76
  error_message: str,
75
77
  host: str,
76
- process_name: str
78
+ process_name: str,
79
+ thread_id: str = str()
77
80
  ):
78
81
  """
79
82
  Write the error message to the statistics CSV file.
@@ -82,10 +85,12 @@ class StatisticsCSVWriter:
82
85
  :param error_message: string, error message.
83
86
  :param host: string, host, the domain or IP address.
84
87
  :param process_name: process name, the command line of the process.
88
+ :param thread_id: integer, the id of the thread.
85
89
  :return:
86
90
  """
87
91
 
88
92
  self.write_row(
93
+ thread_id=thread_id,
89
94
  host=host,
90
95
  tls_type='',
91
96
  tls_version='',
@@ -173,7 +173,7 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
173
173
 
174
174
  if ' ' in interface_settings['NameServer']:
175
175
  interface_settings['NameServer'] = interface_settings['NameServer'].replace(' ', ',')
176
- if ' ' in interface_settings['DhcpNameServer']:
176
+ if 'DhcpNameServer' in interface_settings and ' ' in interface_settings['DhcpNameServer']:
177
177
  interface_settings['DhcpNameServer'] = interface_settings['DhcpNameServer'].replace(' ', ',')
178
178
 
179
179
  if not default_dns_gateway_list:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.16.44
3
+ Version: 2.16.45
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=cTkolJcyCusHq9bBDXgphXYHAv5YCLiWysUXbZegGZc,124
1
+ atomicshop/__init__.py,sha256=fBF5eT1cIzArZVWu2launQIsr0jJhGhMkV0tYXx0zf4,124
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -45,7 +45,7 @@ atomicshop/urls.py,sha256=aJ0NGS9qqaKeqjkkWBs80jaBBg6MYBiPuLIyPGxscVc,1557
45
45
  atomicshop/uuids.py,sha256=JSQdm3ZTJiwPQ1gYe6kU0TKS_7suwVrHc8JZDGYlydM,2214
46
46
  atomicshop/virtualization.py,sha256=LPP4vjE0Vr10R6DA4lqhfX_WaNdDGRAZUW0Am6VeGco,494
47
47
  atomicshop/web.py,sha256=GLdTXgMxg1_0UQaXC4bOvARVyuFg7SPIeJdsCHV8rNE,11662
48
- atomicshop/websocket_parse.py,sha256=PygRqEFnqEcPx3PNRtJoZcKN-mXvT_zighzTJR_B-Tc,14934
48
+ atomicshop/websocket_parse.py,sha256=chqR3NSuW5Ndtclzj-46jCuQbyTh6umm1PBKvSkDzXw,16456
49
49
  atomicshop/a_installs/ubuntu/docker_rootless.py,sha256=9IPNtGZYjfy1_n6ZRt7gWz9KZgR6XCgevjqq02xk-o0,281
50
50
  atomicshop/a_installs/ubuntu/docker_sudo.py,sha256=JzayxeyKDtiuT4Icp2L2LyFRbx4wvpyN_bHLfZ-yX5E,281
51
51
  atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py,sha256=yRB-l1zBxdiN6av-FwNkhcBlaeu4zrDPjQ0uPGgpK2I,244
@@ -76,7 +76,7 @@ atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=cbvukIT
76
76
  atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=T2Ncs0MwKlAaCq8UKFMvfQAfcJdDx-nWiHVBfglrLIU,2112
77
77
  atomicshop/archiver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
79
- atomicshop/archiver/search_in_archive.py,sha256=XAxa0QukkXZvPmKna8cpwBHLDEskRUtbgaW1eHvpn6w,10833
79
+ atomicshop/archiver/search_in_archive.py,sha256=aj35JFNL4Gx1ZBfcnr5MpDhnfOWoOvmnt_ye1KhDQCI,12133
80
80
  atomicshop/archiver/sevenz_app_w.py,sha256=BWcJb4f7jZEiETDBKyNLE0f5YLFPQx6B91_ObEIXWf8,3007
81
81
  atomicshop/archiver/sevenzs.py,sha256=5i_C50-deC1Cz_GQdMGofV2jeMPbbGWAvih-QnA72cg,1370
82
82
  atomicshop/archiver/shutils.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
@@ -86,7 +86,7 @@ atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX
86
86
  atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
87
87
  atomicshop/basics/booleans.py,sha256=V36NaMf8AffhCom_ovQeOZlYcdtGyIcQwWKki6h7O0M,1745
88
88
  atomicshop/basics/bytes_arrays.py,sha256=tU7KF0UAFSErGNcan4Uz1GJxeIYVRuUu9sHVZHgyNnw,6542
89
- atomicshop/basics/classes.py,sha256=T0Bm13hKvkXG3med68ptL7XuoWiCi3TE-K5TMINDlrY,10655
89
+ atomicshop/basics/classes.py,sha256=UayCzPs3eynI3wOzSu-2IJSmTwOB4HwPwgVI2F-7_lQ,12648
90
90
  atomicshop/basics/dicts.py,sha256=DeYHIh940pMMBrFhpXt4dsigFVYzTrlqWymNo4Pq_Js,14049
91
91
  atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
92
92
  atomicshop/basics/enumerations.py,sha256=41VVQYh_vnVapggxKg2IRU5e_EiMpZzX1n1mtxvoSzM,1364
@@ -126,7 +126,7 @@ atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,21
126
126
  atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
127
  atomicshop/mitm/config_static.py,sha256=ROAtbibSWSsF3BraUbhu-QO3MPIFqYY5KUKgsQbiSkk,7813
128
128
  atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
129
- atomicshop/mitm/connection_thread_worker.py,sha256=DxHCgDtCbSQCIX80WK9hJhp3v37pMjmm-mr3y6tyJMY,17581
129
+ atomicshop/mitm/connection_thread_worker.py,sha256=J2fYJZS1IuibvvNmeIsm4hkJMxokQFbBk9xwMGjT7YE,19958
130
130
  atomicshop/mitm/import_config.py,sha256=0Ij14aISTllTOiWYJpIUMOWobQqGofD6uafui5uWllE,9272
131
131
  atomicshop/mitm/initialize_engines.py,sha256=VyJE8QnzlgD3QbX5inz5o6rC3zQ3is9CeTL7-B10g1w,8292
132
132
  atomicshop/mitm/message.py,sha256=URR5JKSuAT8XmGIkyprEjlPW2GW4ef_gfUz_GgcFseE,2184
@@ -139,7 +139,7 @@ atomicshop/mitm/engines/create_module_template.py,sha256=tRjVSm1sD6FzML71Qbuwvit
139
139
  atomicshop/mitm/engines/create_module_template_example.py,sha256=X5xhvbV6-g9jU_bQVhf_crZmaH50LRWz3bS-faQ18ds,489
140
140
  atomicshop/mitm/engines/__parent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
141
  atomicshop/mitm/engines/__parent/parser___parent.py,sha256=RK2wviepP0oeq7zuLpgkvqvTJtc0r0a7hDGWdV0dGc4,657
142
- atomicshop/mitm/engines/__parent/recorder___parent.py,sha256=xJ3YVa8XYwcKqCwUIEXf6_UGnCbpRsvoicUNe0WxMfs,3782
142
+ atomicshop/mitm/engines/__parent/recorder___parent.py,sha256=mHQAvUgsRCy3pJR_ws4v_QBE9Ju5r7RmWhyJyvczyI0,3781
143
143
  atomicshop/mitm/engines/__parent/responder___parent.py,sha256=7WQeR3UmMnN74bDwn-0nz2OfhXJ3-ClXpNGUFZ7wJUE,12004
144
144
  atomicshop/mitm/engines/__reference_general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
145
  atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256=57MEPZMAjTO6xBDZ-yt6lgGJyqRrP0Do5Gk_cgCiPns,2998
@@ -312,11 +312,11 @@ atomicshop/wrappers/socketw/socket_client.py,sha256=zDX7M3KfUOn0tXR92gZ119HVFgA8
312
312
  atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
313
313
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=WtylpezgIIBuz-A6PfM0hO1sm9Exd4j3qhDXcFc74-E,35567
314
314
  atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
315
- atomicshop/wrappers/socketw/statistics_csv.py,sha256=w1AH-zf4mBuT4euf28UKij9ihM-b1BRU9Qfby0QDdqI,2957
315
+ atomicshop/wrappers/socketw/statistics_csv.py,sha256=SDYI1cN0oaapvPeLxSXiJrelTy6xbZl-bopR0jAjVGE,3149
316
316
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
317
- atomicshop/wrappers/winregw/winreg_network.py,sha256=ChnVG8ZecKJ-DMF8nUHRiifWJq2M4slEwKat6FBfPfE,8685
318
- atomicshop-2.16.44.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
319
- atomicshop-2.16.44.dist-info/METADATA,sha256=yeA-HD5CtX5FhTCrJG9ylhYHECkHvJL_ai4gaOnilY4,10500
320
- atomicshop-2.16.44.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
321
- atomicshop-2.16.44.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
322
- atomicshop-2.16.44.dist-info/RECORD,,
317
+ atomicshop/wrappers/winregw/winreg_network.py,sha256=zZQfps-CdODQaTUADbHAwKHr5RUg7BLafnKWBbKaLN4,8728
318
+ atomicshop-2.16.45.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
319
+ atomicshop-2.16.45.dist-info/METADATA,sha256=E-QukR4G1Pts-0XCHJeKddzx3V2jaC6C5eLog6UU0Kw,10500
320
+ atomicshop-2.16.45.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
321
+ atomicshop-2.16.45.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
322
+ atomicshop-2.16.45.dist-info/RECORD,,