diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py index b22ddc17..0450b22d 100644 --- a/plugins/Sidebar/SidebarPlugin.py +++ b/plugins/Sidebar/SidebarPlugin.py @@ -148,6 +148,7 @@ class UiWebsocketPlugin(object): percent = 0 else: percent = 100 * (float(size) / size_total) + percent = math.floor(percent*100)/100 # Floor to 2 digits body.append(u"
" % (percent, extension, color, extension)) # Legend diff --git a/plugins/Sidebar/media/Sidebar.coffee b/plugins/Sidebar/media/Sidebar.coffee index 68a0f68a..ae09755a 100644 --- a/plugins/Sidebar/media/Sidebar.coffee +++ b/plugins/Sidebar/media/Sidebar.coffee @@ -320,10 +320,14 @@ class Sidebar extends Class @globe.addData( globe_data, {format: 'magnitude', name: "hello", animated: false} ) @globe.createPoints() else - @globe = new DAT.Globe( @tag.find(".globe")[0], {"imgDir": "/uimedia/globe/"} ) - @globe.addData( globe_data, {format: 'magnitude', name: "hello"} ) - @globe.createPoints() - @globe.animate() + try + @globe = new DAT.Globe( @tag.find(".globe")[0], {"imgDir": "/uimedia/globe/"} ) + @globe.addData( globe_data, {format: 'magnitude', name: "hello"} ) + @globe.createPoints() + @globe.animate() + catch e + @tag.find(".globe").addClass("error").text("WebGL not supported") + @tag.find(".globe").removeClass("loading") @@ -334,5 +338,7 @@ class Sidebar extends Class @globe = null -window.sidebar = new Sidebar() +setTimeout ( -> + window.sidebar = new Sidebar() +), 500 window.transitionEnd = 'transitionend webkitTransitionEnd oTransitionEnd otransitionend' diff --git a/plugins/Sidebar/media/Sidebar.css b/plugins/Sidebar/media/Sidebar.css index 6593d4cc..b43c99b0 100644 --- a/plugins/Sidebar/media/Sidebar.css +++ b/plugins/Sidebar/media/Sidebar.css @@ -96,4 +96,5 @@ /* Globe */ .globe { width: 360px; height: 360px } -.globe.loading { background: url(/uimedia/img/loading-circle.gif) center center no-repeat } \ No newline at end of file +.globe.loading { background: url(/uimedia/img/loading-circle.gif) center center no-repeat } +.globe.error { text-align: center; padding-top: 156px; box-sizing: border-box; opacity: 0.2; } \ No newline at end of file diff --git a/plugins/Sidebar/media/all.css b/plugins/Sidebar/media/all.css index 68552d50..6398f3d0 100644 --- a/plugins/Sidebar/media/all.css +++ b/plugins/Sidebar/media/all.css @@ -150,4 +150,5 @@ /* Globe */ .globe { width: 360px; height: 360px } -.globe.loading { background: url(/uimedia/img/loading-circle.gif) center center no-repeat } \ No newline at end of file +.globe.loading { background: url(/uimedia/img/loading-circle.gif) center center no-repeat } +.globe.error { text-align: center; padding-top: 156px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; opacity: 0.2; } \ No newline at end of file diff --git a/plugins/Sidebar/media/all.js b/plugins/Sidebar/media/all.js index 61eb5f29..a1bf185b 100644 --- a/plugins/Sidebar/media/all.js +++ b/plugins/Sidebar/media/all.js @@ -521,6 +521,7 @@ window.initScrollable = function () { Sidebar.prototype.displayGlobe = function() { return wrapper.ws.cmd("sidebarGetPeers", [], (function(_this) { return function(globe_data) { + var e; if (_this.globe) { _this.globe.scene.remove(_this.globe.points); _this.globe.addData(globe_data, { @@ -530,15 +531,20 @@ window.initScrollable = function () { }); _this.globe.createPoints(); } else { - _this.globe = new DAT.Globe(_this.tag.find(".globe")[0], { - "imgDir": "/uimedia/globe/" - }); - _this.globe.addData(globe_data, { - format: 'magnitude', - name: "hello" - }); - _this.globe.createPoints(); - _this.globe.animate(); + try { + _this.globe = new DAT.Globe(_this.tag.find(".globe")[0], { + "imgDir": "/uimedia/globe/" + }); + _this.globe.addData(globe_data, { + format: 'magnitude', + name: "hello" + }); + _this.globe.createPoints(); + _this.globe.animate(); + } catch (_error) { + e = _error; + _this.tag.find(".globe").addClass("error").text("WebGL not supported"); + } } return _this.tag.find(".globe").removeClass("loading"); }; @@ -557,7 +563,9 @@ window.initScrollable = function () { })(Class); - window.sidebar = new Sidebar(); + setTimeout((function() { + return window.sidebar = new Sidebar(); + }), 500); window.transitionEnd = 'transitionend webkitTransitionEnd oTransitionEnd otransitionend'; diff --git a/plugins/Trayicon/trayicon.ico b/plugins/Trayicon/trayicon.ico index 8e1a5285..08617225 100644 Binary files a/plugins/Trayicon/trayicon.ico and b/plugins/Trayicon/trayicon.ico differ diff --git a/src/Config.py b/src/Config.py index 4b6a4403..e38af408 100644 --- a/src/Config.py +++ b/src/Config.py @@ -8,7 +8,7 @@ class Config(object): def __init__(self, argv): self.version = "0.3.6" - self.rev = 889 + self.rev = 900 self.argv = argv self.action = None self.config_file = "zeronet.conf" diff --git a/src/Site/SiteStorage.py b/src/Site/SiteStorage.py index 6edd77dc..03110280 100644 --- a/src/Site/SiteStorage.py +++ b/src/Site/SiteStorage.py @@ -206,6 +206,10 @@ class SiteStorage: content = re.sub("\[([^,\{\[]{10,100}?)\]", compact_list, content, flags=re.DOTALL) content = re.sub("\{([^,\[\{]{10,100}?)\}", compact_dict, content, flags=re.DOTALL) + + # Remove end of line whitespace + content = re.sub("(?m)[ ]+$", "", content) + # Write to disk self.write(inner_path, content) diff --git a/src/Test/TestWeb.py b/src/Test/TestWeb.py index e637fde4..72a34a5a 100644 --- a/src/Test/TestWeb.py +++ b/src/Test/TestWeb.py @@ -35,7 +35,7 @@ class TestWeb: assert "Forbidden" in urllib.urlopen("%s/1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr/../../zeronet.py" % site_url).read() def testHomepage(self, browser, site_url): - browser.get("%s" % site_url) + browser.get("%s/1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr" % site_url) assert browser.title == "ZeroHello - ZeroNet" def testLinkSecurity(self, browser, site_url): diff --git a/src/Ui/UiRequest.py b/src/Ui/UiRequest.py index bb85fcf0..155af2f8 100644 --- a/src/Ui/UiRequest.py +++ b/src/Ui/UiRequest.py @@ -229,6 +229,7 @@ class UiRequest(object): query_string = "" body_style = "" meta_tags = "" + postmessage_nonce_security = "false" wrapper_nonce = self.getWrapperNonce() @@ -254,6 +255,9 @@ class UiRequest(object): cgi.escape(site.content_manager.contents["content.json"]["background-color"], True) if content.get("viewport"): meta_tags += '' % cgi.escape(content["viewport"], True) + if content.get("postmessage_nonce_security"): + postmessage_nonce_security = "true" + if site.settings.get("own"): sandbox_permissions = "allow-modals" # For coffeescript compile errors @@ -272,6 +276,8 @@ class UiRequest(object): meta_tags=meta_tags, query_string=query_string, wrapper_key=site.settings["wrapper_key"], + wrapper_nonce=wrapper_nonce, + postmessage_nonce_security=postmessage_nonce_security, permissions=json.dumps(site.settings["permissions"]), show_loadingscreen=json.dumps(not site.storage.isFile(file_inner_path)), sandbox_permissions=sandbox_permissions, diff --git a/src/Ui/UiWebsocket.py b/src/Ui/UiWebsocket.py index 41ca041e..7a3b7f87 100644 --- a/src/Ui/UiWebsocket.py +++ b/src/Ui/UiWebsocket.py @@ -222,7 +222,7 @@ class UiWebsocket(object): def formatServerInfo(self): return { - "ip_external": bool(sys.modules["main"].file_server.port_opened), + "ip_external": sys.modules["main"].file_server.port_opened, "platform": sys.platform, "fileserver_ip": config.fileserver_ip, "fileserver_port": config.fileserver_port, diff --git a/src/Ui/media/Wrapper.coffee b/src/Ui/media/Wrapper.coffee index c0d91756..bb0887fe 100644 --- a/src/Ui/media/Wrapper.coffee +++ b/src/Ui/media/Wrapper.coffee @@ -1,11 +1,6 @@ class Wrapper constructor: (ws_url) -> @log "Created!" - if window.opener - @log "Security error: Opener present, exiting..." - document.write("Forbidden: Opener present.") - document.body.innerHTML = "Forbidden: Opener present." - return @loading = new Loading() @notifications = new Notifications($(".notifications")) @@ -73,6 +68,11 @@ class Wrapper # Incoming message from inner frame onMessageInner: (e) => message = e.data + if window.postmessage_nonce_security and message.wrapper_nonce != window.wrapper_nonce + @log "Message nonce error:", message.wrapper_nonce, '!=', window.wrapper_nonce + @actionNotification({"params": ["error", "Message wrapper_nonce error, please report!"]}) + window.removeEventListener("message", @onMessageInner) + return cmd = message.cmd if cmd == "innerReady" @inner_ready = true @@ -383,4 +383,23 @@ else ws_url = proto.ws + ":" + origin.replace(proto.http+":", "") + "/Websocket?wrapper_key=" + window.wrapper_key -window.wrapper = new Wrapper(ws_url) + +if window.opener + # Window opener security problem workaround: Open a new window, close this one + console.log "Opener present:", window.opener + setTimeout ( -> # Wait 200ms to parent tab closing + if window.opener + # Opener still present, display message + elem = $(" ") + elem.find('a').on "click", -> + window.open("?", "_blank") + window.close() + return false + $("body").prepend(elem) + else + window.location.reload() + # Opener gone, continue init + # window.wrapper = new Wrapper(ws_url) + ), 100 +else + window.wrapper = new Wrapper(ws_url) \ No newline at end of file diff --git a/src/Ui/media/Wrapper.css b/src/Ui/media/Wrapper.css index 5c91e88e..6d4f550c 100644 --- a/src/Ui/media/Wrapper.css +++ b/src/Ui/media/Wrapper.css @@ -130,6 +130,10 @@ a { color: black } box-shadow: 0 0 10px #AF3BFF, 0 0 5px #29d; opacity: 1.0; transform: rotate(3deg) translate(0px, -4px); } +/* Opener overlay */ +.opener-overlay { position: fixed; z-index: 9999; width: 100%; text-align: center; background-color: rgba(100,100,100,0.5); height: 100%; vertical-align: middle; } +.opener-overlay .dialog { background-color: white; padding: 40px; display: inline-block; color: #4F4F4F; font-family: 'Lucida Grande', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; } + /* Icons */ .icon-profile { font-size: 6px; top: 0em; border-radius: 0.7em 0.7em 0 0; background: #FFFFFF; width: 1.5em; height: 0.7em; position: relative; display: inline-block; margin-right: 4px } .icon-profile::before { position: absolute; content: ""; top: -1em; left: 0.38em; width: 0.8em; height: 0.85em; border-radius: 50%; background: #FFFFFF } diff --git a/src/Ui/media/all.css b/src/Ui/media/all.css index bcf20b3a..4f4a5b14 100644 --- a/src/Ui/media/all.css +++ b/src/Ui/media/all.css @@ -135,6 +135,10 @@ a { color: black } -webkit-box-shadow: 0 0 10px #AF3BFF, 0 0 5px #29d; -moz-box-shadow: 0 0 10px #AF3BFF, 0 0 5px #29d; -o-box-shadow: 0 0 10px #AF3BFF, 0 0 5px #29d; -ms-box-shadow: 0 0 10px #AF3BFF, 0 0 5px #29d; box-shadow: 0 0 10px #AF3BFF, 0 0 5px #29d ; opacity: 1.0; -webkit-transform: rotate(3deg) translate(0px, -4px); -moz-transform: rotate(3deg) translate(0px, -4px); -o-transform: rotate(3deg) translate(0px, -4px); -ms-transform: rotate(3deg) translate(0px, -4px); transform: rotate(3deg) translate(0px, -4px) ; } +/* Opener overlay */ +.opener-overlay { position: fixed; z-index: 9999; width: 100%; text-align: center; background-color: rgba(100,100,100,0.5); height: 100%; vertical-align: middle; } +.opener-overlay .dialog { background-color: white; padding: 40px; display: inline-block; color: #4F4F4F; font-family: 'Lucida Grande', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; } + /* Icons */ .icon-profile { font-size: 6px; top: 0em; -webkit-border-radius: 0.7em 0.7em 0 0; -moz-border-radius: 0.7em 0.7em 0 0; -o-border-radius: 0.7em 0.7em 0 0; -ms-border-radius: 0.7em 0.7em 0 0; border-radius: 0.7em 0.7em 0 0 ; background: #FFFFFF; width: 1.5em; height: 0.7em; position: relative; display: inline-block; margin-right: 4px } .icon-profile::before { position: absolute; content: ""; top: -1em; left: 0.38em; width: 0.8em; height: 0.85em; -webkit-border-radius: 50%; -moz-border-radius: 50%; -o-border-radius: 50%; -ms-border-radius: 50%; border-radius: 50% ; background: #FFFFFF } diff --git a/src/Ui/media/all.js b/src/Ui/media/all.js index 436e2e9e..60925cc8 100644 --- a/src/Ui/media/all.js +++ b/src/Ui/media/all.js @@ -762,12 +762,6 @@ jQuery.extend( jQuery.easing, this.onMessageInner = __bind(this.onMessageInner, this); this.onMessageWebsocket = __bind(this.onMessageWebsocket, this); this.log("Created!"); - if (window.opener) { - this.log("Security error: Opener present, exiting..."); - document.write("Forbidden: Opener present."); - document.body.innerHTML = "Forbidden: Opener present."; - return; - } this.loading = new Loading(); this.notifications = new Notifications($(".notifications")); this.fixbutton = new Fixbutton(); @@ -842,6 +836,14 @@ jQuery.extend( jQuery.easing, Wrapper.prototype.onMessageInner = function(e) { var cmd, message, query; message = e.data; + if (window.postmessage_nonce_security && message.wrapper_nonce !== window.wrapper_nonce) { + this.log("Message nonce error:", message.wrapper_nonce, '!=', window.wrapper_nonce); + this.actionNotification({ + "params": ["error", "Message wrapper_nonce error, please report!"] + }); + window.removeEventListener("message", this.onMessageInner); + return; + } cmd = message.cmd; if (cmd === "innerReady") { this.inner_ready = true; @@ -1264,6 +1266,24 @@ jQuery.extend( jQuery.easing, ws_url = proto.ws + ":" + origin.replace(proto.http + ":", "") + "/Websocket?wrapper_key=" + window.wrapper_key; - window.wrapper = new Wrapper(ws_url); + if (window.opener) { + console.log("Opener present:", window.opener); + setTimeout((function() { + var elem; + if (window.opener) { + elem = $(" "); + elem.find('a').on("click", function() { + window.open("?", "_blank"); + window.close(); + return false; + }); + return $("body").prepend(elem); + } else { + return window.location.reload(); + } + }), 100); + } else { + window.wrapper = new Wrapper(ws_url); + } }).call(this); diff --git a/src/Ui/template/wrapper.html b/src/Ui/template/wrapper.html index 8989c240..6dbb0c2f 100644 --- a/src/Ui/template/wrapper.html +++ b/src/Ui/template/wrapper.html @@ -17,6 +17,7 @@ if (window.self !== window.top) window.open(window.location.toString(), "_top"); if (window.self !== window.top) window.stop(); if (window.self !== window.top && document.execCommand) document.execCommand("Stop", false) + // Dont allow site to load in a popup /* if (window.opener) document.write("Opener not allowed") @@ -61,7 +62,9 @@ if (window.opener && window.stop) window.stop() document.getElementById("inner-iframe").src = "about:blank" document.getElementById("inner-iframe").src = "{file_url}{query_string}" address = "{address}" +wrapper_nonce = "{wrapper_nonce}" wrapper_key = "{wrapper_key}" +postmessage_nonce_security = {postmessage_nonce_security} file_inner_path = "{file_inner_path}" permissions = {permissions} show_loadingscreen = {show_loadingscreen} diff --git a/src/Worker/WorkerManager.py b/src/Worker/WorkerManager.py index c4da30ff..9819ad64 100644 --- a/src/Worker/WorkerManager.py +++ b/src/Worker/WorkerManager.py @@ -47,7 +47,8 @@ class WorkerManager: tasks = self.tasks[:] # Copy it so removing elements wont cause any problem for task in tasks: - if task["time_started"] and time.time() >= task["time_started"] + 60: # Task taking too long time, skip it + size_extra_time = task["size"] / (1024*100) # 1 second for every 100k + if task["time_started"] and time.time() >= task["time_started"] + 60 + size_extra_time: # Task taking too long time, skip it self.log.debug("Timeout, Skipping: %s" % task) # Skip to next file workers workers = self.findWorkers(task) @@ -56,17 +57,17 @@ class WorkerManager: worker.skip() else: self.failTask(task) - elif time.time() >= task["time_added"] + 60 and not self.workers: # No workers left + elif time.time() >= task["time_added"] + 60 + size_extra_time and not self.workers: # No workers left self.log.debug("Timeout, Cleanup task: %s" % task) # Remove task self.failTask(task) elif (task["time_started"] and time.time() >= task["time_started"] + 15) or not self.workers: - # Task started more than 15 sec ago or no workers + # Find more workers: Task started more than 15 sec ago or no workers workers = self.findWorkers(task) self.log.debug( - "Task taking more than 15 secs, workers: %s find more peers: %s" % - (len(workers), task["inner_path"]) + "Task taking more than 15+%s secs, workers: %s find more peers: %s" % + (size_extra_time, len(workers), task["inner_path"]) ) task["site"].announce(mode="more") # Find more peers if task["optional_hash_id"]: @@ -326,9 +327,13 @@ class WorkerManager: optional_hash_id = helper.toHashId(file_info["sha512"]) else: optional_hash_id = None + if file_info: + size = file_info.get("size", 0) + else: + size = 0 task = { "evt": evt, "workers_num": 0, "site": self.site, "inner_path": inner_path, "done": False, "optional_hash_id": optional_hash_id, - "time_added": time.time(), "time_started": None, "time_action": None, "peers": peers, "priority": priority, "failed": [] + "time_added": time.time(), "time_started": None, "time_action": None, "peers": peers, "priority": priority, "failed": [], "size": size } self.tasks.append(task)