atomicshop 2.16.41__py3-none-any.whl → 2.16.43__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.41'
4
+ __version__ = '2.16.43'
@@ -53,6 +53,30 @@ def convert_sequence_of_bytes_to_sequence_of_strings(byte_sequence: bytes) -> li
53
53
  return result
54
54
 
55
55
 
56
+ def convert_sequence_of_bytes_to_exact_string(
57
+ byte_sequence: bytes,
58
+ add_space_between_bytes: bool = False,
59
+ ) -> str:
60
+ """
61
+ Convert sequence of bytes to exact string.
62
+ Example: b'\xc0\x00' -> 'c000'
63
+
64
+ :param byte_sequence: bytes, sequence of bytes.
65
+ :param add_space_between_bytes: bool, add space between bytes.
66
+ Example: b'\xc0\x00' -> 'c0 00'
67
+ :return: string.
68
+ """
69
+
70
+ # Convert to hex string and format
71
+ byte_list: list = []
72
+ for byte in byte_sequence:
73
+ byte_list.append(f'{byte:02x}')
74
+
75
+ result = ''.join(byte_list)
76
+
77
+ return result
78
+
79
+
56
80
  def find_position(target: bytes, file_path: str = None, file_bytes: bytes = None, chunk_size: int = None, starting_position: int = 0) -> int:
57
81
  """
58
82
  Find position of the target bytes string in the file.
atomicshop/dns.py CHANGED
@@ -77,6 +77,17 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
77
77
  return is_dynamic, dns_servers
78
78
 
79
79
 
80
+ def get_default_dns_gateway_with_dns_resolver() -> list[str]:
81
+ """
82
+ Get the default DNS gateway from the system using dns.resolver.
83
+ :return: tuple(is dynamic boolean, list of DNS server IPv4s).
84
+ """
85
+
86
+ resolver = dns.resolver.Resolver()
87
+ dns_servers = list(resolver.nameservers)
88
+ return dns_servers
89
+
90
+
80
91
  def set_connection_dns_gateway_static(
81
92
  dns_servers: list[str],
82
93
  connection_name: str = None,
atomicshop/http_parse.py CHANGED
@@ -2,7 +2,6 @@ from http.server import BaseHTTPRequestHandler
2
2
  from http.client import HTTPResponse
3
3
  import http
4
4
  from io import BytesIO
5
- import socket
6
5
 
7
6
 
8
7
  class HTTPRequestParse(BaseHTTPRequestHandler):
@@ -50,8 +49,8 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
50
49
  """
51
50
 
52
51
  # noinspection PyMissingConstructor
53
- def __init__(self, request_text):
54
- self.request_text = request_text
52
+ def __init__(self, request_bytes: bytes):
53
+ self.request_bytes: bytes = request_bytes
55
54
 
56
55
  # noinspection PyTypeChecker
57
56
  self.rfile = None
@@ -107,7 +106,7 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
107
106
  error: str = str()
108
107
  info: str = str()
109
108
 
110
- self.rfile = BytesIO(self.request_text)
109
+ self.rfile = BytesIO(self.request_bytes)
111
110
  self.raw_requestline = self.rfile.readline()
112
111
  self.error_code = self.error_message = None
113
112
  self.parse_request()
@@ -152,6 +151,7 @@ class FakeSocket:
152
151
  def __init__(self, response_bytes):
153
152
  self._file = BytesIO(response_bytes)
154
153
 
154
+ # noinspection PyUnusedLocal
155
155
  def makefile(self, mode='rb', buffering=-1) -> BytesIO:
