]>
Commit | Line | Data |
---|---|---|
562c96c0 BM |
1 | <?php |
2 | ||
50d6d455 | 3 | namespace UnrealIRCd; |
562c96c0 | 4 | |
8d5a112f | 5 | use Exception; |
562c96c0 BM |
6 | use WebSocket; |
7 | ||
8 | class Connection | |
9 | { | |
50d6d455 D |
10 | protected WebSocket\Client $connection; |
11 | ||
1a3fda20 BM |
12 | public $errno = 0; |
13 | public $error = NULL; | |
14 | ||
a18591f9 | 15 | public function __construct(string $uri, string $api_login, array $options = null) |
50d6d455 D |
16 | { |
17 | $context = $options["context"] ?? stream_context_create(); | |
18 | ||
8d5a112f | 19 | if (isset($options["tls_verify"]) && !$options["tls_verify"]) { |
50d6d455 D |
20 | stream_context_set_option($context, 'ssl', 'verify_peer', false); |
21 | stream_context_set_option($context, 'ssl', 'verify_peer_name', false); | |
22 | } | |
23 | ||
24 | $this->connection = new WebSocket\Client($uri, [ | |
25 | 'context' => $context, | |
26 | 'headers' => [ | |
27 | 'Authorization' => sprintf('Basic %s', base64_encode($api_login)), | |
28 | ], | |
29 | 'timeout' => 10, | |
30 | ]); | |
31 | ||
57c1d592 BM |
32 | /* Start the connection now */ |
33 | $this->connection->ping(); | |
50d6d455 D |
34 | } |
35 | ||
8d5a112f D |
36 | /** |
37 | * Encode and send a query to the RPC server. | |
38 | * | |
39 | * @note I'm not sure on the response type except that it may be either an object or array. | |
40 | * | |
41 | * @param string $method | |
a18591f9 D |
42 | * @param array|null $params |
43 | * | |
8d5a112f D |
44 | * @return object|array|bool |
45 | * @throws Exception | |
46 | */ | |
a18591f9 | 47 | public function query(string $method, array|null $params = null): object|array|bool |
50d6d455 | 48 | { |
8d5a112f D |
49 | $id = random_int(1, 99999); |
50 | ||
50d6d455 D |
51 | $rpc = [ |
52 | "jsonrpc" => "2.0", | |
53 | "method" => $method, | |
54 | "params" => $params, | |
8d5a112f | 55 | "id" => $id |
50d6d455 D |
56 | ]; |
57 | ||
58 | $json_rpc = json_encode($rpc); | |
50d6d455 | 59 | $this->connection->text($json_rpc); |
a3518a0a BM |
60 | |
61 | do { | |
62 | $reply = $this->connection->receive(); | |
63 | ||
64 | $reply = json_decode($reply); | |
65 | ||
66 | if (property_exists($reply, 'result')) { | |
67 | /* Possibly we are streaming log events, then ignore this event | |
68 | * NOTE: This does mean that this event is "lost" | |
69 | */ | |
70 | if ($id !== $reply->id) | |
71 | continue; | |
72 | $this->errno = 0; | |
73 | $this->error = NULL; | |
74 | return $reply->result; | |
75 | } | |
76 | ||
77 | if (property_exists($reply, 'error')) { | |
78 | $this->errno = $reply->error->code; | |
79 | $this->error = $reply->error->message; | |
80 | return false; | |
81 | } | |
82 | } while(0); | |
83 | ||
84 | /* This should never happen */ | |
85 | throw new Exception('Invalid JSON-RPC response from UnrealIRCd: not an error and not a result.'); | |
86 | } | |
87 | ||
88 | /** | |
89 | * Grab and/or wait for next event. Used for log streaming. | |
90 | * @note This function will return NULL after a 10 second timeout, | |
91 | * this so the function is not entirely blocking. You can safely | |
92 | * retry the operation if the return value === NULL. | |
93 | * | |
94 | * @return object|array|bool|null | |
95 | * @throws Exception | |
96 | */ | |
97 | public function eventloop(): object|array|bool|null | |
98 | { | |
49faf2cf | 99 | $starttime = microtime(true); |
a3518a0a BM |
100 | try { |
101 | $reply = $this->connection->receive(); | |
49faf2cf BM |
102 | } catch (WebSocket\TimeoutException $e) { |
103 | if (microtime(true) - $starttime < 1) | |
104 | { | |
105 | /* There's some bug in the library: if we | |
106 | * caught the timeout exception once (so | |
107 | * harmless) and then later the server gets | |
108 | * killed or closes the connection otherwise, | |
109 | * then it will again throw WebSocket\TimeoutException | |
110 | * even though it has nothing to do with timeouts. | |
111 | * We detect this by checking if the timeout | |
112 | * took less than 1 second, then we know for sure | |
113 | * that it wasn't really a timeout (since the | |
114 | * timeout is normally 10 seconds). | |
115 | */ | |
116 | throw $e; | |
117 | } | |
a3518a0a BM |
118 | return NULL; |
119 | } | |
50d6d455 D |
120 | |
121 | $reply = json_decode($reply); | |
122 | ||
65cc0295 | 123 | if (property_exists($reply, 'result')) { |
1a3fda20 BM |
124 | $this->errno = 0; |
125 | $this->error = NULL; | |
657dad63 | 126 | return $reply->result; |
50d6d455 | 127 | } |
bea99f3a | 128 | |
a3518a0a | 129 | /* This would be weird */ |
1a3fda20 BM |
130 | if (property_exists($reply, 'error')) { |
131 | $this->errno = $reply->error->code; | |
132 | $this->error = $reply->error->message; | |
133 | return false; | |
bea99f3a D |
134 | } |
135 | ||
1a3fda20 | 136 | /* This should never happen */ |
a3518a0a | 137 | throw new Exception('Invalid JSON-RPC data from UnrealIRCd: not an error and not a result.'); |
50d6d455 | 138 | } |
7c7017a2 | 139 | |
c6e7dd34 BM |
140 | public function rpc(): Rpc |
141 | { | |
142 | return new Rpc($this); | |
143 | } | |
144 | ||
1ef2e09f BM |
145 | public function stats(): Stats |
146 | { | |
147 | return new Stats($this); | |
148 | } | |
149 | ||
7c7017a2 BM |
150 | public function user(): User |
151 | { | |
152 | return new User($this); | |
153 | } | |
154 | ||
155 | public function channel(): Channel | |
156 | { | |
157 | return new Channel($this); | |
158 | } | |
159 | ||
e4de690d | 160 | public function serverban(): ServerBan |
7c7017a2 | 161 | { |
e4de690d | 162 | return new ServerBan($this); |
7c7017a2 BM |
163 | } |
164 | ||
165 | public function spamfilter(): Spamfilter | |
166 | { | |
167 | return new Spamfilter($this); | |
168 | } | |
1ef2e09f | 169 | |
8fd8402e VP |
170 | public function nameban(): NameBan |
171 | { | |
172 | return new NameBan($this); | |
173 | } | |
1ef2e09f | 174 | |
1d1d6c90 VP |
175 | public function server(): Server |
176 | { | |
177 | return new Server($this); | |
178 | } | |
1ef2e09f | 179 | |
65b6ddcb | 180 | public function serverbanexception(): ServerBanException |
9d9157c2 | 181 | { |
9ff6e559 | 182 | return new ServerBanException($this); |
9d9157c2 | 183 | } |
1ef2e09f | 184 | |
a3518a0a BM |
185 | public function log(): Log |
186 | { | |
187 | return new Log($this); | |
188 | } | |
562c96c0 | 189 | } |