Merge branch 'HelloZeroNet-py3' into massive-rework

# Conflicts:
#	src/File/FileServer.py
This commit is contained in:
Vadim Ushakov 2021-11-06 22:39:47 +07:00
commit 545fe9442c
38 changed files with 588 additions and 120 deletions

View file

@ -1,6 +1,6 @@
name: tests name: tests
on: [push] on: [push, pull_request]
jobs: jobs:
test: test:
@ -9,10 +9,10 @@ jobs:
strategy: strategy:
max-parallel: 16 max-parallel: 16
matrix: matrix:
python-version: [3.5, 3.6, 3.7, 3.8] python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1 uses: actions/setup-python@v1

View file

@ -1,6 +1,6 @@
# ZeroNet [![Build Status](https://travis-ci.org/HelloZeroNet/ZeroNet.svg?branch=py3)](https://travis-ci.org/HelloZeroNet/ZeroNet) [![Documentation](https://img.shields.io/badge/docs-faq-brightgreen.svg)](https://zeronet.io/docs/faq/) [![Help](https://img.shields.io/badge/keep_this_project_alive-donate-yellow.svg)](https://zeronet.io/docs/help_zeronet/donate/) ![tests](https://github.com/HelloZeroNet/ZeroNet/workflows/tests/badge.svg) [![Docker Pulls](https://img.shields.io/docker/pulls/nofish/zeronet)](https://hub.docker.com/r/nofish/zeronet) # ZeroNet [![Build Status](https://travis-ci.org/HelloZeroNet/ZeroNet.svg?branch=py3)](https://travis-ci.org/HelloZeroNet/ZeroNet) [![Documentation](https://img.shields.io/badge/docs-faq-brightgreen.svg)](https://zeronet.io/docs/faq/) [![Help](https://img.shields.io/badge/keep_this_project_alive-donate-yellow.svg)](https://zeronet.io/docs/help_zeronet/donate/) ![tests](https://github.com/HelloZeroNet/ZeroNet/workflows/tests/badge.svg) [![Docker Pulls](https://img.shields.io/docker/pulls/nofish/zeronet)](https://hub.docker.com/r/nofish/zeronet)
Decentralized websites using Bitcoin crypto and the BitTorrent network - https://zeronet.io Decentralized websites using Bitcoin crypto and the BitTorrent network - https://zeronet.io / [onion](http://zeronet34m3r5ngdu54uj57dcafpgdjhxsgq5kla5con4qvcmfzpvhad.onion)
## Why? ## Why?
@ -83,12 +83,12 @@ Decentralized websites using Bitcoin crypto and the BitTorrent network - https:/
__Tip:__ Start with `./ZeroNet.sh --ui_ip '*' --ui_restrict your.ip.address` to allow remote connections on the web interface. __Tip:__ Start with `./ZeroNet.sh --ui_ip '*' --ui_restrict your.ip.address` to allow remote connections on the web interface.
### Android (arm, arm64, x86) ### Android (arm, arm64, x86)
- minimum Android version supported 16 (JellyBean). - minimum Android version supported 16 (JellyBean)
- Google Play Store Link https://play.google.com/store/apps/details?id=in.canews.zeronet - [<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Download from Google Play" alt="Download from Google Play"
height="80">](https://play.google.com/store/apps/details?id=in.canews.zeronet) height="80">](https://play.google.com/store/apps/details?id=in.canews.zeronetmobile)
- APK download: https://github.com/canewsin/zeronet_mobile/releases
- XDA Labs: https://labs.xda-developers.com/store/app/in.canews.zeronet
#### Docker #### Docker
There is an official image, built from source at: https://hub.docker.com/r/nofish/zeronet/ There is an official image, built from source at: https://hub.docker.com/r/nofish/zeronet/

View file

@ -146,6 +146,7 @@ class ActionsPlugin:
yield "\n" yield "\n"
res = {} res = {}
res_time_taken = {}
multiplers = [] multiplers = []
for test in tests: for test in tests:
s = time.time() s = time.time()
@ -182,11 +183,35 @@ class ActionsPlugin:
yield self.formatResult(time_taken, time_standard) yield self.formatResult(time_taken, time_standard)
yield "\n" yield "\n"
res[key] = "ok" res[key] = "ok"
res_time_taken[key] = time_taken
multiplers.append(time_standard / max(time_taken, 0.001)) multiplers.append(time_standard / max(time_taken, 0.001))
except Exception as err: except Exception as err:
res[key] = err res[key] = err
yield "Failed!\n! Error: %s\n\n" % Debug.formatException(err) yield "Failed!\n! Error: %s\n\n" % Debug.formatException(err)
yield "\n== Result ==\n"
# Check verification speed
if "testVerify {'lib_verify': 'sslcrypto'}" in res_time_taken:
speed_order = ["sslcrypto_fallback", "sslcrypto", "libsecp256k1"]
time_taken = {}
for lib_verify in speed_order:
time_taken[lib_verify] = res_time_taken["testVerify {'lib_verify': '%s'}" % lib_verify]
time_taken["sslcrypto_fallback"] *= 10 # fallback benchmark only run 20 times instead of 200
speedup_sslcrypto = time_taken["sslcrypto_fallback"] / time_taken["sslcrypto"]
speedup_libsecp256k1 = time_taken["sslcrypto_fallback"] / time_taken["libsecp256k1"]
yield "\n* Verification speedup:\n"
yield " - OpenSSL: %.1fx (reference: 7.0x)\n" % speedup_sslcrypto
yield " - libsecp256k1: %.1fx (reference: 23.8x)\n" % speedup_libsecp256k1
if speedup_sslcrypto < 2:
res["Verification speed"] = "error: OpenSSL speedup low: %.1fx" % speedup_sslcrypto
if speedup_libsecp256k1 < speedup_sslcrypto:
res["Verification speed"] = "error: libsecp256k1 speedup low: %.1fx" % speedup_libsecp256k1
if not res: if not res:
yield "! No tests found" yield "! No tests found"
if config.action == "test": if config.action == "test":
@ -194,17 +219,22 @@ class ActionsPlugin:
else: else:
num_failed = len([res_key for res_key, res_val in res.items() if res_val != "ok"]) num_failed = len([res_key for res_key, res_val in res.items() if res_val != "ok"])
num_success = len([res_key for res_key, res_val in res.items() if res_val == "ok"]) num_success = len([res_key for res_key, res_val in res.items() if res_val == "ok"])
yield "* Result:\n" yield "\n* Tests:\n"
yield " - Total: %s tests\n" % len(res) yield " - Total: %s tests\n" % len(res)
yield " - Success: %s tests\n" % num_success yield " - Success: %s tests\n" % num_success
yield " - Failed: %s tests\n" % num_failed yield " - Failed: %s tests\n" % num_failed
if any(multiplers): if any(multiplers):
multipler_avg = sum(multiplers) / len(multiplers) multipler_avg = sum(multiplers) / len(multiplers)
multipler_title = self.getMultiplerTitle(multipler_avg) multipler_title = self.getMultiplerTitle(multipler_avg)
yield " - Average speed factor: %.2fx (%s)" % (multipler_avg, multipler_title) yield " - Average speed factor: %.2fx (%s)\n" % (multipler_avg, multipler_title)
if num_failed == 0 and config.action == "test":
sys.exit(1)
# Display errors
for res_key, res_val in res.items():
if res_val != "ok":
yield " ! %s %s\n" % (res_key, res_val)
if num_failed != 0 and config.action == "test":
sys.exit(1)
def testHttps(self, num_run=1): def testHttps(self, num_run=1):
""" """
@ -323,13 +353,14 @@ class ActionsPlugin:
valid = "G1GXaDauZ8vX/N9Jn+MRiGm9h+I94zUhDnNYFaqMGuOiBHB+kp4cRPZOL7l1yqK5BHa6J+W97bMjvTXtxzljp6w=" valid = "G1GXaDauZ8vX/N9Jn+MRiGm9h+I94zUhDnNYFaqMGuOiBHB+kp4cRPZOL7l1yqK5BHa6J+W97bMjvTXtxzljp6w="
assert sign == valid, "%s != %s" % (sign, valid) assert sign == valid, "%s != %s" % (sign, valid)
def testVerify(self, num_run=1, lib_verify="btctools"): def testVerify(self, num_run=1, lib_verify="sslcrypto"):
""" """
Test verification of generated signatures Test verification of generated signatures
""" """
from Crypt import CryptBitcoin from Crypt import CryptBitcoin
CryptBitcoin.loadLib(lib_verify, silent=True) CryptBitcoin.loadLib(lib_verify, silent=True)
data = "Hello" * 1024 data = "Hello" * 1024
privatekey = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk" privatekey = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk"
address = CryptBitcoin.privatekeyToAddress(privatekey) address = CryptBitcoin.privatekeyToAddress(privatekey)
@ -340,6 +371,9 @@ class ActionsPlugin:
yield "." yield "."
assert ok, "does not verify from %s" % address assert ok, "does not verify from %s" % address
if lib_verify == "sslcrypto":
yield("(%s)" % CryptBitcoin.sslcrypto.ecc.get_backend())
def testPortCheckers(self): def testPortCheckers(self):
""" """
Test all active open port checker Test all active open port checker
@ -361,7 +395,6 @@ class ActionsPlugin:
""" """
from Peer import PeerPortchecker from Peer import PeerPortchecker
peer_portchecker = PeerPortchecker.PeerPortchecker(None) peer_portchecker = PeerPortchecker.PeerPortchecker(None)
s = time.time()
announce_func = getattr(peer_portchecker, func_name) announce_func = getattr(peer_portchecker, func_name)
res = announce_func(3894) res = announce_func(3894)
yield res yield res

View file

@ -39,7 +39,7 @@ function setState(elem, text) {
} }
} }
formatted = formatted.replace(/(\! Error:.*)/, "<div class='test error'>$1</div>"); formatted = formatted.replace(/(\! Error:.*)/, "<div class='test error'>$1</div>");
formatted = formatted.replace(/(\* Result:[^]*)/, "<div class='test summary'>$1</div>"); formatted = formatted.replace(/(\== Result ==[^]*)/, "<div class='test summary'>$1</div>");
var is_bottom = document.body.scrollTop + document.body.clientHeight >= document.body.scrollHeight - 5; var is_bottom = document.body.scrollTop + document.body.clientHeight >= document.body.scrollHeight - 5;
elem.innerHTML = formatted.trim(); elem.innerHTML = formatted.trim();
if (is_bottom) if (is_bottom)

