Completed
Push — master ( b57115...36326e )
by rugk
07:27
created

getFileList()   C

Complexity

Conditions 9
Paths 72

Size

Total Lines 88
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 88
rs 5.2636
c 0
b 0
f 0
cc 9
eloc 31
nc 72
nop 4

How to fix   Long Method   

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
 * Allows one to query received Threema messages and to delete them.
4
 *
5
 * This class is basically a wrapper around the message model used for
6
 * querying the messages and tries to make it easier to access them.
7
 *
8
 * @package ThreemaGateway
9
 * @author rugk
10
 * @copyright Copyright (c) 2015-2016 rugk
11
 * @license MIT
12
 */
13
14
class ThreemaGateway_Handler_Action_Receiver extends ThreemaGateway_Handler_Action_Abstract
15
{
16
    /**
17
     * @var bool whether the model is already prepared
18
     */
19
    protected $isPrepared = false;
20
21
    /**
22
     * @var bool whether the permissions are already checked
23
     */
24
    protected $isPermissionChecked = false;
25
26
    /**
27
     * @var bool whether the result should be grouped by the message type
28
     */
29
    protected $groupByMessageType = false;
30
31
    /**
32
     * Startup.
33
     *
34
     * @param bool $alreadyPrepared     If the Message Model is already prepared you
35
     *                                  may set this to true.
36
     * @param bool $skipPermissionCheck Set to true to skip the permission check.
37
     *                                  (not recommend)
38
     */
39
    public function __construct($alreadyPrepared = false, $skipPermissionCheck = false)
40
    {
41
        parent::__construct();
42
        $this->isPrepared          = $alreadyPrepared;
43
        $this->isPermissionChecked = $skipPermissionCheck;
44
    }
45
46
    /**
47
     * Sets whether the result should be grouped by the message type.
48
     *
49
     * The option is ignored when you specify the message type in a function
50
     * as grouping in this case would not make any sense.
51
     *
52
     * @param  bool       $groupByMessageType
53
     * @return null|array
54
     */
55
    public function groupByMessageType($groupByMessageType)
56
    {
57
        $this->groupByMessageType = $groupByMessageType;
58
    }
59
60
    /**
61
     * Returns the single last message received.
62
     *
63
     * @param  string     $threemaId   filter by Threema ID (optional)
64
     * @param  string     $messageType filter by message type (optional, use Model constants)
65
     * @param  string     $keyword     filter by this string, which represents
66
     *                                 the text in a text message (Wildcards: * and ?)
67
     * @return null|array
68
     */
69
    public function getLastMessage($threemaId = null, $messageType = null, $keyword = null)
70
    {
71
        $this->initiate();
72
        /** @var ThreemaGateway_Model_Messages $model */
73
        $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
74
75
        // set options
76
        if ($threemaId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $threemaId of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
77
            $model->setSenderId($threemaId);
78
        }
79
        if ($messageType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageType of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
80
            $model->setTypeCode($messageType);
81
        }
82
        if ($keyword) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keyword of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
83
            $keyword = $this->replaceWildcards($keyword);
84
            $model->setKeyword($keyword);
85
        }
86
87
        // only show last result
88
        $model->setResultLimit(1);
89
        // to make sure the message is really the last one, sort it by the send time
90
        $model->setOrder('date_send', 'desc');
91
92
        return $this->execute($model, $messageType);
93
    }
94
95
    /**
96
     * Returns all messages with the specified criterias.
97
     *
98
     * @param  string     $threemaId   filter by Threema ID (optional)
99
     * @param  string     $messageType filter by message type (optional, use Model constants)
100
     * @param  string     $timeSpan    a relative time (parsable by strtotime) to limit the query (e.g. '-7 days')
101
     * @param  string     $keyword     filter by this string, which represents
102
     *                                 the text in a text message (Wildcards: * and ?)
103
     * @return null|array
104
     */
105
    public function getMessages($threemaId = null, $messageType = null, $timeSpan = null, $keyword = null)
