diff --git a/plugins/disabled-UiPassword/UiPasswordPlugin.py b/plugins/disabled-UiPassword/UiPasswordPlugin.py new file mode 100644 index 00000000..c375fac8 --- /dev/null +++ b/plugins/disabled-UiPassword/UiPasswordPlugin.py @@ -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 "
"
+        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 = "" % session_id
+        self.cmd("notification", ["done", message])
diff --git a/plugins/disabled-UiPassword/__init__.py b/plugins/disabled-UiPassword/__init__.py
new file mode 100644
index 00000000..37350c3d
--- /dev/null
+++ b/plugins/disabled-UiPassword/__init__.py
@@ -0,0 +1 @@
+import UiPasswordPlugin
\ No newline at end of file
diff --git a/plugins/disabled-UiPassword/login.html b/plugins/disabled-UiPassword/login.html
new file mode 100644
index 00000000..8880a7a1
--- /dev/null
+++ b/plugins/disabled-UiPassword/login.html
@@ -0,0 +1,116 @@
+
+
+ Log In
+ 
+
+
+
+
+
+
+
+
+
+ + + +
+ Log In + +
+
+ + + + + + \ No newline at end of file diff --git a/plugins/disabled-Zeroname-local/domainLookup.py b/plugins/disabled-Zeroname-local/domainLookup.py index ae0c56bc..5bed7438 100644 --- a/plugins/disabled-Zeroname-local/domainLookup.py +++ b/plugins/disabled-Zeroname-local/domainLookup.py @@ -5,32 +5,32 @@ import time, json, os, sys, re, socket, json # Supports subdomains and .bit on the end def lookupDomain(domain): domain = domain.lower() - + #remove .bit on end if domain[-4:] == ".bit": domain = domain[0:-4] - + #check for subdomain if domain.find(".") != -1: subdomain = domain[0:domain.find(".")] domain = domain[domain.find(".")+1:] else: subdomain = "" - + try: domain_object = rpc.name_show("d/"+domain) except: #domain doesn't exist return None - + domain_json = json.loads(domain_object['value']) - + try: domain_address = domain_json["zeronet"][subdomain] except: #domain exists but doesn't have any zeronet value return None - + return domain_address # Loading config... @@ -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 = 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)) -""" \ No newline at end of file diff --git a/src/Config.py b/src/Config.py index 8197644d..ac3958ff 100644 --- a/src/Config.py +++ b/src/Config.py @@ -6,14 +6,19 @@ import ConfigParser class Config(object): - def __init__(self): + def __init__(self, argv): self.version = "0.3.1" - self.rev = 281 - self.parser = self.createArguments() - argv = sys.argv[:] # Copy command line arguments - argv = self.parseConfig(argv) # Add arguments from config file - self.parseCommandline(argv) # Parse argv - self.setAttributes() + self.rev = 307 + self.argv = argv + self.action = None + self.createParser() + self.createArguments() + + 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): 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" else: 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 - # 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 - action = subparsers.add_parser("main", help='Start UiServer and FileServer (default)') + action = self.subparsers.add_parser("main", help='Start UiServer and FileServer (default)') # SiteCreate - action = subparsers.add_parser("siteCreate", help='Create a new site') + action = self.subparsers.add_parser("siteCreate", help='Create a new site') # 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('privatekey', help='Private key (default: ask on execute)', nargs='?') 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') # 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('peer_ip', help='Peer ip to publish (default: random peers ip from tracker)', default=None, nargs='?') @@ -68,76 +62,76 @@ class Config(object): default="content.json", metavar="inner_path") # 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') # 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') # 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('query', help='Sql query') # 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_port', help='Peer port', nargs='?') # 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_port', help='Peer port') action.add_argument('site', help='Site address') action.add_argument('filename', help='File name to request') # 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_port', help='Peer port') action.add_argument('cmd', help='Command to execute') action.add_argument('parameters', help='Parameters to command', nargs='?') # 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('privatekey', help='Private key') # Config parameters - 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', help='Debug mode', 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") - 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('--config_file', help='Path of config file', default="zeronet.conf", metavar="path") + self.parser.add_argument('--data_dir', help='Path of data directory', default="data", 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') - 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='*') - parser.add_argument('--open_browser', help='Open homepage in web browser automatically', - nargs='?', const="default_browser", metavar='browser_name') - parser.add_argument('--homepage', help='Web interface Homepage', default='1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr', - metavar='address') - parser.add_argument('--size_limit', help='Default site size limit in MB', default=10, metavar='size') + self.parser.add_argument('--ui_ip', help='Web interface bind address', default="127.0.0.1", metavar='ip') + self.parser.add_argument('--ui_port', help='Web interface bind port', default=43110, type=int, metavar='port') + self.parser.add_argument('--ui_restrict', help='Restrict web access', default=False, metavar='ip', nargs='*') + self.parser.add_argument('--open_browser', help='Open homepage in web browser automatically', + nargs='?', const="default_browser", metavar='browser_name') + self.parser.add_argument('--homepage', help='Web interface Homepage', default='1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr', + metavar='address') + 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') - 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') - 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') - parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup', - type='bool', choices=[True, False], default=use_openssl) - parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true') - parser.add_argument('--disable_sslcompression', help='Disable SSL compression to save memory', - type='bool', choices=[True, False], default=True) + self.parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip') + self.parser.add_argument('--fileserver_port', help='FileServer bind port', default=15441, type=int, metavar='port') + self.parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true') + self.parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port') + self.parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip') + self.parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup', + type='bool', choices=[True, False], default=use_openssl) + self.parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true') + self.parser.add_argument('--disable_sslcompression', help='Disable SSL compression to save memory', + type='bool', choices=[True, False], default=True) - parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript, - metavar='executable_path') + self.parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript, + 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 def getActionArguments(self): @@ -147,23 +141,78 @@ class Config(object): back[argument.dest] = getattr(self, argument.dest) return back - # Try to find action from sys.argv + # Try to find action from argv def getAction(self, argv): actions = [action.choices.keys() for action in self.parser._actions if action.dest == "action"][0] # Valid actions 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: found_action = action break 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 - def parseCommandline(self, argv): + def parseCommandline(self, argv, silent=False): # Find out if action is specificed on start 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") - 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 def parseConfig(self, argv): @@ -187,9 +236,24 @@ class Config(object): # Expose arguments as class attributes def setAttributes(self): # Set attributes from arguments - args = vars(self.arguments) - for key, val in args.items(): - setattr(self, key, val) + if self.arguments: + args = vars(self.arguments) + 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) diff --git a/src/Site/Site.py b/src/Site/Site.py index 3010dca3..774a6596 100644 --- a/src/Site/Site.py +++ b/src/Site/Site.py @@ -207,7 +207,7 @@ class Site: elif len(peers_try) < 5: # Backup peers, add to end of the try list 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 self.log.debug("Try to get listModifications from peers: %s since: %s" % (peers_try, since)) diff --git a/src/Test/test.py b/src/Test/test.py index 3ed1a1c2..383f6702 100644 --- a/src/Test/test.py +++ b/src/Test/test.py @@ -6,6 +6,7 @@ import time sys.path.append(os.path.abspath("src")) # Imports relative to src dir from Config import config +config.parse() config.data_dir = "src/Test/testdata" # Use test data for unittests from Crypt import CryptBitcoin @@ -382,13 +383,16 @@ class TestCase(unittest.TestCase): 1458664252141532163166741013621928587528255888800826689784628722366466547364755811L ) + # Re-generate privatekey based on address_index 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 - self.assertNotEqual(user.getSiteData(address)["auth_address"], address) # Site address and auth address is different - self.assertEqual(user.getSiteData(address)["auth_privatekey"], site_data["auth_privatekey"]) # Re-generate auth_privatekey for site + # Site address and auth address is different + 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): from Crypt import CryptConnection @@ -409,8 +413,29 @@ class TestCase(unittest.TestCase): os.unlink("%s/cert-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__": import logging logging.getLogger().setLevel(level=logging.CRITICAL) unittest.main(verbosity=2) - # unittest.main(verbosity=2, defaultTest="TestCase.testUserContentCert") + # unittest.main(verbosity=2, defaultTest="TestCase.testConfigParse") diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index be6abca5..f5517ee7 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -29,19 +29,15 @@ class UiRequest(object): self.log = server.log self.get = get # Get parameters 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.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 def route(self, path): 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() # Site media wrapper 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: return body else: @@ -96,7 +95,16 @@ class UiRequest(object): content_type = "application/octet-stream" return content_type - # Returns: Cookies based on self.env + # Return: 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: Cookies based on self.env def getCookies(self): raw_cookies = self.env.get('HTTP_COOKIE') if raw_cookies: @@ -122,10 +130,11 @@ class UiRequest(object): headers.append(("Access-Control-Allow-Origin", "*")) # Allow json access if self.env["REQUEST_METHOD"] == "OPTIONS": # 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 = ( - content_type == "text/css" or content_type.startswith("image") or + content_type == "text/css" or content_type.startswith("image") or self.env["REQUEST_METHOD"] == "OPTIONS" or content_type == "application/javascript" ) @@ -157,8 +166,6 @@ class UiRequest(object): def actionWrapper(self, path, extra_headers=None): if not 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
[A-Za-z0-9\._-]+)(?P/.*|$)", path) if match: @@ -169,14 +176,6 @@ class UiRequest(object): if self.env.get("HTTP_X_REQUESTED_WITH"): 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) if ( @@ -190,57 +189,72 @@ class UiRequest(object): if not site: return False - - 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 += '' % 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 - ) + return self.renderWrapper(site, path, inner_path, title, extra_headers) else: # Bad url 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 += '' % 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 def isMediaRequestAllowed(self, site_address, referer): referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address @@ -351,7 +365,7 @@ class UiRequest(object): if not user: self.log.error("No user found") 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 ui_websocket.start() for site_check in self.server.sites.values(): diff --git a/src/Ui/UiWebsocket.py b/src/Ui/UiWebsocket.py index 3fb7531d..b1b8f9e2 100644 --- a/src/Ui/UiWebsocket.py +++ b/src/Ui/UiWebsocket.py @@ -15,11 +15,12 @@ from Plugin import PluginManager @PluginManager.acceptPlugins class UiWebsocket(object): - def __init__(self, ws, site, server, user): + def __init__(self, ws, site, server, user, request): self.ws = ws self.site = site self.user = user self.log = site.log + self.request = request self.server = server self.next_message_id = 1 self.waiting_cb = {} # Waiting for callback. Key: message_id, Value: function pointer @@ -101,21 +102,24 @@ class UiWebsocket(object): except Exception, 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 def handleRequest(self, data): req = json.loads(data) cmd = req.get("cmd") params = req.get("params") - permissions = self.site.settings["permissions"] - if req["id"] >= 1000000: # Its a wrapper command, allow admin commands - permissions = permissions[:] - permissions.append("ADMIN") + permissions = self.getPermissions(req["id"]) admin_commands = ( "sitePause", "siteResume", "siteDelete", "siteList", "siteSetLimit", "siteClone", - "channelJoinAllsite", - "serverUpdate", "certSet" + "channelJoinAllsite", "serverUpdate", "certSet" ) if cmd == "response": # It's a response to a command diff --git a/src/Ui/template/wrapper.html b/src/Ui/template/wrapper.html index dcbc5e31..5718a448 100644 --- a/src/Ui/template/wrapper.html +++ b/src/Ui/template/wrapper.html @@ -13,7 +13,7 @@ @@ -46,7 +46,7 @@ if (window.self !== window.top) window.stop(); - +