1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Threema GmbH |
4
|
|
|
* @copyright Copyright (c) 2015-2016 Threema GmbH |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
|
8
|
|
|
namespace Threema\MsgApi; |
9
|
|
|
|
10
|
|
|
use Threema\Core\Exception; |
11
|
|
|
use Threema\Core\Url; |
12
|
|
|
use Threema\MsgApi\Commands\Capability; |
13
|
|
|
use Threema\MsgApi\Commands\CommandInterface; |
14
|
|
|
use Threema\MsgApi\Commands\Credits; |
15
|
|
|
use Threema\MsgApi\Commands\DownloadFile; |
16
|
|
|
use Threema\MsgApi\Commands\FetchPublicKey; |
17
|
|
|
use Threema\MsgApi\Commands\LookupEmail; |
18
|
|
|
use Threema\MsgApi\Commands\LookupPhone; |
19
|
|
|
use Threema\MsgApi\Commands\MultiPartCommandInterface; |
20
|
|
|
use Threema\MsgApi\Commands\Results\CapabilityResult; |
21
|
|
|
use Threema\MsgApi\Commands\Results\DownloadFileResult; |
22
|
|
|
use Threema\MsgApi\Commands\Results\FetchPublicKeyResult; |
23
|
|
|
use Threema\MsgApi\Commands\Results\LookupIdResult; |
24
|
|
|
use Threema\MsgApi\Commands\Results\Result; |
25
|
|
|
use Threema\MsgApi\Commands\Results\SendSimpleResult; |
26
|
|
|
use Threema\MsgApi\Commands\Results\SendE2EResult; |
27
|
|
|
use Threema\MsgApi\Commands\Results\UploadFileResult; |
28
|
|
|
use Threema\MsgApi\Commands\SendSimple; |
29
|
|
|
use Threema\MsgApi\Commands\SendE2E; |
30
|
|
|
use Threema\MsgApi\Commands\UploadFile; |
31
|
|
|
use Threema\MsgApi\Constants; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Class Connection |
35
|
|
|
* @package Threema\MsgApi |
36
|
|
|
*/ |
37
|
|
|
class Connection |
38
|
|
|
{ |
39
|
|
|
/** |
40
|
|
|
* @var ConnectionSettings |
41
|
|
|
*/ |
42
|
|
|
private $setting; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var PublicKeyStore |
46
|
|
|
*/ |
47
|
|
|
private $publicKeyStore; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @param ConnectionSettings $setting |
51
|
|
|
* @param PublicKeyStore $publicKeyStore stores the public keys locally to save network traffic |
52
|
|
|
*/ |
53
|
|
|
public function __construct(ConnectionSettings $setting, PublicKeyStore $publicKeyStore = null) { |
54
|
|
|
$this->setting = $setting; |
55
|
|
|
$this->publicKeyStore = $publicKeyStore; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @param Receiver $receiver |
60
|
|
|
* @param $text |
61
|
|
|
* @return SendSimpleResult |
62
|
|
|
*/ |
63
|
|
|
public function sendSimple(Receiver $receiver, $text) { |
64
|
|
|
$command = new SendSimple($receiver, $text); |
65
|
|
|
return $this->post($command); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @param string $threemaId |
70
|
|
|
* @param string $nonce |
71
|
|
|
* @param string $box |
72
|
|
|
* @return SendE2EResult |
73
|
|
|
*/ |
74
|
|
|
public function sendE2E($threemaId, $nonce, $box) { |
75
|
|
|
$command = new SendE2E($threemaId, $nonce, $box); |
76
|
|
|
return $this->post($command); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @param $encryptedFileData (binary string) |
81
|
|
|
* @return UploadFileResult |
82
|
|
|
*/ |
83
|
|
|
public function uploadFile($encryptedFileData) { |
84
|
|
|
$command = new UploadFile($encryptedFileData); |
85
|
|
|
return $this->postMultiPart($command); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @param $blobId |
91
|
|
|
* @param callable $progress |
92
|
|
|
* @return DownloadFileResult |
93
|
|
|
*/ |
94
|
|
|
public function downloadFile($blobId, \Closure $progress = null) { |
95
|
|
|
$command = new DownloadFile($blobId); |
96
|
|
|
return $this->get($command, $progress); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @param $phoneNumber |
101
|
|
|
* @return LookupIdResult |
102
|
|
|
*/ |
103
|
|
|
public function keyLookupByPhoneNumber($phoneNumber) { |
104
|
|
|
$command = new LookupPhone($phoneNumber); |
105
|
|
|
return $this->get($command); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @param string $email |
110
|
|
|
* @return LookupIdResult |
111
|
|
|
*/ |
112
|
|
|
public function keyLookupByEmail($email) { |
113
|
|
|
$command = new LookupEmail($email); |
114
|
|
|
return $this->get($command); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @param string $threemaId valid threema id (8 Chars) |
119
|
|
|
* @return CapabilityResult |
120
|
|
|
*/ |
121
|
|
|
public function keyCapability($threemaId) { |
122
|
|
|
return $this->get(new Capability($threemaId)); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @return CreditsResult |
128
|
|
|
*/ |
129
|
|
|
public function credits() { |
130
|
|
|
return $this->get(new Credits()); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @param $threemaId |
135
|
|
|
* @return FetchPublicKeyResult |
136
|
|
|
*/ |
137
|
|
|
public function fetchPublicKey($threemaId) { |
138
|
|
|
$publicKey = null; |
139
|
|
|
|
140
|
|
|
if (null !== $this->publicKeyStore) { |
141
|
|
|
$publicKey = $this->publicKeyStore->getPublicKey($threemaId); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
if (null === $publicKey) { |
145
|
|
|
$command = new FetchPublicKey($threemaId); |
146
|
|
|
$result = $this->get($command); |
147
|
|
|
if (false === $result->isSuccess()) { |
148
|
|
|
return $result; |
149
|
|
|
} |
150
|
|
|
$publicKey = $result->getRawResponse(); |
151
|
|
|
|
152
|
|
|
if (null !== $this->publicKeyStore) { |
153
|
|
|
$this->publicKeyStore->setPublicKey($threemaId, $publicKey); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
//create a key result |
158
|
|
|
return new FetchPublicKeyResult(200, $publicKey); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @param callable $progress |
163
|
|
|
* @return array |
164
|
|
|
*/ |
165
|
|
|
private function createDefaultOptions(\Closure $progress = null) { |
166
|
|
|
$options = array( |
167
|
|
|
CURLOPT_RETURNTRANSFER => true |
168
|
|
|
); |
169
|
|
|
|
170
|
|
|
//no progress |
171
|
|
|
if (null !== $progress) { |
172
|
|
|
$options[CURLOPT_NOPROGRESS] = false; |
173
|
|
|
$options[CURLOPT_PROGRESSFUNCTION] = $progress; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
//tls settings |
177
|
|
|
|
178
|
|
|
if (true === $this->setting->getTlsOption(ConnectionSettings::tlsOptionForceHttps, false)) { |
179
|
|
|
//limit allowed protocols to HTTPS |
180
|
|
|
$options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTPS; |
181
|
|
|
} |
182
|
|
|
if ($tlsVersion = $this->setting->getTlsOption(ConnectionSettings::tlsOptionVersion)) { |
183
|
|
|
if (is_int($tlsVersion)) { |
184
|
|
|
//if number is given use it |
185
|
|
|
$options[CURLOPT_SSLVERSION] = $tlsVersion; |
186
|
|
|
} else { |
187
|
|
|
//interpret strings as TLS versions |
188
|
|
|
switch ($tlsVersion) { |
189
|
|
|
case '1.0': |
190
|
|
|
$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_0; |
191
|
|
|
break; |
192
|
|
|
case '1.1': |
193
|
|
|
$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_1; |
194
|
|
|
break; |
195
|
|
|
case '1.2': |
196
|
|
|
$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_2; |
197
|
|
|
break; |
198
|
|
|
default: |
199
|
|
|
$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_DEFAULT; |
200
|
|
|
break; |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
View Code Duplication |
if ($tlsCipher = $this->setting->getTlsOption(ConnectionSettings::tlsOptionCipher, null)) { |
|
|
|
|
205
|
|
|
if(true === is_string($tlsCipher)) { |
206
|
|
|
$options[CURLOPT_SSL_CIPHER_LIST] = $tlsCipher; |
207
|
|
|
} |
208
|
|
|
} |
209
|
|
View Code Duplication |
if ($pinnedKey = $this->setting->getTlsOption(ConnectionSettings::tlsOptionPinnedKey, Constants::DEFAULT_PINNED_KEY)) { |
|
|
|
|
210
|
|
|
if(true === is_string($pinnedKey)) { |
211
|
|
|
$options[CURLOPT_PINNEDPUBLICKEY] = $pinnedKey; |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
return $options; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @param array $params |
219
|
|
|
* @return array |
220
|
|
|
*/ |
221
|
|
|
private function processRequestParams(array $params) { |
222
|
|
|
if (null === $params) { |
223
|
|
|
$params = array(); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
$params['from'] = $this->setting->getThreemaId(); |
227
|
|
|
$params['secret'] = $this->setting->getSecret(); |
228
|
|
|
|
229
|
|
|
return $params; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @param CommandInterface $command |
234
|
|
|
* @param callable $progress |
235
|
|
|
* @return Result |
236
|
|
|
*/ |
237
|
|
|
protected function get(CommandInterface $command, \Closure $progress = null) { |
238
|
|
|
$params = $this->processRequestParams($command->getParams()); |
239
|
|
|
return $this->call($command->getPath(), |
240
|
|
|
$this->createDefaultOptions($progress), |
241
|
|
|
$params, |
242
|
|
|
function ($httpCode, $response) use ($command) { |
243
|
|
|
return $command->parseResult($httpCode, $response); |
244
|
|
|
}); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* @param CommandInterface $command |
249
|
|
|
* @return Result |
250
|
|
|
*/ |
251
|
|
|
protected function post(CommandInterface $command) { |
252
|
|
|
$options = $this->createDefaultOptions(); |
253
|
|
|
$params = $this->processRequestParams($command->getParams()); |
254
|
|
|
|
255
|
|
|
$options[CURLOPT_POST] = true; |
256
|
|
|
$options[CURLOPT_POSTFIELDS] = http_build_query($params); |
257
|
|
|
$options[CURLOPT_HTTPHEADER] = array( |
258
|
|
|
'Content-Type: application/x-www-form-urlencoded'); |
259
|
|
|
|
260
|
|
|
return $this->call($command->getPath(), $options, null, function ($httpCode, $response) use ($command) { |
261
|
|
|
return $command->parseResult($httpCode, $response); |
262
|
|
|
}); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* @param MultiPartCommandInterface $command |
267
|
|
|
* @return Result |
268
|
|
|
*/ |
269
|
|
|
protected function postMultiPart(MultiPartCommandInterface $command) { |
270
|
|
|
$options = $this->createDefaultOptions(); |
271
|
|
|
$params = $this->processRequestParams($command->getParams()); |
272
|
|
|
|
273
|
|
|
$options[CURLOPT_POST] = true; |
274
|
|
|
$options[CURLOPT_HTTPHEADER] = array('Content-Type: multipart/form-data'); |
275
|
|
|
$options[CURLOPT_SAFE_UPLOAD] = true; |
276
|
|
|
$options[CURLOPT_POSTFIELDS] = array( |
277
|
|
|
'blob' => $command->getData() |
278
|
|
|
); |
279
|
|
|
|
280
|
|
|
return $this->call($command->getPath(), $options, $params, function ($httpCode, $response) use ($command) { |
281
|
|
|
return $command->parseResult($httpCode, $response); |
282
|
|
|
}); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @param string $path |
287
|
|
|
* @param array $curlOptions |
288
|
|
|
* @param array $parameters |
289
|
|
|
* @param callable $result |
290
|
|
|
* @return mixed |
291
|
|
|
* @throws \Threema\Core\Exception |
292
|
|
|
*/ |
293
|
|
|
private function call($path, array $curlOptions, array $parameters = null, \Closure $result = null) { |
294
|
|
|
$fullPath = new Url('', $this->setting->getHost()); |
295
|
|
|
$fullPath->addPath($path); |
296
|
|
|
|
297
|
|
|
if (null !== $parameters && count($parameters)) { |
298
|
|
|
foreach ($parameters as $key => $value) { |
299
|
|
|
$fullPath->setValue($key, $value); |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
$session = curl_init($fullPath->getFullPath()); |
303
|
|
|
curl_setopt_array($session, $curlOptions); |
304
|
|
|
|
305
|
|
|
$response = curl_exec($session); |
306
|
|
|
if (false === $response) { |
307
|
|
|
throw new Exception($path . ' ' . curl_error($session)); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
$httpCode = curl_getinfo($session, CURLINFO_HTTP_CODE); |
311
|
|
|
if (null === $result && $httpCode != 200) { |
312
|
|
|
throw new Exception($httpCode); |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
if (null !== $result) { |
316
|
|
|
return $result->__invoke($httpCode, $response); |
317
|
|
|
} else { |
318
|
|
|
return $response; |
319
|
|
|
} |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
|
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.