Completed
Push — master ( 77e6a8...f7b062 )
by smiley
02:40
created

CLIRunner::getKeypair()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 8
Ratio 47.06 %
Metric Value
dl 8
loc 17
rs 8.8571
cc 5
eloc 10
nc 4
nop 2
1
<?php
2
/**
3
 * Class CLIRunner
4
 *
5
 * @filesource   CLIRunner.php
6
 * @created      01.04.2016
7
 * @package      chillerlan\Threema
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2016 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\Threema;
14
15
use chillerlan\Threema\Crypto\CryptoInterface;
16
use ReflectionClass;
17
use ReflectionMethod;
18
use stdClass;
19
20
/**
21
 *
22
 */
23
class CLIRunner implements CLIRunnerInterface{
24
25
	/**
26
	 * @var array
27
	 */
28
	const COMMANDS = [
29
		// local
30
		'keypair'    => 'getKeypair',
31
		'hash_email' => 'hashEmail',
32
		'hash_phone' => 'hashPhone',
33
#		'encrypt'      => '',
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
34
#		'decrypt'      => '',
35
		// network
36
		'credits'    => 'checkCredits',
37
		'check'      => 'checkCapabilities',
38
		'email2id'   => 'getIdByEmail',
39
		'phone2id'   => 'getIdByPhone',
40
		'id2pubkey'  => 'getPubkeyById',
41
#		'send'         => '',
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
42
#		'sende2e'      => '',
43
#		'sendimage'    => '',
44
#		'sendfile'     => '',
45
#		'receive'      => '',
46
		];
47
48
	/**
49
	 * @var \chillerlan\Threema\Crypto\CryptoInterface
50
	 */
51
	protected $cryptoInterface;
52
53
	/**
54
	 * @var \chillerlan\Threema\GatewayOptions
55
	 */
56
	protected $gatewayOptions;
57
58
	/**
59
	 * @var \chillerlan\Threema\Gateway
60
	 */
61
	protected $threemaGateway;
62
63
	/**
64
	 * @var \ReflectionClass
65
	 */
66
	protected $reflection;
67
68
	/**
69
	 * @var array[\ReflectionMethod]
70
	 */
71
	private $CLIRunnerInterfaceMap;
72
73
	/**
74
	 * CLIRunner constructor.
75
	 *
76
	 * @param \chillerlan\Threema\Crypto\CryptoInterface $cryptoInterface
77
	 * @param \chillerlan\Threema\GatewayOptions         $gatewayOptions
78
	 */
79
	public function __construct(CryptoInterface $cryptoInterface, GatewayOptions $gatewayOptions){
80
		$this->cryptoInterface = $cryptoInterface;
81
		$this->gatewayOptions  = $gatewayOptions;
82
		$this->threemaGateway  = new Gateway($this->cryptoInterface, $gatewayOptions);
83
		$this->reflection      = new ReflectionClass(CLIRunnerInterface::class);
84
85
		foreach($this->reflection->getMethods() as $method){
86
			$this->CLIRunnerInterfaceMap[$method->name] = $method;
87
		}
88
	}
89
90
	/**
91
	 * @param array $arguments $_SERVER['argc']
92
	 *
93
	 * @return string
94
	 */
95
	public function run(array $arguments):string{
96
		/** @noinspection PhpUnusedLocalVariableInspection */
97
		$scriptName = basename(array_shift($arguments));
0 ignored issues
show
Unused Code introduced by
$scriptName is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
98
		$command    = strtolower(array_shift($arguments));
99
100
		if(array_key_exists($command, self::COMMANDS) && array_key_exists(self::COMMANDS[$command], $this->CLIRunnerInterfaceMap)){
101
			try{
102
				$method = $this->CLIRunnerInterfaceMap[self::COMMANDS[$command]];
103
				$method = new ReflectionMethod($this, $method->name);
104
105
				// @todo: check method arguments
106
				return $this->log2cli($method->invokeArgs($this, $arguments));
107
			}
108
			catch(GatewayException $gatewayException){
109
				return $this->log2cli('ERROR: '.$gatewayException->getMessage());
110
			}
111
		}
112
113
		return $this->log2cli($this->help());
114
	}
115
116
	/**
117
	 * output a string, wrap at 100 chars
118
	 *
119
	 * @param string $string string to output
120
	 *
121
	 * @return string
122
	 */
123
	protected function log2cli(string $string):string{
124
		return PHP_EOL.wordwrap($string, 78, PHP_EOL).PHP_EOL;
125
	}
126
127
	/**
128
	 * @return string
129
	 * @codeCoverageIgnore
130
	 */
131
	protected function readStdIn(){
132
		$stdin = fopen('php://stdin', 'r');
133
		$lines = [];
134
135
		while($line = trim(fgets($stdin))){
136
137
			if(strlen($line) === 0 || $line === "\n"){
138
				continue;
139
			}
140
141
			$lines[] = $line;
142
		}
143
144
		return implode("\n", $lines);
145
	}
146
147
	/**
148
	 * @param array $params
149
	 *
150
	 * @return \stdClass
151
	 */
152
	protected function parseDocBlock(array $params):stdClass{
153
		$parsed             = new stdClass;
154
		$parsed->paramNames = [];
155
		$parsed->paramDoc   = [];
156
		$parsed->returnDoc  = '';
157
158
		if(empty($params)){
159
			return $parsed; // @codeCoverageIgnore
160
		}
161
162
		foreach($params as $p){
163
			$p = explode(' ', $p, 2);
164
			if(isset($p[1])){
165
				if($p[0] === 'param'){
166
					$p                    = (explode(' ', trim($p[1]), 3));
167
					$name                 = '<'.trim($p[1], ' $').'>';
168
					$parsed->paramNames[] = $name;
169
					$doc                  = isset($p[2]) ? $name.' '.trim($p[2]) : $name;
170
					$parsed->paramDoc[]   = $doc;
171
				}
172
				else if($p[0] === 'return'){
173
					$p = explode(' ', trim($p[1]), 2);
174
					$parsed->returnDoc .= isset($p[1]) ? trim($p[1]) : '';
175
				}
176
			}
177
		}
178
179
		$parsed->paramNames = implode(' ', $parsed->paramNames);
180
		$parsed->paramDoc   = implode(PHP_EOL, $parsed->paramDoc);
181
182
		return $parsed;
183
	}
184
185
	/**
186
	 * @return string
187
	 */
188
	public function help():string{
189
		// return info in case no command was found
190
		$help = 'Threema Gateway CLI tool.'.PHP_EOL;
191
		$help .= 'Crypto: '.$this->threemaGateway->cryptoVersion().PHP_EOL.PHP_EOL;
192
193
		foreach(self::COMMANDS as $command => $method){
194
			$comment = $this->reflection->getMethod($method)->getDocComment();
195
			$comment = str_replace(["\t", ' *'], '', substr($comment, 3, -2));
196
197
			$params  = explode('@', $comment);
198
			$comment = trim(array_shift($params));
199
			$parsed  = $this->parseDocBlock($params);
200
201
			$help .= PHP_EOL.'threema.php '.$command.' '.$parsed->paramNames.PHP_EOL;
202
			$help .= str_repeat('-', strlen($command) + 12).PHP_EOL;
203
			$help .= PHP_EOL.$comment.PHP_EOL;
204
			$help .= PHP_EOL.$parsed->paramDoc.PHP_EOL;
205
			$help .= PHP_EOL.'Returns: '.$parsed->returnDoc.PHP_EOL.PHP_EOL;
206
		}
207
208
		return $help;
209
	}
210
211
	/**
212
	 * @inheritdoc
213
	 */
214
	public function getKeypair(string $privateKeyFile = null, string $publicKeyFile = null):string{
215
		$keypair = $this->cryptoInterface->getKeypair();
216
		$message = '';
217
218
		// @todo: check writable
219 View Code Duplication
		if(!empty($privateKeyFile) && is_dir(dirname($privateKeyFile))){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
			file_put_contents($privateKeyFile, $keypair->privateKey);
221
			$message .= 'Private key saved to: '.$privateKeyFile.PHP_EOL;
222
		}
223
224 View Code Duplication
		if(!empty($publicKeyFile) && is_dir(dirname($publicKeyFile))){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
			file_put_contents($publicKeyFile, $keypair->publicKey);
226
			$message .= 'Public key saved to: '.$publicKeyFile.PHP_EOL;
227
		}
228
229
		return $message.PHP_EOL.'private:'.$keypair->privateKey.PHP_EOL.'public:'.$keypair->publicKey;
230
	}
231
232
	/**
233
	 * @inheritdoc
234
	 */
235
	public function hashEmail(string $email):string{
236
		return $this->cryptoInterface->hmac_hash($email, GatewayInterface::HMAC_KEY_EMAIL_BIN);
237
	}
238
239
	/**
240
	 * @inheritdoc
241
	 */
242
	public function hashPhone(string $phoneNo):string{
243
		return $this->cryptoInterface->hmac_hash($phoneNo, GatewayInterface::HMAC_KEY_PHONE_BIN);
244
	}
245
246
	/**
247
	 * @inheritdoc
248
	 */
249
	public function checkCredits():string{
250
		return $this->threemaGateway->checkCredits();
251
	}
252
253
	/**
254
	 * @inheritdoc
255
	 */
256
	public function checkCapabilities(string $threemaID):string{
257
		return implode(',', $this->threemaGateway->checkCapabilities($threemaID));
258
	}
259
260
	/**
261
	 * @inheritdoc
262
	 */
263
	public function getIdByEmail(string $email):string{
264
		return $this->threemaGateway->getIdByEmailHash($this->hashEmail($email));
265
	}
266
267
	/**
268
	 * @inheritdoc
269
	 */
270
	public function getIdByPhone(string $phoneNo):string{
271
		return $this->threemaGateway->getIdByPhoneHash($this->hashPhone($phoneNo));
272
	}
273
274
	/**
275
	 * @inheritdoc
276
	 */
277
	public function getPubkeyById(string $threemaID):string{
278
		return $this->threemaGateway->getPublicKey($threemaID);
279
	}
280
281
}
282