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