diff --git a/README.md b/README.md index 67e96552..cbb412dc 100644 --- a/README.md +++ b/README.md @@ -31,15 +31,15 @@ Decentralized websites using Bitcoin crypto and the BitTorrent network other peers. ## Features - * Easy, zero configuration setup + * Real-time updated sites + * Namecoin .bit domains support + * Easy to setup: unpack & run * Password-less [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) based authorization: Your account is protected by same cryptography as your bitcoin wallet - * Namecoin .bit domains support * SQL Database support: Allows easier site development and faster page load times + * Tor network support * Automatic, uPnP port opening * Plugin for multiuser (openproxy) support - * [ZeroFrame API](http://zeronet.readthedocs.org/en/latest/site_development/zeroframe_api_reference/) for dynamic sites - * One click ZeroNet client updater ## Screenshots diff --git a/plugins/Stats/StatsPlugin.py b/plugins/Stats/StatsPlugin.py index 77f4a20b..d0270e53 100644 --- a/plugins/Stats/StatsPlugin.py +++ b/plugins/Stats/StatsPlugin.py @@ -104,7 +104,11 @@ class UiRequestPlugin(object): yield self.formatTableRow([ ("%s", (site.address, site.address)), ("%s", [peer.connection.id for peer in site.peers.values() if peer.connection and peer.connection.connected]), - ("%s/%s", ( len([peer for peer in site.peers.values() if peer.connection and peer.connection.connected]), len(site.peers) ) ), + ("%s/%s/%s", ( + len([peer for peer in site.peers.values() if peer.connection and peer.connection.connected]), + len(site.getConnectablePeers(100)), + len(site.peers) + ) ), ("%s", len(site.content_manager.contents)), ]) yield "" % site.address diff --git a/plugins/Trayicon/TrayiconPlugin.py b/plugins/Trayicon/TrayiconPlugin.py index 5517fa96..9e35cb1f 100644 --- a/plugins/Trayicon/TrayiconPlugin.py +++ b/plugins/Trayicon/TrayiconPlugin.py @@ -32,6 +32,7 @@ class ActionsPlugin(object): (self.titleConnections, False), (self.titleTransfer, False), (self.titleConsole, self.toggleConsole), + (self.titleAutorun, self.toggleAutorun), "--", ("ZeroNet Twitter", lambda: self.opensite("https://twitter.com/HelloZeroNet") ), ("ZeroNet Reddit", lambda: self.opensite("http://www.reddit.com/r/zeronet/") ), @@ -51,6 +52,7 @@ class ActionsPlugin(object): super(ActionsPlugin, self).main() icon._die = True + def quit(self): self.icon.die() time.sleep(0.1) @@ -58,10 +60,12 @@ class ActionsPlugin(object): self.main.file_server.stop() #sys.exit() + def opensite(self, url): import webbrowser webbrowser.open(url, new=2) + def titleIp(self): title = "!IP: %s" % config.ip_external if self.main.file_server.port_opened: @@ -70,18 +74,22 @@ class ActionsPlugin(object): title += " (passive)" return title + def titleConnections(self): title = "Connections: %s" % len(self.main.file_server.connections) return title + def titleTransfer(self): title = "Received: %.2f MB | Sent: %.2f MB" % (float(self.main.file_server.bytes_recv)/1024/1024, float(self.main.file_server.bytes_sent)/1024/1024) return title + def titleConsole(self): if self.console: return "+Show console window" else: return "Show console window" + def toggleConsole(self): if self.console: notificationicon.hideConsole() @@ -89,3 +97,34 @@ class ActionsPlugin(object): else: notificationicon.showConsole() self.console = True + + + def getAutorunPath(self): + return "%s\\zeronet.cmd" % winfolders.get(winfolders.STARTUP) + + + def formatAutorun(self): + args = sys.argv[:] + args.insert(0, sys.executable) + if sys.platform == 'win32': + args = ['"%s"' % arg for arg in args] + cmd = " ".join(args) + cmd = cmd.replace("start.py", "zeronet.py").replace('"--open_browser"', "").replace('"default_browser"', "") # Dont open browser on autorun + return "cd /D %s \n%s" % (os.getcwd(), cmd) + + + def isAutorunEnabled(self): + path = self.getAutorunPath() + return os.path.isfile(path) and open(path).read() == self.formatAutorun() + + + def titleAutorun(self): + if self.isAutorunEnabled(): return "+Start ZeroNet when Windows starts" + else: return "Start ZeroNet when Windows starts" + + + def toggleAutorun(self): + if self.isAutorunEnabled(): + os.unlink(self.getAutorunPath()) + else: + open(self.getAutorunPath(), "w").write(self.formatAutorun()) diff --git a/plugins/Trayicon/lib/notificationicon.py b/plugins/Trayicon/lib/notificationicon.py index 19b26af5..f6a2b44d 100644 --- a/plugins/Trayicon/lib/notificationicon.py +++ b/plugins/Trayicon/lib/notificationicon.py @@ -632,9 +632,8 @@ class NotificationIcon(object): Shell_NotifyIcon(NIM_ADD, ctypes.pointer(iconinfo)) iconinfo.union.uVersion = NOTIFYICON_VERSION - self.iconinfo = ctypes.pointer(iconinfo) - Shell_NotifyIcon(NIM_SETVERSION, ctypes.pointer(iconinfo)) + self.iconinfo = iconinfo PostMessage(self._hwnd, WM_NULL, 0, 0) diff --git a/plugins/Trayicon/lib/winfolders.py b/plugins/Trayicon/lib/winfolders.py index 0e390fad..d28efc1a 100644 --- a/plugins/Trayicon/lib/winfolders.py +++ b/plugins/Trayicon/lib/winfolders.py @@ -46,3 +46,8 @@ def get(intFolder): exit_code=_SHGetFolderPath(0, intFolder, 0, 0, auPathBuffer) return auPathBuffer.value + +if __name__ == "__main__": + import os + print get(STARTUP) + open(get(STARTUP)+"\\zeronet.cmd", "w").write("cd /D %s\r\nzeronet.py" % os.getcwd()) \ No newline at end of file diff --git a/plugins/Zeroname/updater/zeroname_updater.py b/plugins/Zeroname/updater/zeroname_updater.py index e7fcda25..c1247305 100644 --- a/plugins/Zeroname/updater/zeroname_updater.py +++ b/plugins/Zeroname/updater/zeroname_updater.py @@ -20,6 +20,9 @@ def processNameOp(domain, value): if "zeronet" not in data: print "No zeronet in ", data.keys() return False + if type(data["zeronet"]) != type({}): + print "Bad type: ", data["zeronet"] + return False if "slave" in sys.argv: print "Waiting for master update arrive" @@ -96,7 +99,7 @@ print "Processing block from #%s to #%s..." % (config["lastprocessed"], last_blo for block_id in range(config["lastprocessed"], last_block+1): processBlock(block_id) -#processBlock(223911) # Testing +# processBlock(223911) # Testing while 1: print "Waiting for new block", diff --git a/src/Site/Site.py b/src/Site/Site.py index cbf652b2..a4d94a6a 100644 --- a/src/Site/Site.py +++ b/src/Site/Site.py @@ -178,6 +178,7 @@ class Site: for changed_file in changed: self.bad_files[changed_file] = self.bad_files.get(changed_file, 0)+1 if not self.settings["own"]: self.storage.checkFiles(quick_check=True) # Quick check files based on file size + if self.bad_files: self.download() @@ -187,12 +188,17 @@ class Site: # Publish worker def publisher(self, inner_path, peers, published, limit, event_done=None): - timeout = 5+int(self.storage.getSize(inner_path)/1024) # Timeout: 5sec + size in kb + file_size = self.storage.getSize(inner_path) + body = self.storage.read(inner_path) while 1: if not peers or len(published) >= limit: if event_done: event_done.set(True) break # All peers done, or published engouht peer = peers.pop(0) + if peer.connection and peer.connection.last_ping_delay: # Peer connected + timeout = timeout = 5+int(file_size/1024)+peer.connection.last_ping_delay # Timeout: 5sec + size in kb + last_ping + else: + timeout = timeout = 5+int(file_size/1024) # Timeout: 5sec + size in kb result = {"exception": "Timeout"} for retry in range(2): @@ -201,7 +207,7 @@ class Site: result = peer.request("update", { "site": self.address, "inner_path": inner_path, - "body": self.storage.open(inner_path).read(), + "body": body, "peer": (config.ip_external, config.fileserver_port) }) if result: break @@ -219,7 +225,7 @@ class Site: # Update content.json on peers def publish(self, limit=5, inner_path="content.json"): self.log.info( "Publishing to %s/%s peers..." % (limit, len(self.peers)) ) - published = [] # Successfuly published (Peer) + published = [] # Successfully published (Peer) publishers = [] # Publisher threads peers = self.peers.values() @@ -233,7 +239,12 @@ class Site: if len(published) < min(len(self.peers), limit): time.sleep(0.2) # If less than we need sleep a bit if len(published) == 0: gevent.joinall(publishers) # No successful publish, wait for all publisher - self.log.info("Successfuly published to %s peers" % len(published)) + # Make sure the connected passive peers got the update + passive_peers = [peer for peer in peers if peer.connection and not peer.connection.closed and peer.key.endswith(":0") and peer not in published] # Every connected passive peer that we not published to + for peer in passive_peers: + gevent.spawn(self.publisher, inner_path, passive_peers, published, limit=10) + + self.log.info("Successfuly published to %s peers, publishing to %s more passive peers" % (len(published), len(passive_peers)) ) return len(published) @@ -399,7 +410,7 @@ class Site: self.announcePex() - # Need open connections + # Keep connections to get the updates (required for passive clients) def needConnections(self): need = min(len(self.peers)/2, 10) # Connect to half of total peers, but max 10 need = max(need, 5) # But minimum 5 peers diff --git a/src/Site/SiteStorage.py b/src/Site/SiteStorage.py index 157e5648..fc69d919 100644 --- a/src/Site/SiteStorage.py +++ b/src/Site/SiteStorage.py @@ -186,14 +186,14 @@ class SiteStorage: self.site.content_manager.loadContent() # Reload content.json for content_inner_path, content in self.site.content_manager.contents.items(): if not os.path.isfile(self.getPath(content_inner_path)): # Missing content.json file - self.log.error("[MISSING] %s" % content_inner_path) + self.log.debug("[MISSING] %s" % content_inner_path) bad_files.append(content_inner_path) for file_relative_path in content["files"].keys(): file_inner_path = self.site.content_manager.toDir(content_inner_path)+file_relative_path # Relative to content.json file_inner_path = file_inner_path.strip("/") # Strip leading / file_path = self.getPath(file_inner_path) if not os.path.isfile(file_path): - self.log.error("[MISSING] %s" % file_inner_path) + self.log.debug("[MISSING] %s" % file_inner_path) bad_files.append(file_inner_path) continue @@ -203,7 +203,7 @@ class SiteStorage: ok = self.site.content_manager.verifyFile(file_inner_path, open(file_path, "rb")) if not ok: - self.log.debug("[CHNAGED] %s" % file_inner_path) + self.log.debug("[CHANGED] %s" % file_inner_path) bad_files.append(file_inner_path) self.log.debug("%s verified: %s files, quick_check: %s, bad files: %s" % (content_inner_path, len(content["files"]), quick_check, bad_files)) @@ -212,11 +212,12 @@ class SiteStorage: # Check and try to fix site files integrity def checkFiles(self, quick_check=True): - self.log.debug("Checking files... Quick:%s" % quick_check) + s = time.time() bad_files = self.verifyFiles(quick_check) if bad_files: for bad_file in bad_files: self.site.bad_files[bad_file] = self.site.bad_files.get("bad_file", 0)+1 + self.log.debug("Checked files in %.2fs... Quick:%s" % (time.time()-s, quick_check)) # Delete site's all file