diff --git a/.gitignore b/.gitignore index 76380f5b..eb61fdcb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ __pycache__/ # Data dir data/* +.db \ No newline at end of file diff --git a/plugins/Newsfeed/NewsfeedPlugin.py b/plugins/Newsfeed/NewsfeedPlugin.py new file mode 100644 index 00000000..118099a0 --- /dev/null +++ b/plugins/Newsfeed/NewsfeedPlugin.py @@ -0,0 +1,48 @@ +from Plugin import PluginManager +import re + +@PluginManager.registerTo("UiWebsocket") +class UiWebsocketPlugin(object): + def actionFeedFollow(self, to, feeds): + self.user.setFeedFollow(self.site.address, feeds) + self.response(to, "ok") + + def actionFeedListFollow(self, to): + feeds = self.user.sites[self.site.address].get("follow", {}) + self.response(to, feeds) + + def actionFeedQuery(self, to): + from Site import SiteManager + rows = [] + for address, site_data in self.user.sites.iteritems(): + feeds = site_data.get("follow") + if not feeds: + continue + for name, query_set in feeds.iteritems(): + site = SiteManager.site_manager.get(address) + try: + query, params = query_set + if ":params" in query: + query = query.replace(":params", ",".join(["?"]*len(params))) + res = site.storage.query(query+" ORDER BY date_added DESC LIMIT 10", params) + else: + res = site.storage.query(query+" ORDER BY date_added DESC LIMIT 10") + except Exception, err: # Log error + self.log.error("%s feed query %s error: %s" % (address, name, err)) + continue + for row in res: + row = dict(row) + row["site"] = address + row["feed_name"] = name + rows.append(row) + return self.response(to, rows) + + +@PluginManager.registerTo("User") +class UserPlugin(object): + # Set queries that user follows + def setFeedFollow(self, address, feeds): + site_data = self.getSiteData(address) + site_data["follow"] = feeds + self.save() + return site_data diff --git a/plugins/Newsfeed/__init__.py b/plugins/Newsfeed/__init__.py new file mode 100644 index 00000000..20cc04a1 --- /dev/null +++ b/plugins/Newsfeed/__init__.py @@ -0,0 +1 @@ +import NewsfeedPlugin \ No newline at end of file diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py index 6f1b525e..b22ddc17 100644 --- a/plugins/Sidebar/SidebarPlugin.py +++ b/plugins/Sidebar/SidebarPlugin.py @@ -290,7 +290,7 @@ class UiWebsocketPlugin(object): def sidebarRenderOwnSettings(self, body, site): title = cgi.escape(site.content_manager.contents["content.json"]["title"], True) - description = cgi.escape(site.content_manager.contents["content.json"]["description"], True) + description = cgi.escape(site.content_manager.contents["content.json"].get("description", ""), True) privatekey = cgi.escape(self.user.getSiteData(site.address, create=False).get("privatekey", "")) body.append(u""" @@ -484,4 +484,4 @@ class UiWebsocketPlugin(object): return self.response(to, "You don't have permission to run this command") self.site.settings["autodownloadoptional"] = bool(owned) self.site.update() - self.site.worker_manager.removeGoodFileTasks() + self.site.worker_manager.removeGoodFileTasks() \ No newline at end of file diff --git a/plugins/Trayicon/trayicon.ico b/plugins/Trayicon/trayicon.ico index fad67073..8e1a5285 100644 Binary files a/plugins/Trayicon/trayicon.ico and b/plugins/Trayicon/trayicon.ico differ diff --git a/plugins/disabled-Multiuser/MultiuserPlugin.py b/plugins/disabled-Multiuser/MultiuserPlugin.py index a2c812ae..6b96bec1 100644 --- a/plugins/disabled-Multiuser/MultiuserPlugin.py +++ b/plugins/disabled-Multiuser/MultiuserPlugin.py @@ -169,3 +169,16 @@ class UiWebsocketPlugin(object): else: self.cmd("notification", ["error", "Error: Invalid master seed"]) self.actionUserLoginForm(0) + + # Disable not Multiuser safe functions + def actionConfigSet(self, to, *args, **kwargs): + self.cmd("notification", ["info", "This function is disabled on this proxy"]) + + def actionServerShutdown(self, to, *args, **kwargs): + self.cmd("notification", ["info", "This function is disabled on this proxy"]) + + def actionServerUpdate(self, to, *args, **kwargs): + self.cmd("notification", ["info", "This function is disabled on this proxy"]) + + def actionSiteClone(self, to, *args, **kwargs): + self.cmd("notification", ["info", "This function is disabled on this proxy"]) diff --git a/src/Config.py b/src/Config.py index 30b417e7..86d62e3d 100644 --- a/src/Config.py +++ b/src/Config.py @@ -7,10 +7,11 @@ import ConfigParser class Config(object): def __init__(self, argv): - self.version = "0.3.5" - self.rev = 860 + self.version = "0.3.6" + self.rev = 879 self.argv = argv self.action = None + self.config_file = "zeronet.conf" self.createParser() self.createArguments() @@ -129,7 +130,7 @@ class Config(object): 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', + self.parser.add_argument('--homepage', help='Web interface Homepage', default='1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D', metavar='address') self.parser.add_argument('--size_limit', help='Default site size limit in MB', default=10, metavar='size') @@ -254,13 +255,12 @@ class Config(object): # Parse config file def parseConfig(self, argv): # Find config file path from parameters - config_file = "zeronet.conf" if "--config_file" in argv: - config_file = argv[argv.index("--config_file") + 1] + self.config_file = argv[argv.index("--config_file") + 1] # Load config file - if os.path.isfile(config_file): + if os.path.isfile(self.config_file): config = ConfigParser.ConfigParser(allow_no_value=True) - config.read(config_file) + config.read(self.config_file) for section in config.sections(): for key, val in config.items(section): if section != "global": # If not global prefix key with section diff --git a/src/Db/DbCursor.py b/src/Db/DbCursor.py index f3a1c532..bdb9cc5b 100644 --- a/src/Db/DbCursor.py +++ b/src/Db/DbCursor.py @@ -32,13 +32,14 @@ class DbCursor: query_wheres.append(key+" = ?") values.append(value) wheres = " AND ".join(query_wheres) - query = query.replace("?", wheres) + query = re.sub("(.*)[?]", "\\1%s" % wheres, query) # Replace the last ? params = values else: # Convert param dict to INSERT INTO table (key, key2) VALUES (?, ?) format keys = ", ".join(params.keys()) values = ", ".join(['?' for key in params.keys()]) - query = query.replace("?", "(%s) VALUES (%s)" % (keys, values)) + keysvalues = "(%s) VALUES (%s)" % (keys, values) + query = re.sub("(.*)[?]", "\\1%s" % keysvalues, query) # Replace the last ? params = tuple(params.values()) s = time.time() diff --git a/src/Site/Site.py b/src/Site/Site.py index 77e74f05..3eacb6ff 100644 --- a/src/Site/Site.py +++ b/src/Site/Site.py @@ -80,11 +80,12 @@ class Site(object): if self.address in sites_settings: self.settings = sites_settings[self.address] else: - if self.address == config.homepage: # Add admin permissions to homepage - permissions = ["ADMIN"] - else: - permissions = [] - self.settings = {"own": False, "serving": True, "permissions": permissions} # Default + self.settings = {"own": False, "serving": True, "permissions": []} # Default + + # Add admin permissions to homepage + if self.address == config.homepage and "ADMIN" not in self.settings["permissions"]: + self.settings["permissions"].append("ADMIN") + return # Save site settings to data/sites.json @@ -171,7 +172,7 @@ class Site(object): # Retry download bad files def retryBadFiles(self, force=False): - for bad_file, tries in self.bad_files.iteritems(): + for bad_file, tries in self.bad_files.items(): if force or random.randint(0, min(20, tries)) == 0: # Larger number tries = less likely to check every 15min self.needFile(bad_file, update=True, blocking=False) @@ -353,6 +354,9 @@ class Site(object): published = [] # Successfully published (Peer) publishers = [] # Publisher threads + if not self.peers: + self.announce() + connected_peers = self.getConnectedPeers() if len(connected_peers) > limit * 2: # Publish to already connected peers if possible peers = connected_peers diff --git a/src/Test/testdata/bootstrapper.db b/src/Test/testdata/bootstrapper.db deleted file mode 100644 index 0513f1e5..00000000 Binary files a/src/Test/testdata/bootstrapper.db and /dev/null differ diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index f755e978..bb85fcf0 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -188,8 +188,10 @@ class UiRequest(object): inner_path = match.group("inner_path").lstrip("/") if "." in inner_path and not inner_path.endswith(".html"): return self.actionSiteMedia("/media" + path) # Only serve html files with frame - if self.env.get("HTTP_X_REQUESTED_WITH") or self.env.get("HTTP_ORIGIN"): + if self.env.get("HTTP_X_REQUESTED_WITH"): return self.error403("Ajax request not allowed to load wrapper") # No ajax allowed on wrapper + # if self.env.get("HTTP_ORIGIN") and self.env.get("HTTP_ORIGIN").strip("/") != self.env.get("HTTP_HOST", "").strip("/"): + # return self.error403("Origin does not match") site = SiteManager.site_manager.get(address) @@ -478,6 +480,7 @@ class UiRequest(object): # You are not allowed to access this def error403(self, message="", details=True): self.sendHeader(403) + self.log.debug("Error 403: %s" % message) return self.formatError("Forbidden", message, details=details) # Send file not found error diff --git a/src/Ui/UiWebsocket.py b/src/Ui/UiWebsocket.py index 7124b575..0de2ee27 100644 --- a/src/Ui/UiWebsocket.py +++ b/src/Ui/UiWebsocket.py @@ -2,6 +2,7 @@ import json import time import sys import hashlib +import os import gevent @@ -21,6 +22,7 @@ class UiWebsocket(object): self.user = user self.log = site.log self.request = request + self.permissions = [] self.server = server self.next_message_id = 1 self.waiting_cb = {} # Waiting for callback. Key: message_id, Value: function pointer @@ -53,7 +55,7 @@ class UiWebsocket(object): """, 10000 ]) - elif config.tor == "always" and not file_server.tor_manager.start_onions == False: + elif config.tor == "always" and file_server.tor_manager.start_onions is not False: self.site.notifications.append([ "error", """ @@ -81,7 +83,6 @@ class UiWebsocket(object): 0 ]) - for notification in self.site.notifications: # Send pending notification messages self.cmd("notification", notification) self.site.notifications = [] @@ -149,16 +150,16 @@ class UiWebsocket(object): cmd = req.get("cmd") params = req.get("params") - permissions = self.getPermissions(req["id"]) + self.permissions = self.getPermissions(req["id"]) admin_commands = ( "sitePause", "siteResume", "siteDelete", "siteList", "siteSetLimit", "siteClone", - "channelJoinAllsite", "serverUpdate", "serverPortcheck", "certSet" + "channelJoinAllsite", "serverUpdate", "serverPortcheck", "certSet", "configSet" ) if cmd == "response": # It's a response to a command return self.actionResponse(req["to"], req["result"]) - elif cmd in admin_commands and "ADMIN" not in permissions: # Admin commands + elif cmd in admin_commands and "ADMIN" not in self.permissions: # Admin commands return self.response(req["id"], {"error:", "You don't have permission to run %s" % cmd}) else: # Normal command func_name = "action" + cmd[0].upper() + cmd[1:] @@ -332,7 +333,7 @@ class UiWebsocket(object): site.updateWebsocket() # Send updated site data to local websocket clients else: if len(site.peers) == 0: - if sys.modules["main"].file_server.port_opened: + if sys.modules["main"].file_server.port_opened or sys.modules["main"].file_server.tor_manager.start_onions: if notification: self.cmd("notification", ["info", "No peers found, but your content is ready to access.", 5000]) self.response(to, "ok") @@ -406,6 +407,7 @@ class UiWebsocket(object): def actionDbQuery(self, to, query, params=None, wait_for=None): rows = [] try: + assert query.upper().startswith("SELECT"), "Only SELECT query supported" res = self.site.storage.query(query, params) except Exception, err: # Response the error to client return self.response(to, {"error": str(err)}) @@ -594,6 +596,8 @@ class UiWebsocket(object): def actionServerUpdate(self, to): self.cmd("updating") sys.modules["main"].update_after_shutdown = True + if sys.modules["main"].file_server.tor_manager.tor_process: + sys.modules["main"].file_server.tor_manager.stopTor() sys.modules["main"].file_server.stop() sys.modules["main"].ui_server.stop() @@ -601,3 +605,40 @@ class UiWebsocket(object): sys.modules["main"].file_server.port_opened = None res = sys.modules["main"].file_server.openport() self.response(to, res) + + def actionServerShutdown(self, to): + sys.modules["main"].file_server.stop() + sys.modules["main"].ui_server.stop() + + def actionConfigSet(self, to, key, value): + if not os.path.isfile(config.config_file): + content = "" + else: + content = open(config.config_file).read() + lines = content.splitlines() + + global_line_i = None + key_line_i = None + i = 0 + for line in lines: + if line.strip() == "[global]": + global_line_i = i + if line.startswith(key+" = "): + key_line_i = i + i += 1 + + if value == None: # Delete line + if key_line_i: + del lines[key_line_i] + else: # Add / update + new_line = "%s = %s" % (key, value) + if key_line_i: # Already in the config, change the line + lines[key_line_i] = new_line + elif global_line_i is None: # No global section yet, append to end of file + lines.append("[global]") + lines.append(new_line) + else: # Has global section, append the line after it + lines.insert(global_line_i+1, new_line) + + open(config.config_file, "w").write("\n".join(lines)) + self.response(to, "ok") diff --git a/src/Ui/media/img/favicon.ico b/src/Ui/media/img/favicon.ico index fad67073..3f75912a 100644 Binary files a/src/Ui/media/img/favicon.ico and b/src/Ui/media/img/favicon.ico differ diff --git a/src/main.py b/src/main.py index 54c9b7a4..a831f95f 100644 --- a/src/main.py +++ b/src/main.py @@ -241,7 +241,7 @@ class Actions(object): while 1: s = time.time() time.sleep(1) - print time.time()-s + print "Switch time:", time.time()-s gevent.spawn(checker) logging.info("Opening a simple connection server") diff --git a/src/util/helper.py b/src/util/helper.py index 937d45da..9e0e76d4 100644 --- a/src/util/helper.py +++ b/src/util/helper.py @@ -51,10 +51,13 @@ def shellquote(*args): def packPeers(peers): packed_peers = {"ip4": [], "onion": []} for peer in peers: - if peer.ip.endswith(".onion"): - packed_peers["onion"].append(peer.packMyAddress()) - else: - packed_peers["ip4"].append(peer.packMyAddress()) + try: + if peer.ip.endswith(".onion"): + packed_peers["onion"].append(peer.packMyAddress()) + else: + packed_peers["ip4"].append(peer.packMyAddress()) + except Exception, err: + logging.error("Error packing peer address: %s" % peer) return packed_peers