atomicshop 2.17.2__py3-none-any.whl → 2.18.0__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.17.2'
4
+ __version__ = '2.18.0'
@@ -23,7 +23,8 @@ def get_colors_basic_dict(color):
23
23
  'yellow': ColorsBasic.YELLOW,
24
24
  'blue': ColorsBasic.BLUE,
25
25
  'header': ColorsBasic.HEADER,
26
- 'cyan': ColorsBasic.CYAN
26
+ 'cyan': ColorsBasic.CYAN,
27
+ 'orange': ColorsBasic.ORANGE
27
28
  }
28
29
 
29
30
  return colors_basic_dict[color]
@@ -48,6 +49,7 @@ class ColorsBasic:
48
49
  BLUE = '\033[94m'
49
50
  HEADER = '\033[95m'
50
51
  CYAN = '\033[96m'
52
+ ORANGE = '\033[38;2;255;165;0m'
51
53
  END = ANSI_END
52
54
 
53
55
 
@@ -12,7 +12,7 @@ def get_hyperlinks(docx_path):
12
12
  :return: list of strings, hyperlinks.
13
13
  """
14
14
 
15
- hyperlinks: list = list()
15
+ hyperlinks: list[dict] = list()
16
16
 
17
17
  try:
18
18
  doc = Document(docx_path)
@@ -32,9 +32,13 @@ def get_hyperlinks(docx_path):
32
32
  # And the fragment is stored in the hyperlink.fragment as 'anchor'.
33
33
  # For the full hyperlink, we need to concatenate the address and the fragment.
34
34
  # If there is no anchor in the link the fragment will be empty string ('').
35
- if hyperlink.fragment:
36
- hyperlinks.append(hyperlink.address + "#" + hyperlink.fragment)
37
- hyperlinks.append(hyperlink.address)
35
+ # Basically, we don't need to add the fragment to the hyperlink if it's empty, we can just use the url.
36
+ # if hyperlink.fragment:
37
+ # hyperlinks.append(hyperlink.address + "#" + hyperlink.fragment)
38
+ hyperlinks.append({
39
+ 'url': hyperlink.url,
40
+ 'text': hyperlink.text
41
+ })
38
42
 
39
43
  return hyperlinks
40
44
 
@@ -78,20 +82,23 @@ def search_for_hyperlink_in_files(directory_path: str, hyperlink: str, relative_
78
82
  directory_path, get_file=True, file_name_check_pattern="*.docx",
79
83
  add_relative_directory=True, relative_file_name_as_directory=True)
80
84
 
81
- found_in_files: list = list()
85
+ found_in_files: list[dict] = list()
82
86
  for file_path in files:
83
- hyperlinks = get_hyperlinks(file_path.path)
84
- if hyperlink in hyperlinks:
85
- found_in_files.append(file_path)
87
+ doc_hyperlinks = get_hyperlinks(file_path.path)
88
+ for doc_link in doc_hyperlinks:
89
+ if hyperlink in doc_link['url']:
90
+ if relative_paths:
91
+ path: str = file_path.relative_dir
92
+ else:
93
+ path: str = file_path.path
86
94
 
87
- if relative_paths:
88
- result_list = list()
89
- for found_file in found_in_files:
90
- result_list.append(found_file.relative_dir)
91
- else:
92
- result_list = found_in_files
95
+ found_in_files.append({
96
+ 'path':path,
97
+ 'link':doc_link['url'],
98
+ 'text':doc_link['text']
99
+ })
93
100
 
94
- return result_list
101
+ return found_in_files
95
102
 
96
103
 
97
104
  def search_for_hyperlink_in_files_interface_main(script_directory: str = None):
@@ -134,10 +141,12 @@ def search_for_hyperlink_in_files_interface_main(script_directory: str = None):
134
141
  found_in_files = search_for_hyperlink_in_files(
135
142
  config['directory_path'], config['hyperlink'], relative_paths=config['relative_paths'])
136
143
 
137
- print_api(f"Found in [{len(found_in_files)}] files:", color="blue")
144
+ print_api(f"Found [{len(found_in_files)}] links:", color="blue")
138
145
 
139
146
  for index, found_file in enumerate(found_in_files):
140
147
  print_api(f"[{index+1}]", print_end="", color="green")
141
- print_api(f" {found_file}")
148
+ print_api(f" {found_file['path']}")
149
+ print_api(f" {found_file['link']}", color="cyan")
150
+ print_api(f" {found_file['text']}", color="orange")
142
151
 
143
152
  input('[*] Press [Enter] to exit...')
atomicshop/http_parse.py CHANGED
@@ -4,6 +4,40 @@ import http
4
4
  from io import BytesIO
5
5
 
6
6
 
7
+ def get_request_methods() -> list[str]:
8
+ """
9
+ Function to return all available HTTP request methods.
10
+ """
11
+ # noinspection PyUnresolvedReferences
12
+ return [method.value for method in http.HTTPMethod]
13
+
14
+
15
+ def is_first_bytes_http_request(request_bytes: bytes) -> bool:
16
+ """
17
+ Function to check if the first bytes are HTTP request or not.
18
+ """
19
+ http_request_methods_list: list = get_request_methods()
20
+ http_request_methods_list = [method.encode() for method in http_request_methods_list]
21
+
22
+ # If the first bytes are HTTP request, then the first word should be one of the HTTP request methods.
23
+ for method in http_request_methods_list:
24
+ if request_bytes.startswith(method):
25
+ return True
26
+ return False
27
+
28
+
29
+ def is_first_bytes_http_response(response_bytes: bytes) -> bool:
30
+ """
31
+ Function to check if the first bytes are HTTP response or not.
32
+ """
33
+ http_response_beginning: bytes = b'HTTP/'
34
+
35
+ # If the first bytes are HTTP response, then the first word should be 'HTTP/'.
36
+ if response_bytes.startswith(http_response_beginning):
37
+ return True
38
+ return False
39
+
40
+
7
41
  class HTTPRequestParse(BaseHTTPRequestHandler):
8
42
  """
