Fix SSE endpoint for UUID account parameter & preserve '+' in phone numbers

Three interrelated fixes for the HTTP SSE endpoint:

1. **SignalAccountFiles** — Replace ACI.parseOrThrow() with UUID-string
   lookup from accountsStore.getAllAccounts(). The old approach failed when
   a raw UUID string (from URL query param) was passed. Added
   getAccountNumberByAci() helper to reduce duplication.

2. **MultiAccountManagerImpl** — Catch IllegalArgumentException in
   getManager() for both phone number and ACI lookup paths. Also check if
   the UUID corresponds to an already-loaded manager before trying to
   initByAci(), preventing OverlappingFileLockException when SSE requests
   arrive with a UUID for an account that was loaded at startup.

3. **Util.getQueryMap()** — Preserve '+' characters in query parameter
   values by escaping them before URLDecoder.decode(). Without this,
   URLDecoder converts '+' to space, breaking phone numbers like
   '+4915422389' which become ' 4915422389'.
This commit is contained in:
tilllt 2026-07-03 22:08:30 +00:00
parent 973e12fde9
commit 45377e5e06
3 changed files with 37 additions and 12 deletions

View File

@ -96,21 +96,28 @@ public class SignalAccountFiles {
return this.initManager(number, accountPath);
}
public Manager initManagerByAci(String aciStr) throws IOException, NotRegisteredException, AccountCheckException {
final var aci = ACI.parseOrThrow(aciStr);
final var accountPath = accountsStore.getPathByAci(aci);
if (accountPath == null) {
throw new NotRegisteredException();
}
public String getAccountNumberByAci(final String aciStr) throws IOException {
final var accounts = accountsStore.getAllAccounts();
final var account = accounts.stream()
.filter(a -> aciStr.equals(a.uuid()))
.findFirst()
.orElseThrow(NotRegisteredException::new);
if (account.number() == null) {
.orElse(null);
if (account == null || account.number() == null) {
return null;
}
return account.number();
}
public Manager initManagerByAci(String aciStr) throws IOException, NotRegisteredException, AccountCheckException {
final var phoneNumber = getAccountNumberByAci(aciStr);
if (phoneNumber == null) {
throw new NotRegisteredException();
}
return this.initManager(account.number(), accountPath);
final var accountPath = accountsStore.getPathByNumber(phoneNumber);
if (accountPath == null) {
throw new NotRegisteredException();
}
return this.initManager(phoneNumber, accountPath);
}
private Manager initManager(

View File

@ -114,11 +114,29 @@ public class MultiAccountManagerImpl implements MultiAccountManager {
} catch (NotRegisteredException e) {
// Not a valid phone number or not registered yet, try ACI
logger.debug("Manager not found by number, trying ACI: {}", identifier);
} catch (IOException | AccountCheckException e) {
} catch (IOException | IllegalArgumentException | AccountCheckException e) {
logger.warn("Failed to load new manager by number: {}", identifier, e);
return null;
}
// Before trying to initByAci (which would try to re-open an already-locked file),
// check if this UUID corresponds to an already-loaded manager
try {
final var phoneNumber = signalAccountFiles.getAccountNumberByAci(identifier);
if (phoneNumber != null) {
final var existingManager = managers.stream()
.filter(m -> m.getSelfNumber().equals(phoneNumber))
.findFirst()
.orElse(null);
if (existingManager != null) {
logger.debug("Found already loaded manager for ACI: {}", identifier);
return existingManager;
}
}
} catch (IOException e) {
logger.warn("Failed to lookup ACI in accounts: {}", identifier, e);
}
// Try to load by ACI (useful for SSE endpoint with ?account=<UUID>)
try {
final var newManager = signalAccountFiles.initManagerByAci(identifier);
@ -126,7 +144,7 @@ public class MultiAccountManagerImpl implements MultiAccountManager {
return newManager;
} catch (NotRegisteredException e) {
logger.debug("Manager not found by ACI: {}", identifier);
} catch (IOException | AccountCheckException e) {
} catch (IOException | IllegalArgumentException | AccountCheckException e) {
logger.warn("Failed to load new manager by ACI: {}", identifier, e);
}

View File

@ -79,7 +79,7 @@ public class Util {
for (var param : params) {
final var paramParts = param.split("=", 2);
var name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8);
var value = paramParts.length == 1 ? null : URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8);
var value = paramParts.length == 1 ? null : URLDecoder.decode(paramParts[1].replace("+", "%2B"), StandardCharsets.UTF_8);
map.put(name, value);
}
return map;