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
|
|
@ -3,7 +3,7 @@ import ConfigParser
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.version = "0.2.6"
|
self.version = "0.2.7"
|
||||||
self.parser = self.createArguments()
|
self.parser = self.createArguments()
|
||||||
argv = sys.argv[:] # Copy command line arguments
|
argv = sys.argv[:] # Copy command line arguments
|
||||||
argv = self.parseConfig(argv) # Add arguments from config file
|
argv = self.parseConfig(argv) # Add arguments from config file
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from src.lib.BitcoinECC import BitcoinECC
|
from lib.BitcoinECC import BitcoinECC
|
||||||
from src.lib.pybitcointools import bitcoin as btctools
|
from lib.pybitcointools import bitcoin as btctools
|
||||||
|
|
||||||
|
|
||||||
def newPrivatekey(uncompressed=True): # Return new private key
|
def newPrivatekey(uncompressed=True): # Return new private key
|
||||||
|
|
|
@ -6,6 +6,7 @@ if config.debug: # Only load pyfilesytem if using debug mode
|
||||||
try:
|
try:
|
||||||
from fs.osfs import OSFS
|
from fs.osfs import OSFS
|
||||||
pyfilesystem = OSFS("src")
|
pyfilesystem = OSFS("src")
|
||||||
|
pyfilesystem_plugins = OSFS("plugins")
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
logging.debug("%s: For autoreload please download pyfilesystem (https://code.google.com/p/pyfilesystem/)" % err)
|
logging.debug("%s: For autoreload please download pyfilesystem (https://code.google.com/p/pyfilesystem/)" % err)
|
||||||
pyfilesystem = False
|
pyfilesystem = False
|
||||||
|
@ -28,6 +29,7 @@ class DebugReloader:
|
||||||
try:
|
try:
|
||||||
time.sleep(1) # Wait for .pyc compiles
|
time.sleep(1) # Wait for .pyc compiles
|
||||||
pyfilesystem.add_watcher(self.changed, path=self.directory, events=None, recursive=recursive)
|
pyfilesystem.add_watcher(self.changed, path=self.directory, events=None, recursive=recursive)
|
||||||
|
pyfilesystem_plugins.add_watcher(self.changed, path=self.directory, events=None, recursive=recursive)
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
print "File system watcher failed: %s (on linux pyinotify not gevent compatible yet :( )" % err
|
print "File system watcher failed: %s (on linux pyinotify not gevent compatible yet :( )" % err
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import os, logging, urllib2, urllib, re, time
|
import os, logging, urllib2, re, time
|
||||||
import gevent, msgpack
|
import gevent, msgpack
|
||||||
import zmq.green as zmq
|
import zmq.green as zmq
|
||||||
from Config import config
|
from Config import config
|
||||||
|
|
|
@ -12,7 +12,7 @@ class Peer:
|
||||||
self.site = site
|
self.site = site
|
||||||
self.key = "%s:%s" % (ip, port)
|
self.key = "%s:%s" % (ip, port)
|
||||||
self.log = None
|
self.log = None
|
||||||
self.connection_server = sys.modules["src.main"].file_server
|
self.connection_server = sys.modules["main"].file_server
|
||||||
|
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.last_found = None # Time of last found in the torrent tracker
|
self.last_found = None # Time of last found in the torrent tracker
|
||||||
|
|
97
src/Plugin/PluginManager.py
Normal file
97
src/Plugin/PluginManager.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import logging, os, sys
|
||||||
|
from Debug import Debug
|
||||||
|
from Config import config
|
||||||
|
|
||||||
|
class PluginManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.log = logging.getLogger("PluginManager")
|
||||||
|
self.plugin_path = "plugins" # Plugin directory
|
||||||
|
self.plugins = {} # Registered plugins (key: class name, value: list of plugins for class)
|
||||||
|
|
||||||
|
sys.path.append(self.plugin_path)
|
||||||
|
|
||||||
|
|
||||||
|
if config.debug: # Auto reload Plugins on file change
|
||||||
|
from Debug import DebugReloader
|
||||||
|
DebugReloader(self.reloadPlugins)
|
||||||
|
|
||||||
|
|
||||||
|
# -- Load / Unload --
|
||||||
|
|
||||||
|
# Load all plugin
|
||||||
|
def loadPlugins(self):
|
||||||
|
for dir_name in os.listdir(self.plugin_path):
|
||||||
|
dir_path = os.path.join(self.plugin_path, dir_name)
|
||||||
|
if dir_name.startswith("disabled"): continue # Dont load if disabled
|
||||||
|
if not os.path.isdir(dir_path): continue # Dont load if not dir
|
||||||
|
if dir_name.startswith("Debug") and not config.debug: continue # Only load in debug mode if module name starts with Debug
|
||||||
|
self.log.debug("Loading plugin: %s" % dir_name)
|
||||||
|
try:
|
||||||
|
__import__(dir_name)
|
||||||
|
except Exception, err:
|
||||||
|
self.log.error("Plugin %s load error: %s" % (dir_name, Debug.formatException(err)))
|
||||||
|
|
||||||
|
|
||||||
|
# Reload all plugins
|
||||||
|
def reloadPlugins(self):
|
||||||
|
self.plugins = {} # Reset registered plugins
|
||||||
|
for module_name, module in sys.modules.items():
|
||||||
|
if module and "__file__" in dir(module) and self.plugin_path in module.__file__: # Module file within plugin_path
|
||||||
|
if "allow_reload" not in dir(module) or module.allow_reload: # Check if reload disabled
|
||||||
|
try:
|
||||||
|
reload(module)
|
||||||
|
except Exception, err:
|
||||||
|
self.log.error("Plugin %s reload error: %s" % (module_name, Debug.formatException(err)))
|
||||||
|
|
||||||
|
self.loadPlugins() # Load new plugins
|
||||||
|
|
||||||
|
|
||||||
|
plugin_manager = PluginManager() # Singletone
|
||||||
|
|
||||||
|
# -- Decorators --
|
||||||
|
|
||||||
|
# Accept plugin to class decorator
|
||||||
|
def acceptPlugins(base_class):
|
||||||
|
class_name = base_class.__name__
|
||||||
|
if class_name in plugin_manager.plugins: # Has plugins
|
||||||
|
classes = plugin_manager.plugins[class_name][:] # Copy the current plugins
|
||||||
|
classes.reverse()
|
||||||
|
classes.append(base_class) # Add the class itself to end of inherience line
|
||||||
|
PluginedClass = type(class_name, tuple(classes), dict()) # Create the plugined class
|
||||||
|
plugin_manager.log.debug("New class accepts plugins: %s (Loaded plugins: %s)" % (class_name, classes))
|
||||||
|
else: # No plugins just use the original
|
||||||
|
PluginedClass = base_class
|
||||||
|
return PluginedClass
|
||||||
|
|
||||||
|
|
||||||
|
# Register plugin to class name decorator
|
||||||
|
def registerTo(class_name):
|
||||||
|
plugin_manager.log.debug("New plugin registered to: %s" % class_name)
|
||||||
|
if class_name not in plugin_manager.plugins: plugin_manager.plugins[class_name] = []
|
||||||
|
|
||||||
|
def classDecorator(self):
|
||||||
|
plugin_manager.plugins[class_name].append(self)
|
||||||
|
return self
|
||||||
|
return classDecorator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# - Example usage -
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
@registerTo("Request")
|
||||||
|
class RequestPlugin(object):
|
||||||
|
def actionMainPage(self, path):
|
||||||
|
return "Hello MainPage!"
|
||||||
|
|
||||||
|
|
||||||
|
@accept
|
||||||
|
class Request(object):
|
||||||
|
def route(self, path):
|
||||||
|
func = getattr(self, "action"+path, None)
|
||||||
|
if func:
|
||||||
|
return func(path)
|
||||||
|
else:
|
||||||
|
return "Can't route to", path
|
||||||
|
|
||||||
|
print Request().route("MainPage")
|
0
src/Plugin/__init__.py
Normal file
0
src/Plugin/__init__.py
Normal file
|
@ -2,6 +2,7 @@ import time, re, os, mimetypes, json, cgi
|
||||||
from Config import config
|
from Config import config
|
||||||
from Site import SiteManager
|
from Site import SiteManager
|
||||||
from User import UserManager
|
from User import UserManager
|
||||||
|
from Plugin import PluginManager
|
||||||
from Ui.UiWebsocket import UiWebsocket
|
from Ui.UiWebsocket import UiWebsocket
|
||||||
|
|
||||||
status_texts = {
|
status_texts = {
|
||||||
|
@ -12,15 +13,15 @@ status_texts = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PluginManager.acceptPlugins
|
||||||
class UiRequest:
|
class UiRequest(object):
|
||||||
def __init__(self, server = None):
|
def __init__(self, server = None):
|
||||||
if server:
|
if server:
|
||||||
self.server = server
|
self.server = server
|
||||||
self.log = server.log
|
self.log = server.log
|
||||||
self.get = {} # Get parameters
|
self.get = {} # Get parameters
|
||||||
self.env = {} # Enviroment settings
|
self.env = {} # Enviroment settings
|
||||||
self.user = UserManager.getCurrent()
|
self.user = None
|
||||||
self.start_response = None # Start response function
|
self.start_response = None # Start response function
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,16 +47,17 @@ class UiRequest:
|
||||||
return self.actionDebug()
|
return self.actionDebug()
|
||||||
elif path == "/Console" and config.debug:
|
elif path == "/Console" and config.debug:
|
||||||
return self.actionConsole()
|
return self.actionConsole()
|
||||||
elif path == "/Stats":
|
|
||||||
return self.actionStats()
|
|
||||||
# Test
|
|
||||||
elif path == "/Test/Websocket":
|
|
||||||
return self.actionFile("Data/temp/ws_test.html")
|
|
||||||
elif path == "/Test/Stream":
|
|
||||||
return self.actionTestStream()
|
|
||||||
# Site media wrapper
|
# Site media wrapper
|
||||||
else:
|
else:
|
||||||
return self.actionWrapper(path)
|
body = self.actionWrapper(path)
|
||||||
|
if body:
|
||||||
|
return body
|
||||||
|
else:
|
||||||
|
func = getattr(self, "action"+path.lstrip("/"), None) # Check if we have action+request_path function
|
||||||
|
if func:
|
||||||
|
return func()
|
||||||
|
else:
|
||||||
|
return self.error404(path)
|
||||||
|
|
||||||
|
|
||||||
# Get mime by filename
|
# Get mime by filename
|
||||||
|
@ -69,6 +71,24 @@ class UiRequest:
|
||||||
return content_type
|
return content_type
|
||||||
|
|
||||||
|
|
||||||
|
# Returns: <dict> Cookies based on self.env
|
||||||
|
def getCookies(self):
|
||||||
|
raw_cookies = self.env.get('HTTP_COOKIE')
|
||||||
|
if raw_cookies:
|
||||||
|
cookies = cgi.parse_qsl(raw_cookies)
|
||||||
|
return {key.strip(): val for key, val in cookies}
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def getCurrentUser(self):
|
||||||
|
if self.user: return self.user # Cache
|
||||||
|
self.user = UserManager.user_manager.get() # Get user
|
||||||
|
if not self.user:
|
||||||
|
self.user = UserManager.user_manager.create()
|
||||||
|
return self.user
|
||||||
|
|
||||||
|
|
||||||
# Send response headers
|
# Send response headers
|
||||||
def sendHeader(self, status=200, content_type="text/html", extra_headers=[]):
|
def sendHeader(self, status=200, content_type="text/html", extra_headers=[]):
|
||||||
if content_type == "text/html": content_type = "text/html; charset=utf-8"
|
if content_type == "text/html": content_type = "text/html; charset=utf-8"
|
||||||
|
@ -89,7 +109,7 @@ class UiRequest:
|
||||||
#template = SimpleTemplate(open(template_path), lookup=[os.path.dirname(template_path)])
|
#template = SimpleTemplate(open(template_path), lookup=[os.path.dirname(template_path)])
|
||||||
#yield str(template.render(*args, **kwargs).encode("utf8"))
|
#yield str(template.render(*args, **kwargs).encode("utf8"))
|
||||||
template = open(template_path).read().decode("utf8")
|
template = open(template_path).read().decode("utf8")
|
||||||
yield template.format(**kwargs).encode("utf8")
|
return template.format(**kwargs).encode("utf8")
|
||||||
|
|
||||||
|
|
||||||
# - Actions -
|
# - Actions -
|
||||||
|
@ -105,7 +125,7 @@ class UiRequest:
|
||||||
|
|
||||||
|
|
||||||
# Render a file from media with iframe site wrapper
|
# Render a file from media with iframe site wrapper
|
||||||
def actionWrapper(self, path):
|
def actionWrapper(self, path, extra_headers=[]):
|
||||||
if "." in path and not path.endswith(".html"): return self.actionSiteMedia("/media"+path) # Only serve html files with frame
|
if "." in path and not path.endswith(".html"): return self.actionSiteMedia("/media"+path) # Only serve html files with frame
|
||||||
if self.get.get("wrapper") == "False": return self.actionSiteMedia("/media"+path) # Only serve html files with frame
|
if self.get.get("wrapper") == "False": return self.actionSiteMedia("/media"+path) # Only serve html files with frame
|
||||||
if self.env.get("HTTP_X_REQUESTED_WITH"): return self.error403() # No ajax allowed on wrapper
|
if self.env.get("HTTP_X_REQUESTED_WITH"): return self.error403() # No ajax allowed on wrapper
|
||||||
|
@ -121,9 +141,11 @@ class UiRequest:
|
||||||
else:
|
else:
|
||||||
title = "Loading %s..." % match.group("site")
|
title = "Loading %s..." % match.group("site")
|
||||||
site = SiteManager.need(match.group("site")) # Start download site
|
site = SiteManager.need(match.group("site")) # Start download site
|
||||||
if not site: return self.error404(path)
|
if not site: return False
|
||||||
|
|
||||||
self.sendHeader(extra_headers=[("X-Frame-Options", "DENY")])
|
extra_headers.append(("X-Frame-Options", "DENY"))
|
||||||
|
|
||||||
|
self.sendHeader(extra_headers=extra_headers)
|
||||||
|
|
||||||
# Wrapper variable inits
|
# Wrapper variable inits
|
||||||
query_string = ""
|
query_string = ""
|
||||||
|
@ -152,7 +174,7 @@ class UiRequest:
|
||||||
)
|
)
|
||||||
|
|
||||||
else: # Bad url
|
else: # Bad url
|
||||||
return self.error404(path)
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Serve a media for site
|
# Serve a media for site
|
||||||
|
@ -241,7 +263,11 @@ class UiRequest:
|
||||||
if site_check.settings["wrapper_key"] == wrapper_key: site = site_check
|
if site_check.settings["wrapper_key"] == wrapper_key: site = site_check
|
||||||
|
|
||||||
if site: # Correct wrapper key
|
if site: # Correct wrapper key
|
||||||
ui_websocket = UiWebsocket(ws, site, self.server, self.user)
|
user = self.getCurrentUser()
|
||||||
|
if not user:
|
||||||
|
self.log.error("No user found")
|
||||||
|
return self.error403()
|
||||||
|
ui_websocket = UiWebsocket(ws, site, self.server, user)
|
||||||
site.websockets.append(ui_websocket) # Add to site websockets to allow notify on events
|
site.websockets.append(ui_websocket) # Add to site websockets to allow notify on events
|
||||||
ui_websocket.start()
|
ui_websocket.start()
|
||||||
for site_check in self.server.sites.values(): # Remove websocket from every site (admin sites allowed to join other sites event channels)
|
for site_check in self.server.sites.values(): # Remove websocket from every site (admin sites allowed to join other sites event channels)
|
||||||
|
@ -260,7 +286,7 @@ class UiRequest:
|
||||||
def actionDebug(self):
|
def actionDebug(self):
|
||||||
# Raise last error from DebugHook
|
# Raise last error from DebugHook
|
||||||
import sys
|
import sys
|
||||||
last_error = sys.modules["src.main"].DebugHook.last_error
|
last_error = sys.modules["main"].DebugHook.last_error
|
||||||
if last_error:
|
if last_error:
|
||||||
raise last_error[0], last_error[1], last_error[2]
|
raise last_error[0], last_error[1], last_error[2]
|
||||||
else:
|
else:
|
||||||
|
@ -272,134 +298,10 @@ class UiRequest:
|
||||||
def actionConsole(self):
|
def actionConsole(self):
|
||||||
import sys
|
import sys
|
||||||
sites = self.server.sites
|
sites = self.server.sites
|
||||||
main = sys.modules["src.main"]
|
main = sys.modules["main"]
|
||||||
raise Exception("Here is your console")
|
raise Exception("Here is your console")
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def actionStats(self):
|
|
||||||
import gc, sys
|
|
||||||
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["src.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)))
|
|
||||||
|
|
||||||
yield "Done in %.3f" % (time.time()-s)
|
|
||||||
|
|
||||||
|
|
||||||
# - Tests -
|
# - Tests -
|
||||||
|
|
||||||
def actionTestStream(self):
|
def actionTestStream(self):
|
||||||
|
@ -433,8 +335,9 @@ class UiRequest:
|
||||||
|
|
||||||
# - Reload for eaiser developing -
|
# - Reload for eaiser developing -
|
||||||
def reload(self):
|
def reload(self):
|
||||||
import imp
|
import imp, sys
|
||||||
global UiWebsocket
|
global UiWebsocket
|
||||||
UiWebsocket = imp.load_source("UiWebsocket", "src/Ui/UiWebsocket.py").UiWebsocket
|
UiWebsocket = imp.load_source("UiWebsocket", "src/Ui/UiWebsocket.py").UiWebsocket
|
||||||
UserManager.reload()
|
#reload(sys.modules["User.UserManager"])
|
||||||
self.user = UserManager.getCurrent()
|
#UserManager.reloadModule()
|
||||||
|
#self.user = UserManager.user_manager.getCurrent()
|
||||||
|
|
|
@ -3,11 +3,12 @@ import logging, time, cgi, string, random
|
||||||
from gevent.pywsgi import WSGIServer
|
from gevent.pywsgi import WSGIServer
|
||||||
from gevent.pywsgi import WSGIHandler
|
from gevent.pywsgi import WSGIHandler
|
||||||
from lib.geventwebsocket.handler import WebSocketHandler
|
from lib.geventwebsocket.handler import WebSocketHandler
|
||||||
from Ui import UiRequest
|
from UiRequest import UiRequest
|
||||||
from Site import SiteManager
|
from Site import SiteManager
|
||||||
from Config import config
|
from Config import config
|
||||||
from Debug import Debug
|
from Debug import Debug
|
||||||
|
|
||||||
|
|
||||||
# Skip websocket handler if not necessary
|
# Skip websocket handler if not necessary
|
||||||
class UiWSGIHandler(WSGIHandler):
|
class UiWSGIHandler(WSGIHandler):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -28,7 +29,10 @@ class UiWSGIHandler(WSGIHandler):
|
||||||
try:
|
try:
|
||||||
return super(UiWSGIHandler, self).run_application()
|
return super(UiWSGIHandler, self).run_application()
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
logging.debug("UiWSGIHandler error: %s" % err)
|
logging.debug("UiWSGIHandler error: %s" % Debug.formatException(err))
|
||||||
|
if config.debug: # Allow websocket errors to appear on /Debug
|
||||||
|
import sys
|
||||||
|
sys.modules["main"].DebugHook.handleError()
|
||||||
del self.server.sockets[self.client_address]
|
del self.server.sockets[self.client_address]
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +63,8 @@ class UiServer:
|
||||||
|
|
||||||
# Reload the UiRequest class to prevent restarts in debug mode
|
# Reload the UiRequest class to prevent restarts in debug mode
|
||||||
def reload(self):
|
def reload(self):
|
||||||
import imp
|
import imp, sys
|
||||||
|
reload(sys.modules["User.UserManager"])
|
||||||
self.ui_request = imp.load_source("UiRequest", "src/Ui/UiRequest.py").UiRequest(self)
|
self.ui_request = imp.load_source("UiRequest", "src/Ui/UiRequest.py").UiRequest(self)
|
||||||
self.ui_request.reload()
|
self.ui_request.reload()
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ from Config import config
|
||||||
from Site import SiteManager
|
from Site import SiteManager
|
||||||
from Debug import Debug
|
from Debug import Debug
|
||||||
from util import QueryJson
|
from util import QueryJson
|
||||||
|
from Plugin import PluginManager
|
||||||
|
|
||||||
|
@PluginManager.acceptPlugins
|
||||||
class UiWebsocket:
|
class UiWebsocket(object):
|
||||||
def __init__(self, ws, site, server, user):
|
def __init__(self, ws, site, server, user):
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
self.site = site
|
self.site = site
|
||||||
|
@ -41,7 +42,7 @@ class UiWebsocket:
|
||||||
if err.message != 'Connection is already closed':
|
if err.message != 'Connection is already closed':
|
||||||
if config.debug: # Allow websocket errors to appear on /Debug
|
if config.debug: # Allow websocket errors to appear on /Debug
|
||||||
import sys
|
import sys
|
||||||
sys.modules["src.main"].DebugHook.handleError()
|
sys.modules["main"].DebugHook.handleError()
|
||||||
self.log.error("WebSocket error: %s" % Debug.formatException(err))
|
self.log.error("WebSocket error: %s" % Debug.formatException(err))
|
||||||
return "Bye."
|
return "Bye."
|
||||||
|
|
||||||
|
@ -133,10 +134,12 @@ class UiWebsocket:
|
||||||
func = self.actionChannelJoinAllsite
|
func = self.actionChannelJoinAllsite
|
||||||
elif cmd == "serverUpdate" and "ADMIN" in permissions:
|
elif cmd == "serverUpdate" and "ADMIN" in permissions:
|
||||||
func = self.actionServerUpdate
|
func = self.actionServerUpdate
|
||||||
# Unknown command
|
|
||||||
else:
|
else:
|
||||||
self.response(req["id"], "Unknown command: %s" % cmd)
|
func_name = "action" + cmd[0].upper() + cmd[1:]
|
||||||
return
|
func = getattr(self, func_name, None)
|
||||||
|
if not func: # Unknown command
|
||||||
|
self.response(req["id"], "Unknown command: %s" % cmd)
|
||||||
|
return
|
||||||
|
|
||||||
# Support calling as named, unnamed paramters and raw first argument too
|
# Support calling as named, unnamed paramters and raw first argument too
|
||||||
if type(params) is dict:
|
if type(params) is dict:
|
||||||
|
@ -152,7 +155,7 @@ class UiWebsocket:
|
||||||
# Do callback on response {"cmd": "response", "to": message_id, "result": result}
|
# Do callback on response {"cmd": "response", "to": message_id, "result": result}
|
||||||
def actionResponse(self, to, result):
|
def actionResponse(self, to, result):
|
||||||
if to in self.waiting_cb:
|
if to in self.waiting_cb:
|
||||||
self.waiting_cb(result) # Call callback function
|
self.waiting_cb[to](result) # Call callback function
|
||||||
else:
|
else:
|
||||||
self.log.error("Websocket callback not found: %s, %s" % (to, result))
|
self.log.error("Websocket callback not found: %s, %s" % (to, result))
|
||||||
|
|
||||||
|
@ -163,7 +166,7 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Format site info
|
# Format site info
|
||||||
def formatSiteInfo(self, site):
|
def formatSiteInfo(self, site, create_user=True):
|
||||||
content = site.content_manager.contents.get("content.json")
|
content = site.content_manager.contents.get("content.json")
|
||||||
if content: # Remove unnecessary data transfer
|
if content: # Remove unnecessary data transfer
|
||||||
content = content.copy()
|
content = content.copy()
|
||||||
|
@ -179,7 +182,7 @@ class UiWebsocket:
|
||||||
ret = {
|
ret = {
|
||||||
"auth_key": self.site.settings["auth_key"], # Obsolete, will be removed
|
"auth_key": self.site.settings["auth_key"], # Obsolete, will be removed
|
||||||
"auth_key_sha512": hashlib.sha512(self.site.settings["auth_key"]).hexdigest()[0:64], # Obsolete, will be removed
|
"auth_key_sha512": hashlib.sha512(self.site.settings["auth_key"]).hexdigest()[0:64], # Obsolete, will be removed
|
||||||
"auth_address": self.user.getAuthAddress(site.address),
|
"auth_address": self.user.getAuthAddress(site.address, create=create_user),
|
||||||
"address": site.address,
|
"address": site.address,
|
||||||
"settings": settings,
|
"settings": settings,
|
||||||
"content_updated": site.content_updated,
|
"content_updated": site.content_updated,
|
||||||
|
@ -208,9 +211,8 @@ class UiWebsocket:
|
||||||
self.channels.append(channel)
|
self.channels.append(channel)
|
||||||
|
|
||||||
|
|
||||||
# Server variables
|
def formatServerInfo(self):
|
||||||
def actionServerInfo(self, to):
|
return {
|
||||||
ret = {
|
|
||||||
"ip_external": bool(config.ip_external),
|
"ip_external": bool(config.ip_external),
|
||||||
"platform": sys.platform,
|
"platform": sys.platform,
|
||||||
"fileserver_ip": config.fileserver_ip,
|
"fileserver_ip": config.fileserver_ip,
|
||||||
|
@ -220,6 +222,11 @@ class UiWebsocket:
|
||||||
"version": config.version,
|
"version": config.version,
|
||||||
"debug": config.debug
|
"debug": config.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Server variables
|
||||||
|
def actionServerInfo(self, to):
|
||||||
|
ret = self.formatServerInfo()
|
||||||
self.response(to, ret)
|
self.response(to, ret)
|
||||||
|
|
||||||
|
|
||||||
|
@ -323,7 +330,7 @@ class UiWebsocket:
|
||||||
SiteManager.load() # Reload sites
|
SiteManager.load() # Reload sites
|
||||||
for site in self.server.sites.values():
|
for site in self.server.sites.values():
|
||||||
if not site.content_manager.contents.get("content.json"): continue # Broken site
|
if not site.content_manager.contents.get("content.json"): continue # Broken site
|
||||||
ret.append(self.formatSiteInfo(site))
|
ret.append(self.formatSiteInfo(site, create_user=False))
|
||||||
self.response(to, ret)
|
self.response(to, ret)
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,7 +402,7 @@ class UiWebsocket:
|
||||||
def actionServerUpdate(self, to):
|
def actionServerUpdate(self, to):
|
||||||
import sys
|
import sys
|
||||||
self.cmd("updating")
|
self.cmd("updating")
|
||||||
sys.modules["src.main"].update_after_shutdown = True
|
sys.modules["main"].update_after_shutdown = True
|
||||||
sys.modules["src.main"].file_server.stop()
|
sys.modules["main"].file_server.stop()
|
||||||
sys.modules["src.main"].ui_server.stop()
|
sys.modules["main"].ui_server.stop()
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Notifications
|
||||||
$(".notification-icon", elem).html("i")
|
$(".notification-icon", elem).html("i")
|
||||||
|
|
||||||
if typeof(body) == "string"
|
if typeof(body) == "string"
|
||||||
$(".body", elem).html(body)
|
$(".body", elem).html("<span class='message'>"+body+"</span>")
|
||||||
else
|
else
|
||||||
$(".body", elem).html("").append(body)
|
$(".body", elem).html("").append(body)
|
||||||
|
|
||||||
|
@ -49,9 +49,11 @@ class Notifications
|
||||||
# Animate
|
# Animate
|
||||||
width = elem.outerWidth()
|
width = elem.outerWidth()
|
||||||
if not timeout then width += 20 # Add space for close button
|
if not timeout then width += 20 # Add space for close button
|
||||||
|
if elem.outerHeight() > 55 then elem.addClass("long")
|
||||||
elem.css({"width": "50px", "transform": "scale(0.01)"})
|
elem.css({"width": "50px", "transform": "scale(0.01)"})
|
||||||
elem.animate({"scale": 1}, 800, "easeOutElastic")
|
elem.animate({"scale": 1}, 800, "easeOutElastic")
|
||||||
elem.animate({"width": width}, 700, "easeInOutCubic")
|
elem.animate({"width": width}, 700, "easeInOutCubic")
|
||||||
|
$(".body", elem).cssLater("box-shadow", "0px 0px 5px rgba(0,0,0,0.1)", 1000)
|
||||||
|
|
||||||
# Close button
|
# Close button
|
||||||
$(".close", elem).on "click", =>
|
$(".close", elem).on "click", =>
|
||||||
|
|
|
@ -42,6 +42,9 @@ class Wrapper
|
||||||
@sendInner message # Pass message to inner frame
|
@sendInner message # Pass message to inner frame
|
||||||
else if cmd == "notification" # Display notification
|
else if cmd == "notification" # Display notification
|
||||||
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
|
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
|
||||||
|
else if cmd == "prompt" # Prompt input
|
||||||
|
@displayPrompt message.params[0], message.params[1], message.params[2], (res) =>
|
||||||
|
@ws.response message.id, res
|
||||||
else if cmd == "setSiteInfo"
|
else if cmd == "setSiteInfo"
|
||||||
@sendInner message # Pass to inner frame
|
@sendInner message # Pass to inner frame
|
||||||
if message.params.address == window.address # Current page
|
if message.params.address == window.address # Current page
|
||||||
|
@ -63,14 +66,15 @@ class Wrapper
|
||||||
@sendInner {"cmd": "wrapperOpenedWebsocket"}
|
@sendInner {"cmd": "wrapperOpenedWebsocket"}
|
||||||
@wrapperWsInited = true
|
@wrapperWsInited = true
|
||||||
else if cmd == "wrapperNotification" # Display notification
|
else if cmd == "wrapperNotification" # Display notification
|
||||||
message.params = @toHtmlSafe(message.params) # Escape html
|
@actionNotification(message)
|
||||||
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
|
|
||||||
else if cmd == "wrapperConfirm" # Display confirm message
|
else if cmd == "wrapperConfirm" # Display confirm message
|
||||||
@actionWrapperConfirm(message)
|
@actionConfirm(message)
|
||||||
else if cmd == "wrapperPrompt" # Prompt input
|
else if cmd == "wrapperPrompt" # Prompt input
|
||||||
@actionWrapperPrompt(message)
|
@actionPrompt(message)
|
||||||
else if cmd == "wrapperSetViewport" # Set the viewport
|
else if cmd == "wrapperSetViewport" # Set the viewport
|
||||||
@actionSetViewport(message)
|
@actionSetViewport(message)
|
||||||
|
else if cmd == "wrapperReload" # Reload current page
|
||||||
|
@actionReload(message)
|
||||||
else if cmd == "wrapperGetLocalStorage"
|
else if cmd == "wrapperGetLocalStorage"
|
||||||
@actionGetLocalStorage(message)
|
@actionGetLocalStorage(message)
|
||||||
else if cmd == "wrapperSetLocalStorage"
|
else if cmd == "wrapperSetLocalStorage"
|
||||||
|
@ -84,46 +88,58 @@ class Wrapper
|
||||||
|
|
||||||
# - Actions -
|
# - Actions -
|
||||||
|
|
||||||
actionWrapperConfirm: (message, cb=false) ->
|
actionNotification: (message) ->
|
||||||
message.params = @toHtmlSafe(message.params) # Escape html
|
message.params = @toHtmlSafe(message.params) # Escape html
|
||||||
if message.params[1] then caption = message.params[1] else caption = "ok"
|
body = $("<span class='message'>"+message.params[1]+"</span>")
|
||||||
@wrapperConfirm message.params[0], caption, =>
|
@notifications.add("notification-#{message.id}", message.params[0], body, message.params[2])
|
||||||
@sendInner {"cmd": "response", "to": message.id, "result": "boom"} # Response to confirm
|
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
wrapperConfirm: (message, caption, cb) ->
|
|
||||||
body = $("<span>"+message+"</span>")
|
displayConfirm: (message, caption, cb) ->
|
||||||
|
body = $("<span class='message'>"+message+"</span>")
|
||||||
button = $("<a href='##{caption}' class='button button-#{caption}'>#{caption}</a>") # Add confirm button
|
button = $("<a href='##{caption}' class='button button-#{caption}'>#{caption}</a>") # Add confirm button
|
||||||
button.on "click", cb
|
button.on "click", cb
|
||||||
body.append(button)
|
body.append(button)
|
||||||
@notifications.add("notification-#{caption}", "ask", body)
|
@notifications.add("notification-#{caption}", "ask", body)
|
||||||
|
|
||||||
|
|
||||||
|
actionConfirm: (message, cb=false) ->
|
||||||
actionWrapperPrompt: (message) ->
|
|
||||||
message.params = @toHtmlSafe(message.params) # Escape html
|
message.params = @toHtmlSafe(message.params) # Escape html
|
||||||
if message.params[1] then type = message.params[1] else type = "text"
|
if message.params[1] then caption = message.params[1] else caption = "ok"
|
||||||
caption = "OK"
|
@displayConfirm message.params[0], caption, =>
|
||||||
|
@sendInner {"cmd": "response", "to": message.id, "result": "boom"} # Response to confirm
|
||||||
|
return false
|
||||||
|
|
||||||
body = $("<span>"+message.params[0]+"</span>")
|
|
||||||
|
|
||||||
|
displayPrompt: (message, type, caption, cb) ->
|
||||||
|
body = $("<span class='message'>"+message+"</span>")
|
||||||
|
|
||||||
input = $("<input type='#{type}' class='input button-#{type}'/>") # Add input
|
input = $("<input type='#{type}' class='input button-#{type}'/>") # Add input
|
||||||
input.on "keyup", (e) => # Send on enter
|
input.on "keyup", (e) => # Send on enter
|
||||||
if e.keyCode == 13
|
if e.keyCode == 13
|
||||||
button.trigger "click" # Response to confirm
|
button.trigger "click" # Response to confirm
|
||||||
|
|
||||||
body.append(input)
|
body.append(input)
|
||||||
|
|
||||||
button = $("<a href='##{caption}' class='button button-#{caption}'>#{caption}</a>") # Add confirm button
|
button = $("<a href='##{caption}' class='button button-#{caption}'>#{caption}</a>") # Add confirm button
|
||||||
button.on "click", => # Response on button click
|
button.on "click", => # Response on button click
|
||||||
@sendInner {"cmd": "response", "to": message.id, "result": input.val()} # Response to confirm
|
cb input.val()
|
||||||
return false
|
return false
|
||||||
body.append(button)
|
body.append(button)
|
||||||
|
|
||||||
@notifications.add("notification-#{message.id}", "ask", body)
|
@notifications.add("notification-#{message.id}", "ask", body)
|
||||||
|
|
||||||
|
|
||||||
|
actionPrompt: (message) ->
|
||||||
|
message.params = @toHtmlSafe(message.params) # Escape html
|
||||||
|
if message.params[1] then type = message.params[1] else type = "text"
|
||||||
|
caption = "OK"
|
||||||
|
|
||||||
|
@displayPrompt message.params[0], type, caption, (res) =>
|
||||||
|
@sendInner {"cmd": "response", "to": message.id, "result": res} # Response to confirm
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
actionSetViewport: (message) ->
|
actionSetViewport: (message) ->
|
||||||
@log "actionSetViewport", message
|
@log "actionSetViewport", message
|
||||||
if $("#viewport").length > 0
|
if $("#viewport").length > 0
|
||||||
|
@ -132,6 +148,16 @@ class Wrapper
|
||||||
$('<meta name="viewport" id="viewport">').attr("content", @toHtmlSafe message.params).appendTo("head")
|
$('<meta name="viewport" id="viewport">').attr("content", @toHtmlSafe message.params).appendTo("head")
|
||||||
|
|
||||||
|
|
||||||
|
reload: (url_post="") ->
|
||||||
|
if url_post
|
||||||
|
if window.location.toString().indexOf("?") > 0
|
||||||
|
window.location += "&"+url_post
|
||||||
|
else
|
||||||
|
window.location += "?"+url_post
|
||||||
|
else
|
||||||
|
window.location.reload()
|
||||||
|
|
||||||
|
|
||||||
actionGetLocalStorage: (message) ->
|
actionGetLocalStorage: (message) ->
|
||||||
data = localStorage.getItem "site.#{window.address}"
|
data = localStorage.getItem "site.#{window.address}"
|
||||||
if data then data = JSON.parse(data)
|
if data then data = JSON.parse(data)
|
||||||
|
|
|
@ -33,21 +33,28 @@ a { color: black }
|
||||||
|
|
||||||
/* Notification */
|
/* Notification */
|
||||||
|
|
||||||
.notifications { position: absolute; top: 0px; right: 85px; display: inline-block; z-index: 999; white-space: nowrap }
|
.notifications { position: absolute; top: 0px; right: 80px; display: inline-block; z-index: 999; white-space: nowrap }
|
||||||
.notification {
|
.notification {
|
||||||
position: relative; float: right; clear: both; margin: 10px; height: 50px; box-sizing: border-box; overflow: hidden; backface-visibility: hidden; perspective: 1000px;
|
position: relative; float: right; clear: both; margin: 10px; box-sizing: border-box; overflow: hidden; backface-visibility: hidden; perspective: 1000px; padding-bottom: 5px;
|
||||||
background-color: white; color: #4F4F4F; font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/
|
color: #4F4F4F; font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/
|
||||||
}
|
}
|
||||||
.notification-icon {
|
.notification-icon {
|
||||||
display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1;
|
display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1;
|
||||||
text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white;
|
text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white;
|
||||||
}
|
}
|
||||||
.notification .body { max-width: 420px; padding-left: 68px; padding-right: 17px; height: 50px; vertical-align: middle; display: table-cell }
|
.notification .body {
|
||||||
|
max-width: 420px; padding-left: 14px; padding-right: 60px; height: 40px; vertical-align: middle; display: table;
|
||||||
|
background-color: white; left: 50px; top: 0px; position: relative; padding-top: 5px; padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
.notification.long .body { padding-top: 10px; padding-bottom: 10px }
|
||||||
|
.notification .message { display: table-cell; vertical-align: middle }
|
||||||
|
|
||||||
.notification.visible { max-width: 350px }
|
.notification.visible { max-width: 350px }
|
||||||
|
|
||||||
.notification .close { position: absolute; top: 0px; right: 0px; font-size: 19px; line-height: 13px; color: #DDD; padding: 7px; text-decoration: none }
|
.notification .close { position: absolute; top: 0px; right: 0px; font-size: 19px; line-height: 13px; color: #DDD; padding: 7px; text-decoration: none }
|
||||||
.notification .close:hover { color: black }
|
.notification .close:hover { color: black }
|
||||||
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
||||||
|
.notification small { color: #AAA }
|
||||||
.body-white .notification { box-shadow: 0px 1px 9px rgba(0,0,0,0.1) }
|
.body-white .notification { box-shadow: 0px 1px 9px rgba(0,0,0,0.1) }
|
||||||
|
|
||||||
/* Notification types */
|
/* Notification types */
|
||||||
|
|
|
@ -38,21 +38,28 @@ a { color: black }
|
||||||
|
|
||||||
/* Notification */
|
/* Notification */
|
||||||
|
|
||||||
.notifications { position: absolute; top: 0px; right: 85px; display: inline-block; z-index: 999; white-space: nowrap }
|
.notifications { position: absolute; top: 0px; right: 80px; display: inline-block; z-index: 999; white-space: nowrap }
|
||||||
.notification {
|
.notification {
|
||||||
position: relative; float: right; clear: both; margin: 10px; height: 50px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; overflow: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ;
|
position: relative; float: right; clear: both; margin: 10px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; overflow: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ; padding-bottom: 5px;
|
||||||
background-color: white; color: #4F4F4F; font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/
|
color: #4F4F4F; font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/
|
||||||
}
|
}
|
||||||
.notification-icon {
|
.notification-icon {
|
||||||
display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1;
|
display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1;
|
||||||
text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white;
|
text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white;
|
||||||
}
|
}
|
||||||
.notification .body { max-width: 420px; padding-left: 68px; padding-right: 17px; height: 50px; vertical-align: middle; display: table-cell }
|
.notification .body {
|
||||||
|
max-width: 420px; padding-left: 14px; padding-right: 60px; height: 40px; vertical-align: middle; display: table;
|
||||||
|
background-color: white; left: 50px; top: 0px; position: relative; padding-top: 5px; padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
.notification.long .body { padding-top: 10px; padding-bottom: 10px }
|
||||||
|
.notification .message { display: table-cell; vertical-align: middle }
|
||||||
|
|
||||||
.notification.visible { max-width: 350px }
|
.notification.visible { max-width: 350px }
|
||||||
|
|
||||||
.notification .close { position: absolute; top: 0px; right: 0px; font-size: 19px; line-height: 13px; color: #DDD; padding: 7px; text-decoration: none }
|
.notification .close { position: absolute; top: 0px; right: 0px; font-size: 19px; line-height: 13px; color: #DDD; padding: 7px; text-decoration: none }
|
||||||
.notification .close:hover { color: black }
|
.notification .close:hover { color: black }
|
||||||
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
||||||
|
.notification small { color: #AAA }
|
||||||
.body-white .notification { -webkit-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; -moz-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; -o-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; -ms-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; box-shadow: 0px 1px 9px rgba(0,0,0,0.1) }
|
.body-white .notification { -webkit-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; -moz-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; -o-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; -ms-box-shadow: 0px 1px 9px rgba(0,0,0,0.1) ; box-shadow: 0px 1px 9px rgba(0,0,0,0.1) }
|
||||||
|
|
||||||
/* Notification types */
|
/* Notification types */
|
||||||
|
|
|
@ -149,7 +149,6 @@
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ---- src/Ui/media/lib/jquery.cssanim.js ---- */
|
/* ---- src/Ui/media/lib/jquery.cssanim.js ---- */
|
||||||
|
|
||||||
|
|
||||||
|
@ -247,7 +246,6 @@ jQuery.fx.step.scale = function(fx) {
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ---- src/Ui/media/lib/jquery.easing.1.3.js ---- */
|
/* ---- src/Ui/media/lib/jquery.easing.1.3.js ---- */
|
||||||
|
|
||||||
|
|
||||||
|
@ -542,7 +540,6 @@ jQuery.extend( jQuery.easing,
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ---- src/Ui/media/Notifications.coffee ---- */
|
/* ---- src/Ui/media/Notifications.coffee ---- */
|
||||||
|
|
||||||
|
|
||||||
|
@ -593,7 +590,7 @@ jQuery.extend( jQuery.easing,
|
||||||
$(".notification-icon", elem).html("i");
|
$(".notification-icon", elem).html("i");
|
||||||
}
|
}
|
||||||
if (typeof body === "string") {
|
if (typeof body === "string") {
|
||||||
$(".body", elem).html(body);
|
$(".body", elem).html("<span class='message'>" + body + "</span>");
|
||||||
} else {
|
} else {
|
||||||
$(".body", elem).html("").append(body);
|
$(".body", elem).html("").append(body);
|
||||||
}
|
}
|
||||||
|
@ -610,6 +607,9 @@ jQuery.extend( jQuery.easing,
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
width += 20;
|
width += 20;
|
||||||
}
|
}
|
||||||
|
if (elem.outerHeight() > 55) {
|
||||||
|
elem.addClass("long");
|
||||||
|
}
|
||||||
elem.css({
|
elem.css({
|
||||||
"width": "50px",
|
"width": "50px",
|
||||||
"transform": "scale(0.01)"
|
"transform": "scale(0.01)"
|
||||||
|
@ -620,6 +620,7 @@ jQuery.extend( jQuery.easing,
|
||||||
elem.animate({
|
elem.animate({
|
||||||
"width": width
|
"width": width
|
||||||
}, 700, "easeInOutCubic");
|
}, 700, "easeInOutCubic");
|
||||||
|
$(".body", elem).cssLater("box-shadow", "0px 0px 5px rgba(0,0,0,0.1)", 1000);
|
||||||
$(".close", elem).on("click", (function(_this) {
|
$(".close", elem).on("click", (function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
_this.close(elem);
|
_this.close(elem);
|
||||||
|
@ -725,7 +726,6 @@ jQuery.extend( jQuery.easing,
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ---- src/Ui/media/Wrapper.coffee ---- */
|
/* ---- src/Ui/media/Wrapper.coffee ---- */
|
||||||
|
|
||||||
|
|
||||||
|
@ -786,6 +786,12 @@ jQuery.extend( jQuery.easing,
|
||||||
}
|
}
|
||||||
} else if (cmd === "notification") {
|
} else if (cmd === "notification") {
|
||||||
return this.notifications.add("notification-" + message.id, message.params[0], message.params[1], message.params[2]);
|
return this.notifications.add("notification-" + message.id, message.params[0], message.params[1], message.params[2]);
|
||||||
|
} else if (cmd === "prompt") {
|
||||||
|
return this.displayPrompt(message.params[0], message.params[1], message.params[2], (function(_this) {
|
||||||
|
return function(res) {
|
||||||
|
return _this.ws.response(message.id, res);
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
} else if (cmd === "setSiteInfo") {
|
} else if (cmd === "setSiteInfo") {
|
||||||
this.sendInner(message);
|
this.sendInner(message);
|
||||||
if (message.params.address === window.address) {
|
if (message.params.address === window.address) {
|
||||||
|
@ -812,14 +818,15 @@ jQuery.extend( jQuery.easing,
|
||||||
return this.wrapperWsInited = true;
|
return this.wrapperWsInited = true;
|
||||||
}
|
}
|
||||||
} else if (cmd === "wrapperNotification") {
|
} else if (cmd === "wrapperNotification") {
|
||||||
message.params = this.toHtmlSafe(message.params);
|
return this.actionNotification(message);
|
||||||
return this.notifications.add("notification-" + message.id, message.params[0], message.params[1], message.params[2]);
|
|
||||||
} else if (cmd === "wrapperConfirm") {
|
} else if (cmd === "wrapperConfirm") {
|
||||||
return this.actionWrapperConfirm(message);
|
return this.actionConfirm(message);
|
||||||
} else if (cmd === "wrapperPrompt") {
|
} else if (cmd === "wrapperPrompt") {
|
||||||
return this.actionWrapperPrompt(message);
|
return this.actionPrompt(message);
|
||||||
} else if (cmd === "wrapperSetViewport") {
|
} else if (cmd === "wrapperSetViewport") {
|
||||||
return this.actionSetViewport(message);
|
return this.actionSetViewport(message);
|
||||||
|
} else if (cmd === "wrapperReload") {
|
||||||
|
return this.actionReload(message);
|
||||||
} else if (cmd === "wrapperGetLocalStorage") {
|
} else if (cmd === "wrapperGetLocalStorage") {
|
||||||
return this.actionGetLocalStorage(message);
|
return this.actionGetLocalStorage(message);
|
||||||
} else if (cmd === "wrapperSetLocalStorage") {
|
} else if (cmd === "wrapperSetLocalStorage") {
|
||||||
|
@ -833,7 +840,23 @@ jQuery.extend( jQuery.easing,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Wrapper.prototype.actionWrapperConfirm = function(message, cb) {
|
Wrapper.prototype.actionNotification = function(message) {
|
||||||
|
var body;
|
||||||
|
message.params = this.toHtmlSafe(message.params);
|
||||||
|
body = $("<span class='message'>" + message.params[1] + "</span>");
|
||||||
|
return this.notifications.add("notification-" + message.id, message.params[0], body, message.params[2]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Wrapper.prototype.displayConfirm = function(message, caption, cb) {
|
||||||
|
var body, button;
|
||||||
|
body = $("<span class='message'>" + message + "</span>");
|
||||||
|
button = $("<a href='#" + caption + "' class='button button-" + caption + "'>" + caption + "</a>");
|
||||||
|
button.on("click", cb);
|
||||||
|
body.append(button);
|
||||||
|
return this.notifications.add("notification-" + caption, "ask", body);
|
||||||
|
};
|
||||||
|
|
||||||
|
Wrapper.prototype.actionConfirm = function(message, cb) {
|
||||||
var caption;
|
var caption;
|
||||||
if (cb == null) {
|
if (cb == null) {
|
||||||
cb = false;
|
cb = false;
|
||||||
|
@ -844,7 +867,7 @@ jQuery.extend( jQuery.easing,
|
||||||
} else {
|
} else {
|
||||||
caption = "ok";
|
caption = "ok";
|
||||||
}
|
}
|
||||||
return this.wrapperConfirm(message.params[0], caption, (function(_this) {
|
return this.displayConfirm(message.params[0], caption, (function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
_this.sendInner({
|
_this.sendInner({
|
||||||
"cmd": "response",
|
"cmd": "response",
|
||||||
|
@ -856,25 +879,9 @@ jQuery.extend( jQuery.easing,
|
||||||
})(this));
|
})(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Wrapper.prototype.wrapperConfirm = function(message, caption, cb) {
|
Wrapper.prototype.displayPrompt = function(message, type, caption, cb) {
|
||||||
var body, button;
|
var body, button, input;
|
||||||
body = $("<span>" + message + "</span>");
|
body = $("<span class='message'>" + message + "</span>");
|
||||||
button = $("<a href='#" + caption + "' class='button button-" + caption + "'>" + caption + "</a>");
|
|
||||||
button.on("click", cb);
|
|
||||||
body.append(button);
|
|
||||||
return this.notifications.add("notification-" + caption, "ask", body);
|
|
||||||
};
|
|
||||||
|
|
||||||
Wrapper.prototype.actionWrapperPrompt = function(message) {
|
|
||||||
var body, button, caption, input, type;
|
|
||||||
message.params = this.toHtmlSafe(message.params);
|
|
||||||
if (message.params[1]) {
|
|
||||||
type = message.params[1];
|
|
||||||
} else {
|
|
||||||
type = "text";
|
|
||||||
}
|
|
||||||
caption = "OK";
|
|
||||||
body = $("<span>" + message.params[0] + "</span>");
|
|
||||||
input = $("<input type='" + type + "' class='input button-" + type + "'/>");
|
input = $("<input type='" + type + "' class='input button-" + type + "'/>");
|
||||||
input.on("keyup", (function(_this) {
|
input.on("keyup", (function(_this) {
|
||||||
return function(e) {
|
return function(e) {
|
||||||
|
@ -887,11 +894,7 @@ jQuery.extend( jQuery.easing,
|
||||||
button = $("<a href='#" + caption + "' class='button button-" + caption + "'>" + caption + "</a>");
|
button = $("<a href='#" + caption + "' class='button button-" + caption + "'>" + caption + "</a>");
|
||||||
button.on("click", (function(_this) {
|
button.on("click", (function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
_this.sendInner({
|
cb(input.val());
|
||||||
"cmd": "response",
|
|
||||||
"to": message.id,
|
|
||||||
"result": input.val()
|
|
||||||
});
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
})(this));
|
})(this));
|
||||||
|
@ -899,6 +902,26 @@ jQuery.extend( jQuery.easing,
|
||||||
return this.notifications.add("notification-" + message.id, "ask", body);
|
return this.notifications.add("notification-" + message.id, "ask", body);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wrapper.prototype.actionPrompt = function(message) {
|
||||||
|
var caption, type;
|
||||||
|
message.params = this.toHtmlSafe(message.params);
|
||||||
|
if (message.params[1]) {
|
||||||
|
type = message.params[1];
|
||||||
|
} else {
|
||||||
|
type = "text";
|
||||||
|
}
|
||||||
|
caption = "OK";
|
||||||
|
return this.displayPrompt(message.params[0], type, caption, (function(_this) {
|
||||||
|
return function(res) {
|
||||||
|
return _this.sendInner({
|
||||||
|
"cmd": "response",
|
||||||
|
"to": message.id,
|
||||||
|
"result": res
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
};
|
||||||
|
|
||||||
Wrapper.prototype.actionSetViewport = function(message) {
|
Wrapper.prototype.actionSetViewport = function(message) {
|
||||||
this.log("actionSetViewport", message);
|
this.log("actionSetViewport", message);
|
||||||
if ($("#viewport").length > 0) {
|
if ($("#viewport").length > 0) {
|
||||||
|
@ -908,6 +931,21 @@ jQuery.extend( jQuery.easing,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wrapper.prototype.reload = function(url_post) {
|
||||||
|
if (url_post == null) {
|
||||||
|
url_post = "";
|
||||||
|
}
|
||||||
|
if (url_post) {
|
||||||
|
if (window.location.toString().indexOf("?") > 0) {
|
||||||
|
return window.location += "&" + url_post;
|
||||||
|
} else {
|
||||||
|
return window.location += "?" + url_post;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Wrapper.prototype.actionGetLocalStorage = function(message) {
|
Wrapper.prototype.actionGetLocalStorage = function(message) {
|
||||||
var data;
|
var data;
|
||||||
data = localStorage.getItem("site." + window.address);
|
data = localStorage.getItem("site." + window.address);
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import logging, json, time
|
import logging, json, time
|
||||||
from Crypt import CryptBitcoin
|
from Crypt import CryptBitcoin
|
||||||
|
from Plugin import PluginManager
|
||||||
|
|
||||||
class User:
|
@PluginManager.acceptPlugins
|
||||||
def __init__(self, master_address=None):
|
class User(object):
|
||||||
if master_address:
|
def __init__(self, master_address=None, master_seed=None):
|
||||||
|
if master_seed:
|
||||||
|
self.master_seed = master_seed
|
||||||
|
self.master_address = CryptBitcoin.privatekeyToAddress(self.master_seed)
|
||||||
|
elif master_address:
|
||||||
self.master_address = master_address
|
self.master_address = master_address
|
||||||
self.master_seed = None
|
self.master_seed = None
|
||||||
else:
|
else:
|
||||||
|
@ -27,8 +32,9 @@ class User:
|
||||||
|
|
||||||
# Get user site data
|
# Get user site data
|
||||||
# Return: {"auth_address": "xxx", "auth_privatekey": "xxx"}
|
# Return: {"auth_address": "xxx", "auth_privatekey": "xxx"}
|
||||||
def getSiteData(self, address):
|
def getSiteData(self, address, create=True):
|
||||||
if not address in self.sites: # Genreate new BIP32 child key based on site address
|
if not address in self.sites: # Genreate new BIP32 child key based on site address
|
||||||
|
if not create: return {"auth_address": None, "auth_privatekey": None} # Dont create user yet
|
||||||
s = time.time()
|
s = time.time()
|
||||||
address_id = int(address.encode("hex"), 16) # Convert site address to int
|
address_id = int(address.encode("hex"), 16) # Convert site address to int
|
||||||
auth_privatekey = CryptBitcoin.hdPrivatekey(self.master_seed, address_id)
|
auth_privatekey = CryptBitcoin.hdPrivatekey(self.master_seed, address_id)
|
||||||
|
@ -43,17 +49,15 @@ class User:
|
||||||
|
|
||||||
# Get BIP32 address from site address
|
# Get BIP32 address from site address
|
||||||
# Return: BIP32 auth address
|
# Return: BIP32 auth address
|
||||||
def getAuthAddress(self, address):
|
def getAuthAddress(self, address, create=True):
|
||||||
return self.getSiteData(address)["auth_address"]
|
return self.getSiteData(address, create)["auth_address"]
|
||||||
|
|
||||||
|
|
||||||
def getAuthPrivatekey(self, address):
|
def getAuthPrivatekey(self, address, create=True):
|
||||||
return self.getSiteData(address)["auth_privatekey"]
|
return self.getSiteData(address, create)["auth_privatekey"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Set user attributes from dict
|
# Set user attributes from dict
|
||||||
def setData(self, data):
|
def setData(self, data):
|
||||||
for key, val in data.items():
|
for key, val in data.items():
|
||||||
setattr(self, key, val)
|
setattr(self, key, val)
|
||||||
|
|
||||||
|
|
|
@ -1,66 +1,79 @@
|
||||||
import json, logging, os
|
import json, logging, os
|
||||||
from User import User
|
from User import User
|
||||||
|
from Plugin import PluginManager
|
||||||
users = None
|
|
||||||
|
|
||||||
# Load all user from data/users.json
|
|
||||||
def load():
|
|
||||||
global users
|
|
||||||
if not users: users = {}
|
|
||||||
|
|
||||||
user_found = []
|
|
||||||
added = 0
|
|
||||||
# Load new users
|
|
||||||
for master_address, data in json.load(open("data/users.json")).items():
|
|
||||||
if master_address not in users:
|
|
||||||
user = User(master_address)
|
|
||||||
user.setData(data)
|
|
||||||
users[master_address] = user
|
|
||||||
added += 1
|
|
||||||
user_found.append(master_address)
|
|
||||||
|
|
||||||
# Remove deleted adresses
|
|
||||||
for master_address in users.keys():
|
|
||||||
if master_address not in user_found:
|
|
||||||
del(users[master_address])
|
|
||||||
logging.debug("Removed user: %s" % master_address)
|
|
||||||
|
|
||||||
if added: logging.debug("UserManager added %s users" % added)
|
|
||||||
|
|
||||||
|
|
||||||
# Create new user
|
@PluginManager.acceptPlugins
|
||||||
# Return: User
|
class UserManager(object):
|
||||||
def create():
|
def __init__(self):
|
||||||
user = User()
|
self.users = {}
|
||||||
logging.debug("Created user: %s" % user.master_address)
|
|
||||||
users[user.master_address] = user
|
|
||||||
user.save()
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
# List all users from data/users.json
|
# Load all user from data/users.json
|
||||||
# Return: {"usermasteraddr": User}
|
def load(self):
|
||||||
def list():
|
if not self.users: self.users = {}
|
||||||
if users == None: # Not loaded yet
|
|
||||||
load()
|
user_found = []
|
||||||
return users
|
added = 0
|
||||||
|
# Load new users
|
||||||
|
for master_address, data in json.load(open("data/users.json")).items():
|
||||||
|
if master_address not in self.users:
|
||||||
|
user = User(master_address)
|
||||||
|
user.setData(data)
|
||||||
|
self.users[master_address] = user
|
||||||
|
added += 1
|
||||||
|
user_found.append(master_address)
|
||||||
|
|
||||||
|
# Remove deleted adresses
|
||||||
|
for master_address in self.users.keys():
|
||||||
|
if master_address not in user_found:
|
||||||
|
del(self.users[master_address])
|
||||||
|
logging.debug("Removed user: %s" % master_address)
|
||||||
|
|
||||||
|
if added: logging.debug("UserManager added %s users" % added)
|
||||||
|
|
||||||
|
|
||||||
# Get current authed user
|
# Create new user
|
||||||
# Return: User
|
# Return: User
|
||||||
def getCurrent():
|
def create(self, master_address=None, master_seed=None):
|
||||||
users = list()
|
user = User(master_address, master_seed)
|
||||||
if users:
|
logging.debug("Created user: %s" % user.master_address)
|
||||||
return users.values()[0]
|
if user.master_address: # If successfully created
|
||||||
else:
|
self.users[user.master_address] = user
|
||||||
return create()
|
user.save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
# List all users from data/users.json
|
||||||
|
# Return: {"usermasteraddr": User}
|
||||||
|
def list(self):
|
||||||
|
if self.users == {}: # Not loaded yet
|
||||||
|
self.load()
|
||||||
|
return self.users
|
||||||
|
|
||||||
|
|
||||||
|
# Get user based on master_address
|
||||||
|
# Return: User or None
|
||||||
|
def get(self, master_address=None):
|
||||||
|
users = self.list()
|
||||||
|
if users:
|
||||||
|
return users.values()[0] # Single user mode, always return the first
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
user_manager = UserManager() # Singletone
|
||||||
|
|
||||||
# Debug: Reload User.py
|
# Debug: Reload User.py
|
||||||
def reload():
|
def reloadModule():
|
||||||
return False # Disabled
|
return "Not used"
|
||||||
"""import imp
|
|
||||||
global users, User
|
import imp
|
||||||
|
global User, UserManager, user_manager
|
||||||
User = imp.load_source("User", "src/User/User.py").User # Reload source
|
User = imp.load_source("User", "src/User/User.py").User # Reload source
|
||||||
users.clear() # Remove all items
|
#module = imp.load_source("UserManager", "src/User/UserManager.py") # Reload module
|
||||||
load()"""
|
#UserManager = module.UserManager
|
||||||
|
#user_manager = module.user_manager
|
||||||
|
# Reload users
|
||||||
|
user_manager = UserManager()
|
||||||
|
user_manager.load()
|
||||||
|
|
29
src/main.py
29
src/main.py
|
@ -1,6 +1,5 @@
|
||||||
import os, sys
|
import os, sys
|
||||||
update_after_shutdown = False
|
update_after_shutdown = False # If set True then update and restart zeronet after main loop ended
|
||||||
sys.path.insert(0, os.path.dirname(__file__)) # Imports relative to main.py
|
|
||||||
|
|
||||||
# Create necessary files and dirs
|
# Create necessary files and dirs
|
||||||
if not os.path.isdir("log"): os.mkdir("log")
|
if not os.path.isdir("log"): os.mkdir("log")
|
||||||
|
@ -11,7 +10,8 @@ if not os.path.isfile("data/users.json"): open("data/users.json", "w").write("{}
|
||||||
# Load config
|
# Load config
|
||||||
from Config import config
|
from Config import config
|
||||||
|
|
||||||
# Init logging
|
|
||||||
|
# Setup logging
|
||||||
import logging
|
import logging
|
||||||
if config.action == "main":
|
if config.action == "main":
|
||||||
if os.path.isfile("log/debug.log"): # Simple logrotate
|
if os.path.isfile("log/debug.log"): # Simple logrotate
|
||||||
|
@ -28,24 +28,35 @@ if config.action == "main": # Add time if main action
|
||||||
else:
|
else:
|
||||||
console_log.setFormatter(logging.Formatter('%(name)s %(message)s', "%H:%M:%S"))
|
console_log.setFormatter(logging.Formatter('%(name)s %(message)s', "%H:%M:%S"))
|
||||||
|
|
||||||
|
|
||||||
logging.getLogger('').addHandler(console_log) # Add console logger
|
logging.getLogger('').addHandler(console_log) # Add console logger
|
||||||
logging.getLogger('').name = "-" # Remove root prefix
|
logging.getLogger('').name = "-" # Remove root prefix
|
||||||
|
|
||||||
|
|
||||||
# Debug dependent configuration
|
# Debug dependent configuration
|
||||||
from Debug import DebugHook
|
from Debug import DebugHook
|
||||||
if config.debug:
|
if config.debug:
|
||||||
console_log.setLevel(logging.DEBUG)
|
console_log.setLevel(logging.DEBUG) # Display everything to console
|
||||||
from gevent import monkey; monkey.patch_all(thread=False) # thread=False because of pyfilesystem
|
from gevent import monkey; monkey.patch_all(thread=False) # thread=False because of pyfilesystem
|
||||||
else:
|
else:
|
||||||
console_log.setLevel(logging.INFO)
|
console_log.setLevel(logging.INFO) # Display only important info to console
|
||||||
from gevent import monkey; monkey.patch_all()
|
from gevent import monkey; monkey.patch_all() # Make time, thread, socket gevent compatible
|
||||||
|
|
||||||
|
|
||||||
import gevent
|
import gevent
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
# Log current config
|
||||||
logging.debug("Config: %s" % config)
|
logging.debug("Config: %s" % config)
|
||||||
|
|
||||||
|
|
||||||
|
# Load plugins
|
||||||
|
from Plugin import PluginManager
|
||||||
|
PluginManager.plugin_manager.loadPlugins()
|
||||||
|
|
||||||
|
|
||||||
|
# -- Actions --
|
||||||
|
|
||||||
# Starts here when running zeronet.py
|
# Starts here when running zeronet.py
|
||||||
def start():
|
def start():
|
||||||
action_func = globals()[config.action] # Function reference
|
action_func = globals()[config.action] # Function reference
|
||||||
|
@ -74,7 +85,7 @@ def main():
|
||||||
|
|
||||||
def siteCreate():
|
def siteCreate():
|
||||||
logging.info("Generating new privatekey...")
|
logging.info("Generating new privatekey...")
|
||||||
from src.Crypt import CryptBitcoin
|
from Crypt import CryptBitcoin
|
||||||
privatekey = CryptBitcoin.newPrivatekey()
|
privatekey = CryptBitcoin.newPrivatekey()
|
||||||
logging.info("----------------------------------------------------------------------")
|
logging.info("----------------------------------------------------------------------")
|
||||||
logging.info("Site private key: %s" % privatekey)
|
logging.info("Site private key: %s" % privatekey)
|
||||||
|
@ -196,7 +207,7 @@ def sitePublish(address, peer_ip=None, peer_port=15441, inner_path="content.json
|
||||||
# Crypto commands
|
# Crypto commands
|
||||||
|
|
||||||
def cryptoPrivatekeyToAddress(privatekey=None):
|
def cryptoPrivatekeyToAddress(privatekey=None):
|
||||||
from src.Crypt import CryptBitcoin
|
from Crypt import CryptBitcoin
|
||||||
if not privatekey: # If no privatekey in args then ask it now
|
if not privatekey: # If no privatekey in args then ask it now
|
||||||
import getpass
|
import getpass
|
||||||
privatekey = getpass.getpass("Private key (input hidden):")
|
privatekey = getpass.getpass("Private key (input hidden):")
|
||||||
|
|
26
update.py
26
update.py
|
@ -16,15 +16,41 @@ def update():
|
||||||
if not buff: break
|
if not buff: break
|
||||||
data.write(buff)
|
data.write(buff)
|
||||||
print ".",
|
print ".",
|
||||||
|
print "Downloaded."
|
||||||
|
|
||||||
|
# Checking plugins
|
||||||
|
plugins_enabled = []
|
||||||
|
plugins_disabled = []
|
||||||
|
if os.path.isdir("plugins"):
|
||||||
|
for dir in os.listdir("plugins"):
|
||||||
|
if dir.startswith("disabled-"):
|
||||||
|
plugins_disabled.append(dir.replace("disabled-", ""))
|
||||||
|
else:
|
||||||
|
plugins_enabled.append(dir)
|
||||||
|
print "Plugins:", plugins_enabled, plugins_disabled
|
||||||
|
|
||||||
|
|
||||||
print "Extracting...",
|
print "Extracting...",
|
||||||
zip = zipfile.ZipFile(data)
|
zip = zipfile.ZipFile(data)
|
||||||
for inner_path in zip.namelist():
|
for inner_path in zip.namelist():
|
||||||
|
inner_path = inner_path.replace("\\", "/") # Make sure we have unix path
|
||||||
print ".",
|
print ".",
|
||||||
dest_path = inner_path.replace("ZeroNet-master/", "")
|
dest_path = inner_path.replace("ZeroNet-master/", "")
|
||||||
if not dest_path: continue
|
if not dest_path: continue
|
||||||
|
|
||||||
|
|
||||||
|
# Keep plugin disabled/enabled status
|
||||||
|
match = re.match("plugins/([^/]+)", dest_path)
|
||||||
|
if match:
|
||||||
|
plugin_name = match.group(1).replace("disabled-","")
|
||||||
|
if plugin_name in plugins_enabled: # Plugin was enabled
|
||||||
|
dest_path = dest_path.replace("plugins/disabled-"+plugin_name, "plugins/"+plugin_name)
|
||||||
|
else: # Plugin was disabled
|
||||||
|
dest_path = dest_path.replace("plugins/"+plugin_name, "plugins/disabled-"+plugin_name)
|
||||||
|
print "P",
|
||||||
|
|
||||||
dest_dir = os.path.dirname(dest_path)
|
dest_dir = os.path.dirname(dest_path)
|
||||||
|
|
||||||
if dest_dir and not os.path.isdir(dest_dir):
|
if dest_dir and not os.path.isdir(dest_dir):
|
||||||
os.makedirs(dest_dir)
|
os.makedirs(dest_dir)
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print " - Starging ZeroNet..."
|
print " - Starging ZeroNet..."
|
||||||
|
import sys, os
|
||||||
try:
|
try:
|
||||||
from src import main
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) # Imports relative to src
|
||||||
|
import main
|
||||||
main.start()
|
main.start()
|
||||||
if main.update_after_shutdown: # Updater
|
if main.update_after_shutdown: # Updater
|
||||||
import update, sys, os, gc
|
import update, sys, os, gc
|
||||||
|
|
Loading…
Reference in a new issue