106
    {
107
        $this->initiate();
108
        /** @var ThreemaGateway_Model_Messages $model */
109
        $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
110
111
        // set options
112
        if ($threemaId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $threemaId of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
113
            $model->setSenderId($threemaId);
114
        }
115
        if ($messageType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageType of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
116
            $model->setTypeCode($messageType);
117
        }
118
        if ($timeSpan) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $timeSpan of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
119
            $model->setTimeLimit(strtotime($timeSpan, XenForo_Application::$time));
120
            // manual ordering is not necessary as only new messages are inserted
121
            // ("at the bottom") and the dates never change,
122
        }
123
        if ($keyword) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keyword of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
124
            $keyword = $this->replaceWildcards($keyword);
125
            $model->setKeyword($keyword);
126
        }
127
128
        return $this->execute($model, $messageType);
129
    }
130
131
    /**
132
     * Returns the message data for a particular message ID.
133
     *
134
     * Note that when the database is corrupt and e.g. for a message some
135
     * datasets are missing, thsi will return null.
136
     *
137
     * @param  string     $messageId
138
     * @param  string     $messageType If you know the message type it is very much
139
     *                                 recommend to specify it here.
140
     * @return null|array
141
     */
142
    public function getMessageData($messageId, $messageType = null)
143
    {
144
        $this->initiate();
145
        /** @var ThreemaGateway_Model_Messages $model */
146
        $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
147
148
        // set options
149
        if ($messageId) {
150
            $model->setMessageId($messageId, 'metamessage');
151
        }
152
        if ($messageType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageType of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
153
            $model->setTypeCode($messageType);
154
        }
155
156
        return $this->execute($model, $messageType);
157
    }
158
159
    /**
160
     * Checks whether a message is saved in the database.
161
     *
162
     * Note that this does not guarantee that other methods return any data as
163
     * a message is also considered "received" when the actual data has
164
     * already been deleted.
165
     * All other methods return "null" in this case as they cannot return all of
166
     * the requested data. This function however would return true.
167
     *
168
     * @param  string $messageId
169
     * @return bool
170
     */
171
    public function messageIsReceived($messageId)
172
    {
173
        $this->initiate();
174
        /** @var ThreemaGateway_Model_Messages $model */
175
        $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
176
177
        // validate parameter
178
        if (!$messageId) {
179
            throw new XenForo_Exception('Parameter $messageId missing.');
180
        }
181
182
        // set options
183
        $model->setMessageId($messageId, 'metamessage');
184
185
        // query meta data
186
        if ($model->getMessageMetaData(false, false)) {
187
            return true;
188
        }
189
190
        return false;
191
    }
192
193
    /**
194
     * Returns the list of all files.
195
     *
196
     * Grouping this result ({@see groupByMessageType()}) is not supported.
197
     * Note that the result is still the usual array of all other message
198
     * queries here.
199
     *
200
     * @param  string     $threemaId     filter by Threema ID (optional)
201
     * @param  string     $mimeType      Filter by mime type (optional).
202
     * @param  string     $fileType      The file type, e.g. thumbnail/file or image (optional).
203
     *                                   This is a Threema-internal type and may not
204
     *                                   be particular useful.
205
     * @param  bool       $queryMetaData Set to false, to prevent querying for meta
206
     *                                   data, which might speed up the query. (default: true)
207
     * @return null|array
208
     */
209
    public function getFileList($mimeType = null, $fileType = null, $threemaId = null, $queryMetaData = true)
