getFileList()   C
last analyzed

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 although 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
        if (!$this->isPermissionChecked) {
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