webui https

This commit is contained in:
Martijn Voncken 2008-11-04 19:15:56 +00:00
parent b3cda61961
commit f2b057bd30
6 changed files with 93 additions and 44 deletions

View File

@ -34,6 +34,7 @@
from deluge.ui.client import sclient as proxy from deluge.ui.client import sclient as proxy
from deluge.log import LOG as log from deluge.log import LOG as log
import utils import utils
import lib.newforms_plus as forms import lib.newforms_plus as forms
import config_forms import config_forms
@ -61,12 +62,28 @@ class Template(config_forms.WebCfgForm):
class Server(config_forms.WebCfgForm): class Server(config_forms.WebCfgForm):
title = _("Server") title = _("Server")
info = _("Manually restart webui to apply changes.")
port = forms.IntegerField(label = _("Port"),min_value=80) port = forms.IntegerField(label = _("Port"),min_value=80)
https = forms.CheckBox(_("Https"))
def validate(self, data):
import os
from deluge.common import get_default_config_dir
if data.https:
cert_path = os.path.join(get_default_config_dir("ssl") ,"deluge.cert.pem" )
if not os.path.exists (cert_path):
raise forms.ValidationError(_("Certificate not found at '%s'" % cert_path))
key_path = os.path.join(get_default_config_dir("ssl") ,"deluge.key.pem" )
if not os.path.exists (key_path):
raise forms.ValidationError(_("Key not found at '%s'" % key_path))
def post_save(self): def post_save(self):
pass pass
#raise forms.ValidationError( #raise forms.ValidationError(
# _("Manually restart server to apply these changes.")) # )
class Password(forms.Form): class Password(forms.Form):
title = _("Password") title = _("Password")

View File

@ -38,6 +38,7 @@ from deluge.ui.client import sclient
import components import components
from deluge.log import LOG as log from deluge.log import LOG as log
# Initialize gettext # Initialize gettext
if deluge.common.windows_check() or deluge.common.osx_check(): if deluge.common.windows_check() or deluge.common.osx_check():
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
@ -121,7 +122,21 @@ def create_webserver(debug = False, base_url =None):
server_address=("0.0.0.0", int(config.get('port'))) server_address=("0.0.0.0", int(config.get('port')))
server = CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost") server = CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")
log.info("http://%s:%d/" % server_address) https = False
if config.get("https"):
import os
from deluge.common import get_default_config_dir
cert_path = os.path.join(get_default_config_dir("ssl") ,"deluge.cert.pem" )
key_path = os.path.join(get_default_config_dir("ssl") ,"deluge.key.pem" )
if os.path.exists (key_path) and os.path.exists (cert_path):
server.ssl_certificate = cert_path
server.ssl_private_key = key_path
https = True
if https:
log.info("https://%s:%d/" % server_address)
else:
log.info("http://%s:%d/" % server_address)
return server return server
def run(debug = False, base_url = ""): def run(debug = False, base_url = ""):

View File

