diff --git a/.hgignore b/.hgignore index 61f68ad..355fe26 100644 --- a/.hgignore +++ b/.hgignore @@ -9,6 +9,8 @@ syntax: glob *.a *.dylib *.so +*.orig +*~ # ignore generated files @@ -30,3 +32,9 @@ android/build android/libs android/obj android/.gradle +android/external/*.git +android/assets + +syntax: regexp +android/external/[^.]* + diff --git a/android/build.gradle b/android/build.gradle index 0f9b527..6b2c6cb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,6 +2,15 @@ import org.apache.tools.ant.taskdefs.condition.Os +def pEpEngineSrc = hasProperty('pEpEngineSrc') ? pEpEngineSrc : "../../pEpEngine" +def buildAutomatic = hasProperty('buildAutomatic') ? buildAutomatic : "true" + +def pEpEngineAndroid = new File(new File(pEpEngineSrc), 'build-android') +def libetpanAndroid = new File(new File(pEpEngineSrc), '../libetpan/build-android') + +def externalInstallDir = file('external/data/data/' + pEpAppPackageName + '/app_opt') +def externalIncludePath = new File(externalInstallDir, 'include').absolutePath + buildscript { repositories { jcenter() @@ -35,10 +44,14 @@ android { // disable automatic ndk-build call, which ignore our Android.mk jni.srcDirs = [] jniLibs.srcDir 'libs' - + assets.srcDirs = ['assets'] } } + dependencies { + compile fileTree(dir: 'libs', include: '*.jar') + } + lintOptions { abortOnError false } @@ -56,21 +69,48 @@ android { commandLine 'make', 'gensource' } + // call external build (GnuPG, GPGME, etc) + task buildExternal(type:Exec) { + workingDir 'external' + commandLine 'make', 'all', 'PEP_PACKAGE_NAME='+pEpAppPackageName + } + + // call pEpEngine Build + task buildpEpEngine(type:Exec) { + workingDir pEpEngineAndroid + environment['GPGME_INCLUDE_PATH'] = externalIncludePath + commandLine './build.sh' + } + if(buildAutomatic=="true"){ + buildpEpEngine.dependsOn buildExternal + } + // unzip some of the dependencies task unzipDeps(type: Copy) { - from zipTree(file('../../pEpEngine/build-android/pEpEngine-android-1.zip')) - from zipTree(file('../../libetpan/build-android/libetpan-android-1.zip')) - from zipTree(file('../../libetpan/build-android/dependencies/openssl/openssl-android-1.zip')) - from zipTree(file('../../libetpan/build-android/dependencies/cyrus-sasl/cyrus-sasl-android-1.zip')) + from zipTree(new File(pEpEngineAndroid, 'pEpEngine-android-1.zip')) + from zipTree(new File(libetpanAndroid, 'libetpan-android-1.zip')) + from zipTree(new File(libetpanAndroid, 'dependencies/openssl/openssl-android-1.zip')) + from zipTree(new File(libetpanAndroid, 'dependencies/cyrus-sasl/cyrus-sasl-android-1.zip')) into file("${buildDir}") } + if(buildAutomatic=="true"){ + unzipDeps.dependsOn buildpEpEngine + } + + // copy pEpEnginge's system.db to asset + task cpDBAssets(type: Copy) { + from file(new File(new File(pEpEngineSrc), 'db/system.db')) + into 'assets' + } + // call regular ndk-build(.cmd) script from app directory task jniBuild(type: Exec) { - commandLine getNdkBuildCmd(), 'V=1' + commandLine getNdkBuildCmd(), 'V=1', 'GPGBUILD='+externalInstallDir.absolutePath } jniBuild.dependsOn genSources jniBuild.dependsOn unzipDeps + jniBuild.dependsOn cpDBAssets // Ensure this is done before java build tasks.withType(JavaCompile) { diff --git a/android/external/Makefile b/android/external/Makefile new file mode 100644 index 0000000..9beeb0e --- /dev/null +++ b/android/external/Makefile @@ -0,0 +1,527 @@ +#------------------------------------------------------------------------------# +# Makefile to build GPGME, GnuPG and deps for use with pEpEngine +# based on gnupg-for-android/external/Makefile +#------------------------------------------------------------------------------# + +#------------------------------------------------------------------------------# +# Build parameters + +# TODO: get params from the outside for multiarch build + +NDK_ABI ?= arm +NDK_TOOLCHAIN_VERSION ?= 4.8 +APP_ABI ?= armeabi-v7a +APP_PLATFORM ?= android-14 +PEP_PACKAGE_NAME ?= com.pep.pepjniaaractivity + +#------------------------------------------------------------------------------# +# Clone update and archive external projects GIT repos +# Local clone is in external/$project.git while +# slected commit is archived in external/$project + +EXTERNAL_GIT_REPOS = \ +gnupg|git://git.gnupg.org/gnupg.git?08c00cd\ +libassuan|git://git.gnupg.org/libassuan.git?261498d\ +libgcrypt|git://git.gnupg.org/libgcrypt.git?b3936b6\ +libgpg-error|git://git.gnupg.org/libgpg-error.git?bcd9295\ +libksba|git://git.gnupg.org/libksba.git?02079b5\ +curl|https://github.com/bagder/curl?f77e89c\ +npth|git://git.gnupg.org/npth.git?79fbdce\ +gpgme|git://git.gnupg.org/gpgme.git?37d927a + +define per_repo_targets +$(1).git: + git clone $(2) $(1).git + +$(1).git_update: $(1).git + cd $(1).git; git pull + +$(1): |$(1).git + mkdir $(1) + (cd $(1).git; git archive --format=tar $(3)) | tar -C $(1) -x + +$(1)_clean: + rm -rf $(1) + +EXTERNAL_LOCAL_GITS += $(1).git +EXTERNAL_LOCAL_GITS_UPDATE += $(1).git_update +EXTERNAL_SRCS += $(1) +EXTERNAL_SRCS_CLEAN += $(1)_clean +endef + +define per_repo +$(call per_repo_targets,\ + $(1),\ + $(word 1,$(subst ?, ,$(2))),\ + $(word 2,$(subst ?, ,$(2)))) +endef + +$(foreach repo, $(EXTERNAL_GIT_REPOS), $(eval $(call per_repo,\ + $(word 1,$(subst |, ,$(repo))),\ + $(word 2,$(subst |, ,$(repo)))))) + +git_update: $(EXTERNAL_LOCAL_GITS_UPDATE) + +#------------------------------------------------------------------------------# +# Manage paths for PREFIX, DESTDIR, LOCAL and PATH + +EXTERNAL_ROOT := $(shell pwd) + +# install root for built files +DESTDIR = $(EXTERNAL_ROOT) +prefix = /data/data/$(PEP_PACKAGE_NAME)/app_opt +LOCAL := $(DESTDIR)$(prefix) + +PATH := ${PATH}:$(NDK_TOOLCHAIN)/bin:$(LOCAL)/bin + +#------------------------------------------------------------------------------# +# NDK toolchain integration +# TODO: cleanup. + +# Android now has 64-bit and 32-bit versions of the NDK for GNU/Linux. We +# assume that the build platform uses the appropriate version, otherwise the +# user building this will have to manually set NDK_PROCESSOR or NDK_TOOLCHAIN. +CPU := $(shell uname -m) +ifeq ($(CPU),x86_64) + NDK_PROCESSOR=x86_64 +else + NDK_PROCESSOR=x86 +endif + +NDK_SYSROOT=$(ANDROID_NDK_HOME)/platforms/$(APP_PLATFORM)/arch-$(NDK_ABI) +NDK_UNAME := $(shell uname -s | tr '[A-Z]' '[a-z]') +ifeq ($(NDK_ABI),x86) + HOST = i686-linux-android + NDK_TOOLCHAIN = $(NDK_ABI)-$(NDK_TOOLCHAIN_VERSION) +else + HOST = $(NDK_ABI)-linux-androideabi + NDK_TOOLCHAIN = $(HOST)-$(NDK_TOOLCHAIN_VERSION) +endif +NDK_TOOLCHAIN_BASE=$(ANDROID_NDK_HOME)/toolchains/$(NDK_TOOLCHAIN)/prebuilt/$(NDK_UNAME)-$(NDK_PROCESSOR) + +# include Android's build flags +TARGET_ARCH_ABI = $(APP_ABI) +include $(ANDROID_NDK_HOME)/toolchains/$(NDK_TOOLCHAIN)/setup.mk + +CC := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-gcc --sysroot=$(NDK_SYSROOT) +CXX := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-g++ +CPP := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-cpp +LD := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-ld +AR := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-ar +RANLIB := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-ranlib +STRIP := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-strip \ + --strip-unneeded -R .note -R .comment + +CFLAGS = -DANDROID -I$(LOCAL)/include $(TARGET_CFLAGS) +LDFLAGS = -llog -L$(LOCAL)/lib $(TARGET_LDFLAGS) + +# change 'release' to 'debug' for unoptimized debug builds +ifeq ($(APP_ABI),armeabi-v7a) + CFLAGS += $(TARGET_arm_release_CFLAGS) +endif +ifeq ($(APP_ABI),armeabi) + CFLAGS += $(TARGET_thumb_release_CFLAGS) +endif + +#------------------------------------------------------------------------------# +# GNU Tools trickery + +# point pkg-config to the .pc files generated from these builds +export PKG_CONFIG_PATH=$(LOCAL)/lib/pkgconfig +# workaround for cross-compiling bug in autoconf +export ac_cv_func_malloc_0_nonnull=yes + +# GnuPG does not work with automake 1.14, it has proven reliable on 1.11, and +# that's widely available, so we'll add it to the env here, and the +# ./autogen.sh scripts will pick it up. +# http://lists.gnupg.org/pipermail/gnupg-devel/2013-August/027857.html +export AUTOMAKE=automake-1.11 + + +.PHONY = clean distclean install-clean \ + libgpg-error-build libgpg-error-clean libgpg-error-install \ + libgcrypt-build libgcrypt-clean libgcrypt-install \ + libassuan-build libassuan-clean libassuan-install \ + libksba-build libksba-clean libksba-install \ + gnupg-build gnupg-clean \ + curl-build curl-clean curl-install \ + npth-build npth-clean npth-install \ + assets clean-assets \ + $(EXTERNAL_LOCAL_GITS_UPDATE) $(EXTERNAL_SRCS_CLEAN) \ + showsetup + +all: gnupg-install gpgme-install assets + + +#------------------------------------------------------------------------------# +# debugging stuff + +showsetup: + @echo "NDK_TOOLCHAIN_VERSION: $(NDK_TOOLCHAIN_VERSION)" + @echo "NDK_TOOLCHAIN: $(NDK_TOOLCHAIN)" + @echo "NDK_SYSROOT: $(NDK_SYSROOT)" + @echo "APP_PLATFORM: $(APP_PLATFORM)" + @echo "APP_ABI: $(APP_ABI)" + @echo "HOST: $(HOST)" + @echo "CC: $(CC)" + @echo "LD: $(LD)" + @echo "CFLAGS: $(CFLAGS)" + @echo "LDFLAGS: $(LDFLAGS)" + + +#------------------------------------------------------------------------------# +# libgpg-error + +libgpg-error/configure: libgpg-error libgpg-error/configure.ac + cd libgpg-error && ./autogen.sh + +libgpg-error/Makefile: libgpg-error/configure + cd libgpg-error && \ + ./configure \ + CC="$(CC)" \ + AR=$(AR) \ + RANLIB=$(RANLIB) \ + CFLAGS="$(CFLAGS)" \ + LDFLAGS="$(LDFLAGS)" \ + --disable-doc \ + --disable-languages \ + --host=$(HOST) \ + --prefix=$(LOCAL) + ls -l libgpg-error/libtool + # brute force and ignorance to make libtool comply with android style + sed -i 's,^fast_install=.*,fast_install=needless,' libgpg-error/libtool + sed -i 's,^version_type=.*,version_type=none,' libgpg-error/libtool + sed -i 's,^shlibpath_overrides_runpath=.*,shlibpath_overrides_runpath=yes,' libgpg-error/libtool + sed -i 's,^library_names_spec=.*,library_names_spec="\\$$libname\\$$release\\$$shared_ext",' libgpg-error/libtool + sed -i 's,^soname_spec=.*,soname_spec="\\$$libname\\$$release\\$$shared_ext",' libgpg-error/libtool + sed -i 's,^finish_cmds=.*,finish_cmds="",' libgpg-error/libtool + sed -i 's,^sys_lib_dlsearch_path_spec=.*,sys_lib_dlsearch_path_spec="/lib /usr/lib",' libgpg-error/libtool + +libgpg-error/src/.libs/libgpg-error.so: libgpg-error/Makefile + $(MAKE) -C libgpg-error + +libgpg-error-build: libgpg-error/src/.libs/libgpg-error.so + +$(LOCAL)/lib/libgpg-error.so: libgpg-error/src/.libs/libgpg-error.so + $(MAKE) -C libgpg-error prefix=$(LOCAL) install + ls -l $(LOCAL)/lib/libgpg-error.so* + +libgpg-error-install: $(LOCAL)/lib/libgpg-error.so + +libgpg-error-clean: + -$(MAKE) -C libgpg-error clean + rm -rf libgpg-error/configure libgpg-error/Makefile + + +#------------------------------------------------------------------------------# +# libgcrypt + +libgcrypt/configure: libgcrypt libgcrypt/configure.ac + cd libgcrypt && ./autogen.sh + +libgcrypt/Makefile: libgcrypt/configure + cd libgcrypt && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --enable-maintainer-mode \ + --host=$(HOST) \ + --with-gpg-error-prefix=$(LOCAL) \ + --prefix=$(LOCAL) + -patch -N -p1 --reject-file=- libgcrypt/tests/random.c libgcrypt-disable-hanging-random-test.patch + +libgcrypt/src/.libs/libgcrypt.so: $(LOCAL)/lib/libgpg-error.so libgcrypt/Makefile + $(MAKE) -C libgcrypt + +$(LOCAL)/lib/libgcrypt.so: libgcrypt/src/.libs/libgcrypt.so + $(MAKE) -C libgcrypt prefix=$(LOCAL) install + ls -l $(LOCAL)/lib/libgcrypt.so + +libgcrypt-build: libgcrypt/src/.libs/libgcrypt.so + +libgcrypt-install: $(LOCAL)/lib/libgcrypt.so + +libgcrypt-clean: + -$(MAKE) -C libgcrypt clean + rm -rf libgcrypt/configure libgcrypt/Makefile + + +#------------------------------------------------------------------------------# +# libassuan + +libassuan/configure: libassuan libassuan/configure.ac + cd libassuan && ./autogen.sh && autoreconf --install --force --verbose + +libassuan/Makefile: libassuan/configure + -patch -N -p1 --reject-file=- libassuan/m4/libtool.m4 libtool-Add-Android-Linux-support.patch + cd libassuan && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --enable-maintainer-mode \ + --host=$(HOST) \ + --with-gpg-error-prefix=$(LOCAL) \ + --prefix=$(LOCAL) + +libassuan/src/.libs/libassuan.so: $(LOCAL)/lib/libgpg-error.so libassuan/Makefile + $(MAKE) -C libassuan + +$(LOCAL)/lib/libassuan.so: libassuan/src/.libs/libassuan.so + $(MAKE) -C libassuan prefix=$(LOCAL) install + ls -l $(LOCAL)/lib/libassuan.so + +libassuan-build: libassuan/src/.libs/libassuan.so + +libassuan-install: $(LOCAL)/lib/libassuan.so + +libassuan-clean: + -$(MAKE) -C libassuan clean + rm -rf libassuan/configure libassuan/Makefile + + +#------------------------------------------------------------------------------# +# npth + +npth/configure: npth npth/configure.ac + cd npth && ./autogen.sh && autoreconf --install --force --verbose + +npth/Makefile: npth/configure + -patch -N -p1 --reject-file=- npth/m4/libtool.m4 libtool-Add-Android-Linux-support.patch + cd npth && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --enable-maintainer-mode \ + --host=$(HOST) \ + --with-gnu-ld \ + --prefix=$(LOCAL) + +npth/src/.libs/libnpth.so: $(LOCAL)/lib/libgpg-error.so npth/Makefile + $(MAKE) -C npth + +$(LOCAL)/lib/libnpth.so: npth/src/.libs/libnpth.so + $(MAKE) -C npth prefix=$(LOCAL) install + ls -l $(LOCAL)/lib/libnpth.so* + +npth-build: npth/src/.libs/libnpth.so + +npth-install: $(LOCAL)/lib/libnpth.so + +npth-clean: + -$(MAKE) -C npth clean + rm -rf npth/Makefile npth/configure + + +#------------------------------------------------------------------------------# +# libksba + +libksba/configure: libksba libksba/configure.ac + cd libksba && ./autogen.sh + +libksba/Makefile: $(LOCAL)/lib/libgpg-error.so libksba/configure + -patch -N -p1 --reject-file=- libksba/m4/libtool.m4 libtool-Add-Android-Linux-support.patch + cd libksba && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --enable-maintainer-mode \ + --host=$(HOST) \ + --with-gpg-error-prefix=$(LOCAL) \ + --prefix=$(LOCAL) + +libksba/src/.libs/libksba.so: libksba/Makefile + $(MAKE) -C libksba + +$(LOCAL)/lib/libksba.so: libksba/src/.libs/libksba.so + $(MAKE) -C libksba prefix=$(LOCAL) install + ls -l $(LOCAL)/lib/libksba.so + +libksba-build: libksba/src/.libs/libksba.so + +libksba-install: $(LOCAL)/lib/libksba.so + +libksba-clean: + -$(MAKE) -C libksba clean + rm -rf libksba/configure libksba/Makefile + + +#------------------------------------------------------------------------------# +# curl + +curl/configure: curl curl/configure.ac + cd curl && ./buildconf + +curl/Makefile: curl/configure + -patch -N -p1 --reject-file=- curl/m4/libtool.m4 libtool-Add-Android-Linux-support.patch + cd curl && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --host=$(HOST) \ + --prefix=$(prefix) \ + --with-gnu-ld \ + --disable-imap \ + --disable-ldap \ + --disable-pop3 \ + --disable-rtsp \ + --disable-smtp + # brute force and ignorance to make libtool comply with android style + sed -i 's,^fast_install=.*,fast_install=needless,' curl/libtool + sed -i 's,^version_type=.*,version_type=none,' curl/libtool + sed -i 's,^shlibpath_overrides_runpath=.*,shlibpath_overrides_runpath=yes,' curl/libtool + sed -i 's,^library_names_spec=.*,library_names_spec="\\$$libname\\$$release\\$$shared_ext",' curl/libtool + sed -i 's,^soname_spec=.*,soname_spec="\\$$libname\\$$release\\$$shared_ext",' curl/libtool + sed -i 's,^finish_cmds=.*,finish_cmds="",' curl/libtool + sed -i 's,^sys_lib_dlsearch_path_spec=.*,sys_lib_dlsearch_path_spec="/lib /usr/lib",' curl/libtool + +curl/lib/.libs/libcurl.so: curl/Makefile + $(MAKE) -C curl + +$(LOCAL)/lib/libcurl.so: curl/lib/.libs/libcurl.so + $(MAKE) -C curl DESTDIR=$(DESTDIR) prefix=$(prefix) install + ls -l $(LOCAL)/lib/libcurl.so + +curl-build: curl/lib/.libs/libcurl.so + +curl-install: $(LOCAL)/lib/libcurl.so + +curl-clean: + -$(MAKE) -C curl clean + rm -f curl/Makefile + +#------------------------------------------------------------------------------# +# gnupg + +gnupg/configure: gnupg + cd gnupg && ./autogen.sh + +gnupg/Makefile: gnupg/configure + cd gnupg && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --enable-maintainer-mode \ + --host=$(HOST) \ + --with-gpg-error-prefix=$(LOCAL) \ + --with-libgcrypt-prefix=$(LOCAL) \ + --with-libassuan-prefix=$(LOCAL) \ + --with-ksba-prefix=$(LOCAL) \ + --with-npth-prefix=$(LOCAL) \ + --with-libcurl=$(LOCAL) \ + --disable-ldap \ + --without-libiconv-prefix \ + --disable-doc \ + --disable-g13 \ + --disable-ntbtls \ + --disable-gnutls \ + --enable-dirmngr-auto-start \ + --with-agent-pgm=$(prefix)/bin/gpg-agent \ + --with-pinentry-pgm=$(prefix)/bin/pinentry.sh \ + --with-dirmngr-pgm=$(prefix)/bin/dirmngr \ + --with-protect-tool-pgm=$(prefix)/libexec/gpg-protect-tool \ + --with-scdaemon-pgm=$(prefix)/bin/scdaemon \ + --prefix=$(prefix) + +gnupg/g10/gpg2: $(LOCAL)/lib/libgpg-error.so $(LOCAL)/lib/libgcrypt.so $(LOCAL)/lib/libksba.so $(LOCAL)/lib/libassuan.so $(LOCAL)/lib/libnpth.so $(LOCAL)/lib/libcurl.so gnupg/Makefile + $(MAKE) -C gnupg + +$(LOCAL)/bin/gpg2: gnupg/g10/gpg2 gnupg/configure + $(MAKE) -C gnupg prefix=$(LOCAL) install + ls -l $(LOCAL)/bin/gpg2 + +$(LOCAL)/bin/pinentry.sh: pinentry.sh + install $< $(LOCAL)/bin + +gnupg-build: gnupg/g10/gpg2 + +gnupg-install: $(LOCAL)/bin/gpg2 $(LOCAL)/bin/pinentry.sh + install -d $(LOCAL)/etc/gnupg + install -d $(LOCAL)/var/run/gnupg + install -d $(LOCAL)/var/cache/gnupg + +gnupg-clean: + -$(MAKE) -C gnupg + +#------------------------------------------------------------------------------# +# gpgme + +gpgme/configure: gpgme gpgme/configure.ac + cd gpgme && ./autogen.sh + +gpgme/Makefile: gpgme/configure + -patch -N -p1 --reject-file=- gpgme/m4/libtool.m4 libtool-Add-Android-Linux-support.patch + cd gpgme && \ + CC="$(CC)" AR="$(AR)" RANLIB=$(RANLIB) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + ./configure \ + --host=$(HOST) \ + --enable-maintainer-mode \ + --with-gpg-error-prefix=$(LOCAL) \ + --with-libassuan-prefix=$(LOCAL) \ + --enable-fixed-path=$(prefix)/bin \ + --without-g13 \ + --disable-glibtest \ + --disable-gpg-test \ + --disable-gpgsm-test \ + --disable-gpgconf-test \ + --disable-g13-test \ + --prefix=$(prefix) + + +gpgme/src/.libs/libgpgme.so: gpgme/Makefile + $(MAKE) -C gpgme + +$(LOCAL)/lib/libgpgme.so: gpgme/src/.libs/libgpgme.so + $(MAKE) -C gpgme DESTDIR=$(DESTDIR) prefix=$(prefix) install + +gpgme-build: gpgme/src/.libs/libgpgme.so + +gpgme-install: $(LOCAL)/bin/gpg2 $(LOCAL)/lib/libgpgme.so + +gpgme-clean: + -$(MAKE) -C gpgme clean + -rm -f gpgme/Makefile gpgme/configure + +#------------------------------------------------------------------------------# +# assets for Android app + +clean-assets: + rm -rf $(ASSETS) + +ASSETS := $(EXTERNAL_ROOT)/../assets +assets: clean-assets + # add the new stuff + install -d $(ASSETS) + cp -a $(LOCAL)/* $(ASSETS) + # remove all the stuff we don't need + rm -f $(ASSETS)/bin/*-static + rm -f $(ASSETS)/bin/curl* + rm -f $(ASSETS)/lib/*.a $(ASSETS)/lib/*.la + # remove lib symlinks since Android AssetManager copies them as files + rm -f $(ASSETS)/lib/*.so + # remove .so.0 symlink and rename the .so.0.12.0 file to it + for f in $(ASSETS)/lib/*.so.[0-9]*; do \ + echo $$f; \ + test ! -L $$f || \ + (rm $$f && mv $$f.[0-9]* $$f); \ + done + rm -rf $(ASSETS)/include + rm -rf $(ASSETS)/share/man $(ASSETS)/share/info $(ASSETS)/share/doc + rm -rf $(ASSETS)/tests + + +#------------------------------------------------------------------------------# +# clean + +install-clean: + rm -rf -- $(LOCAL) + +clean: install-clean gnupg-clean curl-clean libksba-clean libassuan-clean npth-clean libgcrypt-clean libgpg-error-clean + +distclean: clean install-clean + -$(MAKE) -C gnupg distclean + -$(MAKE) -C curl distclean + -rm -f curl/configure # their distclean fails to rm this + -$(MAKE) -C libksba distclean + -$(MAKE) -C npth distclean + -$(MAKE) -C libassuan distclean + -$(MAKE) -C libgcrypt distclean + -$(MAKE) -C libgpg-error distclean + +gitclean: $(EXTERNAL_SRCS_CLEAN) + rm -rf $(ASSETS) + rm -rf $(EXTERNAL_ROOT)/data + diff --git a/android/external/libgcrypt-disable-hanging-random-test.patch b/android/external/libgcrypt-disable-hanging-random-test.patch new file mode 100644 index 0000000..4527cca --- /dev/null +++ b/android/external/libgcrypt-disable-hanging-random-test.patch @@ -0,0 +1,12 @@ +diff --git a/libgcrypt/tests/random.c b/libgcrypt/tests/random.c +index 10bf646..a52aacc 100644 +--- a/libgcrypt/tests/random.c ++++ b/libgcrypt/tests/random.c +@@ -439,7 +439,6 @@ run_all_rng_tests (const char *program) + "--early-rng-check --prefer-fips-rng", + "--early-rng-check --prefer-system-rng", + "--prefer-standard-rng", +- "--prefer-fips-rng", + "--prefer-system-rng", + NULL + }; diff --git a/android/external/libtool-Add-Android-Linux-support.patch b/android/external/libtool-Add-Android-Linux-support.patch new file mode 100644 index 0000000..fed716f --- /dev/null +++ b/android/external/libtool-Add-Android-Linux-support.patch @@ -0,0 +1,48 @@ +commit 8eeeb00daef8c4f720c9b79a0cdb89225d9909b6 +Author: David 'Digit' Turner +Date: Tue Oct 8 14:37:32 2013 -0700 + + libtool: Add Android/Linux support. + + This patch adds proper Android support to libtool. The main + issues are the following: + + - Versioned libraries are not supported by the platform and + its build/packaging tools. + + - The dynamic linker is not GNU ld, there is no support for + DT_RUNPATH. + + - Similarly, there is no ldconfig. + +diff --git a/m4/libtool.m4 b/m4/libtool.m4 +index 80d7e44..080272c 100644 +--- a/m4/libtool.m4 ++++ b/m4/libtool.m4 +@@ -2683,6 +2683,26 @@ linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + ++linux*android*) ++ version_type=none # Android doesn't support versioned libraries. ++ need_lib_prefix=no ++ need_version=no ++ library_names_spec='$libname$release$shared_ext' ++ soname_spec='$libname$release$shared_ext' ++ finish_cmds= ++ shlibpath_var=LD_LIBRARY_PATH ++ shlibpath_overrides_runpath=yes ++ ++ # This implies no fast_install, which is unacceptable. ++ # Some rework will be needed to allow for fast_install ++ # before this can be enabled. ++ hardcode_into_libs=yes ++ ++ dynamic_linker='Android linker' ++ # Don't embed -rpath directories since the linker doesn't support them. ++ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ++ ;; ++ + # This must be glibc/ELF. + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor diff --git a/android/external/pinentry.sh b/android/external/pinentry.sh new file mode 100644 index 0000000..15b1c7a --- /dev/null +++ b/android/external/pinentry.sh @@ -0,0 +1,47 @@ +#!/system/bin/sh +echo OK +while read cmd rest +do + case $cmd in + SETDESC) + DESC=$rest + echo OK + ;; + SETPROMPT) + PROMPT=$rest + echo OK + ;; + SETOK) + OK=$rest + echo OK + ;; + SETERROR) + ERROR=$rest + echo OK + ;; + GETPIN) + + echo "D " + echo OK + ;; + OPTION) + echo OK + ;; + GETINFO) + case $rest in + pid*) + echo D $$ + echo OK + ;; + esac + ;; + BYE) + echo OK + exit + ;; + *) + echo OK + ;; + esac +done + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 46bcc3a..c712f1e 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -1,10 +1,5 @@ LOCAL_PATH:= $(call my-dir) -GPGEXT := ../../../gnupg-for-android/external -GPGROOT := data/data/info.guardianproject.gpg/app_opt -GPGBUILD := $(GPGEXT)/$(GPGROOT) - - include $(CLEAR_VARS) LOCAL_MODULE := libassuan LOCAL_SRC_FILES := $(GPGBUILD)/lib/libassuan.so @@ -28,7 +23,7 @@ include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libgpgme LOCAL_SRC_FILES := $(GPGBUILD)/lib/libgpgme.so -LOCAL_EXPORT_C_INCLUDES := $(GPGEXT)/$(GPGROOT)/include $(GPGEXT)/gpgme/src +LOCAL_EXPORT_C_INCLUDES := $(GPGBUILD)/include include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) @@ -78,11 +73,13 @@ LOCAL_SRC_FILES := \ ../../src/org_pEp_jniadapter_Engine.cc \ ../../src/org_pEp_jniadapter_Message.cc \ ../../src/throw_pEp_exception.cc \ + ../../src/basic_api.cc \ ../../src/jniutils.cc LOCAL_C_INCLUDES := ../../src include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := pEpJNIAndroidHelper +LOCAL_SHARED_LIBRARIES := libgpgme LOCAL_SRC_FILES := org_pEp_jniadapter_AndroidHelper.cc include $(BUILD_SHARED_LIBRARY) diff --git a/android/jni/org_pEp_jniadapter_AndroidHelper.cc b/android/jni/org_pEp_jniadapter_AndroidHelper.cc index 351c341..28e37c6 100644 --- a/android/jni/org_pEp_jniadapter_AndroidHelper.cc +++ b/android/jni/org_pEp_jniadapter_AndroidHelper.cc @@ -3,6 +3,8 @@ #include +#include + extern "C" { JNIEXPORT jint JNICALL Java_org_pEp_jniadapter_AndroidHelper_setenv @@ -16,5 +18,17 @@ JNIEXPORT jint JNICALL Java_org_pEp_jniadapter_AndroidHelper_setenv return err; } +JNIEXPORT jint JNICALL Java_org_pEp_jniadapter_AndroidHelper_nativeSetup + (JNIEnv* env, jclass clazz, jstring debugflag) +{ + char* cdebugflag = (char *) env->GetStringUTFChars(debugflag, NULL); + gpgme_set_global_flag("debug", cdebugflag); + env->ReleaseStringUTFChars(debugflag, cdebugflag); + gpgme_set_global_flag ("disable-gpgconf", ""); + gpgme_set_global_flag ("gpg-name", "gpg2"); + + return 0; +} + } // extern "C" diff --git a/android/libs/commons-io-2.2.jar b/android/libs/commons-io-2.2.jar new file mode 100644 index 0000000..84ca565 Binary files /dev/null and b/android/libs/commons-io-2.2.jar differ diff --git a/android/src/org/pEp/jniadapter/AndroidHelper.java b/android/src/org/pEp/jniadapter/AndroidHelper.java index 106413c..5610d9c 100644 --- a/android/src/org/pEp/jniadapter/AndroidHelper.java +++ b/android/src/org/pEp/jniadapter/AndroidHelper.java @@ -1,17 +1,251 @@ package org.pEp.jniadapter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Scanner; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + import android.content.Context; +import android.content.res.AssetManager; +import android.util.Log; +import android.content.Intent; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; public class AndroidHelper { static { System.loadLibrary("pEpJNIAndroidHelper"); } + public static final String TAG = "AndroidHelper"; + private static native int setenv(String key, String value, boolean overwrite); + private static native int nativeSetup(String debugflag); + + private static File homeDir; + private static File optDir; + private static File versionFile; + public static File binDir; + + // TODO : Increment when needed. + public static int VERSION_CODE = 0; + + private static File shareDir; + + private static final String dBFileName = "system.db"; + private static File dBfile; + + private static boolean already = false; + + public static void startDaemonIfNeeded(Context c) { + if (!new File(homeDir, "S.gpg-agent").exists()) { + Intent service = new Intent(c, GPGAgentService.class); + c.startService(service); + } + } + + public static void envSetup(Context c) { + // "/opt" like dir to unpack GnuPG assets + optDir = c.getDir("opt", Context.MODE_PRIVATE); + + // Add GnuPG's bin to PATH + binDir = new File(optDir, "bin"); + setenv("PATH", System.getenv("PATH") + ":" + + binDir.getAbsolutePath(), true); + + // Tell dynamic loader where to find libs + String appLibDir = ""; + try { + appLibDir = new File(c.getApplicationInfo().nativeLibraryDir).getCanonicalPath(); + } catch (Exception e) { + e.printStackTrace(); + appLibDir = new File(c.getApplicationInfo().nativeLibraryDir).getAbsolutePath(); + } + File libDir = new File(optDir, "lib"); + setenv("LD_LIBRARY_PATH", appLibDir + ":" + + libDir.getAbsolutePath() + ":" + + System.getenv("LD_LIBRARY_PATH"), true); + + // Set HOME environment variable pointing to + // something like "/data/data/app.package.name/home" + // pEpEngine use it to find management DB and gpg home + homeDir = c.getDir("home", Context.MODE_PRIVATE); + setenv("HOME", homeDir.getAbsolutePath(), true); + + // pEpEngine need to find the safe words database + shareDir = c.getDir("trustwords", Context.MODE_PRIVATE); + dBfile = new File(shareDir, dBFileName); + + // TRUSTWORDS is absolute path of dir containig system.db + setenv("TRUSTWORDS", shareDir.getAbsolutePath(), true); + + // Check version file retains latest installed version + versionFile = new File(c.getFilesDir(), "VERSION"); + } + + public static void assetsSetup(Context c) { + envSetup(c); + boolean needUpgrade = needNewAssets(); + + // If system.db still not here, then go get it in the assets. + if (dBfile.exists() && needUpgrade){ + dBfile.delete(); + } + if (!dBfile.exists()){ + assetFileExtract(c, dBFileName, shareDir); + } + + // Copy GnuPG binaries + if (optDir.exists() && needUpgrade){ + try { + FileUtils.deleteDirectory(optDir); + } catch (IOException e) { + Log.e(TAG, "Couldn't delete existing gpg binaries"); + } + } + if (!optDir.exists()){ + optDir.mkdirs(); + assetPathExtract(c, "lib", optDir); + assetPathExtract(c, "bin", optDir); + new File(optDir, "var/cache/gnupg").mkdirs(); + new File(optDir, "var/lib/gnupg").mkdirs(); + new File(optDir, "var/run/gnupg").mkdirs(); + chmod("0755", optDir, true); + } + + // Fill version file + setInstalledVersion(c); + + } + + + public static void nativeSetup(Context c) { + // pre-load libs for pepengine, as + // android cannot solve lib dependencies on its own + System.loadLibrary("gpg-error"); + System.loadLibrary("assuan"); + System.loadLibrary("gpgme"); + // Launch native side setup + // TODO disable debug when done + nativeSetup( "9:"+new File(c.getFilesDir(), "gpgme.log").getAbsolutePath()); + } public static void setup(Context c) { - setenv("HOME", - c.getDir("home", Context.MODE_PRIVATE).getAbsolutePath(), - true); + if(!already){ + already = true; + assetsSetup(c); + nativeSetup(c); + startDaemonIfNeeded(c); + } + } + + private static void assetPathExtract(Context c, String assetPath, File targetDir) { + AssetManager assetManager = c.getAssets(); + + try { + String items[] = assetManager.list(assetPath); + if (items.length > 0) { + File newDir = new File(targetDir, new File(assetPath).getName()); + if (!newDir.exists()) + newDir.mkdirs(); + for (int i = 0; i < items.length; ++i) { + assetPathExtract(c, new File(assetPath, items[i]).getPath(), newDir); + } + } else { + assetFileExtract(c, assetPath, targetDir); + } + } catch (IOException ex) { + Log.e(TAG, assetPath + " : ", ex); + } + } + + private static void assetFileExtract(Context c, String assetPath, File targetDir) { + AssetManager assetManager = c.getAssets(); + + try { + + InputStream inputStream = assetManager.open(assetPath); + String targetFileName = + new File(targetDir, + new File(assetPath).getName()).getAbsolutePath(); + OutputStream outputStream = new FileOutputStream(targetFileName); + IOUtils.copy(inputStream, outputStream); + outputStream.close(); + inputStream.close(); + Log.i(TAG, "asset " + assetPath + " extracted as " + targetFileName); + } catch (Exception e) { + Log.e(TAG, assetPath + ": " + e.getMessage()); + } + } + + private static int getInstalledVersion() { + int versionCode = -1; + if (versionFile.exists()){ + try { + Scanner scan = new Scanner(versionFile); + versionCode = Integer.parseInt(scan.next()); + scan.close(); + } catch (Exception e) { + Log.e(TAG, "getInstalledVersion: " + e.getMessage()); + } + } + return versionCode; + } + + private static void setInstalledVersion(Context context) { + try { + FileOutputStream fileOutputStream = new FileOutputStream(versionFile); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream); + outputStreamWriter.write(String.valueOf(VERSION_CODE) + "\n"); + outputStreamWriter.close(); + fileOutputStream.close(); + } catch (Exception e) { + Log.e(TAG, "setInstalledVersion: " + e.getMessage()); + } + } + + public static boolean needNewAssets() { + return VERSION_CODE != getInstalledVersion(); + } + + // TODO: replace with native impl, less prone to failure. + public static void chmod(String modestr, File path) { + int err = 1; + try { + Class fileUtils = Class.forName("android.os.FileUtils"); + Method setPermissions = fileUtils.getMethod("setPermissions", + String.class, int.class, int.class, int.class); + err = (Integer) setPermissions.invoke( + null, path.getAbsolutePath(), + Integer.parseInt(modestr, 8), + -1, -1); + if (err != 0) { + Log.i(TAG, "android.os.FileUtils.setPermissions() returned " + err + + " for '" + path + "'"); + } + } catch (Exception e) { + Log.i(TAG, "chmod:", e); + } + } + + public static void chmod(String mode, File path, boolean recursive) { + chmod(mode, path); + if (recursive) { + File[] paths = path.listFiles(); + for (File pth : paths) { + if (pth.isDirectory()) { + chmod(mode, pth, true); + } else { + chmod(mode, pth); + } + } + } } } diff --git a/android/src/org/pEp/jniadapter/GPGAgentService.java b/android/src/org/pEp/jniadapter/GPGAgentService.java new file mode 100644 index 0000000..5b87dcd --- /dev/null +++ b/android/src/org/pEp/jniadapter/GPGAgentService.java @@ -0,0 +1,64 @@ +package org.pEp.jniadapter; + +import java.io.File; + +import android.content.Intent; +import android.app.Service; +import android.util.Log; +import android.os.IBinder; + +public class GPGAgentService extends Service { + public static final String TAG = "GPGAgentService"; + private AgentProcessThread process; + + class AgentProcessThread extends Thread { + + @Override + public void run() { + Log.i(TAG, "execute GPG agent"); + try { + Runtime.getRuntime().exec( + "gpg-agent" + + " --pinentry-program " + + new File(AndroidHelper.binDir, "pinentry.sh").getAbsolutePath() + + " --no-detach" + + " --daemon --write-env-file" + + " --batch" + + " --debug-level basic --log-file " + + new File(GPGAgentService.this.getFilesDir(), "gpg-agent.log")).waitFor(); + Log.i(TAG, "execution terminated"); + } catch (Exception e) { + Log.e(TAG, "could not execute process", e); + } finally { + stopSelf(); + // eradicate process in critical section + synchronized (GPGAgentService.this) { + process = null; + } + } + } + } + + @Override + public void onCreate() { + Log.d(TAG, "onCreate"); + // Prepare environment for agent + AndroidHelper.envSetup(this); + } + + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "onStartCommand"); + // use critical section to avoid race conditions + synchronized (this) { + process = new AgentProcessThread(); + process.start(); + } + return START_STICKY; + } + + @Override + public IBinder onBind(Intent arg0) { + // onBind() must return null, even if binder unused + return null; + } +} diff --git a/src/basic_api.cc b/src/basic_api.cc index da68565..5e926df 100644 --- a/src/basic_api.cc +++ b/src/basic_api.cc @@ -96,5 +96,27 @@ JNIEXPORT void JNICALL Java_org_pEp_jniadapter_Engine_keyCompromized( ::key_compromized(session, _ident->fpr); } +JNIEXPORT void JNICALL Java_org_pEp_jniadapter_Engine_importKey( + JNIEnv *env, + jobject obj, + jbyteArray key + ) +{ + PEP_SESSION session = (PEP_SESSION) callLongMethod(env, obj, "getHandle"); + char *_key = to_string(env, key); + + if(_key == NULL){ + throw_pEp_Exception(env, PEP_OUT_OF_MEMORY); + return; + } + + + PEP_STATUS status = ::import_key(session, _key, strlen(_key)); + if (status != PEP_STATUS_OK) { + throw_pEp_Exception(env, status); + return; + } + +} } // extern "C" diff --git a/src/gen_java_Engine.ysl2 b/src/gen_java_Engine.ysl2 index a93e3e4..b9688f8 100644 --- a/src/gen_java_Engine.ysl2 +++ b/src/gen_java_Engine.ysl2 @@ -36,8 +36,13 @@ tstylesheet { private native «$itype» «@name»(«$pitype» «$pname»); public «$jtype» «@name»(«$pjtype» «$pname») { - «$pitype» _«$pname» = new «$pitype»(«$pname»); || + choose { + when "$ptype = 'string'" + |> «$pitype» _«$pname» = «$pname».getBytes(); + otherwise + |> «$pitype» _«$pname» = new «$pitype»(«$pname»); + } choose { when "@type = 'void'" |> «@name»(_«$pname»); diff --git a/src/gen_java_exceptions.ysl2 b/src/gen_java_exceptions.ysl2 index 69caf18..cc73fb3 100644 --- a/src/gen_java_exceptions.ysl2 +++ b/src/gen_java_exceptions.ysl2 @@ -6,7 +6,11 @@ tstylesheet { template "/" { | package org.pEp.jniadapter; | - | public class pEpException extends Exception { } + | public class pEpException extends Exception { + | public pEpException(String message) { + | super(message); + | } + | } apply "namespace/exception[@name='pEp_status']", 0; } @@ -17,7 +21,11 @@ tstylesheet { document "org/pEp/jniadapter/{$name}.java", "text" { | package org.pEp.jniadapter; | - | class «$name» extends pEpException { } + | class «$name» extends pEpException { + | public «$name»(String message) { + | super(message); + | } + | } } } diff --git a/src/pEp.yml2 b/src/pEp.yml2 index 383f3a6..de0fd13 100644 --- a/src/pEp.yml2 +++ b/src/pEp.yml2 @@ -92,6 +92,7 @@ namespace pEp { basic identity myself(identity ident); basic identity updateIdentity(identity ident); basic void keyCompromized(identity ident); + basic void importKey(string key); }; struct message {