From 81bdf506bcdcee1a12120e5b6dd866652accb7d6 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 6 Jan 2021 22:45:40 +0100
Subject: [PATCH] Fix Int type issue when sending PowerLevelsContent to the
 server

---
 .../session/room/model/PowerLevelsContent.kt  |  1 +
 .../session/room/state/DefaultStateService.kt | 14 +++--
 .../room/state/SafePowerLevelContent.kt       | 60 +++++++++++++++++++
 3 files changed, 71 insertions(+), 4 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt
index 317948388d..e18cb7f05f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt
@@ -64,6 +64,7 @@ data class PowerLevelsContent(
         return when (val value = notifications[key]) {
             // the first implementation was a string value
             is String -> value.toInt()
+            is Double -> value.toInt()
             is Int    -> value
             else      -> Role.Moderator.value
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
index b546584450..804968bac0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
@@ -35,13 +35,11 @@ import org.matrix.android.sdk.api.util.JsonDict
 import org.matrix.android.sdk.api.util.MimeTypes
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.internal.session.content.FileUploader
-import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask
 
 internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
                                                                private val stateEventDataSource: StateEventDataSource,
                                                                private val sendStateTask: SendStateTask,
-                                                               private val fileUploader: FileUploader,
-                                                               private val addRoomAliasTask: AddRoomAliasTask
+                                                               private val fileUploader: FileUploader
 ) : StateService {
 
     @AssistedInject.Factory
@@ -74,11 +72,19 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
                 roomId = roomId,
                 stateKey = stateKey,
                 eventType = eventType,
-                body = body
+                body = body.toSafeJson(eventType)
         )
         sendStateTask.execute(params)
     }
 
+    private fun JsonDict.toSafeJson(eventType: String): JsonDict {
+        // Safe treatment for PowerLevelContent
+        return when (eventType) {
+            EventType.STATE_ROOM_POWER_LEVELS -> toSafePowerLevelsContentDict()
+            else                              -> this
+        }
+    }
+
     override suspend fun updateTopic(topic: String) {
         sendStateEvent(
                 eventType = EventType.STATE_ROOM_TOPIC,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt
new file mode 100644
index 0000000000..9f9a63de84
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * 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.
+ */
+
+package org.matrix.android.sdk.internal.session.room.state
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
+import org.matrix.android.sdk.api.session.room.powerlevels.Role
+import org.matrix.android.sdk.api.util.JsonDict
+
+@JsonClass(generateAdapter = true)
+internal data class SerializablePowerLevelsContent(
+        @Json(name = "ban") val ban: Int = Role.Moderator.value,
+        @Json(name = "kick") val kick: Int = Role.Moderator.value,
+        @Json(name = "invite") val invite: Int = Role.Moderator.value,
+        @Json(name = "redact") val redact: Int = Role.Moderator.value,
+        @Json(name = "events_default") val eventsDefault: Int = Role.Default.value,
+        @Json(name = "events") val events: Map<String, Int> = emptyMap(),
+        @Json(name = "users_default") val usersDefault: Int = Role.Default.value,
+        @Json(name = "users") val users: Map<String, Int> = emptyMap(),
+        @Json(name = "state_default") val stateDefault: Int = Role.Moderator.value,
+        // `Int` is the diff here (instead of `Any`)
+        @Json(name = "notifications") val notifications: Map<String, Int> = emptyMap()
+)
+
+internal fun JsonDict.toSafePowerLevelsContentDict(): JsonDict {
+    return toModel<PowerLevelsContent>()
+            ?.let { content ->
+                SerializablePowerLevelsContent(
+                        ban = content.ban,
+                        kick = content.kick,
+                        invite = content.invite,
+                        redact = content.redact,
+                        eventsDefault = content.eventsDefault,
+                        events = content.events,
+                        usersDefault = content.usersDefault,
+                        users = content.users,
+                        stateDefault = content.stateDefault,
+                        notifications = content.notifications.mapValues { content.notificationLevel(it.key)  }
+                )
+            }
+            ?.toContent()
+            ?: emptyMap()
+}