emptyFile()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 45
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 31
nc 6
nop 0
dl 0
loc 45
rs 9.1128
c 0
b 0
f 0
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
Security Best Practice introduced by
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 ignore-unhandled  annotation

26
    /** @scrutinizer ignore-unhandled */ @header('Content-Type: application/json; charset=utf-8');

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...
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

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
Bug introduced by
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 ignore-call  annotation

32
    list($hashData, $error) = $jwtManager->readHash($_GET['hash'], /** @scrutinizer ignore-call */ api_get_security_key());
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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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
Bug introduced by
The constant SYS_COURSE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
Deprecated Code introduced by
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 ignore-deprecated  annotation

155
        $docInfo = /** @scrutinizer ignore-deprecated */ DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId);

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
The variable $payload does not seem to be defined for all execution paths leading up to this point.
Loading history...
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
Bug introduced by
The constant SYS_COURSE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
Deprecated Code introduced by
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 ignore-deprecated  annotation

228
        $docInfo = /** @scrutinizer ignore-deprecated */ DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId);

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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Security Best Practice introduced by
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 ignore-unhandled  annotation

238
    /** @scrutinizer ignore-unhandled */ @header('Content-Type: application/octet-stream');

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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

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
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
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
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
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
Security Best Practice introduced by
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 ignore-unhandled  annotation

289
    /** @scrutinizer ignore-unhandled */ @header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');

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...
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

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
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
293
}
294