atomicshop 2.15.11__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.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
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.15.11'
4
+ __version__ = '3.10.5'
@@ -1,15 +1,16 @@
1
1
  import os
2
2
 
3
+ from atomicshop import filesystem
3
4
  from atomicshop.wrappers.factw.fact_extractor import get_extractor
4
5
  from atomicshop.wrappers.factw import config_install
5
- from atomicshop import permissions, filesystem
6
+ from atomicshop.permissions import ubuntu_permissions
6
7
 
7
8
 
8
9
  def main():
9
10
  get_extractor.get_extractor_script()
10
11
  fact_extractor_executable_path: str = (
11
12
  filesystem.get_working_directory() + os.sep + config_install.FACT_EXTRACTOR_FILE_NAME)
12
- permissions.set_executable_permission(fact_extractor_executable_path)
13
+ ubuntu_permissions.set_executable(fact_extractor_executable_path)
13
14
 
14
15
 
15
16
  if __name__ == '__main__':
@@ -0,0 +1,11 @@
1
+ import sys
2
+
3
+ from atomicshop import dns
4
+
5
+
6
+ def main():
7
+ dns.default_dns_gateway_main()
8
+
9
+
10
+ if __name__ == '__main__':
11
+ sys.exit(main())
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import sys
4
+
5
+ import psutil
6
+
7
+
8
+ def _make_parser():
9
+ parser = argparse.ArgumentParser(
10
+ description="List all local TCP source ports currently in use that have a command line."
11
+ )
12
+
13
+ parser.add_argument(
14
+ '-a', '--show-all', action='store_true',
15
+ help='Show all the TCP ports and not only these that have command line.')
16
+
17
+ return parser
18
+
19
+
20
+ def main(
21
+ show_all: bool = False
22
+ ) -> int:
23
+ # Use a set so we only print each port once
24
+ source_ports = set()
25
+
26
+ try:
27
+ # Get all TCP connections (any state)
28
+ # If you only want established ones, use: kind='tcp' and filter by c.status
29
+ connections = psutil.net_connections(kind='tcp')
30
+ except psutil.AccessDenied:
31
+ print("Access denied when reading connections. Try running as admin/root.")
32
+ return 1
33
+ except Exception as e:
34
+ print(f"Error getting connections: {e}")
35
+ return 1
36
+
37
+ # Cache whether each PID has a non-empty cmdline to avoid repeated lookups
38
+ pid_has_cmdline: dict[int, bool] = {}
39
+
40
+ for c in connections:
41
+ # c.laddr is the local (source) address; may be empty for some entries
42
+ if not c.laddr:
43
+ continue
44
+
45
+ # psutil uses a namedtuple for laddr: (ip, port)
46
+ try:
47
+ port = c.laddr.port # works on modern psutil (namedtuple)
48
+ except AttributeError:
49
+ # fallback if it's a plain tuple
50
+ port = c.laddr[1]
51
+
52
+ if not show_all:
53
+ pid = c.pid
54
+ if pid is None:
55
+ # No associated process => treat as "no command line"
56
+ continue
57
+
58
+ if pid not in pid_has_cmdline:
59
+ try:
60
+ proc = psutil.Process(pid)
61
+ cmdline = proc.cmdline()
62
+ # Non-empty list and at least one non-empty arg
63
+ has_cmd = bool(cmdline and any(arg.strip() for arg in cmdline))
64
+ pid_has_cmdline[pid] = has_cmd
65
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
66
+ # If we can't read it, treat as no command line
67
+ pid_has_cmdline[pid] = False
68
+
69
+ if not pid_has_cmdline[pid]:
70
+ # Skip ports whose owning process has an empty/unavailable cmdline
71
+ continue
72
+
73
+ # Either show_all is True, or the owning process has a non-empty cmdline
74
+ source_ports.add(port)
75
+
76
+ # Print ports sorted for readability
77
+ for port in sorted(source_ports):
78
+ print(port)
79
+
80
+ return 0
81
+
82
+ if __name__ == "__main__":
83
+ arg_parser = _make_parser()
84
+ args = arg_parser.parse_args()
85
+ sys.exit(main(**vars(args)))
@@ -0,0 +1,11 @@
1
+ import sys
2
+
3
+ from atomicshop.wrappers import githubw
4
+
5
+
6
+ def main():
7
+ githubw.github_wrapper_main_with_args()
8
+
9
+
10
+ if __name__ == "__main__":
11
+ sys.exit(main())
@@ -0,0 +1,172 @@
1
+ import sys
2
+ import os
3
+ import tempfile
4
+ import subprocess
5
+ import re
6
+ import base64
7
+ import textwrap
8
+
9
+
10
+ def is_ca_installed(issuer_name: str) -> tuple[bool, str]:
11
+ result = subprocess.run(
12
+ ['certutil', '-store', 'Root', issuer_name],
13
+ capture_output=True,
14
+ text=True,
15
+ )
16
+
17
+ if 'Object was not found' in result.stdout:
18
+ return False, ''
19
+
20
+ if result.returncode == 0:
21
+ return True, ''
22
+ else:
23
+ message: str = (f"stdout: {result.stdout}\n"
24
+ f"stderr: {result.stderr}\n")
25
+ return False, message
26
+
27
+
28
+ def remove_ca_certificate(issuer_name: str) -> int:
29
+ result = subprocess.run(
30
+ ['certutil', '-delstore', 'Root', issuer_name],
31
+ capture_output=True,
32
+ text=True
33
+ )
34
+
35
+ if result.returncode != 0:
36
+ print(f"Error removing certificate: {result.stderr}", file=sys.stderr)
37
+ return 1
38
+
39
+ return 0
40
+
41
+
42
+ def install_ca_certificate(certificate_string: str) -> int:
43
+ pem = certificate_string or ""
44
+
45
+ # Extract one or more CERTIFICATE blocks
46
+ blocks = re.findall(
47
+ r"-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----",
48
+ pem,
49
+ flags=re.DOTALL,
50
+ )
51
+ if not blocks:
52
+ print("Error installing certificate: no PEM CERTIFICATE block found.", file=sys.stderr)
53
+ return 1
54
+
55
+ def normalize_pem(block: str) -> str:
56
+ m = re.search(
57
+ r"-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----",
58
+ block,
59
+ flags=re.DOTALL,
60
+ )
61
+ if not m:
62
+ raise ValueError("Invalid PEM block.")
63
+
64
+ # Remove everything except Base64 alphabet and padding
65
+ b64 = re.sub(r"[^A-Za-z0-9+/=]", "", m.group(1))
66
+
67
+ # Strict decode/encode round-trip to ensure it is valid DER
68
+ der = base64.b64decode(b64, validate=True)
69
+ b64_clean = base64.b64encode(der).decode("ascii")
70
+ wrapped = "\r\n".join(textwrap.wrap(b64_clean, 64))
71
+
72
+ return (
73
+ "-----BEGIN CERTIFICATE-----\r\n"
74
+ + wrapped
75
+ + "\r\n-----END CERTIFICATE-----\r\n"
76
+ )
77
+
78
+ for block in blocks:
79
+ tmp_path = None
80
+ try:
81
+ normalized = normalize_pem(block)
82
+
83
+ with tempfile.NamedTemporaryFile(
84
+ mode="w",
85
+ suffix=".cer",
86
+ delete=False, # certutil needs a real path on Windows
87
+ encoding="ascii",
88
+ newline="",
89
+ ) as f:
90
+ f.write(normalized)
91
+ tmp_path = f.name
92
+
93
+ result = subprocess.run(
94
+ ["certutil", "-f", "-addstore", "Root", tmp_path],
95
+ text=True,
96
+ capture_output=True,
97
+ )
98
+
99
+ if result.returncode != 0:
100
+ # Optional: try to get a more descriptive parse error
101
+ dump = subprocess.run(
102
+ ["certutil", "-dump", tmp_path],
103
+ text=True,
104
+ capture_output=True,
105
+ )
106
+
107
+ print(
108
+ "Error installing certificate:\n"
109
+ f"stdout: {result.stdout}\n"
110
+ f"stderr: {result.stderr}\n"
111
+ "certutil -dump output:\n"
112
+ f"{dump.stdout}\n"
113
+ f"{dump.stderr}",
114
+ file=sys.stderr,
115
+ )
116
+ return 1
117
+
118
+ except Exception as e:
119
+ print(f"Error installing certificate: {e}", file=sys.stderr)
120
+ return 1
121
+
122
+ finally:
123
+ if tmp_path:
124
+ try:
125
+ os.remove(tmp_path)
126
+ except OSError:
127
+ pass
128
+
129
+ return 0
130
+
131
+
132
+ def main() -> int:
133
+ if len(sys.argv) < 3:
134
+ print("Usage: install_ca_certificate.py <Issuer Name> <crt cert string>", file=sys.stderr)
135
+ return 1
136
+
137
+ certificate_string_base64: str = sys.argv[2]
138
+ certificate_string = base64.b64decode(certificate_string_base64).decode("utf-8")
139
+ if not "-----BEGIN CERTIFICATE-----" in certificate_string:
140
+ print("Error: Certificate string must be in PEM format.", file=sys.stderr)
141
+ print(certificate_string, file=sys.stderr)
142
+ return 1
143
+
144
+ issuer_name: str = sys.argv[1]
145
+ is_installed, message = is_ca_installed(issuer_name)
146
+ if not is_installed and message:
147
+ print(f"Error checking certificate installation: {message}", file=sys.stderr)
148
+ return 1
149
+
150
+ if is_installed:
151
+ rc: int = remove_ca_certificate(issuer_name)
152
+ if rc != 0:
153
+ return rc
154
+
155
+ rc: int = install_ca_certificate(certificate_string)
156
+ if rc != 0:
157
+ return rc
158
+
159
+ is_installed, message = is_ca_installed(issuer_name)
160
+ if not is_installed and message:
161
+ print(f"Error checking certificate installation: {message}", file=sys.stderr)
162
+ return 1
163
+
164
+ if not is_installed:
165
+ print("Error: Certificate installation failed.", file=sys.stderr)
166
+ return 1
167
+
168
+ return 0
169
+
170
+
171
+ if __name__ == '__main__':
172
+ sys.exit(main())
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Print the command line of the process that owns a given TCP/UDP source port.
4
+
5
+ Usage:
6
+ python get_cmdline_from_port.py <port> [kind]
7
+
8
+ <port> is required (e.g. 54321)
9
+ [kind] is optional: 'tcp', 'udp', or 'inet' (default: 'inet')
10
+
11
+ Requires: psutil
12
+
13
+ ===========================
14
+
15
+ In Ubuntu, you can check available TCP connections and processes with:
16
+ ss -tnpa
17
+ In Windows, you can check available TCP connections and processes with (only PIDs, no command lines):
18
+ netstat -ano
19
+ """
20
+
21
+ import sys
22
+ import shlex
23
+ import psutil
24
+
25
+
26
+ def find_cmdline_by_port(
27
+ port: int,
28
+ # kind: str = "inet"
29
+ kind: str = "tcp"
30
+ ) -> str | None:
31
+ """
32
+ Return the command line (joined string) of the first process whose local
33
+ port == `port`, for connections of type `kind` ('tcp', 'udp', 'inet', etc).
34
+
35
+ 'tcp' is much more specific and faster than 'inet' (which includes both TCP and UDP).
36
+ """
37
+
38
+ # Single system-wide call; 'inet' = IPv4 + IPv6 only (no unix sockets)
39
+ # Use 'tcp' if you know it's TCP only – slightly faster.
40
+ try:
41
+ conns = psutil.net_connections(kind=kind)
42
+ except psutil.Error:
43
+ return None
44
+
45
+ for conn in conns:
46
+ # Some entries have no local address or PID (e.g. kernel sockets)
47
+ laddr = conn.laddr
48
+ if not laddr:
49
+ continue
50
+
51
+ # laddr is a namedtuple (ip, port) for AF_INET / AF_INET6
52
+ try:
53
+ local_port = laddr.port
54
+ except AttributeError:
55
+ # Older psutil: laddr is a simple tuple (ip, port)
56
+ if len(laddr) < 2:
57
+ continue
58
+ local_port = laddr[1]
59
+
60
+ if local_port != port:
61
+ continue
62
+
63
+ pid = conn.pid
64
+ if pid is None or pid == 0:
65
+ continue
66
+
67
+ try:
68
+ proc = psutil.Process(pid)
69
+ cmdline_list = proc.cmdline()
70
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
71
+ continue
72
+
73
+ if cmdline_list:
74
+ result = shlex.join(cmdline_list)
75
+ else:
76
+ # Fallback to process name if cmdline missing
77
+ try:
78
+ result = proc.name()
79
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
80
+ return None
81
+
82
+ # If result is just a PID string, also fallback to name
83
+ if result.isnumeric():
84
+ try:
85
+ result = proc.name()
86
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
87
+ return None
88
+
89
+ return result
90
+
91
+ return None
92
+
93
+
94
+ def main() -> None:
95
+ if len(sys.argv) < 2:
96
+ print("Usage: get_cmdline_from_port.py <port> [kind]", file=sys.stderr)
97
+ sys.exit(1)
98
+
99
+ try:
100
+ port = int(sys.argv[1])
101
+ except ValueError:
102
+ print("Port must be an integer", file=sys.stderr)
103
+ sys.exit(1)
104
+
105
+ kind = sys.argv[2] if len(sys.argv) >= 3 else "tcp"
106
+
107
+ result = find_cmdline_by_port(port, kind)
108
+ if result is not None:
109
+ print(result)
110
+ sys.exit(0)
111
+ else:
112
+ # Print nothing, or a message – up to you.
113
+ # Empty output is often nicer for scripts that just parse stdout.
114
+ # print(f"No process found with local port {port}", file=sys.stderr)
115
+ sys.exit(2)
116
+
117
+
118
+ if __name__ == "__main__":
119
+ main()
@@ -0,0 +1,90 @@
1
+ import sys
2
+ import socket
3
+ from typing import Union
4
+ import ipaddress
5
+ import subprocess
6
+
7
+ import psutil
8
+
9
+
10
+ def is_ip_address(string_value: str) -> bool:
11
+ try:
12
+ ipaddress.IPv4Address(string_value)
13
+ return True
14
+ except (ipaddress.AddressValueError, ValueError):
15
+ return False
16
+
17
+
18
+ def get_default_internet_ipv4(target: str = "8.8.8.8") -> str:
19
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
20
+ s.connect((target, 80)) # no packet sent; OS just chooses a route
21
+ return s.getsockname()[0] # local address of that route
22
+
23
+
24
+ def get_default_connection_name() -> Union[dict, None]:
25
+ """
26
+ Function to get the default network interface.
27
+ :return: dict[interface_name: details] or None.
28
+ """
29
+ # Get all interfaces.
30
+ interfaces: dict = psutil.net_if_addrs()
31
+ default_ip_address: str = get_default_internet_ipv4()
32
+
33
+ for interface, details in interfaces.items():
34
+ for address in details:
35
+ # Check if the address is an IPv4 address (AF_INET) and not a loopback (127.0.0.1)
36
+ if address.family == socket.AF_INET and not address.address.startswith('127.'):
37
+ # Check if the address is the default IP address.
38
+ if address.address == default_ip_address:
39
+ return {interface: details}
40
+
41
+ return None
42
+
43
+
44
+ def get_default_interface_name() -> str:
45
+ default_connection_name_dict: dict = get_default_connection_name()
46
+ if not default_connection_name_dict:
47
+ return ""
48
+ # Get the first key from the dictionary.
49
+ connection_name: str = list(default_connection_name_dict.keys())[0]
50
+ return connection_name
51
+
52
+
53
+ def set_default_gateway_ipv4(gateway_ipv4: str) -> str | None:
54
+ interface_name: str = get_default_interface_name()
55
+ if not interface_name:
56
+ return "Could not determine the default network interface name."
57
+
58
+ # Set the default gateway using 'netsh' command.
59
+ command: str = f'netsh interface ipv4 set dns name="{interface_name}" static {gateway_ipv4} primary'
60
+ result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
61
+ stdout: str = result.stdout.decode().strip()
62
+ stderr: str = result.stderr.decode().strip()
63
+
64
+ if result.returncode != 0:
65
+ return (f"stdout: {stdout}\n"
66
+ f"stderr: {stderr}")
67
+
68
+ return None
69
+
70
+
71
+ def main() -> int:
72
+ if len(sys.argv) < 2:
73
+ print("Usage: set_default_dns_gateway.py <IPv4>", file=sys.stderr)
74
+ return 1
75
+
76
+ dns_ipv4: str = sys.argv[1]
77
+ if not is_ip_address(dns_ipv4):
78
+ print("Invalid IPv4 address", file=sys.stderr)
79
+ return 1
80
+
81
+ error_message: str | None = set_default_gateway_ipv4(dns_ipv4)
82
+ if error_message:
83
+ print(f"Failed to set default DNS gateway:\n{error_message}", file=sys.stderr)
84
+ return 1
85
+
86
+ return 0
87
+
88
+
89
+ if __name__ == '__main__':
90
+ sys.exit(main())
@@ -0,0 +1,38 @@
1
+ import os.path
2
+ import sys
3
+ import argparse
4
+
5
+ from atomicshop.file_io import tomls
6
+
7
+
8
+ def parse_args():
9
+ parser = argparse.ArgumentParser(
10
+ description='Update the config.toml file.\n'
11
+ 'This script will update the current config.toml file with the keys from the target config.toml file.\n'
12
+ 'If the key is not present in the current config.toml file, it will be added.\n'
13
+ 'If the key is present in the current config.toml file, its value will be left unchanged.\n')
14
+ parser.add_argument('current_config_file', type=str, help="Path to the current config.toml file that will be updated.")
15
+ parser.add_argument('target_config_file', type=str, help="Path to the target config.toml file that we'll get the updates from.")
16
+ parser.add_argument('-n', '--new_file_path', type=str, help="(OPTIONAL) Path to the new config.toml file that will be created with the updates.", default=None)
17
+ return parser.parse_args()
18
+
19
+
20
+ def main():
21
+ args = parse_args()
22
+
23
+ if os.path.isfile(args.current_config_file) is False:
24
+ print(f"Error: The current config file '{args.current_config_file}' does not exist.")
25
+ return 1
26
+ if os.path.isfile(args.target_config_file) is False:
27
+ print(f"Error: The target config file '{args.target_config_file}' does not exist.")
28
+ return 1
29
+
30
+ tomls.update_toml_file_with_new_config(
31
+ main_config_file_path=args.current_config_file,
32
+ changes_config_file_path=args.target_config_file,
33
+ new_config_file_path=args.new_file_path)
34
+ return 0
35
+
36
+
37
+ if __name__ == '__main__':
38
+ sys.exit(main())
@@ -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
 
@@ -36,6 +36,8 @@ class ArgparseWrapper:
36
36
  # formatter_class=RawTextHelpFormatter: shows raw text and not the default argparse text parsing.
37
37
  self.parser = argparse.ArgumentParser(description=self.description_full,
38
38
  usage=self.usage_variable,
39
+ # usage=argparse.SUPPRESS # Remove usage line from the beginning.
40
+ # add_help=False, # Remove help line from the end.
39
41
  formatter_class=RawTextHelpFormatter)
40
42
 
41
43
  def add_arguments(self) -> None:
@@ -1,38 +1,35 @@
1
- def check_3_booleans_when_only_1_can_be_true(boolean1: tuple, boolean2: tuple, boolean3: tuple) -> None:
1
+ def is_only_1_true_in_list(
2
+ booleans_list_of_tuples: list[tuple],
3
+ raise_if_all_false: bool = True
4
+ ) -> None:
2
5
  """
