2019-05-12 13:56:18 +00:00
|
|
|
import os
|
2019-05-03 20:59:24 +00:00
|
|
|
import mimetypes
|
2019-07-05 17:30:24 +00:00
|
|
|
import json
|
2019-12-08 19:27:58 +00:00
|
|
|
import pkg_resources
|
2019-05-12 13:56:18 +00:00
|
|
|
from datetime import datetime
|
2019-07-11 11:40:12 +00:00
|
|
|
from string import Template
|
2019-05-04 14:56:23 +00:00
|
|
|
from owrx.websocket import WebSocketConnection
|
|
|
|
from owrx.config import PropertyManager
|
2019-05-12 16:10:24 +00:00
|
|
|
from owrx.source import ClientRegistry
|
2019-05-12 13:56:18 +00:00
|
|
|
from owrx.connection import WebSocketMessageHandler
|
2019-05-11 12:18:43 +00:00
|
|
|
from owrx.version import openwebrx_version
|
2019-07-05 17:30:24 +00:00
|
|
|
from owrx.feature import FeatureDetector
|
2019-08-04 16:36:03 +00:00
|
|
|
from owrx.metrics import Metrics
|
2019-05-03 20:59:24 +00:00
|
|
|
|
2019-05-10 19:50:58 +00:00
|
|
|
import logging
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-05-10 19:50:58 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-05-03 20:59:24 +00:00
|
|
|
class Controller(object):
|
2019-07-06 11:03:49 +00:00
|
|
|
def __init__(self, handler, request):
|
2019-05-03 20:59:24 +00:00
|
|
|
self.handler = handler
|
2019-07-06 11:03:49 +00:00
|
|
|
self.request = request
|
2019-07-21 17:40:28 +00:00
|
|
|
|
|
|
|
def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None):
|
2019-05-03 20:59:24 +00:00
|
|
|
self.handler.send_response(code)
|
|
|
|
if content_type is not None:
|
|
|
|
self.handler.send_header("Content-Type", content_type)
|
2019-05-09 14:12:05 +00:00
|
|
|
if last_modified is not None:
|
|
|
|
self.handler.send_header("Last-Modified", last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"))
|
|
|
|
if max_age is not None:
|
|
|
|
self.handler.send_header("Cache-Control", "max-age: {0}".format(max_age))
|
2019-05-03 20:59:24 +00:00
|
|
|
self.handler.end_headers()
|
2019-07-21 17:40:28 +00:00
|
|
|
if type(content) == str:
|
2019-05-03 20:59:24 +00:00
|
|
|
content = content.encode()
|
|
|
|
self.handler.wfile.write(content)
|
|
|
|
|
|
|
|
|
|
|
|
class StatusController(Controller):
|
|
|
|
def handle_request(self):
|
2019-05-10 21:00:18 +00:00
|
|
|
pm = PropertyManager.getSharedInstance()
|
|
|
|
# TODO keys that have been left out since they are no longer simple strings: sdr_hw, bands, antenna
|
|
|
|
vars = {
|
|
|
|
"status": "active",
|
|
|
|
"name": pm["receiver_name"],
|
|
|
|
"op_email": pm["receiver_admin"],
|
2019-05-12 16:10:24 +00:00
|
|
|
"users": ClientRegistry.getSharedInstance().clientCount(),
|
2019-05-10 21:00:18 +00:00
|
|
|
"users_max": pm["max_clients"],
|
|
|
|
"gps": pm["receiver_gps"],
|
|
|
|
"asl": pm["receiver_asl"],
|
|
|
|
"loc": pm["receiver_location"],
|
2019-05-11 12:18:43 +00:00
|
|
|
"sw_version": openwebrx_version,
|
2019-07-21 17:40:28 +00:00
|
|
|
"avatar_ctime": os.path.getctime("htdocs/gfx/openwebrx-avatar.png"),
|
2019-05-10 21:00:18 +00:00
|
|
|
}
|
2019-07-21 17:40:28 +00:00
|
|
|
self.send_response("\n".join(["{key}={value}".format(key=key, value=value) for key, value in vars.items()]))
|
|
|
|
|
2019-05-03 20:59:24 +00:00
|
|
|
|
|
|
|
class AssetsController(Controller):
|
2019-12-08 19:27:58 +00:00
|
|
|
def getModified(self, file):
|
|
|
|
return None
|
|
|
|
|
|
|
|
def openFile(self, file):
|
|
|
|
pass
|
2019-09-18 15:22:35 +00:00
|
|
|
|
2019-07-21 17:40:28 +00:00
|
|
|
def serve_file(self, file, content_type=None):
|
2019-05-04 14:56:23 +00:00
|
|
|
try:
|
2019-12-08 19:27:58 +00:00
|
|
|
modified = self.getModified(file)
|
2019-05-09 14:12:05 +00:00
|
|
|
|
2019-12-08 19:27:58 +00:00
|
|
|
if modified is not None and "If-Modified-Since" in self.handler.headers:
|
2019-07-21 17:40:28 +00:00
|
|
|
client_modified = datetime.strptime(
|
|
|
|
self.handler.headers["If-Modified-Since"], "%a, %d %b %Y %H:%M:%S %Z"
|
|
|
|
)
|
2019-05-09 14:12:05 +00:00
|
|
|
if modified <= client_modified:
|
2019-07-21 17:40:28 +00:00
|
|
|
self.send_response("", code=304)
|
2019-05-09 14:12:05 +00:00
|
|
|
return
|
|
|
|
|
2019-12-08 19:27:58 +00:00
|
|
|
f = self.openFile(file)
|
2019-05-04 14:56:23 +00:00
|
|
|
data = f.read()
|
|
|
|
f.close()
|
|
|
|
|
2019-05-09 14:12:05 +00:00
|
|
|
if content_type is None:
|
|
|
|
(content_type, encoding) = mimetypes.MimeTypes().guess_type(file)
|
2019-07-21 17:40:28 +00:00
|
|
|
self.send_response(data, content_type=content_type, last_modified=modified, max_age=3600)
|
2019-05-04 14:56:23 +00:00
|
|
|
except FileNotFoundError:
|
2019-07-21 17:40:28 +00:00
|
|
|
self.send_response("file not found", code=404)
|
|
|
|
|
2019-05-03 20:59:24 +00:00
|
|
|
def handle_request(self):
|
2019-07-06 11:03:49 +00:00
|
|
|
filename = self.request.matches.group(1)
|
2019-05-04 14:56:23 +00:00
|
|
|
self.serve_file(filename)
|
|
|
|
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-09-18 15:22:35 +00:00
|
|
|
class OwrxAssetsController(AssetsController):
|
2019-12-08 19:27:58 +00:00
|
|
|
def openFile(self, file):
|
|
|
|
return pkg_resources.resource_stream('htdocs', file)
|
2019-09-18 15:22:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
class AprsSymbolsController(AssetsController):
|
|
|
|
def __init__(self, handler, request):
|
|
|
|
pm = PropertyManager.getSharedInstance()
|
2019-12-08 19:27:58 +00:00
|
|
|
path = pm["aprs_symbols_path"]
|
|
|
|
if not path.endswith("/"):
|
|
|
|
path += "/"
|
|
|
|
self.path = path
|
|
|
|
super().__init__(handler, request)
|
|
|
|
|
|
|
|
def getFilePath(self, file):
|
|
|
|
return self.path + file
|
|
|
|
|
|
|
|
def getModified(self, file):
|
|
|
|
return datetime.fromtimestamp(os.path.getmtime(self.getFilePath(file)))
|
|
|
|
|
|
|
|
def openFile(self, file):
|
|
|
|
return open(self.getFilePath(file), "rb")
|
2019-09-18 15:22:35 +00:00
|
|
|
|
|
|
|
|
2019-07-11 11:40:12 +00:00
|
|
|
class TemplateController(Controller):
|
|
|
|
def render_template(self, file, **vars):
|
2019-12-08 19:27:58 +00:00
|
|
|
file_content = pkg_resources.resource_string("htdocs", file).decode("utf-8")
|
|
|
|
template = Template(file_content)
|
2019-07-11 11:40:12 +00:00
|
|
|
|
|
|
|
return template.safe_substitute(**vars)
|
|
|
|
|
|
|
|
def serve_template(self, file, **vars):
|
2019-07-21 17:40:28 +00:00
|
|
|
self.send_response(self.render_template(file, **vars), content_type="text/html")
|
2019-07-11 11:40:12 +00:00
|
|
|
|
|
|
|
def default_variables(self):
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
class WebpageController(TemplateController):
|
|
|
|
def template_variables(self):
|
2019-07-21 17:40:28 +00:00
|
|
|
header = self.render_template("include/header.include.html")
|
|
|
|
return {"header": header}
|
2019-07-11 11:40:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
class IndexController(WebpageController):
|
2019-05-09 14:12:05 +00:00
|
|
|
def handle_request(self):
|
2019-07-11 11:40:12 +00:00
|
|
|
self.serve_template("index.html", **self.template_variables())
|
|
|
|
|
2019-05-09 14:12:05 +00:00
|
|
|
|
2019-07-11 11:40:12 +00:00
|
|
|
class MapController(WebpageController):
|
2019-07-01 14:49:39 +00:00
|
|
|
def handle_request(self):
|
2019-07-21 17:40:28 +00:00
|
|
|
# TODO check if we have a google maps api key first?
|
2019-07-11 11:40:12 +00:00
|
|
|
self.serve_template("map.html", **self.template_variables())
|
2019-07-05 17:30:24 +00:00
|
|
|
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-07-11 14:44:33 +00:00
|
|
|
class FeatureController(WebpageController):
|
2019-07-05 17:30:24 +00:00
|
|
|
def handle_request(self):
|
2019-07-11 14:44:33 +00:00
|
|
|
self.serve_template("features.html", **self.template_variables())
|
2019-07-05 17:30:24 +00:00
|
|
|
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-07-05 17:30:24 +00:00
|
|
|
class ApiController(Controller):
|
|
|
|
def handle_request(self):
|
|
|
|
data = json.dumps(FeatureDetector().feature_report())
|
2019-07-21 17:40:28 +00:00
|
|
|
self.send_response(data, content_type="application/json")
|
|
|
|
|
2019-07-01 14:49:39 +00:00
|
|
|
|
2019-08-04 16:36:03 +00:00
|
|
|
class MetricsController(Controller):
|
|
|
|
def handle_request(self):
|
|
|
|
data = json.dumps(Metrics.getSharedInstance().getMetrics())
|
|
|
|
self.send_response(data, content_type="application/json")
|
|
|
|
|
|
|
|
|
2019-05-04 18:26:11 +00:00
|
|
|
class WebSocketController(Controller):
|
|
|
|
def handle_request(self):
|
|
|
|
conn = WebSocketConnection(self.handler, WebSocketMessageHandler())
|
|
|
|
# enter read loop
|
2019-10-21 19:51:31 +00:00
|
|
|
conn.handle()
|