156
156
  """
157
157
  Mimics the socket's makefile method, returning the BytesIO object.
@@ -100,15 +100,12 @@ def thread_worker_main(
100
100
  client_message.protocol = 'HTTP'
101
101
  client_message.request_raw_decoded = request_decoded
102
102
  print_api(request_parsing_info, logger=network_logger, logger_method='info')
103
- network_logger.info(f"Method: {request_decoded.command}")
104
- network_logger.info(f"Path: {request_decoded.path}")
105
- # If there was error - the request is really HTTP, but there's a problem with its structure.
103
+ network_logger.info(f"Method: {request_decoded.command} | Path: {request_decoded.path}")
106
104
  else:
107
- # client_message.error = request_parsing_error
108
- print_api(request_parsing_error, logger=network_logger, logger_method='error', color='yellow')
109
105
  # It doesn't matter if we have HTTP Parsing error, since the request may not be really HTTP, so it is OK
110
106
  # not to log it into statistics.
111
107
  # statistics_error_list.append(error_message)
108
+ print_api(request_parsing_error, logger=network_logger, logger_method='error', color='yellow')
112
109
 
113
110
  def finish_thread():
114
111
  # At this stage there could be several times that the same socket was used to the service server - we need to
@@ -204,8 +201,6 @@ def thread_worker_main(
204
201
  client_message.request_raw_bytes = client_received_raw_data
205
202
 
206
203
  parse_http()
207
- if client_message.protocol != 'HTTP':
208
- pass
209
204
 
210
205
  # Custom parser, should parse HTTP body or the whole message if not HTTP.
211
206
  parser_instance = parser(client_message)
@@ -19,6 +19,7 @@ from . import config_static, recs_files
19
19
 
20
20
  NETWORK_INTERFACE_IS_DYNAMIC: bool = bool()
21
21
  NETWORK_INTERFACE_IPV4_ADDRESS_LIST: list[str] = list()
22
+ IS_SET_DNS_GATEWAY: bool = False
22
23
  # noinspection PyTypeChecker
23
24
  RECS_PROCESS_INSTANCE: multiprocessing.Process = None
24
25
 
@@ -36,7 +37,7 @@ except win_console.NotWindowsConsoleError:
36
37
 
37
38
 
38
39
  def exit_cleanup():
39
- if permissions.is_admin():
40
+ if permissions.is_admin() and IS_SET_DNS_GATEWAY:
40
41
  is_dns_dynamic, current_dns_gateway = dns.get_default_dns_gateway()
41
42
  status_string = 'Dynamic' if is_dns_dynamic else 'Static'
42
43
  print_api.print_api(f'Current DNS Gateway: {status_string}, {current_dns_gateway}')
@@ -347,10 +348,14 @@ def mitm_server(config_file_path: str):
347
348
  dns_gateway_server_list = [base.DEFAULT_IPV4]
348
349
  set_dns_gateway = True
349
350
 
350
- # Get current network interface state.
351
- global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
352
- NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
353
351
  if set_dns_gateway:
352
+ global IS_SET_DNS_GATEWAY
353
+ IS_SET_DNS_GATEWAY = True
354
+
355
+ # Get current network interface state.
356
+ global NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST
357
+ NETWORK_INTERFACE_IS_DYNAMIC, NETWORK_INTERFACE_IPV4_ADDRESS_LIST = dns.get_default_dns_gateway()
358
+
354
359
  # Set the DNS gateway to the specified one only if the DNS gateway is dynamic, or it is static but different
355
360
  # from the one specified in the configuration file.
356
361
  if (NETWORK_INTERFACE_IS_DYNAMIC or (not NETWORK_INTERFACE_IS_DYNAMIC and
atomicshop/web.py CHANGED
@@ -90,10 +90,13 @@ def get_page_bytes(
90
90
 
91
91
 
92
92
  def get_page_content(
93
- url: str, get_method: str = 'urllib', path: str = None,
93
+ url: str,
94
+ get_method: str = 'urllib',
95
+ path: str = None,
94
96
  playwright_pdf_format: str = 'A4',
95
97
  playwright_html_txt_convert_to_bytes: bool = True,
96
- print_kwargs: dict = None) -> any:
98
+ print_kwargs: dict = None
99
+ ) -> any:
97
100
  """
98
101
  Function returns the page content from the given URL.
99
102
 
