]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/Main.java
def569dfec76fd8e4724bff6a8aa1801b7e2c665
[signal-cli] / src / main / java / org / asamk / signal / Main.java
1 /**
2 * Copyright (C) 2015 AsamK
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 package org.asamk.signal;
18
19 import net.sourceforge.argparse4j.ArgumentParsers;
20 import net.sourceforge.argparse4j.impl.Arguments;
21 import net.sourceforge.argparse4j.inf.*;
22 import org.apache.commons.io.IOUtils;
23 import org.apache.http.util.TextUtils;
24 import org.asamk.Signal;
25 import org.freedesktop.dbus.DBusConnection;
26 import org.freedesktop.dbus.DBusSigHandler;
27 import org.freedesktop.dbus.exceptions.DBusException;
28 import org.freedesktop.dbus.exceptions.DBusExecutionException;
29 import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
30 import org.whispersystems.signalservice.api.messages.*;
31 import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
32 import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
33 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
34 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
35 import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptions;
36 import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
37 import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
38 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
39
40 import java.io.File;
41 import java.io.IOException;
42 import java.security.Security;
43 import java.util.ArrayList;
44 import java.util.List;
45
46 public class Main {
47
48 public static final String SIGNAL_BUSNAME = "org.asamk.Signal";
49 public static final String SIGNAL_OBJECTPATH = "/org/asamk/Signal";
50
51 public static void main(String[] args) {
52 // Workaround for BKS truststore
53 Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
54
55 Namespace ns = parseArgs(args);
56 if (ns == null) {
57 System.exit(1);
58 }
59
60 final String username = ns.getString("username");
61 Manager m;
62 Signal ts;
63 DBusConnection dBusConn = null;
64 try {
65 if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) {
66 try {
67 m = null;
68 int busType;
69 if (ns.getBoolean("dbus_system")) {
70 busType = DBusConnection.SYSTEM;
71 } else {
72 busType = DBusConnection.SESSION;
73 }
74 dBusConn = DBusConnection.getConnection(busType);
75 ts = (Signal) dBusConn.getRemoteObject(
76 SIGNAL_BUSNAME, SIGNAL_OBJECTPATH,
77 Signal.class);
78 } catch (DBusException e) {
79 e.printStackTrace();
80 if (dBusConn != null) {
81 dBusConn.disconnect();
82 }
83 System.exit(3);
84 return;
85 }
86 } else {
87 String settingsPath = ns.getString("config");
88 if (TextUtils.isEmpty(settingsPath)) {
89 settingsPath = System.getProperty("user.home") + "/.config/signal";
90 if (!new File(settingsPath).exists()) {
91 String legacySettingsPath = System.getProperty("user.home") + "/.config/textsecure";
92 if (new File(legacySettingsPath).exists()) {
93 settingsPath = legacySettingsPath;
94 }
95 }
96 }
97
98 m = new Manager(username, settingsPath);
99 ts = m;
100 if (m.userExists()) {
101 try {
102 m.load();
103 } catch (Exception e) {
104 System.err.println("Error loading state file \"" + m.getFileName() + "\": " + e.getMessage());
105 System.exit(2);
106 return;
107 }
108 }
109 }
110
111 switch (ns.getString("command")) {
112 case "register":
113 if (dBusConn != null) {
114 System.err.println("register is not yet implemented via dbus");
115 System.exit(1);
116 }
117 if (!m.userHasKeys()) {
118 m.createNewIdentity();
119 }
120 try {
121 m.register(ns.getBoolean("voice"));
122 } catch (IOException e) {
123 System.err.println("Request verify error: " + e.getMessage());
124 System.exit(3);
125 }
126 break;
127 case "verify":
128 if (dBusConn != null) {
129 System.err.println("verify is not yet implemented via dbus");
130 System.exit(1);
131 }
132 if (!m.userHasKeys()) {
133 System.err.println("User has no keys, first call register.");
134 System.exit(1);
135 }
136 if (m.isRegistered()) {
137 System.err.println("User registration is already verified");
138 System.exit(1);
139 }
140 try {
141 m.verifyAccount(ns.getString("verificationCode"));
142 } catch (IOException e) {
143 System.err.println("Verify error: " + e.getMessage());
144 System.exit(3);
145 }
146 break;
147 case "send":
148 if (dBusConn == null && !m.isRegistered()) {
149 System.err.println("User is not registered.");
150 System.exit(1);
151 }
152
153 if (ns.getBoolean("endsession")) {
154 if (ns.getList("recipient") == null) {
155 System.err.println("No recipients given");
156 System.err.println("Aborting sending.");
157 System.exit(1);
158 }
159 try {
160 ts.sendEndSessionMessage(ns.<String>getList("recipient"));
161 } catch (IOException e) {
162 handleIOException(e);
163 } catch (EncapsulatedExceptions e) {
164 handleEncapsulatedExceptions(e);
165 } catch (AssertionError e) {
166 handleAssertionError(e);
167 } catch (DBusExecutionException e) {
168 handleDBusExecutionException(e);
169 }
170 } else {
171 String messageText = ns.getString("message");
172 if (messageText == null) {
173 try {
174 messageText = IOUtils.toString(System.in);
175 } catch (IOException e) {
176 System.err.println("Failed to read message from stdin: " + e.getMessage());
177 System.err.println("Aborting sending.");
178 System.exit(1);
179 }
180 }
181
182 try {
183 List<String> attachments = ns.getList("attachment");
184 if (attachments == null) {
185 attachments = new ArrayList<>();
186 }
187 if (ns.getString("group") != null) {
188 byte[] groupId = decodeGroupId(ns.getString("group"));
189 ts.sendGroupMessage(messageText, attachments, groupId);
190 } else {
191 ts.sendMessage(messageText, attachments, ns.<String>getList("recipient"));
192 }
193 } catch (IOException e) {
194 handleIOException(e);
195 } catch (EncapsulatedExceptions e) {
196 handleEncapsulatedExceptions(e);
197 } catch (AssertionError e) {
198 handleAssertionError(e);
199 } catch (GroupNotFoundException e) {
200 handleGroupNotFoundException(e);
201 } catch (AttachmentInvalidException e) {
202 System.err.println("Failed to add attachment: " + e.getMessage());
203 System.err.println("Aborting sending.");
204 System.exit(1);
205 } catch (DBusExecutionException e) {
206 handleDBusExecutionException(e);
207 }
208 }
209
210 break;
211 case "receive":
212 if (dBusConn != null) {
213 try {
214 dBusConn.addSigHandler(Signal.MessageReceived.class, new DBusSigHandler<Signal.MessageReceived>() {
215 @Override
216 public void handle(Signal.MessageReceived s) {
217 System.out.print(String.format("Envelope from: %s\nTimestamp: %d\nBody: %s\n",
218 s.getSender(), s.getTimestamp(), s.getMessage()));
219 if (s.getGroupId().length > 0) {
220 System.out.println("Group info:");
221 System.out.println(" Id: " + Base64.encodeBytes(s.getGroupId()));
222 }
223 if (s.getAttachments().size() > 0) {
224 System.out.println("Attachments: ");
225 for (String attachment : s.getAttachments()) {
226 System.out.println("- Stored plaintext in: " + attachment);
227 }
228 }
229 System.out.println();
230 }
231 });
232 } catch (DBusException e) {
233 e.printStackTrace();
234 }
235 while (true) {
236 try {
237 Thread.sleep(10000);
238 } catch (InterruptedException e) {
239 System.exit(0);
240 }
241 }
242 }
243 if (!m.isRegistered()) {
244 System.err.println("User is not registered.");
245 System.exit(1);
246 }
247 int timeout = 5;
248 if (ns.getInt("timeout") != null) {
249 timeout = ns.getInt("timeout");
250 }
251 boolean returnOnTimeout = true;
252 if (timeout < 0) {
253 returnOnTimeout = false;
254 timeout = 3600;
255 }
256 try {
257 m.receiveMessages(timeout, returnOnTimeout, new ReceiveMessageHandler(m));
258 } catch (IOException e) {
259 System.err.println("Error while receiving messages: " + e.getMessage());
260 System.exit(3);
261 } catch (AssertionError e) {
262 handleAssertionError(e);
263 }
264 break;
265 case "quitGroup":
266 if (dBusConn != null) {
267 System.err.println("quitGroup is not yet implemented via dbus");
268 System.exit(1);
269 }
270 if (!m.isRegistered()) {
271 System.err.println("User is not registered.");
272 System.exit(1);
273 }
274
275 try {
276 m.sendQuitGroupMessage(decodeGroupId(ns.getString("group")));
277 } catch (IOException e) {
278 handleIOException(e);
279 } catch (EncapsulatedExceptions e) {
280 handleEncapsulatedExceptions(e);
281 } catch (AssertionError e) {
282 handleAssertionError(e);
283 } catch (GroupNotFoundException e) {
284 handleGroupNotFoundException(e);
285 }
286
287 break;
288 case "updateGroup":
289 if (dBusConn != null) {
290 System.err.println("updateGroup is not yet implemented via dbus");
291 System.exit(1);
292 }
293 if (!m.isRegistered()) {
294 System.err.println("User is not registered.");
295 System.exit(1);
296 }
297
298 try {
299 byte[] groupId = null;
300 if (ns.getString("group") != null) {
301 groupId = decodeGroupId(ns.getString("group"));
302 }
303 byte[] newGroupId = m.sendUpdateGroupMessage(groupId, ns.getString("name"), ns.<String>getList("member"), ns.getString("avatar"));
304 if (groupId == null) {
305 System.out.println("Creating new group \"" + Base64.encodeBytes(newGroupId) + "\" …");
306 }
307 } catch (IOException e) {
308 handleIOException(e);
309 } catch (AttachmentInvalidException e) {
310 System.err.println("Failed to add avatar attachment for group\": " + e.getMessage());
311 System.err.println("Aborting sending.");
312 System.exit(1);
313 } catch (GroupNotFoundException e) {
314 handleGroupNotFoundException(e);
315 } catch (EncapsulatedExceptions e) {
316 handleEncapsulatedExceptions(e);
317 }
318
319 break;
320 case "daemon":
321 if (dBusConn != null) {
322 System.err.println("Stop it.");
323 System.exit(1);
324 }
325 if (!m.isRegistered()) {
326 System.err.println("User is not registered.");
327 System.exit(1);
328 }
329 DBusConnection conn = null;
330 try {
331 try {
332 int busType;
333 if (ns.getBoolean("system")) {
334 busType = DBusConnection.SYSTEM;
335 } else {
336 busType = DBusConnection.SESSION;
337 }
338 conn = DBusConnection.getConnection(busType);
339 conn.exportObject(SIGNAL_OBJECTPATH, m);
340 conn.requestBusName(SIGNAL_BUSNAME);
341 } catch (DBusException e) {
342 e.printStackTrace();
343 System.exit(3);
344 }
345 try {
346 m.receiveMessages(3600, false, new DbusReceiveMessageHandler(m, conn));
347 } catch (IOException e) {
348 System.err.println("Error while receiving messages: " + e.getMessage());
349 System.exit(3);
350 } catch (AssertionError e) {
351 handleAssertionError(e);
352 }
353 } finally {
354 if (conn != null) {
355 conn.disconnect();
356 }
357 }
358
359 break;
360 }
361 System.exit(0);
362 } finally {
363 if (dBusConn != null) {
364 dBusConn.disconnect();
365 }
366 }
367 }
368
369 private static void handleGroupNotFoundException(GroupNotFoundException e) {
370 System.err.println("Failed to send to group: " + e.getMessage());
371 System.err.println("Aborting sending.");
372 System.exit(1);
373 }
374
375 private static void handleDBusExecutionException(DBusExecutionException e) {
376 System.err.println("Cannot connect to dbus: " + e.getMessage());
377 System.err.println("Aborting.");
378 System.exit(1);
379 }
380
381 private static byte[] decodeGroupId(String groupId) {
382 try {
383 return Base64.decode(groupId);
384 } catch (IOException e) {
385 System.err.println("Failed to decode groupId (must be base64) \"" + groupId + "\": " + e.getMessage());
386 System.err.println("Aborting sending.");
387 System.exit(1);
388 return null;
389 }
390 }
391
392 private static Namespace parseArgs(String[] args) {
393 ArgumentParser parser = ArgumentParsers.newArgumentParser("signal-cli")
394 .defaultHelp(true)
395 .description("Commandline interface for Signal.")
396 .version(Manager.PROJECT_NAME + " " + Manager.PROJECT_VERSION);
397
398 parser.addArgument("-v", "--version")
399 .help("Show package version.")
400 .action(Arguments.version());
401 parser.addArgument("--config")
402 .help("Set the path, where to store the config (Default: $HOME/.config/signal-cli).");
403
404 MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup();
405 mut.addArgument("-u", "--username")
406 .help("Specify your phone number, that will be used for verification.");
407 mut.addArgument("--dbus")
408 .help("Make request via user dbus.")
409 .action(Arguments.storeTrue());
410 mut.addArgument("--dbus-system")
411 .help("Make request via system dbus.")
412 .action(Arguments.storeTrue());
413
414 Subparsers subparsers = parser.addSubparsers()
415 .title("subcommands")
416 .dest("command")
417 .description("valid subcommands")
418 .help("additional help");
419
420 Subparser parserRegister = subparsers.addParser("register");
421 parserRegister.addArgument("-v", "--voice")
422 .help("The verification should be done over voice, not sms.")
423 .action(Arguments.storeTrue());
424
425 Subparser parserVerify = subparsers.addParser("verify");
426 parserVerify.addArgument("verificationCode")
427 .help("The verification code you received via sms or voice call.");
428
429 Subparser parserSend = subparsers.addParser("send");
430 parserSend.addArgument("-g", "--group")
431 .help("Specify the recipient group ID.");
432 parserSend.addArgument("recipient")
433 .help("Specify the recipients' phone number.")
434 .nargs("*");
435 parserSend.addArgument("-m", "--message")
436 .help("Specify the message, if missing standard input is used.");
437 parserSend.addArgument("-a", "--attachment")
438 .nargs("*")
439 .help("Add file as attachment");
440 parserSend.addArgument("-e", "--endsession")
441 .help("Clear session state and send end session message.")
442 .action(Arguments.storeTrue());
443
444 Subparser parserLeaveGroup = subparsers.addParser("quitGroup");
445 parserLeaveGroup.addArgument("-g", "--group")
446 .required(true)
447 .help("Specify the recipient group ID.");
448
449 Subparser parserUpdateGroup = subparsers.addParser("updateGroup");
450 parserUpdateGroup.addArgument("-g", "--group")
451 .help("Specify the recipient group ID.");
452 parserUpdateGroup.addArgument("-n", "--name")
453 .help("Specify the new group name.");
454 parserUpdateGroup.addArgument("-a", "--avatar")
455 .help("Specify a new group avatar image file");
456 parserUpdateGroup.addArgument("-m", "--member")
457 .nargs("*")
458 .help("Specify one or more members to add to the group");
459
460 Subparser parserReceive = subparsers.addParser("receive");
461 parserReceive.addArgument("-t", "--timeout")
462 .type(int.class)
463 .help("Number of seconds to wait for new messages (negative values disable timeout)");
464
465 Subparser parserDaemon = subparsers.addParser("daemon");
466 parserDaemon.addArgument("--system")
467 .action(Arguments.storeTrue())
468 .help("Use DBus system bus instead of user bus.");
469
470 try {
471 Namespace ns = parser.parseArgs(args);
472 if (!ns.getBoolean("dbus") && !ns.getBoolean("dbus_system")) {
473 if (ns.getString("username") == null) {
474 parser.printUsage();
475 System.err.println("You need to specify a username (phone number)");
476 System.exit(2);
477 }
478 if (!PhoneNumberFormatter.isValidNumber(ns.getString("username"))) {
479 System.err.println("Invalid username (phone number), make sure you include the country code.");
480 System.exit(2);
481 }
482 }
483 if (ns.getList("recipient") != null && !ns.getList("recipient").isEmpty() && ns.getString("group") != null) {
484 System.err.println("You cannot specify recipients by phone number and groups a the same time");
485 System.exit(2);
486 }
487 return ns;
488 } catch (ArgumentParserException e) {
489 parser.handleError(e);
490 return null;
491 }
492 }
493
494 private static void handleAssertionError(AssertionError e) {
495 System.err.println("Failed to send/receive message (Assertion): " + e.getMessage());
496 e.printStackTrace();
497 System.err.println("If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README");
498 System.exit(1);
499 }
500
501 private static void handleEncapsulatedExceptions(EncapsulatedExceptions e) {
502 System.err.println("Failed to send (some) messages:");
503 for (NetworkFailureException n : e.getNetworkExceptions()) {
504 System.err.println("Network failure for \"" + n.getE164number() + "\": " + n.getMessage());
505 }
506 for (UnregisteredUserException n : e.getUnregisteredUserExceptions()) {
507 System.err.println("Unregistered user \"" + n.getE164Number() + "\": " + n.getMessage());
508 }
509 for (UntrustedIdentityException n : e.getUntrustedIdentityExceptions()) {
510 System.err.println("Untrusted Identity for \"" + n.getE164Number() + "\": " + n.getMessage());
511 }
512 }
513
514 private static void handleIOException(IOException e) {
515 System.err.println("Failed to send message: " + e.getMessage());
516 }
517
518 private static class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
519 final Manager m;
520
521 public ReceiveMessageHandler(Manager m) {
522 this.m = m;
523 }
524
525 @Override
526 public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, GroupInfo group) {
527 SignalServiceAddress source = envelope.getSourceAddress();
528 System.out.println(String.format("Envelope from: %s (device: %d)", source.getNumber(), envelope.getSourceDevice()));
529 if (source.getRelay().isPresent()) {
530 System.out.println("Relayed by: " + source.getRelay().get());
531 }
532 System.out.println("Timestamp: " + envelope.getTimestamp());
533
534 if (envelope.isReceipt()) {
535 System.out.println("Got receipt.");
536 } else if (envelope.isSignalMessage() | envelope.isPreKeySignalMessage()) {
537 if (content == null) {
538 System.out.println("Failed to decrypt message.");
539 } else {
540 if (content.getDataMessage().isPresent()) {
541 SignalServiceDataMessage message = content.getDataMessage().get();
542 handleSignalServiceDataMessage(message, group);
543 }
544 if (content.getSyncMessage().isPresent()) {
545 SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
546
547 if (syncMessage.getContacts().isPresent()) {
548 System.out.println("Received sync contacts");
549 printAttachment(syncMessage.getContacts().get());
550 }
551 if (syncMessage.getGroups().isPresent()) {
552 System.out.println("Received sync groups");
553 printAttachment(syncMessage.getGroups().get());
554 }
555 if (syncMessage.getRead().isPresent()) {
556 System.out.println("Received sync read messages list");
557 for (ReadMessage rm : syncMessage.getRead().get()) {
558 System.out.println("From: " + rm.getSender() + " Message timestamp: " + rm.getTimestamp());
559 }
560 }
561 if (syncMessage.getRequest().isPresent()) {
562 System.out.println("Received sync request");
563 if (syncMessage.getRequest().get().isContactsRequest()) {
564 System.out.println(" - contacts request");
565 }
566 if (syncMessage.getRequest().get().isGroupsRequest()) {
567 System.out.println(" - groups request");
568 }
569 }
570 if (syncMessage.getSent().isPresent()) {
571 System.out.println("Received sync sent message");
572 final SentTranscriptMessage sentTranscriptMessage = syncMessage.getSent().get();
573 System.out.println("To: " + (sentTranscriptMessage.getDestination().isPresent() ? sentTranscriptMessage.getDestination().get() : "Unknown") + " , Message timestamp: " + sentTranscriptMessage.getTimestamp());
574 SignalServiceDataMessage message = sentTranscriptMessage.getMessage();
575 handleSignalServiceDataMessage(message, null);
576 }
577 }
578 }
579 } else {
580 System.out.println("Unknown message received.");
581 }
582 System.out.println();
583 }
584
585 // TODO remove group parameter
586 private void handleSignalServiceDataMessage(SignalServiceDataMessage message, GroupInfo group) {
587 System.out.println("Message timestamp: " + message.getTimestamp());
588
589 if (message.getBody().isPresent()) {
590 System.out.println("Body: " + message.getBody().get());
591 }
592 if (message.getGroupInfo().isPresent()) {
593 SignalServiceGroup groupInfo = message.getGroupInfo().get();
594 System.out.println("Group info:");
595 System.out.println(" Id: " + Base64.encodeBytes(groupInfo.getGroupId()));
596 if (groupInfo.getName().isPresent()) {
597 System.out.println(" Name: " + groupInfo.getName().get());
598 } else if (group != null) {
599 System.out.println(" Name: " + group.name);
600 } else {
601 System.out.println(" Name: <Unknown group>");
602 }
603 System.out.println(" Type: " + groupInfo.getType());
604 if (groupInfo.getMembers().isPresent()) {
605 for (String member : groupInfo.getMembers().get()) {
606 System.out.println(" Member: " + member);
607 }
608 }
609 if (groupInfo.getAvatar().isPresent()) {
610 System.out.println(" Avatar:");
611 printAttachment(groupInfo.getAvatar().get());
612 }
613 }
614 if (message.isEndSession()) {
615 System.out.println("Is end session");
616 }
617
618 if (message.getAttachments().isPresent()) {
619 System.out.println("Attachments: ");
620 for (SignalServiceAttachment attachment : message.getAttachments().get()) {
621 printAttachment(attachment);
622 }
623 }
624 }
625
626 private void printAttachment(SignalServiceAttachment attachment) {
627 System.out.println("- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + (attachment.isStream() ? "Stream" : "") + ")");
628 if (attachment.isPointer()) {
629 final SignalServiceAttachmentPointer pointer = attachment.asPointer();
630 System.out.println(" Id: " + pointer.getId() + " Key length: " + pointer.getKey().length + (pointer.getRelay().isPresent() ? " Relay: " + pointer.getRelay().get() : ""));
631 System.out.println(" Size: " + (pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "<unavailable>") + (pointer.getPreview().isPresent() ? " (Preview is available: " + pointer.getPreview().get().length + " bytes)" : ""));
632 File file = m.getAttachmentFile(pointer.getId());
633 if (file.exists()) {
634 System.out.println(" Stored plaintext in: " + file);
635 }
636 }
637 }
638 }
639
640 private static class DbusReceiveMessageHandler extends ReceiveMessageHandler {
641 final DBusConnection conn;
642
643 public DbusReceiveMessageHandler(Manager m, DBusConnection conn) {
644 super(m);
645 this.conn = conn;
646 }
647
648 @Override
649 public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, GroupInfo group) {
650 super.handleMessage(envelope, content, group);
651
652 if (!envelope.isReceipt() && content != null && content.getDataMessage().isPresent()) {
653 SignalServiceDataMessage message = content.getDataMessage().get();
654
655 if (!message.isEndSession() &&
656 !(message.getGroupInfo().isPresent() &&
657 message.getGroupInfo().get().getType() != SignalServiceGroup.Type.DELIVER)) {
658 List<String> attachments = new ArrayList<>();
659 if (message.getAttachments().isPresent()) {
660 for (SignalServiceAttachment attachment : message.getAttachments().get()) {
661 if (attachment.isPointer()) {
662 attachments.add(m.getAttachmentFile(attachment.asPointer().getId()).getAbsolutePath());
663 }
664 }
665 }
666
667 try {
668 conn.sendSignal(new Signal.MessageReceived(
669 SIGNAL_OBJECTPATH,
670 message.getTimestamp(),
671 envelope.getSource(),
672 message.getGroupInfo().isPresent() ? message.getGroupInfo().get().getGroupId() : new byte[0],
673 message.getBody().isPresent() ? message.getBody().get() : "",
674 attachments));
675 } catch (DBusException e) {
676 e.printStackTrace();
677 }
678 }
679 }
680 }
681
682 private void printAttachment(SignalServiceAttachment attachment) {
683 System.out.println("- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + (attachment.isStream() ? "Stream" : "") + ")");
684 if (attachment.isPointer()) {
685 final SignalServiceAttachmentPointer pointer = attachment.asPointer();
686 System.out.println(" Id: " + pointer.getId() + " Key length: " + pointer.getKey().length + (pointer.getRelay().isPresent() ? " Relay: " + pointer.getRelay().get() : ""));
687 System.out.println(" Size: " + (pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "<unavailable>") + (pointer.getPreview().isPresent() ? " (Preview is available: " + pointer.getPreview().get().length + " bytes)" : ""));
688 File file = m.getAttachmentFile(pointer.getId());
689 if (file.exists()) {
690 System.out.println(" Stored plaintext in: " + file);
691 }
692 }
693 }
694 }
695 }