]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java
Reformat code
[signal-cli] / src / main / java / org / asamk / signal / dbus / DbusSignalImpl.java
1 package org.asamk.signal.dbus;
2
3 import org.asamk.Signal;
4 import org.asamk.signal.BaseConfig;
5 import org.asamk.signal.manager.AttachmentInvalidException;
6 import org.asamk.signal.manager.Manager;
7 import org.asamk.signal.manager.NotMasterDeviceException;
8 import org.asamk.signal.manager.StickerPackInvalidException;
9 import org.asamk.signal.manager.UntrustedIdentityException;
10 import org.asamk.signal.manager.api.Device;
11 import org.asamk.signal.manager.api.Message;
12 import org.asamk.signal.manager.api.RecipientIdentifier;
13 import org.asamk.signal.manager.api.TypingAction;
14 import org.asamk.signal.manager.groups.GroupId;
15 import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
16 import org.asamk.signal.manager.groups.GroupNotFoundException;
17 import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
18 import org.asamk.signal.manager.groups.LastGroupAdminException;
19 import org.asamk.signal.manager.groups.NotAGroupMemberException;
20 import org.asamk.signal.manager.storage.identities.IdentityInfo;
21 import org.asamk.signal.util.ErrorUtils;
22 import org.asamk.signal.util.Util;
23 import org.freedesktop.dbus.exceptions.DBusExecutionException;
24 import org.whispersystems.libsignal.InvalidKeyException;
25 import org.whispersystems.libsignal.util.Pair;
26 import org.whispersystems.libsignal.util.guava.Optional;
27 import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
28 import org.whispersystems.signalservice.api.messages.SendMessageResult;
29 import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
30 import org.whispersystems.signalservice.api.util.InvalidNumberException;
31 import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
32
33 import java.io.File;
34 import java.io.IOException;
35 import java.net.URI;
36 import java.net.URISyntaxException;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Set;
44 import java.util.stream.Collectors;
45 import java.util.stream.Stream;
46
47 import static org.asamk.signal.util.Util.getLegacyIdentifier;
48
49 public class DbusSignalImpl implements Signal {
50
51 private final Manager m;
52 private final String objectPath;
53
54 public DbusSignalImpl(final Manager m, final String objectPath) {
55 this.m = m;
56 this.objectPath = objectPath;
57 }
58
59 @Override
60 public boolean isRemote() {
61 return false;
62 }
63
64 @Override
65 public String getObjectPath() {
66 return objectPath;
67 }
68
69 @Override
70 public void addDevice(String uri) {
71 try {
72 m.addDeviceLink(new URI(uri));
73 } catch (IOException | InvalidKeyException e) {
74 throw new Error.Failure(e.getClass().getSimpleName() + " Add device link failed. " + e.getMessage());
75 } catch (URISyntaxException e) {
76 throw new Error.InvalidUri(e.getClass().getSimpleName()
77 + " Device link uri has invalid format: "
78 + e.getMessage());
79 }
80 }
81
82 @Override
83 public void removeDevice(int deviceId) {
84 try {
85 m.removeLinkedDevices(deviceId);
86 } catch (IOException e) {
87 throw new Error.Failure(e.getClass().getSimpleName() + ": Error while removing device: " + e.getMessage());
88 }
89 }
90
91 @Override
92 public List<String> listDevices() {
93 List<Device> devices;
94 List<String> results = new ArrayList<String>();
95
96 try {
97 devices = m.getLinkedDevices();
98 } catch (IOException | Error.Failure e) {
99 throw new Error.Failure("Failed to get linked devices: " + e.getMessage());
100 }
101
102 return devices.stream().map(d -> d.getName() == null ? "" : d.getName()).collect(Collectors.toList());
103 }
104
105 @Override
106 public void updateDeviceName(String deviceName) {
107 try {
108 m.updateAccountAttributes(deviceName);
109 } catch (IOException | Signal.Error.Failure e) {
110 throw new Error.Failure("UpdateAccount error: " + e.getMessage());
111 }
112 }
113
114 @Override
115 public long sendMessage(final String message, final List<String> attachments, final String recipient) {
116 var recipients = new ArrayList<String>(1);
117 recipients.add(recipient);
118 return sendMessage(message, attachments, recipients);
119 }
120
121 @Override
122 public long sendMessage(final String message, final List<String> attachments, final List<String> recipients) {
123 try {
124 final var results = m.sendMessage(new Message(message, attachments),
125 getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
126 .map(RecipientIdentifier.class::cast)
127 .collect(Collectors.toSet()));
128
129 checkSendMessageResults(results.getTimestamp(), results.getResults());
130 return results.getTimestamp();
131 } catch (AttachmentInvalidException e) {
132 throw new Error.AttachmentInvalid(e.getMessage());
133 } catch (IOException e) {
134 throw new Error.Failure(e.getMessage());
135 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
136 throw new Error.GroupNotFound(e.getMessage());
137 }
138 }
139
140 @Override
141 public long sendRemoteDeleteMessage(
142 final long targetSentTimestamp, final String recipient
143 ) {
144 var recipients = new ArrayList<String>(1);
145 recipients.add(recipient);
146 return sendRemoteDeleteMessage(targetSentTimestamp, recipients);
147 }
148
149 @Override
150 public long sendRemoteDeleteMessage(
151 final long targetSentTimestamp, final List<String> recipients
152 ) {
153 try {
154 final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
155 getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
156 .map(RecipientIdentifier.class::cast)
157 .collect(Collectors.toSet()));
158 checkSendMessageResults(results.getTimestamp(), results.getResults());
159 return results.getTimestamp();
160 } catch (IOException e) {
161 throw new Error.Failure(e.getMessage());
162 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
163 throw new Error.GroupNotFound(e.getMessage());
164 }
165 }
166
167 @Override
168 public long sendGroupRemoteDeleteMessage(
169 final long targetSentTimestamp, final byte[] groupId
170 ) {
171 try {
172 final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
173 Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
174 checkSendMessageResults(results.getTimestamp(), results.getResults());
175 return results.getTimestamp();
176 } catch (IOException e) {
177 throw new Error.Failure(e.getMessage());
178 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
179 throw new Error.GroupNotFound(e.getMessage());
180 }
181 }
182
183 @Override
184 public long sendMessageReaction(
185 final String emoji,
186 final boolean remove,
187 final String targetAuthor,
188 final long targetSentTimestamp,
189 final String recipient
190 ) {
191 var recipients = new ArrayList<String>(1);
192 recipients.add(recipient);
193 return sendMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, recipients);
194 }
195
196 @Override
197 public long sendMessageReaction(
198 final String emoji,
199 final boolean remove,
200 final String targetAuthor,
201 final long targetSentTimestamp,
202 final List<String> recipients
203 ) {
204 try {
205 final var results = m.sendMessageReaction(emoji,
206 remove,
207 getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
208 targetSentTimestamp,
209 getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
210 .map(RecipientIdentifier.class::cast)
211 .collect(Collectors.toSet()));
212 checkSendMessageResults(results.getTimestamp(), results.getResults());
213 return results.getTimestamp();
214 } catch (IOException e) {
215 throw new Error.Failure(e.getMessage());
216 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
217 throw new Error.GroupNotFound(e.getMessage());
218 }
219 }
220
221 @Override
222 public void sendTyping(
223 final String recipient, final boolean stop
224 ) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity {
225 try {
226 var recipients = new ArrayList<String>(1);
227 recipients.add(recipient);
228 m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START,
229 getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
230 .map(RecipientIdentifier.class::cast)
231 .collect(Collectors.toSet()));
232 } catch (IOException e) {
233 throw new Error.Failure(e.getMessage());
234 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
235 throw new Error.GroupNotFound(e.getMessage());
236 } catch (UntrustedIdentityException e) {
237 throw new Error.UntrustedIdentity(e.getMessage());
238 }
239 }
240
241 @Override
242 public void sendReadReceipt(
243 final String recipient, final List<Long> timestamps
244 ) throws Error.Failure, Error.UntrustedIdentity {
245 try {
246 m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getUsername()), timestamps);
247 } catch (IOException e) {
248 throw new Error.Failure(e.getMessage());
249 } catch (UntrustedIdentityException e) {
250 throw new Error.UntrustedIdentity(e.getMessage());
251 }
252 }
253
254 @Override
255 public void sendContacts() {
256 try {
257 m.sendContacts();
258 } catch (IOException e) {
259 throw new Error.Failure("SendContacts error: " + e.getMessage());
260 }
261 }
262
263 @Override
264 public void sendSyncRequest() {
265 try {
266 m.requestAllSyncData();
267 } catch (IOException e) {
268 throw new Error.Failure("Request sync data error: " + e.getMessage());
269 }
270 }
271
272 @Override
273 public long sendNoteToSelfMessage(
274 final String message, final List<String> attachments
275 ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity {
276 try {
277 final var results = m.sendMessage(new Message(message, attachments),
278 Set.of(new RecipientIdentifier.NoteToSelf()));
279 checkSendMessageResults(results.getTimestamp(), results.getResults());
280 return results.getTimestamp();
281 } catch (AttachmentInvalidException e) {
282 throw new Error.AttachmentInvalid(e.getMessage());
283 } catch (IOException e) {
284 throw new Error.Failure(e.getMessage());
285 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
286 throw new Error.GroupNotFound(e.getMessage());
287 }
288 }
289
290 @Override
291 public void sendEndSessionMessage(final List<String> recipients) {
292 try {
293 final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getUsername()));
294 checkSendMessageResults(results.getTimestamp(), results.getResults());
295 } catch (IOException e) {
296 throw new Error.Failure(e.getMessage());
297 }
298 }
299
300 @Override
301 public long sendGroupMessage(final String message, final List<String> attachments, final byte[] groupId) {
302 try {
303 var results = m.sendMessage(new Message(message, attachments),
304 Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
305 checkSendMessageResults(results.getTimestamp(), results.getResults());
306 return results.getTimestamp();
307 } catch (IOException e) {
308 throw new Error.Failure(e.getMessage());
309 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
310 throw new Error.GroupNotFound(e.getMessage());
311 } catch (AttachmentInvalidException e) {
312 throw new Error.AttachmentInvalid(e.getMessage());
313 }
314 }
315
316 @Override
317 public long sendGroupMessageReaction(
318 final String emoji,
319 final boolean remove,
320 final String targetAuthor,
321 final long targetSentTimestamp,
322 final byte[] groupId
323 ) {
324 try {
325 final var results = m.sendMessageReaction(emoji,
326 remove,
327 getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
328 targetSentTimestamp,
329 Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
330 checkSendMessageResults(results.getTimestamp(), results.getResults());
331 return results.getTimestamp();
332 } catch (IOException e) {
333 throw new Error.Failure(e.getMessage());
334 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
335 throw new Error.GroupNotFound(e.getMessage());
336 }
337 }
338
339 // Since contact names might be empty if not defined, also potentially return
340 // the profile name
341 @Override
342 public String getContactName(final String number) {
343 return m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getUsername()));
344 }
345
346 @Override
347 public void setContactName(final String number, final String name) {
348 try {
349 m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name);
350 } catch (NotMasterDeviceException e) {
351 throw new Error.Failure("This command doesn't work on linked devices.");
352 } catch (UnregisteredUserException e) {
353 throw new Error.Failure("Contact is not registered.");
354 }
355 }
356
357 @Override
358 public void setExpirationTimer(final String number, final int expiration) {
359 try {
360 m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getUsername()), expiration);
361 } catch (IOException e) {
362 throw new Error.Failure(e.getMessage());
363 }
364 }
365
366 @Override
367 public void setContactBlocked(final String number, final boolean blocked) {
368 try {
369 m.setContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()), blocked);
370 } catch (NotMasterDeviceException e) {
371 throw new Error.Failure("This command doesn't work on linked devices.");
372 } catch (IOException e) {
373 throw new Error.Failure(e.getMessage());
374 }
375 }
376
377 @Override
378 public void setGroupBlocked(final byte[] groupId, final boolean blocked) {
379 try {
380 m.setGroupBlocked(getGroupId(groupId), blocked);
381 } catch (GroupNotFoundException e) {
382 throw new Error.GroupNotFound(e.getMessage());
383 } catch (IOException e) {
384 throw new Error.Failure(e.getMessage());
385 }
386 }
387
388 @Override
389 public List<byte[]> getGroupIds() {
390 var groups = m.getGroups();
391 var ids = new ArrayList<byte[]>(groups.size());
392 for (var group : groups) {
393 ids.add(group.getGroupId().serialize());
394 }
395 return ids;
396 }
397
398 @Override
399 public String getGroupName(final byte[] groupId) {
400 var group = m.getGroup(getGroupId(groupId));
401 if (group == null) {
402 return "";
403 } else {
404 return group.getTitle();
405 }
406 }
407
408 @Override
409 public List<String> getGroupMembers(final byte[] groupId) {
410 var group = m.getGroup(getGroupId(groupId));
411 if (group == null) {
412 return List.of();
413 } else {
414 return group.getMembers()
415 .stream()
416 .map(m::resolveSignalServiceAddress)
417 .map(Util::getLegacyIdentifier)
418 .collect(Collectors.toList());
419 }
420 }
421
422 @Override
423 public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) {
424 try {
425 if (groupId.length == 0) {
426 groupId = null;
427 }
428 if (name.isEmpty()) {
429 name = null;
430 }
431 if (avatar.isEmpty()) {
432 avatar = null;
433 }
434 final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getUsername());
435 if (groupId == null) {
436 final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar));
437 checkSendMessageResults(results.second().getTimestamp(), results.second().getResults());
438 return results.first().serialize();
439 } else {
440 final var results = m.updateGroup(getGroupId(groupId),
441 name,
442 null,
443 memberIdentifiers,
444 null,
445 null,
446 null,
447 false,
448 null,
449 null,
450 null,
451 avatar == null ? null : new File(avatar),
452 null,
453 null);
454 if (results != null) {
455 checkSendMessageResults(results.getTimestamp(), results.getResults());
456 }
457 return groupId;
458 }
459 } catch (IOException e) {
460 throw new Error.Failure(e.getMessage());
461 } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
462 throw new Error.GroupNotFound(e.getMessage());
463 } catch (AttachmentInvalidException e) {
464 throw new Error.AttachmentInvalid(e.getMessage());
465 }
466 }
467
468 @Override
469 public boolean isRegistered() {
470 return true;
471 }
472
473 @Override
474 public void updateProfile(
475 final String name,
476 final String about,
477 final String aboutEmoji,
478 String avatarPath,
479 final boolean removeAvatar
480 ) {
481 try {
482 if (avatarPath.isEmpty()) {
483 avatarPath = null;
484 }
485 Optional<File> avatarFile = removeAvatar
486 ? Optional.absent()
487 : avatarPath == null ? null : Optional.of(new File(avatarPath));
488 m.setProfile(name, null, about, aboutEmoji, avatarFile);
489 } catch (IOException e) {
490 throw new Error.Failure(e.getMessage());
491 }
492 }
493
494 @Override
495 public void removePin() {
496 try {
497 m.setRegistrationLockPin(Optional.absent());
498 } catch (UnauthenticatedResponseException e) {
499 throw new Error.Failure("Remove pin failed with unauthenticated response: " + e.getMessage());
500 } catch (IOException e) {
501 throw new Error.Failure("Remove pin error: " + e.getMessage());
502 }
503 }
504
505 @Override
506 public void setPin(String registrationLockPin) {
507 try {
508 m.setRegistrationLockPin(Optional.of(registrationLockPin));
509 } catch (UnauthenticatedResponseException e) {
510 throw new Error.Failure("Set pin error failed with unauthenticated response: " + e.getMessage());
511 } catch (IOException e) {
512 throw new Error.Failure("Set pin error: " + e.getMessage());
513 }
514 }
515
516 // Provide option to query a version string in order to react on potential
517 // future interface changes
518 @Override
519 public String version() {
520 return BaseConfig.PROJECT_VERSION;
521 }
522
523 // Create a unique list of Numbers from Identities and Contacts to really get
524 // all numbers the system knows
525 @Override
526 public List<String> listNumbers() {
527 return Stream.concat(m.getIdentities().stream().map(IdentityInfo::getRecipientId),
528 m.getContacts().stream().map(Pair::first))
529 .map(m::resolveSignalServiceAddress)
530 .map(a -> a.getNumber().orNull())
531 .filter(Objects::nonNull)
532 .distinct()
533 .collect(Collectors.toList());
534 }
535
536 @Override
537 public List<String> getContactNumber(final String name) {
538 // Contact names have precedence.
539 var numbers = new ArrayList<String>();
540 var contacts = m.getContacts();
541 for (var c : contacts) {
542 if (name.equals(c.second().getName())) {
543 numbers.add(getLegacyIdentifier(m.resolveSignalServiceAddress(c.first())));
544 }
545 }
546 // Try profiles if no contact name was found
547 for (var identity : m.getIdentities()) {
548 final var recipientId = identity.getRecipientId();
549 final var address = m.resolveSignalServiceAddress(recipientId);
550 var number = address.getNumber().orNull();
551 if (number != null) {
552 var profile = m.getRecipientProfile(recipientId);
553 if (profile != null && profile.getDisplayName().equals(name)) {
554 numbers.add(number);
555 }
556 }
557 }
558 return numbers;
559 }
560
561 @Override
562 public void quitGroup(final byte[] groupId) {
563 var group = getGroupId(groupId);
564 try {
565 m.quitGroup(group, Set.of());
566 } catch (GroupNotFoundException | NotAGroupMemberException e) {
567 throw new Error.GroupNotFound(e.getMessage());
568 } catch (IOException | LastGroupAdminException e) {
569 throw new Error.Failure(e.getMessage());
570 }
571 }
572
573 @Override
574 public byte[] joinGroup(final String groupLink) {
575 try {
576 final var linkUrl = GroupInviteLinkUrl.fromUri(groupLink);
577 if (linkUrl == null) {
578 throw new Error.Failure("Group link is invalid:");
579 }
580 final var result = m.joinGroup(linkUrl);
581 return result.first().serialize();
582 } catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupLinkNotActiveException e) {
583 throw new Error.Failure("Group link is invalid: " + e.getMessage());
584 } catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) {
585 throw new Error.Failure("Group link was created with an incompatible version: " + e.getMessage());
586 } catch (IOException e) {
587 throw new Error.Failure(e.getMessage());
588 }
589 }
590
591 @Override
592 public boolean isContactBlocked(final String number) {
593 return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()));
594 }
595
596 @Override
597 public boolean isGroupBlocked(final byte[] groupId) {
598 var group = m.getGroup(getGroupId(groupId));
599 if (group == null) {
600 return false;
601 } else {
602 return group.isBlocked();
603 }
604 }
605
606 @Override
607 public boolean isMember(final byte[] groupId) {
608 var group = m.getGroup(getGroupId(groupId));
609 if (group == null) {
610 return false;
611 } else {
612 return group.isMember(m.getSelfRecipientId());
613 }
614 }
615
616 @Override
617 public String uploadStickerPack(String stickerPackPath) {
618 File path = new File(stickerPackPath);
619 try {
620 return m.uploadStickerPack(path).toString();
621 } catch (IOException e) {
622 throw new Error.Failure("Upload error (maybe image size is too large):" + e.getMessage());
623 } catch (StickerPackInvalidException e) {
624 throw new Error.Failure("Invalid sticker pack: " + e.getMessage());
625 }
626 }
627
628 private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException {
629 var error = ErrorUtils.getErrorMessageFromSendMessageResult(result);
630
631 if (error == null) {
632 return;
633 }
634
635 final var message = timestamp + "\nFailed to send message:\n" + error + '\n';
636
637 if (result.getIdentityFailure() != null) {
638 throw new Error.UntrustedIdentity(message);
639 } else {
640 throw new Error.Failure(message);
641 }
642 }
643
644 private static void checkSendMessageResults(
645 long timestamp, Map<RecipientIdentifier, List<SendMessageResult>> results
646 ) throws DBusExecutionException {
647 final var sendMessageResults = results.values().stream().findFirst();
648 if (results.size() == 1 && sendMessageResults.get().size() == 1) {
649 checkSendMessageResult(timestamp, sendMessageResults.get().stream().findFirst().get());
650 return;
651 }
652
653 var errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results);
654 if (errors.size() == 0) {
655 return;
656 }
657
658 var message = new StringBuilder();
659 message.append(timestamp).append('\n');
660 message.append("Failed to send (some) messages:\n");
661 for (var error : errors) {
662 message.append(error).append('\n');
663 }
664
665 throw new Error.Failure(message.toString());
666 }
667
668 private static void checkSendMessageResults(
669 long timestamp, Collection<SendMessageResult> results
670 ) throws DBusExecutionException {
671 if (results.size() == 1) {
672 checkSendMessageResult(timestamp, results.stream().findFirst().get());
673 return;
674 }
675
676 var errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results);
677 if (errors.size() == 0) {
678 return;
679 }
680
681 var message = new StringBuilder();
682 message.append(timestamp).append('\n');
683 message.append("Failed to send (some) messages:\n");
684 for (var error : errors) {
685 message.append(error).append('\n');
686 }
687
688 throw new Error.Failure(message.toString());
689 }
690
691 private static Set<RecipientIdentifier.Single> getSingleRecipientIdentifiers(
692 final Collection<String> recipientStrings, final String localNumber
693 ) throws DBusExecutionException {
694 final var identifiers = new HashSet<RecipientIdentifier.Single>();
695 for (var recipientString : recipientStrings) {
696 identifiers.add(getSingleRecipientIdentifier(recipientString, localNumber));
697 }
698 return identifiers;
699 }
700
701 private static RecipientIdentifier.Single getSingleRecipientIdentifier(
702 final String recipientString, final String localNumber
703 ) throws DBusExecutionException {
704 try {
705 return RecipientIdentifier.Single.fromString(recipientString, localNumber);
706 } catch (InvalidNumberException e) {
707 throw new Error.InvalidNumber(e.getMessage());
708 }
709 }
710
711 private static GroupId getGroupId(byte[] groupId) throws DBusExecutionException {
712 try {
713 return GroupId.unknownVersion(groupId);
714 } catch (Throwable e) {
715 throw new Error.InvalidGroupId("Invalid group id: " + e.getMessage());
716 }
717 }
718 }