summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaixu Aabuizia <PaixuAabuizia@users.noreply.github.com>2016-01-10 15:40:35 +0100
committerKali Kaneko <kali@leap.se>2016-04-16 20:30:45 -0400
commit0a5d24d64b5f637038a15b01bbe1b3d4bf4108f2 (patch)
tree0a895c3a4c3d94c0cefa4fa26eabf525a824c9ad
parent92f4b40ab48ec537aade244af3e3e4f2c17b1475 (diff)
[pkg] reproducible windows installer for bitmask_client
provide a environment that allows automated builds of windows installers - prepare dockerized environment with wine, python, openssl, zlib and mingw to build windows binaries from python sourcecode - prepare dockerized environment with nullsoft installer to build installers from binaries - configure pyinstaller to build binaries - configure nsis to build distributable executables for bitmask - configure make all in pkg/windows that results in installers - add documentation - ico conversion from data/images - avoid polluting / in docker image - install dirspec and copy to wine env - remove obsolete comments - fix python path - figure out that pip install leap.a and pyinstalling a leap.b does not work - so the build script fixes that - rename dependencies to pyinstaller and move nsis code to installer - build openvpn, export the binaries for further processing - correct openvpn dependencies, fetch tap installer compatible with openvpn just built - install tap-driver with nsis - pyinstaller-build: fix mixed mkdir / show errors if there are some - installer-build: prepare rw-copy, do not expose nsh files - add openvpn_leap.exe to install directory so it gets picked up by nsis - use setup.py to install bitmask to site-packages to have a version - separate build directories for granular make - copy all openvpn dlls to installer - die to signal failure to parent makefile - cache installDependencies for quick turn-arround times - share openssl version between openvpn and pysqlcipher/other pip builds - collect files during prepare for installer - default to eip:false, mail:true - configuration in pyinstaller-build.sh - win64 tap drivers need special care getting removed from 32bit nsis - correct registry key that identifies if we installed TAP - extract version from git-tree, expose to wine python - create nsh with version for build installer - allow clean/dirty version with patches - cleanup / indent / remove comments - die when pysqlchipher patch failed - add psutil in mingw compatible version
-rw-r--r--pkg/pyinst/bitmask.spec30
-rw-r--r--pkg/pyinst/qt.conf2
-rw-r--r--pkg/windows/Makefile25
-rw-r--r--pkg/windows/README.rst144
-rw-r--r--pkg/windows/TODO15
-rw-r--r--pkg/windows/bitmask.nis2
-rw-r--r--pkg/windows/bitmask.nsh115
-rw-r--r--pkg/windows/bitmask_client_product.nsh8
-rw-r--r--pkg/windows/bitmask_client_registry_install.nsh17
-rw-r--r--pkg/windows/docker-compose.yml42
-rwxr-xr-xpkg/windows/installer-build.sh119
-rw-r--r--pkg/windows/installer/Dockerfile17
-rwxr-xr-xpkg/windows/openvpn-build.sh62
-rw-r--r--pkg/windows/openvpn/Dockerfile17
-rwxr-xr-xpkg/windows/pyinstaller-build.sh287
-rw-r--r--pkg/windows/pyinstaller/Dockerfile105
-rw-r--r--pkg/windows/pyinstaller/pysqlcipher_setup.py.patch14
-rw-r--r--pkg/windows/pyinstaller/zlib-mingw-shared.patch10
18 files changed, 1024 insertions, 7 deletions
diff --git a/pkg/pyinst/bitmask.spec b/pkg/pyinst/bitmask.spec
index a76ccb17..cd207816 100644
--- a/pkg/pyinst/bitmask.spec
+++ b/pkg/pyinst/bitmask.spec
@@ -3,17 +3,32 @@ import sys
block_cipher = None
-
-a = Analysis([os.path.join('bitmask.py')],
+a = Analysis(['bitmask.py'],
hiddenimports=[
- 'zope.interface', 'zope.proxy',
- 'PySide.QtCore', 'PySide.QtGui', 'PySide.QtWebKit'],
+ 'zope.interface', 'zope.proxy',
+ 'PySide.QtCore', 'PySide.QtGui',
+ 'pysqlcipher', 'service_identity',
+ 'leap.common', 'leap.bitmask'
+ ],
+ binaries=None,
+ datas=None,
hookspath=None,
runtime_hooks=None,
excludes=None,
+ win_no_prefer_redirects=None,
+ win_private_assemblies=None,
cipher=block_cipher)
-pyz = PYZ(a.pure,
+pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
+
+# Binary files you need to include in the form of:
+# (<destination>, <source>, '<TYPE>')
+
+# Data files you want to include, in the form of:
+# (<destination>, <source>, '<TYPE>')
+data = [
+ ('qt.conf', 'qt.conf', 'DATA')
+]
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
@@ -21,12 +36,13 @@ exe = EXE(pyz,
debug=False,
strip=False,
upx=True,
- console=False )
+ console=False,
+ icon='../../data/images/mask-icon.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
- strip=False,
+ strip=None,
upx=True,
name='bitmask')
if sys.platform.startswith("darwin"):
diff --git a/pkg/pyinst/qt.conf b/pkg/pyinst/qt.conf
new file mode 100644
index 00000000..7eecd342
--- /dev/null
+++ b/pkg/pyinst/qt.conf
@@ -0,0 +1,2 @@
+[Paths]
+Plugins=qt4_plugins \ No newline at end of file
diff --git a/pkg/windows/Makefile b/pkg/windows/Makefile
new file mode 100644
index 00000000..5d19353e
--- /dev/null
+++ b/pkg/windows/Makefile
@@ -0,0 +1,25 @@
+.PHONY: all pkg installer openvpn pyinstaller
+all:
+ docker-compose build
+ $(MAKE) pkg
+
+pkg:
+ $(MAKE) openvpn
+ $(MAKE) pyinstaller
+ $(MAKE) installer
+
+pyinstaller:
+ docker-compose run --rm pyinstaller
+
+openvpn:
+ docker-compose run --rm openvpn
+
+installer:
+ docker-compose run --rm installer
+
+clean:
+ docker rmi windows_pyinstaller
+ docker rmi windows_openvpn
+ docker rmi windows_installer
+ rm -rf ../../dist/*.exe
+ rm -rf ../../build/* \ No newline at end of file
diff --git a/pkg/windows/README.rst b/pkg/windows/README.rst
new file mode 100644
index 00000000..0bdfb1d1
--- /dev/null
+++ b/pkg/windows/README.rst
@@ -0,0 +1,144 @@
+Environment setup in debian:jessie
+==================================
+
+basically you need this to setup your environment:
+
+# apt-get install mingw-w64
+# apt-get install wine
+# apt-get install nsis
+
+this is a incomplete list of dependencies, review the pyinstaller/Dockerfile
+to get a understanding of what needs to be setup in order to have a
+environment that builds the installer
+
+Requirements
+============
+
+docker-compose
+
+Building the package
+====================
+
+make pkg
+
+
+Reproducible builds
+===================
+
+please run the binary and installer builds on a clean machine eg
+using docker or any virtual environment that can easily be prepared
+by a third party to verify that the binaries are actually what the
+sourcecode suggests.
+
+to use reproducible build you need to install docker which then installs
+a clean debian:jessie to install nsis or the mingw environment
+
+
+Installer
+=========
+
+NSIS was choosen because it provided a out of the box toolchain to build
+installers for the windows platform with minimal dependencies. The downside
+of nsis is that it does not produce msi binaries
+
+to build the binary dependencies run:
+
+```
+docker-compose run --rm openvpn
+docker-compose run --rm pyinstaller
+```
+
+the produced binaries will be stored in ${ROOT}/build
+
+to build the installer run:
+
+```
+docker-compose run --rm installer
+```
+
+the produced installer will be stored in ${ROOT}/dist
+
+
+Pyinstaller
+===========
+
+Pyinstaller is a docker image based on debian:jessie with a cross-compile
+toolchain (gcc) for building zlib and openssl in linux and wine (staging)
+with installed python and mingw32 for pip/wheel compiling.
+All pip installed dependencies are
+part of the pyinstaller-build.sh script so they can be re-executed when the
+dependencies of the project change. The image should be rebuild when openssl,
+python or pyinstaller is updated:
+
+```
+docker-compose build pyinstaller
+```
+
+To debug or fine-tune the compile process it may be useful to setup the
+following software on the development machine:
+
+```
+X :1 -listen tcp
+DISPLAY=:1 xhost +
+docker-compose run --rm pyinstaller /bin/bash
+root@0fa19215321f:/# export DISPLAY=${YOUR_LOCAL_IP}:1
+root@0fa19215321f:/# wine cmd
+Z:\>python
+>>>
+```
+
+the configured volumes are:
+
+- the (read-only) sourcecode of the bitmask project in /var/src/bitmask
+- the result of the builds in /var/build
+
+pyinstaller-build.sh
+====================
+
+Contains all steps to build the win32 executables. The project relies on
+a read-write source tree which will pollute the development environment and
+make it hard to reproduce 'clean' builds. therefore it expects that the source
+is freshly checked out and not used to run in the host-environment. Otherwise
+pyc and ui elements will mess up the binary in unpredictable ways.
+
+* copy the /var/src/bitmask sources to a read-write location (/var/build)
+* execute ```make all``` in wine to build the qt ui and other resources
+* execute ```pip install $dependencies``` to have all dependencies available
+* execute ```pyinstaller``` in wine to compile the executable for
+** bitmask (src/leap/bitmask/app.py)
+* cleanup
+** remove the read-write copy
+** remove wine-dlls from the installer
+
+As the step 'install dependencies' may take long on slow internet connections
+during development it is advised to recycle the container and share the
+build/executables path with a windows-vm to test the result in short cycles
+instead of make pkg, uninstall, install.
+
+```
+docker-compose run --rm --entrypoint=/bin/bash pyinstalle
+root@0fa19215321f:/# cd /var/src/bitmask/pkg/windows
+root@0fa19215321f:/var/src/bitmask/pkg/windows# ./pyinstaller-build.sh
+root@0fa19215321f:/var/src/bitmask/pkg/windows# ./pyinstaller-build.sh
+root@0fa19215321f:/var/src/bitmask/pkg/windows# ./pyinstaller-build.sh
+....
+```
+
+and test the result binary (accessible in bitmask/build in a separate vm.
+
+OpenVPN
+=======
+
+OpenVPN is a straight forward cross compile image that builds the openvpn
+sourcecode from the git-repository to a windows executable that can be
+used by bitmask_root to launch eip.
+It needs to be rebuild regulary as openssl gets a new version about every
+month. PyInstaller uses the openssl that is compiled by this image
+
+Installer
+=========
+
+Installer is a straight forward debian image with makensis installed. The
+installer-build script lists the previously built files from pyinstaller and
+openvpn to pass it as nsh file to makensis. bitmask.nis controls what will
+be displayed to the user and how the components are installed and uninstalled \ No newline at end of file
diff --git a/pkg/windows/TODO b/pkg/windows/TODO
new file mode 100644
index 00000000..49c6bac1
--- /dev/null
+++ b/pkg/windows/TODO
@@ -0,0 +1,15 @@
+TODO
+====
+
+fix python-code (0.9.1) that fails on windows:
+- fix the race condition for the backend/frontend startup.
+ spawning the backend takes time. with a introduced 15s timeout
+ it was possible to ensure the backend is up. It would be ideal
+ if the backend could signal the app to continue loading the frontend
+- fix the ~/leap/events/zmq_certificates/public_keys/server.key
+- fix logger (& remove hack in pyinstaller-build.sh:228)
+- fix pysqlcipher (/LIB:,https get not working in setup.py, remove hack
+ in pyinstaller-build:164)
+- merge bitmask_root from https://github.com/alirezamirzaeiyan/bitmask-root
+
+create similar infrastructure for osx dmg, preferably run on osx for compressed dmg \ No newline at end of file
diff --git a/pkg/windows/bitmask.nis b/pkg/windows/bitmask.nis
new file mode 100644
index 00000000..8705c058
--- /dev/null
+++ b/pkg/windows/bitmask.nis
@@ -0,0 +1,2 @@
+!define PKGNAME bitmask
+!include .\bitmask.nsh \ No newline at end of file
diff --git a/pkg/windows/bitmask.nsh b/pkg/windows/bitmask.nsh
new file mode 100644
index 00000000..fe02f84e
--- /dev/null
+++ b/pkg/windows/bitmask.nsh
@@ -0,0 +1,115 @@
+# pwd is a ro-mounted source-tree that had all dependencies build into
+# package-name directories
+!define PKGNAMEPATH ..\..\build\executables\${PKGNAME}
+!include ${PKGNAMEPATH}_version.nsh
+!include .\bitmask_client_product.nsh
+!include ${PKGNAMEPATH}_install_files_size.nsh
+
+RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
+
+InstallDir "$PROGRAMFILES\${APPNAME}"
+
+LicenseData "..\..\LICENSE"
+Name "${COMPANYNAME} - ${APPNAME}"
+Icon "..\..\build\executables\mask-icon.ico"
+
+# /var/dist is a rw mounted volume
+outFile "/var/dist/${PKGNAME}-${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}${VERSIONSUFFIX}.exe"
+!include LogicLib.nsh
+
+# Just three pages - license agreement, install location, and installation
+page license
+page directory
+Page instfiles
+
+!macro VerifyUserIsAdmin
+UserInfo::GetAccountType
+pop $0
+${If} $0 != "admin" ;Require admin rights on NT4+
+ messageBox mb_iconstop "Administrator rights required!"
+ setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
+ quit
+${EndIf}
+!macroend
+
+function .onInit
+ setShellVarContext all
+ !insertmacro VerifyUserIsAdmin
+functionEnd
+
+section "TAP Virtual Ethernet Adapter" SecTAP
+ SetOverwrite on
+ SetOutPath "$TEMP"
+ File /oname=tap-windows.exe "..\..\build\executables\openvpn\tap-windows.exe"
+
+ DetailPrint "Installing TAP (may need confirmation)..."
+ nsExec::ExecToLog '"$TEMP\tap-windows.exe" /S /SELECT_UTILITIES=1'
+ Pop $R0 # return value/error/timeout
+
+ Delete "$TEMP\tap-windows.exe"
+ WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "tap" "installed"
+sectionEnd
+
+section "install"
+ setOutPath $INSTDIR
+
+ !include ${PKGNAMEPATH}_install_files.nsh
+
+ # Uninstaller - See function un.onInit and section "uninstall" for configuration
+ writeUninstaller "$INSTDIR\uninstall.exe"
+
+ # Start Menu
+ createDirectory "$SMPROGRAMS\${COMPANYNAME}"
+ createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\bitmask.exe" "" "$INSTDIR\bitmask.exe"
+
+ !include bitmask_client_registry_install.nsh
+sectionEnd
+
+# Uninstaller
+
+function un.onInit
+ SetShellVarContext all
+ !insertmacro VerifyUserIsAdmin
+functionEnd
+
+section "uninstall"
+
+ delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
+ # Try to remove the Start Menu folder - this will only happen if it is empty
+ rmDir "$SMPROGRAMS\${COMPANYNAME}"
+
+ # Remove files
+ !include ${PKGNAMEPATH}_uninstall_files.nsh
+
+ # Remove TAP Drivers
+ ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "tap"
+ ${If} $R0 == "installed"
+ DetailPrint "Uninstalling TAP as we installed it..."
+ ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TAP-Windows" "UninstallString"
+ ${If} $R0 != ""
+ DetailPrint "Uninstalling TAP..."
+ nsExec::ExecToLog '"$R0" /S'
+ Pop $R0 # return value/error/timeout
+ ${Else}
+ # on x64 windows the uninstall location needs to be accessed using WOW
+ SetRegView 64
+ ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TAP-Windows" "UninstallString"
+ SetRegView 32
+ ${If} $R0 != ""
+ DetailPrint "Uninstalling TAP 64..."
+ nsExec::ExecToLog '"$R0" /S'
+ Pop $R0 # return value/error/timeout
+ ${EndIf}
+ ${EndIf}
+ DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "tap"
+ ${EndIf}
+
+ # Always delete uninstaller as the last action
+ delete $INSTDIR\uninstall.exe
+
+ # Try to remove the install directory - this will only happen if it is empty
+ rmDir $INSTDIR
+
+ # Remove uninstaller information from the registry
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
+sectionEnd \ No newline at end of file
diff --git a/pkg/windows/bitmask_client_product.nsh b/pkg/windows/bitmask_client_product.nsh
new file mode 100644
index 00000000..64f59ac7
--- /dev/null
+++ b/pkg/windows/bitmask_client_product.nsh
@@ -0,0 +1,8 @@
+!define APPNAME "Bitmask"
+!define COMPANYNAME "leap.se"
+!define DESCRIPTION "With Bitmask VPN, all your traffic is securely routed through your provider before it is decrypted and sent on to the open internet."
+# These will be displayed by the "Click here for support information" link in "Add/Remove Programs"
+# It is possible to use "mailto:" links in here to open the email client
+!define HELPURL "https://bitmask.net/en/help" # "Support Information" link
+!define UPDATEURL "https://bitmask.net/en/install" # "Product Updates" link
+!define ABOUTURL "https://bitmask.net/" # "Publisher" link \ No newline at end of file
diff --git a/pkg/windows/bitmask_client_registry_install.nsh b/pkg/windows/bitmask_client_registry_install.nsh
new file mode 100644
index 00000000..5bf04045
--- /dev/null
+++ b/pkg/windows/bitmask_client_registry_install.nsh
@@ -0,0 +1,17 @@
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}"
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\bitmask.exe$\""
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "${COMPANYNAME}"
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "${HELPURL}"
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\""
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}${VERSIONSUFFIX}"
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
+# There is no option for modifying or repairing the install
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
+# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE} \ No newline at end of file
diff --git a/pkg/windows/docker-compose.yml b/pkg/windows/docker-compose.yml
new file mode 100644
index 00000000..92b310c8
--- /dev/null
+++ b/pkg/windows/docker-compose.yml
@@ -0,0 +1,42 @@
+# mingw environment to build dependency binaries in a reproducible environment
+# https://wiki.debian.org/ReproducibleBuilds
+# service to build a windows executable using pyinstaller
+# utilizes wine and pyinstaller-build.sh to produce
+# build/executables/pyinstaller/bitmask/*
+# usage: docker-compose run --rm pyinstaller
+# non-zero exit code on failure
+pyinstaller:
+ build: pyinstaller
+ volumes:
+# bitmask sources
+ - ../../:/var/src/bitmask:ro
+# produced binaries
+ - ../../build:/var/build
+# service to build a windows-executable from openvpn sources
+# uses the openvpn-build infrastructure to produce
+# build/executables/openvpn/*
+# produces the openvpn.exe and provides openssl that is to be
+# used by pyinsaller
+# usage: docker-compose run --rm openvpn
+# non-zero exit code on failure
+openvpn:
+ build: openvpn
+ volumes:
+# bitmask sources
+ - ../../:/var/src/bitmask:ro
+# produced binaries
+ - ../../build:/var/build
+# service to compile a installer using nullsoft installer
+# nsis environment to build installer (exe) that contains all required binaries
+# for a clean, just installed windows machine
+# utilizes the debian makensis and installer-build to produce
+# dist/bitmask-VERSION.exe
+# usage: docker-compose run --rm installer
+# non-zero exit code on failure
+installer:
+ build: installer
+ volumes:
+# bitmask sources
+ - ../../:/var/src/bitmask:ro
+# produced installers - configured in bitmask.nsh
+ - ../../dist:/var/dist
diff --git a/pkg/windows/installer-build.sh b/pkg/windows/installer-build.sh
new file mode 100755
index 00000000..cf664200
--- /dev/null
+++ b/pkg/windows/installer-build.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+
+# build installer
+# ===============
+#
+# builds several installers from previously compiled binaries
+
+product=bitmask
+# the location of the nsis installer nis files dictates the path of the files
+relative_executable_path=../../build/executables
+source_ro_path=/var/src/${product}
+temporary_build_path=/var/tmp/installer
+
+setups=($(ls -1 ${source_ro_path}/pkg/windows | grep '.nis$' | sed 's|.nis$||'))
+
+# generate nsis file references for installer for single directory
+# appends File and Remove to files that are later included by makensis
+# separate files for install and uninstall statements
+#
+# directory_root: the tree root that is currently generated
+# subdir: any directory in the tree
+# setup_name: the name of the setup this nsh entries are generated for
+function generateDirectoryNSISStatements() {
+ directory_root=$1
+ subdir=$2
+ setup_name=$3
+ find ${subdir} -maxdepth 1 -type f -exec echo 'File "'${relative_executable_path}'/{}"' \;>> ${setup_name}_install_files.nsh
+ find ${subdir} -maxdepth 1 -type f -exec echo 'Delete "$INSTDIR/{}"' \; >> ${setup_name}_uninstall_files.nsh
+}
+# generate a tree of files into nsis installer definitions
+# directory_root: the tree root that is currently generated
+# setup_name: the name of the setup this nsh entries are generated for
+function generateDirectoryNSISStatementsTree() {
+ directory_root=$1
+ setup_name=$2
+ subdirs=$(find ${directory_root} -type d | sort)
+ for subdir in ${subdirs[@]}
+ do
+ if [ "${directory_root}" != "${subdir}" ]; then
+ echo 'SetOutPath "$INSTDIR/'${subdir}'"' >> ${setup_name}_install_files.nsh
+ fi
+ generateDirectoryNSISStatements ${directory_root} ${subdir} ${setup_name}
+ done
+ # again to remove emptied directories on uninstall so reverse
+ subdirs=$(find ${directory_root} -type d | sort | tac)
+ for subdir in ${subdirs[@]}
+ do
+ if [ "${directory_root}" != "${subdir}" ]; then
+ echo 'RMDir "$INSTDIR/'${subdir}'"' >> ${setup_name}_uninstall_files.nsh
+ fi
+ done
+}
+# generate installer files for the available setups
+# those files include install and uninstall statements and are
+# modified (backslashes/source_path) to generate a sane target
+# structure
+function generateNSISStatements() {
+ pushd ${temporary_build_path}/build/executables
+ for setup in "${setups[@]}"
+ do
+ echo "setup:" ${setup}
+ echo "# auto generated by pkg/windows/installer-build.sh please do not modify" > ${setup}_install_files.nsh
+ echo "# auto generated by pkg/windows/installer-build.sh please do not modify" > ${setup}_uninstall_files.nsh
+ setup_source_path=${setup}
+ generateDirectoryNSISStatementsTree ${setup_source_path} ${setup}
+ # remove the setup_source_path from the nsh files
+ sed -i "s|INSTDIR/${setup_source_path}/|INSTDIR/|" ${setup}_install_files.nsh
+ sed -i "s|/${setup_source_path}/|/|" ${setup}_uninstall_files.nsh
+ # make backslashes
+ sed -i "s|/|\\\\|g" ${setup}_install_files.nsh ${setup}_uninstall_files.nsh
+ # make install size
+ installed_size=$(du -s --block-size=1000 ${setup} | awk '{print $1}')
+ echo "!define INSTALLSIZE ${installed_size}" > ${setup}_install_files_size.nsh
+ done
+ popd
+}
+# makensis to produce a installer.exe
+# the result is placed in /var/dist
+function buildInstaller() {
+ pushd ${temporary_build_path}/pkg/windows
+ for setup in ${setups[@]}
+ do
+ makensis ${setup}.nis || die 'build setup "'${setup}'" failed'
+ done
+ popd
+}
+# prepare build path
+# copies files that have been produced by other containers
+# merges the product so the nsis files are correct
+function prepareBuildPath() {
+ mkdir -p ${temporary_build_path}/pkg/windows
+ mkdir -p ${temporary_build_path}/build
+ cp -r ${source_ro_path}/pkg/windows/* ${temporary_build_path}/pkg/windows
+ cp -r ${source_ro_path}/build/* ${temporary_build_path}/build
+ cp -r ${source_ro_path}/LICENSE ${temporary_build_path}/LICENSE
+
+ test -d ${temporary_build_path}/build/executables/bitmask || die 'bitmask not available run docker-compose run --rm pyinstaller'
+ test -d ${temporary_build_path}/build/executables/openvpn || die 'openvpn not available run docker-compose run --rm openvpn'
+ pushd ${temporary_build_path}/build/executables
+ cp openvpn/bin/openvpn.exe bitmask
+ cp openvpn/bin/*.dll bitmask
+ popd
+}
+# remove build files to ensure subsequent builds
+function cleanup() {
+ rm -r ${temporary_build_path}
+}
+# display failure message and emit non-zero exit code
+function die() {
+ echo "die:" $@
+ exit 1
+}
+function main() {
+ prepareBuildPath
+ generateNSISStatements
+ buildInstaller
+ cleanup
+}
+main $@ \ No newline at end of file
diff --git a/pkg/windows/installer/Dockerfile b/pkg/windows/installer/Dockerfile
new file mode 100644
index 00000000..ae46acb6
--- /dev/null
+++ b/pkg/windows/installer/Dockerfile
@@ -0,0 +1,17 @@
+FROM debian:jessie
+MAINTAINER paixu@0xn0.de
+RUN apt-get update
+
+######
+# install packages required to build
+
+RUN apt-get -y install \
+ nsis
+WORKDIR /var/src/bitmask/pkg/windows
+
+######
+# set a specific user
+# needs external tuning of the /var/dist rights!
+# RUN useradd installer
+# USER installer
+ENTRYPOINT ["/var/src/bitmask/pkg/windows/installer-build.sh"] \ No newline at end of file
diff --git a/pkg/windows/openvpn-build.sh b/pkg/windows/openvpn-build.sh
new file mode 100755
index 00000000..3f470f55
--- /dev/null
+++ b/pkg/windows/openvpn-build.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# render openvpn prepared for installer
+# ================================================
+#
+# requires
+# - a linux host with mingw installed
+# - a rw directory mounted to /var/build
+# returns nonzero exit code when failed
+#
+# clone openvpn-build repository
+# runs cross-compile build
+# - downloads openvpn dependencies
+# - compiles
+# copy files to executables so they can be installed
+# cleans up (remove read-write copy)
+
+# the location where the openvpn binaries are placed
+absolute_executable_path=/var/build/executables
+temporary_build_path=/var/build/openvpn
+
+# cleanup the temporary build path for subsequent executes
+function cleanup() {
+ rm -r ${temporary_build_path} 2>/dev/null
+}
+# build openvpn source
+function buildSource() {
+ pushd ${temporary_build_path}/openvpn-build/generic
+ CHOST=i686-w64-mingw32 \
+ CBUILD=i686-pc-linux-gnu \
+ ./build \
+ || die 'build openvpn from source failed'
+ cp -r image/openvpn ${absolute_executable_path}/openvpn
+ popd
+}
+# fetch tap-windows.exe as defined in the openvpn vars
+function fetchTapWindows() {
+ pushd ${temporary_build_path}/openvpn-build
+ source windows-nsis/build-complete.vars
+ wget ${TAP_WINDOWS_INSTALLER_URL} -O ${absolute_executable_path}/openvpn/tap-windows.exe || die 'tap-windows.exe could not be fetched'
+ popd
+}
+# prepare read-write copy
+function prepareBuildPath() {
+ cleanup
+ mkdir -p ${temporary_build_path}
+ pushd ${temporary_build_path}
+ git clone https://github.com/OpenVPN/openvpn-build || die 'openvpn-build could not be cloned'
+ popd
+}
+# display failure message and emit non-zero exit code
+function die() {
+ echo "die:" $@
+ exit 1
+}
+function main() {
+ prepareBuildPath
+ buildSource
+ fetchTapWindows
+ cleanup
+}
+main $@ \ No newline at end of file
diff --git a/pkg/windows/openvpn/Dockerfile b/pkg/windows/openvpn/Dockerfile
new file mode 100644
index 00000000..471685a1
--- /dev/null
+++ b/pkg/windows/openvpn/Dockerfile
@@ -0,0 +1,17 @@
+FROM debian:jessie
+MAINTAINER paixu@0xn0.de
+
+######
+# install packages required to build
+# https-transport: winehq deb
+# winbind: pip install keyring (requirements.pip) needs this somehow
+# git-core: clone rw copy of repo and build specific commit
+# imagemagick: convert png to ico-files
+RUN apt-get update && apt-get -y install \
+ unzip bzip2 \
+ curl wget \
+ apt-transport-https \
+ man2html \
+ git-core \
+ build-essential autoconf mingw-w64
+ENTRYPOINT ["/var/src/bitmask/pkg/windows/openvpn-build.sh"] \ No newline at end of file
diff --git a/pkg/windows/pyinstaller-build.sh b/pkg/windows/pyinstaller-build.sh
new file mode 100755
index 00000000..06db8d54
--- /dev/null
+++ b/pkg/windows/pyinstaller-build.sh
@@ -0,0 +1,287 @@
+#!/bin/bash
+
+# render dependencies into separate subdirectories
+# ================================================
+#
+# requires
+# - a linux host with wine, wine with python and mingw installed
+# - the sourcecode mounted to /var/src/
+# - a rw directory mounted to /var/build
+# returns nonzero exit code when pyinstaller failed
+#
+# prepares a read-write copy of the sourcecode
+# executes qt-uic and qt-rcc for gui dialogs
+# installs dependencies from pkg/dependencies-windows.pip
+# runs pyinstaller
+# cleans up (remove wine-dlls, remove read-write copy)
+# creates nsis install/uninstall scripts for the files for each package
+# if $1 is set it is expected to be a branch/git-tag
+
+product=bitmask
+# the location where the pyinstaller results are placed
+absolute_executable_path=/var/build/executables
+# the location of the nsis installer nis files dictates the path of the files
+relative_executable_path=../../build/executables
+source_ro_path=/var/src/${product}
+temporary_build_path=/var/build/pyinstaller
+git_tag=HEAD
+version_prefix=leap.bitmask
+git_version=unknown
+# option that is changed when a dependency-cache is found
+install_dependencies=true
+# default options for components
+with_eip=false
+with_mail=true
+
+setups=($(ls -1 ${source_ro_path}/pkg/windows | grep '.nis$' | sed 's|.nis$||'))
+# add mingw dlls that are build in other steps
+function addMingwDlls() {
+ root=$1
+ cp /usr/lib/gcc/i686-w64-mingw32/4.9-win32/libgcc_s_sjlj-1.dll ${root}
+ cp /root/.wine/drive_c/Python27/Lib/site-packages/zmq/libzmq.pyd ${root}
+ cp /root/.wine/drive_c/Python27/Lib/site-packages/zmq/libzmq.pyd ${root}
+ mkdir -p ${root}/pysqlcipher
+ cp /var/build/pyinstaller/pkg/pyinst/build/bitmask/pysqlcipher-2.6.4-py2.7-win32.egg/pysqlcipher/_sqlite.pyd ${root}/pysqlcipher
+ cp ~/.wine/drive_c/openssl/bin/*.dll ${root}
+}
+# cleanup the temporary build path for subsequent executes
+function cleanup() {
+ rm -r ${temporary_build_path} 2>/dev/null
+}
+# create files that are not part of the repository but are needed
+# in the windows environment:
+# - license with \r\n
+# - ico from png (multiple sizes for best results on high-res displays)
+function createInstallablesDependencies() {
+ pushd ${temporary_build_path} > /dev/null
+ cat LICENSE | sed 's|\n|\r\n|g' > LICENSE.txt
+ convert data/images/mask-icon.png -filter Cubic -scale 256x256! data/images/mask-icon-256.png
+ convert data/images/mask-icon-256.png -define icon:auto-resize data/images/mask-icon.ico
+ # execute qt-uic / qt-rcc
+ wine mingw32-make all || die 'qt-uic / qt-rcc failed'
+ # get version using git (only available in host)
+ git_version=$(python setup.py version| grep 'Version is currently' | awk -F': ' '{print $2}')
+ # run setup.py in a path with the version contained so versioneer can
+ # find the information and put it into the egg
+ versioned_build_path=/var/tmp/${version_prefix}-${git_version}
+ mkdir -p ${versioned_build_path}
+ cp -r ${temporary_build_path}/* ${versioned_build_path}
+ # apply patches to the source that are required for working code
+ # should not be required in the future as it introduces possible
+ # hacks that are hard to debug
+ applyPatches ${versioned_build_path}
+ pushd ${versioned_build_path} > /dev/null
+ wine python setup.py update_files || die 'setup.py update_files failed'
+ wine python setup.py build || die 'setup.py build failed'
+ wine python setup.py install || die 'setup.py install failed'
+ popd
+ rm -rf ${versioned_build_path}
+ popd
+}
+# create installer version that may be used by installer-build.sh / makensis
+# greps the version-parts from the previously extracted git_version and stores
+# the result in a setup_version.nsh
+# when the git_version does provide a suffix it is prefixed with a dash so the
+# installer output needs no conditional for this
+function createInstallerVersion() {
+ setup=$1
+ # [0-9]*.[0-9]*.[0-9]*-[0-9]*_g[0-9a-f]*_dirty
+ VERSIONMAJOR=$(echo ${git_version} | sed 's|^\([0-9]*\)\..*$|\1|')
+ VERSIONMINOR=$(echo ${git_version} | sed 's|^[0-9]*\.\([0-9]*\).*$|\1|')
+ VERSIONBUILD=$(echo ${git_version} | sed 's|^[0-9]*\.[0-9]*\.\([0-9]*\).*$|\1|')
+ VERSIONSUFFIX=$(echo ${git_version} | sed 's|^[0-9]*\.[0-9]*\.[0-9]*-\(.*\)$|\1|')
+ echo "!define VERSIONMAJOR ${VERSIONMAJOR}" > ${absolute_executable_path}/${setup}_version.nsh
+ echo "!define VERSIONMINOR ${VERSIONMINOR}" >> ${absolute_executable_path}/${setup}_version.nsh
+ echo "!define VERSIONBUILD ${VERSIONBUILD}" >> ${absolute_executable_path}/${setup}_version.nsh
+ if [ ${VERSIONSUFFIX} != "" ]; then
+ VERSIONSUFFIX="-${VERSIONSUFFIX}"
+ fi
+ echo "!define VERSIONSUFFIX ${VERSIONSUFFIX}" >> ${absolute_executable_path}/${setup}_version.nsh
+}
+# create installable binaries with dlls
+function createInstallables() {
+ mkdir -p ${absolute_executable_path}
+ pushd ${temporary_build_path}/pkg/pyinst
+ # build install directories (contains multiple files with pyd,dll, some of
+ # them look like windows WS_32.dll but are from wine)
+ for setup in ${setups[@]}
+ do
+ # --clean do not cache anything and overwrite everything --noconfirm
+ # --distpath to place on correct location
+ # --debug to see what may be wrong with the result
+ # --paths=c:\python\lib\site-packages;c:\python27\lib\site-packages
+ wine pyinstaller \
+ --clean \
+ --noconfirm \
+ --distpath=.\\installables \
+ --paths=Z:\\var\\build\\pyinstaller\\src\\ \
+ --paths=C:\\Python27\\Lib\\site-packages\\ \
+ --debug \
+ ${setup}.spec \
+ || die 'pyinstaller for "'${setup}'" failed'
+ removeWineDlls installables/${setup}
+ addMingwDlls installables/${setup}
+ rm -r ${absolute_executable_path}/${setup}
+ cp -r installables/${setup} ${absolute_executable_path}
+ cp ${absolute_executable_path}/cacert.pem ${absolute_executable_path}/${setup}
+ rm -r installables
+ createInstallerVersion ${setup}
+ done
+ popd
+ pushd ${temporary_build_path}
+ cp data/images/mask-icon.ico ${absolute_executable_path}/
+ popd
+}
+# install (windows)dependencies of project
+function installProjectDependencies() {
+ pushd ${temporary_build_path} > /dev/null
+ unsupported_packages="dirspec"
+ pip_flags="--find-links=Z:${temporary_build_path}/wheels"
+ for unsupported_package in ${unsupported_packages}
+ do
+ pip_flags="${pip_flags} --allow-external ${unsupported_package} --allow-unverified ${unsupported_package}"
+ done
+ pip_flags="${pip_flags} -r"
+
+ # install dependencies
+ mkdir -p ${temporary_build_path}/wheels
+ wine pip install ${pip_flags} pkg/requirements-leap.pip || die 'requirements-leap.pip could not be installed'
+ # fix requirements
+ # python-daemon breaks windows build
+ sed -i 's|^python-daemon|#python-daemon|' pkg/requirements.pip
+ wine pip install ${pip_flags} pkg/requirements.pip || die 'requirements.pip could not be installed'
+ git checkout pkg/requirements.pip
+ popd
+ cp -r /root/.wine/drive_c/Python27/Lib/site-packages ${absolute_executable_path}
+ curl https://curl.haxx.se/ca/cacert.pem > ${absolute_executable_path}/cacert.pem || die 'cacert.pem could not be fetched - would result in bad ssl in installer'
+}
+# workaround for broken dependencies
+# runs before pip install requirements
+# fixes failure for pysqlcipher as this requests a https file that the
+# windows-python fails to request
+function installProjectDependenciesBroken() {
+ pushd ${temporary_build_path} > /dev/null
+ curl https://pypi.python.org/packages/source/p/pysqlcipher/pysqlcipher-2.6.4.tar.gz \
+ > pysqlcipher-2.6.4.tar.gz \
+ || die 'fetch pysqlcipher failed'
+ tar xzf pysqlcipher-2.6.4.tar.gz
+ pushd pysqlcipher-2.6.4
+ curl https://downloads.leap.se/libs/pysqlcipher/amalgamation-sqlcipher-2.1.0.zip \
+ > amalgamation-sqlcipher-2.1.0.zip \
+ || die 'fetch amalgamation for pysqlcipher failed'
+ unzip -o amalgamation-sqlcipher-2.1.0.zip || die 'unzip amalgamation failed'
+ mv sqlcipher amalgamation
+ patch -p0 < ${source_ro_path}/pkg/windows/pyinstaller/pysqlcipher_setup.py.patch \
+ || die 'patch pysqlcipher setup.py failed'
+ wine python setup.py build install || die 'setup.py for pysqlcipher failed'
+ popd
+ popd # temporary_build_path
+}
+# prepare read-write copy
+function prepareBuildPath() {
+ cleanup
+ # ensure shared openssl for all pip builds
+ test -d ${absolute_executable_path}/openvpn || die 'openvpn not available run docker-compose run --rm openvpn'
+ cp -r ${absolute_executable_path}/openvpn /root/.wine/drive_c/openssl
+ if [ -d ${absolute_executable_path}/site-packages ]; then
+ # use pip install cache for slow connections
+ rm -r /root/.wine/drive_c/Python27/Lib/site-packages
+ cp -r ${absolute_executable_path}/site-packages /root/.wine/drive_c/Python27/Lib/
+ install_dependencies=false
+ fi
+ if [ ! -z $1 ]; then
+ git_tag=$1
+ fi
+ if [ ${git_tag} != "HEAD" ]; then
+ echo "using ${git_tag} as source for the project"
+ git clone ${source_ro_path} ${temporary_build_path}
+ pushd ${temporary_build_path}
+ git checkout ${git_tag} || die 'checkout "'${git_tag}'" failed'
+ popd
+ else
+ echo "using current source tree for build"
+ mkdir -p ${temporary_build_path}/data
+ mkdir -p ${temporary_build_path}/docs
+ mkdir -p ${temporary_build_path}/pkg
+ mkdir -p ${temporary_build_path}/src
+ mkdir -p ${temporary_build_path}/.git
+ cp -r ${source_ro_path}/data/* ${temporary_build_path}/data
+ cp -r ${source_ro_path}/data/* ${temporary_build_path}/docs
+ cp -r ${source_ro_path}/pkg/* ${temporary_build_path}/pkg
+ cp -r ${source_ro_path}/src/* ${temporary_build_path}/src
+ cp -r ${source_ro_path}/.git/* ${temporary_build_path}/.git
+ cp ${source_ro_path}/* ${temporary_build_path}/
+ fi
+}
+# add patches to the sourcetree
+# this function should do nothing some day and should be run after
+# the version has been evaluated
+function applyPatches() {
+ root_path=$1
+ # disable eip
+ if [ !${with_eip} ]; then
+ sed -i "s|HAS_EIP = True|HAS_EIP = False|" ${root_path}/src/leap/bitmask/_components.py
+ fi
+ # disable mail
+ if [ !${with_mail} ]; then
+ sed -i "s|HAS_MAIL = True|HAS_MAIL = False|" ${root_path}/src/leap/bitmask/_components.py
+ fi
+ # hack the logger
+ sed -i "s|'bitmask.log'|str(random.random()) + '_bitmask.log'|;s|import sys|import sys\nimport random|" ${root_path}/src/leap/bitmask/logs/utils.py
+ sed -i "s|perform_rollover=True|perform_rollover=False|" ${root_path}/src/leap/bitmask/app.py
+ # fix requirements
+ # python-daemon breaks windows build
+ sed -i 's|^python-daemon|#python-daemon|' ${root_path}/pkg/requirements.pip
+}
+# remove wine dlls that should not be in the installer
+# root: path that should be cleaned from dlls
+function removeWineDlls() {
+ root=$1
+ declare -a wine_dlls=(\
+ advapi32.dll \
+ comctl32.dll \
+ comdlg32.dll \
+ gdi32.dll \
+ imm32.dll \
+ iphlpapi.dll \
+ ktmw32.dll \
+ msvcp90.dll \
+ msvcrt.dll \
+ mswsock.dll \
+ mpr.dll \
+ netapi32.dll \
+ ole32.dll \
+ oleaut32.dll \
+ opengl32.dll \
+ psapi.dll \
+ rpcrt4.dll \
+ shell32.dll \
+ user32.dll \
+ version.dll \
+ winmm.dll \
+ winspool.drv \
+ ws2_32.dll \
+ wtsapi32.dll \
+ )
+ for wine_dll in "${wine_dlls[@]}"
+ do
+ # not all of the listed dlls are in all directories
+ rm ${root}/${wine_dll} 2>/dev/null
+ done
+}
+# display failure message and emit non-zero exit code
+function die() {
+ echo "die:" $@
+ exit 1
+}
+function main() {
+ prepareBuildPath $@
+ if [ ${install_dependencies} == true ]; then
+ installProjectDependenciesBroken
+ installProjectDependencies
+ fi
+ createInstallablesDependencies
+ createInstallables
+ cleanup
+}
+main $@ \ No newline at end of file
diff --git a/pkg/windows/pyinstaller/Dockerfile b/pkg/windows/pyinstaller/Dockerfile
new file mode 100644
index 00000000..2da0da3a
--- /dev/null
+++ b/pkg/windows/pyinstaller/Dockerfile
@@ -0,0 +1,105 @@
+FROM debian:jessie
+MAINTAINER paixu@0xn0.de
+
+ENV PYTHON_VERSION=2.7.11
+ENV OPENSSL_VERSION=1.0.2f
+ENV ZLIB_VERSION=1.2.8
+ENV MINGW_VERSION=0.6.2-beta-20131004-1
+ENV MINGW_BIN_VERSION=0.6.2-mingw32-beta-20131004-1-bin
+ENV WINEDEBUG=fixme-all
+
+######
+# install packages required to build
+# https-transport: winehq deb
+# winbind: pip install keyring (requirements.pip) needs this somehow
+# git-core: clone rw copy of repo and build specific commit
+# imagemagick: convert png to ico-files
+RUN apt-get update && apt-get -y install \
+ unzip curl apt-transport-https \
+ winbind \
+ build-essential autoconf bison gperf flex libtool mingw-w64 \
+ git-core \
+ imagemagick \
+ pkg-config
+
+# install wine > 1.6.2 (debian:jessie version fails with pip)
+RUN dpkg --add-architecture i386 \
+ && curl https://dl.winehq.org/wine-builds/Release.key | apt-key add - \
+ && echo 'deb https://dl.winehq.org/wine-builds/debian/ jessie main' >> /etc/apt/sources.list.d/wine.list \
+ && apt-get update
+
+RUN curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}.msi > /tmp/python-${PYTHON_VERSION}.msi
+RUN curl -L http://sourceforge.net/projects/mingw/files/Installer/mingw-get/mingw-get-${MINGW_VERSION}/mingw-get-${MINGW_BIN_VERSION}.zip/download > /tmp/mingw-get.zip
+
+# alternative with messy python afterwards
+# RUN curl -L http://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi > /tmp/msvcforpython27.msi
+
+RUN curl -L http://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz > /tmp/openssl-${OPENSSL_VERSION}.tar.gz
+RUN apt-get install -y winehq-staging
+
+RUN curl -L http://sourceforge.net/projects/mingw/files/Installer/mingw-get/mingw-get-${MINGW_VERSION}/mingw-get-${MINGW_BIN_VERSION}.zip/download > /tmp/mingw-get.zip
+RUN mkdir -p /root/.wine/drive_c/mingw \
+ && unzip -d /root/.wine/drive_c/mingw /tmp/mingw-get.zip
+
+#######
+# Build python dependency
+# using the 'host' (linux) xcompiler instead of fiddeling in wine
+# zlib - needs a update every 5 years
+# adds a patch that makes a shared lib - default is static
+RUN curl -L http://zlib.net/zlib-${ZLIB_VERSION}.tar.gz > /tmp/zlib-${ZLIB_VERSION}.tar.gz
+ADD zlib-mingw-shared.patch /zlib-mingw-shared.patch
+RUN mkdir -p /root/.wine/drive_c/zlib/src \
+ && mv /tmp/zlib-${ZLIB_VERSION}.tar.gz /root/.wine/drive_c/zlib/src \
+ && cd /root/.wine/drive_c/zlib/src \
+ && tar xzf zlib-${ZLIB_VERSION}.tar.gz \
+ && cd zlib-${ZLIB_VERSION} \
+ && patch -p0 < /zlib-mingw-shared.patch \
+ && make -f win32/Makefile.gcc PREFIX=/usr/bin/i686-w64-mingw32- \
+ && make -f win32/Makefile.gcc INCLUDE_PATH=/root/.wine/drive_c/zlib/include LIBRARY_PATH=/root/.wine/drive_c/zlib/lib BINARY_PATH=/root/.wine/drive_c/zlib/bin install
+
+######
+# install gcc for most pip builds
+# install g++ for pycryptopp
+# this is mingw in wine, not to get confused with mingw-w64 in container-host
+RUN wine msiexec -i /tmp/python-${PYTHON_VERSION}.msi -q \
+ && wine c:/mingw/bin/mingw-get.exe install gcc g++ mingw32-make \
+ && rm -r /tmp/.wine-0
+
+####
+# pip configuration
+# set wine mingw compiler to be used by "python setup build"
+# set default include dirs, libraries and library paths
+# the libraries=crypto is added because srp will only link using -lssl but links to BN_* (libcrypto) code
+# 'install' zlib to mingw so python may find its dlls
+# pyside-rcc fix (https://srinikom.github.io/pyside-bz-archive/670.html)
+RUN printf "[build]\ncompiler=mingw32\n\n[build_ext]\ninclude_dirs=c:\\openssl\\include;c:\\zlib\\include\nlibraries=crypto\nlibrary_dirs=c:\\openssl\\lib;c:\\openssl\\bin;c:\\zlib\\lib;c:\\zlib\\bin" > /root/.wine/drive_c/Python27/Lib/distutils/distutils.cfg \
+ && printf 'REGEDIT4\n\n[HKEY_CURRENT_USER\\Environment]\n"PATH"="C:\\\\python27;C:\\\\python27\\\\Scripts;C:\\\\python27\\\\Lib\\\\site-packages\\\\PySide;C:\\\\mingw\\\\bin;c:\\\\windows;c:\\\\windows\\\\system"' > /root/.wine/drive_c/path.reg \
+ && printf 'REGEDIT4\n\n[HKEY_CURRENT_USER\\Environment]\n"OPENSSL_CONF"="C:\\\\openssl"' > /root/.wine/drive_c/openssl_conf.reg \
+ && printf 'REGEDIT4\n\n[HKEY_CURRENT_USER\\Environment]\n"PYTHONPATH"="C:\\\\python27\\\\lib\\\\site-packages;Z:\\\\var\\\\build\\\\bitmask_rw\\\\src"' > /root/.wine/drive_c/pythonpath.reg \
+ && cp /root/.wine/drive_c/zlib/bin/zlib1.dll /root/.wine/drive_c/mingw/bin \
+ && cp /root/.wine/drive_c/zlib/lib/libz.dll.a /root/.wine/drive_c/mingw/lib
+
+####
+# prepare the environment with all python dependencies installed
+# inject dirspec from distribution
+#
+RUN apt-get install -y python-dirspec \
+ && cp -r /usr/lib/python2.7/dist-packages/dirspec* /root/.wine/drive_c/Python27/Lib/site-packages/
+RUN apt-get install -y python-setuptools
+RUN wine regedit /root/.wine/drive_c/path.reg \
+ && wine regedit /root/.wine/drive_c/openssl_conf.reg \
+ && wine regedit /root/.wine/drive_c/pythonpath.reg \
+ && wine pip install virtualenv pyinstaller \
+ && wine pip install wheel \
+ && wine pip install -U setuptools-scm \
+ && wine pip install -U setuptools_scm \
+ && wine pip install -U pyside python-qt \
+ && wine pip install -I psutil==3.4.2 \
+ && rm -r /tmp/.wine-0
+
+# alternative msvc: after python is installed (or before?)
+# && wine msiexec -i /tmp/msvcforpython27.msi -q \
+
+RUN apt-get -y install \
+ mc
+ENTRYPOINT ["/var/src/bitmask/pkg/windows/pyinstaller-build.sh"] \ No newline at end of file
diff --git a/pkg/windows/pyinstaller/pysqlcipher_setup.py.patch b/pkg/windows/pyinstaller/pysqlcipher_setup.py.patch
new file mode 100644
index 00000000..dcec54fa
--- /dev/null
+++ b/pkg/windows/pyinstaller/pysqlcipher_setup.py.patch
@@ -0,0 +1,14 @@
+--- setup.py.org 2014-11-12 16:38:07.000000000 +0000
++++ setup.py 2016-01-23 14:08:13.255261595 +0000
+@@ -192,10 +192,7 @@
+ ext.define_macros.append(("inline", "__inline"))
+
+ # Configure the linker
+- ext.extra_link_args.append("libeay32.lib")
+- ext.extra_link_args.append(
+- "/LIBPATH:" + os.path.join(openssl, "lib")
+- )
++ ext.extra_link_args.append("-lcrypto")
+ else:
+ ext.extra_link_args.append("-lcrypto")
+
diff --git a/pkg/windows/pyinstaller/zlib-mingw-shared.patch b/pkg/windows/pyinstaller/zlib-mingw-shared.patch
new file mode 100644
index 00000000..1b980cb8
--- /dev/null
+++ b/pkg/windows/pyinstaller/zlib-mingw-shared.patch
@@ -0,0 +1,10 @@
+diff -Naur ../zlib-1.2.8-org/win32/Makefile.gcc ./win32/Makefile.gcc
+--- ../zlib-1.2.8-org/win32/Makefile.gcc 2008-10-23 17:44:36.000000000 +0000
++++ ./win32/Makefile.gcc 2015-12-06 19:20:00.449471787 +0000
+@@ -37,6 +37,6 @@
+ # Set to 1 if shared object needs to be installed
+ #
+-SHARED_MODE=0
++SHARED_MODE=1
+
+ #LOC = -DASMV \ No newline at end of file