3
- Example:
4
- check_3_booleans_when_only_1_can_be_true(
5
- (self.config['section']['default_usage'], 'default_usage'),
6
- (self.config['section']['create_usage'], 'create_usage'),
7
- (self.config['section']['custom_usage'], 'custom_usage'))
8
- :param boolean1: tuple, (value, string name of the setting you want to print to the user to be aware of).
9
- :param boolean2: tuple, (value, string name of the setting you want to print to the user to be aware of).
10
- :param boolean3: tuple, (value, string name of the setting you want to print to the user to be aware of).
11
- :return:
6
+ Check if only one boolean can be 'True' from a list of booleans
7
+ :param booleans_list_of_tuples: list of tuples, Structure:
8
+ [(value, string name of the setting you want to print to the user to be aware of), ...]
9
+ :param raise_if_all_false: bool, If True, exception will be raised if all booleans are False.
10
+ :return: None
12
11
  """
13
12
 
14
- check_if_3_booleans_are_false(boolean1, boolean2, boolean3)
15
- check_if_2_booleans_are_true(boolean1, boolean2)
16
- check_if_2_booleans_are_true(boolean1, boolean3)
17
- check_if_2_booleans_are_true(boolean2, boolean3)
13
+ # Filter to get all the `True` conditions and their associated names
14
+ true_conditions = [name for value, name in booleans_list_of_tuples if value]
18
15
 
16
+ # Count the number of True values
17
+ true_count = len(true_conditions)
19
18
 
20
- def check_if_3_booleans_are_false(boolean1: tuple, boolean2: tuple, boolean3: tuple):
21
- if not boolean1[0] and not boolean2[0] and not boolean3[0]:
22
- message = f"All the boolean settings in config ini file were set to 'False',\n" \
23
- f"You need at least one 'True':\n" \
24
- f"{boolean1[1]}={boolean1[0]}\n" \
25
- f"{boolean2[1]}={boolean2[0]}\n" \
26
- f"{boolean3[1]}={boolean3[0]}"
27
- raise ValueError(message)
28
-
29
-
30
- def check_if_2_booleans_are_true(boolean1: tuple, boolean2: tuple) -> None:
31
- if boolean1[0] and boolean2[0]:
32
- message = f"Only one configuration can be 'True':\n" \
33
- f"{boolean1[1]}={boolean1[0]}\n" \
34
- f"{boolean2[1]}={boolean2[0]}\n"
35
- raise ValueError(message)
19
+ if true_count == 1:
20
+ # Only one value is True, which is acceptable
21
+ # print(f"Only one condition is True: {true_conditions[0]}.")
22
+ pass
23
+ elif true_count > 1:
24
+ # More than one value is True, raise an exception
25
+ raise ValueError(f"Multiple conditions are True: {', '.join(true_conditions)}.")
26
+ elif true_count == 0 and raise_if_all_false:
27
+ # None of the values are True, and the user does not want to ignore this case
28
+ raise ValueError("No conditions are True, and raise_if_all_false is set to True.")
29
+ else:
30
+ # If no True values and no_raise_if_all_false is True, just pass silently
31
+ # print("No conditions are True (but raise_if_all_false is set to False).")
32
+ pass
36
33
 
37
34
 
38
35
  def convert_string_to_bool(string: str) -> bool: