Commit log

9f5e282 test(persistance): truncated attributes assertion

Phillip Davis created

a4f7fb2 fix(entities): cache MUC occupants in own DB rows

Phillip Davis created

a9d648b Require a SCRAM iteration count of 4096 or higher

Click to expand commit body
The RFC specifies a minimum iteration count of 4096 only as a "SHOULD".
Therefore, requiring 4096 on the client side is technically not fully in
line with the spec. However, in the existing ecosystem, the servers that
we tested all use 4096 or more.

Daniel Gultsch created

3a0803f support latest UP spec wrt pending intent

Click to expand commit body
Previously we used a custom protocol extension to optionally use pending
intents instead of an 'application' extra; nowadays the UnifiedPush spec
supports pending intents officially (and we can stop accepting the
insecure 'application' extra)

Daniel Gultsch created

abe9fe1 limit XML element nesting to 128 levels

Click to expand commit body
beyond that we could theoretically get stack overflow exceptions due to
the recursion in our parser

Daniel Gultsch created

53e8ec1 Always indicate support for channel binding in SASL header

Click to expand commit body
This commit breaks logging in on servers that announce a -PLUS variant for
SCRAM but do not support XEP-0440.

On servers that do not support XEP-0440, we previously decided to use no
channel binding because picking "none" was better than picking an
unsupported one and failing the login.

However, this behaviour also required us to indicate that we did not
support channel binding; otherwise, the server, seeing an unknown binding
mechanism, would fail our login.

This was a decision made for the broadest possible compatibility with the
pre-0440 ecosystem.

