forked from openlp/openlp
Refactor http server
This commit is contained in:
parent
ab011a22c1
commit
8cf886971c
@ -96,6 +96,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
log.debug(u'Processing settings exit')
|
||||
for tabIndex in range(self.stacked_layout.count()):
|
||||
self.stacked_layout.widget(tabIndex).save()
|
||||
# if the display of image background are changing we need to regenerate the image cache
|
||||
|
@ -126,54 +126,115 @@ from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent
|
||||
from openlp.core.utils import AppLocation, translate
|
||||
from openlp.plugins.remotes.lib.httpauth import AuthController, require_auth
|
||||
|
||||
from cherrypy._cpcompat import md5, sha, ntob
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def sha_password_encrypter(password):
|
||||
|
||||
return sha(ntob(password)).hexdigest()
|
||||
|
||||
|
||||
def fetch_password(username):
|
||||
if username != Settings().value(u'remotes/user id'):
|
||||
return None
|
||||
print "fetch password", username
|
||||
return sha(ntob(Settings().value(u'remotes/password'))).hexdigest()
|
||||
|
||||
|
||||
class HttpServer(object):
|
||||
"""
|
||||
Ability to control OpenLP via a web browser.
|
||||
"""
|
||||
|
||||
_cp_config = {
|
||||
'tools.sessions.on': True,
|
||||
'tools.auth.on': True
|
||||
}
|
||||
|
||||
def __init__(self, plugin):
|
||||
"""
|
||||
Initialise the http server, and start the server.
|
||||
"""
|
||||
log.debug(u'Initialise httpserver')
|
||||
self.plugin = plugin
|
||||
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
|
||||
self.connections = []
|
||||
self.conf = {'/files': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.html_dir}}
|
||||
self.router = HttpRouter()
|
||||
|
||||
def start_server(self):
|
||||
"""
|
||||
Start the http server, use the port in the settings default to 4316.
|
||||
Listen out for slide and song changes so they can be broadcast to clients. Listen out for socket connections.
|
||||
Start the http server based on configuration.
|
||||
"""
|
||||
log.debug(u'Start CherryPy server')
|
||||
# Define to security levels and add the router code
|
||||
self.root = self.Public()
|
||||
self.root.files = self.Files()
|
||||
self.root.stage = self.Stage()
|
||||
self.root.router = self.router
|
||||
self.root.files.router = self.router
|
||||
self.root.stage.router = self.router
|
||||
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
||||
# Turn off the flood of access messages cause by poll
|
||||
cherrypy.log.access_log.propagate = False
|
||||
cherrypy.engine.start()
|
||||
|
||||
def define_config(self):
|
||||
if Settings().value(self.plugin.settings_section + u'/https enabled'):
|
||||
port = Settings().value(self.plugin.settings_section + u'/https port')
|
||||
address = Settings().value(self.plugin.settings_section + u'/ip address')
|
||||
shared_data = AppLocation.get_directory(AppLocation.SharedData)
|
||||
server_config = {u'server.socket_host': str(address),
|
||||
u'server.socket_port': port,
|
||||
u'server.ssl_certificate': os.path.join(shared_data, u'openlp.crt'),
|
||||
u'server.ssl_private_key': os.path.join(shared_data, u'openlp.key')}
|
||||
cherrypy.config.update({u'server.socket_host': str(address),
|
||||
u'server.socket_port': port,
|
||||
u'server.ssl_certificate': os.path.join(shared_data, u'openlp.crt'),
|
||||
u'server.ssl_private_key': os.path.join(shared_data, u'openlp.key')})
|
||||
else:
|
||||
port = Settings().value(self.plugin.settings_section + u'/port')
|
||||
address = Settings().value(self.plugin.settings_section + u'/ip address')
|
||||
server_config = {u'server.socket_host': str(address),
|
||||
u'server.socket_port': port}
|
||||
cherrypy.config.update(server_config)
|
||||
cherrypy.config.update({'environment': 'embedded'})
|
||||
cherrypy.config.update({'engine.autoreload_on': False})
|
||||
cherrypy.tree.mount(HttpConnection(self), '/', config=self.conf)
|
||||
# Turn off the flood of access messages cause by poll
|
||||
cherrypy.log.access_log.propagate = False
|
||||
cherrypy.engine.start()
|
||||
log.debug(u'TCP listening on port %d' % port)
|
||||
cherrypy.config.update({u'server.socket_host': str(address)})
|
||||
cherrypy.config.update({u'server.socket_port': port})
|
||||
cherrypy.config.update({u'environment': u'embedded'})
|
||||
cherrypy.config.update({u'engine.autoreload_on': False})
|
||||
directory_config = {u'/': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': Settings().value(u'remotes/authentication enabled'),
|
||||
u'tools.basic_auth.realm': u'OpenLP Remote Login',
|
||||
u'tools.basic_auth.users': fetch_password,
|
||||
u'tools.basic_auth.encrypt': sha_password_encrypter},
|
||||
u'/files': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False},
|
||||
u'/stage': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False}}
|
||||
return directory_config
|
||||
|
||||
def reload_config(self):
|
||||
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
||||
cherrypy.config.reset()
|
||||
|
||||
class Public:
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
print "public"
|
||||
self.router.request_data = None
|
||||
if isinstance(kwargs, dict):
|
||||
self.router.request_data = kwargs.get(u'data', None)
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Files:
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
print "files"
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Stage:
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@ -181,25 +242,17 @@ class HttpServer(object):
|
||||
"""
|
||||
log.debug(u'close http server')
|
||||
cherrypy.engine.exit()
|
||||
cherrypy.engine.stop()
|
||||
|
||||
|
||||
class HttpConnection(object):
|
||||
class HttpRouter(object):
|
||||
"""
|
||||
A single connection, this handles communication between the server and the client.
|
||||
"""
|
||||
_cp_config = {
|
||||
'tools.sessions.on': True,
|
||||
'tools.auth.on': True
|
||||
}
|
||||
|
||||
auth = AuthController()
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialise the CherryPy Server
|
||||
"""
|
||||
self.parent = parent
|
||||
self.routes = [
|
||||
(u'^/$', self.serve_file),
|
||||
(u'^/(stage)$', self.serve_file),
|
||||
@ -218,33 +271,9 @@ class HttpConnection(object):
|
||||
(r'^/api/(.*)/add$', self.add_to_service)
|
||||
]
|
||||
self.translate()
|
||||
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
|
||||
|
||||
@cherrypy.expose
|
||||
@require_auth()
|
||||
def default(self, *args, **kwargs):
|
||||
"""
|
||||
Handles the requests for the main url. This is secure depending on settings in config.
|
||||
"""
|
||||
self.request_data = None
|
||||
if isinstance(kwargs, dict):
|
||||
self.request_data = kwargs.get(u'data', None)
|
||||
return self._process_http_request(args, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def stage(self, *args, **kwargs):
|
||||
"""
|
||||
Handles the requests for the /stage url. This is not secure.
|
||||
"""
|
||||
return self._process_http_request(args, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def files(self, *args, **kwargs):
|
||||
"""
|
||||
Handles the requests for the /files url. This is not secure.
|
||||
"""
|
||||
return self._process_http_request(args, kwargs)
|
||||
|
||||
def _process_http_request(self, args, kwargs):
|
||||
def process_http_request(self, url_path, *args):
|
||||
"""
|
||||
Common function to process HTTP requests where secure or insecure
|
||||
"""
|
||||
@ -253,7 +282,7 @@ class HttpConnection(object):
|
||||
for route, func in self.routes:
|
||||
match = re.match(route, url.path)
|
||||
if match:
|
||||
log.debug('Route "%s" matched "%s"', route, url.path)
|
||||
log.debug('Route "%s" matched "%s"', route, url_path)
|
||||
args = []
|
||||
for param in match.groups():
|
||||
args.append(param)
|
||||
@ -332,8 +361,8 @@ class HttpConnection(object):
|
||||
filename = u'index.html'
|
||||
elif filename == u'stage':
|
||||
filename = u'stage.html'
|
||||
path = os.path.normpath(os.path.join(self.parent.html_dir, filename))
|
||||
if not path.startswith(self.parent.html_dir):
|
||||
path = os.path.normpath(os.path.join(self.html_dir, filename))
|
||||
if not path.startswith(self.html_dir):
|
||||
return self._http_not_found()
|
||||
ext = os.path.splitext(filename)[1]
|
||||
html = None
|
||||
@ -450,7 +479,6 @@ class HttpConnection(object):
|
||||
if current_item:
|
||||
json_data[u'results'][u'item'] = self.live_controller.service_item.unique_identifier
|
||||
else:
|
||||
print event
|
||||
if self.request_data:
|
||||
try:
|
||||
data = json.loads(self.request_data)[u'request'][u'id']
|
||||
|
@ -155,6 +155,7 @@ class RemoteTab(SettingsTab):
|
||||
self.address_edit.textChanged.connect(self.set_urls)
|
||||
self.port_spin_box.valueChanged.connect(self.set_urls)
|
||||
self.https_port_spin_box.valueChanged.connect(self.set_urls)
|
||||
self.https_settings_group_box.clicked.connect(self.https_changed)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))
|
||||
@ -224,15 +225,22 @@ class RemoteTab(SettingsTab):
|
||||
self.user_id.setText(Settings().value(self.settings_section + u'/user id'))
|
||||
self.password.setText(Settings().value(self.settings_section + u'/password'))
|
||||
self.set_urls()
|
||||
self.https_changed()
|
||||
|
||||
def save(self):
|
||||
changed = False
|
||||
"""
|
||||
Save the configuration and update the server configuration if necessary
|
||||
"""
|
||||
if Settings().value(self.settings_section + u'/ip address') != self.address_edit.text() or \
|
||||
Settings().value(self.settings_section + u'/port') != self.port_spin_box.value() or \
|
||||
Settings().value(self.settings_section + u'/https port') != self.https_port_spin_box.value() or \
|
||||
Settings().value(self.settings_section + u'/https enabled') != \
|
||||
self.https_settings_group_box.isChecked():
|
||||
changed = True
|
||||
self.https_settings_group_box.isChecked() or \
|
||||
Settings().value(self.settings_section + u'/authentication enabled') != \
|
||||
self.user_login_group_box.isChecked() or \
|
||||
Settings().value(self.settings_section + u'/user id') != self.user_id.text() or \
|
||||
Settings().value(self.settings_section + u'/password') != self.password.text():
|
||||
self.settings_form.register_post_process(u'remotes_config_updated')
|
||||
Settings().setValue(self.settings_section + u'/port', self.port_spin_box.value())
|
||||
Settings().setValue(self.settings_section + u'/https port', self.https_port_spin_box.value())
|
||||
Settings().setValue(self.settings_section + u'/https enabled', self.https_settings_group_box.isChecked())
|
||||
@ -241,12 +249,16 @@ class RemoteTab(SettingsTab):
|
||||
Settings().setValue(self.settings_section + u'/authentication enabled', self.user_login_group_box.isChecked())
|
||||
Settings().setValue(self.settings_section + u'/user id', self.user_id.text())
|
||||
Settings().setValue(self.settings_section + u'/password', self.password.text())
|
||||
if changed:
|
||||
Registry().execute(u'remotes_config_updated')
|
||||
|
||||
|
||||
def on_twelve_hour_check_box_changed(self, check_state):
|
||||
self.twelve_hour = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
self.twelve_hour = True
|
||||
|
||||
def https_changed(self):
|
||||
"""
|
||||
Invert the HTTP group box based on Https group settings
|
||||
"""
|
||||
self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked())
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, translate, build_icon
|
||||
from openlp.plugins.remotes.lib import RemoteTab, HttpServer
|
||||
|
||||
@ -76,6 +78,7 @@ class RemotesPlugin(Plugin):
|
||||
Plugin.finalise(self)
|
||||
if self.server:
|
||||
self.server.close()
|
||||
self.server = None
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
@ -105,5 +108,6 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
Called when Config is changed to restart the server on new address or port
|
||||
"""
|
||||
self.finalise()
|
||||
self.initialise()
|
||||
log.debug(u'remote config changed')
|
||||
self.main_window.information_message(translate('RemotePlugin', 'Configuration Change'),
|
||||
translate('RemotePlugin', 'OpenLP will need to be restarted for the Remote changes to become active.'))
|
||||
|
Loading…
Reference in New Issue
Block a user