Control: tags -1 - moreinfo

On 2026-01-01 20:00, Adam D. Barratt wrote:
> We'd need to see a source debdiff of a proposed upload, please.

Sure! Attached.
diff -Nru dino-im-0.5.0/.github/workflows/build.yml dino-im-0.5.1/.github/workflows/build.yml
--- dino-im-0.5.0/.github/workflows/build.yml	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/.github/workflows/build.yml	2025-11-15 20:30:00.000000000 +0000
@@ -18,7 +18,7 @@
         run: |
           sudo apt-get update
           sudo apt-get remove libunwind-14-dev
-          sudo apt-get install -y build-essential gettext libadwaita-1-dev libcanberra-dev libgcrypt20-dev libgee-0.8-dev libgpgme-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libgtk-4-dev libnice-dev libnotify-dev libqrencode-dev libsignal-protocol-c-dev libsoup-3.0-dev libsqlite3-dev libsrtp2-dev libwebrtc-audio-processing-dev meson valac
+          sudo apt-get install -y build-essential gettext libadwaita-1-dev libcanberra-dev libgcrypt20-dev libgee-0.8-dev libgpgme-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libgtk-4-dev libnice-dev libnotify-dev libqrencode-dev libomemo-c-dev libsoup-3.0-dev libsqlite3-dev libsrtp2-dev libwebrtc-audio-processing-dev meson valac
       - name: "Configure"
         run: meson setup build
       - name: "Build"
diff -Nru dino-im-0.5.0/VERSION dino-im-0.5.1/VERSION
--- dino-im-0.5.0/VERSION	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/VERSION	2025-11-15 20:30:00.000000000 +0000
@@ -1 +1 @@
-RELEASE 0.5.0
+RELEASE 0.5.1
diff -Nru dino-im-0.5.0/debian/changelog dino-im-0.5.1/debian/changelog
--- dino-im-0.5.0/debian/changelog	2025-04-11 17:07:22.000000000 +0000
+++ dino-im-0.5.1/debian/changelog	2026-01-02 22:51:00.000000000 +0000
@@ -1,3 +1,16 @@
+dino-im (0.5.1-1~deb13u1) unstable; urgency=medium
+
+  * New upstream version with many bug fixes
+
+ -- Martin <[email protected]>  Fri, 02 Jan 2026 22:51:00 +0000
+
+dino-im (0.5.1-1) unstable; urgency=medium
+
+  * New upstream version
+  * Drop R³: no, it's the default now
+
+ -- Martin <[email protected]>  Sat, 15 Nov 2025 23:53:43 +0000
+
 dino-im (0.5.0-1) unstable; urgency=medium
 
   * New upstream version
diff -Nru dino-im-0.5.0/debian/control dino-im-0.5.1/debian/control
--- dino-im-0.5.0/debian/control	2025-04-11 17:07:22.000000000 +0000
+++ dino-im-0.5.1/debian/control	2025-11-16 00:01:44.000000000 +0000
@@ -28,7 +28,6 @@
 	ninja-build,
 	valac,
 Standards-Version: 4.7.0
-Rules-Requires-Root: no
 Homepage: https://dino.im
 Vcs-Browser: https://salsa.debian.org/xmpp-team/dino-im
 Vcs-Git: https://salsa.debian.org/xmpp-team/dino-im.git
diff -Nru dino-im-0.5.0/im.dino.Dino.json dino-im-0.5.1/im.dino.Dino.json
--- dino-im-0.5.0/im.dino.Dino.json	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/im.dino.Dino.json	2025-11-15 20:30:00.000000000 +0000
@@ -1,7 +1,7 @@
 {
     "id": "im.dino.Dino",
     "runtime": "org.gnome.Platform",
-    "runtime-version": "48",
+    "runtime-version": "49",
     "sdk": "org.gnome.Sdk",
     "command": "dino",
     "finish-args": [
@@ -28,9 +28,15 @@
             ],
             "sources": [
                 {
-                    "type": "git",
-                    "url": "https://github.com/protocolbuffers/protobuf.git";,
-                    "tag": "v29.4"
+                    "type": "archive",
+                    "url": "https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protobuf-30.2.tar.gz";,
+                    "sha512": "555d1b18d175eeaf17f3879f124d33080f490367840d35b34bfc4e4a5b383bf6a1d09f1570acb6af9c53ac4940a14572d46423b6e3dd0c712e7802c986fb6be6",
+                    "x-checker-data": {
+                        "type": "anitya",
+                        "project-id": 3715,
+                        "stable-only": true,
+                        "url-template": "https://github.com/protocolbuffers/protobuf/releases/download/v$version/protobuf-$version.tar.gz";
+                    }
                 }
             ]
         },
@@ -48,9 +54,15 @@
             ],
             "sources": [
                 {
-                    "type": "git",
-                    "url": "https://github.com/protobuf-c/protobuf-c.git";,
-                    "tag": "v1.5.1"
+                    "type": "archive",
+                    "url": "https://github.com/protobuf-c/protobuf-c/releases/download/v1.5.2/protobuf-c-1.5.2.tar.gz";,
+                    "sha512": "78dc72988d7e8232c1b967849aa00939bc05ab7d39b86a8e2af005e38aa4ef4c9b03920d51fb5337399d980e65f35d11bd4742bea745a893ecc909f56a51c9ac",
+                    "x-checker-data": {
+                        "type": "anitya",
+                        "project-id": 3716,
+                        "stable-only": true,
+                        "url-template": "https://github.com/protobuf-c/protobuf-c/releases/download/v$version/protobuf-c-$version.tar.gz";
+                    }
                 }
             ]
         },
@@ -67,26 +79,41 @@
             ],
             "sources": [
                 {
-                    "type": "git",
-                    "url": "https://github.com/dino/libomemo-c.git";,
-                    "tag": "v0.5.1"
+                    "type": "archive",
+                    "url": "https://github.com/dino/libomemo-c/releases/download/v0.5.1/libomemo-c-0.5.1.tar.gz";,
+                    "sha512": "ff59565406c51663f2944e9a7c12c5b0e3fa01073039f5161472dd81f59194b1cf2685bc1e0cc930a141bc409b965c5d93313cfc3e0e237250102af3b5e88826",
+                    "x-checker-data": {
+                        "type": "anitya",
+                        "project-id": 359676,
+                        "stable-only": true,
+                        "url-template": "https://github.com/dino/libomemo-c/releases/download/v$version/libomemo-c-$version.tar.gz";
+                    }
                 }
             ]
         },
         {
             "name": "qrencode",
-            "buildsystem": "cmake-ninja",
+            "buildsystem": "autotools",
             "cleanup": [
                 "*"
             ],
+            "build-options": {
+                "cflags": "-fPIC"
+            },
             "config-opts": [
-                "-DCMAKE_C_FLAGS=-fPIC"
+                "--disable-shared"
             ],
             "sources": [
                 {
                     "type": "archive",
-                    "url": "https://fukuchi.org/works/qrencode/qrencode-4.1.1.tar.gz";,
-                    "sha512": "209bb656ae3f391b03c7b3ceb03e34f7320b0105babf48b619e7a299528b8828449e0e7696f0b5db0d99170a81709d0518e34835229a748701e7df784e58a9ce"
+                    "url": "https://github.com/fukuchi/libqrencode/archive/refs/tags/v4.1.1.tar.gz";,
+                    "sha512": "584106e7bcaaa1ef2efe63d653daad38d4ff436eb4b185a1db3c747169c1ffa74149c3b1329bb0b8ae007903db0a7034aabf135cc196d91a37b5c61348154a65",
+                    "x-checker-data": {
+                        "type": "anitya",
+                        "project-id": 12834,
+                        "stable-only": true,
+                        "url-template": "https://github.com/fukuchi/libqrencode/archive/refs/tags/v$version.tar.gz";
+                    }
                 }
             ]
         },
diff -Nru dino-im-0.5.0/libdino/meson.build dino-im-0.5.1/libdino/meson.build
--- dino-im-0.5.0/libdino/meson.build	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/meson.build	2025-11-15 20:30:00.000000000 +0000
@@ -1,10 +1,16 @@
 # version_vala
-dot_git = meson.current_source_dir() / '../.git'
-version_file = meson.current_source_dir() / '../VERSION'
-command = [prog_python, files('version.py'), version_file, '--git-repo', meson.current_source_dir()]
+version_py = files('version.py')[0]
+if meson.version().version_compare('>=1.4.0')
+    version_py_path = version_py.full_path()
+else
+    version_py_path = meson.current_source_dir() / 'version.py'
+endif
+command = [prog_python, version_py_path, version_file_path, '--git-repo', meson.current_source_dir()]
 if prog_git.found()
     command += ['--git', prog_git]
 endif
