atomicshop 3.3.8__py3-none-any.whl → 3.10.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 +1 -1
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/basics/strings.py +1 -1
- atomicshop/certificates.py +2 -2
- atomicshop/dns.py +26 -28
- atomicshop/etws/traces/trace_tcp.py +1 -2
- atomicshop/mitm/centered_settings.py +133 -0
- atomicshop/mitm/config_static.py +22 -44
- atomicshop/mitm/connection_thread_worker.py +383 -165
- atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
- atomicshop/mitm/engines/__parent/requester___parent.py +1 -1
- atomicshop/mitm/engines/__parent/responder___parent.py +15 -2
- atomicshop/mitm/engines/create_module_template.py +1 -2
- atomicshop/mitm/import_config.py +91 -89
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +238 -122
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +33 -12
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +104 -31
- atomicshop/networks.py +160 -92
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/process.py +11 -4
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +140 -164
- atomicshop/web.py +63 -22
- atomicshop/web_apis/google_llm.py +22 -14
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -1
- atomicshop/wrappers/dockerw/dockerw.py +2 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +0 -190
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +180 -68
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +20 -4
- atomicshop/wrappers/loggingw/reading.py +18 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +0 -38
- atomicshop/wrappers/netshw.py +124 -3
- atomicshop/wrappers/playwrightw/scenarios.py +1 -1
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +9 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +12 -27
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +15 -9
- atomicshop/wrappers/socketw/certificator.py +19 -9
- atomicshop/wrappers/socketw/creator.py +101 -14
- atomicshop/wrappers/socketw/dns_server.py +17 -5
- atomicshop/wrappers/socketw/exception_wrapper.py +21 -16
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +29 -9
- atomicshop/wrappers/socketw/sender.py +10 -9
- atomicshop/wrappers/socketw/sni.py +31 -10
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +11 -10
- atomicshop/wrappers/socketw/socket_wrapper.py +125 -32
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/ubuntu_terminal.py +21 -18
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/RECORD +83 -109
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/ubuntu/docker_rootless.py +0 -11
- atomicshop/a_installs/ubuntu/docker_sudo.py +0 -11
- atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py +0 -10
- atomicshop/a_installs/ubuntu/mongodb.py +0 -12
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/wsl_ubuntu_lts.py +0 -10
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/__init__.py +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/search_in_archive.py +0 -284
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -73
- atomicshop/archiver/shutils.py +0 -34
- atomicshop/archiver/zips.py +0 -353
- atomicshop/file_types.py +0 -24
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/script_as_string_processor.py +0 -42
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -449
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -233
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/__init__.py +0 -0
- atomicshop/wrappers/fibratusw/install.py +0 -80
- atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py +0 -100
- atomicshop/wrappers/mongodbw/install_mongodb_win.py +0 -244
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- atomicshop/wrappers/wslw.py +0 -192
- atomicshop-3.3.8.dist-info/entry_points.txt +0 -2
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
# This was written before 'search_in_archive', currently search_in_archive is in test mode.
|
|
2
|
-
# So probably this will go away eventually.
|
|
3
|
-
import os
|
|
4
|
-
import zipfile
|
|
5
|
-
from io import BytesIO
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _get_unique_filename(directory, filename):
|
|
9
|
-
"""
|
|
10
|
-
Generates a unique filename by appending a number if the file already exists.
|
|
11
|
-
"""
|
|
12
|
-
name, ext = os.path.splitext(filename)
|
|
13
|
-
counter = 1
|
|
14
|
-
unique_filename = filename
|
|
15
|
-
while os.path.exists(os.path.join(directory, unique_filename)):
|
|
16
|
-
unique_filename = f"{name}_{counter}{ext}"
|
|
17
|
-
counter += 1
|
|
18
|
-
return unique_filename
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _is_zip_file(file, zip_obj):
|
|
22
|
-
try:
|
|
23
|
-
with zip_obj.open(file) as file_data:
|
|
24
|
-
with zipfile.ZipFile(BytesIO(file_data.read())) as zip_file:
|
|
25
|
-
if zip_file.testzip() is None: # No errors found
|
|
26
|
-
return True
|
|
27
|
-
except zipfile.BadZipFile:
|
|
28
|
-
return False
|
|
29
|
-
return False
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _match_file_name(target, current, case_sensitive):
|
|
33
|
-
if case_sensitive:
|
|
34
|
-
return current.endswith(target)
|
|
35
|
-
else:
|
|
36
|
-
return current.lower().endswith(target.lower())
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _handle_nested_zip(
|
|
40
|
-
zip_obj, item, archived_file_bytes, file_names, results, found_set, recursive, return_first_only,
|
|
41
|
-
case_sensitive, callback_functions, extract_file_to_path):
|
|
42
|
-
|
|
43
|
-
if recursive and _is_zip_file(item.filename, zip_obj):
|
|
44
|
-
nested_zip_bytes = BytesIO(archived_file_bytes)
|
|
45
|
-
with zipfile.ZipFile(nested_zip_bytes) as nested_zip:
|
|
46
|
-
_search_in_zip(
|
|
47
|
-
nested_zip, file_names, results, found_set, case_sensitive, return_first_only, recursive,
|
|
48
|
-
callback_functions, extract_file_to_path)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _handle_file_extraction(item, extract_file_to_path, archived_file_bytes):
|
|
52
|
-
if extract_file_to_path:
|
|
53
|
-
unique_filename = _get_unique_filename(extract_file_to_path, os.path.basename(item.filename))
|
|
54
|
-
with open(os.path.join(extract_file_to_path, unique_filename), 'wb') as f:
|
|
55
|
-
f.write(archived_file_bytes)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _handle_callback_matching(item, archived_file_bytes, callback_functions, results, found_set, return_first_only):
|
|
59
|
-
for callback in callback_functions:
|
|
60
|
-
callback_result = callback(archived_file_bytes)
|
|
61
|
-
if callback_result:
|
|
62
|
-
# Initialize key for callback function name if not present
|
|
63
|
-
if callback.__name__ not in results:
|
|
64
|
-
results[callback.__name__] = []
|
|
65
|
-
file_info = {
|
|
66
|
-
'bytes': archived_file_bytes,
|
|
67
|
-
'name': item.filename,
|
|
68
|
-
'size': item.file_size,
|
|
69
|
-
'modified_time': item.date_time
|
|
70
|
-
}
|
|
71
|
-
results[callback.__name__].append(file_info)
|
|
72
|
-
if return_first_only:
|
|
73
|
-
found_set.add(item.filename)
|
|
74
|
-
return True
|
|
75
|
-
return False
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def _handle_name_matching(item, archived_file_bytes, file_names, case_sensitive, results, found_set, return_first_only):
|
|
79
|
-
if any(_match_file_name(file_name, item.filename, case_sensitive) for file_name in file_names):
|
|
80
|
-
if item.filename not in results:
|
|
81
|
-
results[item.filename] = []
|
|
82
|
-
file_info = {
|
|
83
|
-
'bytes': archived_file_bytes,
|
|
84
|
-
'name': item.filename,
|
|
85
|
-
'size': item.file_size,
|
|
86
|
-
'modified_time': item.date_time
|
|
87
|
-
}
|
|
88
|
-
results[item.filename].append(file_info)
|
|
89
|
-
if return_first_only:
|
|
90
|
-
found_set.add(item.filename)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def _search_in_zip(
|
|
94
|
-
zip_obj, file_names, results, found_set, case_sensitive, return_first_only, recursive, callback_functions,
|
|
95
|
-
extract_file_to_path):
|
|
96
|
-
|
|
97
|
-
for item in zip_obj.infolist():
|
|
98
|
-
if item.filename.endswith('/'): # Skip directories
|
|
99
|
-
continue
|
|
100
|
-
|
|
101
|
-
with zip_obj.open(item) as file_data:
|
|
102
|
-
archived_file_bytes = file_data.read()
|
|
103
|
-
|
|
104
|
-
callback_matched = False
|
|
105
|
-
if callback_functions:
|
|
106
|
-
callback_matched = _handle_callback_matching(
|
|
107
|
-
item, archived_file_bytes, callback_functions, results, found_set, return_first_only)
|
|
108
|
-
|
|
109
|
-
if callback_matched:
|
|
110
|
-
_handle_file_extraction(item, extract_file_to_path, archived_file_bytes)
|
|
111
|
-
else:
|
|
112
|
-
_handle_nested_zip(
|
|
113
|
-
zip_obj, item, archived_file_bytes, file_names, results, found_set, recursive, return_first_only,
|
|
114
|
-
case_sensitive, callback_functions, extract_file_to_path)
|
|
115
|
-
if file_names and not callback_matched:
|
|
116
|
-
_handle_name_matching(
|
|
117
|
-
item, archived_file_bytes, file_names, case_sensitive, results, found_set, return_first_only)
|
|
118
|
-
|
|
119
|
-
if file_names is not None and len(found_set) == len(file_names):
|
|
120
|
-
break # All files found, stop searching
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def _initialize_results(callback_functions):
|
|
124
|
-
if callback_functions:
|
|
125
|
-
return {callback.__name__: [] for callback in callback_functions}
|
|
126
|
-
else:
|
|
127
|
-
return {}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def _search_zip_content(
|
|
131
|
-
file_path, file_bytes, file_names_to_search, results, found_set, case_sensitive, return_first_only, recursive,
|
|
132
|
-
callback_functions, extract_file_to_path):
|
|
133
|
-
if file_bytes is not None:
|
|
134
|
-
with zipfile.ZipFile(BytesIO(file_bytes), 'r') as zip_ref:
|
|
135
|
-
_search_in_zip(zip_ref, file_names_to_search, results, found_set, case_sensitive, return_first_only,
|
|
136
|
-
recursive, callback_functions, extract_file_to_path)
|
|
137
|
-
elif file_path is not None:
|
|
138
|
-
with zipfile.ZipFile(file_path, 'r') as zip_ref:
|
|
139
|
-
_search_in_zip(zip_ref, file_names_to_search, results, found_set, case_sensitive, return_first_only,
|
|
140
|
-
recursive, callback_functions, extract_file_to_path)
|
|
141
|
-
else:
|
|
142
|
-
raise ValueError("Either file_path or file_bytes must be provided.")
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def search_file_in_zip(
|
|
146
|
-
file_path: str = None,
|
|
147
|
-
file_bytes: bytes = None,
|
|
148
|
-
file_names_to_search: list[str] = None,
|
|
149
|
-
case_sensitive: bool = True,
|
|
150
|
-
return_first_only: bool = False,
|
|
151
|
-
return_empty_list_per_file_name: bool = False,
|
|
152
|
-
recursive: bool = False,
|
|
153
|
-
callback_functions: list = None,
|
|
154
|
-
extract_file_to_path: str = None
|
|
155
|
-
) -> dict[str, list[bytes]]:
|
|
156
|
-
"""
|
|
157
|
-
Function searches for the file names inside the zip file and returns a dictionary where the keys are the
|
|
158
|
-
names of the callback functions and the values are lists of found file bytes.
|
|
159
|
-
:param file_path: string, full path to the zip file.
|
|
160
|
-
:param file_bytes: bytes, the bytes of the zip file.
|
|
161
|
-
:param file_names_to_search: list of strings, the names of the files to search.
|
|
162
|
-
:param case_sensitive: boolean, default is 'True'. Determines if file name search should be case sensitive.
|
|
163
|
-
:param return_first_only: boolean, default is 'False'. Return only the first found file for each file name.
|
|
164
|
-
:param return_empty_list_per_file_name: boolean, default is 'False'.
|
|
165
|
-
True: Return empty list for each file name that wasn't found.
|
|
166
|
-
False: Don't return empty list for each file name that wasn't found.
|
|
167
|
-
:param recursive: boolean, default is 'False'. If True, search for file names recursively in nested zip files.
|
|
168
|
-
:param callback_functions: list of callables, default is None. Each function takes a file name and should return a
|
|
169
|
-
boolean that will tell the main function if this file is 'found' or not.
|
|
170
|
-
:param extract_file_to_path: string, full path to the directory where the found files should be extracted.
|
|
171
|
-
:return: dictionary of lists of bytes.
|
|
172
|
-
"""
|
|
173
|
-
|
|
174
|
-
if file_names_to_search is None and callback_functions is None:
|
|
175
|
-
raise ValueError("Either file_names_to_search or callback_functions must be provided.")
|
|
176
|
-
|
|
177
|
-
# Initialize results dictionary.
|
|
178
|
-
results = _initialize_results(callback_functions)
|
|
179
|
-
found_set = set()
|
|
180
|
-
|
|
181
|
-
_search_zip_content(
|
|
182
|
-
file_path, file_bytes, file_names_to_search, results, found_set, case_sensitive, return_first_only, recursive,
|
|
183
|
-
callback_functions, extract_file_to_path)
|
|
184
|
-
|
|
185
|
-
if not return_empty_list_per_file_name:
|
|
186
|
-
# Filter out keys with empty lists.
|
|
187
|
-
results = {key: value for key, value in results.items() if value}
|
|
188
|
-
|
|
189
|
-
return results
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import zipfile
|
|
3
|
-
from io import BytesIO
|
|
4
|
-
from typing import Union
|
|
5
|
-
|
|
6
|
-
from . import zips, sevenzs
|
|
7
|
-
from .. import file_types
|
|
8
|
-
|
|
9
|
-
import py7zr
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
SUPPORTED_ARCHIVE_MIMES: list = [
|
|
13
|
-
'application/x-7z-compressed',
|
|
14
|
-
'application/zip',
|
|
15
|
-
'application/x-dosexec', # SFX zip files.
|
|
16
|
-
'application/octet-stream', # There are some non-standard zip files that are not recognized by magic.
|
|
17
|
-
'application/vnd.microsoft.portable-executable' # PE files. Some self extracting archives are PE files, But the zip module can handle them.
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Custom exception if the file is not known archive type.
|
|
22
|
-
class UnknownArchiveType(Exception):
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _get_unique_filename(directory, filename):
|
|
27
|
-
"""
|
|
28
|
-
Generates a unique filename by appending a number if the file already exists.
|
|
29
|
-
"""
|
|
30
|
-
name, ext = os.path.splitext(filename)
|
|
31
|
-
counter = 1
|
|
32
|
-
unique_filename = filename
|
|
33
|
-
while os.path.exists(os.path.join(directory, unique_filename)):
|
|
34
|
-
unique_filename = f"{name}_{counter}{ext}"
|
|
35
|
-
counter += 1
|
|
36
|
-
return unique_filename
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _match_file_name(target, current, case_sensitive):
|
|
40
|
-
if case_sensitive:
|
|
41
|
-
return current.endswith(target)
|
|
42
|
-
else:
|
|
43
|
-
return current.lower().endswith(target.lower())
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _handle_file_extraction(item, extract_file_to_path, archived_file_bytes):
|
|
47
|
-
if extract_file_to_path:
|
|
48
|
-
unique_filename = _get_unique_filename(extract_file_to_path, os.path.basename(item.filename))
|
|
49
|
-
with open(os.path.join(extract_file_to_path, unique_filename), 'wb') as f:
|
|
50
|
-
f.write(archived_file_bytes)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _handle_callback_matching(
|
|
54
|
-
item, archive_type, archived_file_bytes, callback_functions, results, found_set, return_first_only):
|
|
55
|
-
for callback in callback_functions:
|
|
56
|
-
callback_result = callback(archived_file_bytes)
|
|
57
|
-
if callback_result:
|
|
58
|
-
# Initialize key for callback function name if not present
|
|
59
|
-
if _get_callback_name(callback) not in results:
|
|
60
|
-
results[_get_callback_name(callback)]: dict = {}
|
|
61
|
-
results[_get_callback_name(callback)]['files']: list = []
|
|
62
|
-
|
|
63
|
-
if archive_type == 'zip':
|
|
64
|
-
file_info = {
|
|
65
|
-
'bytes': archived_file_bytes,
|
|
66
|
-
'name': item.filename,
|
|
67
|
-
'size': item.file_size,
|
|
68
|
-
'modified_time': item.date_time
|
|
69
|
-
}
|
|
70
|
-
elif archive_type == '7z':
|
|
71
|
-
file_info = {
|
|
72
|
-
'bytes': archived_file_bytes,
|
|
73
|
-
'name': item.filename,
|
|
74
|
-
'size': item.uncompressed,
|
|
75
|
-
'modified_time': item.creationtime
|
|
76
|
-
}
|
|
77
|
-
results[_get_callback_name(callback)]['files'].append(file_info)
|
|
78
|
-
results[_get_callback_name(callback)]['callable_result'] = callback_result
|
|
79
|
-
if return_first_only:
|
|
80
|
-
found_set.add(item.filename)
|
|
81
|
-
return True
|
|
82
|
-
return False
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _handle_name_matching(item, archived_file_bytes, file_names, case_sensitive, results, found_set, return_first_only):
|
|
86
|
-
if any(_match_file_name(file_name, item.filename, case_sensitive) for file_name in file_names):
|
|
87
|
-
if item.filename not in results:
|
|
88
|
-
results[item.filename] = []
|
|
89
|
-
file_info = {
|
|
90
|
-
'bytes': archived_file_bytes,
|
|
91
|
-
'name': item.filename,
|
|
92
|
-
'size': item.file_size,
|
|
93
|
-
'modified_time': item.date_time
|
|
94
|
-
}
|
|
95
|
-
results[item.filename].append(file_info)
|
|
96
|
-
if return_first_only:
|
|
97
|
-
found_set.add(item.filename)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def _search_in_archive(
|
|
101
|
-
arch_obj, archive_type, file_names, results, found_set, case_sensitive, return_first_only, recursive,
|
|
102
|
-
callback_functions, extract_file_to_path):
|
|
103
|
-
file_info_list = None
|
|
104
|
-
if archive_type == 'zip':
|
|
105
|
-
file_info_list = arch_obj.infolist()
|
|
106
|
-
elif archive_type == '7z':
|
|
107
|
-
file_info_list = arch_obj.list()
|
|
108
|
-
|
|
109
|
-
# Iterate over each file in the archive.
|
|
110
|
-
for item_index, item in enumerate(file_info_list):
|
|
111
|
-
# At this stage we will get the bytes of the archived file, which is an 'item' in the archive.
|
|
112
|
-
archived_file_bytes = None
|
|
113
|
-
# If the main archive is zip we will use the 'open' method, if it's 7z we will use the 'read' method.
|
|
114
|
-
if archive_type == 'zip':
|
|
115
|
-
# Skip directories.
|
|
116
|
-
if item.filename.endswith('/'):
|
|
117
|
-
continue
|
|
118
|
-
|
|
119
|
-
with arch_obj.open(item) as file_data:
|
|
120
|
-
archived_file_bytes = file_data.read()
|
|
121
|
-
elif archive_type == '7z':
|
|
122
|
-
# Skip directories.
|
|
123
|
-
if item.is_directory:
|
|
124
|
-
continue
|
|
125
|
-
|
|
126
|
-
# If 'SevenZipFile.red()' is used once, the second time you need to read it you will need to reset the
|
|
127
|
-
# SevenZipFile object in order to read again:
|
|
128
|
-
# https://py7zr.readthedocs.io/en/latest/api.html#py7zr.SevenZipFile.read
|
|
129
|
-
file_dict = arch_obj.read([item.filename])
|
|
130
|
-
archived_file_bytes = file_dict[item.filename].read()
|
|
131
|
-
arch_obj.reset()
|
|
132
|
-
|
|
133
|
-
# After we get the file bytes we will check if the file matches the callback functions.
|
|
134
|
-
callback_matched = False
|
|
135
|
-
if callback_functions:
|
|
136
|
-
callback_matched = _handle_callback_matching(
|
|
137
|
-
item, archive_type, archived_file_bytes, callback_functions, results, found_set, return_first_only)
|
|
138
|
-
|
|
139
|
-
if callback_matched:
|
|
140
|
-
_handle_file_extraction(item, extract_file_to_path, archived_file_bytes)
|
|
141
|
-
else:
|
|
142
|
-
if recursive and (zips.is_zip_zipfile(archived_file_bytes) or sevenzs.is_7z_magic_number(archived_file_bytes)):
|
|
143
|
-
_search_archive_content(
|
|
144
|
-
archived_file_bytes, file_names, results, found_set, case_sensitive, return_first_only,
|
|
145
|
-
recursive, callback_functions, extract_file_to_path)
|
|
146
|
-
if file_names and not callback_matched:
|
|
147
|
-
_handle_name_matching(
|
|
148
|
-
item, archived_file_bytes, file_names, case_sensitive, results, found_set, return_first_only)
|
|
149
|
-
|
|
150
|
-
if file_names is not None and len(found_set) == len(file_names):
|
|
151
|
-
break # All files found, stop searching
|
|
152
|
-
|
|
153
|
-
|
|
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}"
|
|
170
|
-
else:
|
|
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 {}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def _get_archive_type(file_object) -> Union[str, None]:
|
|
193
|
-
file_mime: str = file_types.get_mime_type(file_object)
|
|
194
|
-
|
|
195
|
-
if file_mime not in SUPPORTED_ARCHIVE_MIMES:
|
|
196
|
-
return None
|
|
197
|
-
|
|
198
|
-
if zips.is_zip_zipfile(file_object):
|
|
199
|
-
return 'zip'
|
|
200
|
-
elif sevenzs.is_7z_magic_number(file_object):
|
|
201
|
-
return '7z'
|
|
202
|
-
else:
|
|
203
|
-
raise UnknownArchiveType(f"{file_object[:10]} is not a known archive type.")
|
|
204
|
-
# print_api(f"{file_object[:10]} is not a known archive type.", color='yellow')
|
|
205
|
-
# return None
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def _search_archive_content(
|
|
209
|
-
file_object, file_names_to_search, results, found_set, case_sensitive, return_first_only, recursive,
|
|
210
|
-
callback_functions, extract_file_to_path):
|
|
211
|
-
archive_type = _get_archive_type(file_object)
|
|
212
|
-
|
|
213
|
-
if isinstance(file_object, str):
|
|
214
|
-
if archive_type == 'zip':
|
|
215
|
-
with zipfile.ZipFile(file_object, 'r') as archive_ref:
|
|
216
|
-
_search_in_archive(
|
|
217
|
-
archive_ref, archive_type, file_names_to_search, results, found_set, case_sensitive,
|
|
218
|
-
return_first_only, recursive, callback_functions, extract_file_to_path)
|
|
219
|
-
elif archive_type == '7z':
|
|
220
|
-
with py7zr.SevenZipFile(file_object, 'r') as archive_ref:
|
|
221
|
-
_search_in_archive(
|
|
222
|
-
archive_ref, archive_type, file_names_to_search, results, found_set, case_sensitive,
|
|
223
|
-
return_first_only, recursive, callback_functions, extract_file_to_path)
|
|
224
|
-
elif isinstance(file_object, bytes):
|
|
225
|
-
if archive_type == 'zip':
|
|
226
|
-
with BytesIO(file_object) as file_like_object:
|
|
227
|
-
with zipfile.ZipFile(file_like_object, 'r') as archive_ref:
|
|
228
|
-
_search_in_archive(
|
|
229
|
-
archive_ref, archive_type, file_names_to_search, results, found_set, case_sensitive,
|
|
230
|
-
return_first_only, recursive, callback_functions, extract_file_to_path)
|
|
231
|
-
elif archive_type == '7z':
|
|
232
|
-
with BytesIO(file_object) as file_like_object:
|
|
233
|
-
with py7zr.SevenZipFile(file_like_object, 'r') as archive_ref:
|
|
234
|
-
_search_in_archive(
|
|
235
|
-
archive_ref, archive_type, file_names_to_search, results, found_set, case_sensitive,
|
|
236
|
-
return_first_only, recursive, callback_functions, extract_file_to_path)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def search_file_in_archive(
|
|
240
|
-
file_object: Union[str, bytes] = None,
|
|
241
|
-
file_names_to_search: list[str] = None,
|
|
242
|
-
case_sensitive: bool = True,
|
|
243
|
-
return_first_only: bool = False,
|
|
244
|
-
return_empty_list_per_file_name: bool = False,
|
|
245
|
-
recursive: bool = False,
|
|
246
|
-
callback_functions: list = None,
|
|
247
|
-
extract_file_to_path: str = None
|
|
248
|
-
) -> dict[list[bytes], str]:
|
|
249
|
-
"""
|
|
250
|
-
Function searches for the file names inside the zip file and returns a dictionary where the keys are the
|
|
251
|
-
names of the callback functions and the values are lists of found file bytes.
|
|
252
|
-
:param file_object: it can be two types:
|
|
253
|
-
string, full path to the zip file.
|
|
254
|
-
bytes, the bytes of the zip file.
|
|
255
|
-
:param file_names_to_search: list of strings, the names of the files to search.
|
|
256
|
-
:param case_sensitive: boolean, default is 'True'. Determines if file name search should be case sensitive.
|
|
257
|
-
:param return_first_only: boolean, default is 'False'. Return only the first found file for each file name.
|
|
258
|
-
:param return_empty_list_per_file_name: boolean, default is 'False'.
|
|
259
|
-
True: Return empty list for each file name that wasn't found.
|
|
260
|
-
False: Don't return empty list for each file name that wasn't found.
|
|
261
|
-
:param recursive: boolean, default is 'False'. If True, search for file names recursively in nested zip files.
|
|
262
|
-
:param callback_functions: list of callables, default is None. Each function takes a file name and should return a
|
|
263
|
-
boolean that will tell the main function if this file is 'found' or not.
|
|
264
|
-
:param extract_file_to_path: string, full path to the directory where the found files should be extracted.
|
|
265
|
-
:return: dictionary of lists of bytes.
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
if file_names_to_search is None and callback_functions is None:
|
|
269
|
-
raise ValueError("Either file_names_to_search or callback_functions must be provided.")
|
|
270
|
-
|
|
271
|
-
# Initialize results dictionary.
|
|
272
|
-
# results = _initialize_results(callback_functions)
|
|
273
|
-
results: dict[list[bytes], str] = {}
|
|
274
|
-
found_set = set()
|
|
275
|
-
|
|
276
|
-
_search_archive_content(
|
|
277
|
-
file_object, file_names_to_search, results, found_set, case_sensitive, return_first_only, recursive,
|
|
278
|
-
callback_functions, extract_file_to_path)
|
|
279
|
-
|
|
280
|
-
if not return_empty_list_per_file_name:
|
|
281
|
-
# Filter out keys with empty lists.
|
|
282
|
-
results = {key: value for key, value in results.items() if value}
|
|
283
|
-
|
|
284
|
-
return results
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import subprocess
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from ..print_api import print_api
|
|
6
|
-
from .. import process
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def is_path_contains_7z_executable(sevenz_path: str) -> bool:
|
|
10
|
-
"""
|
|
11
|
-
Checks if the path contains 7z executable.
|
|
12
|
-
:param sevenz_path: string, The path to the 7z executable.
|
|
13
|
-
:return: bool, True if the path contains 7z executable, False otherwise.
|
|
14
|
-
"""
|
|
15
|
-
executable_path_parts: tuple = Path(sevenz_path).parts
|
|
16
|
-
|
|
17
|
-
if '7z' not in executable_path_parts[-1]:
|
|
18
|
-
return False
|
|
19
|
-
else:
|
|
20
|
-
return True
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def is_executable_a_7z(sevenz_path: str) -> bool:
|
|
24
|
-
"""
|
|
25
|
-
Checks if the 7z executable is installed.
|
|
26
|
-
:param sevenz_path: string, The path to the 7z executable.
|
|
27
|
-
:return: bool, True if the 7z executable is installed, False otherwise.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
# Check if the process itself is installed.
|
|
31
|
-
if process.is_command_exists(sevenz_path):
|
|
32
|
-
# Check that this is the 7z executable.
|
|
33
|
-
try:
|
|
34
|
-
# Run '7z' command and capture output
|
|
35
|
-
result = subprocess.run([sevenz_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
36
|
-
|
|
37
|
-
# Check if the output contains identifying information
|
|
38
|
-
if b"7-Zip" in result.stdout or b"7-Zip" in result.stderr:
|
|
39
|
-
return True
|
|
40
|
-
except Exception:
|
|
41
|
-
return False
|
|
42
|
-
|
|
43
|
-
return False
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def extract_file(
|
|
47
|
-
file_path: str,
|
|
48
|
-
extract_to: str,
|
|
49
|
-
sevenz_path: str = None,
|
|
50
|
-
force_overwrite: bool = False,
|
|
51
|
-
print_kwargs: dict = None
|
|
52
|
-
):
|
|
53
|
-
"""
|
|
54
|
-
Extracts a file to a directory using 7z executable.
|
|
55
|
-
:param file_path: string, The path to the file to extract.
|
|
56
|
-
:param extract_to: string, The directory to extract the file to.
|
|
57
|
-
:param sevenz_path: string, The path to the 7z executable.
|
|
58
|
-
If None, the default path is used, assuming you added 7z to the PATH environment variable.
|
|
59
|
-
:param force_overwrite: bool, If True, the files will be overwritten if they already exist in the output folder.
|
|
60
|
-
:param print_kwargs: dict, The keyword arguments to pass to the print function.
|
|
61
|
-
:return:
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
# Check if the path contains 7z executable.
|
|
65
|
-
if not is_path_contains_7z_executable(sevenz_path):
|
|
66
|
-
raise ValueError("The path to 7z does not contain 7z executable")
|
|
67
|
-
|
|
68
|
-
if not sevenz_path:
|
|
69
|
-
sevenz_path = '7z'
|
|
70
|
-
|
|
71
|
-
# Check if the 7z executable is installed.
|
|
72
|
-
if not is_executable_a_7z(sevenz_path):
|
|
73
|
-
raise RuntimeError("'7z executable' is not a 7z")
|
|
74
|
-
|
|
75
|
-
if not os.path.exists(extract_to):
|
|
76
|
-
os.makedirs(extract_to)
|
|
77
|
-
|
|
78
|
-
command = [f'{sevenz_path}', 'x', file_path, f'-o{extract_to}']
|
|
79
|
-
if force_overwrite:
|
|
80
|
-
command.append('-y')
|
|
81
|
-
|
|
82
|
-
try:
|
|
83
|
-
subprocess.run(command, check=True)
|
|
84
|
-
print_api(f"Extracted {file_path} to {extract_to}", **(print_kwargs or {}))
|
|
85
|
-
except subprocess.CalledProcessError as e:
|
|
86
|
-
print_api(f"An error occurred: {e}", **(print_kwargs or {}))
|
atomicshop/archiver/sevenzs.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
from io import BytesIO
|
|
2
|
-
from typing import Union
|
|
3
|
-
|
|
4
|
-
import py7zr
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def is_7z_magic_number(
|
|
8
|
-
file_object: Union[str, bytes]
|
|
9
|
-
) -> bool:
|
|
10
|
-
"""
|
|
11
|
-
Function checks if the file is a 7z file, by checking the magic number.
|
|
12
|
-
|
|
13
|
-
:param file_object: can be two types:
|
|
14
|
-
string, full path to the file.
|
|
15
|
-
bytes or BytesIO, the bytes of the file.
|
|
16
|
-
:return: boolean.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
if isinstance(file_object, str):
|
|
20
|
-
with open(file_object, 'rb') as file:
|
|
21
|
-
data = file.read(6)
|
|
22
|
-
elif isinstance(file_object, bytes):
|
|
23
|
-
data = file_object
|
|
24
|
-
else:
|
|
25
|
-
raise TypeError(f'The file_object must be a string or bytes, not {type(file_object)}')
|
|
26
|
-
|
|
27
|
-
# Check if the data is at least 6 bytes long
|
|
28
|
-
if len(data) < 6:
|
|
29
|
-
return False
|
|
30
|
-
|
|
31
|
-
# 7z file signature (magic number)
|
|
32
|
-
# The signature is '7z' followed by 'BCAF271C'
|
|
33
|
-
seven_z_signature = b'7z\xBC\xAF\x27\x1C'
|
|
34
|
-
|
|
35
|
-
# Compare the first 6 bytes of the data with the 7z signature
|
|
36
|
-
result = data.startswith(seven_z_signature)
|
|
37
|
-
|
|
38
|
-
return result
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _is_7z(file_object: Union[str, bytes]) -> bool:
|
|
42
|
-
"""
|
|
43
|
-
THIS IS ONLY FOR THE REFERENCE.
|
|
44
|
-
|
|
45
|
-
Used to use this function since it raised 'py7zr.Bad7zFile' if the file was not a 7z file.
|
|
46
|
-
The problem that 'SevenZipFile.testzip()' checks archived files CRCs and returns the first bad file:
|
|
47
|
-
https://py7zr.readthedocs.io/en/latest/api.html#py7zr.SevenZipFile.testzip
|
|
48
|
-
The problem is when the file IS 7z file, but there can be other problems with the file, it can raise a RuntimeError.
|
|
49
|
-
So, better use the 'is_7z_magic_number' function.
|
|
50
|
-
|
|
51
|
-
Function checks if the file is a 7z file.
|
|
52
|
-
:param file_object: can be two types:
|
|
53
|
-
string, full path to the file.
|
|
54
|
-
bytes or BytesIO, the bytes of the file.
|
|
55
|
-
:return: boolean.
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
if isinstance(file_object, bytes):
|
|
60
|
-
with BytesIO(file_object) as file_object:
|
|
61
|
-
with py7zr.SevenZipFile(file_object) as archive:
|
|
62
|
-
archive.testzip()
|
|
63
|
-
return True
|
|
64
|
-
elif isinstance(file_object, str):
|
|
65
|
-
with py7zr.SevenZipFile(file_object) as archive:
|
|
66
|
-
archive.testzip()
|
|
67
|
-
return True
|
|
68
|
-
except py7zr.Bad7zFile:
|
|
69
|
-
return False
|
|
70
|
-
# For some reason there are files that return this exception instead of Bad7zFile.
|
|
71
|
-
except OSError as e:
|
|
72
|
-
if e.args[0] == 22:
|
|
73
|
-
return False
|
atomicshop/archiver/shutils.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import shutil
|
|
3
|
-
|
|
4
|
-
from ..print_api import print_api
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def extract_archive_with_shutil(file_path: str, target_directory: str, **kwargs) -> str:
|
|
8
|
-
"""
|
|
9
|
-
Function extracts the archive to target directory.
|
|
10
|
-
Returns full path to extracted directory.
|
|
11
|
-
This function doesn't preserve the original date and time of files from the archive, instead the time of extraction
|
|
12
|
-
will be applied.
|
|
13
|
-
|
|
14
|
-
:param file_path: Full file path to archived file to extract.
|
|
15
|
-
:param target_directory: The directory on the filesystem to extract the file to.
|
|
16
|
-
:return: str.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
print_api(f'Extracting {file_path}', **kwargs)
|
|
20
|
-
|
|
21
|
-
extracted_directory: str = str()
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
shutil.unpack_archive(file_path, target_directory)
|
|
25
|
-
file_name = file_path.rsplit(os.sep, maxsplit=1)[1]
|
|
26
|
-
file_name_no_extension = file_name.rsplit('.', maxsplit=1)[0]
|
|
27
|
-
extracted_directory: str = target_directory + os.sep + file_name_no_extension
|
|
28
|
-
except Exception as exception_object:
|
|
29
|
-
print_api(f'Error extracting: {file_path}', error_type=True, **kwargs)
|
|
30
|
-
print_api(exception_object, error_type=True, **kwargs)
|
|
31
|
-
pass
|
|
32
|
-
|
|
33
|
-
print_api(f'Extracted to: {extracted_directory}', **kwargs)
|
|
34
|
-
return extracted_directory
|