@ -9,7 +9,7 @@ __all__ = [
"header", "output", "flush", "debug", "header", "output", "flush", "debug",
"input", "data", "input", "data",
"setcookie", "cookies", "setcookie", "cookies",
"ctx", "ctx",
"loadhooks", "load", "unloadhooks", "unload", "_loadhooks", "loadhooks", "load", "unloadhooks", "unload", "_loadhooks",
"wsgifunc" "wsgifunc"
] ]
@ -59,19 +59,19 @@ def internalerror():
def header(hdr, value, unique=False): def header(hdr, value, unique=False):
""" """
Adds the header `hdr: value` with the response. Adds the header `hdr: value` with the response.
If `unique` is True and a header with that name already exists, If `unique` is True and a header with that name already exists,
it doesn't add a new one. it doesn't add a new one.
""" """
hdr, value = utf8(hdr), utf8(value) hdr, value = utf8(hdr), utf8(value)
# protection against HTTP response splitting attack # protection against HTTP response splitting attack
if '\n' in hdr or '\r' in hdr or '\n' in value or '\r' in value: if '\n' in hdr or '\r' in hdr or '\n' in value or '\r' in value:
raise ValueError, 'invalid characters in header' raise ValueError, 'invalid characters in header'
if unique is True: if unique is True:
for h, v in ctx.headers: for h, v in ctx.headers:
if h.lower() == hdr.lower(): return if h.lower() == hdr.lower(): return
ctx.headers.append((hdr, value)) ctx.headers.append((hdr, value))
def output(string_): def output(string_):
@ -88,20 +88,20 @@ def flush():
def input(*requireds, **defaults): def input(*requireds, **defaults):
""" """
Returns a `storage` object with the GET and POST arguments. Returns a `storage` object with the GET and POST arguments.
See `storify` for how `requireds` and `defaults` work. See `storify` for how `requireds` and `defaults` work.
""" """
from cStringIO import StringIO from cStringIO import StringIO
def dictify(fs): return dict([(k, fs[k]) for k in fs.keys()]) def dictify(fs): return dict([(k, fs[k]) for k in fs.keys()])
_method = defaults.pop('_method', 'both') _method = defaults.pop('_method', 'both')
e = ctx.env.copy() e = ctx.env.copy()
a = b = {} a = b = {}
if _method.lower() in ['both', 'post']: if _method.lower() in ['both', 'post']:
if e['REQUEST_METHOD'] == 'POST': if e['REQUEST_METHOD'] == 'POST':
a = cgi.FieldStorage(fp = StringIO(data()), environ=e, a = cgi.FieldStorage(fp = StringIO(data()), environ=e,
keep_blank_values=1) keep_blank_values=1)
a = dictify(a) a = dictify(a)
@ -125,15 +125,15 @@ def data():
def setcookie(name, value, expires="", domain=None): def setcookie(name, value, expires="", domain=None):
"""Sets a cookie.""" """Sets a cookie."""
if expires < 0: if expires < 0:
expires = -1000000000 expires = -1000000000
kargs = {'expires': expires, 'path':'/'} kargs = {'expires': expires, 'path':'/'}
if domain: if domain:
kargs['domain'] = domain kargs['domain'] = domain
# @@ should we limit cookies to a different path? # @@ should we limit cookies to a different path?
cookie = Cookie.SimpleCookie() cookie = Cookie.SimpleCookie()
cookie[name] = value cookie[name] = value
for key, val in kargs.iteritems(): for key, val in kargs.iteritems():
cookie[name][key] = val cookie[name][key] = val
header('Set-Cookie', cookie.items()[0][1].OutputString()) header('Set-Cookie', cookie.items()[0][1].OutputString())
@ -154,18 +154,18 @@ def debug(*args):
""" """
Prints a prettyprinted version of `args` to stderr. Prints a prettyprinted version of `args` to stderr.
""" """
try: try:
out = ctx.environ['wsgi.errors'] out = ctx.environ['wsgi.errors']
except: except:
out = sys.stderr out = sys.stderr
for arg in args: for arg in args:
print >> out, pprint.pformat(arg) print >> out, pprint.pformat(arg)
return '' return ''
def _debugwrite(x): def _debugwrite(x):
try: try:
out = ctx.environ['wsgi.errors'] out = ctx.environ['wsgi.errors']
except: except:
out = sys.stderr out = sys.stderr
out.write(x) out.write(x)
debug.write = _debugwrite debug.write = _debugwrite
@ -173,8 +173,8 @@ debug.write = _debugwrite
class _outputter: class _outputter:
"""Wraps `sys.stdout` so that print statements go into the response.""" """Wraps `sys.stdout` so that print statements go into the response."""
def __init__(self, file): self.file = file def __init__(self, file): self.file = file
def write(self, string_): def write(self, string_):
if hasattr(ctx, 'output'): if hasattr(ctx, 'output'):
return output(string_) return output(string_)
else: else:
self.file.write(string_) self.file.write(string_)
@ -186,7 +186,7 @@ def _capturedstdout():
while hasattr(sysstd, 'file'): while hasattr(sysstd, 'file'):
if isinstance(sys.stdout, _outputter): return True if isinstance(sys.stdout, _outputter): return True
sysstd = sysstd.file sysstd = sysstd.file
if isinstance(sys.stdout, _outputter): return True if isinstance(sys.stdout, _outputter): return True
return False return False
if not _capturedstdout(): if not _capturedstdout():
@ -197,7 +197,7 @@ ctx = context = threadeddict(_context)
ctx.__doc__ = """ ctx.__doc__ = """
A `storage` object containing various information about the request: A `storage` object containing various information about the request:
`environ` (aka `env`) `environ` (aka `env`)
: A dictionary containing the standard WSGI environment variables. : A dictionary containing the standard WSGI environment variables.
@ -215,7 +215,7 @@ A `storage` object containing various information about the request:
`path` `path`
: The path request. : The path request.
`query` `query`
: If there are no query arguments, the empty string. Otherwise, a `?` followed : If there are no query arguments, the empty string. Otherwise, a `?` followed
by the query string. by the query string.
@ -241,8 +241,8 @@ _loadhooks = {}
def load(): def load():
""" """
Loads a new context for the thread. Loads a new context for the thread.
You can ask for a function to be run at loadtime by You can ask for a function to be run at loadtime by
adding it to the dictionary `loadhooks`. adding it to the dictionary `loadhooks`.
""" """
_context[threading.currentThread()] = storage() _context[threading.currentThread()] = storage()
@ -251,7 +251,7 @@ def load():
if config.get('db_parameters'): if config.get('db_parameters'):
import db import db
db.connect(**config.db_parameters) db.connect(**config.db_parameters)
for x in loadhooks.values(): x() for x in loadhooks.values(): x()
def _load(env): def _load(env):
@ -259,7 +259,13 @@ def _load(env):
ctx.output = '' ctx.output = ''
ctx.environ = ctx.env = env ctx.environ = ctx.env = env
ctx.host = env.get('HTTP_HOST') ctx.host = env.get('HTTP_HOST')
ctx.homedomain = 'http://' + env.get('HTTP_HOST', '[unknown]')
#http://groups.google.com/group/webpy/browse_thread/thread/2a4665e54b07c991?pli=1
if env.get('HTTPS'):
ctx.protocol = "https"
else:
ctx.protocol = "http"
ctx.homedomain = ctx.protocol + "://" + env.get('HTTP_HOST','[unknown]')
ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', '')) ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))
ctx.home = ctx.homedomain + ctx.homepath ctx.home = ctx.homedomain + ctx.homepath
ctx.ip = env.get('REMOTE_ADDR') ctx.ip = env.get('REMOTE_ADDR')
@ -267,14 +273,14 @@ def _load(env):
ctx.path = env.get('PATH_INFO') ctx.path = env.get('PATH_INFO')
# http://trac.lighttpd.net/trac/ticket/406 requires: # http://trac.lighttpd.net/trac/ticket/406 requires:
if env.get('SERVER_SOFTWARE', '').startswith('lighttpd/'): if env.get('SERVER_SOFTWARE', '').startswith('lighttpd/'):
ctx.path = lstrips(env.get('REQUEST_URI').split('?')[0], ctx.path = lstrips(env.get('REQUEST_URI').split('?')[0],
os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))) os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', '')))
if env.get('QUERY_STRING'): if env.get('QUERY_STRING'):
ctx.query = '?' + env.get('QUERY_STRING', '') ctx.query = '?' + env.get('QUERY_STRING', '')
else: else:
ctx.query = '' ctx.query = ''
ctx.fullpath = ctx.path + ctx.query ctx.fullpath = ctx.path + ctx.query
for x in _loadhooks.values(): x() for x in _loadhooks.values(): x()
@ -283,7 +289,7 @@ unloadhooks = {}
def unload(): def unload():
""" """
Unloads the context for the thread. Unloads the context for the thread.
You can ask for a function to be run at loadtime by You can ask for a function to be run at loadtime by
adding it ot the dictionary `unloadhooks`. adding it ot the dictionary `unloadhooks`.
""" """
@ -297,7 +303,7 @@ def _unload():
def wsgifunc(func, *middleware): def wsgifunc(func, *middleware):
"""Returns a WSGI-compatible function from a webpy-function.""" """Returns a WSGI-compatible function from a webpy-function."""
middleware = list(middleware) middleware = list(middleware)
def wsgifunc(env, start_resp): def wsgifunc(env, start_resp):
_load(env) _load(env)
try: try:
@ -307,7 +313,7 @@ def wsgifunc(func, *middleware):
except: except:
print >> debug, traceback.format_exc() print >> debug, traceback.format_exc()
result = internalerror() result = internalerror()
is_generator = result and hasattr(result, 'next') is_generator = result and hasattr(result, 'next')
if is_generator: if is_generator:
# wsgi requires the headers first # wsgi requires the headers first
@ -322,19 +328,19 @@ def wsgifunc(func, *middleware):
ctx._write = start_resp(status, headers) ctx._write = start_resp(status, headers)
# and now, the fun: # and now, the fun:
def cleanup(): def cleanup():
# we insert this little generator # we insert this little generator
# at the end of our itertools.chain # at the end of our itertools.chain
# so that it unloads the request # so that it unloads the request
# when everything else is done # when everything else is done
yield '' # force it to be a generator yield '' # force it to be a generator
_unload() _unload()
# result is the output of calling the webpy function # result is the output of calling the webpy function
# it could be a generator... # it could be a generator...
if is_generator: if is_generator:
if firstchunk is flush: if firstchunk is flush:
# oh, it's just our special flush mode # oh, it's just our special flush mode
@ -348,22 +354,22 @@ def wsgifunc(func, *middleware):
return [] return []
else: else:
return itertools.chain([firstchunk], result, cleanup()) return itertools.chain([firstchunk], result, cleanup())
# ... but it's usually just None # ... but it's usually just None
# #
# output is the stuff in ctx.output # output is the stuff in ctx.output
# it's usually a string... # it's usually a string...
if isinstance(output, str): #@@ other stringlikes? if isinstance(output, str): #@@ other stringlikes?
_unload() _unload()
return [output] return [output]
# it could be a generator... # it could be a generator...
elif hasattr(output, 'next'): elif hasattr(output, 'next'):
return itertools.chain(output, cleanup()) return itertools.chain(output, cleanup())
else: else:
_unload() _unload()
raise Exception, "Invalid ctx.output" raise Exception, "Invalid ctx.output"
for mw_func in middleware: for mw_func in middleware:
wsgifunc = mw_func(wsgifunc) wsgifunc = mw_func(wsgifunc)
return wsgifunc return wsgifunc

