diff --git a/plugins/Stats/StatsPlugin.py b/plugins/Stats/StatsPlugin.py
index e6fa9afa..f8976410 100644
--- a/plugins/Stats/StatsPlugin.py
+++ b/plugins/Stats/StatsPlugin.py
@@ -119,7 +119,7 @@ class UiRequestPlugin(object):
yield ""
- # Objects
+ # Object types
obj_count = {}
for obj in gc.get_objects():
@@ -135,6 +135,24 @@ class UiRequestPlugin(object):
yield " - %.1fkb = %s x %s
" % (stat[1], stat[0], obj, cgi.escape(obj))
+ # Classes
+
+ class_count = {}
+ for obj in gc.get_objects():
+ obj_type = str(type(obj))
+ if obj_type != "": continue
+ class_name = obj.__class__.__name__
+ if not class_name in class_count:
+ class_count[class_name] = [0, 0]
+ class_count[class_name][0] += 1 # Count
+ class_count[class_name][1] += float(sys.getsizeof(obj))/1024 # Size
+
+ yield "
Classes in memory (types: %s, total: %s, %.2fkb):
" % (len(class_count), sum([stat[0] for stat in class_count.values()]), sum([stat[1] for stat in class_count.values()]))
+
+ for obj, stat in sorted(class_count.items(), key=lambda x: x[1][0], reverse=True): # Sorted by count
+ yield " - %.1fkb = %s x %s
" % (stat[1], stat[0], obj, cgi.escape(obj))
+
+
from greenlet import greenlet
objs = [obj for obj in gc.get_objects() if isinstance(obj, greenlet)]
yield "
Greenlets (%s):
" % len(objs)
@@ -203,6 +221,29 @@ class UiRequestPlugin(object):
yield "Done in %.1f" % (time.time()-s)
+ def actionDumpobj(self):
+ import gc, sys
+
+ self.sendHeader()
+ class_filter = self.get.get("class")
+
+ yield """
+
+ """
+
+ objs = gc.get_objects()
+ for obj in objs:
+ obj_type = str(type(obj))
+ if obj_type != "" or obj.__class__.__name__ != class_filter: continue
+ yield "%.1fkb %s... " % (float(sys.getsizeof(obj))/1024, cgi.escape(str(obj)) )
+ for attr in dir(obj):
+ yield "- %s: %s
" % (attr, cgi.escape(str(getattr(obj, attr))))
+ yield "
"
+
+
def actionListobj(self):
import gc, sys
diff --git a/src/Config.py b/src/Config.py
index 2e872935..2d9e47ae 100644
--- a/src/Config.py
+++ b/src/Config.py
@@ -4,7 +4,7 @@ import ConfigParser
class Config(object):
def __init__(self):
self.version = "0.2.9"
- self.rev = 122
+ self.rev = 125
self.parser = self.createArguments()
argv = sys.argv[:] # Copy command line arguments
argv = self.parseConfig(argv) # Add arguments from config file
@@ -16,6 +16,11 @@ class Config(object):
return str(self.arguments).replace("Namespace", "Config") # Using argparse str output
+ # Convert string to bool
+ def strToBool(self, v):
+ return v.lower() in ("yes", "true", "t", "1")
+
+
# Create command line arguments
def createArguments(self):
# Platform specific
@@ -23,9 +28,14 @@ class Config(object):
coffeescript = "type %s | tools\\coffee\\coffee.cmd"
else:
coffeescript = None
+ if sys.platform.startswith("Darwin"): # For some reasons openssl doesnt works on mac yet (https://github.com/HelloZeroNet/ZeroNet/issues/94)
+ disable_openssl = True
+ else:
+ disable_openssl = False
# Create parser
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.register('type','bool', self.strToBool)
subparsers = parser.add_subparsers(title="Action to perform", dest="action")
# Main
@@ -72,6 +82,13 @@ class Config(object):
action.add_argument('site', help='Site address')
action.add_argument('filename', help='File name to request')
+ # PeerGetFile
+ action = subparsers.add_parser("peerCmd", help='Request and print a file content from peer')
+ action.add_argument('peer_ip', help='Peer ip')
+ action.add_argument('peer_port', help='Peer port')
+ action.add_argument('cmd', help='Command to execute')
+ action.add_argument('parameters', help='Parameters to command', nargs='?')
+
# Config parameters
@@ -88,8 +105,9 @@ class Config(object):
parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip')
parser.add_argument('--fileserver_port',help='FileServer bind port', default=15441, type=int, metavar='port')
parser.add_argument('--disable_zeromq', help='Disable compatibility with old clients', action='store_true')
- parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
+ parser.add_argument('--disable_openssl',help='Disable usage of OpenSSL liblary', type='bool', choices=[True, False], default=disable_openssl)
parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true')
+ parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
parser.add_argument('--ip_external', help='External ip (tested on start if None)', metavar='ip')
parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript, metavar='executable_path')
diff --git a/src/Connection/Connection.py b/src/Connection/Connection.py
index 51c616a6..76bdd45c 100644
--- a/src/Connection/Connection.py
+++ b/src/Connection/Connection.py
@@ -12,7 +12,9 @@ if not config.disable_zeromq:
-class Connection:
+class Connection(object):
+ __slots__ = ("sock", "ip", "port", "peer_id", "id", "protocol", "type", "server", "unpacker", "req_id", "handshake", "connected", "event_connected", "closed", "zmq_sock", "zmq_queue", "zmq_working", "forward_thread", "start_time", "last_recv_time", "last_message_time", "last_send_time", "last_sent_time", "incomplete_buff_recv", "bytes_recv", "bytes_sent", "last_ping_delay", "last_req_time", "last_cmd", "name", "updateName", "waiting_requests")
+
def __init__(self, server, ip, port, sock=None):
self.sock = sock
self.ip = ip
@@ -315,7 +317,7 @@ class Connection:
self.sock.shutdown(gevent.socket.SHUT_WR)
self.sock.close()
except Exception, err:
- if config.debug_socket: self.log("Close error: %s" % Debug.formatException(err))
+ if config.debug_socket: self.log("Close error: %s" % err)
# Little cleanup
del self.unpacker
diff --git a/src/Content/ContentManager.py b/src/Content/ContentManager.py
index 9b20e741..2fdfe1a1 100644
--- a/src/Content/ContentManager.py
+++ b/src/Content/ContentManager.py
@@ -70,7 +70,7 @@ class ContentManager:
self.site.bad_files[inner_path] = True
if new_content["modified"] > self.site.settings.get("modified", 0):
- self.site.settings["modified"] = new_content["modified"]
+ self.site.settings["modified"] = min(time.time()+60*10, new_content["modified"]) # Dont store modifications in the far future (more than 10 minute)
return changed
diff --git a/src/Crypt/CryptBitcoin.py b/src/Crypt/CryptBitcoin.py
index 9adfaf9e..5e4c99d7 100644
--- a/src/Crypt/CryptBitcoin.py
+++ b/src/Crypt/CryptBitcoin.py
@@ -1,8 +1,11 @@
from lib.BitcoinECC import BitcoinECC
from lib.pybitcointools import bitcoin as btctools
import logging
+from Config import config
+
# Try to load openssl
try:
+ if config.disable_openssl: raise Exception("Disabled by config")
from lib.opensslVerify import opensslVerify
logging.info("OpenSSL loaded, version: %s" % opensslVerify.openssl_version)
except Exception, err:
diff --git a/src/File/FileRequest.py b/src/File/FileRequest.py
index a53aa85e..d7b136fa 100644
--- a/src/File/FileRequest.py
+++ b/src/File/FileRequest.py
@@ -7,7 +7,9 @@ from util import RateLimit
FILE_BUFF = 1024*512
# Request from me
-class FileRequest:
+class FileRequest(object):
+ __slots__ = ("server", "connection", "req_id", "sites", "log", "responded")
+
def __init__(self, server, connection):
self.server = server
self.connection = connection
@@ -53,6 +55,8 @@ class FileRequest:
elif cmd == "pex":
self.actionPex(params)
+ elif cmd == "modified":
+ self.actionModified(params)
elif cmd == "ping":
self.actionPing()
else:
@@ -158,6 +162,17 @@ class FileRequest:
self.response({"peers": packed_peers})
+ # Get modified content.json files since
+ def actionModified(self, params):
+ site = self.sites.get(params["site"])
+ if not site or not site.settings["serving"]: # Site unknown or not serving
+ self.response({"error": "Unknown site"})
+ return False
+ modified_files = {inner_path: content["modified"] for inner_path, content in site.content_manager.contents.iteritems() if content["modified"] > params["since"]}
+ self.response({"modified_files": modified_files})
+
+
+
# Send a simple Pong! answer
def actionPing(self):
self.response("Pong!")
diff --git a/src/Peer/Peer.py b/src/Peer/Peer.py
index 03452684..98433fbe 100644
--- a/src/Peer/Peer.py
+++ b/src/Peer/Peer.py
@@ -4,7 +4,9 @@ from Config import config
from Debug import Debug
# Communicate remote peers
-class Peer:
+class Peer(object):
+ __slots__ = ("ip", "port", "site", "key", "connection_server", "connection", "last_found", "last_response", "last_ping", "added", "connection_error", "hash_failed", "download_bytes", "download_time")
+
def __init__(self, ip, port, site=None):
self.ip = ip
self.port = port
diff --git a/src/Site/Site.py b/src/Site/Site.py
index c15b306c..82ebaa57 100644
--- a/src/Site/Site.py
+++ b/src/Site/Site.py
@@ -328,7 +328,7 @@ class Site:
# Add myself and get other peers from tracker
def announce(self, force=False):
- if time.time() < self.last_announce+60 and not force: return # No reannouncing within 60 secs
+ if time.time() < self.last_announce+30 and not force: return # No reannouncing within 30 secs
self.last_announce = time.time()
errors = []
address_hash = hashlib.sha1(self.address).hexdigest()
diff --git a/src/Site/SiteStorage.py b/src/Site/SiteStorage.py
index fc69d919..c3344a34 100644
--- a/src/Site/SiteStorage.py
+++ b/src/Site/SiteStorage.py
@@ -36,6 +36,7 @@ class SiteStorage:
def closeDb(self):
if self.db: self.db.close()
+ self.db = None
# Return db class
@@ -120,13 +121,17 @@ class SiteStorage:
# Write content to file
def write(self, inner_path, content):
file_path = self.getPath(inner_path)
+ # Create dir if not exits
+ file_dir = os.path.dirname(file_path)
+ if not os.path.isdir(file_dir):
+ os.makedirs(file_dir)
# Write file
if hasattr(content, 'read'): # File-like object
- file = open(file_path, "wb")
- shutil.copyfileobj(content, file) # Write buff to disk
- file.close()
+ with open(file_path, "wb") as file:
+ shutil.copyfileobj(content, file) # Write buff to disk
else: # Simple string
- open(file_path, "wb").write(content)
+ with open(file_path, "wb") as file:
+ file.write(content)
del content
self.onUpdated(inner_path)
@@ -146,7 +151,8 @@ class SiteStorage:
# Load and parse json file
def loadJson(self, inner_path):
- return json.load(self.open(inner_path))
+ with self.open(inner_path) as file:
+ return json.load(file)
# Get file size
@@ -164,7 +170,7 @@ class SiteStorage:
return os.path.isdir(self.getPath(inner_path))
- # Sercurity check and return path of site's file
+ # Security check and return path of site's file
def getPath(self, inner_path):
inner_path = inner_path.replace("\\", "/") # Windows separator fix
inner_path = re.sub("^%s/" % re.escape(self.directory), "", inner_path) # Remove site directory if begins with it
@@ -176,8 +182,6 @@ class SiteStorage:
-
-
# Verify all files sha512sum using content.json
def verifyFiles(self, quick_check=False): # Fast = using file size
bad_files = []
diff --git a/src/Ui/UiServer.py b/src/Ui/UiServer.py
index 6eec62c9..de757fb9 100644
--- a/src/Ui/UiServer.py
+++ b/src/Ui/UiServer.py
@@ -39,8 +39,9 @@ class UiWSGIHandler(WSGIHandler):
if config.debug: # Allow websocket errors to appear on /Debug
import sys
del self.server.sockets[self.client_address]
- sys.modules["main"].DebugHook.handleError()
- del self.server.sockets[self.client_address]
+ sys.modules["main"].DebugHook.handleError()
+ if self.client_address in self.server.sockets:
+ del self.server.sockets[self.client_address]
class UiServer:
diff --git a/src/Ui/media/img/favicon.psd b/src/Ui/media/img/favicon.psd
index 1cf6f35f..1eea4ccf 100644
Binary files a/src/Ui/media/img/favicon.psd and b/src/Ui/media/img/favicon.psd differ
diff --git a/src/Worker/Worker.py b/src/Worker/Worker.py
index 0129ed4a..18cecaa4 100644
--- a/src/Worker/Worker.py
+++ b/src/Worker/Worker.py
@@ -53,8 +53,6 @@ class Worker:
if correct == True and task["done"] == False: # Save if changed and task not done yet
buff.seek(0)
file_path = site.storage.getPath(task["inner_path"])
- file_dir = os.path.dirname(file_path)
- if not os.path.isdir(file_dir): os.makedirs(file_dir) # Make directory for files
site.storage.write(task["inner_path"], buff)
if task["done"] == False: self.manager.doneTask(task)
task["workers_num"] -= 1
@@ -81,7 +79,7 @@ class Worker:
# Force stop the worker
def stop(self):
- self.manager.log.debug("%s: Force stopping, thread: %s" % (self.key, self.thread))
+ self.manager.log.debug("%s: Force stopping, thread" % self.key)
self.running = False
if self.thread:
self.thread.kill(exception=Debug.Notify("Worker stopped"))
diff --git a/src/main.py b/src/main.py
index 6bdc7484..a2230b10 100644
--- a/src/main.py
+++ b/src/main.py
@@ -248,6 +248,24 @@ class Actions:
print peer.getFile(site, filename).read()
print "Response time: %.3fs" % (time.time()-s)
+
+ def peerCmd(self, peer_ip, peer_port, cmd, parameters):
+ logging.info("Opening a simple connection server")
+ global file_server
+ from Connection import ConnectionServer
+ file_server = ConnectionServer()
+ from Peer import Peer
+ peer = Peer(peer_ip, peer_port)
+
+ import json
+ if parameters:
+ parameters = json.loads(parameters.replace("'", '"'))
+ else:
+ parameters = {}
+ logging.info("Response: %s" % peer.request(cmd, parameters))
+
+
+
actions = Actions()
# Starts here when running zeronet.py
def start():
diff --git a/zeronet.py b/zeronet.py
index a0c4d373..4632274d 100644
--- a/zeronet.py
+++ b/zeronet.py
@@ -3,6 +3,7 @@
def main():
print "- Starting ZeroNet..."
import sys, os
+ main = None
try:
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) # Imports relative to src
import main
@@ -25,7 +26,7 @@ def main():
traceback.print_exc()
raw_input("-- Error happened, press enter to close --")
- if main.update_after_shutdown: # Updater
+ if main and main.update_after_shutdown: # Updater
# Restart
gc.collect() # Garbage collect
print "Restarting..."