]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/dbus/DbusHandler.java
Extend getUserStatus command for usernames
[signal-cli] / src / main / java / org / asamk / signal / dbus / DbusHandler.java
1 package org.asamk.signal.dbus;
2
3 import org.asamk.signal.DbusConfig;
4 import org.asamk.signal.commands.exceptions.CommandException;
5 import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
6 import org.asamk.signal.commands.exceptions.UserErrorException;
7 import org.asamk.signal.manager.Manager;
8 import org.asamk.signal.manager.MultiAccountManager;
9 import org.freedesktop.dbus.connections.impl.DBusConnection;
10 import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
11 import org.freedesktop.dbus.exceptions.DBusException;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14
15 import java.util.ArrayList;
16 import java.util.List;
17
18 public class DbusHandler implements AutoCloseable {
19
20 private static final Logger logger = LoggerFactory.getLogger(DbusHandler.class);
21
22 private final boolean isDbusSystem;
23 private DBusConnection dBusConnection;
24 private final String busname;
25
26 private final List<AutoCloseable> closeables = new ArrayList<>();
27 private final DbusRunner dbusRunner;
28 private final boolean noReceiveOnStart;
29
30 public DbusHandler(
31 final boolean isDbusSystem, final String busname, final Manager m, final boolean noReceiveOnStart
32 ) {
33 this.isDbusSystem = isDbusSystem;
34 this.dbusRunner = (connection) -> {
35 try {
36 exportDbusObject(connection, DbusConfig.getObjectPath(), m).join();
37 } catch (InterruptedException ignored) {
38 }
39 };
40 this.noReceiveOnStart = noReceiveOnStart;
41 this.busname = busname;
42 }
43
44 public DbusHandler(
45 final boolean isDbusSystem,
46 final String busname,
47 final MultiAccountManager c,
48 final boolean noReceiveOnStart
49 ) {
50 this.isDbusSystem = isDbusSystem;
51 this.dbusRunner = (connection) -> {
52 final var signalControl = new DbusSignalControlImpl(c, DbusConfig.getObjectPath());
53 connection.exportObject(signalControl);
54
55 c.addOnManagerAddedHandler(m -> {
56 final var thread = exportManager(connection, m);
57 try {
58 thread.join();
59 } catch (InterruptedException ignored) {
60 }
61 });
62 c.addOnManagerRemovedHandler(m -> {
63 final var path = DbusConfig.getObjectPath(m.getSelfNumber());
64 try {
65 final var object = connection.getExportedObject(null, path);
66 if (object instanceof DbusSignalImpl dbusSignal) {
67 dbusSignal.close();
68 closeables.remove(dbusSignal);
69 }
70 } catch (DBusException ignored) {
71 }
72 });
73
74 final var initThreads = c.getManagers().stream().map(m -> exportManager(connection, m)).toList();
75
76 for (var t : initThreads) {
77 try {
78 t.join();
79 } catch (InterruptedException ignored) {
80 }
81 }
82 };
83 this.noReceiveOnStart = noReceiveOnStart;
84 this.busname = busname;
85 }
86
87 public void init() throws CommandException {
88 if (dBusConnection != null) {
89 throw new AssertionError("DbusHandler already initialized");
90 }
91 final var busType = isDbusSystem ? DBusConnection.DBusBusType.SYSTEM : DBusConnection.DBusBusType.SESSION;
92 logger.debug("Starting DBus server on {} bus: {}", busType, busname);
93 try {
94 dBusConnection = DBusConnectionBuilder.forType(busType).build();
95 dbusRunner.run(dBusConnection);
96 } catch (DBusException e) {
97 throw new UnexpectedErrorException("Dbus command failed: " + e.getMessage(), e);
98 } catch (UnsupportedOperationException e) {
99 throw new UserErrorException("Failed to connect to Dbus: " + e.getMessage(), e);
100 }
101
102 try {
103 dBusConnection.requestBusName(busname);
104 } catch (DBusException e) {
105 throw new UnexpectedErrorException("Dbus command failed, maybe signal-cli dbus daemon is already running: "
106 + e.getMessage(), e);
107 }
108
109 logger.info("Started DBus server on {} bus: {}", busType, busname);
110 }
111
112 @Override
113 public void close() throws Exception {
114 if (dBusConnection == null) {
115 return;
116 }
117 dBusConnection.close();
118 for (final var c : new ArrayList<>(closeables)) {
119 c.close();
120 }
121 closeables.clear();
122 dBusConnection = null;
123 }
124
125 private Thread exportDbusObject(final DBusConnection conn, final String objectPath, final Manager m) {
126 final var signal = new DbusSignalImpl(m, conn, objectPath, noReceiveOnStart);
127 closeables.add(signal);
128
129 return Thread.ofPlatform().name("dbus-init-" + m.getSelfNumber()).start(signal::initObjects);
130 }
131
132 private Thread exportManager(final DBusConnection conn, final Manager m) {
133 final var objectPath = DbusConfig.getObjectPath(m.getSelfNumber());
134 return exportDbusObject(conn, objectPath, m);
135 }
136
137 private interface DbusRunner {
138
139 void run(DBusConnection connection) throws DBusException;
140 }
141 }