+# We add those dummy args to ensure meson sees the dependency
+command += [version_py, version_file]
 version_vala = vcs_tag(command: command, input: 'src/version.vala.in', output: 'version.vala', replace_string: '%VERSION%')
 
 # libdino
diff -Nru dino-im-0.5.0/libdino/src/entity/file_transfer.vala dino-im-0.5.1/libdino/src/entity/file_transfer.vala
--- dino-im-0.5.0/libdino/src/entity/file_transfer.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/entity/file_transfer.vala	2025-11-15 20:30:00.000000000 +0000
@@ -224,7 +224,7 @@
         }
 
         foreach (Xep.StatelessFileSharing.Source source in sfs_sources) {
-            add_sfs_source(source);
+            persist_source(source);
         }
 
         notify.connect(on_update);
@@ -234,7 +234,13 @@
         if (sfs_sources.contains(source)) return; // Don't add the same source twice. Might happen due to MAM and lacking deduplication.
 
         sfs_sources.add(source);
+        if (id != -1) {
+            persist_source(source);
+        }
+        sources_changed();
+    }
 
+    private void persist_source(Xep.StatelessFileSharing.Source source) {
         Xep.StatelessFileSharing.HttpSource? http_source = source as Xep.StatelessFileSharing.HttpSource;
         if (http_source != null) {
             db.sfs_sources.insert()
@@ -243,8 +249,6 @@
                     .value(db.sfs_sources.data, http_source.url)
                     .perform();
         }
-
-        sources_changed();
     }
 
     public File? get_file() {
diff -Nru dino-im-0.5.0/libdino/src/entity/message.vala dino-im-0.5.1/libdino/src/entity/message.vala
--- dino-im-0.5.0/libdino/src/entity/message.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/entity/message.vala	2025-11-15 20:30:00.000000000 +0000
@@ -200,7 +200,7 @@
                     if (!fallbacks_by_ns.has_key(ns_uri)) {
                         fallbacks_by_ns[ns_uri] = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
                     }
-                    fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation(row[db.body_meta.from_char], row[db.body_meta.to_char]));
+                    fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation.partial_body(row[db.body_meta.from_char], row[db.body_meta.to_char]));
                     break;
                 case Xep.MessageMarkup.NS_URI:
                     var types = new ArrayList<Xep.MessageMarkup.SpanType>();
@@ -212,7 +212,7 @@
 
         var fallbacks = new ArrayList<Xep.FallbackIndication.Fallback>();
         foreach (string ns_uri in fallbacks_by_ns.keys) {
-            fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri].to_array()));
+            fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri]));
         }
         this.fallbacks = fallbacks;
         this.markups = markups;
diff -Nru dino-im-0.5.0/libdino/src/plugin/interfaces.vala dino-im-0.5.1/libdino/src/plugin/interfaces.vala
--- dino-im-0.5.0/libdino/src/plugin/interfaces.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/plugin/interfaces.vala	2025-11-15 20:30:00.000000000 +0000
@@ -48,7 +48,7 @@
     public abstract string name { get; }
     public virtual int16 label_top_padding { get { return -1; } }
 
-    public abstract signal void activated();
+    public signal void activated();
     public abstract void deactivate();
 
     public abstract void set_account(Account account);
@@ -90,17 +90,17 @@
     public abstract void unset_conversation();
 }
 