View file

@ -0,0 +1,6 @@
{
"Hide all content from <b>%s</b>?": "<b>%s</b> のコンテンツをすべて隠しますか?",
"Mute": "ミュート",
"Unmute <b>%s</b>?": "<b>%s</b> のミュートを解除しますか?",
"Unmute": "ミュート解除"
}

View file

@ -0,0 +1,5 @@
{
"Add <b>%s</b> new site?": "サイト: <b>%s</b> を追加しますか?",
"Added <b>%s</b> new site": "サイト: <b>%s</b> を追加しました",
"Site deleted: <b>%s</b>": "サイト: <b>%s</b> を削除しました"
}

View file

@ -139,7 +139,7 @@ class UiWebsocketPlugin(object):
wheres = {} wheres = {}
wheres_raw = [] wheres_raw = []
if "bigfile" in filter: if "bigfile" in filter:
wheres["size >"] = 1024 * 1024 * 10 wheres["size >"] = 1024 * 1024 * 1
if "downloaded" in filter: if "downloaded" in filter:
wheres_raw.append("(is_downloaded = 1 OR is_pinned = 1)") wheres_raw.append("(is_downloaded = 1 OR is_pinned = 1)")
if "pinned" in filter: if "pinned" in filter:
@ -166,11 +166,14 @@ class UiWebsocketPlugin(object):
row["address"] = address row["address"] = address
if row["size"] > 1024 * 1024: if row["size"] > 1024 * 1024:
has_info = self.addBigfileInfo(row) has_bigfile_info = self.addBigfileInfo(row)
else: else:
has_info = False has_bigfile_info = False
if not has_info: if not has_bigfile_info and "bigfile" in filter:
continue
if not has_bigfile_info:
if row["is_downloaded"]: if row["is_downloaded"]:
row["bytes_downloaded"] = row["size"] row["bytes_downloaded"] = row["size"]
row["downloaded_percent"] = 100 row["downloaded_percent"] = 100

View file

@ -0,0 +1,7 @@
{
"Pinned %s files": "%s 件のファイルを固定",
"Removed pin from %s files": "%s 件のファイルの固定を解除",
"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "あなたはサイト: <b>%s</b> の配布の援助を開始しました。<br><small>ディレクトリ: %s</small>",
"Help distribute all new optional files on site <b>%s</b>": "サイト: <b>%s</b> のすべての新しいオプションファイルの配布を援助しますか?",
"Yes, I want to help!": "はい、やります!"
}

View file

@ -1,8 +1,11 @@
{ {
"Copy to clipboard": "クリップボードにコピー",
"Peers": "ピア", "Peers": "ピア",
"Connected": "接続済み", "Connected": "接続済み",
"Connectable": "利用可能", "Connectable": "利用可能",
"Connectable peers": "ピアに接続可能", "Connectable peers": "ピアに接続可能",
"Onion": "Onion",
"Local": "ローカル",
"Data transfer": "データ転送", "Data transfer": "データ転送",
"Received": "受信", "Received": "受信",
@ -11,6 +14,8 @@
"Sent bytes": "送信バイト数", "Sent bytes": "送信バイト数",
"Files": "ファイル", "Files": "ファイル",
"Browse files": "ファイルを見る",
"Save as .zip": "ZIP形式で保存",
"Total": "合計", "Total": "合計",
"Image": "画像", "Image": "画像",
"Other": "その他", "Other": "その他",
@ -23,18 +28,23 @@
"Optional files": "オプション ファイル", "Optional files": "オプション ファイル",
"Downloaded": "ダウンロード済み", "Downloaded": "ダウンロード済み",
"Help distribute added optional files": "オプションファイルの配布を支援する",
"Auto download big file size limit": "大きなファイルの自動ダウンロードのサイズ制限",
"Download previous files": "以前のファイルのダウンロード",
"Optional files download started": "オプションファイルのダウンロードを開始",
"Optional files downloaded": "オプションファイルのダウンロードが完了しました",
"Download and help distribute all files": "ダウンロードしてすべてのファイルの配布を支援する", "Download and help distribute all files": "ダウンロードしてすべてのファイルの配布を支援する",
"Total size": "合計サイズ", "Total size": "合計サイズ",
"Downloaded files": "ダウンロードされたファイル", "Downloaded files": "ダウンロードされたファイル",
"Database": "データベース", "Database": "データベース",
"search feeds": "フィードを検索する", "search feeds": "フィードを検索する",
"{feeds} query": "{フィード} お問い合わせ", "{feeds} query": "{feeds} お問い合わせ",
"Reload": "再読込", "Reload": "再読込",
"Rebuild": "再ビルド", "Rebuild": "再ビルド",
"No database found": "データベースが見つかりません", "No database found": "データベースが見つかりません",
"Identity address": "Identity address", "Identity address": "あなたの識別アドレス",
"Change": "編集", "Change": "編集",
"Site control": "サイト管理", "Site control": "サイト管理",
@ -52,15 +62,23 @@
"{} tries": "{} 試行", "{} tries": "{} 試行",
"+ {num_bad_files} more": "+ {num_bad_files} more", "+ {num_bad_files} more": "+ {num_bad_files} more",
"This is my site": "This is my site", "This is my site": "これは私のサイトです",
"Site title": "サイトタイトル", "Site title": "サイトタイトル",
"Site description": "サイトの説明", "Site description": "サイトの説明",
"Save site settings": "サイトの設定を保存する", "Save site settings": "サイトの設定を保存する",
"Open site directory": "サイトのディレクトリを開く",
"Content publishing": "コンテンツを公開する", "Content publishing": "コンテンツを公開する",
"Add saved private key": "秘密鍵の追加と保存",
"Save": "保存",
"Private key saved.": "秘密鍵が保存されています",
"Private key saved for site signing": "サイトに署名するための秘密鍵を保存",
"Forgot": "わすれる",
"Saved private key removed": "保存された秘密鍵を削除しました",
"Choose": "選択", "Choose": "選択",
"Sign": "Sign", "Sign": "署名",
"Publish": "公開する", "Publish": "公開する",
"Sign and publish": "署名して公開",
"This function is disabled on this proxy": "この機能はこのプロキシで無効になっています", "This function is disabled on this proxy": "この機能はこのプロキシで無効になっています",
"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 Cityデータベースのダウンロードエラー: {}!<br>手動でダウンロードして、フォルダに解凍してください。:<br>{}", "GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 Cityデータベースのダウンロードエラー: {}!<br>手動でダウンロードして、フォルダに解凍してください。:<br>{}",
@ -74,9 +92,13 @@
"Database rebuilt!": "データベースが再構築されました!", "Database rebuilt!": "データベースが再構築されました!",
"Site updated!": "サイトが更新されました!", "Site updated!": "サイトが更新されました!",
"Delete this site": "このサイトを削除する", "Delete this site": "このサイトを削除する",
"Blacklist": "NG",
"Blacklist this site": "NGリストに入れる",
"Reason": "理由",
"Delete and Blacklist": "削除してNG",
"File write error: ": "ファイル書き込みエラー:", "File write error: ": "ファイル書き込みエラー:",
"Site settings saved!": "サイト設定が保存されました!", "Site settings saved!": "サイト設定が保存されました!",
"Enter your private key:": "秘密鍵を入力してください:", "Enter your private key:": "秘密鍵を入力してください:",
" Signed!": " Signed!", " Signed!": " 署名しました!",
"WebGL not supported": "WebGLはサポートされていません" "WebGL not supported": "WebGLはサポートされていません"
} }

