Issues (112)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

brokerlib.php (18 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17
/**
18
 * Broker helper methods.
19
 *
20
 * @package   mod_bigbluebuttonbn
21
 * @copyright 2010 onwards, Blindside Networks Inc
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 * @author    Jesus Federico  (jesus [at] blindsidenetworks [dt] com)
24
 * @author    Fred Dixon  (ffdixon [at] blindsidenetworks [dt] com)
25
 * @author    Darko Miletic  (darko.miletic [at] gmail [dt] com)
26
 */
27
28
defined('MOODLE_INTERNAL') || die();
29
30
/**
31
 * Callback for meeting info.
32
 *
33
 * @param array $bbbsession
34
 * @param array $params
35
 * @param boolean $updatecache
36
 *
37
 * @return string
38
 */
39
function bigbluebuttonbn_broker_meeting_info($bbbsession, $params, $updatecache) {
40
    $callbackresponse = array();
41
    $info = bigbluebuttonbn_get_meeting_info($params['id'], $updatecache);
42
    $callbackresponse['info'] = $info;
43
    $running = false;
44
    if ($info['returncode'] == 'SUCCESS') {
45
        $running = ($info['running'] === 'true');
46
    }
47
    $callbackresponse['running'] = $running;
48
    $status = array();
49
    $status["join_url"] = $bbbsession['joinURL'];
50
    $status["join_button_text"] = get_string('view_conference_action_join', 'bigbluebuttonbn');
51
    $status["end_button_text"] = get_string('view_conference_action_end', 'bigbluebuttonbn');
52
    $participantcount = 0;
53
    if (isset($info['participantCount'])) {
54
        $participantcount = $info['participantCount'];
55
    }
56
    $canjoin = bigbluebuttonbn_broker_meeting_info_can_join($bbbsession, $running, $participantcount);
57
    $status["can_join"] = $canjoin["can_join"];
58
    $status["message"] = $canjoin["message"];
59
    $canend = bigbluebuttonbn_broker_meeting_info_can_end($bbbsession, $running);
60
    $status["can_end"] = $canend["can_end"];
61
    $callbackresponse['status'] = $status;
62
    $callbackresponsedata = json_encode($callbackresponse);
63
    return "{$params['callback']}({$callbackresponsedata});";
64
}
65
66
/**
67
 * Helper for evaluating if meeting can be joined, it is used by meeting info callback.
68
 *
69
 * @param array $bbbsession
70
 * @param boolean $running
71
 * @param boolean $participantcount
72
 *
73
 * @return array
74
 */
75
function bigbluebuttonbn_broker_meeting_info_can_join($bbbsession, $running, $participantcount) {
76
    $status = array("can_join" => false);
77
    if ($running) {
78
        $status["message"] = get_string('view_error_userlimit_reached', 'bigbluebuttonbn');
79 View Code Duplication
        if ($bbbsession['userlimit'] == 0 || $participantcount < $bbbsession['userlimit']) {
0 ignored issues
show
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...
80
            $status["message"] = get_string('view_message_conference_in_progress', 'bigbluebuttonbn');
81
            $status["can_join"] = true;
82
        }
83
        return $status;
84
    }
85
    // If user is administrator, moderator or if is viewer and no waiting is required.
86
    $status["message"] = get_string('view_message_conference_wait_for_moderator', 'bigbluebuttonbn');
87 View Code Duplication
    if ($bbbsession['administrator'] || $bbbsession['moderator'] || !$bbbsession['wait']) {
0 ignored issues
show
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...
88
        $status["message"] = get_string('view_message_conference_room_ready', 'bigbluebuttonbn');
89
        $status["can_join"] = true;
90
    }
91
    return $status;
92
}
93
94
/**
95
 * Helper for evaluating if meeting can be ended, it is used by meeting info callback.
96
 *
97
 * @param array $bbbsession
98
 * @param boolean $running
99
 *
100
 * @return boolean
101
 */
102
function bigbluebuttonbn_broker_meeting_info_can_end($bbbsession, $running) {
103
    if ($running && ($bbbsession['administrator'] || $bbbsession['moderator'])) {
104
        return array("can_end" => true);
105
    }
106
    return array("can_end" => false);
107
}
108
109
/**
110
 * Callback for meeting end.
111
 *
112
 * @param array $bbbsession
113
 * @param array $params
114
 *
115
 * @return string
116
 */
117
function bigbluebuttonbn_broker_meeting_end($bbbsession, $params) {
118
    if (!$bbbsession['administrator'] && !$bbbsession['moderator']) {
119
        header('HTTP/1.0 401 Unauthorized. User not authorized to execute end command');
120
        return;
121
    }
122
    // Execute the end command.
123
    bigbluebuttonbn_end_meeting($params['id'], $bbbsession['modPW']);
124
    // Moodle event logger: Create an event for meeting ended.
125
    if (isset($bbbsession['bigbluebuttonbn'])) {
126
        bigbluebuttonbn_event_log(\mod_bigbluebuttonbn\event\events::$events['meeting_end'], $bbbsession['bigbluebuttonbn']);
127
    }
128
    // Update the cache.
129
    bigbluebuttonbn_get_meeting_info($params['id'], BIGBLUEBUTTONBN_UPDATE_CACHE);
130
    $callbackresponse = array('status' => true);
131
    $callbackresponsedata = json_encode($callbackresponse);
132
    return "{$params['callback']}({$callbackresponsedata});";
133
}
134
135
/**
136
 * Callback for recording links.
137
 *
138
 * @param array $bbbsession
139
 * @param array $params
140
 *
141
 * @return string
142
 */
143
function bigbluebuttonbn_broker_recording_links($bbbsession, $params) {
144
    if (!$bbbsession['managerecordings']) {
145
        header('HTTP/1.0 401 Unauthorized. User not authorized to execute update command');
146
        return;
147
    }
148
    $callbackresponse = array('status' => false);
149
    if (isset($params['id']) && $params['id'] != '') {
150
        $importedall = bigbluebuttonbn_get_recording_imported_instances($params['id']);
151
        $callbackresponse['status'] = true;
152
        $callbackresponse['links'] = count($importedall);
153
    }
154
    $callbackresponsedata = json_encode($callbackresponse);
155
    return "{$params['callback']}({$callbackresponsedata});";
156
}
157
158
/**
159
 * Callback for recording info.
160
 *
161
 * @param array $bbbsession
162
 * @param array $params
163
 * @param boolean $showroom
164
 *
165
 * @return string
166
 */
167
function bigbluebuttonbn_broker_recording_info($bbbsession, $params, $showroom) {
168
    if (!$bbbsession['managerecordings']) {
169
        header('HTTP/1.0 401 Unauthorized. User not authorized to execute command');
170
        return;
171
    }
172
    $callbackresponse = array('status' => true, 'found' => false);
173
    $courseid = $bbbsession['course']->id;
174
    $bigbluebuttonbnid = null;
175
    if ($showroom) {
176
        $bigbluebuttonbnid = $bbbsession['bigbluebuttonbn']->id;
177
    }
178
    $includedeleted = $bbbsession['bigbluebuttonbn']->recordings_deleted;
179
    // Retrieve the array of imported recordings.
180
    $recordings = bigbluebuttonbn_get_allrecordings($courseid, $bigbluebuttonbnid, $showroom, $includedeleted);
181
    if (array_key_exists($params['id'], $recordings)) {
182
        // Look up for an update on the imported recording.
183 View Code Duplication
        if (!array_key_exists('messageKey', $recordings[$params['id']])) {
0 ignored issues
show
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...
184
            // The recording was found.
185
            $callbackresponse = bigbluebuttonbn_broker_recording_info_current($recordings[$params['id']], $params);
186
        }
187
        $callbackresponsedata = json_encode($callbackresponse);
188
        return "{$params['callback']}({$callbackresponsedata});";
189
    }
190
    // As the recordingid was not identified as imported recording link, look up for a real recording.
191
    $recordings = bigbluebuttonbn_get_recordings_array($params['idx'], $params['id']);
192 View Code Duplication
    if (array_key_exists($params['id'], $recordings)) {
0 ignored issues
show
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...
193
        // The recording was found.
194
        $callbackresponse = bigbluebuttonbn_broker_recording_info_current($recordings[$params['id']], $params);
195
    }
196
    $callbackresponsedata = json_encode($callbackresponse);
197
    return "{$params['callback']}({$callbackresponsedata});";
198
}
199
200
/**
201
 * Data used as for the callback for recording info.
202
 *
203
 * @param array $recording
204
 * @param array $params
205
 *
206
 * @return string
207
 */
208
function bigbluebuttonbn_broker_recording_info_current($recording, $params) {
209
    $callbackresponse['status'] = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$callbackresponse was never initialized. Although not strictly required by PHP, it is generally a good practice to add $callbackresponse = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
210
    $callbackresponse['found'] = true;
211
    $callbackresponse['published'] = (string) $recording['published'];
212
    if (!isset($params['meta']) || empty($params['meta'])) {
213
        return $callbackresponse;
214
    }
215
    $meta = json_decode($params['meta'], true);
216
    foreach (array_keys($meta) as $key) {
217
        $callbackresponse[$key] = '';
218
        if (isset($recording[$key])) {
219
            $callbackresponse[$key] = trim($recording[$key]);
220
        }
221
    }
222
    return $callbackresponse;
223
}
224
225
/**
226
 * Callback for recording play.
227
 *
228
 * @param array $params
229
 *
230
 * @return string
231
 */
232
function bigbluebuttonbn_broker_recording_play($params) {
233
    $callbackresponse = array('status' => true, 'found' => false);
234
    $recordings = bigbluebuttonbn_get_recordings_array($params['idx'], $params['id']);
235 View Code Duplication
    if (array_key_exists($params['id'], $recordings)) {
0 ignored issues
show
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...
236
        // The recording was found.
237
        $callbackresponse = bigbluebuttonbn_broker_recording_info_current($recordings[$params['id']], $params);
238
    }
239
    $callbackresponsedata = json_encode($callbackresponse);
240
    return "{$params['callback']}({$callbackresponsedata});";
241
}
242
243
/**
244
 * Callback for recording action.
245
 * (publush/unpublish/protect/unprotect/edit/delete)
246
 *
247
 * @param array $bbbsession
248
 * @param array $params
249
 * @param boolean $showroom
250
 *
251
 * @return string
252
 */
253
function bigbluebuttonbn_broker_recording_action($bbbsession, $params, $showroom) {
254
    if (!$bbbsession['managerecordings']) {
255
        header('HTTP/1.0 401 Unauthorized. User not authorized to execute end command');
256
        return;
257
    }
258
    // Retrieve array of recordings that includes real and imported.
259
    $bigbluebuttonbnid = null;
260
    if ($showroom) {
261
        $bigbluebuttonbnid = $bbbsession['bigbluebuttonbn']->id;
262
    }
263
    $recordings = bigbluebuttonbn_get_allrecordings(
264
        $bbbsession['course']->id,
265
        $bigbluebuttonbnid,
266
        $showroom,
267
        $bbbsession['bigbluebuttonbn']->recordings_deleted
268
    );
269
270
    $action = strtolower($params['action']);
271
    // Excecute action.
272
    $callbackresponse = bigbluebuttonbn_broker_recording_action_perform($action, $params, $recordings);
273
    if ($callbackresponse['status']) {
274
        // Moodle event logger: Create an event for action performed on recording.
275
        bigbluebuttonbn_event_log(
276
            \mod_bigbluebuttonbn\event\events::$events[$action],
277
            $bbbsession['bigbluebuttonbn'],
278
            ['other' => $params['id']]
279
        );
280
    }
281
    $callbackresponsedata = json_encode($callbackresponse);
282
    return "{$params['callback']}({$callbackresponsedata});";
283
}
284
285
/**
286
 * Helper for performing actions on recordings.
287
 * (publush/unpublish/protect/unprotect/edit/delete)
288
 *
289
 * @param string $action
290
 * @param array $params
291
 * @param array $recordings
292
 *
293
 * @return array
294
 */
295
function bigbluebuttonbn_broker_recording_action_perform($action, $params, $recordings) {
296
    if ($action == 'recording_publish') {
297
        return bigbluebuttonbn_broker_recording_action_publish($params, $recordings);
298
    }
299
    if ($action == 'recording_unpublish') {
300
        return bigbluebuttonbn_broker_recording_action_unpublish($params, $recordings);
301
    }
302
    if ($action == 'recording_edit') {
303
        return bigbluebuttonbn_broker_recording_action_edit($params, $recordings);
304
    }
305
    if ($action == 'recording_delete') {
306
        return bigbluebuttonbn_broker_recording_action_delete($params, $recordings);
307
    }
308
    if ($action == 'recording_protect') {
309
        return bigbluebuttonbn_broker_recording_action_protect($params, $recordings);
310
    }
311
    if ($action == 'recording_unprotect') {
312
        return bigbluebuttonbn_broker_recording_action_unprotect($params, $recordings);
313
    }
314
}
315
316
/**
317
 * Helper for performing publish on recordings.
318
 *
319
 * @param array $params
320
 * @param array $recordings
321
 *
322
 * @return array
323
 */
324 View Code Duplication
function bigbluebuttonbn_broker_recording_action_publish($params, $recordings) {
0 ignored issues
show
This function seems to be duplicated in 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...
325
    if (bigbluebuttonbn_broker_recording_is_imported($recordings, $params['id'])) {
326
        // Execute publish on imported recording link, if the real recording is published.
327
        $realrecordings = bigbluebuttonbn_get_recordings_array(
328
            $recordings[$params['id']]['meetingID'],
329
            $recordings[$params['id']]['recordID']
330
        );
331
        // Only if the physical recording exist and it is published, execute publish on imported recording link.
332
        if (!isset($realrecordings[$params['id']])) {
333
            return array(
334
                'status' => false,
335
                'message' => get_string('view_recording_publish_link_deleted', 'bigbluebuttonbn')
336
            );
337
        }
338
        if ($realrecordings[$params['id']]['published'] !== 'true') {
339
            return array(
340
                'status' => false,
341
                'message' => get_string('view_recording_publish_link_not_published', 'bigbluebuttonbn')
342
            );
343
        }
344
        return array(
345
            'status' => bigbluebuttonbn_publish_recording_imported(
346
                $recordings[$params['id']]['imported'],
347
                true
348
            )
349
        );
350
    }
351
    // As the recordingid was not identified as imported recording link, execute actual publish.
352
    return array(
353
        'status' => bigbluebuttonbn_publish_recordings(
354
            $params['id'],
355
            'true'
356
        )
357
    );
358
}
359
360
/**
361
 * Helper for performing unprotect on recordings.
362
 *
363
 * @param array $params
364
 * @param array $recordings
365
 *
366
 * @return array
367
 */
368 View Code Duplication
function bigbluebuttonbn_broker_recording_action_unprotect($params, $recordings) {
0 ignored issues
show
This function seems to be duplicated in 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...
369
    if (bigbluebuttonbn_broker_recording_is_imported($recordings, $params['id'])) {
370
        // Execute unprotect on imported recording link, if the real recording is unprotected.
371
        $realrecordings = bigbluebuttonbn_get_recordings_array(
372
            $recordings[$params['id']]['meetingID'],
373
            $recordings[$params['id']]['recordID']
374
        );
375
        // Only if the physical recording exist and it is published, execute unprotect on imported recording link.
376
        if (!isset($realrecordings[$params['id']])) {
377
            return array(
378
                'status' => false,
379
                'message' => get_string('view_recording_unprotect_link_deleted', 'bigbluebuttonbn')
380
            );
381
        }
382
        if ($realrecordings[$params['id']]['protected'] === 'true') {
383
            return array(
384
                'status' => false,
385
                'message' => get_string('view_recording_unprotect_link_not_unprotected', 'bigbluebuttonbn')
386
            );
387
        }
388
        return array(
389
            'status' => bigbluebuttonbn_protect_recording_imported(
390
                $recordings[$params['id']]['imported'],
391
                false
392
            )
393
        );
394
    }
395
    // As the recordingid was not identified as imported recording link, execute actual uprotect.
396
    return array(
397
        'status' => bigbluebuttonbn_update_recordings(
398
            $params['id'],
399
            array('protect' => 'false')
400
        )
401
    );
402
}
403
404
/**
405
 * Helper for performing unpublish on recordings.
406
 *
407
 * @param array $params
408
 * @param array $recordings
409
 *
410
 * @return array
411
 */
412 View Code Duplication
function bigbluebuttonbn_broker_recording_action_unpublish($params, $recordings) {
0 ignored issues
show
This function seems to be duplicated in 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...
413
    global $DB;
414
    if (bigbluebuttonbn_broker_recording_is_imported($recordings, $params['id'])) {
415
        // Execute unpublish or protect on imported recording link.
416
        return array(
417
            'status' => bigbluebuttonbn_publish_recording_imported(
418
                $recordings[$params['id']]['imported'],
419
                false
420
            )
421
        );
422
    }
423
    // As the recordingid was not identified as imported recording link, execute unpublish on a real recording.
424
    // First: Unpublish imported links associated to the recording.
425
    $importedall = bigbluebuttonbn_get_recording_imported_instances($params['id']);
426
    foreach ($importedall as $key => $record) {
427
        $meta = json_decode($record->meta, true);
428
        // Prepare data for the update.
429
        $meta['recording']['published'] = 'false';
430
        $importedall[$key]->meta = json_encode($meta);
431
        // Proceed with the update.
432
        $DB->update_record('bigbluebuttonbn_logs', $importedall[$key]);
433
    }
434
    // Second: Execute the actual unpublish.
435
    return array(
436
        'status' => bigbluebuttonbn_publish_recordings(
437
            $params['id'],
438
            'false'
439
        )
440
    );
441
}
442
443
/**
444
 * Helper for performing protect on recordings.
445
 *
446
 * @param array $params
447
 * @param array $recordings
448
 *
449
 * @return array
450
 */
451 View Code Duplication
function bigbluebuttonbn_broker_recording_action_protect($params, $recordings) {
0 ignored issues
show
This function seems to be duplicated in 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...
452
    global $DB;
453
    if (bigbluebuttonbn_broker_recording_is_imported($recordings, $params['id'])) {
454
        // Execute unpublish or protect on imported recording link.
455
        return array(
456
            'status' => bigbluebuttonbn_protect_recording_imported(
457
                $recordings[$params['id']]['imported'],
458
                true
459
            )
460
        );
461
    }
462
    // As the recordingid was not identified as imported recording link, execute protect on a real recording.
463
    // First: Protect imported links associated to the recording.
464
    $importedall = bigbluebuttonbn_get_recording_imported_instances($params['id']);
465
    foreach ($importedall as $key => $record) {
466
        $meta = json_decode($record->meta, true);
467
        // Prepare data for the update.
468
        $meta['recording']['protected'] = 'true';
469
        $importedall[$key]->meta = json_encode($meta);
470
        // Proceed with the update.
471
        $DB->update_record('bigbluebuttonbn_logs', $importedall[$key]);
472
    }
473
    // Second: Execute the actual protect.
474
    return array(
475
        'status' => bigbluebuttonbn_update_recordings(
476
            $params['id'],
477
            array('protect' => 'true')
478
        )
479
    );
480
}
481
482
/**
483
 * Helper for performing delete on recordings.
484
 *
485
 * @param array $params
486
 * @param array $recordings
487
 *
488
 * @return array
489
 */
490
function bigbluebuttonbn_broker_recording_action_delete($params, $recordings) {
491
    global $DB;
492
    if (bigbluebuttonbn_broker_recording_is_imported($recordings, $params['id'])) {
493
        // Execute delete on imported recording link.
494
        return array(
495
            'status' => bigbluebuttonbn_delete_recording_imported(
496
                $recordings[$params['id']]['imported']
497
            )
498
        );
499
    }
500
    // As the recordingid was not identified as imported recording link, execute delete on a real recording.
501
    // First: Delete imported links associated to the recording.
502
    $importedall = bigbluebuttonbn_get_recording_imported_instances($params['id']);
503
    if ($importedall > 0) {
504
        foreach (array_keys($importedall) as $key) {
505
            // Execute delete on imported links.
506
            $DB->delete_records('bigbluebuttonbn_logs', array('id' => $key));
507
        }
508
    }
509
    // Second: Execute the actual delete.
510
    return array(
511
        'status' => bigbluebuttonbn_delete_recordings($params['id'])
512
    );
513
}
514
515
/**
516
 * Helper for performing edit on recordings.
517
 *
518
 * @param array $params
519
 * @param array $recordings
520
 *
521
 * @return array
522
 */
523
function bigbluebuttonbn_broker_recording_action_edit($params, $recordings) {
524
    if (bigbluebuttonbn_broker_recording_is_imported($recordings, $params['id'])) {
525
        // Execute update on imported recording link.
526
        return array(
527
            'status' => bigbluebuttonbn_update_recording_imported(
528
                $recordings[$params['id']]['imported'],
529
                json_decode($params['meta'], true)
530
            )
531
        );
532
    }
533
534
    // As the recordingid was not identified as imported recording link, execute update on a real recording.
535
    // (No need to update imported links as the update only affects the actual recording).
536
    // Execute update on actual recording.
537
    return array(
538
        'status' => bigbluebuttonbn_update_recordings(
539
            $params['id'],
540
            json_decode($params['meta'])
541
        )
542
    );
543
}
544
545
/**
546
 * Helper for responding when recording ready is performed.
547
 *
548
 * @param array $params
549
 * @param object $bigbluebuttonbn
550
 *
551
 * @return void
552
 */
553
function bigbluebuttonbn_broker_recording_ready($params, $bigbluebuttonbn) {
554
    // Decodes the received JWT string.
555
    try {
556
        $decodedparameters = \Firebase\JWT\JWT::decode(
557
            $params['signed_parameters'],
558
            \mod_bigbluebuttonbn\locallib\config::get('shared_secret'),
0 ignored issues
show
It seems like \mod_bigbluebuttonbn\loc...g::get('shared_secret') targeting mod_bigbluebuttonbn\locallib\config::get() can also be of type boolean or null; however, Firebase\JWT\JWT::decode() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
559
            array('HS256')
560
        );
561
    } catch (Exception $e) {
562
        $error = 'Caught exception: '.$e->getMessage();
563
        header('HTTP/1.0 400 Bad Request. '.$error);
564
        return;
565
    }
566
    // Validate that the bigbluebuttonbn activity corresponds to the meeting_id received.
567
    $meetingidelements = explode('[', $decodedparameters->meeting_id);
568
    $meetingidelements = explode('-', $meetingidelements[0]);
569
570
    if (!isset($bigbluebuttonbn) || $bigbluebuttonbn->meetingid != $meetingidelements[0]) {
571
        header('HTTP/1.0 410 Gone. The activity may have been deleted');
572
        return;
573
    }
574
    // Sends the messages.
575
    try {
576
        // Workaround for CONTRIB-7438.
577
        // Proceed as before when no record_id is provided.
578
        if (!isset($decodedparameters->record_id)) {
579
            bigbluebuttonbn_send_notification_recording_ready($bigbluebuttonbn);
580
            header('HTTP/1.0 202 Accepted');
581
            return;
582
        }
583
        // We make sure messages are sent only once.
584
        if (bigbluebuttonbn_get_count_callback_event_log($decodedparameters->record_id) == 0) {
585
            bigbluebuttonbn_send_notification_recording_ready($bigbluebuttonbn);
586
        }
587
        $overrides = array('meetingid' => $decodedparameters->meeting_id);
588
        $meta['recordid'] = $decodedparameters->record_id;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$meta was never initialized. Although not strictly required by PHP, it is generally a good practice to add $meta = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
589
        $meta['callback'] = 'recording_ready';
590
        bigbluebuttonbn_log($bigbluebuttonbn, BIGBLUEBUTTON_LOG_EVENT_CALLBACK, $overrides, json_encode($meta));
591
        header('HTTP/1.0 202 Accepted');
592
    } catch (Exception $e) {
593
        $error = 'Caught exception: '.$e->getMessage();
594
        header('HTTP/1.0 503 Service Unavailable. '.$error);
595
    }
596
}
597
598
/**
599
 * Helper for performing import on recordings.
600
 *
601
 * @param array $bbbsession
602
 * @param array $params
603
 *
604
 * @return string
605
 */
606
function bigbluebuttonbn_broker_recording_import($bbbsession, $params) {
607
    global $SESSION;
608
    if (!$bbbsession['managerecordings']) {
609
        header('HTTP/1.0 401 Unauthorized. User not authorized to execute end command');
610
        return;
611
    }
612
    $importrecordings = $SESSION->bigbluebuttonbn_importrecordings;
613
    if (!isset($importrecordings[$params['id']])) {
614
        $error = "Recording {$params['id']} could not be found. It can not be imported";
615
        header('HTTP/1.0 404 Not found. '.$error);
616
        return;
617
    }
618
    $callbackresponse = array('status' => true);
619
    $importrecordings[$params['id']]['imported'] = true;
620
    $overrides = array('meetingid' => $importrecordings[$params['id']]['meetingID']);
621
    $meta = '{"recording":'.json_encode($importrecordings[$params['id']]).'}';
622
    bigbluebuttonbn_log($bbbsession['bigbluebuttonbn'], BIGBLUEBUTTONBN_LOG_EVENT_IMPORT, $overrides, $meta);
623
    // Moodle event logger: Create an event for recording imported.
624
    if (isset($bbbsession['bigbluebutton']) && isset($bbbsession['cm'])) {
625
        bigbluebuttonbn_event_log(
626
            \mod_bigbluebuttonbn\event\events::$events['recording_import'],
627
            $bbbsession['bigbluebuttonbn'],
628
            ['other' => $params['id']]
629
        );
630
    }
631
    $callbackresponsedata = json_encode($callbackresponse);
632
    return "{$params['callback']}({$callbackresponsedata});";
633
}
634
635
/**
636
 * Helper for responding when storing live meeting events is requested.
637
 *
638
 * The callback with a POST request includes:
639
 *  - Authentication: Bearer <A JWT token containing {"exp":<TIMESTAMP>} encoded with HS512>
640
 *  - Content Type: application/json
641
 *  - Body: <A JSON Object>
642
 *
643
 * @param object $bigbluebuttonbn
644
 *
645
 * @return void
646
 */
647
function bigbluebuttonbn_broker_meeting_events($bigbluebuttonbn) {
648
    // Decodes the received JWT string.
649
    try {
650
        // Get the HTTP headers (getallheaders is a PHP function that may only work with Apache).
651
        $headers = getallheaders();
652
653
        // Pull the Bearer from the headers.
654
        if (!array_key_exists('Authorization', $headers)) {
655
            $msg = 'Authorization failed';
656
            header('HTTP/1.0 400 Bad Request. ' . $msg);
657
            return;
658
        }
659
        $authorization = explode(" ", $headers['Authorization']);
660
661
        // Verify the authenticity of the request.
662
        $token = \Firebase\JWT\JWT::decode(
0 ignored issues
show
$token is not used, you could remove the assignment.

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

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

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

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

Loading history...
663
            $authorization[1],
664
            \mod_bigbluebuttonbn\locallib\config::get('shared_secret'),
0 ignored issues
show
It seems like \mod_bigbluebuttonbn\loc...g::get('shared_secret') targeting mod_bigbluebuttonbn\locallib\config::get() can also be of type boolean or null; however, Firebase\JWT\JWT::decode() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
665
            array('HS512')
666
        );
667
668
        // Get JSON string from the body.
669
        $jsonstr = file_get_contents('php://input');
670
671
        // Convert JSON string to a JSON object.
672
        $jsonobj = json_decode($jsonstr);
673
    } catch (Exception $e) {
674
        $msg = 'Caught exception: ' . $e->getMessage();
675
        header('HTTP/1.0 400 Bad Request. ' . $msg);
676
        return;
677
    }
678
679
    // Validate that the bigbluebuttonbn activity corresponds to the meeting_id received.
680
    $meetingidelements = explode('[', $jsonobj->{'meeting_id'});
681
    $meetingidelements = explode('-', $meetingidelements[0]);
682
    if (!isset($bigbluebuttonbn) || $bigbluebuttonbn->meetingid != $meetingidelements[0]) {
683
        $msg = 'The activity may have been deleted';
684
        header('HTTP/1.0 410 Gone. ' . $msg);
685
        return;
686
    }
687
688
    // We make sure events are processed only once.
689
    $overrides = array('meetingid' => $jsonobj->{'meeting_id'});
690
    $meta['recordid'] = $jsonobj->{'internal_meeting_id'};
0 ignored issues
show
Coding Style Comprehensibility introduced by
$meta was never initialized. Although not strictly required by PHP, it is generally a good practice to add $meta = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
691
    $meta['callback'] = 'meeting_events';
692
    bigbluebuttonbn_log($bigbluebuttonbn, BIGBLUEBUTTON_LOG_EVENT_CALLBACK, $overrides, json_encode($meta));
693
    if (bigbluebuttonbn_get_count_callback_event_log($meta['recordid'], 'meeting_events') == 1) {
694
        // Process the events.
695
        bigbluebuttonbn_process_meeting_events($bigbluebuttonbn, $jsonobj);
696
        header('HTTP/1.0 200 Accepted. Enqueued.');
697
        return;
698
    }
699
700
    header('HTTP/1.0 202 Accepted. Already processed.');
701
}
702
703
/**
704
 * Helper for validating the parameters received.
705
 *
706
 * @param array $params
707
 *
708
 * @return string
709
 */
710
function bigbluebuttonbn_broker_validate_parameters($params) {
711
    $action = strtolower($params['action']);
712
    $requiredparams = bigbluebuttonbn_broker_required_parameters();
713
    if (!array_key_exists($action, $requiredparams)) {
714
        return 'Action '.$params['action'].' can not be performed.';
715
    }
716
    return bigbluebuttonbn_broker_validate_parameters_message($params, $requiredparams[$action]);
717
}
718
719
/**
720
 * Helper for responding after the parameters received are validated.
721
 *
722
 * @param array $params
723
 * @param array $requiredparams
724
 *
725
 * @return string
726
 */
727
function bigbluebuttonbn_broker_validate_parameters_message($params, $requiredparams) {
728
    foreach ($requiredparams as $param => $message) {
729
        if (!array_key_exists($param, $params) || $params[$param] == '') {
730
            return $message;
731
        }
732
    }
733
}
734
735
/**
736
 * Helper for definig rules for validating required parameters.
737
 */
738
function bigbluebuttonbn_broker_required_parameters() {
739
    $params['server_ping'] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
740
        'callback' => 'This request must include a javascript callback.',
741
        'id' => 'The meetingID must be specified.'
742
    ];
743
    $params['meeting_info'] = [
744
        'callback' => 'This request must include a javascript callback.',
745
        'id' => 'The meetingID must be specified.'
746
    ];
747
    $params['meeting_end'] = [
748
        'callback' => 'This request must include a javascript callback.',
749
        'id' => 'The meetingID must be specified.'
750
    ];
751
    $params['recording_play'] = [
752
        'callback' => 'This request must include a javascript callback.',
753
        'id' => 'The recordingID must be specified.'
754
    ];
755
    $params['recording_info'] = [
756
        'callback' => 'This request must include a javascript callback.',
757
        'id' => 'The recordingID must be specified.'
758
    ];
759
    $params['recording_links'] = [
760
        'callback' => 'This request must include a javascript callback.',
761
        'id' => 'The recordingID must be specified.'
762
    ];
763
    $params['recording_publish'] = [
764
        'callback' => 'This request must include a javascript callback.',
765
        'id' => 'The recordingID must be specified.'
766
    ];
767
    $params['recording_unpublish'] = [
768
        'callback' => 'This request must include a javascript callback.',
769
        'id' => 'The recordingID must be specified.'
770
    ];
771
    $params['recording_delete'] = [
772
        'callback' => 'This request must include a javascript callback.',
773
        'id' => 'The recordingID must be specified.'
774
    ];
775
    $params['recording_protect'] = [
776
        'callback' => 'This request must include a javascript callback.',
777
        'id' => 'The recordingID must be specified.'
778
    ];
779
    $params['recording_unprotect'] = [
780
        'callback' => 'This request must include a javascript callback.',
781
        'id' => 'The recordingID must be specified.'
782
    ];
783
    $params['recording_edit'] = [
784
        'callback' => 'This request must include a javascript callback.',
785
        'id' => 'The recordingID must be specified.',
786
        'meta' => 'A meta parameter should be included'
787
    ];
788
    $params['recording_import'] = [
789
        'callback' => 'This request must include a javascript callback.',
790
        'id' => 'The recordingID must be specified.'
791
    ];
792
    $params['recording_ready'] = [
793
        'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.',
794
        'signed_parameters' => 'A JWT encoded string must be included as [signed_parameters].'
795
    ];
796
    $params['recording_list_table'] = [
797
        'id' => 'The Bigbluebutton activity id must be specified.',
798
        'callback' => 'This request must include a javascript callback.',
799
    ];
800
    $params['meeting_events'] = [
801
        'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.'
802
    ];
803
    $params['completion_validate'] = [
804
        'callback' => 'This request must include a javascript callback.',
805
        'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.'
806
    ];
807
    return $params;
808
}
809
810
/**
811
 * Helper for validating if a recording is an imported link or a real one.
812
 *
813
 * @param array $recordings
814
 * @param string $recordingid
815
 *
816
 * @return boolean
817
 */
