mirror of
https://github.com/AsamK/signal-cli
synced 2025-09-02 20:40:38 +00:00
implement uploadStickerPack for Dbus; fix isRegistered error
Note: isRegistered (and related methods) for Dbus can throw an InvalidNumberException when the phone number is incorrectly formatted. Previously this led to uncaught exceptions. They are now handled. The problem is in SignalServiceAccountManager.java in the package org.whispersystems.signalservice.api, which ignores the first character of a proposed phone number and checks that the rest is a legitimate int64. updated documentation
This commit is contained in:
parent
663f6f6e73
commit
c39b5450ff
5 changed files with 111 additions and 26 deletions
|
@ -322,10 +322,16 @@ public class Manager implements Closeable {
|
|||
* @param numbers The set of phone number in question
|
||||
* @return A map of numbers to booleans. True if registered, false otherwise. Should never be null
|
||||
* @throws IOException if its unable to get the contacts to check if they're registered
|
||||
* @throws InvalidNumberException if phone number is incorrectly formatted
|
||||
*/
|
||||
public Map<String, Boolean> areUsersRegistered(Set<String> numbers) throws IOException {
|
||||
public Map<String, Boolean> areUsersRegistered(Set<String> numbers) throws IOException, InvalidNumberException {
|
||||
// Note "contactDetails" has no optionals. It only gives us info on users who are registered
|
||||
var contactDetails = getRegisteredUsers(numbers);
|
||||
Map<String, UUID> contactDetails = null;
|
||||
try {
|
||||
contactDetails = getRegisteredUsers(numbers);
|
||||
} catch (InvalidNumberException e) {
|
||||
throw new InvalidNumberException(e.getMessage());
|
||||
}
|
||||
|
||||
var registeredUsers = contactDetails.keySet();
|
||||
|
||||
|
@ -924,6 +930,7 @@ public class Manager implements Closeable {
|
|||
.map(this::resolveSignalServiceAddress)
|
||||
.collect(Collectors.toList());
|
||||
final var newE164Members = new HashSet<String>();
|
||||
Map<String, UUID> registeredUsers = null;
|
||||
for (var member : newMemberAddresses) {
|
||||
if (!member.getNumber().isPresent()) {
|
||||
continue;
|
||||
|
@ -931,7 +938,11 @@ public class Manager implements Closeable {
|
|||
newE164Members.add(member.getNumber().get());
|
||||
}
|
||||
|
||||
final var registeredUsers = getRegisteredUsers(newE164Members);
|
||||
try {
|
||||
registeredUsers = getRegisteredUsers(newE164Members);
|
||||
} catch (InvalidNumberException e) {
|
||||
throw new IOException("Invalid number detected: " + e.getMessage());
|
||||
}
|
||||
if (registeredUsers.size() != newE164Members.size()) {
|
||||
// Some of the new members are not registered on Signal
|
||||
newE164Members.removeAll(registeredUsers.keySet());
|
||||
|
@ -1513,25 +1524,35 @@ public class Manager implements Closeable {
|
|||
return signalServiceAddresses.stream().map(this::resolveRecipient).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException {
|
||||
private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException, InvalidNumberException {
|
||||
final var address = resolveSignalServiceAddress(recipientId);
|
||||
if (!address.getNumber().isPresent()) {
|
||||
return recipientId;
|
||||
}
|
||||
final var number = address.getNumber().get();
|
||||
final var uuidMap = getRegisteredUsers(Set.of(number));
|
||||
Map<String, UUID> uuidMap;
|
||||
try {
|
||||
uuidMap = getRegisteredUsers(Set.of(number));
|
||||
} catch (InvalidNumberException e) {
|
||||
logger.warn("Improperly formatted number: {}", e.getMessage());
|
||||
uuidMap = Map.of();
|
||||
}
|
||||
return resolveRecipientTrusted(new SignalServiceAddress(uuidMap.getOrDefault(number, null), number));
|
||||
}
|
||||
|
||||
private Map<String, UUID> getRegisteredUsers(final Set<String> numbers) throws IOException {
|
||||
private Map<String, UUID> getRegisteredUsers(final Set<String> numbers) throws IOException, InvalidNumberException {
|
||||
Map<String, UUID> registeredUsers = null;
|
||||
try {
|
||||
return dependencies.getAccountManager()
|
||||
registeredUsers = dependencies.getAccountManager()
|
||||
.getRegisteredUsers(ServiceConfig.getIasKeyStore(),
|
||||
numbers,
|
||||
serviceEnvironmentConfig.getCdsMrenclave());
|
||||
} catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidNumberException(e.getMessage());
|
||||
}
|
||||
return registeredUsers;
|
||||
}
|
||||
|
||||
public void sendTypingMessage(
|
||||
|
@ -1681,7 +1702,13 @@ public class Manager implements Closeable {
|
|||
ContentHint.DEFAULT,
|
||||
message);
|
||||
} catch (UnregisteredUserException e) {
|
||||
final var newRecipientId = refreshRegisteredUser(recipientId);
|
||||
RecipientId newRecipientId = null;
|
||||
try {
|
||||
newRecipientId = refreshRegisteredUser(recipientId);
|
||||
} catch (InvalidNumberException ine) {
|
||||
logger.warn("Invalid number");
|
||||
return SendMessageResult.unregisteredFailure(resolveSignalServiceAddress(recipientId));
|
||||
}
|
||||
return messageSender.sendDataMessage(resolveSignalServiceAddress(newRecipientId),
|
||||
unidentifiedAccessHelper.getAccessFor(newRecipientId),
|
||||
ContentHint.DEFAULT,
|
||||
|
@ -1700,7 +1727,13 @@ public class Manager implements Closeable {
|
|||
try {
|
||||
return messageSender.sendNullMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId));
|
||||
} catch (UnregisteredUserException e) {
|
||||
final var newRecipientId = refreshRegisteredUser(recipientId);
|
||||
RecipientId newRecipientId = null;
|
||||
try {
|
||||
newRecipientId = refreshRegisteredUser(recipientId);
|
||||
} catch (InvalidNumberException ine) {
|
||||
logger.warn("Invalid number");
|
||||
return SendMessageResult.unregisteredFailure(resolveSignalServiceAddress(recipientId));
|
||||
}
|
||||
final var newAddress = resolveSignalServiceAddress(newRecipientId);
|
||||
return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId));
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ isMember(base64GroupId<s>) -> active<b>::
|
|||
* base64GroupId : String representing the internal group identifier in Base64 format
|
||||
* active : Boolean representing whether you are a member of the group
|
||||
|
||||
Note that this method does not raise an Exception for a non-existing/unknown group but will simply return false
|
||||
Exceptions: None; returns false for a non-existing/unknown group
|
||||
|
||||
sendEndSessionMessage(recipients<as>) -> <>::
|
||||
* recipients : String array of phone numbers
|
||||
|
@ -195,13 +195,13 @@ Depending on the type of the recipient(s) field this deletes a message with one
|
|||
|
||||
Exceptions: Failure, InvalidNumber
|
||||
|
||||
sendContacts() -> <>
|
||||
sendContacts() -> <>::
|
||||
|
||||
Sends a synchronization message with the local contacts list to all linked devices.
|
||||
|
||||
Exceptions: Failure, UntrustedIdentity
|
||||
|
||||
sendSyncRequest() -> <>
|
||||
sendSyncRequest() -> <>::
|
||||
|
||||
Sends a synchronization request to the primary device (for group, contacts, ...). Only works if a secondary device is running the daemon.
|
||||
|
||||
|
@ -213,7 +213,7 @@ trust(number<s>, safetyNumber<s>) -> <>::
|
|||
|
||||
Exceptions: Failure, InvalidNumber;
|
||||
|
||||
sendTyping(typingAction<b>, base64GroupId<s>, recipients<as>) -> <>
|
||||
sendTyping(typingAction<b>, base64GroupId<s>, recipients<as>) -> <>::
|
||||
* typingAction : true = start typing, false = stop typing
|
||||
* base64GroupId : String representing the internal group identifier in Base64 format
|
||||
* recipients : List of phone numbers
|
||||
|
@ -226,14 +226,20 @@ getContactName(number<s>) -> name<s>::
|
|||
* number : Phone number
|
||||
* name : Contact's name in local storage (from the primary device for a linked account, or the one set with setContactName); if not set, contact's profile name is used
|
||||
|
||||
Exception: InvalidNumber
|
||||
|
||||
setContactName(number<s>,name<>) -> <>::
|
||||
* number : Phone number
|
||||
* name : Name to be set in contacts (in local storage with signal-cli)
|
||||
|
||||
Exception: InvalidNumber
|
||||
|
||||
setExpirationTimer(number<s>,expiration<i>) -> <>::
|
||||
* number : Phone number
|
||||
* expiration : Expiration time for messages sent to this number (in seconds). Set to 0 to disable.
|
||||
|
||||
Exception: InvalidNumber
|
||||
|
||||
getGroupIds() -> groupList<aay>::
|
||||
getGroupIds(dummy<s>) -> groupList<as>::
|
||||
dummy : any string (ignored by method; forces output to be identical with getBase64GroupIds)
|
||||
|
@ -242,18 +248,22 @@ base64GroupList : Array of strings representing the internal group identifiers i
|
|||
|
||||
All groups known are returned, regardless of their active or blocked status. To query that use isMember() and isGroupBlocked()
|
||||
|
||||
Exceptions: None
|
||||
|
||||
getBase64GroupIds() -> groupList<as>::
|
||||
groupList : Array of strings representing the internal group identifiers in Base64 format
|
||||
|
||||
All groups known are returned, regardless of their active or blocked status. To query that use isMember() and isGroupBlocked()
|
||||
|
||||
Exceptions: None
|
||||
|
||||
getGroupName(groupId<ay>) -> groupName<s>::
|
||||
getGroupName(base64GroupId<s>) -> groupName<s>::
|
||||
groupName : The display name of the group
|
||||
groupId : Byte array representing the internal group identifier
|
||||
base64GroupId : String representing the internal group identifier in Base64 format
|
||||
|
||||
Exceptions: None, if the group name is not found an empty string is returned
|
||||
Exceptions: None; if the group name is not found an empty string is returned
|
||||
|
||||
getGroupMembers(groupId<ay>) -> members<as>::
|
||||
getGroupMembers(base64GroupId<s>) -> members<as>::
|
||||
|
@ -261,24 +271,28 @@ members : String array with the phone numbers of all active members of a g
|
|||
groupId : Byte array representing the internal group identifier
|
||||
base64GroupId : String representing the internal group identifier in Base64 format
|
||||
|
||||
Exceptions: None, if the group name is not found an empty array is returned
|
||||
Exceptions: None; if the group name is not found an empty array is returned
|
||||
|
||||
listNumbers() -> numbers<as>::
|
||||
numbers : String array of all known numbers
|
||||
|
||||
This is a concatenated list of all defined contacts as well of profiles known (e.g. peer group members or sender of received messages)
|
||||
|
||||
Exceptions: None
|
||||
|
||||
getContactNumber(name<s>) -> numbers<as>::
|
||||
* numbers : Array of phone numbers
|
||||
* name : Contact or profile name ("firstname lastname")
|
||||
|
||||
Searches contacts and known profiles for a given name and returns the list of all known numbers. May result in e.g. two entries if a contact and profile name is set.
|
||||
|
||||
Exception: Failure
|
||||
|
||||
isContactBlocked(number<s>) -> state<b>::
|
||||
* number : Phone number
|
||||
* state : true=blocked, false=not blocked
|
||||
|
||||
Exceptions: None, for unknown numbers false is returned
|
||||
Exception: InvalidNumber for an incorrectly formatted phone number. For unknown numbers, false is returned, but no exception is raised.
|
||||
|
||||
isGroupBlocked(groupId<ay>) -> state<b>::
|
||||
isGroupBlocked(base64GroupId<s>) -> state<b>::
|
||||
|
@ -286,11 +300,13 @@ isGroupBlocked(base64GroupId<s>) -> state<b>::
|
|||
* base64GroupId : String representing the internal group identifier in Base64 format
|
||||
* state : true=blocked, false=not blocked
|
||||
|
||||
Exceptions: None, for unknown groups false is returned
|
||||
Exceptions: None; for unknown groups false is returned
|
||||
|
||||
version() -> version<s>::
|
||||
* version : Version string of signal-cli
|
||||
|
||||
Exceptions: None
|
||||
|
||||
isRegistered(number<s>) -> result<b>::
|
||||
isRegistered(numbers<as>) -> results<ab>::
|
||||
* number : Phone number
|
||||
|
@ -298,6 +314,8 @@ isRegistered(numbers<as>) -> results<ab>::
|
|||
* result : true=number is registered, false=number is not registered
|
||||
* results : Boolean array of results
|
||||
|
||||
Exception: InvalidNumber for an incorrectly formatted phone number. For unknown numbers, false is returned, but no exception is raised.
|
||||
|
||||
listIdentity(number<s>) -> results<a(ssss)>::
|
||||
* number : Phone number
|
||||
* results : Array of elements, each consisting of four strings: trust_level, date_added, fingerprint, safety_number
|
||||
|
@ -309,7 +327,7 @@ listIdentity(number<s>) -> results<a(ssss)>::
|
|||
getObjectPath() -> objectPath<s>::
|
||||
* objectPath : The DBus object path associated with this connection
|
||||
|
||||
updateAccount() -> <>
|
||||
updateAccount() -> <>::
|
||||
|
||||
Updates the account attributes on the Signal server.
|
||||
|
||||
|
@ -351,17 +369,22 @@ removePin() -> <>::
|
|||
|
||||
Removes registration PIN protection.
|
||||
|
||||
verify(number<s>, verificationCode<s>) -> <>
|
||||
verify(number<s>, verificationCode<s>) -> <>::
|
||||
* number : Phone number
|
||||
* verificationCode : Code received from Signal after successful registration request
|
||||
|
||||
Command fails if PIN was set after previous registration; use verifyWithPin instead.
|
||||
|
||||
verifyWithPin(number<s>, verificationCode<s>, pin<s>) -> <>
|
||||
verifyWithPin(number<s>, verificationCode<s>, pin<s>) -> <>::
|
||||
* number : Phone number
|
||||
* verificationCode : Code received from Signal after successful registration request
|
||||
* pin : PIN you set with setPin command after verifying previous registration
|
||||
|
||||
uploadStickerPack(stickerPackPath<s>) -> <>::
|
||||
* stickerPackPath : Path to the sticker pack
|
||||
|
||||
Exception: Failure
|
||||
|
||||
== Signals
|
||||
|
||||
SyncMessageReceived (timestamp<x>, sender<s>, destination<s>, groupId<ay>, message<s>, attachments<as>)::
|
||||
|
|
|
@ -133,7 +133,7 @@ public interface Signal extends DBusInterface {
|
|||
|
||||
void setGroupBlocked(String base64GroupId, boolean blocked) throws Error.GroupNotFound;
|
||||
|
||||
List<String> getGroupMembers(String dummy);
|
||||
List<String> getGroupMembers(String base64GroupId);
|
||||
|
||||
long sendGroupMessage(
|
||||
String message, List<String> attachments, String base64GroupId
|
||||
|
@ -147,9 +147,9 @@ public interface Signal extends DBusInterface {
|
|||
String base64GroupId, String name, List<String> members, String avatar
|
||||
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound;
|
||||
|
||||
boolean isRegistered(String number);
|
||||
boolean isRegistered(String number) throws Error.Failure, Error.InvalidNumber;
|
||||
|
||||
List<Boolean> isRegistered(List<String> numbers);
|
||||
List<Boolean> isRegistered(List<String> numbers) throws Error.Failure, Error.InvalidNumber;
|
||||
|
||||
void updateProfile(
|
||||
String givenName, String familyName, String about, String aboutEmoji, String avatarPath, boolean removeAvatar
|
||||
|
@ -209,6 +209,8 @@ public interface Signal extends DBusInterface {
|
|||
|
||||
void joinGroup(final String groupLink) throws Error.Failure;
|
||||
|
||||
void uploadStickerPack(String stickerPackPath) throws Error.Failure;
|
||||
|
||||
class MessageReceived extends DBusSignal {
|
||||
|
||||
private final long timestamp;
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.asamk.signal.commands.exceptions.IOErrorException;
|
|||
import org.asamk.signal.manager.Manager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
@ -34,12 +35,14 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
|
|||
@Override
|
||||
public void handleCommand(final Namespace ns, final Manager m) throws CommandException {
|
||||
// Get a map of registration statuses
|
||||
Map<String, Boolean> registered;
|
||||
Map<String, Boolean> registered = null;
|
||||
try {
|
||||
registered = m.areUsersRegistered(new HashSet<>(ns.getList("number")));
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to check registered users", e);
|
||||
throw new IOErrorException("Unable to check if users are registered");
|
||||
} catch (InvalidNumberException e) {
|
||||
logger.debug("Invalid number format", e);
|
||||
}
|
||||
|
||||
// Output
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.asamk.signal.manager.AttachmentInvalidException;
|
|||
import org.asamk.signal.manager.AvatarStore;
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.manager.NotMasterDeviceException;
|
||||
import org.asamk.signal.manager.StickerPackInvalidException;
|
||||
import org.asamk.signal.manager.api.Device;
|
||||
import org.asamk.signal.manager.api.TypingAction;
|
||||
import org.asamk.signal.manager.groups.GroupId;
|
||||
|
@ -753,6 +754,9 @@ public class DbusSignalImpl implements Signal {
|
|||
|
||||
@Override
|
||||
public boolean isRegistered(String number) {
|
||||
if (number.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Map<String, Boolean> registered;
|
||||
List<String> numbers = new ArrayList<String>();
|
||||
|
@ -761,14 +765,19 @@ public class DbusSignalImpl implements Signal {
|
|||
return registered.get(number);
|
||||
} catch (IOException e) {
|
||||
throw new Error.Failure(e.getMessage());
|
||||
} catch (InvalidNumberException e) {
|
||||
throw new Error.InvalidNumber(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Boolean> isRegistered(List<String> numbers) {
|
||||
List<Boolean> results = new ArrayList<Boolean> ();
|
||||
if (numbers.isEmpty()) {
|
||||
return results;
|
||||
}
|
||||
try {
|
||||
Map<String, Boolean> registered;
|
||||
List<Boolean> results = new ArrayList<Boolean> ();
|
||||
registered = m.areUsersRegistered(new HashSet<String>(numbers));
|
||||
for (String number : numbers) {
|
||||
results.add(registered.get(number));
|
||||
|
@ -776,6 +785,8 @@ public class DbusSignalImpl implements Signal {
|
|||
return results;
|
||||
} catch (IOException e) {
|
||||
throw new Error.Failure(e.getMessage());
|
||||
} catch (InvalidNumberException e) {
|
||||
throw new Error.InvalidNumber(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1014,4 +1025,17 @@ public class DbusSignalImpl implements Signal {
|
|||
return group.isMember(m.getSelfRecipientId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadStickerPack(String stickerPackPath) {
|
||||
File path = new File(stickerPackPath);
|
||||
|
||||
try {
|
||||
var url = m.uploadStickerPack(path);
|
||||
} catch (IOException e) {
|
||||
throw new Error.Failure("Upload error (maybe image size is too large):" + e.getMessage());
|
||||
} catch (StickerPackInvalidException e) {
|
||||
throw new Error.Failure("Invalid sticker pack: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue