Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4461bf4 authored by Philipp Heckel's avatar Philipp Heckel
Browse files

End to end, dabbling

parent 60f90667
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -2,11 +2,11 @@
  "formatVersion": 1,
  "database": {
    "version": 12,
    "identityHash": "9363ad5196e88862acceb1bb9ee91124",
    "identityHash": "250db1985385d64d880124071eab96fc",
    "entities": [
      {
        "tableName": "Subscription",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `topic` TEXT NOT NULL, `instant` INTEGER NOT NULL, `mutedUntil` INTEGER NOT NULL, `minPriority` INTEGER NOT NULL, `autoDelete` INTEGER NOT NULL, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, PRIMARY KEY(`id`))",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `topic` TEXT NOT NULL, `instant` INTEGER NOT NULL, `mutedUntil` INTEGER NOT NULL, `minPriority` INTEGER NOT NULL, `autoDelete` INTEGER NOT NULL, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, `encryptionKey` BLOB, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
@@ -79,6 +79,12 @@
            "columnName": "displayName",
            "affinity": "TEXT",
            "notNull": false
          },
          {
            "fieldPath": "encryptionKey",
            "columnName": "encryptionKey",
            "affinity": "BLOB",
            "notNull": false
          }
        ],
        "primaryKey": {
@@ -326,7 +332,7 @@
    "views": [],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9363ad5196e88862acceb1bb9ee91124')"
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '250db1985385d64d880124071eab96fc')"
    ]
  }
}
 No newline at end of file
+7 −3
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ package io.heckel.ntfy.backup

import android.content.Context
import android.net.Uri
import android.util.Base64
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.stream.JsonReader
@@ -102,6 +103,7 @@ class Backuper(val context: Context) {
                    upAppId = s.upAppId,
                    upConnectorToken = s.upConnectorToken,
                    displayName = s.displayName,
                    encryptionKey = Base64.decode(s.encryptionKey, Base64.DEFAULT)
                ))
            } catch (e: Exception) {
                Log.w(TAG, "Unable to restore subscription ${s.id} (${topicUrl(s.baseUrl, s.topic)}): ${e.message}. Ignoring.", e)
@@ -226,7 +228,8 @@ class Backuper(val context: Context) {
                icon = s.icon,
                upAppId = s.upAppId,
                upConnectorToken = s.upConnectorToken,
                displayName = s.displayName
                displayName = s.displayName,
                encryptionKey = if (s.encryptionKey != null) Base64.encodeToString(s.encryptionKey, Base64.DEFAULT) else null
            )
        }
    }
@@ -334,7 +337,8 @@ data class Subscription(
    val icon: String?,
    val upAppId: String?,
    val upConnectorToken: String?,
    val displayName: String?
    val displayName: String?,
    val encryptionKey: String? // as base64
)