@@ -0,0 +1,176 @@
1
+ from typing import Union
2
+ import logging
3
+
4
+ from websockets.server import ServerProtocol
5
+ from websockets.client import ClientProtocol
6
+ from websockets.extensions.permessage_deflate import ServerPerMessageDeflateFactory, ClientPerMessageDeflateFactory
7
+ from websockets.http11 import Request, Response
8
+ from websockets.frames import Frame, Opcode, Close
9
+ from websockets.uri import parse_uri
10
+ from websockets.exceptions import InvalidHeaderValue
11
+ from websockets.protocol import OPEN
12
+
13
+
14
+ class WebsocketRequestParse:
15
+ """
16
+ Parse the websocket request and return the data
17
+ """
18
+ def __init__(
19
+ self,
20
+ enable_logging: bool = False,
21
+ ):
22
+ """
23
+ Initialize the websocket parser.
24
+
25
+ :param enable_logging: bool: Enable logging for the websocket protocol.
26
+ """
27
+ # noinspection PyTypeChecker
28
+ self.request_bytes: bytes = None
29
+
30
+ # Set up extensions
31
+ permessage_deflate_factory = ServerPerMessageDeflateFactory()
32
+
33
+ # Create the protocol instance
34
+ self.protocol = ServerProtocol(
35
+ extensions=[permessage_deflate_factory],
36
+ )
37
+ # At this state the protocol.state is State.CONNECTING
38
+
39
+ if enable_logging:
40
+ logging.basicConfig(level=logging.DEBUG)
41
+ self.protocol.logger.setLevel(logging.DEBUG)
42
+
43
+ def parse(
44
+ self,
45
+ request_bytes: bytes
46
+ ) -> Union[str, bytes, Request]:
47
+ """
48
+ Parse the websocket request and return the data
49
+
50
+ :param request_bytes: bytes: The raw bytes of the websocket request.
51
+ :return: Request: The parsed request object.
52
+ """
53
+
54
+ self.protocol.receive_data(request_bytes)
55
+ events = self.protocol.events_received()
56
+ for event in events:
57
+ if isinstance(event, Request):
58
+ # Accept the handshake.
59
+ # After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
60
+ # Only after this state we can parse frames.
61
+ response = self.protocol.accept(event)
62
+ self.protocol.send_response(response)
63
+ return event
64
+ elif isinstance(event, Frame):
65
+ frame = event
66
+ if frame.opcode == Opcode.TEXT:
67
+ message = frame.data.decode('utf-8')
68
+ return message
69
+ elif frame.opcode == Opcode.BINARY:
70
+ return frame.data
71
+
72
+ """
73
+ # Handle control frames, these are here for the future references.
74
+ elif frame.opcode == Opcode.CLOSE:
75
+ close_info = Close.parse(frame.data)
76
+ print(f"Connection closed by client: {close_info.code}, {close_info.reason}")
77
+ # Send a close frame in response if not already sent
78
+ if self.protocol.state == self.protocol.OPEN:
79
+ self.protocol.send_close()
80
+ elif frame.opcode == Opcode.PING:
81
+ # Respond to ping with pong
82
+ self.protocol.send_pong(frame.data)
83
+ elif frame.opcode == Opcode.PONG:
84
+ print("Received pong")
85
+ """
86
+
87
+
88
+ class WebsocketResponseParse:
89
+ """
90
+ Parse the websocket response and return the data
91
+ """
92
+ def __init__(
93
+ self,
94
+ enable_logging: bool = False,
95
+ ):
96
+ """
97
+ Initialize the websocket parser.
98
+
99
+ :param enable_logging: bool: Enable logging for the websocket protocol.
100
+ """
101
+ # noinspection PyTypeChecker
102
+ self.response_bytes: bytes = None
103
+
104
+ # Set up extensions
105
+ permessage_deflate_factory = ClientPerMessageDeflateFactory()
106
+
107
+ # Parse the WebSocket URI.
108
+ # Since we're parsing the response, we don't need the URI, but the protocol object requires it.
109
+ # So we will just use a dummy URI.
110
+ wsuri = parse_uri('ws://example.com/websocket')
111
+
112
+ # Create the protocol instance
113
+ self.protocol = ClientProtocol(
114
+ wsuri,
115
+ extensions=[permessage_deflate_factory],
116
+ )
117
+
118
+ if enable_logging:
119
+ logging.basicConfig(level=logging.DEBUG)
120
+ # self.protocol.logger.setLevel(logging.DEBUG)
121
+ self.protocol.debug = True
122
+
123
+ # Perform the handshake and emulate the connection and request sending.
124
+ request = self.protocol.connect()
125
+ self.protocol.send_request(request)
126
+ _ = self.protocol.data_to_send()
127
+ # At this state the protocol.state is State.CONNECTING
128
+
129
+ def parse(
130
+ self,
131
+ response_bytes: bytes
132
+ ) -> Union[str, bytes, Response]:
133
+ """
134
+ Parse the websocket response and return the data
135
+
136
+ :param response_bytes: bytes: The raw bytes of the websocket response.
137
+ :return: The parsed response.
138
+ """
139
+
140
+ self.protocol.receive_data(response_bytes)
141
+ events = self.protocol.events_received()
142
+ for event in events:
143
+ if isinstance(event, Response):
144
+ # Accept the handshake.
145
+ # After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
146
+ # Only after this state we can parse frames.
147
+ try:
148
+ self.protocol.process_response(event)
149
+ except InvalidHeaderValue as e:
150
+ headers = event.headers
151
+ self.protocol.extensions = self.protocol.process_extensions(headers)
152
+ self.protocol.subprotocol = self.protocol.process_subprotocol(headers)
153
+ self.protocol.state = OPEN
154
+ return event
155
+ elif isinstance(event, Frame):
156
+ frame = event
157
+ if frame.opcode == Opcode.TEXT:
158
+ message = frame.data.decode('utf-8')
159
+ return message
160
+ elif frame.opcode == Opcode.BINARY:
161
+ return frame.data
162
+
163
+ """
164
+ # Handle control frames, these are here for the future references.
165
+ elif frame.opcode == Opcode.CLOSE:
166
+ close_info = Close.parse(frame.data)
167
+ print(f"Connection closed by client: {close_info.code}, {close_info.reason}")
168
+ # Send a close frame in response if not already sent
169
+ if self.protocol.state == self.protocol.OPEN:
170
+ self.protocol.send_close()
171
+ elif frame.opcode == Opcode.PING:
172
+ # Respond to ping with pong
173
+ self.protocol.send_pong(frame.data)
174
+ elif frame.opcode == Opcode.PONG:
175
+ print("Received pong")
176
+ """
@@ -7,7 +7,12 @@ from .. import config_install
7
7
 
