diff --git a/comments.php b/comments.php index 697001c..7ac3fbd 100644 --- a/comments.php +++ b/comments.php @@ -111,7 +111,12 @@ function threadedComments($comments, $options) {
date('Y-m-d H:i'); ?>
- ip); ?> + + ip; // Ensure this is the correct property for IP + $location = get_ip_location($ip); + echo htmlspecialchars($location['country']); + ?> reply(''); ?> diff --git a/functions.php b/functions.php index 09975bb..d43fef6 100644 --- a/functions.php +++ b/functions.php @@ -1,6 +1,6 @@ addInput($logoUrl); @@ -477,155 +477,17 @@ function commentApprove($widget, $email = NULL) /** 获取评论者地址 */ -function convertips($ip) { - $ip1num = 0; - $ip2num = 0; - $ipAddr1 = ""; - $ipAddr2 = ""; - $dat_path = dirname(__FILE__) . '/dist/qqwry.dat'; - - // 验证IP格式 - if (!preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/", $ip)) { - return 'IPV6无法获取归宿地'; - } - - // 打开数据文件 - if (!$fd = @fopen($dat_path, 'rb')) { - return 'IP 数据库路径不正确'; - } - - // 计算IP数值 - $ip = explode('.', $ip); - $ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3]; - $DataBegin = fread($fd, 4); - $DataEnd = fread($fd, 4); - $ipbegin = implode('', unpack('L', $DataBegin)); - if ($ipbegin < 0) $ipbegin += pow(2, 32); - $ipend = implode('', unpack('L', $DataEnd)); - if ($ipend < 0) $ipend += pow(2, 32); - $ipAllNum = ($ipend - $ipbegin) / 7 + 1; - $BeginNum = 0; - $EndNum = $ipAllNum; - - // 二分查找IP - while ($ip1num > $ipNum || $ip2num < $ipNum) { - $Middle = intval(($EndNum + $BeginNum) / 2); - fseek($fd, $ipbegin + 7 * $Middle); - $ipData1 = fread($fd, 4); - if (strlen($ipData1) < 4) { - fclose($fd); - return 'System Error'; - } - $ip1num = implode('', unpack('L', $ipData1)); - if ($ip1num < 0) $ip1num += pow(2, 32); - - if ($ip1num > $ipNum) { - $EndNum = $Middle; - continue; - } - $DataSeek = fread($fd, 3); - if (strlen($DataSeek) < 3) { - fclose($fd); - return 'System Error'; - } - $DataSeek = implode('', unpack('L', $DataSeek . chr(0))); - fseek($fd, $DataSeek); - $ipData2 = fread($fd, 4); - if (strlen($ipData2) < 4) { - fclose($fd); - return 'System Error'; - } - $ip2num = implode('', unpack('L', $ipData2)); - if ($ip2num < 0) $ip2num += pow(2, 32); - if ($ip2num < $ipNum) { - if ($Middle == $BeginNum) { - fclose($fd); - return 'Unknown'; - } - $BeginNum = $Middle; - } - } - - // 读取地址信息 - $ipFlag = fread($fd, 1); - if ($ipFlag == chr(1)) { - $ipSeek = fread($fd, 3); - if (strlen($ipSeek) < 3) { - fclose($fd); - return 'System Error'; - } - $ipSeek = implode('', unpack('L', $ipSeek . chr(0))); - fseek($fd, $ipSeek); - $ipFlag = fread($fd, 1); - } - - // 读取省份和国家 - if ($ipFlag == chr(2)) { - $AddrSeek = fread($fd, 3); - if (strlen($AddrSeek) < 3) { - fclose($fd); - return 'System Error'; - } - $ipFlag = fread($fd, 1); - if ($ipFlag == chr(2)) { - $AddrSeek2 = fread($fd, 3); - if (strlen($AddrSeek2) < 3) { - fclose($fd); - return 'System Error'; - } - $AddrSeek2 = implode('', unpack('L', $AddrSeek2 . chr(0))); - fseek($fd, $AddrSeek2); - } else { - fseek($fd, -1, SEEK_CUR); - } - while (($char = fread($fd, 1)) != chr(0)) - $ipAddr2 .= $char; - $AddrSeek = implode('', unpack('L', $AddrSeek . chr(0))); - fseek($fd, $AddrSeek); - while (($char = fread($fd, 1)) != chr(0)) - $ipAddr1 .= $char; +function get_ip_location($ip) { + $qqwry = new QQWry(__DIR__ . '/dist/qqwry.dat'); + $detail = $qqwry->getDetail($ip); + if ($detail) { + // Assuming dataA is country and dataB is province + return array( + 'country' => $detail['dataA'] + ); } else { - fseek($fd, -1, SEEK_CUR); - while (($char = fread($fd, 1)) != chr(0)) - $ipAddr1 .= $char; - $ipFlag = fread($fd, 1); - if ($ipFlag == chr(2)) { - $AddrSeek2 = fread($fd, 3); - if (strlen($AddrSeek2) < 3) { - fclose($fd); - return 'System Error'; - } - $AddrSeek2 = implode('', unpack('L', $AddrSeek2 . chr(0))); - fseek($fd, $AddrSeek2); - } else { - fseek($fd, -1, SEEK_CUR); - } - while (($char = fread($fd, 1)) != chr(0)) { - $ipAddr2 .= $char; - } + return array( + 'country' => 'Unknown' + ); } - fclose($fd); - - // 处理结果,去除城市和运营商信息 - $ipAddr1 = iconv('gbk', 'utf-8//IGNORE', trim($ipAddr1)); - $ipAddr2 = iconv('gbk', 'utf-8//IGNORE', trim($ipAddr2)); - - // 只返回国家和省份 - $result = "$ipAddr1 $ipAddr2"; - - // 清理多余信息 - $result = preg_replace('/(.*?)(?:–.*)?/', '$1', $result); // 只保留前面的部分 - $result = preg_replace('/CZ88.NET|http.*$/i', '', $result); - $result = preg_replace('/\s+/', ' ', $result); // 合并多余空格 - $result = trim($result); - - if (empty($result)) { - return '可能来自火星'; - } - - return $result; -} - -function convertip($ip) { - echo convertips($ip); -} +} \ No newline at end of file diff --git a/qqwry.php b/qqwry.php new file mode 100644 index 0000000..9795aa0 --- /dev/null +++ b/qqwry.php @@ -0,0 +1,164 @@ +fp = fopen($fileName, 'rb'); + $this->firstRecord = $this->read4byte(); + $this->lastRecord = $this->read4byte(); + $this->recordNum = ($this->lastRecord - $this->firstRecord) / 7; // 每条索引长度为7字节 + } + + public function __destruct() { // 析构函数 + if ($this->fp) { + fclose($this->fp); + } + } + + private function read4byte() { // 读取4字节并转为long + return unpack('Vlong', fread($this->fp, 4))['long']; + } + + private function read3byte() { // 读取3字节并转为long + return unpack('Vlong', fread($this->fp, 3) . chr(0))['long']; + } + + private function readString() { // 读取字符串 + $str = ''; + $char = fread($this->fp, 1); + while (ord($char) != 0) { // 读到二进制0结束 + $str .= $char; + $char = fread($this->fp, 1); + } + return $str; + } + + private function zipIP($ip) { // IP地址转为数字 + $ip_arr = explode('.', $ip); + $tmp = (16777216 * intval($ip_arr[0])) + (65536 * intval($ip_arr[1])) + (256 * intval($ip_arr[2])) + intval($ip_arr[3]); + return pack('N', intval($tmp)); // 32位无符号大端序长整型 + } + + private function unzipIP($ip) { // 数字转为IP地址 + return long2ip($ip); + } + + public function getVersion() { // 获取当前数据库的版本 + fseek($this->fp, $this->lastRecord + 4); + $tmp = $this->getRecord($this->read3byte())['B']; + return substr($tmp, 0, 4) . substr($tmp, 7, 2) . substr($tmp, 12, 2); + } + + public function getDetail($ip) { // 获取IP地址区段及所在位置 + if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { // 判断是否为IPv4地址 + return null; + } + + fseek($this->fp, $this->searchRecord($ip)); // 跳转到对应IP记录的位置 + $detail['beginIP'] = $this->unzipIP($this->read4byte()); // 目标IP所在网段的起始IP + $offset = $this->read3byte(); // 索引后3字节为对应记录的偏移量 + fseek($this->fp, $offset); + $detail['endIP'] = $this->unzipIP($this->read4byte()); // 目标IP所在网段的结束IP + + $tmp = $this->getRecord($offset); // 获取记录的dataA与dataB + $detail['dataA'] = $tmp['A']; + $detail['dataB'] = $tmp['B']; + + if ($detail['beginIP'] == '255.255.255.0') { // 去除附加信息 + $detail['dataA'] = 'IANA'; + $detail['dataB'] = '保留地址'; + } + if ($detail['dataA'] == ' CZ88.NET' || $detail['dataA'] == '纯真网络') { + $detail['dataA'] = ''; + } + if ($detail['dataB'] == ' CZ88.NET') { + $detail['dataB'] = ''; + } + return $detail; + } + + private function searchRecord($ip) { // 根据IP地址获取索引的绝对偏移量 + $ip = $this->zipIP($ip); // 转为数字以比较大小 + $down = 0; + $up = $this->recordNum; + while ($down <= $up) { // 二分法查找 + $mid = floor(($down + $up) / 2); // 计算二分点 + fseek($this->fp, $this->firstRecord + $mid * 7); + $beginip = strrev(fread($this->fp, 4)); // 获取二分区域的下边界 + if ($ip < $beginip) { // 目标IP在二分区域以下 + $up = $mid - 1; // 缩小搜索的上边界 + } else { + fseek($this->fp, $this->read3byte()); + $endip = strrev(fread($this->fp, 4)); // 获取二分区域的上边界 + if ($ip > $endip) { // 目标IP在二分区域以上 + $down = $mid + 1; // 缩小搜索的下边界 + } else { // 目标IP在二分区域内 + return $this->firstRecord + $mid * 7; // 返回索引的偏移量 + } + } + } + return $this->lastRecord; // 无法找到对应索引,返回最后一条记录的偏移量 + } + + private function getRecord($offset) { // 读取IP记录的数据 + fseek($this->fp, $offset + 4); + $flag = ord(fread($this->fp, 1)); + if ($flag == 1) { // dataA与dataB均重定向 + $offset = $this->read3byte(); // 重定向偏移 + fseek($this->fp, $offset); + if (ord(fread($this->fp, 1)) == 2) { // dataA再次重定向 + fseek($this->fp, $this->read3byte()); + $data['A'] = $this->readString(); + fseek($this->fp, $offset + 4); + $data['B'] = $this->getDataB(); + } else { // dataA无重定向 + fseek($this->fp, -1, SEEK_CUR); // 文件指针回退1字节 + $data['A'] = $this->readString(); + $data['B'] = $this->getDataB(); + } + } else if ($flag == 2) { // dataA重定向 + fseek($this->fp, $this->read3byte()); + $data['A'] = $this->readString(); + fseek($this->fp, $offset + 8); // IP占4字节, 重定向标志占1字节, dataA指针占3字节 + $data['B'] = $this->getDataB(); + } else { // dataA无重定向 + fseek($this->fp, -1, SEEK_CUR); // 文件指针回退1字节 + $data['A'] = $this->readString(); + $data['B'] = $this->getDataB(); + } + $data['A'] = iconv("GBK", "UTF-8", $data['A']); // GBK -> UTF-8 + $data['B'] = iconv("GBK", "UTF-8", $data['B']); + return $data; + } + + private function getDataB() { // 从fp指定偏移获取dataB + $flag = ord(fread($this->fp, 1)); + if ($flag == 0) { // dataB无信息 + return ''; + } else if ($flag == 1 || $flag == 2) { // dataB重定向 + fseek($this->fp, $this->read3byte()); + return $this->readString(); + } else { // dataB无重定向 + fseek($this->fp, -1, SEEK_CUR); // 文件指针回退1字节 + return $this->readString(); + } + } +} +?> \ No newline at end of file