diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index b0c7caabb..3bd34d552 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -1,6 +1,7 @@ import time import re import os +import sys import mimetypes import json import cgi @@ -40,6 +41,8 @@ def __init__(self, server, get, env, start_response): self.start_response = start_response # Start response function self.user = None + self.FS_ENCODING = sys.getfilesystemencoding() + # 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 @@ -80,7 +83,7 @@ def route(self, path): # Site media wrapper else: if self.get.get("wrapper_nonce"): - return self.actionSiteMedia("/media" + path) # Only serve html files with frame + return self.actionSiteMedia("/media" + path.decode('utf-8')) # Only serve html files with frame else: body = self.actionWrapper(path) if body: @@ -182,8 +185,14 @@ def render(self, template_path, *args, **kwargs): # Redirect to an url def actionRedirect(self, url): - self.start_response('301 Redirect', [('Location', url)]) - yield "Location changed: %s" % url + try: + ascii_url = str(url) + self.start_response('301 Redirect', [('Location', ascii_url)]) + yield "Location changed: %s" % cgi.escape(ascii_url) + except UnicodeEncodeError: + self.start_response('500 Server Error', []) + yield "URL ASCII encoding error." + def actionIndex(self): return self.actionRedirect("/" + config.homepage) @@ -193,6 +202,7 @@ def actionWrapper(self, path, extra_headers=None): if not extra_headers: extra_headers = [] + path = path.decode('utf-8') match = re.match("/(?P
[A-Za-z0-9\._-]+)(?P/.*|$)", path) if match: address = match.group("address") @@ -324,6 +334,8 @@ def isMediaRequestAllowed(self, site_address, referer): # Return {address: 1Site.., inner_path: /data/users.json} from url path def parsePath(self, path): + if not isinstance(path, unicode): + path = path.decode('utf-8') path = path.replace("/index.html/", "/") # Base Backward compatibility fix if path.endswith("/"): path = path + "index.html" @@ -336,6 +348,13 @@ def parsePath(self, path): else: return None + def fixFsEncoding(self, path): + if isinstance(path, unicode): + return path + elif isinstance(path, str): + return path.decode(self.FS_ENCODING) + else: + raise Exception("Not a string!") # Serve a media for site def actionSiteMedia(self, path, header_length=True): @@ -346,9 +365,10 @@ def actionSiteMedia(self, path, header_length=True): if "htm" in content_type: # Valid nonce must present to render html files wrapper_nonce = self.get.get("wrapper_nonce") if wrapper_nonce not in self.server.wrapper_nonces: - return self.error403("Wrapper nonce error. Please reload the page.") + return self.error403("Wrapper nonce error. Please reload this page.") self.server.wrapper_nonces.remove(self.get["wrapper_nonce"]) + # Check referrer referer = self.env.get("HTTP_REFERER") if referer and path_parts: # Only allow same site to receive media if not self.isMediaRequestAllowed(path_parts["request_address"], referer): @@ -357,9 +377,15 @@ def actionSiteMedia(self, path, header_length=True): if path_parts: # Looks like a valid path address = path_parts["address"] - file_path = "%s/%s/%s" % (config.data_dir, address, path_parts["inner_path"]) - allowed_dir = os.path.abspath("%s/%s" % (config.data_dir, address)) # Only files within data/sitehash allowed - data_dir = os.path.abspath(config.data_dir) # No files from data/ allowed + file_path = u"%s/%s/%s" % (config.data_dir, address, path_parts["inner_path"]) + allowed_dir = os.path.abspath(u"%s/%s" % (config.data_dir, address)) # Only files within data/sitehash allowed + data_dir = self.fixFsEncoding(os.path.abspath(config.data_dir)) # No files from data/ allowed + + self.log.debug("------ repr file_path = " + repr(file_path)) + self.log.debug("------ repr allowed_dir = " + repr(allowed_dir)) + self.log.debug("------ repr data_dir = " + repr(data_dir)) + self.log.debug("------ path_parts = " + repr(path_parts)) + if ( ".." in file_path or not os.path.dirname(os.path.abspath(file_path)).startswith(allowed_dir) or @@ -401,10 +427,10 @@ def actionSiteMedia(self, path, header_length=True): # Serve a media for ui def actionUiMedia(self, path): - match = re.match("/uimedia/(?P.*)", path) + match = re.match("/uimedia/(?P.*)", path.decode('utf-8')) if match: # Looks like a valid path - file_path = "src/Ui/media/%s" % match.group("inner_path") - allowed_dir = os.path.abspath("src/Ui/media") # Only files within data/sitehash allowed + file_path = u"src/Ui/media/%s" % match.group("inner_path") + allowed_dir = os.path.abspath(u"src/Ui/media") # Only files within data/sitehash allowed if ".." in file_path or not os.path.dirname(os.path.abspath(file_path)).startswith(allowed_dir): # File not in allowed path return self.error403() @@ -539,7 +565,16 @@ def error403(self, message="", details=True): # Send file not found error def error404(self, path=""): self.sendHeader(404) - return self.formatError("Not Found", cgi.escape(path.encode("utf8")), details=False) + try: + if isinstance(path, unicode): + encoded_path = path.encode("utf-8") + else: + encoded_path = path + except Exception, e: + self.log.info("Encoding or decoding error") + encoded_path = repr(path).encode("utf-8") + + return self.formatError("Not Found", cgi.escape(encoded_path), details=False) # Internal server error def error500(self, message=":("):