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