Version 0.3.6, Rev879, Fix sidebar error on description missing, New trayicon, New favicon, Disable some functions on MultiUser proxies, New homepage, Replace only the last ? in SQL queries, Alwaays grant ADMIN permission to homepage site, Announce before publish if no peers, configSet, serverShutdown, ADMIN WebsocketAPI command, Stop Tor client before updating, Ignore peer ip packing error, Ignore db files from git, Fix safari ajax error when UiPassword enabled
This commit is contained in:
parent
f7eaf7b222
commit
687a848292
15 changed files with 144 additions and 29 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ __pycache__/
|
||||||
|
|
||||||
# Data dir
|
# Data dir
|
||||||
data/*
|
data/*
|
||||||
|
.db
|
48
plugins/Newsfeed/NewsfeedPlugin.py
Normal file
48
plugins/Newsfeed/NewsfeedPlugin.py
Normal file
|
@ -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
|
1
plugins/Newsfeed/__init__.py
Normal file
1
plugins/Newsfeed/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import NewsfeedPlugin
|
|
@ -290,7 +290,7 @@ class UiWebsocketPlugin(object):
|
||||||
|
|
||||||
def sidebarRenderOwnSettings(self, body, site):
|
def sidebarRenderOwnSettings(self, body, site):
|
||||||
title = cgi.escape(site.content_manager.contents["content.json"]["title"], True)
|
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", ""))
|
privatekey = cgi.escape(self.user.getSiteData(site.address, create=False).get("privatekey", ""))
|
||||||
|
|
||||||
body.append(u"""
|
body.append(u"""
|
||||||
|
@ -484,4 +484,4 @@ class UiWebsocketPlugin(object):
|
||||||
return self.response(to, "You don't have permission to run this command")
|
return self.response(to, "You don't have permission to run this command")
|
||||||
self.site.settings["autodownloadoptional"] = bool(owned)
|
self.site.settings["autodownloadoptional"] = bool(owned)
|
||||||
self.site.update()
|
self.site.update()
|
||||||
self.site.worker_manager.removeGoodFileTasks()
|
self.site.worker_manager.removeGoodFileTasks()
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -169,3 +169,16 @@ class UiWebsocketPlugin(object):
|
||||||
else:
|
else:
|
||||||
self.cmd("notification", ["error", "Error: Invalid master seed"])
|
self.cmd("notification", ["error", "Error: Invalid master seed"])
|
||||||
self.actionUserLoginForm(0)
|
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"])
|
||||||
|
|
|
@ -7,10 +7,11 @@ import ConfigParser
|
||||||
class Config(object):
|
class Config(object):
|
||||||
|
|
||||||
def __init__(self, argv):
|
def __init__(self, argv):
|
||||||
self.version = "0.3.5"
|
self.version = "0.3.6"
|
||||||
self.rev = 860
|
self.rev = 879
|
||||||
self.argv = argv
|
self.argv = argv
|
||||||
self.action = None
|
self.action = None
|
||||||
|
self.config_file = "zeronet.conf"
|
||||||
self.createParser()
|
self.createParser()
|
||||||
self.createArguments()
|
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('--ui_restrict', help='Restrict web access', default=False, metavar='ip', nargs='*')
|
||||||
self.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')
|
||||||
self.parser.add_argument('--homepage', help='Web interface Homepage', default='1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr',
|
self.parser.add_argument('--homepage', help='Web interface Homepage', default='1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D',
|
||||||
metavar='address')
|
metavar='address')
|
||||||
self.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')
|
||||||
|
|
||||||
|
@ -254,13 +255,12 @@ class Config(object):
|
||||||
# Parse config file
|
# Parse config file
|
||||||
def parseConfig(self, argv):
|
def parseConfig(self, argv):
|
||||||
# Find config file path from parameters
|
# Find config file path from parameters
|
||||||
config_file = "zeronet.conf"
|
|
||||||
if "--config_file" in argv:
|
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
|
# Load config file
|
||||||
if os.path.isfile(config_file):
|
if os.path.isfile(self.config_file):
|
||||||
config = ConfigParser.ConfigParser(allow_no_value=True)
|
config = ConfigParser.ConfigParser(allow_no_value=True)
|
||||||
config.read(config_file)
|
config.read(self.config_file)
|
||||||
for section in config.sections():
|
for section in config.sections():
|
||||||
for key, val in config.items(section):
|
for key, val in config.items(section):
|
||||||
if section != "global": # If not global prefix key with section
|
if section != "global": # If not global prefix key with section
|
||||||
|
|
|
@ -32,13 +32,14 @@ class DbCursor:
|
||||||
query_wheres.append(key+" = ?")
|
query_wheres.append(key+" = ?")
|
||||||
values.append(value)
|
values.append(value)
|
||||||
wheres = " AND ".join(query_wheres)
|
wheres = " AND ".join(query_wheres)
|
||||||
query = query.replace("?", wheres)
|
query = re.sub("(.*)[?]", "\\1%s" % wheres, query) # Replace the last ?
|
||||||
params = values
|
params = values
|
||||||
else:
|
else:
|
||||||
# Convert param dict to INSERT INTO table (key, key2) VALUES (?, ?) format
|
# Convert param dict to INSERT INTO table (key, key2) VALUES (?, ?) format
|
||||||
keys = ", ".join(params.keys())
|
keys = ", ".join(params.keys())
|
||||||
values = ", ".join(['?' for key in 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())
|
params = tuple(params.values())
|
||||||
|
|
||||||
s = time.time()
|
s = time.time()
|
||||||
|
|
|
@ -80,11 +80,12 @@ class Site(object):
|
||||||
if self.address in sites_settings:
|
if self.address in sites_settings:
|
||||||
self.settings = sites_settings[self.address]
|
self.settings = sites_settings[self.address]
|
||||||
else:
|
else:
|
||||||
if self.address == config.homepage: # Add admin permissions to homepage
|
self.settings = {"own": False, "serving": True, "permissions": []} # Default
|
||||||
permissions = ["ADMIN"]
|
|
||||||
else:
|
# Add admin permissions to homepage
|
||||||
permissions = []
|
if self.address == config.homepage and "ADMIN" not in self.settings["permissions"]:
|
||||||
self.settings = {"own": False, "serving": True, "permissions": permissions} # Default
|
self.settings["permissions"].append("ADMIN")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Save site settings to data/sites.json
|
# Save site settings to data/sites.json
|
||||||
|
@ -171,7 +172,7 @@ class Site(object):
|
||||||
|
|
||||||
# Retry download bad files
|
# Retry download bad files
|
||||||
def retryBadFiles(self, force=False):
|
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
|
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)
|
self.needFile(bad_file, update=True, blocking=False)
|
||||||
|
|
||||||
|
@ -353,6 +354,9 @@ class Site(object):
|
||||||
published = [] # Successfully published (Peer)
|
published = [] # Successfully published (Peer)
|
||||||
publishers = [] # Publisher threads
|
publishers = [] # Publisher threads
|
||||||
|
|
||||||
|
if not self.peers:
|
||||||
|
self.announce()
|
||||||
|
|
||||||
connected_peers = self.getConnectedPeers()
|
connected_peers = self.getConnectedPeers()
|
||||||
if len(connected_peers) > limit * 2: # Publish to already connected peers if possible
|
if len(connected_peers) > limit * 2: # Publish to already connected peers if possible
|
||||||
peers = connected_peers
|
peers = connected_peers
|
||||||
|
|
BIN
src/Test/testdata/bootstrapper.db
vendored
BIN
src/Test/testdata/bootstrapper.db
vendored
Binary file not shown.
|
@ -188,8 +188,10 @@ class UiRequest(object):
|
||||||
inner_path = match.group("inner_path").lstrip("/")
|
inner_path = match.group("inner_path").lstrip("/")
|
||||||
if "." in inner_path and not inner_path.endswith(".html"):
|
if "." in inner_path and not inner_path.endswith(".html"):
|
||||||
return self.actionSiteMedia("/media" + path) # Only serve html files with frame
|
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
|
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)
|
site = SiteManager.site_manager.get(address)
|
||||||
|
|
||||||
|
@ -478,6 +480,7 @@ class UiRequest(object):
|
||||||
# You are not allowed to access this
|
# You are not allowed to access this
|
||||||
def error403(self, message="", details=True):
|
def error403(self, message="", details=True):
|
||||||
self.sendHeader(403)
|
self.sendHeader(403)
|
||||||
|
self.log.debug("Error 403: %s" % message)
|
||||||
return self.formatError("Forbidden", message, details=details)
|
return self.formatError("Forbidden", message, details=details)
|
||||||
|
|
||||||
# Send file not found error
|
# Send file not found error
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import os
|
||||||
|
|
||||||
import gevent
|
import gevent
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ class UiWebsocket(object):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.log = site.log
|
self.log = site.log
|
||||||
self.request = request
|
self.request = request
|
||||||
|
self.permissions = []
|
||||||
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
|
||||||
|
@ -53,7 +55,7 @@ class UiWebsocket(object):
|
||||||
""",
|
""",
|
||||||
10000
|
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([
|
self.site.notifications.append([
|
||||||
"error",
|
"error",
|
||||||
"""
|
"""
|
||||||
|
@ -81,7 +83,6 @@ class UiWebsocket(object):
|
||||||
0
|
0
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
for notification in self.site.notifications: # Send pending notification messages
|
for notification in self.site.notifications: # Send pending notification messages
|
||||||
self.cmd("notification", notification)
|
self.cmd("notification", notification)
|
||||||
self.site.notifications = []
|
self.site.notifications = []
|
||||||
|
@ -149,16 +150,16 @@ class UiWebsocket(object):
|
||||||
|
|
||||||
cmd = req.get("cmd")
|
cmd = req.get("cmd")
|
||||||
params = req.get("params")
|
params = req.get("params")
|
||||||
permissions = self.getPermissions(req["id"])
|
self.permissions = self.getPermissions(req["id"])
|
||||||
|
|
||||||
admin_commands = (
|
admin_commands = (
|
||||||
"sitePause", "siteResume", "siteDelete", "siteList", "siteSetLimit", "siteClone",
|
"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
|
if cmd == "response": # It's a response to a command
|
||||||
return self.actionResponse(req["to"], req["result"])
|
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})
|
return self.response(req["id"], {"error:", "You don't have permission to run %s" % cmd})
|
||||||
else: # Normal command
|
else: # Normal command
|
||||||
func_name = "action" + cmd[0].upper() + cmd[1:]
|
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
|
site.updateWebsocket() # Send updated site data to local websocket clients
|
||||||
else:
|
else:
|
||||||
if len(site.peers) == 0:
|
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:
|
if notification:
|
||||||
self.cmd("notification", ["info", "No peers found, but your content is ready to access.", 5000])
|
self.cmd("notification", ["info", "No peers found, but your content is ready to access.", 5000])
|
||||||
self.response(to, "ok")
|
self.response(to, "ok")
|
||||||
|
@ -406,6 +407,7 @@ class UiWebsocket(object):
|
||||||
def actionDbQuery(self, to, query, params=None, wait_for=None):
|
def actionDbQuery(self, to, query, params=None, wait_for=None):
|
||||||
rows = []
|
rows = []
|
||||||
try:
|
try:
|
||||||
|
assert query.upper().startswith("SELECT"), "Only SELECT query supported"
|
||||||
res = self.site.storage.query(query, params)
|
res = self.site.storage.query(query, params)
|
||||||
except Exception, err: # Response the error to client
|
except Exception, err: # Response the error to client
|
||||||
return self.response(to, {"error": str(err)})
|
return self.response(to, {"error": str(err)})
|
||||||
|
@ -594,6 +596,8 @@ class UiWebsocket(object):
|
||||||
def actionServerUpdate(self, to):
|
def actionServerUpdate(self, to):
|
||||||
self.cmd("updating")
|
self.cmd("updating")
|
||||||
sys.modules["main"].update_after_shutdown = True
|
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"].file_server.stop()
|
||||||
sys.modules["main"].ui_server.stop()
|
sys.modules["main"].ui_server.stop()
|
||||||
|
|
||||||
|
@ -601,3 +605,40 @@ class UiWebsocket(object):
|
||||||
sys.modules["main"].file_server.port_opened = None
|
sys.modules["main"].file_server.port_opened = None
|
||||||
res = sys.modules["main"].file_server.openport()
|
res = sys.modules["main"].file_server.openport()
|
||||||
self.response(to, res)
|
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")
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -241,7 +241,7 @@ class Actions(object):
|
||||||
while 1:
|
while 1:
|
||||||
s = time.time()
|
s = time.time()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
print time.time()-s
|
print "Switch time:", time.time()-s
|
||||||
gevent.spawn(checker)
|
gevent.spawn(checker)
|
||||||
|
|
||||||
logging.info("Opening a simple connection server")
|
logging.info("Opening a simple connection server")
|
||||||
|
|
|
@ -51,10 +51,13 @@ def shellquote(*args):
|
||||||
def packPeers(peers):
|
def packPeers(peers):
|
||||||
packed_peers = {"ip4": [], "onion": []}
|
packed_peers = {"ip4": [], "onion": []}
|
||||||
for peer in peers:
|
for peer in peers:
|
||||||
if peer.ip.endswith(".onion"):
|
try:
|
||||||
packed_peers["onion"].append(peer.packMyAddress())
|
if peer.ip.endswith(".onion"):
|
||||||
else:
|
packed_peers["onion"].append(peer.packMyAddress())
|
||||||
packed_peers["ip4"].append(peer.packMyAddress())
|
else:
|
||||||
|
packed_peers["ip4"].append(peer.packMyAddress())
|
||||||
|
except Exception, err:
|
||||||
|
logging.error("Error packing peer address: %s" % peer)
|
||||||
return packed_peers
|
return packed_peers
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue