diff options
author | Devaev Maxim <[email protected]> | 2018-12-15 04:29:40 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2018-12-15 04:29:40 +0300 |
commit | 3c33bd37190772a783369894e209bcfe0858177a (patch) | |
tree | e095f08f37371a3182f6ced0b280c4bcaa06983b | |
parent | 3445766a50eab16a96d969397a6fe0422f7cfcd2 (diff) |
own auth
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | PKGBUILD | 1 | ||||
-rw-r--r-- | configs/kvmd/htpasswd | 1 | ||||
-rw-r--r-- | configs/kvmd/platforms/kvmd.v1-hdmi.yaml | 3 | ||||
-rw-r--r-- | configs/kvmd/platforms/kvmd.v1-vga.yaml | 5 | ||||
-rw-r--r-- | configs/nginx/nginx.conf | 65 | ||||
-rw-r--r-- | extras/kvm/manifest.yaml | 2 | ||||
-rw-r--r-- | kvmd/apps/kvmd/__init__.py | 8 | ||||
-rw-r--r-- | kvmd/apps/kvmd/auth.py | 37 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 103 | ||||
-rw-r--r-- | testenv/Dockerfile | 6 | ||||
-rw-r--r-- | testenv/customizepkg.nginx | 1 | ||||
-rw-r--r-- | testenv/kvmd.yaml | 3 | ||||
-rw-r--r-- | testenv/requirements.txt | 1 | ||||
-rw-r--r-- | testenv/tox.ini | 2 | ||||
-rw-r--r-- | web/browserconfig.xml | 9 | ||||
-rw-r--r-- | web/index.html | 26 | ||||
-rw-r--r-- | web/kvm/index.html | 88 | ||||
-rw-r--r-- | web/login/index.html | 47 | ||||
-rw-r--r-- | web/mstile-150x150.png | bin | 3678 -> 0 bytes | |||
-rw-r--r-- | web/share/android-chrome-192x192.png (renamed from web/android-chrome-192x192.png) | bin | 6197 -> 6197 bytes | |||
-rw-r--r-- | web/share/apple-touch-icon.png (renamed from web/apple-touch-icon.png) | bin | 2376 -> 2376 bytes | |||
-rw-r--r-- | web/share/css/index/index.css (renamed from web/css/index/index.css) | 0 | ||||
-rw-r--r-- | web/share/css/kvm/about.css (renamed from web/css/kvm/about.css) | 0 | ||||
-rw-r--r-- | web/share/css/kvm/hid.css (renamed from web/css/kvm/hid.css) | 0 | ||||
-rw-r--r-- | web/share/css/kvm/keyboard.css (renamed from web/css/kvm/keyboard.css) | 0 | ||||
-rw-r--r-- | web/share/css/kvm/msd.css (renamed from web/css/kvm/msd.css) | 0 | ||||
-rw-r--r-- | web/share/css/kvm/stream.css (renamed from web/css/kvm/stream.css) | 0 | ||||
-rw-r--r-- | web/share/css/leds.css (renamed from web/css/leds.css) | 0 | ||||
-rw-r--r-- | web/share/css/login/login.css | 25 | ||||
-rw-r--r-- | web/share/css/main.css (renamed from web/css/main.css) | 13 | ||||
-rw-r--r-- | web/share/css/menu.css (renamed from web/css/menu.css) | 1 | ||||
-rw-r--r-- | web/share/css/modals.css (renamed from web/css/modals.css) | 1 | ||||
-rw-r--r-- | web/share/css/progress.css (renamed from web/css/progress.css) | 0 | ||||
-rw-r--r-- | web/share/css/sliders.css (renamed from web/css/sliders.css) | 0 | ||||
-rw-r--r-- | web/share/css/switches.css (renamed from web/css/switches.css) | 0 | ||||
-rw-r--r-- | web/share/css/vars.css (renamed from web/css/vars.css) | 0 | ||||
-rw-r--r-- | web/share/css/windows.css (renamed from web/css/windows.css) | 0 | ||||
-rw-r--r-- | web/share/favicon-16x16.png (renamed from web/favicon-16x16.png) | bin | 638 -> 638 bytes | |||
-rw-r--r-- | web/share/favicon-32x32.png (renamed from web/favicon-32x32.png) | bin | 937 -> 937 bytes | |||
-rw-r--r-- | web/share/js/bb.js (renamed from web/js/bb.js) | 0 | ||||
-rw-r--r-- | web/share/js/index/main.js (renamed from web/js/index/main.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/atx.js (renamed from web/js/kvm/atx.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/hid.js (renamed from web/js/kvm/hid.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/keyboard.js (renamed from web/js/kvm/keyboard.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/main.js (renamed from web/js/kvm/main.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/mouse.js (renamed from web/js/kvm/mouse.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/msd.js (renamed from web/js/kvm/msd.js) | 0 | ||||
-rw-r--r-- | web/share/js/kvm/session.js (renamed from web/js/kvm/session.js) | 28 | ||||
-rw-r--r-- | web/share/js/kvm/stream.js (renamed from web/js/kvm/stream.js) | 0 | ||||
-rw-r--r-- | web/share/js/login/main.js | 36 | ||||
-rw-r--r-- | web/share/js/tools.js (renamed from web/js/tools.js) | 9 | ||||
-rw-r--r-- | web/share/js/wm.js (renamed from web/js/wm.js) | 0 | ||||
-rw-r--r-- | web/share/png/blank-stream.png (renamed from web/png/blank-stream.png) | bin | 346999 -> 346999 bytes | |||
-rw-r--r-- | web/share/safari-pinned-tab.svg (renamed from web/safari-pinned-tab.svg) | 0 | ||||
-rw-r--r-- | web/share/site.webmanifest (renamed from web/site.webmanifest) | 2 | ||||
-rw-r--r-- | web/share/svg/atx-hdd-led.svg (renamed from web/svg/atx-hdd-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/atx-power-led.svg (renamed from web/svg/atx-power-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/fan-led.svg (renamed from web/svg/fan-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/favicon.svg (renamed from web/svg/favicon.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/gear-led.svg (renamed from web/svg/gear-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/hid-keyboard-led.svg (renamed from web/svg/hid-keyboard-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/hid-mouse-led.svg (renamed from web/svg/hid-mouse-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/info.svg (renamed from web/svg/info.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/kvm.svg (renamed from web/svg/kvm.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/link-led.svg (renamed from web/svg/link-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/logo.svg (renamed from web/svg/logo.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/msd-led.svg (renamed from web/svg/msd-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/select-arrow-inactive.svg (renamed from web/svg/select-arrow-inactive.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/select-arrow-intensive.svg (renamed from web/svg/select-arrow-intensive.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/select-arrow-normal.svg (renamed from web/svg/select-arrow-normal.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/stream-led.svg (renamed from web/svg/stream-led.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/stream-mouse-cursor.svg (renamed from web/svg/stream-mouse-cursor.svg) | 0 | ||||
-rw-r--r-- | web/share/svg/warning.svg (renamed from web/svg/warning.svg) | 0 |
74 files changed, 388 insertions, 136 deletions
@@ -6,6 +6,7 @@ TESTENV_CMD ?= /bin/bash -c " \ (socat PTY,link=$(TESTENV_HID) PTY,link=/dev/ttyS11 &) \ && cp -r /usr/share/kvmd/configs.default/nginx/* /etc/nginx \ && cp /usr/share/kvmd/configs.default/kvmd/*.yaml /etc/kvmd \ + && cp /usr/share/kvmd/configs.default/kvmd/htpasswd /etc/kvmd \ && cp /testenv/kvmd.yaml /etc/kvmd \ && nginx -c /etc/nginx/nginx.conf \ && ln -s $(TESTENV_VIDEO) /dev/kvmd-video \ @@ -14,6 +14,7 @@ depends=( python-yaml python-aiohttp python-aiofiles + python-passlib python-pyudev python-raspberry-gpio python-pyserial diff --git a/configs/kvmd/htpasswd b/configs/kvmd/htpasswd new file mode 100644 index 00000000..e62e44d0 --- /dev/null +++ b/configs/kvmd/htpasswd @@ -0,0 +1 @@ +admin:$apr1$INC0KeyU$YdLQ9qosXzNVlhxQPUf7A/ diff --git a/configs/kvmd/platforms/kvmd.v1-hdmi.yaml b/configs/kvmd/platforms/kvmd.v1-hdmi.yaml index 8a1f9c8b..1991e4c5 100644 --- a/configs/kvmd/platforms/kvmd.v1-hdmi.yaml +++ b/configs/kvmd/platforms/kvmd.v1-hdmi.yaml @@ -7,6 +7,9 @@ kvmd: port: 8081 heartbeat: 3.0 + auth: + htpasswd: /etc/kvmd/htpasswd + info: meta: /etc/kvmd/meta.yaml extras: /usr/share/kvmd/extras diff --git a/configs/kvmd/platforms/kvmd.v1-vga.yaml b/configs/kvmd/platforms/kvmd.v1-vga.yaml index 05c1234d..5688f306 100644 --- a/configs/kvmd/platforms/kvmd.v1-vga.yaml +++ b/configs/kvmd/platforms/kvmd.v1-vga.yaml @@ -1,5 +1,5 @@ # Don't touch this file otherwise your device may stop working. -# You can find a workable configuration in /usr/share/kvmd/configs.default/kvmd. +# You can find a working configuration in /usr/share/kvmd/configs.default/kvmd. kvmd: server: @@ -7,6 +7,9 @@ kvmd: port: 8081 heartbeat: 3.0 + auth: + htpasswd: /etc/kvmd/htpasswd + info: meta: /etc/kvmd/meta.yaml extras: /usr/share/kvmd/extras diff --git a/configs/nginx/nginx.conf b/configs/nginx/nginx.conf index 5a368833..49776d9d 100644 --- a/configs/nginx/nginx.conf +++ b/configs/nginx/nginx.conf @@ -1,5 +1,3 @@ -load_module /usr/lib/nginx/modules/ngx_http_lua_module.so; - user http; worker_processes 4; @@ -28,6 +26,7 @@ http { tcp_nodelay on; tcp_nopush on; keepalive_timeout 10; + client_max_body_size 4k; client_body_temp_path /tmp/nginx.client_body_temp; fastcgi_temp_path /tmp/nginx.fastcgi_temp; @@ -45,11 +44,6 @@ http { include /usr/share/kvmd/extras/*/nginx.http-ctx.conf; -#PROD lua_shared_dict WS_TOKENS 10m; -#PROD init_by_lua_block { -#PROD WS_TOKEN_EXPIRES = 10; -#PROD } - #PROD server { #PROD listen 80; #PROD server_name localhost; @@ -67,34 +61,47 @@ http { #PROD add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; -#PROD auth_basic "Restricted Area"; -#PROD auth_basic_user_file /etc/nginx/htpasswd; + auth_request /auth; + + location = /auth { + internal; + proxy_pass http://kvmd/auth/check; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + auth_request off; + } location / { root /usr/share/kvmd/web; + error_page 401 = @login; + error_page 403 = @login; + } + + location @login { + return 302 /login; + } + + location /login { + root /usr/share/kvmd/web; + auth_request off; + } + + location /share { + root /usr/share/kvmd/web; + auth_request off; + } + + location = /favicon.ico { + alias /usr/share/kvmd/web/favicon.ico; + auth_request off; } - location /ws_auth { - # Workaround for Safari: https://bugs.webkit.org/show_bug.cgi?id=80362 -#PROD access_by_lua_block { -#PROD local token = ngx.encode_base64(ngx.sha1_bin(ngx.var.http_Authorization)); -#PROD ngx.shared.WS_TOKENS:set(token, token, WS_TOKEN_EXPIRES); -#PROD ngx.header["Set-Cookie"] = "WS_ACCESS_TOKEN=" .. token .. "; Path=/; Expires=" .. ngx.cookie_time(ngx.time() + WS_TOKEN_EXPIRES); -#PROD } - content_by_lua_block { - ngx.say("ok"); - } + location = /robots.txt { + alias /usr/share/kvmd/web/robots.txt; + auth_request off; } location /kvmd/ws { -#PROD auth_basic off; -#PROD access_by_lua_block { -#PROD local token = ngx.var.cookie_WS_ACCESS_TOKEN; -#PROD local value, _ = ngx.shared.WS_TOKENS:get(token); -#PROD if value == nil then -#PROD ngx.exec("/ws_auth"); -#PROD end -#PROD } rewrite ^/kvmd/ws$ /ws break; rewrite ^/kvmd/ws\?(.*)$ /ws?$1 break; proxy_pass http://kvmd; @@ -104,6 +111,7 @@ http { proxy_connect_timeout 7d; proxy_send_timeout 7d; proxy_read_timeout 7d; + auth_request off; } location /kvmd/msd/write { @@ -115,6 +123,7 @@ http { limit_rate_after 50k; client_max_body_size 0; proxy_request_buffering off; + auth_request off; } location /kvmd/log { @@ -126,6 +135,7 @@ http { postpone_output 0; proxy_buffering off; proxy_ignore_headers X-Accel-Buffering; + auth_request off; } location /kvmd { @@ -133,6 +143,7 @@ http { rewrite ^/kvmd/(.*)$ /$1 break; proxy_pass http://kvmd; include /etc/nginx/proxy-params.conf; + auth_request off; } location /streamer { diff --git a/extras/kvm/manifest.yaml b/extras/kvm/manifest.yaml index d99cd7bf..609cb495 100644 --- a/extras/kvm/manifest.yaml +++ b/extras/kvm/manifest.yaml @@ -1,6 +1,6 @@ name: KVM description: Open KVM session in a web browser -icon: svg/kvm.svg +icon: share/svg/kvm.svg path: kvm keyboard_cap: true daemon: kvmd diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index 7662fae0..480a1017 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -5,8 +5,9 @@ from ...logging import get_logger from ... import gpio -from .logreader import LogReader +from .auth import AuthManager from .info import InfoManager +from .logreader import LogReader from .hid import Hid from .atx import Atx from .msd import MassStorageDevice @@ -20,6 +21,10 @@ def main() -> None: with gpio.bcm(): loop = asyncio.get_event_loop() + auth_manager = AuthManager( + htpasswd_path=str(config["auth"]["htpasswd"]), + ) + info_manager = InfoManager( meta_path=str(config["info"]["meta"]), extras_path=str(config["info"]["extras"]), @@ -80,6 +85,7 @@ def main() -> None: ) Server( + auth_manager=auth_manager, info_manager=info_manager, log_reader=log_reader, diff --git a/kvmd/apps/kvmd/auth.py b/kvmd/apps/kvmd/auth.py new file mode 100644 index 00000000..f319b5cc --- /dev/null +++ b/kvmd/apps/kvmd/auth.py @@ -0,0 +1,37 @@ +import secrets + +from typing import Dict +from typing import Optional + +import passlib.apache + +from ...logging import get_logger + + +# ===== +class AuthManager: + def __init__(self, htpasswd_path: str) -> None: + self.__htpasswd_path = htpasswd_path + self.__tokens: Dict[str, str] = {} # {token: user} + + def login(self, user: str, passwd: str) -> Optional[str]: + htpasswd = passlib.apache.HtpasswdFile(self.__htpasswd_path) + if htpasswd.check_password(user, passwd): + for (token, token_user) in self.__tokens.items(): + if user == token_user: + return token + token = secrets.token_hex(32) + self.__tokens[token] = user + get_logger().info("Logged in user %r", user) + return token + else: + get_logger().error("Access denied for user %r", user) + return None + + def logout(self, token: str) -> None: + user = self.__tokens.pop(token, "") + if user: + get_logger().info("Logged out user %r", user) + + def check(self, token: str) -> bool: + return (token in self.__tokens) diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 2fae9994..3981f59b 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -1,4 +1,5 @@ import os +import re import signal import socket import asyncio @@ -23,6 +24,7 @@ from ...aioregion import RegionIsBusyError from ... import __version__ +from .auth import AuthManager from .info import InfoManager from .logreader import LogReader from .hid import Hid @@ -33,8 +35,29 @@ from .streamer import Streamer # ===== -def _json(result: Optional[Dict]=None, status: int=200) -> aiohttp.web.Response: - return aiohttp.web.Response( +class HttpError(Exception): + pass + + +class BadRequestError(HttpError): + pass + + +class UnauthorizedError(HttpError): + pass + + +class ForbiddenError(HttpError): + pass + + +def _json( + result: Optional[Dict]=None, + status: int=200, + set_cookies: Optional[Dict[str, str]]=None, +) -> aiohttp.web.Response: + + response = aiohttp.web.Response( text=json.dumps({ "ok": (status == 200), "result": (result or {}), @@ -42,37 +65,53 @@ def _json(result: Optional[Dict]=None, status: int=200) -> aiohttp.web.Response: status=status, content_type="application/json", ) + if set_cookies: + for (key, value) in set_cookies.items(): + response.set_cookie(key, value) + return response def _json_exception(err: Exception, status: int) -> aiohttp.web.Response: name = type(err).__name__ msg = str(err) - get_logger().error("API error: %s: %s", name, msg) + if not isinstance(err, (UnauthorizedError, ForbiddenError)): + get_logger().error("API error: %s: %s", name, msg) return _json({ "error": name, "error_msg": msg, }, status=status) -class BadRequestError(Exception): - pass - - _ATTR_EXPOSED = "exposed" _ATTR_EXPOSED_METHOD = "exposed_method" _ATTR_EXPOSED_PATH = "exposed_path" _ATTR_SYSTEM_TASK = "system_task" +_COOKIE_AUTH_TOKEN = "auth_token" -def _exposed(http_method: str, path: str) -> Callable: + +def _exposed(http_method: str, path: str, auth_required: bool=True) -> Callable: def make_wrapper(method: Callable) -> Callable: async def wrap(self: "Server", request: aiohttp.web.Request) -> aiohttp.web.Response: try: + if auth_required: + token = request.cookies.get(_COOKIE_AUTH_TOKEN, "") + if token: + if not self._auth_manager.check(_valid_token(token)): + raise ForbiddenError("Forbidden") + else: + raise UnauthorizedError("Unauthorized") + return (await method(self, request)) + except RegionIsBusyError as err: return _json_exception(err, 409) except (BadRequestError, MsdOperationError) as err: return _json_exception(err, 400) + except UnauthorizedError as err: + return _json_exception(err, 401) + except ForbiddenError as err: + return _json_exception(err, 403) setattr(wrap, _ATTR_EXPOSED, True) setattr(wrap, _ATTR_EXPOSED_METHOD, http_method) @@ -95,6 +134,29 @@ def _system_task(method: Callable) -> Callable: return wrap +def _valid_user(user: Optional[str]) -> str: + if isinstance(user, str): + stripped = user.strip() + if re.match(r"^[a-z_][a-z0-9_-]*$", stripped): + return stripped + raise BadRequestError("Invalid user characters %r" % (user)) + + +def _valid_passwd(passwd: Optional[str]) -> str: + if isinstance(passwd, str): + if re.match(r"[\x20-\x7e]*$", passwd): + return passwd + raise BadRequestError("Invalid password characters") + + +def _valid_token(token: Optional[str]) -> str: + if isinstance(token, str): + token = token.strip().lower() + if re.match(r"^[0-9a-f]{64}$", token): + return token + raise BadRequestError("Invalid auth token characters") + + def _valid_bool(name: str, flag: Optional[str]) -> bool: flag = str(flag).strip().lower() if flag in ["1", "true", "yes"]: @@ -127,6 +189,7 @@ class _Events(Enum): class Server: # pylint: disable=too-many-instance-attributes def __init__( # pylint: disable=too-many-arguments self, + auth_manager: AuthManager, info_manager: InfoManager, log_reader: LogReader, @@ -142,6 +205,7 @@ class Server: # pylint: disable=too-many-instance-attributes loop: asyncio.AbstractEventLoop, ) -> None: + self._auth_manager = auth_manager self.__info_manager = info_manager self.__log_reader = log_reader @@ -210,6 +274,29 @@ class Server: # pylint: disable=too-many-instance-attributes "extras": await self.__info_manager.get_extras(), } + # ===== AUTH + + @_exposed("POST", "/auth/login", auth_required=False) + async def __auth_login_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: + credentials = await request.post() + token = self._auth_manager.login( + user=_valid_user(credentials.get("user", "")), + passwd=_valid_passwd(credentials.get("passwd", "")), + ) + if token: + return _json({}, set_cookies={_COOKIE_AUTH_TOKEN: token}) + raise ForbiddenError("Forbidden") + + @_exposed("POST", "/auth/logout") + async def __auth_logout_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: + token = _valid_token(request.cookies.get(_COOKIE_AUTH_TOKEN, "")) + self._auth_manager.logout(token) + return _json({}) + + @_exposed("GET", "/auth/check") + async def __auth_check_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response: + return _json({}) + # ===== SYSTEM @_exposed("GET", "/info") diff --git a/testenv/Dockerfile b/testenv/Dockerfile index 78226b17..4fe453e8 100644 --- a/testenv/Dockerfile +++ b/testenv/Dockerfile @@ -31,12 +31,7 @@ RUN useradd -r -d / packer \ && cd - \ && rm -rf /tmp/packer-color -COPY testenv/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-ndk -COPY testenv/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-lua - RUN pacman -Syy \ - && user-packer -S --noconfirm \ - customizepkg \ && mkdir /.npm \ && chmod 777 /.npm \ && user-packer -S --noconfirm \ @@ -50,7 +45,6 @@ RUN pacman -Syy \ htmlhint \ eslint \ && rm -rf /.npm \ - && env MAKEPKGOPTS="--skipchecksums --skippgpcheck" user-packer -S --noconfirm nginx-mainline-mod-lua \ && pacman -Sc --noconfirm COPY testenv/requirements.txt requirements.txt diff --git a/testenv/customizepkg.nginx b/testenv/customizepkg.nginx deleted file mode 100644 index 5e7db5a4..00000000 --- a/testenv/customizepkg.nginx +++ /dev/null @@ -1 +0,0 @@ -replace#global#_nginxver=.*#_nginxver=`pacman -Q nginx-mainline | grep -Po "\\d+\\.\\d+\\.\\d+"` diff --git a/testenv/kvmd.yaml b/testenv/kvmd.yaml index 8233cd58..da37e9d8 100644 --- a/testenv/kvmd.yaml +++ b/testenv/kvmd.yaml @@ -4,6 +4,9 @@ kvmd: port: 8081 heartbeat: 3.0 + auth: + htpasswd: /etc/kvmd/htpasswd + info: meta: /etc/kvmd/meta.yaml extras: /usr/share/kvmd/extras diff --git a/testenv/requirements.txt b/testenv/requirements.txt index 4f95d90a..a968fcb1 100644 --- a/testenv/requirements.txt +++ b/testenv/requirements.txt @@ -1,6 +1,7 @@ git+git://github.com/willbuckner/rpi-gpio-development-mock@master#egg=rpi aiohttp aiofiles +passlib pyudev pyyaml pyserial diff --git a/testenv/tox.ini b/testenv/tox.ini index 47ce1636..b10700a2 100644 --- a/testenv/tox.ini +++ b/testenv/tox.ini @@ -33,7 +33,7 @@ deps = [testenv:eslint] whitelist_externals = eslint -commands = eslint --config=testenv/eslintrc.yaml --color --ext .js web/js +commands = eslint --config=testenv/eslintrc.yaml --color --ext .js web/share/js [testenv:htmlhint] whitelist_externals = htmlhint diff --git a/web/browserconfig.xml b/web/browserconfig.xml deleted file mode 100644 index fcb73892..00000000 --- a/web/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<browserconfig> - <msapplication> - <tile> - <square150x150logo src="/mstile-150x150.png"/> - <TileColor>#2b5797</TileColor> - </tile> - </msapplication> -</browserconfig> diff --git a/web/index.html b/web/index.html index b628b615..e1113454 100644 --- a/web/index.html +++ b/web/index.html @@ -4,22 +4,22 @@ <meta charset="utf-8" /> <title>Pi-KVM Index</title> - <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> - <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> - <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> - <link rel="manifest" href="/site.webmanifest"> - <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"> + <link rel="apple-touch-icon" sizes="180x180" href="/share/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/share/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/share/favicon-16x16.png"> + <link rel="manifest" href="/share/site.webmanifest"> + <link rel="mask-icon" href="/share/safari-pinned-tab.svg" color="#5bbad5"> <meta name="msapplication-TileColor" content="#2b5797"> <meta name="theme-color" content="#ffffff"> - <link rel="stylesheet" href="css/vars.css"> - <link rel="stylesheet" href="css/main.css"> - <link rel="stylesheet" href="css/modals.css"> - <link rel="stylesheet" href="css/index/index.css"> + <link rel="stylesheet" href="share/css/vars.css"> + <link rel="stylesheet" href="share/css/main.css"> + <link rel="stylesheet" href="share/css/modals.css"> + <link rel="stylesheet" href="share/css/index/index.css"> - <script src="js/bb.js"></script> - <script src="js/tools.js"></script> - <script src="js/index/main.js"></script> + <script src="share/js/bb.js"></script> + <script src="share/js/tools.js"></script> + <script src="share/js/index/main.js"></script> <script>window.onload = main;</script> </head> @@ -30,7 +30,7 @@ <table> <tr> <td valign="top" class="logo"> - <img class="svg-gray" src="svg/logo.svg" alt="Open Source Hardware" height="40" /> + <img class="svg-gray" src="share/svg/logo.svg" alt="Open Source Hardware" height="40" /> </td> <td valign="top"> <table> diff --git a/web/kvm/index.html b/web/kvm/index.html index 8cc66513..4472112a 100644 --- a/web/kvm/index.html +++ b/web/kvm/index.html @@ -4,40 +4,40 @@ <meta charset="utf-8" /> <title>Pi-KVM Session</title> - <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> - <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> - <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> - <link rel="manifest" href="/site.webmanifest"> - <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"> + <link rel="apple-touch-icon" sizes="180x180" href="/share/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/share/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/share/favicon-16x16.png"> + <link rel="manifest" href="/share/site.webmanifest"> + <link rel="mask-icon" href="/share/safari-pinned-tab.svg" color="#5bbad5"> <meta name="msapplication-TileColor" content="#2b5797"> <meta name="theme-color" content="#ffffff"> - <link rel="stylesheet" href="../css/vars.css"> - <link rel="stylesheet" href="../css/main.css"> - <link rel="stylesheet" href="../css/menu.css"> - <link rel="stylesheet" href="../css/windows.css"> - <link rel="stylesheet" href="../css/modals.css"> - <link rel="stylesheet" href="../css/leds.css"> - <link rel="stylesheet" href="../css/sliders.css"> - <link rel="stylesheet" href="../css/switches.css"> - <link rel="stylesheet" href="../css/progress.css"> - <link rel="stylesheet" href="../css/kvm/stream.css"> - <link rel="stylesheet" href="../css/kvm/hid.css"> - <link rel="stylesheet" href="../css/kvm/msd.css"> - <link rel="stylesheet" href="../css/kvm/keyboard.css"> - <link rel="stylesheet" href="../css/kvm/about.css"> + <link rel="stylesheet" href="../share/css/vars.css"> + <link rel="stylesheet" href="../share/css/main.css"> + <link rel="stylesheet" href="../share/css/menu.css"> + <link rel="stylesheet" href="../share/css/windows.css"> + <link rel="stylesheet" href="../share/css/modals.css"> + <link rel="stylesheet" href="../share/css/leds.css"> + <link rel="stylesheet" href="../share/css/sliders.css"> + <link rel="stylesheet" href="../share/css/switches.css"> + <link rel="stylesheet" href="../share/css/progress.css"> + <link rel="stylesheet" href="../share/css/kvm/stream.css"> + <link rel="stylesheet" href="../share/css/kvm/hid.css"> + <link rel="stylesheet" href="../share/css/kvm/msd.css"> + <link rel="stylesheet" href="../share/css/kvm/keyboard.css"> + <link rel="stylesheet" href="../share/css/kvm/about.css"> - <script src="../js/bb.js"></script> - <script src="../js/tools.js"></script> - <script src="../js/wm.js"></script> - <script src="../js/kvm/stream.js"></script> - <script src="../js/kvm/atx.js"></script> - <script src="../js/kvm/keyboard.js"></script> - <script src="../js/kvm/mouse.js"></script> - <script src="../js/kvm/hid.js"></script> - <script src="../js/kvm/msd.js"></script> - <script src="../js/kvm/session.js"></script> - <script src="../js/kvm/main.js"></script> + <script src="../share/js/bb.js"></script> + <script src="../share/js/tools.js"></script> + <script src="../share/js/wm.js"></script> + <script src="../share/js/kvm/stream.js"></script> + <script src="../share/js/kvm/atx.js"></script> + <script src="../share/js/kvm/keyboard.js"></script> + <script src="../share/js/kvm/mouse.js"></script> + <script src="../share/js/kvm/hid.js"></script> + <script src="../share/js/kvm/msd.js"></script> + <script src="../share/js/kvm/session.js"></script> + <script src="../share/js/kvm/main.js"></script> <script>window.onload = main;</script> </head> @@ -47,16 +47,16 @@ <li class="menu-left-items"> <a id="menu-logo" href="/"> ↩ - <img class="svg-gray" src="../svg/logo.svg" alt="π-kvm" /> + <img class="svg-gray" src="../share/svg/logo.svg" alt="π-kvm" /> </a> </li> <li class="menu-right-items"> <a class="menu-item" href="#"> - <img data-dont-hide-menu id="link-led" class="led-gray" src="../svg/link-led.svg" /> - <img data-dont-hide-menu id="stream-led" class="led-gray" src="../svg/stream-led.svg" /> - <img data-dont-hide-menu id="hid-keyboard-led" class="led-gray" src="../svg/hid-keyboard-led.svg" /> - <img data-dont-hide-menu id="hid-mouse-led" class="led-gray" src="../svg/hid-mouse-led.svg" /> + <img data-dont-hide-menu id="link-led" class="led-gray" src="../share/svg/link-led.svg" /> + <img data-dont-hide-menu id="stream-led" class="led-gray" src="../share/svg/stream-led.svg" /> + <img data-dont-hide-menu id="hid-keyboard-led" class="led-gray" src="../share/svg/hid-keyboard-led.svg" /> + <img data-dont-hide-menu id="hid-mouse-led" class="led-gray" src="../share/svg/hid-mouse-led.svg" /> System ↴ </a> <div data-dont-hide-menu class="menu-item-content"> @@ -118,8 +118,8 @@ <li class="menu-right-items"> <a class="menu-item" href="#"> - <img data-dont-hide-menu id="atx-power-led" class="led-gray" src="../svg/atx-power-led.svg" /> - <img data-dont-hide-menu id="atx-hdd-led" class="led-gray" src="../svg/atx-hdd-led.svg" /> + <img data-dont-hide-menu id="atx-power-led" class="led-gray" src="../share/svg/atx-power-led.svg" /> + <img data-dont-hide-menu id="atx-hdd-led" class="led-gray" src="../share/svg/atx-hdd-led.svg" /> ATX ↴ </a> <div class="menu-item-content menu-item-content-buttons"> @@ -132,7 +132,7 @@ <li class="menu-right-items"> <a class="menu-item" href="#"> - <img data-dont-hide-menu id="msd-led" class="led-gray" src="../svg/msd-led.svg" /> + <img data-dont-hide-menu id="msd-led" class="led-gray" src="../share/svg/msd-led.svg" /> Mass Storage ↴ </a> <div data-dont-hide-menu id="msd-menu" class="menu-item-content"> @@ -140,7 +140,7 @@ <div class="menu-item-content-text"> <table> <tr> - <td><img src="../svg/warning.svg" /></td> + <td><img src="../share/svg/warning.svg" /></td> <td><b>Mass Storage Device is not operational</b></td> </tr> </table> @@ -152,7 +152,7 @@ <div class="menu-item-content-text"> <table> <tr> - <td><img src="../svg/warning.svg" /></td> + <td><img src="../share/svg/warning.svg" /></td> <td><b>Current image is broken!</b><br><sub>Perhaps uploading was interrupted</sub></td> </tr> </table> @@ -164,7 +164,7 @@ <div class="menu-item-content-text"> <table> <tr> - <td><img src="../svg/info.svg" /></td> + <td><img src="../share/svg/info.svg" /></td> <td><b>Another user uploads an image</b></td> </tr> </table> @@ -233,7 +233,7 @@ <li class="menu-right-items"> <a class="menu-item" href="#"> - <img data-dont-hide-menu id="hid-pak-led" class="led-gray" src="../svg/gear-led.svg" /> + <img data-dont-hide-menu id="hid-pak-led" class="led-gray" src="../share/svg/gear-led.svg" /> Shortcuts ↴ </a> <div data-dont-hide-menu class="menu-item-content"> @@ -282,7 +282,7 @@ </div> <div id="stream-info"></div> <div id="stream-box" class="stream-box-inactive"> - <img id="stream-image" class="stream-image-inactive" src="../png/blank-stream.png" /> + <img id="stream-image" class="stream-image-inactive" src="../share/png/blank-stream.png" /> </div> <div id="stream-mouse-buttons"> <button data-mouse-button="left" class="row50">Left Click</button> @@ -534,7 +534,7 @@ <table> <tr> <td valign="top" class="logo"> - <img class="svg-gray" src="../svg/logo.svg" alt="Open Source Hardware" height="40" /> + <img class="svg-gray" src="../share/svg/logo.svg" alt="Open Source Hardware" height="40" /> </td> <td valign="top"> <table> diff --git a/web/login/index.html b/web/login/index.html new file mode 100644 index 00000000..10ca2e2b --- /dev/null +++ b/web/login/index.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8" /> + <title>Pi-KVM Login</title> + + <link rel="apple-touch-icon" sizes="180x180" href="/share/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/share/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/share/favicon-16x16.png"> + <link rel="manifest" href="/share/site.webmanifest"> + <link rel="mask-icon" href="/share/safari-pinned-tab.svg" color="#5bbad5"> + <meta name="msapplication-TileColor" content="#2b5797"> + <meta name="theme-color" content="#ffffff"> + + <link rel="stylesheet" href="../share/css/vars.css"> + <link rel="stylesheet" href="../share/css/main.css"> + <link rel="stylesheet" href="../share/css/modals.css"> + <link rel="stylesheet" href="../share/css/login/login.css"> + + <script src="../share/js/bb.js"></script> + <script src="../share/js/tools.js"></script> + <script src="../share/js/login/main.js"></script> + + <script>window.onload = main;</script> +</head> + +<body> + <div id="login-box"> + <div id="login"> + <table> + <tr> + <td>Username:</td> + <td><input type="text" id="user-input"></td> + </tr> + <tr> + <td>Password:</td> + <td><input type="password" id="passwd-input"></td> + </tr> + <tr> + <td></td> + <td><button id="login-button">Login</button></td> + </tr> + </table> + </div> + </div> +</body> +</html> diff --git a/web/mstile-150x150.png b/web/mstile-150x150.png Binary files differdeleted file mode 100644 index 353ef691..00000000 --- a/web/mstile-150x150.png +++ /dev/null diff --git a/web/android-chrome-192x192.png b/web/share/android-chrome-192x192.png Binary files differindex 2473df5c..2473df5c 100644 --- a/web/android-chrome-192x192.png +++ b/web/share/android-chrome-192x192.png diff --git a/web/apple-touch-icon.png b/web/share/apple-touch-icon.png Binary files differindex 207dc65e..207dc65e 100644 --- a/web/apple-touch-icon.png +++ b/web/share/apple-touch-icon.png diff --git a/web/css/index/index.css b/web/share/css/index/index.css index 72ab5c24..72ab5c24 100644 --- a/web/css/index/index.css +++ b/web/share/css/index/index.css diff --git a/web/css/kvm/about.css b/web/share/css/kvm/about.css index e6e2360f..e6e2360f 100644 --- a/web/css/kvm/about.css +++ b/web/share/css/kvm/about.css diff --git a/web/css/kvm/hid.css b/web/share/css/kvm/hid.css index 6663a85c..6663a85c 100644 --- a/web/css/kvm/hid.css +++ b/web/share/css/kvm/hid.css diff --git a/web/css/kvm/keyboard.css b/web/share/css/kvm/keyboard.css index 2ff622f3..2ff622f3 100644 --- a/web/css/kvm/keyboard.css +++ b/web/share/css/kvm/keyboard.css diff --git a/web/css/kvm/msd.css b/web/share/css/kvm/msd.css index 4a1c5d28..4a1c5d28 100644 --- a/web/css/kvm/msd.css +++ b/web/share/css/kvm/msd.css diff --git a/web/css/kvm/stream.css b/web/share/css/kvm/stream.css index c99c3c9c..c99c3c9c 100644 --- a/web/css/kvm/stream.css +++ b/web/share/css/kvm/stream.css diff --git a/web/css/leds.css b/web/share/css/leds.css index fe23a434..fe23a434 100644 --- a/web/css/leds.css +++ b/web/share/css/leds.css diff --git a/web/share/css/login/login.css b/web/share/css/login/login.css new file mode 100644 index 00000000..1fae42d6 --- /dev/null +++ b/web/share/css/login/login.css @@ -0,0 +1,25 @@ +div#login-box { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + min-height: 100vh; +} + +div#login { + text-align: left; + outline: none; + word-wrap: break-word; + max-width: 400px; + border: var(--border-window-thin); + border-radius: 8px; + box-sizing: border-box; + box-shadow: var(--shadow-big); + background-color: var(--cs-window-default-bg); + padding: 15px; +} + +input[type="text"]#user-input, input[type="password"]#passwd-input { + text-align: center; +} diff --git a/web/css/main.css b/web/share/css/main.css index 28361905..f5f0db75 100644 --- a/web/css/main.css +++ b/web/share/css/main.css @@ -62,6 +62,7 @@ img.svg-gray { button, select { box-shadow: none; border: none; + border-radius: 4px; color: var(--cs-control-default-fg); background-color: var(--cs-control-default-bg); display: block; @@ -116,8 +117,18 @@ select:active { background-image: url("../svg/select-arrow-intensive.svg") !important; } +input[type=text], input[type=password] { + overflow-x: auto; + font-family: monospace; + border: thin; + border-radius: 4px; + color: var(--cs-code-default-fg); + background-color: var(--cs-code-default-bg); + padding: 2px; +} + @media only screen and (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) { - button, select { + button, select, input[type=text], input[type=password] { height: 45px !important; } } diff --git a/web/css/menu.css b/web/share/css/menu.css index 5ececb32..151b82b1 100644 --- a/web/css/menu.css +++ b/web/share/css/menu.css @@ -98,6 +98,7 @@ ul#menu li div.menu-item-content-text { } ul#menu li div.menu-item-content button, select { + border-radius: 0; text-align: left; padding: 0 16px; } diff --git a/web/css/modals.css b/web/share/css/modals.css index f557bd08..ed9837c7 100644 --- a/web/css/modals.css +++ b/web/share/css/modals.css @@ -47,6 +47,7 @@ div.modal div.modal-window div.modal-buttons { } div.modal div.modal-window div.modal-buttons button { + border-radius: 0; height: 40px; } @media only screen and (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) { diff --git a/web/css/progress.css b/web/share/css/progress.css index 1085e5cf..1085e5cf 100644 --- a/web/css/progress.css +++ b/web/share/css/progress.css diff --git a/web/css/sliders.css b/web/share/css/sliders.css index fe0be262..fe0be262 100644 --- a/web/css/sliders.css +++ b/web/share/css/sliders.css diff --git a/web/css/switches.css b/web/share/css/switches.css index 65e187f9..65e187f9 100644 --- a/web/css/switches.css +++ b/web/share/css/switches.css diff --git a/web/css/vars.css b/web/share/css/vars.css index 57e71c0f..57e71c0f 100644 --- a/web/css/vars.css +++ b/web/share/css/vars.css diff --git a/web/css/windows.css b/web/share/css/windows.css index 32628e55..32628e55 100644 --- a/web/css/windows.css +++ b/web/share/css/windows.css diff --git a/web/favicon-16x16.png b/web/share/favicon-16x16.png Binary files differindex adc66e3e..adc66e3e 100644 --- a/web/favicon-16x16.png +++ b/web/share/favicon-16x16.png diff --git a/web/favicon-32x32.png b/web/share/favicon-32x32.png Binary files differindex 5fec1005..5fec1005 100644 --- a/web/favicon-32x32.png +++ b/web/share/favicon-32x32.png diff --git a/web/js/bb.js b/web/share/js/bb.js index 565a3ab4..565a3ab4 100644 --- a/web/js/bb.js +++ b/web/share/js/bb.js diff --git a/web/js/index/main.js b/web/share/js/index/main.js index 8f5c74a6..8f5c74a6 100644 --- a/web/js/index/main.js +++ b/web/share/js/index/main.js diff --git a/web/js/kvm/atx.js b/web/share/js/kvm/atx.js index ed5f045e..ed5f045e 100644 --- a/web/js/kvm/atx.js +++ b/web/share/js/kvm/atx.js diff --git a/web/js/kvm/hid.js b/web/share/js/kvm/hid.js index 49d659ab..49d659ab 100644 --- a/web/js/kvm/hid.js +++ b/web/share/js/kvm/hid.js diff --git a/web/js/kvm/keyboard.js b/web/share/js/kvm/keyboard.js index 3c821406..3c821406 100644 --- a/web/js/kvm/keyboard.js +++ b/web/share/js/kvm/keyboard.js diff --git a/web/js/kvm/main.js b/web/share/js/kvm/main.js index 5c6d775a..5c6d775a 100644 --- a/web/js/kvm/main.js +++ b/web/share/js/kvm/main.js diff --git a/web/js/kvm/mouse.js b/web/share/js/kvm/mouse.js index 58e46d15..58e46d15 100644 --- a/web/js/kvm/mouse.js +++ b/web/share/js/kvm/mouse.js diff --git a/web/js/kvm/msd.js b/web/share/js/kvm/msd.js index c23de99d..c23de99d 100644 --- a/web/js/kvm/msd.js +++ b/web/share/js/kvm/msd.js diff --git a/web/js/kvm/session.js b/web/share/js/kvm/session.js index e3e74160..5ba43511 100644 --- a/web/js/kvm/session.js +++ b/web/share/js/kvm/session.js @@ -14,8 +14,7 @@ function Session() { var __streamer = new Streamer(); var __init__ = function() { - $("link-led").title = "Not connected yet..."; - __startPoller(); + __startSession(); }; /********************************************************************************/ @@ -44,24 +43,15 @@ function Session() { $("about-version-streamer").innerHTML = `${state.version.streamer} (${state.streamer})`; }; - var __startPoller = function() { + var __startSession = function() { $("link-led").className = "led-yellow"; $("link-led").title = "Connecting..."; - - var http = tools.makeRequest("GET", "/ws_auth", function() { - if (http.readyState === 4) { - if (http.status === 200) { - var proto = (location.protocol === "https:" ? "wss" : "ws"); - __ws = new WebSocket(`${proto}://${location.host}/kvmd/ws`); - __ws.onopen = __wsOpenHandler; - __ws.onmessage = __wsMessageHandler; - __ws.onerror = __wsErrorHandler; - __ws.onclose = __wsCloseHandler; - } else { - __wsCloseHandler(null); - } - } - }); + var proto = (location.protocol === "https:" ? "wss" : "ws"); + __ws = new WebSocket(`${proto}://${location.host}/kvmd/ws`); + __ws.onopen = __wsOpenHandler; + __ws.onmessage = __wsMessageHandler; + __ws.onerror = __wsErrorHandler; + __ws.onclose = __wsCloseHandler; }; var __wsOpenHandler = function(event) { @@ -118,7 +108,7 @@ function Session() { setTimeout(function() { $("link-led").className = "led-yellow"; - setTimeout(__startPoller, 500); + setTimeout(__startSession, 500); }, 500); }; diff --git a/web/js/kvm/stream.js b/web/share/js/kvm/stream.js index 01ded5bc..01ded5bc 100644 --- a/web/js/kvm/stream.js +++ b/web/share/js/kvm/stream.js diff --git a/web/share/js/login/main.js b/web/share/js/login/main.js new file mode 100644 index 00000000..dc45d03f --- /dev/null +++ b/web/share/js/login/main.js @@ -0,0 +1,36 @@ +function main() { + if (checkBrowser()) { + tools.setOnClick($("login-button"), __login); + document.onkeyup = function(event) { + if (event.code == "Enter") { + event.preventDefault(); + __login(); + } + }; + $("user-input").focus(); + } +} + +function __login() { + var user = $("user-input").value; + var passwd = $("passwd-input").value; + var body = `user=${encodeURIComponent(user)}&passwd=${encodeURIComponent(passwd)}`; + var http = tools.makeRequest("POST", "/kvmd/auth/login", function() { + if (http.readyState === 4) { + if (http.status === 200) { + document.location.href = "/"; + } + __setDisabled(false); + $("passwd-input").focus(); + $("passwd-input").select(); + } + }, body, "application/x-www-form-urlencoded"); + http.send(); + __setDisabled(true); +} + +function __setDisabled(disabled) { + $("user-input").disabled = disabled; + $("passwd-input").disabled = disabled; + $("login-button").disabled = disabled; +} diff --git a/web/js/tools.js b/web/share/js/tools.js index 81970ef8..80b074ee 100644 --- a/web/js/tools.js +++ b/web/share/js/tools.js @@ -1,12 +1,15 @@ var tools = new function() { var __debug = (new URL(window.location.href)).searchParams.get("debug"); - this.makeRequest = function(method, url, callback, timeout=null) { + this.makeRequest = function(method, url, callback, body=null, content_type=null) { var http = new XMLHttpRequest(); http.open(method, url, true); + if (content_type) { + http.setRequestHeader("Content-Type", content_type); + } http.onreadystatechange = callback; - http.timeout = (timeout ? timeout : 5000); - http.send(); + http.timeout = 5000; + http.send(body); return http; }; diff --git a/web/js/wm.js b/web/share/js/wm.js index c292ece7..c292ece7 100644 --- a/web/js/wm.js +++ b/web/share/js/wm.js diff --git a/web/png/blank-stream.png b/web/share/png/blank-stream.png Binary files differindex f1e11b14..f1e11b14 100644 --- a/web/png/blank-stream.png +++ b/web/share/png/blank-stream.png diff --git a/web/safari-pinned-tab.svg b/web/share/safari-pinned-tab.svg index 86314c0a..86314c0a 100644 --- a/web/safari-pinned-tab.svg +++ b/web/share/safari-pinned-tab.svg diff --git a/web/site.webmanifest b/web/share/site.webmanifest index 409a6c2e..849f6a89 100644 --- a/web/site.webmanifest +++ b/web/share/site.webmanifest @@ -3,7 +3,7 @@ "short_name": "", "icons": [ { - "src": "/android-chrome-192x192.png", + "src": "/share/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" } diff --git a/web/svg/atx-hdd-led.svg b/web/share/svg/atx-hdd-led.svg index 75987570..75987570 100644 --- a/web/svg/atx-hdd-led.svg +++ b/web/share/svg/atx-hdd-led.svg diff --git a/web/svg/atx-power-led.svg b/web/share/svg/atx-power-led.svg index b82af7bc..b82af7bc 100644 --- a/web/svg/atx-power-led.svg +++ b/web/share/svg/atx-power-led.svg diff --git a/web/svg/fan-led.svg b/web/share/svg/fan-led.svg index 8cbdd76d..8cbdd76d 100644 --- a/web/svg/fan-led.svg +++ b/web/share/svg/fan-led.svg diff --git a/web/svg/favicon.svg b/web/share/svg/favicon.svg index a3fde08f..a3fde08f 100644 --- a/web/svg/favicon.svg +++ b/web/share/svg/favicon.svg diff --git a/web/svg/gear-led.svg b/web/share/svg/gear-led.svg index db9aff21..db9aff21 100644 --- a/web/svg/gear-led.svg +++ b/web/share/svg/gear-led.svg diff --git a/web/svg/hid-keyboard-led.svg b/web/share/svg/hid-keyboard-led.svg index dc17c615..dc17c615 100644 --- a/web/svg/hid-keyboard-led.svg +++ b/web/share/svg/hid-keyboard-led.svg diff --git a/web/svg/hid-mouse-led.svg b/web/share/svg/hid-mouse-led.svg index b922bdde..b922bdde 100644 --- a/web/svg/hid-mouse-led.svg +++ b/web/share/svg/hid-mouse-led.svg diff --git a/web/svg/info.svg b/web/share/svg/info.svg index b28139d5..b28139d5 100644 --- a/web/svg/info.svg +++ b/web/share/svg/info.svg diff --git a/web/svg/kvm.svg b/web/share/svg/kvm.svg index 7a0dcf71..7a0dcf71 100644 --- a/web/svg/kvm.svg +++ b/web/share/svg/kvm.svg diff --git a/web/svg/link-led.svg b/web/share/svg/link-led.svg index 8cc10e13..8cc10e13 100644 --- a/web/svg/link-led.svg +++ b/web/share/svg/link-led.svg diff --git a/web/svg/logo.svg b/web/share/svg/logo.svg index eec39fd3..eec39fd3 100644 --- a/web/svg/logo.svg +++ b/web/share/svg/logo.svg diff --git a/web/svg/msd-led.svg b/web/share/svg/msd-led.svg index a8da7092..a8da7092 100644 --- a/web/svg/msd-led.svg +++ b/web/share/svg/msd-led.svg diff --git a/web/svg/select-arrow-inactive.svg b/web/share/svg/select-arrow-inactive.svg index ba68f05c..ba68f05c 100644 --- a/web/svg/select-arrow-inactive.svg +++ b/web/share/svg/select-arrow-inactive.svg diff --git a/web/svg/select-arrow-intensive.svg b/web/share/svg/select-arrow-intensive.svg index 3223f099..3223f099 100644 --- a/web/svg/select-arrow-intensive.svg +++ b/web/share/svg/select-arrow-intensive.svg diff --git a/web/svg/select-arrow-normal.svg b/web/share/svg/select-arrow-normal.svg index 174663ce..174663ce 100644 --- a/web/svg/select-arrow-normal.svg +++ b/web/share/svg/select-arrow-normal.svg diff --git a/web/svg/stream-led.svg b/web/share/svg/stream-led.svg index 8dfbfbe3..8dfbfbe3 100644 --- a/web/svg/stream-led.svg +++ b/web/share/svg/stream-led.svg diff --git a/web/svg/stream-mouse-cursor.svg b/web/share/svg/stream-mouse-cursor.svg index ff852ef6..ff852ef6 100644 --- a/web/svg/stream-mouse-cursor.svg +++ b/web/share/svg/stream-mouse-cursor.svg diff --git a/web/svg/warning.svg b/web/share/svg/warning.svg index 3137b24d..3137b24d 100644 --- a/web/svg/warning.svg +++ b/web/share/svg/warning.svg |