-public abstract interface ConversationItemPopulator : Object {
+public interface ConversationItemPopulator : Object {
     public abstract string id { get; }
     public abstract void init(Conversation conversation, ConversationItemCollection summary, WidgetType type);
     public abstract void close(Conversation conversation);
 }
 
-public abstract interface ConversationAdditionPopulator : ConversationItemPopulator {
+public interface ConversationAdditionPopulator : ConversationItemPopulator {
     public virtual void populate_timespan(Conversation conversation, DateTime from, DateTime to) { }
 }
 
-public abstract interface VideoCallPlugin : Object {
+public interface VideoCallPlugin : Object {
 
     public abstract bool supported();
     // Video widget
@@ -117,14 +117,14 @@
     public abstract void dump_dot();
 }
 
-public abstract interface VideoCallWidget : Object {
+public interface VideoCallWidget : Object {
     public signal void resolution_changed(uint width, uint height);
     public abstract void display_stream(Xmpp.Xep.JingleRtp.Stream? stream, Jid jid);
     public abstract void display_device(MediaDevice device);
     public abstract void detach();
 }
 
-public abstract interface MediaDevice : Object {
+public interface MediaDevice : Object {
     public abstract string id { owned get; }
     public abstract string display_name { owned get; }
     public abstract string? detail_name { owned get; }
@@ -133,7 +133,7 @@
     public abstract bool incoming { get; }
 }
 
-public abstract interface NotificationPopulator : Object {
+public interface NotificationPopulator : Object {
     public abstract string id { get; }
     public abstract void init(Conversation conversation, NotificationCollection summary, WidgetType type);
     public abstract void close(Conversation conversation);
diff -Nru dino-im-0.5.0/libdino/src/service/file_manager.vala dino-im-0.5.1/libdino/src/service/file_manager.vala
--- dino-im-0.5.0/libdino/src/service/file_manager.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/file_manager.vala	2025-11-15 20:30:00.000000000 +0000
@@ -332,7 +332,10 @@
             file_transfer.path = file.get_basename();
 
             FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE);
-            file_transfer.mime_type = file_info.get_content_type();
+            if (file_info.get_content_type() != "application/octet-stream" || file_transfer.mime_type == null) {
+                // Only overwrite mime_type if it's better than what we had before.
+                file_transfer.mime_type = file_info.get_content_type();
+            }
 
             file_transfer.state = FileTransfer.State.COMPLETE;
         } catch (IOError.CANCELLED e) {
diff -Nru dino-im-0.5.0/libdino/src/service/history_sync.vala dino-im-0.5.1/libdino/src/service/history_sync.vala
--- dino-im-0.5.0/libdino/src/service/history_sync.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/history_sync.vala	2025-11-15 20:30:00.000000000 +0000
@@ -23,7 +23,7 @@
 
     private HashMap<string, Gee.List<Xmpp.MessageStanza>> stanzas = new HashMap<string, Gee.List<Xmpp.MessageStanza>>();
 
-    public class HistorySync(Database db, StreamInteractor stream_interactor) {
+    public HistorySync(Database db, StreamInteractor stream_interactor) {
         this.stream_interactor = stream_interactor;
         this.db = db;
 
@@ -121,7 +121,7 @@
     }
 
     public async void fetch_everything(Account account, Jid mam_server, Cancellable? cancellable = null, DateTime until_earliest_time = new DateTime.from_unix_utc(0)) {
-        debug("Fetch everything for %s %s", mam_server.to_string(), until_earliest_time != null ? @"(until $until_earliest_time)" : "");
+        debug("[%s | %s] Fetch everything %s", account.bare_jid.to_string(), mam_server.to_string(), until_earliest_time != null ? @"(until $until_earliest_time)" : "");
         RowOption latest_row_opt = db.mam_catchup.select()
                 .with(db.mam_catchup.account_id, "=", account.id)
                 .with(db.mam_catchup.server_jid, "=", mam_server.to_string())
@@ -158,9 +158,12 @@
 
         // Fetch messages between two db ranges and merge them
         while (current_row != null && previous_row != null) {
-            if (current_row[db.mam_catchup.from_end]) return;
+            if (current_row[db.mam_catchup.from_end]) {
+                debug("[%s | %s] No logs on server before %s, aborting sync.", account.bare_jid.to_string(), mam_server.to_string(), current_row[db.mam_catchup.from_time].to_string());
+                return;
+            }
 
-            debug("[%s] Fetching between ranges %s - %s", mam_server.to_string(), previous_row[db.mam_catchup.to_time].to_string(), current_row[db.mam_catchup.from_time].to_string());
+            debug("[%s | %s] Fetching between ranges %s - %s", account.bare_jid.to_string(), mam_server.to_string(), previous_row[db.mam_catchup.to_time].to_string(), current_row[db.mam_catchup.from_time].to_string());
             current_row = yield fetch_between_ranges(account, mam_server, previous_row, current_row, cancellable);
             if (current_row == null) return;
 
@@ -175,11 +178,19 @@
         }
 
         // We're at the earliest range. Try to expand it even further back.
-        if (current_row == null || current_row[db.mam_catchup.from_end]) return;
+        if (current_row == null) {
+            debug("[%s | %s] No current range, aborting sync.", account.bare_jid.to_string(), mam_server.to_string());
+            return;
+        } else if (current_row[db.mam_catchup.from_end]) {
+            debug("[%s | %s] No logs on server before %s, aborting sync.", account.bare_jid.to_string(), mam_server.to_string(), current_row[db.mam_catchup.from_time].to_string());
+            return;
+        }
         // We don't want to fetch before the earliest range over and over again in MUCs if it's after until_earliest_time.
         // For now, don't query if we are within a week of until_earliest_time
-        if (until_earliest_time != null &&
-                current_row[db.mam_catchup.from_time] > until_earliest_time.add(-TimeSpan.DAY * 7).to_unix()) return;
+        if (until_earliest_time != null && current_row[db.mam_catchup.from_time] <= until_earliest_time.to_unix()) {
+            debug("[%s | %s] Current range starting %s is before limit %s, aborting sync.", account.bare_jid.to_string(), mam_server.to_string(), current_row[db.mam_catchup.from_time].to_string(), until_earliest_time.to_unix().to_string());
+            return;
+        }
         yield fetch_before_range(account, mam_server, current_row, until_earliest_time);
     }
 
@@ -328,20 +339,29 @@
             page_result = yield get_mam_page(account, query_params, page_result, cancellable);
             debug("[%s | %s] Page result %s (got stanzas: %s)", account.bare_jid.to_string(), query_params.mam_server.to_string(), page_result.page_result.to_string(), (page_result.stanzas != null).to_string());
 
-            if (page_result.page_result == PageResult.Error || page_result.page_result == PageResult.Cancelled || page_result.query_result.first == null) return page_result;
+            if (page_result.page_result == PageResult.Error || page_result.page_result == PageResult.Cancelled) return page_result;
 
             string earliest_mam_id = page_result.query_result.first;
-            long earliest_mam_time = (long)mam_times[account][earliest_mam_id].to_unix();
+            long earliest_mam_time = earliest_mam_id != null ? (long)mam_times[account][earliest_mam_id].to_unix() : 0;
 
-            debug("Updating %s to %s, %s", query_params.mam_server.to_string(), earliest_mam_time.to_string(), earliest_mam_id);
             var query = db.mam_catchup.update()
-                    .with(db.mam_catchup.id, "=", db_id)
-                    .set(db.mam_catchup.from_time, earliest_mam_time)
-                    .set(db.mam_catchup.from_id, earliest_mam_id);
+                    .with(db.mam_catchup.id, "=", db_id);
+            if (earliest_mam_id != null) {
+                debug("[%s | %s] Updating to %s, %s", account.bare_jid.to_string(), query_params.mam_server.to_string(), earliest_mam_time.to_string(), earliest_mam_id);
+                query.set(db.mam_catchup.from_id, earliest_mam_id);
+                if (page_result.page_result != PageResult.NoMoreMessages || query_params.start != null || earliest_mam_time < query_params.start.to_unix()) {
+                    query.set(db.mam_catchup.from_time, earliest_mam_time);
+                }
+            }
 
             if (page_result.page_result == PageResult.NoMoreMessages) {
-                // If the server doesn't have more messages, store that this range is at its end.
-                query.set(db.mam_catchup.from_end, true);
+                // If the server doesn't have more messages, store that this range is at its end or update the from timestamp
+                if (query_params.start != null) {
+                    debug("[%s | %s] Updating to %s based on query", account.bare_jid.to_string(), query_params.mam_server.to_string(), query_params.start.to_string());
+                    query.set(db.mam_catchup.from_time, (long) query_params.start.to_unix());
+                } else {
+                    query.set(db.mam_catchup.from_end, true);
+                }
             }
             query.perform();
         } while (page_result.page_result == PageResult.MorePagesAvailable);
diff -Nru dino-im-0.5.0/libdino/src/service/message_processor.vala dino-im-0.5.1/libdino/src/service/message_processor.vala
--- dino-im-0.5.0/libdino/src/service/message_processor.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/message_processor.vala	2025-11-15 20:30:00.000000000 +0000
@@ -159,19 +159,17 @@
         Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message);
         EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
         if (mam_message_flag != null && mam_message_flag.mam_id != null) {
-            bool server_does_mam = entity_info.has_feature_cached(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI);
-            if (server_does_mam) {
+            bool sender_supports_mam = entity_info.has_feature_cached(account, mam_message_flag.sender_jid, Xmpp.MessageArchiveManagement.NS_URI);
+            if (sender_supports_mam) {
                 new_message.server_id = mam_message_flag.mam_id;
             }
         } else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
-            bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
-                    (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
+            bool server_supports_sid = yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI);
             if (server_supports_sid) {
                 new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
             }
         } else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) {
-            bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
-                    (yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
+            bool server_supports_sid = yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI);
             if (server_supports_sid) {
                 new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid);
             }
diff -Nru dino-im-0.5.0/libdino/src/service/muc_manager.vala dino-im-0.5.1/libdino/src/service/muc_manager.vala
--- dino-im-0.5.0/libdino/src/service/muc_manager.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/muc_manager.vala	2025-11-15 20:30:00.000000000 +0000
@@ -110,7 +110,9 @@
                     }
                     if (!mucs_sync_cancellables[account].has_key(jid.bare_jid)) {
                         mucs_sync_cancellables[account][jid.bare_jid] = new Cancellable();
-                        history_sync.fetch_everything.begin(account, jid.bare_jid, mucs_sync_cancellables[account][jid.bare_jid], conversation.active_last_changed, (_, res) => {
+                        // If the conversation was newly opened, fetch a bit before
+                        DateTime fetch_from_time = conversation.active_last_changed.add(-TimeSpan.DAY * 5);
+                        history_sync.fetch_everything.begin(account, jid.bare_jid, mucs_sync_cancellables[account][jid.bare_jid], fetch_from_time, (_, res) => {
                             history_sync.fetch_everything.end(res);
                             mucs_sync_cancellables[account].unset(jid.bare_jid);
                         });
diff -Nru dino-im-0.5.0/libdino/src/service/reactions.vala dino-im-0.5.1/libdino/src/service/reactions.vala
--- dino-im-0.5.0/libdino/src/service/reactions.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/reactions.vala	2025-11-15 20:30:00.000000000 +0000
@@ -63,8 +63,7 @@
         } else {
             // The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids)
             var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
-            bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
-                    (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
+            bool server_supports_sid = entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI);
             if (!server_supports_sid) return false;
 
             bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI);
diff -Nru dino-im-0.5.0/libdino/src/service/roster_manager.vala dino-im-0.5.1/libdino/src/service/roster_manager.vala
--- dino-im-0.5.0/libdino/src/service/roster_manager.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/roster_manager.vala	2025-11-15 20:30:00.000000000 +0000
@@ -86,7 +86,7 @@
 
     private HashMap<Jid, Roster.Item> items = new HashMap<Jid, Roster.Item>(Jid.hash_bare_func, Jid.equals_bare_func);
 
-    public class RosterStoreImpl(Account account, Database db) {
+    public RosterStoreImpl(Account account, Database db) {
         this.account = account;
         this.db = db;
 
diff -Nru dino-im-0.5.0/libdino/src/service/stateless_file_sharing.vala dino-im-0.5.1/libdino/src/service/stateless_file_sharing.vala
--- dino-im-0.5.0/libdino/src/service/stateless_file_sharing.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/service/stateless_file_sharing.vala	2025-11-15 20:30:00.000000000 +0000
@@ -156,6 +156,12 @@
                     return true;
                 }
             }
+
+            // Don't process messages that are fallback for legacy clients
+            if (Xep.StatelessFileSharing.is_sfs_fallback_message(stanza)) {
+                return true;
+            }
+
             return false;
         }
     }
diff -Nru dino-im-0.5.0/libdino/src/util/limit_input_stream.vala dino-im-0.5.1/libdino/src/util/limit_input_stream.vala
--- dino-im-0.5.0/libdino/src/util/limit_input_stream.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/util/limit_input_stream.vala	2025-11-15 20:30:00.000000000 +0000
@@ -23,7 +23,10 @@
 
     public bool is_readable() {
             if (!can_poll()) throw new IOError.NOT_SUPPORTED("Stream is not pollable");
-            return remaining_bytes == 0 || ((PollableInputStream)inner).is_readable();
+            // Due to https://gitlab.gnome.org/GNOME/libsoup/-/issues/473 and
+            // https://gitlab.gnome.org/GNOME/glib-networking/-/issues/20, is_readable() can return false when
+            // approaching end of stream even if the stream is readable.
+            return remaining_bytes < 65536 || ((PollableInputStream)inner).is_readable();
         }
 
     private ssize_t check_limit(ssize_t read) throws IOError {
diff -Nru dino-im-0.5.0/libdino/src/util/send_message.vala dino-im-0.5.1/libdino/src/util/send_message.vala
--- dino-im-0.5.0/libdino/src/util/send_message.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/src/util/send_message.vala	2025-11-15 20:30:00.000000000 +0000
@@ -26,9 +26,10 @@
             out_message.body = fallback + out_message.body;
 
             // Store fallback location
-            var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback.char_count());
+            var fallback_locations = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
+            fallback_locations.add(new Xep.FallbackIndication.FallbackLocation.partial_body(0, (int)fallback.char_count()));
             var fallback_list = new ArrayList<Xep.FallbackIndication.Fallback>();
-            fallback_list.add(new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location }));
+            fallback_list.add(new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, fallback_locations));
             out_message.set_fallbacks(fallback_list);
 
             // Adjust markups to new prefix
