Merge branch 'AsamK:master' into master

This commit is contained in:
Adimarantis 2021-09-28 10:25:45 +02:00 committed by GitHub
commit e3e7c87f2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 361 additions and 7 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ local.properties
.settings/
out/
.DS_Store
/bin/

View file

@ -1,2 +1,3 @@
liberapay: asamk
ko_fi: asamk
bitcoin: bc1qykae53fry8a8ycgdzgv0rlxfc959hmmllvz698

View file

@ -81,15 +81,15 @@ dependencies. If you have a recent gradle version installed, you can replace `./
./gradlew build
3. Create shell wrapper in *build/install/signal-cli/bin*:
3a. Create shell wrapper in *build/install/signal-cli/bin*:
./gradlew installDist
4. Create tar file in *build/distributions*:
3b. Create tar file in *build/distributions*:
./gradlew distTar
5. Compile and run signal-cli:
3c. Compile and run signal-cli:
./gradlew run --args="--help"

View file

@ -58,7 +58,7 @@ public class RegistrationManager implements Closeable {
private final SignalServiceAccountManager accountManager;
private final PinHelper pinHelper;
public RegistrationManager(
private RegistrationManager(
SignalAccount account,
PathConfig pathConfig,
ServiceEnvironmentConfig serviceEnvironmentConfig,

View file

@ -33,6 +33,7 @@ Where <type> is according to DBus specification:
* <ay> : Byte Array
* <aay> : Array of Byte Arrays
* <as> : String Array
* <ax> : Array of signed 64 bit integer
* <b> : Boolean (0|1)
* <x> : Signed 64 bit integer
* <> : no return value
@ -43,6 +44,64 @@ Phone numbers always have the format +<countrycode><regional number>
== Methods
=== Control methods
These methods are available if the daemon is started anonymously (without an explicit `-u USERNAME`).
Requests are sent to `/org/asamk/Signal`; requests related to individual accounts are sent to
`/org/asamk/Signal/_441234567890` where the + dialing code is replaced by an underscore (_).
Only `version()` is activated in single-user mode; the rest are disabled.
link() -> deviceLinkUri<s>::
link(newDeviceName<s>) -> deviceLinkUri<s>::
* newDeviceName : Name to give new device (defaults to "cli" if no name is given)
* deviceLinkUri : URI of newly linked device
Returns a URI of the form "tsdevice:/?uuid=...". This can be piped to a QR encoder to create a display that
can be captured by a Signal smartphone client. For example:
`dbus-send --session --dest=org.asamk.Signal --type=method_call --print-reply /org/asamk/Signal org.asamk.Signal.link string:"My secondary client"|tr '\n' '\0'|sed 's/.*string //g'|sed 's/\"//g'|qrencode -s10 -tANSI256`
Exception: Failure
listAccounts() -> accountList<as>::
* accountList : Array of all attached accounts in DBus object path form
Exceptions: None
register(number<s>, voiceVerification<b>) -> <>::
* number : Phone number
* voiceVerification : true = use voice verification; false = use SMS verification
Exceptions: Failure, InvalidNumber, RequiresCaptcha
registerWithCaptcha(number<s>, voiceVerification<b>, captcha<s>) -> <>::
* number : Phone number
* voiceVerification : true = use voice verification; false = use SMS verification
* captcha : Captcha string
Exceptions: Failure, InvalidNumber, RequiresCaptcha
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.
Exception: Failure, InvalidNumber
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
Exception: Failure, InvalidNumber
version() -> version<s>::
* version : Version string of signal-cli
Exceptions: None
=== Other methods
updateGroup(groupId<ay>, newName<s>, members<as>, avatar<s>) -> groupId<ay>::
* groupId : Byte array representing the internal group identifier
* newName : New name of group (empty if unchanged)
@ -60,6 +119,13 @@ updateProfile(newName<s>, about <s>, aboutEmoji <s>, avatar<s>, remove<b>) -> <>
Exceptions: Failure
setExpirationTimer(number<s>, expiration<i>) -> <>::
* number : Phone number of recipient
* expiration : int32 for the number of seconds before messages to this recipient disappear. Set to 0 to disable expiration.
Exceptions: Failure
setContactBlocked(number<s>, block<b>) -> <>::
* number : Phone number affected by method
* block : 0=remove block , 1=blocked
@ -106,6 +172,18 @@ sendGroupMessage(message<s>, attachments<as>, groupId<ay>) -> timestamp<x>::
Exceptions: GroupNotFound, Failure, AttachmentInvalid
sendContacts() -> <>::
Sends a synchronization message with the local contacts list to all linked devices. This command should only be used if this is the primary device.
Exceptions: Failure
sendSyncRequest() -> <>::
Sends a synchronization request to the primary device (for group, contacts, ...). Only works if sent from a secondary device.
Exception: Failure
sendNoteToSelfMessage(message<s>, attachments<as>) -> timestamp<x>::
* message : Text to send (can be UTF8)
* attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under)
@ -125,6 +203,19 @@ Depending on the type of the recipient field this sends a message to one or mult
Exceptions: AttachmentInvalid, Failure, InvalidNumber, UntrustedIdentity
sendTyping(recipient<s>, stop<b>) -> <>::
* recipient : Phone number of a single recipient
* targetSentTimestamp : True, if typing state should be stopped
Exceptions: Failure, GroupNotFound, UntrustedIdentity
sendReadReceipt(recipient<s>, targetSentTimestamp<ax>) -> <>::
* recipient : Phone number of a single recipient
* targetSentTimestamp : Array of Longs to identify the corresponding signal messages
Exceptions: Failure, UntrustedIdentity
sendGroupMessageReaction(emoji<s>, remove<b>, targetAuthor<s>, targetSentTimestamp<x>, groupId<ay>) -> timestamp<x>::
* emoji : Unicode grapheme cluster of the emoji
* remove : Boolean, whether a previously sent reaction (emoji) should be removed
@ -215,11 +306,59 @@ isGroupBlocked(groupId<ay>) -> state<b>::
Exceptions: None, for unknown groups 0 (false) is returned
removePin() -> <>::
Removes registration PIN protection.
Exception: Failure
setPin(pin<s>) -> <>::
* pin : PIN you set after registration (resets after 7 days of inactivity)
Sets a registration lock PIN, to prevent others from registering your number.
Exception: Failure
version() -> version<s>::
* version : Version string of signal-cli
isRegistred -> result<b>::
* result : Currently always returns 1=true
isRegistered() -> result<b>::
isRegistered(number<s>) -> result<b>::
isRegistered(numbers<as>) -> results<ab>::
* number : Phone number
* numbers : String array of phone numbers
* 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. If no number is given, returns whether you are registered (presumably true).
addDevice(deviceUri<s>) -> <>::
* deviceUri : URI in the form of tsdevice:/?uuid=... Normally received from Signal desktop or smartphone app
Exception: InvalidUri
listDevices() -> devices<as>::
* devices : String array of linked devices
Exception: Failure
removeDevice(deviceId<i>) -> <>::
* deviceId : Device ID to remove, obtained from listDevices() command
Exception: Failure
updateDeviceName(deviceName<s>) -> <>::
* deviceName : New name
Set a new name for this device (main or linked).
Exception: Failure
uploadStickerPack(stickerPackPath<s>) -> url<s>::
* stickerPackPath : Path to the manifest.json file or a zip file in the same directory
* url : URL of sticker pack after successful upload
Exception: Failure
== Signals

View file

@ -21,6 +21,14 @@ public interface Signal extends DBusInterface {
String message, List<String> attachments, List<String> recipients
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity;
void sendTyping(
String recipient, boolean stop
) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity;
void sendReadReceipt(
String recipient, List<Long> targetSentTimestamp
) throws Error.Failure, Error.UntrustedIdentity;
long sendRemoteDeleteMessage(
long targetSentTimestamp, String recipient
) throws Error.Failure, Error.InvalidNumber;
@ -41,6 +49,10 @@ public interface Signal extends DBusInterface {
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List<String> recipients
) throws Error.InvalidNumber, Error.Failure;
void sendContacts() throws Error.Failure;
void sendSyncRequest() throws Error.Failure;
long sendNoteToSelfMessage(
String message, List<String> attachments
) throws Error.AttachmentInvalid, Error.Failure;
@ -59,6 +71,8 @@ public interface Signal extends DBusInterface {
void setContactName(String number, String name) throws Error.InvalidNumber;
void setExpirationTimer(final String number, final int expiration) throws Error.Failure;
void setContactBlocked(String number, boolean blocked) throws Error.InvalidNumber;
void setGroupBlocked(byte[] groupId, boolean blocked) throws Error.GroupNotFound, Error.InvalidGroupId;
@ -73,12 +87,28 @@ public interface Signal extends DBusInterface {
byte[] groupId, String name, List<String> members, String avatar
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound, Error.InvalidGroupId;
boolean isRegistered();
boolean isRegistered() throws Error.Failure, Error.InvalidNumber;
boolean isRegistered(String number) throws Error.Failure, Error.InvalidNumber;
List<Boolean> isRegistered(List<String> numbers) throws Error.Failure, Error.InvalidNumber;
void addDevice(String uri) throws Error.InvalidUri;
void removeDevice(int deviceId) throws Error.Failure;
List<String> listDevices() throws Error.Failure;
void updateDeviceName(String deviceName) throws Error.Failure;
void updateProfile(
String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar
) throws Error.Failure;
void removePin();
void setPin(String registrationLockPin);
String version();
List<String> listNumbers();
@ -97,6 +127,8 @@ public interface Signal extends DBusInterface {
byte[] joinGroup(final String groupLink) throws Error.Failure;
String uploadStickerPack(String stickerPackPath) throws Error.Failure;
class MessageReceived extends DBusSignal {
private final long timestamp;
@ -223,6 +255,13 @@ public interface Signal extends DBusInterface {
}
}
class InvalidUri extends DBusExecutionException {
public InvalidUri(final String message) {
super(message);
}
}
class Failure extends DBusExecutionException {
public Failure(final String message) {

View file

@ -5,8 +5,12 @@ import org.asamk.signal.BaseConfig;
import org.asamk.signal.manager.AttachmentInvalidException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.NotMasterDeviceException;
import org.asamk.signal.manager.StickerPackInvalidException;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.api.Device;
import org.asamk.signal.manager.api.Message;
import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
import org.asamk.signal.manager.groups.GroupNotFoundException;
@ -17,15 +21,19 @@ import org.asamk.signal.manager.storage.identities.IdentityInfo;
import org.asamk.signal.util.ErrorUtils;
import org.asamk.signal.util.Util;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@ -33,6 +41,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -58,6 +67,51 @@ public class DbusSignalImpl implements Signal {
return objectPath;
}
@Override
public void addDevice(String uri) {
try {
m.addDeviceLink(new URI(uri));
} catch (IOException | InvalidKeyException e) {
throw new Error.Failure(e.getClass().getSimpleName() + " Add device link failed. " + e.getMessage());
} catch (URISyntaxException e) {
throw new Error.InvalidUri(e.getClass().getSimpleName()
+ " Device link uri has invalid format: "
+ e.getMessage());
}
}
@Override
public void removeDevice(int deviceId) {
try {
m.removeLinkedDevices(deviceId);
} catch (IOException e) {
throw new Error.Failure(e.getClass().getSimpleName() + ": Error while removing device: " + e.getMessage());
}
}
@Override
public List<String> listDevices() {
List<Device> devices;
List<String> results = new ArrayList<String>();
try {
devices = m.getLinkedDevices();
} catch (IOException | Error.Failure e) {
throw new Error.Failure("Failed to get linked devices: " + e.getMessage());
}
return devices.stream().map(d -> d.getName() == null ? "" : d.getName()).collect(Collectors.toList());
}
@Override
public void updateDeviceName(String deviceName) {
try {
m.updateAccountAttributes(deviceName);
} catch (IOException | Signal.Error.Failure e) {
throw new Error.Failure("UpdateAccount error: " + e.getMessage());
}
}
@Override
public long sendMessage(final String message, final List<String> attachments, final String recipient) {
var recipients = new ArrayList<String>(1);
@ -165,6 +219,57 @@ public class DbusSignalImpl implements Signal {
}
}
@Override
public void sendTyping(
final String recipient, final boolean stop
) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity {
try {
var recipients = new ArrayList<String>(1);
recipients.add(recipient);
m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
.map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet()));
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new Error.GroupNotFound(e.getMessage());
} catch (UntrustedIdentityException e) {
throw new Error.UntrustedIdentity(e.getMessage());
}
}
@Override
public void sendReadReceipt(
final String recipient, final List<Long> timestamps
) throws Error.Failure, Error.UntrustedIdentity {
try {
m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getUsername()), timestamps);
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
} catch (UntrustedIdentityException e) {
throw new Error.UntrustedIdentity(e.getMessage());
}
}
@Override
public void sendContacts() {
try {
m.sendContacts();
} catch (IOException e) {
throw new Error.Failure("SendContacts error: " + e.getMessage());
}
}
@Override
public void sendSyncRequest() {
try {
m.requestAllSyncData();
} catch (IOException e) {
throw new Error.Failure("Request sync data error: " + e.getMessage());
}
}
@Override
public long sendNoteToSelfMessage(
final String message, final List<String> attachments
@ -250,6 +355,15 @@ public class DbusSignalImpl implements Signal {
}
}
@Override
public void setExpirationTimer(final String number, final int expiration) {
try {
m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getUsername()), expiration);
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
}
}
@Override
public void setContactBlocked(final String number, final boolean blocked) {
try {
@ -357,6 +471,32 @@ public class DbusSignalImpl implements Signal {
return true;
}
@Override
public boolean isRegistered(String number) {
var result = isRegistered(List.of(number));
return result.get(0);
}
@Override
public List<Boolean> isRegistered(List<String> numbers) {
var results = new ArrayList<Boolean>();
if (numbers.isEmpty()) {
return results;
}
Map<String, Pair<String, UUID>> registered;
try {
registered = m.areUsersRegistered(new HashSet<>(numbers));
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
}
return numbers.stream().map(number -> {
var uuid = registered.get(number).second();
return uuid != null;
}).collect(Collectors.toList());
}
@Override
public void updateProfile(
final String name,
@ -378,6 +518,28 @@ public class DbusSignalImpl implements Signal {
}
}
@Override
public void removePin() {
try {
m.setRegistrationLockPin(Optional.absent());
} catch (UnauthenticatedResponseException e) {
throw new Error.Failure("Remove pin failed with unauthenticated response: " + e.getMessage());
} catch (IOException e) {
throw new Error.Failure("Remove pin error: " + e.getMessage());
}
}
@Override
public void setPin(String registrationLockPin) {
try {
m.setRegistrationLockPin(Optional.of(registrationLockPin));
} catch (UnauthenticatedResponseException e) {
throw new Error.Failure("Set pin error failed with unauthenticated response: " + e.getMessage());
} catch (IOException e) {
throw new Error.Failure("Set pin error: " + e.getMessage());
}
}
// Provide option to query a version string in order to react on potential
// future interface changes
@Override
@ -483,6 +645,18 @@ public class DbusSignalImpl implements Signal {
return m.getUsername();
}
@Override
public String uploadStickerPack(String stickerPackPath) {
File path = new File(stickerPackPath);
try {
return m.uploadStickerPack(path).toString();
} 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());
}
}
private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException {
var error = ErrorUtils.getErrorMessageFromSendMessageResult(result);