chamilo /
chamilo-lms
| 1 | <?php |
||||
| 2 | /** |
||||
| 3 | * (c) Copyright Ascensio System SIA 2025. |
||||
| 4 | * |
||||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||||
| 6 | * you may not use this file except in compliance with the License. |
||||
| 7 | * You may obtain a copy of the License at |
||||
| 8 | * |
||||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
||||
| 10 | * |
||||
| 11 | * Unless required by applicable law or agreed to in writing, software |
||||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
||||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
| 14 | * See the License for the specific language governing permissions and |
||||
| 15 | * limitations under the License. |
||||
| 16 | */ |
||||
| 17 | require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
| 18 | |||||
| 19 | use ChamiloSession as Session; |
||||
| 20 | use Onlyoffice\DocsIntegrationSdk\Models\Callback as OnlyofficeCallback; |
||||
| 21 | use Onlyoffice\DocsIntegrationSdk\Models\CallbackDocStatus; |
||||
| 22 | |||||
| 23 | $plugin = OnlyofficePlugin::create(); |
||||
| 24 | |||||
| 25 | if (isset($_GET['hash']) && !empty($_GET['hash'])) { |
||||
| 26 | @header('Content-Type: application/json; charset=utf-8'); |
||||
|
0 ignored issues
–
show
Are you sure the usage of
header('Content-Type: ap...n/json; charset=utf-8') is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 27 | @header('X-Robots-Tag: noindex'); |
||||
|
0 ignored issues
–
show
Are you sure the usage of
header('X-Robots-Tag: noindex') is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 28 | @header('X-Content-Type-Options: nosniff'); |
||||
|
0 ignored issues
–
show
Are you sure the usage of
header('X-Content-Type-Options: nosniff') is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 29 | |||||
| 30 | $appSettings = new OnlyofficeAppsettings($plugin); |
||||
| 31 | $jwtManager = new OnlyofficeJwtManager($appSettings); |
||||
| 32 | list($hashData, $error) = $jwtManager->readHash($_GET['hash'], api_get_security_key()); |
||||
|
0 ignored issues
–
show
The function
api_get_security_key was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 33 | if (null === $hashData) { |
||||
| 34 | error_log("ONLYOFFICE CALLBACK: ERROR - Invalid hash: ".$error); |
||||
| 35 | exit(json_encode(['status' => 'error', 'error' => $error])); |
||||
| 36 | } |
||||
| 37 | |||||
| 38 | $type = $hashData->type; |
||||
| 39 | $courseId = $hashData->courseId; |
||||
| 40 | $userId = $hashData->userId; |
||||
| 41 | $docId = $hashData->docId; |
||||
| 42 | $groupId = $hashData->groupId; |
||||
| 43 | $sessionId = $hashData->sessionId; |
||||
| 44 | $docPath = isset($_GET['docPath']) ? urldecode($_GET['docPath']) : ($hashData->docPath ?? null); |
||||
| 45 | // Load courseCode for various uses from global scope in other functions |
||||
| 46 | $courseInfo = api_get_course_info_by_id($courseId); |
||||
| 47 | $courseCode = $courseInfo['code']; |
||||
| 48 | |||||
| 49 | if (!empty($userId)) { |
||||
| 50 | $userInfo = api_get_user_info($userId); |
||||
| 51 | } else { |
||||
| 52 | exit(json_encode(['error' => 'User not found'])); |
||||
| 53 | } |
||||
| 54 | |||||
| 55 | if (api_is_anonymous()) { |
||||
| 56 | $loggedUser = [ |
||||
| 57 | 'user_id' => $userInfo['id'], |
||||
| 58 | 'status' => $userInfo['status'], |
||||
| 59 | 'uidReset' => true, |
||||
| 60 | ]; |
||||
| 61 | |||||
| 62 | Session::write('_user', $loggedUser); |
||||
| 63 | Login::init_user($loggedUser['user_id'], true); |
||||
| 64 | } else { |
||||
| 65 | $userId = api_get_user_id(); |
||||
| 66 | } |
||||
| 67 | |||||
| 68 | switch ($type) { |
||||
| 69 | case 'track': |
||||
| 70 | $callbackResponseArray = track(); |
||||
| 71 | exit(json_encode($callbackResponseArray)); |
||||
| 72 | case 'download': |
||||
| 73 | $callbackResponseArray = download(); |
||||
| 74 | exit(json_encode($callbackResponseArray)); |
||||
| 75 | case 'empty': |
||||
| 76 | $callbackResponseArray = emptyFile(); |
||||
|
0 ignored issues
–
show
Are you sure the assignment to
$callbackResponseArray is correct as emptyFile() seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 77 | exit(json_encode($callbackResponseArray)); |
||||
| 78 | default: |
||||
| 79 | exit(json_encode(['status' => 'error', 'error' => '404 Method not found'])); |
||||
| 80 | } |
||||
| 81 | } |
||||
| 82 | |||||
| 83 | /** |
||||
| 84 | * Handle request from the document server with the document status information. |
||||
| 85 | */ |
||||
| 86 | function track(): array |
||||
| 87 | { |
||||
| 88 | global $courseCode; |
||||
| 89 | global $userId; |
||||
| 90 | global $docId; |
||||
| 91 | global $docPath; |
||||
| 92 | global $groupId; |
||||
| 93 | global $sessionId; |
||||
| 94 | global $courseInfo; |
||||
| 95 | global $appSettings; |
||||
| 96 | global $jwtManager; |
||||
| 97 | |||||
| 98 | $body_stream = file_get_contents('php://input'); |
||||
| 99 | if ($body_stream === false) { |
||||
| 100 | return ['error' => 'Bad Request']; |
||||
| 101 | } |
||||
| 102 | |||||
| 103 | $data = json_decode($body_stream, true); |
||||
| 104 | |||||
| 105 | if (null === $data) { |
||||
| 106 | return ['error' => 'Bad Response']; |
||||
| 107 | } |
||||
| 108 | |||||
| 109 | if ($data['status'] == 4) { |
||||
| 110 | return ['status' => 'success', 'message' => 'No changes detected']; |
||||
| 111 | } |
||||
| 112 | |||||
| 113 | if ($jwtManager->isJwtEnabled()) { |
||||
| 114 | if (!empty($data['token'])) { |
||||
| 115 | try { |
||||
| 116 | $payload = $jwtManager->decode($data['token'], $appSettings->getJwtKey()); |
||||
| 117 | } catch (UnexpectedValueException $e) { |
||||
| 118 | return ['status' => 'error', 'error' => '403 Access denied']; |
||||
| 119 | } |
||||
| 120 | } else { |
||||
| 121 | $token = substr(getallheaders()[$appSettings->getJwtHeader()], strlen('Bearer ')); |
||||
| 122 | try { |
||||
| 123 | $decodeToken = $jwtManager->decode($token, $appSettings->getJwtKey()); |
||||
| 124 | $payload = $decodeToken->payload; |
||||
| 125 | } catch (UnexpectedValueException $e) { |
||||
| 126 | return ['status' => 'error', 'error' => '403 Access denied']; |
||||
| 127 | } |
||||
| 128 | } |
||||
| 129 | } |
||||
| 130 | |||||
| 131 | if (!empty($docPath)) { |
||||
| 132 | $docPath = urldecode($docPath); |
||||
| 133 | $filePath = api_get_path(SYS_COURSE_PATH).$docPath; |
||||
|
0 ignored issues
–
show
|
|||||
| 134 | |||||
| 135 | if (!file_exists($filePath)) { |
||||
| 136 | return ['status' => 'error', 'error' => 'File not found']; |
||||
| 137 | } |
||||
| 138 | |||||
| 139 | $documentKey = basename($docPath); |
||||
| 140 | if ($data['status'] == 2 || $data['status'] == 3) { |
||||
| 141 | if (!empty($data['url'])) { |
||||
| 142 | $newContent = file_get_contents($data['url']); |
||||
| 143 | if ($newContent === false) { |
||||
| 144 | return ['status' => 'error', 'error' => 'Failed to fetch document']; |
||||
| 145 | } |
||||
| 146 | |||||
| 147 | if (file_put_contents($filePath, $newContent) === false) { |
||||
| 148 | return ['status' => 'error', 'error' => 'Failed to save document']; |
||||
| 149 | } |
||||
| 150 | } else { |
||||
| 151 | return ['status' => 'error', 'error' => 'No file URL provided']; |
||||
| 152 | } |
||||
| 153 | } |
||||
| 154 | } elseif (!empty($docId)) { |
||||
| 155 | $docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
|
0 ignored issues
–
show
The function
DocumentManager::get_document_data_by_id() has been deprecated: use $repo->find()
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. Loading history...
|
|||||
| 156 | if (!$docInfo || !file_exists($docInfo['absolute_path'])) { |
||||
| 157 | return ['status' => 'error', 'error' => 'File not found']; |
||||
| 158 | } |
||||
| 159 | |||||
| 160 | $documentKey = $docId; |
||||
| 161 | $data['url'] = $payload->url ?? null; |
||||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
| 162 | $data['status'] = $payload->status; |
||||
| 163 | } else { |
||||
| 164 | return ['status' => 'error', 'error' => 'File not found']; |
||||
| 165 | } |
||||
| 166 | |||||
| 167 | $docStatus = new CallbackDocStatus($data['status']); |
||||
| 168 | $callback = new OnlyofficeCallback(); |
||||
| 169 | $callback->setStatus($docStatus); |
||||
| 170 | $callback->setKey($documentKey); |
||||
| 171 | $callback->setUrl($data['url']); |
||||
| 172 | $callbackService = new OnlyofficeCallbackService( |
||||
| 173 | $appSettings, |
||||
| 174 | $jwtManager, |
||||
| 175 | [ |
||||
| 176 | 'courseCode' => $courseCode, |
||||
| 177 | 'userId' => $userId, |
||||
| 178 | 'docId' => $docId ?? '', |
||||
| 179 | 'docPath' => $docPath ?? '', |
||||
| 180 | 'groupId' => $groupId, |
||||
| 181 | 'sessionId' => $sessionId, |
||||
| 182 | 'courseInfo' => $courseInfo, |
||||
| 183 | ] |
||||
| 184 | ); |
||||
| 185 | |||||
| 186 | $result = $callbackService->processCallback($callback, $documentKey); |
||||
| 187 | |||||
| 188 | return $result; |
||||
| 189 | } |
||||
| 190 | |||||
| 191 | /** |
||||
| 192 | * Downloading file by the document service. |
||||
| 193 | */ |
||||
| 194 | function download() |
||||
| 195 | { |
||||
| 196 | global $plugin; |
||||
| 197 | global $courseCode; |
||||
| 198 | global $userId; |
||||
| 199 | global $docId; |
||||
| 200 | global $groupId; |
||||
| 201 | global $docPath; |
||||
| 202 | global $sessionId; |
||||
| 203 | global $courseInfo; |
||||
| 204 | global $appSettings; |
||||
| 205 | global $jwtManager; |
||||
| 206 | |||||
| 207 | if ($jwtManager->isJwtEnabled()) { |
||||
| 208 | $token = substr(getallheaders()[$appSettings->getJwtHeader()], strlen('Bearer ')); |
||||
| 209 | try { |
||||
| 210 | $payload = $jwtManager->decode($token, $appSettings->getJwtKey()); |
||||
| 211 | } catch (UnexpectedValueException $e) { |
||||
| 212 | return ['status' => 'error', 'error' => '403 Access denied']; |
||||
| 213 | } |
||||
| 214 | } |
||||
| 215 | |||||
| 216 | if (!empty($docPath)) { |
||||
| 217 | $filePath = api_get_path(SYS_COURSE_PATH).urldecode($docPath); |
||||
|
0 ignored issues
–
show
|
|||||
| 218 | |||||
| 219 | if (!file_exists($filePath)) { |
||||
| 220 | return ['status' => 'error', 'error' => 'File not found']; |
||||
| 221 | } |
||||
| 222 | |||||
| 223 | $docInfo = [ |
||||
| 224 | 'title' => basename($filePath), |
||||
| 225 | 'absolute_path' => $filePath, |
||||
| 226 | ]; |
||||
| 227 | } elseif (!empty($docId) && !empty($courseCode)) { |
||||
| 228 | $docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
|
0 ignored issues
–
show
The function
DocumentManager::get_document_data_by_id() has been deprecated: use $repo->find()
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. Loading history...
|
|||||
| 229 | if (!$docInfo || !file_exists($docInfo['absolute_path'])) { |
||||
| 230 | return ['status' => 'error', 'error' => 'File not found']; |
||||
| 231 | } |
||||
| 232 | |||||
| 233 | $filePath = $docInfo['absolute_path']; |
||||
| 234 | } else { |
||||
| 235 | return ['status' => 'error', 'error' => 'Invalid request']; |
||||
| 236 | } |
||||
| 237 | |||||
| 238 | @header('Content-Type: application/octet-stream'); |
||||
|
0 ignored issues
–
show
Are you sure the usage of
header('Content-Type: application/octet-stream') is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
It seems like you do not handle an error condition for
header(). This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||||
| 239 | @header('Content-Disposition: attachment; filename='.$docInfo['title']); |
||||
|
0 ignored issues
–
show
Are you sure the usage of
header('Content-Disposit...=' . $docInfo['title']) is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 240 | |||||
| 241 | readfile($filePath); |
||||
| 242 | exit; |
||||
|
0 ignored issues
–
show
|
|||||
| 243 | } |
||||
| 244 | |||||
| 245 | /** |
||||
| 246 | * Downloading empty file by the document service. |
||||
| 247 | */ |
||||
| 248 | function emptyFile() |
||||
| 249 | { |
||||
| 250 | global $plugin; |
||||
| 251 | global $type; |
||||
| 252 | global $courseCode; |
||||
| 253 | global $userId; |
||||
| 254 | global $docId; |
||||
| 255 | global $groupId; |
||||
| 256 | global $sessionId; |
||||
| 257 | global $courseInfo; |
||||
| 258 | global $appSettings; |
||||
| 259 | global $jwtManager; |
||||
| 260 | |||||
| 261 | if ($type !== 'empty') { |
||||
| 262 | $result['status'] = 'error'; |
||||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
| 263 | $result['error'] = 'Download empty with other action'; |
||||
| 264 | |||||
| 265 | return $result; |
||||
| 266 | } |
||||
| 267 | |||||
| 268 | if ($jwtManager->isJwtEnabled()) { |
||||
| 269 | $token = substr(getallheaders()[$appSettings->getJwtHeader()], strlen('Bearer ')); |
||||
| 270 | try { |
||||
| 271 | $payload = $jwtManager->decode($token, $appSettings->getJwtKey()); |
||||
| 272 | } catch (UnexpectedValueException $e) { |
||||
| 273 | $result['status'] = 'error'; |
||||
| 274 | $result['error'] = '403 Access denied'; |
||||
| 275 | |||||
| 276 | return $result; |
||||
| 277 | } |
||||
| 278 | } |
||||
| 279 | |||||
| 280 | $template = TemplateManager::getEmptyTemplate('docx'); |
||||
| 281 | |||||
| 282 | if (!$template) { |
||||
| 283 | $result['status'] = 'error'; |
||||
| 284 | $result['error'] = 'File not found'; |
||||
| 285 | |||||
| 286 | return $result; |
||||
| 287 | } |
||||
| 288 | |||||
| 289 | @header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document'); |
||||
|
0 ignored issues
–
show
It seems like you do not handle an error condition for
header(). This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Are you sure the usage of
header('Content-Type: ap...processingml.document') is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 290 | @header('Content-Disposition: attachment; filename='.'docx.docx'); |
||||
|
0 ignored issues
–
show
Are you sure the usage of
header('Content-Disposit...lename=' . 'docx.docx') is correct as it seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 291 | readfile($template); |
||||
| 292 | exit; |
||||
|
0 ignored issues
–
show
|
|||||
| 293 | } |
||||
| 294 |
If you suppress an error, we recommend checking for the error condition explicitly: