diff --git a/.gitignore b/.gitignore index 7fe702f49..ecf9f7099 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/RetroShare.pro.user *.o *.sw? *.so @@ -10,3 +9,6 @@ Makefile* *.sw? *~ Thumbs.db +*.pro.user +.kdev4 +*.kdev4 diff --git a/.travis.yml b/.travis.yml index bae0f21c1..2c5badb2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,12 +47,12 @@ addons: project: name: "RetroShare/RetroShare" description: "RetroShare Build submitted via Travis CI" - build_command_prepend: "qmake CONFIG+=NO_SQLCIPHER; make clean" + build_command_prepend: "qmake CONFIG+=no_sqlcipher; make clean" build_command: "make -j 4" branch_pattern: coverity_scan before_script: - - qmake QMAKE_CC=$CC QMAKE_CXX=$CXX CONFIG+=NO_SQLCIPHER CONFIG+=tests + - qmake QMAKE_CC=$CC QMAKE_CXX=$CXX CONFIG+=no_sqlcipher CONFIG+=tests script: - if [ $TRAVIS_OS_NAME == linux ] && [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then make && tests/unittests/unittests >/dev/null 2>&1 ; fi diff --git a/README-Android.asciidoc b/README-Android.asciidoc new file mode 100644 index 000000000..b84d4a482 --- /dev/null +++ b/README-Android.asciidoc @@ -0,0 +1,86 @@ +Compile Retroshare for Android +============================== + +== Introduction + +Compiling an application for Android is not as easy as one would imagine, expecially one like RetroShare that has a big codebase and is not well documented. This document is aimed to empower the reader so she can hopefully succed or at least have a significant help in compiling her own RetroShare APK package installable on Android. + +== Preparing The Environement + +First of all setup your Qt for Android development environement following the guide on the link:http://doc.qt.io/qt-5/androidgs.html[Qt for android web site]. +At this point you should have Android SDK, Android NDK, and Qt for Android working fine, and you should be capable of executing on an Android emulator or on your Android phone Qt for Android examples. + +But RetroShare is not as simple to compile as those examples. In particular, the Android NDK precompiled toolchain is limited and doesn't support the full C++ specification, and it is missing some part that is needed to build RetroShare. The good news is that Android NDK ships all the necessary to build a custom toolchain that is suitable to build RetroShare. In order to build the toolchain with needed library RetroShare provides the +android-prepare-toolchain.sh+ script; before you execute it you should define some variables the script cannot determine in an easy and reliable manner by itself in your terminal. + +[source,bash] +------------------------------------------------------------------------------- +## The path where Android NDK is installed in your system +export ANDROID_NDK_PATH="/opt/android-ndk/" + +## The path where your fresh compiled toolchain will be installed, make sure +## the parent exists +export NDK_TOOLCHAIN_PATH="/home/$(whoami)/Development/android-toolchains/retroshare-android/" + +## The CPU architecture of the Android device you want to target +export ANDROID_NDK_ARCH="arm" + +## The Android API level the Android device you want to target +export ANDROID_PLATFORM_VER="19" + +## The number of core that yout host CPU have (just to speed up compilation) set it to 1 if unsure +export HOST_NUM_CPU=1 + +./android-prepare-toolchain.sh +------------------------------------------------------------------------------- + +Now is time for the bad news: as of today Qt assumes you use the NDK precompiled toolchain and doesn't have an option to use the custom toolchain you just generated, so you need to tweak Qt internals a little to make usage of the custom toolchain. First of all you need to determine your Qt for Android installation path -in my case it is +/opt/Qt5.7.0/+ - then find the +mkspecs+ directory -in my case it is +/opt/Qt5.7.0/5.7/android_armv7/mkspecs+ - and then modify qmake configuration for the android target according to your toolchain, in my case I have done it with the following commands (in the same shell I used before, to take advantage of the already set variables): + +WARNING: This may need a slight modification if you have a different Qt version. + +[source,bash] +------------------------------------------------------------------------------- +export QT_PATH="/opt/Qt5.7.0/" +export QT_MAJOR_VERSION="5.7" + +cd ${QT_PATH}/${QT_MAJOR_VERSION}/android_armv7/mkspecs +sudo --preserve-env -s +cp -r android-g++ android-g++-backup +cd android-g++ +sed -i "s|^NDK_TOOLCHAIN_PATH =.*|NDK_TOOLCHAIN_PATH = ${NDK_TOOLCHAIN_PATH}|" qmake.conf +------------------------------------------------------------------------------- + + +== Preparing Qt Creator + +Now that your environement is set up you should configure Qt Creator for Android following the link:http://doc.qt.io/qtcreator/creator-developing-android.html[official guide]. At the end of this step your Qt Creator should recognize the Android compiler and the Qt for Android kit. As we use a custom toolchain one more step is needed. + +From the top menu click +_Tools -> Options... -> Build & Run -> Compilers -> Android GCC (arm-4.9) -> Clone_ + +Now a new compiler (usually named +Clone of Android GCC (arm-4.9)+) should have appeared on your compilers list. Select that compiler and press the Browse button to look for your custom toolchain compiler. You should find it at +$NDK_TOOLCHAIN_PATH/bin/arm-linux-androideabi-gcc+. Select it, then press Apply. + +Now go to the Kits tab, select +Android for armeabi-v7a (GCC 4.9, Qt 5.7.0)+ and press the Clone button. A new kit is created (usually named +Clone of Android for armeabi-v7a (GCC 4.9, Qt 5.7.0)+). Now select the new kit and change the compiler to the one you have just created. + +Your Kit is now ready to use. Now you can open RetroShare as a Qt Creator project and in the Projects left menu add the newly created kit if not already present, so you can select it on the build type selection button down on the left. + +Some of RetroShare modules like +retroshare-gui+, +WebUI+ and +sqlcipher+ are not supported yet on Android so to be able to compile RetroShare without errors you will have to go to + + +_Qt Creator left pane -> Projects -> Build and Run -> Android SOMESTUFF kit -> Build Steps -> qmake -> Additional arguments_ + +and add the following configurations + +[source,makefile] +------------------------------------------------------------------------------- +CONFIG+=no_retroshare_gui CONFIG+=no_retroshare_nogui CONFIG+=no_retroshare_plugins CONFIG+=retroshare_android_service CONFIG+=libresapilocalserver CONFIG+=no_libresapihttpserver CONFIG+=no_sqlcipher CONFIG+=retroshare_qml_app +------------------------------------------------------------------------------- + +WARNING: SQLCipher is not supported yet on RetroShare for Android. This poses a major security concern, we are working to fix this ASAP. + +WARNING: Some versions of QtCreator try to find the Android SDK in +/opt/android/sdk+. A workaround to this is to make a symbolic link there pointing to your SDK installation path, like +mkdir -p /opt/android/sdk && ln -s /home/user/android-sdk-linux /opt/android/sdk+ + +== Furter Readings + +- link:http://doc.qt.io/qt-5/android-support.html[] +- link:https://developer.android.com/ndk/guides/libs.html[] +- link:retroshare://forum?name=Compiling%20nogui%20for%20android&id=8fd22bd8f99754461e7ba1ca8a727995&msgid=4e0f92330600bba9cf978f384f4b7b2f2ca64eff[] +- link:retroshare://file?name=Android%20Native%20Development%20Kit%20Cookbook.pdf&size=29214468&hash=0123361c1b14366ce36118e82b90faf7c7b1b136[] diff --git a/RetroShare.pro b/RetroShare.pro index 4f7e1934d..3d71f892c 100644 --- a/RetroShare.pro +++ b/RetroShare.pro @@ -3,36 +3,54 @@ TEMPLATE = subdirs #CONFIG += tests -SUBDIRS += \ - openpgpsdk \ - libbitdht \ - libretroshare \ - libresapi \ - retroshare_gui \ - retroshare_nogui \ - plugins - +SUBDIRS += openpgpsdk openpgpsdk.file = openpgpsdk/src/openpgpsdk.pro +SUBDIRS += libbitdht libbitdht.file = libbitdht/src/libbitdht.pro +SUBDIRS += libretroshare libretroshare.file = libretroshare/src/libretroshare.pro libretroshare.depends = openpgpsdk libbitdht +SUBDIRS += libresapi libresapi.file = libresapi/src/libresapi.pro libresapi.depends = libretroshare -retroshare_gui.file = retroshare-gui/src/retroshare-gui.pro -retroshare_gui.depends = libretroshare libresapi -retroshare_gui.target = retroshare-gui +retroshare_gui { + SUBDIRS += retroshare_gui + retroshare_gui.file = retroshare-gui/src/retroshare-gui.pro + retroshare_gui.depends = libretroshare libresapi + retroshare_gui.target = retroshare_gui +} -retroshare_nogui.file = retroshare-nogui/src/retroshare-nogui.pro -retroshare_nogui.depends = libretroshare libresapi -retroshare_nogui.target = retroshare-nogui +retroshare_nogui { + SUBDIRS += retroshare_nogui + retroshare_nogui.file = retroshare-nogui/src/retroshare-nogui.pro + retroshare_nogui.depends = libretroshare libresapi + retroshare_nogui.target = retroshare_nogui +} -plugins.file = plugins/plugins.pro -plugins.depends = retroshare_gui -plugins.target = plugins +retroshare_android_service { + SUBDIRS += retroshare_android_service + retroshare_android_service.file = retroshare-android-service/src/retroshare-android-service.pro + retroshare_android_service.depends = libretroshare libresapi + retroshare_android_service.target = retroshare_android_service +} + +retroshare_qml_app { + SUBDIRS += retroshare_qml_app + retroshare_qml_app.file = retroshare-qml-app/src/retroshare-qml-app.pro + retroshare_qml_app.depends = libretroshare retroshare_android_service + retroshare_qml_app.target = retroshare_qml_app +} + +retroshare_plugins { + SUBDIRS += plugins + plugins.file = plugins/plugins.pro + plugins.depends = retroshare_gui + plugins.target = plugins +} wikipoos { SUBDIRS += pegmarkdown diff --git a/android-prepare-toolchain.sh b/android-prepare-toolchain.sh new file mode 100755 index 000000000..a0e37a054 --- /dev/null +++ b/android-prepare-toolchain.sh @@ -0,0 +1,152 @@ +#!/bin/bash + +## You are supposed to provide the following variables according to your system setup +[ -z ${ANDROID_NDK_PATH+x} ] && export ANDROID_NDK_PATH="/opt/android-ndk/" +[ -z ${ANDROID_NDK_ARCH+x} ] && export ANDROID_NDK_ARCH="arm" +[ -z ${ANDROID_NDK_ABI_VER+x} ] && export ANDROID_NDK_ABI_VER="4.9" +[ -z ${ANDROID_PLATFORM_VER+x} ] && export ANDROID_PLATFORM_VER="18" +[ -z ${NDK_TOOLCHAIN_PATH+x} ] && export NDK_TOOLCHAIN_PATH="/home/$(whoami)/Development/android-toolchains/retroshare-android-${ANDROID_PLATFORM_VER}-${ANDROID_NDK_ARCH}-abi${ANDROID_NDK_ABI_VER}/" +[ -z ${HOST_NUM_CPU+x} ] && export HOST_NUM_CPU=4 + +runDir="$(pwd)" + +## You should not edit the following variables +if [ "${ANDROID_NDK_ARCH}" == "x86" ]; then + cArch="i686" + eABI="" +else + cArch="${ANDROID_NDK_ARCH}" + eABI="eabi" +fi +export SYSROOT="${NDK_TOOLCHAIN_PATH}/sysroot" +export PREFIX="${SYSROOT}" +export CC="${NDK_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-gcc" +export CXX="${NDK_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-g++" +export AR="${NDK_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-ar" +export RANLIB="${NDK_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-ranlib" +export ANDROID_DEV="${ANDROID_NDK_PATH}/platforms/android-${ANDROID_PLATFORM_VER}/arch-${ANDROID_NDK_ARCH}/usr" + + +## More information available at https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html +build_toolchain() +{ + rm -rf ${NDK_TOOLCHAIN_PATH} + [ "${ANDROID_NDK_ARCH}" == "x86" ] && toolchainName="${ANDROID_NDK_ARCH}-${ANDROID_NDK_ABI_VER}" || toolchainName="${ANDROID_NDK_ARCH}-linux-androideabi-${ANDROID_NDK_ABI_VER}" + ${ANDROID_NDK_PATH}/build/tools/make-standalone-toolchain.sh --ndk-dir=${ANDROID_NDK_PATH} --arch=${ANDROID_NDK_ARCH} --install-dir=${NDK_TOOLCHAIN_PATH} --platform=android-${ANDROID_PLATFORM_VER} --toolchain=${toolchainName} --verbose +} + +## More information available at retroshare://file?name=Android%20Native%20Development%20Kit%20Cookbook.pdf&size=29214468&hash=0123361c1b14366ce36118e82b90faf7c7b1b136 +build_bzlib() +{ + B_dir="bzip2-1.0.6" + rm -rf $B_dir + [ -f $B_dir.tar.gz ] || wget http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz + tar -xf $B_dir.tar.gz + cd $B_dir + sed -i "/^CC=.*/d" Makefile + sed -i "/^AR=.*/d" Makefile + sed -i "/^RANLIB=.*/d" Makefile + sed -i "/^LDFLAGS=.*/d" Makefile + sed -i "s/^all: libbz2.a bzip2 bzip2recover test/all: libbz2.a bzip2 bzip2recover/" Makefile + make -j${HOST_NUM_CPU} + make install PREFIX=${SYSROOT}/usr +# sed -i "/^CC=.*/d" Makefile-libbz2_so +# make -f Makefile-libbz2_so -j${HOST_NUM_CPU} +# cp libbz2.so.1.0.6 ${SYSROOT}/usr/lib/libbz2.so + cd .. +} + +## More information available at http://doc.qt.io/qt-5/opensslsupport.html +build_openssl() +{ + B_dir="openssl-1.0.2h" + rm -rf $B_dir + [ -f $B_dir.tar.gz ] || wget https://www.openssl.org/source/$B_dir.tar.gz + tar -xf $B_dir.tar.gz + cd $B_dir + if [ "${ANDROID_NDK_ARCH}" == "arm" ]; then + oArch="armv7" + else + oArch="${ANDROID_NDK_ARCH}" + fi +# ANDROID_NDK_ROOT="${ANDROID_NDK_PATH}" ./Configure android-${oArch} shared --prefix="${SYSROOT}/usr" --openssldir="${SYSROOT}/etc/ssl" +## We link openssl statically to avoid android silently sneaking in his own +## version of libssl.so (we noticed this because it had some missing symbol +## that made RS crash), the crash in some android version is only one of the +## possible problems the fact that android insert his own binary libssl.so pose +## non neglegible security concerns. + ANDROID_NDK_ROOT="${ANDROID_NDK_PATH}" ./Configure android-${oArch} --prefix="${SYSROOT}/usr" --openssldir="${SYSROOT}/etc/ssl" + sed -i 's/LIBNAME=$$i LIBVERSION=$(SHLIB_MAJOR).$(SHLIB_MINOR) \\/LIBNAME=$$i \\/g' Makefile + sed -i '/LIBCOMPATVERSIONS=";$(SHLIB_VERSION_HISTORY)" \\/d' Makefile + make -j${HOST_NUM_CPU} + make install +# cp *.so "${SYSROOT}/usr/lib" + cd .. +} + +build_sqlite() +{ + B_dir="sqlite-autoconf-3130000" + [ -f $B_dir.tar.gz ] || wget https://www.sqlite.org/2016/$B_dir.tar.gz + tar -xf $B_dir.tar.gz + cd $B_dir + ./configure --prefix="${SYSROOT}/usr" --host=${ANDROID_NDK_ARCH}-linux + make -j${HOST_NUM_CPU} + make install + rm -f ${SYSROOT}/usr/lib/libsqlite3.so* + ${CC} -shared -o libsqlite3.so -fPIC sqlite3.o -ldl + cp libsqlite3.so "${SYSROOT}/usr/lib" + cd .. +} + +build_sqlcipher() +{ + echo "sqlcipher not supported yet on android" + return 0 + + cd sqlcipher + ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="${SYSROOT}/usr/lib/libcrypto.a" + make -j${HOST_NUM_CPU} + make install + cd .. +} + +build_libupnp() +{ + B_dir="libupnp-1.6.20" + rm -rf $B_dir + [ -f $B_dir.tar.bz2 ] || wget https://sourceforge.net/projects/pupnp/files/pupnp/libUPnP%201.6.20/$B_dir.tar.bz2 + tar -xf $B_dir.tar.bz2 + cd $B_dir +## liupnp must be configured as static library because if not the linker will +## look for libthreadutils.so.6 at runtime that cannot be packaged on android +## as it supports only libname.so format for libraries, thus resulting in a +## crash at startup. + ./configure --enable-static --disable-shared --disable-samples --prefix="${SYSROOT}/usr" --host=${ANDROID_NDK_ARCH}-linux + make -j${HOST_NUM_CPU} + make install + cd .. +} + +build_libmicrohttpd() +{ + echo "libmicrohttpd not supported yet on android" + return 0 + + B_dir="libmicrohttpd-0.9.50" + rm -rf $B_dir + [ -f $B_dir.tar.gz ] || wget ftp://ftp.gnu.org/gnu/libmicrohttpd/$B_dir.tar.gz + tar -xf $B_dir.tar.gz + cd $B_dir + ./configure --prefix="${SYSROOT}/usr" --host=${ANDROID_NDK_ARCH}-linux + #make -e ? + make -j${HOST_NUM_CPU} + make install + cd .. +} + +build_toolchain +build_bzlib +build_openssl +build_sqlite +build_libupnp diff --git a/libresapi/src/api/ApiServerLocal.cpp b/libresapi/src/api/ApiServerLocal.cpp index 4e3f50e65..d55d1e5cc 100644 --- a/libresapi/src/api/ApiServerLocal.cpp +++ b/libresapi/src/api/ApiServerLocal.cpp @@ -21,9 +21,9 @@ namespace resource_api{ -ApiServerLocal::ApiServerLocal(ApiServer* server, QObject *parent) : +ApiServerLocal::ApiServerLocal(ApiServer* server, const QString &listenPath, QObject *parent) : QObject(parent), serverThread(this), - localListener(server) // Must have no parent to be movable to other thread + localListener(server, listenPath) // Must have no parent to be movable to other thread { localListener.moveToThread(&serverThread); serverThread.start(); @@ -31,15 +31,17 @@ ApiServerLocal::ApiServerLocal(ApiServer* server, QObject *parent) : ApiServerLocal::~ApiServerLocal() { serverThread.quit(); } -ApiLocalListener::ApiLocalListener(ApiServer *server, QObject *parent) : +ApiLocalListener::ApiLocalListener(ApiServer *server, + const QString &listenPath, + QObject *parent) : QObject(parent), mApiServer(server), mLocalServer(this) { - mLocalServer.removeServer(serverName()); + mLocalServer.removeServer(listenPath); #if QT_VERSION >= 0x050000 mLocalServer.setSocketOptions(QLocalServer::UserAccessOption); #endif connect(&mLocalServer, SIGNAL(newConnection()), this, SLOT(handleConnection())); - mLocalServer.listen(serverName()); + mLocalServer.listen(listenPath); } void ApiLocalListener::handleConnection() @@ -71,16 +73,27 @@ void ApiLocalConnectionHandler::handlePendingRequests() { if(mLocalSocket->canReadLine()) { - readPath: - reqPath = mLocalSocket->readLine().constData(); - mState = WAITING_DATA; +readPath: + QString rString(mLocalSocket->readLine()); + rString = rString.simplified(); + if (!rString.isEmpty()) + { + if(rString.startsWith("PUT", Qt::CaseInsensitive)) reqMeth = resource_api::Request::PUT; + else if (rString.startsWith("DELETE", Qt::CaseInsensitive)) reqMeth = resource_api::Request::DELETE_AA; + else reqMeth = resource_api::Request::GET; + if(rString.contains(' ')) rString = rString.split(' ')[1]; - /* Because QLocalSocket is SOCK_STREAM some clients implementations - * like the one based on QLocalSocket feel free to send the whole - * request (PATH + DATA) in a single write(), causing readyRead() - * signal being emitted only once, in that case we should continue - * processing without waiting for readyRead() being fired again, so - * we don't break here as there may be more lines to read */ + reqPath = rString.toStdString(); + mState = WAITING_DATA; + + /* Because QLocalSocket is SOCK_STREAM some clients implementations + * like the one based on QLocalSocket feel free to send the whole + * request (PATH + DATA) in a single write(), causing readyRead() + * signal being emitted only once, in that case we should continue + * processing without waiting for readyRead() being fired again, so + * we don't break here as there may be more lines to read */ + } + else break; } } case WAITING_DATA: @@ -90,6 +103,7 @@ void ApiLocalConnectionHandler::handlePendingRequests() resource_api::JsonStream reqJson; reqJson.setJsonString(std::string(mLocalSocket->readLine().constData())); resource_api::Request req(reqJson); + req.mMethod = reqMeth; req.setPath(reqPath); std::string resultString = mApiServer->handleRequest(req); mLocalSocket->write(resultString.c_str(), resultString.length()); diff --git a/libresapi/src/api/ApiServerLocal.h b/libresapi/src/api/ApiServerLocal.h index 8835f712c..1d6c87ae5 100644 --- a/libresapi/src/api/ApiServerLocal.h +++ b/libresapi/src/api/ApiServerLocal.h @@ -24,6 +24,7 @@ #include #include +#include "ApiTypes.h" #include "ApiServer.h" namespace resource_api @@ -34,16 +35,9 @@ class ApiLocalListener : public QObject Q_OBJECT public: - ApiLocalListener(ApiServer* server, QObject *parent=0); + ApiLocalListener(ApiServer* server, const QString &listenPath, QObject *parent=0); ~ApiLocalListener() { mLocalServer.close(); } - const static QString& serverName() - { - const static QString sockPath(RsAccounts::AccountDirectory() - .append("/libresapi.sock").c_str()); - return sockPath; - } - public slots: void handleConnection(); @@ -57,9 +51,23 @@ class ApiServerLocal : public QObject Q_OBJECT public: - ApiServerLocal(ApiServer* server, QObject *parent=0); + ApiServerLocal(ApiServer* server, const QString& listenPath, QObject *parent=0); ~ApiServerLocal(); + const static QString& loginServerPath() + { + const static QString sockPath(RsAccounts::ConfigDirectory() + .append("/libresapi.sock").c_str()); + return sockPath; + } + + const static QString& serverPath() + { + const static QString sockPath(RsAccounts::AccountDirectory() + .append("/libresapi.sock").c_str()); + return sockPath; + } + private: QThread serverThread; ApiLocalListener localListener; @@ -82,6 +90,7 @@ private: QLocalSocket* mLocalSocket; State mState; std::string reqPath; + resource_api::Request::Method reqMeth; }; } // namespace resource_api diff --git a/libresapi/src/api/ApiServerMHD.cpp b/libresapi/src/api/ApiServerMHD.cpp index 9522c66be..273010d8b 100644 --- a/libresapi/src/api/ApiServerMHD.cpp +++ b/libresapi/src/api/ApiServerMHD.cpp @@ -211,30 +211,7 @@ public: req.mMethod = resource_api::Request::DELETE_AA; } - std::stack stack; - std::string str; - for(std::string::reverse_iterator sit = path2.rbegin(); sit != path2.rend(); sit++) - { - if((*sit) != '/') - { - // add to front because we are traveling in reverse order - str = *sit + str; - } - else - { - if(str != "") - { - stack.push(str); - str.clear(); - } - } - } - if(str != "") - { - stack.push(str); - } - req.mPath = stack; - req.mFullPath = path2; + req.setPath(path2); std::string result = mApiServer->handleRequest(req); diff --git a/libresapi/src/api/ApiTypes.h b/libresapi/src/api/ApiTypes.h index f5ab6094b..98ad44cd9 100644 --- a/libresapi/src/api/ApiTypes.h +++ b/libresapi/src/api/ApiTypes.h @@ -193,29 +193,21 @@ public: // then each handler should pop the top element std::stack mPath; std::string mFullPath; - bool setPath(std::string reqPath) + bool setPath(const std::string &reqPath) { std::string str; - for(std::string::reverse_iterator sit = reqPath.rbegin(); sit != reqPath.rend(); sit++) + std::string::const_reverse_iterator sit; + for( sit = reqPath.rbegin(); sit != reqPath.rend(); ++sit ) { - if((*sit) != '/') + // add to front because we are traveling in reverse order + if((*sit) != '/') str = *sit + str; + else if(!str.empty()) { - // add to front because we are traveling in reverse order - str = *sit + str; - } - else - { - if(str != "") - { - mPath.push(str); - str.clear(); - } + mPath.push(str); + str.clear(); } } - if(str != "") - { - mPath.push(str); - } + if(!str.empty()) mPath.push(str); mFullPath = reqPath; return true; @@ -231,8 +223,7 @@ public: // contains data for new resources StreamBase& mStream; - // use the is*() methods to query the method type -//private: + // use the is*() methods to query the method type: enum Method { GET, PUT, DELETE_AA, EXEC};// something is wrong with DELETE, it won't compile with it Method mMethod; }; diff --git a/libresapi/src/api/RsControlModule.cpp b/libresapi/src/api/RsControlModule.cpp index 8bf8fd7ea..9430df170 100644 --- a/libresapi/src/api/RsControlModule.cpp +++ b/libresapi/src/api/RsControlModule.cpp @@ -59,7 +59,7 @@ bool RsControlModule::askForPassword(const std::string &title, const std::string { cancelled = false ; { - RsStackMutex stack(mDataMtx); // ********** LOCKED ********** + RS_STACK_MUTEX(mDataMtx); // ********** LOCKED ********** if(mFixedPassword != "") { password = mFixedPassword; @@ -78,7 +78,7 @@ bool RsControlModule::askForPassword(const std::string &title, const std::string { usleep(5*1000); - RsStackMutex stack(mDataMtx); // ********** LOCKED ********** + RS_STACK_MUTEX(mDataMtx); // ********** LOCKED ********** wait = mWantPassword; if(!wait && mPassword != "") { diff --git a/libretroshare/src/gxs/gxssecurity.cc b/libretroshare/src/gxs/gxssecurity.cc index a1de27d89..eb5f38219 100644 --- a/libretroshare/src/gxs/gxssecurity.cc +++ b/libretroshare/src/gxs/gxssecurity.cc @@ -243,7 +243,10 @@ bool GxsSecurity::checkPublicKey(const RsTlvPublicRSAKey &key) bool GxsSecurity::generateKeyPair(RsTlvPublicRSAKey& public_key,RsTlvPrivateRSAKey& private_key) { // admin keys - RSA *rsa = RSA_generate_key(2048, 65537, NULL, NULL); + BIGNUM *ebn = BN_new(); + BN_set_word(ebn, 65537); + RSA *rsa = RSA_new(); + RSA_generate_key_ex(rsa, 2048, ebn, NULL); RSA *rsa_pub = RSAPublicKey_dup(rsa); public_key.keyFlags = RSTLV_KEY_TYPE_PUBLIC_ONLY ; diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 4f7449bf4..db7066c9d 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -156,8 +156,7 @@ linux-* { QMAKE_CXXFLAGS *= -Wall -D_FILE_OFFSET_BITS=64 QMAKE_CC = $${QMAKE_CXX} - contains(CONFIG, NO_SQLCIPHER) { - DEFINES *= NO_SQLCIPHER + no_sqlcipher { PKGCONFIG *= sqlite3 } else { SQLCIPHER_OK = $$system(pkg-config --exists sqlcipher && echo yes) @@ -168,7 +167,7 @@ linux-* { DEPENDPATH += ../../../lib/ INCLUDEPATH += ../../../lib/ } else { - error("libsqlcipher is not installed and libsqlcipher.a not found. SQLCIPHER is necessary for encrypted database, to build with unencrypted database, run: qmake CONFIG+=NO_SQLCIPHER") + error("libsqlcipher is not installed and libsqlcipher.a not found. SQLCIPHER is necessary for encrypted database, to build with unencrypted database, run: qmake CONFIG+=no_sqlcipher") } } else { # Workaround for broken sqlcipher packages, e.g. Ubuntu 14.04 @@ -182,7 +181,7 @@ linux-* { # linux/bsd can use either - libupnp is more complete and packaged. #CONFIG += upnp_miniupnpc - CONFIG += upnp_libupnp + CONFIG += upnp_libupnp # Check if the systems libupnp has been Debian-patched system(grep -E 'char[[:space:]]+PublisherUrl' /usr/include/upnp/upnp.h >/dev/null 2>&1) { @@ -332,7 +331,7 @@ freebsd-* { # linux/bsd can use either - libupnp is more complete and packaged. #CONFIG += upnp_miniupnpc - CONFIG += upnp_libupnp + CONFIG += upnp_libupnp } ################################# OpenBSD ########################################## @@ -891,3 +890,25 @@ test_bitdht { # ENABLED UDP NOW. } +################################# Android ##################################### + +android-g++ { +## ifaddrs is missing on Android add them don't use the one from +## https://github.com/morristech/android-ifaddrs +## because they crash, use QNetworkInterface from Qt instead + CONFIG *= qt + QT *= network + +## Add this here and not in retroshare.pri because static library are very +## sensible to order in command line, has to be in the end of file for the +## same reason + LIBS += -L$$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/ -lssl + INCLUDEPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + DEPENDPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + PRE_TARGETDEPS += $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libssl.a + + LIBS += -L$$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/ -lcrypto + INCLUDEPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + DEPENDPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + PRE_TARGETDEPS += $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libcrypto.a +} diff --git a/libretroshare/src/pqi/pqinetwork.cc b/libretroshare/src/pqi/pqinetwork.cc index db442f380..f15692e06 100644 --- a/libretroshare/src/pqi/pqinetwork.cc +++ b/libretroshare/src/pqi/pqinetwork.cc @@ -24,9 +24,9 @@ */ #ifdef WINDOWS_SYS -#include "util/rswin.h" -#include "util/rsmemory.h" -#include +# include "util/rswin.h" +# include "util/rsmemory.h" +# include #endif // WINDOWS_SYS #include "pqi/pqinetwork.h" @@ -271,12 +271,17 @@ int inet_aton(const char *name, struct in_addr *addr) #include #ifdef WINDOWS_SYS -#include -#include -#pragma comment(lib, "IPHLPAPI.lib") -#else // WINDOWS_SYS -#include -#include +# include +# include +# pragma comment(lib, "IPHLPAPI.lib") +#elif defined(__ANDROID__) +# include +# include +# include +# include +#else // not __ANDROID__ nor WINDOWS => Linux and other unixes +# include +# include #endif // WINDOWS_SYS bool getLocalAddresses(std::list & addrs) @@ -317,7 +322,15 @@ bool getLocalAddresses(std::list & addrs) } } free(adapter_addresses); -#else // WINDOWS_SYS +#elif defined(__ANDROID__) + foreach(QHostAddress qAddr, QNetworkInterface::allAddresses()) + { + sockaddr_storage tmpAddr; + sockaddr_storage_clear(tmpAddr); + if(sockaddr_storage_ipv4_aton(tmpAddr, qAddr.toString().toStdString().c_str())) + addrs.push_back(tmpAddr); + } +#else // not WINDOWS_SYS not ANDROID => Linux and other unixes struct ifaddrs *ifsaddrs, *ifa; if(getifaddrs(&ifsaddrs) != 0) { diff --git a/libretroshare/src/pqi/sslfns.cc b/libretroshare/src/pqi/sslfns.cc index 9e5813b75..07ec804a2 100644 --- a/libretroshare/src/pqi/sslfns.cc +++ b/libretroshare/src/pqi/sslfns.cc @@ -105,11 +105,15 @@ X509_REQ *GenerateX509Req( fprintf(stderr,"GenerateX509Req: reverting to %d\n", nbits); } - rsa = RSA_generate_key(nbits, e, NULL, NULL); + rsa = RSA_new(); if ((rsa == NULL) || !EVP_PKEY_assign_RSA(pkey, rsa)) throw std::runtime_error("Couldn't generate RSA Key"); + BIGNUM *ebn = BN_new(); + BN_set_word(ebn, e); + RSA_generate_key_ex(rsa, nbits, ebn, NULL); + // open the file. FILE *out; if (NULL == (out = RsDirUtil::rs_fopen(pkey_file.c_str(), "w"))) diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index 1555be893..c7abc7bf6 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -129,15 +129,21 @@ class RsInit namespace RsAccounts { - // Directories. - std::string ConfigDirectory(); // aka Base Directory. (normally ~/.retroshare) /** + * @brief ConfigDirectory (normally ~/.retroshare) you can call this method + * even before initialisation (you can't with some other methods) + * @see RsAccountsDetail::PathBaseDirectory() + */ + std::string ConfigDirectory(); + + /** * @brief DataDirectory - * you can call this method even before initialisation (you can't with the other methods) + * you can call this method even before initialisation (you can't with some other methods) * @param check if set to true and directory does not exist, return empty string * @return path where global platform independent files are stored, like bdboot.txt or webinterface files */ std::string DataDirectory(bool check = true); + std::string PGPDirectory(); std::string AccountDirectory(); diff --git a/libretroshare/src/rsserver/p3peers.h b/libretroshare/src/rsserver/p3peers.h index 2edae4abc..9bdf4283e 100644 --- a/libretroshare/src/rsserver/p3peers.h +++ b/libretroshare/src/rsserver/p3peers.h @@ -26,11 +26,12 @@ * */ +#include #include "retroshare/rspeers.h" + class p3LinkMgr; class p3PeerMgr; class p3NetMgr; -struct sockaddr_storage; class p3Peers: public RsPeers diff --git a/libretroshare/src/rsserver/rsaccounts.cc b/libretroshare/src/rsserver/rsaccounts.cc index 09c3e7d63..d2513d452 100644 --- a/libretroshare/src/rsserver/rsaccounts.cc +++ b/libretroshare/src/rsserver/rsaccounts.cc @@ -65,14 +65,8 @@ AccountDetails::AccountDetails() return; } -RsAccountsDetail::RsAccountsDetail() -:mAccountsLocked(false), mPreferredId(""), mBaseDirectory("") -{ - mAccounts.clear(); - mUnsupportedKeys.clear(); - return; -} - +RsAccountsDetail::RsAccountsDetail() : mAccountsLocked(false), mPreferredId("") +{} bool RsAccountsDetail::loadAccounts() { @@ -214,6 +208,7 @@ std::string RsAccountsDetail::PathPGPDirectory() std::string RsAccountsDetail::PathBaseDirectory() { + if(mBaseDirectory.empty()) defaultBaseDirectory(); return mBaseDirectory; } @@ -326,8 +321,6 @@ bool RsAccountsDetail::setupBaseDirectory(std::string alt_basedir) } - - bool RsAccountsDetail::defaultBaseDirectory() { std::string basedir; @@ -339,8 +332,8 @@ bool RsAccountsDetail::defaultBaseDirectory() char *h = getenv("HOME"); if (h == NULL) { - std::cerr << "defaultBaseDirectory() Error: "; - std::cerr << "cannot determine $HOME dir" <PathBaseDirectory(); } +std::string RsAccounts::ConfigDirectory() { return RsAccountsDetail::PathBaseDirectory(); } std::string RsAccounts::DataDirectory(bool check) { return RsAccountsDetail::PathDataDirectory(check); } std::string RsAccounts::PGPDirectory() { return rsAccounts->PathPGPDirectory(); } std::string RsAccounts::AccountDirectory() { return rsAccounts->PathAccountDirectory(); } @@ -1332,3 +1325,4 @@ bool RsAccounts::GenerateSSLCertificate(const RsPgpId& pgp_id, const std::str * END OF: PUBLIC INTERFACE FUNCTIONS ********************************************************************************/ +std::string RsAccountsDetail::mBaseDirectory; diff --git a/libretroshare/src/rsserver/rsaccounts.h b/libretroshare/src/rsserver/rsaccounts.h index 7f1fa9d3a..5832dfa00 100644 --- a/libretroshare/src/rsserver/rsaccounts.h +++ b/libretroshare/src/rsserver/rsaccounts.h @@ -80,7 +80,13 @@ class RsAccountsDetail * @return path where global platform independent files are stored, like bdboot.txt or webinterface files */ static std::string PathDataDirectory(bool check = true); - std::string PathBaseDirectory(); + + /** + * @brief PathBaseDirectory + * @return path where user data is stored ( on Linux and similar + * systems it is usually something like /home/USERNAME/.retroshare ). + */ + static std::string PathBaseDirectory(); // PGP Path is only dependent on BaseDirectory. std::string PathPGPDirectory(); @@ -134,7 +140,7 @@ class RsAccountsDetail private: bool checkPreferredId(); - bool defaultBaseDirectory(); + static bool defaultBaseDirectory(); bool getAvailableAccounts(std::map &accounts, int& failing_accounts, @@ -148,7 +154,7 @@ class RsAccountsDetail std::map mAccounts; RsPeerId mPreferredId; - std::string mBaseDirectory; + static std::string mBaseDirectory; std::map > mUnsupportedKeys ; }; diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index c34ae7b94..0f8a707c7 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -508,7 +508,7 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck AuthSSL::AuthSSLInit(); AuthSSL::getAuthSSL() -> InitAuth(NULL, NULL, NULL, ""); - rsAccounts = new RsAccountsDetail() ; + rsAccounts = new RsAccountsDetail(); // first check config directories, and set bootstrap values. if(!rsAccounts->setupBaseDirectory(opt_base_dir)) diff --git a/libretroshare/src/rsserver/rsloginhandler.cc b/libretroshare/src/rsserver/rsloginhandler.cc index a62381432..b06021ce2 100644 --- a/libretroshare/src/rsserver/rsloginhandler.cc +++ b/libretroshare/src/rsserver/rsloginhandler.cc @@ -198,60 +198,9 @@ bool RsLoginHandler::tryAutoLogin(const RsPeerId& ssl_id,std::string& ssl_passwd return (status == 0); /******************** OSX KeyChain stuff *****************************/ -#else /* UNIX, but not HAS_GNOME_KEYRING or APPLE */ - - FILE* helpFile = RsDirUtil::rs_fopen(getAutologinFileName(ssl_id).c_str(), "r"); - - if(helpFile == NULL){ - std::cerr << "\nFailed to open help file\n" << std::endl; - return false; - } - - /* decrypt help */ - - int c ; - std::string passwd ; - while( (c = getc(helpFile)) != EOF ) - passwd += (char)c ; - - const int DAT_LEN = passwd.length(); - const int KEY_DAT_LEN = RsInitConfig::load_cert.length(); - unsigned char* key_data = (unsigned char*)RsInitConfig::load_cert.c_str(); - unsigned char* indata = new unsigned char[DAT_LEN]; - unsigned char* outdata = new unsigned char[DAT_LEN]; - - for(int i=0;i -#ifndef WIN32 -#include + +#ifdef __ANDROID__ +# include +#endif + +#ifdef WIN32 +# include +#elif defined(__ANDROID__) && (__ANDROID_API__ < 21) +# include +# define statvfs64 statfs +# warning statvfs64 is not supported with android platform < 21 falling back to statfs that is untested (may misbehave) #else -#include +# include #endif #define DELAY_BETWEEN_CHECKS 2 diff --git a/openpgpsdk/src/openpgpsdk.pro b/openpgpsdk/src/openpgpsdk.pro index f624619fc..d158cdcb1 100644 --- a/openpgpsdk/src/openpgpsdk.pro +++ b/openpgpsdk/src/openpgpsdk.pro @@ -119,3 +119,21 @@ SOURCES += openpgpsdk/accumulate.c \ win32{ SOURCES += openpgpsdk/opsstring.c } + +################################# Android ##################################### + +android-g++ { + +## Add this here and not in retroshare.pri because static library are very +## sensible to order in command line + LIBS += -L$$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/ -lssl + INCLUDEPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + DEPENDPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + PRE_TARGETDEPS += $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libssl.a + + LIBS += -L$$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/ -lcrypto + INCLUDEPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + DEPENDPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include + PRE_TARGETDEPS += $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libcrypto.a + +} diff --git a/openpgpsdk/src/openpgpsdk/readerwriter.c b/openpgpsdk/src/openpgpsdk/readerwriter.c index a80fede82..f3a16c364 100644 --- a/openpgpsdk/src/openpgpsdk/readerwriter.c +++ b/openpgpsdk/src/openpgpsdk/readerwriter.c @@ -460,7 +460,12 @@ callback_cmd_get_secret_key(const ops_parser_content_t *content_,ops_parse_cb_in char *ops_get_passphrase(void) { +#ifndef __ANDROID__ return ops_malloc_passphrase(getpass("Passphrase: ")); +#else // __ANDROID__ + return ops_malloc_passphrase("getpass not supported on android"); +#warning "getpass not supported on android" +#endif // __ANDROID__ } char *ops_malloc_passphrase(char *pp) diff --git a/retroshare-android-service/src/retroshare-android-service.pro b/retroshare-android-service/src/retroshare-android-service.pro new file mode 100644 index 000000000..c2caa12a0 --- /dev/null +++ b/retroshare-android-service/src/retroshare-android-service.pro @@ -0,0 +1,24 @@ +!include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") + +TARGET = retroshare-android-service + +QT += core network +QT -= gui + +CONFIG += c++11 +CONFIG += dll + +android-g++:TEMPLATE = lib +!android-g++:TEMPLATE = app + +SOURCES += service.cpp + +DEPENDPATH *= ../../libresapi/src +INCLUDEPATH *= ../../libresapi/src +PRE_TARGETDEPS *= ../../libresapi/src/lib/libresapi.a +LIBS *= ../../libresapi/src/lib/libresapi.a + +DEPENDPATH *= ../../libretroshare/src +INCLUDEPATH *= ../../libretroshare/src +PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a +LIBS *= ../../libretroshare/src/lib/libretroshare.a diff --git a/retroshare-android-service/src/service.cpp b/retroshare-android-service/src/service.cpp new file mode 100644 index 000000000..9359af904 --- /dev/null +++ b/retroshare-android-service/src/service.cpp @@ -0,0 +1,57 @@ +/* + * RetroShare Android Service + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include + +#ifdef __ANDROID__ +# include +#endif + +#include "retroshare/rsinit.h" +#include "api/ApiServer.h" +#include "api/ApiServerLocal.h" +#include "api/RsControlModule.h" + +using namespace resource_api; + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + ApiServer api; + RsControlModule ctrl_mod(argc, argv, api.getStateTokenServer(), &api, true); + api.addResourceHandler("control", dynamic_cast(&ctrl_mod), &resource_api::RsControlModule::handleRequest); + + QString sockPath = QString::fromStdString(RsAccounts::ConfigDirectory()); + sockPath.append("/libresapi.sock"); + qDebug() << "Listening on:" << sockPath; + ApiServerLocal apiServerLocal(&api, sockPath); (void) apiServerLocal; + +#ifdef __ANDROID__ + qDebug() << "Is service.cpp running as a service?" << QtAndroid::androidService().isValid(); + qDebug() << "Is service.cpp running as an activity?" << QtAndroid::androidActivity().isValid(); +#endif + + while (!ctrl_mod.processShouldExit()) + { + a.processEvents(); + usleep(20000); + } + + return 0; +} diff --git a/retroshare-gui/src/gui/settings/ServerPage.h b/retroshare-gui/src/gui/settings/ServerPage.h index 686c1b3d9..afd7809fc 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.h +++ b/retroshare-gui/src/gui/settings/ServerPage.h @@ -26,11 +26,11 @@ #include "ui_ServerPage.h" #include "RsAutoUpdatePage.h" #include +#include class QNetworkReply; class QNetworkAccessManager; class BanListPeer; -struct sockaddr_storage; class ServerPage: public ConfigPage { diff --git a/retroshare-gui/src/gui/settings/WebuiPage.cpp b/retroshare-gui/src/gui/settings/WebuiPage.cpp index cb7121746..f8923a174 100644 --- a/retroshare-gui/src/gui/settings/WebuiPage.cpp +++ b/retroshare-gui/src/gui/settings/WebuiPage.cpp @@ -100,7 +100,7 @@ QString WebuiPage::helpText() const // TODO: LIBRESAPI_LOCAL_SERVER Move in appropriate place #ifdef LIBRESAPI_LOCAL_SERVER - apiServerLocal = new resource_api::ApiServerLocal(apiServer); + apiServerLocal = new resource_api::ApiServerLocal(apiServer, resource_api::ApiServerLocal::serverPath()); #endif return ok; } diff --git a/retroshare-gui/src/util/RsNetUtil.h b/retroshare-gui/src/util/RsNetUtil.h index dded451a8..8fa897425 100644 --- a/retroshare-gui/src/util/RsNetUtil.h +++ b/retroshare-gui/src/util/RsNetUtil.h @@ -24,6 +24,7 @@ #include #include +#include class RsNetUtil { diff --git a/retroshare-qml-app/src/android/.gitignore b/retroshare-qml-app/src/android/.gitignore new file mode 100644 index 000000000..e8dd8d7de --- /dev/null +++ b/retroshare-qml-app/src/android/.gitignore @@ -0,0 +1,5 @@ +.build +.gradle +.idea +captures +local.properties diff --git a/retroshare-qml-app/src/android/AndroidManifest.xml b/retroshare-qml-app/src/android/AndroidManifest.xml new file mode 100644 index 000000000..64b2bca6a --- /dev/null +++ b/retroshare-qml-app/src/android/AndroidManifest.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/retroshare-qml-app/src/android/android.iml b/retroshare-qml-app/src/android/android.iml new file mode 100644 index 000000000..4dde2c8b9 --- /dev/null +++ b/retroshare-qml-app/src/android/android.iml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/retroshare-qml-app/src/android/build.gradle b/retroshare-qml-app/src/android/build.gradle new file mode 100644 index 000000000..ef416b0b8 --- /dev/null +++ b/retroshare-qml-app/src/android/build.gradle @@ -0,0 +1,57 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.1.0' + } +} + +allprojects { + repositories { + jcenter() + } +} + +apply plugin: 'com.android.application' + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + buildToolsVersion androidBuildToolsVersion + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + lintOptions { + abortOnError false + } +} diff --git a/retroshare-qml-app/src/android/captures/org.retroshare.android.qml_app_2016.08.22_16.56.txt b/retroshare-qml-app/src/android/captures/org.retroshare.android.qml_app_2016.08.22_16.56.txt new file mode 100644 index 000000000..f55ee311f --- /dev/null +++ b/retroshare-qml-app/src/android/captures/org.retroshare.android.qml_app_2016.08.22_16.56.txt @@ -0,0 +1,36 @@ +Activity Resolver Table: + Non-Data Actions: + android.intent.action.MAIN: + 42420338 org.retroshare.android.qml_app/.RetroShareQmlActivity filter 42424830 + +Receiver Resolver Table: + Non-Data Actions: + android.intent.action.BOOT_COMPLETED: + 4245c230 org.retroshare.android.qml_app/.BootCompletedReceiver filter 4245c360 + +Packages: + Package [org.retroshare.android.qml_app] (4289b2f8): + userId=10168 gids=[3003, 1015, 1028] + pkg=Package{42244e18 org.retroshare.android.qml_app} + codePath=/data/app/org.retroshare.android.qml_app-2.apk + resourcePath=/data/app/org.retroshare.android.qml_app-2.apk + nativeLibraryPath=/data/app-lib/org.retroshare.android.qml_app-2 + versionCode=1 targetSdk=18 + versionName=1.0 + applicationInfo=ApplicationInfo{4232af50 org.retroshare.android.qml_app} + flags=[ DEBUGGABLE HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] + dataDir=/data/data/org.retroshare.android.qml_app + supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity] + timeStamp=2016-08-22 16:53:12 + firstInstallTime=2016-08-22 16:47:19 + lastUpdateTime=2016-08-22 16:53:30 + signatures=PackageSignatures{4233b1e0 [4265b018]} + permissionsFixed=true haveGids=true installStatus=1 + pkgFlags=[ DEBUGGABLE HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] + User 0: installed=true stopped=false notLaunched=false enabled=0 + grantedPermissions: + android.permission.READ_EXTERNAL_STORAGE + android.permission.RECEIVE_BOOT_COMPLETED + android.permission.ACCESS_NETWORK_STATE + android.permission.WRITE_EXTERNAL_STORAGE + android.permission.INTERNET diff --git a/retroshare-qml-app/src/android/gradle.properties b/retroshare-qml-app/src/android/gradle.properties new file mode 100644 index 000000000..0675a3cf4 --- /dev/null +++ b/retroshare-qml-app/src/android/gradle.properties @@ -0,0 +1,9 @@ +## This file is automatically generated by QtCreator. +# +# This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. + +androidBuildToolsVersion=24.0.1 +androidCompileSdkVersion=18 +buildDir=.build +qt5AndroidDir=/opt/Qt5.7.0/5.7/android_armv7/src/android/java diff --git a/retroshare-qml-app/src/android/gradle/wrapper/gradle-wrapper.jar b/retroshare-qml-app/src/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..8c0fb64a8 Binary files /dev/null and b/retroshare-qml-app/src/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/retroshare-qml-app/src/android/gradle/wrapper/gradle-wrapper.properties b/retroshare-qml-app/src/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..0c71e760d --- /dev/null +++ b/retroshare-qml-app/src/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/retroshare-qml-app/src/android/gradlew b/retroshare-qml-app/src/android/gradlew new file mode 100755 index 000000000..91a7e269e --- /dev/null +++ b/retroshare-qml-app/src/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/retroshare-qml-app/src/android/gradlew.bat b/retroshare-qml-app/src/android/gradlew.bat new file mode 100644 index 000000000..aec99730b --- /dev/null +++ b/retroshare-qml-app/src/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/retroshare-qml-app/src/android/res/values/libs.xml b/retroshare-qml-app/src/android/res/values/libs.xml new file mode 100644 index 000000000..43296f2e7 --- /dev/null +++ b/retroshare-qml-app/src/android/res/values/libs.xml @@ -0,0 +1,25 @@ + + + + https://download.qt.io/ministro/android/qt5/qt-5.7 + + + + + + + + + + + + + + + + + + + + diff --git a/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/AppUpdatedReceiver.java b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/AppUpdatedReceiver.java new file mode 100644 index 000000000..d9f87f650 --- /dev/null +++ b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/AppUpdatedReceiver.java @@ -0,0 +1,36 @@ +/* + * RetroShare Android Service + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.retroshare.android.qml_app; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class AppUpdatedReceiver extends BroadcastReceiver +{ + @Override + public void onReceive(Context context, Intent intent) + { + Log.i("AppUpdatedReceiver", "onReceive() Restarting RetroShare Android Service After Update"); + Intent myIntent = new Intent(context, RetroShareAndroidService.class); + context.stopService(myIntent); + context.startService(myIntent); + } +} diff --git a/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/BootCompletedReceiver.java b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/BootCompletedReceiver.java new file mode 100644 index 000000000..22324158d --- /dev/null +++ b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/BootCompletedReceiver.java @@ -0,0 +1,33 @@ +/* + * RetroShare Android Service + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.retroshare.android.qml_app; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class BootCompletedReceiver extends BroadcastReceiver +{ + @Override + public void onReceive(Context context, Intent intent) + { + Intent myIntent = new Intent(context, RetroShareAndroidService.class); + context.startService(myIntent); + } +} diff --git a/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/RetroShareAndroidService.java b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/RetroShareAndroidService.java new file mode 100644 index 000000000..f7c21a7d8 --- /dev/null +++ b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/RetroShareAndroidService.java @@ -0,0 +1,23 @@ +/* + * RetroShare Android Service + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.retroshare.android.qml_app; + +import org.qtproject.qt5.android.bindings.QtService; + +public class RetroShareAndroidService extends QtService {} diff --git a/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/RetroShareQmlActivity.java b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/RetroShareQmlActivity.java new file mode 100644 index 000000000..faabc266b --- /dev/null +++ b/retroshare-qml-app/src/android/src/org/retroshare/android/qml_app/RetroShareQmlActivity.java @@ -0,0 +1,53 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.retroshare.android.qml_app; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import org.qtproject.qt5.android.bindings.QtActivity; + +public class RetroShareQmlActivity extends QtActivity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + if (!isMyServiceRunning(RetroShareAndroidService.class)) + { + Log.i("RetroShareQmlActivity", "onCreate(): RetroShareAndroidService is not running, let's start it by Intent"); + Intent rsIntent = new Intent(this, RetroShareAndroidService.class); + startService(rsIntent); + } + else Log.v("RetroShareQmlActivity", "onCreate(): RetroShareAndroidService already running"); + + super.onCreate(savedInstanceState); + } + + private boolean isMyServiceRunning(Class serviceClass) + { + ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) + if (serviceClass.getName().equals(service.service.getClassName())) + return true; + return false; + } +} diff --git a/retroshare-qml-app/src/debugutils.h b/retroshare-qml-app/src/debugutils.h new file mode 100644 index 000000000..a9a2f39ff --- /dev/null +++ b/retroshare-qml-app/src/debugutils.h @@ -0,0 +1,15 @@ +#ifndef DEBUGUTILS_H +#define DEBUGUTILS_H + +#include + +//To switch between debugging and normal mode, un-/comment next line +#define DEBUGGING +#ifdef DEBUGGING + #define myDebug(line) qDebug() << "| FILE:" << __FILE__ << " | LINE_NUMBER:"\ + << __LINE__ << " | FUNCTION:" << __FUNCTION__ << " | CONTENT:" << line +#else + #define myDebug(line) +#endif + +#endif // DEBUGUTILS_H diff --git a/retroshare-qml-app/src/deployment.pri b/retroshare-qml-app/src/deployment.pri new file mode 100644 index 000000000..265ce71f3 --- /dev/null +++ b/retroshare-qml-app/src/deployment.pri @@ -0,0 +1,13 @@ +unix:!android { + isEmpty(target.path) { + qnx { + target.path = /tmp/$${TARGET}/bin + } else { + target.path = /opt/$${TARGET}/bin + } + export(target.path) + } + INSTALLS += target +} + +export(INSTALLS) diff --git a/retroshare-qml-app/src/libresapilocalclient.cpp b/retroshare-qml-app/src/libresapilocalclient.cpp new file mode 100644 index 000000000..34831753d --- /dev/null +++ b/retroshare-qml-app/src/libresapilocalclient.cpp @@ -0,0 +1,84 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * Copyright (C) 2016 Manu Pineda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "libresapilocalclient.h" +#include "debugutils.h" +#include + + +void LibresapiLocalClient::openConnection(QString socketPath) +{ + connect(& mLocalSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), + this, SLOT(socketError(QLocalSocket::LocalSocketError))); + connect(& mLocalSocket, SIGNAL(readyRead()), + this, SLOT(read())); + mLocalSocket.connectToServer(socketPath); +} + +int LibresapiLocalClient::request(const QString & path, const QString & jsonData) +{ + qDebug() << "LibresapiLocalClient::request()" << path << jsonData; + QByteArray data; + data.append(path); data.append('\n'); + data.append(jsonData); data.append('\n'); + mLocalSocket.write(data); + + return 1; +} + +void LibresapiLocalClient::socketError(QLocalSocket::LocalSocketError) +{ + myDebug("error!!!!\n" + mLocalSocket.errorString()); +} + +void LibresapiLocalClient::read() +{ + receivedBytes = mLocalSocket.readLine(); + + qDebug() << receivedBytes; + + if(parseResponse()) // pensar en fer un buffer per parsejar, per evitar errors. + emit goodResponseReceived(QString(receivedBytes)); + else + { + QString errMess = "The message was not understood!\n" + "It should be a JSON formatted text file\n" + "Its contents were:\n" + receivedBytes; + myDebug(errMess.replace(QChar('\n'), QChar::LineSeparator)); + } +} + +bool LibresapiLocalClient::parseResponse() +{ + QJsonParseError error; + json = QJsonDocument::fromJson(receivedBytes, &error); + myDebug(QString(json.toJson()).replace(QChar('\n'), QChar::LineSeparator)); + + if(error.error == QJsonParseError::NoError){ + return true; + } + myDebug(error.errorString()); + + return false; +} + +const QJsonDocument & LibresapiLocalClient::getJson() +{ + return json; +} diff --git a/retroshare-qml-app/src/libresapilocalclient.h b/retroshare-qml-app/src/libresapilocalclient.h new file mode 100644 index 000000000..cfbfb6dec --- /dev/null +++ b/retroshare-qml-app/src/libresapilocalclient.h @@ -0,0 +1,56 @@ +/* + * libresapi local socket client + * Copyright (C) 2016 Gioacchino Mazzurco + * Copyright (C) 2016 Manu Pineda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef LIBRESAPILOCALCLIENT_H +#define LIBRESAPILOCALCLIENT_H + +#include +#include +#include +#include + +class LibresapiLocalClient : public QObject +{ + Q_OBJECT + +public: + LibresapiLocalClient() : mLocalSocket(this) {} + + // potser abstreure el següent amb QUrl urlPath (path) i amb QJson jsonData. + Q_INVOKABLE int request(const QString & path, const QString & jsonData); + const QJsonDocument & getJson(); + Q_INVOKABLE void openConnection(QString socketPath); + +private: + QLocalSocket mLocalSocket; + QByteArray receivedBytes; + QJsonDocument json; + //QVector responses; + + bool parseResponse(); //std::string msg); + +private slots: + void socketError(QLocalSocket::LocalSocketError error); + void read(); + +signals: + void goodResponseReceived(const QString & msg);//, int requestId); +}; + +#endif // LIBRESAPILOCALCLIENT_H diff --git a/retroshare-qml-app/src/main.cpp b/retroshare-qml-app/src/main.cpp new file mode 100644 index 000000000..2f03478c9 --- /dev/null +++ b/retroshare-qml-app/src/main.cpp @@ -0,0 +1,61 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#ifdef __ANDROID__ +# include +#endif + +#include +#include + +#include "libresapilocalclient.h" +#include "retroshare/rsinit.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + qmlRegisterType( + "org.retroshare.qml_components.LibresapiLocalClient", 1, 0, + "LibresapiLocalClient"); + + QString sockPath = QString::fromStdString(RsAccounts::ConfigDirectory()); + sockPath.append("/libresapi.sock"); + + engine.rootContext()->setContextProperty("apiSocketPath", sockPath); + engine.load(QUrl(QLatin1String("qrc:/qml/main.qml"))); + + QFileInfo fileInfo(sockPath); + +#ifdef __ANDROID__ + qDebug() << "Is main.cpp running as a service?" << QtAndroid::androidService().isValid(); + qDebug() << "Is main.cpp running as an activity?" << QtAndroid::androidActivity().isValid(); +#endif + + qDebug() << "QML APP:" << sockPath << fileInfo.exists() << fileInfo.lastModified().toString(); + + return app.exec(); +} diff --git a/retroshare-qml-app/src/qml.qrc b/retroshare-qml-app/src/qml.qrc new file mode 100644 index 000000000..54ce78395 --- /dev/null +++ b/retroshare-qml-app/src/qml.qrc @@ -0,0 +1,26 @@ + + + qml/main.qml + qml/icons/star-2-128.png + qml/icons/settings-4-128.png + qml/icons/email-128.png + qml/icons/contacts-128.png + qml/PostedMsgDelegate.qml + qml/GxsService.qml + qml/GxsIdDelegate.qml + qml/GxsGroupDelegate.qml + qml/ForumMsgDelegate.qml + qml/ContactBox.qml + qml/ChannelMsgDelegate.qml + qml/ChannelGroupDelegate.qml + qml/ApplicationBar.qml + qml/AppButton.qml + qml/Locations.qml + qml/jsonpath.js + qml/JSONListModel.qml + qml/Contacts.qml + qml/AddTrustedNode.qml + qml/RsLoginPassView.qml + qml/TrustedNodesView.qml + + diff --git a/retroshare-qml-app/src/qml/AddTrustedNode.qml b/retroshare-qml-app/src/qml/AddTrustedNode.qml new file mode 100644 index 000000000..6af454f8d --- /dev/null +++ b/retroshare-qml-app/src/qml/AddTrustedNode.qml @@ -0,0 +1,78 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import org.retroshare.qml_components.LibresapiLocalClient 1.0 + +Item +{ + function refreshData() { rsApi.request("/peers/self/certificate/", "") } + + Component.onCompleted: + { + rsApi.openConnection(apiSocketPath) + refreshData() + } + onFocusChanged: focus && refreshData() + + LibresapiLocalClient + { + id: rsApi + onGoodResponseReceived: + { + var jsonData = JSON.parse(msg) + if(jsonData && jsonData.data && jsonData.data.cert_string) + myKeyField.text = jsonData.data.cert_string + } + } + + ColumnLayout + { + anchors.fill: parent + + Button + { + id: bottomButton + text: "Add trusted node" + onClicked: + { + console.log("retroshare addtrusted: ", otherKeyField.text) + var jsonData = + { + cert_string: otherKeyField.text, + flags: + { + allow_direct_download: true, + allow_push: false, + require_whitelist: false, + } + } + console.log("retroshare addtrusted jsonData: ", JSON.stringify(jsonData)) + //rsApi.request("/peers/examine_cert/", JSON.stringify({ cert_string: otherKeyField.text })) + rsApi.request("PUT /peers", JSON.stringify(jsonData)) + } + } + + Button + { + text: "Copy" + onClicked: + { + myKeyField.selectAll() + myKeyField.copy() + } + } + + Button + { + text: "Paste" + onClicked: + { + otherKeyField.selectAll() + otherKeyField.paste() + } + } + + TextField { id: myKeyField } + TextField { id: otherKeyField } + } +} diff --git a/retroshare-qml-app/src/qml/AppButton.qml b/retroshare-qml-app/src/qml/AppButton.qml new file mode 100644 index 000000000..6b3f9a881 --- /dev/null +++ b/retroshare-qml-app/src/qml/AppButton.qml @@ -0,0 +1,30 @@ +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import "." + +Rectangle { + id: appButton + property alias icon: appIcon.source + + signal buttonClicked + + width: parent.height + height: parent.height + color: "#00000000" + + Image { + id: appIcon + anchors.centerIn: parent + width: 25 + height: 25 + } + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + appButton.buttonClicked() + } + } +} + + diff --git a/retroshare-qml-app/src/qml/ApplicationBar.qml b/retroshare-qml-app/src/qml/ApplicationBar.qml new file mode 100644 index 000000000..3ec44349d --- /dev/null +++ b/retroshare-qml-app/src/qml/ApplicationBar.qml @@ -0,0 +1,37 @@ +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import "." + +Rectangle { + id: status + anchors.fill: parent + color: "#336699" //"#FF7733" + height: 50 + + default property alias contents: placeholder.children + + RowLayout { + id: placeholder + spacing: 0 + width: 200 + height: parent.height + anchors.top: parent.top + anchors.left: parent.left + + } + + ContactBox { + + width: 200 + height: parent.height + anchors.top: parent.top + anchors.right: parent.right + + icon: "icons/contacts-128.png" + name: "Vade Retro" + status: "Away" + } +} + + + diff --git a/retroshare-qml-app/src/qml/ChannelGroupDelegate.qml b/retroshare-qml-app/src/qml/ChannelGroupDelegate.qml new file mode 100644 index 000000000..a92cab36c --- /dev/null +++ b/retroshare-qml-app/src/qml/ChannelGroupDelegate.qml @@ -0,0 +1,33 @@ +import QtQuick 2.2 +import "." + +Item { + id: item + width: parent.width + height: 50 + + Column { + Text { text: '' + model.GroupName + '' } + Text { text: GroupId } + } + + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + item.ListView.view.currentIndex = index + channelMsgModel.updateEntries(model.GroupId) + console.log("Clicked on Channel GroupId: " + model.GroupId) + } + } + + Rectangle { + width: parent.width + height: 1 + color: "#AAAAAA" + anchors.left: parent.left + anchors.top: parent.bottom + } +} + + diff --git a/retroshare-qml-app/src/qml/ChannelMsgDelegate.qml b/retroshare-qml-app/src/qml/ChannelMsgDelegate.qml new file mode 100644 index 000000000..333d0fab0 --- /dev/null +++ b/retroshare-qml-app/src/qml/ChannelMsgDelegate.qml @@ -0,0 +1,42 @@ +import QtQuick 2.2 +import "." + +Item { + id: msgDelegate + + width: parent.width + height: 150 + + Column { + Text { text: 'MsgId: ' + AuthorId } + Text { text: 'AuthorId: ' + AuthorId } + Row { + Text { text: 'Name: ' + MsgName } + Text { text: ' PublishTs: ' + PublishTs } + } + Text { text: 'Msg: ' + Msg } + Row { + Text { text: 'NumberFiles: ' + NumberFiles } + Text { text: ' TotalFileSize: ' + TotalFileSize } + } + + Text { text: 'FileNames: ' + FileNames } + Text { text: 'FileSizes: ' + FileSizes } + Text { text: 'FileHashes: ' + FileHashes } + Row { + Text { text: 'HaveVoted: ' + HaveVoted } + Text { text: ' UpVotes: ' + UpVotes } + Text { text: ' DownVotes: ' + DownVotes } + Text { text: ' Comments: ' + Comments } + } + } + + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + item.ListView.view.currentIndex = index + } + } +} + diff --git a/retroshare-qml-app/src/qml/ContactBox.qml b/retroshare-qml-app/src/qml/ContactBox.qml new file mode 100644 index 000000000..9790ed677 --- /dev/null +++ b/retroshare-qml-app/src/qml/ContactBox.qml @@ -0,0 +1,61 @@ +import QtQuick 2.2 +import "." + +Item { + + property alias icon: contactIcon.source + property alias name: contactName.text + property alias status: contactStatus.text + + Rectangle { + + anchors.fill: parent + color: "#00000000" + + Image { + id: contactIcon + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + width: 40 + height: 40 + source: "icons/contacts-128.png" + } + + Rectangle { + height: contactIcon.height + anchors.verticalCenter: parent.verticalCenter + anchors.left: contactIcon.right + color: parent.color + + Text { + id: contactName + text: "Username" + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.bottom: contactStatus.top + anchors.bottomMargin: 2 + + horizontalAlignment: Text.AlignHCenter + font.pointSize: 14 + font.bold: false + color: "#FFFFFF" + } + + Text { + id: contactStatus + text: "Hello world!" + anchors.left: parent.right + anchors.leftMargin: 10 + anchors.bottom: parent.bottom + anchors.bottomMargin: 1 + + horizontalAlignment: Text.AlignHCenter + font.pointSize: 10 + font.bold: false + color: "#FFFFFF" + } + } + } +} + + diff --git a/retroshare-qml-app/src/qml/Contacts.qml b/retroshare-qml-app/src/qml/Contacts.qml new file mode 100644 index 000000000..63364d516 --- /dev/null +++ b/retroshare-qml-app/src/qml/Contacts.qml @@ -0,0 +1,52 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import org.retroshare.qml_components.LibresapiLocalClient 1.0 + +Item +{ + function refreshData() { rsApi.request("/identity/*/", "") } + + onFocusChanged: focus && refreshData() + + LibresapiLocalClient + { + id: rsApi + onGoodResponseReceived: locationsModel.json = msg + Component.onCompleted: { openConnection(apiSocketPath) } + } + + JSONListModel + { + id: locationsModel + query: "$.data[*]" + } + + ListView + { + id: locationsListView + width: parent.width + height: 300 + model: locationsModel.model + delegate: Text { text: model.name } + } + + Text { text: "Contacts View"; anchors.bottom: parent.bottom } +} diff --git a/retroshare-qml-app/src/qml/ForumMsgDelegate.qml b/retroshare-qml-app/src/qml/ForumMsgDelegate.qml new file mode 100644 index 000000000..bdb8d9138 --- /dev/null +++ b/retroshare-qml-app/src/qml/ForumMsgDelegate.qml @@ -0,0 +1,41 @@ +import QtQuick 2.2 +import "." + +Item { + id: msgDelegate + + width: parent.width + height: col.height + + Column { + id: col + Text { text: 'MsgId: ' + AuthorId } + Text { text: 'AuthorId: ' + AuthorId } + Row { + Text { text: 'Name: ' + MsgName } + Text { text: ' PublishTs: ' + PublishTs } + } + Text { + wrapMode: Text.Wrap + text: 'Msg: ' + Msg + } + } + + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + item.ListView.view.currentIndex = index + } + } + + Rectangle { + width: parent.width + height: 2 + color: "#AAAAAA" + anchors.left: parent.left + anchors.top: parent.bottom + } + +} + diff --git a/retroshare-qml-app/src/qml/GxsGroupDelegate.qml b/retroshare-qml-app/src/qml/GxsGroupDelegate.qml new file mode 100644 index 000000000..0a0b14f73 --- /dev/null +++ b/retroshare-qml-app/src/qml/GxsGroupDelegate.qml @@ -0,0 +1,26 @@ +import QtQuick 2.2 +import "." + +Item { + id: item + property var msgModel: {} + + width: parent.width + height: 50 + + Column { + Text { text: 'Name: ' + model.GroupName } + Text { text: 'Number: ' + GroupId } + } + + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + item.ListView.view.currentIndex = index + item.msgModel.updateEntries(model.GroupId) + } + } +} + + diff --git a/retroshare-qml-app/src/qml/GxsIdDelegate.qml b/retroshare-qml-app/src/qml/GxsIdDelegate.qml new file mode 100644 index 000000000..8fd7d738d --- /dev/null +++ b/retroshare-qml-app/src/qml/GxsIdDelegate.qml @@ -0,0 +1,33 @@ +import QtQuick 2.2 +import "." + +Item { + id: item + width: parent.width + height: 50 + + Column { + Text { text: '' + model.GroupName + '' } + Text { text: GroupId } + } + + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + item.ListView.view.currentIndex = index + //channelMsgModel.updateEntries(model.GroupId) + //console.log("Clicked on Channel GroupId: " + model.GroupId) + } + } + + Rectangle { + width: parent.width + height: 1 + color: "#AAAAAA" + anchors.left: parent.left + anchors.top: parent.bottom + } +} + + diff --git a/retroshare-qml-app/src/qml/GxsService.qml b/retroshare-qml-app/src/qml/GxsService.qml new file mode 100644 index 000000000..7c2a4798d --- /dev/null +++ b/retroshare-qml-app/src/qml/GxsService.qml @@ -0,0 +1,119 @@ +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.1 +import "." + + +Item { + id: gxsService + + property alias icon: sideIcon.source + property alias title: sideTitle.text + + property alias groupDelegate: sideList.delegate + property alias groupModel: sideList.model + + property alias msgDelegate: mainList.delegate + property alias msgModel: mainList.model + + RowLayout { + spacing: 0 + anchors.fill: parent + + Rectangle { + id: sideBar + width: 200 + Layout.fillHeight: true + + Rectangle { + id: sideHeader + width: parent.width + height: 30 + + Text { + id: sideTitle + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 10 + width: 20 + height: 20 + text: "Service" + color: "#333333" + } + + Image { + id: sideIcon + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 10 + width: 20 + height: 20 + source: "icons/contacts-128.png" + } + } + + Rectangle { + id: sideListBox + width: parent.width + + anchors.top: sideHeader.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + ListView { + id: sideList + anchors.fill: parent + + delegate: GxsGroupDelegate { + msgModel: mainList.model + } + + // section. + section.property: "SubscribeStatus" + section.criteria: ViewSection.FullString + section.delegate: Rectangle { + width: sideListBox.width + height: childrenRect.height + color: "blue" + + Text { + text: section + font.bold: true + font.pixelSize: 20 + } + } + + clip: true + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + focus: true + + onCurrentItemChanged: { + console.log("SideBar Item Changed on " + gxsService.title) + } + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + + ListView { + id: mainList + anchors.fill: parent + + clip: true + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + focus: true + onCurrentItemChanged: { + console.log("item changed") + } + } + + } + } +} + + diff --git a/retroshare-qml-app/src/qml/JSONListModel.qml b/retroshare-qml-app/src/qml/JSONListModel.qml new file mode 100644 index 000000000..cee905d75 --- /dev/null +++ b/retroshare-qml-app/src/qml/JSONListModel.qml @@ -0,0 +1,51 @@ +/* JSONListModel - a QML ListModel with JSON and JSONPath support + * + * Copyright (c) 2012 Romain Pokrzywka (KDAB) (romain@kdab.com) + * Licensed under the MIT licence (http://opensource.org/licenses/mit-license.php) + */ + +import QtQuick 2.0 +import "jsonpath.js" as JSONPath + +Item { + property string source: "" + property string json: "" + property string query: "" + + property ListModel model : ListModel { id: jsonModel } + property alias count: jsonModel.count + + onSourceChanged: { + var xhr = new XMLHttpRequest; + xhr.open("GET", source); + xhr.onreadystatechange = function() { + if (xhr.readyState == XMLHttpRequest.DONE) + json = xhr.responseText; + } + xhr.send(); + } + + onJsonChanged: updateJSONModel() + onQueryChanged: updateJSONModel() + + function updateJSONModel() { + jsonModel.clear(); + + if ( json === "" ) + return; + + var objectArray = parseJSONString(json, query); + for ( var key in objectArray ) { + var jo = objectArray[key]; + jsonModel.append( jo ); + } + } + + function parseJSONString(jsonString, jsonPathQuery) { + var objectArray = JSON.parse(jsonString); + if ( jsonPathQuery !== "" ) + objectArray = JSONPath.jsonPath(objectArray, jsonPathQuery); + + return objectArray; + } +} diff --git a/retroshare-qml-app/src/qml/Locations.qml b/retroshare-qml-app/src/qml/Locations.qml new file mode 100644 index 000000000..f81ae4c42 --- /dev/null +++ b/retroshare-qml-app/src/qml/Locations.qml @@ -0,0 +1,192 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import org.retroshare.qml_components.LibresapiLocalClient 1.0 + +Item +{ + id: locationView + state: "selectLocation" + property var qParent + property bool attemptLogin: false + property string password + property string sslid + + states: + [ + State + { + name: "selectLocation" + PropertyChanges { target: locationsListView; visible: true } + PropertyChanges { target: bottomButton; visible: true } + PropertyChanges { target: loginView; visible: false } + }, + State + { + name: "createLocation" + PropertyChanges { target: locationsListView; visible: false } + PropertyChanges { target: bottomButton; visible: false } + PropertyChanges + { + target: loginView + visible: true + buttonText: "Save" + onSubmit: + { + var jsonData = { pgp_name: login, ssl_name: login, pgp_password: password } + rsApi.request("/control/create_location/", JSON.stringify(jsonData)) + locationView.state = "selectLocation" + } + } + }, + State + { + name: "login" + PropertyChanges { target: locationsListView; visible: false } + PropertyChanges { target: bottomButton; visible: false } + PropertyChanges + { + target: loginView + visible: true + onSubmit: + { + locationView.password = password + console.log("locationView.sslid: ", locationView.sslid) + rsApi.request("/control/login/", JSON.stringify({id: locationView.sslid})) + locationView.attemptLogin = true + busyIndicator.running = true + attemptTimer.start() + } + } + } + ] + + function requestLocationsList() { rsApi.request("/control/locations/", "") } + + onFocusChanged: focus && requestLocationsList() + + LibresapiLocalClient + { + id: rsApi + Component.onCompleted: + { + openConnection(apiSocketPath) + locationView.requestLocationsList() + } + onGoodResponseReceived: + { + var jsonData = JSON.parse(msg) + + + if(jsonData) + { + if(jsonData.data) + { + if(jsonData.data[0] && jsonData.data[0].pgp_id) + { + // if location list update + locationsModel.json = msg + busyIndicator.running = false + } + if (jsonData.data.key_name) + { + if(jsonData.data.want_password) + { + // if Server requested password + var jsonPass = { password: locationView.password } + rsApi.request("/control/password/", JSON.stringify(jsonPass)) + locationView.attemptLogin = false + console.debug("RS core asked for password") + } + else + { + // if Already logged in + bottomButton.enabled = false + bottomButton.text = "Already logged in" + locationView.attemptLogin = false + busyIndicator.running = false + locationView.state = "selectLocation" + locationsListView.enabled = false + console.debug("Already logged in") + } + } + } + } + } + } + + BusyIndicator { id: busyIndicator; anchors.centerIn: parent } + + JSONListModel + { + id: locationsModel + query: "$.data[*]" + } + + ListView + { + id: locationsListView + width: parent.width + anchors.top: parent.top + anchors.bottom: bottomButton.top + model: locationsModel.model + delegate: Button + { + text: model.name + onClicked: + { + loginView.login = text + locationView.sslid = model.id + locationView.state = "login" + } + } + visible: false + } + + Button + { + id: bottomButton + text: "Create new location" + anchors.bottom: parent.bottom + onClicked: locationView.state = "createLocation" + } + + RsLoginPassView + { + id: loginView + visible: false + anchors.fill: parent + } + + Timer + { + id: attemptTimer + interval: 500 + repeat: true + onTriggered: + { + if(locationView.focus) + locationView.requestLocationsList() + + if (locationView.attemptLogin) + rsApi.request("/control/password/", "") + } + } +} diff --git a/retroshare-qml-app/src/qml/PostedMsgDelegate.qml b/retroshare-qml-app/src/qml/PostedMsgDelegate.qml new file mode 100644 index 000000000..f700054aa --- /dev/null +++ b/retroshare-qml-app/src/qml/PostedMsgDelegate.qml @@ -0,0 +1,40 @@ +import QtQuick 2.2 +import "." + +Item { + id: msgDelegate + + width: parent.width + height: 150 + + Column { + Text { text: 'MsgId: ' + AuthorId } + Text { text: 'AuthorId: ' + AuthorId } + Row { + Text { text: 'Name: ' + MsgName } + Text { text: ' PublishTs: ' + PublishTs } + } + Text { text: 'Link: ' + Link } + Text { text: 'Notes: ' + Notes } + Row { + Text { text: 'Hot: ' + HotScore } + Text { text: ' Top: ' + HotScore } + Text { text: ' New: ' + HotScore } + } + Row { + Text { text: 'HaveVoted: ' + HaveVoted } + Text { text: ' UpVotes: ' + UpVotes } + Text { text: ' DownVotes: ' + DownVotes } + Text { text: ' Comments: ' + Comments } + } + } + + MouseArea { + hoverEnabled: false + anchors.fill: parent + onClicked: { + item.ListView.view.currentIndex = index + } + } +} + diff --git a/retroshare-qml-app/src/qml/RsLoginPassView.qml b/retroshare-qml-app/src/qml/RsLoginPassView.qml new file mode 100644 index 000000000..228635fee --- /dev/null +++ b/retroshare-qml-app/src/qml/RsLoginPassView.qml @@ -0,0 +1,52 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 1.4 +import QtQml 2.2 +import org.retroshare.qml_components.LibresapiLocalClient 1.0 + +Item +{ + id: loginView + property string buttonText: "Login" + property string login + property string password + signal submit(string login, string password) + + ColumnLayout + { + id: inputView + width: parent.width + anchors.top: parent.top + anchors.bottom: bottomButton.top + + Row { Text {text: "Name:" } TextField { id: nameField; text: loginView.login } } + Row { Text {text: "Password:" } TextField { id: passwordField; text: loginView.password } } + } + + Button + { + id: bottomButton + text: loginView.buttonText + anchors.bottom: parent.bottom + anchors.right: parent.right + onClicked: loginView.submit(nameField.text, passwordField.text) + } +} diff --git a/retroshare-qml-app/src/qml/TrustedNodesView.qml b/retroshare-qml-app/src/qml/TrustedNodesView.qml new file mode 100644 index 000000000..59a536cc8 --- /dev/null +++ b/retroshare-qml-app/src/qml/TrustedNodesView.qml @@ -0,0 +1,63 @@ +/* + * RetroShare Android QML App + * Copyright (C) 2016 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import org.retroshare.qml_components.LibresapiLocalClient 1.0 +import "jsonpath.js" as JSONPath + +Item +{ + function refreshData() { rsApi.request("/peers/*", "") } + + onFocusChanged: focus && refreshData() + + LibresapiLocalClient + { + id: rsApi + onGoodResponseReceived: jsonModel.json = msg + Component.onCompleted: { openConnection(apiSocketPath) } + } + + JSONListModel + { + id: jsonModel + query: "$.data[*]" + } + + ListView + { + width: parent.width + anchors.top: parent.top + anchors.bottom: bottomButton.top + model: jsonModel.model + delegate: Text + { + text: model.name + onTextChanged: color = JSONPath.jsonPath(JSON.parse(jsonModel.json), "$.data[?(@.pgp_id=='"+model.pgp_id+"')].locations[*].is_online").reduce(function(cur,acc){return cur || acc}, false) ? "lime" : "darkslategray" + } + } + + Button + { + id: bottomButton + text: "Add Trusted Node" + anchors.bottom: parent.bottom + onClicked: swipeView.currentIndex = 3 + } +} diff --git a/retroshare-qml-app/src/qml/icons/contacts-128.png b/retroshare-qml-app/src/qml/icons/contacts-128.png new file mode 100755 index 000000000..41d04a8ae Binary files /dev/null and b/retroshare-qml-app/src/qml/icons/contacts-128.png differ diff --git a/retroshare-qml-app/src/qml/icons/email-128.png b/retroshare-qml-app/src/qml/icons/email-128.png new file mode 100755 index 000000000..d5fa87c56 Binary files /dev/null and b/retroshare-qml-app/src/qml/icons/email-128.png differ diff --git a/retroshare-qml-app/src/qml/icons/settings-4-128.png b/retroshare-qml-app/src/qml/icons/settings-4-128.png new file mode 100755 index 000000000..44a65b18c Binary files /dev/null and b/retroshare-qml-app/src/qml/icons/settings-4-128.png differ diff --git a/retroshare-qml-app/src/qml/icons/star-2-128.png b/retroshare-qml-app/src/qml/icons/star-2-128.png new file mode 100755 index 000000000..3409abc13 Binary files /dev/null and b/retroshare-qml-app/src/qml/icons/star-2-128.png differ diff --git a/retroshare-qml-app/src/qml/jsonpath.js b/retroshare-qml-app/src/qml/jsonpath.js new file mode 100644 index 000000000..2cb6bb07e --- /dev/null +++ b/retroshare-qml-app/src/qml/jsonpath.js @@ -0,0 +1,88 @@ +/* JSONPath 0.8.5 - XPath for JSON + * + * Copyright (c) 2007 Stefan Goessner (goessner.net) + * Licensed under the MIT (MIT-LICENSE.txt) licence. + * + */ +function jsonPath(obj, expr, arg) { + var P = { + resultType: arg && arg.resultType || "VALUE", + result: [], + normalize: function(expr) { + var subx = []; + return expr.replace(/[\['](\??\(.*?\))[\]']|\['(.*?)'\]/g, function($0,$1,$2){return "[#"+(subx.push($1||$2)-1)+"]";}) /* http://code.google.com/p/jsonpath/issues/detail?id=4 */ + .replace(/'?\.'?|\['?/g, ";") + .replace(/;;;|;;/g, ";..;") + .replace(/;$|'?\]|'$/g, "") + .replace(/#([0-9]+)/g, function($0,$1){return subx[$1];}); + }, + asPath: function(path) { + var x = path.split(";"), p = "$"; + for (var i=1,n=x.length; i + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.2 +import QtQuick.Controls 1.4 +import org.retroshare.qml_components.LibresapiLocalClient 1.0 + +ApplicationWindow +{ + id: mainWindow + visible: true + title: qsTr("RSChat") + width: 400 + height: 400 + + Rectangle + { + id: mainView + anchors.fill: parent + states: + [ + State + { + name: "waiting_account_select" + PropertyChanges { target: swipeView; currentIndex: 0 } + PropertyChanges { target: locationsTab; enabled: true } + }, + State + { + name: "running_ok" + PropertyChanges { target: swipeView; currentIndex: 1 } + PropertyChanges { target: locationsTab; enabled: false } + }, + State + { + name: "running_ok_no_full_control" + PropertyChanges { target: swipeView; currentIndex: 1 } + PropertyChanges { target: locationsTab; enabled: false } + } + ] + + LibresapiLocalClient + { + onGoodResponseReceived: + { + var jsonReponse = JSON.parse(msg) + mainView.state = jsonReponse.data.runstate + } + Component.onCompleted: + { + openConnection(apiSocketPath) + request("/control/runstate/", "") + } + } + + TabView + { + id: swipeView + anchors.fill: parent + visible: true + currentIndex: 0 + + Tab + { + title:"Locations" + id: locationsTab + Locations { onVisibleChanged: focus = visible } + } + + Tab + { + title: "Trusted Nodes" + TrustedNodesView { onVisibleChanged: focus = visible } + } + + Tab + { + title: "Contacts" + Contacts { onVisibleChanged: focus = visible } + } + + Tab + { + title: "Add Node" + AddTrustedNode { onVisibleChanged: focus = visible } + } + + Tab + { + title: "Blue" + Rectangle { color: "blue"; anchors.fill: parent } + } + } + } + +/* + onSceneGraphInitialized: llc.openConnection() + + Rectangle { + id: page + width: 600; height: 400 + color: "#336699" // "#FFFFFF" + + Rectangle { + id: header + width: parent.width + anchors.top: parent.top + anchors.left: parent.left + height: 50 + + ApplicationBar { + id: status + + AppButton { + icon: "icons/contacts-128.png" + onButtonClicked : { + tabView.currentIndex = 0 + } + } + + AppButton { + icon: "icons/settings-4-128.png" + onButtonClicked : { + tabView.currentIndex = 1 + } + } + + AppButton { + icon: "icons/email-128.png" + onButtonClicked : { + tabView.currentIndex = 2 + } + } + + AppButton { + icon: "icons/star-2-128.png" + onButtonClicked : { + tabView.currentIndex = 3 + } + } + + } + + } + + TabView { + id: tabView + width: parent.width + anchors.top: header.bottom + anchors.left: parent.left + anchors.bottom: parent.bottom + tabsVisible: false + + Tab { + id: gxsIds + //onActiveChanged: llc.request("/identity/", "") + + onVisibleChanged: llc.request("/identity/", "") + + GxsService { + id: gxss + title: "Friends" +// Button { +// text: "buto" +// anchors.left: gxss.right +// onClicked: { +// // gxss.title = "provaboba" +// // gxss.title = llc.request("/identity/", "") +// //llc.request("/identity/", "") // canviar per onVisibleChanged de Tab potser +// } + +// } + Connections { + target: llc + onGoodResponseReceived: gxss.title = msg //console.log("Image has changed!") + } + //groupDelegate: GxsIdDelegate {} + //groupModel: gxsIdModel + } + } + + Tab { + id: forum + + GxsService { + id: gxssforum + title: "Forums" + onVisibleChanged: llc.request("/control/locations/", "") + Connections { + target: llc + onGoodResponseReceived: gxssforum.title = msg //console.log("Image has changed!") + } + // This one uses the default GxsGroupDelegate. + // groupModel: forumGroupModel + + // msgDelegate: ForumMsgDelegate {} + // msgModel: forumMsgModel + } + } + + Tab { + id: channelLinks + GxsService { + title: "Channels" + + // custom GroupDelegate. + // groupDelegate: ChannelGroupDelegate {} + // groupModel: channelGroupModel + + // msgDelegate: ChannelMsgDelegate {} + // msgModel: channelMsgModel + } + } + + Tab { + id: postedLinks + + GxsService { + title: "Posted" + + // This one uses the default GxsGroupDelegate. +// groupModel: postedGroupModel + +// msgDelegate: PostedMsgDelegate {} +// msgModel: postedMsgModel + } + } + } + } + */ +} diff --git a/retroshare-qml-app/src/retroshare-qml-app.pro b/retroshare-qml-app/src/retroshare-qml-app.pro new file mode 100644 index 000000000..63e34a404 --- /dev/null +++ b/retroshare-qml-app/src/retroshare-qml-app.pro @@ -0,0 +1,36 @@ +!include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") + +QT += qml quick + +CONFIG += c++11 + +SOURCES += main.cpp \ + libresapilocalclient.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Default rules for deployment. +include(deployment.pri) + +DISTFILES += \ + android/AndroidManifest.xml \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradlew \ + android/res/values/libs.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew.bat + +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + +DEPENDPATH *= ../../libretroshare/src +INCLUDEPATH *= ../../libretroshare/src +PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a +LIBS *= ../../libretroshare/src/lib/libretroshare.a + +HEADERS += \ + libresapilocalclient.h \ + debugutils.h diff --git a/retroshare.pri b/retroshare.pri index f0f016d38..eb6308978 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -1,12 +1,44 @@ -# To {dis,en}able libresapi via local socket (unix domain socket or windows named pipes) -# {,un}comment the following line -#CONFIG *= libresapilocalserver +# To disable RetroShare-gui append the following +# assignation to qmake command line "CONFIG+=no_retroshare_gui" +CONFIG *= retroshare_gui +no_retroshare_gui:CONFIG -= retroshare_gui -# To {dis,en}able libresapi via HTTP (libmicrohttpd) {,un}comment the following line +# To disable RetroShare-nogui append the following +# assignation to qmake command line "CONFIG+=no_retroshare_nogui" +CONFIG *= retroshare_nogui +no_retroshare_nogui:CONFIG -= retroshare_nogui + +# To disable RetroShare plugins append the following +# assignation to qmake command line "CONFIG+=no_retroshare_plugins" +CONFIG *= retroshare_plugins +no_retroshare_plugins:CONFIG -= retroshare_plugins + +# To enable RetroShare-android-service append the following assignation to +# qmake command line "CONFIG+=retroshare_android_service" +CONFIG *= no_retroshare_android_service +retroshare_android_service:CONFIG -= no_retroshare_android_service + +# To enable libresapi via local socket (unix domain socket or windows named +# pipes) append the following assignation to qmake command line +#"CONFIG+=libresapilocalserver" +CONFIG *= no_libresapilocalserver +libresapilocalserver:CONFIG -= no_libresapilocalserver + +# To disable libresapi via HTTP (based on libmicrohttpd) append the following +# assignation to qmake command line "CONFIG+=no_libresapihttpserver" CONFIG *= libresapihttpserver +no_libresapihttpserver:CONFIG -= libresapihttpserver + +# To disable SQLCipher support append the following assignation to qmake +# command line "CONFIG+=no_sqlcipher" +CONFIG *= sqlcipher +no_sqlcipher:CONFIG -= sqlcipher + +# To disable GXS (General eXchange System) append the following +# assignation to qmake command line "CONFIG+=no_rs_gxs" +CONFIG *= rs_gxs +no_rs_gxs:CONFIG -= rs_gxs -# Gxs is always enabled now. -DEFINES *= RS_ENABLE_GXS unix { isEmpty(PREFIX) { PREFIX = "/usr" } @@ -18,22 +50,21 @@ unix { } android-g++ { - DEFINES *= NO_SQLCIPHER + CONFIG *= no_libresapihttpserver no_sqlcipher upnp_libupnp + CONFIG -= libresapihttpserver sqlcipher upnp_miniupnpc + QT *= androidextras DEFINES *= "fopen64=fopen" DEFINES *= "fseeko64=fseeko" DEFINES *= "ftello64=ftello" - INCLUDEPATH *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/include/ + INCLUDEPATH += $$NDK_TOOLCHAIN_PATH/sysroot/usr/include LIBS *= -L$$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/ - LIBS *= -lssl -lcrypto -lsqlite3 -lupnp -lixml - ANDROID_EXTRA_LIBS *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libcrypto.so - ANDROID_EXTRA_LIBS *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libssl.so - ANDROID_EXTRA_LIBS *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libbz2.so + LIBS *= -lbz2 -lupnp -lixml -lthreadutil -lsqlite3 ANDROID_EXTRA_LIBS *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libsqlite3.so - ANDROID_EXTRA_LIBS *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libupnp.so - ANDROID_EXTRA_LIBS *= $$NDK_TOOLCHAIN_PATH/sysroot/usr/lib/libixml.so - message(NDK_TOOLCHAIN_PATH: $$NDK_TOOLCHAIN_PATH) - message(LIBS: $$LIBS) - message(ANDROID_EXTRA_LIBS: $$ANDROID_EXTRA_LIBS) +# message(LIBS: $$LIBS) +# message(ANDROID_EXTRA_LIBS: $$ANDROID_EXTRA_LIBS) +# message(ANDROID_PLATFORM: $$ANDROID_PLATFORM) +# message(ANDROID_PLATFORM_ROOT_PATH: $$ANDROID_PLATFORM_ROOT_PATH) +# message(NDK_TOOLCHAIN_PATH: $$NDK_TOOLCHAIN_PATH) } win32 { @@ -95,6 +126,8 @@ unfinished { } wikipoos:DEFINES *= RS_USE_WIKI - +rs_gxs:DEFINES *= RS_ENABLE_GXS libresapilocalserver:DEFINES *= LIBRESAPI_LOCAL_SERVER -libresapihttpserver::DEFINES *= ENABLE_WEBUI +libresapihttpserver:DEFINES *= ENABLE_WEBUI +sqlcipher:DEFINES -= NO_SQLCIPHER +no_sqlcipher:DEFINES *= NO_SQLCIPHER diff --git a/tests/unittests/unittests.pro b/tests/unittests/unittests.pro index 46331e159..790ab1c6d 100644 --- a/tests/unittests/unittests.pro +++ b/tests/unittests/unittests.pro @@ -47,7 +47,7 @@ linux-* { #LIBS += ../../supportlibs/pegmarkdown/lib/libpegmarkdown.a - contains(CONFIG, NO_SQLCIPHER) { + no_sqlcipher { DEFINES *= NO_SQLCIPHER PKGCONFIG *= sqlite3 } else {