9
43
  The class will parse HTTP requests.
@@ -103,45 +137,47 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
103
137
  client_message.request_raw_decoded = request_decoded
104
138
  """
105
139
 
106
- error: str = str()
107
- info: str = str()
108
-
109
- self.rfile = BytesIO(self.request_bytes)
110
- self.raw_requestline = self.rfile.readline()
111
- self.error_code = self.error_message = None
112
- self.parse_request()
113
-
114
- # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values.
115
- if hasattr(self, 'headers'):
116
- # The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
117
- if 'Content-Length' in self.headers.keys():
118
- # "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
119
- self.content_length = int(self.headers.get('Content-Length'))
120
- self.body = self.rfile.read(self.content_length)
121
-
122
- # Examples:
123
- # Getting path: self.path
124
- # Getting Request Version: self.request_version
125
- # Getting specific header: self.headers['host']
126
-
127
- # If there's any error in HTTP parsing
128
- if self.error_message:
129
- # If error status is "BAD_REQUEST"
130
- if self.error_message.startswith("Bad request"):
131
- # If it's 'Bad request' this is not HTTP request, so we can
132
- # continue the execution and parse the code as NON-HTTP Request.
133
- # Currently, seen 'Bad request syntax' and 'Bad request version'.
134
- error = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
135
- is_http = False
136
- else:
137
- error = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
138
- is_http = False
139
- # If there's no error at all in HTTP Parsing, then it's fine HTTP Request
140
+ if self.request_bytes is None or not is_first_bytes_http_request(self.request_bytes):
141
+ error = "HTTP Request Parsing: Not HTTP request by first bytes."
142
+ is_http = False
140
143
  else:
141
- is_http = True
142
- info = "HTTP Request Parsing: Valid HTTP request"
144
+ error: str = str()
145
+
146
+ self.rfile = BytesIO(self.request_bytes)
147
+ self.raw_requestline = self.rfile.readline()
148
+ self.error_code = self.error_message = None
149
+ self.parse_request()
150
+
151
+ # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values.
152
+ if hasattr(self, 'headers'):
153
+ # The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
154
+ if 'Content-Length' in self.headers.keys():
155
+ # "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
156
+ self.content_length = int(self.headers.get('Content-Length'))
157
+ self.body = self.rfile.read(self.content_length)
158
+
159
+ # Examples:
160
+ # Getting path: self.path
161
+ # Getting Request Version: self.request_version
162
+ # Getting specific header: self.headers['host']
143
163
 
144
- return self, is_http, info, error
164
+ # If there's any error in HTTP parsing
165
+ if self.error_message:
166
+ # If error status is "BAD_REQUEST"
167
+ if self.error_message.startswith("Bad request"):
168
+ # If it's 'Bad request' this is not HTTP request, so we can
169
+ # continue the execution and parse the code as NON-HTTP Request.
170
+ # Currently, seen 'Bad request syntax' and 'Bad request version'.
171
+ error = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
172
+ is_http = False
173
+ else:
174
+ error = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
175
+ is_http = False
176
+ # If there's no error at all in HTTP Parsing, then it's fine HTTP Request
177
+ else:
178
+ is_http = True
179
+
180
+ return self, is_http, error
145
181
 
146
182
 
147
183
  class FakeSocket:
@@ -171,49 +207,54 @@ class HTTPResponseParse:
171
207
 
172
208
  self.error = None
173
209
  self.source = None
174
- self.response_raw_decoded = None
210
+ self.response_raw_parsed = None
175
211
  self.is_http: bool = False
176
212
 
177
213
  def parse(self):
178
- # Assigning FakeSocket with response_raw_bytes.
179
- self.source = FakeSocket(self.response_raw_bytes)
180
-
181
- # Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
182
- # noinspection PyTypeChecker
183
- self.response_raw_decoded = HTTPResponse(self.source)
184
-
185
- # Try to parse HTTP Response.
186
- try:
187
- self.response_raw_decoded.begin()
188
- self.is_http = True
189
- # If there were problems with the status line.
190
- except http.client.BadStatusLine:
191
- self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
214
+ if self.response_raw_bytes is None or not is_first_bytes_http_response(self.response_raw_bytes):
215
+ self.error = "HTTP Response Parsing: Not a valid HTTP Response by first bytes."
192
216
  self.is_http = False
217
+ self.response_raw_parsed = None
218
+ else:
219
+ # Assigning FakeSocket with response_raw_bytes.
220
+ self.source = FakeSocket(self.response_raw_bytes)
193
221
 
194
- header_exists: bool = False
195
- if (self.response_raw_decoded is not None and hasattr(self.response_raw_decoded, 'headers') and
196
- self.response_raw_decoded.headers is not None):
197
- header_exists = True
198
-
199
- if header_exists and self.response_raw_decoded.headers.defects:
200
- self.error = f"HTTP Response Parsing: Not a valid HTTP Response: Some defects in headers: " \
201
- f"{self.response_raw_decoded.headers.defects}"
202
- self.is_http = False
222
+ # Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
223
+ # noinspection PyTypeChecker
224
+ self.response_raw_parsed = HTTPResponse(self.source)
203
225
 
204
- if self.is_http:
205
- # Before checking for body, we need to make sure that ".headers" property exists,
206
- # if not, return empty values.
207
- self.response_raw_decoded.content_length = None
208
- self.response_raw_decoded.body = None
209
- if header_exists and 'Content-Length' in self.response_raw_decoded.headers.keys():
210
- # The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
211
- # "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
212
- # "int" converts it to integer.
213
- self.response_raw_decoded.content_length = int(self.response_raw_decoded.headers.get('Content-Length'))
214
- # Basically we need to extract only the number of bytes specified in 'Content-Length' from the end
215
- # of the response that we received.
216
- # self.response_raw_bytes[-23:]
217
- self.response_raw_decoded.body = self.response_raw_bytes[-self.response_raw_decoded.content_length:]
218
-
219
- return self.response_raw_decoded, self.is_http, self.error
226
+ # Try to parse HTTP Response.
227
+ try:
228
+ self.response_raw_parsed.begin()
229
+ self.is_http = True
230
+ # If there were problems with the status line.
231
+ except http.client.BadStatusLine:
232
+ self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
233
+ self.is_http = False
234
+
235
+ header_exists: bool = False
236
+ if (self.response_raw_parsed is not None and hasattr(self.response_raw_parsed, 'headers') and
237
+ self.response_raw_parsed.headers is not None):
238
+ header_exists = True
239
+
240
+ if header_exists and self.response_raw_parsed.headers.defects:
241
+ self.error = f"HTTP Response Parsing: Not a valid HTTP Response: Some defects in headers: " \
242
+ f"{self.response_raw_parsed.headers.defects}"
243
+ self.is_http = False
244
+
245
+ if self.is_http:
246
+ # Before checking for body, we need to make sure that ".headers" property exists,
247
+ # if not, return empty values.
248
+ self.response_raw_parsed.content_length = None
249
+ self.response_raw_parsed.body = None
250
+ if header_exists and 'Content-Length' in self.response_raw_parsed.headers.keys():
251
+ # The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
252
+ # "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
253
+ # "int" converts it to integer.
254
+ self.response_raw_parsed.content_length = int(self.response_raw_parsed.headers.get('Content-Length'))
255
+ # Basically we need to extract only the number of bytes specified in 'Content-Length' from the end
256
+ # of the response that we received.
257
+ # self.response_raw_bytes[-23:]
258
+ self.response_raw_parsed.body = self.response_raw_bytes[-self.response_raw_parsed.content_length:]
259
+
260
+ return self.response_raw_parsed, self.is_http, self.error
@@ -123,6 +123,7 @@ class LogRec:
123
123
 
124
124
  @dataclass
125
125
  class Certificates:
126
+ enable_sslkeylogfile_env_to_client_ssl_context: bool
126
127
  install_ca_certificate_to_root_store: bool
127
128
  uninstall_unused_ca_certificates_with_mitm_ca_name: bool
128
129