data class Notification(
@@ -343,7 +347,7 @@ data class Notification(
    val timestamp: Long,
    val title: String,
    val message: String,
    val encoding: String, // "base64" or ""
    val encoding: String, // "base64", "jwe", or ""
    val priority: Int, // 1=min, 3=default, 5=max
    val tags: String,
    val click: String, // URL/intent to open on notification click
+12 −8
Original line number Diff line number Diff line
@@ -23,13 +23,14 @@ data class Subscription(
    @ColumnInfo(name = "upAppId") val upAppId: String?, // UnifiedPush application package name
    @ColumnInfo(name = "upConnectorToken") val upConnectorToken: String?, // UnifiedPush connector token
    @ColumnInfo(name = "displayName") val displayName: String?,
    @ColumnInfo(name = "encryptionKey") val encryptionKey: ByteArray?,
    @Ignore val totalCount: Int = 0, // Total notifications
    @Ignore val newCount: Int = 0, // New notifications
    @Ignore val lastActive: Long = 0, // Unix timestamp
    @Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE
) {
    constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, minPriority: Int, autoDelete: Long, lastNotificationId: String, icon: String, upAppId: String, upConnectorToken: String, displayName: String?) :
            this(id, baseUrl, topic, instant, mutedUntil, minPriority, autoDelete, lastNotificationId, icon, upAppId, upConnectorToken, displayName, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
    constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, minPriority: Int, autoDelete: Long, lastNotificationId: String, icon: String, upAppId: String, upConnectorToken: String, displayName: String?, encryptionKey: ByteArray?) :
            this(id, baseUrl, topic, instant, mutedUntil, minPriority, autoDelete, lastNotificationId, icon, upAppId, upConnectorToken, displayName, encryptionKey, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
}

enum class ConnectionState {
@@ -49,6 +50,7 @@ data class SubscriptionWithMetadata(
    val upAppId: String?,
    val upConnectorToken: String?,
    val displayName: String?,
    val encryptionKey: ByteArray?,
    val totalCount: Int,
    val newCount: Int,
    val lastActive: Long
@@ -61,7 +63,7 @@ data class Notification(
    @ColumnInfo(name = "timestamp") val timestamp: Long, // Unix timestamp
    @ColumnInfo(name = "title") val title: String,
    @ColumnInfo(name = "message") val message: String,
    @ColumnInfo(name = "encoding") val encoding: String, // "base64" or ""
    @ColumnInfo(name = "encoding") val encoding: String, // "" (raw UTF-8), "base64", or "jwe" (encryption)
    @ColumnInfo(name = "notificationId") val notificationId: Int, // Android notification popup ID
    @ColumnInfo(name = "priority", defaultValue = "3") val priority: Int, // 1=min, 3=default, 5=max
    @ColumnInfo(name = "tags") val tags: String,
@@ -269,6 +271,8 @@ abstract class Database : RoomDatabase() {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL("ALTER TABLE Subscription ADD COLUMN lastNotificationId TEXT")
                db.execSQL("ALTER TABLE Subscription ADD COLUMN displayName TEXT")
                db.execSQL("ALTER TABLE Subscription ADD COLUMN encryptionKey BLOB")
                db.execSQL("ALTER TABLE Notification ADD COLUMN encryption TEXT NOT NULL DEFAULT('')")
            }
        }
    }
@@ -278,7 +282,7 @@ abstract class Database : RoomDatabase() {
interface SubscriptionDao {
    @Query("""
        SELECT 
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.encryptionKey,
          COUNT(n.id) totalCount, 
          COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, 
          IFNULL(MAX(n.timestamp),0) AS lastActive
@@ -291,7 +295,7 @@ interface SubscriptionDao {

    @Query("""
        SELECT 
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.encryptionKey,
          COUNT(n.id) totalCount, 
          COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, 
          IFNULL(MAX(n.timestamp),0) AS lastActive
@@ -304,7 +308,7 @@ interface SubscriptionDao {

    @Query("""
        SELECT 
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.encryptionKey,
          COUNT(n.id) totalCount, 
          COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, 
          IFNULL(MAX(n.timestamp),0) AS lastActive
@@ -317,7 +321,7 @@ interface SubscriptionDao {

    @Query("""
        SELECT 
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.encryptionKey,
          COUNT(n.id) totalCount, 
          COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, 
          IFNULL(MAX(n.timestamp),0) AS lastActive
@@ -330,7 +334,7 @@ interface SubscriptionDao {

    @Query("""
        SELECT 
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName,
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.encryptionKey,
          COUNT(n.id) totalCount, 
          COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, 
          IFNULL(MAX(n.timestamp),0) AS lastActive
+2 −0
Original line number Diff line number Diff line
@@ -385,6 +385,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
                upAppId = s.upAppId,
                upConnectorToken = s.upConnectorToken,
                displayName = s.displayName,
                encryptionKey = s.encryptionKey,
                totalCount = s.totalCount,
                newCount = s.newCount,
                lastActive = s.lastActive,
@@ -410,6 +411,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
            upAppId = s.upAppId,
            upConnectorToken = s.upConnectorToken,
            displayName = s.displayName,
            encryptionKey = s.encryptionKey,
            totalCount = s.totalCount,
            newCount = s.newCount,
            lastActive = s.lastActive,
+3 −1
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package io.heckel.ntfy.service
import io.heckel.ntfy.db.*
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.util.Encryption
import io.heckel.ntfy.util.topicUrl
import kotlinx.coroutines.*
import okhttp3.Call
@@ -42,7 +43,8 @@ class JsonConnection(
                    since = notification.id
                    val subscriptionId = topicsToSubscriptionIds[topic] ?: return@notify
                    val subscription = repository.getSubscription(subscriptionId) ?: return@notify
                    val notificationWithSubscriptionId = notification.copy(subscriptionId = subscription.id)
                    val notificationDecrypted = Encryption.maybeDecrypt(subscription, notification)
                    val notificationWithSubscriptionId = notificationDecrypted.copy(subscriptionId = subscription.id)
                    notificationListener(subscription, notificationWithSubscriptionId)
                }
                val failed = AtomicBoolean(false)
Loading