1: <?php
2:
3: use Xmf\Request;
4:
5: //require_once XOOPS_ROOT_PATH.'/include/cp_header.php' ;
6: include_once __DIR__ . '/admin_header.php'; //mb problem: it shows always the same "Center" tab
7: xoops_cp_header();
8: include __DIR__ . '/mymenu.php';
9: require_once XOOPS_ROOT_PATH . '/class/pagenav.php';
10: require_once dirname(__DIR__) . '/class/gtickets.php';
11:
12: //dirty trick to get navigation working with system menus
13: if (isset($_GET['num'])) {
14: $_SERVER['REQUEST_URI'] = 'admin/center.php?page=center';
15: }
16:
17: $myts = \MyTextSanitizer::getInstance();
18: $db = XoopsDatabaseFactory::getDatabaseConnection();
19:
20: // GET vars
21: $pos = Request::getInt('pos', 0, 'GET');
22: $num = Request::getInt('num', 20, 'GET');
23:
24: // Table Name
25: $log_table = $db->prefix($mydirname . '_log');
26:
27: // Protector object
28: require_once dirname(__DIR__) . '/class/protector.php';
29: $db = XoopsDatabaseFactory::getDatabaseConnection();
30: $protector = Protector::getInstance($db->conn);
31: $conf = $protector->getConf();
32:
33: //
34: // transaction stage
35: //
36:
37: if (!empty($_POST['action'])) {
38:
39: // Ticket check
40: if (!$xoopsGTicket->check(true, 'protector_admin')) {
41: redirect_header(XOOPS_URL . '/', 3, $xoopsGTicket->getErrors());
42: }
43:
44: if ($_POST['action'] === 'update_ips') {
45: $error_msg = '';
46:
47: $lines = empty($_POST['bad_ips']) ? array() : explode("\n", trim($_POST['bad_ips']));
48: $bad_ips = array();
49: foreach ($lines as $line) {
50: @list($bad_ip, $jailed_time) = explode('|', $line, 2);
51: $bad_ips[trim($bad_ip)] = empty($jailed_time) ? 0x7fffffff : (int)$jailed_time;
52: }
53: if (!$protector->write_file_badips($bad_ips)) {
54: $error_msg .= _AM_MSG_BADIPSCANTOPEN;
55: }
56:
57: $group1_ips = empty($_POST['group1_ips']) ? array() : explode("\n", trim($_POST['group1_ips']));
58: foreach (array_keys($group1_ips) as $i) {
59: $group1_ips[$i] = trim($group1_ips[$i]);
60: }
61: $fp = @fopen($protector->get_filepath4group1ips(), 'w');
62: if ($fp) {
63: @flock($fp, LOCK_EX);
64: fwrite($fp, serialize(array_unique($group1_ips)) . "\n");
65: @flock($fp, LOCK_UN);
66: fclose($fp);
67: } else {
68: $error_msg .= _AM_MSG_GROUP1IPSCANTOPEN;
69: }
70:
71: $redirect_msg = $error_msg ? : _AM_MSG_IPFILESUPDATED;
72: redirect_header('center.php?page=center', 2, $redirect_msg);
73: exit;
74: } elseif ($_POST['action'] === 'delete' && isset($_POST['ids']) && \is_array($_POST['ids'])) {
75: // remove selected records
76: foreach ($_POST['ids'] as $lid) {
77: $lid = (int)$lid;
78: $db->query("DELETE FROM $log_table WHERE lid='$lid'");
79: }
80: redirect_header('center.php?page=center', 2, _AM_MSG_REMOVED);
81: exit;
82: } elseif ($_POST['action'] === 'banbyip' && isset($_POST['ids']) && \is_array($_POST['ids'])) {
83: // remove selected records
84: foreach ($_POST['ids'] as $lid) {
85: $lid = (int)$lid;
86: $sql = "SELECT `ip` FROM $log_table WHERE lid='$lid'";
87: $result = $db->query($sql);
88:
89: if (!$db->isResultSet($result)) {
90: list($ip) = $db->fetchRow($result);
91: $protector->register_bad_ips(0, $ip);
92: }
93:
94: if ($db->isResultSet($result)) {
95: $db->freeRecordSet($result);
96: }
97:
98: }
99: redirect_header('center.php?page=center', 2, _AM_MSG_BANNEDIP);
100: exit;
101: } elseif ($_POST['action'] === 'deleteall') {
102: // remove all records
103: $db->query("DELETE FROM $log_table");
104: redirect_header('center.php?page=center', 2, _AM_MSG_REMOVED);
105: exit;
106: } elseif ($_POST['action'] === 'compactlog') {
107: // compactize records (removing duplicated records (ip,type)
108: $sql = "SELECT `lid`,`ip`,`type` FROM $log_table ORDER BY lid DESC";
109: $result = $db->query($sql);
110: if (!$db->isResultSet($result)) {
111: throw new \RuntimeException(
112: \sprintf(_DB_QUERY_ERROR, $sql) . $db->error(), E_USER_ERROR
113: );
114: }
115: $buf = array();
116: $ids = array();
117: while (false !== (list($lid, $ip, $type) = $db->fetchRow($result))) {
118: if (isset($buf[$ip . $type])) {
119: $ids[] = $lid;
120: } else {
121: $buf[$ip . $type] = true;
122: }
123: }
124: $db->query("DELETE FROM $log_table WHERE lid IN (" . implode(',', $ids) . ')');
125: redirect_header('center.php?page=center', 2, _AM_MSG_REMOVED);
126: exit;
127: }
128: }
129:
130: //
131: // display stage
132: //
133:
134: // query for listing
135: $sql = "SELECT count(lid) FROM $log_table";
136: $result = $db->query($sql);
137: if (!$db->isResultSet($result)) {
138: throw new \RuntimeException(
139: \sprintf(_DB_QUERY_ERROR, $sql) . $db->error(), E_USER_ERROR
140: );
141: }
142: list($numrows) = $db->fetchRow($result);
143:
144: $sql = "SELECT l.lid, l.uid, l.ip, l.agent, l.type, l.description, UNIX_TIMESTAMP(l.timestamp), u.uname FROM $log_table l LEFT JOIN " . $db->prefix('users') . " u ON l.uid=u.uid ORDER BY timestamp DESC LIMIT $pos,$num";
145: $result = $db->query($sql);
146: if (!$db->isResultSet($result)) {
147: throw new \RuntimeException(
148: \sprintf(_DB_QUERY_ERROR, $sql) . $db->error(), E_USER_ERROR
149: );
150: }
151:
152: // Page Navigation
153: $nav = new XoopsPageNav($numrows, $num, $pos, 'pos', "page=center&num=$num");
154: $nav_html = $nav->renderNav(10);
155:
156: // Number selection
157: $num_options = '';
158: $num_array = array(20, 100, 500, 2000);
159: foreach ($num_array as $n) {
160: if ($n == $num) {
161: $num_options .= "<option value='$n' selected>$n</option>\n";
162: } else {
163: $num_options .= "<option value='$n'>$n</option>\n";
164: }
165: }
166:
167: // beggining of Output
168:
169: // title
170: echo "<h3 style='text-align:left;'>" . $xoopsModule->name() . "</h3>\n";
171: echo '<style>td.log_description {width: 60em; display: inline-block; word-wrap: break-word; white-space: pre-line;}</style>';
172:
173: // configs writable check
174: if (!is_writable(dirname(__DIR__) . '/configs')) {
175: printf("<p style='color:red;font-weight:bold;'>" . _AM_FMT_CONFIGSNOTWRITABLE . "</p>\n", dirname(__DIR__) . '/configs');
176: }
177:
178: // bad_ips
179: $bad_ips = $protector->get_bad_ips(true);
180: uksort($bad_ips, 'protector_ip_cmp');
181: $bad_ips4disp = '';
182: foreach ($bad_ips as $bad_ip => $jailed_time) {
183: $line = $jailed_time ? $bad_ip . '|' . $jailed_time : $bad_ip;
184: $line = str_replace('|2147483647', '', $line); // remove :0x7fffffff
185: $bad_ips4disp .= htmlspecialchars($line, ENT_QUOTES) . "\n";
186: }
187:
188: // group1_ips
189: $group1_ips = $protector->get_group1_ips();
190: usort($group1_ips, 'protector_ip_cmp');
191: $group1_ips4disp = htmlspecialchars(implode("\n", $group1_ips), ENT_QUOTES);
192:
193: // edit configs about IP ban and IPs for group=1
194: echo "
195: <form name='ConfigForm' action='' method='POST'>
196: " . $xoopsGTicket->getTicketHtml(__LINE__, 1800, 'protector_admin') . "
197: <input type='hidden' name='action' value='update_ips' />
198: <table width='95%' class='outer' cellpadding='4' cellspacing='1'>
199: <tr valign='top' align='left'>
200: <td class='head'>
201: " . _AM_TH_BADIPS . "
202: </td>
203: <td class='even'>
204: <textarea name='bad_ips' id='bad_ips' style='width:360px;height:60px;' spellcheck='false'>$bad_ips4disp</textarea>
205: <br>
206: " . htmlspecialchars($protector->get_filepath4badips(), ENT_QUOTES) . "
207: </td>
208: </tr>
209: <tr valign='top' align='left'>
210: <td class='head'>
211: " . _AM_TH_GROUP1IPS . "
212: </td>
213: <td class='even'>
214: <textarea name='group1_ips' id='group1_ips' style='width:360px;height:60px;' spellcheck='false'>$group1_ips4disp</textarea>
215: <br>
216: " . htmlspecialchars($protector->get_filepath4group1ips(), ENT_QUOTES) . "
217: </td>
218: </tr>
219: <tr valign='top' align='left'>
220: <td class='head'>
221: </td>
222: <td class='even'>
223: <input type='submit' value='" . _GO . "' />
224: </td>
225: </tr>
226: </table>
227: </form>
228: ";
229:
230: // header of log listing
231: echo "
232: <table width='95%' border='0' cellpadding='4' cellspacing='0'><tr><td>
233: <form action='' method='GET' style='margin-bottom:0;'>
234: <table width='95%' border='0' cellpadding='4' cellspacing='0'>
235: <tr>
236: <td align='left'>
237: <select name='num' onchange='submit();'>$num_options</select>
238: <input type='submit' value='" . _SUBMIT . "'>
239: </td>
240: <td align='right'>
241: $nav_html
242: </td>
243: </tr>
244: </table>
245: </form>
246: <form name='MainForm' action='' method='POST' style='margin-top:0;'>
247: " . $xoopsGTicket->getTicketHtml(__LINE__, 1800, 'protector_admin') . "
248: <input type='hidden' name='action' value='' />
249: <table width='95%' class='outer' cellpadding='4' cellspacing='1'>
250: <tr valign='middle'>
251: <th width='5'><input type='checkbox' name='dummy' onclick=\"with(document.MainForm){for (i=0;i<length;i++) {if (elements[i].type=='checkbox') {elements[i].checked=this.checked;}}}\" /></th>
252: <th>" . _AM_TH_DATETIME . '</th>
253: <th>' . _AM_TH_USER . '</th>
254: <th>' . _AM_TH_IP . '<br>' . _AM_TH_AGENT . '</th>
255: <th>' . _AM_TH_TYPE . '</th>
256: <th>' . _AM_TH_DESCRIPTION . '</th>
257: </tr>
258: ';
259:
260: // body of log listing
261: $oddeven = 'odd';
262: while (false !== (list($lid, $uid, $ip, $agent, $type, $description, $timestamp, $uname) = $db->fetchRow($result))) {
263: $oddeven = ($oddeven === 'odd' ? 'even' : 'odd');
264: $style = '';
265:
266: $ip = htmlspecialchars($ip, ENT_QUOTES);
267: $type = htmlspecialchars($type, ENT_QUOTES);
268: if ('{"' == substr($description, 0, 2) && defined('JSON_PRETTY_PRINT')) {
269: $temp = json_decode($description);
270: if (is_object($temp)) {
271: $description = json_encode($temp, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
272: $style = ' log_description';
273: }
274: }
275: $description = htmlspecialchars($description, ENT_QUOTES);
276: $uname = htmlspecialchars(($uid ? $uname : _GUESTS), ENT_QUOTES);
277:
278: // make agents shorter
279: if (preg_match('/Chrome\/([0-9.]+)/', $agent, $regs)) {
280: $agent_short = 'Chrome ' . $regs[1];
281: } elseif (preg_match('/MSIE\s+([0-9.]+)/', $agent, $regs)) {
282: $agent_short = 'IE ' . $regs[1];
283: } elseif (false !== stripos($agent, 'Gecko')) {
284: $agent_short = strrchr($agent, ' ');
285: } else {
286: $agent_short = substr($agent, 0, strpos($agent, ' '));
287: }
288: $agent4disp = htmlspecialchars($agent, ENT_QUOTES);
289: $agent_desc = $agent == $agent_short ? $agent4disp : htmlspecialchars($agent_short, ENT_QUOTES) . "<img src='../images/dotdotdot.gif' alt='$agent4disp' title='$agent4disp' />";
290:
291: echo "
292: <tr>
293: <td class='$oddeven'><input type='checkbox' name='ids[]' value='$lid' /></td>
294: <td class='$oddeven'>" . formatTimestamp($timestamp) . "</td>
295: <td class='$oddeven'>$uname</td>
296: <td class='$oddeven'>$ip<br>$agent_desc</td>
297: <td class='$oddeven'>$type</td>
298: <td class='{$oddeven}{$style}'>$description</td>
299: </tr>\n";
300: }
301:
302: // footer of log listing
303: echo "
304: <tr>
305: <td colspan='8' align='left'>" . _AM_LABEL_REMOVE . "<input type='button' value='" . _AM_BUTTON_REMOVE . "' onclick='if (confirm(\"" . _AM_JS_REMOVECONFIRM . "\")) {document.MainForm.action.value=\"delete\"; submit();}' />
306: &nbsp " . _AM_LABEL_BAN_BY_IP . "<input type='button' value='" . _AM_BUTTON_BAN_BY_IP . "' onclick='if (confirm(\"" . _AM_JS_BANCONFIRM . "\")) {document.MainForm.action.value=\"banbyip\"; submit();}' /></td>
307: </tr>
308: </table>
309: <div align='right'>
310: $nav_html
311: </div>
312: <div style='clear:both;'><br><br></div>
313: <div align='right'>
314: " . _AM_LABEL_COMPACTLOG . "<input type='button' value='" . _AM_BUTTON_COMPACTLOG . "' onclick='if (confirm(\"" . _AM_JS_COMPACTLOGCONFIRM . "\")) {document.MainForm.action.value=\"compactlog\"; submit();}' />
315: &nbsp;
316: " . _AM_LABEL_REMOVEALL . "<input type='button' value='" . _AM_BUTTON_REMOVEALL . "' onclick='if (confirm(\"" . _AM_JS_REMOVEALLCONFIRM . "\")) {document.MainForm.action.value=\"deleteall\"; submit();}' />
317: </div>
318: </form>
319: </td></tr></table>
320: ";
321:
322: xoops_cp_footer();
323:
324: /**
325: * Callback used by uksort and usort for ip sorting
326: *
327: * @param string $a
328: * @param string $b
329: *
330: * @return int
331: */
332: function protector_ip_cmp($a, $b)
333: {
334: // ipv6 below ipv4
335: if ((false === strpos($a, ':')) && false !== strpos($b, ':')) {
336: return -1;
337: }
338: // ipv4 above ipv6
339: if ((false === strpos($a, '.')) && false !== strpos($b, '.')) {
340: return 1;
341: }
342: // normalize ipv4 before comparing
343: if ((is_int(strpos($a, '.'))) && (is_int(strpos($b, '.')))) {
344: $a = protector_normalize_ipv4($a);
345: $b = protector_normalize_ipv4($b);
346: }
347: return strcasecmp($a, $b);
348: }
349:
350: /**
351: * pad all octets in an ipv4 address to 3 digits for sorting
352: *
353: * @param string $n ipv4 address
354: *
355: * @return string
356: */
357: function protector_normalize_ipv4($n)
358: {
359: $temp = explode('.', $n);
360: $n = '';
361: foreach($temp as $k=>$v) {
362: $t = '00'. $v;
363: $n .= substr($t, -3);
364: if ($k<3) {
365: $n .= '.';
366: }
367: }
368: return $n;
369: }
370: