diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py
index 3923f1f7..72a003f3 100644
--- a/plugins/Sidebar/SidebarPlugin.py
+++ b/plugins/Sidebar/SidebarPlugin.py
@@ -65,14 +65,16 @@ class UiWebsocketPlugin(object):
if peers_total:
percent_connected = float(connected) / peers_total
percent_connectable = float(connectable) / peers_total
+ percent_onion = float(onion) / peers_total
else:
- percent_connectable = percent_connected = 0
+ percent_connectable = percent_connected = percent_onion = 0
body.append("""
")
diff --git a/plugins/Sidebar/media-globe/globe.js b/plugins/Sidebar/media-globe/globe.js
index 4f9abec8..ba736185 100644
--- a/plugins/Sidebar/media-globe/globe.js
+++ b/plugins/Sidebar/media-globe/globe.js
@@ -432,5 +432,4 @@ DAT.Globe = function(container, opts) {
return this;
-};
-
+};
\ No newline at end of file
diff --git a/plugins/Sidebar/media/Sidebar.coffee b/plugins/Sidebar/media/Sidebar.coffee
index 03ef5a77..0c732fae 100644
--- a/plugins/Sidebar/media/Sidebar.coffee
+++ b/plugins/Sidebar/media/Sidebar.coffee
@@ -108,7 +108,8 @@ class Sidebar extends Class
@original_set_site_info.apply(wrapper, arguments)
setSiteInfo: (site_info) ->
- @updateHtmlTag()
+ RateLimit 1000, =>
+ @updateHtmlTag()
@displayGlobe()
@@ -137,7 +138,7 @@ class Sidebar extends Class
@log "Patching content"
morphdom @tag.find(".content")[0], '
'+res+'
', {
onBeforeMorphEl: (from_el, to_el) -> # Ignore globe loaded state
- if from_el.className == "globe"
+ if from_el.className == "globe" or from_el.className.indexOf("noupdate") >= 0
return false
else
return true
@@ -234,6 +235,34 @@ class Sidebar extends Class
@updateHtmlTag()
return false
+ # Update site
+ @tag.find("#button-update").off("click").on "click", =>
+ @tag.find("#button-update").addClass("loading")
+ wrapper.ws.cmd "siteUpdate", wrapper.site_info.address, =>
+ wrapper.notifications.add "done-updated", "done", "Site updated!", 5000
+ @tag.find("#button-update").removeClass("loading")
+ return false
+
+ # Pause site
+ @tag.find("#button-pause").off("click").on "click", =>
+ @tag.find("#button-pause").addClass("hidden")
+ wrapper.ws.cmd "sitePause", wrapper.site_info.address
+ return false
+
+ # Resume site
+ @tag.find("#button-resume").off("click").on "click", =>
+ @tag.find("#button-resume").addClass("hidden")
+ wrapper.ws.cmd "siteResume", wrapper.site_info.address
+ return false
+
+ # Delete site
+ @tag.find("#button-delete").off("click").on "click", =>
+ wrapper.displayConfirm "Are you sure?", "Delete this site", =>
+ @tag.find("#button-delete").addClass("loading")
+ wrapper.ws.cmd "siteDelete", wrapper.site_info.address, ->
+ document.location = $(".fixbutton-bg").attr("href")
+ return false
+
# Owned checkbox
@tag.find("#checkbox-owned").off("click").on "click", =>
wrapper.ws.cmd "siteSetOwned", [@tag.find("#checkbox-owned").is(":checked")]
diff --git a/plugins/Sidebar/media/Sidebar.css b/plugins/Sidebar/media/Sidebar.css
index 7d8466ba..1bf834da 100644
--- a/plugins/Sidebar/media/Sidebar.css
+++ b/plugins/Sidebar/media/Sidebar.css
@@ -13,8 +13,10 @@
.sidebar { background-color: #212121; position: fixed; backface-visibility: hidden; right: -1200px; height: 100%; width: 1200px; } /*box-shadow: inset 0px 0px 10px #000*/
.sidebar .content { margin: 30px; font-family: "Segoe UI Light", "Segoe UI", "Helvetica Neue"; color: white; width: 375px; height: 300px; font-weight: 200 }
.sidebar h1, .sidebar h2 { font-weight: lighter; }
-.sidebar .button { margin: 0px; display: inline-block; }
-
+.sidebar .button { margin: 0px; display: inline-block; transition: all 0.3s; box-sizing: border-box; max-width: 160px }
+.sidebar .button.hidden { padding: 0px; max-width: 0px; opacity: 0; pointer-events: none }
+.sidebar #button-delete { background-color: transparent; border: 1px solid #333; color: #AAA; margin-left: 10px }
+.sidebar #button-delete:hover { border: 1px solid #666; color: white }
/* FIELDS */
@@ -23,7 +25,7 @@
.sidebar .fields > li:after, .sidebar .fields .settings-owned > li:after { clear: both; content: ''; display: block }
.sidebar .fields label {
font-family: Consolas, monospace; text-transform: uppercase; font-size: 13px; color: #ACACAC; display: inline-block; margin-bottom: 10px;
- vertical-align: text-bottom; margin-right: 10px;
+ vertical-align: text-bottom; margin-right: 10px; width: 100%
}
.sidebar .fields label small { font-weight: normal; color: white; text-transform: none; }
.sidebar .fields .text { background-color: black; border: 0px; padding: 10px; color: white; border-radius: 3px; width: 250px; font-family: Consolas, monospace; }
diff --git a/plugins/Sidebar/media/all.css b/plugins/Sidebar/media/all.css
index 5f357fdd..7852486b 100644
--- a/plugins/Sidebar/media/all.css
+++ b/plugins/Sidebar/media/all.css
@@ -67,8 +67,10 @@
.sidebar { background-color: #212121; position: fixed; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; right: -1200px; height: 100%; width: 1200px; } /*box-shadow: inset 0px 0px 10px #000*/
.sidebar .content { margin: 30px; font-family: "Segoe UI Light", "Segoe UI", "Helvetica Neue"; color: white; width: 375px; height: 300px; font-weight: 200 }
.sidebar h1, .sidebar h2 { font-weight: lighter; }
-.sidebar .button { margin: 0px; display: inline-block; }
-
+.sidebar .button { margin: 0px; display: inline-block; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; max-width: 160px }
+.sidebar .button.hidden { padding: 0px; max-width: 0px; opacity: 0; pointer-events: none }
+.sidebar #button-delete { background-color: transparent; border: 1px solid #333; color: #AAA; margin-left: 10px }
+.sidebar #button-delete:hover { border: 1px solid #666; color: white }
/* FIELDS */
@@ -77,7 +79,7 @@
.sidebar .fields > li:after, .sidebar .fields .settings-owned > li:after { clear: both; content: ''; display: block }
.sidebar .fields label {
font-family: Consolas, monospace; text-transform: uppercase; font-size: 13px; color: #ACACAC; display: inline-block; margin-bottom: 10px;
- vertical-align: text-bottom; margin-right: 10px;
+ vertical-align: text-bottom; margin-right: 10px; width: 100%
}
.sidebar .fields label small { font-weight: normal; color: white; text-transform: none; }
.sidebar .fields .text { background-color: black; border: 0px; padding: 10px; color: white; -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ; width: 250px; font-family: Consolas, monospace; }
diff --git a/plugins/Sidebar/media/all.js b/plugins/Sidebar/media/all.js
index cf89bf8f..42db3a97 100644
--- a/plugins/Sidebar/media/all.js
+++ b/plugins/Sidebar/media/all.js
@@ -86,7 +86,6 @@
}).call(this);
-
/* ---- plugins/Sidebar/media/Scrollable.js ---- */
@@ -316,7 +315,11 @@ window.initScrollable = function () {
};
Sidebar.prototype.setSiteInfo = function(site_info) {
- this.updateHtmlTag();
+ RateLimit(1000, (function(_this) {
+ return function() {
+ return _this.updateHtmlTag();
+ };
+ })(this));
return this.displayGlobe();
};
@@ -341,7 +344,7 @@ window.initScrollable = function () {
_this.log("Patching content");
morphdom(_this.tag.find(".content")[0], '
' + res + '
', {
onBeforeMorphEl: function(from_el, to_el) {
- if (from_el.className === "globe") {
+ if (from_el.className === "globe" || from_el.className.indexOf("noupdate") >= 0) {
return false;
} else {
return true;
@@ -449,6 +452,41 @@ window.initScrollable = function () {
return false;
};
})(this));
+ this.tag.find("#button-update").off("click").on("click", (function(_this) {
+ return function() {
+ _this.tag.find("#button-update").addClass("loading");
+ wrapper.ws.cmd("siteUpdate", wrapper.site_info.address, function() {
+ wrapper.notifications.add("done-updated", "done", "Site updated!", 5000);
+ return _this.tag.find("#button-update").removeClass("loading");
+ });
+ return false;
+ };
+ })(this));
+ this.tag.find("#button-pause").off("click").on("click", (function(_this) {
+ return function() {
+ _this.tag.find("#button-pause").addClass("hidden");
+ wrapper.ws.cmd("sitePause", wrapper.site_info.address);
+ return false;
+ };
+ })(this));
+ this.tag.find("#button-resume").off("click").on("click", (function(_this) {
+ return function() {
+ _this.tag.find("#button-resume").addClass("hidden");
+ wrapper.ws.cmd("siteResume", wrapper.site_info.address);
+ return false;
+ };
+ })(this));
+ this.tag.find("#button-delete").off("click").on("click", (function(_this) {
+ return function() {
+ wrapper.displayConfirm("Are you sure?", "Delete this site", function() {
+ _this.tag.find("#button-delete").addClass("loading");
+ return wrapper.ws.cmd("siteDelete", wrapper.site_info.address, function() {
+ return document.location = $(".fixbutton-bg").attr("href");
+ });
+ });
+ return false;
+ };
+ })(this));
this.tag.find("#checkbox-owned").off("click").on("click", (function(_this) {
return function() {
return wrapper.ws.cmd("siteSetOwned", [_this.tag.find("#checkbox-owned").is(":checked")]);
diff --git a/src/Config.py b/src/Config.py
index 24cfc3b5..a825e191 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 = 949
+ self.rev = 957
self.argv = argv
self.action = None
self.config_file = "zeronet.conf"
diff --git a/src/Content/ContentManager.py b/src/Content/ContentManager.py
index 101642a0..d509eff7 100644
--- a/src/Content/ContentManager.py
+++ b/src/Content/ContentManager.py
@@ -311,7 +311,7 @@ class ContentManager(object):
ignored = True
elif not re.match("^[a-zA-Z0-9_\.\+\-/]+$", file_relative_path):
ignored = True
- self.log.error("- [ERROR] Only ascii encodes filenames allowed: %s" % file_relative_path)
+ self.log.error("- [ERROR] Only ascii encoded filenames allowed: %s" % file_relative_path)
elif optional_pattern and re.match(optional_pattern, file_relative_path):
optional = True
diff --git a/src/File/FileRequest.py b/src/File/FileRequest.py
index b3cf9a42..289bd547 100644
--- a/src/File/FileRequest.py
+++ b/src/File/FileRequest.py
@@ -306,25 +306,35 @@ class FileRequest(object):
found = site.worker_manager.findOptionalHashIds(params["hash_ids"])
- back = {}
+ back_ip4 = {}
+ back_onion = {}
for hash_id, peers in found.iteritems():
- back[hash_id] = [helper.packAddress(peer.ip, peer.port) for peer in peers]
+ back_onion[hash_id] = [helper.packOnionAddress(peer.ip, peer.port) for peer in peers if peer.ip.endswith("onion")]
+ back_ip4[hash_id] = [helper.packAddress(peer.ip, peer.port) for peer in peers if not peer.ip.endswith("onion")]
+
# Check my hashfield
- if config.ip_external:
- my_ip = config.ip_external
- else:
- my_ip = self.server.ip
+ if self.server.tor_manager and self.server.tor_manager.site_onions.get(site.address): # Running onion
+ my_ip = helper.packOnionAddress(self.server.tor_manager.site_onions[site.address], self.server.port)
+ my_back = back_onion
+ elif config.ip_external: # External ip definied
+ my_ip = helper.packAddress(config.ip_external, self.server.port)
+ my_back = back_ip4
+ else: # No external ip defined
+ my_ip = my_ip = helper.packAddress(self.server.ip, self.server.port)
+ my_back = back_ip4
+
for hash_id in params["hash_ids"]:
if hash_id in site.content_manager.hashfield:
- if hash_id not in back:
- back[hash_id] = []
- back[hash_id].append(helper.packAddress(my_ip, self.server.port)) # Add myself
+ if hash_id not in my_back:
+ my_back[hash_id] = []
+ my_back[hash_id].append(my_ip) # Add myself
+
if config.verbose:
self.log.debug(
- "Found: %s/%s" %
- (len(back), len(params["hash_ids"]))
+ "Found: %s,%s/%s" %
+ (len(back_ip4), len(back_onion), len(params["hash_ids"]))
)
- self.response({"peers": back})
+ self.response({"peers": back_ip4, "peers_onion": back_onion})
def actionSetHashfield(self, params):
site = self.sites.get(params["site"])
diff --git a/src/Peer/Peer.py b/src/Peer/Peer.py
index b658a76a..35edf61e 100644
--- a/src/Peer/Peer.py
+++ b/src/Peer/Peer.py
@@ -274,7 +274,15 @@ class Peer(object):
res = self.request("findHashIds", {"site": self.site.address, "hash_ids": hash_ids})
if not res or "error" in res:
return False
- return {key: map(helper.unpackAddress, val) for key, val in res["peers"].iteritems()}
+ # Unpack IP4
+ back = {key: map(helper.unpackAddress, val) for key, val in res["peers"].iteritems()}
+ # Unpack onion
+ for hash, onion_peers in res.get("peers_onion", {}).iteritems():
+ if not hash in back:
+ back[hash] = []
+ back[hash] += map(helper.unpackOnionAddress, onion_peers)
+
+ return back
# Send my hashfield to peer
# Return: True if sent
diff --git a/src/Site/Site.py b/src/Site/Site.py
index 6c2813e1..4512b2e5 100644
--- a/src/Site/Site.py
+++ b/src/Site/Site.py
@@ -176,7 +176,7 @@ class Site(object):
# Retry download bad files
def retryBadFiles(self, force=False):
for bad_file, tries in self.bad_files.items():
- if force or random.randint(0, min(20, tries)) == 0: # Larger number tries = less likely to check every 15min
+ if force or random.randint(0, min(40, tries)) < 4: # Larger number tries = less likely to check every 15min
self.needFile(bad_file, update=True, blocking=False)
# Download all files of the site
diff --git a/src/Site/SiteStorage.py b/src/Site/SiteStorage.py
index 6664de84..8d7c6187 100644
--- a/src/Site/SiteStorage.py
+++ b/src/Site/SiteStorage.py
@@ -247,6 +247,7 @@ class SiteStorage:
file_abspath = os.path.dirname(os.path.abspath(file_path))
if ".." in file_path or not file_abspath.startswith(self.allowed_dir):
+ self.site.log.error(u"File %s not in allowed dir: %s" % (file_path, self.allowed_dir))
raise Exception(u"File not allowed: %s" % file_path)
return file_path
diff --git a/src/Test/TestPeer.py b/src/Test/TestPeer.py
index 1140c02f..b419a21a 100644
--- a/src/Test/TestPeer.py
+++ b/src/Test/TestPeer.py
@@ -155,3 +155,10 @@ class TestPeer:
1234: [('1.2.3.4', 1544), ('1.2.3.5', 1545)],
1235: [('1.2.3.5', 1545), ('1.2.3.6', 1546)]
}
+
+ # Test my address adding
+ site.content_manager.hashfield.append(1234)
+
+ res = peer_file_server.findHashIds([1234, 1235])
+ assert res[1234] == [('1.2.3.4', 1544), ('1.2.3.5', 1545), ("127.0.0.1", 1544)]
+ assert res[1235] == [('1.2.3.5', 1545), ('1.2.3.6', 1546)]
\ No newline at end of file
diff --git a/src/Test/TestTor.py b/src/Test/TestTor.py
index 2cdbb9e4..ec154f99 100644
--- a/src/Test/TestTor.py
+++ b/src/Test/TestTor.py
@@ -102,6 +102,43 @@ class TestTor:
assert peer_source.pex(need_num=10) == 1 # Need >5 to return also return non-connected peers
assert "bka4ht2bzxchy44r.onion:1555" in site_temp.peers
+ def testFindHash(self, tor_manager, file_server, site, site_temp):
+ file_server.ip_incoming = {} # Reset flood protection
+ file_server.sites[site.address] = site
+ file_server.tor_manager = tor_manager
+
+ client = FileServer("127.0.0.1", 1545)
+ client.sites[site_temp.address] = site_temp
+ site_temp.connection_server = client
+
+ # Add file_server as peer to client
+ peer_file_server = site_temp.addPeer("127.0.0.1", 1544)
+
+ assert peer_file_server.findHashIds([1234]) == {}
+
+ # Add fake peer with requred hash
+ fake_peer_1 = site.addPeer("bka4ht2bzxchy44r.onion", 1544)
+ fake_peer_1.hashfield.append(1234)
+ fake_peer_2 = site.addPeer("1.2.3.5", 1545)
+ fake_peer_2.hashfield.append(1234)
+ fake_peer_2.hashfield.append(1235)
+ fake_peer_3 = site.addPeer("1.2.3.6", 1546)
+ fake_peer_3.hashfield.append(1235)
+ fake_peer_3.hashfield.append(1236)
+
+ assert peer_file_server.findHashIds([1234, 1235]) == {
+ 1234: [('1.2.3.5', 1545), ("bka4ht2bzxchy44r.onion", 1544)],
+ 1235: [('1.2.3.6', 1546), ('1.2.3.5', 1545)]
+ }
+
+ # Test my address adding
+ site.content_manager.hashfield.append(1234)
+ my_onion_address = tor_manager.getOnion(site_temp.address)+".onion"
+
+ res = peer_file_server.findHashIds([1234, 1235])
+ assert res[1234] == [('1.2.3.5', 1545), ("bka4ht2bzxchy44r.onion", 1544), (my_onion_address, 1544)]
+ assert res[1235] == [('1.2.3.6', 1546), ('1.2.3.5', 1545)]
+
def testSiteOnion(self, tor_manager):
assert tor_manager.getOnion("address1") != tor_manager.getOnion("address2")
assert tor_manager.getOnion("address1") == tor_manager.getOnion("address1")
diff --git a/src/Tor/TorManager.py b/src/Tor/TorManager.py
index 0d99c626..7e4123be 100644
--- a/src/Tor/TorManager.py
+++ b/src/Tor/TorManager.py
@@ -153,7 +153,7 @@ class TorManager:
version = re.search('Tor="([0-9\.]+)', res_protocol).group(1)
# Version 0.2.7.5 required because ADD_ONION support
- assert int(version.replace(".", "0")) >= 20705, "Tor version >=0.2.7.5 required"
+ assert float(version.replace(".", "0", 2)) >= 207.5, "Tor version >=0.2.7.5 required, found: %s" % version
# Auth cookie file
cookie_match = re.search('COOKIEFILE="(.*?)"', res_protocol)
diff --git a/src/Ui/UiWebsocket.py b/src/Ui/UiWebsocket.py
index 0643e819..74cded92 100644
--- a/src/Ui/UiWebsocket.py
+++ b/src/Ui/UiWebsocket.py
@@ -534,9 +534,13 @@ class UiWebsocket(object):
# Update site content.json
def actionSiteUpdate(self, to, address):
+ def updateThread():
+ site.update()
+ self.response(to, "Updated")
+
site = self.server.sites.get(address)
if site and (site.address == self.site.address or "ADMIN" in self.site.settings["permissions"]):
- gevent.spawn(site.update)
+ gevent.spawn(updateThread)
else:
self.response(to, {"error": "Unknown site: %s" % address})
@@ -548,6 +552,7 @@ class UiWebsocket(object):
site.saveSettings()
site.updateWebsocket()
site.worker_manager.stopWorkers()
+ self.response(to, "Paused")
else:
self.response(to, {"error": "Unknown site: %s" % address})
@@ -560,6 +565,7 @@ class UiWebsocket(object):
gevent.spawn(site.update, announce=True)
time.sleep(0.001) # Wait for update thread starting
site.updateWebsocket()
+ self.response(to, "Resumed")
else:
self.response(to, {"error": "Unknown site: %s" % address})
@@ -574,6 +580,7 @@ class UiWebsocket(object):
site.updateWebsocket()
SiteManager.site_manager.delete(address)
self.user.deleteSiteData(address)
+ self.response(to, "Deleted")
else:
self.response(to, {"error": "Unknown site: %s" % address})
diff --git a/src/Ui/media/Wrapper.coffee b/src/Ui/media/Wrapper.coffee
index 8889372c..b675d946 100644
--- a/src/Ui/media/Wrapper.coffee
+++ b/src/Ui/media/Wrapper.coffee
@@ -79,6 +79,9 @@ class Wrapper
@opener = false
message = e.data
+ if not message.cmd
+ return false
+
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!"]})
diff --git a/src/Ui/media/all.js b/src/Ui/media/all.js
index 617f493e..8e4a406b 100644
--- a/src/Ui/media/all.js
+++ b/src/Ui/media/all.js
@@ -857,6 +857,9 @@ jQuery.extend( jQuery.easing,
}
}
message = e.data;
+ if (!message.cmd) {
+ return false;
+ }
if (window.postmessage_nonce_security && message.wrapper_nonce !== window.wrapper_nonce) {
this.log("Message nonce error:", message.wrapper_nonce, '!=', window.wrapper_nonce);
this.actionNotification({