818
function bigbluebuttonbn_broker_recording_is_imported($recordings, $recordingid) {
819
    return (isset($recordings[$recordingid]) && isset($recordings[$recordingid]['imported']));
820
}
821
822
/**
823
 * Helper for performing validation of completion.
824
 *
825
 * @param object $bigbluebuttonbn
826
 * @param array $params
827
 *
828
 * @return void
829
 */
830
function bigbluebuttonbn_broker_completion_validate($bigbluebuttonbn, $params) {
831
    $context = \context_course::instance($bigbluebuttonbn->course);
832
    // Get list with all the users enrolled in the course.
833
    list($sort, $sqlparams) = users_order_by_sql('u');
0 ignored issues
show
The assignment to $sqlparams is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
834
    $users = get_enrolled_users($context, 'mod/bigbluebuttonbn:view', 0, 'u.*', $sort);
835
    foreach ($users as $user) {
836
        // Enqueue a task for processing the completion.
837
        bigbluebuttonbn_enqueue_completion_update($bigbluebuttonbn, $user->id);
838
    }
839
    $callbackresponse['status'] = 200;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$callbackresponse was never initialized. Although not strictly required by PHP, it is generally a good practice to add $callbackresponse = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
840
    $callbackresponsedata = json_encode($callbackresponse);
841
    return "{$params['callback']}({$callbackresponsedata});";
842
}
843
844
/**
845
 * Helper function builds the data used by the recording table.
846
 *
847
 * @param array $bbbsession
848
 * @param array $params
849
 * @param array $enabledfeatures
850
 *
851
 * @return array
852
 * @throws coding_exception
853
 */