diff -Nru dino-im-0.5.0/libdino/version.py dino-im-0.5.1/libdino/version.py
--- dino-im-0.5.0/libdino/version.py	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/libdino/version.py	2025-11-15 20:30:00.000000000 +0000
@@ -26,7 +26,7 @@
             return None
         git_describe = subprocess.check_output([git, "describe", "--tags"], cwd=git_repo, text=True).strip()
         if git_release_tag == git_describe:
-            return git_release_tag
+            return git_release_tag[1:]
         matches = re.match("^.*-([0-9]+)-g([0-9a-f]+)$", git_describe)
         if matches is None:
             return None
@@ -34,7 +34,7 @@
         git_commit_hash = matches.groups()[1]
         git_commit_time = subprocess.check_output([git, "show", "--format=%cd", "--date=format:%Y%m%d", "-s"],
                                                   cwd=git_repo, text=True).strip()
-        return "%s~git%s.%s.%s" % (git_release_tag, git_tag_offset, git_commit_time, git_commit_hash)
+        return "%s~git%s.%s.%s" % (git_release_tag[1:], git_tag_offset, git_commit_time, git_commit_hash)
     except subprocess.CalledProcessError:
         pass
     return None
@@ -56,7 +56,7 @@
     p.add_argument("--git", help="Path to git executable", default="git")
     p.add_argument("version_file", metavar="VERSION_FILE",
                    help="Use this file's contents as version if the file exists")
-    args = p.parse_args()
+    args, unknown = p.parse_known_args()
     version = compute_version(args.version_file, args.git_repo, args.git)
     print(version)
 
diff -Nru dino-im-0.5.0/main/data/conversation_view.ui dino-im-0.5.1/main/data/conversation_view.ui
--- dino-im-0.5.0/main/data/conversation_view.ui	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/conversation_view.ui	2025-11-15 20:30:00.000000000 +0000
@@ -68,6 +68,14 @@
                         </child>
                     </object>
                 </child>
+                <child type="overlay">
+                    <object class="GtkRevealer" id="content_revealer">
+                        <property name="transition-type">crossfade</property>
+                        <property name="transition-duration">100</property>
+                        <property name="reveal-child">False</property>
+                        <property name="visible">False</property>
+                    </object>
+                </child>
             </object>
         </child>
     </template>
diff -Nru dino-im-0.5.0/main/data/gresource.xml dino-im-0.5.1/main/data/gresource.xml
--- dino-im-0.5.0/main/data/gresource.xml	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/gresource.xml	2025-11-15 20:30:00.000000000 +0000
@@ -22,6 +22,7 @@
     <file>file_send_overlay.ui</file>
     <file>global_search.ui</file>
     <file>gtk/help-overlay.ui</file>
+    <file>icons/scalable/actions/check-plain-symbolic.svg</file>
     <file>icons/scalable/actions/dino-emoticon-add-symbolic.svg</file>
     <file>icons/scalable/actions/dino-qr-code-symbolic.svg</file>
     <file>icons/scalable/actions/small-x-symbolic.svg</file>
diff -Nru dino-im-0.5.0/main/data/gtk/help-overlay.ui dino-im-0.5.1/main/data/gtk/help-overlay.ui
--- dino-im-0.5.0/main/data/gtk/help-overlay.ui	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/gtk/help-overlay.ui	2025-11-15 20:30:00.000000000 +0000
@@ -49,6 +49,12 @@
                                 <property name="title" translatable="yes">Send a file</property>
                             </object>
                         </child>
+                        <child>
+                            <object class="GtkShortcutsShortcut">
+                                <property name="accelerator">&lt;ctrl&gt;W</property>
+                                <property name="title" translatable="yes">Close conversation</property>
+                            </object>
+                        </child>
                     </object>
                 </child>
                 <child>
diff -Nru dino-im-0.5.0/main/data/icons/scalable/actions/check-plain-symbolic.svg dino-im-0.5.1/main/data/icons/scalable/actions/check-plain-symbolic.svg
--- dino-im-0.5.0/main/data/icons/scalable/actions/check-plain-symbolic.svg	1970-01-01 00:00:00.000000000 +0000
+++ dino-im-0.5.1/main/data/icons/scalable/actions/check-plain-symbolic.svg	2025-11-15 20:30:00.000000000 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; height="16px" viewBox="0 0 16 16" width="16px"><path d="m 13.753906 4.65625 c 0.175782 -0.199219 0.261719 -0.460938 0.246094 -0.722656 c -0.019531 -0.265625 -0.140625 -0.511719 -0.339844 -0.6875 c -0.199218 -0.175782 -0.460937 -0.265625 -0.726562 -0.246094 c -0.265625 0.015625 -0.511719 0.140625 -0.6875 0.339844 l -6.296875 7.195312 l -2.242188 -2.246094 c -0.390625 -0.390624 -1.023437 -0.390624 -1.414062 0 c -0.1875 0.1875 -0.292969 0.445313 -0.292969 0.710938 s 0.105469 0.519531 0.292969 0.707031 l 3 3 c 0.195312 0.195313 0.464843 0.300781 0.742187 0.292969 c 0.273438 -0.011719 0.535156 -0.132812 0.71875 -0.34375 z m 0 0" fill="#222222"/></svg>
diff -Nru dino-im-0.5.0/main/data/icons/scalable/status/dino-double-tick-symbolic.svg dino-im-0.5.1/main/data/icons/scalable/status/dino-double-tick-symbolic.svg
--- dino-im-0.5.0/main/data/icons/scalable/status/dino-double-tick-symbolic.svg	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/icons/scalable/status/dino-double-tick-symbolic.svg	2025-11-15 20:30:00.000000000 +0000
@@ -1,7 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<svg width="16" height="16" style="enable-background:new" version="1.1" xmlns="http://www.w3.org/2000/svg"; xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";>
- <g transform="translate(-345 -121)">
-  <path d="m356.03 124.03c-0.21888 0.0473-0.42059 0.17053-0.5625 0.34375l-6.2812 7.1875-2.25-2.25c-0.37633-0.37638-1.0612-0.3764-1.4376-5e-5 -0.37635 0.37636-0.37633 1.0612 5e-5 1.4376l3 3 0.78125 0.75 0.6875-0.8125 7-8c0.56742-0.61773-0.11583-1.8248-0.9375-1.6562z" style="color:#000000;enable-background:accumulate;fill:#555;text-decoration-line:none;text-indent:0;text-transform:none"/>
-  <path d="m359.75 124.03c-0.23347 0.0504-0.44863 0.1819-0.6 0.36667l-6.7 7.6667-0.34583-0.34583-1.4259 1.6187 1.0384 1.0605 0.83333 0.8 0.73334-0.86667 7.4667-8.5333c0.60525-0.65892-0.12355-1.9465-1-1.7667z" style="color:#000000;enable-background:accumulate;fill:#555;text-decoration-line:none;text-indent:0;text-transform:none"/>
- </g>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg"; >
+ <path d="M11.03 3.03a1.01 1.01 0 0 0-.563.344l-6.28 7.187-2.25-2.25c-.377-.376-1.062-.376-1.438 0-.377.377-.377 1.061 0 1.438l3 3 .781.75.687-.813 7-8c.568-.617-.115-1.824-.937-1.656zM14.75 3.03c-.233.05-.449.182-.6.367l-6.7 7.666-.346-.345-1.426 1.618 1.039 1.06.833.8.733-.866 7.467-8.533c.605-.66-.124-1.947-1-1.767z"/>
 </svg>
