rev307, Plugin for password protected web ui, Faster page load times by yielding wrapper html, Reworked configuration parser to support plugin extensions, Initial site sync bugfix, Test for configuration parsing, Parse posted data function
This commit is contained in:
parent
4c9a677369
commit
a93ca2c3b4
11 changed files with 509 additions and 193 deletions
117
plugins/disabled-UiPassword/UiPasswordPlugin.py
Normal file
117
plugins/disabled-UiPassword/UiPasswordPlugin.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
from Config import config
|
||||||
|
from Plugin import PluginManager
|
||||||
|
|
||||||
|
if "sessions" not in locals().keys(): # To keep sessions between module reloads
|
||||||
|
sessions = {}
|
||||||
|
|
||||||
|
|
||||||
|
@PluginManager.registerTo("UiRequest")
|
||||||
|
class UiRequestPlugin(object):
|
||||||
|
sessions = sessions
|
||||||
|
last_cleanup = time.time()
|
||||||
|
|
||||||
|
def route(self, path):
|
||||||
|
if path.endswith("favicon.ico"):
|
||||||
|
return self.actionFile("src/Ui/media/img/favicon.ico")
|
||||||
|
else:
|
||||||
|
if config.ui_password:
|
||||||
|
if time.time() - self.last_cleanup > 60 * 60: # Cleanup expired sessions every hour
|
||||||
|
self.cleanup()
|
||||||
|
# Validate session
|
||||||
|
session_id = self.getCookies().get("session_id")
|
||||||
|
if session_id not in self.sessions: # Invalid session id, display login
|
||||||
|
return self.actionLogin()
|
||||||
|
return super(UiRequestPlugin, self).route(path)
|
||||||
|
|
||||||
|
# Action: Login
|
||||||
|
def actionLogin(self):
|
||||||
|
template = open("plugins/UiPassword/login.html").read()
|
||||||
|
self.sendHeader()
|
||||||
|
posted = self.getPosted()
|
||||||
|
if posted: # Validate http posted data
|
||||||
|
if self.checkPassword(posted.get("password")):
|
||||||
|
# Valid password, create session
|
||||||
|
session_id = self.randomString(26)
|
||||||
|
self.sessions[session_id] = {
|
||||||
|
"added": time.time(),
|
||||||
|
"keep": posted.get("keep")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect to homepage or referer
|
||||||
|
url = self.env.get("HTTP_REFERER", "")
|
||||||
|
if not url or re.sub("\?.*", "", url).endswith("/Login"):
|
||||||
|
url = "/" + config.homepage
|
||||||
|
cookie_header = ('Set-Cookie', "session_id=%s;path=/;max-age=2592000;" % session_id) # Max age = 30 days
|
||||||
|
self.start_response('301 Redirect', [('Location', url), cookie_header])
|
||||||
|
yield "Redirecting..."
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Invalid password, show login form again
|
||||||
|
template = template.replace("{result}", "bad_password")
|
||||||
|
yield template
|
||||||
|
|
||||||
|
def checkPassword(self, password):
|
||||||
|
if password == config.ui_password:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def randomString(self, chars):
|
||||||
|
return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(chars))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cleanup(cls):
|
||||||
|
for session_id, session in cls.sessions.items():
|
||||||
|
if session["keep"] and time.time() - session["added"] > 60 * 60 * 24 * 60: # Max 60days for keep sessions
|
||||||
|
del(cls.sessions[session_id])
|
||||||
|
elif not session["keep"] and time.time() - session["added"] > 60 * 60 * 24: # Max 24h for non-keep sessions
|
||||||
|
del(cls.sessions[session_id])
|
||||||
|
|
||||||
|
# Action: Display sessions
|
||||||
|
def actionSessions(self):
|
||||||
|
self.sendHeader()
|
||||||
|
yield "<pre>"
|
||||||
|
yield json.dumps(self.sessions, indent=4)
|
||||||
|
|
||||||
|
# Action: Logout
|
||||||
|
def actionLogout(self):
|
||||||
|
# Session id has to passed as get parameter or called without referer to avoid remote logout
|
||||||
|
session_id = self.getCookies().get("session_id")
|
||||||
|
if not self.env.get("HTTP_REFERER") or session_id == self.get.get("session_id"):
|
||||||
|
if session_id in self.sessions:
|
||||||
|
del self.sessions[session_id]
|
||||||
|
self.start_response('301 Redirect', [
|
||||||
|
('Location', "/"),
|
||||||
|
('Set-Cookie', "session_id=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
|
||||||
|
])
|
||||||
|
yield "Redirecting..."
|
||||||
|
else:
|
||||||
|
self.sendHeader()
|
||||||
|
yield "Error: Invalid session id"
|
||||||
|
|
||||||
|
|
||||||
|
@PluginManager.registerTo("ConfigPlugin")
|
||||||
|
class ConfigPlugin(object):
|
||||||
|
def createArguments(self):
|
||||||
|
group = self.parser.add_argument_group("UiPassword plugin")
|
||||||
|
group.add_argument('--ui_password', help='Password to access UiServer', default=None, metavar="password")
|
||||||
|
|
||||||
|
return super(ConfigPlugin, self).createArguments()
|
||||||
|
|
||||||
|
|
||||||
|
@PluginManager.registerTo("UiWebsocket")
|
||||||
|
class UiWebsocketPlugin(object):
|
||||||
|
def actionUiLogout(self, to):
|
||||||
|
permissions = self.getPermissions(to)
|
||||||
|
if "ADMIN" not in permissions:
|
||||||
|
return self.response(to, "You don't have permission to run this command")
|
||||||
|
|
||||||
|
session_id = self.request.getCookies().get("session_id", "")
|
||||||
|
message = "<script>document.location.href = '/Logout?session_id=%s'</script>" % session_id
|
||||||
|
self.cmd("notification", ["done", message])
|
1
plugins/disabled-UiPassword/__init__.py
Normal file
1
plugins/disabled-UiPassword/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import UiPasswordPlugin
|
116
plugins/disabled-UiPassword/login.html
Normal file
116
plugins/disabled-UiPassword/login.html
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Log In</title>
|
||||||
|
<meta name="viewport" id="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #323C4D; font-family: "Segoe UI", Helvetica, Arial; font-weight: lighter;
|
||||||
|
font-size: 22px; color: #333; letter-spacing: 1px; color: white; overflow: hidden;
|
||||||
|
}
|
||||||
|
.login { left: 50%; position: absolute; top: 50%; transform: translateX(-50%) translateY(-50%); width: 100%; max-width: 370px; text-align: center; }
|
||||||
|
|
||||||
|
*:focus { outline: 0; }
|
||||||
|
input[type=text], input[type=password] {
|
||||||
|
padding: 10px 0px; border: 0px; display: block; margin: 15px 0px; width: 100%; border-radius: 30px; transition: 0.3s ease-out; background-color: #DDD;
|
||||||
|
text-align: center; font-family: "Segoe UI", Helvetica, Arial; font-weight: lighter; font-size: 28px; border: 2px solid #323C4D;
|
||||||
|
}
|
||||||
|
input[type=text]:focus, input[type=password]:focus {
|
||||||
|
border: 2px solid #FFF; background-color: #FFF;
|
||||||
|
}
|
||||||
|
input[type=checkbox] { opacity: 0; }
|
||||||
|
input[type=checkbox]:checked + label { color: white; }
|
||||||
|
input[type=checkbox]:focus + label::before { background-color: #435065; }
|
||||||
|
input[type=checkbox]:checked + label::before { box-shadow: inset 0px 0px 0px 5px white; background-color: #4DCC6E; }
|
||||||
|
input.error { border: 2px solid #F44336 !important; animation: shake 1s }
|
||||||
|
label::before {
|
||||||
|
content: ""; width: 20px; height: 20px; background-color: #323C4D;
|
||||||
|
display: inline-block; margin-left: -20px; border-radius: 15px; box-shadow: inset 0px 0px 0px 2px #9EA5B3;
|
||||||
|
transition: all 0.1s; margin-right: 7px; position: relative; top: 2px;
|
||||||
|
}
|
||||||
|
label { vertical-align: -1px; color: #9EA5B3; transition: all 0.3s; }
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 13px; display: inline-block; margin: 15px 0px; width: 100%; border-radius: 30px; text-align: center; white-space: nowrap;
|
||||||
|
font-size: 28px; color: #333; background: linear-gradient(45deg, #6B14D3 0, #7A26E2 25%, #4962DD 90%);
|
||||||
|
box-sizing: border-box; margin-top: 50px; color: white; text-decoration: none; transition: 0.3s ease-out;
|
||||||
|
}
|
||||||
|
.button:hover, .button:focus { box-shadow: 0px 5px 30px rgba(0,0,0,0.3); }
|
||||||
|
.button:active { transform: translateY(1px); box-shadow: 0px 0px 20px rgba(0,0,0,0.5); transition: none; }
|
||||||
|
|
||||||
|
#login_form_submit { display: none; }
|
||||||
|
|
||||||
|
.login-anim { animation: login 1s cubic-bezier(0.785, 0.135, 0.15, 0.86) forwards; }
|
||||||
|
|
||||||
|
@keyframes login {
|
||||||
|
0% { width: 100%; }
|
||||||
|
60% { width: 63px; transform: scale(1); color: rgba(255,255,255,0); }
|
||||||
|
70% { width: 63px; transform: scale(1); color: rgba(255,255,255,0); }
|
||||||
|
100% { transform: scale(80); width: 63px; color: rgba(255,255,255,0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
|
||||||
|
20%, 40%, 60%, 80% { transform: translateX(10px); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="login">
|
||||||
|
<form action="" method="post" id="login_form" onkeypress="return onFormKeypress(event)">
|
||||||
|
<!--<input type="text" name="username" placeholder="Username" required/>-->
|
||||||
|
<input type="password" name="password" placeholder="Password" required/>
|
||||||
|
<input type="checkbox" name="keep" id="keep"><label for="keep">Keep me logged in</label>
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
<a href="Login" class="button" onclick="return submit()" id="login_button"><span>Log In</span></a>
|
||||||
|
<input type="submit" id="login_form_submit"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function onFormKeypress(e) {
|
||||||
|
if (event.keyCode == 13) {
|
||||||
|
submit()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit() {
|
||||||
|
var form = document.getElementById("login_form")
|
||||||
|
if (form.checkValidity()) {
|
||||||
|
document.getElementById("login_button").className = "button login-anim"
|
||||||
|
setTimeout(function () {
|
||||||
|
document.getElementById("login_form_submit").click()
|
||||||
|
}, 1000)
|
||||||
|
} else {
|
||||||
|
document.getElementById("login_form_submit").click()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function badPassword() {
|
||||||
|
var elem = document.getElementsByName("password")[0]
|
||||||
|
elem.className = "error"
|
||||||
|
elem.placeholder = "Wrong Password"
|
||||||
|
elem.focus()
|
||||||
|
elem.addEventListener('input', function() {
|
||||||
|
elem.className = ""
|
||||||
|
elem.placeholder = "Password"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
result = "{result}"
|
||||||
|
|
||||||
|
if (result == "bad_password")
|
||||||
|
badPassword()
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -52,30 +52,3 @@ rpc_pass = re.search("rpcpassword=(.*)$", namecoin_conf, re.M).group(1)
|
||||||
rpc_url = "http://%s:%s@127.0.0.1:8336" % (rpc_user, rpc_pass)
|
rpc_url = "http://%s:%s@127.0.0.1:8336" % (rpc_user, rpc_pass)
|
||||||
|
|
||||||
rpc = AuthServiceProxy(rpc_url, timeout=60*5)
|
rpc = AuthServiceProxy(rpc_url, timeout=60*5)
|
||||||
|
|
||||||
"""
|
|
||||||
while 1:
|
|
||||||
print "Waiting for new block",
|
|
||||||
sys.stdout.flush()
|
|
||||||
while 1:
|
|
||||||
try:
|
|
||||||
rpc = AuthServiceProxy(rpc_url, timeout=60*5)
|
|
||||||
if (int(rpc.getinfo()["blocks"]) > last_block): break
|
|
||||||
time.sleep(1)
|
|
||||||
rpc.waitforblock()
|
|
||||||
print "Found"
|
|
||||||
break # Block found
|
|
||||||
except socket.timeout: # Timeout
|
|
||||||
print ".",
|
|
||||||
sys.stdout.flush()
|
|
||||||
except Exception, err:
|
|
||||||
print "Exception", err.__class__, err
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
last_block = int(rpc.getinfo()["blocks"])
|
|
||||||
for block_id in range(config["lastprocessed"]+1, last_block+1):
|
|
||||||
processBlock(block_id)
|
|
||||||
|
|
||||||
config["lastprocessed"] = last_block
|
|
||||||
open(config_path, "w").write(json.dumps(config, indent=2))
|
|
||||||
"""
|
|
196
src/Config.py
196
src/Config.py
|
@ -6,14 +6,19 @@ import ConfigParser
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, argv):
|
||||||
self.version = "0.3.1"
|
self.version = "0.3.1"
|
||||||
self.rev = 281
|
self.rev = 307
|
||||||
self.parser = self.createArguments()
|
self.argv = argv
|
||||||
argv = sys.argv[:] # Copy command line arguments
|
self.action = None
|
||||||
argv = self.parseConfig(argv) # Add arguments from config file
|
self.createParser()
|
||||||
self.parseCommandline(argv) # Parse argv
|
self.createArguments()
|
||||||
self.setAttributes()
|
|
||||||
|
def createParser(self):
|
||||||
|
# Create parser
|
||||||
|
self.parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
|
self.parser.register('type', 'bool', self.strToBool)
|
||||||
|
self.subparsers = self.parser.add_subparsers(title="Action to perform", dest="action")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.arguments).replace("Namespace", "Config") # Using argparse str output
|
return str(self.arguments).replace("Namespace", "Config") # Using argparse str output
|
||||||
|
@ -29,28 +34,17 @@ class Config(object):
|
||||||
coffeescript = "type %s | tools\\coffee\\coffee.cmd"
|
coffeescript = "type %s | tools\\coffee\\coffee.cmd"
|
||||||
else:
|
else:
|
||||||
coffeescript = None
|
coffeescript = None
|
||||||
""" Probably fixed
|
|
||||||
if sys.platform.lower().startswith("darwin"):
|
|
||||||
# For some reasons openssl doesnt works on mac yet (https://github.com/HelloZeroNet/ZeroNet/issues/94)
|
|
||||||
use_openssl = False
|
|
||||||
else:
|
|
||||||
use_openssl = True
|
|
||||||
"""
|
|
||||||
use_openssl = True
|
use_openssl = True
|
||||||
|
|
||||||
# Create parser
|
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
||||||
parser.register('type', 'bool', self.strToBool)
|
|
||||||
subparsers = parser.add_subparsers(title="Action to perform", dest="action")
|
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
action = subparsers.add_parser("main", help='Start UiServer and FileServer (default)')
|
action = self.subparsers.add_parser("main", help='Start UiServer and FileServer (default)')
|
||||||
|
|
||||||
# SiteCreate
|
# SiteCreate
|
||||||
action = subparsers.add_parser("siteCreate", help='Create a new site')
|
action = self.subparsers.add_parser("siteCreate", help='Create a new site')
|
||||||
|
|
||||||
# SiteSign
|
# SiteSign
|
||||||
action = subparsers.add_parser("siteSign", help='Update and sign content.json: address [privatekey]')
|
action = self.subparsers.add_parser("siteSign", help='Update and sign content.json: address [privatekey]')
|
||||||
action.add_argument('address', help='Site to sign')
|
action.add_argument('address', help='Site to sign')
|
||||||
action.add_argument('privatekey', help='Private key (default: ask on execute)', nargs='?')
|
action.add_argument('privatekey', help='Private key (default: ask on execute)', nargs='?')
|
||||||
action.add_argument('--inner_path', help='File you want to sign (default: content.json)',
|
action.add_argument('--inner_path', help='File you want to sign (default: content.json)',
|
||||||
|
@ -58,7 +52,7 @@ class Config(object):
|
||||||
action.add_argument('--publish', help='Publish site after the signing', action='store_true')
|
action.add_argument('--publish', help='Publish site after the signing', action='store_true')
|
||||||
|
|
||||||
# SitePublish
|
# SitePublish
|
||||||
action = subparsers.add_parser("sitePublish", help='Publish site to other peers: address')
|
action = self.subparsers.add_parser("sitePublish", help='Publish site to other peers: address')
|
||||||
action.add_argument('address', help='Site to publish')
|
action.add_argument('address', help='Site to publish')
|
||||||
action.add_argument('peer_ip', help='Peer ip to publish (default: random peers ip from tracker)',
|
action.add_argument('peer_ip', help='Peer ip to publish (default: random peers ip from tracker)',
|
||||||
default=None, nargs='?')
|
default=None, nargs='?')
|
||||||
|
@ -68,76 +62,76 @@ class Config(object):
|
||||||
default="content.json", metavar="inner_path")
|
default="content.json", metavar="inner_path")
|
||||||
|
|
||||||
# SiteVerify
|
# SiteVerify
|
||||||
action = subparsers.add_parser("siteVerify", help='Verify site files using sha512: address')
|
action = self.subparsers.add_parser("siteVerify", help='Verify site files using sha512: address')
|
||||||
action.add_argument('address', help='Site to verify')
|
action.add_argument('address', help='Site to verify')
|
||||||
|
|
||||||
# dbRebuild
|
# dbRebuild
|
||||||
action = subparsers.add_parser("dbRebuild", help='Rebuild site database cache')
|
action = self.subparsers.add_parser("dbRebuild", help='Rebuild site database cache')
|
||||||
action.add_argument('address', help='Site to rebuild')
|
action.add_argument('address', help='Site to rebuild')
|
||||||
|
|
||||||
# dbQuery
|
# dbQuery
|
||||||
action = subparsers.add_parser("dbQuery", help='Query site sql cache')
|
action = self.subparsers.add_parser("dbQuery", help='Query site sql cache')
|
||||||
action.add_argument('address', help='Site to query')
|
action.add_argument('address', help='Site to query')
|
||||||
action.add_argument('query', help='Sql query')
|
action.add_argument('query', help='Sql query')
|
||||||
|
|
||||||
# PeerPing
|
# PeerPing
|
||||||
action = subparsers.add_parser("peerPing", help='Send Ping command to peer')
|
action = self.subparsers.add_parser("peerPing", help='Send Ping command to peer')
|
||||||
action.add_argument('peer_ip', help='Peer ip')
|
action.add_argument('peer_ip', help='Peer ip')
|
||||||
action.add_argument('peer_port', help='Peer port', nargs='?')
|
action.add_argument('peer_port', help='Peer port', nargs='?')
|
||||||
|
|
||||||
# PeerGetFile
|
# PeerGetFile
|
||||||
action = subparsers.add_parser("peerGetFile", help='Request and print a file content from peer')
|
action = self.subparsers.add_parser("peerGetFile", help='Request and print a file content from peer')
|
||||||
action.add_argument('peer_ip', help='Peer ip')
|
action.add_argument('peer_ip', help='Peer ip')
|
||||||
action.add_argument('peer_port', help='Peer port')
|
action.add_argument('peer_port', help='Peer port')
|
||||||
action.add_argument('site', help='Site address')
|
action.add_argument('site', help='Site address')
|
||||||
action.add_argument('filename', help='File name to request')
|
action.add_argument('filename', help='File name to request')
|
||||||
|
|
||||||
# PeerGetFile
|
# PeerGetFile
|
||||||
action = subparsers.add_parser("peerCmd", help='Request and print a file content from peer')
|
action = self.subparsers.add_parser("peerCmd", help='Request and print a file content from peer')
|
||||||
action.add_argument('peer_ip', help='Peer ip')
|
action.add_argument('peer_ip', help='Peer ip')
|
||||||
action.add_argument('peer_port', help='Peer port')
|
action.add_argument('peer_port', help='Peer port')
|
||||||
action.add_argument('cmd', help='Command to execute')
|
action.add_argument('cmd', help='Command to execute')
|
||||||
action.add_argument('parameters', help='Parameters to command', nargs='?')
|
action.add_argument('parameters', help='Parameters to command', nargs='?')
|
||||||
|
|
||||||
# CryptSign
|
# CryptSign
|
||||||
action = subparsers.add_parser("cryptSign", help='Sign message using Bitcoin private key')
|
action = self.subparsers.add_parser("cryptSign", help='Sign message using Bitcoin private key')
|
||||||
action.add_argument('message', help='Message to sign')
|
action.add_argument('message', help='Message to sign')
|
||||||
action.add_argument('privatekey', help='Private key')
|
action.add_argument('privatekey', help='Private key')
|
||||||
|
|
||||||
# Config parameters
|
# Config parameters
|
||||||
parser.add_argument('--debug', help='Debug mode', action='store_true')
|
self.parser.add_argument('--debug', help='Debug mode', action='store_true')
|
||||||
parser.add_argument('--debug_socket', help='Debug socket connections', action='store_true')
|
self.parser.add_argument('--debug_socket', help='Debug socket connections', action='store_true')
|
||||||
|
|
||||||
parser.add_argument('--config_file', help='Path of config file', default="zeronet.conf", metavar="path")
|
self.parser.add_argument('--config_file', help='Path of config file', default="zeronet.conf", metavar="path")
|
||||||
parser.add_argument('--data_dir', help='Path of data directory', default="data", metavar="path")
|
self.parser.add_argument('--data_dir', help='Path of data directory', default="data", metavar="path")
|
||||||
parser.add_argument('--log_dir', help='Path of logging directory', default="log", metavar="path")
|
self.parser.add_argument('--log_dir', help='Path of logging directory', default="log", metavar="path")
|
||||||
|
|
||||||
parser.add_argument('--ui_ip', help='Web interface bind address', default="127.0.0.1", metavar='ip')
|
self.parser.add_argument('--ui_ip', help='Web interface bind address', default="127.0.0.1", metavar='ip')
|
||||||
parser.add_argument('--ui_port', help='Web interface bind port', default=43110, type=int, metavar='port')
|
self.parser.add_argument('--ui_port', help='Web interface bind port', default=43110, type=int, metavar='port')
|
||||||
parser.add_argument('--ui_restrict', help='Restrict web access', default=False, metavar='ip', nargs='*')
|
self.parser.add_argument('--ui_restrict', help='Restrict web access', default=False, metavar='ip', nargs='*')
|
||||||
parser.add_argument('--open_browser', help='Open homepage in web browser automatically',
|
self.parser.add_argument('--open_browser', help='Open homepage in web browser automatically',
|
||||||
nargs='?', const="default_browser", metavar='browser_name')
|
nargs='?', const="default_browser", metavar='browser_name')
|
||||||
parser.add_argument('--homepage', help='Web interface Homepage', default='1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr',
|
self.parser.add_argument('--homepage', help='Web interface Homepage', default='1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr',
|
||||||
metavar='address')
|
metavar='address')
|
||||||
parser.add_argument('--size_limit', help='Default site size limit in MB', default=10, metavar='size')
|
self.parser.add_argument('--size_limit', help='Default site size limit in MB', default=10, metavar='size')
|
||||||
|
|
||||||
parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip')
|
self.parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip')
|
||||||
parser.add_argument('--fileserver_port', help='FileServer bind port', default=15441, type=int, metavar='port')
|
self.parser.add_argument('--fileserver_port', help='FileServer bind port', default=15441, type=int, metavar='port')
|
||||||
parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true')
|
self.parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true')
|
||||||
parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
|
self.parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
|
||||||
parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip')
|
self.parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip')
|
||||||
parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup',
|
self.parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup',
|
||||||
type='bool', choices=[True, False], default=use_openssl)
|
type='bool', choices=[True, False], default=use_openssl)
|
||||||
parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true')
|
self.parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true')
|
||||||
parser.add_argument('--disable_sslcompression', help='Disable SSL compression to save memory',
|
self.parser.add_argument('--disable_sslcompression', help='Disable SSL compression to save memory',
|
||||||
type='bool', choices=[True, False], default=True)
|
type='bool', choices=[True, False], default=True)
|
||||||
|
|
||||||
parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript,
|
self.parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript,
|
||||||
metavar='executable_path')
|
metavar='executable_path')
|
||||||
|
|
||||||
parser.add_argument('--version', action='version', version='ZeroNet %s r%s' % (self.version, self.rev))
|
self.parser.add_argument('--version', action='version', version='ZeroNet %s r%s' % (self.version, self.rev))
|
||||||
|
|
||||||
return parser
|
return self.parser
|
||||||
|
|
||||||
# Find arguments specificed for current action
|
# Find arguments specificed for current action
|
||||||
def getActionArguments(self):
|
def getActionArguments(self):
|
||||||
|
@ -147,23 +141,78 @@ class Config(object):
|
||||||
back[argument.dest] = getattr(self, argument.dest)
|
back[argument.dest] = getattr(self, argument.dest)
|
||||||
return back
|
return back
|
||||||
|
|
||||||
# Try to find action from sys.argv
|
# Try to find action from argv
|
||||||
def getAction(self, argv):
|
def getAction(self, argv):
|
||||||
actions = [action.choices.keys() for action in self.parser._actions if action.dest == "action"][0] # Valid actions
|
actions = [action.choices.keys() for action in self.parser._actions if action.dest == "action"][0] # Valid actions
|
||||||
found_action = False
|
found_action = False
|
||||||
for action in actions: # See if any in sys.argv
|
for action in actions: # See if any in argv
|
||||||
if action in argv:
|
if action in argv:
|
||||||
found_action = action
|
found_action = action
|
||||||
break
|
break
|
||||||
return found_action
|
return found_action
|
||||||
|
|
||||||
|
# Move plugin parameters to end of argument list
|
||||||
|
def moveUnknownToEnd(self, argv, default_action):
|
||||||
|
valid_actions = sum([action.option_strings for action in self.parser._actions], [])
|
||||||
|
valid_parameters = []
|
||||||
|
plugin_parameters = []
|
||||||
|
plugin = False
|
||||||
|
for arg in argv:
|
||||||
|
if arg.startswith("--"):
|
||||||
|
if arg not in valid_actions:
|
||||||
|
plugin = True
|
||||||
|
else:
|
||||||
|
plugin = False
|
||||||
|
elif arg == default_action:
|
||||||
|
plugin = False
|
||||||
|
|
||||||
|
if plugin:
|
||||||
|
plugin_parameters.append(arg)
|
||||||
|
else:
|
||||||
|
valid_parameters.append(arg)
|
||||||
|
return valid_parameters + plugin_parameters
|
||||||
|
|
||||||
|
# Parse arguments from config file and command line
|
||||||
|
def parse(self, silent=False, parse_config=True):
|
||||||
|
if silent: # Don't display messages or quit on unknown parameter
|
||||||
|
original_print_message = self.parser._print_message
|
||||||
|
original_exit = self.parser.exit
|
||||||
|
|
||||||
|
def silent(parser, function_name):
|
||||||
|
parser.exited = True
|
||||||
|
return None
|
||||||
|
self.parser.exited = False
|
||||||
|
self.parser._print_message = lambda *args, **kwargs: silent(self.parser, "_print_message")
|
||||||
|
self.parser.exit = lambda *args, **kwargs: silent(self.parser, "exit")
|
||||||
|
|
||||||
|
argv = self.argv[:] # Copy command line arguments
|
||||||
|
if parse_config:
|
||||||
|
argv = self.parseConfig(argv) # Add arguments from config file
|
||||||
|
self.parseCommandline(argv, silent) # Parse argv
|
||||||
|
self.setAttributes()
|
||||||
|
|
||||||
|
if silent: # Restore original functions
|
||||||
|
if self.parser.exited and self.action == "main": # Argument parsing halted, don't start ZeroNet with main action
|
||||||
|
self.action = None
|
||||||
|
self.parser._print_message = original_print_message
|
||||||
|
self.parser.exit = original_exit
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
def parseCommandline(self, argv):
|
def parseCommandline(self, argv, silent=False):
|
||||||
# Find out if action is specificed on start
|
# Find out if action is specificed on start
|
||||||
action = self.getAction(argv)
|
action = self.getAction(argv)
|
||||||
if len(argv) == 1 or not action: # If no action specificed set the main action
|
if not action:
|
||||||
argv.append("main")
|
argv.append("main")
|
||||||
self.arguments = self.parser.parse_args(argv[1:])
|
action = "main"
|
||||||
|
argv = self.moveUnknownToEnd(argv, action)
|
||||||
|
if silent:
|
||||||
|
res = self.parser.parse_known_args(argv[1:])
|
||||||
|
if res:
|
||||||
|
self.arguments = res[0]
|
||||||
|
else:
|
||||||
|
self.arguments = {}
|
||||||
|
else:
|
||||||
|
self.arguments = self.parser.parse_args(argv[1:])
|
||||||
|
|
||||||
# Parse config file
|
# Parse config file
|
||||||
def parseConfig(self, argv):
|
def parseConfig(self, argv):
|
||||||
|
@ -187,9 +236,24 @@ class Config(object):
|
||||||
# Expose arguments as class attributes
|
# Expose arguments as class attributes
|
||||||
def setAttributes(self):
|
def setAttributes(self):
|
||||||
# Set attributes from arguments
|
# Set attributes from arguments
|
||||||
args = vars(self.arguments)
|
if self.arguments:
|
||||||
for key, val in args.items():
|
args = vars(self.arguments)
|
||||||
setattr(self, key, val)
|
for key, val in args.items():
|
||||||
|
setattr(self, key, val)
|
||||||
|
|
||||||
|
def loadPlugins(self):
|
||||||
|
from Plugin import PluginManager
|
||||||
|
|
||||||
|
@PluginManager.acceptPlugins
|
||||||
|
class ConfigPlugin(object):
|
||||||
|
def __init__(self, config):
|
||||||
|
self.parser = config.parser
|
||||||
|
self.createArguments()
|
||||||
|
|
||||||
|
def createArguments(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
ConfigPlugin(self)
|
||||||
|
|
||||||
|
|
||||||
config = Config()
|
config = Config(sys.argv)
|
||||||
|
|
|
@ -207,7 +207,7 @@ class Site:
|
||||||
elif len(peers_try) < 5: # Backup peers, add to end of the try list
|
elif len(peers_try) < 5: # Backup peers, add to end of the try list
|
||||||
peers_try.append(peer)
|
peers_try.append(peer)
|
||||||
|
|
||||||
if since is not None: # No since definied, download from last modification time-1day
|
if since is None: # No since definied, download from last modification time-1day
|
||||||
since = self.settings.get("modified", 60 * 60 * 24) - 60 * 60 * 24
|
since = self.settings.get("modified", 60 * 60 * 24) - 60 * 60 * 24
|
||||||
self.log.debug("Try to get listModifications from peers: %s since: %s" % (peers_try, since))
|
self.log.debug("Try to get listModifications from peers: %s since: %s" % (peers_try, since))
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import time
|
||||||
sys.path.append(os.path.abspath("src")) # Imports relative to src dir
|
sys.path.append(os.path.abspath("src")) # Imports relative to src dir
|
||||||
|
|
||||||
from Config import config
|
from Config import config
|
||||||
|
config.parse()
|
||||||
config.data_dir = "src/Test/testdata" # Use test data for unittests
|
config.data_dir = "src/Test/testdata" # Use test data for unittests
|
||||||
|
|
||||||
from Crypt import CryptBitcoin
|
from Crypt import CryptBitcoin
|
||||||
|
@ -382,13 +383,16 @@ class TestCase(unittest.TestCase):
|
||||||
1458664252141532163166741013621928587528255888800826689784628722366466547364755811L
|
1458664252141532163166741013621928587528255888800826689784628722366466547364755811L
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Re-generate privatekey based on address_index
|
||||||
address, address_index, site_data = user.getNewSiteData()
|
address, address_index, site_data = user.getNewSiteData()
|
||||||
self.assertEqual(CryptBitcoin.hdPrivatekey(user.master_seed, address_index), site_data["privatekey"]) # Re-generate privatekey based on address_index
|
self.assertEqual(CryptBitcoin.hdPrivatekey(user.master_seed, address_index), site_data["privatekey"])
|
||||||
|
|
||||||
user.sites = {} # Reset user data
|
user.sites = {} # Reset user data
|
||||||
|
|
||||||
self.assertNotEqual(user.getSiteData(address)["auth_address"], address) # Site address and auth address is different
|
# Site address and auth address is different
|
||||||
self.assertEqual(user.getSiteData(address)["auth_privatekey"], site_data["auth_privatekey"]) # Re-generate auth_privatekey for site
|
self.assertNotEqual(user.getSiteData(address)["auth_address"], address)
|
||||||
|
# Re-generate auth_privatekey for site
|
||||||
|
self.assertEqual(user.getSiteData(address)["auth_privatekey"], site_data["auth_privatekey"])
|
||||||
|
|
||||||
def testSslCert(self):
|
def testSslCert(self):
|
||||||
from Crypt import CryptConnection
|
from Crypt import CryptConnection
|
||||||
|
@ -409,8 +413,29 @@ class TestCase(unittest.TestCase):
|
||||||
os.unlink("%s/cert-rsa.pem" % config.data_dir)
|
os.unlink("%s/cert-rsa.pem" % config.data_dir)
|
||||||
os.unlink("%s/key-rsa.pem" % config.data_dir)
|
os.unlink("%s/key-rsa.pem" % config.data_dir)
|
||||||
|
|
||||||
|
def testConfigParse(self):
|
||||||
|
import Config
|
||||||
|
config_test = Config.Config("zeronet.py".split(" "))
|
||||||
|
config_test.parse(silent=True, parse_config=False)
|
||||||
|
self.assertFalse(config_test.debug)
|
||||||
|
self.assertFalse(config_test.debug_socket)
|
||||||
|
|
||||||
|
config_test = Config.Config("zeronet.py --debug --debug_socket --ui_password hello".split(" "))
|
||||||
|
config_test.parse(silent=True, parse_config=False)
|
||||||
|
self.assertTrue(config_test.debug)
|
||||||
|
self.assertTrue(config_test.debug_socket)
|
||||||
|
|
||||||
|
args = "zeronet.py --unknown_arg --debug --debug_socket --ui_restrict 127.0.0.1 1.2.3.4 "
|
||||||
|
args += "--another_unknown argument --use_openssl False siteSign address privatekey --inner_path users/content.json"
|
||||||
|
config_test = Config.Config(args.split(" "))
|
||||||
|
config_test.parse(silent=True, parse_config=False)
|
||||||
|
self.assertTrue(config_test.debug)
|
||||||
|
self.assertIn("1.2.3.4", config_test.ui_restrict)
|
||||||
|
self.assertFalse(config_test.use_openssl)
|
||||||
|
self.assertEqual(config_test.inner_path, "users/content.json")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import logging
|
import logging
|
||||||
logging.getLogger().setLevel(level=logging.CRITICAL)
|
logging.getLogger().setLevel(level=logging.CRITICAL)
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
# unittest.main(verbosity=2, defaultTest="TestCase.testUserContentCert")
|
# unittest.main(verbosity=2, defaultTest="TestCase.testConfigParse")
|
||||||
|
|
|
@ -29,19 +29,15 @@ class UiRequest(object):
|
||||||
self.log = server.log
|
self.log = server.log
|
||||||
self.get = get # Get parameters
|
self.get = get # Get parameters
|
||||||
self.env = env # Enviroment settings
|
self.env = env # Enviroment settings
|
||||||
|
# ['CONTENT_LENGTH', 'CONTENT_TYPE', 'GATEWAY_INTERFACE', 'HTTP_ACCEPT', 'HTTP_ACCEPT_ENCODING', 'HTTP_ACCEPT_LANGUAGE',
|
||||||
|
# 'HTTP_COOKIE', 'HTTP_CACHE_CONTROL', 'HTTP_HOST', 'HTTP_HTTPS', 'HTTP_ORIGIN', 'HTTP_PROXY_CONNECTION', 'HTTP_REFERER',
|
||||||
|
# 'HTTP_USER_AGENT', 'PATH_INFO', 'QUERY_STRING', 'REMOTE_ADDR', 'REMOTE_PORT', 'REQUEST_METHOD', 'SCRIPT_NAME',
|
||||||
|
# 'SERVER_NAME', 'SERVER_PORT', 'SERVER_PROTOCOL', 'SERVER_SOFTWARE', 'werkzeug.request', 'wsgi.errors',
|
||||||
|
# 'wsgi.input', 'wsgi.multiprocess', 'wsgi.multithread', 'wsgi.run_once', 'wsgi.url_scheme', 'wsgi.version']
|
||||||
|
|
||||||
self.start_response = start_response # Start response function
|
self.start_response = start_response # Start response function
|
||||||
self.user = None
|
self.user = None
|
||||||
|
|
||||||
# Return posted variables as dict
|
|
||||||
def getPosted(self):
|
|
||||||
if self.env['REQUEST_METHOD'] == "POST":
|
|
||||||
return dict(cgi.parse_qsl(
|
|
||||||
self.env['wsgi.input'].readline().decode()
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# Call the request handler function base on path
|
# Call the request handler function base on path
|
||||||
def route(self, path):
|
def route(self, path):
|
||||||
if config.ui_restrict and self.env['REMOTE_ADDR'] not in config.ui_restrict: # Restict Ui access by ip
|
if config.ui_restrict and self.env['REMOTE_ADDR'] not in config.ui_restrict: # Restict Ui access by ip
|
||||||
|
@ -69,7 +65,10 @@ class UiRequest(object):
|
||||||
return self.actionConsole()
|
return self.actionConsole()
|
||||||
# Site media wrapper
|
# Site media wrapper
|
||||||
else:
|
else:
|
||||||
body = self.actionWrapper(path)
|
if self.get.get("wrapper") == "False":
|
||||||
|
return self.actionSiteMedia("/media" + path) # Only serve html files with frame
|
||||||
|
else:
|
||||||
|
body = self.actionWrapper(path)
|
||||||
if body:
|
if body:
|
||||||
return body
|
return body
|
||||||
else:
|
else:
|
||||||
|
@ -96,7 +95,16 @@ class UiRequest(object):
|
||||||
content_type = "application/octet-stream"
|
content_type = "application/octet-stream"
|
||||||
return content_type
|
return content_type
|
||||||
|
|
||||||
# Returns: <dict> Cookies based on self.env
|
# Return: <dict> Posted variables
|
||||||
|
def getPosted(self):
|
||||||
|
if self.env['REQUEST_METHOD'] == "POST":
|
||||||
|
return dict(cgi.parse_qsl(
|
||||||
|
self.env['wsgi.input'].readline().decode()
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Return: <dict> Cookies based on self.env
|
||||||
def getCookies(self):
|
def getCookies(self):
|
||||||
raw_cookies = self.env.get('HTTP_COOKIE')
|
raw_cookies = self.env.get('HTTP_COOKIE')
|
||||||
if raw_cookies:
|
if raw_cookies:
|
||||||
|
@ -122,7 +130,8 @@ class UiRequest(object):
|
||||||
headers.append(("Access-Control-Allow-Origin", "*")) # Allow json access
|
headers.append(("Access-Control-Allow-Origin", "*")) # Allow json access
|
||||||
if self.env["REQUEST_METHOD"] == "OPTIONS":
|
if self.env["REQUEST_METHOD"] == "OPTIONS":
|
||||||
# Allow json access
|
# Allow json access
|
||||||
headers.append(("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"))
|
headers.append(("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Cookie"))
|
||||||
|
headers.append(("Access-Control-Allow-Credentials", "true"))
|
||||||
|
|
||||||
cacheable_type = (
|
cacheable_type = (
|
||||||
content_type == "text/css" or content_type.startswith("image") or
|
content_type == "text/css" or content_type.startswith("image") or
|
||||||
|
@ -157,8 +166,6 @@ class UiRequest(object):
|
||||||
def actionWrapper(self, path, extra_headers=None):
|
def actionWrapper(self, path, extra_headers=None):
|
||||||
if not extra_headers:
|
if not extra_headers:
|
||||||
extra_headers = []
|
extra_headers = []
|
||||||
if self.get.get("wrapper") == "False":
|
|
||||||
return self.actionSiteMedia("/media" + path) # Only serve html files with frame
|
|
||||||
|
|
||||||
match = re.match("/(?P<address>[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
|
match = re.match("/(?P<address>[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
|
||||||
if match:
|
if match:
|
||||||
|
@ -169,14 +176,6 @@ class UiRequest(object):
|
||||||
if self.env.get("HTTP_X_REQUESTED_WITH"):
|
if self.env.get("HTTP_X_REQUESTED_WITH"):
|
||||||
return self.error403("Ajax request not allowed to load wrapper") # No ajax allowed on wrapper
|
return self.error403("Ajax request not allowed to load wrapper") # No ajax allowed on wrapper
|
||||||
|
|
||||||
file_inner_path = inner_path
|
|
||||||
if not file_inner_path:
|
|
||||||
file_inner_path = "index.html" # If inner path defaults to index.html
|
|
||||||
|
|
||||||
if not inner_path and not path.endswith("/"):
|
|
||||||
inner_path = address + "/" # Fix relative resources loading if missing / end of site address
|
|
||||||
inner_path = re.sub(".*/(.+)", "\\1", inner_path) # Load innerframe relative to current url
|
|
||||||
|
|
||||||
site = SiteManager.site_manager.get(address)
|
site = SiteManager.site_manager.get(address)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -190,57 +189,72 @@ class UiRequest(object):
|
||||||
|
|
||||||
if not site:
|
if not site:
|
||||||
return False
|
return False
|
||||||
|
return self.renderWrapper(site, path, inner_path, title, extra_headers)
|
||||||
self.sendHeader(extra_headers=extra_headers[:])
|
|
||||||
|
|
||||||
# Wrapper variable inits
|
|
||||||
query_string = ""
|
|
||||||
body_style = ""
|
|
||||||
meta_tags = ""
|
|
||||||
|
|
||||||
if self.env.get("QUERY_STRING"):
|
|
||||||
query_string = "?" + self.env["QUERY_STRING"] + "&wrapper=False"
|
|
||||||
else:
|
|
||||||
query_string = "?wrapper=False"
|
|
||||||
|
|
||||||
if self.isProxyRequest(): # Its a remote proxy request
|
|
||||||
if self.env["REMOTE_ADDR"] == "127.0.0.1": # Local client, the server address also should be 127.0.0.1
|
|
||||||
server_url = "http://127.0.0.1:%s" % self.env["SERVER_PORT"]
|
|
||||||
else: # Remote client, use SERVER_NAME as server's real address
|
|
||||||
server_url = "http://%s:%s" % (self.env["SERVER_NAME"], self.env["SERVER_PORT"])
|
|
||||||
homepage = "http://zero/" + config.homepage
|
|
||||||
else: # Use relative path
|
|
||||||
server_url = ""
|
|
||||||
homepage = "/" + config.homepage
|
|
||||||
|
|
||||||
if site.content_manager.contents.get("content.json"): # Got content.json
|
|
||||||
content = site.content_manager.contents["content.json"]
|
|
||||||
if content.get("background-color"):
|
|
||||||
body_style += "background-color: %s;" % \
|
|
||||||
cgi.escape(site.content_manager.contents["content.json"]["background-color"], True)
|
|
||||||
if content.get("viewport"):
|
|
||||||
meta_tags += '<meta name="viewport" id="viewport" content="%s">' % cgi.escape(content["viewport"], True)
|
|
||||||
|
|
||||||
return self.render(
|
|
||||||
"src/Ui/template/wrapper.html",
|
|
||||||
server_url=server_url,
|
|
||||||
inner_path=inner_path,
|
|
||||||
file_inner_path=file_inner_path,
|
|
||||||
address=address,
|
|
||||||
title=title,
|
|
||||||
body_style=body_style,
|
|
||||||
meta_tags=meta_tags,
|
|
||||||
query_string=query_string,
|
|
||||||
wrapper_key=site.settings["wrapper_key"],
|
|
||||||
permissions=json.dumps(site.settings["permissions"]),
|
|
||||||
show_loadingscreen=json.dumps(not site.storage.isFile(file_inner_path)),
|
|
||||||
rev=config.rev,
|
|
||||||
homepage=homepage
|
|
||||||
)
|
|
||||||
|
|
||||||
else: # Bad url
|
else: # Bad url
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def renderWrapper(self, site, path, inner_path, title, extra_headers):
|
||||||
|
self.sendHeader(extra_headers=extra_headers[:])
|
||||||
|
|
||||||
|
file_inner_path = inner_path
|
||||||
|
if not file_inner_path:
|
||||||
|
file_inner_path = "index.html" # If inner path defaults to index.html
|
||||||
|
|
||||||
|
address = re.sub("/.*", "", path.lstrip("/"))
|
||||||
|
if self.isProxyRequest() and (not path or "/" in path[1:]):
|
||||||
|
file_url = re.sub(".*/", "", inner_path)
|
||||||
|
else:
|
||||||
|
file_url = "/" + address + "/" + inner_path
|
||||||
|
|
||||||
|
# Wrapper variable inits
|
||||||
|
query_string = ""
|
||||||
|
body_style = ""
|
||||||
|
meta_tags = ""
|
||||||
|
|
||||||
|
if self.env.get("QUERY_STRING"):
|
||||||
|
query_string = "?" + self.env["QUERY_STRING"] + "&wrapper=False"
|
||||||
|
else:
|
||||||
|
query_string = "?wrapper=False"
|
||||||
|
|
||||||
|
if self.isProxyRequest(): # Its a remote proxy request
|
||||||
|
if self.env["REMOTE_ADDR"] == "127.0.0.1": # Local client, the server address also should be 127.0.0.1
|
||||||
|
server_url = "http://127.0.0.1:%s" % self.env["SERVER_PORT"]
|
||||||
|
else: # Remote client, use SERVER_NAME as server's real address
|
||||||
|
server_url = "http://%s:%s" % (self.env["SERVER_NAME"], self.env["SERVER_PORT"])
|
||||||
|
homepage = "http://zero/" + config.homepage
|
||||||
|
else: # Use relative path
|
||||||
|
server_url = ""
|
||||||
|
homepage = "/" + config.homepage
|
||||||
|
|
||||||
|
if site.content_manager.contents.get("content.json"): # Got content.json
|
||||||
|
content = site.content_manager.contents["content.json"]
|
||||||
|
if content.get("background-color"):
|
||||||
|
body_style += "background-color: %s;" % \
|
||||||
|
cgi.escape(site.content_manager.contents["content.json"]["background-color"], True)
|
||||||
|
if content.get("viewport"):
|
||||||
|
meta_tags += '<meta name="viewport" id="viewport" content="%s">' % cgi.escape(content["viewport"], True)
|
||||||
|
|
||||||
|
yield self.render(
|
||||||
|
"src/Ui/template/wrapper.html",
|
||||||
|
server_url=server_url,
|
||||||
|
inner_path=inner_path,
|
||||||
|
file_url=file_url,
|
||||||
|
file_inner_path=file_inner_path,
|
||||||
|
address=site.address,
|
||||||
|
title=title,
|
||||||
|
body_style=body_style,
|
||||||
|
meta_tags=meta_tags,
|
||||||
|
query_string=query_string,
|
||||||
|
wrapper_key=site.settings["wrapper_key"],
|
||||||
|
permissions=json.dumps(site.settings["permissions"]),
|
||||||
|
show_loadingscreen=json.dumps(not site.storage.isFile(file_inner_path)),
|
||||||
|
rev=config.rev,
|
||||||
|
homepage=homepage
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Returns if media request allowed from that referer
|
# Returns if media request allowed from that referer
|
||||||
def isMediaRequestAllowed(self, site_address, referer):
|
def isMediaRequestAllowed(self, site_address, referer):
|
||||||
referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address
|
referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address
|
||||||
|
@ -351,7 +365,7 @@ class UiRequest(object):
|
||||||
if not user:
|
if not user:
|
||||||
self.log.error("No user found")
|
self.log.error("No user found")
|
||||||
return self.error403()
|
return self.error403()
|
||||||
ui_websocket = UiWebsocket(ws, site, self.server, user)
|
ui_websocket = UiWebsocket(ws, site, self.server, user, self)
|
||||||
site.websockets.append(ui_websocket) # Add to site websockets to allow notify on events
|
site.websockets.append(ui_websocket) # Add to site websockets to allow notify on events
|
||||||
ui_websocket.start()
|
ui_websocket.start()
|
||||||
for site_check in self.server.sites.values():
|
for site_check in self.server.sites.values():
|
||||||
|
|
|
@ -15,11 +15,12 @@ from Plugin import PluginManager
|
||||||
@PluginManager.acceptPlugins
|
@PluginManager.acceptPlugins
|
||||||
class UiWebsocket(object):
|
class UiWebsocket(object):
|
||||||
|
|
||||||
def __init__(self, ws, site, server, user):
|
def __init__(self, ws, site, server, user, request):
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
self.site = site
|
self.site = site
|
||||||
self.user = user
|
self.user = user
|
||||||
self.log = site.log
|
self.log = site.log
|
||||||
|
self.request = request
|
||||||
self.server = server
|
self.server = server
|
||||||
self.next_message_id = 1
|
self.next_message_id = 1
|
||||||
self.waiting_cb = {} # Waiting for callback. Key: message_id, Value: function pointer
|
self.waiting_cb = {} # Waiting for callback. Key: message_id, Value: function pointer
|
||||||
|
@ -101,21 +102,24 @@ class UiWebsocket(object):
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
self.log.debug("Websocket send error: %s" % Debug.formatException(err))
|
self.log.debug("Websocket send error: %s" % Debug.formatException(err))
|
||||||
|
|
||||||
|
def getPermissions(self, req_id):
|
||||||
|
permissions = self.site.settings["permissions"]
|
||||||
|
if req_id >= 1000000: # Its a wrapper command, allow admin commands
|
||||||
|
permissions = permissions[:]
|
||||||
|
permissions.append("ADMIN")
|
||||||
|
return permissions
|
||||||
|
|
||||||
# Handle incoming messages
|
# Handle incoming messages
|
||||||
def handleRequest(self, data):
|
def handleRequest(self, data):
|
||||||
req = json.loads(data)
|
req = json.loads(data)
|
||||||
|
|
||||||
cmd = req.get("cmd")
|
cmd = req.get("cmd")
|
||||||
params = req.get("params")
|
params = req.get("params")
|
||||||
permissions = self.site.settings["permissions"]
|
permissions = self.getPermissions(req["id"])
|
||||||
if req["id"] >= 1000000: # Its a wrapper command, allow admin commands
|
|
||||||
permissions = permissions[:]
|
|
||||||
permissions.append("ADMIN")
|
|
||||||
|
|
||||||
admin_commands = (
|
admin_commands = (
|
||||||
"sitePause", "siteResume", "siteDelete", "siteList", "siteSetLimit", "siteClone",
|
"sitePause", "siteResume", "siteDelete", "siteList", "siteSetLimit", "siteClone",
|
||||||
"channelJoinAllsite",
|
"channelJoinAllsite", "serverUpdate", "certSet"
|
||||||
"serverUpdate", "certSet"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if cmd == "response": # It's a response to a command
|
if cmd == "response": # It's a response to a command
|
||||||
|
|
|
@ -46,7 +46,7 @@ if (window.self !== window.top) window.stop();
|
||||||
|
|
||||||
|
|
||||||
<!-- Site Iframe -->
|
<!-- Site Iframe -->
|
||||||
<iframe src='{inner_path}{query_string}' id='inner-iframe' sandbox="allow-forms allow-scripts allow-top-navigation allow-popups"></iframe>
|
<iframe src='{file_url}{query_string}' id='inner-iframe' sandbox="allow-forms allow-scripts allow-top-navigation allow-popups"></iframe>
|
||||||
|
|
||||||
<!-- Site info -->
|
<!-- Site info -->
|
||||||
<script>
|
<script>
|
||||||
|
|
20
src/main.py
20
src/main.py
|
@ -1,5 +1,4 @@
|
||||||
# Included modules
|
# Included modules
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
@ -14,6 +13,7 @@ update_after_shutdown = False # If set True then update and restart zeronet aft
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
from Config import config
|
from Config import config
|
||||||
|
config.parse(silent=True) # Plugins need to access the configuration
|
||||||
|
|
||||||
# Create necessary files and dirs
|
# Create necessary files and dirs
|
||||||
if not os.path.isdir(config.log_dir):
|
if not os.path.isdir(config.log_dir):
|
||||||
|
@ -25,9 +25,7 @@ if not os.path.isfile("%s/sites.json" % config.data_dir):
|
||||||
if not os.path.isfile("%s/users.json" % config.data_dir):
|
if not os.path.isfile("%s/users.json" % config.data_dir):
|
||||||
open("%s/users.json" % config.data_dir, "w").write("{}")
|
open("%s/users.json" % config.data_dir, "w").write("{}")
|
||||||
|
|
||||||
|
|
||||||
# Setup logging
|
# Setup logging
|
||||||
|
|
||||||
if config.action == "main":
|
if config.action == "main":
|
||||||
if os.path.isfile("%s/debug.log" % config.log_dir): # Simple logrotate
|
if os.path.isfile("%s/debug.log" % config.log_dir): # Simple logrotate
|
||||||
if os.path.isfile("%s/debug-last.log" % config.log_dir):
|
if os.path.isfile("%s/debug-last.log" % config.log_dir):
|
||||||
|
@ -58,6 +56,11 @@ else:
|
||||||
|
|
||||||
monkey.patch_all(thread=False) # Not thread: pyfilesystem and system tray icon not compatible
|
monkey.patch_all(thread=False) # Not thread: pyfilesystem and system tray icon not compatible
|
||||||
|
|
||||||
|
# Load plugins
|
||||||
|
from Plugin import PluginManager
|
||||||
|
PluginManager.plugin_manager.loadPlugins()
|
||||||
|
config.loadPlugins()
|
||||||
|
config.parse() # Parse again to add plugin configuration options
|
||||||
|
|
||||||
# Log current config
|
# Log current config
|
||||||
logging.debug("Config: %s" % config)
|
logging.debug("Config: %s" % config)
|
||||||
|
@ -72,17 +75,17 @@ if config.proxy:
|
||||||
SocksProxy.monkeyPath(*config.proxy.split(":"))
|
SocksProxy.monkeyPath(*config.proxy.split(":"))
|
||||||
|
|
||||||
|
|
||||||
# Load plugins
|
|
||||||
from Plugin import PluginManager
|
|
||||||
PluginManager.plugin_manager.loadPlugins()
|
|
||||||
|
|
||||||
|
|
||||||
# -- Actions --
|
# -- Actions --
|
||||||
|
|
||||||
@PluginManager.acceptPlugins
|
@PluginManager.acceptPlugins
|
||||||
class Actions(object):
|
class Actions(object):
|
||||||
# Default action: Start serving UiServer and FileServer
|
def call(self, function_name, kwargs):
|
||||||
|
func = getattr(self, function_name, None)
|
||||||
|
func(**kwargs)
|
||||||
|
|
||||||
|
# Default action: Start serving UiServer and FileServer
|
||||||
def main(self):
|
def main(self):
|
||||||
logging.info("Version: %s r%s, Python %s, Gevent: %s" % (config.version, config.rev, sys.version, gevent.__version__))
|
logging.info("Version: %s r%s, Python %s, Gevent: %s" % (config.version, config.rev, sys.version, gevent.__version__))
|
||||||
global ui_server, file_server
|
global ui_server, file_server
|
||||||
|
@ -295,6 +298,5 @@ actions = Actions()
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
# Call function
|
# Call function
|
||||||
func = getattr(actions, config.action, None)
|
|
||||||
action_kwargs = config.getActionArguments()
|
action_kwargs = config.getActionArguments()
|
||||||
func(**action_kwargs)
|
actions.call(config.action, action_kwargs)
|
||||||
|
|
Loading…
Reference in a new issue