diff --git a/README.md b/README.md index 86d25d36..c5ac0b2f 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,11 @@ Linux (Debian): - `pip install msgpack-python` - start using `python zeronet.py` +Linux (Without root access): + - `wget https://bootstrap.pypa.io/get-pip.py` + - `python get-pip.py --user pyzmq gevent msgpack-python` + - start using `python zeronet.py` + ## Current limitations - No torrent-like, file splitting big file support @@ -91,4 +96,4 @@ Bitcoin: 1QDhxQ6PraUZa21ET5fYUCPgdrwBomnFgX #### Thank you! - More info, help, changelog, zeronet sites: http://www.reddit.com/r/zeronet/ -- Come, chat with us: [#zeronet @ FreeNode](https://kiwiirc.com/client/irc.freenode.net/zeronet) +- Come, chat with us: [#zeronet @ FreeNode](https://kiwiirc.com/client/irc.freenode.net/zeronet) \ No newline at end of file diff --git a/src/Config.py b/src/Config.py index f44c3758..df512c92 100644 --- a/src/Config.py +++ b/src/Config.py @@ -3,7 +3,7 @@ import ConfigParser class Config(object): def __init__(self): - self.version = "0.1.4" + self.version = "0.1.5" self.parser = self.createArguments() argv = sys.argv[:] # Copy command line arguments argv = self.parseConfig(argv) # Add arguments from config file diff --git a/src/File/FileRequest.py b/src/File/FileRequest.py index 973b6fca..f70671b8 100644 --- a/src/File/FileRequest.py +++ b/src/File/FileRequest.py @@ -2,6 +2,7 @@ import os, msgpack, shutil from Site import SiteManager from cStringIO import StringIO from Debug import Debug +from Config import config FILE_BUFF = 1024*512 @@ -80,13 +81,17 @@ class FileRequest: self.send({"error": "Unknown site"}) return False try: - file = open(site.getPath(params["inner_path"]), "rb") + file_path = site.getPath(params["inner_path"]) + if config.debug_socket: self.log.debug("Opening file: %s" % file_path) + file = open(file_path, "rb") file.seek(params["location"]) back = {} back["body"] = file.read(FILE_BUFF) back["location"] = file.tell() back["size"] = os.fstat(file.fileno()).st_size + if config.debug_socket: self.log.debug("Sending file %s from position %s to %s" % (file_path, params["location"], back["location"])) self.send(back) + if config.debug_socket: self.log.debug("File %s sent" % file_path) except Exception, err: self.send({"error": "File read error: %s" % Debug.formatException(err)}) return False diff --git a/src/File/FileServer.py b/src/File/FileServer.py index 34ce5d42..4a5e4f86 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -49,10 +49,14 @@ class FileServer: self.log.info("Try to open port using upnpc...") try: exit = os.system("%s -e ZeroNet -r %s tcp" % (config.upnpc, self.port)) - if exit == 0: + if exit == 0: # Success upnpc_success = True - else: - upnpc_success = False + else: # Failed + exit = os.system("%s -r %s tcp" % (config.upnpc, self.port)) # Try without -e option + if exit == 0: + upnpc_success = True + else: + upnpc_success = False except Exception, err: self.log.error("Upnpc run error: %s" % Debug.formatException(err)) upnpc_success = False @@ -122,13 +126,25 @@ class FileServer: # Announce sites every 10 min def announceSites(self): while 1: - time.sleep(10*60) # Announce sites every 10 min + time.sleep(20*60) # Announce sites every 20 min for address, site in self.sites.items(): if site.settings["serving"]: site.announce() # Announce site to tracker time.sleep(2) # Prevent too quick request + # Detects if computer back from wakeup + def wakeupWatcher(self): + last_time = time.time() + while 1: + time.sleep(30) + if time.time()-last_time > 60: # If taken more than 60 second then the computer was in sleep mode + self.log.info("Wakeup detected: time wrap from %s to %s (%s sleep seconds), acting like startup..." % (last_time, time.time(), time.time()-last_time)) + self.port_opened = None # Check if we still has the open port on router + self.checkSites() + last_time = time.time() + + # Bind and start serving sites def start(self, check_sites = True): self.log = logging.getLogger(__name__) @@ -152,6 +168,7 @@ class FileServer: gevent.spawn(self.checkSites) gevent.spawn(self.announceSites) + gevent.spawn(self.wakeupWatcher) while True: try: diff --git a/src/Site/Site.py b/src/Site/Site.py index 44fbe3a7..ed7a1a84 100644 --- a/src/Site/Site.py +++ b/src/Site/Site.py @@ -267,6 +267,32 @@ class Site: self.bad_files[bad_file] = True + def deleteFiles(self): + self.log.debug("Deleting files from content.json...") + files = self.content["files"].keys() # Make a copy + files.append("content.json") + for inner_path in files: + path = self.getPath(inner_path) + if os.path.isfile(path): os.unlink(path) + + self.log.debug("Deleting empty dirs...") + for root, dirs, files in os.walk(self.directory, topdown=False): + for dir in dirs: + path = os.path.join(root,dir) + if os.path.isdir(path) and os.listdir(path) == []: + os.removedirs(path) + self.log.debug("Removing %s" % path) + if os.path.isdir(self.directory) and os.listdir(self.directory) == []: os.removedirs(self.directory) # Remove sites directory if empty + + if os.path.isdir(self.directory): + self.log.debug("Some unknown file remained in site data dir: %s..." % self.directory) + return False # Some files not deleted + else: + self.log.debug("Site data directory deleted: %s..." % self.directory) + return True # All clean + + + # - Events - # Add event listeners @@ -380,11 +406,10 @@ class Site: else: ok = self.verifyFile(inner_path, open(file_path, "rb")) - if ok: - self.log.debug("[OK] %s" % inner_path) - else: + if not ok: self.log.error("[ERROR] %s" % inner_path) bad_files.append(inner_path) + self.log.debug("Site verified: %s files, quick_check: %s, bad files: %s" % (len(self.content["files"]), quick_check, bad_files)) return bad_files diff --git a/src/Site/SiteManager.py b/src/Site/SiteManager.py index 80d5a8e9..9779a639 100644 --- a/src/Site/SiteManager.py +++ b/src/Site/SiteManager.py @@ -45,12 +45,20 @@ def need(address, all_file=True): from Site import Site if address not in sites: # Site not exits yet if not isAddress(address): raise Exception("Not address: %s" % address) + logging.debug("Added new site: %s" % address) sites[address] = Site(address) + sites[address].settings["serving"] = True # Maybe it was deleted before site = sites[address] if all_file: site.download() return site +def delete(address): + global sites + logging.debug("SiteManager deleted site: %s" % address) + del(sites[address]) + + # Lazy load sites def list(): if sites == None: # Not loaded yet diff --git a/src/Ui/UiWebsocket.py b/src/Ui/UiWebsocket.py index 634cd638..44c8e593 100644 --- a/src/Ui/UiWebsocket.py +++ b/src/Ui/UiWebsocket.py @@ -96,6 +96,8 @@ class UiWebsocket: self.actionSitePause(req["id"], req["params"]) elif cmd == "siteResume" and "ADMIN" in permissions: self.actionSiteResume(req["id"], req["params"]) + elif cmd == "siteDelete" and "ADMIN" in permissions: + self.actionSiteDelete(req["id"], req["params"]) elif cmd == "siteList" and "ADMIN" in permissions: self.actionSiteList(req["id"], req["params"]) elif cmd == "channelJoinAllsite" and "ADMIN" in permissions: @@ -211,6 +213,7 @@ class UiWebsocket: site.settings["serving"] = False site.saveSettings() site.updateWebsocket() + site.worker_manager.stopWorkers() else: self.response(to, {"error": "Unknown site: %s" % address}) @@ -227,3 +230,18 @@ class UiWebsocket: site.updateWebsocket() else: self.response(to, {"error": "Unknown site: %s" % address}) + + + def actionSiteDelete(self, to, params): + address = params.get("address") + site = self.server.sites.get(address) + if site: + site.settings["serving"] = False + site.saveSettings() + site.worker_manager.running = False + site.worker_manager.stopWorkers() + site.deleteFiles() + SiteManager.delete(address) + site.updateWebsocket() + else: + self.response(to, {"error": "Unknown site: %s" % address}) diff --git a/src/Ui/media/Notifications.coffee b/src/Ui/media/Notifications.coffee index d5190c5a..ec26f425 100644 --- a/src/Ui/media/Notifications.coffee +++ b/src/Ui/media/Notifications.coffee @@ -27,10 +27,15 @@ class Notifications $(".notification-icon", elem).html("!") else if type == "done" $(".notification-icon", elem).html("
") + else if type == "ask" + $(".notification-icon", elem).html("?") else $(".notification-icon", elem).html("i") - $(".body", elem).html(body) + if typeof(body) == "string" + $(".body", elem).html(body) + else + $(".body", elem).html("").append(body) elem.appendTo(@elem) @@ -53,7 +58,10 @@ class Notifications @close elem return false - @ + # Close on button click within body (confirm dialog) + $(".button", elem).on "click", => + @close elem + return false close: (elem) -> diff --git a/src/Ui/media/Wrapper.coffee b/src/Ui/media/Wrapper.coffee index b99a326e..a43ddbb2 100644 --- a/src/Ui/media/Wrapper.coffee +++ b/src/Ui/media/Wrapper.coffee @@ -55,12 +55,31 @@ class Wrapper if @ws.ws.readyState == 1 and not @wrapperWsInited # If ws already opened @sendInner {"cmd": "wrapperOpenedWebsocket"} @wrapperWsInited = true - else if cmd == "wrapperNotification" + else if cmd == "wrapperNotification" # Display notification + message.params = @toHtmlSafe(message.params) # Escape html @notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2]) + else if cmd == "wrapperConfirm" # Display confirm message + @actionWrapperConfirm(message) else # Send to websocket @ws.send(message) # Pass message to websocket + # - Actions - + + actionWrapperConfirm: (message) -> + message.params = @toHtmlSafe(message.params) # Escape html + if message.params[1] then caption = message.params[1] else caption = "ok" + + body = $(""+message.params[0]+"") + button = $("#{caption}") # Add confirm button + button.on "click", => # Response on button click + @sendInner {"cmd": "response", "to": message.id, "result": "boom"} # Response to confirm + return false + body.append(button) + + @notifications.add("notification-#{message.id}", "ask", body) + + onOpenWebsocket: (e) => @ws.cmd "channelJoin", {"channel": "siteChanged"} # Get info on modifications @log "onOpenWebsocket", @inner_ready, @wrapperWsInited @@ -125,6 +144,7 @@ class Wrapper @loading.printLine("#{site_info.event[1]} downloaded") if site_info.event[1] == window.inner_path # File downloaded we currently on @loading.hideScreen() + if not @site_info then @reloadSiteInfo() if not $(".loadingscreen").length # Loading screen already removed (loaded +2sec) @notifications.add("modified", "info", "New version of this page has just released.