├── .dockerignore ├── src ├── translate │ ├── __init__.py │ ├── languages │ │ ├── en.json │ │ ├── it.json │ │ ├── fa.json │ │ ├── de.json │ │ ├── pt-br.json │ │ ├── zh-tw.json │ │ └── zh.json │ └── Translate.py ├── bug.png ├── icon.png ├── Roboto.ttf ├── splash.png ├── TextMeOne.ttf ├── Roboto-Regular.otf ├── DroidSansFallback.ttf ├── watchdog_1.py ├── watchdog_2.py ├── os_platform.py ├── light_colors.py ├── env_json.py ├── main.py ├── watchdog.py ├── bug.svg ├── zeronet_config.py ├── service.py ├── platform_linux.py ├── platform_android.py ├── zeronet.kv └── platform_service.py ├── .version.sh ├── .travis └── wait.sh ├── img └── splash.xcf ├── icons ├── icon.512.png └── icon.svg ├── screenshots ├── ui.png ├── loading.png ├── startup.png └── zerohello.png ├── .ci ├── download-signed-stable.sh ├── push-fdroid.sh ├── release-nightly.sh ├── bind-cache.sh ├── package.sh ├── update-cron.sh └── build.sh ├── .gitignore ├── getvagrant.sh ├── .gitmodules ├── vendor └── update-vendor.sh ├── maint └── RELEASE_CHECKLIST.md ├── Tutorial-of-packaging-APK-zh-cn.md ├── .gitlab-ci.yml ├── Dockerfile ├── Tutorial-of-packaging-APK.md ├── README-zh-cn.md ├── Vagrantfile ├── Makefile ├── tool.sh ├── README.md └── LICENSE /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !Makefile 3 | !tool.sh 4 | -------------------------------------------------------------------------------- /src/translate/__init__.py: -------------------------------------------------------------------------------- 1 | from .Translate import * 2 | -------------------------------------------------------------------------------- /.version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUR_VERSION="0.7.0" 4 | 5 | VER_SUFFIX=1 6 | -------------------------------------------------------------------------------- /.travis/wait.sh: -------------------------------------------------------------------------------- 1 | wait() { 2 | echo -n . 3 | sleep 1m 4 | wait 5 | } 6 | wait & 7 | -------------------------------------------------------------------------------- /src/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/bug.png -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/icon.png -------------------------------------------------------------------------------- /img/splash.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/img/splash.xcf -------------------------------------------------------------------------------- /src/Roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/Roboto.ttf -------------------------------------------------------------------------------- /src/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/splash.png -------------------------------------------------------------------------------- /src/TextMeOne.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/TextMeOne.ttf -------------------------------------------------------------------------------- /icons/icon.512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/icons/icon.512.png -------------------------------------------------------------------------------- /screenshots/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/screenshots/ui.png -------------------------------------------------------------------------------- /src/Roboto-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/Roboto-Regular.otf -------------------------------------------------------------------------------- /screenshots/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/screenshots/loading.png -------------------------------------------------------------------------------- /screenshots/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/screenshots/startup.png -------------------------------------------------------------------------------- /screenshots/zerohello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/screenshots/zerohello.png -------------------------------------------------------------------------------- /src/DroidSansFallback.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroNet-kivy/master/src/DroidSansFallback.ttf -------------------------------------------------------------------------------- /.ci/download-signed-stable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wget https://tmp.i.mkg20001.io/zn-last-sign.zip -O signed.zip 4 | unzip signed.zip 5 | -------------------------------------------------------------------------------- /src/watchdog_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | 5 | os.environ["watchdog_id"] = "1" 6 | 7 | if True: 8 | import watchdog 9 | -------------------------------------------------------------------------------- /src/watchdog_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | 5 | os.environ["watchdog_id"] = "2" 6 | 7 | if True: 8 | import watchdog 9 | -------------------------------------------------------------------------------- /.ci/push-fdroid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd release 6 | 7 | CMD=$(echo "$FDROID_UPLOAD" | base64 -d) 8 | 9 | eval $CMD 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .buildozer 2 | dist 3 | bin 4 | .vagrant 5 | *.log 6 | *.pyc 7 | _OLD 8 | screenshots.orig 9 | .pre 10 | *.bak 11 | .env 12 | .deps 13 | -------------------------------------------------------------------------------- /src/translate/languages/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "_translators": "IMPORTANT: If you want to start translating, copy another language that is up-to-date (like de.json) and start translating that one" 3 | } 4 | -------------------------------------------------------------------------------- /getvagrant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | file="https://releases.hashicorp.com/vagrant/1.9.1/vagrant_1.9.1_x86_64.deb" 4 | 5 | o="vagrant.deb" 6 | 7 | set -x 8 | 9 | wget $file -O $o 10 | dpkg -i --force-all $o 11 | apt-get -f install 12 | -------------------------------------------------------------------------------- /src/os_platform.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | if platform=="android": 4 | from platform_android import * 5 | elif platform=="linux": 6 | from platform_linux import * 7 | else: 8 | raise Exception("Unsupported platform: %s" % platform) 9 | -------------------------------------------------------------------------------- /src/light_colors.py: -------------------------------------------------------------------------------- 1 | def format(s): 2 | return float("0."+str(int("0x"+s[0]+s[1],16))) 3 | 4 | def hex(s): #converts the colors into light colors (not really how it should be done - but it works) 5 | return format(s[1]+s[2]),format(s[3]+s[4]),format(s[5]+s[6]) 6 | -------------------------------------------------------------------------------- /src/env_json.py: -------------------------------------------------------------------------------- 1 | import os, json 2 | 3 | def loadEnv(): 4 | env=None 5 | if 'ENV_JSON' in os.environ: 6 | with open(os.environ['ENV_JSON'], 'r') as f: 7 | env = json.load(f) 8 | else: 9 | with open('env.json', 'r') as f: 10 | env = json.load(f) 11 | return env 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/zero"] 2 | path = src/zero 3 | url = https://github.com/HelloZeroNet/ZeroNet 4 | [submodule "vendor/buildozer"] 5 | path = vendor/buildozer 6 | url = https://github.com/mkg20001/buildozer 7 | [submodule "vendor/python-for-android"] 8 | path = vendor/python-for-android 9 | url = https://github.com/mkg20001/python-for-android 10 | -------------------------------------------------------------------------------- /.ci/release-nightly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir release 4 | 5 | UPCMD=$(echo "$NIGHTLY_UPCMD" | base64 -d) 6 | 7 | for f in package/unsigned/*.apk; do 8 | OUT=${f/"package/unsigned/"/"release/"} 9 | OUT=${OUT/"-unsigned"/"-nightly"} 10 | 11 | rm -f /tmp/zn-release.apk 12 | zipalign -v -p 4 "$f" /tmp/zn-release.apk 13 | 14 | curl 172.17.0.1:6234 --header "Token: $NIGHTLY_SIGNING_TOKEN" -F "apk=@/tmp/zn-release.apk" -o "$OUT" 15 | 16 | if [ ! -s "$OUT" ]; then 17 | echo "ERR: Signing failed" >&2 18 | exit 2 19 | fi 20 | done 21 | -------------------------------------------------------------------------------- /.ci/bind-cache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | chmod 777 "$HOME" 6 | 7 | create_and_chmod() { 8 | mkdir -p "$1" 9 | sudo chmod 777 "$1" 10 | } 11 | 12 | bind_folder() { 13 | LOCAL="$1/$2" 14 | CACHE="/cache/kivy/$2" 15 | 16 | echo "[CACHE] Bind $LOCAL to $CACHE" 17 | rm -rf "$LOCAL" 18 | create_and_chmod "$(dirname "$LOCAL")" 19 | create_and_chmod "$CACHE" 20 | # sudo mount --bind "$CACHE" "$LOCAL" 21 | sudo ln -s "$CACHE" "$LOCAL" 22 | } 23 | 24 | bind_folder "$HOME" .buildozer/android 25 | bind_folder "$HOME" .gradle 26 | bind_folder "$HOME" .android 27 | -------------------------------------------------------------------------------- /vendor/update-vendor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for dep in buildozer python-for-android; do 4 | pushd $dep 5 | if ! git remote -v | grep upstream >/dev/null; then 6 | git remote set-url origin git@github.com:mkg20001/$dep 7 | git remote add upstream git@github.com:kivy/$dep 8 | fi 9 | git fetch -p 10 | git remote update -p 11 | git branch -D master 12 | git reset --hard origin/master 13 | git checkout -b master 14 | git rebase upstream/master 15 | bash 16 | if [ -e .ok ]; then 17 | git push origin HEAD:$(date +%s) 18 | git push -uf origin master 19 | rm .ok 20 | fi 21 | popd 22 | done 23 | -------------------------------------------------------------------------------- /src/translate/languages/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome to ZeroNet!":"Benvenuto su ZeroNet!", 3 | "Below you see sites which are available in ZeroNet":"Qui trovi l'elenco dei siti disponibili su ZeroNet", 4 | "ZeroNet Homepage":"Homepage di ZeroNet", 5 | "Simple Messaging Board":"Semplice spazio di messaggistica", 6 | "Reddit-Like, Decentralized Forum":"Forum decentralizzato, stile Reddit", 7 | "Microblogging Platform":"Piattaforma di microblogging", 8 | "End-to-End Encrypted Mailing":"Email con crittografia end-to-end", 9 | "Peer-to-Peer Social Network":"Social network P2P", 10 | "Reddit-Like, Decentralized Forum for China":"Forum decentralizzato in cinese" 11 | } 12 | -------------------------------------------------------------------------------- /src/translate/languages/fa.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome to ZeroNet!":"به زیرونت خوش‌آمدید", 3 | "Below you see sites which are available in ZeroNet":"در زیر شما سایت هایی که در زیرونت در دسترس هستند را میبینید", 4 | "ZeroNet Homepage":"صفحه خانه زیرونت", 5 | "Simple Messaging Board":"تخته پیام‌رسانی ساده", 6 | "Reddit-Like, Decentralized Forum":"فروم غیر‌متمرکز شبیه ردیت", 7 | "Microblogging Platform":"پلت‌فرم وبلاگ نویسی کوچک", 8 | "End-to-End Encrypted Mailing":"نامه‌رسانی سر‌تا‌سر رمزنگاری شده", 9 | "Peer-to-Peer Social Network":"شبکه اجتماعی سر‌تا‌سر رمزنگاری شده", 10 | "Reddit-Like, Decentralized Forum for China":"فروم غیر‌متمرکز شبیه ردیت برای چین", 11 | "[b]Report a Bug[/b]": "[b]گزارش یک مشکل[/b]", 12 | "[b]Donate[/b]": "[b]حمایت[/b]" 13 | } 14 | -------------------------------------------------------------------------------- /src/translate/languages/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome to ZeroNet!":"Willkommen bei ZeroNet!", 3 | "Below you see sites which are available in ZeroNet":"Hier unten siehst du Seiten die im ZeroNet verfügbar sind", 4 | "ZeroNet Homepage":"ZeroNet Startseite", 5 | "Simple Messaging Board":"Einfaches Narichten Board", 6 | "Reddit-Like, Decentralized Forum":"Reddit ähnliches, dezentralisiertes Forum", 7 | "Microblogging Platform":"Microblogging Platform", 8 | "End-to-End Encrypted Mailing":"Ende-zu-Ende verschlüsseltes E-Mail System", 9 | "Peer-to-Peer Social Network":"Peer-zu-Peer Soziales Netzwerk", 10 | "Reddit-Like, Decentralized Forum for China":"Reddit ähnliches, dezentralisiertes Forum für China", 11 | "[b]Report a Bug[/b]": "[b]Fehler melden[/b]", 12 | "[b]Donate[/b]": "[b]Spenden[/b]" 13 | } 14 | -------------------------------------------------------------------------------- /src/translate/languages/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome to ZeroNet!":"Bem-vindo ao ZeroNet!", 3 | "Below you see sites which are available in ZeroNet":"Abaixo você vê sites disponíveis no ZeroNet", 4 | "ZeroNet Homepage":"Página Inicial ZeroNet", 5 | "Simple Messaging Board":"Quadro Simples de mensagens", 6 | "Reddit-Like, Decentralized Forum":"Fórum descentralizado, semelhante ao Reddit", 7 | "Microblogging Platform":"Plataforma de Microblogging", 8 | "End-to-End Encrypted Mailing":"Correspondência criptografada de ponta a ponta", 9 | "Peer-to-Peer Social Network":"Rede social Peer-to-Peer", 10 | "Reddit-Like, Decentralized Forum for China":"Fórum descentralizado e semelhante ao Reddit para a China", 11 | "[b]Report a Bug[/b]": "[b]Reportar um erro[/b]", 12 | "[b]Donate[/b]": "[b]Doar[/b]" 13 | } 14 | -------------------------------------------------------------------------------- /src/translate/languages/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "Roboto":"DroidSansFallback", 3 | "string.feedback":"想要妥善地關閉ZeroNet背景服務,\n請在瀏覽器中點擊ZeroHello首頁左上方的 ┇ 按鈕,\n 選擇關閉ZeroNet,謝謝!\n更多信息和相關討論點這裡(中文版))[/ref]", 4 | "Talk.ZeroNetwork.bit/?Topic:13_13Z7XxTa7JuFat3KzzMWu3onwM6biLuurJ/":"NewGFWTalk.bit/?Topic:59_13Z7XxTa7JuFat3KzzMWu3onwM6biLuurJ/", 5 | 6 | "Welcome to ZeroNet!":"歡迎使用 ZeroNet", 7 | "Below you see sites which are available in ZeroNet":"下面你看到的是 ZeroNet 的可用網站", 8 | "ZeroNet Homepage":"ZeroNet 首頁", 9 | "Simple Messaging Board":"简单的佈告欄", 10 | "Reddit-Like, Decentralized Forum":"類似於 Reddit 的,分散式的論壇", 11 | "Microblogging Platform":"輕量化網誌平台", 12 | "End-to-End Encrypted Mailing":"端對端加密郵件", 13 | "Peer-to-Peer Social Network":"對等社交網路", 14 | "Reddit-Like, Decentralized Forum for China":"來自於中國的,類似於 Reddit 的,分散式的論壇" 15 | } 16 | -------------------------------------------------------------------------------- /src/translate/languages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Roboto":"DroidSansFallback", 3 | "string.feedback":"想要妥善地关闭ZeroNet后台服务,\n请在浏览器中点击ZeroHello首页左上方的 ┇ 按钮,\n 选择关闭ZeroNet,谢谢!\n更多信息和相关讨论点这里(中文版))[/ref]", 4 | "Talk.ZeroNetwork.bit/?Topic:13_13Z7XxTa7JuFat3KzzMWu3onwM6biLuurJ/":"NewGFWTalk.bit/?Topic:59_13Z7XxTa7JuFat3KzzMWu3onwM6biLuurJ/", 5 | 6 | "Welcome to ZeroNet!":"欢迎使用 ZeroNet", 7 | "Below you see sites which are available in ZeroNet":"下面你看到的是 ZeroNet 的可用站点", 8 | "ZeroNet Homepage":"ZeroNet 首页", 9 | "Simple Messaging Board":"简单的消息版", 10 | "Reddit-Like, Decentralized Forum":"类似于 Reddit 的,分布式的论坛", 11 | "Microblogging Platform":"轻量化博客平台", 12 | "End-to-End Encrypted Mailing":"端对端加密邮件", 13 | "Peer-to-Peer Social Network":"点对点社交网络", 14 | "Reddit-Like, Decentralized Forum for China":"来自于中国的,类似于 Reddit 的,分布式的论坛" 15 | } 16 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | from webbrowser import open as browser 2 | 3 | from kivy.app import App 4 | from kivy.lang import Builder 5 | 6 | from os_platform import Service, platform, wrapSentry 7 | from zeronet_config import getConfigValue 8 | 9 | 10 | class ZeronetApp(App): 11 | 12 | '''This is the app itself''' 13 | 14 | def url_click(self, url): 15 | full = "http://127.0.0.1:" + \ 16 | getConfigValue(self.service.config, "ui_port", "43110") + "/" + url 17 | print("Opening in browser %s" % full) 18 | browser(full) 19 | 20 | def build(self): 21 | print("Starting...") 22 | 23 | print("Running on platform %s" % platform) 24 | 25 | self.service = Service() 26 | '''Starts 2 watchdog processes, which will then start zeronet''' 27 | self.service.run() 28 | 29 | if __name__ == "__main__": 30 | '''Start the application''' 31 | 32 | wrapSentry(ZeronetApp().run) 33 | -------------------------------------------------------------------------------- /icons/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /maint/RELEASE_CHECKLIST.md: -------------------------------------------------------------------------------- 1 | # A new version is coming! :tada: 2 | 3 | # Changelog 4 | 5 | - **update** - ZeroNet updated to Version XYZ 6 | - **bug fix** - Another bug squashed 7 | - **feature** - Another cool feature 8 | 9 | ## Pre-Release 10 | 11 | - [ ] Test whether latest devel works 12 | - [ ] 28 aka 9.0 Pie 13 | - [ ] 27 aka 8.1 Oreo 14 | - [ ] 22 aka 5.1 Lollipop 15 | - [ ] Bump version in .version.sh if necesarry 16 | - [ ] Create git tag 17 | - [ ] Push to master and push tag 18 | - [ ] Wait for build to finish 19 | 20 | ## Release 21 | 22 | - [ ] Download package artifacts and extract 23 | - [ ] Run release script 24 | - [ ] Test APKs 25 | - [ ] 28 aka 9.0 Pie 26 | - [ ] 27 aka 8.1 Oreo 27 | - [ ] 22 aka 5.1 Lollipop 28 | - [ ] Run CI job for release manually 29 | - [ ] Publish on 30 | - [ ] F-Droid (check if script succeeded) 31 | - [ ] Google Play 32 | - [ ] GitHub Releases 33 | - [ ] ZeroNet-Android.bit 34 | 35 | ## Post-Release 36 | 37 | - [ ] Share on social media? 38 | - [ ] Close this issue 39 | -------------------------------------------------------------------------------- /Tutorial-of-packaging-APK-zh-cn.md: -------------------------------------------------------------------------------- 1 | # 指导: 2 | 3 | 在Ubuntu 16.04上测试过 4 | 5 | 必需品: 6 | - A phone or armv7 emulator (can't build for non-arm currently) 7 | - Git 8 | - Make 9 | - ADB 10 | - Docker or Vagrant (for vagrant please skip to the bottom) 11 | 12 | ## 初始步骤 13 | - 首先 `git clone https://github.com/HelloZeroNet/ZeroNet-kivy --recursive` 14 | - 现在验证在 `src/zero` 的内容 15 | 16 | ## 安装 17 | - 首先,您需要生成环境配置。使用`make .env`执行此操作 18 | - 之后使用`make .pre`准备构建 19 | 20 | ## 建设 21 | - 运行`make debug`来构建应用程序的调试版本 22 | - 运行`make release`来构建应用程序的发布版本 23 | - 如果构建成功,则会显示带有ZeroNet.apk的bin文件夹 24 | - 运行`make run`来测试连接手机上的应用程序 25 | - 果您使用模拟器,请在命令后附加`ADB_FLAG=-e` 26 | 27 | ## 调试 28 | - 运行 `make test` 应该启动adb logcat 29 | - 其他日志位于 `/storage/emulated/0/Android/data/net.mkg20001.zeronet/files/zero/log` 30 | - 如果`make test`不起作用,请尝试`make run` 31 | 32 | ## Vagrant 33 | - 首先运行 `vagrant up` 34 | - 这应该创建和准备虚拟机 35 | 36 | ### Building 37 | - 运行所有命令 **inside** the vagrant VM (Use `vagrant ssh` to enter the vm) 38 | - 测试你需要安装adb. Run `make run` or `make test` **outside** the VM 39 | - 如果你安装了它,运行 `make run` 40 | - 如果您使用模拟器,请在命令后附加 `ADB_FLAG=-e` 41 | -------------------------------------------------------------------------------- /.ci/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # test: mkdir -p ci-out/{a,b,c} && touch ci-out/{a,b,c}/{buildozer.spec,test-{release-unsigned,debug}.apk} && for f in ci-out/*/buildozer.spec; do echo -e "# test\ntest\n\n" > "$f"; done 4 | 5 | set -e 6 | 7 | mkdir -p package/{unsigned,debug,spec} 8 | 9 | for arch_path in ci-out/*; do 10 | arch=$(basename "$arch_path") 11 | 12 | # generate stripped spec file 13 | cat "$arch_path/buildozer.spec" | sed "s|^#.+||g" | grep -v "^\$" > "package/spec/buildozer.$arch.spec" 14 | 15 | # get apks 16 | release_unsigned=$(echo "$arch_path/"*release-unsigned*) 17 | debug=$(echo "$arch_path/"*debug*) 18 | 19 | # move apks 20 | for f in "$release_unsigned" "$debug"; do 21 | outfolder=$(echo "$f" | sed -r "s|.+-([a-z-]+).apk|\1|g") 22 | fout=${f/".apk"/"-$arch.apk"} 23 | fout=$(basename "$fout") 24 | fout="package/$outfolder/$fout" 25 | mv -v "$f" "${fout}" 26 | done 27 | done 28 | 29 | getmeta() { 30 | cat src/zero/src/Config.py | grep "$1 =" | sed -r "s|(.*) = ||g" 31 | } 32 | 33 | echo "{\"rev\":$(getmeta self.rev),\"ver\":$(getmeta self.version),\"date\":$(expr $(date +%s) \* 1000)}" > package/metadata.json 34 | -------------------------------------------------------------------------------- /src/watchdog.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from time import sleep 4 | 5 | import os_platform as platform 6 | from env_json import loadEnv 7 | 8 | if "watchdog_id" not in os.environ: 9 | raise Exception("No watchdog id!") 10 | id = int(os.environ["watchdog_id"]) 11 | print("This is watchdog %s" % id) 12 | 13 | service = platform.Service() 14 | service.setPid("watchdog%s" % str(id), os.getpid()) 15 | 16 | 17 | def isRunning(what): 18 | i = service.isRunning(what) 19 | if not i: 20 | print("%s is not running, starting now" % what) 21 | # else: 22 | # print "%s is running, pid=%s" % (what,service.getPid(what)) 23 | return i 24 | 25 | while(True): # runs forever 26 | sleep(1) # delay first, so processes can start up 27 | if id == 1: # the first watchdog checks if watchdog2 and zeronet are running and starts them 28 | if not isRunning("watchdog2"): 29 | # if not os.fork(): 30 | service.startWatchdog(2) 31 | # exit(0) 32 | if not isRunning("zeronet"): 33 | # if not os.fork(): 34 | service.runZero() 35 | # exit(0) 36 | elif id == 2: # the second check only if the first is running 37 | if not isRunning("watchdog1"): 38 | # if not os.fork(): 39 | service.startWatchdog(1) 40 | # exit(0) 41 | -------------------------------------------------------------------------------- /.ci/update-cron.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # reset src 4 | if [ -e .pre ]; then 5 | rm -rf src/zero 6 | git submodule update 7 | rm .pre 8 | fi 9 | 10 | git pull --recurse-submodule 11 | 12 | # get version wrapper 13 | zn_ver() { 14 | pushd src/zero/src 15 | 16 | lver=$(cat Config.py | grep "^[ ]*self.version" | grep "[0-9]*\.[0-9]*\.[0-9]*" -o) 17 | 18 | lat=$(git log --grep="Rev[0-9]*" --format="[%H] = %s" | head -n 1) 19 | lc=$(echo "$lat" | grep "\[[a-z0-9A-Z]*\]" -o | grep "[a-z0-9A-Z]*" -o) 20 | lrev=$(echo "$lat" | grep "Rev[0-9]*" -o) 21 | 22 | echo "commit = $lc / rev = $lrev / ver = $lver" 23 | 24 | popd 25 | } 26 | 27 | zn_ver 28 | CMT_BEFORE="$lc" 29 | 30 | # fetch new zn 31 | git -C src/zero remote update -p 32 | git -C src/zero merge --ff-only origin/master 33 | 34 | zn_ver 35 | 36 | if [ "$CMT_BEFORE" != "$lc" ]; then 37 | source .version.sh 38 | 39 | if [ "$CUR_VERSION" != "$lver" ]; then 40 | CUR_VERSION="$lver" 41 | VER_SUFFIX=1 42 | echo "Major upgrade, reset suffix" 43 | else 44 | VER_SUFFIX=$(( $VER_SUFFIX + 1 )) 45 | echo "Minor upgrade, suffix++" 46 | fi 47 | 48 | echo '#!/bin/bash 49 | 50 | CUR_VERSION="'"$CUR_VERSION"' 51 | 52 | VER_SUFFIX='"$VER_SUFFIX"'' > .version.sh 53 | 54 | git commit -m "Update ZeroNet to $lrev" src/zero .version.sh 55 | git push 56 | 57 | echo DONE 58 | fi 59 | -------------------------------------------------------------------------------- /.ci/build.sh: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | set -ev 4 | 5 | # prepare 6 | bash .ci/bind-cache.sh 7 | export TERM=xterm-256color 8 | export DISABLE_PROGRESS=1 9 | # source .version.sh 10 | 11 | replace_var() { 12 | sed -r "s|#* *$1 = .*|$1 = $2|g" -i buildozer.spec 13 | } 14 | 15 | disable_var() { 16 | sed -r "s|#* *$1 = .*|# $1 (disabled) = 0|g" -i buildozer.spec 17 | } 18 | 19 | if [ -z "$CI_COMMIT_TAG" ]; then # build a nightly 20 | replace_var package.domain luna.mkg20001 21 | replace_var title ZeroNetN 22 | # disable_var version.regex 23 | # disable_var version.filename 24 | # replace_var version "$CUR_VERSION.$(date +%s)" 25 | # SUFFIX=$(git log --oneline | wc -l | sed -r "s|(.+)([0-9]{2})|\1.\2|g") # turns commit count into version, e.g. 111 -> 1.11 26 | SUFFIX=$(git log --oneline | wc -l) # counts commits 27 | sed -r "s|VER_SUFFIX.+|VER_SUFFIX=$SUFFIX|g" -i .version.sh 28 | fi 29 | 30 | # setup 31 | replace_var android.arch "$TARGET_ARCH" 32 | echo -e "EXEC=docker\nDOCKER_IMAGE=$IMAGE_TAG\nDISABLE_PROGRESS=1" > .env 33 | docker pull $IMAGE_TAG 34 | bash ./tool.sh prebuild 35 | 36 | # execute 37 | script -q -e -c "make _ci" 38 | 39 | # post-check 40 | ls bin | grep debug | grep .apk # ensure job generated debug apk 41 | ls bin | grep release | grep .apk # ensure job generated release apk 42 | mkdir ci-out && mv bin ci-out/$CI_JOB_NAME && mv buildozer.spec ci-out/$CI_JOB_NAME/ 43 | -------------------------------------------------------------------------------- /src/bug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | services: 2 | - docker:dind 3 | 4 | stages: 5 | - image 6 | - build 7 | - package 8 | - release 9 | - fdroid 10 | 11 | variables: 12 | GIT_SUBMODULE_STRATEGY: recursive 13 | DOCKER_HOST: tcp://docker:2375 14 | DOCKER_DRIVER: overlay2 15 | IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG 16 | DOCKER_TLS: "" 17 | DOCKER_TLS_CERTDIR: "" 18 | 19 | before_script: 20 | - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY 21 | 22 | docker-image: 23 | stage: image 24 | script: 25 | - docker build -t $IMAGE_TAG . 26 | - docker push $IMAGE_TAG 27 | 28 | .build: &build 29 | stage: build 30 | script: 31 | - bash .ci/build.sh 32 | artifacts: 33 | paths: 34 | - ci-out/ 35 | 36 | armeabi-v7a: 37 | <<: *build 38 | variables: 39 | TARGET_ARCH: armeabi-v7a 40 | 41 | arm64-v8a: 42 | <<: *build 43 | variables: 44 | TARGET_ARCH: arm64-v8a 45 | 46 | x86: 47 | <<: *build 48 | variables: 49 | TARGET_ARCH: x86 50 | 51 | x86-64: 52 | <<: *build 53 | variables: 54 | TARGET_ARCH: x86_64 55 | 56 | package: 57 | stage: package 58 | script: 59 | - ./.ci/package.sh 60 | only: 61 | - master 62 | - tags 63 | artifacts: 64 | paths: 65 | - package/ 66 | 67 | release-nightly: 68 | stage: release 69 | script: 70 | - ./.ci/release-nightly.sh 71 | only: 72 | - master 73 | tags: 74 | - argon 75 | artifacts: 76 | paths: 77 | - release/ 78 | 79 | release-production: 80 | stage: release 81 | script: 82 | - ./.ci/download-signed-stable.sh 83 | only: 84 | - tags 85 | except: 86 | - branches 87 | when: manual 88 | artifacts: 89 | paths: 90 | - release/ 91 | 92 | push-to-fdroid: 93 | stage: fdroid 94 | script: 95 | - ./.ci/push-fdroid.sh 96 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV HOME /home 4 | 5 | RUN rm -f /etc/apt/apt.conf.d/01autoremove-kernels \ 6 | \ 7 | #&& echo '#!/bin/sh' > /usr/sbin/policy-rc.d \ 8 | #&& echo 'exit 101' >> /usr/sbin/policy-rc.d \ 9 | #&& chmod +x /usr/sbin/policy-rc.d \ 10 | #\ 11 | #&& dpkg-divert --local --rename --add /sbin/initctl \ 12 | #&& cp -a /usr/sbin/policy-rc.d /sbin/initctl \ 13 | #&& sed -i 's/^exit.*/exit 0/' /sbin/initctl \ 14 | #\ 15 | #&& echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup \ 16 | \ 17 | && echo 'DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' > /etc/apt/apt.conf.d/docker-clean \ 18 | && echo 'APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' >> /etc/apt/apt.conf.d/docker-clean \ 19 | && echo 'Dir::Cache::pkgcache "";' >> /etc/apt/apt.conf.d/docker-clean \ 20 | && echo 'Dir::Cache::srcpkgcache "";' >> /etc/apt/apt.conf.d/docker-clean \ 21 | \ 22 | && echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/docker-no-languages \ 23 | \ 24 | && echo 'Acquire::GzipIndexes "true";' > /etc/apt/apt.conf.d/docker-gzip-indexes \ 25 | && echo 'Acquire::CompressionTypes::Order:: "gz";' > /etc/apt/apt.conf.d/docker-gzip-indexes \ 26 | \ 27 | && echo 'Apt::AutoRemove::SuggestsImportant "false";' > /etc/apt/apt.conf.d/docker-autoremove-suggests 28 | 29 | RUN apt-get update 30 | 31 | #Locale 32 | RUN apt-get install language-pack-en -y 33 | ENV LANG en_US.UTF-8 34 | ENV LANGUAGE en_US:en 35 | ENV LC_ALL en_US.UTF-8 36 | RUN locale-gen en_US.UTF-8 37 | RUN update-locale 38 | 39 | RUN apt-get install -y make sudo 40 | 41 | RUN chmod 777 /home 42 | #This is required, fixes: KeyError: 'getpwuid(): uid not found: 1000' (later tool.sh adds user with right UID, done dynamic so we don't have to mess with user perms) 43 | RUN chmod 777 /etc/passwd 44 | 45 | ADD Makefile . 46 | ADD tool.sh . 47 | RUN make -C . env 48 | 49 | RUN rm -rfv /home 50 | RUN mkdir /home 51 | RUN chmod 777 /home 52 | -------------------------------------------------------------------------------- /Tutorial-of-packaging-APK.md: -------------------------------------------------------------------------------- 1 | # Guide: 2 | 3 | Tested on Ubuntu 18.04 4 | 5 | Required things: 6 | - A phone or armv7 emulator (can't build for non-arm currently) 7 | - Git 8 | - Make 9 | - ADB 10 | - Docker or Vagrant 11 | 12 | ## Initial Steps 13 | - First `git clone https://github.com/HelloZeroNet/ZeroNet-kivy --recursive` 14 | - Now verify the contents in `src/zero` 15 | - (If you cloned without `--recursive` then run `git submodule init && git submodule update`) 16 | 17 | ## Setup 18 | 19 | ### Docker (recommended) 20 | - Run `make .env`. This will prompt you a few questions (you can usually leave the defaults as-is) and pull the docker-image. 21 | - Now run `setup` to complete the setup 22 | 23 | ### Vagrant 24 | - Run `vagrant up`. This will create an ubuntu 18.04 vm and run the necesarry setup-commands for you. 25 | - NOTES: 26 | - Run all the _building_ commands **inside** the vagrant VM (Use `vagrant ssh` to enter the vm) 27 | - Run all the commands that install the app on your phone **outside** the vagrant VM 28 | - To test you need adb installed. Run `make run` or `make test` **outside** the VM 29 | - If you got it installed run `make run` 30 | - If you are using an emulator append `ADB_FLAG=-e` to the command 31 | 32 | ### Local machine 33 | - Run `make .env` 34 | 35 | ## Building 36 | - Run `make debug` to build the debug version of the app 37 | - Run `make release` to build the release version of the app 38 | - If building succeeds a bin folder with the ZeroNet.apk appears 39 | - Run `make run` to test the app on the connected phone 40 | - If you are using an emulator append `ADB_FLAG=-e` to the command 41 | 42 | ## Debugging 43 | - Running `make test` should start adb logcat 44 | - Additional logs are in `/storage/emulated/0/Android/data/net.mkg20001.zeronet/files/zero/log` 45 | - If `make test` does not work try `make run` 46 | 47 | ### Building 48 | - Run all the commands **inside** the vagrant VM (Use `vagrant ssh` to enter the vm) 49 | - To test you need adb installed. Run `make run` or `make test` **outside** the VM 50 | - If you got it installed run `make run` 51 | - If you are using an emulator append `ADB_FLAG=-e` to the command 52 | -------------------------------------------------------------------------------- /README-zh-cn.md: -------------------------------------------------------------------------------- 1 | # ZeroNet-kivy 2 | [English](./README.md) 3 | 4 | ZeroNet的图形界面控制面板和APP打包,使用Kivy框架 5 | [Kivy](https://kivy.org) 是一个基于Python的跨平台的开源GUI框架。它可以部署到Android和iOS,甚至是桌面平台 (Win, Linux, Mac )。 6 | 7 | 目前本项目的代码只能在Android上运行,欢迎对iOS开发感兴趣的同学加入。 8 | 目前本项目处于Alpha阶段,其GUI缺乏功能和美工设计,代码里保留了很多用于测试的代码。请贡献你的创意,无论是美工还是代码! 9 | 10 | ### 屏幕截图: 11 | 12 | #### 启动界面 13 | ![Startup](/screenshots/startup.png) 14 | #### UI 15 | ![UI](/screenshots/ui.png) 16 | 17 | ### ZeroNet: 18 | 19 | #### 加载界面 20 | ![Loading](/screenshots/loading.png) 21 | #### ZeroHello 22 | ![ZeroHello](/screenshots/zerohello.png) 23 | #### ZeroMe 24 | ![ZeroMe](http://i.imgur.com/nog7YPG.png) 25 | 26 | 27 | ## 目标: 28 | 29 | * 易于安装 30 | - 去掉无用文件,减小安装包体积 31 | - 在Google Play, Apple App Store上发布,另外还有其他平台的官方APP市场或软件包仓库 32 | * 易于使用 33 | - 仅仅一触即可启动或停止ZeroNet服务 34 | - 稳定运行,防止被系统杀掉 35 | + 在Android上,使ZeroNet运行为前台服务,降低被杀几率。如果还是被杀,可以创建一两个守护进程,用来重启ZeroNet服务。 36 | - 在移动设备上,降低电池、流量、存储的消耗量。自动调节ZeroNet在不同情景下的运行模式:是Wifi还是流量,是充着电还是低电量。当然,用户也能自行调节。 37 | - 使users.json和其他敏感数据保存在APP的内部私有目录,避免让其他APP接触到 38 | - 通过GUI导入主密钥或users.json,让用户能够跨设备使用同一ID 39 | - 通过GUI设置ZeroNet,而不是手动编辑zeronet.conf 40 | - 提供一个瘦客户端供用户选择。就像比特币瘦客户端一样,用户无需等待同步大量数据、消耗大量电量和存储,即可使用ZeroNet。瘦客户端向随机的代理 ( gateway ) 服务器接收和发送数据(发送前用私钥签名),私钥并不会泄露。 41 | 42 | 以上的目标,有一部分不是本项目单干就能搞定的,需要向ZeroNet贡献代码,提交更改。 43 | 44 | ## ZeroNet的APK打包教程 45 | 46 | 打包过程不是很难,因为Kivy的Buildozer自动化了很多工作。 47 | [打包教程在这,很详细的](./Tutorial-of-packaging-APK-zh-cn.md) 48 | 49 | ## APK下载 50 | 51 | [ » 点击下载](https://github.com/HelloZeroNet/ZeroNet-kivy/releases) 52 | 53 | [ » 老版本](https://github.com/mkg20001/ZeroNet-kivy/releases) 54 | 55 | ## 如何使用APK 56 | 57 | * 注意你手机上的防火墙和权限控制的设置,请让本APK通过 58 | * 如果你浏览网站时遇到问题,请尝试其他浏览器 59 | * 如果你想关闭ZeroNet,请点击ZeroHello首页的左上角的⋮ 按钮,在菜单中选择关闭 60 | * 你可以升级ZeroNet本身的代码,方法跟在电脑上一样:点击ZeroHello首页的左上角的⋮ 按钮,在菜单中选择版本 x.x.x( rev xxxx),不要管它是不是显示最新,就点它,就会升级到最新的开发版 61 | * 遇到bug或其他问题到外部存储/Android/data/包名如android.test.myapp17/files/zero/log里的log看看有什么异常报错 62 | 63 | ## 项目结构一览 64 | * src 65 | - zeronet.kv - Gui 布局 66 | - main.py - 主文件 67 | - service.py - 服务文件 68 | - platform_*.py - 平台具体代码 69 | * zero - ZeroNet本身的全部代码 (if content is missing run `git submodule init --recursive`) 70 | - zeronet.py - ZeroNet 启动器 71 | * buildozer.spec - Buildozer的配置,你可以定义APK的包名、标题、版本号、android权限、服务等等. 72 | -------------------------------------------------------------------------------- /src/zeronet_config.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os 3 | 4 | 5 | # Parse config file 6 | 7 | 8 | def parseConfig(config_file): 9 | # Load config file 10 | res = dict({}) 11 | if os.path.isfile(config_file): 12 | config = configparser.ConfigParser(allow_no_value=True) 13 | config.read(config_file) 14 | for section in config.sections(): 15 | for key, val in config.items(section): 16 | if section != "global": # If not global prefix key with section 17 | key = section + "_" + key 18 | resval = None 19 | if val: 20 | if len(val.strip().split("\n")) > 1: 21 | resval = [] 22 | for line in val.strip().split("\n"): # Allow multi-line values 23 | resval.append(line) 24 | else: 25 | resval = val 26 | res[key] = resval 27 | return res 28 | 29 | 30 | def saveConfigValue(config_file, key, value): 31 | if not os.path.isfile(config_file): 32 | content = "" 33 | else: 34 | content = open(config_file).read() 35 | lines = content.splitlines() 36 | 37 | global_line_i = None 38 | key_line_i = None 39 | i = 0 40 | for line in lines: 41 | if line.strip() == "[global]": 42 | global_line_i = i 43 | if line.startswith(key + " = "): 44 | key_line_i = i 45 | i += 1 46 | 47 | if value is None: # Delete line 48 | if key_line_i: 49 | del lines[key_line_i] 50 | else: # Add / update 51 | new_line = "%s = %s" % ( 52 | key, str(value).replace("\n", "").replace("\r", "")) 53 | if key_line_i: # Already in the config, change the line 54 | lines[key_line_i] = new_line 55 | elif global_line_i is None: # No global section yet, append to end of file 56 | lines.append("[global]") 57 | lines.append(new_line) 58 | else: # Has global section, append the line after it 59 | lines.insert(global_line_i + 1, new_line) 60 | 61 | open(config_file, "w").write("\n".join(lines) + "\n") 62 | 63 | 64 | def getConfigValue(f, key, default=None): 65 | c = parseConfig(f) 66 | if key not in c: 67 | return default 68 | return c[key] 69 | -------------------------------------------------------------------------------- /src/service.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | from env_json import loadEnv 7 | from os_platform import wrapSentry 8 | 9 | env = loadEnv() 10 | 11 | if env["platform"] == "android": 12 | import android 13 | from jnius import autoclass 14 | 15 | ANDROID_VERSION = autoclass('android.os.Build$VERSION') 16 | SDK_INT = ANDROID_VERSION.SDK_INT 17 | 18 | Service = autoclass('org.kivy.android.PythonService').mService 19 | notifi = autoclass("android.app.Notification$Builder")(Service) 20 | notifi.setContentTitle("ZeroNet") 21 | notifi.setContentText("ZeroNet is running") 22 | 23 | if SDK_INT >= 26: 24 | manager = autoclass('android.app.NotificationManager') # manager is NotificationManager 25 | channel = autoclass('android.app.NotificationChannel') 26 | managerID = autoclass('android.content.Context').NOTIFICATION_SERVICE 27 | 28 | app_channel = channel( # val mChannel = NotificationChannel(CHANNEL_ID, name, importance) 29 | "service_zn", "ZeroNet Background Service", manager.IMPORTANCE_MIN # val importance = NotificationManager.IMPORTANCE_DEFAULT 30 | ) 31 | Service.getSystemService(managerID).createNotificationChannel(app_channel) # val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager 32 | # notificationManager.createNotificationChannel(mChannel) 33 | notifi.setChannel("service_zn") 34 | # if SDK_INT >= 28: 35 | # Service.startForeground(233,notifi) 36 | # else: 37 | # notification = notifi.build() 38 | # Service.startForeground(233,notification) 39 | notification = notifi.build() 40 | Service.startForeground(233,notification) 41 | 42 | print("env %s" % env) 43 | 44 | sys.path.insert(1, env['srcdir']) 45 | sys.path.insert(1, env['srcdir'] + '/src') # insert $src/src too, since sometimes it can't find itself 46 | print("srcdir: %s" % env['srcdir']) 47 | print("sys.path: %s" % sys.path) 48 | 49 | os.chdir(env['srcdir']) 50 | 51 | print("sys.argv: %s" % sys.argv) 52 | 53 | with open(env['pidfile'], "w") as f: 54 | f.write(str(os.getpid())) 55 | f.close() 56 | 57 | if True: # so beautification does not move this to the top 58 | import zeronet # this is only available AFTER sys.path.insert, since it isn't anywhere in this directory 59 | 60 | 61 | def main(): 62 | zeronet.start() 63 | 64 | if __name__ == '__main__': 65 | wrapSentry(main) 66 | 67 | -------------------------------------------------------------------------------- /src/platform_linux.py: -------------------------------------------------------------------------------- 1 | # Linux Specific Code 2 | import locale 3 | import os 4 | import re 5 | import sys 6 | import threading 7 | from os import path 8 | from subprocess import PIPE, Popen 9 | 10 | from platform_service import SystemService 11 | 12 | 13 | def getSystemLang(index=0): 14 | ls = locale.getdefaultlocale() 15 | if len(locale.getdefaultlocale()) < index: 16 | return "en" # fallback 17 | l = ls[index] 18 | if l is None: 19 | return "en" # No locales 20 | print("LOCALE: %s" % l) 21 | match = re.search("^([a-z]{2})_[A-Z]+.*", str(l)) 22 | if match: 23 | return match.group(1) 24 | else: 25 | return getSystemLang(index + 1) 26 | 27 | 28 | def getDir(append=""): 29 | if len(append): 30 | return path.join(path.expanduser("~"), ".zeronet", append) 31 | else: 32 | return path.join(path.expanduser("~"), ".zeronet") 33 | 34 | 35 | def realpath(): 36 | return os.path.dirname(os.path.realpath(__file__)) 37 | 38 | 39 | def getDebug(): 40 | return os.path.exists(os.path.join(realpath(), "..", ".git")) 41 | 42 | 43 | class pipeThread (threading.Thread): 44 | 45 | def __init__(self, threadID, name, counter, args): 46 | threading.Thread.__init__(self) 47 | self.threadID = threadID 48 | self.name = name 49 | self.counter = counter 50 | self.args = args 51 | 52 | def run(self): 53 | # print "Starting "+self.name 54 | self.args.communicate() 55 | print(self.name + " has exited") 56 | 57 | 58 | class Service(SystemService): 59 | 60 | def zeroDir(self): 61 | return os.path.join(realpath(), "zero") 62 | 63 | def getPath(self, append=""): 64 | return getDir(append) 65 | 66 | def getEnvJsonPath(self): 67 | return getDir("env.json") 68 | 69 | def getPidfilePath(self, what="zeronet"): 70 | return getDir(what + ".pid") 71 | 72 | def startWatchdog(self, id): 73 | # if not os.fork(): 74 | self.runGeneric("watchdog_" + str(id) + ".py", 75 | "Watchdog_" + str(id), "watchdog" + str(id)) 76 | # sys.exit(0) 77 | 78 | def runService(self): 79 | return self.runGeneric("service.py", "ZeroNet", "zeronet") 80 | 81 | def runGeneric(self, what, name, pidid): 82 | if self.isRunning(pidid): 83 | print("Skip starting %s, already running as %s" % (name, self.getPid(pidid))) 84 | return False 85 | self.count += 1 86 | env = os.environ 87 | env['ENV_JSON'] = getDir("env.json") 88 | process = Popen([os.path.join(realpath(), what)], env=env) 89 | self.setPid(pidid, process.pid) 90 | running = True 91 | thread = pipeThread(self.count, name, self.count, process) 92 | thread.start() 93 | return thread, process 94 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "ubuntu/bionic64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | config.vm.synced_folder "./", "/home/data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | vb.memory = "4096" 52 | vb.cpus = "4" 53 | end 54 | # 55 | # View the documentation for the provider you are using for more 56 | # information on available options. 57 | 58 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 59 | # such as FTP and Heroku are also available. See the documentation at 60 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 61 | # config.push.define "atlas" do |push| 62 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 63 | # end 64 | 65 | # Enable provisioning with a shell script. Additional provisioners such as 66 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 67 | # documentation for more information about their specific syntax and use. 68 | config.vm.provision "shell", inline: <<-SHELL 69 | whoami 70 | sudo apt-get update 71 | sudo apt-get install -y make sudo 72 | echo -e "EXEC=host\n" > /vagrant/.env 73 | sleep 5s # fix clock skew error 74 | sudo make -C /vagrant env 75 | sudo make -C /vagrant .pre 76 | SHELL 77 | end 78 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Config 2 | 3 | # Vars 4 | 5 | TOOL=bash ./tool.sh 6 | EXEC=$(TOOL) exec 7 | EXEC_NAME=$(shell $(TOOL) _getvar EXEC) 8 | 9 | # Continuous Integration (gitlab-ci) 10 | 11 | _ci: # Wrapper 12 | $(EXEC) make -C /home/data _ci_exec 13 | 14 | _ci_exec: # Actual commands 15 | APP_ALLOW_MISSING_DEPS=true CI_MODE=1 CI=1 buildozer android debug 16 | APP_ALLOW_MISSING_DEPS=true CI_MODE=1 CI=1 buildozer android release 17 | 18 | # Setup 19 | 20 | env: 21 | sudo dpkg --add-architecture i386 22 | sudo apt-get update 23 | sudo apt-get install -y python2.7 python-pip software-properties-common python3 python3-dev python-dev 24 | sudo apt-get install -y mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev zip 25 | sudo add-apt-repository ppa:kivy-team/kivy -y 26 | sudo apt-get update 27 | sudo apt-get install -y build-essential cmake swig ccache git libtool pkg-config libncurses5:i386 libstdc++6:i386 libgtk2.0-0:i386 libpangox-1.0-0:i386 libpangoxft-1.0-0:i386 libidn11:i386 python2.7 python2.7-dev openjdk-8-jdk unzip zlib1g-dev zlib1g:i386 28 | sudo apt-get install -y automake aidl libbz2-dev libffi-dev lld 29 | sudo apt-get install -y python-kivy 30 | sudo pip2 install --upgrade Cython==0.28.6 31 | sudo pip2 install --upgrade colorama appdirs 'sh>=1.10,<1.12.5' jinja2 six clint requests 32 | sudo pip2 install --upgrade git+https://github.com/mkg20001/buildozer kivy 33 | sudo pip2 install "appdirs" "colorama>=0.3.3" 'sh>=1.10,<1.12.5' "jinja2" "six" 34 | 35 | # Targets for setup 36 | 37 | setup: .pre 38 | .env: 39 | @echo "No .env file found..." 40 | @echo "Running setup..." 41 | $(TOOL) setup 42 | 43 | .pre: .env .deps 44 | 45 | # Targets for specific build modes 46 | 47 | .deps: 48 | make -C . $(EXEC_NAME)-deps 49 | touch .deps 50 | 51 | host-deps: prebuild 52 | 53 | docker-deps: 54 | # TODO: rethink below cmd 55 | mkdir -p $(HOME)/.buildozer && sudo chmod 777 $(HOME)/.buildozer && mkdir -p $(HOME)/.gradle && sudo chmod 777 $(HOME)/.gradle && mkdir -p $(HOME)/.android/cache && sudo chmod 777 $(HOME)/.android/cache 56 | $(EXEC) make -C /home/data prebuild # Launch in wrapper 57 | 58 | docker-build: 59 | docker build -t kivy . 60 | 61 | # Actual Targets 62 | 63 | debug: .pre 64 | $(EXEC) env APP_ALLOW_MISSING_DEPS=true buildozer -v android debug 65 | 66 | release: .pre 67 | $(EXEC) env APP_ALLOW_MISSING_DEPS=true buildozer -v android release 68 | 69 | run: .pre 70 | adb $(ADB_FLAG) install -r bin/$(shell dir -w 1 bin | sort | tail -n 1) 71 | make logcat 72 | 73 | logcat: 74 | adb $(ADB_FLAG) logcat | grep "[A-Z] python\|linker\|art\|zn\|watch1\|watch2" 75 | 76 | test: .pre 77 | $(EXEC) buildozer -v android deploy logcat 78 | 79 | prebuild: .env 80 | $(TOOL) prebuild 81 | 82 | # Old targets (will be replaced by automated scripts later) 83 | release-do: 84 | cp -v bin/release/ZeroNet.apk $(HOME)/ZeroNet/data/1A9gZwjdcTh3bpdriaWm7Z4LNdUL8GhDu2 85 | cp -v bin/release/metadata.json $(HOME)/ZeroNet/data/1A9gZwjdcTh3bpdriaWm7Z4LNdUL8GhDu2 86 | release-align: 87 | rm -f bin/release.apk 88 | zipalign -v -p 4 $(shell find bin -type f -iname "*-release-unsigned.apk") bin/release.apk 89 | release-sign: 90 | rm -rf release bin/release 91 | mkdir release 92 | $(shell find $(ANDROID_HOME) -iname "apksigner" | sort | tac | head -n 1) sign --ks $(HOME)/.android/release --out release/ZeroNet.apk bin/release.apk 93 | $(shell find $(ANDROID_HOME) -iname "apksigner" | sort | tac | head -n 1) verify release/ZeroNet.apk 94 | mv release bin/release 95 | $(TOOL) metadata 96 | watch: #runs on desktop 97 | nodemon -e kv,py,json -x /usr/bin/python2 src/main.py 98 | -------------------------------------------------------------------------------- /src/translate/Translate.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import json 3 | import logging 4 | import os 5 | import re 6 | 7 | from os_platform import getSystemLang 8 | 9 | 10 | class Translate(dict): 11 | 12 | def __init__(self, lang_dir=None, lang=None): 13 | if not lang_dir: 14 | lang_dir = os.path.join(os.path.dirname( 15 | os.path.abspath(__file__)), "languages") 16 | if not lang: 17 | lang = getSystemLang() 18 | self.lang = lang 19 | self.lang_dir = lang_dir 20 | self.setLanguage(lang) 21 | 22 | def setLanguage(self, lang): 23 | self.lang = lang 24 | self.lang_file = os.path.join(self.lang_dir, "%s.json" % lang) 25 | return self.load() 26 | 27 | def __repr__(self): 28 | return "" % self.lang 29 | 30 | def load(self): 31 | if os.path.isfile(self.lang_file): 32 | data = json.load(open(self.lang_file)) 33 | print("Loaded translate file: %s (%s entries)" % (self.lang_file, len(data))) 34 | dict.__init__(self, data) 35 | return True 36 | else: 37 | data = {} 38 | dict.__init__(self, data) 39 | self.clear() 40 | print("Translate file not exists: %s" % self.lang_file) 41 | return False 42 | 43 | def format(self, s, kwargs, nested=False): 44 | kwargs["_"] = self 45 | if nested: 46 | return s.format(**kwargs).format(**kwargs) 47 | else: 48 | return s.format(**kwargs) 49 | 50 | def formatLocals(self, s, nested=False): 51 | kwargs = inspect.currentframe().f_back.f_locals 52 | return self.format(s, kwargs, nested=nested) 53 | 54 | def __call__(self, s): 55 | return self.translateString(s) 56 | 57 | # def __call__(self, s, kwargs=None, nested=False): 58 | # if kwargs: 59 | # return self.format(s, kwargs, nested=nested) 60 | # else: 61 | # kwargs = inspect.currentframe().f_back.f_locals 62 | # return self.format(s, kwargs, nested=nested) 63 | 64 | def __missing__(self, key): 65 | return key 66 | 67 | def pluralize(self, value, single, multi): 68 | if value > 1: 69 | return self[single].format(value) 70 | else: 71 | return self[multi].format(value) 72 | 73 | def translateString(self, string, translate_table=None, mode="js"): 74 | if not translate_table: 75 | translate_table = self 76 | 77 | if string in translate_table: 78 | return translate_table[string] 79 | else: 80 | return string 81 | 82 | def translateData(self, data, translate_table=None, mode="js"): 83 | if not translate_table: 84 | translate_table = self 85 | 86 | data = data.decode("utf8") 87 | 88 | patterns = [] 89 | for key, val in list(translate_table.items()): 90 | # Problematic string: only match if called between _(" ") function 91 | if key.startswith("_("): 92 | key = key.replace("_(", "").replace( 93 | ")", "").replace(", ", '", "') 94 | translate_table[key] = "|" + val 95 | patterns.append(re.escape(key)) 96 | 97 | def replacer(match): 98 | target = translate_table[match.group(1)] 99 | if mode == "js": 100 | if target and target[0] == "|": # Strict string match 101 | # Only if the match if called between _(" ") function 102 | if match.string[match.start() - 2] == "_": 103 | return '"' + target[1:] + '"' 104 | else: 105 | return '"' + match.group(1) + '"' 106 | return '"' + target + '"' 107 | else: 108 | return match.group(0)[0] + target + match.group(0)[-1] 109 | 110 | if mode == "html": 111 | pattern = '[">](' + "|".join(patterns) + ')["<]' 112 | else: 113 | pattern = '"(' + "|".join(patterns) + ')"' 114 | data = re.sub(pattern, replacer, data) 115 | return data.encode("utf8") 116 | 117 | translate = Translate() 118 | -------------------------------------------------------------------------------- /tool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Config 4 | 5 | ADB_FLAG=-d 6 | source .version.sh 7 | 8 | # Vars 9 | 10 | [ -z "$UID" ] && UID=$(id -u) 11 | [ -z "$PWD" ] && PWD="$PWD" 12 | 13 | bold=$(tput bold) 14 | normal=$(tput sgr0) 15 | RED=$(tput setaf 1) 16 | NC="$normal" # No Color 17 | 18 | # Executors 19 | 20 | exec_docker() { 21 | DOCKER_CMD=(docker run -u "$UID" --rm "--privileged=true" -it -e "APP_ALLOW_MISSING_DEPS=true" -e "USE_SDK_WRAPPER=1" -v "$PWD:/home/data" -v "$HOME/.gradle:/home/.gradle" -v "$HOME/.buildozer:/home/.buildozer" -v "$HOME/.android:/home/.android") 22 | 23 | # if [ ! -z "$USE_VENDOR_BUILDOZER" ]; then 24 | DOCKER_CMD+=(-v "$PWD/vendor/buildozer/buildozer:/usr/local/lib/python2.7/dist-packages/buildozer") 25 | # fi 26 | 27 | if [ ! -z "$CI" ]; then 28 | DOCKER_CMD+=(-e "CI=$CI") 29 | fi 30 | 31 | DOCKER_CMD+=("$docker_image" sh -c "echo builder:x:$UID:27:Builder:/home:/bin/bash | tee /etc/passwd > /dev/null && cd /home/data && $*") 32 | 33 | "${DOCKER_CMD[@]}" 34 | } 35 | 36 | exec_host() { 37 | export USE_SDK_WRAPPER=1 38 | export APP_ALLOW_MISSING_DEPS=true 39 | "$@" 40 | } 41 | 42 | _exec() { 43 | [ -z "$*" ] && die "No command passed to exec!" 44 | debug "EXEC $EXEC, CMD $*" 45 | "exec_$EXEC" "$@" 46 | } 47 | 48 | debug() { 49 | [ ! -z "$DEBUG" ] && echo "${bold}DEBUG${NC}: $*" 50 | } 51 | 52 | info() { 53 | echo "${bold}[INFO]${NC}: $*" 54 | } 55 | 56 | menu() { 57 | a=($1) # arrayify 58 | i=0 59 | for c in $1; do # show list 60 | echo -n "[$i] $c" 61 | [ "$i" == "0" ] && echo " (recommended)" || echo 62 | i=$(expr $i + 1) 63 | done 64 | read -p "> " id # prompt user 65 | res="${a[$id]}" 66 | [ -z "$res" ] && echo "Invalid ID: $id" && menu "$1" && return 0 67 | echo "< $res" 68 | } 69 | 70 | error() { 71 | echo "${RED}${bold}[ERROR]${NC}: $*" 72 | } 73 | 74 | die() { 75 | error "$*" 76 | exit 2 77 | } 78 | 79 | # Scripts 80 | 81 | mkenv() { 82 | info "Running 'make env'" 83 | set +e 84 | make -C env 85 | } 86 | 87 | getmeta() { 88 | cat src/zero/src/Config.py | grep "$1 =" | sed -r "s|(.*) = ||g" 89 | } 90 | 91 | # Main Code 92 | 93 | if [ "$1" == "_getvar" ]; then 94 | [ -e .env ] && source .env && echo "${!2}" 95 | exit 0 96 | fi 97 | 98 | [ ! -e .env ] && [ "$1" != "setup" ] && die "No .env file found! Run '$0 setup'" 99 | 100 | source .env 101 | 102 | docker_image="$DOCKER_IMAGE" 103 | 104 | debug "EXEC=$EXEC" 105 | debug "docker_image=$docker_image" 106 | 107 | case "$1" in 108 | setup) 109 | echo "How should buildozer/kivy be executed?" 110 | menu "docker host" 111 | echo "EXEC=$res" > .env 112 | case "$res" in 113 | docker) 114 | echo "Which image should be used?" 115 | echo "(The 'kivy' image should only be used if you are building the image yourself using '$0 docker-build')" 116 | menu "registry.gitlab.com/mkg20001-gh/zeronet-kivy:latest kivy" 117 | echo "DOCKER_IMAGE=$res" >> .env 118 | [ "$res" != "kivy" ] && docker pull "$res" 119 | ;; 120 | host) 121 | echo "Please run 'make host-deps' to complete the setup" 122 | echo "(This will install all needed packages and tools)" 123 | exit 2 124 | ;; 125 | esac 126 | ;; 127 | env) 128 | mkenv 129 | ;; 130 | exec) 131 | shift 132 | _exec "$@" 133 | ;; 134 | prebuild) 135 | if [ -e .pre ]; then rm -rf src/zero && git submodule update; fi 136 | V=$(echo $(cat src/zero/src/Config.py | grep self.version | sed -r "s|self\.version = ['\"](.*)['\"]|\1|g" | head -n 1)) 137 | if [ "$V" != "$CUR_VERSION" ]; then echo ".version.sh \$VER_SUFFIX needs to be reset, major version change: $CUR_VERSION != $V" && exit 2; fi 138 | cd src/zero && cp src/Config.py src/Config.py_ && sed -r "s|self\.version = ['\"](.*)['\"]|self.version = \"\1.$VER_SUFFIX\"|g" -i src/Config.py 139 | echo ZGlmZiAtLWdpdCBhL3NyYy9tYWluLnB5IGIvc3JjL21haW4ucHkKaW5kZXggZGZjMzJjYzkuLjlmMjFlZjJjIDEwMDY0NAotLS0gYS9zcmMvbWFpbi5weQorKysgYi9zcmMvbWFpbi5weQpAQCAtNTIsNyArNTIsNyBAQCBpZiBjb25maWcuYWN0aW9uID09ICJtYWluIjoKICAgICAgICAgICAgICAgICBwcmludCgiRXJyb3Igc3RhcnRpbmcgYnJvd3NlcjogJXMiICUgZXJyKQogICAgICAgICBzeXMuZXhpdCgpCiAKLWNvbmZpZy5pbml0TG9nZ2luZygpCitjb25maWcuaW5pdExvZ2dpbmcoY29uc29sZV9sb2dnaW5nPUZhbHNlKQogCiAKICMgRGVidWcgZGVwZW5kZW50IGNvbmZpZ3VyYXRpb24K | base64 -d | git apply - 140 | cd ../../ 141 | touch .pre 142 | ;; 143 | metadata) 144 | echo "{\"rev\":$(getmeta self.rev),\"ver\":$(getmeta self.version),\"date\":$(expr $(date +%s) \* 1000)}" > release/metadata.json 145 | ;; 146 | post-sign) 147 | cp -v bin/release/{ZeroNet.apk,metadata.json} $HOME/ZeroNet/data/1A9gZwjdcTh3bpdriaWm7Z4LNdUL8GhDu2 148 | ;; 149 | docker-build) 150 | make docker-build 151 | ;; 152 | *) 153 | die "No command specified! Use '$0 {setup, env, exec, docker-build, prebuild}'" 154 | ;; 155 | esac 156 | -------------------------------------------------------------------------------- /src/platform_android.py: -------------------------------------------------------------------------------- 1 | # Android Specific Code 2 | import os 3 | import re 4 | import sys 5 | import time 6 | from os import path 7 | 8 | import android 9 | import M2Crypto 10 | from jnius import autoclass 11 | from M2Crypto import EVP, RSA 12 | from platform_service import SystemService 13 | # plyer in pip is old and has bugs, so need to download source code 14 | # manually from plyer repo to replace all the plyer source code folder 15 | # downloaded by buildozer 16 | from plyer.platforms.android import SDK_INT, activity 17 | 18 | mActivity = autoclass('org.kivy.android.PythonActivity').mActivity 19 | 20 | 21 | def getSystemLang(): 22 | l = mActivity.getResources().getConfiguration().locale.toString() 23 | print("LOCALE: %s" % l) 24 | match = re.search("^([a-z]{2})_[A-Z]+.*", l) 25 | if match: 26 | return match.group(1) 27 | else: 28 | return "en" 29 | 30 | 31 | def getDir(append=""): 32 | if len(append): 33 | return path.join(mActivity.getExternalFilesDir(None).getPath(), append) 34 | else: 35 | return mActivity.getExternalFilesDir(None).getPath() 36 | 37 | 38 | def realpath(): 39 | return os.path.dirname(os.path.realpath(__file__)) 40 | 41 | 42 | def getDebug(): 43 | return autoclass(activity.getPackageName() + ".BuildConfig").DEBUG 44 | 45 | def wrapSentry(mainFunc): 46 | if not getDebug(): 47 | import traceback 48 | try: 49 | import sentry_sdk 50 | from sentry_sdk import capture_exception 51 | sentry_sdk.init("https://1cc0c8280fa54361920e75f014add9fe@sentry.io/1406946") 52 | try: 53 | mainFunc() 54 | except Exception as e: 55 | traceback.print_exc() 56 | capture_exception(e) 57 | except Exception as e: 58 | traceback.print_exc() 59 | mainFunc() 60 | else: 61 | mainFunc() 62 | 63 | # Generate a SSL certificate using module M2Crypto, an existing one will 64 | # be overwritten . 65 | 66 | 67 | def generate_self_signed_cert_m2(cert_dir): 68 | if not os.path.exists(cert_dir): 69 | os.makedirs(cert_dir) 70 | cert_path = os.path.join(cert_dir, 'cert-rsa.pem') 71 | key_path = os.path.join(cert_dir, 'key-rsa.pem') 72 | 73 | if os.path.exists(cert_path): 74 | os.remove(cert_path) 75 | if os.path.exists(key_path): 76 | os.remove(key_path) 77 | 78 | # create a key pair 79 | key = RSA.gen_key(2048, 65537) 80 | key.save_key(key_path, None) 81 | pkey = EVP.PKey() # Converting the RSA key into a PKey() which is stored in a certificate 82 | pkey.assign_rsa(key) 83 | 84 | # create a self-signed cert, the config is copied from src/lib/opensslVerify/openssl.cnf. not sure whether making it random is good or not. 85 | # time for certificate to stay valid 86 | cur_time = M2Crypto.ASN1.ASN1_UTCTIME() 87 | cur_time.set_time(int(time.time()) - 60 * 60 * 24) 88 | expire_time = M2Crypto.ASN1.ASN1_UTCTIME() 89 | expire_time.set_time(int(time.time()) + 60 * 60 * 24 * 365) 90 | # creating a certificate 91 | cert = M2Crypto.X509.X509() 92 | cert.set_pubkey(pkey) 93 | cs_name = M2Crypto.X509.X509_Name() 94 | cs_name.C = "US" 95 | cs_name.ST = 'NY' 96 | cs_name.L = 'New York' 97 | cs_name.O = 'Example, LLC' 98 | cs_name.CN = 'Example Company' 99 | # cs_name.Email = "example@example.com" 100 | cert.set_subject(cs_name) 101 | cert.set_issuer_name(cs_name) 102 | cert.set_not_before(cur_time) 103 | cert.set_not_after(expire_time) 104 | # self signing a certificate 105 | cert.sign(pkey, md="sha256") 106 | cert.save_pem(cert_path) 107 | return cert_path, key_path 108 | 109 | 110 | class Service(SystemService): 111 | 112 | def zeroDir(self): 113 | return os.path.join(os.environ['ANDROID_APP_PATH'], 'zero') 114 | 115 | def getPath(self, append=""): 116 | return getDir(append) 117 | 118 | def runGeneric(self, what, pidid): 119 | if self.isRunning(pidid): 120 | print("Skip starting %s, already running as %s" % (what, self.getPid(pidid))) 121 | return False 122 | service_fullname = activity.getPackageName() + '.Service' + what 123 | service = autoclass(service_fullname) 124 | argument = "" 125 | service.start(mActivity, argument) 126 | 127 | def startWatchdog(self, id): 128 | return self.runGeneric("Watch%s" % id, "watchdog%s" % id) 129 | 130 | def runService(self): 131 | # Generate a SSL certificate to 'data' folder, existing pem files will 132 | # be overwritten . TODO: read the `data_dir` in zeronet.conf 133 | generate_self_signed_cert_m2( 134 | os.path.join(self.getPath("zero"), 'data')) 135 | # Start service ( system will run service.py) 136 | return self.runGeneric("Zn", "zeronet") 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZeroNet-kivy 2 | [简体中文](./README-zh-cn.md) 3 | [Support](https://t.me/zeronet_android) 4 | 5 | This app is a simple GUI to control the ZeroNet client. It is packaged using buildozer and uses [Kivy](https://kivy.io). 6 | 7 | Currently the code of this repo only works on Android, anyone interested in iOS development is welcome to contribute. Simply drop your comment on [this page](https://github.com/HelloZeroNet/ZeroNet-kivy/issues/35) if you want to become a maintainer for that platform! 8 | Currently this project is in Alpha phase, lack of GUI functionalities and creative design, containing many code for testing purpose. Please feel free to contribute! 9 | 10 | # Downloads 11 | 12 | You can download the latest version of the app from: 13 | 14 | - the [ » PlayStore](https://play.google.com/store/apps/details?id=net.mkg20001.zeronet) 15 | - the [ » F-Droid repository for ZeroNet](https://f-droid.mkg20001.io/repos/jVVkbOD2it_bf_UxFIGGh1qa950KrUsq/repo/?fingerprint=005E888A5A203D40E28F372B939B8E5995FB29081EFC845DB99A8D4C14B509E2) 16 | - [ » GitHub Releases](https://github.com/HelloZeroNet/ZeroNet-kivy/releases) 17 | 18 | ## Downloads for really old phones 19 | 20 | If you're using a phone with Android 4 or less then you can't use this app. If you insist you can use the [ » old version](https://gateway.ipfs.io/ipfs/QmWKSoPfXpfvTU7jtiwn51zPVFZ6fWMiKNgDBzbnH9krXY/ZeroNet-OLDVERSION.apk). But beware: NO functionality and NO security are guaranteed. 21 | 22 | You can also go to the list of [ » older releases](https://github.com/mkg20001/ZeroNet-kivy/releases) if you're intrested in the very first versions 23 | 24 | ## Screenshots: 25 | 26 | ### App: 27 | 28 | #### Splash Screen 29 | ![Startup](/screenshots/startup.png) 30 | #### UI 31 | ![UI](/screenshots/ui.png) 32 | 33 | ### ZeroNet: 34 | 35 | #### Loading Screen 36 | ![Loading](/screenshots/loading.png) 37 | #### ZeroHello 38 | ![ZeroHello](/screenshots/zerohello.png) 39 | #### ZeroMe 40 | ![ZeroMe](http://i.imgur.com/nog7YPG.png) 41 | 42 | 43 | ## Goals: 44 | 45 | * [ ] User-friendly installing 46 | - [ ] Reduce the package size by removing unused files 47 | - [x] Release on F-Droid, Google Play, Apple App Store and other platform's official APP market or package repository. 48 | * [ ] Easy to use 49 | - [ ] Start or stop ZeroNet service by just a single tap 50 | - [ ] Running without killed by system 51 | + [x] On Android, make ZeroNet service Foreground to keep it less likely to be killed. If still killed, create 1~2 daemon services to restart ZeroNet service when it killed 52 | - [ ] Reduce battery and data quota ( bandwidth ) as well as data storage consuming on mobile devices. Auto-adjust the behavior of ZeroNet in different scenarios, e.g. using Wifi or cellular data, being charged or low battery. Of course, users can adjust it by themselves via GUI 53 | - [ ] Keep users.json and other sensitive data in internal private directory of the APP, out of other APPs' reach 54 | - [ ] Import master seed or users.json via GUI to let users import their ZeroNet IDs 55 | - [ ] GUI config of ZeroNet instead of editing zeronet.conf manually 56 | - [ ] Offer a thin client of ZeroNet for users' choice, working like a thin client of Bitcoin, via which users can use ZeroNet without joining as a full client, waiting large data sync, consuming much battery and data quota ( bandwidth ) as well as data storage. The thin client, holding the user's private keys, receives data from random proxies ( gateways ) and posts signed data to random proxies ( gateways ) without user's private keys leaving the user's own device 57 | 58 | Actually, some above goals are out of the scope of this project, which means we also need to contribute to ZeroNet project itself to achieve said goals. 59 | 60 | 61 | ## Tutorial of packaging APK for ZeroNet 62 | 63 | The packaging is not hard, thanks to Kivy's Buildozer which automates many things for us. 64 | [The tutorial is here, which shows you in details how to do that.](./Tutorial-of-packaging-APK.md) 65 | 66 | ## How to use the APK 67 | 68 | * Be careful of your phone's firewall and permission control, let the APK go. 69 | * If you have any problem using the web UI, you can try anther browser 70 | * If you want to shut down ZeroNet, click ZeroHello's top-left ⋮ button and choose `shut down` 71 | * You can update ZeroNet's source code just as you do on your PC: click ZeroHello's top-left ⋮ button, choose Version x.x.x (rev xxxx) 72 | - Even if it says "Already Up-To-Date" just choose it so you'll get newest dev version of ZeroNet 73 | * You can find all the ZeroNet things and do what you want in External Storage/Android/data/net.mkg20001.zeronet/files/zero 74 | * If you find any bug or something, go to External Storage/Android/data/net.mkg20001.zeronet/files/zero/log to see what it said in log 75 | 76 | ## Project Structure 77 | * src 78 | - zeronet.kv - Gui layout 79 | - main.py - Main file 80 | - service.py - Service file 81 | - platform_*.py - Platform Specific code 82 | * zero - ZeroNet Source Code (if content is missing run `git submodule init --recursive`) 83 | - zeronet.py - ZeroNet Launcher 84 | * buildozer.spec - Buildozer config file with package name, title, version, android.permissions, services, etc. 85 | -------------------------------------------------------------------------------- /src/zeronet.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | #:import _ translate.translate 3 | #:import hex kivy.utils.get_color_from_hex 4 | #:import hex2 light_colors.hex 5 | #:import browser webbrowser.open 6 | 7 |