mirror of
https://github.com/AsamK/signal-cli
synced 2025-09-03 20:50:38 +00:00
Added socket ipc and docker support
This commit is contained in:
parent
4177deccf1
commit
04a545b395
13 changed files with 757 additions and 4 deletions
|
@ -40,8 +40,12 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler
|
|||
result.putPOJO("envelope", new JsonMessageEnvelope(envelope, content));
|
||||
}
|
||||
try {
|
||||
jsonProcessor.writeValue(System.out, result);
|
||||
System.out.println();
|
||||
String output = jsonProcessor.writeValueAsString(result);
|
||||
if (m.socketTasks != null) {
|
||||
m.socketTasks.send(output);
|
||||
} else {
|
||||
System.out.println(output);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import net.sourceforge.argparse4j.inf.Subparser;
|
|||
import net.sourceforge.argparse4j.inf.Subparsers;
|
||||
|
||||
import org.asamk.Signal;
|
||||
import org.asamk.signal.storage.SignalAccount;
|
||||
import org.asamk.signal.commands.Command;
|
||||
import org.asamk.signal.commands.Commands;
|
||||
import org.asamk.signal.commands.DbusCommand;
|
||||
|
@ -36,6 +37,7 @@ import org.asamk.signal.dbus.DbusSignalImpl;
|
|||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.manager.ProvisioningManager;
|
||||
import org.asamk.signal.manager.ServiceConfig;
|
||||
import org.asamk.signal.manager.PathConfig;
|
||||
import org.asamk.signal.util.IOUtils;
|
||||
import org.asamk.signal.util.SecurityProvider;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
@ -73,7 +75,7 @@ public class Main {
|
|||
}
|
||||
|
||||
private static int handleCommands(Namespace ns) {
|
||||
final String username = ns.getString("username");
|
||||
String username = ns.getString("username");
|
||||
|
||||
if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) {
|
||||
try {
|
||||
|
@ -103,6 +105,16 @@ public class Main {
|
|||
dataPath = getDefaultDataPath();
|
||||
}
|
||||
|
||||
if (ns.getBoolean("singleuser")) {
|
||||
String completeDataPath = PathConfig.createDefault(dataPath).getDataPath();
|
||||
username = SignalAccount.getSingleUser(completeDataPath);
|
||||
if (username == null) {
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("Assuming username: " + username);
|
||||
ns.getAttrs().put("username", username);
|
||||
}
|
||||
|
||||
final SignalServiceConfiguration serviceConfiguration = ServiceConfig.createDefaultServiceConfiguration(BaseConfig.USER_AGENT);
|
||||
|
||||
if (username == null) {
|
||||
|
@ -234,6 +246,9 @@ public class Main {
|
|||
MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup();
|
||||
mut.addArgument("-u", "--username")
|
||||
.help("Specify your phone number, that will be used for verification.");
|
||||
mut.addArgument("--singleuser")
|
||||
.help("Can be used if only one user account exists on this machine.")
|
||||
.action(Arguments.storeTrue());
|
||||
mut.addArgument("--dbus")
|
||||
.help("Make request via user dbus.")
|
||||
.action(Arguments.storeTrue());
|
||||
|
@ -267,7 +282,7 @@ public class Main {
|
|||
System.err.println("You cannot specify a username (phone number) when linking");
|
||||
System.exit(2);
|
||||
}
|
||||
} else if (!ns.getBoolean("dbus") && !ns.getBoolean("dbus_system")) {
|
||||
} else if (!ns.getBoolean("dbus") && !ns.getBoolean("dbus_system") && !ns.getBoolean("singleuser")) {
|
||||
if (ns.getString("username") == null) {
|
||||
parser.printUsage();
|
||||
System.err.println("You need to specify a username (phone number)");
|
||||
|
|
|
@ -34,6 +34,7 @@ public class Commands {
|
|||
addCommand("updateProfile", new UpdateProfileCommand());
|
||||
addCommand("verify", new VerifyCommand());
|
||||
addCommand("uploadStickerPack", new UploadStickerPackCommand());
|
||||
addCommand("socket", new SocketCommand());
|
||||
}
|
||||
|
||||
public static Map<String, Command> getCommands() {
|
||||
|
|
65
src/main/java/org/asamk/signal/commands/SocketCommand.java
Normal file
65
src/main/java/org/asamk/signal/commands/SocketCommand.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
package org.asamk.signal.commands;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.io.IOException;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import net.sourceforge.argparse4j.inf.Subparser;
|
||||
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.JsonReceiveMessageHandler;
|
||||
import static org.asamk.signal.util.ErrorUtils.handleAssertionError;
|
||||
|
||||
import org.asamk.signal.socket.SocketTasks;
|
||||
|
||||
public class SocketCommand implements LocalCommand {
|
||||
|
||||
@Override
|
||||
public void attachToSubparser(final Subparser subparser) {
|
||||
subparser.addArgument("-a", "--address")
|
||||
.setDefault("127.0.0.1")
|
||||
.help("Socket bind address");
|
||||
subparser.addArgument("-p", "--port")
|
||||
.type(int.class)
|
||||
.setDefault(24250)
|
||||
.help("Socket port to use");
|
||||
subparser.help("Receive at a socket while being able to send as well");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int handleCommand(final Namespace ns, final Manager m) {
|
||||
if (!m.isRegistered()) {
|
||||
System.err.println("User is not registered.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
System.out.println("Starting socket thread...");
|
||||
m.socketTasks = new SocketTasks(ns, m);
|
||||
try {
|
||||
Thread thread = new Thread(m.socketTasks);
|
||||
thread.start();
|
||||
} catch (Exception e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
try {
|
||||
// Let the thread settle for a second
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (Exception e) {}
|
||||
System.out.println("Listening from Signal server...");
|
||||
|
||||
final long timeout = 3600 * 1000; // timeout in ms
|
||||
final boolean returnOnTimeout = false;
|
||||
final boolean ignoreAttachments = true;
|
||||
try {
|
||||
final Manager.ReceiveMessageHandler handler = new JsonReceiveMessageHandler(m);
|
||||
while(true) {
|
||||
m.receiveMessages(timeout, TimeUnit.MILLISECONDS, returnOnTimeout, ignoreAttachments, handler);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error while receiving messages: " + e.getMessage());
|
||||
return 3;
|
||||
} catch (AssertionError e) {
|
||||
handleAssertionError(e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -141,6 +141,8 @@ import java.util.stream.Collectors;
|
|||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.asamk.signal.socket.SocketTasks;
|
||||
|
||||
public class Manager implements Closeable {
|
||||
|
||||
private final SleepTimer timer = new UptimeSleepTimer();
|
||||
|
@ -153,6 +155,8 @@ public class Manager implements Closeable {
|
|||
private SignalServiceMessagePipe messagePipe = null;
|
||||
private SignalServiceMessagePipe unidentifiedMessagePipe = null;
|
||||
|
||||
public SocketTasks socketTasks = null;
|
||||
|
||||
public Manager(SignalAccount account, PathConfig pathConfig, SignalServiceConfiguration serviceConfiguration, String userAgent) {
|
||||
this.account = account;
|
||||
this.pathConfig = pathConfig;
|
||||
|
@ -163,6 +167,10 @@ public class Manager implements Closeable {
|
|||
this.account.setResolver(this::resolveSignalServiceAddress);
|
||||
}
|
||||
|
||||
public SignalAccount getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return account.getUsername();
|
||||
}
|
||||
|
|
287
src/main/java/org/asamk/signal/socket/Commander.java
Normal file
287
src/main/java/org/asamk/signal/socket/Commander.java
Normal file
|
@ -0,0 +1,287 @@
|
|||
package org.asamk.signal.socket;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import org.asamk.signal.storage.SignalAccount;
|
||||
import org.asamk.signal.storage.contacts.ContactInfo;
|
||||
import org.asamk.signal.storage.groups.GroupInfo;
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.util.Util;
|
||||
import org.asamk.signal.util.GroupIdFormatException;
|
||||
import org.asamk.signal.manager.GroupNotFoundException;
|
||||
import org.asamk.signal.manager.AttachmentInvalidException;
|
||||
import org.asamk.signal.manager.NotAGroupMemberException;
|
||||
import org.whispersystems.util.Base64;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptions;
|
||||
import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException;
|
||||
import org.asamk.signal.socket.SocketTasks;
|
||||
|
||||
public class Commander {
|
||||
private Manager m;
|
||||
private SocketTasks tasks;
|
||||
private ObjectMapper mapper;
|
||||
|
||||
public Commander(Manager manager, SocketTasks socketTasks) {
|
||||
this.m = manager;
|
||||
this.tasks = socketTasks;
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public void handleCommand(String command, JsonNode arguments) {
|
||||
switch (command) {
|
||||
case "getContacts":
|
||||
this.tasks.send(this.getContacts());
|
||||
break;
|
||||
case "updateContacts":
|
||||
this.updateContacts(arguments);
|
||||
break;
|
||||
case "sendMessage":
|
||||
this.sendMessage(arguments);
|
||||
break;
|
||||
case "trust":
|
||||
this.trust(arguments);
|
||||
break;
|
||||
case "endSession":
|
||||
this.endSession(arguments);
|
||||
break;
|
||||
case "getGroups":
|
||||
this.tasks.send(this.getGroups());
|
||||
break;
|
||||
default:
|
||||
this.tasks.send("Unknown command: '" + command + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private String getGroups() {
|
||||
ObjectNode node = this.mapper.createObjectNode();
|
||||
List<GroupInfo> groups = this.m.getAccount().getGroupStore().getGroups();
|
||||
for (GroupInfo g: groups) {
|
||||
ObjectNode group = this.mapper.createObjectNode();
|
||||
group.put("name", g.name);
|
||||
group.put("color", g.color);
|
||||
group.put("messageExpirationTime", g.messageExpirationTime);
|
||||
group.put("blocked", g.blocked);
|
||||
group.put("inboxPosition", g.inboxPosition);
|
||||
group.put("archived", g.archived);
|
||||
group.put("avatarId", g.getAvatarId());
|
||||
for (String m: g.getMembersE164()) {
|
||||
group.putArray("members").add(m);
|
||||
}
|
||||
node.set(Base64.encodeBytes(g.groupId), group);
|
||||
}
|
||||
String out = null;
|
||||
try {
|
||||
out = this.mapper.writeValueAsString(node); //writerWithDefaultPrettyPrinter()
|
||||
} catch (Exception e) {
|
||||
System.err.println("Could not send groups");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private void sendMessage(JsonNode args) {
|
||||
Iterator<Map.Entry<String,JsonNode>> entryIt = args.fields();
|
||||
Map.Entry<String,JsonNode> entry;
|
||||
String argument;
|
||||
String message = null;
|
||||
ArrayList<String> contacts = null;
|
||||
ArrayList<String> groups = null;
|
||||
while (entryIt.hasNext()) {
|
||||
entry = entryIt.next();
|
||||
argument = entry.getKey();
|
||||
switch(argument) {
|
||||
case "contacts":
|
||||
try {
|
||||
contacts = this.mapper.readValue(entry.getValue().toString(), new TypeReference<ArrayList<String>>(){});
|
||||
} catch (IOException e) {
|
||||
System.err.println("bad list of contacts");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "groups":
|
||||
try {
|
||||
groups = this.mapper.readValue(entry.getValue().toString(), new TypeReference<ArrayList<String>>(){});
|
||||
} catch (IOException e) {
|
||||
System.err.println("bad list of groups");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "message":
|
||||
message = entry.getValue().asText();
|
||||
if (message.trim().length() <= 0) {
|
||||
System.err.println("cannot send empty message");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contacts != null) {
|
||||
try {
|
||||
this.m.sendMessage(message, new ArrayList<>(), contacts);
|
||||
} catch (Exception | EncapsulatedExceptions | InvalidNumberException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
if (groups != null) {
|
||||
byte[] groupId;
|
||||
for (String group: groups) {
|
||||
try {
|
||||
groupId = Util.decodeGroupId(group);
|
||||
} catch (GroupIdFormatException e) {
|
||||
handleGroupIdFormatException(e);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
this.m.sendGroupMessage(message, new ArrayList<>(), groupId);
|
||||
} catch (IOException | EncapsulatedExceptions | GroupNotFoundException | AttachmentInvalidException | NotAGroupMemberException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void endSession(JsonNode args) {
|
||||
Iterator<Map.Entry<String,JsonNode>> entryIt = args.fields();
|
||||
Map.Entry<String,JsonNode> entry;
|
||||
String argument;
|
||||
ArrayList<String> contacts = null;
|
||||
while (entryIt.hasNext()) {
|
||||
entry = entryIt.next();
|
||||
argument = entry.getKey();
|
||||
switch(argument) {
|
||||
case "contacts":
|
||||
try {
|
||||
contacts = this.mapper.readValue(entry.getValue().toString(), new TypeReference<ArrayList<String>>(){});
|
||||
} catch (IOException e) {
|
||||
System.err.println("bad list of contacts");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contacts != null) {
|
||||
try {
|
||||
this.m.sendEndSessionMessage(contacts);
|
||||
System.err.println("WARNING: Ended session with " + String.join(", ", contacts));
|
||||
} catch (Exception | EncapsulatedExceptions | InvalidNumberException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void trust(JsonNode args) {
|
||||
Iterator<Map.Entry<String,JsonNode>> entryIt = args.fields();
|
||||
Map.Entry<String,JsonNode> entry;
|
||||
String argument;
|
||||
ArrayList<String> contacts = null;
|
||||
while (entryIt.hasNext()) {
|
||||
entry = entryIt.next();
|
||||
argument = entry.getKey();
|
||||
switch(argument) {
|
||||
case "contacts":
|
||||
try {
|
||||
contacts = this.mapper.readValue(entry.getValue().toString(), new TypeReference<ArrayList<String>>(){});
|
||||
} catch (IOException e) {
|
||||
System.err.println("bad list of contacts");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contacts != null) {
|
||||
for (String contact: contacts) {
|
||||
this.m.trustIdentityAllKeys(contact);
|
||||
}
|
||||
System.err.println("WARNING: Updated trust for all keys from " + String.join(", ", contacts));
|
||||
}
|
||||
}
|
||||
|
||||
private String getContacts() {
|
||||
ObjectNode node = this.mapper.createObjectNode();
|
||||
List<ContactInfo> contacts = this.m.getContacts();
|
||||
for (ContactInfo c: contacts) {
|
||||
ObjectNode group = this.mapper.createObjectNode();
|
||||
group.put("name", c.name);
|
||||
group.put("uuid", c.uuid.toString());
|
||||
group.put("color", c.color);
|
||||
group.put("messageExpirationTime", c.messageExpirationTime);
|
||||
group.put("profileKey", c.profileKey);
|
||||
group.put("blocked", c.blocked);
|
||||
group.put("inboxPosition", c.inboxPosition);
|
||||
group.put("archived", c.archived);
|
||||
node.set(c.number, group);
|
||||
}
|
||||
String out = null;
|
||||
try {
|
||||
out = this.mapper.writeValueAsString(node); //writerWithDefaultPrettyPrinter()
|
||||
} catch (Exception e) {
|
||||
System.err.println("Could not send contacts");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private void updateContacts(JsonNode args) {
|
||||
Iterator<Map.Entry<String,JsonNode>> entryIt = args.fields();
|
||||
Map.Entry<String,JsonNode> entry;
|
||||
String number;
|
||||
while (entryIt.hasNext()) {
|
||||
entry = entryIt.next();
|
||||
number = entry.getKey();
|
||||
ContactInfo contactInfo = this.m.getContact(number);
|
||||
if (contactInfo == null) {
|
||||
System.err.println("Could not update " + number);
|
||||
continue;
|
||||
} else {
|
||||
patchContactInfo(contactInfo, entry.getValue());
|
||||
SignalAccount account = this.m.getAccount();
|
||||
account.getContactStore().updateContact(contactInfo);
|
||||
account.save();
|
||||
System.out.println("Updated account info for " + number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void patchContactInfo(ContactInfo contactInfo, JsonNode args) {
|
||||
Iterator<Map.Entry<String,JsonNode>> entryIt = args.fields();
|
||||
Map.Entry<String,JsonNode> entry;
|
||||
String argument;
|
||||
JsonNode value;
|
||||
while (entryIt.hasNext()) {
|
||||
entry = entryIt.next();
|
||||
argument = entry.getKey();
|
||||
value = entry.getValue();
|
||||
switch (argument) {
|
||||
case "name":
|
||||
contactInfo.name = value.asText();
|
||||
break;
|
||||
case "color":
|
||||
contactInfo.color = value.asText();
|
||||
break;
|
||||
case "messageExpirationTime":
|
||||
contactInfo.messageExpirationTime = value.asInt();
|
||||
break;
|
||||
case "blocked":
|
||||
contactInfo.blocked = value.asBoolean();
|
||||
break;
|
||||
case "inboxPosition":
|
||||
contactInfo.inboxPosition = value.asInt();
|
||||
break;
|
||||
case "archived":
|
||||
contactInfo.archived = value.asBoolean();
|
||||
break;
|
||||
default:
|
||||
System.err.println("Invalid field '" + argument + "' in " + contactInfo.number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
102
src/main/java/org/asamk/signal/socket/SocketTasks.java
Normal file
102
src/main/java/org/asamk/signal/socket/SocketTasks.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
package org.asamk.signal.socket;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.socket.Commander;
|
||||
|
||||
public class SocketTasks implements Runnable {
|
||||
private String address;
|
||||
private int port;
|
||||
private Socket socket;
|
||||
private OutputStream output = null;
|
||||
private ObjectMapper mapper;
|
||||
private Commander commander;
|
||||
|
||||
public SocketTasks(Namespace namespace, Manager manager) {
|
||||
this.address = namespace.getString("address");
|
||||
this.port = namespace.getInt("port");
|
||||
this.mapper = new ObjectMapper();
|
||||
this.commander = new Commander(manager, this);
|
||||
}
|
||||
|
||||
public void send(String message) {
|
||||
if (message == null) return;
|
||||
if (this.output == null || this.socket.isClosed()) {
|
||||
System.err.println("WARNING: a message is ready but the socket isn't connected");
|
||||
return;
|
||||
}
|
||||
message += "\n";
|
||||
try {
|
||||
this.output.write(message.getBytes(Charset.forName("UTF-8")));
|
||||
this.output.flush();
|
||||
} catch (SocketException e) {
|
||||
System.err.println("Output socket connection lost!");
|
||||
this.output = null;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
ServerSocket serverSocket = new ServerSocket(this.port, 1, InetAddress.getByName(this.address));
|
||||
System.out.println("Socket ready, binding to address: " + this.address + ":" + this.port);
|
||||
this.socket = serverSocket.accept(); // accept() blocks
|
||||
this.output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input));
|
||||
System.out.println("Connection established.");
|
||||
while (this.responseProcessor(bufferedReader.readLine())); // readLine() blocks
|
||||
|
||||
bufferedReader.close();
|
||||
this.output.close();
|
||||
input.close();
|
||||
this.socket.close();
|
||||
serverSocket.close();
|
||||
} catch (Exception e) { System.err.println(e); }
|
||||
System.err.println("Socket connection lost.");
|
||||
try {
|
||||
// Help relax potential resource hogging and log spam
|
||||
TimeUnit.SECONDS.sleep(3);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean responseProcessor(String line) {
|
||||
if (line == null) return false;
|
||||
JsonNode node = null;
|
||||
try {
|
||||
node = this.mapper.readValue(line.trim(), JsonNode.class);
|
||||
} catch (Exception e) { System.err.println(e); }
|
||||
if (node == null) return true;
|
||||
String reply = null;
|
||||
Iterator<Map.Entry<String,JsonNode>> entryIt = node.fields();
|
||||
Map.Entry<String,JsonNode> entry;
|
||||
String command;
|
||||
while (entryIt.hasNext()) {
|
||||
entry = entryIt.next();
|
||||
command = entry.getKey();
|
||||
this.commander.handleCommand(command, entry.getValue());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -152,6 +152,26 @@ public class SignalAccount implements Closeable {
|
|||
return !(!f.exists() || f.isDirectory());
|
||||
}
|
||||
|
||||
public static String getSingleUser(String dataPath) {
|
||||
File folder = new File(dataPath);
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
if (listOfFiles == null || listOfFiles.length <= 0) {
|
||||
System.err.println("No user account found");
|
||||
return null;
|
||||
}
|
||||
String user = null;
|
||||
for (int i = 0; i < listOfFiles.length; i++) {
|
||||
if (listOfFiles[i].isFile()) {
|
||||
if (user != null) {
|
||||
System.err.println("Too many user accounts found");
|
||||
return null; // too many users
|
||||
}
|
||||
user = listOfFiles[i].getName();
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private void load() throws IOException {
|
||||
JsonNode rootNode;
|
||||
synchronized (fileChannel) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue