commit b56e219bcf6407b2617f6c22a8ef53b800ea8e70 Author: Morgan McMillian Date: Mon Jul 13 17:59:16 2020 -0700 Initial project setup. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07e6538 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +pantalaimon-arm64 +pantalaimon-armhf diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6c83535 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.0.0) +project(pantalaimon C CXX) + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +find_package(Qt5Core) +find_package(Qt5Qml) +find_package(Qt5Quick) + +# Automatically create moc files +set(CMAKE_AUTOMOC ON) + +# Components PATH +execute_process( + COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH + OUTPUT_VARIABLE ARCH_TRIPLET + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +set(QT_IMPORTS_DIR "lib/${ARCH_TRIPLET}") + +set(PROJECT_NAME "pantalaimon") +set(FULL_PROJECT_NAME "pantalaimon.thrrgilag") +set(CMAKE_INSTALL_PREFIX /) +set(DATA_DIR /) +set(DESKTOP_FILE_NAME ${PROJECT_NAME}.desktop) + +# This command figures out the target architecture for use in the manifest file +# Either via the environment variable ARCH (set by Clickable) or dpkg +if(DEFINED ENV{ARCH}) + set(CLICK_ARCH "$ENV{ARCH}") +else() + execute_process( + COMMAND dpkg-architecture -qDEB_HOST_ARCH + OUTPUT_VARIABLE CLICK_ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX}) +install(FILES ${PROJECT_NAME}.apparmor DESTINATION ${DATA_DIR}) +install(DIRECTORY assets DESTINATION ${DATA_DIR}) +install(DIRECTORY src DESTINATION ${DATA_DIR}) + +install(DIRECTORY qml DESTINATION ${DATA_DIR}) +# example config file for local testing +# install(FILES example.conf DESTINATION ${CMAKE_INSTALL_PREFIX} RENAME pantalaimon.conf) + +# Translations +file(GLOB_RECURSE I18N_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po qml/*.qml qml/*.js) +list(APPEND I18N_SRC_FILES ${DESKTOP_FILE_NAME}.in.h) + +find_program(INTLTOOL_MERGE intltool-merge) +if(NOT INTLTOOL_MERGE) + message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package") +endif() +find_program(INTLTOOL_EXTRACT intltool-extract) +if(NOT INTLTOOL_EXTRACT) + message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package") +endif() + +add_custom_target(${DESKTOP_FILE_NAME} ALL + COMMENT "Merging translations into ${DESKTOP_FILE_NAME}..." + COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${CMAKE_SOURCE_DIR}/${DESKTOP_FILE_NAME}.in ${DESKTOP_FILE_NAME} + COMMAND sed -i 's/${PROJECT_NAME}-//g' ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME} +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME} DESTINATION ${DATA_DIR}) + +# TODO: figure out how to cross compile this +install(PROGRAMS ${PROJECT_NAME}-${CLICK_ARCH} DESTINATION ${CMAKE_INSTALL_PREFIX} RENAME ${PROJECT_NAME}) + +add_subdirectory(po) + +# Make source files visible in qtcreator +file(GLOB_RECURSE PROJECT_SRC_FILES + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + qml/*.qml + qml/*.js + src/* + *.json + *.json.in + *.apparmor + *.desktop.in +) + +add_custom_target(${PROJECT_NAME}_FILES ALL SOURCES ${PROJECT_SRC_FILES}) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6ceacce --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +Apache Software License 2.0 + +Copyright (c) 2020, Morgan McMillian + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..30015e8 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Pantalaimon + +End-to-end encryption aware Matrix reverse proxy daemon for Ubuntu Touch. + +## TODO + +- [ ] Proper app icon +- [ ] Cross compile python+pantalaimon +- [ ] Fix image fetching +- [ ] Ability to verify, ingore, or blacklist devices + +## Build + +This project is not currently setup for cross building. A binary will need to +be compiled on a device for each target architecture. + +## Known Issues + +* Images (including avatars) are not fetched +* No user interaction to verify, ignore, or blacklist devices + +## License + +Copyright (C) 2020 Morgan McMillian + +Licensed under the Apache Software License 2.0 diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..89a5124 --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clickable.json b/clickable.json new file mode 100644 index 0000000..bafb7fc --- /dev/null +++ b/clickable.json @@ -0,0 +1,5 @@ +{ + "clickable_minimum_required": "6.12.2", + "builder": "cmake", + "kill": "qmlscene" +} diff --git a/dev-build.sh b/dev-build.sh new file mode 100755 index 0000000..a7db099 --- /dev/null +++ b/dev-build.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +PROJECT_DIR=~/pantalaimon-build +TMP_DIR="${PROJECT_DIR}/tmp" + +VIRTENV_DIR="${PROJECT_DIR}/virtenv" + +OLM_DIR="${PROJECT_DIR}/olm" +PANTALAIMON_DIR="${PROJECT_DIR}/pantalaimon" + +PYTHON_VERSION_MINOR="3.7" +PYTHON_VERSION_PATCH="7" +PYTHON_VERSION="${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" +PYTHON_DIR="${PROJECT_DIR}/python" +PYTHON_SRC_DIR="${TMP_DIR}/Python-${PYTHON_VERSION}" +PYTHON="${PYTHON_DIR}/bin/python${PYTHON_VERSION_MINOR}" + +mkdir -p "${PROJECT_DIR}" +mkdir -p "${TMP_DIR}" + +PACKAGES="make cmake build-essential zlibc python3-venv libffi-dev ppa-purge libssl-dev libsqlite3-dev" + +PANTALAIMON_PATCH_CONTENT=" +diff --git a/pantalaimon/main.py b/pantalaimon/main.py +index 896d29e..3e0fee8 100644 +--- a/pantalaimon/main.py ++++ b/pantalaimon/main.py +@@ -32,6 +32,7 @@ from pantalaimon.log import logger + from pantalaimon.thread_messages import DaemonResponse + from pantalaimon.ui import UI_ENABLED + ++keyring.core.set_keyring(keyring.core.load_keyring('keyring.backends.SecretService.Keyring')) + + def create_dirs(data_dir, conf_dir): + try: +" + +echo "Remounting RootFS writable..." +sudo mount -o remount,rw / + +echo "Installing dependencies..." +sudo apt update +sudo apt install -y ${PACKAGES} + +if [ ! -e "${OLM_DIR}/usr/local/lib/libolm.so" ]; then + cd "${TMP_DIR}" + echo "Downloading Olm..." + wget -qO- "https://gitlab.matrix.org/matrix-org/olm/-/archive/master/olm-master.tar.gz" | tar -xz + + echo "Building Olm..." + cd olm-master + cmake . -Bbuild + cmake --build build + + echo "Installing Olm..." + cd build + make install DESTDIR="${OLM_DIR}" +else + echo "Olm found. Skipping Olm Install..." +fi + +if [ ! -e "${PYTHON}" ]; then + echo "Downloading Python..." + cd "${TMP_DIR}" + wget -qO- "https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz" | tar -xz + + echo "Installing Python..." + cd "${PYTHON_SRC_DIR}" + ./configure --prefix="${PYTHON_DIR}" --enable-shared + make + make install +else + echo "Python found. Skipping Python Install..." +fi + +LD_LIBRARY_PATH=${PYTHON_DIR}/lib:${OLM_DIR}/usr/local/lib:${LD_LIBRARY_PATH} +export LD_LIBRARY_PATH + +echo "Installing PyInstaller..." +${PYTHON} -m pip install --upgrade pyinstaller + +echo "Installing Pantalaimon..." +CFLAGS=-I"${OLM_DIR}/usr/local/include -I${OLM_DIR}/usr/local/include" LDFLAGS="-L${OLM_DIR}/usr/local/lib" ${PYTHON} -m pip install --upgrade pantalaimon + +echo "Bundling Pantalaimon..." +cd ${PROJECT_DIR} +echo "${PANTALAIMON_PATCH_CONTENT}" > main.patch +patch ${PYTHON_DIR}/lib/python${PYTHON_VERSION_MINOR}/site-packages/pantalaimon/main.py main.patch +${PYTHON_DIR}/bin/pyinstaller --onefile ${PYTHON_DIR}/bin/pantalaimon + +echo "Cleaning up, reverting changes to RootFS..." +sudo apt autoremove -y ${PACKAGES} +sudo apt clean + diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..79eb6c2 --- /dev/null +++ b/example.conf @@ -0,0 +1,6 @@ +[matrix.org] +Homeserver = https://matrix.org +ListenAddress = localhost +ListenPort = 8009 +IgnoreVerification = True +UseKeyring = False diff --git a/manifest.json.in b/manifest.json.in new file mode 100644 index 0000000..74fcd31 --- /dev/null +++ b/manifest.json.in @@ -0,0 +1,15 @@ +{ + "name": "pantalaimon.thrrgilag", + "description": "End-to-end encryption aware Matrix reverse proxy daemon for Ubuntu Touch", + "architecture": "@CLICK_ARCH@", + "title": "Pantalaimon UT", + "hooks": { + "pantalaimon": { + "apparmor": "pantalaimon.apparmor", + "desktop": "pantalaimon.desktop" + } + }, + "version": "0.1.0", + "maintainer": "Morgan McMillian ", + "framework" : "ubuntu-sdk-16.04" +} diff --git a/pantalaimon.apparmor b/pantalaimon.apparmor new file mode 100644 index 0000000..39240b1 --- /dev/null +++ b/pantalaimon.apparmor @@ -0,0 +1,5 @@ +{ + "template": "unconfined", + "policy_groups": ["networking"], + "policy_version": 16.04 +} diff --git a/pantalaimon.desktop.in b/pantalaimon.desktop.in new file mode 100644 index 0000000..1a2edf8 --- /dev/null +++ b/pantalaimon.desktop.in @@ -0,0 +1,7 @@ +[Desktop Entry] +_Name=Pantalaimon UT +Exec=qmlscene %U qml/Main.qml +Icon=assets/logo.svg +Terminal=false +Type=Application +X-Ubuntu-Touch=true diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt new file mode 100644 index 0000000..ac31544 --- /dev/null +++ b/po/CMakeLists.txt @@ -0,0 +1,34 @@ +include(FindGettext) +find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) + +set(DOMAIN ${FULL_PROJECT_NAME}) +set(POT_FILE ${DOMAIN}.pot) +file(GLOB PO_FILES *.po) + +# Creates the .pot file containing the translations template +add_custom_target(${POT_FILE} ALL + COMMENT "Generating translation template" + COMMAND ${INTLTOOL_EXTRACT} --update --type=gettext/ini + --srcdir=${CMAKE_SOURCE_DIR} ${DESKTOP_FILE_NAME}.in + + COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} -o ${POT_FILE} + -D ${CMAKE_CURRENT_SOURCE_DIR} + -D ${CMAKE_CURRENT_BINARY_DIR} + --from-code=UTF-8 + --c++ --qt --language=javascript --add-comments=TRANSLATORS + --keyword=tr --keyword=tr:1,2 --keyword=N_ --keyword=_ + --package-name='${DOMAIN}' + --sort-by-file + ${I18N_SRC_FILES} + COMMAND ${CMAKE_COMMAND} -E copy ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR}) + +# Builds the binary translations catalog for each language +# it finds source translations (*.po) for +foreach(PO_FILE ${PO_FILES}) + get_filename_component(LANG ${PO_FILE} NAME_WE) + gettext_process_po_files(${LANG} ALL PO_FILES ${PO_FILE}) + set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/share/locale/${LANG}/LC_MESSAGES) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo + DESTINATION ${INSTALL_DIR} + RENAME ${DOMAIN}.mo) +endforeach(PO_FILE) diff --git a/po/pantalaimon.thrrgilag.pot b/po/pantalaimon.thrrgilag.pot new file mode 100644 index 0000000..fb399fc --- /dev/null +++ b/po/pantalaimon.thrrgilag.pot @@ -0,0 +1,66 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the pantalaimon.thrrgilag package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pantalaimon.thrrgilag\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-07-14 00:16+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../qml/Components/EditServerPage.qml:19 +msgid "Homeserver Configuration" +msgstr "" + +#: ../qml/Components/EditServerPage.qml:24 +msgid "Save" +msgstr "" + +#: ../qml/Components/EditServerPage.qml:83 +msgid "Description:" +msgstr "" + +#: ../qml/Components/EditServerPage.qml:103 +msgid "Homeserver:" +msgstr "" + +#: ../qml/Components/EditServerPage.qml:124 +msgid "Listen on 127.0.0.1:" +msgstr "" + +#: ../qml/SettingsPage.qml:19 pantalaimon.desktop.in.h:1 +msgid "Pantalaimon UT" +msgstr "" + +#: ../qml/SettingsPage.qml:24 +msgid "About" +msgstr "" + +#: ../qml/SettingsPage.qml:36 +msgid "Service start disabled" +msgstr "" + +#: ../qml/SettingsPage.qml:36 +msgid "Service start enabled" +msgstr "" + +#: ../qml/SettingsPage.qml:106 +msgid "Delete homeserver" +msgstr "" + +#: ../qml/SettingsPage.qml:124 +msgid "No homeservers" +msgstr "" + +#: ../qml/SettingsPage.qml:177 +msgid "Add Homeserver" +msgstr "" diff --git a/qml/Components/EditServerPage.qml b/qml/Components/EditServerPage.qml new file mode 100644 index 0000000..678be56 --- /dev/null +++ b/qml/Components/EditServerPage.qml @@ -0,0 +1,146 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 +import Ubuntu.Components 1.3 +import Ubuntu.Components.ListItems 1.3 + +Page { + id: bottomEdgeComponent + + property int idx: 0 + property bool editmode: false + property alias instance: instance.text + property alias homeserver: homeserver.text + property alias listenport: listenport.text + + signal save() + + header: PageHeader { + id: header + title: i18n.tr('Homeserver Configuration') + + trailingActionBar.actions: [ + Action { + iconName: 'ok' + text: i18n.tr('Save') + onTriggered: { + var data = { + name: instance.text, + homeserver: homeserver.text, + listenport: listenport.text + } + if (editmode) { + listModel.set(idx, data); + pageStack.pop(); + } else { + listModel.append(data) + bottomEdge.collapse(); + } + save(); + } + } + ] + } + + width: bottomEdge.width + height: bottomEdge.height + + function resetdata() { + console.log("debug: resetdata"); + instance.text = ""; + homeserver.text = ""; + listenport.text = "8009"; // TODO: maybe autoincrement based on existing entries? + } + + Flickable { + clip: true + + anchors { + top: header.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + contentHeight: contentColumn.height + units.gu(4) + + Column { + id: contentColumn + + anchors { + top: parent.top; + left: parent.left; + right: parent.right; + } + + Row { + width: parent.width + height: units.gu(6) + leftPadding: units.gu(2) + spacing: units.gu(1) + + Label { + anchors.verticalCenter: parent.verticalCenter + text: i18n.tr('Description:') + } + + TextField { + id: instance + text: "" + anchors.verticalCenter: parent.verticalCenter + Layout.fillWidth: true + } + + } + + Row { + width: parent.width + height: units.gu(6) + leftPadding: units.gu(2) + spacing: units.gu(1) + + Label { + anchors.verticalCenter: parent.verticalCenter + text: i18n.tr('Homeserver:') + } + + TextField { + id: homeserver + text: "" + anchors.verticalCenter: parent.verticalCenter + Layout.fillWidth: true + inputMethodHints: Qt.ImhUrlCharactersOnly + } + + } + + Row { + width: parent.width + height: units.gu(6) + leftPadding: units.gu(2) + spacing: units.gu(1) + + Label { + anchors.verticalCenter: parent.verticalCenter + text: i18n.tr('Listen on 127.0.0.1:') + } + + TextField { + id: listenport + text: "8009" + anchors.verticalCenter: parent.verticalCenter + Layout.fillWidth: true + inputMethodHints: Qt.ImhDigitsOnly + } + + } + } + } + + Connections { + target: bottomEdge + + onCollapseCompleted: { + resetdata(); + } + } +} diff --git a/qml/Main.qml b/qml/Main.qml new file mode 100644 index 0000000..021f066 --- /dev/null +++ b/qml/Main.qml @@ -0,0 +1,21 @@ +import QtQuick 2.7 +import Ubuntu.Components 1.3 +//import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Qt.labs.settings 1.0 + +MainView { + id: root + objectName: 'mainView' + applicationName: 'pantalaimon.thrrgilag' + automaticOrientation: true + + width: units.gu(45) + height: units.gu(75) + + PageStack { + id: pageStack + + Component.onCompleted: push(Qt.resolvedUrl('SettingsPage.qml')) + } +} diff --git a/qml/SettingsPage.qml b/qml/SettingsPage.qml new file mode 100644 index 0000000..2c6d7a4 --- /dev/null +++ b/qml/SettingsPage.qml @@ -0,0 +1,198 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 +import Ubuntu.Components 1.3 +import Ubuntu.Components.ListItems 1.3 as ListItems +import Qt.labs.settings 1.0 +import io.thp.pyotherside 1.3 + +import "Components" + +Page { + id: settingsPage + + property bool is_running: false + property bool upstart: false + property string status_msg + + header: PageHeader { + id: header + title: i18n.tr('Pantalaimon UT') + + trailingActionBar.actions: [ + Action { + iconName: 'info' + text: i18n.tr('About') + } + ] + } + + ListItem { + id: upstartState + anchors.top: header.bottom + width: parent.width + + ListItems.Standard { + anchors.fill: parent + text: upstart ? i18n.tr("Service start enabled") : i18n.tr("Service start disabled") + control: Switch { + checked: upstart + onClicked: { + if (checked) { + py.call('service.add', [], function(result) {}); + } else { + py.call('service.remove', [], function(result) {}); + } + get_status(); + } + } + } + } + + ListItem { + id: serviceState + anchors.top: upstartState.bottom + width: parent.width + + ListItems.Standard { + anchors.fill: parent + text: status_msg + control: Switch { + enabled: upstart + checked: is_running + onClicked: { + if (checked) { + py.call('service.start', [], function(result) {}); + } else { + py.call('service.stop', [], function(result) {}); + } + get_status(); + } + } + } + } + + ListView { + id: listView + width: parent.width + height: parent.height - bottomEdgeHint.height + anchors.top: serviceState.bottom + visible: (listView.count !== 0) + model: ListModel { + id: listModel + } + clip: true + + delegate: ListItem { + ListItems.Standard { + anchors.fill: parent + text: name + progression: true + onClicked: { + var item = listModel.get(index); + console.log(item); + pageStack.push(editConfigPage, { + idx: index, + instance: item.name, + homeserver: item.homeserver, + listenport: item.listenport + }); + } + } + + leadingActions: ListItemActions { + actions: [ + Action { + iconName: "delete" + text: i18n.tr("Delete homeserver") + onTriggered: { + console.log("debug: delete " + index); + listModel.remove(index); + saveConfig(); + } + } + ] + } + } + } + + Rectangle { + visible: (listView.count === 0) + // color: "lightgrey" + anchors.fill: parent + + Label { + text: i18n.tr("No homeservers") + fontSize: "x-large" + anchors.centerIn: parent + } + } + + Python { + id: py + + Component.onCompleted: { + addImportPath(Qt.resolvedUrl('../src/')); + + importModule('config', function() { + py.call('config.load', [], function(result) { + for (var i=0; i