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