diff --git a/plugins/Multiuser/MultiuserPlugin.py b/plugins/Multiuser/MultiuserPlugin.py new file mode 100644 index 00000000..850a0995 --- /dev/null +++ b/plugins/Multiuser/MultiuserPlugin.py @@ -0,0 +1,169 @@ +import re, time, sys +from Plugin import PluginManager +from Crypt import CryptBitcoin + +@PluginManager.registerTo("UiRequest") +class UiRequestPlugin(object): + def __init__(self, *args, **kwargs): + self.user_manager = sys.modules["User.UserManager"].user_manager + super(UiRequestPlugin, self).__init__(*args, **kwargs) + + + # Create new user and inject user welcome message if necessary + # Return: Html body also containing the injection + def actionWrapper(self, path): + user_created = False + user = self.getCurrentUser() # Get user from cookie + + if not user: # No user found by cookie + user = self.user_manager.create() + user_created = True + + master_address = user.master_address + master_seed = user.master_seed + + if user_created: + extra_headers = [('Set-Cookie', "master_address=%s;path=/;max-age=2592000;" % user.master_address)] # Max age = 30 days + else: + extra_headers = [] + + loggedin = self.get.get("login") == "done" + + back = super(UiRequestPlugin, self).actionWrapper(path, extra_headers) # Get the wrapper frame output + + if not user_created and not loggedin: return back # No injection necessary + + if not back or not hasattr(back, "endswith"): return back # Wrapper error or not string returned, injection not possible + + if user_created: + # Inject the welcome message + inject_html = """ + + + + + + """.replace("\t", "") + inject_html = inject_html.replace("{master_seed}", master_seed) # Set the master seed in the message + + back = re.sub("\s*\s*$", inject_html, back) # Replace the tags with the injection + + elif loggedin: + inject_html = """ + + + + + """.replace("\t", "") + back = re.sub("\s*\s*$", inject_html, back) # Replace the tags with the injection + + return back + + + # Get the current user based on request's cookies + # Return: User object or None if no match + def getCurrentUser(self): + cookies = self.getCookies() + user_manager = self.user_manager + user = None + if "master_address" in cookies: + users = self.user_manager.list() + user = users.get(cookies["master_address"]) + return user + + +@PluginManager.registerTo("UserManager") +class UserManagerPlugin(object): + # In multiuser mode do not load the users + def load(self): + if not self.users: self.users = {} + return self.users + + + # Find user by master address + # Return: User or None + def get(self, master_address=None): + users = self.list() + if master_address in users: + user = users[master_address] + else: + user = None + return user + + +@PluginManager.registerTo("User") +class UserPlugin(object): + # In multiuser mode users data only exits in memory, dont write to data/user.json + def save(self): + return False + + +@PluginManager.registerTo("UiWebsocket") +class UiWebsocketPlugin(object): + # Let the page know we running in multiuser mode + def formatServerInfo(self): + server_info = super(UiWebsocketPlugin, self).formatServerInfo() + server_info["multiuser"] = True + if "ADMIN" in self.site.settings["permissions"]: + server_info["master_address"] = self.user.master_address + return server_info + + + # Show current user's master seed + def actionUserShowMasterSeed(self, to): + if not "ADMIN" in self.site.settings["permissions"]: return self.response(to, "Show master seed not allowed") + message = "Your unique private key:" + message+= "
%s
" % self.user.master_seed + message+= "(Save it, you can access your account using this information)" + self.cmd("notification", ["info", message]) + + + # Logout user + def actionUserLogout(self, to): + if not "ADMIN" in self.site.settings["permissions"]: return self.response(to, "Logout not allowed") + message = "You have been logged out. Login to another account" + message+= "" + self.cmd("notification", ["done", message, 1000000]) # 1000000 = Show ~forever :) + # Delete from user_manager + user_manager = sys.modules["User.UserManager"].user_manager + if self.user.master_address in user_manager.users: + del user_manager.users[self.user.master_address] + self.response(to, "Successful logout") + else: + self.response(to, "User not found") + + + # Show login form + def actionUserLoginForm(self, to): + self.cmd("prompt", ["Login
Your private key:", "password", "Login"], self.responseUserLogin) + + + # Login form submit + def responseUserLogin(self, master_seed): + user_manager = sys.modules["User.UserManager"].user_manager + user = user_manager.create(master_seed=master_seed) + if user.master_address: + message = "Successfull login, reloading page..." + message+= "" % user.master_address + message+= "" + self.cmd("notification", ["done", message]) + else: + self.cmd("notification", ["error", "Error: Invalid master seed"]) + self.actionUserLoginForm(0) + diff --git a/plugins/Multiuser/__init__.py b/plugins/Multiuser/__init__.py new file mode 100644 index 00000000..154d6008 --- /dev/null +++ b/plugins/Multiuser/__init__.py @@ -0,0 +1 @@ +import MultiuserPlugin diff --git a/plugins/Stats/StatsPlugin.py b/plugins/Stats/StatsPlugin.py index a3df14e6..195bcc53 100644 --- a/plugins/Stats/StatsPlugin.py +++ b/plugins/Stats/StatsPlugin.py @@ -131,7 +131,7 @@ class UiRequestPlugin(object): yield "

