X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/b91c162159c7c28d049ceb8889c419791573d3bb..5cccf521032954d7ad8e3f70e3cbef2ce1293e85:/lib/src/main/java/org/asamk/signal/manager/Manager.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index d2eb0f8f..b7a97c46 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -1,107 +1,123 @@ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.AccountCheckException; +import org.asamk.signal.manager.api.AttachmentInvalidException; +import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; +import org.asamk.signal.manager.api.Group; +import org.asamk.signal.manager.api.Identity; +import org.asamk.signal.manager.api.InactiveGroupLinkException; +import org.asamk.signal.manager.api.InvalidDeviceLinkException; +import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; +import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.manager.api.NotMasterDeviceException; +import org.asamk.signal.manager.api.NotRegisteredException; +import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResults; +import org.asamk.signal.manager.api.StickerPack; +import org.asamk.signal.manager.api.StickerPackInvalidException; +import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; +import org.asamk.signal.manager.api.UnregisteredRecipientException; +import org.asamk.signal.manager.api.UpdateGroup; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; -import org.asamk.signal.manager.groups.GroupLinkState; import org.asamk.signal.manager.groups.GroupNotFoundException; -import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.storage.SignalAccount; -import org.asamk.signal.manager.storage.groups.GroupInfo; -import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; -import org.asamk.signal.manager.storage.recipients.RecipientId; -import org.whispersystems.libsignal.IdentityKey; -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.SignalServiceAttachmentRemoteId; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.URI; -import java.util.Arrays; +import java.time.Duration; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; public interface Manager extends Closeable { static Manager init( - String username, + String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent, TrustNewIdentity trustNewIdentity - ) throws IOException, NotRegisteredException { + ) throws IOException, NotRegisteredException, AccountCheckException { var pathConfig = PathConfig.createDefault(settingsPath); - if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { + if (!SignalAccount.userExists(pathConfig.dataPath(), number)) { throw new NotRegisteredException(); } - var account = SignalAccount.load(pathConfig.getDataPath(), username, true, trustNewIdentity); + var account = SignalAccount.load(pathConfig.dataPath(), number, true, trustNewIdentity); if (!account.isRegistered()) { + account.close(); throw new NotRegisteredException(); } + account.initDatabase(); final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); - return new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); - } - - static List getAllLocalUsernames(File settingsPath) { - var pathConfig = PathConfig.createDefault(settingsPath); - final var dataPath = pathConfig.getDataPath(); - final var files = dataPath.listFiles(); + final var manager = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); - if (files == null) { - return List.of(); + try { + manager.checkAccountState(); + } catch (IOException e) { + manager.close(); + throw new AccountCheckException("Error while checking account " + account + ": " + e.getMessage(), e); } - return Arrays.stream(files) - .filter(File::isFile) - .map(File::getName) - .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) - .collect(Collectors.toList()); + return manager; } - String getUsername(); - - RecipientId getSelfRecipientId(); + static void initLogger() { + LibSignalLogger.initLogger(); + } - int getDeviceId(); + static boolean isValidNumber(final String e164Number, final String countryCode) { + return PhoneNumberFormatter.isValidNumber(e164Number, countryCode); + } - void checkAccountState() throws IOException; + String getSelfNumber(); + /** + * This is used for checking a set of phone numbers for registration on Signal + * + * @param numbers The set of phone number in question + * @return A map of numbers to canonicalized number and uuid. If a number is not registered the uuid is null. + * @throws IOException if it's unable to get the contacts to check if they're registered + */ Map> areUsersRegistered(Set numbers) throws IOException; void updateAccountAttributes(String deviceName) throws IOException; + Configuration getConfiguration(); + + void updateConfiguration(Configuration configuration) throws IOException, NotMasterDeviceException; + + /** + * @param givenName if null, the previous givenName will be kept + * @param familyName if null, the previous familyName will be kept + * @param about if null, the previous about text will be kept + * @param aboutEmoji if null, the previous about emoji will be kept + * @param avatar if avatar is null the image from the local avatar store is used (if present), + */ void setProfile( String givenName, String familyName, String about, String aboutEmoji, Optional avatar ) throws IOException; @@ -116,60 +132,47 @@ public interface Manager extends Closeable { void removeLinkedDevices(int deviceId) throws IOException; - void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException; + void addDeviceLink(URI linkUri) throws IOException, InvalidDeviceLinkException; - void setRegistrationLockPin(Optional pin) throws IOException, UnauthenticatedResponseException; + void setRegistrationLockPin(Optional pin) throws IOException, NotMasterDeviceException; - Profile getRecipientProfile(RecipientId recipientId); + Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException; - List getGroups(); + List getGroups(); SendGroupMessageResults quitGroup( GroupId groupId, Set groupAdmins - ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException; + ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException, UnregisteredRecipientException; void deleteGroup(GroupId groupId) throws IOException; Pair createGroup( String name, Set members, File avatarFile - ) throws IOException, AttachmentInvalidException; + ) throws IOException, AttachmentInvalidException, UnregisteredRecipientException; SendGroupMessageResults updateGroup( - GroupId groupId, - String name, - String description, - Set members, - Set removeMembers, - Set admins, - Set removeAdmins, - boolean resetGroupLink, - GroupLinkState groupLinkState, - GroupPermission addMemberPermission, - GroupPermission editDetailsPermission, - File avatarFile, - Integer expirationTimer, - Boolean isAnnouncementGroup - ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException; + final GroupId groupId, final UpdateGroup updateGroup + ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException, UnregisteredRecipientException; Pair joinGroup( GroupInviteLinkUrl inviteLinkUrl - ) throws IOException, GroupLinkNotActiveException; + ) throws IOException, InactiveGroupLinkException; - void sendTypingMessage( + SendMessageResults sendTypingMessage( TypingAction action, Set recipients - ) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; - void sendReadReceipt( + SendMessageResults sendReadReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException, UntrustedIdentityException; + ) throws IOException; - void sendViewedReceipt( + SendMessageResults sendViewedReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException, UntrustedIdentityException; + ) throws IOException; SendMessageResults sendMessage( Message message, Set recipients - ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; + ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException; SendMessageResults sendRemoteDeleteMessage( long targetSentTimestamp, Set recipients @@ -181,79 +184,138 @@ public interface Manager extends Closeable { RecipientIdentifier.Single targetAuthor, long targetSentTimestamp, Set recipients - ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException; SendMessageResults sendEndSessionMessage(Set recipients) throws IOException; + void deleteRecipient(RecipientIdentifier.Single recipient); + + void deleteContact(RecipientIdentifier.Single recipient); + void setContactName( RecipientIdentifier.Single recipient, String name - ) throws NotMasterDeviceException, UnregisteredUserException; + ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException; void setContactBlocked( RecipientIdentifier.Single recipient, boolean blocked - ) throws NotMasterDeviceException, IOException; + ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException; void setGroupBlocked( GroupId groupId, boolean blocked - ) throws GroupNotFoundException, IOException; + ) throws GroupNotFoundException, IOException, NotMasterDeviceException; + /** + * Change the expiration timer for a contact + */ void setExpirationTimer( RecipientIdentifier.Single recipient, int messageExpirationTimer - ) throws IOException; + ) throws IOException, UnregisteredRecipientException; - URI uploadStickerPack(File path) throws IOException, StickerPackInvalidException; + /** + * Upload the sticker pack from path. + * + * @param path Path can be a path to a manifest.json file or to a zip file that contains a manifest.json file + * @return if successful, returns the URL to install the sticker pack in the signal app + */ + StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException; - void requestAllSyncData() throws IOException; + List getStickerPacks(); - void receiveMessages( - long timeout, - TimeUnit unit, - boolean returnOnTimeout, - boolean ignoreAttachments, - ReceiveMessageHandler handler - ) throws IOException; - - boolean hasCaughtUpWithOldMessages(); - - boolean isContactBlocked(RecipientIdentifier.Single recipient); - - File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId); - - void sendContacts() throws IOException; - - List> getContacts(); - - String getContactOrProfileName(RecipientIdentifier.Single recipientIdentifier); - - GroupInfo getGroup(GroupId groupId); + void requestAllSyncData() throws IOException; - List getIdentities(); + /** + * Add a handler to receive new messages. + * Will start receiving messages from server, if not already started. + */ + default void addReceiveHandler(ReceiveMessageHandler handler) { + addReceiveHandler(handler, false); + } - List getIdentities(RecipientIdentifier.Single recipient); + void addReceiveHandler(ReceiveMessageHandler handler, final boolean isWeakListener); - boolean trustIdentityVerified(RecipientIdentifier.Single recipient, byte[] fingerprint); + /** + * Remove a handler to receive new messages. + * Will stop receiving messages from server, if this was the last registered receiver. + */ + void removeReceiveHandler(ReceiveMessageHandler handler); - boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, String safetyNumber); + boolean isReceiving(); - boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, byte[] safetyNumber); + /** + * Receive new messages from server, returns if no new message arrive in a timespan of timeout. + */ + void receiveMessages(Duration timeout, ReceiveMessageHandler handler) throws IOException; - boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient); + /** + * Receive new messages from server, returns only if the thread is interrupted. + */ + void receiveMessages(ReceiveMessageHandler handler) throws IOException; - String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey); + void setIgnoreAttachments(boolean ignoreAttachments); - byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey); + boolean hasCaughtUpWithOldMessages(); - SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address); + boolean isContactBlocked(RecipientIdentifier.Single recipient); - SignalServiceAddress resolveSignalServiceAddress(UUID uuid); + void sendContacts() throws IOException; - SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId); + List> getContacts(); + + String getContactOrProfileName(RecipientIdentifier.Single recipient); + + Group getGroup(GroupId groupId); + + List getIdentities(); + + List getIdentities(RecipientIdentifier.Single recipient); + + /** + * Trust this the identity with this fingerprint + * + * @param recipient account of the identity + * @param fingerprint Fingerprint + */ + boolean trustIdentityVerified( + RecipientIdentifier.Single recipient, byte[] fingerprint + ) throws UnregisteredRecipientException; + + /** + * Trust this the identity with this safety number + * + * @param recipient account of the identity + * @param safetyNumber Safety number + */ + boolean trustIdentityVerifiedSafetyNumber( + RecipientIdentifier.Single recipient, String safetyNumber + ) throws UnregisteredRecipientException; + + /** + * Trust this the identity with this scannable safety number + * + * @param recipient account of the identity + * @param safetyNumber Scannable safety number + */ + boolean trustIdentityVerifiedSafetyNumber( + RecipientIdentifier.Single recipient, byte[] safetyNumber + ) throws UnregisteredRecipientException; + + /** + * Trust all keys of this identity without verification + * + * @param recipient account of the identity + */ + boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) throws UnregisteredRecipientException; + + void addClosedListener(Runnable listener); @Override void close() throws IOException; interface ReceiveMessageHandler { - void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent decryptedContent, Throwable e); + ReceiveMessageHandler EMPTY = (envelope, e) -> { + }; + + void handleMessage(MessageEnvelope envelope, Throwable e); } }