]> nmode's Git Repositories - signal-cli/blob - src/main/java/cli/Main.java
Catch AssertionError
[signal-cli] / src / main / java / cli / 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 cli;
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.whispersystems.textsecure.api.crypto.UntrustedIdentityException;
24 import org.whispersystems.textsecure.api.messages.*;
25 import org.whispersystems.textsecure.api.messages.multidevice.TextSecureSyncMessage;
26 import org.whispersystems.textsecure.api.push.TextSecureAddress;
27 import org.whispersystems.textsecure.api.push.exceptions.EncapsulatedExceptions;
28 import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException;
29 import org.whispersystems.textsecure.api.push.exceptions.UnregisteredUserException;
30 import org.whispersystems.textsecure.api.util.InvalidNumberException;
31
32 import java.io.File;
33 import java.io.FileInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.nio.file.Files;
37 import java.nio.file.Paths;
38 import java.security.Security;
39 import java.util.ArrayList;
40 import java.util.List;
41
42 public class Main {
43
44 public static void main(String[] args) {
45 // Workaround for BKS truststore
46 Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
47
48 Namespace ns = parseArgs(args);
49 if (ns == null) {
50 System.exit(1);
51 }
52
53 final String username = ns.getString("username");
54 final Manager m = new Manager(username);
55 if (m.userExists()) {
56 try {
57 m.load();
58 } catch (Exception e) {
59 System.out.println("Error loading state file \"" + m.getFileName() + "\": " + e.getMessage());
60 System.exit(2);
61 }
62 }
63
64 switch (ns.getString("command")) {
65 case "register":
66 if (!m.userHasKeys()) {
67 m.createNewIdentity();
68 }
69 try {
70 m.register(ns.getBoolean("voice"));
71 } catch (IOException e) {
72 System.out.println("Request verify error: " + e.getMessage());
73 System.exit(3);
74 }
75 break;
76 case "verify":
77 if (!m.userHasKeys()) {
78 System.out.println("User has no keys, first call register.");
79 System.exit(1);
80 }
81 if (m.isRegistered()) {
82 System.out.println("User registration is already verified");
83 System.exit(1);
84 }
85 try {
86 m.verifyAccount(ns.getString("verificationCode"));
87 } catch (IOException e) {
88 System.out.println("Verify error: " + e.getMessage());
89 System.exit(3);
90 }
91 break;
92 case "send":
93 if (!m.isRegistered()) {
94 System.out.println("User is not registered.");
95 System.exit(1);
96 }
97 String messageText = ns.getString("message");
98 if (messageText == null) {
99 try {
100 messageText = IOUtils.toString(System.in);
101 } catch (IOException e) {
102 System.out.println("Failed to read message from stdin: " + e.getMessage());
103 System.exit(1);
104 }
105 }
106
107 final List<String> attachments = ns.<String>getList("attachment");
108 List<TextSecureAttachment> textSecureAttachments = null;
109 if (attachments != null) {
110 textSecureAttachments = new ArrayList<TextSecureAttachment>(attachments.size());
111 for (String attachment : attachments) {
112 try {
113 File attachmentFile = new File(attachment);
114 InputStream attachmentStream = new FileInputStream(attachmentFile);
115 final long attachmentSize = attachmentFile.length();
116 String mime = Files.probeContentType(Paths.get(attachment));
117 textSecureAttachments.add(new TextSecureAttachmentStream(attachmentStream, mime, attachmentSize, null));
118 } catch (IOException e) {
119 System.out.println("Failed to add attachment \"" + attachment + "\": " + e.getMessage());
120 System.out.println("Aborting sending.");
121 System.exit(1);
122 }
123 }
124 }
125
126 List<TextSecureAddress> recipients = new ArrayList<>(ns.<String>getList("recipient").size());
127 for (String recipient : ns.<String>getList("recipient")) {
128 try {
129 recipients.add(m.getPushAddress(recipient));
130 } catch (InvalidNumberException e) {
131 System.out.println("Failed to add recipient \"" + recipient + "\": " + e.getMessage());
132 System.out.println("Aborting sending.");
133 System.exit(1);
134 }
135 }
136 sendMessage(m, messageText, textSecureAttachments, recipients);
137 break;
138 case "receive":
139 if (!m.isRegistered()) {
140 System.out.println("User is not registered.");
141 System.exit(1);
142 }
143 try {
144 m.receiveMessages(5, true, new ReceiveMessageHandler(m));
145 } catch (IOException e) {
146 System.out.println("Error while receiving message: " + e.getMessage());
147 System.exit(3);
148 } catch (AssertionError e) {
149 System.out.println("Failed to receive message (Assertion): " + e.getMessage());
150 System.out.println(e.getStackTrace());
151 System.out.println("If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README");
152 System.exit(1);
153 }
154 break;
155 }
156 m.save();
157 System.exit(0);
158 }
159
160 private static Namespace parseArgs(String[] args) {
161 ArgumentParser parser = ArgumentParsers.newArgumentParser("textsecure-cli")
162 .defaultHelp(true)
163 .description("Commandline interface for TextSecure.");
164 Subparsers subparsers = parser.addSubparsers()
165 .title("subcommands")
166 .dest("command")
167 .description("valid subcommands")
168 .help("additional help");
169
170 Subparser parserRegister = subparsers.addParser("register");
171 parserRegister.addArgument("-v", "--voice")
172 .help("The verification should be done over voice, not sms.")
173 .action(Arguments.storeTrue());
174
175 Subparser parserVerify = subparsers.addParser("verify");
176 parserVerify.addArgument("verificationCode")
177 .help("The verification code you received via sms or voice call.");
178
179 Subparser parserSend = subparsers.addParser("send");
180 parserSend.addArgument("recipient")
181 .help("Specify the recipients' phone number.")
182 .nargs("*");
183 parserSend.addArgument("-m", "--message")
184 .help("Specify the message, if missing standard input is used.");
185 parserSend.addArgument("-a", "--attachment")
186 .nargs("*")
187 .help("Add file as attachment");
188
189 Subparser parserReceive = subparsers.addParser("receive");
190 parser.addArgument("-u", "--username")
191 .required(true)
192 .help("Specify your phone number, that will be used for verification.");
193
194 try {
195 return parser.parseArgs(args);
196 } catch (ArgumentParserException e) {
197 parser.handleError(e);
198 return null;
199 }
200 }
201
202 private static void sendMessage(Manager m, String messageText, List<TextSecureAttachment> textSecureAttachments,
203 List<TextSecureAddress> recipients) {
204 final TextSecureDataMessage.Builder messageBuilder = TextSecureDataMessage.newBuilder().withBody(messageText);
205 if (textSecureAttachments != null) {
206 messageBuilder.withAttachments(textSecureAttachments);
207 }
208 TextSecureDataMessage message = messageBuilder.build();
209
210 try {
211 m.sendMessage(recipients, message);
212 } catch (IOException e) {
213 System.out.println("Failed to send message: " + e.getMessage());
214 } catch (EncapsulatedExceptions e) {
215 System.out.println("Failed to send (some) messages:");
216 for (NetworkFailureException n : e.getNetworkExceptions()) {
217 System.out.println("Network failure for \"" + n.getE164number() + "\": " + n.getMessage());
218 }
219 for (UnregisteredUserException n : e.getUnregisteredUserExceptions()) {
220 System.out.println("Unregistered user \"" + n.getE164Number() + "\": " + n.getMessage());
221 }
222 for (UntrustedIdentityException n : e.getUntrustedIdentityExceptions()) {
223 System.out.println("Untrusted Identity for \"" + n.getE164Number() + "\": " + n.getMessage());
224 }
225 } catch (AssertionError e) {
226 System.out.println("Failed to send message (Assertion): " + e.getMessage());
227 System.out.println(e.getStackTrace());
228 System.out.println("If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README");
229 System.exit(1);
230 }
231 }
232
233 private static class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
234 Manager m;
235
236 public ReceiveMessageHandler(Manager m) {
237 this.m = m;
238 }
239
240 @Override
241 public void handleMessage(TextSecureEnvelope envelope) {
242 System.out.println("Envelope from: " + envelope.getSource());
243 System.out.println("Timestamp: " + envelope.getTimestamp());
244
245 if (envelope.isReceipt()) {
246 System.out.println("Got receipt.");
247 } else if (envelope.isWhisperMessage() | envelope.isPreKeyWhisperMessage()) {
248 TextSecureContent content = m.decryptMessage(envelope);
249
250 if (content == null) {
251 System.out.println("Failed to decrypt message.");
252 } else {
253 if (content.getDataMessage().isPresent()) {
254 TextSecureDataMessage message = content.getDataMessage().get();
255 System.out.println("Body: " + message.getBody().get());
256
257 if (message.isEndSession()) {
258 m.handleEndSession(envelope.getSource());
259 } else if (message.getAttachments().isPresent()) {
260 System.out.println("Attachments: ");
261 for (TextSecureAttachment attachment : message.getAttachments().get()) {
262 System.out.println("- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + (attachment.isStream() ? "Stream" : "") + ")");
263 if (attachment.isPointer()) {
264 System.out.println(" Id: " + attachment.asPointer().getId() + " Key length: " + attachment.asPointer().getKey().length + (attachment.asPointer().getRelay().isPresent() ? " Relay: " + attachment.asPointer().getRelay().get() : ""));
265 }
266 }
267 }
268 }
269 if (content.getSyncMessage().isPresent()) {
270 TextSecureSyncMessage syncMessage = content.getSyncMessage().get();
271 System.out.println("Received sync message");
272 }
273 }
274 } else {
275 System.out.println("Unknown message received.");
276 }
277 System.out.println();
278 }
279 }
280 }