🔀 Commit

Fixed JIDs with unicode crash
Commitbcca8c05af6219e064b6913aa7d5da470f4dfa50
AuthorJabJab <noreply@xmpp.tel>
Date2026-08-08
Parent586ce881
commit bcca8c05af6219e064b6913aa7d5da470f4dfa50
Author: JabJab <noreply@xmpp.tel>
Date:   Sat Aug 8 06:46:27 2026 +0300

    Fixed JIDs with unicode crash
---
 build.gradle                                       |  2 +-
 .../tel/xmpp/jabjab/http/HttpUploadConnection.java |  8 +++----
 .../tel/xmpp/jabjab/ui/BlogComposerActivity.java   | 12 +++++------
 .../xmpp/jabjab/ui/util/LinkPreviewFetcher.java    |  8 +++----
 .../xmpp/jabjab/ui/util/TranslationService.java    |  4 ++--
 .../tel/xmpp/jabjab/utils/ExceptionHandler.java    |  3 ++-
 .../java/tel/xmpp/jabjab/utils/HeaderUtils.java    | 25 ++++++++++++++++++++++
 .../xmpp/jabjab/xmpp/manager/BannerManager.java    |  4 ++--
 .../xmpp/jabjab/xmpp/manager/StoriesManager.java   |  4 ++--
 9 files changed, 48 insertions(+), 22 deletions(-)