View file

@ -1,5 +1,5 @@
.menu { .menu {
font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; z-index: 999;
} }
.drag-bg { width: 100%; height: 100%; position: fixed; } .drag-bg { width: 100%; height: 100%; position: fixed; }

View file

@ -111,7 +111,7 @@
.menu { .menu {
font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; z-index: 999;
} }
.drag-bg { width: 100%; height: 100%; position: fixed; } .drag-bg { width: 100%; height: 100%; position: fixed; }

View file

@ -2,7 +2,8 @@ import time
import html import html
import os import os
import json import json
from collections import OrderedDict import sys
import itertools
from Plugin import PluginManager from Plugin import PluginManager
from Config import config from Config import config
@ -35,40 +36,9 @@ class UiRequestPlugin(object):
else: else:
return 0 return 0
# /Stats entry point def renderHead(self):
@helper.encodeResponse
def actionStats(self):
import gc
import sys
from Ui import UiRequest
from Crypt import CryptConnection
import main import main
from Crypt import CryptConnection
hpy = None
if self.get.get("size") == "1": # Calc obj size
try:
import guppy
hpy = guppy.hpy()
except:
pass
self.sendHeader()
if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
yield "This function is disabled on this proxy"
return
s = time.time()
# Style
yield """
<style>
* { font-family: monospace }
table td, table th { text-align: right; padding: 0px 10px }
.connections td { white-space: nowrap }
.serving-False { opacity: 0.3 }
</style>
"""
# Memory # Memory
yield "rev%s | " % config.rev yield "rev%s | " % config.rev
@ -99,9 +69,13 @@ class UiRequestPlugin(object):
pass pass
yield "<br>" yield "<br>"
def renderConnectionsTable(self):
import main
# Connections # Connections
yield "<b>Connections</b> (%s, total made: %s, in: %s, out: %s):<br>" % ( yield "<b>Connections</b> (%s, total made: %s, in: %s, out: %s):<br>" % (
len(main.file_server.connections), main.file_server.last_connection_id, main.file_server.num_incoming, main.file_server.num_outgoing len(main.file_server.connections), main.file_server.last_connection_id,
main.file_server.num_incoming, main.file_server.num_outgoing
) )
yield "<table class='connections'><tr> <th>id</th> <th>type</th> <th>ip</th> <th>open</th> <th>crypt</th> <th>ping</th>" yield "<table class='connections'><tr> <th>id</th> <th>type</th> <th>ip</th> <th>open</th> <th>crypt</th> <th>ping</th>"
yield "<th>buff</th> <th>bad</th> <th>idle</th> <th>open</th> <th>delay</th> <th>cpu</th> <th>out</th> <th>in</th> <th>last sent</th>" yield "<th>buff</th> <th>bad</th> <th>idle</th> <th>open</th> <th>delay</th> <th>cpu</th> <th>out</th> <th>in</th> <th>last sent</th>"
@ -140,10 +114,11 @@ class UiRequestPlugin(object):
]) ])
yield "</table>" yield "</table>"
def renderTrackers(self):
# Trackers # Trackers
yield "<br><br><b>Trackers:</b><br>" yield "<br><br><b>Trackers:</b><br>"
yield "<table class='trackers'><tr> <th>address</th> <th>request</th> <th>successive errors</th> <th>last_request</th></tr>" yield "<table class='trackers'><tr> <th>address</th> <th>request</th> <th>successive errors</th> <th>last_request</th></tr>"
from Site import SiteAnnouncer # importing at the top of the file breaks plugins from Site import SiteAnnouncer # importing at the top of the file breaks plugins
for tracker_address, tracker_stat in sorted(SiteAnnouncer.global_stats.items()): for tracker_address, tracker_stat in sorted(SiteAnnouncer.global_stats.items()):
yield self.formatTableRow([ yield self.formatTableRow([
("%s", tracker_address), ("%s", tracker_address),
@ -168,12 +143,13 @@ class UiRequestPlugin(object):
]) ])
yield "</table>" yield "</table>"
# Tor hidden services def renderTor(self):
import main
yield "<br><br><b>Tor hidden services (status: %s):</b><br>" % main.file_server.tor_manager.status yield "<br><br><b>Tor hidden services (status: %s):</b><br>" % main.file_server.tor_manager.status
for site_address, onion in list(main.file_server.tor_manager.site_onions.items()): for site_address, onion in list(main.file_server.tor_manager.site_onions.items()):
yield "- %-34s: %s<br>" % (site_address, onion) yield "- %-34s: %s<br>" % (site_address, onion)
# Db def renderDbStats(self):
yield "<br><br><b>Db</b>:<br>" yield "<br><br><b>Db</b>:<br>"
for db in Db.opened_dbs: for db in Db.opened_dbs:
tables = [row["name"] for row in db.execute("SELECT name FROM sqlite_master WHERE type = 'table'").fetchall()] tables = [row["name"] for row in db.execute("SELECT name FROM sqlite_master WHERE type = 'table'").fetchall()]
@ -185,8 +161,7 @@ class UiRequestPlugin(object):
time.time() - db.last_query_time, db.db_path, db_size, json.dumps(table_rows, sort_keys=True) time.time() - db.last_query_time, db.db_path, db_size, json.dumps(table_rows, sort_keys=True)
) )
def renderSites(self):
# Sites
yield "<br><br><b>Sites</b>:" yield "<br><br><b>Sites</b>:"
yield "<table>" yield "<table>"
yield "<tr><th>address</th> <th>connected</th> <th title='connected/good/total'>peers</th> <th>content.json</th> <th>out</th> <th>in</th> </tr>" yield "<tr><th>address</th> <th>connected</th> <th title='connected/good/total'>peers</th> <th>content.json</th> <th>out</th> <th>in</th> </tr>"
@ -226,7 +201,7 @@ class UiRequestPlugin(object):
yield "<br></td></tr>" yield "<br></td></tr>"
yield "</table>" yield "</table>"
# Big files def renderBigfiles(self):
yield "<br><br><b>Big files</b>:<br>" yield "<br><br><b>Big files</b>:<br>"
for site in list(self.server.sites.values()): for site in list(self.server.sites.values()):
if not site.settings.get("has_bigfile"): if not site.settings.get("has_bigfile"):
@ -250,7 +225,8 @@ class UiRequestPlugin(object):
yield "</table>" yield "</table>"
yield "</div>" yield "</div>"
# Cmd stats def renderRequests(self):
import main
yield "<div style='float: left'>" yield "<div style='float: left'>"
yield "<br><br><b>Sent commands</b>:<br>" yield "<br><br><b>Sent commands</b>:<br>"
yield "<table>" yield "<table>"
@ -268,9 +244,18 @@ class UiRequestPlugin(object):
yield "</div>" yield "</div>"
yield "<div style='clear: both'></div>" yield "<div style='clear: both'></div>"
# No more if not in debug mode def renderMemory(self):
if not config.debug: import gc
return from Ui import UiRequest
hpy = None
if self.get.get("size") == "1": # Calc obj size
try:
import guppy
hpy = guppy.hpy()
except Exception:
pass
self.sendHeader()
# Object types # Object types
@ -371,6 +356,48 @@ class UiRequestPlugin(object):
for module_name, module in objs: for module_name, module in objs:
yield " - %.3fkb: %s %s<br>" % (self.getObjSize(module, hpy), module_name, html.escape(repr(module))) yield " - %.3fkb: %s %s<br>" % (self.getObjSize(module, hpy), module_name, html.escape(repr(module)))
# /Stats entry point
@helper.encodeResponse
def actionStats(self):
import gc
self.sendHeader()
if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
yield "This function is disabled on this proxy"
return
s = time.time()
# Style
yield """
<style>
* { font-family: monospace }
table td, table th { text-align: right; padding: 0px 10px }
.connections td { white-space: nowrap }
.serving-False { opacity: 0.3 }
</style>
"""
renderers = [
self.renderHead(),
self.renderConnectionsTable(),
self.renderTrackers(),
self.renderTor(),
self.renderDbStats(),
self.renderSites(),
self.renderBigfiles(),
self.renderRequests()
]
for part in itertools.chain(*renderers):
yield part
if config.debug:
for part in self.renderMemory():
yield part
gc.collect() # Implicit grabage collection gc.collect() # Implicit grabage collection
yield "Done in %.1f" % (time.time() - s) yield "Done in %.1f" % (time.time() - s)
@ -457,7 +484,7 @@ class UiRequestPlugin(object):
yield "%.1fkb <span title=\"%s\">%s</span>... " % ( yield "%.1fkb <span title=\"%s\">%s</span>... " % (
float(sys.getsizeof(obj)) / 1024, html.escape(str(obj)), html.escape(str(obj)[0:100].ljust(100)) float(sys.getsizeof(obj)) / 1024, html.escape(str(obj)), html.escape(str(obj)[0:100].ljust(100))
) )
except: except Exception:
continue continue
for ref in refs: for ref in refs:
yield " [" yield " ["
@ -485,3 +512,116 @@ class UiRequestPlugin(object):
import gc import gc
self.sendHeader() self.sendHeader()
yield str(gc.collect()) yield str(gc.collect())
# /About entry point
@helper.encodeResponse
def actionEnv(self):
import main
self.sendHeader()
yield """
<style>
* { font-family: monospace; white-space: pre; }
h2 { font-size: 100%; margin-bottom: 0px; }
small { opacity: 0.5; }
table { border-collapse: collapse; }
td { padding-right: 10px; }
</style>
"""
if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
yield "This function is disabled on this proxy"
return
yield from main.actions.testEnv(format="html")
@PluginManager.registerTo("Actions")
class ActionsPlugin:
def formatTable(self, *rows, format="text"):
if format == "html":
return self.formatTableHtml(*rows)
else:
return self.formatTableText(*rows)
def formatHead(self, title, format="text"):
if format == "html":
return "<h2>%s</h2>" % title
else:
return "\n* %s\n" % title
def formatTableHtml(self, *rows):
yield "<table>"
for row in rows:
yield "<tr>"
for col in row:
yield "<td>%s</td>" % html.escape(str(col))
yield "</tr>"
yield "</table>"
def formatTableText(self, *rows):
for row in rows:
yield " "
for col in row:
yield " " + str(col)
yield "\n"
def testEnv(self, format="text"):
import gevent
import msgpack
import pkg_resources
import importlib
import coincurve
import sqlite3
from Crypt import CryptBitcoin
yield "\n"
yield from self.formatTable(
["ZeroNet version:", "%s rev%s" % (config.version, config.rev)],
["Python:", "%s" % sys.version],
["Platform:", "%s" % sys.platform],
["Crypt verify lib:", "%s" % CryptBitcoin.lib_verify_best],
["OpenSSL:", "%s" % CryptBitcoin.sslcrypto.ecc.get_backend()],
["Libsecp256k1:", "%s" % type(coincurve._libsecp256k1.lib).__name__],
["SQLite:", "%s, API: %s" % (sqlite3.sqlite_version, sqlite3.version)],
format=format
)
yield self.formatHead("Libraries:")
rows = []
for lib_name in ["gevent", "greenlet", "msgpack", "base58", "merkletools", "rsa", "socks", "pyasn1", "gevent_ws", "websocket", "maxminddb"]:
try:
module = importlib.import_module(lib_name)
if "__version__" in dir(module):
version = module.__version__
elif "version" in dir(module):
version = module.version
else:
version = "unknown version"
if type(version) is tuple:
version = ".".join(map(str, version))
rows.append(["- %s:" % lib_name, version, "at " + module.__file__])
except Exception as err:
rows.append(["! Error importing %s:", repr(err)])
"""
try:
yield " - %s<br>" % html.escape(repr(pkg_resources.get_distribution(lib_name)))
except Exception as err:
yield " ! %s<br>" % html.escape(repr(err))
"""
yield from self.formatTable(*rows, format=format)
yield self.formatHead("Library config:", format=format)
yield from self.formatTable(
["- gevent:", gevent.config.loop.__module__],
["- msgpack unpacker:", msgpack.Unpacker.__module__],
format=format
)