Note that the y flag wasn't the only security layer. Conversations also
uses pinning (if you logged in once with -PLUS, it won't fall back) and
XEP-0474 (basically a fancy version of the y flag).

In addition there is a setting in Conversations to always require Channel
Binding. This will also automatically be turned on for conversations.im
and quicksy.im.

It has now been two years since XEP-0440 was released for ejabberd and
Prosody, and our compatibility concerns have shifted: if you want to have
-PLUS on your server, please update the server to support XEP-0440.

Daniel Gultsch created

dc5b902 fix: NPE from unsynchronized MucOptions access

Click to expand commit body
- ConversationGetMucOptionsRaceTest is in its own file to make the
  instrumentation barriers a little easier to see and enforce
  - It simulates basically the following situation:
    i. On app startup, all accounts connect to all MUCs, which
    eventually triggers joinMuc, which calls
    Conversation.resetMucOptions.
    This happens on account's individual XMPP connection Thread.
    ii. At the same time, on the UI thread, we bind
    ConversationsOverviewFragment, which eventually leads to
    ConversationAdapter.onBindViewHolder, which calls getMucOptions.
    iii. Conversation.resetMucOptions is not synchronized, so it can
    run after the null-check in getMucOptions, resulting in a null
    return value.
  - Used AtomicReference instead of synchronizing resetMucOptions
  because joinMuc is run on a directExecutor associated with the
  XmppConnection thread. While the synchronized getMucOptions was
  called right after this in the existing code, the documentation for
  directExecutor has this to say:
  When a ListenableFuture listener is registered to run under
  directExecutor, the listener can execute in any of three possible
  threads:
      - When a thread attaches a listener to a ListenableFuture that's
      already complete, the listener runs immediately in that thread.
      - When a thread attaches a listener to a ListenableFuture that's
      incomplete and the ListenableFuture later completes normally,
      the listener runs in the thread that completes the ListenableFuture.
      - When a listener is attached to a ListenableFuture and the
      ListenableFuture gets cancelled, the listener runs immediately
      in the thread that cancelled the Future.
  - (This is applicable, since directExecutor uses in
  XmppConnectionService are extensively attached to ListenableFuture)
  The docs continue:
  A specific warning about locking: Code that executes user-supplied
  tasks, such as ListenableFuture listeners, should take care not to
  do so while holding a lock. Additionally, as a further line of
  defense, prefer not to perform any locking inside a task that will
  be run under directExecutor: Not only might the wait for a lock
  be long, but if the running thread was holding a lock, the
  listener may deadlock or break lock isolation.
  - Altogether, AtomicReference seems much more aligned with how Google
  intends directExecutor to be used.

Anyway, here's the stacktrace that reported this error:
```
2026-02-20 10:41:09.089  7610-7610  AndroidRuntime          com.cheogram.android                 E  FATAL EXCEPTION: main (Ask Gemini)
                                                                                                    Process: com.cheogram.android, PID: 7610
                                                                                                    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String eu.siacs.conversations.entities.MucOptions.getName()' on a null object reference
                                                                                                    	at eu.siacs.conversations.entities.Conversation.getName(Conversation.java:1068)
                                                                                                    	at eu.siacs.conversations.entities.Conversation.getAvatarName(Conversation.java:1784)
                                                                                                    	at eu.siacs.conversations.ui.util.AvatarWorkerTask.setContentDescription(AvatarWorkerTask.java:135)
                                                                                                    	at eu.siacs.conversations.ui.util.AvatarWorkerTask.loadAvatar(AvatarWorkerTask.java:105)
                                                                                                    	at eu.siacs.conversations.ui.adapter.ConversationAdapter.onBindViewHolder(ConversationAdapter.java:255)
                                                                                                    	at eu.siacs.conversations.ui.adapter.ConversationAdapter.onBindViewHolder(ConversationAdapter.java:33)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7846)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7953)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6742)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:7013)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6853)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6849)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2422)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1722)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1682)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:747)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4737)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4459)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:5011)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
                                                                                                    	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
                                                                                                    	at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
                                                                                                    	at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
                                                                                                    	at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
                                                                                                    	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
                                                                                                    	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
                                                                                                    	at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1273)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
                                                                                                    	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
                                                                                                    	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
```

Phillip Davis created

9c165e4 Allow camera/mic in command UI webview

Stephen Paul Weber created

780f04b fix NPE (and other unsurfaced races)

Phillip Davis created

dd3c912 Allow build to pass without secrets

Stephen Paul Weber created

10521d1 fix ConcurrentModificationException

Click to expand commit body
ConcurrentModificationException can only be triggered by (a) obviously,
multiple threads modifying the same collection or (b) one thread which
modifies the thread while iterating over it (see [here](https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html)). Assuming that `systemTags` doesn't alias `this.systemTags`, it must be (a). Also, the stacktrace we got comes from inside `old.equals`. consider:
```java
final JSONArray old = this.systemTags;
this.systemTags = new JSONArray();
final JSONArray old = this.systemTags;
...
this.systemTags.put(...)
!old.equal(...)
```
and we get ConcurrentModificationException bc Thread 2's `old` alias's
thread 1's `this.systemTags`

The patch fixes this bug by making suring that no references escape
function scope, so no aliasing can occur.

Phillip Davis created

8bc6439 Default to jabber.fr for fallback

Stephen Paul Weber created

d8152c4 Never fall back to iterative DNS for DNSSEC

Click to expand commit body
This can work around if your local resolver strips DNSSEC, but also it
means resolution is bonkers slow and might even take forever / fail if
DNS queries are blocked (because you're on TOR VPN or similar). So if
recursive DNSSEC fails, just fail DNSSEC and fall back to regular DNS lookups.

Stephen Paul Weber created

d0488fb Need to put this file in the source dir

Stephen Paul Weber created

8518d6a add wasm extension and application/wasm MIME

Phillip Davis created

26eabc6 Swap play apk for free bundle

Stephen Paul Weber created

b69787c Silly hack to render code tags at all

Stephen Paul Weber created

0c62120 fix crash from non-existant notification channel

Click to expand commit body
ticket: https://todo.sr.ht/~singpolyma/soprani.ca/502

Phillip Davis created

c41c023 Use less quote for reaction fallback

Stephen Paul Weber created

0a3a86a Put blank line after fallback quote

Stephen Paul Weber created

2ee0fee EmojiSearch.addEmoji fixes CustomEmoji shortcodes

Click to expand commit body
- now `EmojiSearch.emoji` is truly immutable, in the sense that its
members are not mutated after being inserted.
- attempt to fix: https://todo.sr.ht/~singpolyma/soprani.ca/473

Phillip Davis created

b05fdb5 if setAvatar to null, dont check blocked media

Click to expand commit body
codepath is deleteAvatar, which is setAvatar(from, null)

Phillip Davis created

eba6c51 Use correct size for all body elements

Stephen Paul Weber created

e067ced Also check for abilities on bare jid

Click to expand commit body
If we have them

Stephen Paul Weber created

cbdb5ca Match text sizes from new snikket

Stephen Paul Weber created

cafbf17 dont try to load messages if the activity is null

Phillip Davis created

b156e93 Make default option look less like a header

Stephen Paul Weber created

37c5e43 use synchronously initialized activity

Click to expand commit body
fixes NPE when using 'Return to ongoing call' context button

Phillip Davis created

979ba82 add log to unexpected BinderProxy exception

Phillip Davis created

4985523 Add some more UI around call failures

Stephen Paul Weber created

f6802e0 add fitsSystemWindows to activity_webxdc_store.xml

Phillip Davis created

b18cb97 Upgrade sentry for the 16kb problem

Stephen Paul Weber created

47b611d defer account cancellation to another thread

Click to expand commit body
otherwise causes NetworkOnMainThreadException

Phillip Davis created

58d6ff8 kill MAM queries for blocked convos

Phillip Davis created

5059358 fix NPE by refactoring onClick

Phillip Davis created

6065d1b use error-catching version of openInputStream

Phillip Davis created

24c6c03 unhide ImportBackupActivity ActionBar

Phillip Davis created

a8324a0 try-catch `new ToneGenerator` and log errors

Click to expand commit body
trouble reproducing reports of failure stemming from
`WebRTCWrapper.applyDtmfTone` failing on this constructor, specifically:
```
at android.media.ToneGenerator.native_setup(Native Method)
at android.media.ToneGenerator.<init>(ToneGenerator.java:751)
```
so we log the error for next time and catch it to prevent crashing, and
instead just dont play the tone

Phillip Davis created

bcbafc9 fix TOCTOU in command pager

Click to expand commit body
- in CommandSession constructor, must construct a hard reference to the
  ViewPager to make garbage collection impossible until at least
  `getContext()` is called
- in setupLayoutManager, current code doesn't have a case for if mPager
  is null. Probably we wouldn't be there anyway, but in any case the
  `ctx` parameter should be equivalent, i.e., it should refer to the
  ActivityContext, so we use that instead of going through mPager

Phillip Davis created

815d67b do precedence right

Phillip Davis created

c2c37f2 fix(menu): return true from menu handlers

Click to expand commit body
ConversationFragment.onOptionsItemSelected was using break statements
instead of return true for handled menu items. This caused the method to
return false (from super.onOptionsItemSelected), indicating the event was
not handled.

On tablets with split-pane view, this caused menu events to bubble from the
temporary fragment in ConversationsOverviewFragment to the visible
ConversationFragment, resulting in duplicate actions. For example, selecting
"Contact details" from the context menu would open contact details twice:
first for the selected conversation (correct), then for the currently active
conversation (wrong), with the wrong one appearing on top.

Fixed by changing all break statements to return true, matching the pattern
already used in onContextItemSelected. This also adds a missing return after
action_block_avatar which was previously falling through to the next case.

Fixes: https://todo.sr.ht/~singpolyma/soprani.ca/437

Phillip Davis created

da4c216 filter accts in ConversationsOverviewFragment

Phillip Davis created

b11972b escape jids in more places

Phillip Davis created

d675927 dont timeout w/o ack

Phillip Davis created

575cfdf declare ImportBackupService in android manifest

Click to expand commit body
was causing all backup restores to fail,
service was never even created

Phillip Davis created

b947940 guard against null url in oob stanza

Click to expand commit body
previously, would crash whole app

`if (url != null) {` makes it seem like we were
fine with the subsequent code not running anyway

Phillip Davis created

4fa90ed Update for 16KB alignment

Stephen Paul Weber created

95bcad4 Merge branch '442-connect-via-tor-not-working' of https://git.secluded.site/cheogram-android

Click to expand commit body
* '442-connect-via-tor-not-working' of https://git.secluded.site/cheogram-android:
  fix(settings): enable Tor conns for other flavs

Stephen Paul Weber created

011e884 fix(settings): enable Tor conns for other flavs

Click to expand commit body
Remove QuickConversationsService.isConversations() checks from
AppSettings.java (isUseTor, isExtendedConnectionOptions) to allow Tor
connections in non-Conversations flavours.

I tested by installing with a modified package ID, adding a new account,
enabling the Tor toggle, and seeing that 0 out of 1 accounts were
connected because Tor is unavailable. After installing/enabling Orbot
and waiting a bit, I had to toggle the account to get it to try again.

Fixes: https://todo.sr.ht/~singpolyma/soprani.ca/442

Amolith created

8863066 don't care abt old pw if didUnlock

Phillip Davis created