summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2016-04-18 12:07:25 -0400
committerKali Kaneko <kali@leap.se>2016-04-18 12:07:25 -0400
commitba7699c7e186d2eb8e58d1232f039b62584e2333 (patch)
tree8224cd9b2499ababebc913f88d9c552f8c43632b
parent033bb6e2a0112e909d3c77df3d93ec6c99b729c9 (diff)
parentf3e5121b5a08979d8b4f0be3ed084c7d45517696 (diff)
Merge branch 'paixu-installer' into develop
Great work by paixu, one step into the direction of winodows reproducible builds. The installer doesn't work, but this probably has to do with remaining bugs in the main qt app.
-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.sh63
-rw-r--r--pkg/windows/openvpn/Dockerfile17
-rwxr-xr-xpkg/windows/pyinstaller-build.sh288
-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, 1026 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..7f8ed84f
--- /dev/null
+++ b/pkg/windows/openvpn-build.sh
@@ -0,0 +1,63 @@
+#!/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'
+ mkdir -p ${absolute_executable_path}
+ 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 $@
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..522fc10f
--- /dev/null
+++ b/pkg/windows/pyinstaller-build.sh
@@ -0,0 +1,288 @@
+#!/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 -rf ${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
+ # XXX what's this update_files command?
+ #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 $@
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