View file

@ -2,7 +2,7 @@
"ZeroNet Twitter": "ZeroNet Twitter", "ZeroNet Twitter": "ZeroNet Twitter",
"ZeroNet Reddit": "ZeroNet Reddit", "ZeroNet Reddit": "ZeroNet Reddit",
"ZeroNet Github": "ZeroNet Github", "ZeroNet Github": "ZeroNet Github",
"Report bug/request feature": "Rapport d'erreur/Demanger une fonctionnalité", "Report bug/request feature": "Rapport d'erreur/Demander une fonctionnalité",
"!Open ZeroNet": "!Ouvrir ZeroNet", "!Open ZeroNet": "!Ouvrir ZeroNet",
"Quit": "Quitter", "Quit": "Quitter",
"(active)": "(actif)", "(active)": "(actif)",

View file

@ -0,0 +1,14 @@
{
"ZeroNet Twitter": "ZeroNet Twitter",
"ZeroNet Reddit": "ZeroNet Reddit",
"ZeroNet Github": "ZeroNet Github",
"Report bug/request feature": "バグ報告/要望",
"!Open ZeroNet": "!ZeroNetをブラウザで開く",
"Quit": "閉じる",
"(active)": "(アクティブ)",
"(passive)": "(パッシブ)",
"Connections: %s": "接続数: %s",
"Received: %.2f MB | Sent: %.2f MB": "受信: %.2f MB | 送信: %.2f MB",
"Show console window": "コンソールを表示",
"Start ZeroNet when Windows starts": "Windows起動時にZeroNetも起動する"
}

View file

@ -0,0 +1,62 @@
{
"ZeroNet config": "ZeroNetの設定",
"Web Interface": "WEBインターフェース",
"Open web browser on ZeroNet startup": "ZeroNet起動時に自動でブラウザーを開く",
"Network": "ネットワーク",
"Offline mode": "オフラインモード",
"Disable network communication.": "通信を無効化します",
"File server network": "ファイルサーバネットワーク",
"Accept incoming peers using IPv4 or IPv6 address. (default: dual)": "IPv4とIPv6からの受信を許可既定: 両方)",
"Dual (IPv4 & IPv6)": "両方 IPv4 & IPv6",
"File server port": "ファイルサーバのポート",
"Other peers will use this port to reach your served sites. (default: randomize)": "他のピアはこのポートを使用してあなたが所持しているサイトにアクセスします (既定: ランダム)",
"File server external ip": "ファイルサーバの外部IP",
"Detect automatically": "自動検出",
"Your file server is accessible on these ips. (default: detect automatically)": "あなたのファイルサーバへはここで設定したIPでアクセスできます (既定: 自動検出)",
"Disable: Don't connect to peers on Tor network": "無効: Torネットワーク上のピアに接続しない",
"Enable: Only use Tor for Tor network peers": "有効: Torネットワーク上のピアに対してのみTorを使って接続する",
"Always: Use Tor for every connections to hide your IP address (slower)": "常時: 全ての接続にTorを使いIPを秘匿する低速",
"Disable": "無効",
"Enable": "有効",
"Always": "常時",
"Use Tor bridges": "Torブリッジを使用",
"Use obfuscated bridge relays to avoid network level Tor block (even slower)": "難読化されたブリッジリレーを使用してネットワークレベルのTorブロックを避ける超低速",
"Discover new peers using these adresses": "ここで設定したアドレスを用いてピアを発見します",
"Trackers files": "トラッカーファイル",
"Load additional list of torrent trackers dynamically, from a file": "ファイルからトレントラッカーの追加リストを動的に読み込みます",
"Eg.: data/trackers.json": "例: data/trackers.json",
"Proxy for tracker connections": "トラッカーへの接続に使うプロキシ",
"Custom": "カスタム",
"Custom socks proxy address for trackers": "トラッカーに接続するためのカスタムsocksプロキシのアドレス",
"Performance": "性能",
"Level of logging to file": "ログレベル",
"Everything": "全て",
"Only important messages": "重要なメッセージのみ",
"Only errors": "エラーのみ",
"Threads for async file system reads": "非同期ファイルシステムの読み込みに使うスレッド",
"Threads for async file system writes": "非同期ファイルシステムの書き込みに使うスレッド",
"Threads for cryptographic functions": "暗号機能に使うスレッド",
"Threads for database operations": "データベースの操作に使うスレッド",
"Sync read": "同期読み取り",
"Sync write": "同期書き込み",
"Sync execution": "同期実行",
"1 thread": "1スレッド",
"2 threads": "2スレッド",
"3 threads": "3スレッド",
"4 threads": "4スレッド",
"5 threads": "5スレッド",
"10 threads": "10スレッド",
" configuration item value changed": " の項目の値が変更されました",
"Save settings": "設定を保存",
"Some changed settings requires restart": "一部の変更の適用には再起動が必要です。",
"Restart ZeroNet client": "ZeroNetクライアントを再起動"
}

View file

@ -73,11 +73,17 @@ class UiFileManagerPlugin(object):
return super().error404(path) return super().error404(path)
path_parts = self.parsePath(path) path_parts = self.parsePath(path)
if not path_parts:
return super().error404(path)
site = self.server.site_manager.get(path_parts["request_address"]) site = self.server.site_manager.get(path_parts["request_address"])
if not site or not site.content_manager.contents.get("content.json"): if not site or not site.content_manager.contents.get("content.json"):
return super().error404(path) return super().error404(path)
if path_parts["inner_path"] in site.content_manager.contents.get("content.json").get("files", {}):
return super().error404(path)
self.sendHeader(200) self.sendHeader(200)
path_redirect = "/list" + re.sub("^/media/", "/", path) path_redirect = "/list" + re.sub("^/media/", "/", path)
self.log.debug("Index.html not found: %s, redirecting to: %s" % (path, path_redirect)) self.log.debug("Index.html not found: %s, redirecting to: %s" % (path, path_redirect))

View file

@ -0,0 +1,20 @@
{
"New file name:": "新しいファイルの名前:",
"Delete": "削除",
"Cancel": "キャンセル",
"Selected:": "選択済み: ",
"Delete and remove optional:": "オプションを削除",
" files": " ファイル",
" (modified)": " (編集済み)",
" (new)": " (新しい)",
" (optional)": " (オプション)",
" (ignored from content.json)": " content.jsonから無視されます",
"Total: ": "合計: ",
" dir, ": " のディレクトリ, ",
" file in ": " のファイル, ",
"+ New": "+ 新規作成",
"Edit": "編集",
"View": "閲覧",
"Save": "保存",
"Save: done!": "保存完了!"
}

View file

@ -0,0 +1,15 @@
window.BINARY_EXTENSIONS = [
"3dm", "3ds", "3g2", "3gp", "7z", "a", "aac", "adp", "ai", "aif", "aiff", "alz", "ape", "apk", "appimage", "ar", "arj", "asc", "asf", "au", "avi", "bak",
"baml", "bh", "bin", "bk", "bmp", "btif", "bz2", "bzip2", "cab", "caf", "cgm", "class", "cmx", "cpio", "cr2", "cur", "dat", "dcm", "deb", "dex", "djvu",
"dll", "dmg", "dng", "doc", "docm", "docx", "dot", "dotm", "dra", "DS_Store", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ecelp4800", "ecelp7470",
"ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "flatpak", "fli", "flv", "fpx", "fst", "fvt", "g3", "gh", "gif",
"gpg", "graffle", "gz", "gzip", "h261", "h263", "h264", "icns", "ico", "ief", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "key",
"ktx", "lha", "lib", "lvp", "lz", "lzh", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mht", "mid", "midi", "mj2", "mka", "mkv", "mmr", "mng",
"mobi", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msgpack", "mxu", "nef", "npx", "numbers", "nupkg", "o", "oga", "ogg", "ogv",
"otf", "pages", "pbm", "pcx", "pdb", "pdf", "pea", "pgm", "pic", "png", "pnm", "pot", "potm", "potx", "ppa", "ppam", "ppm", "pps", "ppsm", "ppsx",
"ppt", "pptm", "pptx", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "resources", "rgb", "rip", "rlc", "rmf", "rmvb", "rpm", "rtf",
"rz", "s3m", "s7z", "scpt", "sgi", "shar", "sig", "sil", "sketch", "slk", "smv", "snap", "snk", "so", "stl", "sub", "suo", "swf", "tar", "tbz2", "tbz",
"tga", "tgz", "thmx", "tif", "tiff", "tlz", "ttc", "ttf", "txz", "udf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax",
"wbmp", "wdp", "weba", "webm", "webp", "whl", "wim", "wm", "wma", "wmv", "wmx", "woff2", "woff", "wrm", "wvx", "xbm", "xif", "xla", "xlam", "xls",
"xlsb", "xlsm", "xlsx", "xlt", "xltm", "xltx", "xm", "xmind", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx"
]

View file

@ -1,5 +1,3 @@
BINARY_EXTENSIONS = ["png", "gif", "jpg", "pdf", "doc", "msgpack", "zip", "rar", "gz", "tar", "exe"]
class FileList extends Class class FileList extends Class
constructor: (@site, @inner_path, @is_owner=false) -> constructor: (@site, @inner_path, @is_owner=false) ->
@need_update = true @need_update = true
@ -179,7 +177,7 @@ class FileList extends Class
ext = item.name.split(".").pop() ext = item.name.split(".").pop()
is_editing = inner_path == Page.file_editor?.inner_path is_editing = inner_path == Page.file_editor?.inner_path
is_editable = not is_dir and item.size < 1024 * 1024 and ext not in BINARY_EXTENSIONS is_editable = not is_dir and item.size < 1024 * 1024 and ext not in window.BINARY_EXTENSIONS
is_modified = @item_list.isModified(inner_path) is_modified = @item_list.isModified(inner_path)
is_added = @item_list.isAdded(inner_path) is_added = @item_list.isAdded(inner_path)
optional_info = @item_list.getOptionalInfo(inner_path) optional_info = @item_list.getOptionalInfo(inner_path)

View file

@ -1918,6 +1918,15 @@
})); }));
/* ---- Config.coffee ---- */
(function() {
window.BINARY_EXTENSIONS = ["3dm", "3ds", "3g2", "3gp", "7z", "a", "aac", "adp", "ai", "aif", "aiff", "alz", "ape", "apk", "appimage", "ar", "arj", "asc", "asf", "au", "avi", "bak", "baml", "bh", "bin", "bk", "bmp", "btif", "bz2", "bzip2", "cab", "caf", "cgm", "class", "cmx", "cpio", "cr2", "cur", "dat", "dcm", "deb", "dex", "djvu", "dll", "dmg", "dng", "doc", "docm", "docx", "dot", "dotm", "dra", "DS_Store", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ecelp4800", "ecelp7470", "ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "flatpak", "fli", "flv", "fpx", "fst", "fvt", "g3", "gh", "gif", "gpg", "graffle", "gz", "gzip", "h261", "h263", "h264", "icns", "ico", "ief", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "key", "ktx", "lha", "lib", "lvp", "lz", "lzh", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mht", "mid", "midi", "mj2", "mka", "mkv", "mmr", "mng", "mobi", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msgpack", "mxu", "nef", "npx", "numbers", "nupkg", "o", "oga", "ogg", "ogv", "otf", "pages", "pbm", "pcx", "pdb", "pdf", "pea", "pgm", "pic", "png", "pnm", "pot", "potm", "potx", "ppa", "ppam", "ppm", "pps", "ppsm", "ppsx", "ppt", "pptm", "pptx", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "resources", "rgb", "rip", "rlc", "rmf", "rmvb", "rpm", "rtf", "rz", "s3m", "s7z", "scpt", "sgi", "shar", "sig", "sil", "sketch", "slk", "smv", "snap", "snk", "so", "stl", "sub", "suo", "swf", "tar", "tbz2", "tbz", "tga", "tgz", "thmx", "tif", "tiff", "tlz", "ttc", "ttf", "txz", "udf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax", "wbmp", "wdp", "weba", "webm", "webp", "whl", "wim", "wm", "wma", "wmv", "wmx", "woff2", "woff", "wrm", "wvx", "xbm", "xif", "xla", "xlam", "xls", "xlsb", "xlsm", "xlsx", "xlt", "xltm", "xltx", "xm", "xmind", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx"];
}).call(this);
/* ---- FileEditor.coffee ---- */ /* ---- FileEditor.coffee ---- */
@ -2187,7 +2196,6 @@
}).call(this); }).call(this);
/* ---- FileItemList.coffee ---- */ /* ---- FileItemList.coffee ---- */
@ -2477,14 +2485,12 @@
(function() { (function() {
var BINARY_EXTENSIONS, FileList, var FileList,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty, hasProp = {}.hasOwnProperty,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
BINARY_EXTENSIONS = ["png", "gif", "jpg", "pdf", "doc", "msgpack", "zip", "rar", "gz", "tar", "exe"];
FileList = (function(superClass) { FileList = (function(superClass) {
extend(FileList, superClass); extend(FileList, superClass);
@ -2759,7 +2765,7 @@
is_dir = (ref = item.type) === "dir" || ref === "parent"; is_dir = (ref = item.type) === "dir" || ref === "parent";
ext = item.name.split(".").pop(); ext = item.name.split(".").pop();
is_editing = inner_path === ((ref1 = Page.file_editor) != null ? ref1.inner_path : void 0); is_editing = inner_path === ((ref1 = Page.file_editor) != null ? ref1.inner_path : void 0);
is_editable = !is_dir && item.size < 1024 * 1024 && indexOf.call(BINARY_EXTENSIONS, ext) < 0; is_editable = !is_dir && item.size < 1024 * 1024 && indexOf.call(window.BINARY_EXTENSIONS, ext) < 0;
is_modified = this.item_list.isModified(inner_path); is_modified = this.item_list.isModified(inner_path);
is_added = this.item_list.isAdded(inner_path); is_added = this.item_list.isAdded(inner_path);
optional_info = this.item_list.getOptionalInfo(inner_path); optional_info = this.item_list.getOptionalInfo(inner_path);

View file

@ -14,7 +14,7 @@ class Config(object):
def __init__(self, argv): def __init__(self, argv):
self.version = "0.7.2" self.version = "0.7.2"
self.rev = 4538 self.rev = 4555
self.argv = argv self.argv = argv
self.action = None self.action = None
self.test_parser = None self.test_parser = None

View file

@ -7,6 +7,8 @@ import hashlib
from util.Electrum import dbl_format from util.Electrum import dbl_format
from Config import config from Config import config
import util.OpensslFindPatch
lib_verify_best = "sslcrypto" lib_verify_best = "sslcrypto"
from lib import sslcrypto from lib import sslcrypto
@ -28,6 +30,8 @@ def loadLib(lib_name, silent=False):
) )
elif lib_name == "sslcrypto": elif lib_name == "sslcrypto":
sslcurve = sslcurve_native sslcurve = sslcurve_native
if sslcurve_native == sslcurve_fallback:
logging.warning("SSLCurve fallback loaded instead of native")
elif lib_name == "sslcrypto_fallback": elif lib_name == "sslcrypto_fallback":
sslcurve = sslcurve_fallback sslcurve = sslcurve_fallback

View file

@ -28,7 +28,11 @@ def formatExceptionMessage(err):
return "%s: %s" % (err_type, err_message) return "%s: %s" % (err_type, err_message)
python_lib_dir = os.path.dirname(os.__file__) python_lib_dirs = [path.replace("\\", "/") for path in sys.path if re.sub(r".*[\\/]", "", path) in ("site-packages", "dist-packages")]
python_lib_dirs.append(os.path.dirname(os.__file__).replace("\\", "/")) # TODO: check if returns the correct path for PyPy
root_dir = os.path.realpath(os.path.dirname(__file__) + "/../../")
root_dir = root_dir.replace("\\", "/")
def formatTraceback(items, limit=None, fold_builtin=True): def formatTraceback(items, limit=None, fold_builtin=True):
@ -40,30 +44,63 @@ def formatTraceback(items, limit=None, fold_builtin=True):
for path, line in items: for path, line in items:
i += 1 i += 1
is_last = i == len(items) is_last = i == len(items)
dir_name, file_name = os.path.split(path.replace("\\", "/")) path = path.replace("\\", "/")
plugin_match = re.match(".*/plugins/(.+)$", dir_name) if path.startswith("src/gevent/"):
if plugin_match: file_title = "<gevent>/" + path[len("src/gevent/"):]
file_title = "%s/%s" % (plugin_match.group(1), file_name) is_builtin = True
is_prev_builtin = False is_skippable_builtin = False
elif path.startswith(python_lib_dir): elif path in ("<frozen importlib._bootstrap>", "<frozen importlib._bootstrap_external>"):
if is_prev_builtin and not is_last and fold_builtin: file_title = "(importlib)"
if back[-1] != "...": is_builtin = True
back.append("...") is_skippable_builtin = True
continue else:
is_skippable_builtin = False
for base in python_lib_dirs:
if path.startswith(base + "/"):
file_title = path[len(base + "/"):]
module_name, *tail = file_title.split("/")
if module_name.endswith(".py"):
module_name = module_name[:-3]
file_title = "/".join(["<%s>" % module_name] + tail)
is_builtin = True
break
else: else:
file_title = path.replace(python_lib_dir, "").replace("\\", "/").strip("/").replace("site-packages/", "") is_builtin = False
is_prev_builtin = True for base in (root_dir + "/src", root_dir + "/plugins", root_dir):
else: if path.startswith(base + "/"):
file_title = file_name file_title = path[len(base + "/"):]
is_prev_builtin = False break
else:
# For unknown paths, do our best to hide absolute path
file_title = path
for needle in ("/zeronet/", "/core/"):
if needle in file_title.lower():
file_title = "?/" + file_title[file_title.lower().rindex(needle) + len(needle):]
if file_title == prev_file_title: # Path compression: A/AB/ABC/X/Y.py -> ABC/X/Y.py
back.append("%s" % line) # E.g.: in 'Db/DbCursor.py' the directory part is unnecessary
if not file_title.startswith("/"):
prev_part = ""
for i, part in enumerate(file_title.split("/") + [""]):
if not part.startswith(prev_part):
break
prev_part = part
file_title = "/".join(file_title.split("/")[i - 1:])
if is_skippable_builtin and fold_builtin:
pass
elif is_builtin and is_prev_builtin and not is_last and fold_builtin:
if back[-1] != "...":
back.append("...")
else: else:
back.append("%s line %s" % (file_title, line)) if file_title == prev_file_title:
back.append("%s" % line)
else:
back.append("%s line %s" % (file_title, line))
prev_file_title = file_title prev_file_title = file_title
is_prev_builtin = is_builtin
if limit and i >= limit: if limit and i >= limit:
back.append("...") back.append("...")

View file

@ -760,10 +760,6 @@ class FileServer(ConnectionServer):
self.stream_server.start() self.stream_server.start()
except Exception as err: except Exception as err:
log.error("Error listening on: %s:%s: %s" % (self.ip, self.port, err)) log.error("Error listening on: %s:%s: %s" % (self.ip, self.port, err))
if "ui_server" in dir(sys.modules["main"]):
log.debug("Stopping UI Server.")
sys.modules["main"].ui_server.stop()
return False
if config.debug: if config.debug:
# Auto reload FileRequest on change # Auto reload FileRequest on change

52
src/Test/TestDebug.py Normal file
View file

@ -0,0 +1,52 @@
from Debug import Debug
import gevent
import os
import re
import pytest
class TestDebug:
@pytest.mark.parametrize("items,expected", [
(["@/src/A/B/C.py:17"], ["A/B/C.py line 17"]), # basic test
(["@/src/Db/Db.py:17"], ["Db.py line 17"]), # path compression
(["%s:1" % __file__], ["TestDebug.py line 1"]),
(["@/plugins/Chart/ChartDb.py:100"], ["ChartDb.py line 100"]), # plugins
(["@/main.py:17"], ["main.py line 17"]), # root
(["@\\src\\Db\\__init__.py:17"], ["Db/__init__.py line 17"]), # Windows paths
(["<frozen importlib._bootstrap>:1"], []), # importlib builtins
(["<frozen importlib._bootstrap_external>:1"], []), # importlib builtins
(["/home/ivanq/ZeroNet/src/main.py:13"], ["?/src/main.py line 13"]), # best-effort anonymization
(["C:\\ZeroNet\\core\\src\\main.py:13"], ["?/src/main.py line 13"]),
(["/root/main.py:17"], ["/root/main.py line 17"]),
(["{gevent}:13"], ["<gevent>/__init__.py line 13"]), # modules
(["{os}:13"], ["<os> line 13"]), # python builtin modules
(["src/gevent/event.py:17"], ["<gevent>/event.py line 17"]), # gevent-overriden __file__
(["@/src/Db/Db.py:17", "@/src/Db/DbQuery.py:1"], ["Db.py line 17", "DbQuery.py line 1"]), # mutliple args
(["@/src/Db/Db.py:17", "@/src/Db/Db.py:1"], ["Db.py line 17", "1"]), # same file
(["{os}:1", "@/src/Db/Db.py:17"], ["<os> line 1", "Db.py line 17"]), # builtins
(["{gevent}:1"] + ["{os}:3"] * 4 + ["@/src/Db/Db.py:17"], ["<gevent>/__init__.py line 1", "...", "Db.py line 17"])
])
def testFormatTraceback(self, items, expected):
q_items = []
for item in items:
file, line = item.rsplit(":", 1)
if file.startswith("@"):
file = Debug.root_dir + file[1:]
file = file.replace("{os}", os.__file__)
file = file.replace("{gevent}", gevent.__file__)
q_items.append((file, int(line)))
assert Debug.formatTraceback(q_items) == expected
def testFormatException(self):
try:
raise ValueError("Test exception")
except Exception:
assert re.match(r"ValueError: Test exception in TestDebug.py line [0-9]+", Debug.formatException())
try:
os.path.abspath(1)
except Exception:
assert re.search(r"in TestDebug.py line [0-9]+ > <(posixpath|ntpath)> line ", Debug.formatException())
def testFormatStack(self):
assert re.match(r"TestDebug.py line [0-9]+ > <_pytest>/python.py line [0-9]+", Debug.formatStack())

View file

@ -8,11 +8,13 @@
"or configure Tor to become a full member of the ZeroNet network.": "または、TorをZeroNetネットワークのメンバーになるように設定してください。", "or configure Tor to become a full member of the ZeroNet network.": "または、TorをZeroNetネットワークのメンバーになるように設定してください。",
"Select account you want to use in this site:": "このサイトで使用するアカウントを選択:", "Select account you want to use in this site:": "このサイトで使用するアカウントを選択:",
"No certificate": "証明書がありません",
"currently selected": "現在選択中", "currently selected": "現在選択中",
"Unique to site": "サイト固有", "Unique to site": "サイト固有",
"Content signing failed": "コンテンツの署名に失敗", "Content signing failed": "コンテンツの署名に失敗",
"Content publish queued for {0:.0f} seconds.": "コンテンツの公開は{0:.0f}秒のキューに入れられました。", "Content publish queued for {0:.0f} seconds.": "コンテンツの公開は{0:.0f}秒のキューに入れられました。",
"Content published to {0}/{1} peers.": "サイトの更新を通知済 {0}/{1} ピア",
"Content published to {0} peers.": "{0}ピアに公開されたコンテンツ。", "Content published to {0} peers.": "{0}ピアに公開されたコンテンツ。",
"No peers found, but your content is ready to access.": "ピアは見つかりませんでしたが、コンテンツにアクセスする準備ができました。", "No peers found, but your content is ready to access.": "ピアは見つかりませんでしたが、コンテンツにアクセスする準備ができました。",
"Your network connection is restricted. Please, open <b>{0}</b> port": "ネットワーク接続が制限されています。ポート <b>{0}</b> を開いて、", "Your network connection is restricted. Please, open <b>{0}</b> port": "ネットワーク接続が制限されています。ポート <b>{0}</b> を開いて、",
@ -43,6 +45,7 @@
"No peers found": "ピアが見つかりません", "No peers found": "ピアが見つかりません",
"Running out of size limit (": "サイズ制限を使い果たしました (", "Running out of size limit (": "サイズ制限を使い果たしました (",
"Set limit to \" + site_info.next_size_limit + \"MB": "制限を \" + site_info.next_size_limit + \"MB に設定", "Set limit to \" + site_info.next_size_limit + \"MB": "制限を \" + site_info.next_size_limit + \"MB に設定",
"Cloning site...": "サイトを複製中…",
"Site size limit changed to {0}MB": "サイトのサイズ制限が {0}MB に変更されました", "Site size limit changed to {0}MB": "サイトのサイズ制限が {0}MB に変更されました",
" New version of this page has just released.<br>Reload to see the modified content.": " このページの新しいバージョンが公開されました。<br>変更されたコンテンツを見るには再読み込みしてください。", " New version of this page has just released.<br>Reload to see the modified content.": " このページの新しいバージョンが公開されました。<br>変更されたコンテンツを見るには再読み込みしてください。",
"This site requests permission:": "このサイトは権限を要求しています:", "This site requests permission:": "このサイトは権限を要求しています:",
@ -52,6 +55,12 @@
"Trackers announcing": "トラッカーをお知らせ", "Trackers announcing": "トラッカーをお知らせ",
"Error": "エラー", "Error": "エラー",
"Done": "完了", "Done": "完了",
"Tracker connection error detected.": "トラッカー接続エラーが検出されました。" "Tracker connection error detected.": "トラッカー接続エラーが検出されました。",
"Update <b>ZeroNet client</b> to latest version?": "<b>ZeroNetクライアント</b>を最新版に更新しますか?",
"Update": "更新",
"Restart <b>ZeroNet client</b>?": "ZeroNetクライアントを再起動しますか",
"Restart": "再起動",
"Shut down <b>ZeroNet client</b>?": "<b>ZeroNetクライアント</b>を終了しますか?",
"Shut down": "終了"
} }

View file

@ -33,6 +33,9 @@ content_types = {
"html": "text/html", "html": "text/html",
"js": "application/javascript", "js": "application/javascript",
"json": "application/json", "json": "application/json",
"oga": "audio/ogg",
"ogg": "application/ogg",
"ogv": "video/ogg",
"sig": "application/pgp-signature", "sig": "application/pgp-signature",
"txt": "text/plain", "txt": "text/plain",
"webmanifest": "application/manifest+json", "webmanifest": "application/manifest+json",

View file

@ -9,9 +9,16 @@ class Infopanel
else else
@open() @open()
unfold: =>
@elem.toggleClass("unfolded")
return false
updateEvents: => updateEvents: =>
@elem.off("click") @elem.off("click")
@elem.find(".close").off("click") @elem.find(".close").off("click")
@elem.find(".line").off("click")
@elem.find(".line").on("click", @unfold)
if @elem.hasClass("closed") if @elem.hasClass("closed")
@elem.on "click", => @elem.on "click", =>

View file

@ -658,7 +658,7 @@ class Wrapper
else else
@announcer_line = @loading.printLine(status_line) @announcer_line = @loading.printLine(status_line)
if status_db.error.length > (status_db.announced.length + status_db.announcing.length) if status_db.error.length > (status_db.announced.length + status_db.announcing.length) and status_db.announced.length < 3
@loading.showTrackerTorBridge(@server_info) @loading.showTrackerTorBridge(@server_info)
updateProgress: (site_info) -> updateProgress: (site_info) ->

View file

@ -131,6 +131,7 @@ a { color: black }
padding: 4px; border-top-left-radius: 6px; border-bottom-left-radius: 6px; font-size: 10px; padding: 4px; border-top-left-radius: 6px; border-bottom-left-radius: 6px; font-size: 10px;
opacity: 0; margin-left: 0px; pointer-events: none; transition: all 0.6s; opacity: 0; margin-left: 0px; pointer-events: none; transition: all 0.6s;
} }
.infopanel.unfolded .message .line { overflow: visible; white-space: normal; }
.body-sidebar .infopanel { right: 425px; } .body-sidebar .infopanel { right: 425px; }
.body-sidebar .infopanel.closed { right: 0px; } .body-sidebar .infopanel.closed { right: 0px; }

View file

@ -144,6 +144,7 @@ a { color: black }
padding: 4px; border-top-left-radius: 6px; border-bottom-left-radius: 6px; font-size: 10px; padding: 4px; border-top-left-radius: 6px; border-bottom-left-radius: 6px; font-size: 10px;
opacity: 0; margin-left: 0px; pointer-events: none; -webkit-transition: all 0.6s; -moz-transition: all 0.6s; -o-transition: all 0.6s; -ms-transition: all 0.6s; transition: all 0.6s ; opacity: 0; margin-left: 0px; pointer-events: none; -webkit-transition: all 0.6s; -moz-transition: all 0.6s; -o-transition: all 0.6s; -ms-transition: all 0.6s; transition: all 0.6s ;
} }
.infopanel.unfolded .message .line { overflow: visible; white-space: normal; }
.body-sidebar .infopanel { right: 425px; } .body-sidebar .infopanel { right: 425px; }
.body-sidebar .infopanel.closed { right: 0px; } .body-sidebar .infopanel.closed { right: 0px; }

View file

@ -548,6 +548,7 @@ $.extend( $.easing,
this.close = bind(this.close, this); this.close = bind(this.close, this);
this.hide = bind(this.hide, this); this.hide = bind(this.hide, this);
this.updateEvents = bind(this.updateEvents, this); this.updateEvents = bind(this.updateEvents, this);
this.unfold = bind(this.unfold, this);
this.show = bind(this.show, this); this.show = bind(this.show, this);
this.visible = false; this.visible = false;
} }
@ -564,9 +565,16 @@ $.extend( $.easing,
} }
}; };
Infopanel.prototype.unfold = function() {
this.elem.toggleClass("unfolded");
return false;
};
Infopanel.prototype.updateEvents = function() { Infopanel.prototype.updateEvents = function() {
this.elem.off("click"); this.elem.off("click");
this.elem.find(".close").off("click"); this.elem.find(".close").off("click");
this.elem.find(".line").off("click");
this.elem.find(".line").on("click", this.unfold);
if (this.elem.hasClass("closed")) { if (this.elem.hasClass("closed")) {
return this.elem.on("click", (function(_this) { return this.elem.on("click", (function(_this) {
return function() { return function() {
@ -779,7 +787,6 @@ $.extend( $.easing,
}).call(this); }).call(this);
/* ---- Notifications.coffee ---- */ /* ---- Notifications.coffee ---- */
@ -1910,7 +1917,7 @@ $.extend( $.easing,
} else { } else {
this.announcer_line = this.loading.printLine(status_line); this.announcer_line = this.loading.printLine(status_line);
} }
if (status_db.error.length > (status_db.announced.length + status_db.announcing.length)) { if (status_db.error.length > (status_db.announced.length + status_db.announcing.length) && status_db.announced.length < 3) {
return this.loading.showTrackerTorBridge(this.server_info); return this.loading.showTrackerTorBridge(this.server_info);
} }
}; };
@ -2004,6 +2011,7 @@ $.extend( $.easing,
}).call(this); }).call(this);
/* ---- WrapperZeroFrame.coffee ---- */ /* ---- WrapperZeroFrame.coffee ---- */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1 @@
<svg version="1.1" viewBox="0 0 2050 2050" xmlns="http://www.w3.org/2000/svg"><g fill="white"><path d="m299 1211v-787.6l725.7-423.4 725.3 420-175.34 325.7-549.86-340.7-373.5 221v381.2z"/><path d="m1749.4 842.6v787.6l-725.4 423.3-724.5-419.5 219.38-278.79 505.12 293.39 373.2-221.2v-381z"/><path d="M299.5 1634L1750 786.4V420L299 1267.4z"/></g></svg>

After

Width:  |  Height:  |  Size: 350 B

View file

@ -38,7 +38,7 @@ else if (window.opener && window.opener.location.toString()) {
<!-- Fixed button --> <!-- Fixed button -->
<div class='fixbutton'> <div class='fixbutton'>
<div class='fixbutton-text'><img width=22 src='/uimedia/img/logo-white.png'/></div> <div class='fixbutton-text'><img width=30 src='/uimedia/img/logo-white.svg'/></div>
<div class='fixbutton-burger'>&#x2261;</div> <div class='fixbutton-burger'>&#x2261;</div>
<a class='fixbutton-bg' href="{homepage}/"></a> <a class='fixbutton-bg' href="{homepage}/"></a>
</div> </div>

View file

@ -579,7 +579,7 @@ class Actions(object):
func_name = "test" + test_name[0].upper() + test_name[1:] func_name = "test" + test_name[0].upper() + test_name[1:]
if hasattr(self, func_name): if hasattr(self, func_name):
func = getattr(self, func_name) func = getattr(self, func_name)
print("- Running %s" % test_name, end="") print("- Running test: %s" % test_name, end="")
s = time.time() s = time.time()
ret = func(*args, **kwargs) ret = func(*args, **kwargs)
if type(ret) is types.GeneratorType: if type(ret) is types.GeneratorType:

View file

@ -1,11 +1,12 @@
import logging import logging
import os import os
import sys import sys
from ctypes.util import find_library import ctypes.util
from lib.sslcrypto.openssl import discovery
from Config import config from Config import config
find_library_original = ctypes.util.find_library
def getOpensslPath(): def getOpensslPath():
if config.openssl_lib_file: if config.openssl_lib_file:
@ -47,11 +48,22 @@ def getOpensslPath():
logging.debug("OpenSSL lib not found in: %s (%s)" % (path, err)) logging.debug("OpenSSL lib not found in: %s (%s)" % (path, err))
lib_path = ( lib_path = (
find_library('ssl.so') or find_library('ssl') or find_library_original('ssl.so') or find_library_original('ssl') or
find_library('crypto') or find_library('libcrypto') or 'libeay32' find_library_original('crypto') or find_library_original('libcrypto') or 'libeay32'
) )
return lib_path return lib_path
discovery.discover = getOpensslPath def patchCtypesOpensslFindLibrary():
def findLibraryPatched(name):
if name in ("ssl", "crypto", "libeay32"):
lib_path = getOpensslPath()
return lib_path
else:
return find_library_original(name)
ctypes.util.find_library = findLibraryPatched
patchCtypesOpensslFindLibrary()

View file

@ -339,7 +339,7 @@ def _communicate_with_igd(port=15441,
success = True success = True
break break
# Wait another 10sec for competition or any positibe result # Wait another 10sec for competition or any positive result
for _ in range(10): for _ in range(10):
all_done = all([thread.value is not None for thread in threads]) all_done = all([thread.value is not None for thread in threads])
any_succeed = any([thread.value for thread in threads]) any_succeed = any([thread.value for thread in threads])