210
    {
211
        $this->initiate();
212
        $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
213
214
        // set options
215
        if ($threemaId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $threemaId of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
216
            $model->setSenderId($threemaId);
217
        }
218
219
        if ($fileType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fileType of type string|null 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
220
            $model->injectFetchOption('where', 'filelist.file_type = ?', true);
221
            $model->injectFetchOption('params', $fileType, true);
222
        }
223
224
        // reset grouping as it cannot be processed
225
        $this->groupByMessageType = false;
226
227
        // determinate, which message types may be affected
228
        if ($mimeType !== null &&
229
            $mimeType !== 'image/jpeg') {
230
            // we can skip the image table as it is impossible that image files
231
            // would be returned in this query
232
            /** @var string|null $messageType */
233
            $messageType = ThreemaGateway_Model_Messages::TYPE_FILE_MESSAGE;
234
235
            // and we can already set the mime type as a condition
236
            $model->injectFetchOption('where', 'message.mime_type = ?', true);
237
            $model->injectFetchOption('params', $mimeType, true);
238
239
            // set message type as option
240
            $model->setTypeCode($messageType);
241
242
            // As the mime type is set to something different than image/jpeg,
243
            // now all images are already excluded, so we can return the data
244
            // with one query querying only the files table.
245
246
            /** @var array $result */
247
            $result = $model->getMessageDataByType($messageType, $queryMetaData);
248
        } else {
249
            // It's more complex if we want to query image & file messages
250
            // together, as the MIME type includes image files.
251
            //
252
            // Forunately this problem can be solved by just querying each
253
            // message type individually. This does also only do 2 queries,
254
            // which is even less than if we would use getAllMessageData, as
255
            // there we need 3 queries: metadata + msg type 1 + msg type 2.
256
            // As we know the possible message types this is possible. In
257
            // all other ways one must query the metadata and filter or split
258
            // it accordingly, to later execute the queries.
259
260
            // first we query the image files
261
            // (without MIME type setting as images can only have one
262
            // MIME type - image/jpeg - anyway)
263
264
            /** @var array|null $images */
265
            $images = $model->getMessageDataByType(ThreemaGateway_Model_Messages::TYPE_IMAGE_MESSAGE, $queryMetaData);
266
267
            // now set the MIME type if there is one
268
            if ($mimeType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimeType 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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
269
                $model->injectFetchOption('where', 'message.mime_type = ?', true);
270
                $model->injectFetchOption('params', $mimeType, true);
271
            }
272
273
            // and now query all other files
274
            /** @var array|null $files */
275
            $files = $model->getMessageDataByType(ThreemaGateway_Model_Messages::TYPE_FILE_MESSAGE, $queryMetaData);
276
            $model->resetFetchOptions();
277
278
            // handle empty results transparently
279
            if (!$images) {
280
                $images = [];
281
            }
282
            if (!$files) {
283
                $files = [];
284
            }
285
286
            // and combine results
287
            /** @var array $result */
288
            $result = array_merge($images, $files);
289
        }
290
291
        if (empty($result)) {
292
            return null;
293
        }
294
295
        return $result;
296
    }
297
298
    /**
299
     * Returns the current state of a particular message.
300
     *
301
     * Only the state of *send* messages can be queried.
302
     * Note: In contrast to most other methods here, this already returns the
303
     * message/delivery state as an integer.
304
     *
305
     * @param  string   $messageSentId The ID of message, which has been send to a user
306
     * @return null|int
307
     */
308
    public function getMessageState($messageSentId)
309
    {
310
        $this->initiate();
311
312
        // reset grouping as it cannot be processed
313
        $this->groupByMessageType = false;
314
315
        /** @var array $result */
316
        $result = $this->getMessageStateHistory($messageSentId, false, 1);
317
        if (!$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
318
            return null;
319
        }
320
321
        // dig into array
322
        $result = reset($result)['ackmsgs'];
323
324
        // as theoretically one delivery message could include multiple
325
        // delivery receipts we formally have to walk through the result
326
        /** @var int $deliveryReceipt */
327
        $deliveryReceipt = 0;
328
        foreach ($result as $content) {
329
            if ($content['receipt_type'] > $deliveryReceipt) {
330
                $deliveryReceipt = $content['receipt_type'];
331
            }
332
        }
333
334
        // finally return the state integer
335
        return $deliveryReceipt;
336
    }
337
338
    /**
339
     * Returns the history of all state changes of a particular message.
340
     *
341
     * Only the state of *send* messages can be queried.
342
     * The result is already ordered from the not so important state to the most
343
     * important one.
344
     *
345
     * @param  string     $messageSentId The ID of message, which has been send to a user
346
     * @param  bool       $getMetaData   Set to false, to speed up the query by not
347
     *                                   asking for meta data (when the state was received etc).
348
     *                                   (default: false)
349
     * @param  int        $limitQuery    When set, only the last x states are returned.
350
     * @return null|array
351
     */
352
    public function getMessageStateHistory($messageSentId, $getMetaData = true, $limitQuery = null)
