import re import os import html import sys import math import time import json import io import urllib import urllib.parse import gevent import util from Config import config from Plugin import PluginManager from Debug import Debug from Translate import Translate from util import helper from util.Flag import flag from .ZipStream import ZipStream plugin_dir = os.path.dirname(__file__) media_dir = plugin_dir + "/media" loc_cache = {} if "_" not in locals(): _ = 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) if ext == "js": yield _.translateData(open(plugin_media_file).read()).encode("utf8") 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 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", "") 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}) return self.streamZip(site.storage.getPath(".")) def streamZip(self, dir_path): zs = ZipStream(dir_path) while 1: data = zs.read() if not data: break yield data @PluginManager.registerTo("UiWebsocket") class UiWebsocketPlugin(object): def sidebarRenderPeerStats(self, body, site): 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 if local: local_html = _("
  • {_[Local]}:{local}
  • ") 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) ) body.append(_("""
  • """.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 body.append(_("""
  • """)) def sidebarRenderFileStats(self, body, site): body.append(_("""
  • ") 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 body.append(_("""
  • MB {_[Set]}
  • """)) def sidebarRenderOptionalFileStats(self, body, site): 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 body.append(_("""
  • """)) return True def sidebarRenderOptionalFileSettings(self, body, site): if self.site.settings.get("autodownloadoptional"): checked = "checked='checked'" else: checked = "" body.append(_("""
  • """)) 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(_("""
    MB {_[Set]} {_[Download previous files]}
    """)) body.append("
  • ") def sidebarRenderBadFiles(self, body, site): body.append(_("""
  • """) 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: inner_path = _["No database found"] size = 0.0 feeds = 0 body.append(_("""
  • {_[Reload]} {_[Rebuild]}
  • """, 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] 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 body.append(_("""
  • {auth_address} {_[Change]}
  • """)) 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 = "" body.append(_("""
  • {_[Update]} {_[Pause]} {_[Resume]} {_[Delete]}
  • """)) donate_key = site.content_manager.contents.get("content.json", {}).get("donate", True) site_address = self.site.address body.append(_("""

  • {site_address} """)) if donate_key == False or donate_key == "": pass elif (type(donate_key) == str or type(donate_key) == str) and len(donate_key) > 0: body.append(_("""

  • {donate_key} """)) else: body.append(_(""" {_[Donate]} """)) body.append(_("""
  • """)) def sidebarRenderOwnedCheckbox(self, body, site): if self.site.settings["own"]: checked = "checked='checked'" else: checked = "" body.append(_("""

    {_[This is my site]}

    """)) 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", "") body.append(_("""
  • {_[Save site settings]}
  • """)) def sidebarRenderContents(self, body, site): has_privatekey = bool(self.user.getSiteData(site.address, create=False).get("privatekey")) if has_privatekey: tag_privatekey = _("{_[Private key saved.]} {_[Forget]}") else: tag_privatekey = _("{_[Add saved private key]}") body.append(_("""
  • """.replace("{tag_privatekey}", tag_privatekey))) # Choose content you want to sign body.append(_("""
    {_[Sign and publish]} \u22EE
    """)) contents = ["content.json"] contents += list(site.content_manager.contents.get("content.json", {}).get("includes", {}).keys()) body.append(_("
    {_[Choose]}: ")) for content in contents: body.append(_("{content} ")) body.append("
    ") body.append("
  • ") @flag.admin def actionSidebarGetHtmlTag(self, to): site = self.site body = [] body.append("
    ") body.append("×") body.append("

    %s

    " % html.escape(site.content_manager.contents.get("content.json", {}).get("title", ""), True)) body.append("
    ") body.append("") body.append("
    ") body.append("") 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...") 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: downloadl_err = None try: # Download response = helper.httpRequest(db_url) data_size = response.getheader('content-length') data_recv = 0 data = io.BytesIO() while True: buff = response.read(1024 * 512) if not buff: break data.write(buff) 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")) 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 except Exception as err: download_err = err self.log.error("Error downloading %s: %s" % (db_url, err)) pass self.cmd("progress", [ "geolite-info", _["GeoLite2 City database download error: {}!
    Please download manually and unpack to data dir:
    {}"].format(download_err, db_urls[0]), -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 @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) 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): 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) 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: my_loc = self.getLoc(geodb, ip) if my_loc: my_loc["ping"] = 0 peer_locations.append(my_loc) return peer_locations @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) except Exception as err: self.log.debug("sidebarGetPeers error: %s" % Debug.formatException(err)) self.response(to, {"error": str(err)}) @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"} @flag.admin @flag.no_multiuser def actionUserSetSitePrivatekey(self, to, privatekey): site_data = self.user.sites[self.site.address] site_data["privatekey"] = privatekey self.site.updateWebsocket(set_privatekey=bool(privatekey)) self.user.save() return "ok" @flag.admin @flag.no_multiuser def actionSiteSetAutodownloadoptional(self, to, owned): self.site.settings["autodownloadoptional"] = bool(owned) self.site.worker_manager.removeSolvedFileTasks() @flag.no_multiuser @flag.admin def actionDbReload(self, to): self.site.storage.closeDb() self.site.storage.getDb() return self.response(to, "ok") @flag.no_multiuser @flag.admin def actionDbRebuild(self, to): try: self.site.storage.rebuildDb() except Exception as err: return self.response(to, {"error": str(err)}) return self.response(to, "ok")