854
function bigbluebuttonbn_broker_get_recording_data($bbbsession, $params, $enabledfeatures) {
855
    $tools = ['protect', 'publish', 'delete'];
856
    $recordings = bigbluebutton_get_recordings_for_table_view($bbbsession, $enabledfeatures);
857
    $tabledata = array();
858
    $typeprofiles = bigbluebuttonbn_get_instance_type_profiles();
859
    $tabledata['activity'] = bigbluebuttonbn_view_get_activity_status($bbbsession);
860
    $tabledata['ping_interval'] = (int) \mod_bigbluebuttonbn\locallib\config::get('waitformoderator_ping_interval') * 1000;
861
    $tabledata['locale'] = bigbluebuttonbn_get_localcode();
862
    $tabledata['profile_features'] = $typeprofiles[0]['features'];
863
    $tabledata['recordings_html'] = $bbbsession['bigbluebuttonbn']->recordings_html == '1';
864
865
    $data = array();
866
    // Build table content.
867
    if (isset($recordings) && !array_key_exists('messageKey', $recordings)) {
868
        // There are recordings for this meeting.
869
        foreach ($recordings as $recording) {
870
            $rowdata = bigbluebuttonbn_get_recording_data_row($bbbsession, $recording, $tools);
871
            if (!empty($rowdata)) {
872
                array_push($data, $rowdata);
873
            }
874
        }
875
    }
876
877
    $columns = array();
878
    // Initialize table headers.
879
    $columns[] = array('key' => 'playback', 'label' => get_string('view_recording_playback', 'bigbluebuttonbn'),
880
        'width' => '125px', 'allowHTML' => true); // Note: here a strange bug noted whilst changing the columns, ref CONTRIB.
881
    $columns[] = array('key' => 'recording', 'label' => get_string('view_recording_name', 'bigbluebuttonbn'),
882
        'width' => '125px', 'allowHTML' => true);
883
    $columns[] = array('key' => 'description', 'label' => get_string('view_recording_description', 'bigbluebuttonbn'),
884
        'sortable' => true, 'width' => '250px', 'allowHTML' => true);
885
    if (bigbluebuttonbn_get_recording_data_preview_enabled($bbbsession)) {
886
        $columns[] = array('key' => 'preview', 'label' => get_string('view_recording_preview', 'bigbluebuttonbn'),
887
            'width' => '250px', 'allowHTML' => true);
888
    }
889
    $columns[] = array('key' => 'date', 'label' => get_string('view_recording_date', 'bigbluebuttonbn'),
890
        'sortable' => true, 'width' => '225px', 'allowHTML' => true);
891
    $columns[] = array('key' => 'duration', 'label' => get_string('view_recording_duration', 'bigbluebuttonbn'),
892
        'width' => '50px');
893
    if ($bbbsession['managerecordings']) {
894
        $columns[] = array('key' => 'actionbar', 'label' => get_string('view_recording_actionbar', 'bigbluebuttonbn'),
895
            'width' => '120px', 'allowHTML' => true);
896
    }
897
898
    $tabledata['data'] = array(
899
        'columns' => $columns,
900
        'data' => $data
901
    );
902
    $callbackresponsedata = json_encode($tabledata);
903
    return "{$params['callback']}({$callbackresponsedata});";
904
}