diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt index 28d09482aff50e213beec4fbfe08901464d9e485..d400951e538f7f433f402f39c9f3bcc16daf7ea0 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt @@ -17,8 +17,10 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.libquassel.protocol.io +package de.kuschku.quasseldroid.protocol.io +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.print import de.kuschku.quasseldroid.util.TlsInfo import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher @@ -65,12 +67,17 @@ class CoroutineChannel { } suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) { + buffer.print() this.channel.write(buffer) } - suspend fun write(chainedBuffer: de.kuschku.libquassel.protocol.io.ChainedByteBuffer) { + suspend fun write(chainedBuffer: ChainedByteBuffer) { for (buffer in chainedBuffer.buffers()) { write(buffer) } } + + suspend fun flush() = runInterruptible(writeContext) { + this.channel.flush() + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt index 1e86f228408aca6b315bb1bb9fefccaa7ecb52e8..abdad9f25eddc40e639b1e0d094cf674f544cf4f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt @@ -17,7 +17,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.libquassel.protocol.io +package de.kuschku.quasseldroid.protocol.io import java.io.OutputStream import java.util.zip.DeflaterOutputStream diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt index 36377b1134846e62abf20ec16a802410d7df9fb9..7f8de746680e751bf6c3d0e2781d704e330fda7d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt @@ -17,7 +17,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.libquassel.protocol.io +package de.kuschku.quasseldroid.protocol.io import android.util.Log import java.io.InputStream @@ -43,7 +43,7 @@ class ReadableWrappedChannel( // Only read as long as we have content to read, and until we’ve blocked at most once while (remainingData > 0 && !(hasRead && backingStream.available() == 0)) { // Data to be read, always the minimum of available data and the page size - val toReadOnce = Math.min(remainingData, PAGE_SIZE) + val toReadOnce = remainingData.coerceAtMost(PAGE_SIZE) var readData = 0 try { diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt index eaec977eb441427f8e16803a53140135860750f0..28d2697ea1cd7ceca71eebc54f9fda25e72afb5e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt @@ -17,7 +17,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.libquassel.protocol.io +package de.kuschku.quasseldroid.protocol.io import de.kuschku.quasseldroid.util.TlsInfo import java.io.Flushable diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt index abd9ecc5bd8d62327bc25190f3a41765a03ef29d..ce25556b3981a2ff3434de7032c9e0d484ea8c39 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt @@ -17,7 +17,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.libquassel.protocol.io +package de.kuschku.quasseldroid.protocol.io import java.io.OutputStream import java.nio.ByteBuffer @@ -37,7 +37,7 @@ class WritableWrappedChannel( synchronized(lock) { while (remainingData > 0) { // Data to be written, always the minimum of available data and the page size - val writtenData = Math.min(remainingData, PAGE_SIZE) + val writtenData = remainingData.coerceAtMost(PAGE_SIZE) // Set new bufferId info buffer.clear() diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index 2b068d11462a4b96669193de13a711a3a36220a0..62d2a2d26c58695b7b6cb6b4e0d0285e16ab1147 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -1,30 +1,30 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:aapt="http://schemas.android.com/aapt" - android:width="108dp" - android:height="108dp" - android:viewportWidth="108" - android:viewportHeight="108"> - <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> - <aapt:attr name="android:fillColor"> - <gradient - android:endX="85.84757" - android:endY="92.4963" - android:startX="42.9492" - android:startY="49.59793" - android:type="linear"> - <item - android:color="#44000000" - android:offset="0.0" /> - <item - android:color="#00000000" - android:offset="1.0" /> - </gradient> - </aapt:attr> - </path> - <path - android:fillColor="#FFFFFF" - android:fillType="nonZero" - android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" - android:strokeWidth="1" - android:strokeColor="#00000000" /> -</vector> \ No newline at end of file + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="85.84757" + android:endY="92.4963" + android:startX="42.9492" + android:startY="49.59793" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" + android:strokeWidth="1" + android:strokeColor="#00000000" /> +</vector> diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9cbf141911847041df5d7b87f0dd5ef9d4..140f8294680e4955599e9a5a7ceb576d6b6d8468 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,170 @@ <?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="108dp" - android:height="108dp" - android:viewportWidth="108" - android:viewportHeight="108"> - <path - android:fillColor="#3DDC84" - android:pathData="M0,0h108v108h-108z" /> - <path - android:fillColor="#00000000" - android:pathData="M9,0L9,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,0L19,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M29,0L29,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M39,0L39,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M49,0L49,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M59,0L59,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M69,0L69,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M79,0L79,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M89,0L89,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M99,0L99,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,9L108,9" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,19L108,19" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,29L108,29" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,39L108,39" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,49L108,49" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,59L108,59" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,69L108,69" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,79L108,79" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,89L108,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,99L108,99" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,29L89,29" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,39L89,39" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,49L89,49" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,59L89,59" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,69L89,69" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,79L89,79" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M29,19L29,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M39,19L39,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M49,19L49,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M59,19L59,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M69,19L69,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M79,19L79,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:fillColor="#3DDC84" + android:pathData="M0,0h108v108h-108z" /> + <path + android:fillColor="#00000000" + android:pathData="M9,0L9,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,0L19,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M29,0L29,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M39,0L39,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M49,0L49,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M59,0L59,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M69,0L69,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M79,0L79,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M89,0L89,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M99,0L99,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,9L108,9" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,19L108,19" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,29L108,29" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,39L108,39" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,49L108,49" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,59L108,59" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,69L108,69" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,79L108,79" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,89L108,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,99L108,99" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,29L89,29" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,39L89,39" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,49L89,49" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,59L89,59" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,69L89,69" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,79L89,79" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M29,19L29,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M39,19L39,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M49,19L49,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M59,19L59,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M69,19L69,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M79,19L79,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> </vector> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127d327620c93d2b2d00342a68e97b98a48d..758655a2e8832810ac62c86c12e924fb02c665c8 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="purple_200">#FFBB86FC</color> - <color name="purple_500">#FF6200EE</color> - <color name="purple_700">#FF3700B3</color> - <color name="teal_200">#FF03DAC5</color> - <color name="teal_700">#FF018786</color> - <color name="black">#FF000000</color> - <color name="white">#FFFFFFFF</color> -</resources> \ No newline at end of file + <color name="purple_200">#FFBB86FC</color> + <color name="purple_500">#FF6200EE</color> + <color name="purple_700">#FF3700B3</color> + <color name="teal_200">#FF03DAC5</color> + <color name="teal_700">#FF018786</color> + <color name="black">#FF000000</color> + <color name="white">#FFFFFFFF</color> +</resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26c8176cf4bc43f6a84b40fc0fc32e72a0bd67bf..99343ac8f8f8dea59670dd411a99f370adf20f35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ <resources> - <string name="app_name">Quasseldroid</string> -</resources> \ No newline at end of file + <string name="app_name">Quasseldroid</string> +</resources> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 80cb390147002bad3cc4125663a04d0c301d1490..51fe81c59b581cbfaea87b1981473fafa5ee910d 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,25 +1,25 @@ <resources xmlns:tools="http://schemas.android.com/tools"> - <!-- Base application theme. --> - <style name="Theme.Quasseldroid" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> - <!-- Primary brand color. --> - <item name="colorPrimary">@color/purple_500</item> - <item name="colorPrimaryVariant">@color/purple_700</item> - <item name="colorOnPrimary">@color/white</item> - <!-- Secondary brand color. --> - <item name="colorSecondary">@color/teal_200</item> - <item name="colorSecondaryVariant">@color/teal_700</item> - <item name="colorOnSecondary">@color/black</item> - <!-- Status bar color. --> - <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> - <!-- Customize your theme here. --> - </style> + <!-- Base application theme. --> + <style name="Theme.Quasseldroid" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <!-- Primary brand color. --> + <item name="colorPrimary">@color/purple_500</item> + <item name="colorPrimaryVariant">@color/purple_700</item> + <item name="colorOnPrimary">@color/white</item> + <!-- Secondary brand color. --> + <item name="colorSecondary">@color/teal_200</item> + <item name="colorSecondaryVariant">@color/teal_700</item> + <item name="colorOnSecondary">@color/black</item> + <!-- Status bar color. --> + <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> + <!-- Customize your theme here. --> + </style> - <style name="Theme.Quasseldroid.NoActionBar"> - <item name="windowActionBar">false</item> - <item name="windowNoTitle">true</item> - </style> + <style name="Theme.Quasseldroid.NoActionBar"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> + </style> - <style name="Theme.Quasseldroid.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> + <style name="Theme.Quasseldroid.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> - <style name="Theme.Quasseldroid.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> -</resources> \ No newline at end of file + <style name="Theme.Quasseldroid.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> +</resources> diff --git a/app/src/main/res/xml/backup_descriptor.xml b/app/src/main/res/xml/backup_descriptor.xml index 6fd6103a4d93e4ef450dabdafd296f1cab15dac7..6767bee654cf4978c823d1bcba05c8f5320b64fc 100644 --- a/app/src/main/res/xml/backup_descriptor.xml +++ b/app/src/main/res/xml/backup_descriptor.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <full-backup-content> - <!-- Exclude specific shared preferences that contain GCM registration Id --> + <!-- Exclude specific shared preferences that contain GCM registration Id --> </full-backup-content> diff --git a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt index d3fe73cb4a1ad26af687a6b92fe263562a7bcfd3..9663335fd549d713d5a5446fa04731b22f6091bf 100644 --- a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt +++ b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt @@ -1,10 +1,19 @@ package de.kuschku.quasseldroid +import de.kuschku.bitflags.flags +import de.kuschku.libquassel.protocol.connection.ProtocolInfoSerializer +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.messages.handshake.ClientInit +import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitAckSerializer +import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitRejectSerializer +import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitSerializer +import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeMapSerializer import de.kuschku.libquassel.protocol.serializers.primitive.IntSerializer -import de.kuschku.libquassel.protocol.serializers.primitive.ProtocolInfoSerializer import de.kuschku.libquassel.protocol.serializers.primitive.UIntSerializer -import de.kuschku.libquassel.protocol.io.CoroutineChannel +import de.kuschku.libquassel.protocol.variant.into +import de.kuschku.quasseldroid.protocol.io.CoroutineChannel import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Test @@ -14,16 +23,7 @@ import java.security.cert.X509Certificate import javax.net.ssl.SSLContext import javax.net.ssl.X509TrustManager -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } @Test fun testNetworking() { @@ -44,22 +44,81 @@ class ExampleUnitTest { }), null) runBlocking { + val connectionFeatureSet = FeatureSet.build() val sizeBuffer = ByteBuffer.allocateDirect(4) - val sendBuffer = de.kuschku.libquassel.protocol.io.ChainedByteBuffer(direct = true) + val sendBuffer = ChainedByteBuffer(direct = true) val channel = CoroutineChannel() channel.connect(InetSocketAddress("kuschku.de", 4242)) - val readBuffer = ByteBuffer.allocateDirect(4) - UIntSerializer.serialize(sendBuffer, 0x42b3_3f00u or 0x03u) - IntSerializer.serialize(sendBuffer, 2) - UIntSerializer.serialize(sendBuffer, 0x8000_0000u) - channel.write(sendBuffer) - channel.read(readBuffer) - readBuffer.flip() - println(ProtocolInfoSerializer.deserialize(readBuffer)) - println(channel.tlsInfo.value) - channel.enableTLS(context) - println(channel.tlsInfo.value) - channel.enableCompression() + + suspend fun readAmount(amount: Int? = null): Int { + if (amount != null) return amount + + sizeBuffer.clear() + channel.read(sizeBuffer) + sizeBuffer.flip() + val size = IntSerializer.deserialize(sizeBuffer, connectionFeatureSet) + sizeBuffer.clear() + return size + } + + suspend fun write(sizePrefix: Boolean = true, f: suspend (ChainedByteBuffer) -> Unit) { + f(sendBuffer) + if (sizePrefix) { + sizeBuffer.clear() + sizeBuffer.putInt(sendBuffer.size) + sizeBuffer.flip() + channel.write(sizeBuffer) + sizeBuffer.clear() + } + channel.write(sendBuffer) + channel.flush() + sendBuffer.clear() + } + + suspend fun <T> read(amount: Int? = null, f: suspend (ByteBuffer) -> T): T { + val amount1 = readAmount(amount) + val messageBuffer = ByteBuffer.allocateDirect(minOf(amount1, 65 * 1024 * 1024)) + channel.read(messageBuffer) + messageBuffer.flip() + return f(messageBuffer) + } + + println("Writing protocol") + write(sizePrefix = false) { + UIntSerializer.serialize(it, 0x42b3_3f00u or 0x03u, connectionFeatureSet) + IntSerializer.serialize(it, 2, connectionFeatureSet) + UIntSerializer.serialize(it, 0x8000_0000u, connectionFeatureSet) + } + + println("Reading protocol") + read(4) { + println(ProtocolInfoSerializer.deserialize(it, connectionFeatureSet)) + println(channel.tlsInfo.value) + channel.enableTLS(context) + println(channel.tlsInfo.value) + channel.enableCompression() + } + println("Writing clientInit") + write { + HandshakeMapSerializer.serialize( + it, + ClientInitSerializer.serialize(ClientInit( + clientVersion = "Quasseldroid test", + buildDate = "Never", + clientFeatures = flags(), + featureList = emptyList() + )), + connectionFeatureSet + ) + } + read { + val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet) + println(data) + when (data["MsgType"].into<String>()) { + "ClientInitAck" -> println(ClientInitAckSerializer.deserialize(data)) + "ClientInitReject" -> println(ClientInitRejectSerializer.deserialize(data)) + } + } } } } diff --git a/bitflags/src/main/java/de/kuschku/bitflags/Flags.kt b/bitflags/src/main/java/de/kuschku/bitflags/Flags.kt index 23fa4e46bd79597996dee4a4ce46d073e7db0b1d..a06610add197c1d79bcf412db7943d3317e2cf40 100644 --- a/bitflags/src/main/java/de/kuschku/bitflags/Flags.kt +++ b/bitflags/src/main/java/de/kuschku/bitflags/Flags.kt @@ -23,3 +23,6 @@ interface Flags<T, U : Flag<T>> { operator fun get(value: T): U? fun all(): Collection<U> } + +inline fun <reified T> flags(vararg values: T) where T : Flag<*>, T : Enum<T> = setOf(*values) +inline fun <reified T> flags(values: Collection<T>) where T : Flag<*>, T : Enum<T> = values.toSet() diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..db699001ac4c963bbf2b958749070583e101d601 --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt @@ -0,0 +1,42 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.connection + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.serializers.primitive.UByteSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.UShortSerializer +import java.nio.ByteBuffer + +object ProtocolInfoSerializer { + fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo, featureSet: FeatureSet) { + UByteSerializer.serialize(buffer, data.flags, featureSet) + UShortSerializer.serialize(buffer, data.data, featureSet) + UByteSerializer.serialize(buffer, data.version, featureSet) + } + + fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): ProtocolInfo { + return ProtocolInfo( + UByteSerializer.deserialize(buffer, featureSet), + UShortSerializer.deserialize(buffer, featureSet), + UByteSerializer.deserialize(buffer, featureSet) + ) + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt new file mode 100644 index 0000000000000000000000000000000000000000..9ea9379f526e95db5da11625279c2a62d707aa8f --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt @@ -0,0 +1,59 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.features + +import de.kuschku.bitflags.flags + +class FeatureSet internal constructor( + private val features: Set<QuasselFeature>, + private val additional: Set<QuasselFeatureName> = emptySet() +) { + fun enabled(feature: QuasselFeature) = features.contains(feature) + + fun featureList(): List<QuasselFeatureName> = + features.map(QuasselFeature::feature) + additional + + fun legacyFeatures(): LegacyFeatures = + flags(features.mapNotNull(LegacyFeature.Companion::get)) + + companion object { + fun parse( + legacy: LegacyFeatures, + features: Collection<QuasselFeatureName> + ) = FeatureSet( + features = parseFeatures(legacy) + parseFeatures(features), + additional = unknownFeatures(features) + ) + + fun build(vararg features: QuasselFeature) = FeatureSet(features.toSet()) + fun build(features: Set<QuasselFeature>) = FeatureSet(features) + fun all() = build(*QuasselFeature.values()) + fun empty() = build() + + private fun parseFeatures(features: LegacyFeatures) = + features.map(LegacyFeature::feature).toSet() + + private fun parseFeatures(features: Collection<QuasselFeatureName>) = + features.mapNotNull(QuasselFeature.Companion::valueOf).toSet() + + private fun unknownFeatures(features: Collection<QuasselFeatureName>) = + features.filter { QuasselFeature.valueOf(it) == null }.toSet() + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt index 362e6b6dd3e46c457f497ebd15d84941b7832d1a..eefd1480561b6469eba509c78069c9afe45b2347 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt @@ -31,35 +31,41 @@ import de.kuschku.bitflags.Flags * * This list should be cleaned up after every protocol break, as we can assume them to be present then. */ -enum class LegacyFeature(override val value: UInt): Flag<UInt> { - SynchronizedMarkerLine(0x0001u), - SaslAuthentication(0x0002u), - SaslExternal(0x0004u), - HideInactiveNetworks(0x0008u), - PasswordChange(0x0010u), +enum class LegacyFeature( + override val value: UInt, + val feature: QuasselFeature, +): Flag<UInt> { + SynchronizedMarkerLine(0x0001u, QuasselFeature.SynchronizedMarkerLine), + SaslAuthentication(0x0002u, QuasselFeature.SaslAuthentication), + SaslExternal(0x0004u, QuasselFeature.SaslExternal), + HideInactiveNetworks(0x0008u, QuasselFeature.HideInactiveNetworks), + PasswordChange(0x0010u, QuasselFeature.PasswordChange), /** IRCv3 capability negotiation, account tracking */ - CapNegotiation(0x0020u), + CapNegotiation(0x0020u, QuasselFeature.CapNegotiation), /** IRC server SSL validation */ - VerifyServerSSL(0x0040u), + VerifyServerSSL(0x0040u, QuasselFeature.VerifyServerSSL), /** IRC server custom message rate limits */ - CustomRateLimits(0x0080u), - DccFileTransfer(0x0100u), + CustomRateLimits(0x0080u, QuasselFeature.CustomRateLimits), + DccFileTransfer(0x0100u, QuasselFeature.DccFileTransfer), /** Timestamp formatting in away (e.g. %%hh:mm%%) */ - AwayFormatTimestamp(0x0200u), + AwayFormatTimestamp(0x0200u, QuasselFeature.AwayFormatTimestamp), /** Whether or not the core supports auth backends. */ - Authenticators(0x0400u), + Authenticators(0x0400u, QuasselFeature.Authenticators), /** Sync buffer activity status */ - BufferActivitySync(0x0800u), + BufferActivitySync(0x0800u, QuasselFeature.BufferActivitySync), /** Core-Side highlight configuration and matching */ - CoreSideHighlights(0x1000u), + CoreSideHighlights(0x1000u, QuasselFeature.CoreSideHighlights), /** Show prefixes for senders in backlog */ - SenderPrefixes(0x2000u), + SenderPrefixes(0x2000u, QuasselFeature.SenderPrefixes), /** Supports RPC call disconnectFromCore to remotely disconnect a client */ - RemoteDisconnect(0x4000u), + RemoteDisconnect(0x4000u, QuasselFeature.RemoteDisconnect), /** Transmit features as list of strings */ - ExtendedFeatures(0x8000u); + ExtendedFeatures(0x8000u, QuasselFeature.ExtendedFeatures); companion object : Flags<UInt, LegacyFeature> { + private val features = values().associateBy(LegacyFeature::feature) + fun get(feature: QuasselFeature) = features[feature] + private val values = values().associateBy(LegacyFeature::value) override fun get(value: UInt) = values[value] override fun all() = values.values diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/ExtendedFeature.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/QuasselFeature.kt similarity index 87% rename from protocol/src/main/java/de/kuschku/libquassel/protocol/features/ExtendedFeature.kt rename to protocol/src/main/java/de/kuschku/libquassel/protocol/features/QuasselFeature.kt index f5cd94c1ea83a293b2fac96665655a539d037276..e827769a9fb5a2172e9d732d833bcf208d3e3811 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/ExtendedFeature.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/QuasselFeature.kt @@ -19,11 +19,11 @@ package de.kuschku.libquassel.protocol.features -inline class ExtendedFeatureName( +inline class QuasselFeatureName( val name: String, ) -enum class ExtendedFeature { +enum class QuasselFeature { SynchronizedMarkerLine, SaslAuthentication, SaslExternal, @@ -64,5 +64,11 @@ enum class ExtendedFeature { /** CoreInfo dynamically updated using signals */ SyncedCoreInfo; - fun feature(): ExtendedFeatureName = ExtendedFeatureName(name) + val feature = QuasselFeatureName(name) + + companion object { + private val values = values().associateBy(QuasselFeature::feature) + @JvmStatic + fun valueOf(name: QuasselFeatureName): QuasselFeature? = values[name] + } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ByteBufferUtil.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ByteBufferUtil.kt new file mode 100644 index 0000000000000000000000000000000000000000..b2e8f7c4ce86a4d1fec34cda44cff24d3152291d --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ByteBufferUtil.kt @@ -0,0 +1,69 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.io + +import java.nio.ByteBuffer + +fun copyData(from: ByteBuffer, to: ByteBuffer, amount: Int = -1) { + val actualAmount = + if (amount >= 0) minOf(from.remaining(), to.remaining(), amount) + else minOf(from.remaining(), to.remaining()) + for (i in 0 until actualAmount) { + to.put(from.get()) + }/* + if (actualAmount > 0) { + val fromLimit = from.limit() + val toLimit = to.limit() + from.limit(from.position() + actualAmount) + to.limit(to.position() + actualAmount) + to.put(from) + from.limit(fromLimit) + to.limit(toLimit) + } + */ +} + +val alphabet = charArrayOf( + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +) + +fun ByteBuffer.contentToString(): String { + mark() + var result = "" + while (remaining() > 0) { + val byte = get() + val upperNibble = byte.toInt() shr 4 + val lowerNibble = byte.toInt() % 16 + result += alphabet[(upperNibble + 16) % 16] + result += alphabet[(lowerNibble + 16) % 16] + } + reset() + return result +} + +fun ByteBuffer.print() = println(contentToString()) + +fun copyData(from: ByteBuffer, amount: Int) = ByteBuffer.allocateDirect(amount).also { + if (amount > 0) { + copyData(from, it, amount) + it.clear() + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ChainedByteBuffer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ChainedByteBuffer.kt index ac9622e7d11601c05cdab05ad84166009244e3a2..88e6997edbb0240361ff03df4a9e5b7bcc23e118 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ChainedByteBuffer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/io/ChainedByteBuffer.kt @@ -48,9 +48,14 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: this.size += size } - fun nextBuffer(length: Int = 1): ByteBuffer { + fun <T> withBuffer(length: Int = 0, f: (ByteBuffer) -> T) : T{ ensureSpace(length) - return bufferList.last() + val buffer = bufferList.last() + val positionBefore = buffer.position() + val result = f(buffer) + val positionAfter = buffer.position() + size += (positionAfter - positionBefore) + return result } fun put(value: Byte) { @@ -99,15 +104,7 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: ensureSpace(value.remaining()) while (value.remaining() > 0) { - val buffer = bufferList.last() - if (buffer.remaining() >= value.remaining()) { - buffer.put(value) - } else { - val oldLimit = value.limit() - value.limit(value.position() + buffer.remaining()) - buffer.put(value) - value.limit(oldLimit) - } + copyData(value, bufferList.last()) } } @@ -124,12 +121,16 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: fun buffers() = sequence { for (buffer in bufferList) { buffer.flip() + val position = buffer.position() + val limit = buffer.limit() yield(buffer) + buffer.position(position) + buffer.limit(limit) } } fun toBuffer(): ByteBuffer { - val byteBuffer = allocate(size) + val byteBuffer = allocate(bufferSize * bufferList.size) for (buffer in bufferList) { buffer.flip() byteBuffer.put(buffer) diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/io/StringEncoder.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/io/StringEncoder.kt index d86c16efa05d36b2b659f61792da6ffb084152a6..280f7b0a936402664ded2f37ee05334d05ff279a 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/io/StringEncoder.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/io/StringEncoder.kt @@ -30,59 +30,75 @@ class StringEncoder(charset: Charset) { private val charBuffer = CharBuffer.allocate(1024) private fun charBuffer(length: Int): CharBuffer = - if (length < 1024) charBuffer + if (length < 1024) charBuffer.clear() else CharBuffer.allocate(length) - private fun encodingLength(length: Int, nullLimited: Boolean) = - if (nullLimited) length + 1 - else length - - private fun decodingLength(length: Int, nullLimited: Boolean) = - if (nullLimited) length - 1 - else length - - fun encode(data: String?, target: ChainedByteBuffer, nullLimited: Boolean = false) { + fun encode(data: String?, target: ChainedByteBuffer) { if (data == null) return - val charBuffer = charBuffer(encodingLength(data.length, nullLimited)) + val charBuffer = charBuffer(data.length) charBuffer.put(data) - if (nullLimited) charBuffer.put(0.toChar()) charBuffer.flip() encoder.reset() var result: CoderResult do { - result = encoder.encode(charBuffer, target.nextBuffer(data.length), true) + result = target.withBuffer(charBuffer.remaining()) { + encoder.encode(charBuffer, it, true) + } } while (result == CoderResult.OVERFLOW) } - fun encode(data: String?, nullLimited: Boolean = false): ByteBuffer { - if (data == null) return ByteBuffer.allocate(0) + fun encode(data: String?): ByteBuffer { + if (data == null) { + return ByteBuffer.allocateDirect(0) + } - val charBuffer = charBuffer(encodingLength(data.length, nullLimited)) + val charBuffer = charBuffer(data.length) charBuffer.put(data) - if (nullLimited) charBuffer.put(0.toChar()) charBuffer.flip() encoder.reset() return encoder.encode(charBuffer) } - fun decode(source: ByteBuffer, length: Int, nullLimited: Boolean = false): String { - val charBuffer = charBuffer(decodingLength(length, nullLimited)) + fun encodeChar(data: Char?, target: ChainedByteBuffer) { + if (data == null) { + target.putShort(0) + } else { + target.putChar(data) + } + } + + fun decode(source: ByteBuffer, length: Int): String { + val charBuffer = charBuffer(length) val oldlimit = source.limit() - source.limit(decodingLength(source.position() + length, nullLimited)) + source.limit(source.position() + length) decoder.reset() - decoder.decode(source, charBuffer, true) + decoder.decode(source, charBuffer, true).also { + if (it.isError) { + source.position(source.position() + it.length()) + } + } source.limit(oldlimit) charBuffer.flip() return charBuffer.toString() } - fun decode(source: ByteBuffer, nullLimited: Boolean = false): String { - val charBuffer = charBuffer(decodingLength(source.remaining(), nullLimited)) - source.limit(decodingLength(source.capacity(), nullLimited)) + fun decode(source: ByteBuffer): String { + println("Called to decode ${source.contentToString()}") + val charBuffer = charBuffer(source.remaining()) decoder.reset() - decoder.decode(source, charBuffer, true) + decoder.decode(source, charBuffer, true).also { + if (it.isError) { + println("Encountered error: $it") + source.position(source.position() + it.length()) + } + } charBuffer.flip() + println("Result: $charBuffer") return charBuffer.toString() } + + fun decodeChar(source: ByteBuffer): Char { + return source.getChar() + } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInit.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInit.kt index 7bc0a07b558bbfbfbdc6b8b32e4c39e18c7f15db..433f354b0e8dac0a2c1a84fe301178cc870b3288 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInit.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInit.kt @@ -20,11 +20,11 @@ package de.kuschku.libquassel.protocol.messages.handshake import de.kuschku.libquassel.protocol.features.LegacyFeatures -import de.kuschku.libquassel.protocol.features.ExtendedFeatureName +import de.kuschku.libquassel.protocol.features.QuasselFeatureName data class ClientInit( val clientVersion: String?, val buildDate: String?, val clientFeatures: LegacyFeatures, - val featureList: List<ExtendedFeatureName> + val featureList: List<QuasselFeatureName> ) diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInitAck.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInitAck.kt index cd2e16d444d7996de05061dca866f77e67b91998..ec75b1dba2252c048941876926a0463ebaa008fb 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInitAck.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/messages/handshake/ClientInitAck.kt @@ -19,14 +19,14 @@ package de.kuschku.libquassel.protocol.messages.handshake -import de.kuschku.libquassel.protocol.features.ExtendedFeatureName import de.kuschku.libquassel.protocol.features.LegacyFeatures +import de.kuschku.libquassel.protocol.features.QuasselFeatureName import de.kuschku.libquassel.protocol.variant.QVariantList data class ClientInitAck( - val coreFeatures: LegacyFeatures, - val coreConfigured: Boolean?, - val backendInfo: QVariantList, - val authenticatorInfo: QVariantList, - val featureList: List<ExtendedFeatureName> + val coreFeatures: LegacyFeatures, + val coreConfigured: Boolean?, + val backendInfo: QVariantList, + val authenticatorInfo: QVariantList, + val featureList: List<QuasselFeatureName> ) diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt index 76b74d167ace1d8a1dc6eb9d99cdd5a25e8f9166..b9754ccff75ea5091865b465ebac543b3c3beba2 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt @@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.serializers.handshake import de.kuschku.bitflags.toBits import de.kuschku.bitflags.toFlag -import de.kuschku.libquassel.protocol.features.ExtendedFeatureName +import de.kuschku.libquassel.protocol.features.QuasselFeatureName import de.kuschku.libquassel.protocol.features.LegacyFeature import de.kuschku.libquassel.protocol.messages.handshake.ClientInitAck import de.kuschku.libquassel.protocol.variant.* @@ -42,6 +42,7 @@ object ClientInitAckSerializer : HandshakeSerializer<ClientInitAck> { authenticatorInfo = data["Authenticators"].into(emptyList()), coreConfigured = data["Configured"].into(), featureList = data["FeatureList"].into<QStringList>(emptyList()) - .map(::ExtendedFeatureName), + .filterNotNull() + .map(::QuasselFeatureName), ) } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt index cb44de100afc62e6e88876cf7de2cb4ff2f46c16..91488db6fa06b8dca21aa6c178aad545f0310a08 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt @@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.serializers.handshake import de.kuschku.bitflags.toBits import de.kuschku.bitflags.toFlag -import de.kuschku.libquassel.protocol.features.ExtendedFeatureName +import de.kuschku.libquassel.protocol.features.QuasselFeatureName import de.kuschku.libquassel.protocol.features.LegacyFeature import de.kuschku.libquassel.protocol.messages.handshake.ClientInit import de.kuschku.libquassel.protocol.variant.QVariantMap @@ -36,7 +36,7 @@ object ClientInitSerializer : HandshakeSerializer<ClientInit> { "ClientDate" to qVariant(data.buildDate, QtType.QString), "Features" to qVariant(data.clientFeatures.toBits(), QtType.UInt), "FeatureList" to qVariant( - data.featureList.map(ExtendedFeatureName::name), + data.featureList.map(QuasselFeatureName::name), QtType.QStringList ), ) @@ -46,7 +46,7 @@ object ClientInitSerializer : HandshakeSerializer<ClientInit> { clientVersion = data["ClientVersion"].into(), buildDate = data["ClientDate"].into(), clientFeatures = LegacyFeature.toFlag(data["Features"].into<UInt>()), - featureList = data["FeatureList"].into(emptyList()), + featureList = data["FeatureList"].into(emptyList<String>()).map(::QuasselFeatureName), ) } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/HandshakeMapSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/HandshakeMapSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..5a8484134632c01777e8b7a1e6d7dfa98dd51a7e --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/handshake/HandshakeMapSerializer.kt @@ -0,0 +1,54 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.handshake + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.serializers.primitive.QVariantListSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.StringSerializerAscii +import de.kuschku.libquassel.protocol.serializers.primitive.StringSerializerUtf8 +import de.kuschku.libquassel.protocol.variant.* +import java.nio.ByteBuffer + +object HandshakeMapSerializer : QtSerializer<QVariantMap> { + override val qtType = QtType.QVariantMap + @Suppress("UNCHECKED_CAST") + override val javaType: Class<out QVariantMap> = Map::class.java as Class<QVariantMap> + + override fun serialize(buffer: ChainedByteBuffer, data: QVariantMap, featureSet: FeatureSet) { + val list: QVariantList = data.entries.flatMap { (key, value) -> + val encodedKey = StringSerializerUtf8.serializeRaw(key) + listOf(qVariant(encodedKey, QtType.QByteArray), value) + } + + QVariantListSerializer.serialize(buffer, list, featureSet) + } + + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): QVariantMap { + val list = QVariantListSerializer.deserialize(buffer, featureSet) + return (list.indices step 2).map { + val encodedKey = list[it].into<ByteBuffer>(ByteBuffer.allocateDirect(0)) + val value = list[it+1] + + Pair(StringSerializerUtf8.deserializeRaw(encodedKey), value) + }.toMap() + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializer.kt index c31721a499c2cf501010822d2e014ab7df6b4f19..0e0b73a6a2733210c97398126268faed1c44d858 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,14 +28,14 @@ object BoolSerializer : QtSerializer<Boolean> { override val qtType: QtType = QtType.Bool override val javaType: Class<Boolean> = Boolean::class.java - override fun serialize(buffer: ChainedByteBuffer, data: Boolean) { + override fun serialize(buffer: ChainedByteBuffer, data: Boolean, featureSet: FeatureSet) { buffer.put( if (data) 0x01.toByte() else 0x00.toByte() ) } - override fun deserialize(buffer: ByteBuffer): Boolean { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Boolean { return buffer.get() != 0x00.toByte() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..1aa629ec7d273262f66c4c6dcd7e5479696fdbbf --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializer.kt @@ -0,0 +1,49 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.copyData +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.variant.QtType +import java.nio.ByteBuffer + +object ByteBufferSerializer : QtSerializer<ByteBuffer?> { + override val qtType: QtType = QtType.QByteArray + override val javaType: Class<out ByteBuffer?> = ByteBuffer::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: ByteBuffer?, featureSet: FeatureSet) { + IntSerializer.serialize(buffer, data?.remaining() ?: 0, featureSet) + if (data != null) { + buffer.put(data) + } + } + + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): ByteBuffer? { + val length = IntSerializer.deserialize(buffer, featureSet) + if (length < 0) { + return null + } + val result = copyData(buffer, length) + result.limit(length) + return result + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializer.kt index 7866c1bb1fc9d90b983c40bb200d182343a2c844..e996faf6e43370032f993b83a47e117c85a87915 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object ByteSerializer : QtSerializer<Byte> { override val qtType: QtType = QtType.Char override val javaType: Class<Byte> = Byte::class.java - override fun serialize(buffer: ChainedByteBuffer, data: Byte) { + override fun serialize(buffer: ChainedByteBuffer, data: Byte, featureSet: FeatureSet) { buffer.put(data) } - override fun deserialize(buffer: ByteBuffer): Byte { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Byte { return buffer.get() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializer.kt index 2327a2ffae103ed7d683217f72d5d13f478e22e6..98a38bea7cf7b03af192980564a909e43c914cea 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object IntSerializer : QtSerializer<Int> { override val qtType: QtType = QtType.Int override val javaType: Class<Int> = Int::class.java - override fun serialize(buffer: ChainedByteBuffer, data: Int) { + override fun serialize(buffer: ChainedByteBuffer, data: Int, featureSet: FeatureSet) { buffer.putInt(data) } - override fun deserialize(buffer: ByteBuffer): Int { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Int { return buffer.getInt() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializer.kt index 58b441caeb9975ef2a39d58388ccb6400bc7d57e..82bfdcff41d6b1d1efcaf08c27fd59bb880aeb3b 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object LongSerializer : QtSerializer<Long> { override val qtType: QtType = QtType.Long override val javaType: Class<Long> = Long::class.java - override fun serialize(buffer: ChainedByteBuffer, data: Long) { + override fun serialize(buffer: ChainedByteBuffer, data: Long, featureSet: FeatureSet) { buffer.putLong(data) } - override fun deserialize(buffer: ByteBuffer): Long { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Long { return buffer.getLong() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ProtocolInfoSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializer.kt similarity index 56% rename from protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ProtocolInfoSerializer.kt rename to protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializer.kt index 228d917d4160f231b4e085a6cf1832d4fe3147b3..e8147e54be3c4baea5788bfd4af27c9ba209159f 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ProtocolInfoSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializer.kt @@ -19,26 +19,25 @@ package de.kuschku.libquassel.protocol.serializers.primitive -import de.kuschku.libquassel.protocol.connection.ProtocolInfo +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.StringEncoder import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer +import kotlin.concurrent.getOrSet -object ProtocolInfoSerializer : QtSerializer<ProtocolInfo> { - override val qtType: QtType = QtType.UserType - override val javaType: Class<ProtocolInfo> = ProtocolInfo::class.java +object QCharSerializer : QtSerializer<Char> { + override val qtType: QtType = QtType.QChar + override val javaType: Class<out Char> = Char::class.java - override fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo) { - UByteSerializer.serialize(buffer, data.flags) - UShortSerializer.serialize(buffer, data.data) - UByteSerializer.serialize(buffer, data.version) + private val encoderLocal = ThreadLocal<StringEncoder>() + private fun encoder() = encoderLocal.getOrSet { StringEncoder(Charsets.UTF_16BE) } + + override fun serialize(buffer: ChainedByteBuffer, data: Char, featureSet: FeatureSet) { + encoder().encodeChar(data, buffer) } - override fun deserialize(buffer: ByteBuffer): ProtocolInfo { - return ProtocolInfo( - UByteSerializer.deserialize(buffer), - UShortSerializer.deserialize(buffer), - UByteSerializer.deserialize(buffer) - ) + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Char { + return encoder().decodeChar(buffer) } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QStringListSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QStringListSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..70779d2899844d28d98c541bb00d609b4cd69e21 --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QStringListSerializer.kt @@ -0,0 +1,50 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.variant.QStringList +import de.kuschku.libquassel.protocol.variant.QVariantList +import de.kuschku.libquassel.protocol.variant.QVariant_ +import de.kuschku.libquassel.protocol.variant.QtType +import java.nio.ByteBuffer + +object QStringListSerializer : QtSerializer<QStringList> { + override val qtType = QtType.QStringList + @Suppress("UNCHECKED_CAST") + override val javaType: Class<QStringList> = List::class.java as Class<QStringList> + + override fun serialize(buffer: ChainedByteBuffer, data: QStringList, featureSet: FeatureSet) { + IntSerializer.serialize(buffer, data.size, featureSet) + data.forEach { + StringSerializerUtf16.serialize(buffer, it, featureSet) + } + } + + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): QStringList { + val result = mutableListOf<String?>() + val length = IntSerializer.deserialize(buffer, featureSet) + for (i in 0 until length) { + result.add(StringSerializerUtf16.deserialize(buffer, featureSet)) + } + return result + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantListSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantListSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..b77e47fbb8c8e06e8ba0df081f4e671d998f55e4 --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantListSerializer.kt @@ -0,0 +1,49 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.variant.QVariantList +import de.kuschku.libquassel.protocol.variant.QVariant_ +import de.kuschku.libquassel.protocol.variant.QtType +import java.nio.ByteBuffer + +object QVariantListSerializer : QtSerializer<QVariantList> { + override val qtType = QtType.QVariantList + @Suppress("UNCHECKED_CAST") + override val javaType: Class<QVariantList> = List::class.java as Class<QVariantList> + + override fun serialize(buffer: ChainedByteBuffer, data: QVariantList, featureSet: FeatureSet) { + IntSerializer.serialize(buffer, data.size, featureSet) + data.forEach { + QVariantSerializer.serialize(buffer, it, featureSet) + } + } + + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): QVariantList { + val result = mutableListOf<QVariant_>() + val length = IntSerializer.deserialize(buffer, featureSet) + for (i in 0 until length) { + result.add(QVariantSerializer.deserialize(buffer, featureSet)) + } + return result + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantMapSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantMapSerializer.kt index dc9480e294f8dfb18138bc4603ea1dc01fa6dee4..956e58e26953465ebc0068672a4fe9c6cc555af9 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantMapSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantMapSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QVariantMap import de.kuschku.libquassel.protocol.variant.QVariant_ @@ -30,19 +31,21 @@ object QVariantMapSerializer : QtSerializer<QVariantMap> { @Suppress("UNCHECKED_CAST") override val javaType: Class<out QVariantMap> = Map::class.java as Class<QVariantMap> - override fun serialize(buffer: ChainedByteBuffer, data: QVariantMap) { - IntSerializer.serialize(buffer, data.size) + override fun serialize(buffer: ChainedByteBuffer, data: QVariantMap, featureSet: FeatureSet) { + IntSerializer.serialize(buffer, data.size, featureSet) data.entries.forEach { (key, value) -> - StringSerializerUtf16.serialize(buffer, key) - QVariantSerializer.serialize(buffer, value) + StringSerializerUtf16.serialize(buffer, key, featureSet) + QVariantSerializer.serialize(buffer, value, featureSet) } } - override fun deserialize(buffer: ByteBuffer): QVariantMap { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): QVariantMap { val result = mutableMapOf<String, QVariant_>() - val length = IntSerializer.deserialize(buffer) + val length = IntSerializer.deserialize(buffer, featureSet) for (i in 0 until length) { - result[StringSerializerUtf16.deserialize(buffer) ?: ""] = QVariantSerializer.deserialize(buffer) + val key = StringSerializerUtf16.deserialize(buffer, featureSet) ?: "" + val value = QVariantSerializer.deserialize(buffer, featureSet) + result[key] = value } return result } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt index 4481125d9f28e70b835f8b5afddc75b569e42c3e..b3ec8e15e38524dc414689701c732189c18037e8 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.serializers.* import de.kuschku.libquassel.protocol.variant.QVariant @@ -31,45 +32,45 @@ object QVariantSerializer : QtSerializer<QVariant_> { override val qtType = QtType.QVariant override val javaType: Class<QVariant_> = QVariant::class.java - override fun serialize(buffer: ChainedByteBuffer, data: QVariant_) { - IntSerializer.serialize(buffer, data.serializer.qtType.id) - BoolSerializer.serialize(buffer, false) + override fun serialize(buffer: ChainedByteBuffer, data: QVariant_, featureSet: FeatureSet) { + IntSerializer.serialize(buffer, data.serializer.qtType.id, featureSet) + BoolSerializer.serialize(buffer, false, featureSet) if (data is QVariant.Custom && data.serializer.qtType == QtType.UserType) { - StringSerializerAscii.serialize(buffer, data.serializer.quasselType.typeName) + StringSerializerAscii.serialize(buffer, data.serializer.quasselType.typeName, featureSet) } - data.serialize(buffer) + data.serialize(buffer, featureSet) } - override fun deserialize(buffer: ByteBuffer): QVariant_ { - val rawType = IntSerializer.deserialize(buffer) + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): QVariant_ { + val rawType = IntSerializer.deserialize(buffer, featureSet) val qtType = QtType.of(rawType) ?: throw NoSerializerForTypeException(rawType, null) // isNull, but we ignore it as it has no meaning - BoolSerializer.deserialize(buffer) + BoolSerializer.deserialize(buffer, featureSet) return if (qtType == QtType.UserType) { - val name = StringSerializerAscii.deserialize(buffer) + val name = StringSerializerAscii.deserialize(buffer, featureSet) val quasselType = QuasselType.of(name) ?: throw NoSerializerForTypeException(qtType.id, name) - deserialize(quasselType, buffer) + deserialize(quasselType, buffer, featureSet) } else { - deserialize(qtType, buffer) + deserialize(qtType, buffer, featureSet) } } @Suppress("UNCHECKED_CAST") - private fun deserialize(type: QtType, buffer: ByteBuffer): QVariant_ { + private fun deserialize(type: QtType, buffer: ByteBuffer, featureSet: FeatureSet): QVariant_ { val serializer = Serializers[type] ?: throw NoSerializerForTypeException(type) - val value = serializer.deserialize(buffer) - return QVariant.of(value, serializer as QuasselSerializer<Any?>) + val value = serializer.deserialize(buffer, featureSet) + return QVariant.of(value, serializer as QtSerializer<Any?>) } @Suppress("UNCHECKED_CAST") - private fun deserialize(type: QuasselType, buffer: ByteBuffer): QVariant_ { + private fun deserialize(type: QuasselType, buffer: ByteBuffer, featureSet: FeatureSet): QVariant_ { val serializer = Serializers[type] ?: throw NoSerializerForTypeException(type) - val value = serializer.deserialize(buffer) + val value = serializer.deserialize(buffer, featureSet) return QVariant.of(value, serializer as QuasselSerializer<Any?>) } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt index 89854b79d26a41349572fd79e38eda9dcccb28f3..dfc7eeccb472e15df824cfeea12d7a14ce2d26bc 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -26,6 +27,6 @@ import java.nio.ByteBuffer interface QtSerializer<T> { val qtType: QtType val javaType: Class<out T> - fun serialize(buffer: ChainedByteBuffer, data: T) - fun deserialize(buffer: ByteBuffer): T + fun serialize(buffer: ChainedByteBuffer, data: T, featureSet: FeatureSet) + fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): T } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt index b3e4a711fef08b8ccfd3b8f42727872a6d7bda2f..e870e9a358a77b014b13e7d2c7e215015546e701 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt @@ -36,8 +36,12 @@ object Serializers { UIntSerializer, LongSerializer, ULongSerializer, + ByteBufferSerializer, StringSerializerUtf16, + QCharSerializer, + QStringListSerializer, QVariantSerializer, + QVariantListSerializer, QVariantMapSerializer, ).associateBy(QtSerializer<*>::qtType) diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializer.kt index e429280eb9d5dd7d12a231b1c265f10d1a71cb6a..409beba5f5d83466cedae5ce845f678e286a5ef7 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object ShortSerializer : QtSerializer<Short> { override val qtType: QtType = QtType.Short override val javaType: Class<Short> = Short::class.java - override fun serialize(buffer: ChainedByteBuffer, data: Short) { + override fun serialize(buffer: ChainedByteBuffer, data: Short, featureSet: FeatureSet) { buffer.putShort(data) } - override fun deserialize(buffer: ByteBuffer): Short { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Short { return buffer.getShort() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..56bfcf60f3e27988765ec62beb9df1e3de382f8b --- /dev/null +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt @@ -0,0 +1,86 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.StringEncoder +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.variant.QtType +import java.nio.ByteBuffer +import java.nio.charset.Charset +import kotlin.concurrent.getOrSet + +abstract class StringSerializer( + private val charset: Charset, + private val nullLimited: Boolean = false, +) : QtSerializer<String?> { + override val qtType = QtType.QString + override val javaType: Class<out String> = String::class.java + + private val encoderLocal = ThreadLocal<StringEncoder>() + private fun encoder() = encoderLocal.getOrSet { StringEncoder(charset) } + + private inline fun addNullBytes(before: Int) = if (nullLimited) before + 1 else before + private inline fun removeNullBytes(before: Int) = if (nullLimited) before - 1 else before + + override fun serialize(buffer: ChainedByteBuffer, data: String?, featureSet: FeatureSet) { + if (data == null) { + IntSerializer.serialize(buffer, -1, featureSet) + } else { + val encodedData = encoder().encode(data) + IntSerializer.serialize(buffer, addNullBytes(encodedData.remaining()), featureSet) + buffer.put(encodedData) + if (nullLimited) { + buffer.put(0) + } + } + } + + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): String? { + val length = IntSerializer.deserialize(buffer, featureSet) + if (length < 0) { + return null + } + val result = encoder().decode(buffer, removeNullBytes(length)) + if (nullLimited) { + buffer.position(addNullBytes(buffer.position())) + } + return result + } + + fun serializeRaw(data: String?): ByteBuffer { + val result = encoder().encode(data) + if (nullLimited) { + val buffer = ByteBuffer.allocateDirect(result.remaining() + 1) + buffer.put(result) + buffer.clear() + return buffer + } + return result + } + + fun deserializeRaw(data: ByteBuffer): String { + if (nullLimited) { + data.limit(removeNullBytes(data.limit())) + } + return encoder().decode(data) + } +} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerAscii.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerAscii.kt index 2c5933c5321074c92225d0b26aeabda55f295ee9..f13a9a51fe5b809180df02c148e15d1172d3a797 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerAscii.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerAscii.kt @@ -19,31 +19,11 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.io.stringEncoderAscii import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer -object StringSerializerAscii : QtSerializer<String?> { - override val qtType = QtType.QString - override val javaType: Class<out String> = String::class.java +object StringSerializerAscii : StringSerializer(Charsets.ISO_8859_1, true) - override fun serialize(buffer: ChainedByteBuffer, data: String?) { - if (data == null) { - IntSerializer.serialize(buffer, -1) - } else { - val stringBuffer = stringEncoderAscii().encode(data, true) - IntSerializer.serialize(buffer, stringBuffer.remaining()) - buffer.put(stringBuffer) - } - } - - override fun deserialize(buffer: ByteBuffer): String? { - val length = IntSerializer.deserialize(buffer) - 1 - return if (length < 0) { - null - } else { - stringEncoderAscii().decode(buffer, length, true) - } - } -} diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt index add6cbf8c5fab4098287a3fe45f08c704bd06138..65114cd0caab15fb7028aef5eb6e58801067f1a0 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt @@ -19,31 +19,4 @@ package de.kuschku.libquassel.protocol.serializers.primitive -import de.kuschku.libquassel.protocol.io.ChainedByteBuffer -import de.kuschku.libquassel.protocol.io.stringEncoderUtf16 -import de.kuschku.libquassel.protocol.variant.QtType -import java.nio.ByteBuffer - -object StringSerializerUtf16 : QtSerializer<String?> { - override val qtType = QtType.QString - override val javaType: Class<out String> = String::class.java - - override fun serialize(buffer: ChainedByteBuffer, data: String?) { - if (data == null) { - IntSerializer.serialize(buffer, -1) - } else { - val stringBuffer = stringEncoderUtf16().encode(data) - IntSerializer.serialize(buffer, stringBuffer.remaining()) - buffer.put(stringBuffer) - } - } - - override fun deserialize(buffer: ByteBuffer): String? { - val length = IntSerializer.deserialize(buffer) - return if (length < 0) { - null - } else { - stringEncoderUtf16().decode(buffer, length) - } - } -} +object StringSerializerUtf16 : StringSerializer(Charsets.UTF_16BE) diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt index 6324179fe79aca4d196ac7a507ea697ad9b798cb..23c93e97607a95c36562dfa8b8c456c3f51abfde 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt @@ -19,32 +19,4 @@ package de.kuschku.libquassel.protocol.serializers.primitive -import de.kuschku.libquassel.protocol.io.ChainedByteBuffer -import de.kuschku.libquassel.protocol.io.stringEncoderUtf8 -import de.kuschku.libquassel.protocol.variant.QtType -import java.nio.ByteBuffer - -object StringSerializerUtf8 : QtSerializer<String?> { - override val qtType = QtType.QString - override val javaType: Class<out String> = String::class.java - - override fun serialize(buffer: ChainedByteBuffer, data: String?) { - if (data == null) { - IntSerializer.serialize(buffer, -1) - } else { - val stringBuffer = stringEncoderUtf8().encode(data) - IntSerializer.serialize(buffer, stringBuffer.remaining()) - buffer.put(stringBuffer) - } - } - - override fun deserialize(buffer: ByteBuffer): String? { - val length = IntSerializer.deserialize(buffer) - return if (length < 0) { - null - } else { - stringEncoderUtf8().decode(buffer, length) - } - } -} - +object StringSerializerUtf8 : StringSerializer(Charsets.UTF_8) diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializer.kt index fb5af5e06467ebf7fd929a0d34926b75b1ba8e01..e303014798af0df3dbda8cea5abea217815d8094 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object UByteSerializer : QtSerializer<UByte> { override val qtType: QtType = QtType.UChar override val javaType: Class<UByte> = UByte::class.java - override fun serialize(buffer: ChainedByteBuffer, data: UByte) { + override fun serialize(buffer: ChainedByteBuffer, data: UByte, featureSet: FeatureSet) { buffer.put(data.toByte()) } - override fun deserialize(buffer: ByteBuffer): UByte { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): UByte { return buffer.get().toUByte() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializer.kt index 35bdb1afec3b783df0e9a730651ff241bd45e3f0..39221efb9283fc6c1d8c0fea401cbd6fc13a9620 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object UIntSerializer : QtSerializer<UInt> { override val qtType: QtType = QtType.UInt override val javaType: Class<UInt> = UInt::class.java - override fun serialize(buffer: ChainedByteBuffer, data: UInt) { + override fun serialize(buffer: ChainedByteBuffer, data: UInt, featureSet: FeatureSet) { buffer.putInt(data.toInt()) } - override fun deserialize(buffer: ByteBuffer): UInt { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): UInt { return buffer.getInt().toUInt() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializer.kt index ffea3f6ea8dbb449b3b26e8845f816929a8677d5..92e3c68bfc314018d7e1207935dbe3efb7c01f5e 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object ULongSerializer : QtSerializer<ULong> { override val qtType: QtType = QtType.ULong override val javaType: Class<ULong> = ULong::class.java - override fun serialize(buffer: ChainedByteBuffer, data: ULong) { + override fun serialize(buffer: ChainedByteBuffer, data: ULong, featureSet: FeatureSet) { buffer.putLong(data.toLong()) } - override fun deserialize(buffer: ByteBuffer): ULong { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): ULong { return buffer.getLong().toULong() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializer.kt index 50b9564153a53db00d4535ccd714be90e779827b..ae6e66f577f092c85ce39191c858d017c073622e 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializer.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializer.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer import de.kuschku.libquassel.protocol.variant.QtType import java.nio.ByteBuffer @@ -27,11 +28,11 @@ object UShortSerializer : QtSerializer<UShort> { override val qtType: QtType = QtType.UShort override val javaType: Class<UShort> = UShort::class.java - override fun serialize(buffer: ChainedByteBuffer, data: UShort) { + override fun serialize(buffer: ChainedByteBuffer, data: UShort, featureSet: FeatureSet) { buffer.putShort(data.toShort()) } - override fun deserialize(buffer: ByteBuffer): UShort { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): UShort { return buffer.getShort().toUShort() } } diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt index b83839b90b6ef8c13415548f5c1d1f1871425157..6b6c01114dd5db1ccec6b487ab85af675148eebb 100644 --- a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt +++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt @@ -19,15 +19,19 @@ package de.kuschku.libquassel.protocol.variant +import de.kuschku.libquassel.protocol.features.FeatureSet import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.contentToString import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer import de.kuschku.libquassel.protocol.serializers.primitive.QuasselSerializer import de.kuschku.libquassel.protocol.serializers.primitive.serializerFor +import java.nio.ByteBuffer +import java.util.* typealias QVariant_ = QVariant<*> typealias QVariantList = List<QVariant_> typealias QVariantMap = Map<String, QVariant_> -typealias QStringList = List<String> +typealias QStringList = List<String?> sealed class QVariant<T> constructor( internal val data: T, @@ -35,7 +39,6 @@ sealed class QVariant<T> constructor( ) { class Typed<T> internal constructor(data: T, serializer: QtSerializer<T>) : QVariant<T>(data, serializer) { - override fun toString() = "QVariant.Typed(${serializer.qtType.serializableName}, $data)" override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Typed<*>) return false @@ -55,7 +58,6 @@ sealed class QVariant<T> constructor( class Custom<T> internal constructor(data: T, override val serializer: QuasselSerializer<T>) : QVariant<T>(data, serializer) { - override fun toString() = "QVariant.Custom(${serializer.quasselType}, $data)" override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Custom<*>) return false @@ -75,8 +77,18 @@ sealed class QVariant<T> constructor( fun value(): T = data - fun serialize(buffer: ChainedByteBuffer) { - serializer.serialize(buffer, data) + + fun serialize(buffer: ChainedByteBuffer, featureSet: FeatureSet) { + serializer.serialize(buffer, data, featureSet) + } + + override fun toString() = when (data) { + is ByteBuffer -> + "QVariant(${serializer::class.java.simpleName}, ${data.contentToString()})" + is Array<*> -> + "QVariant(${serializer::class.java.simpleName}, ${Arrays.toString(data)})" + else -> + "QVariant(${serializer::class.java.simpleName}, $data)" } companion object { diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..75328093677e98243e81cf75de125f7d198a6de2 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt @@ -0,0 +1,62 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.handshake + +import de.kuschku.bitflags.flags +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.messages.handshake.ClientInit +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testHandshakeSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testHandshakeSerializerEncoded +import org.junit.Test + +class ClientInitSerializerTest { + @Test + fun testSimple() { + val value = ClientInit( + clientVersion = "Quasseldroid test", + buildDate = "Never", + clientFeatures = flags(), + featureList = emptyList() + ) + + testHandshakeSerializerDirect(ClientInitSerializer, value) + testHandshakeSerializerEncoded(ClientInitSerializer, value) + + // @formatter:off + testDeserialize(ClientInitSerializer, value, byteBufferOf(0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x07u, 0x4Du, 0x73u, 0x67u, 0x54u, 0x79u, 0x70u, 0x65u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x14u, 0x00u, 0x43u, 0x00u, 0x6Cu, 0x00u, 0x69u, 0x00u, 0x65u, 0x00u, 0x6Eu, 0x00u, 0x74u, 0x00u, 0x49u, 0x00u, 0x6Eu, 0x00u, 0x69u, 0x00u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Du, 0x43u, 0x6Cu, 0x69u, 0x65u, 0x6Eu, 0x74u, 0x56u, 0x65u, 0x72u, 0x73u, 0x69u, 0x6Fu, 0x6Eu, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x22u, 0x00u, 0x51u, 0x00u, 0x75u, 0x00u, 0x61u, 0x00u, 0x73u, 0x00u, 0x73u, 0x00u, 0x65u, 0x00u, 0x6Cu, 0x00u, 0x64u, 0x00u, 0x72u, 0x00u, 0x6Fu, 0x00u, 0x69u, 0x00u, 0x64u, 0x00u, 0x20u, 0x00u, 0x74u, 0x00u, 0x65u, 0x00u, 0x73u, 0x00u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x43u, 0x6Cu, 0x69u, 0x65u, 0x6Eu, 0x74u, 0x44u, 0x61u, 0x74u, 0x65u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x4Eu, 0x00u, 0x65u, 0x00u, 0x76u, 0x00u, 0x65u, 0x00u, 0x72u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x08u, 0x46u, 0x65u, 0x61u, 0x74u, 0x75u, 0x72u, 0x65u, 0x73u, 0x00u, 0x00u, 0x00u, 0x03u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Bu, 0x46u, 0x65u, 0x61u, 0x74u, 0x75u, 0x72u, 0x65u, 0x4Cu, 0x69u, 0x73u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Bu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u)) + // @formatter:on + } + + @Test + fun testRealistic() { + val features = FeatureSet.all() + val value = ClientInit( + clientVersion = "Quasseldroid <a href=\"https://git.kuschku.de/justJanne/QuasselDroid-ng/commit/b622ad63056b6054b06e09f8e1f1ef2b0c3aaf9a\">v1.3.3</a>", + buildDate = "2020-04-27T22:21:17Z", + clientFeatures = features.legacyFeatures(), + featureList = features.featureList() + ) + + testHandshakeSerializerDirect(ClientInitSerializer, value) + testHandshakeSerializerEncoded(ClientInitSerializer, value) + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..64f50e7aff4aa83435315d31fdb560c4014cb0d7 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt @@ -0,0 +1,46 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Test + +class BoolSerializerTest { + @Test + fun testTrue() { + testQtSerializerDirect(BoolSerializer, true) + testQtSerializerVariant(BoolSerializer, true) + // @formatter:off + testDeserialize(BoolSerializer, true, byteBufferOf(1)) + // @formatter:on + } + + @Test + fun testFalse() { + testQtSerializerDirect(BoolSerializer, false) + testQtSerializerVariant(BoolSerializer, false) + // @formatter:off + testDeserialize(BoolSerializer, false, byteBufferOf(0)) + // @formatter:on + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..fa706ec99120284fccccdf7b8e7cb9413e3cfc7f --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt @@ -0,0 +1,46 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import org.junit.Test + +class ByteBufferSerializerTest { + @Test + fun testBaseCase() { + val value = byteBufferOf(0) + testQtSerializerDirect(ByteBufferSerializer, value, ByteBufferMatcher(value)) + // @formatter:off + testDeserialize(ByteBufferSerializer, ByteBufferMatcher(value), byteBufferOf(0, 0, 0, 1, 0)) + // @formatter:on + } + + @Test + fun testNormal() { + val value = byteBufferOf(1, 2, 3, 4, 5, 6, 7, 8, 9) + testQtSerializerDirect(ByteBufferSerializer, value, ByteBufferMatcher(value)) + // @formatter:off + testDeserialize(ByteBufferSerializer, ByteBufferMatcher(value), byteBufferOf(0, 0, 0, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9)) + // @formatter:on + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..600952e1a53f3ccc329eb993afc58a86ca3a0468 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt @@ -0,0 +1,70 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Test +import kotlin.experimental.inv + +class ByteSerializerTest { + @Test + fun testZero() { + val value = 0.toByte() + testQtSerializerDirect(ByteSerializer, value) + testQtSerializerVariant(ByteSerializer, value) + // @formatter:off + testDeserialize(ByteSerializer, value, byteBufferOf(0)) + // @formatter:on + } + + @Test + fun testMinimal() { + val value = Byte.MIN_VALUE + testQtSerializerDirect(ByteSerializer, value) + testQtSerializerVariant(ByteSerializer, value) + // @formatter:off + testDeserialize(ByteSerializer, value, byteBufferOf(-128)) + // @formatter:on + } + + @Test + fun testMaximal() { + val value = Byte.MAX_VALUE + testQtSerializerDirect(ByteSerializer, value) + testQtSerializerVariant(ByteSerializer, value) + // @formatter:off + testDeserialize(ByteSerializer, value, byteBufferOf(127)) + // @formatter:on + } + + @Test + fun testAllOnes() { + val value = 0.toByte().inv() + + testQtSerializerDirect(ByteSerializer, value) + testQtSerializerVariant(ByteSerializer, value) + // @formatter:off + testDeserialize(ByteSerializer, value, byteBufferOf(-1)) + // @formatter:on + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..26161a3c65687f3d7b2b53508686b93d5cc3882e --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt @@ -0,0 +1,69 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Test + +class IntSerializerTest { + @Test + fun testZero() { + val value = 0 + testQtSerializerDirect(IntSerializer, value) + testQtSerializerVariant(IntSerializer, value) + // @formatter:off + testDeserialize(IntSerializer, value, byteBufferOf(0, 0, 0, 0)) + // @formatter:on + } + + @Test + fun testMinimal() { + val value = Int.MIN_VALUE + testQtSerializerDirect(IntSerializer, value) + testQtSerializerVariant(IntSerializer, value) + // @formatter:off + testDeserialize(IntSerializer, value, byteBufferOf(-128, 0, 0, 0)) + // @formatter:on + } + + @Test + fun testMaximal() { + val value = Int.MAX_VALUE + testQtSerializerDirect(IntSerializer, value) + testQtSerializerVariant(IntSerializer, value) + // @formatter:off + testDeserialize(IntSerializer, value, byteBufferOf(127, -1, -1, -1)) + // @formatter:on + } + + @Test + fun testAllOnes() { + val value = 0.inv() + + testQtSerializerDirect(IntSerializer, value) + testQtSerializerVariant(IntSerializer, value) + // @formatter:off + testDeserialize(IntSerializer, value, byteBufferOf(-1, -1, -1, -1)) + // @formatter:on + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..48835fdc6f485ed029dd8b61dca41d28f0d95069 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt @@ -0,0 +1,69 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not,see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Test + +class LongSerializerTest { + @Test + fun testZero() { + val value = 0.toLong() + testQtSerializerDirect(LongSerializer, value) + testQtSerializerVariant(LongSerializer, value) + // @formatter:off + testDeserialize(LongSerializer, value, byteBufferOf(0, 0, 0, 0, 0, 0, 0, 0)) + // @formatter:on + } + + @Test + fun testMinimal() { + val value = Long.MIN_VALUE + testQtSerializerDirect(LongSerializer, value) + testQtSerializerVariant(LongSerializer, value) + // @formatter:off + testDeserialize(LongSerializer, value, byteBufferOf(-128, 0, 0, 0, 0, 0, 0, 0)) + // @formatter:on + } + + @Test + fun testMaximal() { + val value = Long.MAX_VALUE + testQtSerializerDirect(LongSerializer, value) + testQtSerializerVariant(LongSerializer, value) + // @formatter:off + testDeserialize(LongSerializer, value, byteBufferOf(127, -1, -1, -1, -1, -1, -1, -1)) + // @formatter:on + } + + @Test + fun testAllOnes() { + val value = 0.toLong().inv() + + testQtSerializerDirect(LongSerializer, value) + testQtSerializerVariant(LongSerializer, value) + // @formatter:off + testDeserialize(LongSerializer, value, byteBufferOf(-1, -1, -1, -1, -1, -1, -1, -1)) + // @formatter:on + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..8c6f8f88ec1da79c1a138fffeae70094355c9fc1 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt @@ -0,0 +1,105 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.matchers.BomMatcherChar +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Test + +class QCharSerializerTest { + @Test + fun testNull() { + val value = '\u0000' + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, value, byteBufferOf(0, 0)) + // @formatter:on + } + + @Test + fun testAllOnes() { + val value = '\uFFFF' + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, value, byteBufferOf(-1, -1)) + // @formatter:on + } + + @Test + fun testBOM1() { + val value = '\uFFFE' + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, BomMatcherChar(value), byteBufferOf(-2, -1)) + // @formatter:on + } + + @Test + fun testBOM2() { + val value = '\uFEFF' + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, BomMatcherChar(value), byteBufferOf(-1, -2)) + // @formatter:on + } + + @Test + fun testAlphabet() { + for (index in 0..25) { + val value = 'a' + index + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, value, byteBufferOf(0, (97 + index).toByte())) + // @formatter:on + } + for (index in 0..25) { + val value = 'A' + index + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, value, byteBufferOf(0, (65 + index).toByte())) + // @formatter:on + } + for (index in 0..9) { + val value = '0' + index + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + // @formatter:off + testDeserialize(QCharSerializer, value, byteBufferOf(0, (48 + index).toByte())) + // @formatter:on + } + } + + @Test + fun testAlphabetExtended() { + for (value in listOf('ä', 'ö', 'ü', 'ß', 'æ', 'ø', 'µ')) { + testQtSerializerDirect(QCharSerializer, value) + testQtSerializerVariant(QCharSerializer, value) + } + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ad6a534688e3c552e65c7019df1075fae49104b5 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt @@ -0,0 +1,70 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2020 Janne Mareike Koschinski + * Copyright (c) 2020 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.testDeserialize +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Test +import kotlin.experimental.inv + +class ShortSerializerTest { + @Test + fun testZero() { + val value = 0.toShort() + testQtSerializerDirect(ShortSerializer, value) + testQtSerializerVariant(ShortSerializer, value) + // @formatter:off + testDeserialize(ShortSerializer, value, byteBufferOf(0, 0)) + // @formatter:on + } + + @Test + fun testMinimal() { + val value = Short.MIN_VALUE + testQtSerializerDirect(ShortSerializer, value) + testQtSerializerVariant(ShortSerializer, value) + // @formatter:off + testDeserialize(ShortSerializer, value, byteBufferOf(-128, 0)) + // @formatter:on + } + + @Test + fun testMaximal() { + val value = Short.MAX_VALUE + testQtSerializerDirect(ShortSerializer, value) + testQtSerializerVariant(ShortSerializer, value) + // @formatter:off + testDeserialize(ShortSerializer, value, byteBufferOf(127, -1)) + // @formatter:on + } + + @Test + fun testAllOnes() { + val value = 0.toShort().inv() + + testQtSerializerDirect(ShortSerializer, value) + testQtSerializerVariant(ShortSerializer, value) + // @formatter:off + testDeserialize(ShortSerializer, value, byteBufferOf(-1, -1)) + // @formatter:on + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ebf80db7924d4168e2c77ea30a7188f1c1faccbd --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt @@ -0,0 +1,118 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.serializers.primitive + +import de.kuschku.libquassel.protocol.testutil.byteBufferOf +import de.kuschku.libquassel.protocol.testutil.deserialize +import de.kuschku.libquassel.protocol.variant.QtType +import de.kuschku.libquassel.protocol.variant.qVariant +import de.kuschku.libquassel.protocol.testutil.matchers.BomMatcherString +import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher +import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect +import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant +import org.junit.Assert +import org.junit.Test + +class StringSerializerTest { + @Test + fun testRoundtripStringList() { + val data = listOf("FeatureList") + testQtSerializerDirect(QStringListSerializer, data) + testQtSerializerVariant(QStringListSerializer, data) + } + + @Test + fun testRoundtripVariantList() { + val data = listOf(qVariant("FeatureList", QtType.QString)) + testQtSerializerDirect(QVariantListSerializer, data) + testQtSerializerVariant(QVariantListSerializer, data) + } + + @Test + fun testBigListOfNaughtyStrings() { + this::class.java.getResourceAsStream("/blns.txt")!!.bufferedReader(Charsets.UTF_8).forEachLine { + // Ignore comments + if (!it.startsWith('#')) { + testQtSerializerDirect(StringSerializerUtf8, it, BomMatcherString(it)) + testQtSerializerDirect(StringSerializerUtf16, it, BomMatcherString(it)) + } + } + } + + @Test + fun testRoundtripEnglish() { + val data = """ + : ACHTUNG! + ALLES TURISTEN UND NONTEKNISCHEN LOOKENPEEPERS! + DAS KOMPUTERMASCHINE IST NICHT FÜR DER GEFINGERPOKEN UND MITTENGRABEN! ODERWISE IST EASY TO SCHNAPPEN DER SPRINGENWERK, BLOWENFUSEN UND POPPENCORKEN MIT SPITZENSPARKEN. + IST NICHT FÜR GEWERKEN BEI DUMMKOPFEN. DER RUBBERNECKEN SIGHTSEEREN KEEPEN DAS COTTONPICKEN HÄNDER IN DAS POCKETS MUSS. + ZO RELAXEN UND WATSCHEN DER BLINKENLICHTEN. + """.trimIndent() + + testQtSerializerDirect(StringSerializerAscii, data) + + val bufferAscii = StringSerializerAscii.serializeRaw(data) + testQtSerializerDirect(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii)) + testQtSerializerVariant(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii)) + + testQtSerializerDirect(StringSerializerUtf8, data) + + val bufferUtf8 = StringSerializerUtf8.serializeRaw(data) + testQtSerializerDirect(ByteBufferSerializer, bufferUtf8, ByteBufferMatcher(bufferUtf8)) + testQtSerializerVariant(ByteBufferSerializer, bufferUtf8, ByteBufferMatcher(bufferUtf8)) + + testQtSerializerDirect(StringSerializerUtf16, data) + testQtSerializerVariant(StringSerializerUtf16, data) + + val bufferUtf16 = StringSerializerUtf16.serializeRaw(data) + testQtSerializerDirect(ByteBufferSerializer, bufferUtf16, ByteBufferMatcher(bufferUtf16)) + testQtSerializerVariant(ByteBufferSerializer, bufferUtf16, ByteBufferMatcher(bufferUtf16)) + } + + @Test + fun testDeserializeEnglish() { + val value = """ + : ACHTUNG! + ALLES TURISTEN UND NONTEKNISCHEN LOOKENPEEPERS! + DAS KOMPUTERMASCHINE IST NICHT FÜR DER GEFINGERPOKEN UND MITTENGRABEN! ODERWISE IST EASY TO SCHNAPPEN DER SPRINGENWERK, BLOWENFUSEN UND POPPENCORKEN MIT SPITZENSPARKEN. + IST NICHT FÜR GEWERKEN BEI DUMMKOPFEN. DER RUBBERNECKEN SIGHTSEEREN KEEPEN DAS COTTONPICKEN HÄNDER IN DAS POCKETS MUSS. + ZO RELAXEN UND WATSCHEN DER BLINKENLICHTEN. + """.trimIndent() + + // @formatter:off + val utf8Buffer = byteBufferOf(0, 0, 1, -118, 58, 32, 65, 67, 72, 84, 85, 78, 71, 33, 10, 65, 76, 76, 69, 83, 32, 84, 85, 82, 73, 83, 84, 69, 78, 32, 85, 78, 68, 32, 78, 79, 78, 84, 69, 75, 78, 73, 83, 67, 72, 69, 78, 32, 76, 79, 79, 75, 69, 78, 80, 69, 69, 80, 69, 82, 83, 33, 10, 68, 65, 83, 32, 75, 79, 77, 80, 85, 84, 69, 82, 77, 65, 83, 67, 72, 73, 78, 69, 32, 73, 83, 84, 32, 78, 73, 67, 72, 84, 32, 70, -61, -100, 82, 32, 68, 69, 82, 32, 71, 69, 70, 73, 78, 71, 69, 82, 80, 79, 75, 69, 78, 32, 85, 78, 68, 32, 77, 73, 84, 84, 69, 78, 71, 82, 65, 66, 69, 78, 33, 32, 79, 68, 69, 82, 87, 73, 83, 69, 32, 73, 83, 84, 32, 69, 65, 83, 89, 32, 84, 79, 32, 83, 67, 72, 78, 65, 80, 80, 69, 78, 32, 68, 69, 82, 32, 83, 80, 82, 73, 78, 71, 69, 78, 87, 69, 82, 75, 44, 32, 66, 76, 79, 87, 69, 78, 70, 85, 83, 69, 78, 32, 85, 78, 68, 32, 80, 79, 80, 80, 69, 78, 67, 79, 82, 75, 69, 78, 32, 77, 73, 84, 32, 83, 80, 73, 84, 90, 69, 78, 83, 80, 65, 82, 75, 69, 78, 46, 10, 73, 83, 84, 32, 78, 73, 67, 72, 84, 32, 70, -61, -100, 82, 32, 71, 69, 87, 69, 82, 75, 69, 78, 32, 66, 69, 73, 32, 68, 85, 77, 77, 75, 79, 80, 70, 69, 78, 46, 32, 68, 69, 82, 32, 82, 85, 66, 66, 69, 82, 78, 69, 67, 75, 69, 78, 32, 83, 73, 71, 72, 84, 83, 69, 69, 82, 69, 78, 32, 75, 69, 69, 80, 69, 78, 32, 68, 65, 83, 32, 67, 79, 84, 84, 79, 78, 80, 73, 67, 75, 69, 78, 32, 72, -61, -124, 78, 68, 69, 82, 32, 73, 78, 32, 68, 65, 83, 32, 80, 79, 67, 75, 69, 84, 83, 32, 77, 85, 83, 83, 46, 10, 90, 79, 32, 82, 69, 76, 65, 88, 69, 78, 32, 85, 78, 68, 32, 87, 65, 84, 83, 67, 72, 69, 78, 32, 68, 69, 82, 32, 66, 76, 73, 78, 75, 69, 78, 76, 73, 67, 72, 84, 69, 78, 46) + val utf16Buffer = byteBufferOf(0, 0, 3, 14, 0, 58, 0, 32, 0, 65, 0, 67, 0, 72, 0, 84, 0, 85, 0, 78, 0, 71, 0, 33, 0, 10, 0, 65, 0, 76, 0, 76, 0, 69, 0, 83, 0, 32, 0, 84, 0, 85, 0, 82, 0, 73, 0, 83, 0, 84, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 78, 0, 79, 0, 78, 0, 84, 0, 69, 0, 75, 0, 78, 0, 73, 0, 83, 0, 67, 0, 72, 0, 69, 0, 78, 0, 32, 0, 76, 0, 79, 0, 79, 0, 75, 0, 69, 0, 78, 0, 80, 0, 69, 0, 69, 0, 80, 0, 69, 0, 82, 0, 83, 0, 33, 0, 10, 0, 68, 0, 65, 0, 83, 0, 32, 0, 75, 0, 79, 0, 77, 0, 80, 0, 85, 0, 84, 0, 69, 0, 82, 0, 77, 0, 65, 0, 83, 0, 67, 0, 72, 0, 73, 0, 78, 0, 69, 0, 32, 0, 73, 0, 83, 0, 84, 0, 32, 0, 78, 0, 73, 0, 67, 0, 72, 0, 84, 0, 32, 0, 70, 0, -36, 0, 82, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 71, 0, 69, 0, 70, 0, 73, 0, 78, 0, 71, 0, 69, 0, 82, 0, 80, 0, 79, 0, 75, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 77, 0, 73, 0, 84, 0, 84, 0, 69, 0, 78, 0, 71, 0, 82, 0, 65, 0, 66, 0, 69, 0, 78, 0, 33, 0, 32, 0, 79, 0, 68, 0, 69, 0, 82, 0, 87, 0, 73, 0, 83, 0, 69, 0, 32, 0, 73, 0, 83, 0, 84, 0, 32, 0, 69, 0, 65, 0, 83, 0, 89, 0, 32, 0, 84, 0, 79, 0, 32, 0, 83, 0, 67, 0, 72, 0, 78, 0, 65, 0, 80, 0, 80, 0, 69, 0, 78, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 83, 0, 80, 0, 82, 0, 73, 0, 78, 0, 71, 0, 69, 0, 78, 0, 87, 0, 69, 0, 82, 0, 75, 0, 44, 0, 32, 0, 66, 0, 76, 0, 79, 0, 87, 0, 69, 0, 78, 0, 70, 0, 85, 0, 83, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 80, 0, 79, 0, 80, 0, 80, 0, 69, 0, 78, 0, 67, 0, 79, 0, 82, 0, 75, 0, 69, 0, 78, 0, 32, 0, 77, 0, 73, 0, 84, 0, 32, 0, 83, 0, 80, 0, 73, 0, 84, 0, 90, 0, 69, 0, 78, 0, 83, 0, 80, 0, 65, 0, 82, 0, 75, 0, 69, 0, 78, 0, 46, 0, 10, 0, 73, 0, 83, 0, 84, 0, 32, 0, 78, 0, 73, 0, 67, 0, 72, 0, 84, 0, 32, 0, 70, 0, -36, 0, 82, 0, 32, 0, 71, 0, 69, 0, 87, 0, 69, 0, 82, 0, 75, 0, 69, 0, 78, 0, 32, 0, 66, 0, 69, 0, 73, 0, 32, 0, 68, 0, 85, 0, 77, 0, 77, 0, 75, 0, 79, 0, 80, 0, 70, 0, 69, 0, 78, 0, 46, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 82, 0, 85, 0, 66, 0, 66, 0, 69, 0, 82, 0, 78, 0, 69, 0, 67, 0, 75, 0, 69, 0, 78, 0, 32, 0, 83, 0, 73, 0, 71, 0, 72, 0, 84, 0, 83, 0, 69, 0, 69, 0, 82, 0, 69, 0, 78, 0, 32, 0, 75, 0, 69, 0, 69, 0, 80, 0, 69, 0, 78, 0, 32, 0, 68, 0, 65, 0, 83, 0, 32, 0, 67, 0, 79, 0, 84, 0, 84, 0, 79, 0, 78, 0, 80, 0, 73, 0, 67, 0, 75, 0, 69, 0, 78, 0, 32, 0, 72, 0, -60, 0, 78, 0, 68, 0, 69, 0, 82, 0, 32, 0, 73, 0, 78, 0, 32, 0, 68, 0, 65, 0, 83, 0, 32, 0, 80, 0, 79, 0, 67, 0, 75, 0, 69, 0, 84, 0, 83, 0, 32, 0, 77, 0, 85, 0, 83, 0, 83, 0, 46, 0, 10, 0, 90, 0, 79, 0, 32, 0, 82, 0, 69, 0, 76, 0, 65, 0, 88, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 87, 0, 65, 0, 84, 0, 83, 0, 67, 0, 72, 0, 69, 0, 78, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 66, 0, 76, 0, 73, 0, 78, 0, 75, 0, 69, 0, 78, 0, 76, 0, 73, 0, 67, 0, 72, 0, 84, 0, 69, 0, 78, 0, 46) + // @formatter:on + Assert.assertEquals(value, deserialize(StringSerializerUtf8, utf8Buffer)) + Assert.assertEquals(value, deserialize(StringSerializerUtf16, utf16Buffer)) + } + + @Test + fun testAscii() { + // The simple solution: Just test all + val data = String(CharArray(256, Int::toChar).toList().shuffled().toCharArray()) + testQtSerializerDirect(StringSerializerAscii, data) + + val bufferAscii = StringSerializerAscii.serializeRaw(data) + testQtSerializerDirect(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii)) + testQtSerializerVariant(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii)) + } +} + diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt new file mode 100644 index 0000000000000000000000000000000000000000..2d415ea2f7e4803ae25671e4c7221e0b16be683b --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt @@ -0,0 +1,25 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil + +import java.nio.ByteBuffer + +inline fun byteBufferOf(vararg elements: Byte) = ByteBuffer.wrap(byteArrayOf(*elements)) +inline fun byteBufferOf(vararg elements: UByte) = ByteBuffer.wrap(ubyteArrayOf(*elements).toByteArray()) diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt new file mode 100644 index 0000000000000000000000000000000000000000..1ae7d9908851f746e3bcf2b5e4675ae66bb36d6c --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt @@ -0,0 +1,61 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeMapSerializer +import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert +import org.junit.Assert +import java.nio.ByteBuffer + +fun <T> deserialize(serializer: QtSerializer<T>, buffer: ByteBuffer): T { + val connectionFeatureSet = FeatureSet.build() + val result = serializer.deserialize( + buffer, + connectionFeatureSet + ) + Assert.assertEquals(0, buffer.remaining()) + return result +} + +fun <T> testDeserialize(serializer: QtSerializer<T>, matcher: Matcher<in T>, buffer: ByteBuffer) { + val after = deserialize(serializer, buffer) + MatcherAssert.assertThat(after, matcher) +} + +fun <T> testDeserialize(serializer: QtSerializer<T>, data: T, buffer: ByteBuffer) { + val after = deserialize(serializer, buffer) + Assert.assertEquals(data, after) +} + +fun <T> testDeserialize(serializer: HandshakeSerializer<T>, matcher: Matcher<in T>, buffer: ByteBuffer) { + val map = deserialize(HandshakeMapSerializer, buffer) + val after = serializer.deserialize(map) + MatcherAssert.assertThat(after, matcher) +} + +fun <T> testDeserialize(serializer: HandshakeSerializer<T>, data: T, buffer: ByteBuffer) { + val map = deserialize(HandshakeMapSerializer, buffer) + val after = serializer.deserialize(map) + Assert.assertEquals(data, after) +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt new file mode 100644 index 0000000000000000000000000000000000000000..bd00fb1edb99c09e0d751bd3fd3a12cf18ed4869 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt @@ -0,0 +1,40 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil.matchers + +import org.hamcrest.BaseMatcher +import org.hamcrest.Description + +class BomMatcherChar(private val expected: Char) : BaseMatcher<Char>() { + private val malformed = charArrayOf( + '', '' + ) + + override fun describeTo(description: Description?) { + description?.appendText(expected.toString()) + } + + override fun matches(item: Any?): Boolean { + if (item is Char) { + return (item == expected) || (item in malformed && expected in malformed) + } + return false + } +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt new file mode 100644 index 0000000000000000000000000000000000000000..de2e83e1639291ff8bcea8304bdf85389c90f75a --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt @@ -0,0 +1,36 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil.matchers + +import org.hamcrest.BaseMatcher +import org.hamcrest.Description + +class BomMatcherString(private val expected: String) : BaseMatcher<String>() { + private val malformed = charArrayOf( + '', '' + ) + + override fun describeTo(description: Description?) { + description?.appendText(expected) + } + + override fun matches(item: Any?) = + (item as? String)?.endsWith(expected.trimStart(*malformed)) == true +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt new file mode 100644 index 0000000000000000000000000000000000000000..98a904e325c5553a26e7263ee9581b6e74632cc1 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt @@ -0,0 +1,34 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil.matchers + +import de.kuschku.libquassel.protocol.io.contentToString +import org.hamcrest.BaseMatcher +import org.hamcrest.Description +import java.nio.ByteBuffer + +class ByteBufferMatcher(private val expected: ByteBuffer) : BaseMatcher<ByteBuffer>() { + override fun describeTo(description: Description?) { + description?.appendText(expected.contentToString()) + } + + override fun matches(item: Any?) = + (item as? ByteBuffer)?.clear()?.contentToString() == expected.clear().contentToString() +} diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt new file mode 100644 index 0000000000000000000000000000000000000000..a4c23c3f874e0ccfeb122bace63f0a113fadff46 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt @@ -0,0 +1,40 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert +import org.junit.Assert + +fun <T> testHandshakeSerializerDirect(serializer: HandshakeSerializer<T>, data: T, matcher: Matcher<T>? = null) { + val after = serializer.deserialize(serializer.serialize(data)) + + if (matcher != null) { + MatcherAssert.assertThat(data, matcher) + } else { + Assert.assertEquals(data, after) + } +} + diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt new file mode 100644 index 0000000000000000000000000000000000000000..1ace04a4f72b3cac3312280cdc9e0ca0804c1f1f --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt @@ -0,0 +1,49 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeMapSerializer +import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert +import org.junit.Assert + +fun <T> testHandshakeSerializerEncoded(serializer: HandshakeSerializer<T>, data: T, matcher: Matcher<T>? = null) { + val connectionFeatureSet = FeatureSet.build() + val buffer = ChainedByteBuffer() + + HandshakeMapSerializer.serialize(buffer, serializer.serialize(data), connectionFeatureSet) + val result = buffer.toBuffer() + result.print() + val after = serializer.deserialize(HandshakeMapSerializer.deserialize(result, connectionFeatureSet)) + + Assert.assertEquals(0, result.remaining()) + + if (matcher != null) { + MatcherAssert.assertThat(data, matcher) + } else { + Assert.assertEquals(data, after) + } +} + diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt new file mode 100644 index 0000000000000000000000000000000000000000..1855cab9e09d460060e62267204d9ed291e40c21 --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt @@ -0,0 +1,46 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert +import org.junit.Assert + +fun <T> testQtSerializerDirect(serializer: QtSerializer<T>, data: T, matcher: Matcher<T>? = null) { + val connectionFeatureSet = FeatureSet.build() + val buffer = ChainedByteBuffer() + + serializer.serialize(buffer, data, connectionFeatureSet) + val result = buffer.toBuffer() + result.print() + val after = serializer.deserialize(result, connectionFeatureSet) + + Assert.assertEquals(0, result.remaining()) + if (matcher != null) { + MatcherAssert.assertThat(data, matcher) + } else { + Assert.assertEquals(data, after) + } +} + diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt new file mode 100644 index 0000000000000000000000000000000000000000..e1da9981e23b3d0adc96136b954ee675b1ad703a --- /dev/null +++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt @@ -0,0 +1,47 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.testutil + +import de.kuschku.libquassel.protocol.features.FeatureSet +import de.kuschku.libquassel.protocol.io.ChainedByteBuffer +import de.kuschku.libquassel.protocol.io.print +import de.kuschku.libquassel.protocol.serializers.primitive.QVariantSerializer +import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer +import de.kuschku.libquassel.protocol.variant.QVariant +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert +import org.junit.Assert + +fun <T> testQtSerializerVariant(serializer: QtSerializer<T>, data: T, matcher: Matcher<in T>? = null) { + val connectionFeatureSet = FeatureSet.build() + val buffer = ChainedByteBuffer() + + QVariantSerializer.serialize(buffer, QVariant.of(data, serializer), connectionFeatureSet) + val result = buffer.toBuffer() + result.print() + val after = QVariantSerializer.deserialize(result, connectionFeatureSet) + + Assert.assertEquals(0, result.remaining()) + if (matcher != null) { + MatcherAssert.assertThat(data, matcher) + } else { + Assert.assertEquals(data, after.value()) + } +} diff --git a/protocol/src/test/resources/blns.txt b/protocol/src/test/resources/blns.txt new file mode 100644 index 0000000000000000000000000000000000000000..54d73bf4682d4558c8427a79e798831e335bd48e --- /dev/null +++ b/protocol/src/test/resources/blns.txt @@ -0,0 +1,712 @@ +# Reserved Strings +# +# Strings which may be used elsewhere in code + +undefined +undef +null +NULL +(null) +nil +NIL +true +false +True +False +TRUE +FALSE +None +hasOwnProperty +then +\ +\\ + +# Numeric Strings +# +# Strings which can be interpreted as numeric + +0 +1 +1.00 +$1.00 +1/2 +1E2 +1E02 +1E+02 +-1 +-1.00 +-$1.00 +-1/2 +-1E2 +-1E02 +-1E+02 +1/0 +0/0 +-2147483648/-1 +-9223372036854775808/-1 +-0 +-0.0 ++0 ++0.0 +0.00 +0..0 +. +0.0.0 +0,00 +0,,0 +, +0,0,0 +0.0/0 +1.0/0.0 +0.0/0.0 +1,0/0,0 +0,0/0,0 +--1 +- +-. +-, +999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +NaN +Infinity +-Infinity +INF +1#INF +-1#IND +1#QNAN +1#SNAN +1#IND +0x0 +0xffffffff +0xffffffffffffffff +0xabad1dea +123456789012345678901234567890123456789 +1,000.00 +1 000.00 +1'000.00 +1,000,000.00 +1 000 000.00 +1'000'000.00 +1.000,00 +1 000,00 +1'000,00 +1.000.000,00 +1 000 000,00 +1'000'000,00 +01000 +08 +09 +2.2250738585072011e-308 + +# Special Characters +# +# ASCII punctuation. All of these characters may need to be escaped in some +# contexts. Divided into three groups based on (US-layout) keyboard position. + +,./;'[]\-= +<>?:"{}|_+ +!@#$%^&*()`~ + +# Non-whitespace C0 controls: U+0001 through U+0008, U+000E through U+001F, +# and U+007F (DEL) +# Often forbidden to appear in various text-based file formats (e.g. XML), +# or reused for internal delimiters on the theory that they should never +# appear in input. +# The next line may appear to be blank or mojibake in some viewers. + + +# Non-whitespace C1 controls: U+0080 through U+0084 and U+0086 through U+009F. +# Commonly misinterpreted as additional graphic characters. +# The next line may appear to be blank, mojibake, or dingbats in some viewers. + + +# Whitespace: all of the characters with category Zs, Zl, or Zp (in Unicode +# version 8.0.0), plus U+0009 (HT), U+000B (VT), U+000C (FF), U+0085 (NEL), +# and U+200B (ZERO WIDTH SPACE), which are in the C categories but are often +# treated as whitespace in some contexts. +# This file unfortunately cannot express strings containing +# U+0000, U+000A, or U+000D (NUL, LF, CR). +# The next line may appear to be blank or mojibake in some viewers. +# The next line may be flagged for "trailing whitespace" in some viewers. + + +# Unicode additional control characters: all of the characters with +# general category Cf (in Unicode 8.0.0). +# The next line may appear to be blank or mojibake in some viewers. + + +# "Byte order marks", U+FEFF and U+FFFE, each on its own line. +# The next two lines may appear to be blank or mojibake in some viewers. + + + +# Unicode Symbols +# +# Strings which contain common unicode symbols (e.g. smart quotes) + +Ω≈ç√∫˜µ≤≥÷ +åß∂ƒ©˙∆˚¬…æ +œ∑´®†¥¨ˆøπ“‘ +¡™£¢∞§¶•ªº–≠ +¸˛Ç◊ı˜Â¯˘¿ +ÅÍÎÏ˝ÓÔÒÚÆ☃ +Œ„´‰ˇÁ¨ˆØ∏”’ +`⁄€‹›fifl‡°·‚—± +⅛⅜⅝⅞ +ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя +٠١٢٣٤٥٦٧٨٩ + +# Unicode Subscript/Superscript/Accents +# +# Strings which contain unicode subscripts/superscripts; can cause rendering issues + +⁰⁴⁵ +₀₁₂ +⁰⁴⁵₀₁₂ +ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ + +# Quotation Marks +# +# Strings which contain misplaced quotation marks; can cause encoding errors + +' +" +'' +"" +'"' +"''''"'" +"'"'"''''" +<foo val=“bar” /> +<foo val=“bar” /> +<foo val=”bar“ /> +<foo val=`bar' /> + +# Two-Byte Characters +# +# Strings which contain two-byte characters: can cause rendering issues or character-length issues + +田中さんにあげて下さい +パーティーへ行かないか +和製漢語 +部落格 +사회과학원 어학연구소 +찦차를 타고 온 펲시맨과 쑛다리 똠방각하 +社會科學院語學研究所 +울란바토르 +𠜎𠜱𠝹𠱓𠱸𠲖𠳏 + +# Special Unicode Characters Union +# +# A super string recommended by VMware Inc. Globalization Team: can effectively cause rendering issues or character-length issues to validate product globalization readiness. +# +# 表 CJK_UNIFIED_IDEOGRAPHS (U+8868) +# ポ KATAKANA LETTER PO (U+30DD) +# あ HIRAGANA LETTER A (U+3042) +# A LATIN CAPITAL LETTER A (U+0041) +# 鷗 CJK_UNIFIED_IDEOGRAPHS (U+9DD7) +# Œ LATIN SMALL LIGATURE OE (U+0153) +# é LATIN SMALL LETTER E WITH ACUTE (U+00E9) +# B FULLWIDTH LATIN CAPITAL LETTER B (U+FF22) +# 逍 CJK_UNIFIED_IDEOGRAPHS (U+900D) +# Ü LATIN SMALL LETTER U WITH DIAERESIS (U+00FC) +# ß LATIN SMALL LETTER SHARP S (U+00DF) +# ª FEMININE ORDINAL INDICATOR (U+00AA) +# ą LATIN SMALL LETTER A WITH OGONEK (U+0105) +# ñ LATIN SMALL LETTER N WITH TILDE (U+00F1) +# 丂 CJK_UNIFIED_IDEOGRAPHS (U+4E02) +# 㐀 CJK Ideograph Extension A, First (U+3400) +# 𠀀 CJK Ideograph Extension B, First (U+20000) + +表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀 + +# Changing length when lowercased +# +# Characters which increase in length (2 to 3 bytes) when lowercased +# Credit: https://twitter.com/jifa/status/625776454479970304 + +Ⱥ +Ⱦ + +# Japanese Emoticons +# +# Strings which consists of Japanese-style emoticons which are popular on the web + +ヽ༼ຈل͜ຈ༽ノ ヽ༼ຈل͜ຈ༽ノ +(。◕ ∀ ◕。) +`ィ(´∀`∩ +__ロ(,_,*) +・( ̄∀ ̄)・:*: +゚・✿ヾ╲(。◕‿◕。)╱✿・゚ +,。・:*:・゜’( ☻ ω ☻ )。・:*:・゜’ +(╯°□°)╯︵ ┻━┻) +(ノಥ益ಥ)ノ ┻━┻ +┬─┬ノ( º _ ºノ) +( ͡° ͜ʖ ͡°) +¯\_(ツ)_/¯ + +# Emoji +# +# Strings which contain Emoji; should be the same behavior as two-byte characters, but not always + +😍 +👩🏽 +👾 🙇 💁 🙅 🙆 🙋 🙎 🙍 +🐵 🙈 🙉 🙊 +❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙 +✋🏿 💪🏿 👐🏿 🙌🏿 👏🏿 🙏🏿 +🚾 🆒 🆓 🆕 🆖 🆗 🆙 🏧 +0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 + +# Regional Indicator Symbols +# +# Regional Indicator Symbols can be displayed differently across +# fonts, and have a number of special behaviors + +🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸 +🇺🇸🇷🇺🇸🇦🇫🇦🇲 +🇺🇸🇷🇺🇸🇦 + +# Unicode Numbers +# +# Strings which contain unicode numbers; if the code is localized, it should see the input as numeric + +123 +١٢٣ + +# Right-To-Left Strings +# +# Strings which contain text that should be rendered RTL if possible (e.g. Arabic, Hebrew) + +ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو. +בְּרֵאשִׁית, בָּרָא אֱלֹהִים, אֵת הַשָּׁמַיִם, וְאֵת הָאָרֶץ +הָיְתָהtestالصفحات التّحول +﷽ +ﷺ +مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ، + +# Trick Unicode +# +# Strings which contain unicode with unusual properties (e.g. Right-to-left override) (c.f. http://www.unicode.org/charts/PDF/U2000.pdf) + +test +test + test +testtest +test + +# Zalgo Text +# +# Strings which contain "corrupted" text. The corruption will not appear in non-HTML text, however. (via http://www.eeemo.net) + +Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣ +̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰ +̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟ +̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕ +Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮ + +# Unicode Upsidedown +# +# Strings which contain unicode with an "upsidedown" effect (via http://www.upsidedowntext.com) + +˙ɐnbᴉlɐ ɐuƃɐɯ ǝɹolop ʇǝ ǝɹoqɐl ʇn ʇunpᴉpᴉɔuᴉ ɹodɯǝʇ poɯsnᴉǝ op pǝs 'ʇᴉlǝ ƃuᴉɔsᴉdᴉpɐ ɹnʇǝʇɔǝsuoɔ 'ʇǝɯɐ ʇᴉs ɹolop ɯnsdᴉ ɯǝɹo˥ +00˙Ɩ$- + +# Unicode font +# +# Strings which contain bold/italic/etc. versions of normal characters + +The quick brown fox jumps over the lazy dog +𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠 +𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌 +𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈 +𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰 +𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘 +𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐 +⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢ + +# Script Injection +# +# Strings which attempt to invoke a benign script injection; shows vulnerability to XSS + +<script>alert(123)</script> +<script>alert('123');</script> +<img src=x onerror=alert(123) /> +<svg><script>123<1>alert(123)</script> +"><script>alert(123)</script> +'><script>alert(123)</script> +><script>alert(123)</script> +</script><script>alert(123)</script> +< / script >< script >alert(123)< / script > + onfocus=JaVaSCript:alert(123) autofocus +" onfocus=JaVaSCript:alert(123) autofocus +' onfocus=JaVaSCript:alert(123) autofocus +<script>alert(123)</script> +<sc<script>ript>alert(123)</sc</script>ript> +--><script>alert(123)</script> +";alert(123);t=" +';alert(123);t=' +JavaSCript:alert(123) +;alert(123); +src=JaVaSCript:prompt(132) +"><script>alert(123);</script x=" +'><script>alert(123);</script x=' +><script>alert(123);</script x= +" autofocus onkeyup="javascript:alert(123) +' autofocus onkeyup='javascript:alert(123) +<script\x20type="text/javascript">javascript:alert(1);</script> +<script\x3Etype="text/javascript">javascript:alert(1);</script> +<script\x0Dtype="text/javascript">javascript:alert(1);</script> +<script\x09type="text/javascript">javascript:alert(1);</script> +<script\x0Ctype="text/javascript">javascript:alert(1);</script> +<script\x2Ftype="text/javascript">javascript:alert(1);</script> +<script\x0Atype="text/javascript">javascript:alert(1);</script> +'`"><\x3Cscript>javascript:alert(1)</script> +'`"><\x00script>javascript:alert(1)</script> +ABC<div style="x\x3Aexpression(javascript:alert(1)">DEF +ABC<div style="x:expression\x5C(javascript:alert(1)">DEF +ABC<div style="x:expression\x00(javascript:alert(1)">DEF +ABC<div style="x:exp\x00ression(javascript:alert(1)">DEF +ABC<div style="x:exp\x5Cression(javascript:alert(1)">DEF +ABC<div style="x:\x0Aexpression(javascript:alert(1)">DEF +ABC<div style="x:\x09expression(javascript:alert(1)">DEF +ABC<div style="x:\xE3\x80\x80expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x84expression(javascript:alert(1)">DEF +ABC<div style="x:\xC2\xA0expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x80expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x8Aexpression(javascript:alert(1)">DEF +ABC<div style="x:\x0Dexpression(javascript:alert(1)">DEF +ABC<div style="x:\x0Cexpression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x87expression(javascript:alert(1)">DEF +ABC<div style="x:\xEF\xBB\xBFexpression(javascript:alert(1)">DEF +ABC<div style="x:\x20expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x88expression(javascript:alert(1)">DEF +ABC<div style="x:\x00expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x8Bexpression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x86expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x85expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x82expression(javascript:alert(1)">DEF +ABC<div style="x:\x0Bexpression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x81expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x83expression(javascript:alert(1)">DEF +ABC<div style="x:\xE2\x80\x89expression(javascript:alert(1)">DEF +<a href="\x0Bjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x0Fjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xC2\xA0javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x05javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE1\xA0\x8Ejavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x18javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x11javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x88javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x89javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x80javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x17javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x03javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x0Ejavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x1Ajavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x00javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x10javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x82javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x20javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x13javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x09javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x8Ajavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x14javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x19javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\xAFjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x1Fjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x81javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x1Djavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x87javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x07javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE1\x9A\x80javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x83javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x04javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x01javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x08javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x84javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x86javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE3\x80\x80javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x12javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x0Djavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x0Ajavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x0Cjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x15javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\xA8javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x16javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x02javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x1Bjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x06javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\xA9javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x80\x85javascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x1Ejavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\xE2\x81\x9Fjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="\x1Cjavascript:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="javascript\x00:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="javascript\x3A:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="javascript\x09:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="javascript\x0D:javascript:alert(1)" id="fuzzelement1">test</a> +<a href="javascript\x0A:javascript:alert(1)" id="fuzzelement1">test</a> +`"'><img src=xxx:x \x0Aonerror=javascript:alert(1)> +`"'><img src=xxx:x \x22onerror=javascript:alert(1)> +`"'><img src=xxx:x \x0Bonerror=javascript:alert(1)> +`"'><img src=xxx:x \x0Donerror=javascript:alert(1)> +`"'><img src=xxx:x \x2Fonerror=javascript:alert(1)> +`"'><img src=xxx:x \x09onerror=javascript:alert(1)> +`"'><img src=xxx:x \x0Conerror=javascript:alert(1)> +`"'><img src=xxx:x \x00onerror=javascript:alert(1)> +`"'><img src=xxx:x \x27onerror=javascript:alert(1)> +`"'><img src=xxx:x \x20onerror=javascript:alert(1)> +"`'><script>\x3Bjavascript:alert(1)</script> +"`'><script>\x0Djavascript:alert(1)</script> +"`'><script>\xEF\xBB\xBFjavascript:alert(1)</script> +"`'><script>\xE2\x80\x81javascript:alert(1)</script> +"`'><script>\xE2\x80\x84javascript:alert(1)</script> +"`'><script>\xE3\x80\x80javascript:alert(1)</script> +"`'><script>\x09javascript:alert(1)</script> +"`'><script>\xE2\x80\x89javascript:alert(1)</script> +"`'><script>\xE2\x80\x85javascript:alert(1)</script> +"`'><script>\xE2\x80\x88javascript:alert(1)</script> +"`'><script>\x00javascript:alert(1)</script> +"`'><script>\xE2\x80\xA8javascript:alert(1)</script> +"`'><script>\xE2\x80\x8Ajavascript:alert(1)</script> +"`'><script>\xE1\x9A\x80javascript:alert(1)</script> +"`'><script>\x0Cjavascript:alert(1)</script> +"`'><script>\x2Bjavascript:alert(1)</script> +"`'><script>\xF0\x90\x96\x9Ajavascript:alert(1)</script> +"`'><script>-javascript:alert(1)</script> +"`'><script>\x0Ajavascript:alert(1)</script> +"`'><script>\xE2\x80\xAFjavascript:alert(1)</script> +"`'><script>\x7Ejavascript:alert(1)</script> +"`'><script>\xE2\x80\x87javascript:alert(1)</script> +"`'><script>\xE2\x81\x9Fjavascript:alert(1)</script> +"`'><script>\xE2\x80\xA9javascript:alert(1)</script> +"`'><script>\xC2\x85javascript:alert(1)</script> +"`'><script>\xEF\xBF\xAEjavascript:alert(1)</script> +"`'><script>\xE2\x80\x83javascript:alert(1)</script> +"`'><script>\xE2\x80\x8Bjavascript:alert(1)</script> +"`'><script>\xEF\xBF\xBEjavascript:alert(1)</script> +"`'><script>\xE2\x80\x80javascript:alert(1)</script> +"`'><script>\x21javascript:alert(1)</script> +"`'><script>\xE2\x80\x82javascript:alert(1)</script> +"`'><script>\xE2\x80\x86javascript:alert(1)</script> +"`'><script>\xE1\xA0\x8Ejavascript:alert(1)</script> +"`'><script>\x0Bjavascript:alert(1)</script> +"`'><script>\x20javascript:alert(1)</script> +"`'><script>\xC2\xA0javascript:alert(1)</script> +<img \x00src=x onerror="alert(1)"> +<img \x47src=x onerror="javascript:alert(1)"> +<img \x11src=x onerror="javascript:alert(1)"> +<img \x12src=x onerror="javascript:alert(1)"> +<img\x47src=x onerror="javascript:alert(1)"> +<img\x10src=x onerror="javascript:alert(1)"> +<img\x13src=x onerror="javascript:alert(1)"> +<img\x32src=x onerror="javascript:alert(1)"> +<img\x47src=x onerror="javascript:alert(1)"> +<img\x11src=x onerror="javascript:alert(1)"> +<img \x47src=x onerror="javascript:alert(1)"> +<img \x34src=x onerror="javascript:alert(1)"> +<img \x39src=x onerror="javascript:alert(1)"> +<img \x00src=x onerror="javascript:alert(1)"> +<img src\x09=x onerror="javascript:alert(1)"> +<img src\x10=x onerror="javascript:alert(1)"> +<img src\x13=x onerror="javascript:alert(1)"> +<img src\x32=x onerror="javascript:alert(1)"> +<img src\x12=x onerror="javascript:alert(1)"> +<img src\x11=x onerror="javascript:alert(1)"> +<img src\x00=x onerror="javascript:alert(1)"> +<img src\x47=x onerror="javascript:alert(1)"> +<img src=x\x09onerror="javascript:alert(1)"> +<img src=x\x10onerror="javascript:alert(1)"> +<img src=x\x11onerror="javascript:alert(1)"> +<img src=x\x12onerror="javascript:alert(1)"> +<img src=x\x13onerror="javascript:alert(1)"> +<img[a][b][c]src[d]=x[e]onerror=[f]"alert(1)"> +<img src=x onerror=\x09"javascript:alert(1)"> +<img src=x onerror=\x10"javascript:alert(1)"> +<img src=x onerror=\x11"javascript:alert(1)"> +<img src=x onerror=\x12"javascript:alert(1)"> +<img src=x onerror=\x32"javascript:alert(1)"> +<img src=x onerror=\x00"javascript:alert(1)"> +<a href=javascript:javascript:alert(1)>XXX</a> +<img src="x` `<script>javascript:alert(1)</script>"` `> +<img src onerror /" '"= alt=javascript:alert(1)//"> +<title onpropertychange=javascript:alert(1)></title><title title=> +<a href=http://foo.bar/#x=`y></a><img alt="`><img src=x:x onerror=javascript:alert(1)></a>"> +<!--[if]><script>javascript:alert(1)</script --> +<!--[if<img src=x onerror=javascript:alert(1)//]> --> +<script src="/\%(jscript)s"></script> +<script src="\\%(jscript)s"></script> +<IMG """><SCRIPT>alert("XSS")</SCRIPT>"> +<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))> +<IMG SRC=# onmouseover="alert('xxs')"> +<IMG SRC= onmouseover="alert('xxs')"> +<IMG onmouseover="alert('xxs')"> +<IMG SRC=javascript:alert('XSS')> +<IMG SRC=javascript:alert('XSS')> +<IMG SRC=javascript:alert('XSS')> +<IMG SRC="jav ascript:alert('XSS');"> +<IMG SRC="jav	ascript:alert('XSS');"> +<IMG SRC="jav
ascript:alert('XSS');"> +<IMG SRC="jav
ascript:alert('XSS');"> +perl -e 'print "<IMG SRC=java\0script:alert(\"XSS\")>";' > out +<IMG SRC="  javascript:alert('XSS');"> +<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT> +<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")> +<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT> +<<SCRIPT>alert("XSS");//<</SCRIPT> +<SCRIPT SRC=http://ha.ckers.org/xss.js?< B > +<SCRIPT SRC=//ha.ckers.org/.j> +<IMG SRC="javascript:alert('XSS')" +<iframe src=http://ha.ckers.org/scriptlet.html < +\";alert('XSS');// +<u oncopy=alert()> Copy me</u> +<i onwheel=alert(1)> Scroll over me </i> +<plaintext> +http://a/%%30%30 +</textarea><script>alert(123)</script> + +# SQL Injection +# +# Strings which can cause a SQL injection if inputs are not sanitized + +1;DROP TABLE users +1'; DROP TABLE users-- 1 +' OR 1=1 -- 1 +' OR '1'='1 + +% +_ + +# Server Code Injection +# +# Strings which can cause user to run code on server as a privileged user (c.f. https://news.ycombinator.com/item?id=7665153) + +- +-- +--version +--help +$USER +/dev/null; touch /tmp/blns.fail ; echo +`touch /tmp/blns.fail` +$(touch /tmp/blns.fail) +@{[system "touch /tmp/blns.fail"]} + +# Command Injection (Ruby) +# +# Strings which can call system commands within Ruby/Rails applications + +eval("puts 'hello world'") +System("ls -al /") +`ls -al /` +Kernel.exec("ls -al /") +Kernel.exit(1) +%x('ls -al /') + +# XXE Injection (XML) +# +# String which can reveal system files when parsed by a badly configured XML parser + +<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo> + +# Unwanted Interpolation +# +# Strings which can be accidentally expanded into different strings if evaluated in the wrong context, e.g. used as a printf format string or via Perl or shell eval. Might expose sensitive data from the program doing the interpolation, or might just represent the wrong string. + +$HOME +$ENV{'HOME'} +%d +%s%s%s%s%s +{0} +%*.*s +%@ +%n +File:/// + +# File Inclusion +# +# Strings which can cause user to pull in files that should not be a part of a web server + +../../../../../../../../../../../etc/passwd%00 +../../../../../../../../../../../etc/hosts + +# Known CVEs and Vulnerabilities +# +# Strings that test for known vulnerabilities + +() { 0; }; touch /tmp/blns.shellshock1.fail; +() { _; } >_[$($())] { touch /tmp/blns.shellshock2.fail; } +<<< %s(un='%s') = %u ++++ATH0 + +# MSDOS/Windows Special Filenames +# +# Strings which are reserved characters in MSDOS/Windows + +CON +PRN +AUX +CLOCK$ +NUL +A: +ZZ: +COM1 +LPT1 +LPT2 +LPT3 +COM2 +COM3 +COM4 + +# IRC specific strings +# +# Strings that may occur on IRC clients that make security products freak out + +DCC SEND STARTKEYLOGGER 0 0 0 + +# Scunthorpe Problem +# +# Innocuous strings which may be blocked by profanity filters (https://en.wikipedia.org/wiki/Scunthorpe_problem) + +Scunthorpe General Hospital +Penistone Community Church +Lightwater Country Park +Jimmy Clitheroe +Horniman Museum +shitake mushrooms +RomansInSussex.co.uk +http://www.cum.qc.ca/ +Craig Cockburn, Software Specialist +Linda Callahan +Dr. Herman I. Libshitz +magna cum laude +Super Bowl XXX +medieval erection of parapets +evaluate +mocha +expression +Arsenal canal +classic +Tyson Gay +Dick Van Dyke +basement + +# Human injection +# +# Strings which may cause human to reinterpret worldview + +If you're reading this, you've been in a coma for almost 20 years now. We're trying a new technique. We don't know where this message will end up in your dream, but we hope it works. Please wake up, we miss you. + +# Terminal escape codes +# +# Strings which punish the fools who use cat/type on this file + +Roses are [0;31mred[0m, violets are [0;34mblue. Hope you enjoy terminal hue +But now...[20Cfor my greatest trick...[8m +The quick brown fox... [Beeeep] + +# iOS Vulnerabilities +# +# Strings which crashed iMessage in various versions of iOS + +Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗 +🏳0🌈️ +జ్ఞా