1 package org
.asamk
.signal
.jsonrpc
;
3 import com
.fasterxml
.jackson
.core
.TreeNode
;
4 import com
.fasterxml
.jackson
.databind
.JsonMappingException
;
5 import com
.fasterxml
.jackson
.databind
.JsonNode
;
6 import com
.fasterxml
.jackson
.databind
.ObjectMapper
;
7 import com
.fasterxml
.jackson
.databind
.node
.ContainerNode
;
9 import org
.asamk
.signal
.JsonReceiveMessageHandler
;
10 import org
.asamk
.signal
.JsonWriter
;
11 import org
.asamk
.signal
.OutputWriter
;
12 import org
.asamk
.signal
.commands
.Commands
;
13 import org
.asamk
.signal
.commands
.JsonRpcCommand
;
14 import org
.asamk
.signal
.commands
.exceptions
.CommandException
;
15 import org
.asamk
.signal
.commands
.exceptions
.IOErrorException
;
16 import org
.asamk
.signal
.commands
.exceptions
.UntrustedKeyErrorException
;
17 import org
.asamk
.signal
.commands
.exceptions
.UserErrorException
;
18 import org
.asamk
.signal
.manager
.Manager
;
19 import org
.asamk
.signal
.util
.Util
;
20 import org
.slf4j
.Logger
;
21 import org
.slf4j
.LoggerFactory
;
23 import java
.io
.IOException
;
25 import java
.util
.function
.Supplier
;
27 public class SignalJsonRpcDispatcherHandler
{
29 private final static Logger logger
= LoggerFactory
.getLogger(SignalJsonRpcDispatcherHandler
.class);
31 private static final int USER_ERROR
= -1;
32 private static final int IO_ERROR
= -3;
33 private static final int UNTRUSTED_KEY_ERROR
= -4;
35 private final Manager m
;
36 private final JsonWriter outputWriter
;
37 private final Supplier
<String
> lineSupplier
;
39 public SignalJsonRpcDispatcherHandler(
40 final Manager m
, final JsonWriter outputWriter
, final Supplier
<String
> lineSupplier
43 this.outputWriter
= outputWriter
;
44 this.lineSupplier
= lineSupplier
;
47 public void handleConnection() {
48 final var objectMapper
= Util
.createJsonObjectMapper();
49 final var jsonRpcSender
= new JsonRpcSender(outputWriter
);
51 final var receiveMessageHandler
= new JsonReceiveMessageHandler(m
,
52 s
-> jsonRpcSender
.sendRequest(JsonRpcRequest
.forNotification("receive",
53 objectMapper
.valueToTree(s
),
56 m
.addReceiveHandler(receiveMessageHandler
);
58 // Maybe this should be handled inside the Manager
59 while (!m
.hasCaughtUpWithOldMessages()) {
64 } catch (InterruptedException ignored
) {
68 final var jsonRpcReader
= new JsonRpcReader(jsonRpcSender
, lineSupplier
);
69 jsonRpcReader
.readRequests((method
, params
) -> handleRequest(m
, objectMapper
, method
, params
),
70 response
-> logger
.debug("Received unexpected response for id {}", response
.getId()));
72 m
.removeReceiveHandler(receiveMessageHandler
);
76 private JsonNode
handleRequest(
77 final Manager m
, final ObjectMapper objectMapper
, final String method
, ContainerNode
<?
> params
78 ) throws JsonRpcException
{
79 final Object
[] result
= {null};
80 final JsonWriter commandOutputWriter
= s
-> {
81 if (result
[0] != null) {
82 throw new AssertionError("Command may only write one json result");
88 var command
= Commands
.getCommand(method
);
89 if (!(command
instanceof JsonRpcCommand
)) {
90 throw new JsonRpcException(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.METHOD_NOT_FOUND
,
91 "Method not implemented",
96 parseParamsAndRunCommand(m
, objectMapper
, params
, commandOutputWriter
, (JsonRpcCommand
<?
>) command
);
97 } catch (JsonMappingException e
) {
98 throw new JsonRpcException(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INVALID_REQUEST
,
101 } catch (UserErrorException e
) {
102 throw new JsonRpcException(new JsonRpcResponse
.Error(USER_ERROR
, e
.getMessage(), null));
103 } catch (IOErrorException e
) {
104 throw new JsonRpcException(new JsonRpcResponse
.Error(IO_ERROR
, e
.getMessage(), null));
105 } catch (UntrustedKeyErrorException e
) {
106 throw new JsonRpcException(new JsonRpcResponse
.Error(UNTRUSTED_KEY_ERROR
, e
.getMessage(), null));
107 } catch (Throwable e
) {
108 logger
.error("Command execution failed", e
);
109 throw new JsonRpcException(new JsonRpcResponse
.Error(JsonRpcResponse
.Error
.INTERNAL_ERROR
,
114 Object output
= result
[0] == null ? Map
.of() : result
[0];
115 return objectMapper
.valueToTree(output
);
118 private <T
> void parseParamsAndRunCommand(
120 final ObjectMapper objectMapper
,
121 final TreeNode params
,
122 final OutputWriter outputWriter
,
123 final JsonRpcCommand
<T
> command
124 ) throws CommandException
, JsonMappingException
{
125 T requestParams
= null;
126 final var requestType
= command
.getRequestType();
127 if (params
!= null && requestType
!= null) {
129 requestParams
= objectMapper
.readValue(objectMapper
.treeAsTokens(params
), requestType
);
130 } catch (JsonMappingException e
) {
132 } catch (IOException e
) {
133 throw new AssertionError(e
);
136 command
.handleCommand(requestParams
, m
, outputWriter
);