PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.4
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Controllers / Configuration / Security.php

Security.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.4, at back/Controllers/Configuration/Security.php

655 lines 26.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Controllers\Configuration;
4
5 use AcyMailing\Classes\UrlClass;
6 use AcyMailing\Classes\UserClass;
7
8 trait Security
9 {
10 private array $messagesNoHtml = [];
11
12 public function checkDBAjax(): void
13 {
14 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
15
16 $this->checkDB();
17
18 if (empty($this->messagesNoHtml)) {
19 echo '<i class="acymicon-check-circle acym__color__green"></i>';
20 } else {
21 $nbMessages = count($this->messagesNoHtml);
22 foreach ($this->messagesNoHtml as $i => $oneMsg) {
23 echo '<span style="color:'.esc_attr($oneMsg['color']).'">'.esc_html($oneMsg['msg']).'</span>';
24
25 if ($i < $nbMessages) {
26 echo '<br />';
27 }
28 }
29 }
30
31 exit;
32 }
33
34 /**
35 * Check database integrity button in the security tab
36 */
37 public function checkDB(bool $fromConfiguration = true): array
38 {
39 // Get the structure that the AcyMailing tables should have in the database
40 $correctTablesStructure = $this->getCorrectTablesStructure();
41 // Get the current structure of the AcyMailing tables and tries to repair/create them if needed
42 $currentTablesStructure = $this->getCurrentTablesStructure($correctTablesStructure);
43 // Adds missing columns in AcyMailing tables and missing indexes / primary keys / constraints on the tables
44 $this->fixCurrentStructure($correctTablesStructure, $currentTablesStructure);
45
46 // Clean the duplicates in the acym_url table, caused by a bug before the 12/04/19
47 $this->cleanDuplicatedUrls($fromConfiguration);
48 // Fills the key column in the users table when missing
49 $this->addMissingUserKeys();
50
51 return $this->messagesNoHtml;
52 }
53
54 /**
55 * Returns the structure that the AcyMailing tables should have in the database
56 */
57 private function getCorrectTablesStructure(): array
58 {
59 $correctTablesStructure = [
60 'structure' => [],
61 'createTable' => [],
62 'indexes' => [],
63 'constraints' => [],
64 ];
65
66 $queries = file_get_contents(ACYM_BACK.'tables.sql');
67 $tables = explode('CREATE TABLE IF NOT EXISTS ', $queries);
68
69 // For each table, get its name, its column names and its indexes / primary key
70 foreach ($tables as $oneTable) {
71 if (strpos($oneTable, '`#__') !== 0) {
72 continue;
73 }
74
75 $tableName = substr($oneTable, 1, strpos($oneTable, '`', 1) - 1);
76 $correctTablesStructure['createTable'][$tableName] = 'CREATE TABLE IF NOT EXISTS '.$oneTable;
77 $correctTablesStructure['indexes'][$tableName] = [];
78 $correctTablesStructure['constraints'][$tableName] = [];
79
80 $fields = explode("\n", $oneTable);
81 foreach ($fields as $key => $oneField) {
82 if (strpos($oneField, '#__') === 1) {
83 continue;
84 }
85 $oneField = rtrim(trim($oneField), ',');
86
87 // Find the column names and remember them
88 if (substr($oneField, 0, 1) === '`') {
89 $columnName = substr($oneField, 1, strpos($oneField, '`', 1) - 1);
90 $correctTablesStructure['structure'][$tableName][$columnName] = trim($oneField, ',');
91 continue;
92 }
93
94 // Remember the primary key and indexes of the table
95 if (strpos($oneField, 'PRIMARY KEY') === 0) {
96 $correctTablesStructure['indexes'][$tableName]['PRIMARY'] = $oneField;
97 } elseif (strpos($oneField, 'INDEX') === 0) {
98 $firstBackquotePos = strpos($oneField, '`');
99 $indexName = substr($oneField, $firstBackquotePos + 1, strpos($oneField, '`', $firstBackquotePos + 1) - $firstBackquotePos - 1);
100
101 $correctTablesStructure['indexes'][$tableName][$indexName] = $oneField;
102 } elseif (strpos($oneField, 'FOREIGN KEY') !== false) {
103 preg_match('/(#__fk.*)\`/Uis', $fields[$key - 1], $matchesConstraints);
104 preg_match('/(#__.*)\`\(`(.*)`\)/Uis', $fields[$key + 1], $matchesTable);
105 preg_match('/\`(.*)\`/Uis', $oneField, $matchesColumn);
106 if (!empty($matchesConstraints) && !empty($matchesTable) && !empty($matchesColumn)) {
107 $correctTablesStructure['constraints'][$tableName][$matchesConstraints[1]] = [
108 'table' => $matchesTable[1],
109 'column' => $matchesColumn[1],
110 'table_column' => $matchesTable[2],
111 ];
112 }
113 }
114 }
115 }
116
117 $correctTablesStructure['tableNames'] = array_keys($correctTablesStructure['structure']);
118
119 return $correctTablesStructure;
120 }
121
122 /**
123 * Returns the current structure of the AcyMailing tables and tries to repair/create them if needed
124 */
125 private function getCurrentTablesStructure(array $correctTablesStructure): array
126 {
127 $currentTablesStructure = [];
128
129 foreach ($correctTablesStructure['tableNames'] as $oneTableName) {
130 try {
131 $columns = acym_loadObjectList('SHOW COLUMNS FROM '.$oneTableName);
132 } catch (\Exception $e) {
133 $columns = null;
134 }
135
136 if (!empty($columns)) {
137 foreach ($columns as $oneField) {
138 $currentTablesStructure[$oneTableName][$oneField->Field] = $oneField->Field;
139 }
140 continue;
141 }
142
143 // We didn't get the columns, the table crashed or doesn't exist
144 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
145 $this->messagesNoHtml[] = [
146 'error' => false,
147 'color' => 'blue',
148 'msg' => acym_translationSprintf('ACYM_CHECKDB_LOAD_COLUMNS_ERROR', $oneTableName, $errorMessage),
149 ];
150
151 if (strpos($errorMessage, 'marked as crashed')) {
152 try {
153 $isError = acym_query('REPAIR TABLE '.$oneTableName);
154 } catch (\Exception $e) {
155 $isError = null;
156 }
157
158 if ($isError === null) {
159 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
160 $this->messagesNoHtml[] = [
161 'error' => true,
162 'color' => 'red',
163 'msg' => acym_translationSprintf('ACYM_CHECKDB_REPAIR_TABLE_ERROR', $oneTableName, $errorMessage),
164 ];
165 } else {
166 $this->messagesNoHtml[] = [
167 'error' => false,
168 'color' => 'green',
169 'msg' => acym_translationSprintf('ACYM_CHECKDB_REPAIR_TABLE_SUCCESS', $oneTableName),
170 ];
171 }
172 continue;
173 } else {
174 try {
175 // Create missing table
176 $isError = acym_query($correctTablesStructure['createTable'][$oneTableName]);
177 } catch (\Exception $e) {
178 $isError = null;
179 }
180
181 if ($isError === null) {
182 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
183 $this->messagesNoHtml[] = [
184 'error' => true,
185 'color' => 'red',
186 'msg' => acym_translationSprintf('ACYM_CHECKDB_CREATE_TABLE_ERROR', $oneTableName, $errorMessage),
187 ];
188 } else {
189 $this->messagesNoHtml[] = [
190 'error' => false,
191 'color' => 'green',
192 'msg' => acym_translationSprintf('ACYM_CHECKDB_CREATE_TABLE_SUCCESS', $oneTableName),
193 ];
194 }
195 }
196 }
197
198 return $currentTablesStructure;
199 }
200
201 /**
202 * Adds missing columns in AcyMailing tables and missing indexes / primary keys on the tables
203 */
204 private function fixCurrentStructure(array $correctTablesStructure, array $currentTablesStructure): void
205 {
206 foreach ($correctTablesStructure['tableNames'] as $oneTableName) {
207 if (empty($currentTablesStructure[$oneTableName])) {
208 continue;
209 }
210
211 $this->addMissingColumns($correctTablesStructure['structure'][$oneTableName], $currentTablesStructure[$oneTableName], $oneTableName);
212 $this->removeExtraColumns($correctTablesStructure['structure'][$oneTableName], $currentTablesStructure[$oneTableName], $oneTableName);
213 $this->fixDefaultValues($correctTablesStructure['structure'][$oneTableName], $oneTableName);
214 $this->addMissingTableKeys($correctTablesStructure['indexes'][$oneTableName], $oneTableName);
215 $this->addMissingTableConstraints($correctTablesStructure['constraints'][$oneTableName], $oneTableName);
216 }
217 }
218
219 /**
220 * Add missing columns in an AcyMailing table
221 */
222 private function addMissingColumns(array $correctTableColumns, array $currentTableColumnNames, string $oneTableName): void
223 {
224 $idealColumnNames = array_keys($correctTableColumns);
225 $missingColumns = array_diff($idealColumnNames, $currentTableColumnNames);
226
227 if (empty($missingColumns)) {
228 return;
229 }
230
231 foreach ($missingColumns as $oneColumn) {
232 $this->messagesNoHtml[] = [
233 'error' => false,
234 'color' => 'blue',
235 'msg' => acym_translationSprintf('ACYM_CHECKDB_MISSING_COLUMN', $oneColumn, $oneTableName),
236 ];
237
238 try {
239 $isError = acym_query('ALTER TABLE '.$oneTableName.' ADD '.$correctTableColumns[$oneColumn]);
240 } catch (\Exception $e) {
241 $isError = null;
242 }
243
244 if ($isError === null) {
245 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
246 $this->messagesNoHtml[] = [
247 'error' => true,
248 'color' => 'red',
249 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_COLUMN_ERROR', $oneColumn, $oneTableName, $errorMessage),
250 ];
251 } else {
252 $this->messagesNoHtml[] = [
253 'error' => false,
254 'color' => 'green',
255 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_COLUMN_SUCCESS', $oneColumn, $oneTableName),
256 ];
257 }
258 }
259 }
260
261 private function removeExtraColumns(array $correctTableColumns, array $currentTableColumnNames, string $oneTableName): void
262 {
263 $idealColumnNames = array_keys($correctTableColumns);
264 $extraColumns = array_diff($currentTableColumnNames, $idealColumnNames);
265
266 if (empty($extraColumns)) {
267 return;
268 }
269
270 foreach ($extraColumns as $oneColumn) {
271 $this->messagesNoHtml[] = [
272 'error' => false,
273 'color' => 'blue',
274 'msg' => acym_translationSprintf('ACYM_CHECKDB_EXTRA_COLUMN', $oneColumn, $oneTableName),
275 ];
276
277 try {
278 $isError = acym_query('ALTER TABLE '.$oneTableName.' DROP COLUMN `'.acym_secureDBColumn($oneColumn).'`');
279 } catch (\Exception $e) {
280 $isError = null;
281 }
282
283 if ($isError === null) {
284 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
285 $this->messagesNoHtml[] = [
286 'error' => true,
287 'color' => 'red',
288 'msg' => acym_translationSprintf('ACYM_CHECKDB_EXTRA_COLUMN_ERROR', $oneColumn, $oneTableName, $errorMessage),
289 ];
290 } else {
291 $this->messagesNoHtml[] = [
292 'error' => false,
293 'color' => 'green',
294 'msg' => acym_translationSprintf('ACYM_CHECKDB_EXTRA_COLUMN_SUCCESS', $oneColumn, $oneTableName),
295 ];
296 }
297 }
298 }
299
300 private function fixDefaultValues(array $correctTableColumns, string $oneTableName): void
301 {
302 $oneTableName = str_replace('#__', acym_getPrefix(), $oneTableName);
303 try {
304 $currentTableColumns = acym_loadObjectList(
305 'SELECT COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, COLUMN_TYPE
306 FROM information_schema.COLUMNS
307 WHERE TABLE_SCHEMA = DATABASE()
308 AND TABLE_NAME = '.acym_escapeDB($oneTableName),
309 'COLUMN_NAME'
310 );
311
312 if (empty($currentTableColumns)) {
313 return;
314 }
315 } catch (\Exception $e) {
316 $this->messagesNoHtml[] = [
317 'error' => true,
318 'color' => 'orange',
319 'msg' => acym_translationSprintf('ACYM_CHECKDB_ERROR_GET_COLUMNS', $oneTableName, $e->getMessage()),
320 ];
321
322 return;
323 }
324
325 foreach ($correctTableColumns as $oneColumn => $oneColumnDefinition) {
326 $defaultValue = '';
327 if (preg_match('#DEFAULT ([^ ]+)$#Ui', $oneColumnDefinition, $matches)) {
328 $defaultValue = $matches[1];
329 }
330
331 if (strlen($defaultValue) === 0) {
332 continue;
333 }
334 // Normalisation of current and expected default values
335 $currentDefault = $currentTableColumns[$oneColumn]->COLUMN_DEFAULT;
336 if (!empty($currentDefault)) {
337 $currentDefault = trim($currentDefault, "'\"");
338 }
339 $expectedDefault = trim($defaultValue, "'\"");
340
341 if (strtoupper($expectedDefault) === 'NULL') {
342 $expectedDefault = null;
343 }
344
345 $isNullable = strtoupper($currentTableColumns[$oneColumn]->IS_NULLABLE) === 'YES';
346
347 if ($isNullable && $currentDefault === '') {
348 $currentDefault = null;
349 }
350
351 if (!$isNullable && $currentDefault === null) {
352 $currentDefault = '';
353 }
354
355 if ($currentDefault === $expectedDefault) {
356 continue;
357 }
358
359 // if current value is surrounded by double quotes, replace them by quotes before comparing
360 if (!empty($currentTableColumns[$oneColumn]->COLUMN_DEFAULT) && substr($currentTableColumns[$oneColumn]->COLUMN_DEFAULT, 0, 1) === '"') {
361 $currentTableColumns[$oneColumn]->COLUMN_DEFAULT = '\''.substr($currentTableColumns[$oneColumn]->COLUMN_DEFAULT, 1, -1).'\'';
362 }
363
364 $isColumnDefaultEmpty = empty($currentTableColumns[$oneColumn]->COLUMN_DEFAULT) && $currentTableColumns[$oneColumn]->COLUMN_DEFAULT !== '0';
365
366 if (!$isColumnDefaultEmpty && $currentTableColumns[$oneColumn]->COLUMN_DEFAULT === $defaultValue) {
367 continue;
368 }
369
370 $this->messagesNoHtml[] = [
371 'error' => false,
372 'color' => 'blue',
373 'msg' => acym_translationSprintf('ACYM_CHECKDB_WRONG_DEFAULT_VALUE', $oneColumn, $oneTableName),
374 ];
375
376 try {
377 $isError = acym_query('ALTER TABLE '.$oneTableName.' CHANGE `'.acym_secureDBColumn($oneColumn).'` '.$oneColumnDefinition);
378 } catch (\Exception $e) {
379 $isError = null;
380 }
381
382 if ($isError === null) {
383 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
384 $this->messagesNoHtml[] = [
385 'error' => true,
386 'color' => 'red',
387 'msg' => acym_translationSprintf('ACYM_CHECKDB_WRONG_DEFAULT_VALUE_ERROR', $oneColumn, $oneTableName, $errorMessage),
388 ];
389 } else {
390 $this->messagesNoHtml[] = [
391 'error' => false,
392 'color' => 'green',
393 'msg' => acym_translationSprintf('ACYM_CHECKDB_WRONG_DEFAULT_VALUE_SUCCESS', $oneColumn, $oneTableName),
394 ];
395 }
396 }
397 }
398
399 /**
400 * Adds the missing indexes / primary keys on an AcyMailing table
401 */
402 private function addMissingTableKeys(array $correctTableIndexes, string $oneTableName): void
403 {
404 // Add missing index and primary keys
405 $results = acym_loadObjectList('SHOW INDEX FROM '.$oneTableName, 'Key_name');
406 if (empty($results)) {
407 $results = [];
408 }
409
410 foreach ($correctTableIndexes as $name => $query) {
411 $name = acym_prepareQuery($name);
412 if (in_array($name, array_keys($results))) {
413 continue;
414 }
415
416 // The index / primary key is missing, add it
417
418 $keyName = $name === 'PRIMARY' ? 'primary key' : 'index '.$name;
419
420 $this->messagesNoHtml[] = [
421 'error' => false,
422 'color' => 'blue',
423 'msg' => acym_translationSprintf('ACYM_CHECKDB_MISSING_INDEX', $keyName, $oneTableName),
424 ];
425
426 try {
427 $isError = acym_query('ALTER TABLE '.$oneTableName.' ADD '.$query);
428 } catch (\Exception $e) {
429 $isError = null;
430 }
431
432 if ($isError === null) {
433 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
434 $this->messagesNoHtml[] = [
435 'error' => true,
436 'color' => 'red',
437 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_INDEX_ERROR', $keyName, $oneTableName, $errorMessage),
438 ];
439 } else {
440 $this->messagesNoHtml[] = [
441 'error' => false,
442 'color' => 'green',
443 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_INDEX_SUCCESS', $keyName, $oneTableName),
444 ];
445 }
446 }
447 }
448
449 /**
450 * Adds or fixes the table's foreign keys
451 */
452 private function addMissingTableConstraints(array $correctTableConstraints, string $oneTableName): void
453 {
454 if (empty($correctTableConstraints)) {
455 return;
456 }
457
458 $tableNameQuery = str_replace('#__', acym_getPrefix(), $oneTableName);
459 $databaseName = acym_loadResult('SELECT DATABASE();');
460 $foreignKeys = acym_loadObjectList(
461 'SELECT i.CONSTRAINT_NAME, k.REFERENCED_TABLE_NAME, k.REFERENCED_COLUMN_NAME, k.COLUMN_NAME
462 FROM information_schema.TABLE_CONSTRAINTS AS i
463 JOIN information_schema.KEY_COLUMN_USAGE AS k ON i.CONSTRAINT_NAME = k.CONSTRAINT_NAME
464 WHERE i.TABLE_NAME = '.acym_escapeDB($tableNameQuery).' AND i.CONSTRAINT_TYPE = "FOREIGN KEY" AND i.TABLE_SCHEMA = '.acym_escapeDB($databaseName),
465 'CONSTRAINT_NAME'
466 );
467
468 acym_query('SET foreign_key_checks = 0');
469
470 foreach ($correctTableConstraints as $constraintName => $constraintInfo) {
471 $constraintTableNamePrefix = str_replace('#__', acym_getPrefix(), $constraintInfo['table']);
472 $constraintName = str_replace('#__', acym_getPrefix(), $constraintName);
473
474 if (!empty($foreignKeys[$constraintName]) && $foreignKeys[$constraintName]->REFERENCED_TABLE_NAME === $constraintTableNamePrefix && $foreignKeys[$constraintName]->REFERENCED_COLUMN_NAME === $constraintInfo['table_column'] && $foreignKeys[$constraintName]->COLUMN_NAME === $constraintInfo['column']) {
475 continue;
476 }
477
478 $this->messagesNoHtml[] = [
479 'error' => false,
480 'color' => 'blue',
481 'msg' => acym_translationSprintf('ACYM_CHECKDB_WRONG_FOREIGN_KEY', $constraintName, $oneTableName),
482 ];
483
484 // The foreign key exists, but it is incorrect. We remove it then add the correct one
485 if (!empty($foreignKeys[$constraintName])) {
486 try {
487 $isError = acym_query('ALTER TABLE `'.$oneTableName.'` DROP FOREIGN KEY `'.$constraintName.'`');
488 } catch (\Exception $e) {
489 $isError = null;
490 }
491
492 if ($isError === null) {
493 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
494 $this->messagesNoHtml[] = [
495 'error' => true,
496 'color' => 'red',
497 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_FOREIGN_KEY_ERROR', $constraintName, $oneTableName, $errorMessage),
498 ];
499 continue;
500 }
501 }
502
503 // Add the missing foreign key
504 try {
505 $isError = acym_query(
506 'ALTER TABLE `'.$oneTableName.'` ADD CONSTRAINT `'.$constraintName.'` FOREIGN KEY (`'.$constraintInfo['column'].'`) REFERENCES `'.$constraintInfo['table'].'` (`'.$constraintInfo['table_column'].'`) ON DELETE NO ACTION ON UPDATE NO ACTION;'
507 );
508 } catch (\Exception $e) {
509 $isError = null;
510 }
511
512 if ($isError === null) {
513 $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
514 $this->messagesNoHtml[] = [
515 'error' => true,
516 'color' => 'red',
517 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_FOREIGN_KEY_ERROR', $constraintName, $oneTableName, $errorMessage),
518 ];
519 } else {
520 $this->messagesNoHtml[] = [
521 'error' => false,
522 'color' => 'green',
523 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_FOREIGN_KEY_SUCCESS', $constraintName, $oneTableName),
524 ];
525 }
526 }
527
528 acym_query('SET foreign_key_checks = 1');
529 }
530
531 /**
532 * Clean the duplicates in the acym_url table, caused by a bug before the 12/04/19
533 */
534 private function cleanDuplicatedUrls(bool $fromConfiguration): void
535 {
536 if (!$fromConfiguration) {
537 return;
538 }
539
540 $urlClass = new UrlClass();
541 $duplicatedUrls = $urlClass->getDuplicatedUrls();
542
543 if (empty($duplicatedUrls)) {
544 return;
545 }
546
547 $maxExecutionTime = intval(ini_get('max_execution_time'));
548 $time = time();
549 $interrupted = false;
550 $this->messagesNoHtml[] = [
551 'error' => false,
552 'color' => 'blue',
553 'msg' => acym_translation('ACYM_CHECKDB_DUPLICATED_URLS'),
554 ];
555
556 // Make sure we don't reach the max execution time
557 if (empty($maxExecutionTime) || $maxExecutionTime - 20 < 20) {
558 $maxExecutionTime = 20;
559 } else {
560 $maxExecutionTime -= 20;
561 }
562
563 acym_increasePerf();
564 while (!empty($duplicatedUrls)) {
565 $urlClass->delete($duplicatedUrls);
566
567 if (time() - $time > $maxExecutionTime) {
568 $interrupted = true;
569 break;
570 }
571
572 $duplicatedUrls = $urlClass->getDuplicatedUrls();
573 }
574
575 if (empty($interrupted)) {
576 $this->messagesNoHtml[] = [
577 'error' => false,
578 'color' => 'green',
579 'msg' => acym_translation('ACYM_CHECKDB_DUPLICATED_URLS_SUCCESS'),
580 ];
581 } else {
582 $this->messagesNoHtml[] = [
583 'error' => false,
584 'color' => 'blue',
585 'msg' => acym_translation('ACYM_CHECKDB_DUPLICATED_URLS_REMAINING'),
586 ];
587 }
588 }
589
590 /**
591 * Fills the key column in the users table when missing
592 */
593 private function addMissingUserKeys(): void
594 {
595 $userClass = new UserClass();
596 $nbAddedKeys = $userClass->addMissingKeys();
597
598 if (!empty($nbAddedKeys)) {
599 $this->messagesNoHtml[] = [
600 'error' => false,
601 'color' => 'green',
602 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADDED_KEYS', $nbAddedKeys),
603 ];
604 }
605 }
606
607 public function redomigration(): void
608 {
609 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
610
611 if (!acym_isAllowed('configuration')) {
612 return;
613 }
614
615 $this->config->saveConfig(['migration' => 0]);
616 acym_redirect(acym_completeLink('dashboard', false, true));
617 }
618
619 public function scanSiteFiles(): void
620 {
621 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
622
623 $maliciousFiles = [];
624 $siteFiles = acym_getFiles(ACYM_ROOT, '.', true, true);
625 foreach ($siteFiles as $oneFilePath) {
626 $lastSlashPos = strrpos($oneFilePath, '/');
627 if (
628 !empty($lastSlashPos)
629 && strpos($oneFilePath, ACYM_UPLOAD_FOLDER_THUMBNAIL) !== false
630 && preg_match('/.*thumbnail.*php.*$/', substr($oneFilePath, $lastSlashPos + 1))
631 ) {
632 $maliciousFiles[] = $oneFilePath;
633 } elseif (filesize($oneFilePath) < 10000) {
634 $fileContent = file_get_contents($oneFilePath);
635 if (preg_match('/^<\?php echo "jm"\."te"\."st"; \?>$/U', $fileContent)) {
636 $maliciousFiles[] = $oneFilePath;
637 } elseif (preg_match('/^<\?php\n\$[a-z]+\s*=\s*\$_COOKIE\s*;/Ui', $fileContent)) {
638 $maliciousFiles[] = $oneFilePath;
639 }
640 }
641 }
642
643 ob_start();
644 if (!empty($maliciousFiles)) {
645 $message = acym_translation('ACYM_MALICIOUS_FILES');
646 $message .= '<ul><li>'.implode('</li><li>', $maliciousFiles).'</li></ul>';
647 acym_display($message, 'error', false);
648 } else {
649 acym_display(acym_translation('ACYM_NO_MALICIOUS_FILES'), 'success', false);
650 }
651 $message = ob_get_clean();
652 acym_sendAjaxResponse($message);
653 }
654 }
655