]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/Main.java
refactor extracts method for security inits
[signal-cli] / src / main / java / org / asamk / signal / Main.java
1 /*
2 Copyright (C) 2015-2020 AsamK and contributors
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.ArgumentParser;
22 import net.sourceforge.argparse4j.inf.ArgumentParserException;
23 import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup;
24 import net.sourceforge.argparse4j.inf.Namespace;
25 import net.sourceforge.argparse4j.inf.Subparser;
26 import net.sourceforge.argparse4j.inf.Subparsers;
27
28 import org.asamk.Signal;
29 import org.asamk.signal.commands.Command;
30 import org.asamk.signal.commands.Commands;
31 import org.asamk.signal.commands.DbusCommand;
32 import org.asamk.signal.commands.ExtendedDbusCommand;
33 import org.asamk.signal.commands.LocalCommand;
34 import org.asamk.signal.manager.BaseConfig;
35 import org.asamk.signal.manager.Manager;
36 import org.asamk.signal.util.IOUtils;
37 import org.asamk.signal.util.SecurityProvider;
38 import org.bouncycastle.jce.provider.BouncyCastleProvider;
39 import org.freedesktop.dbus.DBusConnection;
40 import org.freedesktop.dbus.exceptions.DBusException;
41 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
42
43 import java.io.File;
44 import java.security.Security;
45 import java.util.Map;
46
47 import static org.whispersystems.signalservice.internal.util.Util.isEmpty;
48
49 public class Main {
50
51 public static void main(String[] args) {
52 installSecurityProviderWorkaround();
53
54 Namespace ns = parseArgs(args);
55 if (ns == null) {
56 System.exit(1);
57 }
58
59 int res = handleCommands(ns);
60 System.exit(res);
61 }
62
63 public static void installSecurityProviderWorkaround() {
64 // Register our own security provider
65 Security.insertProviderAt(new SecurityProvider(), 1);
66 Security.addProvider(new BouncyCastleProvider());
67 }
68
69 private static int handleCommands(Namespace ns) {
70 final String username = ns.getString("username");
71 Manager m;
72 Signal ts;
73 DBusConnection dBusConn = null;
74 try {
75 if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) {
76 try {
77 m = null;
78 int busType;
79 if (ns.getBoolean("dbus_system")) {
80 busType = DBusConnection.SYSTEM;
81 } else {
82 busType = DBusConnection.SESSION;
83 }
84 dBusConn = DBusConnection.getConnection(busType);
85 ts = dBusConn.getRemoteObject(
86 DbusConfig.SIGNAL_BUSNAME, DbusConfig.SIGNAL_OBJECTPATH,
87 Signal.class);
88 } catch (UnsatisfiedLinkError e) {
89 System.err.println("Missing native library dependency for dbus service: " + e.getMessage());
90 return 1;
91 } catch (DBusException e) {
92 e.printStackTrace();
93 if (dBusConn != null) {
94 dBusConn.disconnect();
95 }
96 return 3;
97 }
98 } else {
99 String dataPath = ns.getString("config");
100 if (isEmpty(dataPath)) {
101 dataPath = getDefaultDataPath();
102 }
103
104 m = new Manager(username, dataPath);
105 ts = m;
106 try {
107 m.init();
108 } catch (Exception e) {
109 System.err.println("Error loading state file: " + e.getMessage());
110 return 2;
111 }
112 }
113
114 String commandKey = ns.getString("command");
115 final Map<String, Command> commands = Commands.getCommands();
116 if (commands.containsKey(commandKey)) {
117 Command command = commands.get(commandKey);
118
119 if (dBusConn != null) {
120 if (command instanceof ExtendedDbusCommand) {
121 return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn);
122 } else if (command instanceof DbusCommand) {
123 return ((DbusCommand) command).handleCommand(ns, ts);
124 } else {
125 System.err.println(commandKey + " is not yet implemented via dbus");
126 return 1;
127 }
128 } else {
129 if (command instanceof LocalCommand) {
130 return ((LocalCommand) command).handleCommand(ns, m);
131 } else if (command instanceof DbusCommand) {
132 return ((DbusCommand) command).handleCommand(ns, ts);
133 } else {
134 System.err.println(commandKey + " is only works via dbus");
135 return 1;
136 }
137 }
138 }
139 return 0;
140 } finally {
141 if (dBusConn != null) {
142 dBusConn.disconnect();
143 }
144 }
145 }
146
147 /**
148 * Uses $XDG_DATA_HOME/signal-cli if it exists, or if none of the legacy directories exist:
149 * - $HOME/.config/signal
150 * - $HOME/.config/textsecure
151 *
152 * @return the data directory to be used by signal-cli.
153 */
154 private static String getDefaultDataPath() {
155 String dataPath = IOUtils.getDataHomeDir() + "/signal-cli";
156 if (new File(dataPath).exists()) {
157 return dataPath;
158 }
159
160 String legacySettingsPath = System.getProperty("user.home") + "/.config/signal";
161 if (new File(legacySettingsPath).exists()) {
162 return legacySettingsPath;
163 }
164
165 legacySettingsPath = System.getProperty("user.home") + "/.config/textsecure";
166 if (new File(legacySettingsPath).exists()) {
167 return legacySettingsPath;
168 }
169
170 return dataPath;
171 }
172
173 private static Namespace parseArgs(String[] args) {
174 ArgumentParser parser = ArgumentParsers.newFor("signal-cli")
175 .build()
176 .defaultHelp(true)
177 .description("Commandline interface for Signal.")
178 .version(BaseConfig.PROJECT_NAME + " " + BaseConfig.PROJECT_VERSION);
179
180 parser.addArgument("-v", "--version")
181 .help("Show package version.")
182 .action(Arguments.version());
183 parser.addArgument("--config")
184 .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli).");
185
186 MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup();
187 mut.addArgument("-u", "--username")
188 .help("Specify your phone number, that will be used for verification.");
189 mut.addArgument("--dbus")
190 .help("Make request via user dbus.")
191 .action(Arguments.storeTrue());
192 mut.addArgument("--dbus-system")
193 .help("Make request via system dbus.")
194 .action(Arguments.storeTrue());
195
196 Subparsers subparsers = parser.addSubparsers()
197 .title("subcommands")
198 .dest("command")
199 .description("valid subcommands")
200 .help("additional help");
201
202 final Map<String, Command> commands = Commands.getCommands();
203 for (Map.Entry<String, Command> entry : commands.entrySet()) {
204 Subparser subparser = subparsers.addParser(entry.getKey());
205 entry.getValue().attachToSubparser(subparser);
206 }
207
208 Namespace ns;
209 try {
210 ns = parser.parseArgs(args);
211 } catch (ArgumentParserException e) {
212 parser.handleError(e);
213 return null;
214 }
215
216 if ("link".equals(ns.getString("command"))) {
217 if (ns.getString("username") != null) {
218 parser.printUsage();
219 System.err.println("You cannot specify a username (phone number) when linking");
220 System.exit(2);
221 }
222 } else if (!ns.getBoolean("dbus") && !ns.getBoolean("dbus_system")) {
223 if (ns.getString("username") == null) {
224 parser.printUsage();
225 System.err.println("You need to specify a username (phone number)");
226 System.exit(2);
227 }
228 if (!PhoneNumberFormatter.isValidNumber(ns.getString("username"), null)) {
229 System.err.println("Invalid username (phone number), make sure you include the country code.");
230 System.exit(2);
231 }
232 }
233 if (ns.getList("recipient") != null && !ns.getList("recipient").isEmpty() && ns.getString("group") != null) {
234 System.err.println("You cannot specify recipients by phone number and groups at the same time");
235 System.exit(2);
236 }
237 return ns;
238 }
239 }