From 9c74ffb925325de98e8dd73acdf1602dd40bfcc0 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 2 Aug 2017 18:40:21 -0700 Subject: [pkg] working bundle in osx using pywebview + pyobjc --- pkg/pyinst/app.spec | 28 ++++++--- pkg/pyinst/build.mk | 3 +- src/leap/bitmask/gui/app2.py | 144 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 src/leap/bitmask/gui/app2.py diff --git a/pkg/pyinst/app.spec b/pkg/pyinst/app.spec index a14dbcb5..9e99d7e9 100644 --- a/pkg/pyinst/app.spec +++ b/pkg/pyinst/app.spec @@ -1,10 +1,12 @@ # -*- mode: python -*- -import platform +import os import sys +import platform block_cipher = None IS_MAC = sys.platform.startswith('darwin') +IS_WIN = platform.system() == 'Windows' BITMASK_VERSION = open('pkg/next-version').read() if IS_MAC: @@ -19,7 +21,6 @@ hiddenimports = [ 'pysqlcipher', 'service_identity', 'leap.common', 'leap.bitmask', 'leap.bitmask.core.logs', - 'leap.bitmask.gui.icons_rc', 'leap.soledad.common', 'leap.soledad.common.document', 'leap.soledad.common.l2db', @@ -28,7 +29,7 @@ hiddenimports = [ 'packaging', 'packaging.version', 'packaging.specifiers', 'packaging.requirements'] -if platform.system() == 'Windows': +if IS_WIN: print "Platform=Windows, using pyside..." hiddenimports.extend( ['PySide.QtCore', 'PySide.QtGui', 'PySide.QtWebKit', @@ -39,19 +40,28 @@ if platform.system() == 'Windows': 'packaging.requirements', 'python-gnupg']) excludes = ['PyQt5'] + QT5PATH = [''] +elif IS_MAC: + hiddenimports.extend(['pywebview', 'pyobjc']) + excludes = ['PyQt5', 'IPython', 'PySide'] + QT5PATH = [''] else: hiddenimports.extend( - ['PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWebKit']) + ['PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWebEngine', + 'leap.bitmask.gui.icons_rc']) excludes = ['PySide'] -import os VENV = os.environ.get('VIRTUAL_ENV', '') -a = Analysis(['../../src/leap/bitmask/gui/app.py'], +if IS_MAC: + # experimental pywebview entrypoint + ENTRYPOINT = ['../../src/leap/bitmask/gui/app2.py'] +else: + ENTRYPOINT = ['../../src/leap/bitmask/gui/app.py'] + +a = Analysis(ENTRYPOINT, pathex=[ - '/usr/lib/python2.7/dist-packages/', - VENV + '/Lib/site-packages/', - VENV + '/Lib/site-packages/leap/soledad'], + '/usr/lib/python2.7/dist-packages/'] + QT5PATH, binaries=None, datas=None, hiddenimports=hiddenimports, diff --git a/pkg/pyinst/build.mk b/pkg/pyinst/build.mk index a9df4974..b6dea151 100644 --- a/pkg/pyinst/build.mk +++ b/pkg/pyinst/build.mk @@ -11,7 +11,7 @@ bundle: bundle_clean pyinstaller -y pkg/pyinst/app.spec cp $(VIRTUAL_ENV)/lib/python2.7/site-packages/_scrypt.so $(DIST) cp src/leap/bitmask/core/bitmaskd.tac $(DIST) - mkdir $(DIST)/leap + mkdir -p $(DIST)/leap # if you find yourself puzzled becase the following files are not found in your # virtualenv, make sure that you're installing the packages from wheels and not eggs. mkdir -p $(DIST)/leap/soledad/client/_db @@ -58,7 +58,6 @@ bundle_osx: bundle bundle_osx_helpers cp $(DIST_VERSION)/lib/_scrypt.so $(OSX_CON)/ cp $(DIST_VERSION)/lib/bitmaskd.tac $(OSX_CON)/ cp -r $(DIST_VERSION)/lib/leap $(OSX_CON)/ - cp -r $(DIST_VERSION)/lib/pixelated_www $(OSX_CON)/ mv dist/Bitmask.app/Contents/MacOS/bitmask $(OSX_CON)/bitmask-app cp pkg/osx/bitmask-wrapper $(OSX_CON)/bitmask mkdir -p $(OSX_RES)/bitmask-helper diff --git a/src/leap/bitmask/gui/app2.py b/src/leap/bitmask/gui/app2.py new file mode 100644 index 00000000..13f369c4 --- /dev/null +++ b/src/leap/bitmask/gui/app2.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +# app.py +# Copyright (C) 2016 LEAP Encryption Acess Project +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Main entrypoint for the Bitmask Qt GUI. +It just launches a webview browser that runs the local web-ui served by bitmaskd +when the web service is running. +""" + +import os +import platform +import signal +import sys +import time +import webbrowser + +from functools import partial +from multiprocessing import Process + +from leap.bitmask.core.launcher import run_bitmaskd, pid +from leap.common.config import get_path_prefix +#from leap.bitmask.gui import app_rc + +import webview + +DEBUG = os.environ.get("DEBUG", False) + +BITMASK_URI = 'http://localhost:7070/' +PIXELATED_URI = 'http://localhost:9090/' + +qApp = None +bitmaskd = None +browser = None + + +class BrowserWindow(object): + """ + A browser window using pywebview. + + This BrowserWindow assumes that the backend is already running, since it is + going to look for the authtoken in the configuration folder. + """ + def __init__(self, *args, **kw): + url = kw.pop('url', None) + first = False + if not url: + url = "http://localhost:7070" + path = os.path.join(get_path_prefix(), 'leap', 'authtoken') + waiting = 20 + while not os.path.isfile(path): + if waiting == 0: + # If we arrive here, something really messed up happened, + # because touching the token file is one of the first + # things the backend does, and this BrowserWindow + # should be called *right after* launching the backend. + raise NoAuthToken( + 'No authentication token found!') + time.sleep(0.1) + waiting -= 1 + token = open(path).read().strip() + url += '#' + token + first = True + self.url = url + self.closing = False + + webview.create_window('Bitmask', self.url) + + + def loadPage(self, web_page): + self.load(url) + + def shutdown(self, *args): + if self.closing: + return + self.closing = True + global bitmaskd + bitmaskd.join() + if os.path.isfile(pid): + with open(pid) as f: + pidno = int(f.read()) + print('[bitmask] terminating bitmaskd...') + os.kill(pidno, signal.SIGTERM) + print('[bitmask] shutting down gui...') + + + +def launch_gui(): + global bitmaskd + global browser + + bitmaskd = Process(target=run_bitmaskd) + bitmaskd.start() + + try: + browser = BrowserWindow(None) + except NoAuthToken as e: + print('ERROR: ' + e.message) + sys.exit(1) + + +def start_app(): + from leap.bitmask.util import STANDALONE + os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1' + + # Allow the frozen binary in the bundle double as the cli entrypoint + # Why have only a user interface when you can have two? + + if STANDALONE and len(sys.argv) > 1: + if sys.argv[1] == 'bitmask_helpers': + from leap.bitmask.vpn.helpers import main + return main() + + from leap.bitmask.cli import bitmask_cli + return bitmask_cli.main() + + prev_auth = os.path.join(get_path_prefix(), 'leap', 'authtoken') + try: + os.remove(prev_auth) + except OSError: + pass + + launch_gui() + + +class NoAuthToken(Exception): + pass + + +if __name__ == "__main__": + start_app() -- cgit v1.2.3