[libtacky / JID] Accounts and chats are keyed by exact spelling: -acc/-chat case is not normalized (RFC 7622) #19
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: Critical
Found in: libtacky v1.2.0-beta2 (
2f42a2f2), anotherim-swiftfeat/ui-redesign(TestFlight 94/95)Date: November 5, 2026
Summary
libtacky keys accounts and chats by the exact string the host passes in, without case normalization. Messages from the server, however, are filed in lowercase. When the host spells an address differently from how it was stored, lookups fail or land in a separate, empty conversation.
RFC 7622 says this is the same address. The localpart is enforced with the PRECIS
UsernameCaseMappedprofile (RFC 8265), which maps it to lowercase. The domainpart is case-insensitive (§3.2). Comparison must happen after enforcement (§3.3). Only the resourcepart (OpaqueString) is case-sensitive.Ivan@another.imandivan@another.imare one JID. RFC 6122 / nodeprep gave the same result.Where
-acclookup is case-sensitiveclient(tacky/xmpp/client/client.tcl:924-929) →account exists→account_find:SELECT id FROM accounts WHERE bare_jid = $jid(tacky/core/storage.tcl:1639-1642).bare_jidhas noCOLLATE NOCASE(storage.tcl:110).Ivan@another.imis not found by-acc ivan@another.im. Every call fails withAccount does not exist: ivan@another.im.account addstores the address as typed,add_from_urinormalizes itaccount addstores-accverbatim (tacky/xmpp/modules/account.tcl:321-363, checked only byjid valid-account,jid.tcl:401-413).add_from_uriusesjid norm [jid bare …](account.tcl:1703).client.tcl:690, 961). On a case-insensitive filesystem two spellings share one file.libtacky feeds the server's spelling back as
-accoptions(-jid)becomes the bound JID (client.tcl:666, 690), which servers normally return in lowercase.-acctoaccount store_fast_token/clear_fast_token/fast_token_used(client.tcl:765-784), and toOAuthSignedIn,OAuthTokenRejectedandCertificateRejected(client.tcl:695, 792, 798).Requireand fail. The rotated FAST token is never saved.client account_id(client.tcl:510-515) may create a second, lowercase account row in the per-account store.Events change spelling at bind
emitsends-acc [jid bare $options(-jid)](tacky/xmpp/modules/client.tcl:660-663).-accsees two different accounts.-chatis verbatim on the way in, lowercase on the way backmessage sendfiles the row underchat_jid $opts(-chat)as given (message.tcl:3971-3973) and emits<New> -jidwith that spelling.message historypasses-chatthroughGetLocalunchanged (message.tcl:7436-7480).messagestorecompareschat_jid=$jidexactly (messagestore.tcl:641-656).PeerChat:jid norm [jid bare $peer](message.tcl:1264-1272, 1506-1508, 1593-1600).message send -chat Bob@xstarts a second conversation next tobob@x. Replies never appear in it, andhistory -chat Bob@xdoes not see them.clear_history(message.tcl:6546), roster-jid(roster.tcl:293, 318, 343), chatlist (chatlist.tcl:707).Impact on the client
ChatID). Because libtacky looks accounts up by exact spelling, that workaround breaks accounts stored with capitals: from build 94, every call from the chat screen fails withAccount does not exist. Before it, the app dropped those accounts' live events instead, because events carry the lowercase bound JID.Expected
-acc,-chat,-jid) withjid normon entry to the API and inaccount add, leaving the resourcepart as given.accounts.bare_jidandmessages.chat_jid(and the other per-chat tables) to lowercase, merging duplicate chats. Rename per-account store files.-accin one stable, normalized spelling.Steps to reproduce
account add -acc Ivan@another.im …and sign in.message history -acc ivan@another.im -chat bob@another.imreturnsAccount does not exist: ivan@another.im.-accof the events before and after bind:Ivan@another.im, thenivan@another.im.message send -acc Ivan@another.im -chat Bob@another.im -body hi, then letbob@another.imreply. The reply is filed underbob@another.im, andmessage history -chat Bob@another.imshows only the sent message.Fixed in libtacky 1.2.0-rc4. Every bare-JID argument of every method is normalised at the API's door (the table of
jid-typed arguments is generated from the schema, so nothing is kept by hand): localpart and domain are lowercased and width-mapped, a final dot dropped, a resource and a room's?joinkept as given; whatjid validrefuses (the characters RFC 7622 forbids, parts over 1023 bytes, control characters) is an error at the call and nothing is stored or sent.account addstores the normalised address, the client's own JID is normalised before and after the bind, so-accin events is one spelling throughout and the FAST-token and certificate callbacks find the account;account listis the source of truth. A store written by an older build is migrated once at open: accounts, every per-chat table, the roster, PEP, OTR and the module state are re-keyed, duplicate chats merged without losing a message (the earlier copy of a message kept, the newer read point or draft), store files renamed through a temporary name so a case-insensitive filesystem is safe. No schema-version bump, so rc3 can still open the store. Hosts: from the first event after the upgrade-accand chat JIDs are the normalised ones.