This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Class Gateway |
||
4 | * |
||
5 | * @filesource Gateway.php |
||
6 | * @created 10.06.2017 |
||
7 | * @package chillerlan\Threema |
||
8 | * @author Smiley <[email protected]> |
||
9 | * @copyright 2017 Smiley |
||
10 | * @license MIT |
||
11 | */ |
||
12 | |||
13 | namespace chillerlan\Threema; |
||
14 | |||
15 | use chillerlan\Threema\Crypto\CryptoInterface; |
||
16 | use chillerlan\Threema\HTTP\HTTPClientInterface; |
||
17 | |||
18 | class Gateway{ |
||
19 | |||
20 | const API_BASE = 'https://msgapi.threema.ch'; |
||
21 | |||
22 | const API_ERRORS = [ |
||
23 | 400 => 'bad request', |
||
24 | 401 => 'unauthorized', |
||
25 | 402 => 'no credits remain', |
||
26 | 404 => 'not found', |
||
27 | 413 => 'message too large', |
||
28 | 500 => 'internal server error', |
||
29 | ]; |
||
30 | |||
31 | const HMAC_KEY_EMAIL_BIN = "\x30\xa5\x50\x0f\xed\x97\x01\xfa\x6d\xef\xdb\x61\x08\x41\x90\x0f\xeb\xb8\xe4\x30\x88\x1f\x7a\xd8\x16\x82\x62\x64\xec\x09\xba\xd7"; |
||
32 | const HMAC_KEY_PHONE_BIN = "\x85\xad\xf8\x22\x69\x53\xf3\xd9\x6c\xfd\x5d\x09\xbf\x29\x55\x5e\xb9\x55\xfc\xd8\xaa\x5e\xc4\xf9\xfc\xd8\x69\xe2\x58\x37\x07\x23"; |
||
33 | |||
34 | const FILE_NONCE = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; |
||
35 | const FILE_THUMBNAIL_NONCE = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"; |
||
36 | |||
37 | /** |
||
38 | * @var \chillerlan\Threema\HTTP\HTTPClientInterface |
||
39 | */ |
||
40 | protected $HTTP; |
||
41 | |||
42 | /** |
||
43 | * @var \chillerlan\Threema\Crypto\CryptoInterface |
||
44 | */ |
||
45 | protected $crypto; |
||
46 | |||
47 | /** |
||
48 | * @var string |
||
49 | */ |
||
50 | protected $gatewayID; |
||
51 | |||
52 | /** |
||
53 | * @var string |
||
54 | */ |
||
55 | protected $gatewaySecret; |
||
56 | |||
57 | /** |
||
58 | * Gateway constructor. |
||
59 | * |
||
60 | * @param \chillerlan\Threema\HTTP\HTTPClientInterface $HTTP |
||
61 | * @param \chillerlan\Threema\Crypto\CryptoInterface $crypto |
||
62 | * @param string $gatewayID |
||
63 | * @param string $gatewaySecret |
||
64 | */ |
||
65 | public function __construct(HTTPClientInterface $HTTP, CryptoInterface $crypto, string $gatewayID, string $gatewaySecret){ |
||
66 | $this->HTTP = $HTTP; |
||
67 | $this->crypto = $crypto; |
||
68 | $this->gatewayID = $gatewayID; |
||
69 | $this->gatewaySecret = $gatewaySecret; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * @return array |
||
74 | */ |
||
75 | protected function getAuthParams():array { |
||
76 | return [ |
||
77 | 'from' => $this->gatewayID, |
||
78 | 'secret' => $this->gatewaySecret, |
||
79 | ]; |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * @param $endpoint |
||
84 | * @param array $params |
||
85 | * @param null $body |
||
86 | * @param array $headers |
||
87 | * |
||
88 | * @return mixed |
||
0 ignored issues
–
show
|
|||
89 | * @throws \chillerlan\Threema\EndpointException |
||
90 | */ |
||
91 | protected function getResponse($endpoint, $params = [], $body = null, $headers = []){ |
||
92 | $response = $this->HTTP->getResponse(self::API_BASE.$endpoint, $params, !is_null($body) ? 'POST' : 'GET', $body, $headers); |
||
93 | |||
94 | if($response->code !== 200){ |
||
95 | |||
96 | if(array_key_exists($response->code, self::API_ERRORS)){ |
||
97 | throw new EndpointException('gateway error: '.self::API_ERRORS[$response->code].' '.print_r($response->body, true)); |
||
98 | } |
||
99 | |||
100 | throw new EndpointException('unknown error: "compiles on my machine." '.print_r([$response->code, $response->body], true)); |
||
101 | } |
||
102 | |||
103 | return $response->body; |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Get remaining credits |
||
108 | * |
||
109 | * URL: https://msgapi.threema.ch/credits?from=<gatewayID>&secret=<gatewaySecret> |
||
110 | * |
||
111 | * The API identity and secret must be passed in the corresponding GET parameters for authentication (use URL |
||
112 | * encoding). The number of credits left on the account that the given ID belongs to will be returned as a |
||
113 | * text/plain response. Note: several IDs may use the same account, and thus share the same credit balance. |
||
114 | * |
||
115 | * Possible HTTP result codes: |
||
116 | * |
||
117 | * - 200 (on success) |
||
118 | * - 401 (if API identity or secret are incorrect) |
||
119 | * - 500 (if a temporary internal server error occurs) |
||
120 | * |
||
121 | * @return int |
||
122 | */ |
||
123 | public function checkCredits():int{ |
||
124 | return (int)$this->getResponse('/credits', $this->getAuthParams()); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | *Check file reception capability of an ID |
||
129 | * |
||
130 | * Before you send a file to a Threema ID using the blob upload (+ file message), you may want to check whether the |
||
131 | * recipient uses a Threema version that supports receiving files. The receiver may be using an old version, or a |
||
132 | * platform where file reception is not supported. |
||
133 | * |
||
134 | * URL: https://msgapi.threema.ch/capabilities/<threemaID>?from=<gatewayID>&secret=<gatewaySecret> |
||
135 | * |
||
136 | * The API identity and secret must be passed in the corresponding GET parameters for authentication (use URL |
||
137 | * encoding). The result is a text/plain response of supported capabilities, separated by commas. Currently defined |
||
138 | * capabilities: |
||
139 | * |
||
140 | * - text |
||
141 | * - image |
||
142 | * - video |
||
143 | * - audio |
||
144 | * - file |
||
145 | * |
||
146 | * More capabilities may be added in the future (separated with commas), so you should match on substrings when |
||
147 | * checking for file. The order in which the capabilities are returned is not defined. |
||
148 | * |
||
149 | * Example result: text,image,video,audio,file |
||
150 | * |
||
151 | * Possible HTTP result codes: |
||
152 | * |
||
153 | * - 200 (on success) |
||
154 | * - 401 (if API identity or secret are incorrect) |
||
155 | * - 404 (if no matching ID could be found) |
||
156 | * - 500 (if a temporary internal server error occurs) |
||
157 | * |
||
158 | * @param string $threemaID |
||
159 | * |
||
160 | * @return array |
||
161 | */ |
||
162 | public function checkCapabilities(string $threemaID):array{ |
||
163 | $response = explode(',', $this->getResponse('/capabilities/'.$this->checkThreemaID($threemaID), $this->getAuthParams())); |
||
164 | |||
165 | sort($response); |
||
166 | |||
167 | return $response; |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Find ID by phone number |
||
172 | * |
||
173 | * URL: https://msgapi.threema.ch/lookup/phone/<phoneno>?from=<gatewayID>&secret=<gatewaySecret> |
||
174 | * |
||
175 | * The phone number must be passed in E.164 format, without the leading +. The API identity and secret must be |
||
176 | * passed in the corresponding GET parameters for authentication (use URL encoding). |
||
177 | * The Threema ID corresponding to the phone number will be returned as a text/plain response. |
||
178 | * |
||
179 | * Possible HTTP result codes: |
||
180 | * |
||
181 | * - 200 (on success) |
||
182 | * - 401 (if API identity or secret are incorrect) |
||
183 | * - 404 (if no matching ID could be found) |
||
184 | * - 500 (if a temporary internal server error occurs) |
||
185 | * |
||
186 | * @param string $phoneno |
||
187 | * |
||
188 | * @return string |
||
189 | */ |
||
190 | public function getIdByPhone(string $phoneno):string{ |
||
191 | return $this->getResponse('/lookup/phone/'.$this->checkPhoneNo($phoneno), $this->getAuthParams()); |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * URL: https://msgapi.threema.ch/lookup/phone_hash/<phonenoHash>?from=<gatewayID>&secret=<gatewaySecret> |
||
196 | * |
||
197 | * The phone number must be passed as an HMAC-SHA256 hash of the E.164 number without the leading +. |
||
198 | * The HMAC key is 85adf8226953f3d96cfd5d09bf29555eb955fcd8aa5ec4f9fcd869e258370723 (in hexadecimal). |
||
199 | * |
||
200 | * Example: the phone number 41791234567 hashes to |
||
201 | * ad398f4d7ebe63c6550a486cc6e07f9baa09bd9d8b3d8cb9d9be106d35a7fdbc. |
||
202 | * |
||
203 | * The API identity and secret must be passed in the corresponding GET parameters for authentication (use URL |
||
204 | * encoding). The Threema ID corresponding to the phone number will be returned as a text/plain response. |
||
205 | * |
||
206 | * Possible HTTP result codes: |
||
207 | * |
||
208 | * - 200 (on success) |
||
209 | * - 400 (if the hash length is wrong) |
||
210 | * - 401 (if API identity or secret are incorrect) |
||
211 | * - 404 (if no matching ID could be found) |
||
212 | * - 500 (if a temporary internal server error occurs) |
||
213 | * |
||
214 | * @param string $phonenoHash |
||
215 | * |
||
216 | * @return string |
||
217 | */ |
||
218 | public function getIdByPhoneHash(string $phonenoHash):string{ |
||
219 | return $this->getResponse('/lookup/phone_hash/'.$this->checkHash($phonenoHash), $this->getAuthParams()); |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * URL: https://msgapi.threema.ch/lookup/email/<email>?from=<gatewayID>&secret=<gatewaySecret> |
||
224 | * |
||
225 | * The API identity and secret must be passed in the corresponding GET parameters for authentication (use URL |
||
226 | * encoding). The Threema ID corresponding to the email address will be returned as a text/plain response. |
||
227 | * |
||
228 | * Possible HTTP result codes: |
||
229 | * |
||
230 | * - 200 (on success) |
||
231 | * - 401 (if API identity or secret are incorrect) |
||
232 | * - 404 (if no matching ID could be found) |
||
233 | * - 500 (if a temporary internal server error occurs) |
||
234 | * |
||
235 | * |
||
236 | * @param string $email |
||
237 | * |
||
238 | * @return string |
||
239 | */ |
||
240 | public function getIdByEmail(string $email):string{ |
||
241 | return $this->getResponse('/lookup/email/'.$this->checkEmail($email), $this->getAuthParams()); |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Find ID by email address hash |
||
246 | * |
||
247 | * URL: https://msgapi.threema.ch/lookup/email_hash/<emailHash>?from=<gatewayID>&secret=<gatewaySecret> |
||
248 | * |
||
249 | * The lowercased and whitespace-trimmed email address must be hashed with HMAC-SHA256. |
||
250 | * The HMAC key is 30a5500fed9701fa6defdb610841900febb8e430881f7ad816826264ec09bad7 (in hexadecimal). |
||
251 | * |
||
252 | * Example: the email address [email protected] hashes to |
||
253 | * 1ea093239cc5f0e1b6ec81b866265b921f26dc4033025410063309f4d1a8ee2c. |
||
254 | * |
||
255 | * The API identity and secret must be passed in the corresponding GET parameters for authentication (use URL |
||
256 | * encoding). The Threema ID corresponding to the email address will be returned as a text/plain response. |
||
257 | * |
||
258 | * Possible HTTP result codes: |
||
259 | * |
||
260 | * - 200 (on success) |
||
261 | * - 400 (if the hash length is wrong) |
||
262 | * - 401 (if API identity or secret are incorrect) |
||
263 | * - 404 (if no matching ID could be found) |
||
264 | * - 500 (if a temporary internal server error occurs) |
||
265 | * |
||
266 | * @param string $emailHash |
||
267 | * |
||
268 | * @return string |
||
269 | */ |
||
270 | public function getIdByEmailHash(string $emailHash):string{ |
||
271 | return $this->getResponse('/lookup/email_hash/'.$this->checkHash($emailHash), $this->getAuthParams()); |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * @param array $emails |
||
276 | * @param array $phonenumbers |
||
277 | * |
||
278 | * @return mixed |
||
279 | */ |
||
280 | /* |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
61% 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. ![]() |
|||
281 | public function bulkLookup($emails = [], $phonenumbers = []){ |
||
282 | $lookup = []; |
||
283 | |||
284 | foreach($phonenumbers as $phonenumber){ |
||
285 | $lookup['phoneHashes'][] = $this->hashPhoneNo($phonenumber); |
||
286 | } |
||
287 | |||
288 | foreach($emails as $email){ |
||
289 | $lookup['emailHashes'][] = $this->hashEmail($email); |
||
290 | } |
||
291 | |||
292 | $r = $this->getResponse('/lookup/bulk', $this->getAuthParams(), json_encode($lookup)); |
||
293 | |||
294 | return json_decode($r); |
||
295 | } |
||
296 | */ |
||
297 | |||
298 | /** |
||
299 | * Key lookups |
||
300 | * |
||
301 | * For the end-to-end encrypted mode, you need the public key of the recipient in order to encrypt a message. While |
||
302 | * it's best to obtain this directly from the recipient (extract it from the QR code), this may not be convenient, |
||
303 | * and therefore you can also look up the key associated with a given ID from the server. |
||
304 | * |
||
305 | * URL: https://msgapi.threema.ch/pubkeys/<threemaID>?from=<gatewayID>&secret=<gatewaySecret> |
||
306 | * |
||
307 | * The API identity and secret must be passed in the corresponding GET parameters for authentication (use URL |
||
308 | * encoding). The public key corresponding to the ID will be returned as a text/plain response (hex encoded). |
||
309 | * |
||
310 | * Possible HTTP result codes: |
||
311 | * |
||
312 | * - 200 (on success) |
||
313 | * - 401 (if API identity or secret are incorrect) |
||
314 | * - 404 (if no matching ID could be found) |
||
315 | * - 500 (if a temporary internal server error occurs) |
||
316 | * |
||
317 | * It is strongly recommended that you cache the public keys to avoid querying the API for each message. |
||
318 | * |
||
319 | * @param string $threemaID |
||
320 | * |
||
321 | * @return string |
||
322 | * @throws \chillerlan\Threema\EndpointException |
||
323 | */ |
||
324 | public function getPublicKey(string $threemaID):string{ |
||
325 | $threemaID = $this->checkThreemaID($threemaID); |
||
326 | |||
327 | if(!$threemaID){ |
||
0 ignored issues
–
show
The expression
$threemaID of type null|string is loosely compared to false ; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
328 | throw new EndpointException('invalid threema id'); |
||
329 | } |
||
330 | |||
331 | $response = $this->getResponse('/pubkeys/'.$threemaID, $this->getAuthParams()); |
||
332 | |||
333 | if(!$response || !$this->checkHash($response)){ |
||
0 ignored issues
–
show
The expression
$this->checkHash($response) of type null|string is loosely compared to false ; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
334 | throw new EndpointException('invalid public key'); |
||
335 | } |
||
336 | |||
337 | return $response; |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * Send Messages, Basic mode |
||
342 | * |
||
343 | * URL: https://msgapi.threema.ch/send_simple |
||
344 | * |
||
345 | * POST parameters (application/x-www-form-urlencoded): |
||
346 | * |
||
347 | * - recipient: choose one of the following: |
||
348 | * - to recipient identity (8 characters) |
||
349 | * - phone recipient phone number (E.164), without leading + |
||
350 | * - email recipient email address |
||
351 | * - text message text, max. 3500 bytes, UTF-8 encoded |
||
352 | * |
||
353 | * - from your API identity (8 characters, usually starts with '*') |
||
354 | * - secret API authentication secret |
||
355 | * |
||
356 | * By using the phone or email recipient specifiers, one can avoid having to look up the corresponding ID |
||
357 | * and instead do everything in one call (may be more suitable for SMS gateway style integration). |
||
358 | * |
||
359 | * Possible HTTP result codes: |
||
360 | * |
||
361 | * - 200 (on success) |
||
362 | * - 400 (if the recipient identity is invalid or the account is not set up for basic mode) |
||
363 | * - 401 (if API identity or secret are incorrect) |
||
364 | * - 402 (if no credits remain) |
||
365 | * - 404 (if using phone or email as the recipient specifier, and the corresponding recipient could not be found) |
||
366 | * - 413 (if the message is too long) |
||
367 | * - 500 (if a temporary internal server error occurs) |
||
368 | * |
||
369 | * On success (HTTP 200), the ID of the new message is returned as text/plain. |
||
370 | * |
||
371 | * @param string $to |
||
372 | * @param string $message |
||
373 | * |
||
374 | * @return string |
||
375 | * @throws \chillerlan\Threema\EndpointException |
||
376 | * |
||
377 | */ |
||
378 | /* |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
62% 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. ![]() |
|||
379 | public function sendSimple(string $to, string $message):string{ |
||
380 | $params = array_merge($this->getRecipient($to), ['text' => $message], $this->getAuthParams()); |
||
381 | |||
382 | ksort($params); |
||
383 | |||
384 | return $this->getResponse('/send_simple', [], $params); |
||
385 | } |
||
386 | */ |
||
387 | |||
388 | |||
389 | /** |
||
390 | * @param string $to |
||
391 | * |
||
392 | * @return array |
||
393 | * @throws \chillerlan\Threema\EndpointException |
||
394 | */ |
||
395 | protected function getRecipient(string $to):array { |
||
396 | |||
397 | switch(true){ |
||
398 | case $x = $this->checkEmail($to): |
||
399 | return ['email' => $x]; |
||
0 ignored issues
–
show
The variable
$x seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?
This error can happen if you refactor code and forget to move the variable initialization. Let’s take a look at a simple example: function someFunction() {
$x = 5;
echo $x;
}
The above code is perfectly fine. Now imagine that we re-order the statements: function someFunction() {
echo $x;
$x = 5;
}
In that case, ![]() |
|||
400 | case $x = $this->checkThreemaID($to): |
||
401 | return ['to' => $x]; |
||
0 ignored issues
–
show
The variable
$x seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?
This error can happen if you refactor code and forget to move the variable initialization. Let’s take a look at a simple example: function someFunction() {
$x = 5;
echo $x;
}
The above code is perfectly fine. Now imagine that we re-order the statements: function someFunction() {
echo $x;
$x = 5;
}
In that case, ![]() |
|||
402 | case $x = $this->checkPhoneNo($to): |
||
403 | return ['phone' => $x]; |
||
0 ignored issues
–
show
The variable
$x seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?
This error can happen if you refactor code and forget to move the variable initialization. Let’s take a look at a simple example: function someFunction() {
$x = 5;
echo $x;
}
The above code is perfectly fine. Now imagine that we re-order the statements: function someFunction() {
echo $x;
$x = 5;
}
In that case, ![]() |
|||
404 | default: |
||
405 | throw new EndpointException('"to" not specified: '.$to); |
||
406 | } |
||
407 | |||
408 | } |
||
409 | |||
410 | /** |
||
411 | * End-to-end encrypted mode |
||
412 | * |
||
413 | * URL: https://msgapi.threema.ch/send_e2e |
||
414 | * |
||
415 | * POST parameters (application/x-www-form-urlencoded): |
||
416 | * |
||
417 | * - to recipient identity (8 characters) |
||
418 | * - box encrypted message data (max. 4000 bytes, hex encoded) |
||
419 | * - nonce nonce used for encryption (24 bytes, hex encoded) |
||
420 | * |
||
421 | * - from your API identity (8 characters, usually starts with '*') |
||
422 | * - secret API authentication secret |
||
423 | * |
||
424 | * Possible HTTP result codes: |
||
425 | * |
||
426 | * - 200 (on success) |
||
427 | * - 400 (if the recipient identity is invalid or the account is not set up for end-to-end mode) |
||
428 | * - 401 (if API identity or secret are incorrect) |
||
429 | * - 402 (if no credits remain) |
||
430 | * - 413 (if the message is too long) |
||
431 | * - 500 (if a temporary internal server error occurs) |
||
432 | * |
||
433 | * On success (HTTP 200), the ID of the new message is returned as text/plain. |
||
434 | * |
||
435 | * @param string $recipientThreemaID |
||
436 | * @param string $senderPrivateKey |
||
437 | * |
||
438 | * @param string $data |
||
439 | * |
||
440 | * |
||
441 | * @return string |
||
442 | * @throws \chillerlan\Threema\EndpointException |
||
443 | */ |
||
444 | protected function sendE2E(string $recipientThreemaID, string $senderPrivateKey, string $data):string{ |
||
445 | $recipientThreemaID = $this->checkThreemaID($recipientThreemaID); |
||
446 | |||
447 | if(!$recipientThreemaID){ |
||
0 ignored issues
–
show
The expression
$recipientThreemaID of type null|string is loosely compared to false ; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
448 | throw new EndpointException('no threema id given'); |
||
449 | } |
||
450 | |||
451 | $recipientPubKey = $this->getPublicKey($recipientThreemaID); |
||
452 | |||
453 | $params = array_merge( |
||
454 | $this->getAuthParams(), |
||
455 | ['to' => $recipientThreemaID], |
||
456 | $this->crypto->createBox($data.$this->crypto->getPadBytes(), $senderPrivateKey, $recipientPubKey) |
||
457 | ); |
||
458 | |||
459 | ksort($params); |
||
460 | |||
461 | return $this->getResponse('/send_e2e', [], $params); |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * @param string $recipientThreemaID |
||
466 | * @param string $senderPrivateKey |
||
467 | * @param string $text |
||
468 | * |
||
469 | * @return string |
||
470 | */ |
||
471 | public function sendE2EText(string $recipientThreemaID, string $senderPrivateKey, string $text):string{ |
||
472 | return $this->sendE2E($recipientThreemaID, $senderPrivateKey, "\x01".$text); |
||
473 | } |
||
474 | |||
475 | /** |
||
476 | * @param string $recipientThreemaID |
||
477 | * @param string $senderPrivateKey |
||
478 | * @param string $file |
||
479 | * @param string $description |
||
480 | * @param string|null $thumbnail |
||
481 | * |
||
482 | * @return string |
||
483 | * @throws \chillerlan\Threema\EndpointException |
||
484 | */ |
||
485 | public function sendE2EFile(string $recipientThreemaID, string $senderPrivateKey, string $file, string $description = '', string $thumbnail = null):string{ |
||
486 | |||
487 | if(!in_array('file', $this->checkCapabilities($recipientThreemaID))){ |
||
488 | throw new EndpointException('given threema id is not capable of receiving files'); |
||
489 | } |
||
490 | |||
491 | $fileinfo = $this->checkFile($file); |
||
492 | |||
493 | if(!is_array($fileinfo)){ |
||
494 | throw new EndpointException('invalid file'); |
||
495 | } |
||
496 | |||
497 | $key = $this->crypto->getRandomBytes(32); |
||
498 | |||
499 | $content = [ |
||
500 | 'b' => $this->upload($this->crypto->createSecretBox(file_get_contents($file), self::FILE_NONCE, $key)), |
||
501 | 'k' => $this->crypto->bin2hex($key), |
||
502 | 'm' => $fileinfo['mime'], |
||
503 | 'n' => $fileinfo['name'], |
||
504 | 's' => $fileinfo['size'], |
||
505 | 'i' => 0, |
||
506 | ]; |
||
507 | |||
508 | if(!empty($description)){ |
||
509 | $content['d'] = $description; |
||
510 | } |
||
511 | |||
512 | if($thumbnail){ |
||
0 ignored issues
–
show
The expression
$thumbnail of type null|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
513 | // @todo: autocreate thumbnail |
||
514 | $thumbinfo = $this->checkFile($thumbnail); |
||
515 | |||
516 | if(is_array($thumbinfo)){ |
||
517 | $content['t'] = $this->upload($this->crypto->createSecretBox(file_get_contents($thumbnail), self::FILE_THUMBNAIL_NONCE, $key)); |
||
518 | } |
||
519 | |||
520 | } |
||
521 | |||
522 | return $this->sendE2E($recipientThreemaID, $senderPrivateKey, "\x17".json_encode($content)); |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * @param string $recipientThreemaID |
||
527 | * @param string $senderPrivateKey |
||
528 | * @param string $image |
||
529 | * |
||
530 | * @return mixed |
||
531 | * @throws \chillerlan\Threema\EndpointException |
||
532 | */ |
||
533 | /* |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
62% 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. ![]() |
|||
534 | public function sendE2EImage(string $recipientThreemaID, string $senderPrivateKey, string $image){ |
||
535 | |||
536 | if(!in_array('image', $this->checkCapabilities($recipientThreemaID))){ |
||
537 | throw new EndpointException('given threema id is not capable of receiving image'); |
||
538 | } |
||
539 | |||
540 | $fileinfo = $this->checkFile($image); |
||
541 | |||
542 | if(!is_array($fileinfo) || !in_array($fileinfo['mime'], ['image/jpg', 'image/jpeg', 'image/png'])){ |
||
543 | throw new EndpointException('invalid image'); |
||
544 | } |
||
545 | |||
546 | $recipientPubKey = $this->getPublicKey($recipientThreemaID); |
||
547 | |||
548 | $box = $this->crypto->createBox(file_get_contents($image), $senderPrivateKey, $recipientPubKey); |
||
549 | |||
550 | $message = "\x02".$this->crypto->hex2bin($this->upload($box['box'])); |
||
551 | $message .= pack('V', $fileinfo['size']); |
||
552 | $message .= $this->crypto->hex2bin($box['nonce']); |
||
553 | |||
554 | $params = array_merge( |
||
555 | $this->getAuthParams(), |
||
556 | ['to' => $recipientThreemaID], |
||
557 | $this->crypto->createBox($message.$this->crypto->getPadBytes(), $senderPrivateKey, $recipientPubKey) |
||
558 | ); |
||
559 | |||
560 | ksort($params); |
||
561 | |||
562 | return $this->getResponse('/send_e2e', [], $params); |
||
563 | } |
||
564 | */ |
||
565 | |||
566 | /** |
||
567 | * Upload |
||
568 | * |
||
569 | * URL: https://msgapi.threema.ch/upload_blob |
||
570 | * |
||
571 | * POST parameters (multipart/form-data): |
||
572 | * |
||
573 | * - blob blob data (binary), max. 20 MB |
||
574 | * |
||
575 | * GET parameters: |
||
576 | * |
||
577 | * - from your API identity (8 characters, usually starts with '*') |
||
578 | * - secret API authentication secret |
||
579 | * |
||
580 | * Please note that the authentication parameters must be passed in the request URL, |
||
581 | * while the actual blob data needs to be sent as a multipart/form-data parameter. |
||
582 | * |
||
583 | * Possible HTTP result codes: |
||
584 | * |
||
585 | * - 200 (on success) |
||
586 | * - 400 (if required parameters are missing or the blob is empty) |
||
587 | * - 401 (if API identity or secret are incorrect) |
||
588 | * - 402 (if no credits remain) |
||
589 | * - 413 (if the blob is too big) |
||
590 | * - 500 (if a temporary internal server error occurs) |
||
591 | * |
||
592 | * The ID of the new blob is returned as text/plain. One credit is deducted for the upload of a blob. |
||
593 | * |
||
594 | * @param string $blob |
||
595 | * |
||
596 | * @return string |
||
597 | */ |
||
598 | protected function upload(string $blob):string{ |
||
599 | return $this->getResponse('/upload_blob', $this->getAuthParams(), ['blob' => $blob], ['Content-type' => 'multipart/form-data']); |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * URL: https://msgapi.threema.ch/blobs/<blobID> |
||
604 | * |
||
605 | * GET parameters: |
||
606 | * |
||
607 | * - from your API identity (8 characters, usually starts with '*') |
||
608 | * - secret API authentication secret |
||
609 | * |
||
610 | * Possible HTTP result codes: |
||
611 | * |
||
612 | * 200 (on success, body is the blob data as application/octet-stream) |
||
613 | * 401 (if API identity or secret are incorrect) |
||
614 | * 404 (if no blob with this ID could be found) |
||
615 | * 500 (if a temporary internal server error occurs) |
||
616 | * |
||
617 | * Please note: after a blob download has first been attempted, the blob may be deleted from the server within an |
||
618 | * hour. |
||
619 | * |
||
620 | * @param string $blobID |
||
621 | * |
||
622 | * @return mixed |
||
0 ignored issues
–
show
|
|||
623 | */ |
||
624 | public function download(string $blobID){ |
||
625 | return $this->getResponse('/blobs/'.$blobID, $this->getAuthParams()); |
||
626 | } |
||
627 | |||
628 | /** |
||
629 | * Hashes an email address for identity lookup. |
||
630 | * |
||
631 | * @param string $email the email address |
||
632 | * |
||
633 | * @return string the email hash (hex) |
||
634 | * @throws \chillerlan\Threema\EndpointException |
||
635 | */ |
||
636 | public function hashEmail($email){ |
||
637 | $email = $this->checkEmail($email); |
||
638 | |||
639 | if(!$email){ |
||
0 ignored issues
–
show
The expression
$email of type null|string is loosely compared to false ; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
640 | throw new EndpointException('invalid email'); |
||
641 | } |
||
642 | |||
643 | return $this->crypto->hmac_hash($email, self::HMAC_KEY_EMAIL_BIN); |
||
644 | } |
||
645 | |||
646 | /** |
||
647 | * Hashes an phone number address for identity lookup. |
||
648 | * |
||
649 | * @param string $phoneNo the phone number (in E.164 format, no leading +) |
||
650 | * |
||
651 | * @return bool|string the phone number hash (hex), false on failure |
||
652 | * @throws \chillerlan\Threema\EndpointException |
||
653 | */ |
||
654 | public function hashPhoneNo($phoneNo){ |
||
655 | $phoneNo = $this->checkPhoneNo($phoneNo); |
||
656 | |||
657 | if(!$phoneNo){ |
||
0 ignored issues
–
show
The expression
$phoneNo of type null|string is loosely compared to false ; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
658 | throw new EndpointException('invalid phonenumber'); |
||
659 | } |
||
660 | |||
661 | return $this->crypto->hmac_hash($phoneNo, self::HMAC_KEY_PHONE_BIN); |
||
662 | } |
||
663 | |||
664 | /** |
||
665 | * @param string $threemaID |
||
666 | * |
||
667 | * @return null|string |
||
668 | */ |
||
669 | protected function checkThreemaID(string $threemaID){ |
||
670 | $threemaID = trim($threemaID); |
||
671 | |||
672 | if(!preg_match('/^[a-z\d\*]{8}$/i', $threemaID)){ |
||
673 | return null; |
||
674 | } |
||
675 | |||
676 | return strtoupper($threemaID); |
||
677 | } |
||
678 | |||
679 | /** |
||
680 | * @param string $phoneNo |
||
681 | * |
||
682 | * @return null|string |
||
683 | */ |
||
684 | protected function checkPhoneNo(string $phoneNo){ |
||
685 | $phoneNo = trim($phoneNo); |
||
686 | |||
687 | if(!preg_match('/^[\d]+$/', $phoneNo)){ |
||
688 | return null; |
||
689 | } |
||
690 | |||
691 | return $phoneNo; |
||
692 | } |
||
693 | |||
694 | /** |
||
695 | * @param $email |
||
696 | * |
||
697 | * @return null|string |
||
698 | */ |
||
699 | protected function checkEmail($email){ |
||
700 | $email = filter_var(trim($email), FILTER_VALIDATE_EMAIL); |
||
701 | |||
702 | if(empty($email)){ |
||
703 | return null; |
||
704 | } |
||
705 | |||
706 | return strtolower($email); |
||
707 | } |
||
708 | |||
709 | /** |
||
710 | * @param string $hash |
||
711 | * |
||
712 | * @return null|string |
||
713 | */ |
||
714 | protected function checkHash(string $hash){ |
||
715 | $hash = trim($hash); |
||
716 | |||
717 | if(!preg_match('/^[a-f\d]{64}$/i', $hash)){ |
||
718 | return null; |
||
719 | } |
||
720 | |||
721 | return $hash; |
||
722 | } |
||
723 | |||
724 | /** |
||
725 | * @param string $path |
||
726 | * |
||
727 | * @return array|null |
||
0 ignored issues
–
show
|
|||
728 | */ |
||
729 | protected function checkFile(string $path){ |
||
730 | |||
731 | if(!file_exists($path) || !is_file($path)){ |
||
732 | return null; |
||
733 | } |
||
734 | |||
735 | $mime = 'application/octet-stream'; |
||
736 | |||
737 | if(class_exists('finfo')){ |
||
738 | $mime = (new \finfo(FILEINFO_MIME_TYPE ))->file($path); |
||
739 | } |
||
740 | else if(function_exists('mime_content_type')) { |
||
741 | $mime = mime_content_type($path); |
||
742 | } |
||
743 | |||
744 | return ['path' => $path, 'name' => basename($path), 'size' => filesize($path), 'mime' => $mime]; |
||
745 | } |
||
746 | } |
||
747 |
This check looks for the generic type
array
as a return type and suggests a more specific type. This type is inferred from the actual code.