#!/usr/bin/python # Generate bundles for brandable Bitmask Lite. # (c) LEAP Encryption Access Project # (c) Kali Kaneko 2018 import os import os.path import shutil import sys import stat from string import Template # Variables ---------------------------- APP_NAME = "RiseupVPN" BUNDLE_IDENTIFIER = "se.leap.riseupvpn" # Do not edit below -------------------- here = os.path.split(os.path.abspath(__file__))[0] ENTRYPOINT = 'bitmask-vpn' HELPER = 'bitmask_helper' OPENVPN = 'openvpn' TEMPLATE_INFO = 'template-info.plist' TEMPLATE_HELPER = 'template-helper.plist' TEMPLATE_PREINSTALL = 'template-preinstall' TEMPLATE_POSTINSTALL = 'template-postinstall' VERSION = sys.argv[1] APP_PATH = os.path.abspath(here + '/../dist/' + APP_NAME + ".app") STAGING = os.path.abspath(here + '/../staging/') ASSETS = os.path.abspath(here + '/../assets/') ICON = os.path.join(ASSETS, APP_NAME.lower() + '.icns') SCRIPTS = os.path.join(os.path.abspath(here), 'scripts') INFO_PLIST = APP_PATH + '/Contents/Info.plist' HELPER_PLIST = os.path.join(SCRIPTS, 'se.leap.bitmask-helper.plist') PREINSTALL = os.path.join(SCRIPTS, 'preinstall') POSTINSTALL = os.path.join(SCRIPTS, 'postinstall') RULEFILE = os.path.join(here, 'bitmask.pf.conf') VPN_UP = os.path.join(here, 'client.up.sh') VPN_DOWN = os.path.join(here, 'client.down.sh') try: os.makedirs(APP_PATH + "/Contents/MacOS") except: pass try: os.makedirs(APP_PATH + "/Contents/Resources") except: pass try: os.makedirs(APP_PATH + "/Contents/helper") except: pass vardict = { 'entrypoint': ENTRYPOINT, 'info_string': APP_NAME + " " + VERSION, 'bundle_identifier': BUNDLE_IDENTIFIER, 'bundle_name': APP_NAME, 'version': VERSION, 'app_name': APP_NAME } # utils def copy_payload(filename, destfile=None): if destfile is None: destfile = APP_PATH + "/Contents/MacOS/" + filename else: destfile = APP_PATH + destfile shutil.copyfile(STAGING + '/' + filename, destfile) cmode = os.stat(destfile).st_mode os.chmod(destfile, cmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) def generate_from_template(template, dest, vardict): print("[+] File written from template to", dest) template = Template(open(template).read()) with open(dest, 'w') as output: output.write(template.safe_substitute(vardict)) # 1. Generation of the Bundle Info.plist # -------------------------------------- generate_from_template(TEMPLATE_INFO, INFO_PLIST, vardict) # 2. Generate PkgInfo # ------------------------------------------- with open(APP_PATH + "/Contents/PkgInfo", "w") as f: # is this enough? See what PyInstaller does. f.write("APPL????") # 3. Copy the binary payloads # -------------------------------------------- copy_payload(ENTRYPOINT) copy_payload(HELPER) copy_payload(OPENVPN, destfile='/Contents/Resources/openvpn.leap') # 4. Copy the app icon from the assets folder # ----------------------------------------------- shutil.copyfile(ICON, APP_PATH + '/Contents/Resources/app.icns') # 5. Generate the scripts for the installer # ----------------------------------------------- # Watch out that, for now, all the brandings are sharing the same helper name. # This is intentional: I prefer not to have too many root helpers laying around # until we consolidate a way of uninstalling and/or updating them. # This also means that only one of the derivatives will work at a given time # (ie, uninstall bitmask legacy to use riseupvpn). # If this bothers you, and it should, let's work on improving uninstall and # updates. generate_from_template(TEMPLATE_HELPER, HELPER_PLIST, vardict) generate_from_template(TEMPLATE_PREINSTALL, PREINSTALL, vardict) generate_from_template(TEMPLATE_POSTINSTALL, POSTINSTALL, vardict) # 6. Copy helper pf rule file # ------------------------------------------------ shutil.copy(RULEFILE, APP_PATH + '/Contents/helper/') # 7. Copy openvpn up/down scripts # ------------------------------------------------ shutil.copy(VPN_UP, APP_PATH + '/Contents/helper/') shutil.copy(VPN_DOWN, APP_PATH + '/Contents/helper/') # 8. Generate uninstall script # ----------------------------------------------- # TODO copy the uninstaller script from bitmask-dev # TODO substitute vars # this is a bit weak procedure for now. # To begin with, this assumes everything is hardcoded into /Applications/APPNAME.app # We could consider moving the helpers into /usr/local/sbin, so that the plist files # always reference there. # We're all set! # ----------------------------------------------- print("[+] Output written to dist/{appname}.app".format(appname=APP_NAME))