lanscape 2.1.2__py3-none-any.whl → 2.2.0a3__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.
lanscape/ui/main.py CHANGED
@@ -1,17 +1,19 @@
1
1
  """Main entry point for the LANscape application when running as a module."""
2
2
  import socket
3
3
 
4
-
5
- import threading
6
4
  import time
7
5
  import logging
8
6
  import traceback
9
7
  import os
8
+ from subprocess import Popen
9
+ import webbrowser
10
10
  import requests
11
11
 
12
+ from pwa_launcher import open_pwa, ChromiumNotFoundError
13
+
14
+
12
15
  from lanscape.core.logger import configure_logging
13
16
  from lanscape.core.runtime_args import parse_args
14
- from lanscape.core.web_browser import open_webapp
15
17
  from lanscape.core.version_manager import get_installed_version, is_update_available
16
18
  from lanscape.ui.app import start_webserver_daemon, start_webserver
17
19
  # do this so any logs generated on import are displayed
@@ -70,7 +72,7 @@ def try_check_update():
70
72
  log.warning('Unable to check for updates.')
71
73
 
72
74
 
73
- def open_browser(url: str, wait=2) -> bool:
75
+ def open_browser(url: str, wait=2) -> Popen:
74
76
  """
75
77
  Open a browser window to the specified
76
78
  url after waiting for the server to start
@@ -78,12 +80,18 @@ def open_browser(url: str, wait=2) -> bool:
78
80
  try:
79
81
  time.sleep(wait)
80
82
  log.info(f'Starting UI - http://127.0.0.1:{args.port}')
81
- return open_webapp(url)
82
-
83
+ return open_pwa(url)
84
+
85
+ except ChromiumNotFoundError:
86
+ success = webbrowser.open(url)
87
+ if success:
88
+ log.warning("Chromium browser not found. Falling back to default web browser.")
89
+ else:
90
+ log.warning(f"Cannot find any web browser. LANScape UI running on {url}")
83
91
  except BaseException:
84
92
  log.debug(traceback.format_exc())
85
93
  log.info(f'Unable to open web browser, server running on {url}')
86
- return False
94
+ return None
87
95
 
88
96
 
89
97
  def start_webserver_ui():
@@ -97,19 +105,16 @@ def start_webserver_ui():
97
105
  # if it was, dont open the browser again
98
106
  log.info('Opening UI as daemon')
99
107
  if not IS_FLASK_RELOAD:
100
- threading.Thread(
101
- target=open_browser,
102
- args=(uri,),
103
- daemon=True
104
- ).start()
108
+ open_browser(uri)
105
109
  start_webserver(args)
106
110
  else:
107
111
  flask_thread = start_webserver_daemon(args)
108
- app_closed = open_browser(uri)
112
+ proc = open_browser(uri)
113
+ if proc:
114
+ app_closed = proc.wait()
115
+ else:
116
+ app_closed = False
109
117
 
110
- # depending on env, open_browser may or
111
- # may not be coupled with the closure of UI
112
- # (if in browser tab, it's uncoupled)
113
118
  if not app_closed or args.persistent:
114
119
  # not doing a direct join so i can still
115
120
  # terminate the app with ctrl+c
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lanscape
3
- Version: 2.1.2
3
+ Version: 2.2.0a3
4
4
  Summary: A python based local network scanner
5
5
  Author-email: Michael Dennis <michael@dipduo.com>
6
6
  License-Expression: MIT
@@ -25,6 +25,7 @@ Requires-Dist: scapy<3.0,>=2.3.2
25
25
  Requires-Dist: tabulate==0.9.0
26
26
  Requires-Dist: pydantic
27
27
  Requires-Dist: icmplib
28
+ Requires-Dist: pwa-launcher>=0.5.0
28
29
  Provides-Extra: dev
29
30
  Requires-Dist: pytest>=8.0; extra == "dev"
30
31
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
@@ -15,7 +15,6 @@ lanscape/core/scan_config.py,sha256=A2ZKXqXKW9nrP6yLb7b9b3XqSY_cQB3LZ5K0LVCSebE,
15
15
  lanscape/core/service_scan.py,sha256=wTDxOdazOsbI0hwCBR__4UCmB2RIbl2pw3F2YUW9aaE,6428
16
16
  lanscape/core/subnet_scan.py,sha256=PtSOk92dK05-reyr8LBkOXaI15qpYnar5nDqALCX1tQ,14850
17
17
  lanscape/core/version_manager.py,sha256=eGjyKgsv31QO0W26se9pPQ1TwmEN8qn37dHULtoocqc,2841
18
- lanscape/core/web_browser.py,sha256=23MuGIrBYdGhw6ejj6OWxwReeKIlWhtWukc1dKV_3_0,6736
19
18
  lanscape/resources/mac_addresses/convert_csv.py,sha256=hvlyLs0XmuuhBuvXBNRGP1cKJzYVRSf8VfUJ1VqROms,1189
20
19
  lanscape/resources/mac_addresses/mac_db.json,sha256=Lng2yJerwO7vjefzxzgtE203hry1lIsCadHL1A5Rptg,2136137
21
20
  lanscape/resources/ports/convert_csv.py,sha256=MMLDa-5pGMsn4A2_k45jHsRYffrRY_0Z2D1ziiikeQA,1143
@@ -27,7 +26,7 @@ lanscape/resources/ports/test_port_list_scan.json,sha256=qXuWGQ_sGIRCVrhJxMeWcHK
27
26
  lanscape/resources/services/definitions.jsonc,sha256=M9BDeK-mh25sEVj8xDEYbU2ix7EETVWhbiYmMb14Gjg,20905
28
27
  lanscape/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
28
  lanscape/ui/app.py,sha256=rg4UGHgbVHpU2jdabSwBoSqGna7WGOunPkPc5Tvds9w,4076
30
- lanscape/ui/main.py,sha256=UCrhHnyFBqVmHTMus33CTp3vdNn3miT1xXJatR4N7ho,4176
29
+ lanscape/ui/main.py,sha256=xgsf3QCLsIsdHX03xTalXJ63p11vJWo2u54ODEmKoCA,4327
31
30
  lanscape/ui/shutdown_handler.py,sha256=HrEnWrdYSzLDVsPgD8tf9FgtAwQrZNECDu6wnEs27EY,1704
32
31
  lanscape/ui/blueprints/__init__.py,sha256=EjPtaR5Nh17pGiOVYTJULVNaZntpFZOiYyep8rBWAiE,265
33
32
  lanscape/ui/blueprints/api/__init__.py,sha256=5Z4Y7B36O-bNFenpomfuNhPuJ9dW_MC0TPUU3pCFVfA,103
@@ -69,9 +68,9 @@ lanscape/ui/templates/scan/ip-table-row.html,sha256=iSW3PYev3_k7pxTZUJUboqDUgdhs
69
68
  lanscape/ui/templates/scan/ip-table.html,sha256=AT2ZvCPYdKl-XJiAkEAawPOVuQw-w0MXumGQTr3zyKM,926
70
69
  lanscape/ui/templates/scan/overview.html,sha256=xWj9jWDPg2KcPLvS8fnSins23_UXjKCdb2NJwNG2U2Q,1176
71
70
  lanscape/ui/templates/scan/scan-error.html,sha256=wmAYQ13IJHUoO8fAGNDjMvNml7tu4rsIU3Vav71ETlA,999
72
- lanscape-2.1.2.dist-info/licenses/LICENSE,sha256=VLoE0IrNTIc09dFm7hMN0qzk4T3q8V0NaPcFQqMemDs,1070
73
- lanscape-2.1.2.dist-info/METADATA,sha256=oxHxoHbAYnhTnSIgsMyzzgqevSrWmF5Fzti1bqjLAao,3578
74
- lanscape-2.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
- lanscape-2.1.2.dist-info/entry_points.txt,sha256=evxSxUikFa1OEd4e0Boky9sLH87HdgM0YqB_AbB2HYc,51
76
- lanscape-2.1.2.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
77
- lanscape-2.1.2.dist-info/RECORD,,
71
+ lanscape-2.2.0a3.dist-info/licenses/LICENSE,sha256=VLoE0IrNTIc09dFm7hMN0qzk4T3q8V0NaPcFQqMemDs,1070
72
+ lanscape-2.2.0a3.dist-info/METADATA,sha256=li6uvk8iCT3y43dvRKjuLhn-lcN6_rk2KAJ4GPuurNg,3615
73
+ lanscape-2.2.0a3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
+ lanscape-2.2.0a3.dist-info/entry_points.txt,sha256=evxSxUikFa1OEd4e0Boky9sLH87HdgM0YqB_AbB2HYc,51
75
+ lanscape-2.2.0a3.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
76
+ lanscape-2.2.0a3.dist-info/RECORD,,
@@ -1,210 +0,0 @@
1
- """
2
- Get the executable path of the system’s default web browser.
3
-
4
- Supports:
5
- - Windows (reads from the registry)
6
- - Linux (uses xdg-mime / xdg-settings + .desktop file parsing)
7
- """
8
-
9
- import sys
10
- import os
11
- import subprocess
12
- import webbrowser
13
- import logging
14
- import re
15
- import time
16
- from typing import Optional
17
-
18
- log = logging.getLogger('WebBrowser')
19
-
20
-
21
- def open_webapp(url: str) -> bool:
22
- """
23
- will try to open the web page as an app
24
- on failure, will open as a tab in default browser
25
-
26
- returns:
27
- """
28
- start = time.time()
29
- try:
30
- exe = get_default_browser_executable()
31
- if not exe:
32
- raise RuntimeError('Unable to find browser binary')
33
- log.debug(f'Opening {url} with {exe}')
34
-
35
- cmd = f'"{exe}" --app="{url}"'
36
- subprocess.run(cmd, check=True, shell=True)
37
-
38
- if time.time() - start < 2:
39
- log.debug(
40
- 'Unable to hook into closure of UI, listening for flask shutdown')
41
- return False
42
- return True
43
-
44
- except Exception as e:
45
- log.warning(
46
- 'Failed to open webpage as app, falling back to browser tab')
47
- log.debug(f'As app error: {e}')
48
- try:
49
- success = webbrowser.open(url)
50
- log.debug(f'Opened {url} in browser tab: {success}')
51
- if not success:
52
- # pylint: disable=raise-missing-from
53
- raise RuntimeError(
54
- 'Unknown error while opening browser tab') from e
55
- except Exception as e2:
56
- log.warning(
57
- 'Exhausted all options to open browser, you need to open manually')
58
- log.debug(f'As tab error: {e2}')
59
- log.info(f'LANScape UI is running on {url}')
60
- return False
61
-
62
-
63
- def get_default_browser_executable() -> Optional[str]:
64
- """Platform-agnostic method to get the default browser executable path."""
65
- if sys.platform.startswith("win"):
66
- return windows_get_browser_from_registry()
67
-
68
- if sys.platform.startswith("linux"):
69
- return linux_get_browser_executable()
70
-
71
- if sys.platform.startswith("darwin"):
72
- # macOS: try to find Chrome first for app mode support, fallback to default
73
- try:
74
- p = subprocess.run(
75
- ["mdfind", "kMDItemCFBundleIdentifier == 'com.google.Chrome'"],
76
- capture_output=True, text=True, check=True
77
- )
78
- chrome_paths = p.stdout.strip().split('\n')
79
- if chrome_paths and chrome_paths[0]:
80
- return f"{chrome_paths[0]}/Contents/MacOS/Google Chrome"
81
- except subprocess.CalledProcessError:
82
- pass
83
-
84
- # Fallback to system default
85
- return "/usr/bin/open"
86
-
87
- # Unsupported platform
88
- return None
89
-
90
-
91
- def linux_get_browser_executable() -> Optional[str]:
92
- """Get the default web browser executable path on Linux."""
93
- # First, find the .desktop file name
94
- desktop_file = None
95
- try:
96
- # Try xdg-mime
97
- p = subprocess.run(
98
- ["xdg-mime", "query", "default", "x-scheme-handler/http"],
99
- capture_output=True, text=True,
100
- check=True
101
- )
102
- desktop_file = p.stdout.strip()
103
- except subprocess.CalledProcessError:
104
- pass
105
-
106
- if not desktop_file:
107
- # Fallback to xdg-settings
108
- try:
109
- p = subprocess.run(
110
- ["xdg-settings", "get", "default-web-browser"],
111
- capture_output=True, text=True,
112
- check=True
113
- )
114
- desktop_file = p.stdout.strip()
115
- except subprocess.CalledProcessError:
116
- pass
117
-
118
- # Final fallback: BROWSER environment variable
119
- if not desktop_file:
120
- return os.environ.get("BROWSER")
121
-
122
- # Look for that .desktop file in standard locations
123
- search_paths = [
124
- os.path.expanduser("~/.local/share/applications"),
125
- "/usr/local/share/applications",
126
- "/usr/share/applications",
127
- ]
128
-
129
- exec_cmd = None
130
- for path in search_paths:
131
- full_path = os.path.join(path, desktop_file)
132
- if os.path.isfile(full_path):
133
- with open(full_path, encoding="utf-8", errors="ignore") as f:
134
- for line in f:
135
- if line.startswith("Exec="):
136
- exec_cmd = line[len("Exec="):].strip()
137
- # strip arguments like "%u", "--flag", etc.
138
- exec_cmd = exec_cmd.split()[0]
139
- exec_cmd = exec_cmd.split("%")[0]
140
- return exec_cmd
141
-
142
- return exec_cmd
143
-
144
-
145
- def windows_get_browser_from_registry() -> Optional[str]:
146
- """Get the default web browser executable path on Windows."""
147
- # Import winreg only on Windows platforms
148
- if not sys.platform.startswith("win"):
149
- return None
150
-
151
- try:
152
- import winreg # pylint: disable=import-outside-toplevel
153
- except ImportError:
154
- log.debug("winreg module not available")
155
- return None
156
-
157
- def get_reg(base, path, key=None):
158
- """Helper function to read a registry key."""
159
- try:
160
- with winreg.OpenKey(base, path) as reg:
161
- return winreg.QueryValueEx(reg, key)[0]
162
- except FileNotFoundError:
163
- return None
164
-
165
- def extract_executable(cmd: str) -> Optional[str]:
166
- """Extract the executable path from a command string."""
167
- match = re.match(r'"?([^"]+)"?', cmd)
168
- return match.group(1) if match else None
169
-
170
- def get_user_preferred_browser():
171
- """Get the user preferred browser from the registry."""
172
- progid = get_reg(
173
- winreg.HKEY_CURRENT_USER,
174
- r'Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice',
175
- 'ProgId'
176
- )
177
- if not progid:
178
- log.debug('No user preferred browser found in registry')
179
- return None
180
-
181
- browser_path = get_reg(
182
- winreg.HKEY_CLASSES_ROOT,
183
- f'{progid}\\shell\\open\\command'
184
- )
185
-
186
- if not browser_path:
187
- log.debug(f'progid {progid} does not have a command in registry')
188
- return None
189
-
190
- return extract_executable(browser_path)
191
-
192
- def get_system_default_browser():
193
- """Get the system default browser from the registry."""
194
- reg = get_reg(
195
- winreg.HKEY_CLASSES_ROOT,
196
- r'http\shell\open\command'
197
- )
198
- if not reg:
199
- log.debug('No system default browser found in registry')
200
- return None
201
-
202
- return extract_executable(reg)
203
-
204
- user_browser = get_user_preferred_browser()
205
- if user_browser:
206
- return extract_executable(user_browser)
207
-
208
- system_browser = get_system_default_browser()
209
- if system_browser:
210
- return extract_executable(system_browser)