PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
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
← All changes | back/Controllers/Configuration/Security.php +64 -10 10.11.1 → 11.1.0 View file →
@@ -10,8 +10,10 @@
10 10 private array $messagesNoHtml = [];
11 11
12 12 public function checkDBAjax(): void
13 13 {
14 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
15 +
14 16 $this->checkDB();
15 17
16 18 if (empty($this->messagesNoHtml)) {
17 19 echo '<i class="acymicon-check-circle acym__color__green"></i>';
@@ -17,9 +19,9 @@
17 19 echo '<i class="acymicon-check-circle acym__color__green"></i>';
18 20 } else {
19 21 $nbMessages = count($this->messagesNoHtml);
20 22 foreach ($this->messagesNoHtml as $i => $oneMsg) {
21 - echo '<span style="color:'.$oneMsg['color'].'">'.$oneMsg['msg'].'</span>';
23 + echo '<span style="color:'.esc_attr($oneMsg['color']).'">'.esc_html($oneMsg['msg']).'</span>';
22 24
23 25 if ($i < $nbMessages) {
24 26 echo '<br />';
25 27 }
@@ -28,20 +30,31 @@
28 30
29 31 exit;
30 32 }
31 33
34 + /**
35 + * Check database integrity button in the security tab
36 + */
32 37 public function checkDB(bool $fromConfiguration = true): array
33 38 {
39 + // Get the structure that the AcyMailing tables should have in the database
34 40 $correctTablesStructure = $this->getCorrectTablesStructure();
41 + // Get the current structure of the AcyMailing tables and tries to repair/create them if needed
35 42 $currentTablesStructure = $this->getCurrentTablesStructure($correctTablesStructure);
43 + // Adds missing columns in AcyMailing tables and missing indexes / primary keys / constraints on the tables
36 44 $this->fixCurrentStructure($correctTablesStructure, $currentTablesStructure);
37 45
46 + // Clean the duplicates in the acym_url table, caused by a bug before the 12/04/19
38 47 $this->cleanDuplicatedUrls($fromConfiguration);
48 + // Fills the key column in the users table when missing
39 49 $this->addMissingUserKeys();
40 50
41 51 return $this->messagesNoHtml;
42 52 }
43 53
54 + /**
55 + * Returns the structure that the AcyMailing tables should have in the database
56 + */
44 57 private function getCorrectTablesStructure(): array
45 58 {
46 59 $correctTablesStructure = [
47 60 'structure' => [],
@@ -52,8 +65,9 @@
52 65
53 66 $queries = file_get_contents(ACYM_BACK.'tables.sql');
54 67 $tables = explode('CREATE TABLE IF NOT EXISTS ', $queries);
55 68
69 + // For each table, get its name, its column names and its indexes / primary key
56 70 foreach ($tables as $oneTable) {
57 71 if (strpos($oneTable, '`#__') !== 0) {
58 72 continue;
59 73 }
@@ -69,8 +83,9 @@
69 83 continue;
70 84 }
71 85 $oneField = rtrim(trim($oneField), ',');
72 86
87 + // Find the column names and remember them
73 88 if (substr($oneField, 0, 1) === '`') {
74 89 $columnName = substr($oneField, 1, strpos($oneField, '`', 1) - 1);
75 90 $correctTablesStructure['structure'][$tableName][$columnName] = trim($oneField, ',');
76 91 continue;
@@ -75,8 +90,9 @@
75 90 $correctTablesStructure['structure'][$tableName][$columnName] = trim($oneField, ',');
76 91 continue;
77 92 }
78 93
94 + // Remember the primary key and indexes of the table
79 95 if (strpos($oneField, 'PRIMARY KEY') === 0) {
80 96 $correctTablesStructure['indexes'][$tableName]['PRIMARY'] = $oneField;
81 97 } elseif (strpos($oneField, 'INDEX') === 0) {
82 98 $firstBackquotePos = strpos($oneField, '`');
@@ -102,8 +118,11 @@
102 118
103 119 return $correctTablesStructure;
104 120 }
105 121
122 + /**
123 + * Returns the current structure of the AcyMailing tables and tries to repair/create them if needed
124 + */
106 125 private function getCurrentTablesStructure(array $correctTablesStructure): array
107 126 {
108 127 $currentTablesStructure = [];
109 128
@@ -120,9 +139,10 @@
120 139 }
121 140 continue;
122 141 }
123 142
124 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
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));
125 145 $this->messagesNoHtml[] = [
126 146 'error' => false,
127 147 'color' => 'blue',
128 148 'msg' => acym_translationSprintf('ACYM_CHECKDB_LOAD_COLUMNS_ERROR', $oneTableName, $errorMessage),
@@ -135,9 +155,9 @@
135 155 $isError = null;
136 156 }
137 157
138 158 if ($isError === null) {
139 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
159 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
140 160 $this->messagesNoHtml[] = [
141 161 'error' => true,
142 162 'color' => 'red',
143 163 'msg' => acym_translationSprintf('ACYM_CHECKDB_REPAIR_TABLE_ERROR', $oneTableName, $errorMessage),
@@ -151,8 +171,9 @@
151 171 }
152 172 continue;
153 173 } else {
154 174 try {
175 + // Create missing table
155 176 $isError = acym_query($correctTablesStructure['createTable'][$oneTableName]);
156 177 } catch (\Exception $e) {
157 178 $isError = null;
158 179 }
@@ -157,9 +178,9 @@
157 178 $isError = null;
158 179 }
159 180
160 181 if ($isError === null) {
161 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
182 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
162 183 $this->messagesNoHtml[] = [
163 184 'error' => true,
164 185 'color' => 'red',
165 186 'msg' => acym_translationSprintf('ACYM_CHECKDB_CREATE_TABLE_ERROR', $oneTableName, $errorMessage),
@@ -176,8 +197,11 @@
176 197
177 198 return $currentTablesStructure;
178 199 }
179 200
201 + /**
202 + * Adds missing columns in AcyMailing tables and missing indexes / primary keys on the tables
203 + */
180 204 private function fixCurrentStructure(array $correctTablesStructure, array $currentTablesStructure): void
181 205 {
182 206 foreach ($correctTablesStructure['tableNames'] as $oneTableName) {
183 207 if (empty($currentTablesStructure[$oneTableName])) {
@@ -191,8 +215,11 @@
191 215 $this->addMissingTableConstraints($correctTablesStructure['constraints'][$oneTableName], $oneTableName);
192 216 }
193 217 }
194 218
219 + /**
220 + * Add missing columns in an AcyMailing table
221 + */
195 222 private function addMissingColumns(array $correctTableColumns, array $currentTableColumnNames, string $oneTableName): void
196 223 {
197 224 $idealColumnNames = array_keys($correctTableColumns);
198 225 $missingColumns = array_diff($idealColumnNames, $currentTableColumnNames);
@@ -214,9 +241,9 @@
214 241 $isError = null;
215 242 }
216 243
217 244 if ($isError === null) {
218 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
245 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
219 246 $this->messagesNoHtml[] = [
220 247 'error' => true,
221 248 'color' => 'red',
222 249 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_COLUMN_ERROR', $oneColumn, $oneTableName, $errorMessage),
@@ -253,9 +280,9 @@
253 280 $isError = null;
254 281 }
255 282
256 283 if ($isError === null) {
257 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
284 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
258 285 $this->messagesNoHtml[] = [
259 286 'error' => true,
260 287 'color' => 'red',
261 288 'msg' => acym_translationSprintf('ACYM_CHECKDB_EXTRA_COLUMN_ERROR', $oneColumn, $oneTableName, $errorMessage),
@@ -303,8 +330,9 @@
303 330
304 331 if (strlen($defaultValue) === 0) {
305 332 continue;
306 333 }
334 + // Normalisation of current and expected default values
307 335 $currentDefault = $currentTableColumns[$oneColumn]->COLUMN_DEFAULT;
308 336 if (!empty($currentDefault)) {
309 337 $currentDefault = trim($currentDefault, "'\"");
310 338 }
@@ -327,8 +355,9 @@
327 355 if ($currentDefault === $expectedDefault) {
328 356 continue;
329 357 }
330 358
359 + // if current value is surrounded by double quotes, replace them by quotes before comparing
331 360 if (!empty($currentTableColumns[$oneColumn]->COLUMN_DEFAULT) && substr($currentTableColumns[$oneColumn]->COLUMN_DEFAULT, 0, 1) === '"') {
332 361 $currentTableColumns[$oneColumn]->COLUMN_DEFAULT = '\''.substr($currentTableColumns[$oneColumn]->COLUMN_DEFAULT, 1, -1).'\'';
333 362 }
334 363
@@ -350,9 +379,9 @@
350 379 $isError = null;
351 380 }
352 381
353 382 if ($isError === null) {
354 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
383 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
355 384 $this->messagesNoHtml[] = [
356 385 'error' => true,
357 386 'color' => 'red',
358 387 'msg' => acym_translationSprintf('ACYM_CHECKDB_WRONG_DEFAULT_VALUE_ERROR', $oneColumn, $oneTableName, $errorMessage),
@@ -366,10 +395,14 @@
366 395 }
367 396 }
368 397 }
369 398
399 + /**
400 + * Adds the missing indexes / primary keys on an AcyMailing table
401 + */
370 402 private function addMissingTableKeys(array $correctTableIndexes, string $oneTableName): void
371 403 {
404 + // Add missing index and primary keys
372 405 $results = acym_loadObjectList('SHOW INDEX FROM '.$oneTableName, 'Key_name');
373 406 if (empty($results)) {
374 407 $results = [];
375 408 }
@@ -379,8 +412,9 @@
379 412 if (in_array($name, array_keys($results))) {
380 413 continue;
381 414 }
382 415
416 + // The index / primary key is missing, add it
383 417
384 418 $keyName = $name === 'PRIMARY' ? 'primary key' : 'index '.$name;
385 419
386 420 $this->messagesNoHtml[] = [
@@ -395,9 +429,9 @@
395 429 $isError = null;
396 430 }
397 431
398 432 if ($isError === null) {
399 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
433 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
400 434 $this->messagesNoHtml[] = [
401 435 'error' => true,
402 436 'color' => 'red',
403 437 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_INDEX_ERROR', $keyName, $oneTableName, $errorMessage),
@@ -411,8 +445,11 @@
411 445 }
412 446 }
413 447 }
414 448
449 + /**
450 + * Adds or fixes the table's foreign keys
451 + */
415 452 private function addMissingTableConstraints(array $correctTableConstraints, string $oneTableName): void
416 453 {
417 454 if (empty($correctTableConstraints)) {
418 455 return;
@@ -443,8 +480,9 @@
443 480 'color' => 'blue',
444 481 'msg' => acym_translationSprintf('ACYM_CHECKDB_WRONG_FOREIGN_KEY', $constraintName, $oneTableName),
445 482 ];
446 483
484 + // The foreign key exists, but it is incorrect. We remove it then add the correct one
447 485 if (!empty($foreignKeys[$constraintName])) {
448 486 try {
449 487 $isError = acym_query('ALTER TABLE `'.$oneTableName.'` DROP FOREIGN KEY `'.$constraintName.'`');
450 488 } catch (\Exception $e) {
@@ -451,9 +489,9 @@
451 489 $isError = null;
452 490 }
453 491
454 492 if ($isError === null) {
455 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
493 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
456 494 $this->messagesNoHtml[] = [
457 495 'error' => true,
458 496 'color' => 'red',
459 497 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_FOREIGN_KEY_ERROR', $constraintName, $oneTableName, $errorMessage),
@@ -461,8 +499,9 @@
461 499 continue;
462 500 }
463 501 }
464 502
503 + // Add the missing foreign key
465 504 try {
466 505 $isError = acym_query(
467 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;'
468 507 );
@@ -470,9 +509,9 @@
470 509 $isError = null;
471 510 }
472 511
473 512 if ($isError === null) {
474 - $errorMessage = (isset($e) ? $e->getMessage() : substr(strip_tags(acym_getDBError()), 0, 200));
513 + $errorMessage = (isset($e) ? $e->getMessage() : substr(acym_stripTags(acym_getDBError()), 0, 200));
475 514 $this->messagesNoHtml[] = [
476 515 'error' => true,
477 516 'color' => 'red',
478 517 'msg' => acym_translationSprintf('ACYM_CHECKDB_ADD_FOREIGN_KEY_ERROR', $constraintName, $oneTableName, $errorMessage),
@@ -488,8 +527,11 @@
488 527
489 528 acym_query('SET foreign_key_checks = 1');
490 529 }
491 530
531 + /**
532 + * Clean the duplicates in the acym_url table, caused by a bug before the 12/04/19
533 + */
492 534 private function cleanDuplicatedUrls(bool $fromConfiguration): void
493 535 {
494 536 if (!$fromConfiguration) {
495 537 return;
@@ -510,8 +552,9 @@
510 552 'color' => 'blue',
511 553 'msg' => acym_translation('ACYM_CHECKDB_DUPLICATED_URLS'),
512 554 ];
513 555
556 + // Make sure we don't reach the max execution time
514 557 if (empty($maxExecutionTime) || $maxExecutionTime - 20 < 20) {
515 558 $maxExecutionTime = 20;
516 559 } else {
517 560 $maxExecutionTime -= 20;
@@ -543,8 +586,11 @@
543 586 ];
544 587 }
545 588 }
546 589
590 + /**
591 + * Fills the key column in the users table when missing
592 + */
547 593 private function addMissingUserKeys(): void
548 594 {
549 595 $userClass = new UserClass();
550 596 $nbAddedKeys = $userClass->addMissingKeys();
@@ -559,8 +605,14 @@
559 605 }
560 606
561 607 public function redomigration(): void
562 608 {
609 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
610 +
611 + if (!acym_isAllowed('configuration')) {
612 + return;
613 + }
614 +
563 615 $this->config->saveConfig(['migration' => 0]);
564 616 acym_redirect(acym_completeLink('dashboard', false, true));
565 617 }
566 618
@@ -565,8 +617,10 @@
565 617 }
566 618
567 619 public function scanSiteFiles(): void
568 620 {
621 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
622 +
569 623 $maliciousFiles = [];
570 624 $siteFiles = acym_getFiles(ACYM_ROOT, '.', true, true);
571 625 foreach ($siteFiles as $oneFilePath) {
572 626 $lastSlashPos = strrpos($oneFilePath, '/');