View File

@ -5,6 +5,15 @@ $def with (title, active_tab="NONE")
<link rel="icon" href="$base/static/images/deluge-icon.png" type="image/png" /> <link rel="icon" href="$base/static/images/deluge-icon.png" type="image/png" />
<link rel="shortcut icon" href="$base/static/images/deluge-icon.png" type="image/png" /> <link rel="shortcut icon" href="$base/static/images/deluge-icon.png" type="image/png" />
<link rel="stylesheet" type="text/css" href="$base/static/simple_site_style.css" /> <link rel="stylesheet" type="text/css" href="$base/static/simple_site_style.css" />
<!--javascript is only used for plugins in the classic-ui
classic ui should function without js.
-->
<script language="javascript" src="$base/gettext.js"></script>
<script language="javascript" src="$base/static/mootools-1.2-core.js"></script>
<script language="javascript" src="$base/static/mootools-1.2-more.js"></script>
<script language="javascript" src="$base/static/mooui.js"></script>
$for js in include_javascript:
<script language="javascript" src="$base$js"></script>
</head> </head>
<body> <body>
<div id="page"> <div id="page">

View File

@ -7,6 +7,7 @@ $def with (title, active_tab="NONE")
<link rel="icon" href="$base/static/images/deluge-icon.png" type="image/png" /> <link rel="icon" href="$base/static/images/deluge-icon.png" type="image/png" />
<link rel="shortcut icon" href="$base/static/images/deluge-icon.png" type="image/png" /> <link rel="shortcut icon" href="$base/static/images/deluge-icon.png" type="image/png" />
<link rel="stylesheet" type="text/css" href="$base/template_style.css" /> <link rel="stylesheet" type="text/css" href="$base/template_style.css" />
<script language="javascript" src="$base/gettext.js"></script>
<script language="javascript" src="$base/static/deluge.js"></script> <script language="javascript" src="$base/static/deluge.js"></script>
<script language="javascript" src="$base/static/mootools-1.2-core.js"></script> <script language="javascript" src="$base/static/mootools-1.2-core.js"></script>
<script language="javascript" src="$base/static/mootools-1.2-more.js"></script> <script language="javascript" src="$base/static/mootools-1.2-more.js"></script>

View File

@ -65,5 +65,6 @@ CONFIG_DEFAULTS = {
"sidebar_show_zero":False, "sidebar_show_zero":False,
"sidebar_show_trackers":False, "sidebar_show_trackers":False,
"show_keyword_search":False, "show_keyword_search":False,
"show_sidebar":True "show_sidebar":True,
"https":False
} }