Passed
Pull Request — master (#668)
by
unknown
01:39
created

Gateway::send()   D

Complexity

Conditions 21
Paths 12

Size

Total Lines 83
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 462

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 52
c 2
b 0
f 1
dl 0
loc 83
ccs 0
cts 64
cp 0
rs 4.1666
cc 21
nc 12
nop 3
crap 462

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * SPDX-FileCopyrightText: 2024 Christoph Wurst <[email protected]>
7
 * SPDX-License-Identifier: AGPL-3.0-or-later
8
 */
9
10
namespace OCA\TwoFactorGateway\Provider\Channel\Signal;
11
12
use OCA\TwoFactorGateway\Exception\MessageTransmissionException;
13
use OCA\TwoFactorGateway\Provider\Gateway\AGateway;
14
use OCP\AppFramework\Utility\ITimeFactory;
15
use OCP\Http\Client\IClientService;
16
use OCP\IAppConfig;
17
use Psr\Log\LoggerInterface;
18
use Symfony\Component\Console\Helper\QuestionHelper;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Question\Question;
22
23
/**
24
 * An integration of https://gitlab.com/morph027/signal-web-gateway
25
 *
26
 * @method string getUrl()
27
 * @method AGateway setUrl(string $url)
28
 * @method string getAccount()
29
 * @method AGateway setAccount(string $account)
30
 */
31
class Gateway extends AGateway {
32
	public const SCHEMA = [
33
		'name' => 'Signal',
34
		'instructions' => 'The gateway can send authentication to your Signal mobile and deskop app.',
35
		'fields' => [
36
			['field' => 'url', 'prompt' => 'Please enter the URL of the Signal gateway (leave blank to use default):', 'default' => 'http://localhost:5000'],
37
			['field' => 'account', 'prompt' => 'Please enter the account (phone-number) of the sending signal account (leave blank if a phone-number is not required):', 'default' => ''],
38
		],
39
	];
40
41
	public function __construct(
42
		public IAppConfig $appConfig,
43
		private IClientService $clientService,
44
		private ITimeFactory $timeFactory,
45
		private LoggerInterface $logger,
46
	) {
47
		parent::__construct($appConfig);
48
	}
49
50
	#[\Override]
51
	public function send(string $identifier, string $message, array $extra = []): void {
52
		$client = $this->clientService->newClient();
53
		// determine type of gateway
54
55
		// test for native signal-cli JSON RPC.
56
		$response = $client->post(
57
			$this->getUrl() . '/api/v1/rpc',
58
			[
59
				'http_errors' => false,
60
				'json' => [
61
					'jsonrpc' => '2.0',
62
					'method' => 'version',
63
					'id' => 'version_' . $this->timeFactory->getTime(),
64
				],
65
			]);
66
		if ($response->getStatusCode() === 200 || $response->getStatusCode() === 201) {
67
			// native signal-cli JSON RPC.
68
69
			// Groups have to be detected and passed with the "group-id" parameter. We assume a group is given as base64 encoded string
70
			$groupId = base64_decode($identifier, strict: true);
71
			$isGroup = $groupId !== false && base64_encode($groupId) === $identifier;
72
			$recipientKey = $isGroup ? 'group-id' : 'recipient';
73
			$params = [
74
				'message' => $message,
75
				$recipientKey => $identifier,
76
				'account' => $this->getAccount(), // mandatory for native RPC API
77
			];
78
			$response = $response = $client->post(
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
79
				$this->getUrl() . '/api/v1/rpc',
80
				[
81
					'json' => [
82
						'jsonrpc' => '2.0',
83
						'method' => 'send',
84
						'id' => 'code_' . $this->timeFactory->getTime(),
85
						'params' => $params,
86
					],
87
				]);
88
			$body = $response->getBody();
89
			$json = json_decode($body, true);
0 ignored issues
show
Bug introduced by
It seems like $body can also be of type null and resource; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

89
			$json = json_decode(/** @scrutinizer ignore-type */ $body, true);
Loading history...
90
			$statusCode = $response->getStatusCode();
91
			// The 201 "created" is probably a bug.
92
			if ($statusCode < 200 || $statusCode >= 300 || is_null($json) || !is_array($json) || ($json['jsonrpc'] ?? null) != '2.0' || !isset($json['result']['timestamp'])) {
93
				throw new MessageTransmissionException("error reported by Signal gateway, status=$statusCode, body=$body}");
94
			}
95
		} else {
96
			// Try gateway in the style of https://gitlab.com/morph027/signal-cli-dbus-rest-api
97
			$response = $client->get($this->getUrl() . '/v1/about');
98
			if ($response->getStatusCode() === 200) {
99
				// Not so "ńew style" gateway, see
100
				// https://gitlab.com/morph027/signal-cli-dbus-rest-api
101
				// https://gitlab.com/morph027/python-signal-cli-rest-api
102
				// https://github.com/bbernhard/signal-cli-rest-api
103
				$response = $client->post(
104
					$this->getUrl() . '/v1/send/' . $identifier,
105
					[
106
						'json' => [ 'message' => $message ],
107
					]
108
				);
109
				$body = (string)$response->getBody();
110
				$json = json_decode($body, true);
111
				$status = $response->getStatusCode();
112
				if ($status !== 201 || is_null($json) || !is_array($json) || !isset($json['timestamp'])) {
113
					throw new MessageTransmissionException("error reported by Signal gateway, status=$status, body=$body}");
114
				}
115
			} else {
116
				// Try old deprecated gateway https://gitlab.com/morph027/signal-web-gateway
117
				$response = $client->post(
118
					$this->getUrl() . '/v1/send/' . $identifier,
119
					[
120
						'body' => [
121
							'to' => $identifier,
122
							'message' => $message,
123
						],
124
						'json' => [ 'message' => $message ],
125
					]
126
				);
127
				$body = (string)$response->getBody();
128
				$json = json_decode($body, true);
129
130
				$status = $response->getStatusCode();
131
				if ($status !== 200 || is_null($json) || !is_array($json) || !isset($json['success']) || $json['success'] !== true) {
132
					throw new MessageTransmissionException("error reported by Signal gateway, status=$status, body=$body}");
133
				}
134
			}
135
		}
136
	}
137
138
	#[\Override]
139
	public function cliConfigure(InputInterface $input, OutputInterface $output): int {
140
		$helper = new QuestionHelper();
141
		$urlQuestion = new Question(self::SCHEMA['fields'][0]['prompt'], self::SCHEMA['fields'][0]['default']);
142
		$url = $helper->ask($input, $output, $urlQuestion);
143
		$output->writeln("Using $url.");
144
145
		$this->setUrl($url);
146
147
		$accountQuestion = new Question(self::SCHEMA['fields'][1]['prompt'], self::SCHEMA['fields'][1]['default']);
148
		$account = $helper->ask($input, $output, $accountQuestion);
149
		if ($account == '') {
150
			$account = SignalConfig::ACCOUNT_UNNECESSARY;
0 ignored issues
show
Bug introduced by
The type OCA\TwoFactorGateway\Pro...nel\Signal\SignalConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
151
			$output->writeln("A signal account is not needed, assuming it is hardcoded into the signal gateway server.");
152
		} else {
153
			$output->writeln("Using $account.");
154
		}
155
156
		$this->setAccount($account);
157
158
		return 0;
159
	}
160
}
161