atomicshop 2.11.47__py3-none-any.whl → 3.10.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- 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/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- 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 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → 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/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/scheduling.py
CHANGED
|
@@ -12,7 +12,7 @@ def periodic_task(interval, priority, function_ref, args=(), sched_object=None):
|
|
|
12
12
|
sched_object.run()
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_name=None):
|
|
15
|
+
def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_name=None, daemon=True):
|
|
16
16
|
"""
|
|
17
17
|
The function executes referenced function 'function_ref' with arguments 'args' each 'interval' in a new thread.
|
|
18
18
|
The old thread is closed, each time the new is executed.
|
|
@@ -22,8 +22,12 @@ def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_
|
|
|
22
22
|
:param args: tuple, of arguments to provide for the 'function_ref' to execute.
|
|
23
23
|
:param kwargs: dictionary, of keyword arguments to provide for the 'function_ref' to execute.
|
|
24
24
|
:param thread_name: the name of the thread that will be created:
|
|
25
|
-
threading.Thread(target=thread_timer, name=thread_name)
|
|
25
|
+
threading.Thread(target=thread_timer, name=thread_name)
|
|
26
26
|
The default parameter for 'Thread' 'name' is 'None', so if you don't specify the name it works as default.
|
|
27
|
+
:param daemon: bool, if True, the thread will be a daemon thread. Default is True.
|
|
28
|
+
Since this is a periodic task, we don't need to wait for the thread to finish, so we can set it to True.
|
|
29
|
+
|
|
30
|
+
:return: thread object.
|
|
27
31
|
"""
|
|
28
32
|
|
|
29
33
|
# If 'kwargs' is not provided, we'll initialize it as an empty dictionary.
|
|
@@ -50,7 +54,13 @@ def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_
|
|
|
50
54
|
time.sleep(interval)
|
|
51
55
|
|
|
52
56
|
# Start in a new thread.
|
|
53
|
-
threading.Thread(target=thread_timer, name=thread_name)
|
|
57
|
+
thread = threading.Thread(target=thread_timer, name=thread_name)
|
|
58
|
+
|
|
59
|
+
if daemon:
|
|
60
|
+
thread.daemon = True
|
|
61
|
+
|
|
62
|
+
thread.start()
|
|
63
|
+
return thread
|
|
54
64
|
|
|
55
65
|
|
|
56
66
|
class ThreadLooper:
|
|
@@ -60,7 +70,14 @@ class ThreadLooper:
|
|
|
60
70
|
def __init__(self):
|
|
61
71
|
self.loop_queue = queue.Queue()
|
|
62
72
|
|
|
63
|
-
def run_loop(
|
|
73
|
+
def run_loop(
|
|
74
|
+
self,
|
|
75
|
+
function_reference,
|
|
76
|
+
args=(),
|
|
77
|
+
kwargs=None,
|
|
78
|
+
interval_seconds=0,
|
|
79
|
+
thread_name: str = None
|
|
80
|
+
):
|
|
64
81
|
"""
|
|
65
82
|
The function executes referenced function 'function_ref' with arguments 'args' each 'interval' in a new thread.
|
|
66
83
|
|
|
@@ -86,7 +103,9 @@ class ThreadLooper:
|
|
|
86
103
|
self.loop_queue.put(result_list)
|
|
87
104
|
time.sleep(interval_seconds)
|
|
88
105
|
|
|
89
|
-
threading.Thread(target=thread_function, name=thread_name)
|
|
106
|
+
thread = threading.Thread(target=thread_function, name=thread_name)
|
|
107
|
+
thread.daemon = True
|
|
108
|
+
thread.start()
|
|
90
109
|
|
|
91
110
|
def emit_from_loop(self):
|
|
92
111
|
"""
|
atomicshop/sound.py
CHANGED
|
@@ -280,11 +280,13 @@ class StereoMixRecorder:
|
|
|
280
280
|
# when playback can start before the input interface is initialized, since thread runs in parallel.
|
|
281
281
|
self.loopback_input = self._initialize_input_interface()
|
|
282
282
|
|
|
283
|
-
threading.Thread(
|
|
283
|
+
thread = threading.Thread(
|
|
284
284
|
target=self._thread_record,
|
|
285
285
|
args=(split_emit_buffers, emit_type, file_path, record_until_zero_array, seconds),
|
|
286
286
|
kwargs=kwargs
|
|
287
|
-
)
|
|
287
|
+
)
|
|
288
|
+
thread.daemon = True
|
|
289
|
+
thread.start()
|
|
288
290
|
|
|
289
291
|
def _thread_record(
|
|
290
292
|
self, split_emit_buffers: int, emit_type: str, file_path: str, record_until_zero_array: bool, seconds,
|
atomicshop/speech_recognize.py
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# TODO: Change manual wrapper to:
|
|
2
|
+
# from ffmpy import FFmpeg
|
|
3
|
+
# ff = FFmpeg(
|
|
4
|
+
# inputs={'input.mp4': None},
|
|
5
|
+
# outputs={'output.avi': None}
|
|
6
|
+
# )
|
|
7
|
+
# ff.run()
|
|
8
|
+
|
|
1
9
|
from .wrappers.ffmpegw import FFmpegWrapper
|
|
2
10
|
from .tempfiles import TempFile
|
|
3
11
|
from .web import download
|
atomicshop/ssh_remote.py
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import shlex
|
|
4
5
|
|
|
5
|
-
from .print_api import print_api
|
|
6
|
-
from .wrappers.loggingw import loggingw
|
|
7
|
-
from .wrappers.socketw import base
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# External Libraries
|
|
11
6
|
try:
|
|
12
7
|
import paramiko
|
|
13
8
|
except ImportError as exception_object:
|
|
14
9
|
print(f"Library missing: {exception_object.name}. Install by executing: pip install paramiko")
|
|
15
10
|
sys.exit()
|
|
16
11
|
|
|
12
|
+
from .print_api import print_api
|
|
13
|
+
from .wrappers.loggingw import loggingw
|
|
14
|
+
from .wrappers.socketw import socket_base
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SSHRemoteWrapperNoPythonFound(Exception):
|
|
18
|
+
"""Raised when no usable Python 3 interpreter found on remote host."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
class SSHRemote:
|
|
19
23
|
"""
|
|
@@ -87,9 +91,13 @@ class SSHRemote:
|
|
|
87
91
|
sys.exit(main())
|
|
88
92
|
|
|
89
93
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
ip_address: str,
|
|
97
|
+
username: str,
|
|
98
|
+
password: str,
|
|
99
|
+
logger: logging.Logger = None
|
|
100
|
+
):
|
|
93
101
|
self.ip_address: str = ip_address
|
|
94
102
|
self.username: str = username
|
|
95
103
|
self.password: str = password
|
|
@@ -97,11 +105,23 @@ class SSHRemote:
|
|
|
97
105
|
# Initializing paramiko SSHClient class
|
|
98
106
|
self.ssh_client = paramiko.SSHClient()
|
|
99
107
|
|
|
100
|
-
|
|
108
|
+
# Variable to store detected python command on remote (python3 / python).
|
|
109
|
+
self.python_cmd: str | None = None
|
|
110
|
+
|
|
111
|
+
if logger:
|
|
112
|
+
# Create child logger for the provided logger with the module's name.
|
|
113
|
+
self.logger: logging.Logger = loggingw.get_logger_with_level(f'{logger.name}.{Path(__file__).stem}')
|
|
114
|
+
else:
|
|
115
|
+
self.logger: logging.Logger = logger
|
|
116
|
+
|
|
117
|
+
def connect(
|
|
118
|
+
self,
|
|
119
|
+
timeout: int = 60
|
|
120
|
+
):
|
|
101
121
|
error: str = str()
|
|
102
122
|
|
|
103
123
|
# Get all local interfaces IPv4 addresses.
|
|
104
|
-
local_interfaces_ipv4 =
|
|
124
|
+
local_interfaces_ipv4 = socket_base.get_local_network_interfaces_ip_address("ipv4", True)
|
|
105
125
|
# Check if the target IP address is in the list of local interfaces.
|
|
106
126
|
if self.ip_address in local_interfaces_ipv4:
|
|
107
127
|
# If it is, we don't need to connect to it via SSH, it means that we want to connect to ourselves.
|
|
@@ -119,75 +139,15 @@ class SSHRemote:
|
|
|
119
139
|
# with description of
|
|
120
140
|
# Server 'address_goes_here' not found in known_hosts
|
|
121
141
|
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# When port 22 is unreachable on the client.
|
|
126
|
-
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
|
127
|
-
error = str(e)
|
|
128
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
129
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
130
|
-
pass
|
|
131
|
-
except paramiko.ssh_exception.SSHException as e:
|
|
132
|
-
error = str(e)
|
|
133
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
134
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
135
|
-
pass
|
|
136
|
-
except ConnectionResetError:
|
|
137
|
-
# Returning the error.
|
|
138
|
-
error = "An existing connection was forcibly closed by the remote host."
|
|
139
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
140
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
141
|
-
pass
|
|
142
|
-
except TimeoutError:
|
|
143
|
-
# Returning the error.
|
|
144
|
-
error = "Connection timed out."
|
|
145
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
146
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
147
|
-
pass
|
|
142
|
+
|
|
143
|
+
# Executing SSH connection to client.
|
|
144
|
+
self.ssh_client.connect(self.ip_address, username=self.username, password=self.password, timeout=timeout)
|
|
148
145
|
|
|
149
146
|
return error
|
|
150
147
|
|
|
151
148
|
def close(self):
|
|
152
149
|
self.ssh_client.close()
|
|
153
150
|
|
|
154
|
-
def exec_command_with_error_handling(self, script_string: str):
|
|
155
|
-
# Defining variables.
|
|
156
|
-
stdin = None
|
|
157
|
-
stdout = None
|
|
158
|
-
stderr = None
|
|
159
|
-
result_exception = None
|
|
160
|
-
|
|
161
|
-
# Don't put debugging break point over the next line in PyCharm. For some reason it gets stuck.
|
|
162
|
-
# Put the point right after that.
|
|
163
|
-
try:
|
|
164
|
-
stdin, stdout, stderr = self.ssh_client.exec_command(command=script_string, timeout=30)
|
|
165
|
-
except AttributeError as function_exception_object:
|
|
166
|
-
if function_exception_object.name == "open_session":
|
|
167
|
-
result_exception = "'SSHRemote().connect' wasn't executed."
|
|
168
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
169
|
-
|
|
170
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
171
|
-
pass
|
|
172
|
-
else:
|
|
173
|
-
result_exception = f"Couldn't execute script over SSH. Unknown yet exception with 'AttributeError' " \
|
|
174
|
-
f"and name: {function_exception_object.name}"
|
|
175
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
176
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
177
|
-
pass
|
|
178
|
-
except socket.error:
|
|
179
|
-
result_exception = "Couldn't execute script over SSH. SSH socket closed."
|
|
180
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
181
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
182
|
-
pass
|
|
183
|
-
except Exception:
|
|
184
|
-
result_exception = "Couldn't execute script over SSH. Unknown yet exception."
|
|
185
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
186
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
187
|
-
pass
|
|
188
|
-
|
|
189
|
-
return stdin, stdout, stderr, result_exception
|
|
190
|
-
|
|
191
151
|
@staticmethod
|
|
192
152
|
def check_console_output_for_errors(console_output_string: str):
|
|
193
153
|
# Defining variables.
|
|
@@ -202,104 +162,132 @@ class SSHRemote:
|
|
|
202
162
|
if "ModuleNotFoundError: No module named" in line:
|
|
203
163
|
function_result = f"Python library is not installed - {line}"
|
|
204
164
|
break
|
|
165
|
+
else:
|
|
166
|
+
function_result = console_output_string
|
|
205
167
|
|
|
206
168
|
return function_result
|
|
207
169
|
|
|
208
|
-
def remote_execution(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
170
|
+
def remote_execution(
|
|
171
|
+
self,
|
|
172
|
+
command: str,
|
|
173
|
+
script_string: str = None
|
|
174
|
+
) -> tuple[str, str]:
|
|
175
|
+
"""
|
|
176
|
+
Function to execute any command over SSH.
|
|
177
|
+
|
|
178
|
+
:param command: command to execute over SSH.
|
|
179
|
+
:param script_string: string representation of script to execute as input.
|
|
180
|
+
Example:
|
|
181
|
+
command = "python - 56734"
|
|
182
|
+
script_string = "import sys;print(f'sys.argv[0]')"
|
|
183
|
+
|
|
184
|
+
Under ssh in terminal it would execute like this:
|
|
185
|
+
ssh User@HostIpv4
|
|
186
|
+
|
|
187
|
+
python - 56734 << EOF
|
|
188
|
+
import sys;print(f'sys.argv[0]')
|
|
189
|
+
EOF
|
|
190
|
+
|
|
191
|
+
Or as onliner:
|
|
192
|
+
ssh User@HostIpv4 "python - 56734 << EOF import sys;print(f'sys.argv[0]') EOF"
|
|
193
|
+
|
|
194
|
+
or using a specific file path:
|
|
195
|
+
ssh User@HostIpv4 "python - 56734" < /path/to/script.py
|
|
196
|
+
|
|
197
|
+
:return: SSH console output, Error output
|
|
198
|
+
"""
|
|
199
|
+
output_result: str = str()
|
|
200
|
+
error_result: str = str()
|
|
216
201
|
|
|
217
202
|
# Execute the command over SSH remotely.
|
|
218
|
-
stdin, stdout, stderr
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# And connect again.
|
|
225
|
-
self.connect()
|
|
226
|
-
self.logger.info("Reconnected. Trying to send the command one more time.")
|
|
227
|
-
# Try to execute the command over SSH remotely again.
|
|
228
|
-
stdin, stdout, stderr, exec_exception = self.exec_command_with_error_handling(script_string)
|
|
229
|
-
# If there was an exception again.
|
|
230
|
-
if exec_exception:
|
|
231
|
-
# Populate the function_error variable that will be returned outside.
|
|
232
|
-
function_error = exec_exception
|
|
233
|
-
|
|
234
|
-
# If there was no exception executing the remote command.
|
|
235
|
-
if not function_error:
|
|
236
|
-
# Reading the buffer of stdout.
|
|
237
|
-
output_lines = stdout.readlines()
|
|
238
|
-
# Reading the buffer of stderr.
|
|
239
|
-
function_error = stderr.readlines()
|
|
240
|
-
|
|
241
|
-
# Joining error lines list to string if not empty.
|
|
242
|
-
if function_error:
|
|
243
|
-
function_error = ''.join(function_error)
|
|
244
|
-
# Else, joining output lines to string.
|
|
245
|
-
else:
|
|
246
|
-
output_lines = ''.join(output_lines)
|
|
203
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(command=command, timeout=30)
|
|
204
|
+
|
|
205
|
+
# Writing the script string into stdin buffer.
|
|
206
|
+
if script_string:
|
|
207
|
+
stdin.write(script_string)
|
|
208
|
+
stdin.channel.shutdown_write()
|
|
247
209
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
210
|
+
# Reading the buffer of stdout.
|
|
211
|
+
output_lines: list = stdout.readlines()
|
|
212
|
+
# Reading the buffer of stderr.
|
|
213
|
+
error_lines: list = stderr.readlines()
|
|
252
214
|
|
|
253
|
-
|
|
215
|
+
# Joining error lines list to string if not empty.
|
|
216
|
+
if error_lines:
|
|
217
|
+
error_result: str = ''.join(error_lines)
|
|
218
|
+
# Else, joining output lines to string.
|
|
219
|
+
else:
|
|
220
|
+
output_result = ''.join(output_lines)
|
|
254
221
|
|
|
255
|
-
|
|
222
|
+
# Since they're "file-like" objects we need to close them after we finished using.
|
|
223
|
+
stdin.close()
|
|
224
|
+
stdout.close()
|
|
225
|
+
stderr.close()
|
|
226
|
+
|
|
227
|
+
return output_result, error_result
|
|
228
|
+
|
|
229
|
+
def _detect_remote_python_cmd_name(self) -> str:
|
|
230
|
+
"""
|
|
231
|
+
Try 'python3' then 'python' on the remote, return the one that is Python 3.
|
|
232
|
+
Raises if neither works.
|
|
233
|
+
"""
|
|
234
|
+
for candidate in ("python3", "python"):
|
|
235
|
+
# Use a simple version check that works on both Windows and Linux
|
|
236
|
+
cmd = f'{candidate} -c "import sys; print(sys.version_info[0])"'
|
|
237
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(cmd, timeout=5)
|
|
238
|
+
|
|
239
|
+
out = stdout.read().decode().strip()
|
|
240
|
+
exit_status = stdout.channel.recv_exit_status()
|
|
241
|
+
|
|
242
|
+
if exit_status == 0 and out == "3":
|
|
243
|
+
print_api(f"Detected remote Python 3 interpreter (once per client port): {candidate}", logger=self.logger)
|
|
244
|
+
return candidate
|
|
245
|
+
|
|
246
|
+
raise SSHRemoteWrapperNoPythonFound("No usable Python 3 interpreter found on remote host")
|
|
247
|
+
|
|
248
|
+
def _get_python_cmd(self) -> str:
|
|
249
|
+
if self.python_cmd is None:
|
|
250
|
+
self.python_cmd = self._detect_remote_python_cmd_name()
|
|
251
|
+
return self.python_cmd
|
|
252
|
+
|
|
253
|
+
def remote_execution_python(
|
|
254
|
+
self,
|
|
255
|
+
script_string: str,
|
|
256
|
+
script_arg_values: tuple = None,
|
|
257
|
+
script_kwargs: dict = None,
|
|
258
|
+
):
|
|
256
259
|
"""
|
|
257
260
|
Function to execute python script over SSH.
|
|
258
261
|
|
|
259
|
-
Example:
|
|
260
|
-
network.logger.info("Initializing SSH connection to get the calling process.")
|
|
261
|
-
|
|
262
|
-
# Initializing SSHRemote class.
|
|
263
|
-
ssh_client = SSHRemote(ip_address=client_message.client_ip, username=username, password=password)
|
|
264
|
-
# Making actual SSH Connection to the computer.
|
|
265
|
-
ssh_connection_error = ssh_client.connect()
|
|
266
|
-
# If there's an exception / error during connection.
|
|
267
|
-
if ssh_connection_error:
|
|
268
|
-
# Put the error in the process name value.
|
|
269
|
-
client_message.process_name = ssh_connection_error
|
|
270
|
-
else:
|
|
271
|
-
# If no error, then initialize the variables for python script execution over SSH.
|
|
272
|
-
remote_output = remote_error = None
|
|
273
|
-
|
|
274
|
-
# Put source port variable inside the string script.
|
|
275
|
-
script_string: str = \
|
|
276
|
-
put_variable_into_string_script(ssh_script_port_by_process_string, client_message.source_port)
|
|
277
|
-
|
|
278
|
-
# Execute the python script on remote computer over SSH.
|
|
279
|
-
remote_output, remote_error = ssh_client.remote_execution_python(script_string)
|
|
280
|
-
|
|
281
|
-
# If there was an error during execution, put it in process name.
|
|
282
|
-
if remote_error:
|
|
283
|
-
client_message.process_name = remote_error
|
|
284
|
-
# If there was no error during execution, put the output of the ssh to process name.
|
|
285
|
-
else:
|
|
286
|
-
client_message.process_name = remote_output
|
|
287
|
-
network.logger.info(f"Remote SSH: Client executing Command Line: {client_message.process_name}")
|
|
288
|
-
|
|
289
262
|
:param script_string: string representation of python script.
|
|
263
|
+
:param script_arg_values: values arguments to pass to the script. Example for first argument: 56734
|
|
264
|
+
:param script_kwargs: keyword arguments to pass to the script.
|
|
265
|
+
Example: {'-r': None}
|
|
266
|
+
Interpreted as: -r
|
|
267
|
+
Example: {'-f': 'value'}
|
|
268
|
+
Interpreted as: -f value
|
|
269
|
+
Example: {'--arg': value}
|
|
270
|
+
Interpreted as: --arg value
|
|
271
|
+
|
|
290
272
|
:return: SSH console output, Error output
|
|
291
273
|
"""
|
|
292
274
|
# Defining variables.
|
|
293
|
-
|
|
294
|
-
|
|
275
|
+
error_result: str | None = None
|
|
276
|
+
|
|
277
|
+
python_cmd = self._get_python_cmd()
|
|
278
|
+
command: str = f"{python_cmd} -"
|
|
295
279
|
|
|
296
|
-
|
|
297
|
-
|
|
280
|
+
if script_arg_values:
|
|
281
|
+
for arg in script_arg_values:
|
|
282
|
+
command += " " + shlex.quote(str(arg))
|
|
298
283
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
284
|
+
if script_kwargs:
|
|
285
|
+
for key, value in script_kwargs.items():
|
|
286
|
+
command += f" {shlex.quote(str(key))}"
|
|
287
|
+
if value is not None:
|
|
288
|
+
command += " " + shlex.quote(str(value))
|
|
289
|
+
|
|
290
|
+
remote_output, remote_error = self.remote_execution(command=command, script_string=script_string)
|
|
303
291
|
|
|
304
292
|
# If there was an error during remote execution
|
|
305
293
|
if remote_error:
|
|
@@ -308,37 +296,35 @@ class SSHRemote:
|
|
|
308
296
|
# If the message is known and didn't return empty.
|
|
309
297
|
if console_check:
|
|
310
298
|
# 'execution_error' variable will be that full error.
|
|
311
|
-
|
|
299
|
+
error_result = console_check
|
|
300
|
+
else:
|
|
301
|
+
error_result = remote_error
|
|
312
302
|
|
|
313
|
-
return remote_output,
|
|
303
|
+
return remote_output, error_result
|
|
314
304
|
|
|
315
|
-
def connect_get_client_commandline(
|
|
305
|
+
def connect_get_client_commandline(
|
|
306
|
+
self,
|
|
307
|
+
port: int,
|
|
308
|
+
script_string: str):
|
|
316
309
|
# Defining locals.
|
|
317
|
-
execution_output = None
|
|
318
|
-
execution_error = None
|
|
310
|
+
execution_output: str | None = None
|
|
319
311
|
|
|
320
312
|
# Making actual SSH Connection to the computer.
|
|
321
313
|
execution_error = self.connect()
|
|
322
314
|
# if there was an error, try to connect again.
|
|
323
315
|
if execution_error:
|
|
324
|
-
|
|
316
|
+
print_api("Retrying SSH Connection Initialization.", logger=self.logger, logger_method='info')
|
|
325
317
|
execution_error = self.connect()
|
|
326
318
|
|
|
327
319
|
# If there was still an error, we won't be executing the script. And the error will be passed to
|
|
328
320
|
# 'process_name'.
|
|
329
321
|
if not execution_error:
|
|
330
|
-
|
|
322
|
+
print_api("Executing SSH command to acquire the calling process.", logger=self.logger, logger_method='info')
|
|
331
323
|
|
|
332
|
-
|
|
333
|
-
execution_output, execution_error = self.remote_execution_python(script_string)
|
|
334
|
-
# Basically we don't care much about SSH exceptions. Just log them and pass to record.
|
|
335
|
-
except Exception as function_exception_object:
|
|
336
|
-
execution_error = function_exception_object
|
|
337
|
-
print_api(execution_error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
338
|
-
pass
|
|
324
|
+
execution_output, execution_error = self.remote_execution_python(script_string=script_string, script_arg_values=(str(port),))
|
|
339
325
|
|
|
340
326
|
# Closing SSH connection at this stage.
|
|
341
327
|
self.close()
|
|
342
|
-
|
|
328
|
+
print_api("Acquired. Closed SSH connection.", logger=self.logger, logger_method='info')
|
|
343
329
|
|
|
344
330
|
return execution_output, execution_error
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from ...wrappers.pywin32w import winshell
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
STARTUP_FOLDER = os.path.join(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def add_to_startup_folder_with_shortcut(exe_file_path: str, shortcut_name: str) -> str:
|
|
11
|
+
"""
|
|
12
|
+
This function will create a shortcut in the startup folder to your executable.
|
|
13
|
+
|
|
14
|
+
:param exe_file_path: The path to your executable file.
|
|
15
|
+
:param shortcut_name: The name of the shortcut file to create in the startup folder.
|
|
16
|
+
No need to add the ".lnk" extension.
|
|
17
|
+
:return: The path to the shortcut file created.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Get the startup folder path and create if non-existent.
|
|
21
|
+
Path(STARTUP_FOLDER).mkdir(parents=True, exist_ok=True)
|
|
22
|
+
|
|
23
|
+
shortcut_file_path = str(Path(STARTUP_FOLDER, f'{shortcut_name}.lnk'))
|
|
24
|
+
|
|
25
|
+
# Create a shortcut to the executable file.
|
|
26
|
+
winshell.create_shortcut(exe_file_path, shortcut_file_path)
|
|
27
|
+
|
|
28
|
+
return shortcut_file_path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_in_startup_folder(shortcut_name: str):
|
|
32
|
+
"""
|
|
33
|
+
This function will check if the shortcut is in the startup folder.
|
|
34
|
+
|
|
35
|
+
:param shortcut_name: The name of the shortcut file to check in the startup folder.
|
|
36
|
+
No need to add the ".LNK" extension.
|
|
37
|
+
:return: True if the shortcut is in the startup folder, False otherwise.
|
|
38
|
+
"""
|
|
39
|
+
return Path(STARTUP_FOLDER, f'{shortcut_name}.lnk').exists()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def remove_from_startup_folder(shortcut_name: str):
|
|
43
|
+
"""
|
|
44
|
+
This function will remove the shortcut from the startup folder.
|
|
45
|
+
|
|
46
|
+
:param shortcut_name: The name of the shortcut file to remove from the startup folder.
|
|
47
|
+
No need to add the ".LNK" extension.
|
|
48
|
+
"""
|
|
49
|
+
shortcut_file_path = Path(STARTUP_FOLDER, f'{shortcut_name}.lnk')
|
|
50
|
+
if shortcut_file_path.exists():
|
|
51
|
+
shortcut_file_path.unlink()
|
|
52
|
+
return True
|
|
53
|
+
return False
|