diff --git a/.gitignore b/.gitignore index f834d003..4586b0f0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,11 +9,16 @@ cmd/bitmask-vpn/bitmask-vpn cmd/bitmask-helper/bitmask-helper /bitmask-connect cmd/bitmask-connect/bitmask-connect +gui/i18n/*.qm +/snap locales/*/out.gotext.json tools/transifex/transifex branding/assets/default +lib/* +qtbuild/* +providers/assets .*.swp *.exe diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 23575cf9..4f6efa4e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,75 +5,70 @@ stages: - trigger - vendor -variables: - GOPATH: /go - APP_PATH: /go/src/0xacab.org/leap/bitmask-vpn - build_test: - image: registry.0xacab.org/leap/docker/bitmask-vpn:latest + image: registry.0xacab.org/leap/bitmask-vpn:latest stage: build script: - - mkdir -p /go/src/0xacab.org/leap/ - - ln -s "$(pwd)" ${APP_PATH} - - cd ${APP_PATH} - - make get + - make generate - make test - - make build_bitmaskd + # TODO missing xcb package in the docker image. investigate what to install to run minimal tests. + # - make test_ui - make build tags: - linux artifacts: paths: - - 'bitmask-vpn' - - 'bitmask-helper' - - 'bitmask-connect' + - 'qtbuild/release/riseup-vpn' expire_in: 1 month -branded_push: - image: registry.0xacab.org/leap/docker/bitmask-vpn:latest - stage: push - only: - - master - script: - # install the command-line openssh client to manage private keys - - apt install -y openssh-client - # activate the ssh-agent - - eval $(ssh-agent -s) - # load the private key, which is accessed vi a gitlab CI secret environment variable - # We're using tr to fix line endings which makes ed25519 keys work - # without extra base64 encoding. - - ssh-add <(echo "$RISEUP_VPN_PACKAGE_SSH_KEY") - - mkdir -p ~/.ssh - # ensure that ssh will trust a new host, instead of asking - - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config - # we also need to configure name and email for git user - - git config user.name "Gitlab CI" - - git config user.email "gitlabci@0xacab.org" - # Add the remote repository and push to it - sometimes it already exists, and it causes the pipeline to fail, so we only add if its not already there - - git remote -v |grep -q riseup-vpn || git remote add riseup-vpn git@0xacab.org:leap/riseup-vpn_package.git - - git push --force riseup-vpn HEAD:incoming +# branded_push: +# image: registry.0xacab.org/leap/bitmask-vpn:latest +# stage: push +# only: +# - master +# script: +# # install the command-line openssh client to manage private keys +# - apt install -y openssh-client +# # activate the ssh-agent +# - eval $(ssh-agent -s) +# # load the private key, which is accessed vi a gitlab CI secret environment variable +# # We're using tr to fix line endings which makes ed25519 keys work +# # without extra base64 encoding. +# - ssh-add <(echo "$RISEUP_VPN_PACKAGE_SSH_KEY") +# - mkdir -p ~/.ssh +# # ensure that ssh will trust a new host, instead of asking +# - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config +# # we also need to configure name and email for git user +# - git config user.name "Gitlab CI" +# - git config user.email "gitlabci@0xacab.org" +# # Add the remote repository and push to it - sometimes it already exists, and it causes the pipeline to fail, so we only add if its not already there +# - git remote -v |grep -q riseup-vpn || git remote add riseup-vpn git@0xacab.org:leap/riseup-vpn_package.git +# - git push --force riseup-vpn HEAD:incoming -trigger_deb: - image: registry.0xacab.org/leap/docker/buster_amd64:latest - stage: trigger - script: - - echo "Triggering CI pipeline on https://0xacab.org/leap/riseup-vpn_package/pipelines" - - "curl -X POST -F token=$RISEUP_VPN_DEB_TRIGGER_TOKEN -F ref=master https://0xacab.org/api/v4/projects/1916/trigger/pipeline" +# trigger_deb: +# image: registry.0xacab.org/leap/buster_amd64:latest +# stage: trigger +# script: +# - echo "Triggering CI pipeline on https://0xacab.org/leap/riseup-vpn_package/pipelines" +# - "curl -X POST -F token=$RISEUP_VPN_DEB_TRIGGER_TOKEN -F ref=master https://0xacab.org/api/v4/projects/1916/trigger/pipeline" vendorize: - image: registry.0xacab.org/leap/docker/bitmask-vpn:latest + image: registry.0xacab.org/leap/bitmask-vpn:latest stage: vendor script: - - 'PROVIDERS="riseup calyx" make build_all_providers' + #- TODO build_all_providers script can be rescued when we achieve to bring back xbuild for win/osx. + # For now it only makes sense to build the snap. + #- 'PROVIDERS="riseup calyx" make build_all_providers' + - 'PROVIDER="riseup" make vendor && make package_snap' artifacts: name: installers-$CI_COMMIT_REF_NAME paths: - 'deploy/*.snap' - - 'deploy/RiseupVPN-*.exe' - - 'deploy/RiseupVPN-*.pkg' - - 'deploy/riseup-vpn_*.deb' - - 'deploy/CalyxVPN-*.exe' - - 'deploy/CalyxVPN-*.pkg' - - 'deploy/calyx-vpn_*.deb' +# - 'deploy/RiseupVPN-*.exe' +# - 'deploy/RiseupVPN-*.pkg' +# - 'deploy/riseup-vpn_*.deb' +# - 'deploy/CalyxVPN-*.exe' +# - 'deploy/CalyxVPN-*.pkg' +# - 'deploy/calyx-vpn_*.deb' expire_in: 1 month diff --git a/Makefile b/Makefile index 96f60e33..ff93312a 100644 --- a/Makefile +++ b/Makefile @@ -3,30 +3,47 @@ # (c) LEAP Encryption Access Project, 2019-2020 ######################################################################### -.PHONY: all get build build_bitmaskd icon locales generate_locales clean - -TAGS ?= gtk_3_18 +.PHONY: all get build icon locales generate_locales clean check_qtifw HAS-qtifw relink_vendor XBUILD ?= no SKIP_CACHECK ?= no -PROVIDER ?= $(shell grep ^'provider =' branding/config/vendor.conf | cut -d '=' -f 2 | tr -d "[:space:]") -PROVIDER_CONFIG ?= branding/config/vendor.conf -DEFAULT_PROVIDER = branding/assets/default/ +VENDOR_PATH ?= providers +APPNAME ?= $(shell VENDOR_PATH=${VENDOR_PATH} branding/scripts/getparam appname | tail -n 1) +TARGET ?= $(shell VENDOR_PATH=${VENDOR_PATH} branding/scripts/getparam binname | tail -n 1) +PROVIDER ?= $(shell grep ^'provider =' ${VENDOR_PATH}/vendor.conf | cut -d '=' -f 2 | tr -d "[:space:]") VERSION ?= $(shell git describe) # go paths GOPATH = $(shell go env GOPATH) -SYSTRAY = 0xacab.org/leap/bitmask-vpn -GOSYSTRAY = ${GOPATH}/src/${SYSTRAY} +TARGET_GOLIB=lib/libgoshim.a +SOURCE_GOLIB=gui/backend.go -# detect OS, we use it for dependencies +# detect OS +ifeq ($(OS), Windows_NT) +PLATFORM = windows +else UNAME = $(shell uname -s) PLATFORM ?= $(shell echo ${UNAME} | awk "{print tolower(\$$0)}") +endif -TEMPLATES = branding/templates +QTBUILD = build/qt +INSTALLER = build/installer +INST_DATA = ${INSTALLER}/packages/bitmaskvpn/data/ +OSX_CERT="Developer ID Installer: LEAP Encryption Access Project" +MACDEPLOYQT_OPTS = -appstore-compliant -qmldir=gui/qml -always-overwrite +# XXX expired cert -codesign="${OSX_CERT}" + SCRIPTS = branding/scripts +TEMPLATES = branding/templates + +TAP_WINDOWS = https://build.openvpn.net/downloads/releases/tap-windows-9.24.2-I601-Win10.exe -all: icon locales build +ifeq ($(PLATFORM), windows) +HAS_QTIFW := $(shell which binarycreator.exe) +else +HAS_QTIFW := $(shell PATH=$(PATH) which binarycreator) +endif +OPENVPN_BIN = "$(HOME)/openvpn_build/sbin/$(shell grep OPENVPN branding/thirdparty/openvpn/build_openvpn.sh | head -n 1 | cut -d = -f 2 | tr -d '"')" ######################################################################### @@ -41,116 +58,159 @@ install_go: @sudo apt-get install golang-go depends: - -@make depends$(UNAME) - @go get -u golang.org/x/text/cmd/gotext github.com/cratonica/2goarray - + -@make depends$(PLATFORM) + #@go get -u golang.org/x/text/cmd/gotext github.com/cratonica/2goarray + dependsLinux: - @sudo apt install libgtk-3-dev libappindicator3-dev golang pkg-config dh-golang golang-golang-x-text-dev cmake devscripts fakeroot debhelper curl + @sudo apt install golang pkg-config dh-golang golang-golang-x-text-dev cmake devscripts fakeroot debhelper curl g++ qt5-qmake qttools5-dev-tools qtdeclarative5-dev qml-module-qtquick-controls libqt5qml5 qtdeclarative5-dev qml-module-qt-labs-platform qml-module-qt-labs-qmlmodels qml-module-qtquick-extras qml-module-qtquick-dialogs @make -C docker deps @# debian needs also: snap install snapcraft --classic; snap install multipass --beta --classic dependsDarwin: - # TODO - bootstrap homebrew if not there - @brew install python3 golang make pkg-config upx curl + @brew install python3 golang make pkg-config curl @brew install --default-names gnu-sed +dependswindows: -dependsCygwin: @choco install -y golang python nssm nsis wget 7zip -build: -ifeq (${XBUILD}, yes) - $(MAKE) build_cross_win - $(MAKE) build_cross_osx - $(MAKE) _build_xbuild_done -else ifeq (${XBUILD}, win) - $(MAKE) build_cross_win - $(MAKE) _build_done -else ifeq (${XBUILD}, osx) - $(MAKE) build_cross_osx - $(MAKE) _build_done -else - $(MAKE) _buildparts + +ifeq ($(PLATFORM), darwin) +EXTRA_FLAGS = MACOSX_DEPLOYMENT_TARGET=10.10 GOOS=darwin CC=clang +else +EXTRA_FLAGS = endif -_buildparts: $(foreach path,$(wildcard cmd/*),build_$(patsubst cmd/%,%,$(path))) -build_%: - @echo "PLATFORM: ${PLATFORM}" - @mkdir -p build/bin/${PLATFORM} - go build -tags $(TAGS) -ldflags "-s -w -X main.version=`git describe --tags` ${EXTRA_LDFLAGS}" -o build/bin/${PLATFORM}/$* ./cmd/$* - -@rm -rf build/${PROVIDER}/staging/${PLATFORM} && mkdir -p build/${PROVIDER}/staging/${PLATFORM} - -@ln -s ../../../bin/${PLATFORM}/$* build/${PROVIDER}/staging/${PLATFORM}/$* -test: - @go test -tags "integration $(TAGS)" ./... +ifeq ($(PLATFORM), windows) +EXTRA_GO_LDFLAGS = "-H=windowsgui" +PKGFILES = $(shell cmd /c dir /s /b *.go) +# $(info $(PKGFILES)) +else +PKGFILES = $(shell find pkg -type f -name '*.go') +endif -build_bitmaskd: - @go build -tags "$(TAGS) bitmaskd" -ldflags "-X main.version=`git describe --tags`" ./cmd/* +lib/%.a: $(PKGFILES) + @XBUILD=no ./gui/build.sh --just-golib -build_win: - powershell -Command '$$version=git describe --tags; go build -ldflags "-H windowsgui -X main.version=$$version" ./cmd/*' +relink_vendor: +ifeq ($(VENDOR_PATH), providers) + @unlink providers/assets || true + @ln -s ${PROVIDER}/assets providers/assets +endif -CROSS_WIN_FLAGS = CGO_ENABLED=1 GOARCH=386 GOOS=windows CC="/usr/bin/i686-w64-mingw32-gcc" CGO_LDFLAGS="-lssp" CXX="i686-w64-mingw32-c++" -PLATFORM_WIN = PLATFORM=windows -EXTRA_LDFLAGS_WIN = EXTRA_LDFLAGS="-H windowsgui" -build_cross_win: - @echo "[+] Cross-building for windows..." - $(CROSS_WIN_FLAGS) $(PLATFORM_WIN) $(EXTRA_LDFLAGS_WIN) $(MAKE) _buildparts - # workaround for helper: we use the go compiler - @echo "[+] Compiling helper with the Go compiler to work around missing stdout bug..." - cd cmd/bitmask-helper && GOOS=windows GOARCH=386 go build -ldflags "-X main.version=`git describe --tags` -H windowsgui" -o ../../build/bin/windows/bitmask-helper-go +build_golib: lib/libgoshim.a -CROSS_OSX_FLAGS = MACOSX_DEPLOYMENT_TARGET=10.10 CGO_ENABLED=1 GOOS=darwin CC="o64-clang" -PLATFORM_OSX = PLATFORM=darwin -build_cross_osx: - $(CROSS_OSX_FLAGS) $(PLATFORM_OSX) $(MAKE) _buildparts +build_gui: relink_vendor + @XBUILD=no TARGET=${TARGET} VENDOR_PATH=${VENDOR_PATH} gui/build.sh --skip-golib -_build_done: - @echo - @echo 'Done. You can build your package now.' +build: build_golib build_helper build_gui -_build_xbuild_done: - @echo - @echo 'Done. You can do "make packages" now.' +build_helper: + @echo "PLATFORM: ${PLATFORM}" + @mkdir -p build/bin/${PLATFORM} + @go build -o build/bin/${PLATFORM}/bitmask-helper -ldflags "-X main.AppName=${APPNAME} -X main.Version=${VERSION} ${EXTRA_GO_LDFLAGS}" ./cmd/bitmask-helper/ + @echo "build helper done." + +build_openvpn: + @[ -f $(OPENVPN_BIN) ] && echo "OpenVPN already built at" $(OPENVPN_BIN) || ./branding/thirdparty/openvpn/build_openvpn.sh + +installer: check_qtifw build_openvpn build + @mkdir -p ${INST_DATA} + @cp -r ${TEMPLATES}/qtinstaller/packages ${INSTALLER} + @cp -r ${TEMPLATES}/qtinstaller/installer.pro ${INSTALLER} + @cp -r ${TEMPLATES}/qtinstaller/config ${INSTALLER} +ifeq (${PLATFORM}, darwin) + @mkdir -p ${INST_DATA}/helper + @VERSION=${VERSION} VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/gen-qtinstaller osx ${INSTALLER} + @cp "${TEMPLATES}/osx/bitmask.pf.conf" ${INST_DATA}helper/bitmask.pf.conf + @cp "${TEMPLATES}/osx/client.up.sh" ${INST_DATA}/ + @cp "${TEMPLATES}/osx/client.down.sh" ${INST_DATA}/ + @cp "${TEMPLATES}/qtinstaller/osx-data/post-install.py" ${INST_DATA}/ + @cp "${TEMPLATES}/qtinstaller/osx-data/uninstall.py" ${INST_DATA}/ + @cp "${TEMPLATES}/qtinstaller/osx-data/se.leap.bitmask-helper.plist" ${INST_DATA}/ + @cp $(OPENVPN_BIN) ${INST_DATA}/openvpn.leap + @cp build/bin/${PLATFORM}/bitmask-helper ${INST_DATA}/ + @echo "[+] Running macdeployqt" + @macdeployqt ${QTBUILD}/release/${PROVIDER}-vpn.app ${MACDEPLOYQT_OPTS} + @cp -r "${QTBUILD}/release/${TARGET}.app"/ ${INST_DATA}/ +endif +ifeq (${PLATFORM}, windows) + @VERSION=${VERSION} VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/gen-qtinstaller windows ${INSTALLER} + @cp build/bin/${PLATFORM}/bitmask-helper ${INST_DATA}helper.exe +ifeq (${VENDOR_PATH}, providers) + @cp ${VENDOR_PATH}/${PROVIDER}/assets/icon.ico ${INST_DATA}/icon.ico +else + @cp ${VENDOR_PATH}/assets/icon.ico ${INST_DATA}/icon.ico +endif + @cp ${QTBUILD}/release/${TARGET}.exe ${INST_DATA}${TARGET}.exe + # FIXME get the signed binaries with curl from openvpn downloads page - see if we have to adapt the openvpn-build to install tap drivers etc from our installer. + @cp "/c/Program Files/OpenVPN/bin/openvpn.exe" ${INST_DATA} + @cp "/c/Program Files/OpenVPN/bin/"*.dll ${INST_DATA} + # FIXME add sign options + @windeployqt --qmldir gui/qml ${INST_DATA}${TARGET}.exe + # TODO stage it to shave some time + @wget ${TAP_WINDOWS} -O ${INST_DATA}/tap-windows.exe +endif +ifeq (${PLATFORM}, linux) + @VERSION=${VERSION} ${SCRIPTS}/gen-qtinstaller linux ${INSTALLER} +endif + @echo "[+] All templates, binaries and libraries copied to build/installer." + @echo "[+] Now building the installer." + @cd build/installer && qmake VENDOR_PATH=${VENDOR_PATH} INSTALLER=${APPNAME}-installer-${VERSION} && make + +check_qtifw: +ifdef HAS_QTIFW + @echo "[+] Found QTIFW" +else + $(error "[!] Cannot find QTIFW. Please install it and add it to your PATH") +endif clean: @rm -rf build/ - @unlink branding/assets/default + @unlink branding/assets/default || true +######################################################################## +# tests ######################################################################### -# build them all -######################################################################### -build_all_providers: - branding/scripts/build-all-providers + +test: + @go test -tags "integration $(TAGS)" ./pkg/... + +test_ui: golib + @qmake -o tests/Makefile test.pro + @make -C tests clean + @make -C tests + @./tests/build/test_ui + ######################################################################### # packaging templates ######################################################################### -prepare: re_vendor prepare_templates gen_pkg_win gen_pkg_osx gen_pkg_snap gen_pkg_deb prepare_done +vendor_init: + @VENDOR_PATH=${VENDOR_PATH} ./branding/scripts/init + +vendor_check: + @VENDOR_PATH=${VENDOR_PATH} ./branding/scripts/check ${PROVIDER} +ifeq (${SKIP_CACHECK}, no) + @VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/check-ca-crt ${PROVIDER} +endif + +vendor: gen_providers_json prepare_templates gen_pkg_snap gen_pkg_deb -re_vendor: - # we update the module vendoring in case we're building with a different - # go version than in development - @go mod vendor +gen_providers_json: + @VENDOR_PATH=${VENDOR_PATH} branding/scripts/gen-providers-json gui/providers/providers.json -prepare_templates: generate relink_default tgz +prepare_templates: generate tgz @mkdir -p build/${PROVIDER}/bin/ deploy @cp ${TEMPLATES}/makefile/Makefile build/${PROVIDER}/Makefile - @VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} ${SCRIPTS}/generate-vendor-make.py build/${PROVIDER}/vendor.mk -ifeq (${SKIP_CACHECK}, no) - @${SCRIPTS}/check-ca-crt.py ${PROVIDER} ${PROVIDER_CONFIG} -endif + @VERSION=${VERSION} VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/generate-vendor-make build/${PROVIDER}/vendor.mk generate: - @go generate cmd/bitmask-vpn/main.go - -relink_default: -ifneq (,$(wildcard ${DEFAULT_PROVIDER})) - @cd branding/assets && unlink default -endif - @cd branding/assets && ln -s ${PROVIDER} default + @go generate gui/backend.go + @go generate pkg/config/version/genver/gen.go TGZ_NAME = bitmask-vpn_${VERSION}-src TGZ_PATH = $(shell pwd)/build/${TGZ_NAME} @@ -160,48 +220,37 @@ tgz: @cd build/ && tar czf bitmask-vpn_$(VERSION).tgz ${TGZ_NAME} @rm -rf $(TGZ_PATH) -gen_pkg_win: - @mkdir -p build/${PROVIDER}/windows/ - @cp -r ${TEMPLATES}/windows build/${PROVIDER} - @VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} ${SCRIPTS}/generate-win.py build/${PROVIDER}/windows/data.json - @cd build/${PROVIDER}/windows && python3 generate.py - # TODO create/copy build/PROVIDER/assets/ - # TODO create/copy build/PROVIDER/staging/ - -gen_pkg_osx: - @mkdir -p build/${PROVIDER}/osx/scripts - @mkdir -p build/${PROVIDER}/staging -ifeq (,$(wildcard build/${PROVIDER}/assets)) - @ln -s ../../branding/assets/default build/${PROVIDER}/assets -endif -ifeq (,$(wildcard build/${PROVIDER}/staging/openvpn-osx)) - @curl -L https://downloads.leap.se/thirdparty/osx/openvpn/openvpn -o build/${PROVIDER}/staging/openvpn-osx + +gen_pkg_deb: +ifeq (${PLATFORM}, linux) + @cp -r ${TEMPLATES}/debian build/${PROVIDER} + @VERSION=${VERSION} VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/generate-debian build/${PROVIDER}/debian/data.json + @mkdir -p build/${PROVIDER}/debian/icons/scalable && cp ${VENDOR_PATH}/${PROVIDER}/assets/icon.svg build/${PROVIDER}/debian/icons/scalable/icon.svg + @cd build/${PROVIDER}/debian && python3 generate.py + @cd build/${PROVIDER}/debian && rm app.desktop-template changelog-template rules-template control-template generate.py data.json && chmod +x rules endif - @cp -r ${TEMPLATES}/osx build/${PROVIDER} - @VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} ${SCRIPTS}/generate-osx.py build/${PROVIDER}/osx/data.json - @cd build/${PROVIDER}/osx && python3 generate.py - @cd build/${PROVIDER}/osx/scripts && chmod +x preinstall postinstall gen_pkg_snap: +ifeq (${PLATFORM}, linux) @cp -r ${TEMPLATES}/snap build/${PROVIDER} - @VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} ${SCRIPTS}/generate-snap.py build/${PROVIDER}/snap/data.json + @VERSION=${VERSION} VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/generate-snap build/${PROVIDER}/snap/data.json @cp helpers/se.leap.bitmask.snap.policy build/${PROVIDER}/snap/local/pre/ @cp helpers/bitmask-root build/${PROVIDER}/snap/local/pre/ @cd build/${PROVIDER}/snap && python3 generate.py @rm build/${PROVIDER}/snap/data.json build/${PROVIDER}/snap/snapcraft-template.yaml - @mkdir -p build/${PROVIDER}/snap/gui && cp branding/assets/default/icon.svg build/${PROVIDER}/snap/gui/icon.svg - @cp branding/assets/default/icon.png build/${PROVIDER}/snap/gui/${PROVIDER}-vpn.png + @mkdir -p build/${PROVIDER}/snap/gui +ifeq (${VENDOR_PATH}, providers) + @cp ${VENDOR_PATH}/${PROVIDER}/assets/icon.svg build/${PROVIDER}/snap/gui/icon.svg + # FIXME is this png needed?? then add it to ASSETS_REQUIRED + @cp ${VENDOR_PATH}/${PROVIDER}/assets/icon.png build/${PROVIDER}/snap/gui/${PROVIDER}-vpn.png +else + @cp ${VENDOR_PATH}/assets/icon.svg build/${PROVIDER}/snap/gui/icon.svg + @cp ${VENDOR_PATH}/assets/icon.png build/${PROVIDER}/snap/gui/${PROVIDER}-vpn.png +endif + @rm build/${PROVIDER}/snap/generate.py +endif -gen_pkg_deb: - @cp -r ${TEMPLATES}/debian build/${PROVIDER} - @VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} ${SCRIPTS}/generate-debian.py build/${PROVIDER}/debian/data.json - @mkdir -p build/${PROVIDER}/debian/icons/scalable && cp branding/assets/default/icon.svg build/${PROVIDER}/debian/icons/scalable/icon.svg - @cd build/${PROVIDER}/debian && python3 generate.py - @cd build/${PROVIDER}/debian && rm app.desktop-template changelog-template rules-template control-template generate.py data.json && chmod +x rules -prepare_done: - @echo - @echo 'Done. You can do "make build" now.' ######################################################################### # packaging action @@ -215,25 +264,14 @@ packages: package_deb package_snap package_osx package_win package_snap_in_docker: @make -C docker package_snap -package_win_in_docker: - @make -C docker package_win - package_snap: + @unlink snap || true + @ln -s build/${PROVIDER}/snap snap @make -C build/${PROVIDER} pkg_snap package_deb: @make -C build/${PROVIDER} pkg_deb -package_win_stage_1: - @make -C build/${PROVIDER} pkg_win_stage_1 - -package_win_stage_2: - @make -C build/${PROVIDER} pkg_win_stage_2 - -package_osx: - @make -C build/${PROVIDER} pkg_osx - - ######################################################################### # icons & locales @@ -243,23 +281,12 @@ icon: @make -C icon -LANGS ?= $(foreach path,$(wildcard locales/*),$(patsubst locales/%,%,$(path))) -empty := -space := $(empty) $(empty) -lang_list := $(subst $(space),,$(foreach lang,$(LANGS),$(lang),)) +LANGS ?= $(foreach path,$(wildcard gui/i18n/main_*.ts),$(patsubst gui/i18n/main_%.ts,%,$(path))) -locales: $(foreach lang,$(LANGS),get_$(lang)) cmd/bitmask-vpn/catalog.go +locales: $(foreach lang,$(LANGS),get_$(lang)) generate_locales: - @gotext update -lang=$(lang_list) ./pkg/systray ./pkg/bitmask - @make -C tools/transifex - -locales/%/out.gotext.json: pkg/systray/systray.go pkg/systray/notificator.go pkg/bitmask/standalone.go pkg/bitmask/bitmaskd.go - @gotext update -lang=$* ./pkg/systray ./pkg/bitmask - -cmd/bitmask-vpn/catalog.go: $(foreach lang,$(LANGS),locales/$(lang)/messages.gotext.json) - @gotext update -lang=$(lang_list) -out cmd/bitmask-vpn/catalog.go ./pkg/systray ./pkg/bitmask + @lupdate bitmask.pro -get_%: locales/%/out.gotext.json - @make -C tools/transifex build - @curl -L -X GET --user "api:${API_TOKEN}" "https://www.transifex.com/api/2/project/bitmask/resource/RiseupVPN/translation/${subst -,_,$*}/?file" | tools/transifex/transifex t2g locales/$*/ +get_%: + @curl -L -X GET --user "api:${API_TOKEN}" "https://www.transifex.com/api/2/project/bitmask/resource/riseupvpn-test/translation/${subst -,_,$*}/?file" > gui/i18n/main_$*.ts diff --git a/README.md b/README.md index 5cb91fe3..8a3daa20 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ -Install it ----------- +Build +----- -Install dependencies: -``` - # make depends -``` +Clone this repo, install dependencies and build the application. Dependencies +assume debian packages, or homebrew for osx. For other systems try +manually, or send us a patch. -Build the systray: ``` - $ git clone 0xacab.org/leap/bitmask-vpn && cd bitmask-vpn - $ make build + git clone 0xacab.org/leap/bitmask-vpn && cd bitmask-vpn + sudo make depends + make build ``` You need at least go 1.11. If you have something older and are using ubuntu, you can do: @@ -20,6 +19,7 @@ You need at least go 1.11. If you have something older and are using ubuntu, you For other situations, have a look at https://github.com/golang/go/wiki/Ubuntu or https://golang.org/dl/ +Test OSX ---------- @@ -46,6 +46,36 @@ In file included from /usr/include/gtk-3.0/gtk/gtk.h:106:0, ``` They are expected and don't produce any problem on the systray. +Windows +--------- +Download cygwin // https://cygwin.com/setup-x86_64.exe +``` +Install with the necessary packages: + +mingw64-x86_64-gcc-core +mingw64-x86_64-gcc-g++ +and +x86_64-w64-mingw32-c++ +x86_64-w64-mingw32-gcc +make + +Add to windowspath "C:\cygwin64\bin" +``` +Build it +``` +make build + +Build flags +ARCH : 386 or amd64 (default: amd64) +CCPAath and CXXPath are either paths of compiler or filenames in %PATH% (defaults: x86_64-w64-mingw32-gcc and x86_64-w64-mingw32-c++) + +Examples: +make build ARCH=386 +make build ARCH=386 CCPath=i686-w64-mingw32-gcc CXXPath=i686-w64-mingw32-c++ + +All options can be omitted! + +``` Run it ------------- @@ -53,10 +83,10 @@ The default build is a standalone systray. It still requires a helper and openvp [bitmask-root](https://0xacab.org/leap/bitmask-dev/blob/master/src/leap/bitmask/vpn/helpers/linux/bitmask-root) for windows and OSX there is [a helper written in go](https://0xacab.org/leap/bitmask-vpn/tree/master/pkg/helper/). -To build and run it: +Run it: ``` - $ make build $ build/bin/bitmask-vpn + ``` @@ -77,34 +107,33 @@ In that case bitmask-systray assumes that you already have bitmaskd running. Run i18n ---- -The translations are done in transifex. To help us contribute your translations there and/or review the existing -ones: -https://www.transifex.com/otf/bitmask/RiseupVPN/ +You can run some tests too. -When a string has being modified you need to regenerate the locales: ``` - $ make generate_locales + sudo apt install qml-module-qttest + make test + make test_ui ``` -To fetch the translations from transifex and rebuild the catalog.go (API\_TOKEN is the transifex API token): -``` - $ API_TOKEN='xxxxxxxxxxx' make locales -``` -There is some bug on gotext and the catalog.go generated doesn't have a package, you will need to edit -cmd/bitmask-vpn/catalog.go and to have a `package main` at the beginning of the file. -If you want to add a new language create the folder `locales/$lang` before running `make locales`. +Translations +------------ + +We use [transifex](https://www.transifex.com/otf/bitmask/RiseupVPN/) to coordinate translations. Any help is welcome! -Report an issue -------------------- +Bugs? Crashes? UI feedback? Any other suggestions or complains? +--------------------------------------------------------------- -When you report an issue include the following information: +When you are willing to [report an issue](https://0xacab.org/leap/bitmask-vpn/-/issues) please +use the search tool first. if you cannot find your issue, please make sure to +include the following information: -* what you expected to see -* what you got -* the version of the program. You can check the version on the about page. +* the platform you're using and the installation method. +* the version of the program. You can check the version on the "about" menu. +* what you expected to see. +* what you got instead. * the logs of the program. The location of the logs depends on the OS: - * linux: `/home//.config/leap/bitmaskd.log` & `/home//.config/leap/systray.log` + * gnu/linux: `/home//.config/leap/systray.log` * OSX: `/Users//Library/Preferences/leap/systray.log`, `/Applications/RiseupVPN.app/Contents/helper/helper.log` & `/Applications/RiseupVPN.app/Contents/helper/openvpn.log` * windows: `C:\Users\\AppData\Local\leap\systray.log`, `C:\Program Files\RiseupVPN\helper.log` & `C:\Program Files\RiseupVPN\openvp.log` diff --git a/bitmask.pro b/bitmask.pro new file mode 100644 index 00000000..22c3d7f9 --- /dev/null +++ b/bitmask.pro @@ -0,0 +1,66 @@ +#TARGET = $$BINARY_NAME + +CONFIG += qt staticlib +windows:CONFIG += console +unix:DEBUG:CONFIG += debug +lessThan(QT_MAJOR_VERSION, 5): error("requires Qt 5") +QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.11 + +!defined(VENDOR_PATH, var):VENDOR_PATH="providers/riseup" + +message("[qmake] VENDOR_PATH: $$VENDOR_PATH") + +RESOURCES += gui/gui.qrc +RESOURCES += $$VENDOR_PATH/vendor.qrc + +ICON = $$VENDOR_PATH/icon.png + +macx { + ICON = $$VENDOR_PATH/assets/icon.icns + LIBS += -framework Security +} +win32 { + RC_ICONS = $$VENDOR_PATH/assets/icon.ico +} + +QT += qml quick widgets + +SOURCES += \ + gui/main.cpp \ + gui/qjsonmodel.cpp \ + gui/handlers.cpp + + +HEADERS += \ + gui/handlers.h \ + gui/qjsonmodel.h \ + lib/libgoshim.h + +# we build from build/qt +LIBS += -L../../lib -lgoshim -lpthread + +DESTDIR = release +OBJECTS_DIR = release/.obj +MOC_DIR = release/.moc +RCC_DIR = release/.rcc +UI_DIR = release/.ui + +Release:DESTDIR = release +Release:OBJECTS_DIR = release/.obj +Release:MOC_DIR = release/.moc +Release:RCC_DIR = release/.rcc +Release:UI_DIR = release/.ui + +Debug:DESTDIR = debug +Debug:OBJECTS_DIR = debug/.obj +Debug:MOC_DIR = debug/.moc +Debug:RCC_DIR = debug/.rcc +Debug:UI_DIR = debug/.ui + +DISTFILES += \ + README.md + +CONFIG += lrelease embed_translations + +TRANSLATIONS += $$files(gui/i18n/*.ts, true) +RESOURCES += $$files(gui/i18n/*.qm, true) diff --git a/branding/README.rst b/branding/README.rst deleted file mode 100644 index e52160b1..00000000 --- a/branding/README.rst +++ /dev/null @@ -1,51 +0,0 @@ -Branding for BitmaskVPN -================================================================================ - -This folder contains everything that is needed to generate a customized built of -BitmaskVPN for your provider. - - -Configure --------------------------------------------------------------------------------- - -* Copy or edit the file at 'branding/config/vendor.conf'. Add all the needed variables. -* Copy your provider CA certificate to the same folder: 'branding/config/-ca.crt' -* Make sure that the folder 'branding/assets/' exists. Copy there all the needed assets. - -Checkout --------------------------------------------------------------------------------- - - git clone https://0xacab.org/leap/bitmask-vpn - cd bitmask-vpn - git pull --tags - - -Package --------------------------------------------------------------------------------- - -NOTE: Some of the following scripts need network access, since they will check -whether the configuration published by your provider matches what is configured -before the build. If you want to skip this check, pass `SKIP_CACHECK=yes` - -Run:: - - PROVIDER=example make prepare - -You can also specify a custom config file:: - - PROVIDER=example PROVIDER_CONFIG=/path/to/vendor.conf make prepare - -Then you need to build the package:: - - make build - -Then you can build all the packages:: - - make packages - -Alternatively, you can build only for an specific os:: - - make package_win - make package_osx - make package_snap - make package_deb diff --git a/branding/assets/calyx/osx-background.png b/branding/assets/calyx/osx-background.png deleted file mode 100644 index 91bcd8fa..00000000 Binary files a/branding/assets/calyx/osx-background.png and /dev/null differ diff --git a/branding/assets/demo/icon.bmp b/branding/assets/demo/icon.bmp deleted file mode 100644 index 9e918e4d..00000000 Binary files a/branding/assets/demo/icon.bmp and /dev/null differ diff --git a/branding/assets/demo/icon.png b/branding/assets/demo/icon.png deleted file mode 100644 index fb1aacd3..00000000 Binary files a/branding/assets/demo/icon.png and /dev/null differ diff --git a/branding/assets/riseup/icon.bmp b/branding/assets/riseup/icon.bmp deleted file mode 100644 index 9e918e4d..00000000 Binary files a/branding/assets/riseup/icon.bmp and /dev/null differ diff --git a/branding/assets/riseup/icon.icns b/branding/assets/riseup/icon.icns deleted file mode 100644 index b4c9a204..00000000 Binary files a/branding/assets/riseup/icon.icns and /dev/null differ diff --git a/branding/assets/riseup/icon.ico b/branding/assets/riseup/icon.ico deleted file mode 100755 index c65c9e17..00000000 Binary files a/branding/assets/riseup/icon.ico and /dev/null differ diff --git a/branding/assets/riseup/icon.png b/branding/assets/riseup/icon.png deleted file mode 100755 index 492d8508..00000000 Binary files a/branding/assets/riseup/icon.png and /dev/null differ diff --git a/branding/assets/riseup/icon.svg b/branding/assets/riseup/icon.svg deleted file mode 100644 index a19c6c61..00000000 --- a/branding/assets/riseup/icon.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/branding/assets/riseup/osx-background.png b/branding/assets/riseup/osx-background.png deleted file mode 100644 index 0e6cbf63..00000000 Binary files a/branding/assets/riseup/osx-background.png and /dev/null differ diff --git a/branding/config/demo-ca.crt b/branding/config/demo-ca.crt deleted file mode 100644 index 80d41c3e..00000000 --- a/branding/config/demo-ca.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt -YXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v -Yml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw -FgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV -BAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai -dHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB -7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r/bEHUSevmR09BRp86syHZerdNGpXYhcQ84 -CA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+ -znCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe/RGk4 -MEqGFuOzrtsgEhPIX0hplhb0Tgz/rtug+yTT7oJjBa3u20AAOQ38/M99EfdeJvc4 -lPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu/qt3DfvLfhboq+0 -bQvLUPXrVDr70onv5UDjpmEA/cLmaIqqrduuTkFZOym65/PfAPvpGnt7crQj/Ibl -DEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB -lfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy -YMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw -XjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH/BAQDAgIE -MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w -DQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl -cXYyB6hY5hv/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY -k/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj -RnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG -htD/JKwt6xBmNwktH0GI/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX -EIQ0ZR56bFuJA/CwValBqV/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J -aF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l -mlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK -G6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co -Ja8zlx64jmMZPg/t3wWqkZgXZ14qnbyG5/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d -69db12/g4f6phldhxiWuGC/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e -yV8e ------END CERTIFICATE----- diff --git a/branding/scripts/ASSETS_REQUIRED b/branding/scripts/ASSETS_REQUIRED new file mode 100644 index 00000000..b1889d17 --- /dev/null +++ b/branding/scripts/ASSETS_REQUIRED @@ -0,0 +1,3 @@ +icon.svg +icon.ico +icon.icns diff --git a/branding/scripts/build-all-providers b/branding/scripts/build-all-providers index 2efcfc5e..92a1307d 100755 --- a/branding/scripts/build-all-providers +++ b/branding/scripts/build-all-providers @@ -12,6 +12,7 @@ export XBUILD=yes for _provider in $PROVIDERS; do export PROVIDER=$_provider; make prepare - make build - make packages + #make build + #make packages + make package_snap done diff --git a/branding/scripts/check b/branding/scripts/check new file mode 100755 index 00000000..aab962e3 --- /dev/null +++ b/branding/scripts/check @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +import os +import sys + +# TODO check file list +# TODO remove fom README + +VENDOR_PATH = None + +def getVendorPath(): + global VENDOR_PATH + VENDOR_PATH = os.environ.get("VENDOR_PATH") + if not VENDOR_PATH: + print("[ERROR] VENDOR_PATH not set") + sys.exit(1) + + if not os.path.isdir(os.path.abspath(VENDOR_PATH)): + print("[ERROR] VENDOR_PATH folder does not exist:", VENDOR_PATH) + sys.exit(1) + +def checkCAFile(provider): + caFile = os.path.join(os.path.abspath(VENDOR_PATH), provider, provider + '-ca.crt') + if not os.path.isfile(caFile): + print("ERROR: Missing provider CA file:", caFile) + sys.exit(1) + print('[+] CA file ok:', caFile) + +def checkAssets(provider): + top = os.path.join(os.path.abspath(VENDOR_PATH), 'assets') + if os.path.isdir(top): + ok = checkAssetFiles(top) + if ok: + return + under = os.path.join(os.path.abspath(VENDOR_PATH), provider, 'assets') + if os.path.isdir(under): + ok = checkAssetFiles(under) + if ok: + return + print('[!] ERROR: cannot find some assets for provider {provider}'.format(provider=provider)) + sys.exit(1) + +def checkAssetFiles(path): + for item in allAssets(): + asset = os.path.join(path, item) + if not os.path.isfile(asset): + print("[!] Error: missing asset file:", asset) + return False + return True + +def allAssets(): + with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "ASSETS_REQUIRED")) as f: + allAssets = f.readlines() + return list(map(lambda s: s.strip(), allAssets)) + +if __name__ == "__main__": + print("[+] Checking your provider config...") + provider = sys.argv[1] + if not provider: + print("ERROR: must pass provider as first argument") + sys.exit(1) + getVendorPath() + checkCAFile(provider) + checkAssets(provider) diff --git a/branding/scripts/check-ca-crt.py b/branding/scripts/check-ca-crt similarity index 57% rename from branding/scripts/check-ca-crt.py rename to branding/scripts/check-ca-crt index dbf9b40f..74582638 100755 --- a/branding/scripts/check-ca-crt.py +++ b/branding/scripts/check-ca-crt @@ -1,21 +1,22 @@ #!/usr/bin/env python3 +import os import re import sys import configparser import urllib.request -SCRIPT_NAME = 'check-ca-crt.py' +SCRIPT_NAME = 'check-ca-crt' +VENDOR_PATH = None USAGE = '''Check that the stored provider CA matches the one announced online. -Usage: {name} +Usage: {name} -Example: {name} riseup branding/config/vendor.conf'''.format(name=SCRIPT_NAME) +Example: {name} riseup'''.format(name=SCRIPT_NAME) def getLocalCert(provider): - sanitized = re.sub(r'[^\w\s-]', '', provider).strip().lower() - with open('branding/config/' - '{provider}-ca.crt'.format(provider=sanitized)) as crt: + with open(os.path.join(VENDOR_PATH, provider, + '{provider}-ca.crt'.format(provider=sanitize(provider).lower()))) as crt: return crt.read().strip() @@ -26,22 +27,34 @@ def getRemoteCert(uri): fp.close() return remote_cert - def getUriForProvider(provider, configfile): c = configparser.ConfigParser() c.read(configfile) return c[provider]['caURL'] +def sanitize(s): + return re.sub(r'[^\w\s-]', '', s).strip() if __name__ == '__main__': + VENDOR_PATH = os.environ.get('VENDOR_PATH') - if len(sys.argv) != 3: + if not VENDOR_PATH: + print('[!] ERROR: Please set VENDOR_PATH variable first') + sys.exit(1) + if not os.path.isdir(os.path.abspath(VENDOR_PATH)): + print('[!] ERROR: VENDOR_PATH points to non-existent dir') + sys.exit(1) + + if len(sys.argv) != 2: print('[!] Not enough arguments') print(USAGE) sys.exit(1) provider = sys.argv[1] - config = sys.argv[2] + config = os.path.abspath(os.path.join(VENDOR_PATH, 'vendor.conf')) + if not os.path.isfile(config): + print('[!] ERROR: cannot open {config}') + sys.exit(1) try: uri = getUriForProvider(provider, config) diff --git a/branding/scripts/gen-providers-json b/branding/scripts/gen-providers-json new file mode 100755 index 00000000..0c95cb99 --- /dev/null +++ b/branding/scripts/gen-providers-json @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import configparser +import json +import os +import sys + + +from provider import getDefaultProvider +from provider import getProviderData + +OUTFILE = 'providers.json' +SCRIPT_NAME = 'gen-providers-json' + + +def generateProvidersJSON(configPath, outputJSONPath): + print("output:", outputJSONPath) + config = configparser.ConfigParser() + config.read(configPath) + + # TODO as a first step, we just get the defaultProvider. + # For multi-provider, just add more providers to the dict + + providers = {} + defaultProvider = getDefaultProvider(config) + + providers['default'] = defaultProvider + providers['providers'] = [] + providerData = getProviderData(defaultProvider, config) + addCaData(providerData, configPath) + + providers['providers'].append(providerData) + with open(outputJSONPath, 'w', encoding='utf-8') as f: + json.dump(providers, f, ensure_ascii=False, indent=4) + +def addCaData(data, configfile): + provider = data.get('name').lower() + folder, f = os.path.split(configfile) + caFile = os.path.join(folder, provider, provider + '-ca.crt') + if not os.path.isfile(caFile): + bail('[!] Cannot find CA file in {path}'.format(path=caFile)) + with open(caFile) as ca: + data['caCertString'] = ca.read().strip() + +def bail(msg=None): + if not msg: + print("ERROR: not enough arguments!") + print('Usage: {scriptname}.py '.format( + scriptname=SCRIPT_NAME)) + else: + print(msg) + sys.exit(1) + +if __name__ == "__main__": + print("[+] Generating providers.json...") + if len(sys.argv) != 2: + bail() + + VENDOR_PATH = os.environ.get('VENDOR_PATH') + conf = os.path.join(VENDOR_PATH, 'vendor.conf') + output = sys.argv[1] + generateProvidersJSON(conf, output) diff --git a/branding/scripts/gen-qtinstaller b/branding/scripts/gen-qtinstaller new file mode 100755 index 00000000..1f2c03d9 --- /dev/null +++ b/branding/scripts/gen-qtinstaller @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +import configparser +import json +import os +import sys +import time + +from string import Template + +from provider import getDefaultProvider +from provider import getProviderData + +OS_CONFIG = { + 'osx': """ + + @ApplicationsDir@/$APPNAME.app + $APPNAME.app + open + Start $APPNAME now! + + @TargetDir@ + + mac + +""", + 'windows': """ + @ApplicationsDir@/$APPNAME + $APPNAME + + @TargetDir@/$BINNAME.exe + + + +""" +} + +def getData(): + config = configparser.ConfigParser() + configPath = os.path.join(VENDOR_PATH, 'vendor.conf') + config.read(configPath) + + provider = os.environ.get('PROVIDER') + if not provider: + provider = getDefaultProvider(config) + return getProviderData(provider, config) + +def generateQtInstallerBoilerplate(data, platform, outDir): + generateConfig(data, platform, outDir) + generatePackageMetadata(data, platform, outDir) + +def generateConfig(data, platform, outDir): + templateData = { + 'APPNAME': data['applicationName'], + 'BINNAME': data['binaryName'], + 'VERSION': getVersion(), + 'TIMESTAMP': time.strftime('%Y-%m-%d'), + } + + platformStr = OS_CONFIG[platform] + platBlock = Template(platformStr).substitute(**templateData) + templateData['PLATFORM_BLOCK'] = platBlock + + renderTemplate( + templatePath='../templates/qtinstaller/config/config.xml', + outPath=os.path.join(outDir, 'config/config.xml'), + data=templateData) + +def generatePackageMetadata(data, platform, outDir): + appname = 'applicationName' + templateData = { + 'APPNAME': data[appname], + 'BINNAME': data['binaryName'], + 'VERSION': getVersion(), + 'TIMESTAMP': time.strftime('%Y-%m-%d') + } + if platform == "windows": + p = data[appname] + " for Windows" + elif platform == "osx": + p = data[appname] + " for OSX" + elif platform == "linux": + p = data[appname] + " for GNU/Linux" + else: + p = data[appname] + templateData['APPNAME_PLATFORM'] = p + + renderTemplate( + templatePath='../templates/qtinstaller/packages/bitmaskvpn/meta/package.xml', + outPath=os.path.join(outDir, 'packages/bitmaskvpn/meta/package.xml'), + data=templateData) + + renderTemplate( + templatePath='../templates/qtinstaller/packages/bitmaskvpn/meta/install.js', + outPath=os.path.join(outDir, 'packages/bitmaskvpn/meta/install.js'), + data=templateData) + +def renderTemplate(templatePath=None, outPath=None, data=None): + with open(os.path.join(here(), templatePath), 'r') as f: + t = f.read() + rendered = Template(t).substitute(**data) + os.makedirs(os.path.dirname(outPath), exist_ok=True) + with open(outPath, 'w') as out: + out.write(rendered) + +def here(): + return os.path.abspath(os.path.dirname(__file__)) + +def bail(msg=None): + if not msg: + print("ERROR: not enough arguments!") + print('Usage: {scriptname}.py '.format( + scriptname=SCRIPT_NAME)) + else: + print(msg) + sys.exit(1) + +def getVersion(): + return os.environ.get('VERSION', 'unknown') + +if __name__ == "__main__": + VENDOR_PATH = os.environ.get('VENDOR_PATH') + + if len(sys.argv) != 3: + bail() + platform = sys.argv[1] + outDir = sys.argv[2] + print("[+] Generating qtinstaller files...") + data = getData() + generateQtInstallerBoilerplate(data, platform, outDir) diff --git a/branding/scripts/generate-debian.py b/branding/scripts/generate-debian similarity index 69% rename from branding/scripts/generate-debian.py rename to branding/scripts/generate-debian index 56e533bc..0db1fde6 100755 --- a/branding/scripts/generate-debian.py +++ b/branding/scripts/generate-debian @@ -19,17 +19,11 @@ def writeOutput(data, outfile): with open(outfile, 'w') as outf: outf.write(json.dumps(data)) - if __name__ == "__main__": - env_provider_conf = os.environ.get('PROVIDER_CONFIG') - if env_provider_conf: - if os.path.isfile(env_provider_conf): - print("[+] Overriding provider config per " - "PROVIDER_CONFIG variable") - configfile = env_provider_conf - + VENDOR_PATH = os.environ.get('VENDOR_PATH') + configFile = os.path.join(VENDOR_PATH, 'vendor.conf') config = configparser.ConfigParser() - config.read(configfile) + config.read(configFile) provider = getDefaultProvider(config) data = getProviderData(provider, config) diff --git a/branding/scripts/generate-osx.py b/branding/scripts/generate-osx.py deleted file mode 100755 index 44307623..00000000 --- a/branding/scripts/generate-osx.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 - -import json -import os -import sys - -import configparser - -from provider import getDefaultProvider -from provider import getProviderData - - -VERSION = os.environ.get('VERSION', 'unknown') - - -def writeOutput(data, outfile): - - with open(outfile, 'w') as outf: - outf.write(json.dumps(data)) - - -if __name__ == "__main__": - env_provider_conf = os.environ.get('PROVIDER_CONFIG') - if env_provider_conf: - if os.path.isfile(env_provider_conf): - print("[+] Overriding provider config per " - "PROVIDER_CONFIG variable") - configfile = env_provider_conf - - config = configparser.ConfigParser() - config.read(configfile) - provider = getDefaultProvider(config) - data = getProviderData(provider, config) - - if len(sys.argv) != 2: - print('Usage: generate-osx.py ') - sys.exit(1) - - outputf = sys.argv[1] - - data['applicationNameLower'] = data.get('applicationName').lower() - data['URL'] = data.get('infoURL') - data['version'] = VERSION - writeOutput(data, outputf) diff --git a/branding/scripts/generate-snap.py b/branding/scripts/generate-snap similarity index 69% rename from branding/scripts/generate-snap.py rename to branding/scripts/generate-snap index c3c54197..3271d9e9 100755 --- a/branding/scripts/generate-snap.py +++ b/branding/scripts/generate-snap @@ -21,15 +21,10 @@ def writeOutput(data, outfile): if __name__ == "__main__": - env_provider_conf = os.environ.get('PROVIDER_CONFIG') - if env_provider_conf: - if os.path.isfile(env_provider_conf): - print("[+] Overriding provider config per " - "PROVIDER_CONFIG variable") - configfile = env_provider_conf - + VENDOR_PATH = os.environ.get('VENDOR_PATH') + configFile = os.path.join(VENDOR_PATH, 'vendor.conf') config = configparser.ConfigParser() - config.read(configfile) + config.read(configFile) provider = getDefaultProvider(config) data = getProviderData(provider, config) diff --git a/branding/scripts/generate-vendor-make.py b/branding/scripts/generate-vendor-make similarity index 74% rename from branding/scripts/generate-vendor-make.py rename to branding/scripts/generate-vendor-make index e7794c37..60dc180e 100755 --- a/branding/scripts/generate-vendor-make.py +++ b/branding/scripts/generate-vendor-make @@ -36,20 +36,15 @@ def writeOutput(data, outfile): if __name__ == "__main__": - env_provider_conf = os.environ.get('PROVIDER_CONFIG') - if env_provider_conf: - if os.path.isfile(env_provider_conf): - print("[+] Overriding provider config per " - "PROVIDER_CONFIG variable") - configfile = env_provider_conf - + VENDOR_PATH = os.environ.get('VENDOR_PATH') + configFile = os.path.join(VENDOR_PATH, 'vendor.conf') config = configparser.ConfigParser() - config.read(configfile) + config.read(configFile) provider = getDefaultProvider(config) data = getProviderData(provider, config) if len(sys.argv) != 2: - print('Usage: generate-vendor-make.py ') + print('Usage: generate-vendor-make ') sys.exit(1) outputf = sys.argv[1] diff --git a/branding/scripts/generate-win.py b/branding/scripts/generate-win.py deleted file mode 100755 index fb15f22a..00000000 --- a/branding/scripts/generate-win.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 - -import json -import os -import sys - -import configparser - -from provider import getDefaultProvider -from provider import getProviderData - - -VERSION = os.environ.get('VERSION', 'unknown') - - -def writeOutput(data, outfile): - - with open(outfile, 'w') as outf: - outf.write(json.dumps(data)) - - -if __name__ == "__main__": - env_provider_conf = os.environ.get('PROVIDER_CONFIG') - if env_provider_conf: - if os.path.isfile(env_provider_conf): - print("[+] Overriding provider config per " - "PROVIDER_CONFIG variable") - configfile = env_provider_conf - - config = configparser.ConfigParser() - config.read(configfile) - provider = getDefaultProvider(config) - data = getProviderData(provider, config) - - if len(sys.argv) != 2: - print('Usage: generate-win.py ') - sys.exit(1) - - outputf = sys.argv[1] - - data['applicationNameLower'] = data.get('applicationName').lower() - data['URL'] = data.get('infoURL') - data['version'] = VERSION - writeOutput(data, outputf) diff --git a/branding/scripts/getparam b/branding/scripts/getparam new file mode 100755 index 00000000..235745df --- /dev/null +++ b/branding/scripts/getparam @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# A short utility to get appname in compilation time. +# This is a convenience to build helpers while we move the branding mechanism to a separate repo. + +import configparser + +import os +import sys + +from provider import getDefaultProvider +from provider import getProviderData + +def getData(): + here = os.path.abspath(os.path.dirname(__file__)) + vendorPath = os.environ.get('VENDOR_PATH') + configPath = os.path.join(vendorPath, 'vendor.conf') + if not os.path.isfile(configPath): + print("ERROR: path does not exist", configPath) + os.exit(1) + config = configparser.ConfigParser() + config.read(configPath) + defaultProvider = getDefaultProvider(config) + return getProviderData(getDefaultProvider(config), config) + +if __name__ == "__main__": + param = sys.argv[1] + if param == "appname": + field = "applicationName" + elif param == "binname": + field = "binaryName" + else: + print("ERROR: unknown param") + sys.exit(1) + + data = getData() + print(data[field]) diff --git a/branding/scripts/init b/branding/scripts/init new file mode 100755 index 00000000..ecc40258 --- /dev/null +++ b/branding/scripts/init @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# (c) LEAP Encryption Access Project 2020 +# License: GPL + +import string +import subprocess +import sys +import os + +VENDOR_PATH = None +PROVIDER=None +SCRIPT_NAME = sys.argv[0] +CA_README = "CERT.Readme" +ASSETS_README = "assets/FILES.Readme" +VENDOR_QRC = "vendor.qrc" + +def initVendor(): + global VENDOR_PATH + if not VENDOR_PATH: + bail("ERROR: Please set VENDOR_PATH environment variable.") + VENDOR_PATH = os.path.abspath(VENDOR_PATH) + if os.path.isdir(VENDOR_PATH): + bail("ERROR: VENDOR_PATH folder already exists {path}".format(path=VENDOR_PATH)) + + for d in ["assets"]: + os.makedirs(os.path.join(VENDOR_PATH, d)) + + initVendorConfig() + initGitRepo() + displayRepoInfo() + +def displayRepoInfo(): + print() + print("[+] Initialized repo in", VENDOR_PATH) + print() + print(f"- Please add all the needed assets. See {VENDOR_PATH}/{ASSETS_README}.") + print(f"- Add your provider's CA certificate, see see {VENDOR_PATH}/{PROVIDER}/{CA_README}.") + print("- Remember to commit your changes.") + print() + print("[+] After doing that, you can run 'make vendor_check' to validate the configuration for your provider.") + +def bail(msg=None): + if not msg: + print("ERROR: no arguments supported!") + print('Usage: {scriptname}'.format( + scriptname=SCRIPT_NAME)) + else: + print(msg) + sys.exit(1) + +def getVendorPath(): + return os.environ.get('VENDOR_PATH') + +def sanitize(word): + result = "" + for letter in word: + if letter in string.ascii_letters: + result = result + letter.lower() + return result + +def getProvider(): + provider = os.environ.get('PROVIDER') + if not provider: + provider = input('> provider name? ') + provider = sanitize(provider) + print("[+] provider name:", provider) + return provider + +def getProviderURL(): + url = os.environ.get('PROVIDER_URL') + if not url : + url = input('> provider url?: https://') + return url.replace('https://', '').replace('/', '') + +def getAppName(provider): + return provider[0].capitalize() + provider[1:] + "VPN" + +def initVendorConfig(): + with open(os.path.join(VENDOR_PATH, "vendor.conf"), "w") as f: + f.write(getConf()) + + caDir = os.path.join(VENDOR_PATH, PROVIDER) + os.makedirs(caDir, exist_ok=True) + with open(os.path.join(caDir, CA_README), "w") as f: + f.write(getCAInfo()) + + with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "ASSETS_REQUIRED")) as f: + allAssets = f.read() + with open(os.path.join(VENDOR_PATH, ASSETS_README), "w") as f: + f.write(ASSETS_INFO) + f.write(allAssets) + + with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../templates/vendor/vendor.qrc")) as f: + qrc = f.read() + with open(os.path.join(VENDOR_PATH, VENDOR_QRC), "w") as f: + f.write(qrc) + +def initGitRepo(): + out = subprocess.run(['git', 'init'], cwd=VENDOR_PATH) + if out.returncode != 0: + print(f'ERROR: cannot initialize git repo in {VENDOR_PATH}') + + +CONF_TEMPLATE = """[default] + +provider = {provider} + +[{provider}] + +name = {provider} +applicationName = {appName} +binaryName = {provider}-vpn + +providerURL = https://{providerURL} +auth = anon +apiURL = https://api.{providerURL}/ +caURL = https://{providerURL}/ca.crt + +infoURL = https://{providerURL}/vpn +tosURL = https://{providerURL}/tos +helpURL = https://{providerURL}/support + +geolocationAPI = https://{providerURL}:9001/json + +askForDonations = false +donateURL = https://{providerURL}/vpn/donate +""" + +def getConf(): + return CONF_TEMPLATE.format( + provider=PROVIDER, + appName=getAppName(PROVIDER), + providerURL=PROVIDER_URL) + + +CA_INFO = """Place in this folder your provider's CA certificate, with the name: + + {provider}-ca.crt +""" + +def getCAInfo(): + return CA_INFO.format(provider=PROVIDER) + +ASSETS_INFO = """This is the list of assets that you MUST place in this folder for your provider: + +""" + +if __name__ == "__main__": + if len(sys.argv) != 1: + bail() + + VENDOR_PATH = getVendorPath() + PROVIDER = getProvider() + PROVIDER_URL = getProviderURL() + initVendor() diff --git a/branding/scripts/provider.py b/branding/scripts/provider.py index b1beab91..22b36fdf 100644 --- a/branding/scripts/provider.py +++ b/branding/scripts/provider.py @@ -3,31 +3,36 @@ def getDefaultProvider(config): - provider = os.environ.get('PROVIDER') - if provider: - print('[+] Got provider {} from environment'.format(provider)) - else: + if os.environ.get('VENDOR_PATH'): print('[+] Using default provider from config file') provider = config['default']['provider'] + else: + provider = os.environ.get('PROVIDER') + if provider: + print('[+] Got provider {} from environment'.format(provider)) return provider def getProviderData(provider, config): print("[+] Configured provider:", provider) - - c = config[provider] + try: + c = config[provider] + except Exception: + raise ValueError('Cannot find provider') + d = dict() - - keys = ('name', 'applicationName', 'binaryName', + keys = ('name', 'applicationName', 'binaryName', 'auth', 'authEmptyPass', 'providerURL', 'tosURL', 'helpURL', 'askForDonations', 'donateURL', 'apiURL', 'geolocationAPI', 'caCertString') + boolValues = ['askForDonations', 'authEmptyPass'] for value in keys: d[value] = c.get(value) + if value in boolValues: + d[value] = bool(d[value]) d['timeStamp'] = '{:%Y-%m-%d %H:%M:%S}'.format( datetime.datetime.now()) return d - diff --git a/branding/scripts/vendorize.py b/branding/scripts/vendorize.py deleted file mode 100755 index ba248b0c..00000000 --- a/branding/scripts/vendorize.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys - -from string import Template -import configparser - -from provider import getDefaultProvider -from provider import getProviderData - -OUTFILE = 'config.go' -INFILE = '../templates/bitmaskvpn/config.go' -CONFIGFILE = '../config/vendor.conf' -SCRIPT_NAME = 'vendorize' - - -def addCaData(data, configfile): - provider = data.get('name').lower() - folder, f = os.path.split(configfile) - caFile = os.path.join(folder, provider + '-ca.crt') - if not os.path.isfile(caFile): - bail('[!] Cannot find CA file in {path}'.format(path=caFile)) - with open(caFile) as ca: - data['caCertString'] = ca.read().strip() - - -def writeOutput(data, infile, outfile): - - with open(infile) as infile: - s = Template(infile.read()) - - with open(outfile, 'w') as outf: - outf.write(s.substitute(data)) - - -def bail(msg=None): - if not msg: - print('Usage: {scriptname}.py