diff --git a/build.gradle b/build.gradle
index 1734e99..cd96fa3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -113,7 +113,7 @@ android {
 
     defaultConfig {
         minSdkVersion 23
-        versionCode 42307
+        versionCode 42308
         versionName "1.0.6"
         applicationId "tel.xmpp.jabjab"
         resValue "string", "applicationId", applicationId
diff --git a/src/main/java/tel/xmpp/jabjab/http/HttpUploadConnection.java b/src/main/java/tel/xmpp/jabjab/http/HttpUploadConnection.java
index 4466115..601d4b9 100644
--- a/src/main/java/tel/xmpp/jabjab/http/HttpUploadConnection.java
+++ b/src/main/java/tel/xmpp/jabjab/http/HttpUploadConnection.java
@@ -295,8 +295,8 @@ public class HttpUploadConnection
         }
         final long timerSeconds = message.getExpireAfterSeconds();
         final Headers headers = new Headers.Builder()
-                .add("X-Author-Jid", account.getJid().asBareJid().toString())
-                .add("User-Agent", "JabJab")
+                .add("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(account.getJid().asBareJid().toString()))
+                .add("User-Agent", HttpConnectionManager.getUserAgent())
                 .add("X-Ephemeral-Timer", String.valueOf(timerSeconds))
                 .build();
         Log.d(Config.LOGTAG, "ephemeral upload slot: " + url + " timer=" + timerSeconds + "s");
@@ -316,8 +316,8 @@ public class HttpUploadConnection
             throw new IllegalStateException("Invalid sticker upload base URL: " + Config.STICKER_UPLOAD_BASE);
         }
         final Headers headers = new Headers.Builder()
-                .add("X-Author-Jid", account.getJid().asBareJid().toString())
-                .add("User-Agent", "JabJab")
+                .add("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(account.getJid().asBareJid().toString()))
+                .add("User-Agent", HttpConnectionManager.getUserAgent())
                 .build();
         Log.d(Config.LOGTAG, "STICKER direct upload slot: " + url);
         return new HttpUploadManager.Slot(url, url, headers);
diff --git a/src/main/java/tel/xmpp/jabjab/ui/BlogComposerActivity.java b/src/main/java/tel/xmpp/jabjab/ui/BlogComposerActivity.java
index fea3aaa..759dc5a 100644
--- a/src/main/java/tel/xmpp/jabjab/ui/BlogComposerActivity.java
+++ b/src/main/java/tel/xmpp/jabjab/ui/BlogComposerActivity.java
@@ -302,7 +302,7 @@ public class BlogComposerActivity extends XmppActivity
                     .build();
             final Request request = new Request.Builder()
                     .url(BLOG_API_URL.replace("/api/posts", "/api/upload-image"))
-                    .header("User-Agent", "JabJab/2.20.1 (+https://xmpp.tel)")
+                    .header("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent() + " (+https://xmpp.tel)")
                     .header("Author-Jid", authorJid)
                     .post(body)
                     .build();
@@ -340,8 +340,8 @@ public class BlogComposerActivity extends XmppActivity
                 final var client = new OkHttpClient();
                 final var request = new Request.Builder()
                         .url(BLOG_API_URL + "/" + postId)
-                        .header("User-Agent", "JabJab/2.20.1 (+https://xmpp.tel)")
-                        .header("X-Author-Jid", authorJid)
+                        .header("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent() + " (+https://xmpp.tel)")
+                        .header("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(authorJid))
                         .delete()
                         .build();
                 try (final var response = client.newCall(request).execute()) {
@@ -384,8 +384,8 @@ public class BlogComposerActivity extends XmppActivity
                 final var client = new OkHttpClient();
                 final var request = new Request.Builder()
                         .url(BLOG_API_URL + "/" + postId)
-                        .header("User-Agent", "JabJab/2.20.1 (+https://xmpp.tel)")
-                        .header("X-Author-Jid", authorJid)
+                        .header("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent() + " (+https://xmpp.tel)")
+                        .header("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(authorJid))
                         .put(RequestBody.create(json.toString(), JSON))
                         .build();
                 try (final var response = client.newCall(request).execute()) {
@@ -611,7 +611,7 @@ public class BlogComposerActivity extends XmppActivity
                 final var client = new OkHttpClient();
                 final var request = new Request.Builder()
                         .url(BLOG_API_URL)
-                        .header("User-Agent", "JabJab/2.20.1 (+https://xmpp.tel)")
+                        .header("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent() + " (+https://xmpp.tel)")
                         .post(RequestBody.create(json.toString(), JSON))
                         .build();
                 try (final var response = client.newCall(request).execute()) {
diff --git a/src/main/java/tel/xmpp/jabjab/ui/util/LinkPreviewFetcher.java b/src/main/java/tel/xmpp/jabjab/ui/util/LinkPreviewFetcher.java
index e18d33b..8062417 100644
--- a/src/main/java/tel/xmpp/jabjab/ui/util/LinkPreviewFetcher.java
+++ b/src/main/java/tel/xmpp/jabjab/ui/util/LinkPreviewFetcher.java
@@ -117,7 +117,7 @@ public class LinkPreviewFetcher {
             try {
                 final Request request = new Request.Builder()
                         .url(url)
-                        .header("User-Agent", "JabJab/2")
+                        .header("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
                         .header("Cache-Control", "no-cache")
                         .build();
                 try (Response response = client(context).newCall(request).execute()) {
@@ -310,7 +310,7 @@ public class LinkPreviewFetcher {
                     android.util.Log.d(Config.LOGTAG, "PREVIEW LOADIMAGE network-fetch " + imageUrl);
                     final Request request = new Request.Builder()
                             .url(imageUrl)
-                            .header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36 JabJab/2.20.1")
+                            .header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36 " + tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
                             .build();
                     try (Response response =
                             client(imageView.getContext()).newCall(request).execute()) {
@@ -419,7 +419,7 @@ public class LinkPreviewFetcher {
                 if (bytes == null) {
                     final Request request = new Request.Builder()
                             .url(imageUrl)
-                            .header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36 JabJab/2.20.1")
+                            .header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36 " + tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
                             .build();
                     try (Response response =
                             client(thumbnailView.getContext()).newCall(request).execute()) {
@@ -455,7 +455,7 @@ public class LinkPreviewFetcher {
         try {
             final Request request = new Request.Builder()
                     .url(url)
-                    .header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36 JabJab/2.20.1")
+                    .header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36 " + tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
                     .build();
             try (Response response = client(context).newCall(request).execute()) {
                 if (!response.isSuccessful() || response.body() == null) {
diff --git a/src/main/java/tel/xmpp/jabjab/ui/util/TranslationService.java b/src/main/java/tel/xmpp/jabjab/ui/util/TranslationService.java
index de9b72f..bbfe11d 100644
--- a/src/main/java/tel/xmpp/jabjab/ui/util/TranslationService.java
+++ b/src/main/java/tel/xmpp/jabjab/ui/util/TranslationService.java
@@ -166,8 +166,8 @@ public class TranslationService {
             request =
                     new Request.Builder()
                             .url(Config.TRANSLATION_BASE_URL + "/api/translate")
-                            .header("User-Agent", "JabJab")
-                            .header("X-Author-Jid", authorJid)
+                            .header("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
+                            .header("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(authorJid))
                             .post(RequestBody.create(body.toString(), JSON))
                             .build();
         } catch (final JSONException e) {
diff --git a/src/main/java/tel/xmpp/jabjab/utils/ExceptionHandler.java b/src/main/java/tel/xmpp/jabjab/utils/ExceptionHandler.java
index c5797f6..bd6c960 100644
--- a/src/main/java/tel/xmpp/jabjab/utils/ExceptionHandler.java
+++ b/src/main/java/tel/xmpp/jabjab/utils/ExceptionHandler.java
@@ -49,7 +49,8 @@ public class ExceptionHandler implements UncaughtExceptionHandler {
                 ImmutableList.of(
                         String.format(
                                 "Version: %s %s", BuildConfig.APP_NAME, BuildConfig.VERSION_NAME),
-                        String.format("Build: %s", BuildConfig.VERSION_CODE),
+                        String.format("Build: %s (%s)",
+                                BuildConfig.VERSION_CODE, BuildConfig.BUILD_TYPE),
                         String.format("Manufacturer: %s", Strings.nullToEmpty(Build.MANUFACTURER)),
                         String.format("Device: %s", Strings.nullToEmpty(Build.DEVICE)),
                         String.format("Timestamp: %s", DATE_FORMAT.format(new Date())),
diff --git a/src/main/java/tel/xmpp/jabjab/utils/HeaderUtils.java b/src/main/java/tel/xmpp/jabjab/utils/HeaderUtils.java
new file mode 100644
index 0000000..9da66da
--- /dev/null
+++ b/src/main/java/tel/xmpp/jabjab/utils/HeaderUtils.java
@@ -0,0 +1,25 @@
+package tel.xmpp.jabjab.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+public class HeaderUtils {
+
+    private HeaderUtils() {}
+
+    /**
+     * XMPP JIDs may contain non-ASCII localparts (nodeprep allows Unicode), but HTTP header
+     * values must be ASCII. Percent-encode so it's always a valid header value; server side
+     * must URL-decode when reading X-Author-Jid.
+     */
+    public static String encodeJidForHeader(final String jid) {
+        if (jid == null) {
+            return null;
+        }
+        try {
+            return URLEncoder.encode(jid, "UTF-8");
+        } catch (final UnsupportedEncodingException e) {
+            return jid;
+        }
+    }
+}
diff --git a/src/main/java/tel/xmpp/jabjab/xmpp/manager/BannerManager.java b/src/main/java/tel/xmpp/jabjab/xmpp/manager/BannerManager.java
index 666b381..54c31c7 100644
--- a/src/main/java/tel/xmpp/jabjab/xmpp/manager/BannerManager.java
+++ b/src/main/java/tel/xmpp/jabjab/xmpp/manager/BannerManager.java
@@ -173,8 +173,8 @@ public class BannerManager extends AbstractManager {
             }
             final var slot = new HttpUploadManager.Slot(url, url,
                     new okhttp3.Headers.Builder()
-                            .add("X-Author-Jid", selfJid.toString())
-                            .add("User-Agent", "JabJab")
+                            .add("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(selfJid.toString()))
+                            .add("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
                             .build());
             Log.d(Config.LOGTAG, "BANNER direct upload slot: " + url);
             uploadFuture = getManager(HttpUploadManager.class).upload(file, mime, slot);
diff --git a/src/main/java/tel/xmpp/jabjab/xmpp/manager/StoriesManager.java b/src/main/java/tel/xmpp/jabjab/xmpp/manager/StoriesManager.java
index d2081b9..2d48294 100644
--- a/src/main/java/tel/xmpp/jabjab/xmpp/manager/StoriesManager.java
+++ b/src/main/java/tel/xmpp/jabjab/xmpp/manager/StoriesManager.java
@@ -191,8 +191,8 @@ public class StoriesManager extends AbstractManager {
             if (url == null) throw new IllegalStateException("Invalid story upload URL");
             final HttpUploadManager.Slot slot = new HttpUploadManager.Slot(url, url,
                     new Headers.Builder()
-                            .add("X-Author-Jid", getAccount().getJid().asBareJid().toString())
-                            .add("User-Agent", "JabJab")
+                            .add("X-Author-Jid", tel.xmpp.jabjab.utils.HeaderUtils.encodeJidForHeader(getAccount().getJid().asBareJid().toString()))
+                            .add("User-Agent", tel.xmpp.jabjab.http.HttpConnectionManager.getUserAgent())
                             .build());
             Log.d(Config.LOGTAG, "STORIES direct upload slot: " + url);
             return getManager(HttpUploadManager.class).upload(file, mime, slot);

Built with passion for open-source software, XMPP, privacy, and security —
the belief that people deserve communication tools that serve them, not surveil them.

Dedicated to every brave man and woman who stands up against tyranny and oppression across the world.
And in memory of those who were silenced before they could speak freely.