Objects in memory (total: %s, %.2fkb):
" % (len(obj_count), sum([stat[1] for stat in obj_count.values()])) for obj, stat in sorted(obj_count.items(), key=lambda x: x[1][0], reverse=True): # Sorted by count - yield " - %.1fkb = %s x %s
" % (stat[1], stat[0], cgi.escape(obj)) + yield " - %.1fkb = %s x %s
" % (stat[1], stat[0], obj, cgi.escape(obj)) from greenlet import greenlet @@ -189,6 +189,49 @@ class UiRequestPlugin(object): yield "Done in %.1f" % (time.time()-s) + def actionListobj(self): + import gc, sys + + self.sendHeader() + type_filter = self.get.get("type") + + yield """ + + """ + + yield "Listing all %s objects in memory...
" % cgi.escape(type_filter) + + ref_count = {} + objs = gc.get_objects() + for obj in objs: + obj_type = str(type(obj)) + if obj_type != type_filter: continue + refs = [ref for ref in gc.get_referrers(obj) if hasattr(ref, "__class__") and ref.__class__.__name__ not in ["list", "dict", "function", "type", "frame", "WeakSet", "tuple"]] + if not refs: continue + yield "%.1fkb %s... " % (float(sys.getsizeof(obj))/1024, cgi.escape(str(obj)[0:100].ljust(100)) ) + for ref in refs: + yield " [" + if "object at" in str(ref) or len(str(ref)) > 100: + yield str(ref.__class__.__name__) + else: + yield str(ref.__class__.__name__)+":"+cgi.escape(str(ref)) + yield "] " + ref_type = ref.__class__.__name__ + if ref_type not in ref_count: + ref_count[ref_type] = [0,0] + ref_count[ref_type][0] += 1 # Count + ref_count[ref_type][1] += float(sys.getsizeof(obj))/1024 # Size + yield "
" + + yield "
Object referrer (total: %s, %.2fkb):
" % (len(ref_count), sum([stat[1] for stat in ref_count.values()])) + + for obj, stat in sorted(ref_count.items(), key=lambda x: x[1][0], reverse=True)[0:30]: # Sorted by count + yield " - %.1fkb = %s x %s
" % (stat[1], stat[0], cgi.escape(str(obj)) ) + + def actionBenchmark(self): import sys from contextlib import contextmanager diff --git a/plugins/Zeroname/UiRequestPlugin.py b/plugins/Zeroname/UiRequestPlugin.py index 91eea2a2..9be5f62d 100644 --- a/plugins/Zeroname/UiRequestPlugin.py +++ b/plugins/Zeroname/UiRequestPlugin.py @@ -23,6 +23,7 @@ class UiRequestPlugin(object): # Is mediarequest allowed from that 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("\?.*", "", referer_path) # Remove html params referer_site_address = re.match("/(?P
[A-Za-z0-9\.]+)(?P/.*|$)", referer_path).group("address") if referer_site_address == site_address: # Referer site address as simple address diff --git a/plugins/Zeroname/updater/zeroname_updater.py b/plugins/Zeroname/updater/zeroname_updater.py index c1247305..d3ed7f6c 100644 --- a/plugins/Zeroname/updater/zeroname_updater.py +++ b/plugins/Zeroname/updater/zeroname_updater.py @@ -37,7 +37,7 @@ def processNameOp(domain, value): else: names["%s.bit" % domain] = address - new_names_raw = json.dumps(names, indent=2) + new_names_raw = json.dumps(names, indent=2, sort_keys=True) if new_names_raw != names_raw: open(names_path, "wb").write(new_names_raw) return True diff --git a/src/Config.py b/src/Config.py index 8427f585..b5edd501 100644 --- a/src/Config.py +++ b/src/Config.py @@ -4,7 +4,7 @@ import ConfigParser class Config(object): def __init__(self): self.version = "0.2.9" - self.rev = 102 + self.rev = 106 self.parser = self.createArguments() argv = sys.argv[:] # Copy command line arguments argv = self.parseConfig(argv) # Add arguments from config file @@ -80,7 +80,7 @@ class Config(object): 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') + 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_limit') diff --git a/src/Debug/DebugHook.py b/src/Debug/DebugHook.py index 2c8146d6..858847ac 100644 --- a/src/Debug/DebugHook.py +++ b/src/Debug/DebugHook.py @@ -36,3 +36,20 @@ else: gevent.Greenlet = gevent.greenlet.Greenlet = ErrorhookedGreenlet reload(gevent) + +if __name__ == "__main__": + import time + from gevent import monkey; monkey.patch_all(thread=False, ssl=False) + import Debug + def sleeper(): + print "started" + time.sleep(3) + print "stopped" + thread1 = gevent.spawn(sleeper) + thread2 = gevent.spawn(sleeper) + time.sleep(1) + print "killing..." + thread1.throw(Exception("Hello")) + thread2.throw(Debug.Notify("Throw")) + print "killed" + diff --git a/src/Peer/Peer.py b/src/Peer/Peer.py index 4f7259b1..03452684 100644 --- a/src/Peer/Peer.py +++ b/src/Peer/Peer.py @@ -10,7 +10,6 @@ class Peer: self.port = port self.site = site self.key = "%s:%s" % (ip, port) - self.log = None self.connection_server = sys.modules["main"].file_server self.connection = None @@ -25,14 +24,20 @@ class Peer: self.download_time = 0 # Time spent to download + def log(self, text): + if self.site: + self.site.log.debug("%s:%s %s" % (self.ip, self.port, text)) + else: + logging.debug("%s:%s %s" % (self.ip, self.port, text)) + + # Connect to host def connect(self, connection = None): - if not self.log: self.log = logging.getLogger("Peer:%s:%s %s" % (self.ip, self.port, self.site)) if self.connection: - self.log.debug("Getting connection (Closing %s)..." % self.connection) + self.log("Getting connection (Closing %s)..." % self.connection) self.connection.close() else: - self.log.debug("Getting connection...") + self.log("Getting connection...") if connection: # Connection specificed self.connection = connection @@ -43,7 +48,7 @@ class Peer: self.connection = self.connection_server.getConnection(self.ip, self.port) except Exception, err: self.onConnectionError() - self.log.debug("Getting connection error: %s (connection_error: %s, hash_failed: %s)" % (Debug.formatException(err), self.connection_error, self.hash_failed)) + self.log("Getting connection error: %s (connection_error: %s, hash_failed: %s)" % (Debug.formatException(err), self.connection_error, self.hash_failed)) self.connection = None def __str__(self): @@ -85,7 +90,7 @@ class Peer: if not response: raise Exception("Send error") #if config.debug_socket: self.log.debug("Got response to: %s" % cmd) if "error" in response: - self.log.debug("%s error: %s" % (cmd, response["error"])) + self.log("%s error: %s" % (cmd, response["error"])) self.onConnectionError() else: # Successful request, reset connection error num self.connection_error = 0 @@ -93,11 +98,11 @@ class Peer: return response except Exception, err: if type(err).__name__ == "Notify": # Greenlet kill by worker - self.log.debug("Peer worker got killed: %s, aborting cmd: %s" % (err.message, cmd)) + self.log("Peer worker got killed: %s, aborting cmd: %s" % (err.message, cmd)) break else: self.onConnectionError() - self.log.debug("%s (connection_error: %s, hash_failed: %s, retry: %s)" % (Debug.formatException(err), self.connection_error, self.hash_failed, retry)) + self.log("%s (connection_error: %s, hash_failed: %s, retry: %s)" % (Debug.formatException(err), self.connection_error, self.hash_failed, retry)) time.sleep(1*retry) self.connect() return None # Failed after 4 retry @@ -141,9 +146,9 @@ class Peer: time.sleep(1) if response_time: - self.log.debug("Ping: %.3f" % response_time) + self.log("Ping: %.3f" % response_time) else: - self.log.debug("Ping failed") + self.log("Ping failed") self.last_ping = response_time return response_time @@ -160,13 +165,13 @@ class Peer: address = self.unpackAddress(peer) if (site.addPeer(*address)): added += 1 if added: - self.log.debug("Added peers using pex: %s" % added) + self.log("Added peers using pex: %s" % added) return added # Stop and remove from site def remove(self): - self.log.debug("Removing peer...Connection error: %s, Hash failed: %s" % (self.connection_error, self.hash_failed)) + self.log("Removing peer...Connection error: %s, Hash failed: %s" % (self.connection_error, self.hash_failed)) if self.key in self.site.peers: del(self.site.peers[self.key]) if self.connection: self.connection.close() diff --git a/src/Site/Site.py b/src/Site/Site.py index a51a7ece..517020c0 100644 --- a/src/Site/Site.py +++ b/src/Site/Site.py @@ -445,8 +445,8 @@ class Site: found.append(peer) if len(found) >= need_num: break # Found requested number of peers - if not found and not ignore: # Not found any peer and the requester dont have any, return not that good peers - found = [peer for peer in peers if not peer.key.endswith(":0") and peer.key not in ignore][0:need_num] + if (not found and not ignore) or (need_num > 5 and need_num < 100 and len(found) < need_num): # Not found any peer and the requester dont have any, return not that good peers or Initial pex, but not /Stats page and we can't give enought peer + found = [peer for peer in peers if not peer.key.endswith(":0") and peer.key not in ignore][0:need_num-len(found)] return found diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index fb080b79..dc837168 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -28,7 +28,7 @@ class UiRequest(object): # Call the request handler function base on path def route(self, path): - if config.ui_restrict and self.env['REMOTE_ADDR'] != 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 return self.error403() if path == "/":