353
    {
354
        $this->initiate();
355
        /** @var ThreemaGateway_Model_Messages $model */
356
        $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
357
358
        $model->injectFetchOption('where', 'ack_messages.ack_message_id = ?', true);
359
        $model->injectFetchOption('params', $messageSentId, true);
360
361
        $model->setOrder('delivery_state', 'desc');
362
363
        if ($limitQuery) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limitQuery of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
364
            $model->setResultLimit($limitQuery);
365
        }
366
367
        return $model->getMessageDataByType(ThreemaGateway_Model_Messages::TYPE_DELIVERY_MESSAGE, $getMetaData);
368
    }
369
370
    /**
371
     * Returns an array with all type codes currently supported.
372
     *
373
     * @return array
374
     */
375
    public function getTypesArray()
376
    {
377
        return [
378
            ThreemaGateway_Model_Messages::TYPE_DELIVERY_MESSAGE,
379
            ThreemaGateway_Model_Messages::TYPE_FILE_MESSAGE,
380
            ThreemaGateway_Model_Messages::TYPE_IMAGE_MESSAGE,
381
            ThreemaGateway_Model_Messages::TYPE_TEXT_MESSAGE
382
        ];
383
    }
384
385
    /**
386
     * Remove a message from the database.
387
     *
388
     * Note that the message is usually not completly removed and the message ID
389
     * will stay in the database. The exact behaviour depends on the ACP "Harden
390
     * against replay attacks" setting.
391
     * This prevents replay attacks as otherwise a message with the same message
392
     * ID could be inserted again into the database and would therefore be
393
     * considered a new message, which has just been received altghough it
394
     * actually had been received two times.
395
     * However the message ID alone does not reveal any data (as all data &
396
     * meta data, even the message type, is deleted).
397
     *
398
     * @param string $messageId
399
     */
400
    public function removeMessage($messageId)
401
    {
402
        $this->initiate();
403
404
        /** @var ThreemaGateway_DataWriter_Messages $dataWriter */
405
        $dataWriter = XenForo_DataWriter::create('ThreemaGateway_DataWriter_Messages');
406
        $dataWriter->setExistingData($messageId);
407
        $dataWriter->delete();
408
    }
409
410
    /**
411
     * Checks whether the user is allowed to receive messages and prepares Model
412
     * if necessary.
413
     *
414
     * @throws XenForo_Exception
415
     */
416
    protected function initiate()
417
    {
418
        // check permission
419 View Code Duplication
        if (!$this->isPermissionChecked) {
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...
420
            if (!$this->permissions->hasPermission('receive')) {
421
                throw new XenForo_Exception(new XenForo_Phrase('threemagw_permission_error'));
422
            }
423
424
            $this->isPermissionChecked = true;
425
        }
426
427
        if (!$this->isPrepared) {
428
            /** @var ThreemaGateway_Model_Messages $model */
429
            $model = XenForo_Model::create('ThreemaGateway_Model_Messages');
430
            $model->preQuery();
431
432
            $this->isPrepared = true;
433
        }
434
    }
435
436
    /**
437
     * Queries the meta data and the main data of the messages itself.
438
     *
439
     * @param ThreemaGateway_Model_Messages $model
440
     * @param int                           $messageType The type of the message (optional)
441
     */
442
    protected function execute($model, $messageType = null)
443
    {
444
        if ($messageType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageType of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
445
            return $model->getMessageDataByType($messageType, true);
446
        } else {
447
            // query meta data
448
            $metaData = $model->getMessageMetaData();
449
            if (!$metaData) {
450
                return null;
451
            }
452
453
            $model->resetFetchOptions();
454
455
            // query details
456
            return $model->getAllMessageData($metaData, $this->groupByMessageType);
457
        }
458
    }
459
460
    /**
461
     * Replace usual wildcards (?, *) with the ones used by MySQL (%, _).
462
     *
463
     * @param  string $string The string to replace
464
     * @return string
465
     */
466
    protected function replaceWildcards($string)
467
    {
468
        return str_replace([
469
            '%',
470
            '_',
471
            '*',
472
            '?',
473
        ], [
474
            '\%',
475
            '\_',
476
            '%',
477
            '?',
478
        ], $string);
479
    }
480
}
481