version 0.2.7, plugin system, multiuser plugin for zeroproxies, reworked imports, cookie parse, stats moved to plugin, usermanager class, dont generate site auth on listing, multiline notifications, allow server side prompt from user, update script keep plugins disabled status
This commit is contained in:
parent
3b8d49207e
commit
78f97dcbe8
26 changed files with 789 additions and 308 deletions
135
plugins/Stats/StatsPlugin.py
Normal file
135
plugins/Stats/StatsPlugin.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
import re, time, cgi, os
|
||||
from Plugin import PluginManager
|
||||
|
||||
@PluginManager.registerTo("UiRequest")
|
||||
class UiRequestPlugin(object):
|
||||
def formatTableRow(self, row):
|
||||
back = []
|
||||
for format, val in row:
|
||||
if val == None:
|
||||
formatted = "n/a"
|
||||
elif format == "since":
|
||||
if val:
|
||||
formatted = "%.0f" % (time.time()-val)
|
||||
else:
|
||||
formatted = "n/a"
|
||||
else:
|
||||
formatted = format % val
|
||||
back.append("<td>%s</td>" % formatted)
|
||||
return "<tr>%s</tr>" % "".join(back)
|
||||
|
||||
|
||||
def getObjSize(self, obj, hpy = None):
|
||||
if hpy:
|
||||
return float(hpy.iso(obj).domisize)/1024
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
# /Stats entry point
|
||||
def actionStats(self):
|
||||
import gc, sys
|
||||
from Ui import UiRequest
|
||||
|
||||
hpy = None
|
||||
if self.get.get("size") == "1": # Calc obj size
|
||||
try:
|
||||
import guppy
|
||||
hpy = guppy.hpy()
|
||||
except:
|
||||
pass
|
||||
self.sendHeader()
|
||||
s = time.time()
|
||||
main = sys.modules["main"]
|
||||
|
||||
# Style
|
||||
yield """
|
||||
<style>
|
||||
* { font-family: monospace }
|
||||
table * { text-align: right; padding: 0px 10px }
|
||||
</style>
|
||||
"""
|
||||
|
||||
# Memory
|
||||
try:
|
||||
import psutil
|
||||
process = psutil.Process(os.getpid())
|
||||
mem = process.get_memory_info()[0] / float(2 ** 20)
|
||||
yield "Memory usage: %.2fMB | " % mem
|
||||
yield "Threads: %s | " % len(process.threads())
|
||||
yield "CPU: usr %.2fs sys %.2fs | " % process.cpu_times()
|
||||
yield "Open files: %s | " % len(process.open_files())
|
||||
yield "Sockets: %s" % len(process.connections())
|
||||
yield " | Calc size <a href='?size=1'>on</a> <a href='?size=0'>off</a><br>"
|
||||
except Exception, err:
|
||||
pass
|
||||
|
||||
yield "Connections (%s):<br>" % len(main.file_server.connections)
|
||||
yield "<table><tr> <th>id</th> <th>protocol</th> <th>type</th> <th>ip</th> <th>ping</th> <th>buff</th>"
|
||||
yield "<th>idle</th> <th>open</th> <th>delay</th> <th>sent</th> <th>received</th> <th>last sent</th> <th>waiting</th> <th>version</th> <th>peerid</th> </tr>"
|
||||
for connection in main.file_server.connections:
|
||||
yield self.formatTableRow([
|
||||
("%3d", connection.id),
|
||||
("%s", connection.protocol),
|
||||
("%s", connection.type),
|
||||
("%s", connection.ip),
|
||||
("%6.3f", connection.last_ping_delay),
|
||||
("%s", connection.incomplete_buff_recv),
|
||||
("since", max(connection.last_send_time, connection.last_recv_time)),
|
||||
("since", connection.start_time),
|
||||
("%.3f", connection.last_sent_time-connection.last_send_time),
|
||||
("%.0fkB", connection.bytes_sent/1024),
|
||||
("%.0fkB", connection.bytes_recv/1024),
|
||||
("%s", connection.last_cmd),
|
||||
("%s", connection.waiting_requests.keys()),
|
||||
("%s", connection.handshake.get("version")),
|
||||
("%s", connection.handshake.get("peer_id")),
|
||||
])
|
||||
yield "</table>"
|
||||
|
||||
from greenlet import greenlet
|
||||
objs = [obj for obj in gc.get_objects() if isinstance(obj, greenlet)]
|
||||
yield "<br>Greenlets (%s):<br>" % len(objs)
|
||||
for obj in objs:
|
||||
yield " - %.3fkb: %s<br>" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
|
||||
|
||||
|
||||
from Worker import Worker
|
||||
objs = [obj for obj in gc.get_objects() if isinstance(obj, Worker)]
|
||||
yield "<br>Workers (%s):<br>" % len(objs)
|
||||
for obj in objs:
|
||||
yield " - %.3fkb: %s<br>" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
|
||||
|
||||
|
||||
from Connection import Connection
|
||||
objs = [obj for obj in gc.get_objects() if isinstance(obj, Connection)]
|
||||
yield "<br>Connections (%s):<br>" % len(objs)
|
||||
for obj in objs:
|
||||
yield " - %.3fkb: %s<br>" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
|
||||
|
||||
|
||||
from Site import Site
|
||||
objs = [obj for obj in gc.get_objects() if isinstance(obj, Site)]
|
||||
yield "<br>Sites (%s):<br>" % len(objs)
|
||||
for obj in objs:
|
||||
yield " - %.3fkb: %s<br>" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
|
||||
|
||||
|
||||
objs = [obj for obj in gc.get_objects() if isinstance(obj, self.server.log.__class__)]
|
||||
yield "<br>Loggers (%s):<br>" % len(objs)
|
||||
for obj in objs:
|
||||
yield " - %.3fkb: %s<br>" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj.name)))
|
||||
|
||||
|
||||
objs = [obj for obj in gc.get_objects() if isinstance(obj, UiRequest)]
|
||||
yield "<br>UiRequest (%s):<br>" % len(objs)
|
||||
for obj in objs:
|
||||
yield " - %.3fkb: %s<br>" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
|
||||
|
||||
objs = [(key, val) for key, val in sys.modules.iteritems() if val is not None]
|
||||
objs.sort()
|
||||
yield "<br>Modules (%s):<br>" % len(objs)
|
||||
for module_name, module in objs:
|
||||
yield " - %.3fkb: %s %s<br>" % (self.getObjSize(module, hpy), module_name, cgi.escape(repr(module)))
|
||||
|
||||
yield "Done in %.3f" % (time.time()-s)
|
1
plugins/Stats/__init__.py
Normal file
1
plugins/Stats/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
import StatsPlugin
|
24
plugins/disabled-DonationMessage/DonationMessagePlugin.py
Normal file
24
plugins/disabled-DonationMessage/DonationMessagePlugin.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import re
|
||||
from Plugin import PluginManager
|
||||
|
||||
# Warning: If you modify the donation address then renmae the plugin's directory to "MyDonationMessage" to prevent the update script overwrite
|
||||
|
||||
|
||||
@PluginManager.registerTo("UiRequest")
|
||||
class UiRequestPlugin(object):
|
||||
# Inject a donation message to every page top right corner
|
||||
def actionWrapper(self, path):
|
||||
back = super(UiRequestPlugin, self).actionWrapper(path)
|
||||
if not back or not hasattr(back, "endswith"): return back # Wrapper error or not string returned, injection not possible
|
||||
|
||||
back = re.sub("</body>\s*</html>\s*$",
|
||||
"""
|
||||
<style>
|
||||
#donation_message { position: absolute; bottom: 0px; right: 20px; padding: 7px; font-family: Arial; font-size: 11px }
|
||||
</style>
|
||||
<a id='donation_message' href='https://blockchain.info/address/1QDhxQ6PraUZa21ET5fYUCPgdrwBomnFgX' target='_blank'>Please donate to help to keep this ZeroProxy alive</a>
|
||||
</body>
|
||||
</html>
|
||||
""", back)
|
||||
|
||||
return back
|
1
plugins/disabled-DonationMessage/__init__.py
Normal file
1
plugins/disabled-DonationMessage/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
import DonationMessagePlugin
|
169
plugins/disabled-Multiuser/MultiuserPlugin.py
Normal file
169
plugins/disabled-Multiuser/MultiuserPlugin.py
Normal file
|
@ -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, server = None):
|
||||
self.user_manager = sys.modules["User.UserManager"].user_manager
|
||||
super(UiRequestPlugin, self).__init__(server)
|
||||
|
||||
|
||||
# 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 = """
|
||||
<!-- Multiser plugin -->
|
||||
<style>
|
||||
.masterseed { font-size: 95%; background-color: #FFF0AD; padding: 5px 8px; margin: 9px 0px }
|
||||
</style>
|
||||
<script>
|
||||
hello_message = "<b>Hello, welcome to ZeroProxy!</b><div style='margin-top: 8px'>A new, unique account created for you:</div>"
|
||||
hello_message+= "<div class='masterseed'>{master_seed}</div> <div>This is your private key, <b>save it</b>, so you can login next time.</div><br>"
|
||||
hello_message+= "<a href='#' class='button' style='margin-left: 0px'>Ok, Saved it!</a> or <a href='#Login' onclick='wrapper.ws.cmd(\\"userLoginForm\\", []); return false'>Login</a><br><br>"
|
||||
hello_message+= "<small>This site is allows you to browse ZeroNet content, but if you want to secure your account <br>"
|
||||
hello_message+= "and help to make a better network, then please run your own <a href='https://github.com/HelloZeroNet/ZeroNet' target='_blank'>ZeroNet client</a>.</small>"
|
||||
setTimeout(function() {
|
||||
wrapper.notifications.add("hello", "info", hello_message)
|
||||
delete(hello_message)
|
||||
}, 1000)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
""".replace("\t", "")
|
||||
inject_html = inject_html.replace("{master_seed}", master_seed) # Set the master seed in the message
|
||||
|
||||
back = re.sub("</body>\s*</html>\s*$", inject_html, back) # Replace the </body></html> tags with the injection
|
||||
|
||||
elif loggedin:
|
||||
inject_html = """
|
||||
<!-- Multiser plugin -->
|
||||
<script>
|
||||
setTimeout(function() {
|
||||
wrapper.notifications.add("login", "done", "Hello again!<br><small>You have been logged in successfully</small>", 5000)
|
||||
}, 1000)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
""".replace("\t", "")
|
||||
back = re.sub("</body>\s*</html>\s*$", inject_html, back) # Replace the </body></html> 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 = "<b style='padding-top: 5px; display: inline-block'>Your unique private key:</b>"
|
||||
message+= "<div style='font-size: 84%%; background-color: #FFF0AD; padding: 5px 8px; margin: 9px 0px'>%s</div>" % self.user.master_seed
|
||||
message+= "<small>(Save it, you can access your account using this information)</small>"
|
||||
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 = "<b>You have been logged out.</b> <a href='#Login' class='button' onclick='wrapper.ws.cmd(\"userLoginForm\", []); return false'>Login to another account</a>"
|
||||
message+= "<script>document.cookie = 'master_address=; expires=Thu, 01 Jan 1970 00:00:00 UTC'</script>"
|
||||
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", ["<b>Login</b><br>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+= "<script>document.cookie = 'master_address=%s;path=/;max-age=2592000;'</script>" % user.master_address
|
||||
message+= "<script>wrapper.reload('login=done')</script>"
|
||||
self.cmd("notification", ["done", message])
|
||||
else:
|
||||
self.cmd("notification", ["error", "Error: Invalid master seed"])
|
||||
self.actionUserLoginForm(0)
|
||||
|
1
plugins/disabled-Multiuser/__init__.py
Normal file
1
plugins/disabled-Multiuser/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
import MultiuserPlugin
|
Loading…
Add table
Add a link
Reference in a new issue