diff -Nru dino-im-0.5.0/main/data/icons/scalable/status/dino-tick-symbolic.svg dino-im-0.5.1/main/data/icons/scalable/status/dino-tick-symbolic.svg
--- dino-im-0.5.0/main/data/icons/scalable/status/dino-tick-symbolic.svg	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/icons/scalable/status/dino-tick-symbolic.svg	2025-11-15 20:30:00.000000000 +0000
@@ -1,6 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<svg width="16" height="16" style="enable-background:new" version="1.1" xmlns="http://www.w3.org/2000/svg"; xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";>
- <g transform="translate(-345 -121)">
-  <path d="m356.03 124.03c-0.21888 0.0473-0.42059 0.17053-0.5625 0.34375l-6.2812 7.1875-2.25-2.25c-0.37633-0.37638-1.0612-0.3764-1.4376-5e-5 -0.37635 0.37636-0.37633 1.0612 5e-5 1.4376l3 3 0.78125 0.75 0.6875-0.8125 7-8c0.56742-0.61773-0.11583-1.8248-0.9375-1.6562z" style="color:#000000;enable-background:accumulate;fill:#555;text-decoration-line:none;text-indent:0;text-transform:none"/>
- </g>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg";>
+ <path d="M11.03 3.03a1.01 1.01 0 0 0-.563.344l-6.28 7.187-2.25-2.25c-.377-.376-1.062-.376-1.438 0-.377.377-.377 1.061 0 1.438l3 3 .781.75.687-.813 7-8c.568-.617-.115-1.824-.937-1.656z"/>
 </svg>
diff -Nru dino-im-0.5.0/main/data/im.dino.Dino.appdata.xml dino-im-0.5.1/main/data/im.dino.Dino.appdata.xml
--- dino-im-0.5.0/main/data/im.dino.Dino.appdata.xml	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/im.dino.Dino.appdata.xml	2025-11-15 20:30:00.000000000 +0000
@@ -227,7 +227,7 @@
     <control>pointing</control>
     <control>keyboard</control>
     <control>touch</control>
-    <display_length>small</display_length>
+    <display_length>360</display_length>
   </supports>
   <project_license>GPL-3.0+</project_license>
   <developer id="im.dino">
diff -Nru dino-im-0.5.0/main/data/im.dino.Dino.appdata.xml.in dino-im-0.5.1/main/data/im.dino.Dino.appdata.xml.in
--- dino-im-0.5.0/main/data/im.dino.Dino.appdata.xml.in	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/im.dino.Dino.appdata.xml.in	2025-11-15 20:30:00.000000000 +0000
@@ -64,7 +64,7 @@
     <control>pointing</control>
     <control>keyboard</control>
     <control>touch</control>
-    <display_length>small</display_length>
+    <display_length>360</display_length>
   </supports>
   <project_license>GPL-3.0+</project_license>
   <developer id="im.dino">
diff -Nru dino-im-0.5.0/main/data/preferences_window/account_preferences_subpage.ui dino-im-0.5.1/main/data/preferences_window/account_preferences_subpage.ui
--- dino-im-0.5.0/main/data/preferences_window/account_preferences_subpage.ui	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/data/preferences_window/account_preferences_subpage.ui	2025-11-15 20:30:00.000000000 +0000
@@ -69,6 +69,7 @@
                     <child>
                       <object class="AdwActionRow" id="xmpp_address">
                         <property name="title" translatable="yes">XMPP Address</property>
+                        <property name="subtitle_selectable">True</property>
                         <style>
                           <class name="property"/>
                         </style>
diff -Nru dino-im-0.5.0/main/src/ui/conversation_content_view/file_default_widget.vala dino-im-0.5.1/main/src/ui/conversation_content_view/file_default_widget.vala
--- dino-im-0.5.0/main/src/ui/conversation_content_view/file_default_widget.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/conversation_content_view/file_default_widget.vala	2025-11-15 20:30:00.000000000 +0000
@@ -20,7 +20,7 @@
 
     private FileTransfer.State state;
 
-    public FileDefaultWidget() {
+    public void init_updating_file_info() {
         EventControllerMotion this_motion_events = new EventControllerMotion();
         this.add_controller(this_motion_events);
         this_motion_events.enter.connect(on_pointer_entered_event);
@@ -39,6 +39,14 @@
         });
     }
 
+    public void set_static_file_info(string? mime_type) {
+        spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap
+
+        image_stack.set_visible_child_name("content_type_image");
+        content_type_image.icon_name = get_file_icon_name(mime_type);
+        mime_label.label = mime_type != null ? ContentType.get_description(mime_type) : null;
+    }
+
     public void update_file_info(string? mime_type, FileTransfer.State state, bool direction, int64 size, int64 transferred_bytes) {
         this.state = state;
 
diff -Nru dino-im-0.5.0/main/src/ui/conversation_content_view/file_widget.vala dino-im-0.5.1/main/src/ui/conversation_content_view/file_widget.vala
--- dino-im-0.5.0/main/src/ui/conversation_content_view/file_widget.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/conversation_content_view/file_widget.vala	2025-11-15 20:30:00.000000000 +0000
@@ -213,6 +213,7 @@
     public void set_file_transfer(FileTransfer file_transfer) {
         this.file_transfer = file_transfer;
 
+        widget.init_updating_file_info();
         widget.name_label.label = file_transfer.file_name;
 
         file_transfer.bind_property("state", this, "file-transfer-state");
diff -Nru dino-im-0.5.0/main/src/ui/conversation_details.vala dino-im-0.5.1/main/src/ui/conversation_details.vala
--- dino-im-0.5.0/main/src/ui/conversation_details.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/conversation_details.vala	2025-11-15 20:30:00.000000000 +0000
@@ -36,6 +36,7 @@
                 affiliation = conference_member.affiliation
             };
         });
+        view_model.account_jid = stream_interactor.get_accounts().size > 1 ? model.conversation.account.bare_jid.to_string() : null;
 
         if (model.domain_blocked) {
             view_model.blocked = DOMAIN;
diff -Nru dino-im-0.5.0/main/src/ui/conversation_titlebar/menu_entry.vala dino-im-0.5.1/main/src/ui/conversation_titlebar/menu_entry.vala
--- dino-im-0.5.0/main/src/ui/conversation_titlebar/menu_entry.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/conversation_titlebar/menu_entry.vala	2025-11-15 20:30:00.000000000 +0000
@@ -18,7 +18,7 @@
 
         Menu menu_model = new Menu();
         menu_model.append(_("Conversation Details"), "conversation.details");
-        menu_model.append(_("Close Conversation"), "conversation.close");
+        menu_model.append(_("Close Conversation"), "app.close-current-conversation");
         Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
         button.popover = popover_menu;
 
@@ -29,11 +29,6 @@
             GLib.Application.get_default().activate_action("open-conversation-details", variant);
         });
         action_group.insert(details_action);
-        SimpleAction close_action = new SimpleAction("close", null);
-        close_action.activate.connect((parameter) => {
-            stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation);
-        });
-        action_group.insert(close_action);
         button.insert_action_group("conversation", action_group);
     }
 
diff -Nru dino-im-0.5.0/main/src/ui/conversation_view.vala dino-im-0.5.1/main/src/ui/conversation_view.vala
--- dino-im-0.5.0/main/src/ui/conversation_view.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/conversation_view.vala	2025-11-15 20:30:00.000000000 +0000
@@ -16,6 +16,7 @@
     [GtkChild] public unowned ChatInput.View chat_input;
     [GtkChild] public unowned ConversationSummary.ConversationView conversation_frame;
     [GtkChild] public unowned Revealer white_revealer;
+    [GtkChild] public unowned Revealer content_revealer;
 
     public ListView list_view = new ListView(null, null);
 
@@ -31,24 +32,25 @@
     }
 
     public void add_overlay_dialog(Widget widget) {
-        Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100 };
-        revealer.set_child(widget);
+        content_revealer.set_child(widget);
 
-        overlay.add_overlay(revealer);
-
-        revealer.reveal_child = true;
+        content_revealer.visible = true;
+        content_revealer.reveal_child = true;
         white_revealer.visible = true;
         white_revealer.reveal_child = true;
-        widget.destroy.connect(() => {
-            overlay.remove_overlay(revealer);
-            white_revealer.reveal_child = false;
-            chat_input.do_focus();
-        });
+    }
+
+    public void remove_overlay_dialog() {
+        white_revealer.reveal_child = false;
+        content_revealer.reveal_child = false;
+        chat_input.do_focus();
     }
 
     private void on_child_revealed_changed() {
         if (!white_revealer.child_revealed) {
             white_revealer.visible = false;
+            content_revealer.visible = false;
+            content_revealer.set_child(null);
         }
     }
 }
diff -Nru dino-im-0.5.0/main/src/ui/conversation_view_controller.vala dino-im-0.5.1/main/src/ui/conversation_view_controller.vala
--- dino-im-0.5.0/main/src/ui/conversation_view_controller.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/conversation_view_controller.vala	2025-11-15 20:30:00.000000000 +0000
@@ -23,6 +23,8 @@
     private StreamInteractor stream_interactor;
     private Conversation? conversation;
 
