1: <?php
2: /**
3: * Maintenance class manager
4: *
5: * You may not change or alter any portion of this comment or credits
6: * of supporting developers from this source code or any supporting source code
7: * which is considered copyrighted (c) material of the original comment or credit authors.
8: * This program is distributed in the hope that it will be useful,
9: * but WITHOUT ANY WARRANTY; without even the implied warranty of
10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11: *
12: * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
13: * @license GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14: * @author Cointin Maxime (AKA Kraven30)
15: * @package system
16: */
17:
18: // defined('XOOPS_ROOT_PATH') || exit('XOOPS root path not defined');
19:
20: /**
21: * System Maintenance
22: *
23: * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
24: * @package system
25: */
26: class SystemMaintenance
27: {
28: public $db;
29: public $prefix;
30:
31: /**
32: * Constructor
33: */
34: public function __construct()
35: {
36: /** @var XoopsMySQLDatabase $db */
37: $db = XoopsDatabaseFactory::getDatabaseConnection();
38: $this->db = $db;
39: $this->prefix = $this->db->prefix . '_';
40: }
41:
42: /**
43: * Display Tables
44: *
45: * @param bool $array
46: *
47: * @internal param $array
48: * @return array|string
49: */
50: public function displayTables($array = true)
51: {
52: $tables = array();
53: $sql = 'SHOW TABLES';
54: $result = $this->db->queryF($sql);
55: if (!$this->db->isResultSet($result)) {
56: throw new \RuntimeException(
57: \sprintf(_DB_QUERY_ERROR, $sql) . $this->db->error(), E_USER_ERROR
58: );
59: }
60: while (false !== ($myrow = $this->db->fetchArray($result))) {
61: $value = array_values($myrow);
62: $value = substr($value[0], strlen(XOOPS_DB_PREFIX) + 1);
63: $tables[$value] = $value;
64: }
65: if (true === (bool)$array) {
66: return $tables;
67: } else {
68: return implode(',', $tables);
69: }
70: }
71:
72: /**
73: * Clear sessions
74: *
75: * @return bool
76: */
77: public function CleanSession()
78: {
79: $result = $this->db->queryF('TRUNCATE TABLE ' . $this->db->prefix('session'));
80:
81: return true;
82: }
83:
84: /**
85: * CleanAvatar
86: *
87: * Clean up orphaned custom avatars left when a user is deleted.
88: *
89: * @author slider84 of Team FrXoops
90: *
91: * @return boolean
92: */
93: public function CleanAvatar()
94: {
95: $sql = 'SELECT avatar_id, avatar_file FROM ' . $this->db->prefix('avatar') . " WHERE avatar_type='C' AND avatar_id IN (" . 'SELECT t1.avatar_id FROM ' . $this->db->prefix('avatar_user_link') . ' AS t1 ' . 'LEFT JOIN ' . $this->db->prefix('users') . ' AS t2 ON t2.uid=t1.user_id ' . 'WHERE t2.uid IS NULL)';
96: $result = $this->db->queryF($sql);
97: if (!$this->db->isResultSet($result)) {
98: throw new \RuntimeException(
99: \sprintf(_DB_QUERY_ERROR, $sql) . $this->db->error(), E_USER_ERROR
100: );
101: }
102:
103: /** @var array $myrow */
104: while (false !== ($myrow = $this->db->fetchArray($result))) {
105: //delete file
106: @unlink(XOOPS_UPLOAD_PATH . '/' . $myrow['avatar_file']);
107: //clean avatar table
108: $result1 = $this->db->queryF('DELETE FROM ' . $this->db->prefix('avatar') . ' WHERE avatar_id=' . $myrow['avatar_id']);
109: }
110: //clean any deleted users from avatar_user_link table
111: $result2 = $this->db->queryF('DELETE FROM ' . $this->db->prefix('avatar_user_link') . ' WHERE user_id NOT IN (SELECT uid FROM ' . $this->db->prefix('users') . ')');
112:
113: return true;
114: }
115:
116: /**
117: * Delete all files in a directory
118: *
119: * @param string $dir directory to clear
120: *
121: * @return void
122: */
123: protected function clearDirectory($dir)
124: {
125: if (is_dir($dir)) {
126: if ($dirHandle = opendir($dir)) {
127: while (($file = readdir($dirHandle)) !== false) {
128: if (filetype($dir . $file) === 'file') {
129: unlink($dir . $file);
130: }
131: }
132: closedir($dirHandle);
133: }
134: file_put_contents($dir . 'index.php', '<?php' . PHP_EOL . "header('HTTP/1.0 404 Not Found');" . PHP_EOL);
135: }
136: }
137:
138: /**
139: * Clean cache 'xoops_data/caches/smarty_cache'
140: *
141: * @param array $cacheList int[] of cache "ids"
142: * 1 = smarty cache
143: * 2 = smarty compile
144: * 3 = xoops cache
145: * @return bool
146: */
147: public function CleanCache($cacheList)
148: {
149: foreach ($cacheList as $cache) {
150: switch ($cache) {
151: case 1:
152: $this->clearDirectory(XOOPS_VAR_PATH . '/caches/smarty_cache/');
153: break;
154: case 2:
155: $this->clearDirectory(XOOPS_VAR_PATH . '/caches/smarty_compile/');
156: break;
157: case 3:
158: $this->clearDirectory(XOOPS_VAR_PATH . '/caches/xoops_cache/');
159: break;
160: default:
161: return false;
162: }
163: }
164:
165: return true;
166: }
167:
168: /**
169: * Maintenance database
170: *
171: * @param array tables 'list of tables'
172: * @param array maintenance 'optimize, check, repair, analyze'
173: * @return array
174: */
175: public function CheckRepairAnalyzeOptimizeQueries($tables, $maintenance)
176: {
177: $ret = '<table class="outer"><th>' . _AM_SYSTEM_MAINTENANCE_TABLES1 . '</th><th>' . _AM_SYSTEM_MAINTENANCE_TABLES_OPTIMIZE . '</th><th>' . _AM_SYSTEM_MAINTENANCE_TABLES_CHECK . '</th><th>' . _AM_SYSTEM_MAINTENANCE_TABLES_REPAIR . '</th><th>' . _AM_SYSTEM_MAINTENANCE_TABLES_ANALYZE . '</th>';
178: $tab = array();
179: for ($i = 0; $i < 4; ++$i) {
180: $tab[$i] = $i + 1;
181: }
182: $tab1 = array();
183: for ($i = 0; $i < 4; ++$i) {
184: if (in_array($tab[$i], $maintenance)) {
185: $tab1[$i] = $tab[$i];
186: } else {
187: $tab1[$i] = '0';
188: }
189: }
190: unset($tab);
191: $class = 'odd';
192: $tablesCount = count($tables);
193: for ($i = 0; $i < $tablesCount; ++$i) {
194: $ret .= '<tr class="' . $class . '"><td align="center">' . $this->prefix . $tables[$i] . '</td>';
195: for ($j = 0; $j < 4; ++$j) {
196: if ($tab1[$j] == 1) {
197: // Optimize
198: $result = $this->db->queryF('OPTIMIZE TABLE ' . $this->prefix . $tables[$i]);
199: if ($result) {
200: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('success.png') . '" /></td>';
201: } else {
202: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('cancel.png') . '" /></td>';
203: }
204: } elseif ($tab1[$j] == 2) {
205: // Check tables
206: $result = $this->db->queryF('CHECK TABLE ' . $this->prefix . $tables[$i]);
207: if ($result) {
208: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('success.png') . '" /></td>';
209: } else {
210: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('cancel.png') . '" /></td>';
211: }
212: } elseif ($tab1[$j] == 3) {
213: // Repair
214: $result = $this->db->queryF('REPAIR TABLE ' . $this->prefix . $tables[$i]);
215: if ($result) {
216: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('success.png') . '" /></td>';
217: } else {
218: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('cancel.png') . '" /></td>';
219: }
220: } elseif ($tab1[$j] == 4) {
221: // Analyze
222: $result = $this->db->queryF('ANALYZE TABLE ' . $this->prefix . $tables[$i]);
223: if ($result) {
224: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('success.png') . '" /></td>';
225: } else {
226: $ret .= '<td class="xo-actions txtcenter"><img src="' . system_AdminIcons('cancel.png') . '" /></td>';
227: }
228: } else {
229: $ret .= '<td>&nbsp;</td>';
230: }
231: }
232: $ret .= '</tr>';
233: $class = ($class === 'even') ? 'odd' : 'even';
234: }
235: $ret .= '</table>';
236:
237: return $ret;
238: }
239:
240: /**
241: * Dump by tables
242: *
243: * @param array tables 'list of tables'
244: * @param int drop
245: * @return array 'ret[0] = dump, ret[1] = display result
246: */
247: public function dump_tables($tables, $drop)
248: {
249: $ret = array();
250: $ret[0] = "# \n";
251: $ret[0] .= "# Dump SQL, Generate by Xoops \n";
252: $ret[0] .= '# Date : ' . date('d-m-Y - H:i') . " \n";
253: $ret[1] = '<table class="outer"><tr><th width="30%">' . _AM_SYSTEM_MAINTENANCE_DUMP_TABLES . '</th><th width="35%">' . _AM_SYSTEM_MAINTENANCE_DUMP_STRUCTURES . '</th><th width="35%">' . _AM_SYSTEM_MAINTENANCE_DUMP_NB_RECORDS . '</th></tr>';
254: $class = 'odd';
255: $tablesCount = count($tables);
256: for ($i = 0; $i < $tablesCount; ++$i) {
257: //structure
258: $ret = $this->dump_table_structure($ret, $this->prefix . $tables[$i], $drop, $class);
259: //data
260: $ret = $this->dump_table_datas($ret, $this->prefix . $tables[$i]);
261: $class = ($class === 'even') ? 'odd' : 'even';
262: }
263: $ret = $this->dump_write($ret);
264: $ret[1] .= '</table>';
265:
266: return $ret;
267: }
268:
269: /**
270: * Dump by modules
271: *
272: * @param array modules 'list of modules'
273: * @param int drop
274: * @return array 'ret[0] = dump, ret[1] = display result
275: */
276: public function dump_modules($modules, $drop)
277: {
278: $ret = array();
279: $ret[0] = "# \n";
280: $ret[0] .= "# Dump SQL, Generate by Xoops \n";
281: $ret[0] .= '# Date : ' . date('d-m-Y - H:i') . " \n";
282: $ret[0] .= "# \n\n";
283: $ret[1] = '<table class="outer"><tr><th width="30%">' . _AM_SYSTEM_MAINTENANCE_DUMP_TABLES . '</th><th width="35%">' . _AM_SYSTEM_MAINTENANCE_DUMP_STRUCTURES . '</th><th width="35%">' . _AM_SYSTEM_MAINTENANCE_DUMP_NB_RECORDS . '</th></tr>';
284: $class = 'odd';
285: $modulesCount = count($modules);
286: for ($i = 0; $i < $modulesCount; ++$i) {
287: /** @var XoopsModuleHandler $module_handler */
288: $module_handler = xoops_getHandler('module');
289: $module = $module_handler->getByDirname($modules[$i]);
290: $ret[1] .= '<tr><th colspan="3" align="left">' . ucfirst($modules[$i]) . '</th></tr>';
291: $modtables = $module->getInfo('tables');
292: if ($modtables !== false && \is_array($modtables)) {
293: foreach ($modtables as $table) {
294: //structure
295: $ret = $this->dump_table_structure($ret, $this->prefix . $table, $drop, $class);
296: //data
297: $ret = $this->dump_table_datas($ret, $this->prefix . $table);
298: $class = ($class === 'even') ? 'odd' : 'even';
299: }
300: } else {
301: $ret[1] .= '<tr><td colspan="3" align="center">' . _AM_SYSTEM_MAINTENANCE_DUMP_NO_TABLES . '</td></tr>';
302: }
303: }
304: $ret[1] .= '</table>';
305: $ret = $this->dump_write($ret);
306:
307: return $ret;
308: }
309:
310: /**
311: * Dump table structure
312: *
313: * @param array
314: * @param string table
315: * @param int drop
316: * @param string class
317: * @return array 'ret[0] = dump, ret[1] = display result
318: */
319: public function dump_table_structure($ret, $table, $drop, $class)
320: {
321: $verif = false;
322: $sql = 'SHOW create table `' . $table . '`;';
323: $result = $this->db->queryF($sql);
324: if ($this->db->isResultSet($result)) {
325: if ($row = $this->db->fetchArray($result)) {
326: $ret[0] .= '# Table structure for table `' . $table . "` \n\n";
327: if ($drop == 1) {
328: $ret[0] .= 'DROP TABLE IF EXISTS `' . $table . "`;\n\n";
329: }
330: $verif = true;
331: $ret[0] .= $row['Create Table'] . ";\n\n";
332: }
333: }
334:
335: $ret[1] .= '<tr class="' . $class . '"><td align="center">' . $table . '</td><td class="xo-actions txtcenter">';
336: $ret[1] .= ($verif === true) ? '<img src="' . system_AdminIcons('success.png') . '" />' : '<img src="' . system_AdminIcons('cancel.png') . '" />';
337: $ret[1] .= '</td>';
338: if ($this->db->isResultSet($result)) {
339: $this->db->freeRecordSet($result);
340: }
341:
342: return $ret;
343: }
344:
345: /**
346: * Dump table data
347: *
348: * @param array
349: * @param string table
350: * @return array 'ret[0] = dump, ret[1] = display result
351: */
352: public function dump_table_datas($ret, $table)
353: {
354: $count = 0;
355: $sql = 'SELECT * FROM ' . $table . ';';
356: $result = $this->db->queryF($sql);
357: if ($this->db->isResultSet($result)) {
358: $num_rows = $this->db->getRowsNum($result);
359: $num_fields = $this->db->getFieldsNum($result);
360:
361: if ($num_rows > 0) {
362: $field_type = array();
363: $i = 0;
364: while ($i < $num_fields) {
365: $meta = mysqli_fetch_field($result);
366: $field_type[] = $meta->type;
367: ++$i;
368: }
369:
370: $ret[0] .= 'INSERT INTO `' . $table . "` values\n";
371: $index = 0;
372: while ($row = $this->db->fetchRow($result)) {
373: ++$count;
374: $ret[0] .= '(';
375: for ($i = 0; $i < $num_fields; ++$i) {
376: if (null === $row[$i]) {
377: $ret[0] .= 'null';
378: } else {
379: switch ($field_type[$i]) {
380: case 'int':
381: $ret[0] .= $row[$i];
382: break;
383: default:
384: $ret[0] .= "'" . $this->db->escape($row[$i]) . "'";
385: }
386: }
387: if ($i < $num_fields - 1) {
388: $ret[0] .= ',';
389: }
390: }
391: $ret[0] .= ')';
392:
393: if ($index < $num_rows - 1) {
394: $ret[0] .= ',';
395: } else {
396: $ret[0] .= ';';
397: }
398: $ret[0] .= "\n";
399: ++$index;
400: }
401: }
402: }
403: $ret[1] .= '<td align="center">';
404: $ret[1] .= $count . '&nbsp;' . _AM_SYSTEM_MAINTENANCE_DUMP_RECORDS . '</td></tr>';
405: $ret[0] .= "\n";
406: if ($this->db->isResultSet($result)) {
407: $this->db->freeRecordSet($result);
408: }
409: $ret[0] .= "\n";
410:
411: return $ret;
412: }
413:
414: /**
415: * Dump Write
416: *
417: * @param array
418: * @return array 'ret[0] = dump, ret[1] = display result
419: */
420: public function dump_write($ret)
421: {
422: $file_name = 'dump_' . date('Y.m.d') . '_' . date('H.i.s') . '.sql';
423: $path_file = './admin/maintenance/dump/' . $file_name;
424: if (file_put_contents($path_file, $ret[0])) {
425: $ret[1] .= '<table class="outer"><tr><th colspan="2" align="center">' . _AM_SYSTEM_MAINTENANCE_DUMP_FILE_CREATED . '</th><th>' . _AM_SYSTEM_MAINTENANCE_DUMP_RESULT . '</th></tr><tr><td colspan="2" align="center"><a href="' . XOOPS_URL . '/modules/system/admin/maintenance/dump/' . $file_name . '">' . $file_name . '</a></td><td class="xo-actions txtcenter"><img src="' . system_AdminIcons('success.png') . '" /></td><tr></table>';
426: } else {
427: $ret[1] .= '<table class="outer"><tr><th colspan="2" align="center">' . _AM_SYSTEM_MAINTENANCE_DUMP_FILE_CREATED . '</th><th>' . _AM_SYSTEM_MAINTENANCE_DUMP_RESULT . '</th></tr><tr><td colspan="2" class="xo-actions txtcenter">' . $file_name . '</td><td class="xo-actions txtcenter"><img src="' . system_AdminIcons('cancel.png') . '" /></td><tr></table>';
428: }
429:
430: return $ret;
431: }
432: }
433: