├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature_request.md └── workflows │ └── tests.yml ├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── CHANGELOG.md ├── COPYING ├── Dockerfile ├── Dockerfile.arm64v8 ├── LICENSE ├── README-ru.md ├── README-zh-cn.md ├── README.md ├── Vagrantfile ├── plugins ├── AnnounceBitTorrent │ ├── AnnounceBitTorrentPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── AnnounceLocal │ ├── AnnounceLocalPlugin.py │ ├── BroadcastServer.py │ ├── Test │ │ ├── TestAnnounce.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── __init__.py │ └── plugin_info.json ├── AnnounceShare │ ├── AnnounceSharePlugin.py │ ├── Test │ │ ├── TestAnnounceShare.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── __init__.py │ └── plugin_info.json ├── AnnounceZero │ ├── AnnounceZeroPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── Benchmark │ ├── BenchmarkDb.py │ ├── BenchmarkPack.py │ ├── BenchmarkPlugin.py │ ├── __init__.py │ ├── media │ │ └── benchmark.html │ └── plugin_info.json ├── Bigfile │ ├── BigfilePiecefield.py │ ├── BigfilePlugin.py │ ├── Test │ │ ├── TestBigfile.py │ │ ├── conftest.py │ │ └── pytest.ini │ └── __init__.py ├── Chart │ ├── ChartCollector.py │ ├── ChartDb.py │ ├── ChartPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── ContentFilter │ ├── ContentFilterPlugin.py │ ├── ContentFilterStorage.py │ ├── Test │ │ ├── TestContentFilter.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── __init__.py │ ├── languages │ │ ├── hu.json │ │ ├── it.json │ │ ├── jp.json │ │ ├── pt-br.json │ │ ├── zh-tw.json │ │ └── zh.json │ ├── media │ │ ├── blocklisted.html │ │ └── js │ │ │ └── ZeroFrame.js │ └── plugin_info.json ├── Cors │ ├── CorsPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── CryptMessage │ ├── CryptMessage.py │ ├── CryptMessagePlugin.py │ ├── Test │ │ ├── TestCrypt.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── __init__.py │ └── plugin_info.json ├── FilePack │ ├── FilePackPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── MergerSite │ ├── MergerSitePlugin.py │ ├── __init__.py │ └── languages │ │ ├── es.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── it.json │ │ ├── jp.json │ │ ├── pt-br.json │ │ ├── tr.json │ │ ├── zh-tw.json │ │ └── zh.json ├── Newsfeed │ ├── NewsfeedPlugin.py │ └── __init__.py ├── OptionalManager │ ├── ContentDbPlugin.py │ ├── OptionalManagerPlugin.py │ ├── Test │ │ ├── TestOptionalManager.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── UiWebsocketPlugin.py │ ├── __init__.py │ └── languages │ │ ├── es.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── jp.json │ │ ├── pt-br.json │ │ ├── zh-tw.json │ │ └── zh.json ├── PeerDb │ ├── PeerDbPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── Sidebar │ ├── ConsolePlugin.py │ ├── SidebarPlugin.py │ ├── ZipStream.py │ ├── __init__.py │ ├── languages │ │ ├── da.json │ │ ├── de.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── it.json │ │ ├── jp.json │ │ ├── pl.json │ │ ├── pt-br.json │ │ ├── ru.json │ │ ├── tr.json │ │ ├── zh-tw.json │ │ └── zh.json │ ├── media │ │ ├── Class.coffee │ │ ├── Console.coffee │ │ ├── Console.css │ │ ├── Menu.coffee │ │ ├── Menu.css │ │ ├── Prototypes.coffee │ │ ├── RateLimit.coffee │ │ ├── Scrollable.js │ │ ├── Scrollbable.css │ │ ├── Sidebar.coffee │ │ ├── Sidebar.css │ │ ├── all.css │ │ ├── all.js │ │ └── morphdom.js │ ├── media_globe │ │ ├── Detector.js │ │ ├── Tween.js │ │ ├── all.js │ │ ├── globe.js │ │ ├── three.min.js │ │ └── world.jpg │ └── plugin_info.json ├── Stats │ ├── StatsPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── TranslateSite │ ├── TranslateSitePlugin.py │ ├── __init__.py │ └── plugin_info.json ├── Trayicon │ ├── TrayiconPlugin.py │ ├── __init__.py │ ├── languages │ │ ├── es.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── it.json │ │ ├── jp.json │ │ ├── pl.json │ │ ├── pt-br.json │ │ ├── tr.json │ │ ├── zh-tw.json │ │ └── zh.json │ ├── lib │ │ ├── __init__.py │ │ ├── notificationicon.py │ │ └── winfolders.py │ ├── plugin_info.json │ └── trayicon.ico ├── UiConfig │ ├── UiConfigPlugin.py │ ├── __init__.py │ ├── languages │ │ ├── hu.json │ │ ├── jp.json │ │ ├── pl.json │ │ ├── pt-br.json │ │ └── zh.json │ ├── media │ │ ├── config.html │ │ ├── css │ │ │ ├── Config.css │ │ │ ├── all.css │ │ │ ├── button.css │ │ │ └── fonts.css │ │ ├── img │ │ │ └── loading.gif │ │ └── js │ │ │ ├── ConfigStorage.coffee │ │ │ ├── ConfigView.coffee │ │ │ ├── UiConfig.coffee │ │ │ ├── all.js │ │ │ ├── lib │ │ │ ├── Class.coffee │ │ │ ├── Promise.coffee │ │ │ ├── Prototypes.coffee │ │ │ └── maquette.js │ │ │ └── utils │ │ │ ├── Animation.coffee │ │ │ ├── Dollar.coffee │ │ │ └── ZeroFrame.coffee │ └── plugin_info.json ├── UiFileManager │ ├── UiFileManagerPlugin.py │ ├── __init__.py │ ├── languages │ │ ├── hu.json │ │ └── jp.json │ └── media │ │ ├── codemirror │ │ ├── LICENSE │ │ ├── all.css │ │ ├── all.js │ │ ├── base │ │ │ ├── codemirror.css │ │ │ └── codemirror.js │ │ ├── extension │ │ │ ├── dialog │ │ │ │ ├── dialog.css │ │ │ │ └── dialog.js │ │ │ ├── edit │ │ │ │ ├── closebrackets.js │ │ │ │ ├── closetag.js │ │ │ │ ├── continuelist.js │ │ │ │ ├── matchbrackets.js │ │ │ │ ├── matchtags.js │ │ │ │ └── trailingspace.js │ │ │ ├── fold │ │ │ │ ├── brace-fold.js │ │ │ │ ├── comment-fold.js │ │ │ │ ├── foldcode.js │ │ │ │ ├── foldgutter.css │ │ │ │ ├── foldgutter.js │ │ │ │ ├── indent-fold.js │ │ │ │ ├── markdown-fold.js │ │ │ │ └── xml-fold.js │ │ │ ├── hint │ │ │ │ ├── anyword-hint.js │ │ │ │ ├── html-hint.js │ │ │ │ ├── show-hint.css │ │ │ │ ├── show-hint.js │ │ │ │ ├── sql-hint.js │ │ │ │ └── xml-hint.js │ │ │ ├── lint │ │ │ │ ├── json-lint.js │ │ │ │ ├── jsonlint.js │ │ │ │ ├── lint.css │ │ │ │ └── lint.js │ │ │ ├── mdn-like-custom.css │ │ │ ├── scroll │ │ │ │ ├── annotatescrollbar.js │ │ │ │ ├── scrollpastend.js │ │ │ │ ├── simplescrollbars.css │ │ │ │ └── simplescrollbars.js │ │ │ ├── search │ │ │ │ ├── jump-to-line.js │ │ │ │ ├── match-highlighter.js │ │ │ │ ├── matchesonscrollbar.css │ │ │ │ ├── matchesonscrollbar.js │ │ │ │ ├── search.js │ │ │ │ └── searchcursor.js │ │ │ ├── selection │ │ │ │ ├── active-line.js │ │ │ │ ├── mark-selection.js │ │ │ │ └── selection-pointer.js │ │ │ ├── simple.js │ │ │ └── sublime.js │ │ └── mode │ │ │ ├── coffeescript.js │ │ │ ├── css.js │ │ │ ├── go.js │ │ │ ├── htmlembedded.js │ │ │ ├── htmlmixed.js │ │ │ ├── javascript.js │ │ │ ├── markdown.js │ │ │ ├── python.js │ │ │ ├── rust.js │ │ │ └── xml.js │ │ ├── css │ │ ├── Menu.css │ │ ├── Selectbar.css │ │ ├── UiFileManager.css │ │ └── all.css │ │ ├── img │ │ └── loading.gif │ │ ├── js │ │ ├── Config.coffee │ │ ├── FileEditor.coffee │ │ ├── FileItemList.coffee │ │ ├── FileList.coffee │ │ ├── UiFileManager.coffee │ │ ├── all.js │ │ └── lib │ │ │ ├── Animation.coffee │ │ │ ├── Class.coffee │ │ │ ├── Dollar.coffee │ │ │ ├── ItemList.coffee │ │ │ ├── Menu.coffee │ │ │ ├── Promise.coffee │ │ │ ├── Prototypes.coffee │ │ │ ├── RateLimitCb.coffee │ │ │ ├── Text.coffee │ │ │ ├── Time.coffee │ │ │ ├── ZeroFrame.coffee │ │ │ └── maquette.js │ │ └── list.html ├── UiPluginManager │ ├── UiPluginManagerPlugin.py │ ├── __init__.py │ └── media │ │ ├── css │ │ ├── PluginManager.css │ │ ├── all.css │ │ ├── button.css │ │ └── fonts.css │ │ ├── img │ │ └── loading.gif │ │ ├── js │ │ ├── PluginList.coffee │ │ ├── UiPluginManager.coffee │ │ ├── all.js │ │ ├── lib │ │ │ ├── Class.coffee │ │ │ ├── Promise.coffee │ │ │ ├── Prototypes.coffee │ │ │ └── maquette.js │ │ └── utils │ │ │ ├── Animation.coffee │ │ │ ├── Dollar.coffee │ │ │ └── ZeroFrame.coffee │ │ └── plugin_manager.html ├── Zeroname │ ├── README.md │ ├── SiteManagerPlugin.py │ ├── __init__.py │ └── updater │ │ └── zeroname_updater.py ├── __init__.py ├── disabled-Bootstrapper │ ├── BootstrapperDb.py │ ├── BootstrapperPlugin.py │ ├── Test │ │ ├── TestBootstrapper.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── __init__.py │ └── plugin_info.json ├── disabled-Dnschain │ ├── SiteManagerPlugin.py │ ├── UiRequestPlugin.py │ └── __init__.py ├── disabled-DonationMessage │ ├── DonationMessagePlugin.py │ └── __init__.py ├── disabled-Multiuser │ ├── MultiuserPlugin.py │ ├── Test │ │ ├── TestMultiuser.py │ │ ├── conftest.py │ │ └── pytest.ini │ ├── UserPlugin.py │ ├── __init__.py │ └── plugin_info.json ├── disabled-StemPort │ ├── StemPortPlugin.py │ └── __init__.py ├── disabled-UiPassword │ ├── UiPasswordPlugin.py │ ├── __init__.py │ ├── login.html │ └── plugin_info.json └── disabled-ZeronameLocal │ ├── SiteManagerPlugin.py │ ├── UiRequestPlugin.py │ └── __init__.py ├── requirements.txt ├── src ├── Config.py ├── Connection │ ├── Connection.py │ ├── ConnectionServer.py │ └── __init__.py ├── Content │ ├── ContentDb.py │ ├── ContentDbDict.py │ ├── ContentManager.py │ └── __init__.py ├── Crypt │ ├── Crypt.py │ ├── CryptBitcoin.py │ ├── CryptConnection.py │ ├── CryptHash.py │ ├── CryptRsa.py │ └── __init__.py ├── Db │ ├── Db.py │ ├── DbCursor.py │ ├── DbQuery.py │ └── __init__.py ├── Debug │ ├── Debug.py │ ├── DebugHook.py │ ├── DebugLock.py │ ├── DebugMedia.py │ ├── DebugReloader.py │ └── __init__.py ├── File │ ├── FileRequest.py │ ├── FileServer.py │ └── __init__.py ├── Peer │ ├── Peer.py │ ├── PeerHashfield.py │ ├── PeerPortchecker.py │ └── __init__.py ├── Plugin │ ├── PluginManager.py │ └── __init__.py ├── Site │ ├── Site.py │ ├── SiteAnnouncer.py │ ├── SiteManager.py │ ├── SiteStorage.py │ └── __init__.py ├── Test │ ├── BenchmarkSsl.py │ ├── Spy.py │ ├── TestCached.py │ ├── TestConfig.py │ ├── TestConnectionServer.py │ ├── TestContent.py │ ├── TestContentUser.py │ ├── TestCryptBitcoin.py │ ├── TestCryptConnection.py │ ├── TestCryptHash.py │ ├── TestDb.py │ ├── TestDbQuery.py │ ├── TestDebug.py │ ├── TestDiff.py │ ├── TestEvent.py │ ├── TestFileRequest.py │ ├── TestFlag.py │ ├── TestHelper.py │ ├── TestMsgpack.py │ ├── TestNoparallel.py │ ├── TestPeer.py │ ├── TestRateLimit.py │ ├── TestSafeRe.py │ ├── TestSite.py │ ├── TestSiteDownload.py │ ├── TestSiteStorage.py │ ├── TestThreadPool.py │ ├── TestTor.py │ ├── TestTranslate.py │ ├── TestUiWebsocket.py │ ├── TestUpnpPunch.py │ ├── TestUser.py │ ├── TestWeb.py │ ├── TestWorkerTaskManager.py │ ├── __init__.py │ ├── conftest.py │ ├── coverage.ini │ ├── pytest.ini │ └── testdata │ │ └── 1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original │ │ ├── content.json │ │ ├── css │ │ └── all.css │ │ ├── data-default │ │ ├── data.json │ │ └── users │ │ │ └── content-default.json │ │ ├── data │ │ ├── data.json │ │ ├── img │ │ │ ├── autoupdate.png │ │ │ ├── direct_domains.png │ │ │ ├── domain.png │ │ │ ├── memory.png │ │ │ ├── multiuser.png │ │ │ ├── progressbar.png │ │ │ ├── slides.png │ │ │ ├── slots_memory.png │ │ │ ├── trayicon.png │ │ │ ├── zeroblog-comments.png │ │ │ ├── zeroid.png │ │ │ ├── zeroname.png │ │ │ ├── zerotalk-mark.png │ │ │ ├── zerotalk-upvote.png │ │ │ └── zerotalk.png │ │ ├── optional.txt │ │ ├── test_include │ │ │ ├── content.json │ │ │ └── data.json │ │ └── users │ │ │ ├── 1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q │ │ │ ├── content.json │ │ │ └── data.json │ │ │ ├── 1CjfbrbwtP8Y2QjPy12vpTATkUT7oSiPQ9 │ │ │ ├── content.json │ │ │ ├── data.json │ │ │ └── peanut-butter-jelly-time.gif │ │ │ ├── 1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C │ │ │ ├── content.json │ │ │ └── data.json │ │ │ └── content.json │ │ ├── dbschema.json │ │ ├── img │ │ └── loading.gif │ │ ├── index.html │ │ └── js │ │ └── all.js ├── Tor │ ├── TorManager.py │ └── __init__.py ├── Translate │ ├── Translate.py │ ├── __init__.py │ └── languages │ │ ├── da.json │ │ ├── de.json │ │ ├── es.json │ │ ├── fa.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── it.json │ │ ├── jp.json │ │ ├── nl.json │ │ ├── pl.json │ │ ├── pt-br.json │ │ ├── ru.json │ │ ├── sk.json │ │ ├── sl.json │ │ ├── tr.json │ │ ├── zh-tw.json │ │ └── zh.json ├── Ui │ ├── UiRequest.py │ ├── UiServer.py │ ├── UiWebsocket.py │ ├── __init__.py │ ├── media │ │ ├── Fixbutton.coffee │ │ ├── Infopanel.coffee │ │ ├── Loading.coffee │ │ ├── Notifications.coffee │ │ ├── Wrapper.coffee │ │ ├── Wrapper.css │ │ ├── WrapperZeroFrame.coffee │ │ ├── ZeroSiteTheme.coffee │ │ ├── all.css │ │ ├── all.js │ │ ├── img │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon.ico │ │ │ ├── favicon.psd │ │ │ ├── loading-circle.gif │ │ │ ├── loading.gif │ │ │ ├── logo-white.svg │ │ │ ├── logo.png │ │ │ ├── logo.psd │ │ │ └── logo.svg │ │ └── lib │ │ │ ├── 00-jquery.min.js │ │ │ ├── RateLimit.coffee │ │ │ ├── Translate.coffee │ │ │ ├── ZeroWebsocket.coffee │ │ │ ├── jquery.cssanim.js │ │ │ ├── jquery.csslater.coffee │ │ │ └── jquery.easing.js │ └── template │ │ ├── site_add.html │ │ └── wrapper.html ├── User │ ├── User.py │ ├── UserManager.py │ └── __init__.py ├── Worker │ ├── Worker.py │ ├── WorkerManager.py │ ├── WorkerTaskManager.py │ └── __init__.py ├── __init__.py ├── lib │ ├── __init__.py │ ├── bencode_open │ │ ├── LICENSE │ │ └── __init__.py │ ├── cssvendor │ │ ├── __init__.py │ │ └── cssvendor.py │ ├── gevent_ws │ │ └── __init__.py │ ├── libsecp256k1message │ │ ├── __init__.py │ │ └── libsecp256k1message.py │ ├── openssl │ │ └── openssl.cnf │ ├── pyaes │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── __init__.py │ │ ├── aes.py │ │ ├── blockfeeder.py │ │ └── util.py │ ├── sslcrypto │ │ ├── LICENSE │ │ ├── __init__.py │ │ ├── _aes.py │ │ ├── _ecc.py │ │ ├── _ripemd.py │ │ ├── fallback │ │ │ ├── __init__.py │ │ │ ├── _jacobian.py │ │ │ ├── _util.py │ │ │ ├── aes.py │ │ │ ├── ecc.py │ │ │ └── rsa.py │ │ └── openssl │ │ │ ├── __init__.py │ │ │ ├── aes.py │ │ │ ├── discovery.py │ │ │ ├── ecc.py │ │ │ ├── library.py │ │ │ └── rsa.py │ └── subtl │ │ ├── LICENCE │ │ ├── README.md │ │ ├── __init__.py │ │ └── subtl.py ├── main.py └── util │ ├── Cached.py │ ├── Diff.py │ ├── Electrum.py │ ├── Event.py │ ├── Flag.py │ ├── GreenletManager.py │ ├── Msgpack.py │ ├── Noparallel.py │ ├── OpensslFindPatch.py │ ├── Platform.py │ ├── Pooled.py │ ├── QueryJson.py │ ├── RateLimit.py │ ├── SafeRe.py │ ├── SocksProxy.py │ ├── ThreadPool.py │ ├── UpnpPunch.py │ ├── __init__.py │ └── helper.py ├── start.py ├── tools └── coffee │ ├── README.md │ ├── coffee-script.js │ ├── coffee.cmd │ └── coffee.wsf ├── update.py └── zeronet.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://zeronet.io/docs/help_zeronet/donate/ 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve ZeroNet 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Step 1: Please describe your environment 11 | 12 | * ZeroNet version: _____ 13 | * Operating system: _____ 14 | * Web browser: _____ 15 | * Tor status: not available/always/disabled 16 | * Opened port: yes/no 17 | * Special configuration: ____ 18 | 19 | ### Step 2: Describe the problem: 20 | 21 | #### Steps to reproduce: 22 | 23 | 1. _____ 24 | 2. _____ 25 | 3. _____ 26 | 27 | #### Observed Results: 28 | 29 | * What happened? This could be a screenshot, a description, log output (you can send log/debug.log file to hello@zeronet.io if necessary), etc. 30 | 31 | #### Expected Results: 32 | 33 | * What did you expect to happen? 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for ZeroNet 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # Log files 6 | **/*.log 7 | 8 | # Hidden files 9 | .* 10 | !/.github 11 | !/.gitignore 12 | !/.travis.yml 13 | !/.gitlab-ci.yml 14 | 15 | # Temporary files 16 | *.bak 17 | 18 | # Data dir 19 | data/* 20 | *.db 21 | 22 | # Virtualenv 23 | env/* 24 | 25 | # Tor data 26 | tools/tor/data 27 | 28 | # PhantomJS, downloaded manually for unit tests 29 | tools/phantomjs 30 | 31 | # ZeroNet config file 32 | zeronet.conf 33 | 34 | # ZeroNet log files 35 | log/* 36 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - test 3 | 4 | .test_template: &test_template 5 | stage: test 6 | before_script: 7 | - pip install --upgrade pip wheel 8 | # Selenium and requests can't be installed without a requests hint on Python 3.4 9 | - pip install --upgrade requests>=2.22.0 10 | - pip install --upgrade codecov coveralls flake8 mock pytest==4.6.3 pytest-cov selenium 11 | - pip install --upgrade -r requirements.txt 12 | script: 13 | - pip list 14 | - openssl version -a 15 | - python -m pytest -x plugins/CryptMessage/Test --color=yes 16 | - python -m pytest -x plugins/Bigfile/Test --color=yes 17 | - python -m pytest -x plugins/AnnounceLocal/Test --color=yes 18 | - python -m pytest -x plugins/OptionalManager/Test --color=yes 19 | - python -m pytest src/Test --cov=src --cov-config src/Test/coverage.ini --color=yes 20 | - mv plugins/disabled-Multiuser plugins/Multiuser 21 | - python -m pytest -x plugins/Multiuser/Test --color=yes 22 | - mv plugins/disabled-Bootstrapper plugins/Bootstrapper 23 | - python -m pytest -x plugins/Bootstrapper/Test --color=yes 24 | - flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics --exclude=src/lib/pyaes/ 25 | 26 | test:py3.4: 27 | image: python:3.4.3 28 | <<: *test_template 29 | 30 | test:py3.5: 31 | image: python:3.5.7 32 | <<: *test_template 33 | 34 | test:py3.6: 35 | image: python:3.6.9 36 | <<: *test_template 37 | 38 | test:py3.7-openssl1.1.0: 39 | image: python:3.7.0b5 40 | <<: *test_template 41 | 42 | test:py3.7-openssl1.1.1: 43 | image: python:3.7.4 44 | <<: *test_template 45 | 46 | test:py3.8: 47 | image: python:3.8.0b3 48 | <<: *test_template -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 3.4 4 | - 3.5 5 | - 3.6 6 | - 3.7 7 | - 3.8 8 | services: 9 | - docker 10 | cache: pip 11 | before_install: 12 | - pip install --upgrade pip wheel 13 | - pip install --upgrade codecov coveralls flake8 mock pytest==4.6.3 pytest-cov selenium 14 | # - docker build -t zeronet . 15 | # - docker run -d -v $PWD:/root/data -p 15441:15441 -p 127.0.0.1:43110:43110 zeronet 16 | install: 17 | - pip install --upgrade -r requirements.txt 18 | - pip list 19 | before_script: 20 | - openssl version -a 21 | # Add an IPv6 config - see the corresponding Travis issue 22 | # https://github.com/travis-ci/travis-ci/issues/8361 23 | - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then 24 | sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; 25 | fi 26 | script: 27 | - catchsegv python -m pytest src/Test --cov=src --cov-config src/Test/coverage.ini 28 | - export ZERONET_LOG_DIR="log/CryptMessage"; catchsegv python -m pytest -x plugins/CryptMessage/Test 29 | - export ZERONET_LOG_DIR="log/Bigfile"; catchsegv python -m pytest -x plugins/Bigfile/Test 30 | - export ZERONET_LOG_DIR="log/AnnounceLocal"; catchsegv python -m pytest -x plugins/AnnounceLocal/Test 31 | - export ZERONET_LOG_DIR="log/OptionalManager"; catchsegv python -m pytest -x plugins/OptionalManager/Test 32 | - export ZERONET_LOG_DIR="log/Multiuser"; mv plugins/disabled-Multiuser plugins/Multiuser && catchsegv python -m pytest -x plugins/Multiuser/Test 33 | - export ZERONET_LOG_DIR="log/Bootstrapper"; mv plugins/disabled-Bootstrapper plugins/Bootstrapper && catchsegv python -m pytest -x plugins/Bootstrapper/Test 34 | - find src -name "*.json" | xargs -n 1 python3 -c "import json, sys; print(sys.argv[1], end=' '); json.load(open(sys.argv[1])); print('[OK]')" 35 | - find plugins -name "*.json" | xargs -n 1 python3 -c "import json, sys; print(sys.argv[1], end=' '); json.load(open(sys.argv[1])); print('[OK]')" 36 | - flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics --exclude=src/lib/pyaes/ 37 | after_failure: 38 | - zip -r log.zip log/ 39 | - curl --upload-file ./log.zip https://transfer.sh/log.zip 40 | after_success: 41 | - codecov 42 | - coveralls --rcfile=src/Test/coverage.ini 43 | notifications: 44 | email: 45 | recipients: 46 | hello@zeronet.io 47 | on_success: change 48 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.11 2 | 3 | #Base settings 4 | ENV HOME /root 5 | 6 | COPY requirements.txt /root/requirements.txt 7 | 8 | #Install ZeroNet 9 | RUN apk --update --no-cache --no-progress add python3 python3-dev gcc libffi-dev musl-dev make tor openssl \ 10 | && pip3 install -r /root/requirements.txt \ 11 | && apk del python3-dev gcc libffi-dev musl-dev make \ 12 | && echo "ControlPort 9051" >> /etc/tor/torrc \ 13 | && echo "CookieAuthentication 1" >> /etc/tor/torrc 14 | 15 | RUN python3 -V \ 16 | && python3 -m pip list \ 17 | && tor --version \ 18 | && openssl version 19 | 20 | #Add Zeronet source 21 | COPY . /root 22 | VOLUME /root/data 23 | 24 | #Control if Tor proxy is started 25 | ENV ENABLE_TOR false 26 | 27 | WORKDIR /root 28 | 29 | #Set upstart command 30 | CMD (! ${ENABLE_TOR} || tor&) && python3 zeronet.py --ui_ip 0.0.0.0 --fileserver_port 26552 31 | 32 | #Expose ports 33 | EXPOSE 43110 26552 34 | -------------------------------------------------------------------------------- /Dockerfile.arm64v8: -------------------------------------------------------------------------------- 1 | FROM alpine:3.12 2 | 3 | #Base settings 4 | ENV HOME /root 5 | 6 | COPY requirements.txt /root/requirements.txt 7 | 8 | #Install ZeroNet 9 | RUN apk --update --no-cache --no-progress add python3 python3-dev gcc libffi-dev musl-dev make tor openssl \ 10 | && pip3 install -r /root/requirements.txt \ 11 | && apk del python3-dev gcc libffi-dev musl-dev make \ 12 | && echo "ControlPort 9051" >> /etc/tor/torrc \ 13 | && echo "CookieAuthentication 1" >> /etc/tor/torrc 14 | 15 | RUN python3 -V \ 16 | && python3 -m pip list \ 17 | && tor --version \ 18 | && openssl version 19 | 20 | #Add Zeronet source 21 | COPY . /root 22 | VOLUME /root/data 23 | 24 | #Control if Tor proxy is started 25 | ENV ENABLE_TOR false 26 | 27 | WORKDIR /root 28 | 29 | #Set upstart command 30 | CMD (! ${ENABLE_TOR} || tor&) && python3 zeronet.py --ui_ip 0.0.0.0 --fileserver_port 26552 31 | 32 | #Expose ports 33 | EXPOSE 43110 26552 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This program is free software: you can redistribute it and/or modify 2 | it under the terms of the GNU General Public License as published by 3 | the Free Software Foundation, version 3. 4 | 5 | This program is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | 13 | 14 | Additional Conditions : 15 | 16 | Contributing to this repo 17 | This repo is governed by GPLv3, same is located at the root of the ZeroNet git repo, 18 | unless specified separately all code is governed by that license, contributions to this repo 19 | are divided into two key types, key contributions and non-key contributions, key contributions 20 | are which, directly affects the code performance, quality and features of software, 21 | non key contributions include things like translation datasets, image, graphic or video 22 | contributions that does not affect the main usability of software but improves the existing 23 | usability of certain thing or feature, these also include tests written with code, since their 24 | purpose is to check, whether something is working or not as intended. All the non-key contributions 25 | are governed by [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/), unless specified 26 | above, a contribution is ruled by the type of contribution if there is a conflict between two 27 | contributing parties of repo in any case. 28 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | VAGRANTFILE_API_VERSION = "2" 5 | 6 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 7 | 8 | #Set box 9 | config.vm.box = "ubuntu/trusty64" 10 | 11 | #Do not check fo updates 12 | config.vm.box_check_update = false 13 | 14 | #Add private network 15 | config.vm.network "private_network", type: "dhcp" 16 | 17 | #Redirect ports 18 | config.vm.network "forwarded_port", guest: 43110, host: 43110 19 | config.vm.network "forwarded_port", guest: 15441, host: 15441 20 | 21 | #Sync folder using NFS if not windows 22 | config.vm.synced_folder ".", "/vagrant", 23 | :nfs => !Vagrant::Util::Platform.windows? 24 | 25 | #Virtal Box settings 26 | config.vm.provider "virtualbox" do |vb| 27 | # Don't boot with headless mode 28 | #vb.gui = true 29 | 30 | # Set VM settings 31 | vb.customize ["modifyvm", :id, "--memory", "512"] 32 | vb.customize ["modifyvm", :id, "--cpus", 1] 33 | end 34 | 35 | #Update system 36 | config.vm.provision "shell", 37 | inline: "sudo apt-get update -y && sudo apt-get upgrade -y" 38 | 39 | #Install deps 40 | config.vm.provision "shell", 41 | inline: "sudo apt-get install msgpack-python python-gevent python-pip python-dev -y" 42 | config.vm.provision "shell", 43 | inline: "sudo pip install msgpack --upgrade" 44 | 45 | end 46 | -------------------------------------------------------------------------------- /plugins/AnnounceBitTorrent/__init__.py: -------------------------------------------------------------------------------- 1 | from . import AnnounceBitTorrentPlugin -------------------------------------------------------------------------------- /plugins/AnnounceBitTorrent/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AnnounceBitTorrent", 3 | "description": "Discover new peers using BitTorrent trackers.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/AnnounceLocal/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * 2 | 3 | from Config import config 4 | config.broadcast_port = 0 5 | -------------------------------------------------------------------------------- /plugins/AnnounceLocal/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/AnnounceLocal/__init__.py: -------------------------------------------------------------------------------- 1 | from . import AnnounceLocalPlugin -------------------------------------------------------------------------------- /plugins/AnnounceLocal/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AnnounceLocal", 3 | "description": "Discover LAN clients using UDP broadcasting.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/AnnounceShare/Test/TestAnnounceShare.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from AnnounceShare import AnnounceSharePlugin 4 | from Peer import Peer 5 | from Config import config 6 | 7 | 8 | @pytest.mark.usefixtures("resetSettings") 9 | @pytest.mark.usefixtures("resetTempSettings") 10 | class TestAnnounceShare: 11 | def testAnnounceList(self, file_server): 12 | open("%s/trackers.json" % config.data_dir, "w").write("{}") 13 | tracker_storage = AnnounceSharePlugin.tracker_storage 14 | tracker_storage.load() 15 | peer = Peer(file_server.ip, 1544, connection_server=file_server) 16 | assert peer.request("getTrackers")["trackers"] == [] 17 | 18 | tracker_storage.onTrackerFound("zero://%s:15441" % file_server.ip) 19 | assert peer.request("getTrackers")["trackers"] == [] 20 | 21 | # It needs to have at least one successfull announce to be shared to other peers 22 | tracker_storage.onTrackerSuccess("zero://%s:15441" % file_server.ip, 1.0) 23 | assert peer.request("getTrackers")["trackers"] == ["zero://%s:15441" % file_server.ip] 24 | 25 | -------------------------------------------------------------------------------- /plugins/AnnounceShare/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * 2 | 3 | from Config import config 4 | -------------------------------------------------------------------------------- /plugins/AnnounceShare/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/AnnounceShare/__init__.py: -------------------------------------------------------------------------------- 1 | from . import AnnounceSharePlugin 2 | -------------------------------------------------------------------------------- /plugins/AnnounceShare/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AnnounceShare", 3 | "description": "Share possible trackers between clients.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/AnnounceZero/__init__.py: -------------------------------------------------------------------------------- 1 | from . import AnnounceZeroPlugin -------------------------------------------------------------------------------- /plugins/AnnounceZero/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AnnounceZero", 3 | "description": "Announce using ZeroNet protocol.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Benchmark/__init__.py: -------------------------------------------------------------------------------- 1 | from . import BenchmarkPlugin 2 | from . import BenchmarkDb 3 | from . import BenchmarkPack 4 | -------------------------------------------------------------------------------- /plugins/Benchmark/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Benchmark", 3 | "description": "Test and benchmark database and cryptographic functions related to ZeroNet.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Bigfile/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * 2 | -------------------------------------------------------------------------------- /plugins/Bigfile/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/Bigfile/__init__.py: -------------------------------------------------------------------------------- 1 | from . import BigfilePlugin 2 | from .BigfilePiecefield import BigfilePiecefield, BigfilePiecefieldPacked -------------------------------------------------------------------------------- /plugins/Chart/ChartPlugin.py: -------------------------------------------------------------------------------- 1 | import time 2 | import itertools 3 | 4 | import gevent 5 | 6 | from Config import config 7 | from util import helper 8 | from util.Flag import flag 9 | from Plugin import PluginManager 10 | from .ChartDb import ChartDb 11 | from .ChartCollector import ChartCollector 12 | 13 | if "db" not in locals().keys(): # Share on reloads 14 | db = ChartDb() 15 | gevent.spawn_later(10 * 60, db.archive) 16 | helper.timer(60 * 60 * 6, db.archive) 17 | collector = ChartCollector(db) 18 | 19 | @PluginManager.registerTo("SiteManager") 20 | class SiteManagerPlugin(object): 21 | def load(self, *args, **kwargs): 22 | back = super(SiteManagerPlugin, self).load(*args, **kwargs) 23 | collector.setInitialLastValues(self.sites.values()) 24 | return back 25 | 26 | def delete(self, address, *args, **kwargs): 27 | db.deleteSite(address) 28 | return super(SiteManagerPlugin, self).delete(address, *args, **kwargs) 29 | 30 | @PluginManager.registerTo("UiWebsocket") 31 | class UiWebsocketPlugin(object): 32 | @flag.admin 33 | def actionChartDbQuery(self, to, query, params=None): 34 | if config.debug or config.verbose: 35 | s = time.time() 36 | rows = [] 37 | try: 38 | if not query.strip().upper().startswith("SELECT"): 39 | raise Exception("Only SELECT query supported") 40 | res = db.execute(query, params) 41 | except Exception as err: # Response the error to client 42 | self.log.error("ChartDbQuery error: %s" % err) 43 | return {"error": str(err)} 44 | # Convert result to dict 45 | for row in res: 46 | rows.append(dict(row)) 47 | if config.verbose and time.time() - s > 0.1: # Log slow query 48 | self.log.debug("Slow query: %s (%.3fs)" % (query, time.time() - s)) 49 | return rows 50 | 51 | @flag.admin 52 | def actionChartGetPeerLocations(self, to): 53 | peers = {} 54 | for site in self.server.sites.values(): 55 | peers.update(site.peers) 56 | peer_locations = self.getPeerLocations(peers) 57 | return peer_locations 58 | -------------------------------------------------------------------------------- /plugins/Chart/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ChartPlugin -------------------------------------------------------------------------------- /plugins/Chart/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chart", 3 | "description": "Collect and provide stats of client information.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/ContentFilter/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * 2 | -------------------------------------------------------------------------------- /plugins/ContentFilter/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/ContentFilter/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ContentFilterPlugin 2 | -------------------------------------------------------------------------------- /plugins/ContentFilter/languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hide all content from %s?": "%s tartalmaniak elrejtése?", 3 | "Mute": "Elnémítás", 4 | "Unmute %s?": "%s tartalmaniak megjelenítése?", 5 | "Unmute": "Némítás visszavonása" 6 | } 7 | -------------------------------------------------------------------------------- /plugins/ContentFilter/languages/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hide all content from %s?": "%s Vuoi nascondere i contenuti di questo utente ?", 3 | "Mute": "Attiva Silenzia", 4 | "Unmute %s?": "%s Vuoi mostrare i contenuti di questo utente ?", 5 | "Unmute": "Disattiva Silenzia" 6 | } 7 | -------------------------------------------------------------------------------- /plugins/ContentFilter/languages/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hide all content from %s?": "%s のコンテンツをすべて隠しますか?", 3 | "Mute": "ミュート", 4 | "Unmute %s?": "%s のミュートを解除しますか?", 5 | "Unmute": "ミュート解除" 6 | } 7 | -------------------------------------------------------------------------------- /plugins/ContentFilter/languages/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hide all content from %s?": "%s Ocultar todo o conteúdo de ?", 3 | "Mute": "Ativar o Silêncio", 4 | "Unmute %s?": "%s Você quer mostrar o conteúdo deste usuário ?", 5 | "Unmute": "Desligar o silêncio" 6 | } 7 | -------------------------------------------------------------------------------- /plugins/ContentFilter/languages/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hide all content from %s?": "屏蔽 %s 的所有內容?", 3 | "Mute": "屏蔽", 4 | "Unmute %s?": "對 %s 解除屏蔽?", 5 | "Unmute": "解除屏蔽" 6 | } 7 | -------------------------------------------------------------------------------- /plugins/ContentFilter/languages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hide all content from %s?": "屏蔽 %s 的所有内容?", 3 | "Mute": "屏蔽", 4 | "Unmute %s?": "对 %s 解除屏蔽?", 5 | "Unmute": "解除屏蔽" 6 | } 7 | -------------------------------------------------------------------------------- /plugins/ContentFilter/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ContentFilter", 3 | "description": "Manage site and user block list.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Cors/__init__.py: -------------------------------------------------------------------------------- 1 | from . import CorsPlugin -------------------------------------------------------------------------------- /plugins/Cors/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cors", 3 | "description": "Cross site resource read.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/CryptMessage/CryptMessage.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import base64 3 | import struct 4 | from lib import sslcrypto 5 | from Crypt import Crypt 6 | 7 | 8 | curve = sslcrypto.ecc.get_curve("secp256k1") 9 | 10 | 11 | def eciesEncrypt(data, pubkey, ciphername="aes-256-cbc"): 12 | ciphertext, key_e = curve.encrypt( 13 | data, 14 | base64.b64decode(pubkey), 15 | algo=ciphername, 16 | derivation="sha512", 17 | return_aes_key=True 18 | ) 19 | return key_e, ciphertext 20 | 21 | 22 | @Crypt.thread_pool_crypt.wrap 23 | def eciesDecryptMulti(encrypted_datas, privatekey): 24 | texts = [] # Decoded texts 25 | for encrypted_data in encrypted_datas: 26 | try: 27 | text = eciesDecrypt(encrypted_data, privatekey).decode("utf8") 28 | texts.append(text) 29 | except Exception: 30 | texts.append(None) 31 | return texts 32 | 33 | 34 | def eciesDecrypt(ciphertext, privatekey): 35 | return curve.decrypt(base64.b64decode(ciphertext), curve.wif_to_private(privatekey.encode()), derivation="sha512") 36 | 37 | 38 | def decodePubkey(pubkey): 39 | i = 0 40 | curve = struct.unpack('!H', pubkey[i:i + 2])[0] 41 | i += 2 42 | tmplen = struct.unpack('!H', pubkey[i:i + 2])[0] 43 | i += 2 44 | pubkey_x = pubkey[i:i + tmplen] 45 | i += tmplen 46 | tmplen = struct.unpack('!H', pubkey[i:i + 2])[0] 47 | i += 2 48 | pubkey_y = pubkey[i:i + tmplen] 49 | i += tmplen 50 | return curve, pubkey_x, pubkey_y, i 51 | 52 | 53 | def split(encrypted): 54 | iv = encrypted[0:16] 55 | curve, pubkey_x, pubkey_y, i = decodePubkey(encrypted[16:]) 56 | ciphertext = encrypted[16 + i:-32] 57 | 58 | return iv, ciphertext 59 | -------------------------------------------------------------------------------- /plugins/CryptMessage/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * -------------------------------------------------------------------------------- /plugins/CryptMessage/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/CryptMessage/__init__.py: -------------------------------------------------------------------------------- 1 | from . import CryptMessagePlugin -------------------------------------------------------------------------------- /plugins/CryptMessage/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CryptMessage", 3 | "description": "Cryptographic functions of ECIES and AES data encryption/decryption.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/FilePack/__init__.py: -------------------------------------------------------------------------------- 1 | from . import FilePackPlugin -------------------------------------------------------------------------------- /plugins/FilePack/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FilePack", 3 | "description": "Transparent web access for Zip and Tar.gz files.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/MergerSite/__init__.py: -------------------------------------------------------------------------------- 1 | from . import MergerSitePlugin -------------------------------------------------------------------------------- /plugins/MergerSite/languages/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "¿Agregar %s nuevo sitio?", 3 | "Added %s new site": "Sitio %s agregado", 4 | "Site deleted: %s": "Sitio removido: %s" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "Ajouter le site %s ?", 3 | "Added %s new site": "Site %s ajouté", 4 | "Site deleted: %s": "Site %s supprimé" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "Új oldal hozzáadása: %s?", 3 | "Added %s new site": "Új oldal hozzáadva: %s", 4 | "Site deleted: %s": "Oldal törölve: %s" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "Aggiungere %s nuovo sito ?", 3 | "Added %s new site": "Sito %s aggiunto", 4 | "Site deleted: %s": "Sito %s eliminato" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "サイト: %s を追加しますか?", 3 | "Added %s new site": "サイト: %s を追加しました", 4 | "Site deleted: %s": "サイト: %s を削除しました" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "Adicionar %s novo site?", 3 | "Added %s new site": "Site %s adicionado", 4 | "Site deleted: %s": "Site removido: %s" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "%s sitesi eklensin mi?", 3 | "Added %s new site": "%s sitesi eklendi", 4 | "Site deleted: %s": "%s sitesi silindi" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "添加新網站: %s?", 3 | "Added %s new site": "已添加到新網站:%s", 4 | "Site deleted: %s": "網站已刪除:%s" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/MergerSite/languages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Add %s new site?": "添加新站点: %s?", 3 | "Added %s new site": "已添加到新站点:%s", 4 | "Site deleted: %s": "站点已删除:%s" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/Newsfeed/__init__.py: -------------------------------------------------------------------------------- 1 | from . import NewsfeedPlugin -------------------------------------------------------------------------------- /plugins/OptionalManager/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * -------------------------------------------------------------------------------- /plugins/OptionalManager/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/OptionalManager/__init__.py: -------------------------------------------------------------------------------- 1 | from . import OptionalManagerPlugin 2 | from . import UiWebsocketPlugin 3 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "Archivos %s fijados", 3 | "Removed pin from %s files": "Archivos %s que no estan fijados", 4 | "You started to help distribute %s.
Directory: %s": "Tu empezaste a ayudar a distribuir %s.
Directorio: %s", 5 | "Help distribute all new optional files on site %s": "Ayude a distribuir todos los archivos opcionales en el sitio %s", 6 | "Yes, I want to help!": "¡Si, yo quiero ayudar!" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "Fichiers %s épinglés", 3 | "Removed pin from %s files": "Fichiers %s ne sont plus épinglés", 4 | "You started to help distribute %s.
Directory: %s": "Vous avez commencé à aider à distribuer %s.
Dossier : %s", 5 | "Help distribute all new optional files on site %s": "Aider à distribuer tous les fichiers optionnels du site %s", 6 | "Yes, I want to help!": "Oui, je veux aider !" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "%s fájl rögzítve", 3 | "Removed pin from %s files": "%s fájl rögzítés eltávolítva", 4 | "You started to help distribute %s.
Directory: %s": "Új segítség a terjesztésben: %s.
Könyvtár: %s", 5 | "Help distribute all new optional files on site %s": "Segítség az összes új opcionális fájl terjesztésében az %s oldalon", 6 | "Yes, I want to help!": "Igen, segíteni akarok!" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "%s 件のファイルを固定", 3 | "Removed pin from %s files": "%s 件のファイルの固定を解除", 4 | "You started to help distribute %s.
Directory: %s": "あなたはサイト: %s の配布の援助を開始しました。
ディレクトリ: %s", 5 | "Help distribute all new optional files on site %s": "サイト: %s のすべての新しいオプションファイルの配布を援助しますか?", 6 | "Yes, I want to help!": "はい、やります!" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "Arquivos %s fixados", 3 | "Removed pin from %s files": "Arquivos %s não estão fixados", 4 | "You started to help distribute %s.
Directory: %s": "Você começou a ajudar a distribuir %s.
Pasta: %s", 5 | "Help distribute all new optional files on site %s": "Ajude a distribuir todos os novos arquivos opcionais no site %s", 6 | "Yes, I want to help!": "Sim, eu quero ajudar!" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "已固定 %s 個檔", 3 | "Removed pin from %s files": "已解除固定 %s 個檔", 4 | "You started to help distribute %s.
Directory: %s": "你已經開始幫助分發 %s
目錄:%s", 5 | "Help distribute all new optional files on site %s": "你想要幫助分發 %s 網站的所有檔嗎?", 6 | "Yes, I want to help!": "是,我想要幫助!" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/OptionalManager/languages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Pinned %s files": "已固定 %s 个文件", 3 | "Removed pin from %s files": "已解除固定 %s 个文件", 4 | "You started to help distribute %s.
Directory: %s": "您已经开始帮助分发 %s
目录:%s", 5 | "Help distribute all new optional files on site %s": "您想要帮助分发 %s 站点的所有文件吗?", 6 | "Yes, I want to help!": "是,我想要帮助!" 7 | } 8 | -------------------------------------------------------------------------------- /plugins/PeerDb/__init__.py: -------------------------------------------------------------------------------- 1 | from . import PeerDbPlugin 2 | 3 | -------------------------------------------------------------------------------- /plugins/PeerDb/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PeerDb", 3 | "description": "Save/restore peer list on client restart.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Sidebar/ZipStream.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import zipfile 4 | 5 | class ZipStream(object): 6 | def __init__(self, dir_path): 7 | self.dir_path = dir_path 8 | self.pos = 0 9 | self.buff_pos = 0 10 | self.zf = zipfile.ZipFile(self, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) 11 | self.buff = io.BytesIO() 12 | self.file_list = self.getFileList() 13 | 14 | def getFileList(self): 15 | for root, dirs, files in os.walk(self.dir_path): 16 | for file in files: 17 | file_path = root + "/" + file 18 | relative_path = os.path.join(os.path.relpath(root, self.dir_path), file) 19 | yield file_path, relative_path 20 | self.zf.close() 21 | 22 | def read(self, size=60 * 1024): 23 | for file_path, relative_path in self.file_list: 24 | self.zf.write(file_path, relative_path) 25 | if self.buff.tell() >= size: 26 | break 27 | self.buff.seek(0) 28 | back = self.buff.read() 29 | self.buff.truncate(0) 30 | self.buff.seek(0) 31 | self.buff_pos += len(back) 32 | return back 33 | 34 | def write(self, data): 35 | self.pos += len(data) 36 | self.buff.write(data) 37 | 38 | def tell(self): 39 | return self.pos 40 | 41 | def seek(self, pos, whence=0): 42 | if pos >= self.buff_pos: 43 | self.buff.seek(pos - self.buff_pos, whence) 44 | self.pos = pos 45 | 46 | def flush(self): 47 | pass 48 | 49 | 50 | if __name__ == "__main__": 51 | zs = ZipStream(".") 52 | out = open("out.zip", "wb") 53 | while 1: 54 | data = zs.read() 55 | print("Write %s" % len(data)) 56 | if not data: 57 | break 58 | out.write(data) 59 | out.close() 60 | -------------------------------------------------------------------------------- /plugins/Sidebar/__init__.py: -------------------------------------------------------------------------------- 1 | from . import SidebarPlugin 2 | from . import ConsolePlugin -------------------------------------------------------------------------------- /plugins/Sidebar/media/Class.coffee: -------------------------------------------------------------------------------- 1 | class Class 2 | trace: true 3 | 4 | log: (args...) -> 5 | return unless @trace 6 | return if typeof console is 'undefined' 7 | args.unshift("[#{@.constructor.name}]") 8 | console.log(args...) 9 | @ 10 | 11 | logStart: (name, args...) -> 12 | return unless @trace 13 | @logtimers or= {} 14 | @logtimers[name] = +(new Date) 15 | @log "#{name}", args..., "(started)" if args.length > 0 16 | @ 17 | 18 | logEnd: (name, args...) -> 19 | ms = +(new Date)-@logtimers[name] 20 | @log "#{name}", args..., "(Done in #{ms}ms)" 21 | @ 22 | 23 | window.Class = Class -------------------------------------------------------------------------------- /plugins/Sidebar/media/Menu.coffee: -------------------------------------------------------------------------------- 1 | class Menu 2 | constructor: (@button) -> 3 | @elem = $(".menu.template").clone().removeClass("template") 4 | @elem.appendTo("body") 5 | @items = [] 6 | 7 | show: -> 8 | if window.visible_menu and window.visible_menu.button[0] == @button[0] # Same menu visible then hide it 9 | window.visible_menu.hide() 10 | @hide() 11 | else 12 | button_pos = @button.offset() 13 | left = button_pos.left 14 | @elem.css({"top": button_pos.top+@button.outerHeight(), "left": left}) 15 | @button.addClass("menu-active") 16 | @elem.addClass("visible") 17 | if @elem.position().left + @elem.width() + 20 > window.innerWidth 18 | @elem.css("left", window.innerWidth - @elem.width() - 20) 19 | if window.visible_menu then window.visible_menu.hide() 20 | window.visible_menu = @ 21 | 22 | 23 | hide: -> 24 | @elem.removeClass("visible") 25 | @button.removeClass("menu-active") 26 | window.visible_menu = null 27 | 28 | 29 | addItem: (title, cb) -> 30 | item = $(".menu-item.template", @elem).clone().removeClass("template") 31 | item.html(title) 32 | item.on "click", => 33 | if not cb(item) 34 | @hide() 35 | return false 36 | item.appendTo(@elem) 37 | @items.push item 38 | return item 39 | 40 | 41 | log: (args...) -> 42 | console.log "[Menu]", args... 43 | 44 | window.Menu = Menu 45 | 46 | # Hide menu on outside click 47 | $("body").on "click", (e) -> 48 | if window.visible_menu and e.target != window.visible_menu.button[0] and $(e.target).parent()[0] != window.visible_menu.elem[0] 49 | window.visible_menu.hide() 50 | -------------------------------------------------------------------------------- /plugins/Sidebar/media/Menu.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | background-color: white; padding: 10px 0px; position: absolute; top: 0px; left: 0px; max-height: 0px; overflow: hidden; transform: translate(0px, -30px); pointer-events: none; 3 | box-shadow: 0px 2px 8px rgba(0,0,0,0.3); border-radius: 2px; opacity: 0; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; 4 | } 5 | .menu.visible { opacity: 1; max-height: 350px; transform: translate(0px, 0px); transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; pointer-events: all } 6 | 7 | .menu-item { display: block; text-decoration: none; color: black; padding: 6px 24px; transition: all 0.2s; border-bottom: none; font-weight: normal; padding-left: 30px; } 8 | .menu-item-separator { margin-top: 5px; border-top: 1px solid #eee } 9 | 10 | .menu-item:hover { background-color: #F6F6F6; transition: none; color: inherit; border: none } 11 | .menu-item:active, .menu-item:focus { background-color: #AF3BFF; color: white; transition: none } 12 | .menu-item.selected:before { 13 | content: "L"; display: inline-block; transform: rotateZ(45deg) scaleX(-1); 14 | font-weight: bold; position: absolute; margin-left: -17px; font-size: 12px; margin-top: 2px; 15 | } 16 | 17 | @media only screen and (max-width: 800px) { 18 | .menu, .menu.visible { position: absolute; left: unset !important; right: 20px; } 19 | } -------------------------------------------------------------------------------- /plugins/Sidebar/media/Prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::startsWith = (s) -> @[...s.length] is s 2 | String::endsWith = (s) -> s is '' or @[-s.length..] is s 3 | String::capitalize = -> if @.length then @[0].toUpperCase() + @.slice(1) else "" 4 | String::repeat = (count) -> new Array( count + 1 ).join(@) 5 | 6 | window.isEmpty = (obj) -> 7 | for key of obj 8 | return false 9 | return true 10 | -------------------------------------------------------------------------------- /plugins/Sidebar/media/RateLimit.coffee: -------------------------------------------------------------------------------- 1 | limits = {} 2 | call_after_interval = {} 3 | window.RateLimit = (interval, fn) -> 4 | if not limits[fn] 5 | call_after_interval[fn] = false 6 | fn() # First call is not delayed 7 | limits[fn] = setTimeout (-> 8 | if call_after_interval[fn] 9 | fn() 10 | delete limits[fn] 11 | delete call_after_interval[fn] 12 | ), interval 13 | else # Called within iterval, delay the call 14 | call_after_interval[fn] = true 15 | -------------------------------------------------------------------------------- /plugins/Sidebar/media/Scrollbable.css: -------------------------------------------------------------------------------- 1 | .scrollable { 2 | overflow: hidden; 3 | } 4 | 5 | .scrollable.showScroll::after { 6 | position: absolute; 7 | content: ''; 8 | top: 5%; 9 | right: 7px; 10 | height: 90%; 11 | width: 3px; 12 | background: rgba(224, 224, 255, .3); 13 | } 14 | 15 | .scrollable .content-wrapper { 16 | width: 100%; 17 | height: 100%; 18 | padding-right: 50%; 19 | overflow-y: scroll; 20 | } 21 | .scroller { 22 | margin-top: 5px; 23 | z-index: 5; 24 | cursor: pointer; 25 | position: absolute; 26 | width: 7px; 27 | border-radius: 5px; 28 | background: #3A3A3A; 29 | top: 0px; 30 | left: 395px; 31 | -webkit-transition: top .08s; 32 | -moz-transition: top .08s; 33 | -ms-transition: top .08s; 34 | -o-transition: top .08s; 35 | transition: top .08s; 36 | } 37 | .scroller { 38 | -webkit-touch-callout: none; 39 | -webkit-user-select: none; 40 | -khtml-user-select: none; 41 | -moz-user-select: none; 42 | -ms-user-select: none; 43 | user-select: none; 44 | } 45 | -------------------------------------------------------------------------------- /plugins/Sidebar/media_globe/Detector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | * @author mr.doob / http://mrdoob.com/ 4 | */ 5 | 6 | Detector = { 7 | 8 | canvas : !! window.CanvasRenderingContext2D, 9 | webgl : ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(), 10 | workers : !! window.Worker, 11 | fileapi : window.File && window.FileReader && window.FileList && window.Blob, 12 | 13 | getWebGLErrorMessage : function () { 14 | 15 | var domElement = document.createElement( 'div' ); 16 | 17 | domElement.style.fontFamily = 'monospace'; 18 | domElement.style.fontSize = '13px'; 19 | domElement.style.textAlign = 'center'; 20 | domElement.style.background = '#eee'; 21 | domElement.style.color = '#000'; 22 | domElement.style.padding = '1em'; 23 | domElement.style.width = '475px'; 24 | domElement.style.margin = '5em auto 0'; 25 | 26 | if ( ! this.webgl ) { 27 | 28 | domElement.innerHTML = window.WebGLRenderingContext ? [ 29 | 'Sorry, your graphics card doesn\'t support WebGL' 30 | ].join( '\n' ) : [ 31 | 'Sorry, your browser doesn\'t support WebGL
', 32 | 'Please try with', 33 | 'Chrome, ', 34 | 'Firefox 4 or', 35 | 'Webkit Nightly (Mac)' 36 | ].join( '\n' ); 37 | 38 | } 39 | 40 | return domElement; 41 | 42 | }, 43 | 44 | addGetWebGLMessage : function ( parameters ) { 45 | 46 | var parent, id, domElement; 47 | 48 | parameters = parameters || {}; 49 | 50 | parent = parameters.parent !== undefined ? parameters.parent : document.body; 51 | id = parameters.id !== undefined ? parameters.id : 'oldie'; 52 | 53 | domElement = Detector.getWebGLErrorMessage(); 54 | domElement.id = id; 55 | 56 | parent.appendChild( domElement ); 57 | 58 | } 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /plugins/Sidebar/media_globe/world.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/Sidebar/media_globe/world.jpg -------------------------------------------------------------------------------- /plugins/Sidebar/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sidebar", 3 | "description": "Access site management sidebar and console by dragging top-right 0 button to left or down.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Stats/__init__.py: -------------------------------------------------------------------------------- 1 | from . import StatsPlugin -------------------------------------------------------------------------------- /plugins/Stats/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Stats", 3 | "description": "/Stats and /Benchmark pages.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/TranslateSite/__init__.py: -------------------------------------------------------------------------------- 1 | from . import TranslateSitePlugin 2 | -------------------------------------------------------------------------------- /plugins/TranslateSite/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TranslateSite", 3 | "description": "Transparent support translation of site javascript and html files.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Trayicon/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if sys.platform == 'win32': 4 | from . import TrayiconPlugin -------------------------------------------------------------------------------- /plugins/Trayicon/languages/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Reportar fallo/sugerir característica", 6 | "!Open ZeroNet": "!Abrir ZeroNet", 7 | "Quit": "Sair", 8 | "(active)": "(activo)", 9 | "(passive)": "(pasivo)", 10 | "Connections: %s": "Conecciones: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Recibido: %.2f MB | Enviado: %.2f MB", 12 | "Show console window": "Mostrar consola", 13 | "Start ZeroNet when Windows starts": "Iniciar Zeronet cuando inicie Windows" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Rapport d'erreur/Demander une fonctionnalité", 6 | "!Open ZeroNet": "!Ouvrir ZeroNet", 7 | "Quit": "Quitter", 8 | "(active)": "(actif)", 9 | "(passive)": "(passif)", 10 | "Connections: %s": "Connexions: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Reçu: %.2f MB | Envoyé: %.2f MB", 12 | "Show console window": "Afficher la console", 13 | "Start ZeroNet when Windows starts": "Lancer ZeroNet au démarrage de Windows" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Hiba bejelentés/ötletek", 6 | "!Open ZeroNet": "!ZeroNet megnyitása", 7 | "Quit": "Kilépés", 8 | "(active)": "(aktív)", 9 | "(passive)": "(passive)", 10 | "Connections: %s": "Kapcsolatok: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Fogadott: %.2f MB | Küldött: %.2f MB", 12 | "Show console window": "Parancssor mutatása", 13 | "Start ZeroNet when Windows starts": "ZeroNet indítása a Windows-al együtt" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Segnala bug/richiesta di una funzione", 6 | "!Open ZeroNet": "!Apri ZeroNet", 7 | "Quit": "Chiudi", 8 | "(active)": "(attivo)", 9 | "(passive)": "(passivo)", 10 | "Connections: %s": "Connessioni: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Ricevuto: %.2f MB | Inviato: %.2f MB", 12 | "Show console window": "Mostra finestra console", 13 | "Start ZeroNet when Windows starts": "Avvia ZeroNet all'avvio di Windows" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "バグ報告/要望", 6 | "!Open ZeroNet": "!ZeroNetをブラウザで開く", 7 | "Quit": "閉じる", 8 | "(active)": "(アクティブ)", 9 | "(passive)": "(パッシブ)", 10 | "Connections: %s": "接続数: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "受信: %.2f MB | 送信: %.2f MB", 12 | "Show console window": "コンソールを表示", 13 | "Start ZeroNet when Windows starts": "Windows起動時にZeroNetも起動する" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Zgłoś błąd / propozycję", 6 | "!Open ZeroNet": "!Otwórz ZeroNet", 7 | "Quit": "Zamknij", 8 | "(active)": "(aktywny)", 9 | "(passive)": "(pasywny)", 10 | "Connections: %s": "Połączenia: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Odebrano: %.2f MB | Wysłano: %.2f MB", 12 | "Show console window": "Pokaż okno konsoli", 13 | "Start ZeroNet when Windows starts": "Uruchom ZeroNet podczas startu Windows" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Reportar bug/sugerir recurso", 6 | "!Open ZeroNet": "!Abrir ZeroNet", 7 | "Quit": "Sair", 8 | "(active)": "(ativo)", 9 | "(passive)": "(passivo)", 10 | "Connections: %s": "Conexões: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Recebido: %.2f MB | Enviado: %.2f MB", 12 | "Show console window": "Mostrar console", 13 | "Start ZeroNet when Windows starts": "Iniciar o ZeroNet quando o Windows for iniciado" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "Hata bildir/geliştirme taleb et", 6 | "!Open ZeroNet": "!ZeroNet'i Aç", 7 | "Quit": "Kapat", 8 | "(active)": "(aktif)", 9 | "(passive)": "(pasif)", 10 | "Connections: %s": "Bağlantı sayısı: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "Gelen: %.2f MB | Gönderilen: %.2f MB", 12 | "Show console window": "Konsolu aç", 13 | "Start ZeroNet when Windows starts": "ZeroNet'i açılışta otomatik başlat" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "回饋问题/請求功能", 6 | "!Open ZeroNet": "!開啟 ZeroNet", 7 | "Quit": "退出", 8 | "(active)": "(主動模式)", 9 | "(passive)": "(被動模式)", 10 | "Connections: %s": "連線數: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "已收到: %.2f MB | 已傳送: %.2f MB", 12 | "Show console window": "顯示控制臺窗體", 13 | "Start ZeroNet when Windows starts": "在 Windows 啟動時執行 ZeroNet" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/languages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet Twitter": "ZeroNet Twitter", 3 | "ZeroNet Reddit": "ZeroNet Reddit", 4 | "ZeroNet Github": "ZeroNet Github", 5 | "Report bug/request feature": "反馈问题/请求功能", 6 | "!Open ZeroNet": "!打开 ZeroNet", 7 | "Quit": "退出", 8 | "(active)": "(主动模式)", 9 | "(passive)": "(被动模式)", 10 | "Connections: %s": "连接数: %s", 11 | "Received: %.2f MB | Sent: %.2f MB": "已接收: %.2f MB | 已发送: %.2f MB", 12 | "Show console window": "显示控制台窗口", 13 | "Start ZeroNet when Windows starts": "在 Windows 启动时运行 ZeroNet" 14 | } 15 | -------------------------------------------------------------------------------- /plugins/Trayicon/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/Trayicon/lib/__init__.py -------------------------------------------------------------------------------- /plugins/Trayicon/lib/winfolders.py: -------------------------------------------------------------------------------- 1 | ''' Get windows special folders without pythonwin 2 | Example: 3 | import specialfolders 4 | start_programs = specialfolders.get(specialfolders.PROGRAMS) 5 | 6 | Code is public domain, do with it what you will. 7 | 8 | Luke Pinner - Environment.gov.au, 2010 February 10 9 | ''' 10 | 11 | #Imports use _syntax to mask them from autocomplete IDE's 12 | import ctypes as _ctypes 13 | from ctypes import create_unicode_buffer as _cub 14 | from ctypes.wintypes import HWND as _HWND, HANDLE as _HANDLE,DWORD as _DWORD,LPCWSTR as _LPCWSTR,MAX_PATH as _MAX_PATH 15 | _SHGetFolderPath = _ctypes.windll.shell32.SHGetFolderPathW 16 | 17 | #public special folder constants 18 | DESKTOP= 0 19 | PROGRAMS= 2 20 | MYDOCUMENTS= 5 21 | FAVORITES= 6 22 | STARTUP= 7 23 | RECENT= 8 24 | SENDTO= 9 25 | STARTMENU= 11 26 | MYMUSIC= 13 27 | MYVIDEOS= 14 28 | NETHOOD= 19 29 | FONTS= 20 30 | TEMPLATES= 21 31 | ALLUSERSSTARTMENU= 22 32 | ALLUSERSPROGRAMS= 23 33 | ALLUSERSSTARTUP= 24 34 | ALLUSERSDESKTOP= 25 35 | APPLICATIONDATA= 26 36 | PRINTHOOD= 27 37 | LOCALSETTINGSAPPLICATIONDATA= 28 38 | ALLUSERSFAVORITES= 31 39 | LOCALSETTINGSTEMPORARYINTERNETFILES=32 40 | COOKIES= 33 41 | LOCALSETTINGSHISTORY= 34 42 | ALLUSERSAPPLICATIONDATA= 35 43 | 44 | def get(intFolder): 45 | _SHGetFolderPath.argtypes = [_HWND, _ctypes.c_int, _HANDLE, _DWORD, _LPCWSTR] 46 | auPathBuffer = _cub(_MAX_PATH) 47 | exit_code=_SHGetFolderPath(0, intFolder, 0, 0, auPathBuffer) 48 | return auPathBuffer.value 49 | 50 | 51 | if __name__ == "__main__": 52 | import os 53 | print(get(STARTUP)) 54 | open(get(STARTUP)+"\\zeronet.cmd", "w").write("cd /D %s\r\nzeronet.py" % os.getcwd()) -------------------------------------------------------------------------------- /plugins/Trayicon/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Trayicon", 3 | "description": "Icon for system tray. (Windows only)", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/Trayicon/trayicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/Trayicon/trayicon.ico -------------------------------------------------------------------------------- /plugins/UiConfig/__init__.py: -------------------------------------------------------------------------------- 1 | from . import UiConfigPlugin 2 | -------------------------------------------------------------------------------- /plugins/UiConfig/languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "ZeroNet config": "ZeroNet beállítások", 3 | "Web Interface": "Web felület", 4 | "Open web browser on ZeroNet startup": "Bögésző megnyitása a ZeroNet indulásakor", 5 | 6 | "Network": "Hálózat", 7 | "File server port": "FIle szerver port", 8 | "Other peers will use this port to reach your served sites. (default: 15441)": "Más kliensek ezen a porton tudják elérni a kiszolgált oldalaidat (alapbeállítás: 15441)", 9 | 10 | "Disable: Don't connect to peers on Tor network": "Kikapcsolás: Ne csatlakozzon a Tor hálózatra", 11 | "Enable: Only use Tor for Tor network peers": "Bekapcsolás: Csak a Tor kliensekhez használja a Tor hálózatot", 12 | "Always: Use Tor for every connections to hide your IP address (slower)": "Mindig: Minden kapcsolatot a Tor hálózaton keresztül hozza létre az IP cím elrejtéséhez (lassabb)", 13 | 14 | "Disable": "Kikapcsolás", 15 | "Enable": "Bekapcsolás", 16 | "Always": "Mindig", 17 | 18 | "Use Tor bridges": "Tor bridge-ek használata", 19 | "Use obfuscated bridge relays to avoid network level Tor block (even slower)": "Tor elrejtő bridge-ek használata a hálózat szintű Tor tiltás megkerüléséhez (még lassabb)", 20 | 21 | "Discover new peers using these adresses": "Új kapcsolat felfedezése ezen címek használatával", 22 | 23 | "Trackers files": "Tracker file-ok", 24 | "Load additional list of torrent trackers dynamically, from a file": "További trackerek felfedezése dinamikusan, ezen file használatával", 25 | "Eg.: data/trackers.json": "Pl.: data/trackers.json", 26 | 27 | "Proxy for tracker connections": "Proxy tracker kapcsolatohoz", 28 | 29 | " configuration item value changed": " beállítás megváltoztatva", 30 | "Save settings": "Beállítások mentése", 31 | "Some changed settings requires restart": "A beállítások érvényesítéséhez a kliens újraindítása szükséges", 32 | "Restart ZeroNet client": "ZeroNet kliens újraindítása" 33 | } -------------------------------------------------------------------------------- /plugins/UiConfig/media/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Settings - ZeroNet 6 | 7 | 8 | 9 | 10 | 11 | 12 |

ZeroNet config

13 | 14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugins/UiConfig/media/css/button.css: -------------------------------------------------------------------------------- 1 | /* Button */ 2 | .button { 3 | background-color: #FFDC00; color: black; padding: 10px 20px; display: inline-block; background-position: left center; 4 | border-radius: 2px; border-bottom: 2px solid #E8BE29; transition: all 0.5s ease-out; text-decoration: none; 5 | } 6 | .button:hover { border-color: white; border-bottom: 2px solid #BD960C; transition: none ; background-color: #FDEB07 } 7 | .button:active { position: relative; top: 1px } 8 | .button.loading { 9 | color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center; 10 | transition: all 0.5s ease-out ; pointer-events: none; border-bottom: 2px solid #666 11 | } 12 | .button.disabled { color: #DDD; background-color: #999; pointer-events: none; border-bottom: 2px solid #666 } 13 | -------------------------------------------------------------------------------- /plugins/UiConfig/media/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/UiConfig/media/img/loading.gif -------------------------------------------------------------------------------- /plugins/UiConfig/media/js/lib/Class.coffee: -------------------------------------------------------------------------------- 1 | class Class 2 | trace: true 3 | 4 | log: (args...) -> 5 | return unless @trace 6 | return if typeof console is 'undefined' 7 | args.unshift("[#{@.constructor.name}]") 8 | console.log(args...) 9 | @ 10 | 11 | logStart: (name, args...) -> 12 | return unless @trace 13 | @logtimers or= {} 14 | @logtimers[name] = +(new Date) 15 | @log "#{name}", args..., "(started)" if args.length > 0 16 | @ 17 | 18 | logEnd: (name, args...) -> 19 | ms = +(new Date)-@logtimers[name] 20 | @log "#{name}", args..., "(Done in #{ms}ms)" 21 | @ 22 | 23 | window.Class = Class -------------------------------------------------------------------------------- /plugins/UiConfig/media/js/lib/Promise.coffee: -------------------------------------------------------------------------------- 1 | # From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html 2 | 3 | class Promise 4 | @when: (tasks...) -> 5 | num_uncompleted = tasks.length 6 | args = new Array(num_uncompleted) 7 | promise = new Promise() 8 | 9 | for task, task_id in tasks 10 | ((task_id) -> 11 | task.then(() -> 12 | args[task_id] = Array.prototype.slice.call(arguments) 13 | num_uncompleted-- 14 | promise.complete.apply(promise, args) if num_uncompleted == 0 15 | ) 16 | )(task_id) 17 | 18 | return promise 19 | 20 | constructor: -> 21 | @resolved = false 22 | @end_promise = null 23 | @result = null 24 | @callbacks = [] 25 | 26 | resolve: -> 27 | if @resolved 28 | return false 29 | @resolved = true 30 | @data = arguments 31 | if not arguments.length 32 | @data = [true] 33 | @result = @data[0] 34 | for callback in @callbacks 35 | back = callback.apply callback, @data 36 | if @end_promise 37 | @end_promise.resolve(back) 38 | 39 | fail: -> 40 | @resolve(false) 41 | 42 | then: (callback) -> 43 | if @resolved == true 44 | callback.apply callback, @data 45 | return 46 | 47 | @callbacks.push callback 48 | 49 | @end_promise = new Promise() 50 | 51 | window.Promise = Promise 52 | 53 | ### 54 | s = Date.now() 55 | log = (text) -> 56 | console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ") 57 | 58 | log "Started" 59 | 60 | cmd = (query) -> 61 | p = new Promise() 62 | setTimeout ( -> 63 | p.resolve query+" Result" 64 | ), 100 65 | return p 66 | 67 | back = cmd("SELECT * FROM message").then (res) -> 68 | log res 69 | return "Return from query" 70 | .then (res) -> 71 | log "Back then", res 72 | 73 | log "Query started", back 74 | ### -------------------------------------------------------------------------------- /plugins/UiConfig/media/js/lib/Prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::startsWith = (s) -> @[...s.length] is s 2 | String::endsWith = (s) -> s is '' or @[-s.length..] is s 3 | String::repeat = (count) -> new Array( count + 1 ).join(@) 4 | 5 | window.isEmpty = (obj) -> 6 | for key of obj 7 | return false 8 | return true 9 | -------------------------------------------------------------------------------- /plugins/UiConfig/media/js/utils/Dollar.coffee: -------------------------------------------------------------------------------- 1 | window.$ = (selector) -> 2 | if selector.startsWith("#") 3 | return document.getElementById(selector.replace("#", "")) 4 | -------------------------------------------------------------------------------- /plugins/UiConfig/media/js/utils/ZeroFrame.coffee: -------------------------------------------------------------------------------- 1 | class ZeroFrame extends Class 2 | constructor: (url) -> 3 | @url = url 4 | @waiting_cb = {} 5 | @wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1") 6 | @connect() 7 | @next_message_id = 1 8 | @history_state = {} 9 | @init() 10 | 11 | 12 | init: -> 13 | @ 14 | 15 | 16 | connect: -> 17 | @target = window.parent 18 | window.addEventListener("message", @onMessage, false) 19 | @cmd("innerReady") 20 | 21 | # Save scrollTop 22 | window.addEventListener "beforeunload", (e) => 23 | @log "save scrollTop", window.pageYOffset 24 | @history_state["scrollTop"] = window.pageYOffset 25 | @cmd "wrapperReplaceState", [@history_state, null] 26 | 27 | # Restore scrollTop 28 | @cmd "wrapperGetState", [], (state) => 29 | @history_state = state if state? 30 | @log "restore scrollTop", state, window.pageYOffset 31 | if window.pageYOffset == 0 and state 32 | window.scroll(window.pageXOffset, state.scrollTop) 33 | 34 | 35 | onMessage: (e) => 36 | message = e.data 37 | cmd = message.cmd 38 | if cmd == "response" 39 | if @waiting_cb[message.to]? 40 | @waiting_cb[message.to](message.result) 41 | else 42 | @log "Websocket callback not found:", message 43 | else if cmd == "wrapperReady" # Wrapper inited later 44 | @cmd("innerReady") 45 | else if cmd == "ping" 46 | @response message.id, "pong" 47 | else if cmd == "wrapperOpenedWebsocket" 48 | @onOpenWebsocket() 49 | else if cmd == "wrapperClosedWebsocket" 50 | @onCloseWebsocket() 51 | else 52 | @onRequest cmd, message.params 53 | 54 | 55 | onRequest: (cmd, message) => 56 | @log "Unknown request", message 57 | 58 | 59 | response: (to, result) -> 60 | @send {"cmd": "response", "to": to, "result": result} 61 | 62 | 63 | cmd: (cmd, params={}, cb=null) -> 64 | @send {"cmd": cmd, "params": params}, cb 65 | 66 | 67 | send: (message, cb=null) -> 68 | message.wrapper_nonce = @wrapper_nonce 69 | message.id = @next_message_id 70 | @next_message_id += 1 71 | @target.postMessage(message, "*") 72 | if cb 73 | @waiting_cb[message.id] = cb 74 | 75 | 76 | onOpenWebsocket: => 77 | @log "Websocket open" 78 | 79 | 80 | onCloseWebsocket: => 81 | @log "Websocket close" 82 | 83 | 84 | 85 | window.ZeroFrame = ZeroFrame 86 | -------------------------------------------------------------------------------- /plugins/UiConfig/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UiConfig", 3 | "description": "Change client settings using the web interface.", 4 | "default": "enabled" 5 | } -------------------------------------------------------------------------------- /plugins/UiFileManager/__init__.py: -------------------------------------------------------------------------------- 1 | from . import UiFileManagerPlugin 2 | -------------------------------------------------------------------------------- /plugins/UiFileManager/languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "New file name:": "Új fájl neve:", 3 | "Delete": "Törlés", 4 | "Cancel": "Mégse", 5 | "Selected:": "Köjelölt:", 6 | "Delete and remove optional:": "Törlés és opcionális fájl eltávolítása", 7 | " files": " fájl", 8 | " (modified)": " (módostott)", 9 | " (new)": " (új)", 10 | " (optional)": " (opcionális)", 11 | " (ignored from content.json)": " (content.json-ból kihagyott)", 12 | "Total: ": "Összesen: ", 13 | " dir, ": " könyvtár, ", 14 | " file in ": " fájl, ", 15 | "+ New": "+ Új", 16 | "Edit": "Módosít", 17 | "View": "Megnyit", 18 | "Save": "Mentés", 19 | "Save: done!": "Mentés: Kész!" 20 | } -------------------------------------------------------------------------------- /plugins/UiFileManager/languages/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "New file name:": "新しいファイルの名前:", 3 | "Delete": "削除", 4 | "Cancel": "キャンセル", 5 | "Selected:": "選択済み: ", 6 | "Delete and remove optional:": "オプションを削除", 7 | " files": " ファイル", 8 | " (modified)": " (編集済み)", 9 | " (new)": " (新しい)", 10 | " (optional)": " (オプション)", 11 | " (ignored from content.json)": " (content.jsonから無視されます)", 12 | "Total: ": "合計: ", 13 | " dir, ": " のディレクトリ, ", 14 | " file in ": " のファイル, ", 15 | "+ New": "+ 新規作成", 16 | "Edit": "編集", 17 | "View": "閲覧", 18 | "Save": "保存", 19 | "Save: done!": "保存完了!" 20 | } 21 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2017 by Marijn Haverbeke and others 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/dialog/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: absolute; 3 | left: 0; right: 0; 4 | background: inherit; 5 | z-index: 15; 6 | padding: .1em .8em; 7 | overflow: hidden; 8 | color: inherit; 9 | } 10 | 11 | .CodeMirror-dialog-top { 12 | border-bottom: 1px solid #eee; 13 | top: 0; 14 | } 15 | 16 | .CodeMirror-dialog-bottom { 17 | border-top: 1px solid #eee; 18 | bottom: 0; 19 | } 20 | 21 | .CodeMirror-dialog input { 22 | border: none; 23 | outline: none; 24 | background: transparent; 25 | width: 20em; 26 | color: inherit; 27 | font-family: monospace; 28 | } 29 | 30 | .CodeMirror-dialog button { 31 | font-size: 70%; 32 | } 33 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/edit/trailingspace.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { 13 | if (prev == CodeMirror.Init) prev = false; 14 | if (prev && !val) 15 | cm.removeOverlay("trailingspace"); 16 | else if (!prev && val) 17 | cm.addOverlay({ 18 | token: function(stream) { 19 | for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} 20 | if (i > stream.pos) { stream.pos = i; return null; } 21 | stream.pos = l; 22 | return "trailingspace"; 23 | }, 24 | name: "trailingspace" 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-foldmarker { 2 | color: blue; 3 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; 4 | font-family: arial; 5 | line-height: .3; 6 | cursor: pointer; 7 | } 8 | .CodeMirror-foldgutter { 9 | width: .7em; 10 | } 11 | .CodeMirror-foldgutter-open, 12 | .CodeMirror-foldgutter-folded { 13 | cursor: pointer; 14 | } 15 | .CodeMirror-foldgutter-open:after { 16 | content: "\25BE"; 17 | } 18 | .CodeMirror-foldgutter-folded:after { 19 | content: "\25B8"; 20 | } 21 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/fold/indent-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function lineIndent(cm, lineNo) { 15 | var text = cm.getLine(lineNo) 16 | var spaceTo = text.search(/\S/) 17 | if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) 18 | return -1 19 | return CodeMirror.countColumn(text, null, cm.getOption("tabSize")) 20 | } 21 | 22 | CodeMirror.registerHelper("fold", "indent", function(cm, start) { 23 | var myIndent = lineIndent(cm, start.line) 24 | if (myIndent < 0) return 25 | var lastLineInFold = null 26 | 27 | // Go through lines until we find a line that definitely doesn't belong in 28 | // the block we're folding, or to the end. 29 | for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { 30 | var indent = lineIndent(cm, i) 31 | if (indent == -1) { 32 | } else if (indent > myIndent) { 33 | // Lines with a greater indent are considered part of the block. 34 | lastLineInFold = i; 35 | } else { 36 | // If this line has non-space, non-comment content, and is 37 | // indented less or equal to the start line, it is the start of 38 | // another block. 39 | break; 40 | } 41 | } 42 | if (lastLineInFold) return { 43 | from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), 44 | to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) 45 | }; 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/fold/markdown-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerHelper("fold", "markdown", function(cm, start) { 15 | var maxDepth = 100; 16 | 17 | function isHeader(lineNo) { 18 | var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0)); 19 | return tokentype && /\bheader\b/.test(tokentype); 20 | } 21 | 22 | function headerLevel(lineNo, line, nextLine) { 23 | var match = line && line.match(/^#+/); 24 | if (match && isHeader(lineNo)) return match[0].length; 25 | match = nextLine && nextLine.match(/^[=\-]+\s*$/); 26 | if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2; 27 | return maxDepth; 28 | } 29 | 30 | var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1); 31 | var level = headerLevel(start.line, firstLine, nextLine); 32 | if (level === maxDepth) return undefined; 33 | 34 | var lastLineNo = cm.lastLine(); 35 | var end = start.line, nextNextLine = cm.getLine(end + 2); 36 | while (end < lastLineNo) { 37 | if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break; 38 | ++end; 39 | nextLine = nextNextLine; 40 | nextNextLine = cm.getLine(end + 2); 41 | } 42 | 43 | return { 44 | from: CodeMirror.Pos(start.line, firstLine.length), 45 | to: CodeMirror.Pos(end, cm.getLine(end).length) 46 | }; 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/hint/anyword-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var WORD = /[\w$]+/, RANGE = 500; 15 | 16 | CodeMirror.registerHelper("hint", "anyword", function(editor, options) { 17 | var word = options && options.word || WORD; 18 | var range = options && options.range || RANGE; 19 | var cur = editor.getCursor(), curLine = editor.getLine(cur.line); 20 | var end = cur.ch, start = end; 21 | while (start && word.test(curLine.charAt(start - 1))) --start; 22 | var curWord = start != end && curLine.slice(start, end); 23 | 24 | var list = options && options.list || [], seen = {}; 25 | var re = new RegExp(word.source, "g"); 26 | for (var dir = -1; dir <= 1; dir += 2) { 27 | var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; 28 | for (; line != endLine; line += dir) { 29 | var text = editor.getLine(line), m; 30 | while (m = re.exec(text)) { 31 | if (line == cur.line && m[0] === curWord) continue; 32 | if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { 33 | seen[m[0]] = true; 34 | list.push(m[0]); 35 | } 36 | } 37 | } 38 | } 39 | return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/hint/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | white-space: pre; 29 | color: black; 30 | cursor: pointer; 31 | } 32 | 33 | li.CodeMirror-hint-active { 34 | background: #08f; 35 | color: white; 36 | } 37 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/lint/json-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Depends on jsonlint.js from https://github.com/zaach/jsonlint 5 | 6 | // declare global: jsonlint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "json", function(text) { 19 | var found = []; 20 | if (!window.jsonlint) { 21 | if (window.console) { 22 | window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run."); 23 | } 24 | return found; 25 | } 26 | // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError 27 | // is a subproperty 28 | var jsonlint = window.jsonlint.parser || window.jsonlint 29 | jsonlint.parseError = function(str, hash) { 30 | var loc = hash.loc; 31 | found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), 32 | to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), 33 | message: str}); 34 | }; 35 | try { jsonlint.parse(text); } 36 | catch(e) {} 37 | return found; 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/scroll/scrollpastend.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) { 15 | if (old && old != CodeMirror.Init) { 16 | cm.off("change", onChange); 17 | cm.off("refresh", updateBottomMargin); 18 | cm.display.lineSpace.parentNode.style.paddingBottom = ""; 19 | cm.state.scrollPastEndPadding = null; 20 | } 21 | if (val) { 22 | cm.on("change", onChange); 23 | cm.on("refresh", updateBottomMargin); 24 | updateBottomMargin(cm); 25 | } 26 | }); 27 | 28 | function onChange(cm, change) { 29 | if (CodeMirror.changeEnd(change).line == cm.lastLine()) 30 | updateBottomMargin(cm); 31 | } 32 | 33 | function updateBottomMargin(cm) { 34 | var padding = ""; 35 | if (cm.lineCount() > 1) { 36 | var totalH = cm.display.scroller.clientHeight - 30, 37 | lastLineH = cm.getLineHandle(cm.lastLine()).height; 38 | padding = (totalH - lastLineH) + "px"; 39 | } 40 | if (cm.state.scrollPastEndPadding != padding) { 41 | cm.state.scrollPastEndPadding = padding; 42 | cm.display.lineSpace.parentNode.style.paddingBottom = padding; 43 | cm.off("refresh", updateBottomMargin); 44 | cm.setSize(); 45 | cm.on("refresh", updateBottomMargin); 46 | } 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div { 2 | position: absolute; 3 | background: #ccc; 4 | -moz-box-sizing: border-box; 5 | box-sizing: border-box; 6 | border: 1px solid #bbb; 7 | border-radius: 2px; 8 | } 9 | 10 | .CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical { 11 | position: absolute; 12 | z-index: 6; 13 | background: #eee; 14 | } 15 | 16 | .CodeMirror-simplescroll-horizontal { 17 | bottom: 0; left: 0; 18 | height: 8px; 19 | } 20 | .CodeMirror-simplescroll-horizontal div { 21 | bottom: 0; 22 | height: 100%; 23 | } 24 | 25 | .CodeMirror-simplescroll-vertical { 26 | right: 0; top: 0; 27 | width: 8px; 28 | } 29 | .CodeMirror-simplescroll-vertical div { 30 | right: 0; 31 | width: 100%; 32 | } 33 | 34 | 35 | .CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler { 36 | display: none; 37 | } 38 | 39 | .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div { 40 | position: absolute; 41 | background: #bcd; 42 | border-radius: 3px; 43 | } 44 | 45 | .CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical { 46 | position: absolute; 47 | z-index: 6; 48 | } 49 | 50 | .CodeMirror-overlayscroll-horizontal { 51 | bottom: 0; left: 0; 52 | height: 6px; 53 | } 54 | .CodeMirror-overlayscroll-horizontal div { 55 | bottom: 0; 56 | height: 100%; 57 | } 58 | 59 | .CodeMirror-overlayscroll-vertical { 60 | right: 0; top: 0; 61 | width: 6px; 62 | } 63 | .CodeMirror-overlayscroll-vertical div { 64 | right: 0; 65 | width: 100%; 66 | } 67 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/search/jump-to-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Defines jumpToLine command. Uses dialog.js if present. 5 | 6 | (function(mod) { 7 | if (typeof exports == "object" && typeof module == "object") // CommonJS 8 | mod(require("../../lib/codemirror"), require("../dialog/dialog")); 9 | else if (typeof define == "function" && define.amd) // AMD 10 | define(["../../lib/codemirror", "../dialog/dialog"], mod); 11 | else // Plain browser env 12 | mod(CodeMirror); 13 | })(function(CodeMirror) { 14 | "use strict"; 15 | 16 | function dialog(cm, text, shortText, deflt, f) { 17 | if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true}); 18 | else f(prompt(shortText, deflt)); 19 | } 20 | 21 | function getJumpDialog(cm) { 22 | return cm.phrase("Jump to line:") + ' ' + cm.phrase("(Use line:column or scroll% syntax)") + ''; 23 | } 24 | 25 | function interpretLine(cm, string) { 26 | var num = Number(string) 27 | if (/^[-+]/.test(string)) return cm.getCursor().line + num 28 | else return num - 1 29 | } 30 | 31 | CodeMirror.commands.jumpToLine = function(cm) { 32 | var cur = cm.getCursor(); 33 | dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) { 34 | if (!posStr) return; 35 | 36 | var match; 37 | if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) { 38 | cm.setCursor(interpretLine(cm, match[1]), Number(match[2])) 39 | } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) { 40 | var line = Math.round(cm.lineCount() * Number(match[1]) / 100); 41 | if (/^[-+]/.test(match[1])) line = cur.line + line + 1; 42 | cm.setCursor(line - 1, cur.ch); 43 | } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) { 44 | cm.setCursor(interpretLine(cm, match[1]), cur.ch); 45 | } 46 | }); 47 | }; 48 | 49 | CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine"; 50 | }); 51 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-search-match { 2 | background: gold; 3 | border-top: 1px solid orange; 4 | border-bottom: 1px solid orange; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | opacity: .5; 8 | } 9 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/codemirror/mode/htmlembedded.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), 7 | require("../../addon/mode/multiplex")); 8 | else if (typeof define == "function" && define.amd) // AMD 9 | define(["../../lib/codemirror", "../htmlmixed/htmlmixed", 10 | "../../addon/mode/multiplex"], mod); 11 | else // Plain browser env 12 | mod(CodeMirror); 13 | })(function(CodeMirror) { 14 | "use strict"; 15 | 16 | CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { 17 | var closeComment = parserConfig.closeComment || "--%>" 18 | return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), { 19 | open: parserConfig.openComment || "<%--", 20 | close: closeComment, 21 | delimStyle: "comment", 22 | mode: {token: function(stream) { 23 | stream.skipTo(closeComment) || stream.skipToEnd() 24 | return "comment" 25 | }} 26 | }, { 27 | open: parserConfig.open || parserConfig.scriptStartRegex || "<%", 28 | close: parserConfig.close || parserConfig.scriptEndRegex || "%>", 29 | mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec) 30 | }); 31 | }, "htmlmixed"); 32 | 33 | CodeMirror.defineMIME("application/x-ejs", {name: "htmlembedded", scriptingModeSpec:"javascript"}); 34 | CodeMirror.defineMIME("application/x-aspx", {name: "htmlembedded", scriptingModeSpec:"text/x-csharp"}); 35 | CodeMirror.defineMIME("application/x-jsp", {name: "htmlembedded", scriptingModeSpec:"text/x-java"}); 36 | CodeMirror.defineMIME("application/x-erb", {name: "htmlembedded", scriptingModeSpec:"ruby"}); 37 | }); 38 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/css/Menu.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | background-color: white; padding: 10px 0px; position: absolute; top: 0px; max-height: 0px; overflow: hidden; transform: translate(-100%, -30px); pointer-events: none; 3 | box-shadow: 0px 2px 8px rgba(0,0,0,0.3); border-radius: 2px; opacity: 0; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; z-index: 99; 4 | display: inline-block; z-index: 999; transform-style: preserve-3d; 5 | } 6 | .menu.menu-left { transform: translate(0%, -30px); } 7 | .menu.menu-left.visible { transform: translate(0%, 0px); } 8 | .menu.visible { 9 | opacity: 1; transform: translate(-100%, 0px); pointer-events: all; 10 | transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1); 11 | } 12 | 13 | .menu-item { 14 | display: block; text-decoration: none; color: black; padding: 6px 24px; transition: all 0.2s; border-bottom: none; font-weight: normal; 15 | max-height: 150px; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 6; -webkit-box-orient: vertical; display: -webkit-box; 16 | } 17 | .menu-item-separator { margin-top: 3px; margin-bottom: 3px; border-top: 1px solid #eee } 18 | 19 | .menu-item.noaction { cursor: default } 20 | .menu-item:hover:not(.noaction) { background-color: #F6F6F6; transition: none; color: inherit; cursor: pointer; color: black } 21 | .menu-item:active:not(.noaction), .menu-item:focus:not(.noaction) { background-color: #AF3BFF !important; color: white !important; transition: none } 22 | .menu-item.selected:before { 23 | content: "L"; display: inline-block; transform: rotateZ(45deg) scaleX(-1); 24 | font-weight: bold; position: absolute; margin-left: -14px; font-size: 12px; margin-top: 2px; 25 | } 26 | 27 | .menu-radio { white-space: normal; line-height: 26px } 28 | .menu-radio a { 29 | background-color: #EEE; width: 18.5%;; text-align: center; margin-top: 2px; margin-bottom: 2px; color: #666; font-weight: bold; 30 | text-decoration: none; font-size: 13px; transition: all 0.3s; text-transform: uppercase; display: inline-block; 31 | } 32 | .menu-radio a:hover, .menu-radio a.selected { transition: none; background-color: #AF3BFF !important; color: white !important } 33 | .menu-radio a.long { font-size: 10px; vertical-align: -1px; } 34 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/css/Selectbar.css: -------------------------------------------------------------------------------- 1 | .selectbar.visible { margin-top: 0px; visibility: visible } 2 | .selectbar { 3 | position: fixed; top: 0; left: 0; background-color: white; box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); margin-top: -75px; 4 | transition: all 0.3s; visibility: hidden; z-index: 9999; color: black; border-left: 5px solid #ede1f582; width: 100%; 5 | padding: 13px; font-size: 13px; font-weight: lighter; backface-visibility: hidden; 6 | } 7 | 8 | .selectbar .num { margin-left: 15px; min-width: 30px; text-align: right; display: inline-block; } 9 | .selectbar .size { margin-left: 10px; color: #9f9ba2; min-width: 75px; display: inline-block; } 10 | .selectbar .actions { display: inline-block; margin-left: 20px; font-size: 13px; text-transform: uppercase; line-height: 20px; } 11 | .selectbar .action { padding: 5px 20px; border: 1px solid #edd4ff; margin-left: 10px; border-radius: 30px; color: #af3bff; text-decoration: none; transition: all 0.3s } 12 | .selectbar .action:hover { border-color: #c788f3; transition: none; color: #9700ff } 13 | .selectbar .delete { color: #AAA; border-color: #DDD; } 14 | .selectbar .delete:hover { color: #333; border-color: #AAA } 15 | .selectbar .action:active { background-color: #af3bff; color: white; border-color: #af3bff; transition: none } 16 | .selectbar .cancel { margin: 20px; font-size: 10px; text-decoration: none; color: #999; text-transform: uppercase; } 17 | .selectbar .cancel:hover { color: #333; transition: none } -------------------------------------------------------------------------------- /plugins/UiFileManager/media/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/UiFileManager/media/img/loading.gif -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/Config.coffee: -------------------------------------------------------------------------------- 1 | window.BINARY_EXTENSIONS = [ 2 | "3dm", "3ds", "3g2", "3gp", "7z", "a", "aac", "adp", "ai", "aif", "aiff", "alz", "ape", "apk", "appimage", "ar", "arj", "asc", "asf", "au", "avi", "bak", 3 | "baml", "bh", "bin", "bk", "bmp", "btif", "bz2", "bzip2", "cab", "caf", "cgm", "class", "cmx", "cpio", "cr2", "cur", "dat", "dcm", "deb", "dex", "djvu", 4 | "dll", "dmg", "dng", "doc", "docm", "docx", "dot", "dotm", "dra", "DS_Store", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ecelp4800", "ecelp7470", 5 | "ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "flatpak", "fli", "flv", "fpx", "fst", "fvt", "g3", "gh", "gif", 6 | "gpg", "graffle", "gz", "gzip", "h261", "h263", "h264", "icns", "ico", "ief", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "key", 7 | "ktx", "lha", "lib", "lvp", "lz", "lzh", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mht", "mid", "midi", "mj2", "mka", "mkv", "mmr", "mng", 8 | "mobi", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msgpack", "mxu", "nef", "npx", "numbers", "nupkg", "o", "oga", "ogg", "ogv", 9 | "otf", "pages", "pbm", "pcx", "pdb", "pdf", "pea", "pgm", "pic", "png", "pnm", "pot", "potm", "potx", "ppa", "ppam", "ppm", "pps", "ppsm", "ppsx", 10 | "ppt", "pptm", "pptx", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "resources", "rgb", "rip", "rlc", "rmf", "rmvb", "rpm", "rtf", 11 | "rz", "s3m", "s7z", "scpt", "sgi", "shar", "sig", "sil", "sketch", "slk", "smv", "snap", "snk", "so", "stl", "sub", "suo", "swf", "tar", "tbz2", "tbz", 12 | "tga", "tgz", "thmx", "tif", "tiff", "tlz", "ttc", "ttf", "txz", "udf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax", 13 | "wbmp", "wdp", "weba", "webm", "webp", "whl", "wim", "wm", "wma", "wmv", "wmx", "woff2", "woff", "wrm", "wvx", "xbm", "xif", "xla", "xlam", "xls", 14 | "xlsb", "xlsm", "xlsx", "xlt", "xltm", "xltx", "xm", "xmind", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx" 15 | ] 16 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/Class.coffee: -------------------------------------------------------------------------------- 1 | class Class 2 | trace: true 3 | 4 | log: (args...) -> 5 | return unless @trace 6 | return if typeof console is 'undefined' 7 | args.unshift("[#{@.constructor.name}]") 8 | console.log(args...) 9 | @ 10 | 11 | logStart: (name, args...) -> 12 | return unless @trace 13 | @logtimers or= {} 14 | @logtimers[name] = +(new Date) 15 | @log "#{name}", args..., "(started)" if args.length > 0 16 | @ 17 | 18 | logEnd: (name, args...) -> 19 | ms = +(new Date)-@logtimers[name] 20 | @log "#{name}", args..., "(Done in #{ms}ms)" 21 | @ 22 | 23 | window.Class = Class -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/Dollar.coffee: -------------------------------------------------------------------------------- 1 | window.$ = (selector) -> 2 | if selector.startsWith("#") 3 | return document.getElementById(selector.replace("#", "")) 4 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/ItemList.coffee: -------------------------------------------------------------------------------- 1 | class ItemList 2 | constructor: (@item_class, @key) -> 3 | @items = [] 4 | @items_bykey = {} 5 | 6 | sync: (rows, item_class, key) -> 7 | @items.splice(0, @items.length) # Empty items 8 | for row in rows 9 | current_obj = @items_bykey[row[@key]] 10 | if current_obj 11 | current_obj.row = row 12 | @items.push current_obj 13 | else 14 | item = new @item_class(row, @) 15 | @items_bykey[row[@key]] = item 16 | @items.push item 17 | 18 | deleteItem: (item) -> 19 | index = @items.indexOf(item) 20 | if index > -1 21 | @items.splice(index, 1) 22 | else 23 | console.log "Can't delete item", item 24 | delete @items_bykey[item.row[@key]] 25 | 26 | window.ItemList = ItemList -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/Promise.coffee: -------------------------------------------------------------------------------- 1 | # From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html 2 | 3 | class Promise 4 | @when: (tasks...) -> 5 | num_uncompleted = tasks.length 6 | args = new Array(num_uncompleted) 7 | promise = new Promise() 8 | 9 | for task, task_id in tasks 10 | ((task_id) -> 11 | task.then(() -> 12 | args[task_id] = Array.prototype.slice.call(arguments) 13 | num_uncompleted-- 14 | promise.complete.apply(promise, args) if num_uncompleted == 0 15 | ) 16 | )(task_id) 17 | 18 | return promise 19 | 20 | constructor: -> 21 | @resolved = false 22 | @end_promise = null 23 | @result = null 24 | @callbacks = [] 25 | 26 | resolve: -> 27 | if @resolved 28 | return false 29 | @resolved = true 30 | @data = arguments 31 | if not arguments.length 32 | @data = [true] 33 | @result = @data[0] 34 | for callback in @callbacks 35 | back = callback.apply callback, @data 36 | if @end_promise 37 | @end_promise.resolve(back) 38 | 39 | fail: -> 40 | @resolve(false) 41 | 42 | then: (callback) -> 43 | if @resolved == true 44 | callback.apply callback, @data 45 | return 46 | 47 | @callbacks.push callback 48 | 49 | @end_promise = new Promise() 50 | 51 | window.Promise = Promise 52 | 53 | ### 54 | s = Date.now() 55 | log = (text) -> 56 | console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ") 57 | 58 | log "Started" 59 | 60 | cmd = (query) -> 61 | p = new Promise() 62 | setTimeout ( -> 63 | p.resolve query+" Result" 64 | ), 100 65 | return p 66 | 67 | back = cmd("SELECT * FROM message").then (res) -> 68 | log res 69 | return "Return from query" 70 | .then (res) -> 71 | log "Back then", res 72 | 73 | log "Query started", back 74 | ### -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/Prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::startsWith = (s) -> @[...s.length] is s 2 | String::endsWith = (s) -> s is '' or @[-s.length..] is s 3 | String::repeat = (count) -> new Array( count + 1 ).join(@) 4 | 5 | window.isEmpty = (obj) -> 6 | for key of obj 7 | return false 8 | return true 9 | 10 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/Time.coffee: -------------------------------------------------------------------------------- 1 | class Time 2 | since: (timestamp) -> 3 | now = +(new Date)/1000 4 | if timestamp > 1000000000000 # In ms 5 | timestamp = timestamp/1000 6 | secs = now - timestamp 7 | if secs < 60 8 | back = "Just now" 9 | else if secs < 60*60 10 | minutes = Math.round(secs/60) 11 | back = "" + minutes + " minutes ago" 12 | else if secs < 60*60*24 13 | back = "#{Math.round(secs/60/60)} hours ago" 14 | else if secs < 60*60*24*3 15 | back = "#{Math.round(secs/60/60/24)} days ago" 16 | else 17 | back = "on "+@date(timestamp) 18 | back = back.replace(/^1 ([a-z]+)s/, "1 $1") # 1 days ago fix 19 | return back 20 | 21 | dateIso: (timestamp=null) -> 22 | if not timestamp 23 | timestamp = window.Time.timestamp() 24 | 25 | if timestamp > 1000000000000 # In ms 26 | timestamp = timestamp/1000 27 | tzoffset = (new Date()).getTimezoneOffset() * 60 28 | return (new Date((timestamp - tzoffset) * 1000)).toISOString().split("T")[0] 29 | 30 | date: (timestamp=null, format="short") -> 31 | if not timestamp 32 | timestamp = window.Time.timestamp() 33 | 34 | if timestamp > 1000000000000 # In ms 35 | timestamp = timestamp/1000 36 | parts = (new Date(timestamp * 1000)).toString().split(" ") 37 | if format == "short" 38 | display = parts.slice(1, 4) 39 | else if format == "day" 40 | display = parts.slice(1, 3) 41 | else if format == "month" 42 | display = [parts[1], parts[3]] 43 | else if format == "long" 44 | display = parts.slice(1, 5) 45 | return display.join(" ").replace(/( [0-9]{4})/, ",$1") 46 | 47 | weekDay: (timestamp) -> 48 | if timestamp > 1000000000000 # In ms 49 | timestamp = timestamp/1000 50 | return ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][ (new Date(timestamp * 1000)).getDay() ] 51 | 52 | timestamp: (date="") -> 53 | if date == "now" or date == "" 54 | return parseInt(+(new Date)/1000) 55 | else 56 | return parseInt(Date.parse(date)/1000) 57 | 58 | 59 | window.Time = new Time -------------------------------------------------------------------------------- /plugins/UiFileManager/media/js/lib/ZeroFrame.coffee: -------------------------------------------------------------------------------- 1 | class ZeroFrame extends Class 2 | constructor: (url) -> 3 | @url = url 4 | @waiting_cb = {} 5 | @wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1") 6 | @connect() 7 | @next_message_id = 1 8 | @history_state = {} 9 | @init() 10 | 11 | 12 | init: -> 13 | @ 14 | 15 | 16 | connect: -> 17 | @target = window.parent 18 | window.addEventListener("message", @onMessage, false) 19 | @cmd("innerReady") 20 | 21 | # Save scrollTop 22 | window.addEventListener "beforeunload", (e) => 23 | @log "save scrollTop", window.pageYOffset 24 | @history_state["scrollTop"] = window.pageYOffset 25 | @cmd "wrapperReplaceState", [@history_state, null] 26 | 27 | # Restore scrollTop 28 | @cmd "wrapperGetState", [], (state) => 29 | @history_state = state if state? 30 | @log "restore scrollTop", state, window.pageYOffset 31 | if window.pageYOffset == 0 and state 32 | window.scroll(window.pageXOffset, state.scrollTop) 33 | 34 | 35 | onMessage: (e) => 36 | message = e.data 37 | cmd = message.cmd 38 | if cmd == "response" 39 | if @waiting_cb[message.to]? 40 | @waiting_cb[message.to](message.result) 41 | else 42 | @log "Websocket callback not found:", message 43 | else if cmd == "wrapperReady" # Wrapper inited later 44 | @cmd("innerReady") 45 | else if cmd == "ping" 46 | @response message.id, "pong" 47 | else if cmd == "wrapperOpenedWebsocket" 48 | @onOpenWebsocket() 49 | else if cmd == "wrapperClosedWebsocket" 50 | @onCloseWebsocket() 51 | else 52 | @onRequest cmd, message.params 53 | 54 | 55 | onRequest: (cmd, message) => 56 | @log "Unknown request", message 57 | 58 | 59 | response: (to, result) -> 60 | @send {"cmd": "response", "to": to, "result": result} 61 | 62 | 63 | cmd: (cmd, params={}, cb=null) -> 64 | @send {"cmd": cmd, "params": params}, cb 65 | 66 | 67 | send: (message, cb=null) -> 68 | message.wrapper_nonce = @wrapper_nonce 69 | message.id = @next_message_id 70 | @next_message_id += 1 71 | @target.postMessage(message, "*") 72 | if cb 73 | @waiting_cb[message.id] = cb 74 | 75 | 76 | onOpenWebsocket: => 77 | @log "Websocket open" 78 | 79 | 80 | onCloseWebsocket: => 81 | @log "Websocket close" 82 | 83 | 84 | 85 | window.ZeroFrame = ZeroFrame 86 | -------------------------------------------------------------------------------- /plugins/UiFileManager/media/list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | List - ZeroNet 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugins/UiPluginManager/__init__.py: -------------------------------------------------------------------------------- 1 | from . import UiPluginManagerPlugin 2 | -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/css/button.css: -------------------------------------------------------------------------------- 1 | /* Button */ 2 | .button { 3 | background-color: #FFDC00; color: black; padding: 10px 20px; display: inline-block; background-position: left center; 4 | border-radius: 2px; border-bottom: 2px solid #E8BE29; transition: all 0.5s ease-out; text-decoration: none; 5 | } 6 | .button:hover { border-color: white; border-bottom: 2px solid #BD960C; transition: none ; background-color: #FDEB07 } 7 | .button:active { position: relative; top: 1px } 8 | .button.loading { 9 | color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center; 10 | transition: all 0.5s ease-out ; pointer-events: none; border-bottom: 2px solid #666 11 | } 12 | .button.disabled { color: #DDD; background-color: #999; pointer-events: none; border-bottom: 2px solid #666 } -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/UiPluginManager/media/img/loading.gif -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/js/lib/Class.coffee: -------------------------------------------------------------------------------- 1 | class Class 2 | trace: true 3 | 4 | log: (args...) -> 5 | return unless @trace 6 | return if typeof console is 'undefined' 7 | args.unshift("[#{@.constructor.name}]") 8 | console.log(args...) 9 | @ 10 | 11 | logStart: (name, args...) -> 12 | return unless @trace 13 | @logtimers or= {} 14 | @logtimers[name] = +(new Date) 15 | @log "#{name}", args..., "(started)" if args.length > 0 16 | @ 17 | 18 | logEnd: (name, args...) -> 19 | ms = +(new Date)-@logtimers[name] 20 | @log "#{name}", args..., "(Done in #{ms}ms)" 21 | @ 22 | 23 | window.Class = Class -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/js/lib/Promise.coffee: -------------------------------------------------------------------------------- 1 | # From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html 2 | 3 | class Promise 4 | @when: (tasks...) -> 5 | num_uncompleted = tasks.length 6 | args = new Array(num_uncompleted) 7 | promise = new Promise() 8 | 9 | for task, task_id in tasks 10 | ((task_id) -> 11 | task.then(() -> 12 | args[task_id] = Array.prototype.slice.call(arguments) 13 | num_uncompleted-- 14 | promise.complete.apply(promise, args) if num_uncompleted == 0 15 | ) 16 | )(task_id) 17 | 18 | return promise 19 | 20 | constructor: -> 21 | @resolved = false 22 | @end_promise = null 23 | @result = null 24 | @callbacks = [] 25 | 26 | resolve: -> 27 | if @resolved 28 | return false 29 | @resolved = true 30 | @data = arguments 31 | if not arguments.length 32 | @data = [true] 33 | @result = @data[0] 34 | for callback in @callbacks 35 | back = callback.apply callback, @data 36 | if @end_promise 37 | @end_promise.resolve(back) 38 | 39 | fail: -> 40 | @resolve(false) 41 | 42 | then: (callback) -> 43 | if @resolved == true 44 | callback.apply callback, @data 45 | return 46 | 47 | @callbacks.push callback 48 | 49 | @end_promise = new Promise() 50 | 51 | window.Promise = Promise 52 | 53 | ### 54 | s = Date.now() 55 | log = (text) -> 56 | console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ") 57 | 58 | log "Started" 59 | 60 | cmd = (query) -> 61 | p = new Promise() 62 | setTimeout ( -> 63 | p.resolve query+" Result" 64 | ), 100 65 | return p 66 | 67 | back = cmd("SELECT * FROM message").then (res) -> 68 | log res 69 | return "Return from query" 70 | .then (res) -> 71 | log "Back then", res 72 | 73 | log "Query started", back 74 | ### -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/js/lib/Prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::startsWith = (s) -> @[...s.length] is s 2 | String::endsWith = (s) -> s is '' or @[-s.length..] is s 3 | String::repeat = (count) -> new Array( count + 1 ).join(@) 4 | 5 | window.isEmpty = (obj) -> 6 | for key of obj 7 | return false 8 | return true 9 | -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/js/utils/Dollar.coffee: -------------------------------------------------------------------------------- 1 | window.$ = (selector) -> 2 | if selector.startsWith("#") 3 | return document.getElementById(selector.replace("#", "")) 4 | -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/js/utils/ZeroFrame.coffee: -------------------------------------------------------------------------------- 1 | class ZeroFrame extends Class 2 | constructor: (url) -> 3 | @url = url 4 | @waiting_cb = {} 5 | @wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1") 6 | @connect() 7 | @next_message_id = 1 8 | @history_state = {} 9 | @init() 10 | 11 | 12 | init: -> 13 | @ 14 | 15 | 16 | connect: -> 17 | @target = window.parent 18 | window.addEventListener("message", @onMessage, false) 19 | @cmd("innerReady") 20 | 21 | # Save scrollTop 22 | window.addEventListener "beforeunload", (e) => 23 | @log "save scrollTop", window.pageYOffset 24 | @history_state["scrollTop"] = window.pageYOffset 25 | @cmd "wrapperReplaceState", [@history_state, null] 26 | 27 | # Restore scrollTop 28 | @cmd "wrapperGetState", [], (state) => 29 | @history_state = state if state? 30 | @log "restore scrollTop", state, window.pageYOffset 31 | if window.pageYOffset == 0 and state 32 | window.scroll(window.pageXOffset, state.scrollTop) 33 | 34 | 35 | onMessage: (e) => 36 | message = e.data 37 | cmd = message.cmd 38 | if cmd == "response" 39 | if @waiting_cb[message.to]? 40 | @waiting_cb[message.to](message.result) 41 | else 42 | @log "Websocket callback not found:", message 43 | else if cmd == "wrapperReady" # Wrapper inited later 44 | @cmd("innerReady") 45 | else if cmd == "ping" 46 | @response message.id, "pong" 47 | else if cmd == "wrapperOpenedWebsocket" 48 | @onOpenWebsocket() 49 | else if cmd == "wrapperClosedWebsocket" 50 | @onCloseWebsocket() 51 | else 52 | @onRequest cmd, message.params 53 | 54 | 55 | onRequest: (cmd, message) => 56 | @log "Unknown request", message 57 | 58 | 59 | response: (to, result) -> 60 | @send {"cmd": "response", "to": to, "result": result} 61 | 62 | 63 | cmd: (cmd, params={}, cb=null) -> 64 | @send {"cmd": cmd, "params": params}, cb 65 | 66 | 67 | send: (message, cb=null) -> 68 | message.wrapper_nonce = @wrapper_nonce 69 | message.id = @next_message_id 70 | @next_message_id += 1 71 | @target.postMessage(message, "*") 72 | if cb 73 | @waiting_cb[message.id] = cb 74 | 75 | 76 | onOpenWebsocket: => 77 | @log "Websocket open" 78 | 79 | 80 | onCloseWebsocket: => 81 | @log "Websocket close" 82 | 83 | 84 | 85 | window.ZeroFrame = ZeroFrame 86 | -------------------------------------------------------------------------------- /plugins/UiPluginManager/media/plugin_manager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Settings - ZeroNet 6 | 7 | 8 | 9 | 10 | 11 | 12 |

ZeroNet plugin manager

13 | 14 |
15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /plugins/Zeroname/README.md: -------------------------------------------------------------------------------- 1 | # ZeroName 2 | 3 | Zeroname plugin to connect Namecoin and register all the .bit domain name. 4 | 5 | ## Start 6 | 7 | You can create your own Zeroname. 8 | 9 | ### Namecoin node 10 | 11 | You need to run a namecoin node. 12 | 13 | [Namecoin](https://namecoin.org/download/) 14 | 15 | You will need to start it as a RPC server. 16 | 17 | Example of `~/.namecoin/namecoin.conf` minimal setup: 18 | ``` 19 | daemon=1 20 | rpcuser=your-name 21 | rpcpassword=your-password 22 | rpcport=8336 23 | server=1 24 | txindex=1 25 | valueencoding=utf8 26 | ``` 27 | 28 | Don't forget to change the `rpcuser` value and `rpcpassword` value! 29 | 30 | You can start your node : `./namecoind` 31 | 32 | ### Create a Zeroname site 33 | 34 | You will also need to create a site `python zeronet.py createSite` and regitser the info. 35 | 36 | In the site you will need to create a file `./data//data/names.json` with this is it: 37 | ``` 38 | {} 39 | ``` 40 | 41 | ### `zeroname_config.json` file 42 | 43 | In `~/.namecoin/zeroname_config.json` 44 | ``` 45 | { 46 | "lastprocessed": 223910, 47 | "zeronet_path": "/root/ZeroNet", # Update with your path 48 | "privatekey": "", # Update with your private key of your site 49 | "site": "" # Update with the address of your site 50 | } 51 | ``` 52 | 53 | ### Run updater 54 | 55 | You can now run the script : `updater/zeroname_updater.py` and wait until it is fully sync (it might take a while). 56 | -------------------------------------------------------------------------------- /plugins/Zeroname/__init__.py: -------------------------------------------------------------------------------- 1 | from . import SiteManagerPlugin 2 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/__init__.py -------------------------------------------------------------------------------- /plugins/disabled-Bootstrapper/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * -------------------------------------------------------------------------------- /plugins/disabled-Bootstrapper/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | slow: mark a tests as slow. 6 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/disabled-Bootstrapper/__init__.py: -------------------------------------------------------------------------------- 1 | from . import BootstrapperPlugin -------------------------------------------------------------------------------- /plugins/disabled-Bootstrapper/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bootstrapper", 3 | "description": "Add BitTorrent tracker server like features to your ZeroNet client.", 4 | "default": "disabled" 5 | } -------------------------------------------------------------------------------- /plugins/disabled-Dnschain/UiRequestPlugin.py: -------------------------------------------------------------------------------- 1 | import re 2 | from Plugin import PluginManager 3 | 4 | @PluginManager.registerTo("UiRequest") 5 | class UiRequestPlugin(object): 6 | def __init__(self, server = None): 7 | from Site import SiteManager 8 | self.site_manager = SiteManager.site_manager 9 | super(UiRequestPlugin, self).__init__(server) 10 | 11 | 12 | # Media request 13 | def actionSiteMedia(self, path): 14 | match = re.match(r"/media/(?P
[A-Za-z0-9-]+\.[A-Za-z0-9\.-]+)(?P/.*|$)", path) 15 | if match: # Its a valid domain, resolve first 16 | domain = match.group("address") 17 | address = self.site_manager.resolveDomain(domain) 18 | if address: 19 | path = "/media/"+address+match.group("inner_path") 20 | return super(UiRequestPlugin, self).actionSiteMedia(path) # Get the wrapper frame output 21 | 22 | 23 | # Is mediarequest allowed from that referer 24 | def isMediaRequestAllowed(self, site_address, referer): 25 | referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address 26 | referer_site_address = re.match(r"/(?P
[A-Za-z0-9\.-]+)(?P/.*|$)", referer_path).group("address") 27 | 28 | if referer_site_address == site_address: # Referer site address as simple address 29 | return True 30 | elif self.site_manager.resolveDomain(referer_site_address) == site_address: # Referer site address as dns 31 | return True 32 | else: # Invalid referer 33 | return False 34 | 35 | -------------------------------------------------------------------------------- /plugins/disabled-Dnschain/__init__.py: -------------------------------------------------------------------------------- 1 | # This plugin is experimental, if you really want to enable uncomment the following lines: 2 | # import DnschainPlugin 3 | # import SiteManagerPlugin -------------------------------------------------------------------------------- /plugins/disabled-DonationMessage/DonationMessagePlugin.py: -------------------------------------------------------------------------------- 1 | import re 2 | from Plugin import PluginManager 3 | 4 | # Warning: If you modify the donation address then renmae the plugin's directory to "MyDonationMessage" to prevent the update script overwrite 5 | 6 | 7 | @PluginManager.registerTo("UiRequest") 8 | class UiRequestPlugin(object): 9 | # Inject a donation message to every page top right corner 10 | def renderWrapper(self, *args, **kwargs): 11 | body = super(UiRequestPlugin, self).renderWrapper(*args, **kwargs) # Get the wrapper frame output 12 | 13 | inject_html = """ 14 | 17 | Please donate to help to keep this ZeroProxy alive 18 | 19 | 20 | """ 21 | 22 | return re.sub(r"\s*\s*$", inject_html, body) 23 | -------------------------------------------------------------------------------- /plugins/disabled-DonationMessage/__init__.py: -------------------------------------------------------------------------------- 1 | from . import DonationMessagePlugin 2 | -------------------------------------------------------------------------------- /plugins/disabled-Multiuser/Test/TestMultiuser.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import json 3 | from Config import config 4 | from User import UserManager 5 | 6 | @pytest.mark.usefixtures("resetSettings") 7 | @pytest.mark.usefixtures("resetTempSettings") 8 | class TestMultiuser: 9 | def testMemorySave(self, user): 10 | # It should not write users to disk 11 | users_before = open("%s/users.json" % config.data_dir).read() 12 | user = UserManager.user_manager.create() 13 | user.save() 14 | assert open("%s/users.json" % config.data_dir).read() == users_before 15 | -------------------------------------------------------------------------------- /plugins/disabled-Multiuser/Test/conftest.py: -------------------------------------------------------------------------------- 1 | from src.Test.conftest import * 2 | -------------------------------------------------------------------------------- /plugins/disabled-Multiuser/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 4 | markers = 5 | webtest: mark a test as a webtest. -------------------------------------------------------------------------------- /plugins/disabled-Multiuser/UserPlugin.py: -------------------------------------------------------------------------------- 1 | from Config import config 2 | from Plugin import PluginManager 3 | 4 | allow_reload = False 5 | 6 | @PluginManager.registerTo("UserManager") 7 | class UserManagerPlugin(object): 8 | def load(self): 9 | if not config.multiuser_local: 10 | # In multiuser mode do not load the users 11 | if not self.users: 12 | self.users = {} 13 | return self.users 14 | else: 15 | return super(UserManagerPlugin, self).load() 16 | 17 | # Find user by master address 18 | # Return: User or None 19 | def get(self, master_address=None): 20 | users = self.list() 21 | if master_address in users: 22 | user = users[master_address] 23 | else: 24 | user = None 25 | return user 26 | 27 | 28 | @PluginManager.registerTo("User") 29 | class UserPlugin(object): 30 | # In multiuser mode users data only exits in memory, dont write to data/user.json 31 | def save(self): 32 | if not config.multiuser_local: 33 | return False 34 | else: 35 | return super(UserPlugin, self).save() 36 | -------------------------------------------------------------------------------- /plugins/disabled-Multiuser/__init__.py: -------------------------------------------------------------------------------- 1 | from . import MultiuserPlugin 2 | -------------------------------------------------------------------------------- /plugins/disabled-Multiuser/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MultiUser", 3 | "description": "Cookie based multi-users support on your ZeroNet web interface.", 4 | "default": "disabled" 5 | } -------------------------------------------------------------------------------- /plugins/disabled-StemPort/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from stem.control import Controller 3 | stem_found = True 4 | except Exception as err: 5 | print(("STEM NOT FOUND! %s" % err)) 6 | stem_found = False 7 | 8 | if stem_found: 9 | print("Starting Stem plugin...") 10 | from . import StemPortPlugin 11 | -------------------------------------------------------------------------------- /plugins/disabled-UiPassword/__init__.py: -------------------------------------------------------------------------------- 1 | from . import UiPasswordPlugin -------------------------------------------------------------------------------- /plugins/disabled-UiPassword/plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UiPassword", 3 | "description": "Password based autentication on the web interface.", 4 | "default": "disabled" 5 | } -------------------------------------------------------------------------------- /plugins/disabled-ZeronameLocal/UiRequestPlugin.py: -------------------------------------------------------------------------------- 1 | import re 2 | from Plugin import PluginManager 3 | 4 | @PluginManager.registerTo("UiRequest") 5 | class UiRequestPlugin(object): 6 | def __init__(self, *args, **kwargs): 7 | from Site import SiteManager 8 | self.site_manager = SiteManager.site_manager 9 | super(UiRequestPlugin, self).__init__(*args, **kwargs) 10 | 11 | 12 | # Media request 13 | def actionSiteMedia(self, path): 14 | match = re.match(r"/media/(?P
[A-Za-z0-9-]+\.[A-Za-z0-9\.-]+)(?P/.*|$)", path) 15 | if match: # Its a valid domain, resolve first 16 | domain = match.group("address") 17 | address = self.site_manager.resolveDomain(domain) 18 | if address: 19 | path = "/media/"+address+match.group("inner_path") 20 | return super(UiRequestPlugin, self).actionSiteMedia(path) # Get the wrapper frame output 21 | 22 | 23 | # Is mediarequest allowed from that referer 24 | def isMediaRequestAllowed(self, site_address, referer): 25 | referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address 26 | referer_path = re.sub(r"\?.*", "", referer_path) # Remove http params 27 | 28 | if self.isProxyRequest(): # Match to site domain 29 | referer = re.sub("^http://zero[/]+", "http://", referer) # Allow /zero access 30 | referer_site_address = re.match("http[s]{0,1}://(.*?)(/|$)", referer).group(1) 31 | else: # Match to request path 32 | referer_site_address = re.match(r"/(?P
[A-Za-z0-9\.-]+)(?P/.*|$)", referer_path).group("address") 33 | 34 | if referer_site_address == site_address: # Referer site address as simple address 35 | return True 36 | elif self.site_manager.resolveDomain(referer_site_address) == site_address: # Referer site address as dns 37 | return True 38 | else: # Invalid referer 39 | return False 40 | -------------------------------------------------------------------------------- /plugins/disabled-ZeronameLocal/__init__.py: -------------------------------------------------------------------------------- 1 | from . import UiRequestPlugin 2 | from . import SiteManagerPlugin -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gevent==1.4.0; python_version <= "3.6" 2 | greenlet==0.4.16; python_version <= "3.6" 3 | gevent>=20.9.0; python_version >= "3.7" 4 | msgpack>=0.4.4 5 | base58 6 | merkletools 7 | rsa 8 | PySocks>=1.6.8 9 | pyasn1 10 | websocket_client 11 | gevent-ws 12 | coincurve 13 | maxminddb 14 | -------------------------------------------------------------------------------- /src/Connection/__init__.py: -------------------------------------------------------------------------------- 1 | from .ConnectionServer import ConnectionServer 2 | from .Connection import Connection 3 | -------------------------------------------------------------------------------- /src/Content/__init__.py: -------------------------------------------------------------------------------- 1 | from .ContentManager import ContentManager -------------------------------------------------------------------------------- /src/Crypt/Crypt.py: -------------------------------------------------------------------------------- 1 | from Config import config 2 | from util import ThreadPool 3 | 4 | thread_pool_crypt = ThreadPool.ThreadPool(config.threads_crypt) -------------------------------------------------------------------------------- /src/Crypt/CryptHash.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import os 3 | import base64 4 | 5 | 6 | def sha512sum(file, blocksize=65536, format="hexdigest"): 7 | if type(file) is str: # Filename specified 8 | file = open(file, "rb") 9 | hash = hashlib.sha512() 10 | for block in iter(lambda: file.read(blocksize), b""): 11 | hash.update(block) 12 | 13 | # Truncate to 256bits is good enough 14 | if format == "hexdigest": 15 | return hash.hexdigest()[0:64] 16 | else: 17 | return hash.digest()[0:32] 18 | 19 | 20 | def sha256sum(file, blocksize=65536): 21 | if type(file) is str: # Filename specified 22 | file = open(file, "rb") 23 | hash = hashlib.sha256() 24 | for block in iter(lambda: file.read(blocksize), b""): 25 | hash.update(block) 26 | return hash.hexdigest() 27 | 28 | 29 | def random(length=64, encoding="hex"): 30 | if encoding == "base64": # Characters: A-Za-z0-9 31 | hash = hashlib.sha512(os.urandom(256)).digest() 32 | return base64.b64encode(hash).decode("ascii").replace("+", "").replace("/", "").replace("=", "")[0:length] 33 | else: # Characters: a-f0-9 (faster) 34 | return hashlib.sha512(os.urandom(256)).hexdigest()[0:length] 35 | 36 | 37 | # Sha512 truncated to 256bits 38 | class Sha512t: 39 | def __init__(self, data): 40 | if data: 41 | self.sha512 = hashlib.sha512(data) 42 | else: 43 | self.sha512 = hashlib.sha512() 44 | 45 | def hexdigest(self): 46 | return self.sha512.hexdigest()[0:64] 47 | 48 | def digest(self): 49 | return self.sha512.digest()[0:32] 50 | 51 | def update(self, data): 52 | return self.sha512.update(data) 53 | 54 | 55 | def sha512t(data=None): 56 | return Sha512t(data) 57 | -------------------------------------------------------------------------------- /src/Crypt/CryptRsa.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | 4 | def sign(data, privatekey): 5 | import rsa 6 | from rsa import pkcs1 7 | 8 | if "BEGIN RSA PRIVATE KEY" not in privatekey: 9 | privatekey = "-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----" % privatekey 10 | 11 | priv = rsa.PrivateKey.load_pkcs1(privatekey) 12 | sign = rsa.pkcs1.sign(data, priv, 'SHA-256') 13 | return sign 14 | 15 | def verify(data, publickey, sign): 16 | import rsa 17 | from rsa import pkcs1 18 | 19 | pub = rsa.PublicKey.load_pkcs1(publickey, format="DER") 20 | try: 21 | valid = rsa.pkcs1.verify(data, sign, pub) 22 | except pkcs1.VerificationError: 23 | valid = False 24 | return valid 25 | 26 | def privatekeyToPublickey(privatekey): 27 | import rsa 28 | from rsa import pkcs1 29 | 30 | if "BEGIN RSA PRIVATE KEY" not in privatekey: 31 | privatekey = "-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----" % privatekey 32 | 33 | priv = rsa.PrivateKey.load_pkcs1(privatekey) 34 | pub = rsa.PublicKey(priv.n, priv.e) 35 | return pub.save_pkcs1("DER") 36 | 37 | def publickeyToOnion(publickey): 38 | return base64.b32encode(hashlib.sha1(publickey).digest()[:10]).lower().decode("ascii") 39 | -------------------------------------------------------------------------------- /src/Crypt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Crypt/__init__.py -------------------------------------------------------------------------------- /src/Db/DbQuery.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | # Parse and modify sql queries 5 | class DbQuery: 6 | def __init__(self, query): 7 | self.setQuery(query.strip()) 8 | 9 | # Split main parts of query 10 | def parseParts(self, query): 11 | parts = re.split("(SELECT|FROM|WHERE|ORDER BY|LIMIT)", query) 12 | parts = [_f for _f in parts if _f] # Remove empty parts 13 | parts = [s.strip() for s in parts] # Remove whitespace 14 | return dict(list(zip(parts[0::2], parts[1::2]))) 15 | 16 | # Parse selected fields SELECT ... FROM 17 | def parseFields(self, query_select): 18 | fields = re.findall("([^,]+) AS ([^,]+)", query_select) 19 | return {key: val.strip() for val, key in fields} 20 | 21 | # Parse query conditions WHERE ... 22 | def parseWheres(self, query_where): 23 | if " AND " in query_where: 24 | return query_where.split(" AND ") 25 | elif query_where: 26 | return [query_where] 27 | else: 28 | return [] 29 | 30 | # Set the query 31 | def setQuery(self, query): 32 | self.parts = self.parseParts(query) 33 | self.fields = self.parseFields(self.parts["SELECT"]) 34 | self.wheres = self.parseWheres(self.parts.get("WHERE", "")) 35 | 36 | # Convert query back to string 37 | def __str__(self): 38 | query_parts = [] 39 | for part_name in ["SELECT", "FROM", "WHERE", "ORDER BY", "LIMIT"]: 40 | if part_name == "WHERE" and self.wheres: 41 | query_parts.append("WHERE") 42 | query_parts.append(" AND ".join(self.wheres)) 43 | elif part_name in self.parts: 44 | query_parts.append(part_name) 45 | query_parts.append(self.parts[part_name]) 46 | return "\n".join(query_parts) 47 | -------------------------------------------------------------------------------- /src/Db/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Db/__init__.py -------------------------------------------------------------------------------- /src/Debug/DebugLock.py: -------------------------------------------------------------------------------- 1 | import time 2 | import logging 3 | 4 | import gevent.lock 5 | 6 | from Debug import Debug 7 | 8 | 9 | class DebugLock: 10 | def __init__(self, log_after=0.01, name="Lock"): 11 | self.name = name 12 | self.log_after = log_after 13 | self.lock = gevent.lock.Semaphore(1) 14 | self.release = self.lock.release 15 | 16 | def acquire(self, *args, **kwargs): 17 | s = time.time() 18 | res = self.lock.acquire(*args, **kwargs) 19 | time_taken = time.time() - s 20 | if time_taken >= self.log_after: 21 | logging.debug("%s: Waited %.3fs after called by %s" % 22 | (self.name, time_taken, Debug.formatStack()) 23 | ) 24 | return res 25 | -------------------------------------------------------------------------------- /src/Debug/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Debug/__init__.py -------------------------------------------------------------------------------- /src/File/__init__.py: -------------------------------------------------------------------------------- 1 | from .FileServer import FileServer 2 | from .FileRequest import FileRequest -------------------------------------------------------------------------------- /src/Peer/__init__.py: -------------------------------------------------------------------------------- 1 | from .Peer import Peer 2 | from .PeerHashfield import PeerHashfield 3 | -------------------------------------------------------------------------------- /src/Plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Plugin/__init__.py -------------------------------------------------------------------------------- /src/Site/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Site/__init__.py -------------------------------------------------------------------------------- /src/Test/Spy.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | class Spy: 4 | def __init__(self, obj, func_name): 5 | self.obj = obj 6 | self.__name__ = func_name 7 | self.func_original = getattr(self.obj, func_name) 8 | self.calls = [] 9 | 10 | def __enter__(self, *args, **kwargs): 11 | logging.debug("Spy started") 12 | def loggedFunc(cls, *args, **kwargs): 13 | call = dict(enumerate(args, 1)) 14 | call[0] = cls 15 | call.update(kwargs) 16 | logging.debug("Spy call: %s" % call) 17 | self.calls.append(call) 18 | return self.func_original(cls, *args, **kwargs) 19 | setattr(self.obj, self.__name__, loggedFunc) 20 | return self.calls 21 | 22 | def __exit__(self, *args, **kwargs): 23 | setattr(self.obj, self.__name__, self.func_original) -------------------------------------------------------------------------------- /src/Test/TestCached.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from util import Cached 4 | 5 | 6 | class CachedObject: 7 | def __init__(self): 8 | self.num_called_add = 0 9 | self.num_called_multiply = 0 10 | self.num_called_none = 0 11 | 12 | @Cached(timeout=1) 13 | def calcAdd(self, a, b): 14 | self.num_called_add += 1 15 | return a + b 16 | 17 | @Cached(timeout=1) 18 | def calcMultiply(self, a, b): 19 | self.num_called_multiply += 1 20 | return a * b 21 | 22 | @Cached(timeout=1) 23 | def none(self): 24 | self.num_called_none += 1 25 | return None 26 | 27 | 28 | class TestCached: 29 | def testNoneValue(self): 30 | cached_object = CachedObject() 31 | assert cached_object.none() is None 32 | assert cached_object.none() is None 33 | assert cached_object.num_called_none == 1 34 | time.sleep(2) 35 | assert cached_object.none() is None 36 | assert cached_object.num_called_none == 2 37 | 38 | def testCall(self): 39 | cached_object = CachedObject() 40 | 41 | assert cached_object.calcAdd(1, 2) == 3 42 | assert cached_object.calcAdd(1, 2) == 3 43 | assert cached_object.calcMultiply(1, 2) == 2 44 | assert cached_object.calcMultiply(1, 2) == 2 45 | assert cached_object.num_called_add == 1 46 | assert cached_object.num_called_multiply == 1 47 | 48 | assert cached_object.calcAdd(2, 3) == 5 49 | assert cached_object.calcAdd(2, 3) == 5 50 | assert cached_object.num_called_add == 2 51 | 52 | assert cached_object.calcAdd(1, 2) == 3 53 | assert cached_object.calcMultiply(2, 3) == 6 54 | assert cached_object.num_called_add == 2 55 | assert cached_object.num_called_multiply == 2 56 | 57 | time.sleep(2) 58 | assert cached_object.calcAdd(1, 2) == 3 59 | assert cached_object.num_called_add == 3 60 | -------------------------------------------------------------------------------- /src/Test/TestConfig.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import Config 4 | 5 | 6 | @pytest.mark.usefixtures("resetSettings") 7 | class TestConfig: 8 | def testParse(self): 9 | # Defaults 10 | config_test = Config.Config("zeronet.py".split(" ")) 11 | config_test.parse(silent=True, parse_config=False) 12 | assert not config_test.debug 13 | assert not config_test.debug_socket 14 | 15 | # Test parse command line with unknown parameters (ui_password) 16 | config_test = Config.Config("zeronet.py --debug --debug_socket --ui_password hello".split(" ")) 17 | config_test.parse(silent=True, parse_config=False) 18 | assert config_test.debug 19 | assert config_test.debug_socket 20 | with pytest.raises(AttributeError): 21 | config_test.ui_password 22 | 23 | # More complex test 24 | args = "zeronet.py --unknown_arg --debug --debug_socket --ui_restrict 127.0.0.1 1.2.3.4 " 25 | args += "--another_unknown argument --use_openssl False siteSign address privatekey --inner_path users/content.json" 26 | config_test = Config.Config(args.split(" ")) 27 | config_test.parse(silent=True, parse_config=False) 28 | assert config_test.debug 29 | assert "1.2.3.4" in config_test.ui_restrict 30 | assert not config_test.use_openssl 31 | assert config_test.inner_path == "users/content.json" 32 | -------------------------------------------------------------------------------- /src/Test/TestCryptConnection.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from Config import config 4 | from Crypt import CryptConnection 5 | 6 | 7 | class TestCryptConnection: 8 | def testSslCert(self): 9 | # Remove old certs 10 | if os.path.isfile("%s/cert-rsa.pem" % config.data_dir): 11 | os.unlink("%s/cert-rsa.pem" % config.data_dir) 12 | if os.path.isfile("%s/key-rsa.pem" % config.data_dir): 13 | os.unlink("%s/key-rsa.pem" % config.data_dir) 14 | 15 | # Generate certs 16 | CryptConnection.manager.loadCerts() 17 | 18 | assert "tls-rsa" in CryptConnection.manager.crypt_supported 19 | assert CryptConnection.manager.selectCrypt(["tls-rsa", "unknown"]) == "tls-rsa" # It should choose the known crypt 20 | 21 | # Check openssl cert generation 22 | assert os.path.isfile("%s/cert-rsa.pem" % config.data_dir) 23 | assert os.path.isfile("%s/key-rsa.pem" % config.data_dir) 24 | -------------------------------------------------------------------------------- /src/Test/TestCryptHash.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from Crypt import CryptHash 4 | 5 | sha512t_sum_hex = "2e9466d8aa1f340c91203b4ddbe9b6669879616a1b8e9571058a74195937598d" 6 | sha512t_sum_bin = b".\x94f\xd8\xaa\x1f4\x0c\x91 ;M\xdb\xe9\xb6f\x98yaj\x1b\x8e\x95q\x05\x8at\x19Y7Y\x8d" 7 | sha256_sum_hex = "340cd04be7f530e3a7c1bc7b24f225ba5762ec7063a56e1ae01a30d56722e5c3" 8 | 9 | 10 | class TestCryptBitcoin: 11 | 12 | def testSha(self, site): 13 | file_path = site.storage.getPath("dbschema.json") 14 | assert CryptHash.sha512sum(file_path) == sha512t_sum_hex 15 | assert CryptHash.sha512sum(open(file_path, "rb")) == sha512t_sum_hex 16 | assert CryptHash.sha512sum(open(file_path, "rb"), format="digest") == sha512t_sum_bin 17 | 18 | assert CryptHash.sha256sum(file_path) == sha256_sum_hex 19 | assert CryptHash.sha256sum(open(file_path, "rb")) == sha256_sum_hex 20 | 21 | with open(file_path, "rb") as f: 22 | hash = CryptHash.Sha512t(f.read(100)) 23 | hash.hexdigest() != sha512t_sum_hex 24 | hash.update(f.read(1024 * 1024)) 25 | assert hash.hexdigest() == sha512t_sum_hex 26 | 27 | def testRandom(self): 28 | assert len(CryptHash.random(64)) == 64 29 | assert CryptHash.random() != CryptHash.random() 30 | assert bytes.fromhex(CryptHash.random(encoding="hex")) 31 | assert base64.b64decode(CryptHash.random(encoding="base64")) 32 | -------------------------------------------------------------------------------- /src/Test/TestDbQuery.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from Db.DbQuery import DbQuery 4 | 5 | 6 | class TestDbQuery: 7 | def testParse(self): 8 | query_text = """ 9 | SELECT 10 | 'comment' AS type, 11 | date_added, post.title AS title, 12 | keyvalue.value || ': ' || comment.body AS body, 13 | '?Post:' || comment.post_id || '#Comments' AS url 14 | FROM 15 | comment 16 | LEFT JOIN json USING (json_id) 17 | LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') 18 | LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') 19 | LEFT JOIN post ON (comment.post_id = post.post_id) 20 | WHERE 21 | post.date_added > 123 22 | ORDER BY 23 | date_added DESC 24 | LIMIT 20 25 | """ 26 | query = DbQuery(query_text) 27 | assert query.parts["LIMIT"] == "20" 28 | assert query.fields["body"] == "keyvalue.value || ': ' || comment.body" 29 | assert re.sub("[ \r\n]", "", str(query)) == re.sub("[ \r\n]", "", query_text) 30 | query.wheres.append("body LIKE '%hello%'") 31 | assert "body LIKE '%hello%'" in str(query) 32 | -------------------------------------------------------------------------------- /src/Test/TestDiff.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from util import Diff 4 | 5 | 6 | class TestDiff: 7 | def testDiff(self): 8 | assert Diff.diff( 9 | [], 10 | ["one", "two", "three"] 11 | ) == [("+", ["one", "two","three"])] 12 | 13 | assert Diff.diff( 14 | ["one", "two", "three"], 15 | ["one", "two", "three", "four", "five"] 16 | ) == [("=", 11), ("+", ["four", "five"])] 17 | 18 | assert Diff.diff( 19 | ["one", "two", "three", "six"], 20 | ["one", "two", "three", "four", "five", "six"] 21 | ) == [("=", 11), ("+", ["four", "five"]), ("=", 3)] 22 | 23 | assert Diff.diff( 24 | ["one", "two", "three", "hmm", "six"], 25 | ["one", "two", "three", "four", "five", "six"] 26 | ) == [("=", 11), ("-", 3), ("+", ["four", "five"]), ("=", 3)] 27 | 28 | assert Diff.diff( 29 | ["one", "two", "three"], 30 | [] 31 | ) == [("-", 11)] 32 | 33 | def testUtf8(self): 34 | assert Diff.diff( 35 | ["one", "\xe5\xad\xa6\xe4\xb9\xa0\xe4\xb8\x8b", "two", "three"], 36 | ["one", "\xe5\xad\xa6\xe4\xb9\xa0\xe4\xb8\x8b", "two", "three", "four", "five"] 37 | ) == [("=", 20), ("+", ["four", "five"])] 38 | 39 | def testDiffLimit(self): 40 | old_f = io.BytesIO(b"one\ntwo\nthree\nhmm\nsix") 41 | new_f = io.BytesIO(b"one\ntwo\nthree\nfour\nfive\nsix") 42 | actions = Diff.diff(list(old_f), list(new_f), limit=1024) 43 | assert actions 44 | 45 | old_f = io.BytesIO(b"one\ntwo\nthree\nhmm\nsix") 46 | new_f = io.BytesIO(b"one\ntwo\nthree\nfour\nfive\nsix"*1024) 47 | actions = Diff.diff(list(old_f), list(new_f), limit=1024) 48 | assert actions is False 49 | 50 | def testPatch(self): 51 | old_f = io.BytesIO(b"one\ntwo\nthree\nhmm\nsix") 52 | new_f = io.BytesIO(b"one\ntwo\nthree\nfour\nfive\nsix") 53 | actions = Diff.diff( 54 | list(old_f), 55 | list(new_f) 56 | ) 57 | old_f.seek(0) 58 | assert Diff.patch(old_f, actions).getvalue() == new_f.getvalue() 59 | -------------------------------------------------------------------------------- /src/Test/TestFlag.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | from util.Flag import Flag 6 | 7 | class TestFlag: 8 | def testFlagging(self): 9 | flag = Flag() 10 | @flag.admin 11 | @flag.no_multiuser 12 | def testFn(anything): 13 | return anything 14 | 15 | assert "admin" in flag.db["testFn"] 16 | assert "no_multiuser" in flag.db["testFn"] 17 | 18 | def testSubclassedFlagging(self): 19 | flag = Flag() 20 | class Test: 21 | @flag.admin 22 | @flag.no_multiuser 23 | def testFn(anything): 24 | return anything 25 | 26 | class SubTest(Test): 27 | pass 28 | 29 | assert "admin" in flag.db["testFn"] 30 | assert "no_multiuser" in flag.db["testFn"] 31 | 32 | def testInvalidFlag(self): 33 | flag = Flag() 34 | with pytest.raises(Exception) as err: 35 | @flag.no_multiuser 36 | @flag.unknown_flag 37 | def testFn(anything): 38 | return anything 39 | assert "Invalid flag" in str(err.value) 40 | -------------------------------------------------------------------------------- /src/Test/TestSafeRe.py: -------------------------------------------------------------------------------- 1 | from util import SafeRe 2 | 3 | import pytest 4 | 5 | 6 | class TestSafeRe: 7 | def testSafeMatch(self): 8 | assert SafeRe.match( 9 | "((js|css)/(?!all.(js|css))|data/users/.*db|data/users/.*/.*|data/archived|.*.py)", 10 | "js/ZeroTalk.coffee" 11 | ) 12 | assert SafeRe.match(".+/data.json", "data/users/1J3rJ8ecnwH2EPYa6MrgZttBNc61ACFiCj/data.json") 13 | 14 | @pytest.mark.parametrize("pattern", ["([a-zA-Z]+)*", "(a|aa)+*", "(a|a?)+", "(.*a){10}", "((?!json).)*$", r"(\w+\d+)+C"]) 15 | def testUnsafeMatch(self, pattern): 16 | with pytest.raises(SafeRe.UnsafePatternError) as err: 17 | SafeRe.match(pattern, "aaaaaaaaaaaaaaaaaaaaaaaa!") 18 | assert "Potentially unsafe" in str(err.value) 19 | 20 | @pytest.mark.parametrize("pattern", ["^(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)(.*a)$"]) 21 | def testUnsafeRepetition(self, pattern): 22 | with pytest.raises(SafeRe.UnsafePatternError) as err: 23 | SafeRe.match(pattern, "aaaaaaaaaaaaaaaaaaaaaaaa!") 24 | assert "More than" in str(err.value) 25 | -------------------------------------------------------------------------------- /src/Test/TestSiteStorage.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.usefixtures("resetSettings") 5 | class TestSiteStorage: 6 | def testWalk(self, site): 7 | # Rootdir 8 | walk_root = list(site.storage.walk("")) 9 | assert "content.json" in walk_root 10 | assert "css/all.css" in walk_root 11 | 12 | # Subdir 13 | assert list(site.storage.walk("data-default")) == ["data.json", "users/content-default.json"] 14 | 15 | def testList(self, site): 16 | # Rootdir 17 | list_root = list(site.storage.list("")) 18 | assert "content.json" in list_root 19 | assert "css/all.css" not in list_root 20 | 21 | # Subdir 22 | assert set(site.storage.list("data-default")) == set(["data.json", "users"]) 23 | 24 | def testDbRebuild(self, site): 25 | assert site.storage.rebuildDb() 26 | -------------------------------------------------------------------------------- /src/Test/TestUiWebsocket.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | 4 | @pytest.mark.usefixtures("resetSettings") 5 | class TestUiWebsocket: 6 | def testPermission(self, ui_websocket): 7 | res = ui_websocket.testAction("ping") 8 | assert res == "pong" 9 | 10 | res = ui_websocket.testAction("certList") 11 | assert "You don't have permission" in res["error"] 12 | -------------------------------------------------------------------------------- /src/Test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/__init__.py -------------------------------------------------------------------------------- /src/Test/coverage.ini: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | concurrency = gevent 4 | omit = 5 | src/lib/* 6 | src/Test/* 7 | 8 | [report] 9 | exclude_lines = 10 | pragma: no cover 11 | if __name__ == .__main__.: 12 | if config.debug: 13 | if config.debug_socket: 14 | if self.logging: 15 | def __repr__ 16 | -------------------------------------------------------------------------------- /src/Test/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = Test*.py 3 | addopts = -rsxX -v --durations=6 --no-print-logs --capture=fd 4 | markers = 5 | slow: mark a tests as slow. 6 | webtest: mark a test as a webtest. 7 | -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data-default/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "MyZeroBlog", 3 | "description": "My ZeroBlog.", 4 | "links": "- [Source code](https://github.com/HelloZeroNet)", 5 | "next_post_id": 1, 6 | "demo": false, 7 | "modified": 1432515193, 8 | "post": [ 9 | ] 10 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data-default/users/content-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": {}, 3 | "ignore": ".*", 4 | "modified": 1432466966.003, 5 | "signs": { 6 | "1BLogC9LN4oPDcruNz3qo1ysa133E9AGg8": "HChU28lG4MCnAiui6wDAaVCD4QUrgSy4zZ67+MMHidcUJRkLGnO3j4Eb1N0AWQ86nhSBwoOQf08Rha7gRyTDlAk=" 7 | }, 8 | "user_contents": { 9 | "cert_signers": { 10 | "zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ] 11 | }, 12 | "permission_rules": { 13 | ".*": { 14 | "files_allowed": "data.json", 15 | "max_size": 10000 16 | }, 17 | "bitid/.*@zeroid.bit": { "max_size": 40000 }, 18 | "bitmsg/.*@zeroid.bit": { "max_size": 15000 } 19 | }, 20 | "permissions": { 21 | "banexample@zeroid.bit": false, 22 | "nofish@zeroid.bit": { "max_size": 20000 } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/autoupdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/autoupdate.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/direct_domains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/direct_domains.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/domain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/domain.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/memory.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/multiuser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/multiuser.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/progressbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/progressbar.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/slides.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/slides.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/slots_memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/slots_memory.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/trayicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/trayicon.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zeroblog-comments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zeroblog-comments.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zeroid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zeroid.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zeroname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zeroname.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zerotalk-mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zerotalk-mark.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zerotalk-upvote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zerotalk-upvote.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zerotalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/img/zerotalk.png -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/optional.txt: -------------------------------------------------------------------------------- 1 | hello! -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/test_include/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT", 3 | "files": { 4 | "data.json": { 5 | "sha512": "369d4e780cc80504285f13774ca327fe725eed2d813aad229e62356b07365906", 6 | "size": 505 7 | } 8 | }, 9 | "inner_path": "data/test_include/content.json", 10 | "modified": 1470340816.513, 11 | "signs": { 12 | "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT": "GxF2ZD0DaMx+CuxafnnRx+IkWTrXubcmTHaJIPyemFpzCvbSo6DyjstN8T3qngFhYIZI/MkcG4ogStG0PLv6p3w=" 13 | } 14 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/test_include/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "next_topic_id": 1, 3 | "topics": [], 4 | "next_message_id": 5, 5 | "comments": { 6 | "1@2": [ 7 | { 8 | "comment_id": 1, 9 | "body": "New user test!", 10 | "added": 1423442049 11 | }, 12 | { 13 | "comment_id": 2, 14 | "body": "test 321", 15 | "added": 1423531445 16 | }, 17 | { 18 | "comment_id": 3, 19 | "body": "0.2.4 test.", 20 | "added": 1424133003 21 | } 22 | ] 23 | }, 24 | "topic_votes": { 25 | "1@2": 1, 26 | "1@6": 1, 27 | "1@69": 1, 28 | "607@69": 1 29 | }, 30 | "comment_votes": { 31 | "35@2": 1, 32 | "7@64": 1, 33 | "8@64": 1, 34 | "50@2": 1, 35 | "13@77": 1 36 | } 37 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "cert_auth_type": "web", 3 | "cert_sign": "G4YB7y749GI6mJboyI7cNNfyMwOS0rcVXLmgq8qmCC4TCaRqup3TGWm8hzeru7+B5iXhq19Ruz286bNVKgNbnwU=", 4 | "cert_user_id": "newzeroid@zeroid.bit", 5 | "files": { 6 | "data.json": { 7 | "sha512": "2378ef20379f1db0c3e2a803bfbfda2b68515968b7e311ccc604406168969d34", 8 | "size": 161 9 | } 10 | }, 11 | "modified": 1432554679.913, 12 | "signs": { 13 | "1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q": "GzX/Ht6ms1dOnqB3kVENvDnxpH+mqA0Zlg3hWy0iwgxpyxWcA4zgmwxcEH41BN9RrvCaxgSd2m1SG1/8qbQPzDY=" 14 | } 15 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "next_comment_id": 2, 3 | "comment": [ 4 | { 5 | "comment_id": 1, 6 | "body": "Test me!", 7 | "post_id": 40, 8 | "date_added": 1432554679 9 | } 10 | ], 11 | "comment_vote": {} 12 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1CjfbrbwtP8Y2QjPy12vpTATkUT7oSiPQ9/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT", 3 | "cert_auth_type": "web", 4 | "cert_sign": "HBsTrjTmv+zD1iY93tSci8n9DqdEtYwzxJmRppn4/b+RYktcANGm5tXPOb+Duw3AJcgWDcGUvQVgN1D9QAwIlCw=", 5 | "cert_user_id": "toruser@zeroid.bit", 6 | "files": { 7 | "data.json": { 8 | "sha512": "4868b5e6d70a55d137db71c2e276bda80437e0235ac670962acc238071296b45", 9 | "size": 168 10 | } 11 | }, 12 | "files_optional": { 13 | "peanut-butter-jelly-time.gif": { 14 | "sha512": "a238fd27bda2a06f07f9f246954b34dcf82e6472aebdecc2c5dc1f01a50721ef", 15 | "size": 1606 16 | } 17 | }, 18 | "inner_path": "data/users/1CjfbrbwtP8Y2QjPy12vpTATkUT7oSiPQ9/content.json", 19 | "modified": 1470340817.676, 20 | "optional": ".*\\.(jpg|png|gif)", 21 | "signs": { 22 | "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT": "G6UOG3ne1hVe3mDGXHnWX8A1vKzH0XHD6LGMsshvNFVXGn003IFNLUL9dlb3XXJf3tyJGZncvGobzNpwBib08QY=" 23 | } 24 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1CjfbrbwtP8Y2QjPy12vpTATkUT7oSiPQ9/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "next_comment_id": 2, 3 | "comment": [ 4 | { 5 | "comment_id": 1, 6 | "body": "hello from Tor!", 7 | "post_id": 38, 8 | "date_added": 1432491109 9 | } 10 | ], 11 | "comment_vote": {} 12 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1CjfbrbwtP8Y2QjPy12vpTATkUT7oSiPQ9/peanut-butter-jelly-time.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1CjfbrbwtP8Y2QjPy12vpTATkUT7oSiPQ9/peanut-butter-jelly-time.gif -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT", 3 | "cert_auth_type": "web", 4 | "cert_sign": "HBsTrjTmv+zD1iY93tSci8n9DqdEtYwzxJmRppn4/b+RYktcANGm5tXPOb+Duw3AJcgWDcGUvQVgN1D9QAwIlCw=", 5 | "cert_user_id": "toruser@zeroid.bit", 6 | "files": { 7 | "data.json": { 8 | "sha512": "4868b5e6d70a55d137db71c2e276bda80437e0235ac670962acc238071296b45", 9 | "size": 168 10 | } 11 | }, 12 | "inner_path": "data/users/1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C/content.json", 13 | "modified": 1470340818.389, 14 | "signs": { 15 | "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT": "G6oCzql6KWKAq2aSmZ1pm4SqvwL3e3LRdWxsvILrDc6VWpGZmVgbNn5qW18bA7fewhtA/oKc5+yYjGlTLLOWrB4=" 16 | } 17 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "next_comment_id": 2, 3 | "comment": [ 4 | { 5 | "comment_id": 1, 6 | "body": "hello from Tor!", 7 | "post_id": 38, 8 | "date_added": 1432491109 9 | } 10 | ], 11 | "comment_vote": {} 12 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/data/users/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT", 3 | "files": {}, 4 | "ignore": ".*", 5 | "inner_path": "data/users/content.json", 6 | "modified": 1470340815.228, 7 | "signs": { 8 | "1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT": "G25hsrlyTOy8PHKuovKDRC7puoBj/OLIZ3U4OJ01izkhE1BBQ+TOgxX96+HXoZGme2/P4IdEnYjc1rqIZ6O+nFk=" 9 | }, 10 | "user_contents": { 11 | "cert_signers": { 12 | "zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ] 13 | }, 14 | "permission_rules": { 15 | ".*": { 16 | "files_allowed": "data.json", 17 | "files_allowed_optional": ".*\\.(png|jpg|gif)", 18 | "max_size": 10000, 19 | "max_size_optional": 10000000, 20 | "signers": [ "14wgQ4VDDZNoRMFF4yCDuTrBSHmYhL3bet" ] 21 | }, 22 | "bitid/.*@zeroid.bit": { "max_size": 40000 }, 23 | "bitmsg/.*@zeroid.bit": { "max_size": 15000 } 24 | }, 25 | "permissions": { 26 | "bad@zeroid.bit": false, 27 | "nofish@zeroid.bit": { "max_size": 100000 } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/dbschema.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_name": "ZeroBlog", 3 | "db_file": "data/zeroblog.db", 4 | "version": 2, 5 | "maps": { 6 | "users/.+/data.json": { 7 | "to_table": [ 8 | "comment", 9 | {"node": "comment_vote", "table": "comment_vote", "key_col": "comment_uri", "val_col": "vote"} 10 | ] 11 | }, 12 | "users/.+/content.json": { 13 | "to_keyvalue": [ "cert_user_id" ] 14 | }, 15 | "data.json": { 16 | "to_table": [ "post" ], 17 | "to_keyvalue": [ "title", "description", "links", "next_post_id", "demo", "modified" ] 18 | } 19 | 20 | }, 21 | "tables": { 22 | "comment": { 23 | "cols": [ 24 | ["comment_id", "INTEGER"], 25 | ["post_id", "INTEGER"], 26 | ["body", "TEXT"], 27 | ["date_added", "INTEGER"], 28 | ["json_id", "INTEGER REFERENCES json (json_id)"] 29 | ], 30 | "indexes": ["CREATE UNIQUE INDEX comment_key ON comment(json_id, comment_id)", "CREATE INDEX comment_post_id ON comment(post_id)"], 31 | "schema_changed": 1426195823 32 | }, 33 | "comment_vote": { 34 | "cols": [ 35 | ["comment_uri", "TEXT"], 36 | ["vote", "INTEGER"], 37 | ["json_id", "INTEGER REFERENCES json (json_id)"] 38 | ], 39 | "indexes": ["CREATE INDEX comment_vote_comment_uri ON comment_vote(comment_uri)", "CREATE INDEX comment_vote_json_id ON comment_vote(json_id)"], 40 | "schema_changed": 1426195822 41 | }, 42 | "post": { 43 | "cols": [ 44 | ["post_id", "INTEGER"], 45 | ["title", "TEXT"], 46 | ["body", "TEXT"], 47 | ["date_published", "INTEGER"], 48 | ["json_id", "INTEGER REFERENCES json (json_id)"] 49 | ], 50 | "indexes": ["CREATE UNIQUE INDEX post_uri ON post(json_id, post_id)", "CREATE INDEX post_id ON post(post_id)"], 51 | "schema_changed": 1426195823 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Test/testdata/1TeSTvb4w2PWE81S2rEELgmX2GCCExQGT-original/img/loading.gif -------------------------------------------------------------------------------- /src/Tor/__init__.py: -------------------------------------------------------------------------------- 1 | from .TorManager import TorManager -------------------------------------------------------------------------------- /src/Translate/__init__.py: -------------------------------------------------------------------------------- 1 | from .Translate import * -------------------------------------------------------------------------------- /src/Ui/__init__.py: -------------------------------------------------------------------------------- 1 | from .UiServer import UiServer 2 | from .UiRequest import UiRequest 3 | from .UiWebsocket import UiWebsocket -------------------------------------------------------------------------------- /src/Ui/media/Fixbutton.coffee: -------------------------------------------------------------------------------- 1 | class Fixbutton 2 | constructor: -> 3 | @dragging = false 4 | $(".fixbutton-bg").on "mouseover", -> 5 | $(".fixbutton-bg").stop().animate({"scale": 0.7}, 800, "easeOutElastic") 6 | $(".fixbutton-burger").stop().animate({"opacity": 1.5, "left": 0}, 800, "easeOutElastic") 7 | $(".fixbutton-text").stop().animate({"opacity": 0, "left": 20}, 300, "easeOutCubic") 8 | 9 | $(".fixbutton-bg").on "mouseout", -> 10 | if $(".fixbutton").hasClass("dragging") 11 | return true 12 | $(".fixbutton-bg").stop().animate({"scale": 0.6}, 300, "easeOutCubic") 13 | $(".fixbutton-burger").stop().animate({"opacity": 0, "left": -20}, 300, "easeOutCubic") 14 | $(".fixbutton-text").stop().animate({"opacity": 0.9, "left": 0}, 300, "easeOutBack") 15 | 16 | 17 | ###$(".fixbutton-bg").on "click", -> 18 | return false 19 | ### 20 | 21 | $(".fixbutton-bg").on "mousedown", -> 22 | # $(".fixbutton-burger").stop().animate({"scale": 0.7, "left": 0}, 300, "easeOutCubic") 23 | #$("#inner-iframe").toggleClass("back") 24 | #$(".wrapper-iframe").stop().animate({"scale": 0.9}, 600, "easeOutCubic") 25 | #$("body").addClass("back") 26 | 27 | $(".fixbutton-bg").on "mouseup", -> 28 | # $(".fixbutton-burger").stop().animate({"scale": 1, "left": 0}, 600, "easeOutElastic") 29 | 30 | 31 | 32 | window.Fixbutton = Fixbutton 33 | -------------------------------------------------------------------------------- /src/Ui/media/Infopanel.coffee: -------------------------------------------------------------------------------- 1 | class Infopanel 2 | constructor: (@elem) -> 3 | @visible = false 4 | 5 | show: (closed=false) => 6 | @elem.parent().addClass("visible") 7 | if closed 8 | @close() 9 | else 10 | @open() 11 | 12 | unfold: => 13 | @elem.toggleClass("unfolded") 14 | return false 15 | 16 | updateEvents: => 17 | @elem.off("click") 18 | @elem.find(".close").off("click") 19 | @elem.find(".line").off("click") 20 | 21 | @elem.find(".line").on("click", @unfold) 22 | 23 | if @elem.hasClass("closed") 24 | @elem.on "click", => 25 | @onOpened() 26 | @open() 27 | else 28 | @elem.find(".close").on "click", => 29 | @onClosed() 30 | @close() 31 | 32 | hide: => 33 | @elem.parent().removeClass("visible") 34 | 35 | close: => 36 | @elem.addClass("closed") 37 | @updateEvents() 38 | return false 39 | 40 | open: => 41 | @elem.removeClass("closed") 42 | @updateEvents() 43 | return false 44 | 45 | setTitle: (line1, line2) => 46 | @elem.find(".line-1").text(line1) 47 | @elem.find(".line-2").text(line2) 48 | 49 | setClosedNum: (num) => 50 | @elem.find(".closed-num").text(num) 51 | 52 | setAction: (title, func) => 53 | @elem.find(".button").text(title).off("click").on("click", func) 54 | 55 | 56 | 57 | window.Infopanel = Infopanel 58 | -------------------------------------------------------------------------------- /src/Ui/media/WrapperZeroFrame.coffee: -------------------------------------------------------------------------------- 1 | class WrapperZeroFrame 2 | constructor: (wrapper) -> 3 | @wrapperCmd = wrapper.cmd 4 | @wrapperResponse = wrapper.ws.response 5 | console.log "WrapperZeroFrame", wrapper 6 | 7 | cmd: (cmd, params={}, cb=null) => 8 | @wrapperCmd(cmd, params, cb) 9 | 10 | response: (to, result) => 11 | @wrapperResponse(to, result) 12 | 13 | isProxyRequest: -> 14 | return window.location.pathname == "/" 15 | 16 | certSelectGotoSite: (elem) => 17 | href = $(elem).attr("href") 18 | if @isProxyRequest() # Fix for proxy request 19 | $(elem).attr("href", "http://zero#{href}") 20 | 21 | 22 | window.zeroframe = new WrapperZeroFrame(window.wrapper) 23 | -------------------------------------------------------------------------------- /src/Ui/media/ZeroSiteTheme.coffee: -------------------------------------------------------------------------------- 1 | DARK = "(prefers-color-scheme: dark)" 2 | LIGHT = "(prefers-color-scheme: light)" 3 | 4 | mqDark = window.matchMedia(DARK) 5 | mqLight = window.matchMedia(LIGHT) 6 | 7 | 8 | changeColorScheme = (theme) -> 9 | zeroframe.cmd "userGetGlobalSettings", [], (user_settings) -> 10 | if user_settings.theme != theme 11 | user_settings.theme = theme 12 | zeroframe.cmd "userSetGlobalSettings", [user_settings], (status) -> 13 | if status == "ok" 14 | location.reload() 15 | return 16 | return 17 | return 18 | 19 | 20 | displayNotification = ({matches, media}) -> 21 | if !matches 22 | return 23 | 24 | zeroframe.cmd "siteInfo", [], (site_info) -> 25 | if "ADMIN" in site_info.settings.permissions 26 | zeroframe.cmd "wrapperNotification", ["info", "Your system's theme has been changed.
Please reload site to use it."] 27 | else 28 | zeroframe.cmd "wrapperNotification", ["info", "Your system's theme has been changed.
Please open ZeroHello to use it."] 29 | return 30 | return 31 | 32 | 33 | detectColorScheme = -> 34 | if mqDark.matches 35 | changeColorScheme("dark") 36 | else if mqLight.matches 37 | changeColorScheme("light") 38 | 39 | mqDark.addListener(displayNotification) 40 | mqLight.addListener(displayNotification) 41 | 42 | return 43 | 44 | 45 | zeroframe.cmd "userGetGlobalSettings", [], (user_settings) -> 46 | if user_settings.use_system_theme == true 47 | detectColorScheme() 48 | 49 | return 50 | -------------------------------------------------------------------------------- /src/Ui/media/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/apple-touch-icon.png -------------------------------------------------------------------------------- /src/Ui/media/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/favicon.ico -------------------------------------------------------------------------------- /src/Ui/media/img/favicon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/favicon.psd -------------------------------------------------------------------------------- /src/Ui/media/img/loading-circle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/loading-circle.gif -------------------------------------------------------------------------------- /src/Ui/media/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/loading.gif -------------------------------------------------------------------------------- /src/Ui/media/img/logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/Ui/media/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/logo.png -------------------------------------------------------------------------------- /src/Ui/media/img/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/media/img/logo.psd -------------------------------------------------------------------------------- /src/Ui/media/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/Ui/media/lib/RateLimit.coffee: -------------------------------------------------------------------------------- 1 | limits = {} 2 | call_after_interval = {} 3 | window.RateLimit = (interval, fn) -> 4 | if not limits[fn] 5 | call_after_interval[fn] = false 6 | fn() # First call is not delayed 7 | limits[fn] = setTimeout (-> 8 | if call_after_interval[fn] 9 | fn() 10 | delete limits[fn] 11 | delete call_after_interval[fn] 12 | ), interval 13 | else # Called within iterval, delay the call 14 | call_after_interval[fn] = true 15 | -------------------------------------------------------------------------------- /src/Ui/media/lib/Translate.coffee: -------------------------------------------------------------------------------- 1 | window._ = (s) -> return s -------------------------------------------------------------------------------- /src/Ui/media/lib/jquery.cssanim.js: -------------------------------------------------------------------------------- 1 | jQuery.cssHooks['scale'] = { 2 | get: function(elem, computed) { 3 | var match = window.getComputedStyle(elem)[transform_property].match("[0-9\.]+") 4 | if (match) { 5 | var scale = parseFloat(match[0]) 6 | return scale 7 | } else { 8 | return 1.0 9 | } 10 | }, 11 | set: function(elem, val) { 12 | //var transforms = $(elem).css("transform").match(/[0-9\.]+/g) 13 | var transforms = window.getComputedStyle(elem)[transform_property].match(/[0-9\.]+/g) 14 | if (transforms) { 15 | transforms[0] = val 16 | transforms[3] = val 17 | //$(elem).css("transform", 'matrix('+transforms.join(", ")+")") 18 | elem.style[transform_property] = 'matrix('+transforms.join(", ")+')' 19 | } else { 20 | elem.style[transform_property] = "scale("+val+")" 21 | } 22 | } 23 | } 24 | 25 | jQuery.fx.step.scale = function(fx) { 26 | jQuery.cssHooks['scale'].set(fx.elem, fx.now) 27 | }; 28 | 29 | 30 | if (window.getComputedStyle(document.body).transform) { 31 | transform_property = "transform" 32 | } else { 33 | transform_property = "webkitTransform" 34 | } 35 | -------------------------------------------------------------------------------- /src/Ui/media/lib/jquery.csslater.coffee: -------------------------------------------------------------------------------- 1 | jQuery.fn.readdClass = (class_name) -> 2 | elem = @ 3 | elem.removeClass class_name 4 | setTimeout ( -> 5 | elem.addClass class_name 6 | ), 1 7 | return @ 8 | 9 | jQuery.fn.removeLater = (time = 500) -> 10 | elem = @ 11 | setTimeout ( -> 12 | elem.remove() 13 | ), time 14 | return @ 15 | 16 | jQuery.fn.hideLater = (time = 500) -> 17 | elem = @ 18 | setTimeout ( -> 19 | if elem.css("opacity") == 0 20 | elem.css("display", "none") 21 | ), time 22 | return @ 23 | 24 | jQuery.fn.addClassLater = (class_name, time = 5) -> 25 | elem = @ 26 | setTimeout ( -> 27 | elem.addClass(class_name) 28 | ), time 29 | return @ 30 | 31 | jQuery.fn.cssLater = (name, val, time = 500) -> 32 | elem = @ 33 | setTimeout ( -> 34 | elem.css name, val 35 | ), time 36 | return @ -------------------------------------------------------------------------------- /src/Ui/template/site_add.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Add new site 4 | 5 | 6 | 7 | 27 | 28 |
29 |

Add new site

30 |

Please confirm before adding a new site to the client

31 |
32 | 33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/User/__init__.py: -------------------------------------------------------------------------------- 1 | from .User import User 2 | -------------------------------------------------------------------------------- /src/Worker/__init__.py: -------------------------------------------------------------------------------- 1 | from .Worker import Worker 2 | from .WorkerManager import WorkerManager 3 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/__init__.py -------------------------------------------------------------------------------- /src/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/lib/__init__.py -------------------------------------------------------------------------------- /src/lib/bencode_open/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Machugovskiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib/cssvendor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/lib/cssvendor/__init__.py -------------------------------------------------------------------------------- /src/lib/cssvendor/cssvendor.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def prefix(content): 5 | content = re.sub( 6 | b"@keyframes (.*? {.*?}\s*})", b"@keyframes \\1\n@-webkit-keyframes \\1\n@-moz-keyframes \\1\n", 7 | content, flags=re.DOTALL 8 | ) 9 | content = re.sub( 10 | b'([^-\*])(border-radius|box-shadow|appearance|transition|animation|box-sizing|' + 11 | b'backface-visibility|transform|filter|perspective|animation-[a-z-]+): (.*?)([;}])', 12 | b'\\1-webkit-\\2: \\3; -moz-\\2: \\3; -o-\\2: \\3; -ms-\\2: \\3; \\2: \\3 \\4', content 13 | ) 14 | content = re.sub( 15 | b'(?<=[^a-zA-Z0-9-])([a-zA-Z0-9-]+): {0,1}(linear-gradient)\((.*?)(\)[;\n])', 16 | b'\\1: -webkit-\\2(\\3);' + 17 | b'\\1: -moz-\\2(\\3);' + 18 | b'\\1: -o-\\2(\\3);' + 19 | b'\\1: -ms-\\2(\\3);' + 20 | b'\\1: \\2(\\3);', content 21 | ) 22 | return content 23 | 24 | if __name__ == "__main__": 25 | print(prefix(b""" 26 | .test { 27 | border-radius: 5px; 28 | background: linear-gradient(red, blue); 29 | } 30 | 31 | 32 | @keyframes flip { 33 | 0% { transform: perspective(120px) rotateX(0deg) rotateY(0deg); } 34 | 50% { transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) } 35 | 100% { transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); } 36 | } 37 | 38 | 39 | """).decode("utf8")) 40 | -------------------------------------------------------------------------------- /src/lib/libsecp256k1message/__init__.py: -------------------------------------------------------------------------------- 1 | from .libsecp256k1message import * -------------------------------------------------------------------------------- /src/lib/openssl/openssl.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 2048 3 | default_keyfile = server-key.pem 4 | distinguished_name = subject 5 | req_extensions = req_ext 6 | x509_extensions = x509_ext 7 | string_mask = utf8only 8 | 9 | # The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description). 10 | # Its sort of a mashup. For example, RFC 4514 does not provide emailAddress. 11 | [ subject ] 12 | countryName = US 13 | stateOrProvinceName = NY 14 | localityName = New York 15 | organizationName = Example, LLC 16 | 17 | # Use a friendly name here because its presented to the user. The server's DNS 18 | # names are placed in Subject Alternate Names. Plus, DNS names here is deprecated 19 | # by both IETF and CA/Browser Forums. If you place a DNS name here, then you 20 | # must include the DNS name in the SAN too (otherwise, Chrome and others that 21 | # strictly follow the CA/Browser Baseline Requirements will fail). 22 | commonName = Example Company 23 | 24 | emailAddress = test@example.com 25 | 26 | # Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ... 27 | [ x509_ext ] 28 | 29 | subjectKeyIdentifier = hash 30 | authorityKeyIdentifier = keyid,issuer 31 | 32 | basicConstraints = CA:FALSE 33 | keyUsage = digitalSignature, keyEncipherment 34 | extendedKeyUsage = clientAuth, serverAuth 35 | subjectAltName = @alternate_names 36 | 37 | # RFC 5280, Section 4.2.1.12 makes EKU optional 38 | # CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused 39 | # extendedKeyUsage = serverAuth, clientAuth 40 | 41 | # Section req_ext is used when generating a certificate signing request. I.e., openssl req ... 42 | [ req_ext ] 43 | 44 | subjectKeyIdentifier = hash 45 | 46 | basicConstraints = CA:FALSE 47 | keyUsage = digitalSignature, keyEncipherment 48 | extendedKeyUsage = clientAuth, serverAuth 49 | subjectAltName = @alternate_names 50 | 51 | # RFC 5280, Section 4.2.1.12 makes EKU optional 52 | # CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused 53 | # extendedKeyUsage = serverAuth, clientAuth 54 | 55 | [ alternate_names ] 56 | 57 | DNS.1 = $ENV::CN 58 | DNS.2 = www.$ENV::CN -------------------------------------------------------------------------------- /src/lib/pyaes/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Richard Moore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/lib/pyaes/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 28 | 29 | 30 | # Supported key sizes: 31 | # 128-bit 32 | # 192-bit 33 | # 256-bit 34 | 35 | 36 | # Supported modes of operation: 37 | # ECB - Electronic Codebook 38 | # CBC - Cipher-Block Chaining 39 | # CFB - Cipher Feedback 40 | # OFB - Output Feedback 41 | # CTR - Counter 42 | 43 | # See the README.md for API details and general information. 44 | 45 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 46 | # https://www.dlitz.net/software/pycrypto/ 47 | 48 | 49 | VERSION = [1, 3, 0] 50 | 51 | from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter 52 | from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter 53 | from .blockfeeder import PADDING_NONE, PADDING_DEFAULT 54 | -------------------------------------------------------------------------------- /src/lib/pyaes/util.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # Why to_bufferable? 24 | # Python 3 is very different from Python 2.x when it comes to strings of text 25 | # and strings of bytes; in Python 3, strings of bytes do not exist, instead to 26 | # represent arbitrary binary data, we must use the "bytes" object. This method 27 | # ensures the object behaves as we need it to. 28 | 29 | def to_bufferable(binary): 30 | return binary 31 | 32 | def _get_byte(c): 33 | return ord(c) 34 | 35 | try: 36 | xrange 37 | except: 38 | 39 | def to_bufferable(binary): 40 | if isinstance(binary, bytes): 41 | return binary 42 | return bytes(ord(b) for b in binary) 43 | 44 | def _get_byte(c): 45 | return c 46 | 47 | def append_PKCS7_padding(data): 48 | pad = 16 - (len(data) % 16) 49 | return data + to_bufferable(chr(pad) * pad) 50 | 51 | def strip_PKCS7_padding(data): 52 | if len(data) % 16 != 0: 53 | raise ValueError("invalid length") 54 | 55 | pad = _get_byte(data[-1]) 56 | 57 | if pad > 16: 58 | raise ValueError("invalid padding byte") 59 | 60 | return data[:-pad] 61 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Machugovskiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | Additionally, the following licenses must be preserved: 25 | 26 | - ripemd implementation is licensed under BSD-3 by Markus Friedl, see `_ripemd.py`; 27 | - jacobian curve implementation is dual-licensed under MIT or public domain license, see `_jacobian.py`. 28 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["aes", "ecc", "rsa"] 2 | 3 | try: 4 | from .openssl import aes, ecc, rsa 5 | except OSError: 6 | from .fallback import aes, ecc, rsa 7 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/_aes.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=import-outside-toplevel 2 | 3 | class AES: 4 | def __init__(self, backend, fallback=None): 5 | self._backend = backend 6 | self._fallback = fallback 7 | 8 | 9 | def get_algo_key_length(self, algo): 10 | if algo.count("-") != 2: 11 | raise ValueError("Invalid algorithm name") 12 | try: 13 | return int(algo.split("-")[1]) // 8 14 | except ValueError: 15 | raise ValueError("Invalid algorithm name") from None 16 | 17 | 18 | def new_key(self, algo="aes-256-cbc"): 19 | if not self._backend.is_algo_supported(algo): 20 | if self._fallback is None: 21 | raise ValueError("This algorithm is not supported") 22 | return self._fallback.new_key(algo) 23 | return self._backend.random(self.get_algo_key_length(algo)) 24 | 25 | 26 | def encrypt(self, data, key, algo="aes-256-cbc"): 27 | if not self._backend.is_algo_supported(algo): 28 | if self._fallback is None: 29 | raise ValueError("This algorithm is not supported") 30 | return self._fallback.encrypt(data, key, algo) 31 | 32 | key_length = self.get_algo_key_length(algo) 33 | if len(key) != key_length: 34 | raise ValueError("Expected key to be {} bytes, got {} bytes".format(key_length, len(key))) 35 | 36 | return self._backend.encrypt(data, key, algo) 37 | 38 | 39 | def decrypt(self, ciphertext, iv, key, algo="aes-256-cbc"): 40 | if not self._backend.is_algo_supported(algo): 41 | if self._fallback is None: 42 | raise ValueError("This algorithm is not supported") 43 | return self._fallback.decrypt(ciphertext, iv, key, algo) 44 | 45 | key_length = self.get_algo_key_length(algo) 46 | if len(key) != key_length: 47 | raise ValueError("Expected key to be {} bytes, got {} bytes".format(key_length, len(key))) 48 | 49 | return self._backend.decrypt(ciphertext, iv, key, algo) 50 | 51 | 52 | def get_backend(self): 53 | return self._backend.get_backend() 54 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/fallback/__init__.py: -------------------------------------------------------------------------------- 1 | from .aes import aes 2 | from .ecc import ecc 3 | from .rsa import rsa 4 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/fallback/_util.py: -------------------------------------------------------------------------------- 1 | def int_to_bytes(raw, length): 2 | data = [] 3 | for _ in range(length): 4 | data.append(raw % 256) 5 | raw //= 256 6 | return bytes(data[::-1]) 7 | 8 | 9 | def bytes_to_int(data): 10 | raw = 0 11 | for byte in data: 12 | raw = raw * 256 + byte 13 | return raw 14 | 15 | 16 | def legendre(a, p): 17 | res = pow(a, (p - 1) // 2, p) 18 | if res == p - 1: 19 | return -1 20 | else: 21 | return res 22 | 23 | 24 | def inverse(a, n): 25 | if a == 0: 26 | return 0 27 | lm, hm = 1, 0 28 | low, high = a % n, n 29 | while low > 1: 30 | r = high // low 31 | nm, new = hm - lm * r, high - low * r 32 | lm, low, hm, high = nm, new, lm, low 33 | return lm % n 34 | 35 | 36 | def square_root_mod_prime(n, p): 37 | if n == 0: 38 | return 0 39 | if p == 2: 40 | return n # We should never get here but it might be useful 41 | if legendre(n, p) != 1: 42 | raise ValueError("No square root") 43 | # Optimizations 44 | if p % 4 == 3: 45 | return pow(n, (p + 1) // 4, p) 46 | # 1. By factoring out powers of 2, find Q and S such that p - 1 = 47 | # Q * 2 ** S with Q odd 48 | q = p - 1 49 | s = 0 50 | while q % 2 == 0: 51 | q //= 2 52 | s += 1 53 | # 2. Search for z in Z/pZ which is a quadratic non-residue 54 | z = 1 55 | while legendre(z, p) != -1: 56 | z += 1 57 | m, c, t, r = s, pow(z, q, p), pow(n, q, p), pow(n, (q + 1) // 2, p) 58 | while True: 59 | if t == 0: 60 | return 0 61 | elif t == 1: 62 | return r 63 | # Use repeated squaring to find the least i, 0 < i < M, such 64 | # that t ** (2 ** i) = 1 65 | t_sq = t 66 | i = 0 67 | for i in range(1, m): 68 | t_sq = t_sq * t_sq % p 69 | if t_sq == 1: 70 | break 71 | else: 72 | raise ValueError("Should never get here") 73 | # Let b = c ** (2 ** (m - i - 1)) 74 | b = pow(c, 2 ** (m - i - 1), p) 75 | m = i 76 | c = b * b % p 77 | t = t * b * b % p 78 | r = r * b % p 79 | return r 80 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/fallback/rsa.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=too-few-public-methods 2 | 3 | class RSA: 4 | def get_backend(self): 5 | return "fallback" 6 | 7 | 8 | rsa = RSA() 9 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/openssl/__init__.py: -------------------------------------------------------------------------------- 1 | from .aes import aes 2 | from .ecc import ecc 3 | from .rsa import rsa 4 | -------------------------------------------------------------------------------- /src/lib/sslcrypto/openssl/discovery.py: -------------------------------------------------------------------------------- 1 | # Can be redefined by user 2 | def discover(): 3 | pass -------------------------------------------------------------------------------- /src/lib/sslcrypto/openssl/rsa.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=too-few-public-methods 2 | 3 | from .library import openssl_backend 4 | 5 | 6 | class RSA: 7 | def get_backend(self): 8 | return openssl_backend 9 | 10 | 11 | rsa = RSA() 12 | -------------------------------------------------------------------------------- /src/lib/subtl/LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Packetloop. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of Packetloop nor the names of its contributors may be 11 | used to endorse or promote products derived from this software without 12 | specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /src/lib/subtl/README.md: -------------------------------------------------------------------------------- 1 | # subtl 2 | 3 | ## Overview 4 | 5 | SUBTL is a **s**imple **U**DP **B**itTorrent **t**racker **l**ibrary for Python, licenced under the modified BSD license. 6 | 7 | ## Example 8 | 9 | This short example will list a few IP Addresses from a certain hash: 10 | 11 | from subtl import UdpTrackerClient 12 | utc = UdpTrackerClient('tracker.openbittorrent.com', 80) 13 | utc.connect() 14 | if not utc.poll_once(): 15 | raise Exception('Could not connect') 16 | print('Success!') 17 | 18 | utc.announce(info_hash='089184ED52AA37F71801391C451C5D5ADD0D9501') 19 | data = utc.poll_once() 20 | if not data: 21 | raise Exception('Could not announce') 22 | for a in data['response']['peers']: 23 | print(a) 24 | 25 | ## Caveats 26 | 27 | * There is no automatic retrying of sending packets yet. 28 | * This library won't download torrent files--it is simply a tracker client. 29 | -------------------------------------------------------------------------------- /src/lib/subtl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet/454c0b2e7e000fda7000cba49027541fbf327b96/src/lib/subtl/__init__.py -------------------------------------------------------------------------------- /src/util/Cached.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Cached(object): 5 | def __init__(self, timeout): 6 | self.cache_db = {} 7 | self.timeout = timeout 8 | 9 | def __call__(self, func): 10 | def wrapper(*args, **kwargs): 11 | key = "%s %s" % (args, kwargs) 12 | cached_value = None 13 | cache_hit = False 14 | if key in self.cache_db: 15 | cache_hit = True 16 | cached_value, time_cached_end = self.cache_db[key] 17 | if time.time() > time_cached_end: 18 | self.cleanupExpired() 19 | cached_value = None 20 | cache_hit = False 21 | 22 | if cache_hit: 23 | return cached_value 24 | else: 25 | cached_value = func(*args, **kwargs) 26 | time_cached_end = time.time() + self.timeout 27 | self.cache_db[key] = (cached_value, time_cached_end) 28 | return cached_value 29 | 30 | wrapper.emptyCache = self.emptyCache 31 | 32 | return wrapper 33 | 34 | def cleanupExpired(self): 35 | for key in list(self.cache_db.keys()): 36 | cached_value, time_cached_end = self.cache_db[key] 37 | if time.time() > time_cached_end: 38 | del(self.cache_db[key]) 39 | 40 | def emptyCache(self): 41 | num = len(self.cache_db) 42 | self.cache_db.clear() 43 | return num 44 | 45 | 46 | if __name__ == "__main__": 47 | from gevent import monkey 48 | monkey.patch_all() 49 | 50 | @Cached(timeout=2) 51 | def calcAdd(a, b): 52 | print("CalcAdd", a, b) 53 | return a + b 54 | 55 | @Cached(timeout=1) 56 | def calcMultiply(a, b): 57 | print("calcMultiply", a, b) 58 | return a * b 59 | 60 | for i in range(5): 61 | print("---") 62 | print("Emptied", calcAdd.emptyCache()) 63 | assert calcAdd(1, 2) == 3 64 | print("Emptied", calcAdd.emptyCache()) 65 | assert calcAdd(1, 2) == 3 66 | assert calcAdd(2, 3) == 5 67 | assert calcMultiply(2, 3) == 6 68 | time.sleep(1) 69 | -------------------------------------------------------------------------------- /src/util/Diff.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | import difflib 4 | 5 | 6 | def sumLen(lines): 7 | return sum(map(len, lines)) 8 | 9 | 10 | def diff(old, new, limit=False): 11 | matcher = difflib.SequenceMatcher(None, old, new) 12 | actions = [] 13 | size = 0 14 | for tag, old_from, old_to, new_from, new_to in matcher.get_opcodes(): 15 | if tag == "insert": 16 | new_line = new[new_from:new_to] 17 | actions.append(("+", new_line)) 18 | size += sum(map(len, new_line)) 19 | elif tag == "equal": 20 | actions.append(("=", sumLen(old[old_from:old_to]))) 21 | elif tag == "delete": 22 | actions.append(("-", sumLen(old[old_from:old_to]))) 23 | elif tag == "replace": 24 | actions.append(("-", sumLen(old[old_from:old_to]))) 25 | new_lines = new[new_from:new_to] 26 | actions.append(("+", new_lines)) 27 | size += sumLen(new_lines) 28 | if limit and size > limit: 29 | return False 30 | return actions 31 | 32 | 33 | def patch(old_f, actions): 34 | new_f = io.BytesIO() 35 | for action, param in actions: 36 | if type(action) is bytes: 37 | action = action.decode() 38 | if action == "=": # Same lines 39 | new_f.write(old_f.read(param)) 40 | elif action == "-": # Delete lines 41 | old_f.seek(param, 1) # Seek from current position 42 | continue 43 | elif action == "+": # Add lines 44 | for add_line in param: 45 | new_f.write(add_line) 46 | else: 47 | raise "Unknown action: %s" % action 48 | return new_f 49 | -------------------------------------------------------------------------------- /src/util/Electrum.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import struct 3 | 4 | 5 | # Electrum, the heck?! 6 | 7 | def bchr(i): 8 | return struct.pack("B", i) 9 | 10 | def encode(val, base, minlen=0): 11 | base, minlen = int(base), int(minlen) 12 | code_string = b"".join([bchr(x) for x in range(256)]) 13 | result = b"" 14 | while val > 0: 15 | index = val % base 16 | result = code_string[index:index + 1] + result 17 | val //= base 18 | return code_string[0:1] * max(minlen - len(result), 0) + result 19 | 20 | def insane_int(x): 21 | x = int(x) 22 | if x < 253: 23 | return bchr(x) 24 | elif x < 65536: 25 | return bchr(253) + encode(x, 256, 2)[::-1] 26 | elif x < 4294967296: 27 | return bchr(254) + encode(x, 256, 4)[::-1] 28 | else: 29 | return bchr(255) + encode(x, 256, 8)[::-1] 30 | 31 | 32 | def magic(message): 33 | return b"\x18Bitcoin Signed Message:\n" + insane_int(len(message)) + message 34 | 35 | def format(message): 36 | return hashlib.sha256(magic(message)).digest() 37 | 38 | def dbl_format(message): 39 | return hashlib.sha256(format(message)).digest() 40 | -------------------------------------------------------------------------------- /src/util/Event.py: -------------------------------------------------------------------------------- 1 | # Based on http://stackoverflow.com/a/2022629 2 | 3 | 4 | class Event(list): 5 | 6 | def __call__(self, *args, **kwargs): 7 | for f in self[:]: 8 | if "once" in dir(f) and f in self: 9 | self.remove(f) 10 | f(*args, **kwargs) 11 | 12 | def __repr__(self): 13 | return "Event(%s)" % list.__repr__(self) 14 | 15 | def once(self, func, name=None): 16 | func.once = True 17 | func.name = None 18 | if name: # Dont function with same name twice 19 | names = [f.name for f in self if "once" in dir(f)] 20 | if name not in names: 21 | func.name = name 22 | self.append(func) 23 | else: 24 | self.append(func) 25 | return self 26 | 27 | 28 | if __name__ == "__main__": 29 | def testBenchmark(): 30 | def say(pre, text): 31 | print("%s Say: %s" % (pre, text)) 32 | 33 | import time 34 | s = time.time() 35 | on_changed = Event() 36 | for i in range(1000): 37 | on_changed.once(lambda pre: say(pre, "once"), "once") 38 | print("Created 1000 once in %.3fs" % (time.time() - s)) 39 | on_changed("#1") 40 | 41 | def testUsage(): 42 | def say(pre, text): 43 | print("%s Say: %s" % (pre, text)) 44 | 45 | on_changed = Event() 46 | on_changed.once(lambda pre: say(pre, "once")) 47 | on_changed.once(lambda pre: say(pre, "once")) 48 | on_changed.once(lambda pre: say(pre, "namedonce"), "namedonce") 49 | on_changed.once(lambda pre: say(pre, "namedonce"), "namedonce") 50 | on_changed.append(lambda pre: say(pre, "always")) 51 | on_changed("#1") 52 | on_changed("#2") 53 | on_changed("#3") 54 | 55 | testBenchmark() 56 | -------------------------------------------------------------------------------- /src/util/Flag.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | 4 | class Flag(object): 5 | def __init__(self): 6 | self.valid_flags = set([ 7 | "admin", # Only allowed to run sites with ADMIN permission 8 | "async_run", # Action will be ran async with gevent.spawn 9 | "no_multiuser" # Action disabled if Multiuser plugin running in open proxy mode 10 | ]) 11 | self.db = defaultdict(set) 12 | 13 | def __getattr__(self, key): 14 | def func(f): 15 | if key not in self.valid_flags: 16 | raise Exception("Invalid flag: %s (valid: %s)" % (key, self.valid_flags)) 17 | self.db[f.__name__].add(key) 18 | return f 19 | return func 20 | 21 | 22 | flag = Flag() 23 | -------------------------------------------------------------------------------- /src/util/GreenletManager.py: -------------------------------------------------------------------------------- 1 | import gevent 2 | from Debug import Debug 3 | 4 | 5 | class GreenletManager: 6 | def __init__(self): 7 | self.greenlets = set() 8 | 9 | def spawnLater(self, *args, **kwargs): 10 | greenlet = gevent.spawn_later(*args, **kwargs) 11 | greenlet.link(lambda greenlet: self.greenlets.remove(greenlet)) 12 | self.greenlets.add(greenlet) 13 | return greenlet 14 | 15 | def spawn(self, *args, **kwargs): 16 | greenlet = gevent.spawn(*args, **kwargs) 17 | greenlet.link(lambda greenlet: self.greenlets.remove(greenlet)) 18 | self.greenlets.add(greenlet) 19 | return greenlet 20 | 21 | def stopGreenlets(self, reason="Stopping all greenlets"): 22 | num = len(self.greenlets) 23 | gevent.killall(list(self.greenlets), Debug.createNotifyType(reason), block=False) 24 | return num 25 | -------------------------------------------------------------------------------- /src/util/Platform.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging 3 | 4 | 5 | def setMaxfilesopened(limit): 6 | try: 7 | if sys.platform == "win32": 8 | import ctypes 9 | dll = None 10 | last_err = None 11 | for dll_name in ["msvcr100", "msvcr110", "msvcr120"]: 12 | try: 13 | dll = getattr(ctypes.cdll, dll_name) 14 | break 15 | except OSError as err: 16 | last_err = err 17 | 18 | if not dll: 19 | raise last_err 20 | 21 | maxstdio = dll._getmaxstdio() 22 | if maxstdio < limit: 23 | logging.debug("%s: Current maxstdio: %s, changing to %s..." % (dll, maxstdio, limit)) 24 | dll._setmaxstdio(limit) 25 | return True 26 | else: 27 | import resource 28 | soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) 29 | if soft < limit: 30 | logging.debug("Current RLIMIT_NOFILE: %s (max: %s), changing to %s..." % (soft, hard, limit)) 31 | resource.setrlimit(resource.RLIMIT_NOFILE, (limit, hard)) 32 | return True 33 | 34 | except Exception as err: 35 | logging.error("Failed to modify max files open limit: %s" % err) 36 | return False 37 | -------------------------------------------------------------------------------- /src/util/Pooled.py: -------------------------------------------------------------------------------- 1 | import gevent.pool 2 | 3 | 4 | class Pooled(object): 5 | def __init__(self, size=100): 6 | self.pool = gevent.pool.Pool(size) 7 | self.pooler_running = False 8 | self.queue = [] 9 | self.func = None 10 | 11 | def waiter(self, evt, args, kwargs): 12 | res = self.func(*args, **kwargs) 13 | if type(res) == gevent.event.AsyncResult: 14 | evt.set(res.get()) 15 | else: 16 | evt.set(res) 17 | 18 | def pooler(self): 19 | while self.queue: 20 | evt, args, kwargs = self.queue.pop(0) 21 | self.pool.spawn(self.waiter, evt, args, kwargs) 22 | self.pooler_running = False 23 | 24 | def __call__(self, func): 25 | def wrapper(*args, **kwargs): 26 | evt = gevent.event.AsyncResult() 27 | self.queue.append((evt, args, kwargs)) 28 | if not self.pooler_running: 29 | self.pooler_running = True 30 | gevent.spawn(self.pooler) 31 | return evt 32 | wrapper.__name__ = func.__name__ 33 | self.func = func 34 | 35 | return wrapper 36 | 37 | if __name__ == "__main__": 38 | import gevent 39 | import gevent.pool 40 | import gevent.queue 41 | import gevent.event 42 | import gevent.monkey 43 | import time 44 | 45 | gevent.monkey.patch_all() 46 | 47 | def addTask(inner_path): 48 | evt = gevent.event.AsyncResult() 49 | gevent.spawn_later(1, lambda: evt.set(True)) 50 | return evt 51 | 52 | def needFile(inner_path): 53 | return addTask(inner_path) 54 | 55 | @Pooled(10) 56 | def pooledNeedFile(inner_path): 57 | return needFile(inner_path) 58 | 59 | threads = [] 60 | for i in range(100): 61 | threads.append(pooledNeedFile(i)) 62 | 63 | s = time.time() 64 | gevent.joinall(threads) # Should take 10 second 65 | print(time.time() - s) 66 | -------------------------------------------------------------------------------- /src/util/SafeRe.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class UnsafePatternError(Exception): 5 | pass 6 | 7 | cached_patterns = {} 8 | 9 | 10 | def isSafePattern(pattern): 11 | if len(pattern) > 255: 12 | raise UnsafePatternError("Pattern too long: %s characters in %s" % (len(pattern), pattern)) 13 | 14 | unsafe_pattern_match = re.search(r"[^\.][\*\{\+]", pattern) # Always should be "." before "*{+" characters to avoid ReDoS 15 | if unsafe_pattern_match: 16 | raise UnsafePatternError("Potentially unsafe part of the pattern: %s in %s" % (unsafe_pattern_match.group(0), pattern)) 17 | 18 | repetitions = re.findall(r"\.[\*\{\+]", pattern) 19 | if len(repetitions) >= 10: 20 | raise UnsafePatternError("More than 10 repetitions of %s in %s" % (repetitions[0], pattern)) 21 | 22 | return True 23 | 24 | 25 | def match(pattern, *args, **kwargs): 26 | cached_pattern = cached_patterns.get(pattern) 27 | if cached_pattern: 28 | return cached_pattern.match(*args, **kwargs) 29 | else: 30 | if isSafePattern(pattern): 31 | cached_patterns[pattern] = re.compile(pattern) 32 | return cached_patterns[pattern].match(*args, **kwargs) 33 | -------------------------------------------------------------------------------- /src/util/SocksProxy.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | import socks 4 | from Config import config 5 | 6 | def create_connection(address, timeout=None, source_address=None): 7 | if address in config.ip_local: 8 | sock = socket.socket_noproxy(socket.AF_INET, socket.SOCK_STREAM) 9 | sock.connect(address) 10 | else: 11 | sock = socks.socksocket() 12 | sock.connect(address) 13 | return sock 14 | 15 | 16 | # Dns queries using the proxy 17 | def getaddrinfo(*args): 18 | return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))] 19 | 20 | 21 | def monkeyPatch(proxy_ip, proxy_port): 22 | socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, proxy_ip, int(proxy_port)) 23 | socket.socket_noproxy = socket.socket 24 | socket.socket = socks.socksocket 25 | socket.create_connection = create_connection 26 | socket.getaddrinfo = getaddrinfo 27 | -------------------------------------------------------------------------------- /src/util/__init__.py: -------------------------------------------------------------------------------- 1 | from .Cached import Cached 2 | from .Event import Event 3 | from .Noparallel import Noparallel 4 | from .Pooled import Pooled 5 | -------------------------------------------------------------------------------- /start.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | # Included modules 5 | import sys 6 | 7 | # ZeroNet Modules 8 | import zeronet 9 | 10 | 11 | def main(): 12 | if "--open_browser" not in sys.argv: 13 | sys.argv = [sys.argv[0]] + ["--open_browser", "default_browser"] + sys.argv[1:] 14 | zeronet.start() 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /tools/coffee/README.md: -------------------------------------------------------------------------------- 1 | # CoffeeScript compiler for Windows 2 | 3 | A simple command-line utilty for Windows that will compile `*.coffee` files to JavaScript `*.js` files using [CoffeeScript](http://jashkenas.github.com/coffee-script/) and the venerable Windows Script Host, ubiquitous on Windows since the 90s. 4 | 5 | ## Usage 6 | 7 | To use it, invoke `coffee.cmd` like so: 8 | 9 | coffee input.coffee output.js 10 | 11 | If an output is not specified, it is written to `stdout`. In neither an input or output are specified then data is assumed to be on `stdin`. For example: 12 | 13 | type input.coffee | coffee > output.js 14 | 15 | Errors are written to `stderr`. 16 | 17 | In the `test` directory there's a version of the standard CoffeeScript tests which can be kicked off using `test.cmd`. The test just attempts to compile the *.coffee files but doesn't execute them. 18 | 19 | To upgrade to the latest CoffeeScript simply replace `coffee-script.js` from the upstream https://github.com/jashkenas/coffee-script/blob/master/extras/coffee-script.js (the tests will likely need updating as well, if you want to run them). 20 | -------------------------------------------------------------------------------- /tools/coffee/coffee.cmd: -------------------------------------------------------------------------------- 1 | ::For convenience 2 | @cscript //nologo "%~dp0coffee.wsf" %* 3 | --------------------------------------------------------------------------------