+    private const string[] KEY_COMBINATION_CLOSE_CONVERSATION = {"<Ctrl>W", null};
+
     public ConversationViewController(ConversationView view, ConversationTitlebar titlebar, StreamInteractor stream_interactor) {
         this.view = view;
         this.titlebar = titlebar;
@@ -112,6 +114,13 @@
             return false;
         }));
         ((Gtk.Window)view.get_root()).add_shortcut(shortcut);
+
+        SimpleAction close_conversation_action = new SimpleAction("close-current-conversation", null);
+        close_conversation_action.activate.connect(() => {
+            stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation);
+        });
+        app.add_action(close_conversation_action);
+        app.set_accels_for_action("app.close-current-conversation", KEY_COMBINATION_CLOSE_CONVERSATION);
     }
 
     public void select_conversation(Conversation? conversation, bool default_initialize_conversation) {
@@ -240,7 +249,7 @@
                 }
             }
             if (!something_works && limits.has_key(0)) {
-                if (!something_works && file_info.get_size() > limits[0]) {
+                if (!something_works && file_info.get_size() > limits[0] && overlay != null) {
                     overlay.set_file_too_large();
                 }
             }
@@ -248,8 +257,10 @@
 
         overlay.close.connect(() => {
             // We don't want drag'n'drop to be active while the overlay is active
+            view.remove_overlay_dialog();
             overlay_dialog = null;
             update_file_upload_status.begin();
+            overlay = null;
         });
 
         view.add_overlay_dialog(overlay.get_widget());
diff -Nru dino-im-0.5.0/main/src/ui/file_send_overlay.vala dino-im-0.5.1/main/src/ui/file_send_overlay.vala
--- dino-im-0.5.0/main/src/ui/file_send_overlay.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/ui/file_send_overlay.vala	2025-11-15 20:30:00.000000000 +0000
@@ -6,16 +6,16 @@
 
 namespace Dino.Ui {
 
-public class FileSendOverlay {
+public class FileSendOverlay : Object {
 
     public signal void close();
     public signal void send_file();
 
     public Box main_box;
-    public Button close_button;
-    public Button send_button;
-    public SizingBin file_widget_insert;
-    public Label info_label;
+    public unowned Button close_button;
+    public unowned Button send_button;
+    public unowned SizingBin file_widget_insert;
+    public unowned Label info_label;
 
     private bool can_send = true;
 
@@ -28,11 +28,11 @@
         info_label = (Label) builder.get_object("info_label");
 
         close_button.clicked.connect(() => {
-            do_close();
+            close();
         });
         send_button.clicked.connect(() => {
             send_file();
-            do_close();
+            close();
         });
 
         load_file_widget.begin(file, file_info);
@@ -48,7 +48,7 @@
         var key_events = new EventControllerKey();
         key_events.key_pressed.connect((keyval) => {
             if (keyval == Gdk.Key.Escape) {
-                do_close();
+                close();
             }
             return false;
         });
@@ -73,7 +73,7 @@
         if (widget == null) {
             FileDefaultWidget default_widget = new FileDefaultWidget();
             default_widget.name_label.label = file_name;
-            default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, FileTransfer.DIRECTION_SENT, (long)file_info.get_size(), 0);
+            default_widget.set_static_file_info(mime_type);
             widget = default_widget;
         }
 
@@ -87,12 +87,6 @@
         can_send = false;
     }
 
-    private void do_close() {
-        this.close();
-        main_box.unparent();
-        main_box.destroy();
-    }
-
     public Widget get_widget() {
         return main_box;
     }
diff -Nru dino-im-0.5.0/main/src/view_model/conversation_details.vala dino-im-0.5.1/main/src/view_model/conversation_details.vala
--- dino-im-0.5.0/main/src/view_model/conversation_details.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/view_model/conversation_details.vala	2025-11-15 20:30:00.000000000 +0000
@@ -40,6 +40,8 @@
     public BlockState blocked { get; set; }
 
     public GLib.ListStore about_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
+    public string? account_jid { get; set; }
+
     public GLib.ListStore settings_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
     public GLib.ListStore room_configuration_rows { get; set; }
     public MapListModel members = new MapListModel(null, null);
diff -Nru dino-im-0.5.0/main/src/windows/conversation_details.vala dino-im-0.5.1/main/src/windows/conversation_details.vala
--- dino-im-0.5.0/main/src/windows/conversation_details.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/main/src/windows/conversation_details.vala	2025-11-15 20:30:00.000000000 +0000
@@ -198,6 +198,12 @@
             if (model.room_configuration_rows != null && model.room_configuration_rows.get_n_items() > 0) {
                 about_box.append(Util.rows_to_preference_group(model.room_configuration_rows, _("Room Configuration")));
             }
+
+            if (model.account_jid != null) {
+                var account_label = new Label(@"via $(model.account_jid)") { halign=Align.START, margin_start=14, margin_top=4 };
+                account_label.add_css_class("dim-label");
+                about_box.append(account_label);
+            }
         }
 
         public void add_encryption_tab_element(Adw.PreferencesGroup preferences_group) {
diff -Nru dino-im-0.5.0/meson.build dino-im-0.5.1/meson.build
--- dino-im-0.5.0/meson.build	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/meson.build	2025-11-15 20:30:00.000000000 +0000
@@ -1,4 +1,4 @@
-project('Dino', 'c', 'cpp', 'vala', license: 'GPL-3.0')
+project('Dino', 'c', 'cpp', 'vala', license: 'GPL-3.0', meson_version: '>=1.3.0')
 
 fs = import('fs')
 gnome = import('gnome')
@@ -56,6 +56,14 @@
 prog_git = find_program('git', required: false)
 prog_python = python.find_installation()
 
+if import('fs').is_file('VERSION') and meson.version().version_compare('>=1.4.0')
+    version_file = files('VERSION')[0]
+    version_file_path = version_file.full_path()
+else
+    version_file = 'invalid'
+    version_file_path = meson.current_source_dir() / 'VERSION'
+endif
+
 default_install_rpath = ''
 if get_option('set-install-rpath')
     default_install_rpath = get_option('prefix') / get_option('libdir')
diff -Nru dino-im-0.5.0/plugins/http-files/src/file_sender.vala dino-im-0.5.1/plugins/http-files/src/file_sender.vala
--- dino-im-0.5.0/plugins/http-files/src/file_sender.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/plugins/http-files/src/file_sender.vala	2025-11-15 20:30:00.000000000 +0000
@@ -43,7 +43,11 @@
         HttpFileSendData? send_data = file_send_data as HttpFileSendData;
         if (send_data == null) return;
 
-        bool can_reference_element = !conversation.type_.is_muc_semantic() || stream_interactor.get_module(EntityInfo.IDENTITY).has_feature_cached(conversation.account, conversation.counterpart, Xep.UniqueStableStanzaIDs.NS_URI);
+        bool can_reference_element = conversation.type_ == Conversation.Type.CHAT || (
+                // The stable stanza ID XEP is not clear about an announcing MUC having to attach stanza-ids, thus we also check for MAM, which requires this.
+                stream_interactor.get_module(EntityInfo.IDENTITY).has_feature_cached(conversation.account, conversation.counterpart, Xep.UniqueStableStanzaIDs.NS_URI) &&
+                stream_interactor.get_module(EntityInfo.IDENTITY).has_feature_cached(conversation.account, conversation.counterpart, Xmpp.MessageArchiveManagement.NS_URI)
+            );
 
         // Share unencrypted files via SFS (only if we'll be able to reference messages)
         if (conversation.encryption == Encryption.NONE && can_reference_element) {
diff -Nru dino-im-0.5.0/plugins/omemo/src/protocol/bundle.vala dino-im-0.5.1/plugins/omemo/src/protocol/bundle.vala
--- dino-im-0.5.0/plugins/omemo/src/protocol/bundle.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/plugins/omemo/src/protocol/bundle.vala	2025-11-15 20:30:00.000000000 +0000
@@ -12,7 +12,7 @@
         assert(Plugin.ensure_context());
     }
 
-    public int32 signed_pre_key_id { owned get {
+    public int32 signed_pre_key_id { get {
         if (node == null) return -1;
         string? id = ((!)node).get_deep_attribute("signedPreKeyPublic", "signedPreKeyId");
         if (id == null) return -1;
@@ -69,7 +69,7 @@
             this.node = node;
         }
 
-        public int32 key_id { owned get {
+        public int32 key_id { get {
             return int.parse(node.get_attribute("preKeyId") ?? "-1");
         }}
 
diff -Nru dino-im-0.5.0/plugins/omemo/src/ui/encryption_preferences_entry.vala dino-im-0.5.1/plugins/omemo/src/ui/encryption_preferences_entry.vala
--- dino-im-0.5.0/plugins/omemo/src/ui/encryption_preferences_entry.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/plugins/omemo/src/ui/encryption_preferences_entry.vala	2025-11-15 20:30:00.000000000 +0000
@@ -280,7 +280,7 @@
         action_row.subtitle = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64]));
 
         Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true };
-        accept_button.set_icon_name("emblem-ok-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita
+        accept_button.set_icon_name("check-plain-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita
         accept_button.add_css_class("suggested-action");
         accept_button.tooltip_text = _("Accept key");
 
diff -Nru dino-im-0.5.0/plugins/omemo/src/ui/manage_key_dialog.vala dino-im-0.5.1/plugins/omemo/src/ui/manage_key_dialog.vala
--- dino-im-0.5.0/plugins/omemo/src/ui/manage_key_dialog.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/plugins/omemo/src/ui/manage_key_dialog.vala	2025-11-15 20:30:00.000000000 +0000
@@ -155,7 +155,7 @@
                 return_to_main = true;
                 current_response = TrustLevel.UNTRUSTED;
             } else if (row == accept_row) {
-                confirm_image.set_from_icon_name("emblem-ok-symbolic");
+                confirm_image.set_from_icon_name("check-plain-symbolic");
                 confirm_title_label.label = _("Accept key");
                 confirm_desc_label.set_markup(_("You will be able to exchange encrypted messages with the device of %s that uses this key.").printf(@"<b>$(device[db.identity_meta.address_name])</b>"));
                 manage_stack.set_visible_child_name("confirm");
diff -Nru dino-im-0.5.0/plugins/rtp/src/device.vala dino-im-0.5.1/plugins/rtp/src/device.vala
--- dino-im-0.5.0/plugins/rtp/src/device.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/plugins/rtp/src/device.vala	2025-11-15 20:30:00.000000000 +0000
@@ -10,7 +10,7 @@
 }
 
 public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
-    private const int[] common_widths = {320, 360, 400, 480, 640, 960, 1280, 1920, 2560, 3840};
+    private const int[] common_widths = {320, 352, 416, 480, 640, 960, 1280, 1920, 2560, 3840};
 
     public Plugin plugin { get; private set; }
     public CodecUtil codec_util { get { return plugin.codec_util; } }
@@ -218,6 +218,9 @@
         }
         if (new_width == active_caps_width) return;
         int new_height = device_caps_height * new_width / device_caps_width;
+        if ((new_height % 2) != 0) {
+            new_height += 1;
+        }
         Gst.Caps new_caps;
         if (device_caps_framerate_den != 0) {
             new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null);
@@ -362,8 +365,9 @@
             int best_height = 0;
             for (int i = 0; i < device.caps.get_size(); i++) {
                 unowned Gst.Structure? that = device.caps.get_structure(i);
+                unowned Gst.CapsFeatures? features = device.caps.get_features(i);
                 Value? best_fraction_now = null;
-                if (!that.has_name("video/x-raw")) continue;
+                if (!that.has_name("video/x-raw") || !features.contains("memory:SystemMemory")) continue;
                 int num = 0, den = 0, width = 0, height = 0;
                 if (!that.has_field("framerate")) continue;
                 Value framerate = that.get_value("framerate");
@@ -400,16 +404,46 @@
             }
             if (best_index == -1) {
                 // No caps in first round, try without framerate
+                int target_pixel_count = 1280 * 720;
+                double target_pixel_ratio = 16.0 / 9.0;
+                double best_diversion_factor = 0.0;
+
                 for (int i = 0; i < device.caps.get_size(); i++) {
                     unowned Gst.Structure? that = device.caps.get_structure(i);
-                    if (!that.has_name("video/x-raw")) continue;
+                    unowned Gst.CapsFeatures? features = device.caps.get_features(i);
+                    if (!that.has_name("video/x-raw") || !features.contains("memory:SystemMemory")) continue;
                     int width = 0, height = 0;
-                    if (!that.has_field("width") || !that.get_int("width", out width)) continue;
-                    if (!that.has_field("height") || !that.get_int("height", out height)) continue;
-                    if (best_width < width || best_width == width && best_height < height) {
+                    if (!that.has_field("width") || !that.get_int("width", out width) || width == 0) continue;
+                    if (!that.has_field("height") || !that.get_int("height", out height) || height == 0) continue;
+
+                    int pixel_count = width * height;
+                    double pixel_count_diversion_factor;
+                    if (pixel_count > target_pixel_count)
+                        pixel_count_diversion_factor = (double)pixel_count / target_pixel_count;
+                    else
+                        pixel_count_diversion_factor = (double)target_pixel_count / pixel_count;
+
+                    double pixel_ratio;
+                    if (width > height)
+                        pixel_ratio = (double)width / height;
+                    else
+                        pixel_ratio = (double)height / width;
+
+                    double ratio_diversion_factor;
+                    if (pixel_ratio > target_pixel_ratio)
+                        ratio_diversion_factor = pixel_ratio / target_pixel_ratio;
+                    else
+                        ratio_diversion_factor = target_pixel_ratio / pixel_ratio;
+
+                    // 1.0 is the best possible value. Any diversion from
+                    // target_pixel_count or target_pixel_ratio increases it.
+                    double diversion_factor = pixel_count_diversion_factor * ratio_diversion_factor;
+
+                    if (best_index == -1 || diversion_factor < best_diversion_factor) {
+                        best_index = i;
                         best_width = width;
                         best_height = height;
-                        best_index = i;
+                        best_diversion_factor = diversion_factor;
                     }
                 }
             }
@@ -418,6 +452,11 @@
             Value? framerate = that.get_value("framerate");
             if (framerate != null && framerate.type() == typeof(Gst.ValueList) && best_fraction != null) {
                 that.set_value("framerate", best_fraction);
+            } else if (framerate == null) {
+                // The source does not support framerate negotiation. Just set a
+                // reasonable value. The source might be able to honor it and
+                // should ignore it otherwise.
+                that.@set("framerate", typeof(Gst.Fraction), 30, 1, null);
             }
             debug("Selected caps %s", res.to_string());
             return res;
diff -Nru dino-im-0.5.0/plugins/rtp/src/voice_processor_native.cpp dino-im-0.5.1/plugins/rtp/src/voice_processor_native.cpp
--- dino-im-0.5.0/plugins/rtp/src/voice_processor_native.cpp	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/plugins/rtp/src/voice_processor_native.cpp	2025-11-15 20:30:00.000000000 +0000
@@ -218,6 +218,7 @@
     frame.samples_per_channel_ = info->rate / 100;
     memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf);
 
+    apm->set_stream_delay_ms(native->stream_delay);
     err = apm->ProcessStream(&frame);
     if (err >= 0) memcpy(map.data, frame.data_, frame.samples_per_channel_ * info->bpf);
 
diff -Nru dino-im-0.5.0/xmpp-vala/src/module/xep/0084_user_avatars.vala dino-im-0.5.1/xmpp-vala/src/module/xep/0084_user_avatars.vala
--- dino-im-0.5.0/xmpp-vala/src/module/xep/0084_user_avatars.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/xmpp-vala/src/module/xep/0084_user_avatars.vala	2025-11-15 20:30:00.000000000 +0000
@@ -57,10 +57,14 @@
         }
 
         public void on_pupsub_item(XmppStream stream, Jid jid, string hash, StanzaNode? node) {
-            StanzaNode? info_node = node.get_subnode("info", NS_URI_METADATA);
-            string? type = info_node == null ? null : info_node.get_attribute("type");
-            if (type != "image/png" && type != "image/jpeg") return;
-            received_avatar_hash(stream, jid, hash);
+            foreach (var info_node in node.get_subnodes("info", NS_URI_METADATA)) {
+                if (info_node.get_attribute("id") == hash) {
+                    string? type = info_node == null ? null : info_node.get_attribute("type");
+                    if (type != "image/png" && type != "image/jpeg") return;
+                    received_avatar_hash(stream, jid, hash);
+                    return;
+                }
+            }
         }
 
         public void on_pubsub_delete(XmppStream stream, Jid jid) {
diff -Nru dino-im-0.5.0/xmpp-vala/src/module/xep/0166_jingle/content.vala dino-im-0.5.1/xmpp-vala/src/module/xep/0166_jingle/content.vala
--- dino-im-0.5.0/xmpp-vala/src/module/xep/0166_jingle/content.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/xmpp-vala/src/module/xep/0166_jingle/content.vala	2025-11-15 20:30:00.000000000 +0000
@@ -254,7 +254,7 @@
     public uint8[] our_key;
     public uint8[] peer_key;
 
-    public class ContentEncryption(string encryption_ns, string encryption_name, uint8[] our_key = new uint8[]{}, uint8[] peer_key = new uint8[]{}) {
+    public ContentEncryption(string encryption_ns, string encryption_name, uint8[] our_key = new uint8[]{}, uint8[] peer_key = new uint8[]{}) {
         this.encryption_ns = encryption_ns;
         this.encryption_name = encryption_name;
         this.our_key = our_key;
diff -Nru dino-im-0.5.0/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala dino-im-0.5.1/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala
--- dino-im-0.5.0/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala	2025-11-15 20:30:00.000000000 +0000
@@ -52,7 +52,7 @@
             field.set_value_string(DateTimeProfiles.to_datetime(mam_params.start));
             fields.add(field);
         }
-        if (mam_params.end != null) {
+        if (mam_params.end != null && mam_params.end_id == null && !mam_params.use_ns2_extended) {
             DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="end" };
             field.set_value_string(DateTimeProfiles.to_datetime(mam_params.end));
             fields.add(field);
diff -Nru dino-im-0.5.0/xmpp-vala/src/module/xep/0428_fallback_indication.vala dino-im-0.5.1/xmpp-vala/src/module/xep/0428_fallback_indication.vala
--- dino-im-0.5.0/xmpp-vala/src/module/xep/0428_fallback_indication.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/xmpp-vala/src/module/xep/0428_fallback_indication.vala	2025-11-15 20:30:00.000000000 +0000
@@ -6,23 +6,28 @@
 
     public class Fallback {
         public string ns_uri { get; set; }
-        public FallbackLocation[] locations;
+        public Gee.List<FallbackLocation> locations;
 
-
-        public Fallback(string ns_uri, FallbackLocation[] locations) {
+        public Fallback(string ns_uri, Gee.List<FallbackLocation> locations) {
             this.ns_uri = ns_uri;
             this.locations = locations;
         }
     }
 
     public class FallbackLocation {
+        public bool is_whole { get { return from_char == -1 && to_char == -1; } }
         public int from_char { get; set; }
         public int to_char { get; set; }
 
-        public FallbackLocation(int from_char, int to_char) {
+        public FallbackLocation.partial_body(int from_char, int to_char) {
             this.from_char = from_char;
             this.to_char = to_char;
         }
+
+        public FallbackLocation.whole_body() {
+            this.from_char = -1;
+            this.to_char = -1;
+        }
     }
 
     public static void set_fallback(MessageStanza message, Fallback fallback) {
@@ -30,10 +35,12 @@
                 .add_self_xmlns()
                 .put_attribute("for", fallback.ns_uri);
         foreach (FallbackLocation location in fallback.locations) {
-            fallback_node.put_node(new StanzaNode.build("body", NS_URI)
-                .add_self_xmlns()
-                .put_attribute("start", location.from_char.to_string())
-                .put_attribute("end", location.to_char.to_string()));
+            var node = new StanzaNode.build("body", NS_URI).add_self_xmlns();
+            if (!location.is_whole) {
+                node.put_attribute("start", location.from_char.to_string())
+                        .put_attribute("end", location.to_char.to_string());
+            }
+            fallback_node.put_node(node);
         }
         message.stanza.put_node(fallback_node);
     }
@@ -48,18 +55,24 @@
             string? ns_uri = fallback_node.get_attribute("for");
             if (ns_uri == null) continue;
 
-            Gee.List<StanzaNode> body_nodes = fallback_node.get_subnodes("body", NS_URI);
-            if (body_nodes.is_empty) continue;
-
             var locations = new ArrayList<FallbackLocation>();
-            foreach (StanzaNode body_node in body_nodes) {
-                int start_char = body_node.get_attribute_int("start", -1);
-                int end_char = body_node.get_attribute_int("end", -1);
-                if (start_char == -1 || end_char == -1) continue;
-                locations.add(new FallbackLocation(start_char, end_char));
+            if (fallback_node.get_all_subnodes().is_empty) {
+                locations.add(new FallbackLocation.whole_body());
+            } else {
+                Gee.List<StanzaNode> body_nodes = fallback_node.get_subnodes("body", NS_URI);
+                foreach (StanzaNode body_node in body_nodes) {
+                    int start_char = body_node.get_attribute_int("start", -1);
+                    int end_char = body_node.get_attribute_int("end", -1);
+                    if (start_char == -1 && end_char == -1) {
+                        locations.add(new FallbackLocation.whole_body());
+                        continue;
+                    }
+                    if (start_char == -1 || end_char == -1) continue;
+                    locations.add(new FallbackLocation.partial_body(start_char, end_char));
+                }
             }
             if (locations.is_empty) continue;
-            ret.add(new Fallback(ns_uri, locations.to_array()));
+            ret.add(new Fallback(ns_uri, locations));
         }
 
         return ret;
diff -Nru dino-im-0.5.0/xmpp-vala/src/module/xep/0446_file_metadata_element.vala dino-im-0.5.1/xmpp-vala/src/module/xep/0446_file_metadata_element.vala
--- dino-im-0.5.0/xmpp-vala/src/module/xep/0446_file_metadata_element.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/xmpp-vala/src/module/xep/0446_file_metadata_element.vala	2025-11-15 20:30:00.000000000 +0000
@@ -20,13 +20,17 @@
                 node.put_node(new StanzaNode.build("name", NS_URI).put_node(new StanzaNode.text(this.name)));
             }
             if (this.mime_type != null) {
+                // TODO remove the media_type node, it was implemented by accident and temporary provides backwards-compatibility
                 node.put_node(new StanzaNode.build("media_type", NS_URI).put_node(new StanzaNode.text(this.mime_type)));
+                node.put_node(new StanzaNode.build("media-type", NS_URI).put_node(new StanzaNode.text(this.mime_type)));
             }
             if (this.size != -1) {
                 node.put_node(new StanzaNode.build("size", NS_URI).put_node(new StanzaNode.text(this.size.to_string())));
             }
             if (this.date != null) {
-                node.put_node(new StanzaNode.build("date", NS_URI).put_node(new StanzaNode.text(this.date.to_string())));
+                node.put_node(new StanzaNode.build("date", NS_URI).put_node(
+                        new StanzaNode.text(DateTimeProfiles.to_datetime(this.date))
+                ));
             }
             if (this.desc != null) {
                 node.put_node(new StanzaNode.build("desc", NS_URI).put_node(new StanzaNode.text(this.desc)));
@@ -65,7 +69,12 @@
         if (desc_node != null && desc_node.get_string_content() != null) {
             metadata.desc = desc_node.get_string_content();
         }
-        StanzaNode? mime_node = file_node.get_subnode("media_type");
+        // TODO remove media_type handling, it was implemented by accident and temporary provides backwards-compatibility
+        StanzaNode? mime_node_bad = file_node.get_subnode("media_type");
+        if (mime_node_bad != null && mime_node_bad.get_string_content() != null) {
+            metadata.mime_type = mime_node_bad.get_string_content();
+        }
+        StanzaNode? mime_node = file_node.get_subnode("media-type");
         if (mime_node != null && mime_node.get_string_content() != null) {
             metadata.mime_type = mime_node.get_string_content();
         }
@@ -75,7 +84,7 @@
         }
         StanzaNode? date_node = file_node.get_subnode("date");
         if (date_node != null && date_node.get_string_content() != null) {
-            metadata.date = new DateTime.from_iso8601(date_node.get_string_content(), null);
+            metadata.date = DateTimeProfiles.parse_string(date_node.get_string_content());
         }
         StanzaNode? width_node = file_node.get_subnode("width");
         if (width_node != null && width_node.get_string_content() != null) {
diff -Nru dino-im-0.5.0/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala dino-im-0.5.1/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala
--- dino-im-0.5.0/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala	2025-04-11 16:30:00.000000000 +0000
+++ dino-im-0.5.1/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala	2025-11-15 20:30:00.000000000 +0000
@@ -11,7 +11,7 @@
             var metadata = Xep.FileMetadataElement.get_file_metadata(file_sharing_node);
             if (metadata == null) continue;
 
-            var sources_node = message.stanza.get_subnode("sources", NS_URI);
+            var sources_node = file_sharing_node.get_subnode("sources", NS_URI);
 
             ret.add(new FileShare() {
                 id = file_sharing_node.get_attribute("id", NS_URI),
@@ -44,6 +44,16 @@
         return ret;
     }
 
+    public static bool is_sfs_fallback_message(MessageStanza message) {
+        Gee.List<FallbackIndication.Fallback> fallbacks = Xep.FallbackIndication.get_fallbacks(message);
+        foreach (var fallback in fallbacks) {
+            if (fallback.ns_uri == StatelessFileSharing.NS_URI && fallback.locations.any_match((it) => it.is_whole)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // Currently only returns a single http source
     private static Gee.List<Source>? get_sources(StanzaNode sources_node) {
         string? url = HttpSchemeForUrlData.get_url(sources_node);

Reply via email to