ZeroNet/plugins/Sidebar/SidebarPlugin.py

804 lines
31 KiB
Python
Raw Normal View History

import re
import os
2019-03-15 21:06:59 +01:00
import html
import sys
import math
import time
import json
2019-03-15 21:06:59 +01:00
import io
import urllib
import urllib.parse
import gevent
2019-11-19 01:44:26 +01:00
import util
from Config import config
from Plugin import PluginManager
from Debug import Debug
2016-11-18 20:13:12 +01:00
from Translate import Translate
from util import helper
2019-08-26 03:20:07 +02:00
from util.Flag import flag
2019-03-15 21:06:59 +01:00
from .ZipStream import ZipStream
plugin_dir = os.path.dirname(__file__)
media_dir = plugin_dir + "/media"
loc_cache = {}
2016-11-18 20:13:12 +01:00
if "_" not in locals():
2016-12-03 13:20:26 +01:00
_ = Translate(plugin_dir + "/languages/")
@PluginManager.registerTo("UiRequest")
class UiRequestPlugin(object):
# Inject our resources to end of original file streams
def actionUiMedia(self, path):
if path == "/uimedia/all.js" or path == "/uimedia/all.css":
# First yield the original file and header
body_generator = super(UiRequestPlugin, self).actionUiMedia(path)
for part in body_generator:
yield part
# Append our media file to the end
ext = re.match(".*(js|css)$", path).group(1)
plugin_media_file = "%s/all.%s" % (media_dir, ext)
if config.debug:
# If debugging merge *.css to all.css and *.js to all.js
from Debug import DebugMedia
DebugMedia.merge(plugin_media_file)
2016-11-18 20:13:12 +01:00
if ext == "js":
2019-03-15 21:06:59 +01:00
yield _.translateData(open(plugin_media_file).read()).encode("utf8")
2016-11-18 20:13:12 +01:00
else:
for part in self.actionFile(plugin_media_file, send_header=False):
yield part
elif path.startswith("/uimedia/globe/"): # Serve WebGL globe files
file_name = re.match(".*/(.*)", path).group(1)
plugin_media_file = "%s_globe/%s" % (media_dir, file_name)
if config.debug and path.endswith("all.js"):
# If debugging merge *.css to all.css and *.js to all.js
from Debug import DebugMedia
DebugMedia.merge(plugin_media_file)
for part in self.actionFile(plugin_media_file):
yield part
else:
for part in super(UiRequestPlugin, self).actionUiMedia(path):
yield part
2018-05-02 02:31:31 +02:00
def actionZip(self):
address = self.get["address"]
site = self.server.site_manager.get(address)
if not site:
return self.error404("Site not found")
title = site.content_manager.contents.get("content.json", {}).get("title", "")
2018-05-02 02:31:31 +02:00
filename = "%s-backup-%s.zip" % (title, time.strftime("%Y-%m-%d_%H_%M"))
filename_quoted = urllib.parse.quote(filename)
self.sendHeader(content_type="application/zip", extra_headers={'Content-Disposition': 'attachment; filename="%s"' % filename_quoted})
2018-05-02 02:31:31 +02:00
return self.streamZip(site.storage.getPath("."))
2019-05-30 04:27:01 +02:00
def streamZip(self, dir_path):
zs = ZipStream(dir_path)
2018-05-02 02:31:31 +02:00
while 1:
data = zs.read()
if not data:
break
yield data
@PluginManager.registerTo("UiWebsocket")
class UiWebsocketPlugin(object):
def sidebarRenderPeerStats(self, body, site):
2019-03-15 21:06:59 +01:00
connected = len([peer for peer in list(site.peers.values()) if peer.connection and peer.connection.connected])
connectable = len([peer_id for peer_id in list(site.peers.keys()) if not peer_id.endswith(":0")])
onion = len([peer_id for peer_id in list(site.peers.keys()) if ".onion" in peer_id])
local = len([peer for peer in list(site.peers.values()) if helper.isPrivateIp(peer.ip)])
peers_total = len(site.peers)
# Add myself
if site.isServing():
peers_total += 1
if any(site.connection_server.port_opened.values()):
connectable += 1
if site.connection_server.tor_manager.start_onions:
onion += 1
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 = percent_onion = 0
2016-11-18 20:13:54 +01:00
2018-03-10 02:03:12 +01:00
if local:
2019-03-15 21:06:59 +01:00
local_html = _("<li class='color-yellow'><span>{_[Local]}:</span><b>{local}</b></li>")
2018-03-10 02:03:12 +01:00
else:
local_html = ""
peer_ips = [peer.key for peer in site.getConnectablePeers(20, allow_private=False)]
peer_ips.sort(key=lambda peer_ip: ".onion:" in peer_ip)
copy_link = "http://127.0.0.1:43110/%s/?zeronet_peers=%s" % (
site.content_manager.contents.get("content.json", {}).get("domain", site.address),
",".join(peer_ips)
)
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
<label>
{_[Peers]}
2018-09-21 02:17:53 +02:00
<small class="label-right"><a href='{copy_link}' id='link-copypeers' class='link-right'>{_[Copy to clipboard]}</a></small>
</label>
<ul class='graph'>
2016-11-18 20:13:54 +01:00
<li style='width: 100%' class='total back-black' title="{_[Total peers]}"></li>
<li style='width: {percent_connectable:.0%}' class='connectable back-blue' title='{_[Connectable peers]}'></li>
<li style='width: {percent_onion:.0%}' class='connected back-purple' title='{_[Onion]}'></li>
<li style='width: {percent_connected:.0%}' class='connected back-green' title='{_[Connected peers]}'></li>
</ul>
<ul class='graph-legend'>
2016-11-18 20:13:54 +01:00
<li class='color-green'><span>{_[Connected]}:</span><b>{connected}</b></li>
<li class='color-blue'><span>{_[Connectable]}:</span><b>{connectable}</b></li>
<li class='color-purple'><span>{_[Onion]}:</span><b>{onion}</b></li>
2018-03-10 02:03:12 +01:00
{local_html}
2016-11-18 20:13:54 +01:00
<li class='color-black'><span>{_[Total]}:</span><b>{peers_total}</b></li>
</ul>
</li>
""".replace("{local_html}", local_html)))
def sidebarRenderTransferStats(self, body, site):
recv = float(site.settings.get("bytes_recv", 0)) / 1024 / 1024
sent = float(site.settings.get("bytes_sent", 0)) / 1024 / 1024
transfer_total = recv + sent
if transfer_total:
percent_recv = recv / transfer_total
percent_sent = sent / transfer_total
else:
percent_recv = 0.5
percent_sent = 0.5
2016-11-18 20:13:54 +01:00
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2016-11-18 20:13:54 +01:00
<label>{_[Data transfer]}</label>
<ul class='graph graph-stacked'>
2016-11-18 20:13:54 +01:00
<li style='width: {percent_recv:.0%}' class='received back-yellow' title="{_[Received bytes]}"></li>
<li style='width: {percent_sent:.0%}' class='sent back-green' title="{_[Sent bytes]}"></li>
</ul>
<ul class='graph-legend'>
2016-11-18 20:13:54 +01:00
<li class='color-yellow'><span>{_[Received]}:</span><b>{recv:.2f}MB</b></li>
<li class='color-green'<span>{_[Sent]}:</span><b>{sent:.2f}MB</b></li>
</ul>
</li>
2016-11-18 20:13:54 +01:00
"""))
def sidebarRenderFileStats(self, body, site):
2019-03-15 21:06:59 +01:00
body.append(_("""
2018-05-02 02:31:31 +02:00
<li>
<label>
{_[Files]}
2018-09-21 02:17:53 +02:00
<small class="label-right"><a href='#Site+directory' id='link-directory' class='link-right'>{_[Open site directory]}</a>
2018-07-19 20:40:55 +02:00
<a href='/ZeroNet-Internal/Zip?address={site.address}' id='link-zip' class='link-right' download='site.zip'>{_[Save as .zip]}</a></small>
2018-05-02 02:31:31 +02:00
</label>
<ul class='graph graph-stacked'>
"""))
extensions = (
("html", "yellow"),
("css", "orange"),
("js", "purple"),
2016-11-18 20:13:54 +01:00
("Image", "green"),
2016-09-04 17:46:09 +02:00
("json", "darkblue"),
2016-11-18 20:13:54 +01:00
("User data", "blue"),
("Other", "white"),
("Total", "black")
)
# Collect stats
size_filetypes = {}
size_total = 0
2016-09-04 17:46:09 +02:00
contents = site.content_manager.listContents() # Without user files
for inner_path in contents:
content = site.content_manager.contents[inner_path]
if "files" not in content or content["files"] is None:
continue
2019-03-15 21:06:59 +01:00
for file_name, file_details in list(content["files"].items()):
size_total += file_details["size"]
ext = file_name.split(".")[-1]
size_filetypes[ext] = size_filetypes.get(ext, 0) + file_details["size"]
2016-04-27 11:14:15 +02:00
2016-09-04 17:46:09 +02:00
# Get user file sizes
size_user_content = site.content_manager.contents.execute(
"SELECT SUM(size) + SUM(size_files) AS size FROM content WHERE ?",
{"not__inner_path": contents}
).fetchone()["size"]
if not size_user_content:
size_user_content = 0
2016-11-18 20:13:54 +01:00
size_filetypes["User data"] = size_user_content
2016-09-04 17:46:09 +02:00
size_total += size_user_content
2016-04-27 11:14:15 +02:00
# The missing difference is content.json sizes
if "json" in size_filetypes:
2016-09-04 17:46:09 +02:00
size_filetypes["json"] += max(0, site.settings["size"] - size_total)
2016-04-27 11:14:15 +02:00
size_total = size_other = site.settings["size"]
# Bar
2017-07-18 22:39:48 +02:00
for extension, color in extensions:
2016-11-18 20:13:54 +01:00
if extension == "Total":
continue
2016-11-18 20:13:54 +01:00
if extension == "Other":
2016-09-04 17:46:09 +02:00
size = max(0, size_other)
2016-11-18 20:13:54 +01:00
elif extension == "Image":
size = size_filetypes.get("jpg", 0) + size_filetypes.get("png", 0) + size_filetypes.get("gif", 0)
size_other -= size
else:
size = size_filetypes.get(extension, 0)
size_other -= size
if size_total == 0:
percent = 0
else:
percent = 100 * (float(size) / size_total)
percent = math.floor(percent * 100) / 100 # Floor to 2 digits
body.append(
2019-03-15 21:06:59 +01:00
"""<li style='width: %.2f%%' class='%s back-%s' title="%s"></li>""" %
2016-11-18 20:13:54 +01:00
(percent, _[extension], color, _[extension])
)
# Legend
body.append("</ul><ul class='graph-legend'>")
for extension, color in extensions:
2016-11-18 20:13:54 +01:00
if extension == "Other":
2016-09-04 17:46:09 +02:00
size = max(0, size_other)
2016-11-18 20:13:54 +01:00
elif extension == "Image":
size = size_filetypes.get("jpg", 0) + size_filetypes.get("png", 0) + size_filetypes.get("gif", 0)
2016-12-30 18:53:24 +01:00
elif extension == "Total":
size = size_total
else:
size = size_filetypes.get(extension, 0)
if extension == "js":
title = "javascript"
else:
title = extension
if size > 1024 * 1024 * 10: # Format as mB is more than 10mB
size_formatted = "%.0fMB" % (size / 1024 / 1024)
else:
size_formatted = "%.0fkB" % (size / 1024)
2019-03-15 21:06:59 +01:00
body.append("<li class='color-%s'><span>%s:</span><b>%s</b></li>" % (color, _[title], size_formatted))
body.append("</ul></li>")
def sidebarRenderSizeLimit(self, body, site):
free_space = helper.getFreeSpace() / 1024 / 1024
size = float(site.settings["size"]) / 1024 / 1024
size_limit = site.getSizeLimit()
percent_used = size / size_limit
2016-11-18 20:13:54 +01:00
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2019-03-15 21:06:59 +01:00
<label>{_[Size limit]} <small>({_[limit used]}: {percent_used:.0%}, {_[free space]}: {free_space:,.0f}MB)</small></label>
<input type='text' class='text text-num' value="{size_limit}" id='input-sitelimit'/><span class='text-post'>MB</span>
2016-11-18 20:13:54 +01:00
<a href='#Set' class='button' id='button-sitelimit'>{_[Set]}</a>
</li>
2016-11-18 20:13:54 +01:00
"""))
def sidebarRenderOptionalFileStats(self, body, site):
2016-11-07 23:48:48 +01:00
size_total = float(site.settings["size_optional"])
size_downloaded = float(site.settings["optional_downloaded"])
if not size_total:
return False
percent_downloaded = size_downloaded / size_total
size_formatted_total = size_total / 1024 / 1024
size_formatted_downloaded = size_downloaded / 1024 / 1024
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2016-11-18 20:13:54 +01:00
<label>{_[Optional files]}</label>
<ul class='graph'>
2016-11-18 20:13:54 +01:00
<li style='width: 100%' class='total back-black' title="{_[Total size]}"></li>
<li style='width: {percent_downloaded:.0%}' class='connected back-green' title='{_[Downloaded files]}'></li>
</ul>
<ul class='graph-legend'>
2016-11-18 20:13:54 +01:00
<li class='color-green'><span>{_[Downloaded]}:</span><b>{size_formatted_downloaded:.2f}MB</b></li>
<li class='color-black'><span>{_[Total]}:</span><b>{size_formatted_total:.2f}MB</b></li>
</ul>
</li>
2016-11-18 20:13:54 +01:00
"""))
return True
def sidebarRenderOptionalFileSettings(self, body, site):
if self.site.settings.get("autodownloadoptional"):
checked = "checked='checked'"
else:
checked = ""
2016-11-18 20:13:54 +01:00
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
<label>{_[Help distribute added optional files]}</label>
<input type="checkbox" class="checkbox" id="checkbox-autodownloadoptional" {checked}/><div class="checkbox-skin"></div>
2016-11-18 20:13:54 +01:00
"""))
if hasattr(config, "autodownload_bigfile_size_limit"):
autodownload_bigfile_size_limit = int(site.settings.get("autodownload_bigfile_size_limit", config.autodownload_bigfile_size_limit))
body.append(_("""
<div class='settings-autodownloadoptional'>
<label>{_[Auto download big file size limit]}</label>
<input type='text' class='text text-num' value="{autodownload_bigfile_size_limit}" id='input-autodownload_bigfile_size_limit'/><span class='text-post'>MB</span>
<a href='#Set' class='button' id='button-autodownload_bigfile_size_limit'>{_[Set]}</a>
<a href='#Download+previous' class='button' id='button-autodownload_previous'>{_[Download previous files]}</a>
</div>
"""))
body.append("</li>")
def sidebarRenderBadFiles(self, body, site):
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
<label>{_[Needs to be updated]}:</label>
<ul class='filelist'>
2016-11-18 20:13:54 +01:00
"""))
i = 0
2019-03-15 21:06:59 +01:00
for bad_file, tries in site.bad_files.items():
i += 1
2019-03-15 21:06:59 +01:00
body.append(_("""<li class='color-red' title="{bad_file_path} ({tries})">{bad_filename}</li>""", {
"bad_file_path": bad_file,
"bad_filename": helper.getFilename(bad_file),
"tries": _.pluralize(tries, "{} try", "{} tries")
2016-11-18 20:13:54 +01:00
}))
if i > 30:
break
if len(site.bad_files) > 30:
2016-11-18 20:13:54 +01:00
num_bad_files = len(site.bad_files) - 30
2019-03-15 21:06:59 +01:00
body.append(_("""<li class='color-red'>{_[+ {num_bad_files} more]}</li>""", nested=True))
body.append("""
</ul>
</li>
""")
def sidebarRenderDbOptions(self, body, site):
if site.storage.db:
inner_path = site.storage.getInnerPath(site.storage.db.db_path)
size = float(site.storage.getSize(inner_path)) / 1024
feeds = len(site.storage.db.schema.get("feeds", {}))
else:
2019-03-15 21:06:59 +01:00
inner_path = _["No database found"]
size = 0.0
feeds = 0
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2016-11-18 20:13:54 +01:00
<label>{_[Database]} <small>({size:.2f}kB, {_[search feeds]}: {_[{feeds} query]})</small></label>
<div class='flex'>
2016-11-24 19:50:30 +01:00
<input type='text' class='text disabled' value="{inner_path}" disabled='disabled'/>
2016-11-18 20:13:54 +01:00
<a href='#Reload' id="button-dbreload" class='button'>{_[Reload]}</a>
<a href='#Rebuild' id="button-dbrebuild" class='button'>{_[Rebuild]}</a>
</div>
</li>
2016-11-18 20:13:54 +01:00
""", nested=True))
def sidebarRenderIdentity(self, body, site):
auth_address = self.user.getAuthAddress(self.site.address, create=False)
rules = self.site.content_manager.getRules("data/users/%s/content.json" % auth_address)
if rules and rules.get("max_size"):
quota = rules["max_size"] / 1024
try:
content = site.content_manager.contents["data/users/%s/content.json" % auth_address]
2019-03-15 21:06:59 +01:00
used = len(json.dumps(content)) + sum([file["size"] for file in list(content["files"].values())])
except:
used = 0
used = used / 1024
else:
quota = used = 0
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2016-11-18 20:13:54 +01:00
<label>{_[Identity address]} <small>({_[limit used]}: {used:.2f}kB / {quota:.2f}kB)</small></label>
<div class='flex'>
<span class='input text disabled'>{auth_address}</span>
<a href='#Change' class='button' id='button-identity'>{_[Change]}</a>
</div>
</li>
2016-11-18 20:13:54 +01:00
"""))
def sidebarRenderControls(self, body, site):
auth_address = self.user.getAuthAddress(self.site.address, create=False)
if self.site.settings["serving"]:
class_pause = ""
class_resume = "hidden"
else:
class_pause = "hidden"
class_resume = ""
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2016-11-18 20:13:54 +01:00
<label>{_[Site control]}</label>
<a href='#Update' class='button noupdate' id='button-update'>{_[Update]}</a>
<a href='#Pause' class='button {class_pause}' id='button-pause'>{_[Pause]}</a>
<a href='#Resume' class='button {class_resume}' id='button-resume'>{_[Resume]}</a>
<a href='#Delete' class='button noupdate' id='button-delete'>{_[Delete]}</a>
</li>
2016-11-18 20:13:54 +01:00
"""))
donate_key = site.content_manager.contents.get("content.json", {}).get("donate", True)
2018-07-07 17:55:20 +02:00
site_address = self.site.address
2019-03-15 21:06:59 +01:00
body.append(_("""
2018-07-07 17:55:20 +02:00
<li>
<label>{_[Site address]}</label><br>
<div class='flex'>
<span class='input text disabled'>{site_address}</span>
"""))
if donate_key == False or donate_key == "":
pass
2019-03-15 21:06:59 +01:00
elif (type(donate_key) == str or type(donate_key) == str) and len(donate_key) > 0:
body.append(_("""
2018-07-07 17:55:20 +02:00
</div>
</li>
<li>
<label>{_[Donate]}</label><br>
<div class='flex'>
{donate_key}
"""))
else:
2019-03-15 21:06:59 +01:00
body.append(_("""
2018-07-07 17:55:20 +02:00
<a href='bitcoin:{site_address}' class='button' id='button-donate'>{_[Donate]}</a>
"""))
2019-03-15 21:06:59 +01:00
body.append(_("""
2018-07-07 17:55:20 +02:00
</div>
</li>
"""))
def sidebarRenderOwnedCheckbox(self, body, site):
if self.site.settings["own"]:
checked = "checked='checked'"
else:
checked = ""
2019-03-15 21:06:59 +01:00
body.append(_("""
2016-11-18 20:13:54 +01:00
<h2 class='owned-title'>{_[This is my site]}</h2>
<input type="checkbox" class="checkbox" id="checkbox-owned" {checked}/><div class="checkbox-skin"></div>
2016-11-18 20:13:54 +01:00
"""))
def sidebarRenderOwnSettings(self, body, site):
title = site.content_manager.contents.get("content.json", {}).get("title", "")
description = site.content_manager.contents.get("content.json", {}).get("description", "")
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2016-11-18 20:13:54 +01:00
<label for='settings-title'>{_[Site title]}</label>
<input type='text' class='text' value="{title}" id='settings-title'/>
</li>
<li>
2016-11-18 20:13:54 +01:00
<label for='settings-description'>{_[Site description]}</label>
<input type='text' class='text' value="{description}" id='settings-description'/>
</li>
<li>
2016-11-18 20:13:54 +01:00
<a href='#Save' class='button' id='button-settings'>{_[Save site settings]}</a>
</li>
2016-11-18 20:13:54 +01:00
"""))
def sidebarRenderContents(self, body, site):
has_privatekey = bool(self.user.getSiteData(site.address, create=False).get("privatekey"))
2018-09-21 02:17:53 +02:00
if has_privatekey:
tag_privatekey = _("{_[Private key saved.]} <a href='#Forget+private+key' id='privatekey-forget' class='link-right'>{_[Forget]}</a>")
2018-09-21 02:17:53 +02:00
else:
2019-03-15 21:06:59 +01:00
tag_privatekey = _("<a href='#Add+private+key' id='privatekey-add' class='link-right'>{_[Add saved private key]}</a>")
2018-09-21 02:17:53 +02:00
2019-03-15 21:06:59 +01:00
body.append(_("""
<li>
2018-09-21 02:17:53 +02:00
<label>{_[Content publishing]} <small class='label-right'>{tag_privatekey}</small></label>
""".replace("{tag_privatekey}", tag_privatekey)))
# Choose content you want to sign
2019-03-15 21:06:59 +01:00
body.append(_("""
<div class='flex'>
<input type='text' class='text' value="content.json" id='input-contents'/>
<a href='#Sign-and-Publish' id='button-sign-publish' class='button'>{_[Sign and publish]}</a>
<a href='#Sign-or-Publish' id='menu-sign-publish'>\u22EE</a>
</div>
"""))
contents = ["content.json"]
2019-03-15 21:06:59 +01:00
contents += list(site.content_manager.contents.get("content.json", {}).get("includes", {}).keys())
body.append(_("<div class='contents'>{_[Choose]}: "))
for content in contents:
body.append(_("<a href='{content}' class='contents-content'>{content}</a> "))
body.append("</div>")
body.append("</li>")
2019-08-26 03:20:07 +02:00
@flag.admin
def actionSidebarGetHtmlTag(self, to):
site = self.site
body = []
body.append("<div>")
2017-07-29 13:44:54 +02:00
body.append("<a href='#Close' class='close'>&times;</a>")
2019-03-15 21:06:59 +01:00
body.append("<h1>%s</h1>" % html.escape(site.content_manager.contents.get("content.json", {}).get("title", ""), True))
body.append("<div class='globe loading'></div>")
body.append("<ul class='fields'>")
self.sidebarRenderPeerStats(body, site)
self.sidebarRenderTransferStats(body, site)
self.sidebarRenderFileStats(body, site)
self.sidebarRenderSizeLimit(body, site)
has_optional = self.sidebarRenderOptionalFileStats(body, site)
if has_optional:
self.sidebarRenderOptionalFileSettings(body, site)
self.sidebarRenderDbOptions(body, site)
self.sidebarRenderIdentity(body, site)
self.sidebarRenderControls(body, site)
if site.bad_files:
self.sidebarRenderBadFiles(body, site)
self.sidebarRenderOwnedCheckbox(body, site)
body.append("<div class='settings-owned'>")
self.sidebarRenderOwnSettings(body, site)
self.sidebarRenderContents(body, site)
body.append("</div>")
body.append("</ul>")
body.append("</div>")
2017-12-31 14:10:00 +01:00
body.append("<div class='menu template'>")
body.append("<a href='#'' class='menu-item template'>Template</a>")
body.append("</div>")
self.response(to, "".join(body))
def downloadGeoLiteDb(self, db_path):
import gzip
import shutil
from util import helper
if config.offline:
return False
self.log.info("Downloading GeoLite2 City database...")
2017-10-21 11:01:57 +02:00
self.cmd("progress", ["geolite-info", _["Downloading GeoLite2 City database (one time only, ~20MB)..."], 0])
db_urls = [
"https://raw.githubusercontent.com/aemr3/GeoLite2-Database/master/GeoLite2-City.mmdb.gz",
"https://raw.githubusercontent.com/texnikru/GeoLite2-Database/master/GeoLite2-City.mmdb.gz"
]
for db_url in db_urls:
2019-03-15 21:06:59 +01:00
downloadl_err = None
try:
# Download
response = helper.httpRequest(db_url)
2017-10-21 11:01:57 +02:00
data_size = response.getheader('content-length')
data_recv = 0
2019-03-15 21:06:59 +01:00
data = io.BytesIO()
while True:
buff = response.read(1024 * 512)
if not buff:
break
data.write(buff)
2017-10-21 11:01:57 +02:00
data_recv += 1024 * 512
if data_size:
progress = int(float(data_recv) / int(data_size) * 100)
self.cmd("progress", ["geolite-info", _["Downloading GeoLite2 City database (one time only, ~20MB)..."], progress])
self.log.info("GeoLite2 City database downloaded (%s bytes), unpacking..." % data.tell())
data.seek(0)
# Unpack
with gzip.GzipFile(fileobj=data) as gzip_file:
shutil.copyfileobj(gzip_file, open(db_path, "wb"))
2017-10-21 11:01:57 +02:00
self.cmd("progress", ["geolite-info", _["GeoLite2 City database downloaded!"], 100])
time.sleep(2) # Wait for notify animation
self.log.info("GeoLite2 City database is ready at: %s" % db_path)
return True
2017-07-16 22:51:01 +02:00
except Exception as err:
2019-03-15 21:06:59 +01:00
download_err = err
self.log.error("Error downloading %s: %s" % (db_url, err))
pass
2017-10-21 11:01:57 +02:00
self.cmd("progress", [
"geolite-info",
2019-03-15 21:06:59 +01:00
_["GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}"].format(download_err, db_urls[0]),
2017-10-21 11:01:57 +02:00
-100
])
def getLoc(self, geodb, ip):
global loc_cache
if ip in loc_cache:
return loc_cache[ip]
else:
try:
loc_data = geodb.get(ip)
except:
loc_data = None
if not loc_data or "location" not in loc_data:
loc_cache[ip] = None
return None
loc = {
"lat": loc_data["location"]["latitude"],
"lon": loc_data["location"]["longitude"],
}
if "city" in loc_data:
loc["city"] = loc_data["city"]["names"]["en"]
if "country" in loc_data:
loc["country"] = loc_data["country"]["names"]["en"]
loc_cache[ip] = loc
return loc
2019-11-19 01:44:26 +01:00
@util.Noparallel()
def getGeoipDb(self):
db_name = 'GeoLite2-City.mmdb'
sys_db_paths = []
if sys.platform == "linux":
sys_db_paths += ['/usr/share/GeoIP/' + db_name]
data_dir_db_path = os.path.join(config.data_dir, db_name)
2019-04-15 22:48:43 +02:00
db_paths = sys_db_paths + [data_dir_db_path]
for path in db_paths:
if os.path.isfile(path) and os.path.getsize(path) > 0:
return path
self.log.info("GeoIP database not found at [%s]. Downloading to: %s",
" ".join(db_paths), data_dir_db_path)
if self.downloadGeoLiteDb(data_dir_db_path):
return data_dir_db_path
return None
def getPeerLocations(self, peers):
2019-03-16 04:26:27 +01:00
import maxminddb
db_path = self.getGeoipDb()
if not db_path:
self.log.debug("Not showing peer locations: no GeoIP database")
return False
geodb = maxminddb.open_database(db_path)
2019-03-15 21:06:59 +01:00
peers = list(peers.values())
# Place bars
peer_locations = []
placed = {} # Already placed bars here
for peer in peers:
# Height of bar
if peer.connection and peer.connection.last_ping_delay:
ping = round(peer.connection.last_ping_delay * 1000)
else:
ping = None
loc = self.getLoc(geodb, peer.ip)
if not loc:
continue
# Create position array
lat, lon = loc["lat"], loc["lon"]
latlon = "%s,%s" % (lat, lon)
if latlon in placed and helper.getIpType(peer.ip) == "ipv4": # Dont place more than 1 bar to same place, fake repos using ip address last two part
lat += float(128 - int(peer.ip.split(".")[-2])) / 50
lon += float(128 - int(peer.ip.split(".")[-1])) / 50
latlon = "%s,%s" % (lat, lon)
placed[latlon] = True
peer_location = {}
peer_location.update(loc)
peer_location["lat"] = lat
peer_location["lon"] = lon
peer_location["ping"] = ping
peer_locations.append(peer_location)
# Append myself
for ip in self.site.connection_server.ip_external_list:
2019-01-24 15:20:08 +01:00
my_loc = self.getLoc(geodb, ip)
if my_loc:
my_loc["ping"] = 0
peer_locations.append(my_loc)
return peer_locations
2019-08-26 03:20:07 +02:00
@flag.admin
@flag.async_run
def actionSidebarGetPeers(self, to):
try:
peer_locations = self.getPeerLocations(self.site.peers)
globe_data = []
ping_times = [
peer_location["ping"]
for peer_location in peer_locations
if peer_location["ping"]
]
if ping_times:
ping_avg = sum(ping_times) / float(len(ping_times))
else:
ping_avg = 0
for peer_location in peer_locations:
if peer_location["ping"] == 0: # Me
height = -0.135
elif peer_location["ping"]:
height = min(0.20, math.log(1 + peer_location["ping"] / ping_avg, 300))
else:
height = -0.03
globe_data += [peer_location["lat"], peer_location["lon"], height]
self.response(to, globe_data)
2019-03-15 21:06:59 +01:00
except Exception as err:
self.log.debug("sidebarGetPeers error: %s" % Debug.formatException(err))
2019-03-15 21:06:59 +01:00
self.response(to, {"error": str(err)})
2019-08-26 03:20:07 +02:00
@flag.admin
@flag.no_multiuser
def actionSiteSetOwned(self, to, owned):
if self.site.address == config.updatesite:
return {"error": "You can't change the ownership of the updater site"}
self.site.settings["own"] = bool(owned)
self.site.updateWebsocket(owned=owned)
return "ok"
@flag.admin
@flag.no_multiuser
def actionSiteRecoverPrivatekey(self, to):
from Crypt import CryptBitcoin
site_data = self.user.sites[self.site.address]
if site_data.get("privatekey"):
return {"error": "This site already has saved privated key"}
address_index = self.site.content_manager.get("content.json", {}).get("address_index")
if not address_index:
return {"error": "No address_index in content.json"}
privatekey = CryptBitcoin.hdPrivatekey(self.user.master_seed, address_index)
privatekey_address = CryptBitcoin.privatekeyToAddress(privatekey)
if privatekey_address == self.site.address:
site_data["privatekey"] = privatekey
self.user.save()
self.site.updateWebsocket(recover_privatekey=True)
return "ok"
else:
return {"error": "Unable to deliver private key for this site from current user's master_seed"}
2019-08-26 03:20:07 +02:00
@flag.admin
@flag.no_multiuser
2018-09-21 02:17:53 +02:00
def actionUserSetSitePrivatekey(self, to, privatekey):
site_data = self.user.sites[self.site.address]
site_data["privatekey"] = privatekey
self.site.updateWebsocket(set_privatekey=bool(privatekey))
2020-09-04 18:21:52 +02:00
self.user.save()
2018-09-21 02:17:53 +02:00
return "ok"
2019-08-26 03:20:07 +02:00
@flag.admin
@flag.no_multiuser
def actionSiteSetAutodownloadoptional(self, to, owned):
self.site.settings["autodownloadoptional"] = bool(owned)
self.site.worker_manager.removeSolvedFileTasks()
2019-08-26 03:20:07 +02:00
@flag.no_multiuser
@flag.admin
def actionDbReload(self, to):
self.site.storage.closeDb()
self.site.storage.getDb()
return self.response(to, "ok")
2016-08-10 12:52:18 +02:00
2019-08-26 03:20:07 +02:00
@flag.no_multiuser
@flag.admin
2016-08-10 12:52:18 +02:00
def actionDbRebuild(self, to):
try:
self.site.storage.rebuildDb()
except Exception as err:
return self.response(to, {"error": str(err)})
2016-08-10 12:52:18 +02:00
return self.response(to, "ok")