8
8
  def install_after_restart(
9
9
  installation_directory: str,
10
- install_type: Union[None, Literal['backend', 'frontend', 'db']] = None
10
+ install_type: Union[
11
+ None,
12
+ Literal['backend', 'frontend', 'db']] = None,
13
+ log_level: Union[
14
+ None,
15
+ Literal['DEBUG', 'INFO', 'WARNING', 'ERROR']] = None
11
16
  ):
12
17
  """
13
18
  This function will continue the installation the FACT_core after the restart of the computer.
@@ -20,6 +25,9 @@ def install_after_restart(
20
25
  --backend: Distributed setup, Install the FACT_core backend.
21
26
  --frontend: Distributed setup, Install the FACT_core frontend.
22
27
  --db: Distributed setup, Install the FACT_core database.
28
+ :param log_level: string, the log level to use for the installation.
29
+ The same as using the '--log-level' parameter in the 'install.py' script.
30
+ The default is 'INFO' in the 'install.py' script.
23
31
  :return:
24
32
  """
25
33
 
@@ -28,8 +36,11 @@ def install_after_restart(
28
36
  if install_type:
29
37
  install_command = install_command + ' --' + install_type
30
38
 
39
+ if log_level:
40
+ install_command = install_command + ' --log-level ' + log_level
41
+
31
42
  # Install the FACT_core repo.
32
43
  process.execute_with_live_output(cmd=install_command, verbose=True)
33
44
  # Remove the FACT_core installation log.
34
- working_directory_path: str = filesystem.get_working_directory()
45
+ # working_directory_path: str = filesystem.get_working_directory()
35
46
  # filesystem.remove_file(str(Path(working_directory_path, config_install.INSTALL_LOG_FILE_NAME)))