[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Toyo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ServerStatus-Toyo： \n\n![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)\n\n* ServerStatus-Toyo版是一个酷炫高逼格的云探针、云监控、服务器云监控、多服务器探针~，该云监控（云探针）是ServerStatus（ https://github.com/tenyue/ServerStatus ）项目的优化/修改版。\n* 在线演示：https://tz.toyoo.pw    \n* 我的博客：https://doub.io/shell-jc3/\n\n# 目录介绍：\n\n* clients  客户端文件\n* server   服务端文件\n* web      网站文件  \n\n# 更新说明：\n\n* 2018.08.21, 修改新样式，效果见 https://tz.toyoo.pw  \n* 2017.10.12, 负载Load 优化，并且支持CentOS6系统\n* 2017.10.10, 修改负载 Load 的值为：当前服务器上链接SSR等软件的IP总数(只要软件监听IPv6那么就能统计，例如SSH)\n* 2017.04.30, 优化手机显示式样\n* 2017.04.29, 去除主机名设定\n* 2017.04.27, 增加一键部署脚本\n\n# 安装教程：     \n\n执行下面的代码下载并运行脚本。\n``` bash\nwget -N --no-check-certificate https://raw.githubusercontent.com/ToyoDAdoubiBackup/doubi/master/status.sh && chmod +x status.sh\n```\n下载脚本后，根据需要安装客户端或者服务端：\n``` bash\n# 显示客户端管理菜单\nbash status.sh c\n \n# 显示服务端管理菜单\nbash status.sh s\n```\n运行脚本后会出现脚本操作菜单，选择并输入` 1 `就会开始安装。\n\n一开始会提示你输入 网站服务器的域名和端口，如果没有域名可以直接回车代表使用` 本机IP:8888`\n\n## 简单步骤：\n\n首先安装服务端，安装过程中会提示：\n\n``` bash\n是否由脚本自动配置HTTP服务(服务端的在线监控网站)[Y/n]\n \n# 如果你不懂，那就直接回车，如果你想用其他的HTTP服务自己配置，那么请输入 n 并回车。\n# 注意，当你曾经安装过 服务端，同时没有卸载Caddy(HTTP服务)，那么重新安装服务端的时候，请输入 n 并回车。\n```\n\n然后 添加或修改 初始示例的节点配置，注意用户名每个节点配置都不能重复，其他的参数都无所谓了。\n\n然后安装客户端，根据提示填写 服务端的IP 和前面添加/修改 对应的 节点用户名和密码（用于和服务端验证），然后启动就好了，有问题请贴出 详细步骤+日志(如果有)联系我。\n\n# 使用说明：\n\n进入下载脚本的目录并运行脚本：\n\n``` bash\n# 客户端管理菜单\n./status.sh c\n# 服务端管理菜单\n./status.sh s\n```\n\n然后选择你要执行的选项即可。\n\n``` bash\nServerStatus 一键安装管理脚本 [vx.x.x]\n-- Toyo | doub.io/shell-jc3 --\n \n0. 升级脚本\n————————————\n1. 安装 服务端\n2. 卸载 服务端\n————————————\n3. 启动 服务端\n4. 停止 服务端\n5. 重启 服务端\n————————————\n6. 设置 服务端配置\n7. 查看 服务端信息\n8. 查看 服务端日志\n————————————\n9. 切换为 客户端菜单\n \n当前状态: 服务端 已安装 并 已启动\n \n请输入数字 [0-9]:\n```\n# 其他操作\n\n### 客户端：\n\n启动：service status-client start\n\n停止：service status-client stop\n\n重启：service status-client restart\n\n查看状态：service status-client status\n\n### 服务端：\n\n启动：service status-server start\n\n停止：service status-server stop\n\n重启：service status-server restart\n\n查看状态：service status-server status\n\n### Caddy（HTTP服务）：\n\n启动：service caddy start\n\n停止：service caddy stop\n\n重启：service caddy restart\n\n查看状态：service caddy status\n\nCaddy配置文件：/usr/local/caddy/caddy\n\n默认脚本只能一开始安装的时候设置配置文件，更多的Caddy使用方法，可以参考这些教程：https://doub.io/search/caddy\n\n——————————————————————————————————————\n\n安装目录：/usr/local/ServerStatus\n\n网页文件：/usr/local/ServerStatus/web\n\n配置文件：/usr/local/ServerStatus/server/config.json\n\n客户端查看日志：tail -f tmp/serverstatus_client.log\n\n服务端查看日志：tail -f /tmp/serverstatus_server.log\n\n# 其他说明\n\n网络实时流量单位为：G=GB/s，M=MB/s，K=KB/s\n\n服务器总流量单位为：T=TB，G=GB，M=MB，K=KB\n\n### CentOS7系统 负载显示异常的问题\n\nCentOS7系统 默认可能没有安装 netstat 依赖，所以会造成IP检测(负载)出错，手动安装即可：\n`yum install net-tools -y `\n\n# 相关开源项目，感谢： \n\n* ServerStatus：https://github.com/BotoX/ServerStatus\n* mojeda: https://github.com/mojeda \n* mojeda's ServerStatus: https://github.com/mojeda/ServerStatus\n* BlueVM's project: http://www.lowendtalk.com/discussion/comment/169690#Comment_169690\n"
  },
  {
    "path": "clients/client-linux.py",
    "content": "# -*- coding: utf-8 -*-\n# Update by : https://github.com/tenyue/ServerStatus\n# 支持Python版本：2.6 to 3.5\n# 支持操作系统： Linux, OSX, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures\n\n\nSERVER = \"127.0.0.1\"\nPORT = 35601\nUSER = \"USER\" \nPASSWORD = \"USER_PASSWORD\"\nINTERVAL = 1 #更新间隔\n\n\nimport socket\nimport time\nimport string\nimport math\nimport re\nimport os\nimport json\nimport subprocess\nimport collections\nimport platform\n\ndef get_uptime():\n\tf = open('/proc/uptime', 'r')\n\tuptime = f.readline()\n\tf.close()\n\tuptime = uptime.split('.', 2)\n\ttime = int(uptime[0])\n\treturn int(time)\n\ndef get_memory():\n\tre_parser = re.compile(r'^(?P<key>\\S*):\\s*(?P<value>\\d*)\\s*kB')\n\tresult = dict()\n\tfor line in open('/proc/meminfo'):\n\t\tmatch = re_parser.match(line)\n\t\tif not match:\n\t\t\tcontinue;\n\t\tkey, value = match.groups(['key', 'value'])\n\t\tresult[key] = int(value)\n\n\tMemTotal = float(result['MemTotal'])\n\tMemFree = float(result['MemFree'])\n\tCached = float(result['Cached'])\n\tMemUsed = MemTotal - (Cached + MemFree)\n\tSwapTotal = float(result['SwapTotal'])\n\tSwapFree = float(result['SwapFree'])\n\treturn int(MemTotal), int(MemUsed), int(SwapTotal), int(SwapFree)\n\ndef get_hdd():\n\tp = subprocess.check_output(['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode(\"Utf-8\")\n\ttotal = p.splitlines()[-1]\n\tused = total.split()[3]\n\tsize = total.split()[2]\n\treturn int(size), int(used)\n\ndef get_load():\n\tsystem = platform.linux_distribution()\n\tif system[0][:6] == \"CentOS\":\n\t\tif system[1][0] == \"6\":\n\t\t\ttmp_load = os.popen(\"netstat -anp |grep ESTABLISHED |grep tcp |grep '::ffff:' |awk '{print $5}' |awk -F ':' '{print $4}' |sort -u |grep -E -o '([0-9]{1,3}[\\.]){3}[0-9]{1,3}' |wc -l\").read()\n\t\telse:\n\t\t\ttmp_load = os.popen(\"netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\\.]){3}[0-9]{1,3}' |wc -l\").read()\n\telse:\n\t\ttmp_load = os.popen(\"netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\\.]){3}[0-9]{1,3}' |wc -l\").read()\n\t\n\treturn float(tmp_load)\n\t#return os.getloadavg()[0]\n\ndef get_time():\n\tstat_file = file(\"/proc/stat\", \"r\")\n\ttime_list = stat_file.readline().split(' ')[2:6]\n\tstat_file.close()\n\tfor i in range(len(time_list))  :\n\t\ttime_list[i] = int(time_list[i])\n\treturn time_list\ndef delta_time():\n\tx = get_time()\n\ttime.sleep(INTERVAL)\n\ty = get_time()\n\tfor i in range(len(x)):\n\t\ty[i]-=x[i]\n\treturn y\ndef get_cpu():\n\tt = delta_time()\n\tst = sum(t)\n\tif st == 0:\n\t\tst = 1\n\tresult = 100-(t[len(t)-1]*100.00/st)\n\treturn round(result)\n\nclass Traffic:\n\tdef __init__(self):\n\t\tself.rx = collections.deque(maxlen=10)\n\t\tself.tx = collections.deque(maxlen=10)\n\tdef get(self):\n\t\tf = open('/proc/net/dev', 'r')\n\t\tnet_dev = f.readlines()\n\t\tf.close()\n\t\tavgrx = 0; avgtx = 0\n\n\t\tfor dev in net_dev[2:]:\n\t\t\tdev = dev.split(':')\n\t\t\tif dev[0].strip() == \"lo\" or dev[0].find(\"tun\") > -1:\n\t\t\t\tcontinue\n\t\t\tdev = dev[1].split()\n\t\t\tavgrx += int(dev[0])\n\t\t\tavgtx += int(dev[8])\n\n\t\tself.rx.append(avgrx)\n\t\tself.tx.append(avgtx)\n\t\tavgrx = 0; avgtx = 0\n\n\t\tl = len(self.rx)\n\t\tfor x in range(l - 1):\n\t\t\tavgrx += self.rx[x+1] - self.rx[x]\n\t\t\tavgtx += self.tx[x+1] - self.tx[x]\n\n\t\tavgrx = int(avgrx / l / INTERVAL)\n\t\tavgtx = int(avgtx / l / INTERVAL)\n\n\t\treturn avgrx, avgtx\n\ndef liuliang():\n    NET_IN = 0\n    NET_OUT = 0\n    with open('/proc/net/dev') as f:\n        for line in f.readlines():\n            netinfo = re.findall('([^\\s]+):[\\s]{0,}(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)', line)\n            if netinfo:\n                if netinfo[0][0] == 'lo' or 'tun' in netinfo[0][0] or netinfo[0][1]=='0' or netinfo[0][9]=='0':\n                    continue\n                else:\n                    NET_IN += int(netinfo[0][1])\n                    NET_OUT += int(netinfo[0][9])\n    return NET_IN, NET_OUT\n\ndef get_network(ip_version):\n\tif(ip_version == 4):\n\t\tHOST = \"ipv4.google.com\"\n\telif(ip_version == 6):\n\t\tHOST = \"ipv6.google.com\"\n\ttry:\n\t\ts = socket.create_connection((HOST, 80), 2)\n\t\treturn True\n\texcept:\n\t\tpass\n\treturn False\n\nif __name__ == '__main__':\n\tsocket.setdefaulttimeout(30)\n\twhile 1:\n\t\ttry:\n\t\t\tprint(\"Connecting...\")\n\t\t\ts = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n\t\t\ts.connect((SERVER, PORT))\n\t\t\tdata = s.recv(1024)\n\t\t\tif data.find(\"Authentication required\") > -1:\n\t\t\t\ts.send(USER + ':' + PASSWORD + '\\n')\n\t\t\t\tdata = s.recv(1024)\n\t\t\t\tif data.find(\"Authentication successful\") < 0:\n\t\t\t\t\tprint(data)\n\t\t\t\t\traise socket.error\n\t\t\telse:\n\t\t\t\tprint(data)\n\t\t\t\traise socket.error\n\n\t\t\tprint(data)\n\t\t\tdata = s.recv(1024)\n\t\t\tprint(data)\n\n\t\t\ttimer = 0\n\t\t\tcheck_ip = 0\n\t\t\tif data.find(\"IPv4\") > -1:\n\t\t\t\tcheck_ip = 6\n\t\t\telif data.find(\"IPv6\") > -1:\n\t\t\t\tcheck_ip = 4\n\t\t\telse:\n\t\t\t\tprint(data)\n\t\t\t\traise socket.error\n\n\t\t\ttraffic = Traffic()\n\t\t\ttraffic.get()\n\t\t\twhile 1:\n\t\t\t\tCPU = get_cpu()\n\t\t\t\tNetRx, NetTx = traffic.get()\n\t\t\t\tNET_IN, NET_OUT = liuliang()\n\t\t\t\tUptime = get_uptime()\n\t\t\t\tLoad = get_load()\n\t\t\t\tMemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory()\n\t\t\t\tHDDTotal, HDDUsed = get_hdd()\n\n\t\t\t\tarray = {}\n\t\t\t\tif not timer:\n\t\t\t\t\tarray['online' + str(check_ip)] = get_network(check_ip)\n\t\t\t\t\ttimer = 10\n\t\t\t\telse:\n\t\t\t\t\ttimer -= 1*INTERVAL\n\n\t\t\t\tarray['uptime'] = Uptime\n\t\t\t\tarray['load'] = Load\n\t\t\t\tarray['memory_total'] = MemoryTotal\n\t\t\t\tarray['memory_used'] = MemoryUsed\n\t\t\t\tarray['swap_total'] = SwapTotal\n\t\t\t\tarray['swap_used'] = SwapTotal - SwapFree\n\t\t\t\tarray['hdd_total'] = HDDTotal\n\t\t\t\tarray['hdd_used'] = HDDUsed\n\t\t\t\tarray['cpu'] = CPU\n\t\t\t\tarray['network_rx'] = NetRx\n\t\t\t\tarray['network_tx'] = NetTx\n\t\t\t\tarray['network_in'] = NET_IN\n\t\t\t\tarray['network_out'] = NET_OUT\n\n\t\t\t\ts.send(\"update \" + json.dumps(array) + \"\\n\")\n\t\texcept KeyboardInterrupt:\n\t\t\traise\n\t\texcept socket.error:\n\t\t\tprint(\"Disconnected...\")\n\t\t\t# keep on trying after a disconnect\n\t\t\ts.close()\n\t\t\ttime.sleep(3)\n\t\texcept Exception as e:\n\t\t\tprint(\"Caught Exception:\", e)\n\t\t\ts.close()\n\t\t\ttime.sleep(3)\n"
  },
  {
    "path": "clients/status-client.py",
    "content": "﻿# -*- coding: utf-8 -*-\n\nSERVER = \"127.0.0.1\"\nPORT = PORT\nUSER = \"USER\" \nPASSWORD = \"USER_PASSWORD\"\nINTERVAL = 1 #更新间隔，单位：秒\n\n\nimport socket\nimport time\nimport string\nimport math\nimport re\nimport os\nimport json\nimport subprocess\nimport collections\nimport platform\n\ndef get_uptime():\n\tf = open('/proc/uptime', 'r')\n\tuptime = f.readline()\n\tf.close()\n\tuptime = uptime.split('.', 2)\n\ttime = int(uptime[0])\n\treturn int(time)\n\ndef get_memory():\n\tre_parser = re.compile(r'^(?P<key>\\S*):\\s*(?P<value>\\d*)\\s*kB')\n\tresult = dict()\n\tfor line in open('/proc/meminfo'):\n\t\tmatch = re_parser.match(line)\n\t\tif not match:\n\t\t\tcontinue;\n\t\tkey, value = match.groups(['key', 'value'])\n\t\tresult[key] = int(value)\n\n\tMemTotal = float(result['MemTotal'])\n\tMemFree = float(result['MemFree'])\n\tCached = float(result['Cached'])\n\tMemUsed = MemTotal - (Cached + MemFree)\n\tSwapTotal = float(result['SwapTotal'])\n\tSwapFree = float(result['SwapFree'])\n\treturn int(MemTotal), int(MemUsed), int(SwapTotal), int(SwapFree)\n\ndef get_hdd():\n\tp = subprocess.check_output(['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode(\"Utf-8\")\n\ttotal = p.splitlines()[-1]\n\tused = total.split()[3]\n\tsize = total.split()[2]\n\treturn int(size), int(used)\n\ndef get_load():\n\tsystem = platform.linux_distribution()\n\tif system[0][:6] == \"CentOS\":\n\t\tif system[1][0] == \"6\":\n\t\t\ttmp_load = os.popen(\"netstat -anp |grep ESTABLISHED |grep tcp |grep '::ffff:' |awk '{print $5}' |awk -F ':' '{print $4}' |sort -u |grep -E -o '([0-9]{1,3}[\\.]){3}[0-9]{1,3}' |wc -l\").read()\n\t\telse:\n\t\t\ttmp_load = os.popen(\"netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\\.]){3}[0-9]{1,3}' |wc -l\").read()\n\telse:\n\t\ttmp_load = os.popen(\"netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\\.]){3}[0-9]{1,3}' |wc -l\").read()\n\t\n\treturn float(tmp_load)\n\t#return os.getloadavg()[0]\n\ndef get_time():\n\tstat_file = file(\"/proc/stat\", \"r\")\n\ttime_list = stat_file.readline().split(' ')[2:6]\n\tstat_file.close()\n\tfor i in range(len(time_list))  :\n\t\ttime_list[i] = int(time_list[i])\n\treturn time_list\ndef delta_time():\n\tx = get_time()\n\ttime.sleep(INTERVAL)\n\ty = get_time()\n\tfor i in range(len(x)):\n\t\ty[i]-=x[i]\n\treturn y\ndef get_cpu():\n\tt = delta_time()\n\tst = sum(t)\n\tif st == 0:\n\t\tst = 1\n\tresult = 100-(t[len(t)-1]*100.00/st)\n\treturn round(result)\n\nclass Traffic:\n\tdef __init__(self):\n\t\tself.rx = collections.deque(maxlen=10)\n\t\tself.tx = collections.deque(maxlen=10)\n\tdef get(self):\n\t\tf = open('/proc/net/dev', 'r')\n\t\tnet_dev = f.readlines()\n\t\tf.close()\n\t\tavgrx = 0; avgtx = 0\n\n\t\tfor dev in net_dev[2:]:\n\t\t\tdev = dev.split(':')\n\t\t\tif dev[0].strip() == \"lo\" or dev[0].find(\"tun\") > -1:\n\t\t\t\tcontinue\n\t\t\tdev = dev[1].split()\n\t\t\tavgrx += int(dev[0])\n\t\t\tavgtx += int(dev[8])\n\n\t\tself.rx.append(avgrx)\n\t\tself.tx.append(avgtx)\n\t\tavgrx = 0; avgtx = 0\n\n\t\tl = len(self.rx)\n\t\tfor x in range(l - 1):\n\t\t\tavgrx += self.rx[x+1] - self.rx[x]\n\t\t\tavgtx += self.tx[x+1] - self.tx[x]\n\n\t\tavgrx = int(avgrx / l / INTERVAL)\n\t\tavgtx = int(avgtx / l / INTERVAL)\n\n\t\treturn avgrx, avgtx\n\ndef liuliang():\n    NET_IN = 0\n    NET_OUT = 0\n    with open('/proc/net/dev') as f:\n        for line in f.readlines():\n            netinfo = re.findall('([^\\s]+):[\\s]{0,}(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)', line)\n            if netinfo:\n                if netinfo[0][0] == 'lo' or 'tun' in netinfo[0][0] or netinfo[0][1]=='0' or netinfo[0][9]=='0':\n                    continue\n                else:\n                    NET_IN += int(netinfo[0][1])\n                    NET_OUT += int(netinfo[0][9])\n    return NET_IN, NET_OUT\n\ndef get_network(ip_version):\n\tif(ip_version == 4):\n\t\tHOST = \"ipv4.google.com\"\n\telif(ip_version == 6):\n\t\tHOST = \"ipv6.google.com\"\n\ttry:\n\t\ts = socket.create_connection((HOST, 80), 2)\n\t\treturn True\n\texcept:\n\t\tpass\n\treturn False\n\nif __name__ == '__main__':\n\tsocket.setdefaulttimeout(30)\n\twhile 1:\n\t\ttry:\n\t\t\tprint(\"Connecting...\")\n\t\t\ts = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n\t\t\ts.connect((SERVER, PORT))\n\t\t\tdata = s.recv(1024)\n\t\t\tif data.find(\"Authentication required\") > -1:\n\t\t\t\ts.send(USER + ':' + PASSWORD + '\\n')\n\t\t\t\tdata = s.recv(1024)\n\t\t\t\tif data.find(\"Authentication successful\") < 0:\n\t\t\t\t\tprint(data)\n\t\t\t\t\traise socket.error\n\t\t\telse:\n\t\t\t\tprint(data)\n\t\t\t\traise socket.error\n\n\t\t\tprint(data)\n\t\t\tdata = s.recv(1024)\n\t\t\tprint(data)\n\n\t\t\ttimer = 0\n\t\t\tcheck_ip = 0\n\t\t\tif data.find(\"IPv4\") > -1:\n\t\t\t\tcheck_ip = 6\n\t\t\telif data.find(\"IPv6\") > -1:\n\t\t\t\tcheck_ip = 4\n\t\t\telse:\n\t\t\t\tprint(data)\n\t\t\t\traise socket.error\n\n\t\t\ttraffic = Traffic()\n\t\t\ttraffic.get()\n\t\t\twhile 1:\n\t\t\t\tCPU = get_cpu()\n\t\t\t\tNetRx, NetTx = traffic.get()\n\t\t\t\tNET_IN, NET_OUT = liuliang()\n\t\t\t\tUptime = get_uptime()\n\t\t\t\tLoad = get_load()\n\t\t\t\tMemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory()\n\t\t\t\tHDDTotal, HDDUsed = get_hdd()\n\n\t\t\t\tarray = {}\n\t\t\t\tif not timer:\n\t\t\t\t\tarray['online' + str(check_ip)] = get_network(check_ip)\n\t\t\t\t\ttimer = 10\n\t\t\t\telse:\n\t\t\t\t\ttimer -= 1*INTERVAL\n\n\t\t\t\tarray['uptime'] = Uptime\n\t\t\t\tarray['load'] = Load\n\t\t\t\tarray['memory_total'] = MemoryTotal\n\t\t\t\tarray['memory_used'] = MemoryUsed\n\t\t\t\tarray['swap_total'] = SwapTotal\n\t\t\t\tarray['swap_used'] = SwapTotal - SwapFree\n\t\t\t\tarray['hdd_total'] = HDDTotal\n\t\t\t\tarray['hdd_used'] = HDDUsed\n\t\t\t\tarray['cpu'] = CPU\n\t\t\t\tarray['network_rx'] = NetRx\n\t\t\t\tarray['network_tx'] = NetTx\n\t\t\t\tarray['network_in'] = NET_IN\n\t\t\t\tarray['network_out'] = NET_OUT\n\n\t\t\t\ts.send(\"update \" + json.dumps(array) + \"\\n\")\n\t\texcept KeyboardInterrupt:\n\t\t\traise\n\t\texcept socket.error:\n\t\t\tprint(\"Disconnected...\")\n\t\t\t# keep on trying after a disconnect\n\t\t\ts.close()\n\t\t\ttime.sleep(3)\n\t\texcept Exception as e:\n\t\t\tprint(\"Caught Exception:\", e)\n\t\t\ts.close()\n\t\t\ttime.sleep(3)\n"
  },
  {
    "path": "server/Makefile",
    "content": "OUT = sergate\n\n#CC = clang\nCC = gcc\nCFLAGS = -Wall -O2\n\n#CXX = clang++\nCXX = g++\nCXXFLAGS = -Wall -O2\n\nODIR = obj\nSDIR = src\nLIBS = -pthread -lm\nINC = -Iinclude\n\nC_SRCS := $(wildcard $(SDIR)/*.c)\nCXX_SRCS := $(wildcard $(SDIR)/*.cpp)\nC_OBJS := $(patsubst $(SDIR)/%.c,$(ODIR)/%.o,$(C_SRCS))\nCXX_OBJS := $(patsubst $(SDIR)/%.cpp,$(ODIR)/%.o,$(CXX_SRCS))\nOBJS := $(C_OBJS) $(CXX_OBJS)\n\n$(ODIR)/%.o: $(SDIR)/%.c\n\t$(CC) -c $(INC) $(CFLAGS) $< -o $@\n\n$(ODIR)/%.o: $(SDIR)/%.cpp\n\t$(CXX) -c $(INC) $(CXXFLAGS) $< -o $@\n\n$(OUT): $(OBJS)\n\t$(CXX) $(LIBS) $^ -o $(OUT)\n\n.PHONY: clean\n\nclean:\n\trm -f $(ODIR)/*.o $(OUT)\n"
  },
  {
    "path": "server/config.json",
    "content": "{\"servers\":\n\t[\n\t\t{\n\t\t\t\"username\": \"s01\",\n\t\t\t\"password\": \"password\",\n\t\t\t\"name\": \"Mainserver 1\",\n\t\t\t\"type\": \"Dedicated Server\",\n\t\t\t\"host\": \"No\",\n\t\t\t\"location\": \"Austria\",\n\t\t\t\"disabled\": false\n\t\t},\n\t\t{\n\t\t\t\"username\": \"bs01\",\n\t\t\t\"password\": \"password\",\n\t\t\t\"name\": \"Backupserver 1\",\n\t\t\t\"type\": \"Virtual Server\",\n\t\t\t\"host\": \"No\",\n\t\t\t\"location\": \"Switzerland\",\n\t\t\t\"disabled\": false\n\t\t},\n\t\t{\n\t\t\t\"username\": \"hidden\",\n\t\t\t\"password\": \"password\",\n\t\t\t\"name\": \"Secret\",\n\t\t\t\"type\": \"Nothing\",\n\t\t\t\"host\": \"No\",\n\t\t\t\"location\": \"Nowhere\",\n\t\t\t\"disabled\": true\n\t\t},\n\t\t{\n\t\t\t\"username\": \"butt\",\n\t\t\t\"password\": \"password\"，\n\t\t\t\"name\": \"Butt\",\n\t\t\t\"type\": \"Cloud Server\",\n\t\t\t\"host\": \"No\",\n\t\t\t\"location\": \"Heaven??!\",\n\t\t\t\"disabled\": false\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "server/include/argparse.h",
    "content": "#ifndef ARGPARSE_H\n#define ARGPARSE_H\n\n/**\n * Command-line arguments parsing library.\n *\n * This module is inspired by parse-options.c (git) and python's argparse\n * module.\n *\n * Arguments parsing is common task in cli program, but traditional `getopt`\n * libraries are not easy to use. This library provides high-level arguments\n * parsing solutions.\n *\n * The program defines what arguments it requires, and `argparse` will figure\n * out how to parse those out of `argc` and `argv`, it also automatically\n * generates help and usage messages and issues errors when users give the\n * program invalid arguments.\n *\n * Reserved namespaces:\n *  argparse\n *  OPT\n * Author: Yecheng Fu <cofyc.jackson@gmail.com>\n */\n\n#include <assert.h>\n#include <math.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct argparse;\nstruct argparse_option;\n\ntypedef int argparse_callback(struct argparse *this_,\n                              const struct argparse_option *option);\n\nenum argparse_flag {\n    ARGPARSE_STOP_AT_NON_OPTION = 1,\n};\n\nenum argparse_option_type {\n    /* special */\n    ARGPARSE_OPT_END,\n    /* options with no arguments */\n    ARGPARSE_OPT_BOOLEAN,\n    ARGPARSE_OPT_BIT,\n    /* options with arguments (optional or required) */\n    ARGPARSE_OPT_INTEGER,\n    ARGPARSE_OPT_STRING,\n};\n\nenum argparse_option_flags {\n    OPT_NONEG = 1,              /* Negation disabled. */\n};\n\n/*\n *  Argparse option struct.\n *\n *  `type`:\n *    holds the type of the option, you must have an ARGPARSE_OPT_END last in your\n *    array.\n *\n *  `short_name`:\n *    the character to use as a short option name, '\\0' if none.\n *\n *  `long_name`:\n *    the long option name, without the leading dash, NULL if none.\n *\n *  `value`:\n *    stores pointer to the value to be filled.\n *\n *  `help`:\n *    the short help message associated to what the option does.\n *    Must never be NULL (except for ARGPARSE_OPT_END).\n *\n *  `callback`:\n *    function is called when corresponding argument is parsed.\n *\n *  `data`:\n *    associated data. Callbacks can use it like they want.\n *\n *  `flags`:\n *    option flags.\n *\n */\nstruct argparse_option {\n    enum argparse_option_type type;\n    const char short_name;\n    const char *long_name;\n    void *value;\n    const char *help;\n    argparse_callback *callback;\n    intptr_t data;\n    int flags;\n};\n\n/*\n * argpparse\n */\nstruct argparse {\n    // user supplied\n    const struct argparse_option *options;\n    const char *usage;\n    int flags;\n    // internal context\n    int argc;\n    const char **argv;\n    const char **out;\n    int cpidx;\n    const char *optvalue;       // current option value\n};\n\n// builtin callbacks\nint argparse_help_cb(struct argparse *this_,\n                     const struct argparse_option *option);\n\n// builtin option macros\n#define OPT_END()          { ARGPARSE_OPT_END, 0 }\n#define OPT_BOOLEAN(...)   { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ }\n#define OPT_BIT(...)       { ARGPARSE_OPT_BIT, __VA_ARGS__ }\n#define OPT_INTEGER(...)   { ARGPARSE_OPT_INTEGER, __VA_ARGS__ }\n#define OPT_STRING(...)    { ARGPARSE_OPT_STRING, __VA_ARGS__ }\n#define OPT_HELP()         OPT_BOOLEAN('h', \"help\", 0, \"Show this help message and exit\", argparse_help_cb)\n\nint argparse_init(struct argparse *this_, struct argparse_option *options,\n                  const char *usage, int flags);\nint argparse_parse(struct argparse *this_, int argc, const char **argv);\nvoid argparse_usage(struct argparse *this_);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "server/include/detect.h",
    "content": "/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */\n/* If you are missing that file, acquire a complete release at teeworlds.com.                */\n#ifndef BASE_DETECT_H\n#define BASE_DETECT_H\n\n/*\n\tthis file detected the family, platform and architecture\n\tto compile for.\n*/\n\n/* platforms */\n\n/* windows Family */\n#if defined(WIN64) || defined(_WIN64)\n\t/* Hmm, is this IA64 or x86-64? */\n\t#define CONF_FAMILY_WINDOWS 1\n\t#define CONF_FAMILY_STRING \"windows\"\n\t#define CONF_PLATFORM_WIN64 1\n\t#define CONF_PLATFORM_STRING \"win64\"\n#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__)\n\t#define CONF_FAMILY_WINDOWS 1\n\t#define CONF_FAMILY_STRING \"windows\"\n\t#define CONF_PLATFORM_WIN32 1\n\t#define CONF_PLATFORM_STRING \"win32\"\n#endif\n\n/* unix family */\n#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)\n\t#define CONF_FAMILY_UNIX 1\n\t#define CONF_FAMILY_STRING \"unix\"\n\t#define CONF_PLATFORM_FREEBSD 1\n\t#define CONF_PLATFORM_STRING \"freebsd\"\n#endif\n\n#if defined(__OpenBSD__)\n\t#define CONF_FAMILY_UNIX 1\n\t#define CONF_FAMILY_STRING \"unix\"\n\t#define CONF_PLATFORM_OPENBSD 1\n\t#define CONF_PLATFORM_STRING \"openbsd\"\n#endif\n\n#if defined(__LINUX__) || defined(__linux__)\n\t#define CONF_FAMILY_UNIX 1\n\t#define CONF_FAMILY_STRING \"unix\"\n\t#define CONF_PLATFORM_LINUX 1\n\t#define CONF_PLATFORM_STRING \"linux\"\n#endif\n\n#if defined(__GNU__) || defined(__gnu__)\n\t#define CONF_FAMILY_UNIX 1\n\t#define CONF_FAMILY_STRING \"unix\"\n\t#define CONF_PLATFORM_HURD 1\n\t#define CONF_PLATFORM_STRING \"gnu\"\n#endif\n\n#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__)\n\t#define CONF_FAMILY_UNIX 1\n\t#define CONF_FAMILY_STRING \"unix\"\n\t#define CONF_PLATFORM_MACOSX 1\n\t#define CONF_PLATFORM_STRING \"macosx\"\n#endif\n\n#if defined(__sun)\n\t#define CONF_FAMILY_UNIX 1\n\t#define CONF_FAMILY_STRING \"unix\"\n\t#define CONF_PLATFORM_SOLARIS 1\n\t#define CONF_PLATFORM_STRING \"solaris\"\n#endif\n\n/* beos family */\n#if defined(__BeOS) || defined(__BEOS__)\n\t#define CONF_FAMILY_BEOS 1\n\t#define CONF_FAMILY_STRING \"beos\"\n\t#define CONF_PLATFORM_BEOS 1\n\t#define CONF_PLATFORM_STRING \"beos\"\n#endif\n\n\n/* use gcc endianness definitions when available */\n#if defined(__GNUC__) && !defined(__APPLE__) && !defined(__MINGW32__) && !defined(__sun)\n\t#if defined(__FreeBSD__) || defined(__OpenBSD__)\n\t\t#include <sys/endian.h>\n\t#else\n\t\t#include <endian.h>\n\t#endif\n\n\t#if __BYTE_ORDER == __LITTLE_ENDIAN\n\t\t#define CONF_ARCH_ENDIAN_LITTLE 1\n\t#elif __BYTE_ORDER == __BIG_ENDIAN\n\t\t#define CONF_ARCH_ENDIAN_BIG 1\n\t#endif\n#endif\n\n\n/* architectures */\n#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32)\n\t#define CONF_ARCH_IA32 1\n\t#define CONF_ARCH_STRING \"ia32\"\n\t#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)\n\t\t#define CONF_ARCH_ENDIAN_LITTLE 1\n\t#endif\n#endif\n\n#if defined(__ia64__) || defined(_M_IA64)\n\t#define CONF_ARCH_IA64 1\n\t#define CONF_ARCH_STRING \"ia64\"\n\t#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)\n\t\t#define CONF_ARCH_ENDIAN_LITTLE 1\n\t#endif\n#endif\n\n#if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64)\n\t#define CONF_ARCH_AMD64 1\n\t#define CONF_ARCH_STRING \"amd64\"\n\t#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)\n\t\t#define CONF_ARCH_ENDIAN_LITTLE 1\n\t#endif\n#endif\n\n#if defined(__powerpc__) || defined(__ppc__)\n\t#define CONF_ARCH_PPC 1\n\t#define CONF_ARCH_STRING \"ppc\"\n\t#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)\n\t\t#define CONF_ARCH_ENDIAN_BIG 1\n\t#endif\n#endif\n\n#if defined(__sparc__)\n\t#define CONF_ARCH_SPARC 1\n\t#define CONF_ARCH_STRING \"sparc\"\n\t#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)\n\t\t#define CONF_ARCH_ENDIAN_BIG 1\n\t#endif\n#endif\n\n\n#ifndef CONF_FAMILY_STRING\n#define CONF_FAMILY_STRING \"unknown\"\n#endif\n\n#ifndef CONF_PLATFORM_STRING\n#define CONF_PLATFORM_STRING \"unknown\"\n#endif\n\n#ifndef CONF_ARCH_STRING\n#define CONF_ARCH_STRING \"unknown\"\n#endif\n\n#endif\n"
  },
  {
    "path": "server/include/json.h",
    "content": "\n/* vim: set et ts=3 sw=3 sts=3 ft=c:\n *\n * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved.\n * https://github.com/udp/json-parser\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *   notice, this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright\n *   notice, this list of conditions and the following disclaimer in the\n *   documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef _JSON_H\n#define _JSON_H\n\n#ifndef json_char\n   #define json_char char\n#endif\n\n#ifndef json_int_t\n   #ifndef _MSC_VER\n      #include <inttypes.h>\n      #define json_int_t int64_t\n   #else\n      #define json_int_t __int64\n   #endif\n#endif\n\n#include <stdlib.h>\n\n#ifdef __cplusplus\n\n   #include <string.h>\n\n   extern \"C\"\n   {\n\n#endif\n\ntypedef struct\n{\n   unsigned long max_memory;\n   int settings;\n\n   /* Custom allocator support (leave null to use malloc/free)\n    */\n\n   void * (* mem_alloc) (size_t, int zero, void * user_data);\n   void (* mem_free) (void *, void * user_data);\n\n   void * user_data;  /* will be passed to mem_alloc and mem_free */\n\n} json_settings;\n\n#define json_enable_comments  0x01\n\ntypedef enum\n{\n   json_none,\n   json_object,\n   json_array,\n   json_integer,\n   json_double,\n   json_string,\n   json_boolean,\n   json_null\n\n} json_type;\n\nextern const struct _json_value json_value_none;\n\ntypedef struct _json_value\n{\n   struct _json_value * parent;\n\n   json_type type;\n\n   union\n   {\n      int boolean;\n      json_int_t integer;\n      double dbl;\n\n      struct\n      {\n         unsigned int length;\n         json_char * ptr; /* null terminated */\n\n      } string;\n\n      struct\n      {\n         unsigned int length;\n\n         struct\n         {\n            json_char * name;\n            unsigned int name_length;\n\n            struct _json_value * value;\n\n         } * values;\n\n         #if defined(__cplusplus) && __cplusplus >= 201103L\n         decltype(values) begin () const\n         {  return values;\n         }\n         decltype(values) end () const\n         {  return values + length;\n         }\n         #endif\n\n      } object;\n\n      struct\n      {\n         unsigned int length;\n         struct _json_value ** values;\n\n         #if defined(__cplusplus) && __cplusplus >= 201103L\n         decltype(values) begin () const\n         {  return values;\n         }\n         decltype(values) end () const\n         {  return values + length;\n         }\n         #endif\n\n      } array;\n\n   } u;\n\n   union\n   {\n      struct _json_value * next_alloc;\n      void * object_mem;\n\n   } _reserved;\n\n\n   /* Some C++ operator sugar */\n\n   #ifdef __cplusplus\n\n      public:\n\n         inline _json_value ()\n         {  memset (this, 0, sizeof (_json_value));\n         }\n\n         inline const struct _json_value &operator [] (int index) const\n         {\n            if (type != json_array || index < 0\n                     || ((unsigned int) index) >= u.array.length)\n            {\n               return json_value_none;\n            }\n\n            return *u.array.values [index];\n         }\n\n         inline const struct _json_value &operator [] (const char * index) const\n         { \n            if (type != json_object)\n               return json_value_none;\n\n            for (unsigned int i = 0; i < u.object.length; ++ i)\n               if (!strcmp (u.object.values [i].name, index))\n                  return *u.object.values [i].value;\n\n            return json_value_none;\n         }\n\n         inline operator const char * () const\n         {  \n            switch (type)\n            {\n               case json_string:\n                  return u.string.ptr;\n\n               default:\n                  return \"\";\n            };\n         }\n\n         inline operator json_int_t () const\n         {  \n            switch (type)\n            {\n               case json_integer:\n                  return u.integer;\n\n               case json_double:\n                  return (json_int_t) u.dbl;\n\n               default:\n                  return 0;\n            };\n         }\n\n         inline operator bool () const\n         {  \n            if (type != json_boolean)\n               return false;\n\n            return u.boolean != 0;\n         }\n\n         inline operator double () const\n         {  \n            switch (type)\n            {\n               case json_integer:\n                  return (double) u.integer;\n\n               case json_double:\n                  return u.dbl;\n\n               default:\n                  return 0;\n            };\n         }\n\n   #endif\n\n} json_value;\n\njson_value * json_parse (const json_char * json,\n                         size_t length);\n\n#define json_error_max 128\njson_value * json_parse_ex (json_settings * settings,\n                            const json_char * json,\n                            size_t length,\n                            char * error);\n\nvoid json_value_free (json_value *);\n\n\n/* Not usually necessary, unless you used a custom mem_alloc and now want to\n * use a custom mem_free.\n */\nvoid json_value_free_ex (json_settings * settings,\n                         json_value *);\n\n\n#ifdef __cplusplus\n   } /* extern \"C\" */\n#endif\n\n#endif\n"
  },
  {
    "path": "server/include/system.h",
    "content": "/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */\n/* If you are missing that file, acquire a complete release at teeworlds.com.                */\n\n/*\n\tTitle: OS Abstraction\n*/\n\n#ifndef BASE_SYSTEM_H\n#define BASE_SYSTEM_H\n\n#include \"detect.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Group: Debug */\n/*\n\tFunction: dbg_assert\n\t\tBreaks into the debugger based on a test.\n\n\tParameters:\n\t\ttest - Result of the test.\n\t\tmsg - Message that should be printed if the test fails.\n\n\tRemarks:\n\t\tDoes nothing in release version of the library.\n\n\tSee Also:\n\t\t<dbg_break>\n*/\nvoid dbg_assert(int test, const char *msg);\n#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg)\nvoid dbg_assert_imp(const char *filename, int line, int test, const char *msg);\n\n\n#ifdef __clang_analyzer__\n#include <assert.h>\n#undef dbg_assert\n#define dbg_assert(test,msg) assert(test)\n#endif\n\n/*\n\tFunction: dbg_break\n\t\tBreaks into the debugger.\n\n\tRemarks:\n\t\tDoes nothing in release version of the library.\n\n\tSee Also:\n\t\t<dbg_assert>\n*/\nvoid dbg_break();\n\n/*\n\tFunction: dbg_msg\n\n\tPrints a debug message.\n\n\tParameters:\n\t\tsys - A string that describes what system the message belongs to\n\t\tfmt - A printf styled format string.\n\n\tRemarks:\n\t\tDoes nothing in release version of the library.\n\n\tSee Also:\n\t\t<dbg_assert>\n*/\nvoid dbg_msg(const char *sys, const char *fmt, ...);\n\n/* Group: Memory */\n\n/*\n\tFunction: mem_alloc\n\t\tAllocates memory.\n\n\tParameters:\n\t\tsize - Size of the needed block.\n\t\talignment - Alignment for the block.\n\n\tReturns:\n\t\tReturns a pointer to the newly allocated block. Returns a\n\t\tnull pointer if the memory couldn't be allocated.\n\n\tRemarks:\n\t\t- Passing 0 to size will allocated the smallest amount possible\n\t\tand return a unique pointer.\n\n\tSee Also:\n\t\t<mem_free>\n*/\nvoid *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment);\n#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a))\n\n/*\n\tFunction: mem_free\n\t\tFrees a block allocated through <mem_alloc>.\n\n\tRemarks:\n\t\t- In the debug version of the library the function will assert if\n\t\ta non-valid block is passed, like a null pointer or a block that\n\t\tisn't allocated.\n\n\tSee Also:\n\t\t<mem_alloc>\n*/\nvoid mem_free(void *block);\n\n/*\n\tFunction: mem_copy\n\t\tCopies a a memory block.\n\n\tParameters:\n\t\tdest - Destination.\n\t\tsource - Source to copy.\n\t\tsize - Size of the block to copy.\n\n\tRemarks:\n\t\t- This functions DOES NOT handles cases where source and\n\t\tdestination is overlapping.\n\n\tSee Also:\n\t\t<mem_move>\n*/\nvoid mem_copy(void *dest, const void *source, unsigned size);\n\n/*\n\tFunction: mem_move\n\t\tCopies a a memory block\n\n\tParameters:\n\t\tdest - Destination\n\t\tsource - Source to copy\n\t\tsize - Size of the block to copy\n\n\tRemarks:\n\t\t- This functions handles cases where source and destination\n\t\tis overlapping\n\n\tSee Also:\n\t\t<mem_copy>\n*/\nvoid mem_move(void *dest, const void *source, unsigned size);\n\n/*\n\tFunction: mem_zero\n\t\tSets a complete memory block to 0\n\n\tParameters:\n\t\tblock - Pointer to the block to zero out\n\t\tsize - Size of the block\n*/\nvoid mem_zero(void *block, unsigned size);\n\n/*\n\tFunction: mem_comp\n\t\tCompares two blocks of memory\n\n\tParameters:\n\t\ta - First block of data\n\t\tb - Second block of data\n\t\tsize - Size of the data to compare\n\n\tReturns:\n\t\t<0 - Block a is lesser then block b\n\t\t0 - Block a is equal to block b\n\t\t>0 - Block a is greater then block b\n*/\nint mem_comp(const void *a, const void *b, int size);\n\n/*\n\tFunction: mem_check\n\t\tValidates the heap\n\t\tWill trigger a assert if memory has failed.\n*/\nint mem_check_imp();\n#define mem_check() dbg_assert_imp(__FILE__, __LINE__, mem_check_imp(), \"Memory check failed\")\n\n/* Group: File IO */\nenum {\n\tIOFLAG_READ = 1,\n\tIOFLAG_WRITE = 2,\n\tIOFLAG_RANDOM = 4,\n\n\tIOSEEK_START = 0,\n\tIOSEEK_CUR = 1,\n\tIOSEEK_END = 2\n};\n\ntypedef struct IOINTERNAL *IOHANDLE;\n\n/*\n\tFunction: io_open\n\t\tOpens a file.\n\n\tParameters:\n\t\tfilename - File to open.\n\t\tflags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM.\n\n\tReturns:\n\t\tReturns a handle to the file on success and 0 on failure.\n\n*/\nIOHANDLE io_open(const char *filename, int flags);\n\n/*\n\tFunction: io_read\n\t\tReads data into a buffer from a file.\n\n\tParameters:\n\t\tio - Handle to the file to read data from.\n\t\tbuffer - Pointer to the buffer that will recive the data.\n\t\tsize - Number of bytes to read from the file.\n\n\tReturns:\n\t\tNumber of bytes read.\n\n*/\nunsigned io_read(IOHANDLE io, void *buffer, unsigned size);\n\n/*\n\tFunction: io_skip\n\t\tSkips data in a file.\n\n\tParameters:\n\t\tio - Handle to the file.\n\t\tsize - Number of bytes to skip.\n\n\tReturns:\n\t\tNumber of bytes skipped.\n*/\nunsigned io_skip(IOHANDLE io, int size);\n\n/*\n\tFunction: io_write\n\t\tWrites data from a buffer to file.\n\n\tParameters:\n\t\tio - Handle to the file.\n\t\tbuffer - Pointer to the data that should be written.\n\t\tsize - Number of bytes to write.\n\n\tReturns:\n\t\tNumber of bytes written.\n*/\nunsigned io_write(IOHANDLE io, const void *buffer, unsigned size);\n\n/*\n\tFunction: io_write_newline\n\t\tWrites newline to file.\n\n\tParameters:\n\t\tio - Handle to the file.\n\n\tReturns:\n\t\tNumber of bytes written.\n*/\nunsigned io_write_newline(IOHANDLE io);\n\n/*\n\tFunction: io_seek\n\t\tSeeks to a specified offset in the file.\n\n\tParameters:\n\t\tio - Handle to the file.\n\t\toffset - Offset from pos to stop.\n\t\torigin - Position to start searching from.\n\n\tReturns:\n\t\tReturns 0 on success.\n*/\nint io_seek(IOHANDLE io, int offset, int origin);\n\n/*\n\tFunction: io_tell\n\t\tGets the current position in the file.\n\n\tParameters:\n\t\tio - Handle to the file.\n\n\tReturns:\n\t\tReturns the current position. -1L if an error occured.\n*/\nlong int io_tell(IOHANDLE io);\n\n/*\n\tFunction: io_length\n\t\tGets the total length of the file. Resetting cursor to the beginning\n\n\tParameters:\n\t\tio - Handle to the file.\n\n\tReturns:\n\t\tReturns the total size. -1L if an error occured.\n*/\nlong int io_length(IOHANDLE io);\n\n/*\n\tFunction: io_close\n\t\tCloses a file.\n\n\tParameters:\n\t\tio - Handle to the file.\n\n\tReturns:\n\t\tReturns 0 on success.\n*/\nint io_close(IOHANDLE io);\n\n/*\n\tFunction: io_flush\n\t\tEmpties all buffers and writes all pending data.\n\n\tParameters:\n\t\tio - Handle to the file.\n\n\tReturns:\n\t\tReturns 0 on success.\n*/\nint io_flush(IOHANDLE io);\n\n\n/*\n\tFunction: io_stdin\n\t\tReturns an <IOHANDLE> to the standard input.\n*/\nIOHANDLE io_stdin();\n\n/*\n\tFunction: io_stdout\n\t\tReturns an <IOHANDLE> to the standard output.\n*/\nIOHANDLE io_stdout();\n\n/*\n\tFunction: io_stderr\n\t\tReturns an <IOHANDLE> to the standard error.\n*/\nIOHANDLE io_stderr();\n\n\n/* Group: Threads */\n\n/*\n\tFunction: thread_sleep\n\t\tSuspends the current thread for a given period.\n\n\tParameters:\n\t\tmilliseconds - Number of milliseconds to sleep.\n*/\nvoid thread_sleep(int milliseconds);\n\n/*\n\tFunction: thread_create\n\t\tCreates a new thread.\n\n\tParameters:\n\t\tthreadfunc - Entry point for the new thread.\n\t\tuser - Pointer to pass to the thread.\n\n*/\nvoid *thread_create(void (*threadfunc)(void *), void *user);\n\n/*\n\tFunction: thread_wait\n\t\tWaits for a thread to be done or destroyed.\n\n\tParameters:\n\t\tthread - Thread to wait for.\n*/\nvoid thread_wait(void *thread);\n\n/*\n\tFunction: thread_destroy\n\t\tDestroys a thread.\n\n\tParameters:\n\t\tthread - Thread to destroy.\n*/\nvoid thread_destroy(void *thread);\n\n/*\n\tFunction: thread_yeild\n\t\tYeild the current threads execution slice.\n*/\nvoid thread_yield();\n\n/*\n\tFunction: thread_detach\n\t\tPuts the thread in the detached thread, guaranteeing that\n\t\tresources of the thread will be freed immediately when the\n\t\tthread terminates.\n\n\tParameters:\n\t\tthread - Thread to detach\n*/\nvoid thread_detach(void *thread);\n\n/* Group: Locks */\ntypedef void* LOCK;\n\nLOCK lock_create();\nvoid lock_destroy(LOCK lock);\n\nint lock_try(LOCK lock);\nvoid lock_wait(LOCK lock);\nvoid lock_release(LOCK lock);\n\n\n/* Group: Semaphores */\n\n#if !defined(CONF_PLATFORM_MACOSX)\n\t#if defined(CONF_FAMILY_UNIX)\n\t\t#include <semaphore.h>\n\t\ttypedef sem_t SEMAPHORE;\n\t#elif defined(CONF_FAMILY_WINDOWS)\n\t\ttypedef void* SEMAPHORE;\n\t#else\n\t\t#error missing sempahore implementation\n\t#endif\n\n\tvoid semaphore_init(SEMAPHORE *sem);\n\tvoid semaphore_wait(SEMAPHORE *sem);\n\tvoid semaphore_signal(SEMAPHORE *sem);\n\tvoid semaphore_destroy(SEMAPHORE *sem);\n#endif\n\n/* Group: Timer */\n#ifdef __GNUC__\n/* if compiled with -pedantic-errors it will complain about long\n\tnot being a C90 thing.\n*/\n__extension__ typedef long long int64;\n#else\ntypedef long long int64;\n#endif\n/*\n\tFunction: time_get\n\t\tFetches a sample from a high resolution timer.\n\n\tReturns:\n\t\tCurrent value of the timer.\n\n\tRemarks:\n\t\tTo know how fast the timer is ticking, see <time_freq>.\n*/\nint64 time_get();\n\n/*\n\tFunction: time_freq\n\t\tReturns the frequency of the high resolution timer.\n\n\tReturns:\n\t\tReturns the frequency of the high resolution timer.\n*/\nint64 time_freq();\n\n/*\n\tFunction: time_timestamp\n\t\tRetrives the current time as a UNIX timestamp\n\n\tReturns:\n\t\tThe time as a UNIX timestamp\n*/\nint time_timestamp();\n\n/* Group: Network General */\ntypedef struct\n{\n\tint type;\n\tint ipv4sock;\n\tint ipv6sock;\n} NETSOCKET;\n\nenum\n{\n\tNETADDR_MAXSTRSIZE = 1+(8*4+7)+1+1+5+1, // [XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX]:XXXXX\n\n\tNETTYPE_INVALID = 0,\n\tNETTYPE_IPV4 = 1,\n\tNETTYPE_IPV6 = 2,\n\tNETTYPE_LINK_BROADCAST = 4,\n\tNETTYPE_ALL = NETTYPE_IPV4|NETTYPE_IPV6\n};\n\ntypedef struct\n{\n\tunsigned int type;\n\tunsigned char ip[16];\n\tunsigned short port;\n} NETADDR;\n\n/*\n\tFunction: net_init\n\t\tInitiates network functionallity.\n\n\tReturns:\n\t\tReturns 0 on success,\n\n\tRemarks:\n\t\tYou must call this function before using any other network\n\t\tfunctions.\n*/\nint net_init();\n\n/*\n\tFunction: net_host_lookup\n\t\tDoes a hostname lookup by name and fills out the passed\n\t\tNETADDR struct with the recieved details.\n\n\tReturns:\n\t\t0 on success.\n*/\nint net_host_lookup(const char *hostname, NETADDR *addr, int types);\n\n/*\n\tFunction: net_addr_comp\n\t\tCompares two network addresses.\n\n\tParameters:\n\t\ta - Address to compare\n\t\tb - Address to compare to.\n\n\tReturns:\n\t\t<0 - Address a is lesser then address b\n\t\t0 - Address a is equal to address b\n\t\t>0 - Address a is greater then address b\n*/\nint net_addr_comp(const NETADDR *a, const NETADDR *b);\n\n/*\n\tFunction: net_addr_str\n\t\tTurns a network address into a representive string.\n\n\tParameters:\n\t\taddr - Address to turn into a string.\n\t\tstring - Buffer to fill with the string.\n\t\tmax_length - Maximum size of the string.\n\t\tadd_port - add port to string or not\n\n\tRemarks:\n\t\t- The string will always be zero terminated\n\n*/\nvoid net_addr_str(const NETADDR *addr, char *string, int max_length, int add_port);\n\n/*\n\tFunction: net_addr_from_str\n\t\tTurns string into a network address.\n\n\tReturns:\n\t\t0 on success\n\n\tParameters:\n\t\taddr - Address to fill in.\n\t\tstring - String to parse.\n*/\nint net_addr_from_str(NETADDR *addr, const char *string);\n\n/* Group: Network UDP */\n\n/*\n\tFunction: net_udp_create\n\t\tCreates a UDP socket and binds it to a port.\n\n\tParameters:\n\t\tbindaddr - Address to bind the socket to.\n\n\tReturns:\n\t\tOn success it returns an handle to the socket. On failure it\n\t\treturns NETSOCKET_INVALID.\n*/\nNETSOCKET net_udp_create(NETADDR bindaddr);\n\n/*\n\tFunction: net_udp_send\n\t\tSends a packet over an UDP socket.\n\n\tParameters:\n\t\tsock - Socket to use.\n\t\taddr - Where to send the packet.\n\t\tdata - Pointer to the packet data to send.\n\t\tsize - Size of the packet.\n\n\tReturns:\n\t\tOn success it returns the number of bytes sent. Returns -1\n\t\ton error.\n*/\nint net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size);\n\n/*\n\tFunction: net_udp_recv\n\t\tRecives a packet over an UDP socket.\n\n\tParameters:\n\t\tsock - Socket to use.\n\t\taddr - Pointer to an NETADDR that will recive the address.\n\t\tdata - Pointer to a buffer that will recive the data.\n\t\tmaxsize - Maximum size to recive.\n\n\tReturns:\n\t\tOn success it returns the number of bytes recived. Returns -1\n\t\ton error.\n*/\nint net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize);\n\n/*\n\tFunction: net_udp_close\n\t\tCloses an UDP socket.\n\n\tParameters:\n\t\tsock - Socket to close.\n\n\tReturns:\n\t\tReturns 0 on success. -1 on error.\n*/\nint net_udp_close(NETSOCKET sock);\n\n\n/* Group: Network TCP */\n\n/*\n\tFunction: net_tcp_create\n\t\tCreates a TCP socket.\n\n\tParameters:\n\t\tbindaddr - Address to bind the socket to.\n\n\tReturns:\n\t\tOn success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID.\n*/\nNETSOCKET net_tcp_create(NETADDR bindaddr);\n\n/*\n\tFunction: net_tcp_listen\n\t\tMakes the socket start listening for new connections.\n\n\tParameters:\n\t\tsock - Socket to start listen to.\n\t\tbacklog - Size of the queue of incomming connections to keep.\n\n\tReturns:\n\t\tReturns 0 on success.\n*/\nint net_tcp_listen(NETSOCKET sock, int backlog);\n\n/*\n\tFunction: net_tcp_accept\n\t\tPolls a listning socket for a new connection.\n\n\tParameters:\n\t\tsock - Listning socket to poll.\n\t\tnew_sock - Pointer to a socket to fill in with the new socket.\n\t\taddr - Pointer to an address that will be filled in the remote address (optional, can be NULL).\n\n\tReturns:\n\t\tReturns a non-negative integer on success. Negative integer on failure.\n*/\nint net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *addr);\n\n/*\n\tFunction: net_tcp_connect\n\t\tConnects one socket to another.\n\n\tParameters:\n\t\tsock - Socket to connect.\n\t\taddr - Address to connect to.\n\n\tReturns:\n\t\tReturns 0 on success.\n\n*/\nint net_tcp_connect(NETSOCKET sock, const NETADDR *addr);\n\n/*\n\tFunction: net_tcp_send\n\t\tSends data to a TCP stream.\n\n\tParameters:\n\t\tsock - Socket to send data to.\n\t\tdata - Pointer to the data to send.\n\t\tsize - Size of the data to send.\n\n\tReturns:\n\t\tNumber of bytes sent. Negative value on failure.\n*/\nint net_tcp_send(NETSOCKET sock, const void *data, int size);\n\n/*\n\tFunction: net_tcp_recv\n\t\tRecvives data from a TCP stream.\n\n\tParameters:\n\t\tsock - Socket to recvive data from.\n\t\tdata - Pointer to a buffer to write the data to\n\t\tmax_size - Maximum of data to write to the buffer.\n\n\tReturns:\n\t\tNumber of bytes recvived. Negative value on failure. When in\n\t\tnon-blocking mode, it returns 0 when there is no more data to\n\t\tbe fetched.\n*/\nint net_tcp_recv(NETSOCKET sock, void *data, int maxsize);\n\n/*\n\tFunction: net_tcp_close\n\t\tCloses a TCP socket.\n\n\tParameters:\n\t\tsock - Socket to close.\n\n\tReturns:\n\t\tReturns 0 on success. Negative value on failure.\n*/\nint net_tcp_close(NETSOCKET sock);\n\n/* Group: Strings */\n\n/*\n\tFunction: str_append\n\t\tAppends a string to another.\n\n\tParameters:\n\t\tdst - Pointer to a buffer that contains a string.\n\t\tsrc - String to append.\n\t\tdst_size - Size of the buffer of the dst string.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n\t\t- Garantees that dst string will contain zero-termination.\n*/\nvoid str_append(char *dst, const char *src, int dst_size);\n\n/*\n\tFunction: str_copy\n\t\tCopies a string to another.\n\n\tParameters:\n\t\tdst - Pointer to a buffer that shall recive the string.\n\t\tsrc - String to be copied.\n\t\tdst_size - Size of the buffer dst.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n\t\t- Garantees that dst string will contain zero-termination.\n*/\nvoid str_copy(char *dst, const char *src, int dst_size);\n\n/*\n\tFunction: str_length\n\t\tReturns the length of a zero terminated string.\n\n\tParameters:\n\t\tstr - Pointer to the string.\n\n\tReturns:\n\t\tLength of string in bytes excluding the zero termination.\n*/\nint str_length(const char *str);\n\n/*\n\tFunction: str_format\n\t\tPerforms printf formating into a buffer.\n\n\tParameters:\n\t\tbuffer - Pointer to the buffer to recive the formated string.\n\t\tbuffer_size - Size of the buffer.\n\t\tformat - printf formating string.\n\t\t... - Parameters for the formating.\n\n\tRemarks:\n\t\t- See the C manual for syntax for the printf formating string.\n\t\t- The strings are treated as zero-termineted strings.\n\t\t- Garantees that dst string will contain zero-termination.\n*/\nvoid str_format(char *buffer, int buffer_size, const char *format, ...);\n\n/*\n\tFunction: str_sanitize_strong\n\t\tReplaces all characters below 32 and above 127 with whitespace.\n\n\tParameters:\n\t\tstr - String to sanitize.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nvoid str_sanitize_strong(char *str);\n\n/*\n\tFunction: str_sanitize_cc\n\t\tReplaces all characters below 32 with whitespace.\n\n\tParameters:\n\t\tstr - String to sanitize.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nvoid str_sanitize_cc(char *str);\n\n/*\n\tFunction: str_sanitize\n\t\tReplaces all characters below 32 with whitespace with\n\t\texception to \\t, \\n and \\r.\n\n\tParameters:\n\t\tstr - String to sanitize.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nvoid str_sanitize(char *str);\n\n/*\n\tFunction: str_skip_to_whitespace\n\t\tSkips leading non-whitespace characters(all but ' ', '\\t', '\\n', '\\r').\n\n\tParameters:\n\t\tstr - Pointer to the string.\n\n\tReturns:\n\t\tPointer to the first whitespace character found\n\t\twithin the string.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nchar *str_skip_to_whitespace(char *str);\n\n/*\n\tFunction: str_skip_whitespaces\n\t\tSkips leading whitespace characters(' ', '\\t', '\\n', '\\r').\n\n\tParameters:\n\t\tstr - Pointer to the string.\n\n\tReturns:\n\t\tPointer to the first non-whitespace character found\n\t\twithin the string.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nchar *str_skip_whitespaces(char *str);\n\n/*\n\tFunction: str_comp_nocase\n\t\tCompares to strings case insensitive.\n\n\tParameters:\n\t\ta - String to compare.\n\t\tb - String to compare.\n\n\tReturns:\n\t\t<0 - String a is lesser then string b\n\t\t0 - String a is equal to string b\n\t\t>0 - String a is greater then string b\n\n\tRemarks:\n\t\t- Only garanted to work with a-z/A-Z.\n\t\t- The strings are treated as zero-termineted strings.\n*/\nint str_comp_nocase(const char *a, const char *b);\n\n/*\n\tFunction: str_comp_nocase_num\n\t\tCompares up to num characters of two strings case insensitive.\n\n\tParameters:\n\t\ta - String to compare.\n\t\tb - String to compare.\n\t\tnum - Maximum characters to compare\n\n\tReturns:\n\t\t<0 - String a is lesser than string b\n\t\t0 - String a is equal to string b\n\t\t>0 - String a is greater than string b\n\n\tRemarks:\n\t\t- Only garanted to work with a-z/A-Z.\n\t\t- The strings are treated as zero-termineted strings.\n*/\nint str_comp_nocase_num(const char *a, const char *b, const int num);\n\n/*\n\tFunction: str_comp\n\t\tCompares to strings case sensitive.\n\n\tParameters:\n\t\ta - String to compare.\n\t\tb - String to compare.\n\n\tReturns:\n\t\t<0 - String a is lesser then string b\n\t\t0 - String a is equal to string b\n\t\t>0 - String a is greater then string b\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nint str_comp(const char *a, const char *b);\n\n/*\n\tFunction: str_comp_num\n\t\tCompares up to num characters of two strings case sensitive.\n\n\tParameters:\n\t\ta - String to compare.\n\t\tb - String to compare.\n\t\tnum - Maximum characters to compare\n\n\tReturns:\n\t\t<0 - String a is lesser then string b\n\t\t0 - String a is equal to string b\n\t\t>0 - String a is greater then string b\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nint str_comp_num(const char *a, const char *b, const int num);\n\n/*\n\tFunction: str_comp_filenames\n\t\tCompares two strings case sensitive, digit chars will be compared as numbers.\n\n\tParameters:\n\t\ta - String to compare.\n\t\tb - String to compare.\n\n\tReturns:\n\t\t<0 - String a is lesser then string b\n\t\t0 - String a is equal to string b\n\t\t>0 - String a is greater then string b\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nint str_comp_filenames(const char *a, const char *b);\n\n/*\n\tFunction: str_find_nocase\n\t\tFinds a string inside another string case insensitive.\n\n\tParameters:\n\t\thaystack - String to search in\n\t\tneedle - String to search for\n\n\tReturns:\n\t\tA pointer into haystack where the needle was found.\n\t\tReturns NULL of needle could not be found.\n\n\tRemarks:\n\t\t- Only garanted to work with a-z/A-Z.\n\t\t- The strings are treated as zero-termineted strings.\n*/\nconst char *str_find_nocase(const char *haystack, const char *needle);\n\n/*\n\tFunction: str_find\n\t\tFinds a string inside another string case sensitive.\n\n\tParameters:\n\t\thaystack - String to search in\n\t\tneedle - String to search for\n\n\tReturns:\n\t\tA pointer into haystack where the needle was found.\n\t\tReturns NULL of needle could not be found.\n\n\tRemarks:\n\t\t- The strings are treated as zero-termineted strings.\n*/\nconst char *str_find(const char *haystack, const char *needle);\n\n/*\n\tFunction: str_hex\n\t\tTakes a datablock and generates a hexstring of it.\n\n\tParameters:\n\t\tdst - Buffer to fill with hex data\n\t\tdst_size - size of the buffer\n\t\tdata - Data to turn into hex\n\t\tdata - Size of the data\n\n\tRemarks:\n\t\t- The desination buffer will be zero-terminated\n*/\nvoid str_hex(char *dst, int dst_size, const void *data, int data_size);\n\n/*\n\tFunction: str_timestamp\n\t\tCopies a time stamp in the format year-month-day_hour-minute-second to the string.\n\n\tParameters:\n\t\tbuffer - Pointer to a buffer that shall receive the time stamp string.\n\t\tbuffer_size - Size of the buffer.\n\n\tRemarks:\n\t\t- Guarantees that buffer string will contain zero-termination.\n*/\nvoid str_timestamp(char *buffer, int buffer_size);\n\n/* Group: Filesystem */\n\n/*\n\tFunction: fs_listdir\n\t\tLists the files in a directory\n\n\tParameters:\n\t\tdir - Directory to list\n\t\tcb - Callback function to call for each entry\n\t\ttype - Type of the directory\n\t\tuser - Pointer to give to the callback\n\n\tReturns:\n\t\tAlways returns 0.\n*/\ntypedef int (*FS_LISTDIR_CALLBACK)(const char *name, int is_dir, int dir_type, void *user);\nint fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user);\n\n/*\n\tFunction: fs_makedir\n\t\tCreates a directory\n\n\tParameters:\n\t\tpath - Directory to create\n\n\tReturns:\n\t\tReturns 0 on success. Negative value on failure.\n\n\tRemarks:\n\t\tDoes not create several directories if needed. \"a/b/c\" will result\n\t\tin a failure if b or a does not exist.\n*/\nint fs_makedir(const char *path);\n\n/*\n\tFunction: fs_storage_path\n\t\tFetches per user configuration directory.\n\n\tReturns:\n\t\tReturns 0 on success. Negative value on failure.\n\n\tRemarks:\n\t\t- Returns ~/.appname on UNIX based systems\n\t\t- Returns ~/Library/Applications Support/appname on Mac OS X\n\t\t- Returns %APPDATA%/Appname on Windows based systems\n*/\nint fs_storage_path(const char *appname, char *path, int max);\n\n/*\n\tFunction: fs_is_dir\n\t\tChecks if directory exists\n\n\tReturns:\n\t\tReturns 1 on success, 0 on failure.\n*/\nint fs_is_dir(const char *path);\n\n/*\n\tFunction: fs_chdir\n\t\tChanges current working directory\n\n\tReturns:\n\t\tReturns 0 on success, 1 on failure.\n*/\nint fs_chdir(const char *path);\n\n/*\n\tFunction: fs_getcwd\n\t\tGets the current working directory.\n\n\tReturns:\n\t\tReturns a pointer to the buffer on success, 0 on failure.\n*/\nchar *fs_getcwd(char *buffer, int buffer_size);\n\n/*\n\tFunction: fs_parent_dir\n\t\tGet the parent directory of a directory\n\n\tParameters:\n\t\tpath - The directory string\n\n\tReturns:\n\t\tReturns 0 on success, 1 on failure.\n\n\tRemarks:\n\t\t- The string is treated as zero-termineted string.\n*/\nint fs_parent_dir(char *path);\n\n/*\n\tFunction: fs_remove\n\t\tDeletes the file with the specified name.\n\n\tParameters:\n\t\tfilename - The file to delete\n\n\tReturns:\n\t\tReturns 0 on success, 1 on failure.\n\n\tRemarks:\n\t\t- The strings are treated as zero-terminated strings.\n*/\nint fs_remove(const char *filename);\n\n/*\n\tFunction: fs_rename\n\t\tRenames the file or directory. If the paths differ the file will be moved.\n\n\tParameters:\n\t\toldname - The actual name\n\t\tnewname - The new name\n\n\tReturns:\n\t\tReturns 0 on success, 1 on failure.\n\n\tRemarks:\n\t\t- The strings are treated as zero-terminated strings.\n*/\nint fs_rename(const char *oldname, const char *newname);\n\n/*\n\tGroup: Undocumented\n*/\n\n\n/*\n\tFunction: net_tcp_connect_non_blocking\n\n\tDOCTODO: serp\n*/\nint net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr);\n\n/*\n\tFunction: net_set_non_blocking\n\n\tDOCTODO: serp\n*/\nint net_set_non_blocking(NETSOCKET sock);\n\n/*\n\tFunction: net_set_non_blocking\n\n\tDOCTODO: serp\n*/\nint net_set_blocking(NETSOCKET sock);\n\n/*\n\tFunction: net_errno\n\n\tDOCTODO: serp\n*/\nint net_errno();\n\n/*\n\tFunction: net_would_block\n\n\tDOCTODO: serp\n*/\nint net_would_block();\n\nint net_socket_read_wait(NETSOCKET sock, int time);\n\nvoid mem_debug_dump(IOHANDLE file);\n\nvoid swap_endian(void *data, unsigned elem_size, unsigned num);\n\n\ntypedef void (*DBG_LOGGER)(const char *line);\nvoid dbg_logger(DBG_LOGGER logger);\n\nvoid dbg_logger_stdout();\nvoid dbg_logger_debugger();\nvoid dbg_logger_file(const char *filename);\n\ntypedef struct\n{\n\tint allocated;\n\tint active_allocations;\n\tint total_allocations;\n} MEMSTATS;\n\nconst MEMSTATS *mem_stats();\n\ntypedef struct\n{\n\tint sent_packets;\n\tint sent_bytes;\n\tint recv_packets;\n\tint recv_bytes;\n} NETSTATS;\n\n\nvoid net_stats(NETSTATS *stats);\n\nint str_toint(const char *str);\nfloat str_tofloat(const char *str);\nint str_isspace(char c);\nchar str_uppercase(char c);\nunsigned str_quickhash(const char *str);\n\n/*\n\tFunction: gui_messagebox\n\t\tDisplay plain OS-dependent message box\n\n\tParameters:\n\t\ttitle - title of the message box\n\t\tmessage - text to display\n*/\nvoid gui_messagebox(const char *title, const char *message);\n\n\n/*\n\tFunction: str_utf8_rewind\n\t\tMoves a cursor backwards in an utf8 string\n\n\tParameters:\n\t\tstr - utf8 string\n\t\tcursor - position in the string\n\n\tReturns:\n\t\tNew cursor position.\n\n\tRemarks:\n\t\t- Won't move the cursor less then 0\n*/\nint str_utf8_rewind(const char *str, int cursor);\n\n/*\n\tFunction: str_utf8_forward\n\t\tMoves a cursor forwards in an utf8 string\n\n\tParameters:\n\t\tstr - utf8 string\n\t\tcursor - position in the string\n\n\tReturns:\n\t\tNew cursor position.\n\n\tRemarks:\n\t\t- Won't move the cursor beyond the zero termination marker\n*/\nint str_utf8_forward(const char *str, int cursor);\n\n/*\n\tFunction: str_utf8_decode\n\t\tDecodes an utf8 character\n\n\tParameters:\n\t\tptr - pointer to an utf8 string. this pointer will be moved forward\n\n\tReturns:\n\t\tUnicode value for the character. -1 for invalid characters and 0 for end of string.\n\n\tRemarks:\n\t\t- This function will also move the pointer forward.\n*/\nint str_utf8_decode(const char **ptr);\n\n/*\n\tFunction: str_utf8_encode\n\t\tEncode an utf8 character\n\n\tParameters:\n\t\tptr - Pointer to a buffer that should recive the data. Should be able to hold at least 4 bytes.\n\n\tReturns:\n\t\tNumber of bytes put into the buffer.\n\n\tRemarks:\n\t\t- Does not do zero termination of the string.\n*/\nint str_utf8_encode(char *ptr, int chr);\n\n/*\n\tFunction: str_utf8_check\n\t\tChecks if a strings contains just valid utf8 characters.\n\n\tParameters:\n\t\tstr - Pointer to a possible utf8 string.\n\n\tReturns:\n\t\t0 - invalid characters found.\n\t\t1 - only valid characters found.\n\n\tRemarks:\n\t\t- The string is treated as zero-terminated utf8 string.\n*/\nint str_utf8_check(const char *str);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "server/obj/.gitignore",
    "content": "*.o"
  },
  {
    "path": "server/src/argparse.c",
    "content": "#include \"argparse.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n#define OPT_UNSET 1\n\nstatic const char *\nprefix_skip(const char *str, const char *prefix)\n{\n    size_t len = strlen(prefix);\n    return strncmp(str, prefix, len) ? NULL : str + len;\n}\n\nint\nprefix_cmp(const char *str, const char *prefix)\n{\n    for (;; str++, prefix++)\n        if (!*prefix)\n            return 0;\n        else if (*str != *prefix)\n            return (unsigned char)*prefix - (unsigned char)*str;\n}\n\nstatic void\nargparse_error(struct argparse *this_, const struct argparse_option *opt,\n               const char *reason)\n{\n    if (!strncmp(this_->argv[0], \"--\", 2)) {\n        fprintf(stderr, \"error: option `%s` %s\\n\", opt->long_name, reason);\n        exit(-1);\n    } else {\n        fprintf(stderr, \"error: option `%c` %s\\n\", opt->short_name, reason);\n        exit(-1);\n    }\n}\n\nstatic int\nargparse_getvalue(struct argparse *this_, const struct argparse_option *opt,\n                  int flags)\n{\n    const char *s = NULL;\n    if (!opt->value)\n        goto skipped;\n    switch (opt->type) {\n    case ARGPARSE_OPT_BOOLEAN:\n        if (flags & OPT_UNSET) {\n            *(int *)opt->value = *(int *)opt->value - 1;\n        } else {\n            *(int *)opt->value = *(int *)opt->value + 1;\n        }\n        if (*(int *)opt->value < 0) { \n            *(int *)opt->value = 0;\n        }\n        break;\n    case ARGPARSE_OPT_BIT:\n        if (flags & OPT_UNSET) {\n            *(int *)opt->value &= ~opt->data;\n        } else {\n            *(int *)opt->value |= opt->data;\n        }\n        break;\n    case ARGPARSE_OPT_STRING:\n        if (this_->optvalue) {\n            *(const char **)opt->value = this_->optvalue;\n            this_->optvalue = NULL;\n        } else if (this_->argc > 1) {\n            this_->argc--;\n            *(const char **)opt->value = *++this_->argv;\n        } else {\n            argparse_error(this_, opt, \"requires a value\");\n        }\n        break;\n    case ARGPARSE_OPT_INTEGER:\n        if (this_->optvalue) {\n            *(int *)opt->value = strtol(this_->optvalue, (char **)&s, 0);\n            this_->optvalue = NULL;\n        } else if (this_->argc > 1) {\n            this_->argc--;\n            *(int *)opt->value = strtol(*++this_->argv, (char **)&s, 0);\n        } else {\n            argparse_error(this_, opt, \"requires a value\");\n        }\n        if (*s)\n            argparse_error(this_, opt, \"expects a numerical value\");\n        break;\n    default:\n        assert(0);\n    }\n\nskipped:\n    if (opt->callback) {\n        return opt->callback(this_, opt);\n    }\n\n    return 0;\n}\n\nstatic void\nargparse_options_check(const struct argparse_option *options)\n{\n    for (; options->type != ARGPARSE_OPT_END; options++) {\n        switch (options->type) {\n        case ARGPARSE_OPT_END:\n        case ARGPARSE_OPT_BOOLEAN:\n        case ARGPARSE_OPT_BIT:\n        case ARGPARSE_OPT_INTEGER:\n        case ARGPARSE_OPT_STRING:\n            continue;\n        default:\n            fprintf(stderr, \"wrong option type: %d\", options->type);\n            break;\n        }\n    }\n}\n\nstatic int\nargparse_short_opt(struct argparse *this_, const struct argparse_option *options)\n{\n    for (; options->type != ARGPARSE_OPT_END; options++) {\n        if (options->short_name == *this_->optvalue) {\n            this_->optvalue = this_->optvalue[1] ? this_->optvalue + 1 : NULL;\n            return argparse_getvalue(this_, options, 0);\n        }\n    }\n    return -2;\n}\n\nstatic int\nargparse_long_opt(struct argparse *this_, const struct argparse_option *options)\n{\n    for (; options->type != ARGPARSE_OPT_END; options++) {\n        const char *rest;\n        int opt_flags = 0;\n        if (!options->long_name)\n            continue;\n\n        rest = prefix_skip(this_->argv[0] + 2, options->long_name);\n        if (!rest) {\n            // Negation allowed?\n            if (options->flags & OPT_NONEG) {\n                continue;\n            }\n            // Only boolean/bit allow negation.\n            if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) {\n                continue;\n            }\n\n            if (!prefix_cmp(this_->argv[0] + 2, \"no-\")) {\n                rest = prefix_skip(this_->argv[0] + 2 + 3, options->long_name);\n                if (!rest)\n                    continue;\n                opt_flags |= OPT_UNSET;\n            } else {\n                continue;\n            }\n        }\n        if (*rest) {\n            if (*rest != '=')\n                continue;\n            this_->optvalue = rest + 1;\n        }\n        return argparse_getvalue(this_, options, opt_flags);\n    }\n    return -2;\n}\n\nint\nargparse_init(struct argparse *this_, struct argparse_option *options,\n              const char *usage, int flags)\n{\n    memset(this_, 0, sizeof(*this_));\n    this_->options = options;\n    this_->usage = usage;\n    this_->flags = flags;\n    return 0;\n}\n\nint\nargparse_parse(struct argparse *this_, int argc, const char **argv)\n{\n    this_->argc = argc - 1;\n    this_->argv = argv + 1;\n    this_->out = argv;\n\n    argparse_options_check(this_->options);\n\n    for (; this_->argc; this_->argc--, this_->argv++) {\n        const char *arg = this_->argv[0];\n        if (arg[0] != '-' || !arg[1]) {\n            if (this_->flags & ARGPARSE_STOP_AT_NON_OPTION) {\n                goto end;\n            }\n            // if it's not option or is a single char '-', copy verbatimly\n            this_->out[this_->cpidx++] = this_->argv[0];\n            continue;\n        }\n        // short option\n        if (arg[1] != '-') {\n            this_->optvalue = arg + 1;\n            switch (argparse_short_opt(this_, this_->options)) {\n            case -1:\n                break;\n            case -2:\n                goto unknown;\n            }\n            while (this_->optvalue) {\n                switch (argparse_short_opt(this_, this_->options)) {\n                case -1:\n                    break;\n                case -2:\n                    goto unknown;\n                }\n            }\n            continue;\n        }\n        // if '--' presents\n        if (!arg[2]) {\n            this_->argc--;\n            this_->argv++;\n            break;\n        }\n        // long option\n        switch (argparse_long_opt(this_, this_->options)) {\n        case -1:\n            break;\n        case -2:\n            goto unknown;\n        }\n        continue;\n\nunknown:\n        fprintf(stderr, \"error: unknown option `%s`\\n\", this_->argv[0]);\n        argparse_usage(this_);\n        exit(0);\n    }\n\nend:\n    memmove(this_->out + this_->cpidx, this_->argv,\n            this_->argc * sizeof(*this_->out));\n    this_->out[this_->cpidx + this_->argc] = NULL;\n\n    return this_->cpidx + this_->argc;\n}\n\nvoid\nargparse_usage(struct argparse *this_)\n{\n    fprintf(stdout, \"Usage: %s\\n\", this_->usage);\n    fputc('\\n', stdout);\n\n    const struct argparse_option *options;\n\n    // figure out best width\n    size_t usage_opts_width = 0;\n    size_t len;\n    options = this_->options;\n    for (; options->type != ARGPARSE_OPT_END; options++) {\n        len = 0;\n        if ((options)->short_name) {\n            len += 2;\n        }\n        if ((options)->short_name && (options)->long_name) {\n            len += 2;           // separator \", \"\n        }\n        if ((options)->long_name) {\n            len += strlen((options)->long_name) + 2;\n        }\n        if (options->type == ARGPARSE_OPT_INTEGER) {\n            len += strlen(\"=<int>\");\n        } else if (options->type == ARGPARSE_OPT_STRING) {\n            len += strlen(\"=<str>\");\n        }\n        len = ceil((float)len / 4) * 4;\n        if (usage_opts_width < len) {\n            usage_opts_width = len;\n        }\n    }\n    usage_opts_width += 4;      // 4 spaces prefix\n\n    options = this_->options;\n    for (; options->type != ARGPARSE_OPT_END; options++) {\n        size_t pos;\n        int pad;\n        pos = fprintf(stdout, \"    \");\n        if (options->short_name) {\n            pos += fprintf(stdout, \"-%c\", options->short_name);\n        }\n        if (options->long_name && options->short_name) {\n            pos += fprintf(stdout, \", \");\n        }\n        if (options->long_name) {\n            pos += fprintf(stdout, \"--%s\", options->long_name);\n        }\n        if (options->type == ARGPARSE_OPT_INTEGER) {\n            pos += fprintf(stdout, \"=<int>\");\n        } else if (options->type == ARGPARSE_OPT_STRING) {\n            pos += fprintf(stdout, \"=<str>\");\n        }\n        if (pos <= usage_opts_width) {\n            pad = usage_opts_width - pos;\n        } else {\n            fputc('\\n', stdout);\n            pad = usage_opts_width;\n        }\n        fprintf(stdout, \"%*s%s\\n\", pad + 2, \"\", options->help);\n    }\n}\n\nint\nargparse_help_cb(struct argparse *this_, const struct argparse_option *option)\n{\n    (void)option;\n    argparse_usage(this_);\n    exit(0);\n    return 0;\n}\n\n#if defined(__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "server/src/json.c",
    "content": "/* vim: set et ts=3 sw=3 sts=3 ft=c:\n *\n * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved.\n * https://github.com/udp/json-parser\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *   notice, this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright\n *   notice, this list of conditions and the following disclaimer in the\n *   documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"json.h\"\n\n#ifdef _MSC_VER\n   #ifndef _CRT_SECURE_NO_WARNINGS\n      #define _CRT_SECURE_NO_WARNINGS\n   #endif\n#endif\n\n#ifdef __cplusplus\n   const struct _json_value json_value_none; /* zero-d by ctor */\n#else\n   const struct _json_value json_value_none = { 0 };\n#endif\n\n#include <stdio.h>\n#include <string.h>\n#include <ctype.h>\n#include <math.h>\n\ntypedef unsigned short json_uchar;\n\nstatic unsigned char hex_value (json_char c)\n{\n   if (isdigit(c))\n      return c - '0';\n\n   switch (c) {\n      case 'a': case 'A': return 0x0A;\n      case 'b': case 'B': return 0x0B;\n      case 'c': case 'C': return 0x0C;\n      case 'd': case 'D': return 0x0D;\n      case 'e': case 'E': return 0x0E;\n      case 'f': case 'F': return 0x0F;\n      default: return 0xFF;\n   }\n}\n\ntypedef struct\n{\n   unsigned long used_memory;\n\n   unsigned int uint_max;\n   unsigned long ulong_max;\n\n   json_settings settings;\n   int first_pass;\n\n} json_state;\n\nstatic void * default_alloc (size_t size, int zero, void * user_data)\n{\n   return zero ? calloc (1, size) : malloc (size);\n}\n\nstatic void default_free (void * ptr, void * user_data)\n{\n   free (ptr);\n}\n\nstatic void * json_alloc (json_state * state, unsigned long size, int zero)\n{\n   if ((state->ulong_max - state->used_memory) < size)\n      return 0;\n\n   if (state->settings.max_memory\n         && (state->used_memory += size) > state->settings.max_memory)\n   {\n      return 0;\n   }\n\n   return state->settings.mem_alloc (size, zero, state->settings.user_data);\n}\n\nstatic int new_value\n   (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type)\n{\n   json_value * value;\n   int values_size;\n\n   if (!state->first_pass)\n   {\n      value = *top = *alloc;\n      *alloc = (*alloc)->_reserved.next_alloc;\n\n      if (!*root)\n         *root = value;\n\n      switch (value->type)\n      {\n         case json_array:\n\n            if (! (value->u.array.values = (json_value **) json_alloc\n               (state, value->u.array.length * sizeof (json_value *), 0)) )\n            {\n               return 0;\n            }\n\n            value->u.array.length = 0;\n            break;\n\n         case json_object:\n\n            values_size = sizeof (*value->u.object.values) * value->u.object.length;\n\n            if (! ((*(void **) &value->u.object.values) = json_alloc\n                  (state, values_size + ((unsigned long) value->u.object.values), 0)) )\n            {\n               return 0;\n            }\n\n            value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size;\n\n            value->u.object.length = 0;\n            break;\n\n         case json_string:\n\n            if (! (value->u.string.ptr = (json_char *) json_alloc\n               (state, (value->u.string.length + 1) * sizeof (json_char), 0)) )\n            {\n               return 0;\n            }\n\n            value->u.string.length = 0;\n            break;\n\n         default:\n            break;\n      };\n\n      return 1;\n   }\n\n   value = (json_value *) json_alloc (state, sizeof (json_value), 1);\n\n   if (!value)\n      return 0;\n\n   if (!*root)\n      *root = value;\n\n   value->type = type;\n   value->parent = *top;\n\n   if (*alloc)\n      (*alloc)->_reserved.next_alloc = value;\n\n   *alloc = *top = value;\n\n   return 1;\n}\n\n#define e_off \\\n   ((int) (i - cur_line_begin))\n\n#define whitespace \\\n   case '\\n': ++ cur_line;  cur_line_begin = i; \\\n   case ' ': case '\\t': case '\\r'\n\n#define string_add(b)  \\\n   do { if (!state.first_pass) string [string_length] = b;  ++ string_length; } while (0);\n\nstatic const long\n   flag_next             = 1 << 0,\n   flag_reproc           = 1 << 1,\n   flag_need_comma       = 1 << 2,\n   flag_seek_value       = 1 << 3, \n   flag_escaped          = 1 << 4,\n   flag_string           = 1 << 5,\n   flag_need_colon       = 1 << 6,\n   flag_done             = 1 << 7,\n   flag_num_negative     = 1 << 8,\n   flag_num_zero         = 1 << 9,\n   flag_num_e            = 1 << 10,\n   flag_num_e_got_sign   = 1 << 11,\n   flag_num_e_negative   = 1 << 12,\n   flag_line_comment     = 1 << 13,\n   flag_block_comment    = 1 << 14;\n\njson_value * json_parse_ex (json_settings * settings,\n                            const json_char * json,\n                            size_t length,\n                            char * error_buf)\n{\n   json_char error [json_error_max];\n   unsigned int cur_line;\n   const json_char * cur_line_begin, * i, * end;\n   json_value * top, * root, * alloc = 0;\n   json_state state = { 0 };\n   long flags;\n   long num_digits = 0, num_e = 0;\n   json_int_t num_fraction = 0;\n\n   /* Skip UTF-8 BOM\n    */\n   if (length >= 3 && ((unsigned char) json [0]) == 0xEF\n                   && ((unsigned char) json [1]) == 0xBB\n                   && ((unsigned char) json [2]) == 0xBF)\n   {\n      json += 3;\n      length -= 3;\n   }\n\n   error[0] = '\\0';\n   end = (json + length);\n\n   memcpy (&state.settings, settings, sizeof (json_settings));\n\n   if (!state.settings.mem_alloc)\n      state.settings.mem_alloc = default_alloc;\n\n   if (!state.settings.mem_free)\n      state.settings.mem_free = default_free;\n\n   memset (&state.uint_max, 0xFF, sizeof (state.uint_max));\n   memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));\n\n   state.uint_max -= 8; /* limit of how much can be added before next check */\n   state.ulong_max -= 8;\n\n   for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)\n   {\n      json_uchar uchar;\n      unsigned char uc_b1, uc_b2, uc_b3, uc_b4;\n      json_char * string = 0;\n      unsigned int string_length = 0;\n\n      top = root = 0;\n      flags = flag_seek_value;\n\n      cur_line = 1;\n      cur_line_begin = json;\n\n      for (i = json ;; ++ i)\n      {\n         json_char b = (i == end ? 0 : *i);\n         \n         if (flags & flag_string)\n         {\n            if (!b)\n            {  sprintf (error, \"Unexpected EOF in string (at %d:%d)\", cur_line, e_off);\n               goto e_failed;\n            }\n\n            if (string_length > state.uint_max)\n               goto e_overflow;\n\n            if (flags & flag_escaped)\n            {\n               flags &= ~ flag_escaped;\n\n               switch (b)\n               {\n                  case 'b':  string_add ('\\b');  break;\n                  case 'f':  string_add ('\\f');  break;\n                  case 'n':  string_add ('\\n');  break;\n                  case 'r':  string_add ('\\r');  break;\n                  case 't':  string_add ('\\t');  break;\n                  case 'u':\n\n                    if (end - i < 4 || \n                        (uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF\n                          || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF)\n                    {\n                        sprintf (error, \"Invalid character value `%c` (at %d:%d)\", b, cur_line, e_off);\n                        goto e_failed;\n                    }\n\n                    uc_b1 = uc_b1 * 16 + uc_b2;\n                    uc_b2 = uc_b3 * 16 + uc_b4;\n\n                    uchar = ((json_char) uc_b1) * 256 + uc_b2;\n\n                    if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F))\n                    {\n                       string_add ((json_char) uchar);\n                       break;\n                    }\n\n                    if (uchar <= 0x7FF)\n                    {\n                        if (state.first_pass)\n                           string_length += 2;\n                        else\n                        {  string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x7) << 2);\n                           string [string_length ++] = 0x80 | (uc_b2 & 0x3F);\n                        }\n\n                        break;\n                    }\n\n                    if (state.first_pass)\n                       string_length += 3;\n                    else\n                    {  string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4);\n                       string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6);\n                       string [string_length ++] = 0x80 | (uc_b2 & 0x3F);\n                    }\n\n                    break;\n\n                  default:\n                     string_add (b);\n               };\n\n               continue;\n            }\n\n            if (b == '\\\\')\n            {\n               flags |= flag_escaped;\n               continue;\n            }\n\n            if (b == '\"')\n            {\n               if (!state.first_pass)\n                  string [string_length] = 0;\n\n               flags &= ~ flag_string;\n               string = 0;\n\n               switch (top->type)\n               {\n                  case json_string:\n\n                     top->u.string.length = string_length;\n                     flags |= flag_next;\n\n                     break;\n\n                  case json_object:\n\n                     if (state.first_pass)\n                        (*(json_char **) &top->u.object.values) += string_length + 1;\n                     else\n                     {  \n                        top->u.object.values [top->u.object.length].name\n                           = (json_char *) top->_reserved.object_mem;\n\n                        top->u.object.values [top->u.object.length].name_length\n                           = string_length;\n\n                        (*(json_char **) &top->_reserved.object_mem) += string_length + 1;\n                     }\n\n                     flags |= flag_seek_value | flag_need_colon;\n                     continue;\n\n                  default:\n                     break;\n               };\n            }\n            else\n            {\n               string_add (b);\n               continue;\n            }\n         }\n\n         if (state.settings.settings & json_enable_comments)\n         {\n            if (flags & (flag_line_comment | flag_block_comment))\n            {\n               if (flags & flag_line_comment)\n               {\n                  if (b == '\\r' || b == '\\n' || !b)\n                  {\n                     flags &= ~ flag_line_comment;\n                     -- i;  /* so null can be reproc'd */\n                  }\n\n                  continue;\n               }\n\n               if (flags & flag_block_comment)\n               {\n                  if (!b)\n                  {  sprintf (error, \"%d:%d: Unexpected EOF in block comment\", cur_line, e_off);\n                     goto e_failed;\n                  }\n\n                  if (b == '*' && i < (end - 1) && i [1] == '/')\n                  {\n                     flags &= ~ flag_block_comment;\n                     ++ i;  /* skip closing sequence */\n                  }\n\n                  continue;\n               }\n            }\n            else if (b == '/')\n            {\n               if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object)\n               {\n                  sprintf (error, \"%d:%d: Comment not allowed here\", cur_line, e_off);\n                  goto e_failed;\n               }\n\n               if (++ i == end)\n               {  sprintf (error, \"%d:%d: EOF unexpected\", cur_line, e_off);\n                  goto e_failed;\n               }\n\n               switch (b = *i)\n               {\n                  case '/':\n                     flags |= flag_line_comment;\n                     continue;\n\n                  case '*':\n                     flags |= flag_block_comment;\n                     continue;\n\n                  default:\n                     sprintf (error, \"%d:%d: Unexpected `%c` in comment opening sequence\", cur_line, e_off, b);\n                     goto e_failed;\n               };\n            }\n         }\n\n         if (flags & flag_done)\n         {\n            if (!b)\n               break;\n\n            switch (b)\n            {\n               whitespace:\n                  continue;\n\n               default:\n                  sprintf (error, \"%d:%d: Trailing garbage: `%c`\", cur_line, e_off, b);\n                  goto e_failed;\n            };\n         }\n\n         if (flags & flag_seek_value)\n         {\n            switch (b)\n            {\n               whitespace:\n                  continue;\n\n               case ']':\n\n                  if (top->type == json_array)\n                     flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;\n                  else\n                  {  sprintf (error, \"%d:%d: Unexpected ]\", cur_line, e_off);\n                     goto e_failed;\n                  }\n\n                  break;\n\n               default:\n\n                  if (flags & flag_need_comma)\n                  {\n                     if (b == ',')\n                     {  flags &= ~ flag_need_comma;\n                        continue;\n                     }\n                     else\n                     {  sprintf (error, \"%d:%d: Expected , before %c\", cur_line, e_off, b);\n                        goto e_failed;\n                     }\n                  }\n\n                  if (flags & flag_need_colon)\n                  {\n                     if (b == ':')\n                     {  flags &= ~ flag_need_colon;\n                        continue;\n                     }\n                     else\n                     {  sprintf (error, \"%d:%d: Expected : before %c\", cur_line, e_off, b);\n                        goto e_failed;\n                     }\n                  }\n\n                  flags &= ~ flag_seek_value;\n\n                  switch (b)\n                  {\n                     case '{':\n\n                        if (!new_value (&state, &top, &root, &alloc, json_object))\n                           goto e_alloc_failure;\n\n                        continue;\n\n                     case '[':\n\n                        if (!new_value (&state, &top, &root, &alloc, json_array))\n                           goto e_alloc_failure;\n\n                        flags |= flag_seek_value;\n                        continue;\n\n                     case '\"':\n\n                        if (!new_value (&state, &top, &root, &alloc, json_string))\n                           goto e_alloc_failure;\n\n                        flags |= flag_string;\n\n                        string = top->u.string.ptr;\n                        string_length = 0;\n\n                        continue;\n\n                     case 't':\n\n                        if ((end - i) < 3 || *(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e')\n                           goto e_unknown_value;\n\n                        if (!new_value (&state, &top, &root, &alloc, json_boolean))\n                           goto e_alloc_failure;\n\n                        top->u.boolean = 1;\n\n                        flags |= flag_next;\n                        break;\n\n                     case 'f':\n\n                        if ((end - i) < 4 || *(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e')\n                           goto e_unknown_value;\n\n                        if (!new_value (&state, &top, &root, &alloc, json_boolean))\n                           goto e_alloc_failure;\n\n                        flags |= flag_next;\n                        break;\n\n                     case 'n':\n\n                        if ((end - i) < 3 || *(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l')\n                           goto e_unknown_value;\n\n                        if (!new_value (&state, &top, &root, &alloc, json_null))\n                           goto e_alloc_failure;\n\n                        flags |= flag_next;\n                        break;\n\n                     default:\n\n                        if (isdigit (b) || b == '-')\n                        {\n                           if (!new_value (&state, &top, &root, &alloc, json_integer))\n                              goto e_alloc_failure;\n\n                           if (!state.first_pass)\n                           {\n                              while (isdigit (b) || b == '+' || b == '-'\n                                        || b == 'e' || b == 'E' || b == '.')\n                              {\n                                 if ( (++ i) == end)\n                                 {\n                                    b = 0;\n                                    break;\n                                 }\n\n                                 b = *i;\n                              }\n\n                              flags |= flag_next | flag_reproc;\n                              break;\n                           }\n\n                           flags &= ~ (flag_num_negative | flag_num_e |\n                                        flag_num_e_got_sign | flag_num_e_negative |\n                                           flag_num_zero);\n\n                           num_digits = 0;\n                           num_fraction = 0;\n                           num_e = 0;\n\n                           if (b != '-')\n                           {\n                              flags |= flag_reproc;\n                              break;\n                           }\n\n                           flags |= flag_num_negative;\n                           continue;\n                        }\n                        else\n                        {  sprintf (error, \"%d:%d: Unexpected %c when seeking value\", cur_line, e_off, b);\n                           goto e_failed;\n                        }\n                  };\n            };\n         }\n         else\n         {\n            switch (top->type)\n            {\n            case json_object:\n               \n               switch (b)\n               {\n                  whitespace:\n                     continue;\n\n                  case '\"':\n\n                     if (flags & flag_need_comma)\n                     {\n                        sprintf (error, \"%d:%d: Expected , before \\\"\", cur_line, e_off);\n                        goto e_failed;\n                     }\n\n                     flags |= flag_string;\n\n                     string = (json_char *) top->_reserved.object_mem;\n                     string_length = 0;\n\n                     break;\n                  \n                  case '}':\n\n                     flags = (flags & ~ flag_need_comma) | flag_next;\n                     break;\n\n                  case ',':\n\n                     if (flags & flag_need_comma)\n                     {\n                        flags &= ~ flag_need_comma;\n                        break;\n                     }\n\n                  default:\n\n                     sprintf (error, \"%d:%d: Unexpected `%c` in object\", cur_line, e_off, b);\n                     goto e_failed;\n               };\n\n               break;\n\n            case json_integer:\n            case json_double:\n\n               if (isdigit (b))\n               {\n                  ++ num_digits;\n\n                  if (top->type == json_integer || flags & flag_num_e)\n                  {\n                     if (! (flags & flag_num_e))\n                     {\n                        if (flags & flag_num_zero)\n                        {  sprintf (error, \"%d:%d: Unexpected `0` before `%c`\", cur_line, e_off, b);\n                           goto e_failed;\n                        }\n\n                        if (num_digits == 1 && b == '0')\n                           flags |= flag_num_zero;\n                     }\n                     else\n                     {\n                        flags |= flag_num_e_got_sign;\n                        num_e = (num_e * 10) + (b - '0');\n                        continue;\n                     }\n\n                     top->u.integer = (top->u.integer * 10) + (b - '0');\n                     continue;\n                  }\n\n                  num_fraction = (num_fraction * 10) + (b - '0');\n                  continue;\n               }\n\n               if (b == '+' || b == '-')\n               {\n                  if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign))\n                  {\n                     flags |= flag_num_e_got_sign;\n\n                     if (b == '-')\n                        flags |= flag_num_e_negative;\n\n                     continue;\n                  }\n               }\n               else if (b == '.' && top->type == json_integer)\n               {\n                  if (!num_digits)\n                  {  sprintf (error, \"%d:%d: Expected digit before `.`\", cur_line, e_off);\n                     goto e_failed;\n                  }\n\n                  top->type = json_double;\n                  top->u.dbl = (double) top->u.integer;\n\n                  num_digits = 0;\n                  continue;\n               }\n\n               if (! (flags & flag_num_e))\n               {\n                  if (top->type == json_double)\n                  {\n                     if (!num_digits)\n                     {  sprintf (error, \"%d:%d: Expected digit after `.`\", cur_line, e_off);\n                        goto e_failed;\n                     }\n\n                     top->u.dbl += ((double) num_fraction) / (pow (10, (double) num_digits));\n                  }\n\n                  if (b == 'e' || b == 'E')\n                  {\n                     flags |= flag_num_e;\n\n                     if (top->type == json_integer)\n                     {\n                        top->type = json_double;\n                        top->u.dbl = (double) top->u.integer;\n                     }\n\n                     num_digits = 0;\n                     flags &= ~ flag_num_zero;\n\n                     continue;\n                  }\n               }\n               else\n               {\n                  if (!num_digits)\n                  {  sprintf (error, \"%d:%d: Expected digit after `e`\", cur_line, e_off);\n                     goto e_failed;\n                  }\n\n                  top->u.dbl *= pow (10, (double) (flags & flag_num_e_negative ? - num_e : num_e));\n               }\n\n               if (flags & flag_num_negative)\n               {\n                  if (top->type == json_integer)\n                     top->u.integer = - top->u.integer;\n                  else\n                     top->u.dbl = - top->u.dbl;\n               }\n\n               flags |= flag_next | flag_reproc;\n               break;\n\n            default:\n               break;\n            };\n         }\n\n         if (flags & flag_reproc)\n         {\n            flags &= ~ flag_reproc;\n            -- i;\n         }\n\n         if (flags & flag_next)\n         {\n            flags = (flags & ~ flag_next) | flag_need_comma;\n\n            if (!top->parent)\n            {\n               /* root value done */\n\n               flags |= flag_done;\n               continue;\n            }\n\n            if (top->parent->type == json_array)\n               flags |= flag_seek_value;\n               \n            if (!state.first_pass)\n            {\n               json_value * parent = top->parent;\n\n               switch (parent->type)\n               {\n                  case json_object:\n\n                     parent->u.object.values\n                        [parent->u.object.length].value = top;\n\n                     break;\n\n                  case json_array:\n\n                     parent->u.array.values\n                           [parent->u.array.length] = top;\n\n                     break;\n\n                  default:\n                     break;\n               };\n            }\n\n            if ( (++ top->parent->u.array.length) > state.uint_max)\n               goto e_overflow;\n\n            top = top->parent;\n\n            continue;\n         }\n      }\n\n      alloc = root;\n   }\n\n   return root;\n\ne_unknown_value:\n\n   sprintf (error, \"%d:%d: Unknown value\", cur_line, e_off);\n   goto e_failed;\n\ne_alloc_failure:\n\n   strcpy (error, \"Memory allocation failure\");\n   goto e_failed;\n\ne_overflow:\n\n   sprintf (error, \"%d:%d: Too long (caught overflow)\", cur_line, e_off);\n   goto e_failed;\n\ne_failed:\n\n   if (error_buf)\n   {\n      if (*error)\n         strcpy (error_buf, error);\n      else\n         strcpy (error_buf, \"Unknown error\");\n   }\n\n   if (state.first_pass)\n      alloc = root;\n\n   while (alloc)\n   {\n      top = alloc->_reserved.next_alloc;\n      state.settings.mem_free (alloc, state.settings.user_data);\n      alloc = top;\n   }\n\n   if (!state.first_pass)\n      json_value_free_ex (&state.settings, root);\n\n   return 0;\n}\n\njson_value * json_parse (const json_char * json, size_t length)\n{\n   json_settings settings = { 0 };\n   return json_parse_ex (&settings, json, length, 0);\n}\n\nvoid json_value_free_ex (json_settings * settings, json_value * value)\n{\n   json_value * cur_value;\n\n   if (!value)\n      return;\n\n   value->parent = 0;\n\n   while (value)\n   {\n      switch (value->type)\n      {\n         case json_array:\n\n            if (!value->u.array.length)\n            {\n               settings->mem_free (value->u.array.values, settings->user_data);\n               break;\n            }\n\n            value = value->u.array.values [-- value->u.array.length];\n            continue;\n\n         case json_object:\n\n            if (!value->u.object.length)\n            {\n               settings->mem_free (value->u.object.values, settings->user_data);\n               break;\n            }\n\n            value = value->u.object.values [-- value->u.object.length].value;\n            continue;\n\n         case json_string:\n\n            settings->mem_free (value->u.string.ptr, settings->user_data);\n            break;\n\n         default:\n            break;\n      };\n\n      cur_value = value;\n      value = value->parent;\n      settings->mem_free (cur_value, settings->user_data);\n   }\n}\n\nvoid json_value_free (json_value * value)\n{\n   json_settings settings = { 0 };\n   settings.mem_free = default_free;\n   json_value_free_ex (&settings, value);\n}\n"
  },
  {
    "path": "server/src/main.cpp",
    "content": "#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n#include <time.h>\n#include <detect.h>\n#include <system.h>\n#include <argparse.h>\n#include <json.h>\n#include \"server.h\"\n#include \"main.h\"\n\n#if defined(CONF_FAMILY_UNIX)\n\t#include <signal.h>\n#endif\n\n#ifndef PRId64\n\t#define PRId64 \"I64d\"\n#endif\n\nstatic volatile int gs_Running = 1;\nstatic volatile int gs_ReloadConfig = 0;\n\nstatic void ExitFunc(int Signal)\n{\n\tprintf(\"[EXIT] Caught signal %d\\n\", Signal);\n\tgs_Running = 0;\n}\n\nstatic void ReloadFunc(int Signal)\n{\n\tprintf(\"[RELOAD] Caught signal %d\\n\", Signal);\n\tgs_ReloadConfig = 1;\n}\n\nCConfig::CConfig()\n{\n\t// Initialize to default values\n\tm_Verbose = false; // -v, --verbose\n\tstr_copy(m_aConfigFile, \"config.json\", sizeof(m_aConfigFile)); // -c, --config\n\tstr_copy(m_aWebDir, \"../web/\", sizeof(m_aJSONFile)); // -d, --web-dir\n\tstr_copy(m_aTemplateFile, \"template.html\", sizeof(m_aTemplateFile));\n\tstr_copy(m_aJSONFile, \"json/stats.json\", sizeof(m_aJSONFile));\n\tstr_copy(m_aBindAddr, \"\", sizeof(m_aBindAddr)); // -b, --bind\n\tm_Port = 35601; // -p, --port\n}\n\nCMain::CMain(CConfig Config) : m_Config(Config)\n{\n\tmem_zero(m_aClients, sizeof(m_aClients));\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\tm_aClients[i].m_ClientNetID = -1;\n}\n\nCMain::CClient *CMain::ClientNet(int ClientNetID)\n{\n\tif(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS)\n\t\treturn 0;\n\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t{\n\t\tif(Client(i)->m_ClientNetID == ClientNetID)\n\t\t\treturn Client(i);\n\t}\n\n\treturn 0;\n}\n\nint CMain::ClientNetToClient(int ClientNetID)\n{\n\tif(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS)\n\t\treturn -1;\n\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t{\n\t\tif(Client(i)->m_ClientNetID == ClientNetID)\n\t\t\treturn i;\n\t}\n\n\treturn -1;\n}\n\nvoid CMain::OnNewClient(int ClientNetID, int ClientID)\n{\n\tdbg_msg(\"main\", \"OnNewClient(ncid=%d, cid=%d)\", ClientNetID, ClientID);\n\tClient(ClientID)->m_ClientNetID = ClientNetID;\n\tClient(ClientID)->m_ClientNetType = m_Server.Network()->ClientAddr(ClientNetID)->type;\n\tClient(ClientID)->m_TimeConnected = time_get();\n\tClient(ClientID)->m_Connected = true;\n\n\tif(Client(ClientID)->m_ClientNetType == NETTYPE_IPV4)\n\t\tClient(ClientID)->m_Stats.m_Online4 = true;\n\telse if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV6)\n\t\tClient(ClientID)->m_Stats.m_Online6 = true;\n}\n\nvoid CMain::OnDelClient(int ClientNetID)\n{\n\tint ClientID = ClientNetToClient(ClientNetID);\n\tdbg_msg(\"main\", \"OnDelClient(ncid=%d, cid=%d)\", ClientNetID, ClientID);\n\tif(ClientID >= 0 && ClientID < NET_MAX_CLIENTS)\n\t{\n\t\tClient(ClientID)->m_Connected = false;\n\t\tClient(ClientID)->m_ClientNetID = -1;\n\t\tClient(ClientID)->m_ClientNetType = NETTYPE_INVALID;\n\t\tmem_zero(&Client(ClientID)->m_Stats, sizeof(CClient::CStats));\n\t}\n}\n\nint CMain::HandleMessage(int ClientNetID, char *pMessage)\n{\n\tCClient *pClient = ClientNet(ClientNetID);\n\tif(!pClient)\n\t\treturn true;\n\n\tif(str_comp_num(pMessage, \"update\", sizeof(\"update\")-1) == 0)\n\t{\n\t\tchar *pData = str_skip_whitespaces(&pMessage[sizeof(\"update\")-1]);\n\n\t\t// parse json data\n\t\tjson_settings JsonSettings;\n\t\tmem_zero(&JsonSettings, sizeof(JsonSettings));\n\t\tchar aError[256];\n\t\tjson_value *pJsonData = json_parse_ex(&JsonSettings, pData, strlen(pData), aError);\n\t\tif(!pJsonData)\n\t\t{\n\t\t\tdbg_msg(\"main\", \"JSON Error: %s\", aError);\n\t\t\tif(pClient->m_Stats.m_Pong)\n\t\t\t\tm_Server.Network()->Send(ClientNetID, \"1\");\n\t\t\treturn 1;\n\t\t}\n\n\t\t// extract data\n\t\tconst json_value &rStart = (*pJsonData);\n\t\tif(rStart[\"uptime\"].type)\n\t\t\tpClient->m_Stats.m_Uptime = rStart[\"uptime\"].u.integer;\n\t\tif(rStart[\"load\"].type)\n\t\t\tpClient->m_Stats.m_Load = rStart[\"load\"].u.dbl;\n\t\tif(rStart[\"network_rx\"].type)\n\t\t\tpClient->m_Stats.m_NetworkRx = rStart[\"network_rx\"].u.integer;\n\t\tif(rStart[\"network_tx\"].type)\n\t\t\tpClient->m_Stats.m_NetworkTx = rStart[\"network_tx\"].u.integer;\n\t\tif(rStart[\"network_in\"].type)\n\t\t\tpClient->m_Stats.m_NetworkIN = rStart[\"network_in\"].u.integer;\n\t\tif(rStart[\"network_out\"].type)\n\t\t\tpClient->m_Stats.m_NetworkOUT = rStart[\"network_out\"].u.integer;\n\t\tif(rStart[\"memory_total\"].type)\n\t\t\tpClient->m_Stats.m_MemTotal = rStart[\"memory_total\"].u.integer;\n\t\tif(rStart[\"memory_used\"].type)\n\t\t\tpClient->m_Stats.m_MemUsed = rStart[\"memory_used\"].u.integer;\n\t\tif(rStart[\"swap_total\"].type)\n\t\t\tpClient->m_Stats.m_SwapTotal = rStart[\"swap_total\"].u.integer;\n\t\tif(rStart[\"swap_used\"].type)\n\t\t\tpClient->m_Stats.m_SwapUsed = rStart[\"swap_used\"].u.integer;\n\t\tif(rStart[\"hdd_total\"].type)\n\t\t\tpClient->m_Stats.m_HDDTotal = rStart[\"hdd_total\"].u.integer;\n\t\tif(rStart[\"hdd_used\"].type)\n\t\t\tpClient->m_Stats.m_HDDUsed = rStart[\"hdd_used\"].u.integer;\n\t\tif(rStart[\"cpu\"].type)\n\t\t\tpClient->m_Stats.m_CPU = rStart[\"cpu\"].u.dbl;\n\t\tif(rStart[\"online4\"].type && pClient->m_ClientNetType == NETTYPE_IPV6)\n\t\t\tpClient->m_Stats.m_Online4 = rStart[\"online4\"].u.boolean;\n\t\tif(rStart[\"online6\"].type && pClient->m_ClientNetType == NETTYPE_IPV4)\n\t\t\tpClient->m_Stats.m_Online6 = rStart[\"online6\"].u.boolean;\n\t\tif(rStart[\"custom\"].type == json_string)\n\t\t\tstr_copy(pClient->m_Stats.m_aCustom, rStart[\"custom\"].u.string.ptr, sizeof(pClient->m_Stats.m_aCustom));\n\n\t\tif(m_Config.m_Verbose)\n\t\t{\n\t\t\tif(rStart[\"online4\"].type)\n\t\t\t\tdbg_msg(\"main\", \"Online4: %s\\nUptime: %\" PRId64 \"\\nLoad: %f\\nNetworkRx: %\" PRId64 \"\\nNetworkTx: %\" PRId64 \"\\nNetworkIN: %\" PRId64 \"\\nNetworkOUT: %\" PRId64 \"\\nMemTotal: %\" PRId64 \"\\nMemUsed: %\" PRId64 \"\\nSwapTotal: %\" PRId64 \"\\nSwapUsed: %\" PRId64 \"\\nHDDTotal: %\" PRId64 \"\\nHDDUsed: %\" PRId64 \"\\nCPU: %f\\n\",\n\t\t\t\t\trStart[\"online4\"].u.boolean ? \"true\" : \"false\",\n\t\t\t\t\tpClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU);\n\t\t\telse if(rStart[\"online6\"].type)\n\t\t\t\tdbg_msg(\"main\", \"Online6: %s\\nUptime: %\" PRId64 \"\\nLoad: %f\\nNetworkRx: %\" PRId64 \"\\nNetworkTx: %\" PRId64 \"\\nNetworkIN: %\" PRId64 \"\\nNetworkOUT: %\" PRId64 \"\\nMemTotal: %\" PRId64 \"\\nMemUsed: %\" PRId64 \"\\nSwapTotal: %\" PRId64 \"\\nSwapUsed: %\" PRId64 \"\\nHDDTotal: %\" PRId64 \"\\nHDDUsed: %\" PRId64 \"\\nCPU: %f\\n\",\n\t\t\t\t\trStart[\"online6\"].u.boolean ? \"true\" : \"false\",\n\t\t\t\t\tpClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU);\n\t\t\telse\n\t\t\t\tdbg_msg(\"main\", \"Uptime: %\" PRId64 \"\\nLoad: %f\\nNetworkRx: %\" PRId64 \"\\nNetworkTx: %\" PRId64 \"\\nNetworkIN: %\" PRId64 \"\\nNetworkOUT: %\" PRId64 \"\\nMemTotal: %\" PRId64 \"\\nMemUsed: %\" PRId64 \"\\nSwapTotal: %\" PRId64 \"\\nSwapUsed: %\" PRId64 \"\\nHDDTotal: %\" PRId64 \"\\nHDDUsed: %\" PRId64 \"\\nCPU: %f\\n\",\n\t\t\t\t\tpClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU);\n\t\t}\n\n\t\t// clean up\n\t\tjson_value_free(pJsonData);\n\n\t\tif(pClient->m_Stats.m_Pong)\n\t\t\tm_Server.Network()->Send(ClientNetID, \"0\");\n\t\treturn 0;\n\t}\n\telse if(str_comp_num(pMessage, \"pong\", sizeof(\"pong\")-1) == 0)\n\t{\n\t\tchar *pData = str_skip_whitespaces(&pMessage[sizeof(\"pong\")-1]);\n\n\t\tif(!str_comp(pData, \"0\") || !str_comp(pData, \"off\"))\n\t\t\tpClient->m_Stats.m_Pong = false;\n\t\telse if(!str_comp(pData, \"1\") || !str_comp(pData, \"on\"))\n\t\t\tpClient->m_Stats.m_Pong = true;\n\n\t\treturn 0;\n\t}\n\n\tif(pClient->m_Stats.m_Pong)\n\t\tm_Server.Network()->Send(ClientNetID, \"1\");\n\n\treturn 1;\n}\n\nvoid CMain::JSONUpdateThread(void *pUser)\n{\n\tCJSONUpdateThreadData *m_pJSONUpdateThreadData = (CJSONUpdateThreadData *)pUser;\n\tCClient *pClients = m_pJSONUpdateThreadData->pClients;\n\tCConfig *pConfig = m_pJSONUpdateThreadData->pConfig;\n\n\twhile(gs_Running)\n\t{\n\t\tchar aFileBuf[2048*NET_MAX_CLIENTS];\n\t\tchar *pBuf = aFileBuf;\n\n\t\tstr_format(pBuf, sizeof(aFileBuf), \"{\\n\\\"servers\\\": [\\n\");\n\t\tpBuf += strlen(pBuf);\n\n\t\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\t{\n\t\t\tif(!pClients[i].m_Active || pClients[i].m_Disabled)\n\t\t\t\tcontinue;\n\n\t\t\tif(pClients[i].m_Connected)\n\t\t\t{\n\t\t\t\t// Uptime\n\t\t\t\tchar aUptime[16];\n\t\t\t\tint Days = pClients[i].m_Stats.m_Uptime/60.0/60.0/24.0;\n\t\t\t\tif(Days > 0)\n\t\t\t\t{\n\t\t\t\t\tif(Days > 1)\n\t\t\t\t\t\tstr_format(aUptime, sizeof(aUptime), \"%d 天\", Days);\n\t\t\t\t\telse\n\t\t\t\t\t\tstr_format(aUptime, sizeof(aUptime), \"%d 天\", Days);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tstr_format(aUptime, sizeof(aUptime), \"%02d:%02d:%02d\", (int)(pClients[i].m_Stats.m_Uptime/60.0/60.0), (int)((pClients[i].m_Stats.m_Uptime/60)%60), (int)((pClients[i].m_Stats.m_Uptime)%60));\n\n\t\t\t\tstr_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), \"{ \\\"name\\\": \\\"%s\\\", \\\"type\\\": \\\"%s\\\", \\\"host\\\": \\\"%s\\\", \\\"location\\\": \\\"%s\\\", \\\"online4\\\": %s, \\\"online6\\\": %s, \\\"uptime\\\": \\\"%s\\\", \\\"load\\\": %.2f, \\\"network_rx\\\": %\" PRId64 \", \\\"network_tx\\\": %\" PRId64 \", \\\"network_in\\\": %\" PRId64 \", \\\"network_out\\\": %\" PRId64 \", \\\"cpu\\\": %d, \\\"memory_total\\\": %\" PRId64 \", \\\"memory_used\\\": %\" PRId64 \", \\\"swap_total\\\": %\" PRId64 \", \\\"swap_used\\\": %\" PRId64 \", \\\"hdd_total\\\": %\" PRId64 \", \\\"hdd_used\\\": %\" PRId64 \", \\\"custom\\\": \\\"%s\\\" },\\n\",\n\t\t\t\t\tpClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation, pClients[i].m_Stats.m_Online4 ? \"true\" : \"false\", pClients[i].m_Stats.m_Online6 ? \"true\" : \"false\",\taUptime, pClients[i].m_Stats.m_Load, pClients[i].m_Stats.m_NetworkRx, pClients[i].m_Stats.m_NetworkTx, pClients[i].m_Stats.m_NetworkIN, pClients[i].m_Stats.m_NetworkOUT, (int)pClients[i].m_Stats.m_CPU, pClients[i].m_Stats.m_MemTotal, pClients[i].m_Stats.m_MemUsed, pClients[i].m_Stats.m_SwapTotal, pClients[i].m_Stats.m_SwapUsed, pClients[i].m_Stats.m_HDDTotal, pClients[i].m_Stats.m_HDDUsed, pClients[i].m_Stats.m_aCustom);\n\t\t\t\tpBuf += strlen(pBuf);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstr_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), \"{ \\\"name\\\": \\\"%s\\\", \\\"type\\\": \\\"%s\\\", \\\"host\\\": \\\"%s\\\", \\\"location\\\": \\\"%s\\\", \\\"online4\\\": false, \\\"online6\\\": false },\\n\",\n\t\t\t\t\tpClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation);\n\t\t\t\tpBuf += strlen(pBuf);\n\t\t\t}\n\t\t}\n\t\tif(!m_pJSONUpdateThreadData->m_ReloadRequired)\n\t\t\tstr_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), \"\\n],\\n\\\"updated\\\": \\\"%lld\\\"\\n}\", (long long)time(/*ago*/0));\n\t\telse\n\t\t{\n\t\t\tstr_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), \"\\n],\\n\\\"updated\\\": \\\"%lld\\\",\\n\\\"reload\\\": true\\n}\", (long long)time(/*ago*/0));\n\t\t\tm_pJSONUpdateThreadData->m_ReloadRequired--;\n\t\t}\n\t\tpBuf += strlen(pBuf);\n\n\t\tchar aJSONFileTmp[1024];\n\t\tstr_format(aJSONFileTmp, sizeof(aJSONFileTmp), \"%s~\", pConfig->m_aJSONFile);\n\t\tIOHANDLE File = io_open(aJSONFileTmp, IOFLAG_WRITE);\n\t\tif(!File)\n\t\t{\n\t\t\tdbg_msg(\"main\", \"Couldn't open %s\", aJSONFileTmp);\n\t\t\texit(1);\n\t\t}\n\t\tio_write(File, aFileBuf, (pBuf - aFileBuf));\n\t\tio_flush(File);\n\t\tio_close(File);\n\t\tfs_rename(aJSONFileTmp, pConfig->m_aJSONFile);\n\t\tthread_sleep(1000);\n\t}\n\tfs_remove(pConfig->m_aJSONFile);\n}\n\nint CMain::ReadConfig()\n{\n\t// read and parse config\n\tIOHANDLE File = io_open(m_Config.m_aConfigFile, IOFLAG_READ);\n\tif(!File)\n\t{\n\t\tdbg_msg(\"main\", \"Couldn't open %s\", m_Config.m_aConfigFile);\n\t\treturn 1;\n\t}\n\tint FileSize = (int)io_length(File);\n\tchar *pFileData = (char *)mem_alloc(FileSize + 1, 1);\n\n\tio_read(File, pFileData, FileSize);\n\tpFileData[FileSize] = 0;\n\tio_close(File);\n\n\t// parse json data\n\tjson_settings JsonSettings;\n\tmem_zero(&JsonSettings, sizeof(JsonSettings));\n\tchar aError[256];\n\tjson_value *pJsonData = json_parse_ex(&JsonSettings, pFileData, strlen(pFileData), aError);\n\tif(!pJsonData)\n\t{\n\t\tdbg_msg(\"main\", \"JSON Error in file %s: %s\", m_Config.m_aConfigFile, aError);\n\t\tmem_free(pFileData);\n\t\treturn 1;\n\t}\n\n\t// reset clients\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t{\n\t\tif(!Client(i)->m_Active || !Client(i)->m_Connected)\n\t\t\tcontinue;\n\n\t\tm_Server.Network()->Drop(Client(i)->m_ClientNetID, \"Server reloading...\");\n\t}\n\tmem_zero(m_aClients, sizeof(m_aClients));\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\tm_aClients[i].m_ClientNetID = -1;\n\n\t// extract data\n\tint ID = 0;\n\tconst json_value &rStart = (*pJsonData)[\"servers\"];\n\tif(rStart.type == json_array)\n\t{\n\t\tfor(unsigned i = 0; i < rStart.u.array.length; i++)\n\t\t{\n\t\t\tif(ID < 0 || ID >= NET_MAX_CLIENTS)\n\t\t\t\tcontinue;\n\n\t\t\tClient(ID)->m_Active = true;\n\t\t\tClient(ID)->m_Disabled = rStart[i][\"disabled\"].u.boolean;\n\t\t\tstr_copy(Client(ID)->m_aName, rStart[i][\"name\"].u.string.ptr, sizeof(Client(ID)->m_aName));\n\t\t\tstr_copy(Client(ID)->m_aUsername, rStart[i][\"username\"].u.string.ptr, sizeof(Client(ID)->m_aUsername));\n\t\t\tstr_copy(Client(ID)->m_aType, rStart[i][\"type\"].u.string.ptr, sizeof(Client(ID)->m_aType));\n\t\t\tstr_copy(Client(ID)->m_aHost, rStart[i][\"host\"].u.string.ptr, sizeof(Client(ID)->m_aHost));\n\t\t\tstr_copy(Client(ID)->m_aLocation, rStart[i][\"location\"].u.string.ptr, sizeof(Client(ID)->m_aLocation));\n\t\t\tstr_copy(Client(ID)->m_aPassword, rStart[i][\"password\"].u.string.ptr, sizeof(Client(ID)->m_aPassword));\n\n\t\t\tif(m_Config.m_Verbose)\n\t\t\t{\n\t\t\t\tif(Client(ID)->m_Disabled)\n\t\t\t\t\tdbg_msg(\"main\", \"[#%d: Name: \\\"%s\\\", Username: \\\"%s\\\", Type: \\\"%s\\\", Host: \\\"%s\\\", Location: \\\"%s\\\", Password: \\\"%s\\\"]\",\n\t\t\t\t\t\tID, Client(ID)->m_aName, Client(ID)->m_aUsername, Client(ID)->m_aType, Client(ID)->m_aHost, Client(ID)->m_aLocation, Client(ID)->m_aPassword);\n\t\t\t\telse\n\t\t\t\t\tdbg_msg(\"main\", \"#%d: Name: \\\"%s\\\", Username: \\\"%s\\\", Type: \\\"%s\\\", Host: \\\"%s\\\", Location: \\\"%s\\\", Password: \\\"%s\\\"\",\n\t\t\t\t\t\tID, Client(ID)->m_aName, Client(ID)->m_aUsername, Client(ID)->m_aType, Client(ID)->m_aHost, Client(ID)->m_aLocation, Client(ID)->m_aPassword);\n\n\t\t\t}\n\t\t\tID++;\n\t\t}\n\t}\n\n\t// clean up\n\tjson_value_free(pJsonData);\n\tmem_free(pFileData);\n\n\t// tell clients to reload the page\n\tm_JSONUpdateThreadData.m_ReloadRequired = 2;\n\n\treturn 0;\n}\n\nint CMain::Run()\n{\n\tif(m_Server.Init(this, m_Config.m_aBindAddr, m_Config.m_Port))\n\t\treturn 1;\n\n\tif(ReadConfig())\n\t\treturn 1;\n\n\t// Start JSON Update Thread\n\tm_JSONUpdateThreadData.m_ReloadRequired = 2;\n\tm_JSONUpdateThreadData.pClients = m_aClients;\n\tm_JSONUpdateThreadData.pConfig = &m_Config;\n\tvoid *LoadThread = thread_create(JSONUpdateThread, &m_JSONUpdateThreadData);\n\t//thread_detach(LoadThread);\n\n\twhile(gs_Running)\n\t{\n\t\tif(gs_ReloadConfig)\n\t\t{\n\t\t\tif(ReadConfig())\n\t\t\t\treturn 1;\n\t\t\tm_Server.NetBan()->UnbanAll();\n\t\t\tgs_ReloadConfig = 0;\n\t\t}\n\n\t\tm_Server.Update();\n\n\t\t// wait for incomming data\n\t\tnet_socket_read_wait(*m_Server.Network()->Socket(), 10);\n\t}\n\n\tdbg_msg(\"server\", \"Closing.\");\n\tm_Server.Network()->Close();\n\tthread_wait(LoadThread);\n\n\treturn 0;\n}\n\nint main(int argc, const char *argv[])\n{\n\tint RetVal;\n\tdbg_logger_stdout();\n\n\t#if defined(CONF_FAMILY_UNIX)\n\t\tsignal(SIGINT, ExitFunc);\n\t\tsignal(SIGTERM, ExitFunc);\n\t\tsignal(SIGQUIT, ExitFunc);\n\t\tsignal(SIGHUP, ReloadFunc);\n\t#endif\n\n\tchar aUsage[128];\n\tCConfig Config;\n\tstr_format(aUsage, sizeof(aUsage), \"%s [options]\", argv[0]);\n\tconst char *pConfigFile = 0;\n\tconst char *pWebDir = 0;\n\tconst char *pBindAddr = 0;\n\n\tstruct argparse_option aOptions[] = {\n\t\tOPT_HELP(),\n\t\tOPT_BOOLEAN('v', \"verbose\", &Config.m_Verbose, \"Verbose output\", 0),\n\t\tOPT_STRING('c', \"config\", &pConfigFile, \"Config file to use\", 0),\n\t\tOPT_STRING('d', \"web-dir\", &pWebDir, \"Location of the web directory\", 0),\n\t\tOPT_STRING('b', \"bind\", &pBindAddr, \"Bind to address\", 0),\n\t\tOPT_INTEGER('p', \"port\", &Config.m_Port, \"Listen on port\", 0),\n\t\tOPT_END(),\n\t};\n\tstruct argparse Argparse;\n\targparse_init(&Argparse, aOptions, aUsage, 0);\n\targc = argparse_parse(&Argparse, argc, argv);\n\n\tif(pConfigFile)\n\t\tstr_copy(Config.m_aConfigFile, pConfigFile, sizeof(Config.m_aConfigFile));\n\tif(pWebDir)\n\t\tstr_copy(Config.m_aWebDir, pWebDir, sizeof(Config.m_aWebDir));\n\tif(pBindAddr)\n\t\tstr_copy(Config.m_aBindAddr, pBindAddr, sizeof(Config.m_aBindAddr));\n\n\tif(Config.m_aWebDir[strlen(Config.m_aWebDir)-1] != '/')\n\t\tstr_append(Config.m_aWebDir, \"/\", sizeof(Config.m_aWebDir));\n\tif(!fs_is_dir(Config.m_aWebDir))\n\t{\n\t\tdbg_msg(\"main\", \"ERROR: Can't find web directory: %s\", Config.m_aWebDir);\n\t\treturn 1;\n\t}\n\n\tchar aTmp[1024];\n\tstr_format(aTmp, sizeof(aTmp), \"%s%s\", Config.m_aWebDir, Config.m_aJSONFile);\n\tstr_copy(Config.m_aJSONFile, aTmp, sizeof(Config.m_aJSONFile));\n\n\tCMain Main(Config);\n\tRetVal = Main.Run();\n\n\treturn RetVal;\n}\n"
  },
  {
    "path": "server/src/main.h",
    "content": "#ifndef MAIN_H\n#define MAIN_H\n\n#include <stdint.h>\n#include \"server.h\"\n\nclass CConfig\n{\npublic:\n\tbool m_Verbose;\n\tchar m_aConfigFile[1024];\n\tchar m_aWebDir[1024];\n\tchar m_aTemplateFile[1024];\n\tchar m_aJSONFile[1024];\n\tchar m_aBindAddr[256];\n\tint m_Port;\n\n\tCConfig();\n};\n\nclass CMain\n{\n\tCConfig m_Config;\n\tCServer m_Server;\n\n\tstruct CClient\n\t{\n\t\tbool m_Active;\n\t\tbool m_Disabled;\n\t\tbool m_Connected;\n\t\tint m_ClientNetID;\n\t\tint m_ClientNetType;\n\t\tchar m_aUsername[128];\n\t\tchar m_aName[128];\n\t\tchar m_aType[128];\n\t\tchar m_aHost[128];\n\t\tchar m_aLocation[128];\n\t\tchar m_aPassword[128];\n\n\t\tint64 m_TimeConnected;\n\t\tint64 m_LastUpdate;\n\n\t\tstruct CStats\n\t\t{\n\t\t\tbool m_Online4;\n\t\t\tbool m_Online6;\n\t\t\tint64_t m_Uptime;\n\t\t\tdouble m_Load;\n\t\t\tint64_t m_NetworkRx;\n\t\t\tint64_t m_NetworkTx;\n\t\t\tint64_t m_NetworkIN;\n\t\t\tint64_t m_NetworkOUT;\n\t\t\tint64_t m_MemTotal;\n\t\t\tint64_t m_MemUsed;\n\t\t\tint64_t m_SwapTotal;\n\t\t\tint64_t m_SwapUsed;\n\t\t\tint64_t m_HDDTotal;\n\t\t\tint64_t m_HDDUsed;\n\t\t\tdouble m_CPU;\n\t\t\tchar m_aCustom[512];\n\t\t\t// Options\n\t\t\tbool m_Pong;\n\t\t} m_Stats;\n\t} m_aClients[NET_MAX_CLIENTS];\n\n\tstruct CJSONUpdateThreadData\n\t{\n\t\tCClient *pClients;\n\t\tCConfig *pConfig;\n\t\tvolatile short m_ReloadRequired;\n\t} m_JSONUpdateThreadData;\n\n\tstatic void JSONUpdateThread(void *pUser);\npublic:\n\tCMain(CConfig Config);\n\n\tvoid OnNewClient(int ClienNettID, int ClientID);\n\tvoid OnDelClient(int ClientNetID);\n\tint HandleMessage(int ClientNetID, char *pMessage);\n\tint ReadConfig();\n\tint Run();\n\n\tCClient *Client(int ClientID) { return &m_aClients[ClientID]; }\n\tCClient *ClientNet(int ClientNetID);\n\tconst CConfig *Config() const { return &m_Config; }\n\tint ClientNetToClient(int ClientNetID);\n};\n\n\n#endif\n"
  },
  {
    "path": "server/src/netban.cpp",
    "content": "#include <math.h>\n#include \"netban.h\"\n\nbool CNetBan::StrAllnum(const char *pStr)\n{\n\twhile(*pStr)\n\t{\n\t\tif(!(*pStr >= '0' && *pStr <= '9'))\n\t\t\treturn false;\n\t\tpStr++;\n\t}\n\treturn true;\n}\n\n\nCNetBan::CNetHash::CNetHash(const NETADDR *pAddr)\n{\n\tif(pAddr->type==NETTYPE_IPV4)\n\t\tm_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3])&0xFF;\n\telse\n\t\tm_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3]+pAddr->ip[4]+pAddr->ip[5]+pAddr->ip[6]+pAddr->ip[7]+\n\t\t\tpAddr->ip[8]+pAddr->ip[9]+pAddr->ip[10]+pAddr->ip[11]+pAddr->ip[12]+pAddr->ip[13]+pAddr->ip[14]+pAddr->ip[15])&0xFF;\n\tm_HashIndex = 0;\n}\n\nCNetBan::CNetHash::CNetHash(const CNetRange *pRange)\n{\n\tm_Hash = 0;\n\tm_HashIndex = 0;\n\tfor(int i = 0; pRange->m_LB.ip[i] == pRange->m_UB.ip[i]; ++i)\n\t{\n\t\tm_Hash += pRange->m_LB.ip[i];\n\t\t++m_HashIndex;\n\t}\n\tm_Hash &= 0xFF;\n}\n\nint CNetBan::CNetHash::MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17])\n{\n\tint Length = pAddr->type==NETTYPE_IPV4 ? 4 : 16;\n\taHash[0].m_Hash = 0;\n\taHash[0].m_HashIndex = 0;\n\tfor(int i = 1, Sum = 0; i <= Length; ++i)\n\t{\n\t\tSum += pAddr->ip[i-1];\n\t\taHash[i].m_Hash = Sum&0xFF;\n\t\taHash[i].m_HashIndex = i%Length;\n\t}\n\treturn Length;\n}\n\n\ntemplate<class T, int HashCount>\ntypename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Add(const T *pData, const CBanInfo *pInfo, const CNetHash *pNetHash)\n{\n\tif(!m_pFirstFree)\n\t\treturn 0;\n\n\t// create new ban\n\tCBan<T> *pBan = m_pFirstFree;\n\tpBan->m_Data = *pData;\n\tpBan->m_Info = *pInfo;\n\tpBan->m_NetHash = *pNetHash;\n\tif(pBan->m_pNext)\n\t\tpBan->m_pNext->m_pPrev = pBan->m_pPrev;\n\tif(pBan->m_pPrev)\n\t\tpBan->m_pPrev->m_pNext = pBan->m_pNext;\n\telse\n\t\tm_pFirstFree = pBan->m_pNext;\n\n\t// add it to the hash list\n\tif(m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash])\n\t\tm_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]->m_pHashPrev = pBan;\n\tpBan->m_pHashPrev = 0;\n\tpBan->m_pHashNext = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash];\n\tm_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash] = pBan;\n\n\t// insert it into the used list\n\tif(m_pFirstUsed)\n\t{\n\t\tfor(CBan<T> *p = m_pFirstUsed; ; p = p->m_pNext)\n\t\t{\n\t\t\tif(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires))\n\t\t\t{\n\t\t\t\t// insert before\n\t\t\t\tpBan->m_pNext = p;\n\t\t\t\tpBan->m_pPrev = p->m_pPrev;\n\t\t\t\tif(p->m_pPrev)\n\t\t\t\t\tp->m_pPrev->m_pNext = pBan;\n\t\t\t\telse\n\t\t\t\t\tm_pFirstUsed = pBan;\n\t\t\t\tp->m_pPrev = pBan;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif(!p->m_pNext)\n\t\t\t{\n\t\t\t\t// last entry\n\t\t\t\tp->m_pNext = pBan;\n\t\t\t\tpBan->m_pPrev = p;\n\t\t\t\tpBan->m_pNext = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tm_pFirstUsed = pBan;\n\t\tpBan->m_pNext = pBan->m_pPrev = 0;\n\t}\n\n\t// update ban count\n\t++m_CountUsed;\n\n\treturn pBan;\n}\n\ntemplate<class T, int HashCount>\nint CNetBan::CBanPool<T, HashCount>::Remove(CBan<T> *pBan)\n{\n\tif(pBan == 0)\n\t\treturn -1;\n\n\t// remove from hash list\n\tif(pBan->m_pHashNext)\n\t\tpBan->m_pHashNext->m_pHashPrev = pBan->m_pHashPrev;\n\tif(pBan->m_pHashPrev)\n\t\tpBan->m_pHashPrev->m_pHashNext = pBan->m_pHashNext;\n\telse\n\t\tm_paaHashList[pBan->m_NetHash.m_HashIndex][pBan->m_NetHash.m_Hash] = pBan->m_pHashNext;\n\tpBan->m_pHashNext = pBan->m_pHashPrev = 0;\n\n\t// remove from used list\n\tif(pBan->m_pNext)\n\t\tpBan->m_pNext->m_pPrev = pBan->m_pPrev;\n\tif(pBan->m_pPrev)\n\t\tpBan->m_pPrev->m_pNext = pBan->m_pNext;\n\telse\n\t\tm_pFirstUsed = pBan->m_pNext;\n\n\t// add to recycle list\n\tif(m_pFirstFree)\n\t\tm_pFirstFree->m_pPrev = pBan;\n\tpBan->m_pPrev = 0;\n\tpBan->m_pNext = m_pFirstFree;\n\tm_pFirstFree = pBan;\n\n\t// update ban count\n\t--m_CountUsed;\n\n\treturn 0;\n}\n\ntemplate<class T, int HashCount>\nvoid CNetBan::CBanPool<T, HashCount>::Update(CBan<CDataType> *pBan, const CBanInfo *pInfo)\n{\n\tpBan->m_Info = *pInfo;\n\n\t// remove from used list\n\tif(pBan->m_pNext)\n\t\tpBan->m_pNext->m_pPrev = pBan->m_pPrev;\n\tif(pBan->m_pPrev)\n\t\tpBan->m_pPrev->m_pNext = pBan->m_pNext;\n\telse\n\t\tm_pFirstUsed = pBan->m_pNext;\n\n\t// insert it into the used list\n\tif(m_pFirstUsed)\n\t{\n\t\tfor(CBan<T> *p = m_pFirstUsed; ; p = p->m_pNext)\n\t\t{\n\t\t\tif(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires))\n\t\t\t{\n\t\t\t\t// insert before\n\t\t\t\tpBan->m_pNext = p;\n\t\t\t\tpBan->m_pPrev = p->m_pPrev;\n\t\t\t\tif(p->m_pPrev)\n\t\t\t\t\tp->m_pPrev->m_pNext = pBan;\n\t\t\t\telse\n\t\t\t\t\tm_pFirstUsed = pBan;\n\t\t\t\tp->m_pPrev = pBan;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif(!p->m_pNext)\n\t\t\t{\n\t\t\t\t// last entry\n\t\t\t\tp->m_pNext = pBan;\n\t\t\t\tpBan->m_pPrev = p;\n\t\t\t\tpBan->m_pNext = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tm_pFirstUsed = pBan;\n\t\tpBan->m_pNext = pBan->m_pPrev = 0;\n\t}\n}\n\ntemplate<class T, int HashCount>\nvoid CNetBan::CBanPool<T, HashCount>::Reset()\n{\n\tmem_zero(m_paaHashList, sizeof(m_paaHashList));\n\tmem_zero(m_aBans, sizeof(m_aBans));\n\tm_pFirstUsed = 0;\n\tm_CountUsed = 0;\n\n\tfor(int i = 1; i < MAX_BANS-1; ++i)\n\t{\n\t\tm_aBans[i].m_pNext = &m_aBans[i+1];\n\t\tm_aBans[i].m_pPrev = &m_aBans[i-1];\n\t}\n\n\tm_aBans[0].m_pNext = &m_aBans[1];\n\tm_aBans[MAX_BANS-1].m_pPrev = &m_aBans[MAX_BANS-2];\n\tm_pFirstFree = &m_aBans[0];\n}\n\ntemplate<class T, int HashCount>\ntypename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Get(int Index) const\n{\n\tif(Index < 0 || Index >= Num())\n\t\treturn 0;\n\n\tfor(CNetBan::CBan<T> *pBan = m_pFirstUsed; pBan; pBan = pBan->m_pNext, --Index)\n\t{\n\t\tif(Index == 0)\n\t\t\treturn pBan;\n\t}\n\n\treturn 0;\n}\n\n\ntemplate<class T>\nvoid CNetBan::MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const\n{\n\tif(pBan == 0 || pBuf == 0)\n\t{\n\t\tif(BuffSize > 0)\n\t\t\tpBuf[0] = 0;\n\t\treturn;\n\t}\n\n\t// build type based part\n\tchar aBuf[256];\n\tif(Type == MSGTYPE_PLAYER)\n\t\tstr_copy(aBuf, \"You have been banned\", sizeof(aBuf));\n\telse\n\t{\n\t\tchar aTemp[256];\n\t\tswitch(Type)\n\t\t{\n\t\tcase MSGTYPE_LIST:\n\t\t\tstr_format(aBuf, sizeof(aBuf), \"%s banned\", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break;\n\t\tcase MSGTYPE_BANADD:\n\t\t\tstr_format(aBuf, sizeof(aBuf), \"banned %s\", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break;\n\t\tcase MSGTYPE_BANREM:\n\t\t\tstr_format(aBuf, sizeof(aBuf), \"unbanned %s\", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break;\n\t\tdefault:\n\t\t\taBuf[0] = 0;\n\t\t}\n\t}\n\n\t// add info part\n\tif(pBan->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER)\n\t{\n\t\tint Mins = ((pBan->m_Info.m_Expires-time_timestamp()) + 59) / 60;\n\t\tif(Mins <= 1)\n\t\t\tstr_format(pBuf, BuffSize, \"%s for 1 minute (%s)\", aBuf, pBan->m_Info.m_aReason);\n\t\telse\n\t\t\tstr_format(pBuf, BuffSize, \"%s for %d minutes (%s)\", aBuf, Mins, pBan->m_Info.m_aReason);\n\t}\n\telse\n\t\tstr_format(pBuf, BuffSize, \"%s for life (%s)\", aBuf, pBan->m_Info.m_aReason);\n}\n\ntemplate<class T>\nint CNetBan::Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason)\n{\n\t// do not ban localhost\n\tif(NetMatch(pData, &m_LocalhostIPV4) || NetMatch(pData, &m_LocalhostIPV6))\n\t{\n\t\tdbg_msg(\"net_ban\", \"ban failed (localhost)\");\n\t\treturn -1;\n\t}\n\n\tint Stamp = Seconds > 0 ? time_timestamp()+Seconds : CBanInfo::EXPIRES_NEVER;\n\n\t// set up info\n\tCBanInfo Info = {0};\n\tInfo.m_Expires = Stamp;\n\tstr_copy(Info.m_aReason, pReason, sizeof(Info.m_aReason));\n\n\t// check if it already exists\n\tCNetHash NetHash(pData);\n\tCBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash);\n\tif(pBan)\n\t{\n\t\t// adjust the ban\n\t\tpBanPool->Update(pBan, &Info);\n\t\tchar aBuf[128];\n\t\tMakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST);\n\t\tdbg_msg(\"net_ban\", aBuf);\n\t\treturn 1;\n\t}\n\n\t// add ban and print result\n\tpBan = pBanPool->Add(pData, &Info, &NetHash);\n\tif(pBan)\n\t{\n\t\tchar aBuf[128];\n\t\tMakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANADD);\n\t\tdbg_msg(\"net_ban\", aBuf);\n\t\treturn 0;\n\t}\n\telse\n\t\tdbg_msg(\"net_ban\", \"ban failed (full banlist)\");\n\treturn -1;\n}\n\ntemplate<class T>\nint CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData)\n{\n\tCNetHash NetHash(pData);\n\tCBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash);\n\tif(pBan)\n\t{\n\t\tchar aBuf[256];\n\t\tMakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANREM);\n\t\tpBanPool->Remove(pBan);\n\t\tdbg_msg(\"net_ban\", aBuf);\n\t\treturn 0;\n\t}\n\telse\n\t\tdbg_msg(\"net_ban\", \"unban failed (invalid entry)\");\n\treturn -1;\n}\n\nvoid CNetBan::Init()\n{\n\tm_BanAddrPool.Reset();\n\tm_BanRangePool.Reset();\n\n\tnet_host_lookup(\"localhost\", &m_LocalhostIPV4, NETTYPE_IPV4);\n\tnet_host_lookup(\"localhost\", &m_LocalhostIPV6, NETTYPE_IPV6);\n}\n\nvoid CNetBan::Update()\n{\n\tint Now = time_timestamp();\n\n\t// remove expired bans\n\tchar aBuf[256], aNetStr[256];\n\twhile(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now)\n\t{\n\t\tstr_format(aBuf, sizeof(aBuf), \"ban %s expired\", NetToString(&m_BanAddrPool.First()->m_Data, aNetStr, sizeof(aNetStr)));\n\t\tdbg_msg(\"net_ban\", aBuf);\n\t\tm_BanAddrPool.Remove(m_BanAddrPool.First());\n\t}\n\twhile(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now)\n\t{\n\t\tstr_format(aBuf, sizeof(aBuf), \"ban %s expired\", NetToString(&m_BanRangePool.First()->m_Data, aNetStr, sizeof(aNetStr)));\n\t\tdbg_msg(\"net_ban\", aBuf);\n\t\tm_BanRangePool.Remove(m_BanRangePool.First());\n\t}\n}\n\nint CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason)\n{\n\treturn Ban(&m_BanAddrPool, pAddr, Seconds, pReason);\n}\n\nint CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason)\n{\n\tif(pRange->IsValid())\n\t\treturn Ban(&m_BanRangePool, pRange, Seconds, pReason);\n\n\tdbg_msg(\"net_ban\", \"ban failed (invalid range)\");\n\treturn -1;\n}\n\nint CNetBan::UnbanByAddr(const NETADDR *pAddr)\n{\n\treturn Unban(&m_BanAddrPool, pAddr);\n}\n\nint CNetBan::UnbanByRange(const CNetRange *pRange)\n{\n\tif(pRange->IsValid())\n\t\treturn Unban(&m_BanRangePool, pRange);\n\n\tdbg_msg(\"net_ban\", \"ban failed (invalid range)\");\n\treturn -1;\n}\n\nint CNetBan::UnbanByIndex(int Index)\n{\n\tint Result;\n\tchar aBuf[256];\n\tCBanAddr *pBan = m_BanAddrPool.Get(Index);\n\tif(pBan)\n\t{\n\t\tNetToString(&pBan->m_Data, aBuf, sizeof(aBuf));\n\t\tResult = m_BanAddrPool.Remove(pBan);\n\t}\n\telse\n\t{\n\t\tCBanRange *pBan = m_BanRangePool.Get(Index-m_BanAddrPool.Num());\n\t\tif(pBan)\n\t\t{\n\t\t\tNetToString(&pBan->m_Data, aBuf, sizeof(aBuf));\n\t\t\tResult = m_BanRangePool.Remove(pBan);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdbg_msg(\"net_ban\", \"unban failed (invalid index)\");\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tchar aMsg[256];\n\tstr_format(aMsg, sizeof(aMsg), \"unbanned index %i (%s)\", Index, aBuf);\n\tdbg_msg(\"net_ban\", aMsg);\n\treturn Result;\n}\n\nvoid CNetBan::UnbanAll()\n{\n\tm_BanAddrPool.Reset();\n\tm_BanRangePool.Reset();\n}\n\nbool CNetBan::IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const\n{\n\tCNetHash aHash[17];\n\tint Length = CNetHash::MakeHashArray(pAddr, aHash);\n\n\t// check ban adresses\n\tCBanAddr *pBan = m_BanAddrPool.Find(pAddr, &aHash[Length]);\n\tif(pBan)\n\t{\n\t\tMakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER);\n\t\treturn true;\n\t}\n\n\t// check ban ranges\n\tfor(int i = Length-1; i >= 0; --i)\n\t{\n\t\tfor(CBanRange *pBan = m_BanRangePool.First(&aHash[i]); pBan; pBan = pBan->m_pHashNext)\n\t\t{\n\t\t\tif(NetMatch(&pBan->m_Data, pAddr, i, Length))\n\t\t\t{\n\t\t\t\tMakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn false;\n}\n"
  },
  {
    "path": "server/src/netban.h",
    "content": "#ifndef NETBAN_H\n#define NETBAN_H\n\n#include <system.h>\n\ninline int NetComp(const NETADDR *pAddr1, const NETADDR *pAddr2)\n{\n\treturn mem_comp(pAddr1, pAddr2, pAddr1->type==NETTYPE_IPV4 ? 8 : 20);\n}\n\nclass CNetRange\n{\npublic:\n\tNETADDR m_LB;\n\tNETADDR m_UB;\n\n\tbool IsValid() const { return m_LB.type == m_UB.type && NetComp(&m_LB, &m_UB) < 0; }\n};\n\ninline int NetComp(const CNetRange *pRange1, const CNetRange *pRange2)\n{\n\treturn NetComp(&pRange1->m_LB, &pRange2->m_LB) || NetComp(&pRange1->m_UB, &pRange2->m_UB);\n}\n\n\nclass CNetBan\n{\nprotected:\n\tbool NetMatch(const NETADDR *pAddr1, const NETADDR *pAddr2) const\n\t{\n\t\treturn NetComp(pAddr1, pAddr2) == 0;\n\t}\n\n\tbool NetMatch(const CNetRange *pRange, const NETADDR *pAddr, int Start, int Length) const\n\t{\n\t\treturn pRange->m_LB.type == pAddr->type && (Start == 0 || mem_comp(&pRange->m_LB.ip[0], &pAddr->ip[0], Start) == 0) &&\n\t\t\tmem_comp(&pRange->m_LB.ip[Start], &pAddr->ip[Start], Length-Start) <= 0 && mem_comp(&pRange->m_UB.ip[Start], &pAddr->ip[Start], Length-Start) >= 0;\n\t}\n\n\tbool NetMatch(const CNetRange *pRange, const NETADDR *pAddr) const\n\t{\n\t\treturn NetMatch(pRange, pAddr, 0,  pRange->m_LB.type==NETTYPE_IPV4 ? 4 : 16);\n\t}\n\n\tconst char *NetToString(const NETADDR *pData, char *pBuffer, unsigned BufferSize) const\n\t{\n\t\tchar aAddrStr[NETADDR_MAXSTRSIZE];\n\t\tnet_addr_str(pData, aAddrStr, sizeof(aAddrStr), false);\n\t\tstr_format(pBuffer, BufferSize, \"'%s'\", aAddrStr);\n\t\treturn pBuffer;\n\t}\n\n\tconst char *NetToString(const CNetRange *pData, char *pBuffer, unsigned BufferSize) const\n\t{\n\t\tchar aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE];\n\t\tnet_addr_str(&pData->m_LB, aAddrStr1, sizeof(aAddrStr1), false);\n\t\tnet_addr_str(&pData->m_UB, aAddrStr2, sizeof(aAddrStr2), false);\n\t\tstr_format(pBuffer, BufferSize, \"'%s' - '%s'\", aAddrStr1, aAddrStr2);\n\t\treturn pBuffer;\n\t}\n\n\t// todo: move?\n\tstatic bool StrAllnum(const char *pStr);\n\n\tclass CNetHash\n\t{\n\tpublic:\n\t\tint m_Hash;\n\t\tint m_HashIndex;\t// matching parts for ranges, 0 for addr\n\n\t\tCNetHash() {}\t\n\t\tCNetHash(const NETADDR *pAddr);\n\t\tCNetHash(const CNetRange *pRange);\n\n\t\tstatic int MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]);\n\t};\n\n\tstruct CBanInfo\n\t{\n\t\tenum\n\t\t{\n\t\t\tEXPIRES_NEVER=-1,\n\t\t\tREASON_LENGTH=64,\n\t\t};\n\t\tint m_Expires;\n\t\tchar m_aReason[REASON_LENGTH];\n\t};\n\n\ttemplate<class T> struct CBan\n\t{\n\t\tT m_Data;\n\t\tCBanInfo m_Info;\n\t\tCNetHash m_NetHash;\n\n\t\t// hash list\n\t\tCBan *m_pHashNext;\n\t\tCBan *m_pHashPrev;\n\n\t\t// used or free list\n\t\tCBan *m_pNext;\n\t\tCBan *m_pPrev;\n\t};\n\n\ttemplate<class T, int HashCount> class CBanPool\n\t{\n\tpublic:\n\t\ttypedef T CDataType;\n\n\t\tCBan<CDataType> *Add(const CDataType *pData, const CBanInfo *pInfo, const CNetHash *pNetHash);\n\t\tint Remove(CBan<CDataType> *pBan);\n\t\tvoid Update(CBan<CDataType> *pBan, const CBanInfo *pInfo);\n\t\tvoid Reset();\n\t\n\t\tint Num() const { return m_CountUsed; }\n\t\tbool IsFull() const { return m_CountUsed == MAX_BANS; }\n\n\t\tCBan<CDataType> *First() const { return m_pFirstUsed; }\n\t\tCBan<CDataType> *First(const CNetHash *pNetHash) const { return m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; }\n\t\tCBan<CDataType> *Find(const CDataType *pData, const CNetHash *pNetHash) const\n\t\t{\n\t\t\tfor(CBan<CDataType> *pBan = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; pBan; pBan = pBan->m_pHashNext)\n\t\t\t{\n\t\t\t\tif(NetComp(&pBan->m_Data, pData) == 0)\n\t\t\t\t\treturn pBan;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t}\n\t\tCBan<CDataType> *Get(int Index) const;\n\n\tprivate:\n\t\tenum\n\t\t{\n\t\t\tMAX_BANS=1024,\n\t\t};\n\n\t\tCBan<CDataType> *m_paaHashList[HashCount][256];\n\t\tCBan<CDataType> m_aBans[MAX_BANS];\n\t\tCBan<CDataType> *m_pFirstFree;\n\t\tCBan<CDataType> *m_pFirstUsed;\n\t\tint m_CountUsed;\n\t};\n\n\ttypedef CBanPool<NETADDR, 1> CBanAddrPool;\n\ttypedef CBanPool<CNetRange, 16> CBanRangePool;\n\ttypedef CBan<NETADDR> CBanAddr;\n\ttypedef CBan<CNetRange> CBanRange;\n\t\n\ttemplate<class T> void MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const;\n\ttemplate<class T> int Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason);\n\ttemplate<class T> int Unban(T *pBanPool, const typename T::CDataType *pData);\n\n\tCBanAddrPool m_BanAddrPool;\n\tCBanRangePool m_BanRangePool;\n\tNETADDR m_LocalhostIPV4, m_LocalhostIPV6;\n\npublic:\n\tenum\n\t{\n\t\tMSGTYPE_PLAYER=0,\n\t\tMSGTYPE_LIST,\n\t\tMSGTYPE_BANADD,\n\t\tMSGTYPE_BANREM,\n\t};\n\n\tvirtual ~CNetBan() {}\n\tvoid Init();\n\tvoid Update();\n\n\tvirtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason);\n\tvirtual int BanRange(const CNetRange *pRange, int Seconds, const char *pReason);\n\tint UnbanByAddr(const NETADDR *pAddr);\n\tint UnbanByRange(const CNetRange *pRange);\n\tint UnbanByIndex(int Index);\n\tvoid UnbanAll();\n\tbool IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const;\n};\n\n#endif\n"
  },
  {
    "path": "server/src/network.cpp",
    "content": "#include <system.h>\n#include \"netban.h\"\n#include \"network.h\"\n\nbool CNetwork::Open(NETADDR BindAddr, CNetBan *pNetBan)\n{\n\t// zero out the whole structure\n\tmem_zero(this, sizeof(*this));\n\tm_Socket.type = NETTYPE_INVALID;\n\tm_Socket.ipv4sock = -1;\n\tm_Socket.ipv6sock = -1;\n\tm_pNetBan = pNetBan;\n\n\t// open socket\n\tm_Socket = net_tcp_create(BindAddr);\n\tif(!m_Socket.type)\n\t\treturn false;\n\tif(net_tcp_listen(m_Socket, NET_MAX_CLIENTS))\n\t\treturn false;\n\tnet_set_non_blocking(m_Socket);\n\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\tm_aSlots[i].m_Connection.Reset();\n\n\treturn true;\n}\n\nvoid CNetwork::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)\n{\n\tm_pfnNewClient = pfnNewClient;\n\tm_pfnDelClient = pfnDelClient;\n\tm_UserPtr = pUser;\n}\n\nint CNetwork::Close()\n{\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\tm_aSlots[i].m_Connection.Disconnect(\"Closing connection.\");\n\n\tnet_tcp_close(m_Socket);\n\n\treturn 0;\n}\n\nint CNetwork::Drop(int ClientID, const char *pReason)\n{\n\tif(m_pfnDelClient)\n\t\tm_pfnDelClient(ClientID, pReason, m_UserPtr);\n\n\tm_aSlots[ClientID].m_Connection.Disconnect(pReason);\n\n\treturn 0;\n}\n\nint CNetwork::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr)\n{\n\tchar aError[256] = { 0 };\n\tint FreeSlot = -1;\n\n\t// look for free slot or multiple client\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t{\n\t\tif(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)\n\t\t\tFreeSlot = i;\n\t\tif(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE)\n\t\t{\n\t\t\tif(net_addr_comp(pAddr, m_aSlots[i].m_Connection.PeerAddress()) == 0)\n\t\t\t{\n\t\t\t\tstr_copy(aError, \"Only one client per IP allowed.\", sizeof(aError));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// accept client\n\tif(!aError[0] && FreeSlot != -1)\n\t{\n\t\tm_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr);\n\t\tif(m_pfnNewClient)\n\t\t\tm_pfnNewClient(FreeSlot, m_UserPtr);\n\t\treturn 0;\n\t}\n\n\t// reject client\n\tif(!aError[0])\n\t\tstr_copy(aError, \"No free slot available.\", sizeof(aError));\n\n\tnet_tcp_send(Socket, aError, str_length(aError));\n\tnet_tcp_close(Socket);\n\n\treturn -1;\n}\n\nint CNetwork::Update()\n{\n\tNETSOCKET Socket;\n\tNETADDR Addr;\n\n\tif(net_tcp_accept(m_Socket, &Socket, &Addr) > 0)\n\t{\n\t\t// check if we should just drop the packet\n\t\tchar aBuf[128];\n\t\tif(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf)))\n\t\t{\n\t\t\t// banned, reply with a message and drop\n\t\t\tnet_tcp_send(Socket, aBuf, str_length(aBuf));\n\t\t\tnet_tcp_close(Socket);\n\t\t}\n\t\telse\n\t\t\tAcceptClient(Socket, &Addr);\n\t}\n\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t{\n\t\tif(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE)\n\t\t\tm_aSlots[i].m_Connection.Update();\n\t\tif(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)\n\t\t\tDrop(i, m_aSlots[i].m_Connection.ErrorString());\n\t}\n\n\treturn 0;\n}\n\nint CNetwork::Recv(char *pLine, int MaxLength, int *pClientID)\n{\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t{\n\t\tif(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength))\n\t\t{\n\t\t\tif(pClientID)\n\t\t\t\t*pClientID = i;\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n}\n\nint CNetwork::Send(int ClientID, const char *pLine)\n{\n\tif(m_aSlots[ClientID].m_Connection.State() == NET_CONNSTATE_ONLINE)\n\t\treturn m_aSlots[ClientID].m_Connection.Send(pLine);\n\telse\n\t\treturn -1;\n}\n"
  },
  {
    "path": "server/src/network.h",
    "content": "#ifndef NETWORK_H\n#define NETWORK_H\n\nenum\n{\n\tNET_CONNSTATE_OFFLINE=0,\n\tNET_CONNSTATE_CONNECT=1,\n\tNET_CONNSTATE_PENDING=2,\n\tNET_CONNSTATE_ONLINE=3,\n\tNET_CONNSTATE_ERROR=4,\n\n\tNET_MAX_PACKETSIZE = 1400,\n\tNET_MAX_CLIENTS = 64\n};\n\ntypedef int (*NETFUNC_DELCLIENT)(int ClientID, const char* pReason, void *pUser);\ntypedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);\n\nclass CNetworkClient\n{\nprivate:\n\tint m_State;\n\n\tNETADDR m_PeerAddr;\n\tNETSOCKET m_Socket;\n\n\tchar m_aBuffer[NET_MAX_PACKETSIZE];\n\tint m_BufferOffset;\n\n\tchar m_aErrorString[256];\n\n\tbool m_LineEndingDetected;\n\tchar m_aLineEnding[3];\n\npublic:\n\tvoid Init(NETSOCKET Socket, const NETADDR *pAddr);\n\tvoid Disconnect(const char *pReason);\n\n\tint State() const { return m_State; }\n\tconst NETADDR *PeerAddress() const { return &m_PeerAddr; }\n\tconst char *ErrorString() const { return m_aErrorString; }\n\n\tvoid Reset();\n\tint Update();\n\tint Send(const char *pLine);\n\tint Recv(char *pLine, int MaxLength);\n};\n\nclass CNetwork\n{\nprivate:\n\tstruct CSlot\n\t{\n\t\tCNetworkClient m_Connection;\n\t};\n\n\tNETSOCKET m_Socket;\n\tclass CNetBan *m_pNetBan;\n\tCSlot m_aSlots[NET_MAX_CLIENTS];\n\n\tNETFUNC_NEWCLIENT m_pfnNewClient;\n\tNETFUNC_DELCLIENT m_pfnDelClient;\n\tvoid *m_UserPtr;\n\npublic:\n\tvoid SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);\n\n\t//\n\tbool Open(NETADDR BindAddr, CNetBan *pNetBan);\n\tint Close();\n\n\t//\n\tint Recv(char *pLine, int MaxLength, int *pClientID = 0);\n\tint Send(int ClientID, const char *pLine);\n\tint Update();\n\n\t//\n\tint AcceptClient(NETSOCKET Socket, const NETADDR *pAddr);\n\tint Drop(int ClientID, const char *pReason);\n\n\t// status requests\n\tconst NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }\n\tconst NETSOCKET *Socket() const { return &m_Socket; }\n\tclass CNetBan *NetBan() const { return m_pNetBan; }\n};\n\n#endif\n"
  },
  {
    "path": "server/src/network_client.cpp",
    "content": "#include <system.h>\n#include \"network.h\"\n\nvoid CNetworkClient::Reset()\n{\n\tm_State = NET_CONNSTATE_OFFLINE;\n\tmem_zero(&m_PeerAddr, sizeof(m_PeerAddr));\n\tm_aErrorString[0] = 0;\n\n\tm_Socket.type = NETTYPE_INVALID;\n\tm_Socket.ipv4sock = -1;\n\tm_Socket.ipv6sock = -1;\n\tm_aBuffer[0] = 0;\n\tm_BufferOffset = 0;\n\n\tm_LineEndingDetected = false;\n\t#if defined(CONF_FAMILY_WINDOWS)\n\t\tm_aLineEnding[0] = '\\r';\n\t\tm_aLineEnding[1] = '\\n';\n\t\tm_aLineEnding[2] = 0;\n\t#else\n\t\tm_aLineEnding[0] = '\\n';\n\t\tm_aLineEnding[1] = 0;\n\t\tm_aLineEnding[2] = 0;\n\t#endif\n}\n\nvoid CNetworkClient::Init(NETSOCKET Socket, const NETADDR *pAddr)\n{\n\tReset();\n\n\tm_Socket = Socket;\n\tnet_set_non_blocking(m_Socket);\n\n\tm_PeerAddr = *pAddr;\n\tm_State = NET_CONNSTATE_ONLINE;\n}\n\nvoid CNetworkClient::Disconnect(const char *pReason)\n{\n\tif(State() == NET_CONNSTATE_OFFLINE)\n\t\treturn;\n\n\tif(pReason && pReason[0])\n\t\tSend(pReason);\n\n\tnet_tcp_close(m_Socket);\n\n\tReset();\n}\n\nint CNetworkClient::Update()\n{\n\tif(State() == NET_CONNSTATE_ONLINE)\n\t{\n\t\tif((int)(sizeof(m_aBuffer)) <= m_BufferOffset)\n\t\t{\n\t\t\tm_State = NET_CONNSTATE_ERROR;\n\t\t\tstr_copy(m_aErrorString, \"too weak connection (out of buffer)\", sizeof(m_aErrorString));\n\t\t\treturn -1;\n\t\t}\n\n\t\tint Bytes = net_tcp_recv(m_Socket, m_aBuffer+m_BufferOffset, (int)(sizeof(m_aBuffer))-m_BufferOffset);\n\n\t\tif(Bytes > 0)\n\t\t{\n\t\t\tm_BufferOffset += Bytes;\n\t\t}\n\t\telse if(Bytes < 0)\n\t\t{\n\t\t\tif(net_would_block()) // no data received\n\t\t\t\treturn 0;\n\n\t\t\tm_State = NET_CONNSTATE_ERROR; // error\n\t\t\tstr_copy(m_aErrorString, \"connection failure\", sizeof(m_aErrorString));\n\t\t\treturn -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_State = NET_CONNSTATE_ERROR;\n\t\t\tstr_copy(m_aErrorString, \"remote end closed the connection\", sizeof(m_aErrorString));\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint CNetworkClient::Recv(char *pLine, int MaxLength)\n{\n\tif(State() == NET_CONNSTATE_ONLINE)\n\t{\n\t\tif(m_BufferOffset)\n\t\t{\n\t\t\t// find message start\n\t\t\tint StartOffset = 0;\n\t\t\twhile(m_aBuffer[StartOffset] == '\\r' || m_aBuffer[StartOffset] == '\\n')\n\t\t\t{\n\t\t\t\t// detect clients line ending format\n\t\t\t\tif(!m_LineEndingDetected)\n\t\t\t\t{\n\t\t\t\t\tm_aLineEnding[0] = m_aBuffer[StartOffset];\n\t\t\t\t\tif(StartOffset+1 < m_BufferOffset && (m_aBuffer[StartOffset+1] == '\\r' || m_aBuffer[StartOffset+1] == '\\n') &&\n\t\t\t\t\t\tm_aBuffer[StartOffset] != m_aBuffer[StartOffset+1])\n\t\t\t\t\t\tm_aLineEnding[1] = m_aBuffer[StartOffset+1];\n\t\t\t\t\tm_LineEndingDetected = true;\n\t\t\t\t}\n\n\t\t\t\tif(++StartOffset >= m_BufferOffset)\n\t\t\t\t{\n\t\t\t\t\tm_BufferOffset = 0;\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// find message end\n\t\t\tint EndOffset = StartOffset;\n\t\t\twhile(m_aBuffer[EndOffset] != '\\r' && m_aBuffer[EndOffset] != '\\n')\n\t\t\t{\n\t\t\t\tif(++EndOffset >= m_BufferOffset)\n\t\t\t\t{\n\t\t\t\t\tif(StartOffset > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tmem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset);\n\t\t\t\t\t\tm_BufferOffset -= StartOffset;\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// extract message and update buffer\n\t\t\tif(MaxLength-1 < EndOffset-StartOffset)\n\t\t\t{\n\t\t\t\tif(StartOffset > 0)\n\t\t\t\t{\n\t\t\t\t\tmem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset);\n\t\t\t\t\tm_BufferOffset -= StartOffset;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tmem_copy(pLine, m_aBuffer+StartOffset, EndOffset-StartOffset);\n\t\t\tpLine[EndOffset-StartOffset] = 0;\n\t\t\tstr_sanitize_cc(pLine);\n\t\t\tmem_move(m_aBuffer, m_aBuffer+EndOffset, m_BufferOffset-EndOffset);\n\t\t\tm_BufferOffset -= EndOffset;\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n}\n\nint CNetworkClient::Send(const char *pLine)\n{\n\tif(State() != NET_CONNSTATE_ONLINE)\n\t\treturn -1;\n\n\tchar aBuf[1024];\n\tstr_copy(aBuf, pLine, (int)(sizeof(aBuf))-2);\n\tint Length = str_length(aBuf);\n\taBuf[Length] = m_aLineEnding[0];\n\taBuf[Length+1] = m_aLineEnding[1];\n\taBuf[Length+2] = m_aLineEnding[2];\n\tLength += 3;\n\tconst char *pData = aBuf;\n\n\twhile(1)\n\t{\n\t\tint Send = net_tcp_send(m_Socket, pData, Length);\n\t\tif(Send < 0)\n\t\t{\n\t\t\tm_State = NET_CONNSTATE_ERROR;\n\t\t\tstr_copy(m_aErrorString, \"failed to send packet\", sizeof(m_aErrorString));\n\t\t\treturn -1;\n\t\t}\n\n\t\tif(Send >= Length)\n\t\t\tbreak;\n\n\t\tpData += Send;\n\t\tLength -= Send;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "server/src/server.cpp",
    "content": "#include <system.h>\n#include \"netban.h\"\n#include \"network.h\"\n#include \"main.h\"\n#include \"server.h\"\n\nint CServer::NewClientCallback(int ClientID, void *pUser)\n{\n\tCServer *pThis = (CServer *)pUser;\n\n\tchar aAddrStr[NETADDR_MAXSTRSIZE];\n\tnet_addr_str(pThis->m_Network.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);\n\tif(pThis->Main()->Config()->m_Verbose)\n\t\tdbg_msg(\"server\", \"Connection accepted. ncid=%d addr=%s'\", ClientID, aAddrStr);\n\n\tpThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED;\n\tpThis->m_aClients[ClientID].m_TimeConnected = time_get();\n\tpThis->m_Network.Send(ClientID, \"Authentication required:\");\n\n\treturn 0;\n}\n\nint CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)\n{\n\tCServer *pThis = (CServer *)pUser;\n\n\tchar aAddrStr[NETADDR_MAXSTRSIZE];\n\tnet_addr_str(pThis->m_Network.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true);\n\tif(pThis->Main()->Config()->m_Verbose)\n\t\tdbg_msg(\"server\", \"Client dropped. ncid=%d addr=%s reason='%s'\", ClientID, aAddrStr, pReason);\n\n\tif(pThis->m_aClients[ClientID].m_State == CClient::STATE_AUTHED)\n\t\tpThis->Main()->OnDelClient(ClientID);\n\tpThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY;\n\n\treturn 0;\n}\n\nint CServer::Init(CMain *pMain, const char *Bind, int Port)\n{\n\tm_pMain = pMain;\n\tm_NetBan.Init();\n\n\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\tm_aClients[i].m_State = CClient::STATE_EMPTY;\n\n\tm_Ready = false;\n\n\tif(Port == 0)\n\t{\n\t\tdbg_msg(\"server\", \"Will not bind to port 0.\");\n\t\treturn 1;\n\t}\n\n\tNETADDR BindAddr;\n\tif(Bind[0] && net_host_lookup(Bind, &BindAddr, NETTYPE_ALL) == 0)\n\t{\n\t\t// got bindaddr\n\t\tBindAddr.type = NETTYPE_ALL;\n\t\tBindAddr.port = Port;\n\t}\n\telse\n\t{\n\t\tmem_zero(&BindAddr, sizeof(BindAddr));\n\t\tBindAddr.type = NETTYPE_ALL;\n\t\tBindAddr.port = Port;\n\t}\n\n\tif(m_Network.Open(BindAddr, &m_NetBan))\n\t{\n\t\tm_Network.SetCallbacks(NewClientCallback, DelClientCallback, this);\n\t\tm_Ready = true;\n\t\tdbg_msg(\"server\", \"Bound to %s:%d\", Bind, Port);\n\t\treturn 0;\n\t}\n\telse\n\t\tdbg_msg(\"server\", \"Couldn't open socket. Port (%d) might already be in use.\", Port);\n\n\treturn 1;\n}\n\nvoid CServer::Update()\n{\n\tif(!m_Ready)\n\t\treturn;\n\n\tm_NetBan.Update();\n\tm_Network.Update();\n\n\tchar aBuf[NET_MAX_PACKETSIZE];\n\tint ClientID;\n\n\twhile(m_Network.Recv(aBuf, (int)(sizeof(aBuf))-1, &ClientID))\n\t{\n\t\tdbg_assert(m_aClients[ClientID].m_State != CClient::STATE_EMPTY, \"Got message from empty slot.\");\n\t\tif(m_aClients[ClientID].m_State == CClient::STATE_CONNECTED)\n\t\t{\n\t\t\tint ID = -1;\n\t\t\tchar aUsername[128] = {0};\n\t\t\tchar aPassword[128] = {0};\n\t\t\tconst char *pTmp;\n\n\t\t\tif(!(pTmp = str_find(aBuf, \":\"))\n\t\t\t\t|| (unsigned)(pTmp - aBuf) > sizeof(aUsername) || (unsigned)(str_length(pTmp) - 1) > sizeof(aPassword))\n\t\t\t{\n\t\t\t\tm_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, \"You're an idiot, go away.\");\n\t\t\t\tm_Network.Drop(ClientID, \"Fuck off.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstr_copy(aUsername, aBuf, pTmp - aBuf + 1);\n\t\t\tstr_copy(aPassword, pTmp + 1, sizeof(aPassword));\n\t\t\tif(!*aUsername || !*aPassword)\n\t\t\t{\n\t\t\t\tm_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, \"You're an idiot, go away.\");\n\t\t\t\tm_Network.Drop(ClientID, \"Username and password must not be blank.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\t\t{\n\t\t\t\tif(!Main()->Client(i)->m_Active)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif(str_comp(Main()->Client(i)->m_aUsername, aUsername) == 0 && str_comp(Main()->Client(i)->m_aPassword, aPassword) == 0)\n\t\t\t\t\tID = i;\n\t\t\t}\n\n\t\t\tif(ID == -1)\n\t\t\t{\n\t\t\t\tm_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, \"Wrong username and/or password.\");\n\t\t\t\tm_Network.Drop(ClientID, \"Wrong username and/or password.\");\n\t\t\t}\n\t\t\telse if(Main()->Client(ID)->m_ClientNetID != -1)\n\t\t\t{\n\t\t\t\tm_Network.Drop(ClientID, \"Only one connection per user allowed.\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_aClients[ClientID].m_State = CClient::STATE_AUTHED;\n\t\t\t\tm_aClients[ClientID].m_LastReceived = time_get();\n\t\t\t\tm_Network.Send(ClientID, \"Authentication successful. Access granted.\");\n\n\t\t\t\tif(m_Network.ClientAddr(ClientID)->type == NETTYPE_IPV4)\n\t\t\t\t\tm_Network.Send(ClientID, \"You are connecting via: IPv4\");\n\t\t\t\telse if(m_Network.ClientAddr(ClientID)->type == NETTYPE_IPV6)\n\t\t\t\t\tm_Network.Send(ClientID, \"You are connecting via: IPv6\");\n\n\t\t\t\tif(Main()->Config()->m_Verbose)\n\t\t\t\t\tdbg_msg(\"server\", \"ncid=%d authed\", ClientID);\n\t\t\t\tMain()->OnNewClient(ClientID, ID);\n\t\t\t}\n\t\t}\n\t\telse if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED)\n\t\t{\n\t\t\tm_aClients[ClientID].m_LastReceived = time_get();\n\t\t\tif(Main()->Config()->m_Verbose)\n\t\t\t\tdbg_msg(\"server\", \"ncid=%d cmd='%s'\", ClientID, aBuf);\n\n\t\t\tif(str_comp(aBuf, \"logout\") == 0)\n\t\t\t\tm_Network.Drop(ClientID, \"Logout. Bye Bye ~\");\n\t\t\telse\n\t\t\t\tMain()->HandleMessage(ClientID, aBuf);\n\t\t}\n\t}\n\n\tfor(int i = 0; i < NET_MAX_CLIENTS; ++i)\n\t{\n\t\tif(m_aClients[i].m_State == CClient::STATE_CONNECTED &&\n\t\t\ttime_get() > m_aClients[i].m_TimeConnected + 5 * time_freq())\n\t\t{\n\t\t\tm_Network.NetBan()->BanAddr(m_Network.ClientAddr(i), 30, \"Authentication timeout.\");\n\t\t\tm_Network.Drop(i, \"Authentication timeout.\");\n\t\t}\n\t\telse if(m_aClients[i].m_State == CClient::STATE_AUTHED &&\n\t\t\ttime_get() > m_aClients[i].m_LastReceived + 15 * time_freq())\n\t\t\tm_Network.Drop(i, \"Timeout.\");\n\t}\n}\n\nvoid CServer::Send(int ClientID, const char *pLine)\n{\n\tif(!m_Ready)\n\t\treturn;\n\n\tif(ClientID == -1)\n\t{\n\t\tfor(int i = 0; i < NET_MAX_CLIENTS; i++)\n\t\t{\n\t\t\tif(m_aClients[i].m_State == CClient::STATE_AUTHED)\n\t\t\t\tm_Network.Send(i, pLine);\n\t\t}\n\t}\n\telse if(ClientID >= 0 && ClientID < NET_MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_AUTHED)\n\t\tm_Network.Send(ClientID, pLine);\n}\n\nvoid CServer::Shutdown()\n{\n\tif(!m_Ready)\n\t\treturn;\n\n\tm_Network.Close();\n}\n"
  },
  {
    "path": "server/src/server.h",
    "content": "#ifndef SERVER_H\n#define SERVER_H\n\n#include \"netban.h\"\n#include \"network.h\"\n\nclass CServer\n{\n\tclass CClient\n\t{\n\tpublic:\n\t\tenum\n\t\t{\n\t\t\tSTATE_EMPTY=0,\n\t\t\tSTATE_CONNECTED,\n\t\t\tSTATE_AUTHED,\n\t\t};\n\n\t\tint m_State;\n\t\tint64 m_TimeConnected;\n\t\tint64 m_LastReceived;\n\t};\n\tCClient m_aClients[NET_MAX_CLIENTS];\n\n\tCNetwork m_Network;\n\tCNetBan m_NetBan;\n\n\tclass CMain *m_pMain;\n\n\tbool m_Ready;\n\n\tstatic int NewClientCallback(int ClientID, void *pUser);\n\tstatic int DelClientCallback(int ClientID, const char *pReason, void *pUser);\n\npublic:\n\tint Init(CMain *pMain, const char *Bind, int Port);\n\tvoid Update();\n\tvoid Send(int ClientID, const char *pLine);\n\tvoid Shutdown();\n\n\tCNetwork *Network() { return &m_Network; }\n\tCNetBan *NetBan() { return &m_NetBan; }\n\tCMain *Main() { return m_pMain; }\n};\n\n#endif\n"
  },
  {
    "path": "server/src/system.c",
    "content": "/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */\n/* If you are missing that file, acquire a complete release at teeworlds.com.                */\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <ctype.h>\n#include <time.h>\n\n#include \"system.h\"\n\n#if defined(CONF_FAMILY_UNIX)\n\t#include <sys/time.h>\n\t#include <unistd.h>\n\n\t/* unix net includes */\n\t#include <sys/stat.h>\n\t#include <sys/types.h>\n\t#include <sys/socket.h>\n\t#include <sys/ioctl.h>\n\t#include <errno.h>\n\t#include <netdb.h>\n\t#include <netinet/in.h>\n\t#include <fcntl.h>\n\t#include <pthread.h>\n\t#include <arpa/inet.h>\n\n\t#include <dirent.h>\n\n\t#if defined(CONF_PLATFORM_MACOSX)\n\t\t#include <Carbon/Carbon.h>\n\t#endif\n\n#elif defined(CONF_FAMILY_WINDOWS)\n\t#define WIN32_LEAN_AND_MEAN\n\t#define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to work */\n\t#include <windows.h>\n\t#include <winsock2.h>\n\t#include <ws2tcpip.h>\n\t#include <fcntl.h>\n\t#include <direct.h>\n\t#include <errno.h>\n#else\n\t#error NOT IMPLEMENTED\n#endif\n\n#if defined(CONF_PLATFORM_SOLARIS)\n\t#include <sys/filio.h>\n#endif\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\nIOHANDLE io_stdin() { return (IOHANDLE)stdin; }\nIOHANDLE io_stdout() { return (IOHANDLE)stdout; }\nIOHANDLE io_stderr() { return (IOHANDLE)stderr; }\n\nstatic DBG_LOGGER loggers[16];\nstatic int num_loggers = 0;\n\nstatic NETSTATS network_stats = {0};\nstatic MEMSTATS memory_stats = {0};\n\nstatic NETSOCKET invalid_socket = {NETTYPE_INVALID, -1, -1};\n\nvoid dbg_logger(DBG_LOGGER logger)\n{\n\tloggers[num_loggers++] = logger;\n}\n\nvoid dbg_assert_imp(const char *filename, int line, int test, const char *msg)\n{\n\tif(!test)\n\t{\n\t\tdbg_msg(\"assert\", \"%s(%d): %s\", filename, line, msg);\n\t\tdbg_break();\n\t}\n}\n\nvoid dbg_break()\n{\n\t*((volatile unsigned*)0) = 0x0;\n}\n\nvoid dbg_msg(const char *sys, const char *fmt, ...)\n{\n\tva_list args;\n\tchar str[1024*8];\n\tchar *msg;\n\tint i, len;\n\n\tstr_format(str, sizeof(str), \"[%s]: \", sys);\n\tlen = strlen(str);\n\tmsg = (char *)str + len;\n\n\tva_start(args, fmt);\n#if defined(CONF_FAMILY_WINDOWS)\n\t_vsnprintf(msg, sizeof(str)-len, fmt, args);\n#else\n\tvsnprintf(msg, sizeof(str)-len, fmt, args);\n#endif\n\tva_end(args);\n\n\tfor(i = 0; i < num_loggers; i++)\n\t\tloggers[i](str);\n}\n\nstatic void logger_stdout(const char *line)\n{\n\tprintf(\"%s\\n\", line);\n\tfflush(stdout);\n}\n\nstatic void logger_debugger(const char *line)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tOutputDebugString(line);\n\tOutputDebugString(\"\\n\");\n#endif\n}\n\n\nstatic IOHANDLE logfile = 0;\nstatic void logger_file(const char *line)\n{\n\tio_write(logfile, line, strlen(line));\n\tio_write_newline(logfile);\n\tio_flush(logfile);\n}\n\nvoid dbg_logger_stdout() { dbg_logger(logger_stdout); }\nvoid dbg_logger_debugger() { dbg_logger(logger_debugger); }\nvoid dbg_logger_file(const char *filename)\n{\n\tlogfile = io_open(filename, IOFLAG_WRITE);\n\tif(logfile)\n\t\tdbg_logger(logger_file);\n\telse\n\t\tdbg_msg(\"dbg/logger\", \"failed to open '%s' for logging\", filename);\n\n}\n/* */\n\ntypedef struct MEMHEADER\n{\n\tconst char *filename;\n\tint line;\n\tint size;\n\tstruct MEMHEADER *prev;\n\tstruct MEMHEADER *next;\n} MEMHEADER;\n\ntypedef struct MEMTAIL\n{\n\tint guard;\n} MEMTAIL;\n\nstatic struct MEMHEADER *first = 0;\nstatic const int MEM_GUARD_VAL = 0xbaadc0de;\n\nvoid *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment)\n{\n\t/* TODO: fix alignment */\n\t/* TODO: add debugging */\n\tMEMTAIL *tail;\n\tMEMHEADER *header = (struct MEMHEADER *)malloc(size+sizeof(MEMHEADER)+sizeof(MEMTAIL));\n\tdbg_assert(header != 0, \"mem_alloc failure\");\n\tif(!header)\n\t\treturn NULL;\n\ttail = (struct MEMTAIL *)(((char*)(header+1))+size);\n\theader->size = size;\n\theader->filename = filename;\n\theader->line = line;\n\n\tmemory_stats.allocated += header->size;\n\tmemory_stats.total_allocations++;\n\tmemory_stats.active_allocations++;\n\n\ttail->guard = MEM_GUARD_VAL;\n\n\theader->prev = (MEMHEADER *)0;\n\theader->next = first;\n\tif(first)\n\t\tfirst->prev = header;\n\tfirst = header;\n\n\t/*dbg_msg(\"mem\", \"++ %p\", header+1); */\n\treturn header+1;\n}\n\nvoid mem_free(void *p)\n{\n\tif(p)\n\t{\n\t\tMEMHEADER *header = (MEMHEADER *)p - 1;\n\t\tMEMTAIL *tail = (MEMTAIL *)(((char*)(header+1))+header->size);\n\n\t\tif(tail->guard != MEM_GUARD_VAL)\n\t\t\tdbg_msg(\"mem\", \"!! %p\", p);\n\t\t/* dbg_msg(\"mem\", \"-- %p\", p); */\n\t\tmemory_stats.allocated -= header->size;\n\t\tmemory_stats.active_allocations--;\n\n\t\tif(header->prev)\n\t\t\theader->prev->next = header->next;\n\t\telse\n\t\t\tfirst = header->next;\n\t\tif(header->next)\n\t\t\theader->next->prev = header->prev;\n\n\t\tfree(header);\n\t}\n}\n\nvoid mem_debug_dump(IOHANDLE file)\n{\n\tchar buf[1024];\n\tMEMHEADER *header = first;\n\tif(!file)\n\t\tfile = io_open(\"memory.txt\", IOFLAG_WRITE);\n\n\tif(file)\n\t{\n\t\twhile(header)\n\t\t{\n\t\t\tstr_format(buf, sizeof(buf), \"%s(%d): %d\", header->filename, header->line, header->size);\n\t\t\tio_write(file, buf, strlen(buf));\n\t\t\tio_write_newline(file);\n\t\t\theader = header->next;\n\t\t}\n\n\t\tio_close(file);\n\t}\n}\n\n\nvoid mem_copy(void *dest, const void *source, unsigned size)\n{\n\tmemcpy(dest, source, size);\n}\n\nvoid mem_move(void *dest, const void *source, unsigned size)\n{\n\tmemmove(dest, source, size);\n}\n\nvoid mem_zero(void *block, unsigned size)\n{\n\tmemset(block, 0, size);\n}\n\nint mem_check_imp()\n{\n\tMEMHEADER *header = first;\n\twhile(header)\n\t{\n\t\tMEMTAIL *tail = (MEMTAIL *)(((char*)(header+1))+header->size);\n\t\tif(tail->guard != MEM_GUARD_VAL)\n\t\t{\n\t\t\tdbg_msg(\"mem\", \"Memory check failed at %s(%d): %d\", header->filename, header->line, header->size);\n\t\t\treturn 0;\n\t\t}\n\t\theader = header->next;\n\t}\n\n\treturn 1;\n}\n\nIOHANDLE io_open(const char *filename, int flags)\n{\n\tif(flags == IOFLAG_READ)\n\t{\n\t#if defined(CONF_FAMILY_WINDOWS)\n\t\t// check for filename case sensitive\n\t\tWIN32_FIND_DATA finddata;\n\t\tHANDLE handle;\n\t\tint length;\n\n\t\tlength = str_length(filename);\n\t\tif(!filename || !length || filename[length-1] == '\\\\')\n\t\t\treturn 0x0;\n\t\thandle = FindFirstFile(filename, &finddata);\n\t\tif(handle == INVALID_HANDLE_VALUE)\n\t\t\treturn 0x0;\n\t\telse if(str_comp(filename+length-str_length(finddata.cFileName), finddata.cFileName) != 0)\n\t\t{\n\t\t\tFindClose(handle);\n\t\t\treturn 0x0;\n\t\t}\n\t\tFindClose(handle);\n\t#endif\n\t\treturn (IOHANDLE)fopen(filename, \"rb\");\n\t}\n\tif(flags == IOFLAG_WRITE)\n\t\treturn (IOHANDLE)fopen(filename, \"wb\");\n\treturn 0x0;\n}\n\nunsigned io_read(IOHANDLE io, void *buffer, unsigned size)\n{\n\treturn fread(buffer, 1, size, (FILE*)io);\n}\n\nunsigned io_skip(IOHANDLE io, int size)\n{\n\tfseek((FILE*)io, size, SEEK_CUR);\n\treturn size;\n}\n\nint io_seek(IOHANDLE io, int offset, int origin)\n{\n\tint real_origin;\n\n\tswitch(origin)\n\t{\n\tcase IOSEEK_START:\n\t\treal_origin = SEEK_SET;\n\t\tbreak;\n\tcase IOSEEK_CUR:\n\t\treal_origin = SEEK_CUR;\n\t\tbreak;\n\tcase IOSEEK_END:\n\t\treal_origin = SEEK_END;\n\t\tbreak;\n\tdefault:\n\t\treturn -1;\n\t}\n\n\treturn fseek((FILE*)io, offset, real_origin);\n}\n\nlong int io_tell(IOHANDLE io)\n{\n\treturn ftell((FILE*)io);\n}\n\nlong int io_length(IOHANDLE io)\n{\n\tlong int length;\n\tio_seek(io, 0, IOSEEK_END);\n\tlength = io_tell(io);\n\tio_seek(io, 0, IOSEEK_START);\n\treturn length;\n}\n\nunsigned io_write(IOHANDLE io, const void *buffer, unsigned size)\n{\n\treturn fwrite(buffer, 1, size, (FILE*)io);\n}\n\nunsigned io_write_newline(IOHANDLE io)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\treturn fwrite(\"\\r\\n\", 1, 2, (FILE*)io);\n#else\n\treturn fwrite(\"\\n\", 1, 1, (FILE*)io);\n#endif\n}\n\nint io_close(IOHANDLE io)\n{\n\tfclose((FILE*)io);\n\treturn 1;\n}\n\nint io_flush(IOHANDLE io)\n{\n\tfflush((FILE*)io);\n\treturn 0;\n}\n\nvoid *thread_create(void (*threadfunc)(void *), void *u)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_t id;\n\tpthread_create(&id, NULL, (void *(*)(void*))threadfunc, u);\n\treturn (void*)id;\n#elif defined(CONF_FAMILY_WINDOWS)\n\treturn CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadfunc, u, 0, NULL);\n#else\n\t#error not implemented\n#endif\n}\n\nvoid thread_wait(void *thread)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_join((pthread_t)thread, NULL);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tWaitForSingleObject((HANDLE)thread, INFINITE);\n#else\n\t#error not implemented\n#endif\n}\n\nvoid thread_destroy(void *thread)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tvoid *r = 0;\n\tpthread_join((pthread_t)thread, &r);\n#else\n\t/*#error not implemented*/\n#endif\n}\n\nvoid thread_yield()\n{\n#if defined(CONF_FAMILY_UNIX)\n\tsched_yield();\n#elif defined(CONF_FAMILY_WINDOWS)\n\tSleep(0);\n#else\n\t#error not implemented\n#endif\n}\n\nvoid thread_sleep(int milliseconds)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tusleep(milliseconds*1000);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tSleep(milliseconds);\n#else\n\t#error not implemented\n#endif\n}\n\nvoid thread_detach(void *thread)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_detach((pthread_t)(thread));\n#elif defined(CONF_FAMILY_WINDOWS)\n\tCloseHandle(thread);\n#else\n\t#error not implemented\n#endif\n}\n\n\n\n\n#if defined(CONF_FAMILY_UNIX)\ntypedef pthread_mutex_t LOCKINTERNAL;\n#elif defined(CONF_FAMILY_WINDOWS)\ntypedef CRITICAL_SECTION LOCKINTERNAL;\n#else\n\t#error not implemented on this platform\n#endif\n\nLOCK lock_create()\n{\n\tLOCKINTERNAL *lock = (LOCKINTERNAL*)mem_alloc(sizeof(LOCKINTERNAL), 4);\n\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_mutex_init(lock, 0x0);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tInitializeCriticalSection((LPCRITICAL_SECTION)lock);\n#else\n\t#error not implemented on this platform\n#endif\n\treturn (LOCK)lock;\n}\n\nvoid lock_destroy(LOCK lock)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_mutex_destroy((LOCKINTERNAL *)lock);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tDeleteCriticalSection((LPCRITICAL_SECTION)lock);\n#else\n\t#error not implemented on this platform\n#endif\n\tmem_free(lock);\n}\n\nint lock_try(LOCK lock)\n{\n#if defined(CONF_FAMILY_UNIX)\n\treturn pthread_mutex_trylock((LOCKINTERNAL *)lock);\n#elif defined(CONF_FAMILY_WINDOWS)\n\treturn !TryEnterCriticalSection((LPCRITICAL_SECTION)lock);\n#else\n\t#error not implemented on this platform\n#endif\n}\n\nvoid lock_wait(LOCK lock)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_mutex_lock((LOCKINTERNAL *)lock);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tEnterCriticalSection((LPCRITICAL_SECTION)lock);\n#else\n\t#error not implemented on this platform\n#endif\n}\n\nvoid lock_release(LOCK lock)\n{\n#if defined(CONF_FAMILY_UNIX)\n\tpthread_mutex_unlock((LOCKINTERNAL *)lock);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tLeaveCriticalSection((LPCRITICAL_SECTION)lock);\n#else\n\t#error not implemented on this platform\n#endif\n}\n\n#if !defined(CONF_PLATFORM_MACOSX)\n\t#if defined(CONF_FAMILY_UNIX)\n\tvoid semaphore_init(SEMAPHORE *sem) { sem_init(sem, 0, 0); }\n\tvoid semaphore_wait(SEMAPHORE *sem) { sem_wait(sem); }\n\tvoid semaphore_signal(SEMAPHORE *sem) { sem_post(sem); }\n\tvoid semaphore_destroy(SEMAPHORE *sem) { sem_destroy(sem); }\n\t#elif defined(CONF_FAMILY_WINDOWS)\n\tvoid semaphore_init(SEMAPHORE *sem) { *sem = CreateSemaphore(0, 0, 10000, 0); }\n\tvoid semaphore_wait(SEMAPHORE *sem) { WaitForSingleObject((HANDLE)*sem, INFINITE); }\n\tvoid semaphore_signal(SEMAPHORE *sem) { ReleaseSemaphore((HANDLE)*sem, 1, NULL); }\n\tvoid semaphore_destroy(SEMAPHORE *sem) { CloseHandle((HANDLE)*sem); }\n\t#else\n\t\t#error not implemented on this platform\n\t#endif\n#endif\n\n\n/* -----  time ----- */\nint64 time_get()\n{\n#if defined(CONF_FAMILY_UNIX)\n\tstruct timeval val;\n\tgettimeofday(&val, NULL);\n\treturn (int64)val.tv_sec*(int64)1000000+(int64)val.tv_usec;\n#elif defined(CONF_FAMILY_WINDOWS)\n\tstatic int64 last = 0;\n\tint64 t;\n\tQueryPerformanceCounter((PLARGE_INTEGER)&t);\n\tif(t<last) /* for some reason, QPC can return values in the past */\n\t\treturn last;\n\tlast = t;\n\treturn t;\n#else\n\t#error not implemented\n#endif\n}\n\nint64 time_freq()\n{\n#if defined(CONF_FAMILY_UNIX)\n\treturn 1000000;\n#elif defined(CONF_FAMILY_WINDOWS)\n\tint64 t;\n\tQueryPerformanceFrequency((PLARGE_INTEGER)&t);\n\treturn t;\n#else\n\t#error not implemented\n#endif\n}\n\n/* -----  network ----- */\nstatic void netaddr_to_sockaddr_in(const NETADDR *src, struct sockaddr_in *dest)\n{\n\tmem_zero(dest, sizeof(struct sockaddr_in));\n\tif(src->type != NETTYPE_IPV4)\n\t{\n\t\tdbg_msg(\"system\", \"couldn't convert NETADDR of type %d to ipv4\", src->type);\n\t\treturn;\n\t}\n\n\tdest->sin_family = AF_INET;\n\tdest->sin_port = htons(src->port);\n\tmem_copy(&dest->sin_addr.s_addr, src->ip, 4);\n}\n\nstatic void netaddr_to_sockaddr_in6(const NETADDR *src, struct sockaddr_in6 *dest)\n{\n\tmem_zero(dest, sizeof(struct sockaddr_in6));\n\tif(src->type != NETTYPE_IPV6)\n\t{\n\t\tdbg_msg(\"system\", \"couldn't not convert NETADDR of type %d to ipv6\", src->type);\n\t\treturn;\n\t}\n\n\tdest->sin6_family = AF_INET6;\n\tdest->sin6_port = htons(src->port);\n\tmem_copy(&dest->sin6_addr.s6_addr, src->ip, 16);\n}\n\nstatic void sockaddr_to_netaddr(const struct sockaddr *src, NETADDR *dst)\n{\n\tif(src->sa_family == AF_INET)\n\t{\n\t\tmem_zero(dst, sizeof(NETADDR));\n\t\tdst->type = NETTYPE_IPV4;\n\t\tdst->port = htons(((struct sockaddr_in*)src)->sin_port);\n\t\tmem_copy(dst->ip, &((struct sockaddr_in*)src)->sin_addr.s_addr, 4);\n\t}\n\telse if(src->sa_family == AF_INET6)\n\t{\n\t\tmem_zero(dst, sizeof(NETADDR));\n\t\tdst->type = NETTYPE_IPV6;\n\t\tdst->port = htons(((struct sockaddr_in6*)src)->sin6_port);\n\t\tmem_copy(dst->ip, &((struct sockaddr_in6*)src)->sin6_addr.s6_addr, 16);\n\t}\n\telse\n\t{\n\t\tmem_zero(dst, sizeof(struct sockaddr));\n\t\tdbg_msg(\"system\", \"couldn't convert sockaddr of family %d\", src->sa_family);\n\t}\n}\n\nint net_addr_comp(const NETADDR *a, const NETADDR *b)\n{\n\treturn mem_comp(a, b, sizeof(NETADDR));\n}\n\nvoid net_addr_str(const NETADDR *addr, char *string, int max_length, int add_port)\n{\n\tif(addr->type == NETTYPE_IPV4)\n\t{\n\t\tif(add_port != 0)\n\t\t\tstr_format(string, max_length, \"%d.%d.%d.%d:%d\", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], addr->port);\n\t\telse\n\t\t\tstr_format(string, max_length, \"%d.%d.%d.%d\", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);\n\t}\n\telse if(addr->type == NETTYPE_IPV6)\n\t{\n\t\tif(add_port != 0)\n\t\t\tstr_format(string, max_length, \"[%x:%x:%x:%x:%x:%x:%x:%x]:%d\",\n\t\t\t\t(addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7],\n\t\t\t\t(addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15],\n\t\t\t\taddr->port);\n\t\telse\n\t\t\tstr_format(string, max_length, \"[%x:%x:%x:%x:%x:%x:%x:%x]\",\n\t\t\t\t(addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7],\n\t\t\t\t(addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15]);\n\t}\n\telse\n\t\tstr_format(string, max_length, \"unknown type %d\", addr->type);\n}\n\nstatic int priv_net_extract(const char *hostname, char *host, int max_host, int *port)\n{\n\tint i;\n\n\t*port = 0;\n\thost[0] = 0;\n\n\tif(hostname[0] == '[')\n\t{\n\t\t// ipv6 mode\n\t\tfor(i = 1; i < max_host && hostname[i] && hostname[i] != ']'; i++)\n\t\t\thost[i-1] = hostname[i];\n\t\thost[i-1] = 0;\n\t\tif(hostname[i] != ']') // malformatted\n\t\t\treturn -1;\n\n\t\ti++;\n\t\tif(hostname[i] == ':')\n\t\t\t*port = atol(hostname+i+1);\n\t}\n\telse\n\t{\n\t\t// generic mode (ipv4, hostname etc)\n\t\tfor(i = 0; i < max_host-1 && hostname[i] && hostname[i] != ':'; i++)\n\t\t\thost[i] = hostname[i];\n\t\thost[i] = 0;\n\n\t\tif(hostname[i] == ':')\n\t\t\t*port = atol(hostname+i+1);\n\t}\n\n\treturn 0;\n}\n\nint net_host_lookup(const char *hostname, NETADDR *addr, int types)\n{\n\tstruct addrinfo hints;\n\tstruct addrinfo *result;\n\tint e;\n\tchar host[256];\n\tint port = 0;\n\n\tif(priv_net_extract(hostname, host, sizeof(host), &port))\n\t\treturn -1;\n\t/*\n\tdbg_msg(\"host lookup\", \"host='%s' port=%d %d\", host, port, types);\n\t*/\n\n\tmem_zero(&hints, sizeof(hints));\n\n\thints.ai_family = AF_UNSPEC;\n\n\tif(types == NETTYPE_IPV4)\n\t\thints.ai_family = AF_INET;\n\telse if(types == NETTYPE_IPV6)\n\t\thints.ai_family = AF_INET6;\n\n\te = getaddrinfo(host, NULL, &hints, &result);\n\tif(e != 0 || !result)\n\t\treturn -1;\n\n\tsockaddr_to_netaddr(result->ai_addr, addr);\n\tfreeaddrinfo(result);\n\taddr->port = port;\n\treturn 0;\n}\n\nstatic int parse_int(int *out, const char **str)\n{\n\tint i = 0;\n\t*out = 0;\n\tif(**str < '0' || **str > '9')\n\t\treturn -1;\n\n\ti = **str - '0';\n\t(*str)++;\n\n\twhile(1)\n\t{\n\t\tif(**str < '0' || **str > '9')\n\t\t{\n\t\t\t*out = i;\n\t\t\treturn 0;\n\t\t}\n\n\t\ti = (i*10) + (**str - '0');\n\t\t(*str)++;\n\t}\n\n\treturn 0;\n}\n\nstatic int parse_char(char c, const char **str)\n{\n\tif(**str != c) return -1;\n\t(*str)++;\n\treturn 0;\n}\n\nstatic int parse_uint8(unsigned char *out, const char **str)\n{\n\tint i;\n\tif(parse_int(&i, str) != 0) return -1;\n\tif(i < 0 || i > 0xff) return -1;\n\t*out = i;\n\treturn 0;\n}\n\nstatic int parse_uint16(unsigned short *out, const char **str)\n{\n\tint i;\n\tif(parse_int(&i, str) != 0) return -1;\n\tif(i < 0 || i > 0xffff) return -1;\n\t*out = i;\n\treturn 0;\n}\n\nint net_addr_from_str(NETADDR *addr, const char *string)\n{\n\tconst char *str = string;\n\tmem_zero(addr, sizeof(NETADDR));\n\n\tif(str[0] == '[')\n\t{\n\t\t/* ipv6 */\n\t\tstruct sockaddr_in6 sa6;\n\t\tchar buf[128];\n\t\tint i;\n\t\tstr++;\n\t\tfor(i = 0; i < 127 && str[i] && str[i] != ']'; i++)\n\t\t\tbuf[i] = str[i];\n\t\tbuf[i] = 0;\n\t\tstr += i;\n#if defined(CONF_FAMILY_WINDOWS)\n\t\t{\n\t\t\tint size;\n\t\t\tsa6.sin6_family = AF_INET6;\n\t\t\tsize = (int)sizeof(sa6);\n\t\t\tif(WSAStringToAddress(buf, AF_INET6, NULL, (struct sockaddr *)&sa6, &size) != 0)\n\t\t\t\treturn -1;\n\t\t}\n#else\n\t\tif(inet_pton(AF_INET6, buf, &sa6) != 1)\n\t\t\treturn -1;\n#endif\n\t\tsockaddr_to_netaddr((struct sockaddr *)&sa6, addr);\n\n\t\tif(*str == ']')\n\t\t{\n\t\t\tstr++;\n\t\t\tif(*str == ':')\n\t\t\t{\n\t\t\t\tstr++;\n\t\t\t\tif(parse_uint16(&addr->port, &str))\n\t\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\treturn -1;\n\n\t\treturn 0;\n\t}\n\telse\n\t{\n\t\t/* ipv4 */\n\t\tif(parse_uint8(&addr->ip[0], &str)) return -1;\n\t\tif(parse_char('.', &str)) return -1;\n\t\tif(parse_uint8(&addr->ip[1], &str)) return -1;\n\t\tif(parse_char('.', &str)) return -1;\n\t\tif(parse_uint8(&addr->ip[2], &str)) return -1;\n\t\tif(parse_char('.', &str)) return -1;\n\t\tif(parse_uint8(&addr->ip[3], &str)) return -1;\n\t\tif(*str == ':')\n\t\t{\n\t\t\tstr++;\n\t\t\tif(parse_uint16(&addr->port, &str)) return -1;\n\t\t}\n\n\t\taddr->type = NETTYPE_IPV4;\n\t}\n\n\treturn 0;\n}\n\nstatic void priv_net_close_socket(int sock)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tclosesocket(sock);\n#else\n\tclose(sock);\n#endif\n}\n\nstatic int priv_net_close_all_sockets(NETSOCKET sock)\n{\n\t/* close down ipv4 */\n\tif(sock.ipv4sock >= 0)\n\t{\n\t\tpriv_net_close_socket(sock.ipv4sock);\n\t\tsock.ipv4sock = -1;\n\t\tsock.type &= ~NETTYPE_IPV4;\n\t}\n\n\t/* close down ipv6 */\n\tif(sock.ipv6sock >= 0)\n\t{\n\t\tpriv_net_close_socket(sock.ipv6sock);\n\t\tsock.ipv6sock = -1;\n\t\tsock.type &= ~NETTYPE_IPV6;\n\t}\n\treturn 0;\n}\n\nstatic int priv_net_create_socket(int domain, int type, struct sockaddr *addr, int sockaddrlen)\n{\n\tint sock, e;\n\n\t/* create socket */\n\tsock = socket(domain, type, 0);\n\tif(sock < 0)\n\t{\n#if defined(CONF_FAMILY_WINDOWS)\n\t\tchar buf[128];\n\t\tint error = WSAGetLastError();\n\t\tif(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, buf, sizeof(buf), 0) == 0)\n\t\t\tbuf[0] = 0;\n\t\tdbg_msg(\"net\", \"failed to create socket with domain %d and type %d (%d '%s')\", domain, type, error, buf);\n#else\n\t\tdbg_msg(\"net\", \"failed to create socket with domain %d and type %d (%d '%s')\", domain, type, errno, strerror(errno));\n#endif\n\t\treturn -1;\n\t}\n\n\t/* set to IPv6 only if thats what we are creating */\n#if defined(IPV6_V6ONLY)\t/* windows sdk 6.1 and higher */\n\tif(domain == AF_INET6)\n\t{\n\t\tint ipv6only = 1;\n\t\tsetsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, sizeof(ipv6only));\n\t}\n#endif\n\n\tif(type == SOCK_STREAM)\n\t{\n\t\tint tmp = 1;\n\t\tsetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));\n\t}\n\n\t/* bind the socket */\n\te = bind(sock, addr, sockaddrlen);\n\tif(e != 0)\n\t{\n#if defined(CONF_FAMILY_WINDOWS)\n\t\tchar buf[128];\n\t\tint error = WSAGetLastError();\n\t\tif(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, buf, sizeof(buf), 0) == 0)\n\t\t\tbuf[0] = 0;\n\t\tdbg_msg(\"net\", \"failed to bind socket with domain %d and type %d (%d '%s')\", domain, type, error, buf);\n#else\n\t\tdbg_msg(\"net\", \"failed to bind socket with domain %d and type %d (%d '%s')\", domain, type, errno, strerror(errno));\n#endif\n\t\tpriv_net_close_socket(sock);\n\t\treturn -1;\n\t}\n\n\t/* return the newly created socket */\n\treturn sock;\n}\n\nNETSOCKET net_udp_create(NETADDR bindaddr)\n{\n\tNETSOCKET sock = invalid_socket;\n\tNETADDR tmpbindaddr = bindaddr;\n\tint broadcast = 1;\n\tint recvsize = 65536;\n\n\tif(bindaddr.type&NETTYPE_IPV4)\n\t{\n\t\tstruct sockaddr_in addr;\n\t\tint socket = -1;\n\n\t\t/* bind, we should check for error */\n\t\ttmpbindaddr.type = NETTYPE_IPV4;\n\t\tnetaddr_to_sockaddr_in(&tmpbindaddr, &addr);\n\t\tsocket = priv_net_create_socket(AF_INET, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr));\n\t\tif(socket >= 0)\n\t\t{\n\t\t\tsock.type |= NETTYPE_IPV4;\n\t\t\tsock.ipv4sock = socket;\n\n\t\t\t/* set broadcast */\n\t\t\tsetsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));\n\n\t\t\t/* set receive buffer size */\n\t\t\tsetsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize));\n\t\t}\n\t}\n\n\tif(bindaddr.type&NETTYPE_IPV6)\n\t{\n\t\tstruct sockaddr_in6 addr;\n\t\tint socket = -1;\n\n\t\t/* bind, we should check for error */\n\t\ttmpbindaddr.type = NETTYPE_IPV6;\n\t\tnetaddr_to_sockaddr_in6(&tmpbindaddr, &addr);\n\t\tsocket = priv_net_create_socket(AF_INET6, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr));\n\t\tif(socket >= 0)\n\t\t{\n\t\t\tsock.type |= NETTYPE_IPV6;\n\t\t\tsock.ipv6sock = socket;\n\n\t\t\t/* set broadcast */\n\t\t\tsetsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));\n\n\t\t\t/* set receive buffer size */\n\t\t\tsetsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize));\n\t\t}\n\t}\n\n\t/* set non-blocking */\n\tnet_set_non_blocking(sock);\n\n\t/* return */\n\treturn sock;\n}\n\nint net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size)\n{\n\tint d = -1;\n\n\tif(addr->type&NETTYPE_IPV4)\n\t{\n\t\tif(sock.ipv4sock >= 0)\n\t\t{\n\t\t\tstruct sockaddr_in sa;\n\t\t\tif(addr->type&NETTYPE_LINK_BROADCAST)\n\t\t\t{\n\t\t\t\tmem_zero(&sa, sizeof(sa));\n\t\t\t\tsa.sin_port = htons(addr->port);\n\t\t\t\tsa.sin_family = AF_INET;\n\t\t\t\tsa.sin_addr.s_addr = INADDR_BROADCAST;\n\t\t\t}\n\t\t\telse\n\t\t\t\tnetaddr_to_sockaddr_in(addr, &sa);\n\n\t\t\td = sendto((int)sock.ipv4sock, (const char*)data, size, 0, (struct sockaddr *)&sa, sizeof(sa));\n\t\t}\n\t\telse\n\t\t\tdbg_msg(\"net\", \"can't sent ipv4 traffic to this socket\");\n\t}\n\n\tif(addr->type&NETTYPE_IPV6)\n\t{\n\t\tif(sock.ipv6sock >= 0)\n\t\t{\n\t\t\tstruct sockaddr_in6 sa;\n\t\t\tif(addr->type&NETTYPE_LINK_BROADCAST)\n\t\t\t{\n\t\t\t\tmem_zero(&sa, sizeof(sa));\n\t\t\t\tsa.sin6_port = htons(addr->port);\n\t\t\t\tsa.sin6_family = AF_INET6;\n\t\t\t\tsa.sin6_addr.s6_addr[0] = 0xff; /* multicast */\n\t\t\t\tsa.sin6_addr.s6_addr[1] = 0x02; /* link local scope */\n\t\t\t\tsa.sin6_addr.s6_addr[15] = 1; /* all nodes */\n\t\t\t}\n\t\t\telse\n\t\t\t\tnetaddr_to_sockaddr_in6(addr, &sa);\n\n\t\t\td = sendto((int)sock.ipv6sock, (const char*)data, size, 0, (struct sockaddr *)&sa, sizeof(sa));\n\t\t}\n\t\telse\n\t\t\tdbg_msg(\"net\", \"can't sent ipv6 traffic to this socket\");\n\t}\n\t/*\n\telse\n\t\tdbg_msg(\"net\", \"can't sent to network of type %d\", addr->type);\n\t\t*/\n\n\t/*if(d < 0)\n\t{\n\t\tchar addrstr[256];\n\t\tnet_addr_str(addr, addrstr, sizeof(addrstr));\n\n\t\tdbg_msg(\"net\", \"sendto error (%d '%s')\", errno, strerror(errno));\n\t\tdbg_msg(\"net\", \"\\tsock = %d %x\", sock, sock);\n\t\tdbg_msg(\"net\", \"\\tsize = %d %x\", size, size);\n\t\tdbg_msg(\"net\", \"\\taddr = %s\", addrstr);\n\n\t}*/\n\tnetwork_stats.sent_bytes += size;\n\tnetwork_stats.sent_packets++;\n\treturn d;\n}\n\nint net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize)\n{\n\tchar sockaddrbuf[128];\n\tsocklen_t fromlen;// = sizeof(sockaddrbuf);\n\tint bytes = 0;\n\n\tif(bytes == 0 && sock.ipv4sock >= 0)\n\t{\n\t\tfromlen = sizeof(struct sockaddr_in);\n\t\tbytes = recvfrom(sock.ipv4sock, (char*)data, maxsize, 0, (struct sockaddr *)&sockaddrbuf, &fromlen);\n\t}\n\n\tif(bytes <= 0 && sock.ipv6sock >= 0)\n\t{\n\t\tfromlen = sizeof(struct sockaddr_in6);\n\t\tbytes = recvfrom(sock.ipv6sock, (char*)data, maxsize, 0, (struct sockaddr *)&sockaddrbuf, &fromlen);\n\t}\n\n\tif(bytes > 0)\n\t{\n\t\tsockaddr_to_netaddr((struct sockaddr *)&sockaddrbuf, addr);\n\t\tnetwork_stats.recv_bytes += bytes;\n\t\tnetwork_stats.recv_packets++;\n\t\treturn bytes;\n\t}\n\telse if(bytes == 0)\n\t\treturn 0;\n\treturn -1; /* error */\n}\n\nint net_udp_close(NETSOCKET sock)\n{\n\treturn priv_net_close_all_sockets(sock);\n}\n\nNETSOCKET net_tcp_create(NETADDR bindaddr)\n{\n\tNETSOCKET sock = invalid_socket;\n\tNETADDR tmpbindaddr = bindaddr;\n\n\tif(bindaddr.type&NETTYPE_IPV4)\n\t{\n\t\tstruct sockaddr_in addr;\n\t\tint socket = -1;\n\n\t\t/* bind, we should check for error */\n\t\ttmpbindaddr.type = NETTYPE_IPV4;\n\t\tnetaddr_to_sockaddr_in(&tmpbindaddr, &addr);\n\t\tsocket = priv_net_create_socket(AF_INET, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr));\n\t\tif(socket >= 0)\n\t\t{\n\t\t\tsock.type |= NETTYPE_IPV4;\n\t\t\tsock.ipv4sock = socket;\n\t\t}\n\t}\n\n\tif(bindaddr.type&NETTYPE_IPV6)\n\t{\n\t\tstruct sockaddr_in6 addr;\n\t\tint socket = -1;\n\n\t\t/* bind, we should check for error */\n\t\ttmpbindaddr.type = NETTYPE_IPV6;\n\t\tnetaddr_to_sockaddr_in6(&tmpbindaddr, &addr);\n\t\tsocket = priv_net_create_socket(AF_INET6, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr));\n\t\tif(socket >= 0)\n\t\t{\n\t\t\tsock.type |= NETTYPE_IPV6;\n\t\t\tsock.ipv6sock = socket;\n\t\t}\n\t}\n\n\t/* return */\n\treturn sock;\n}\n\nint net_set_non_blocking(NETSOCKET sock)\n{\n\tunsigned long mode = 1;\n\tif(sock.ipv4sock >= 0)\n\t{\n#if defined(CONF_FAMILY_WINDOWS)\n\t\tioctlsocket(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);\n#else\n\t\tioctl(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);\n#endif\n\t}\n\n\tif(sock.ipv6sock >= 0)\n\t{\n#if defined(CONF_FAMILY_WINDOWS)\n\t\tioctlsocket(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);\n#else\n\t\tioctl(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);\n#endif\n\t}\n\n\treturn 0;\n}\n\nint net_set_blocking(NETSOCKET sock)\n{\n\tunsigned long mode = 0;\n\tif(sock.ipv4sock >= 0)\n\t{\n#if defined(CONF_FAMILY_WINDOWS)\n\t\tioctlsocket(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);\n#else\n\t\tioctl(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);\n#endif\n\t}\n\n\tif(sock.ipv6sock >= 0)\n\t{\n#if defined(CONF_FAMILY_WINDOWS)\n\t\tioctlsocket(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);\n#else\n\t\tioctl(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);\n#endif\n\t}\n\n\treturn 0;\n}\n\nint net_tcp_listen(NETSOCKET sock, int backlog)\n{\n\tint err = -1;\n\tif(sock.ipv4sock >= 0)\n\t\terr = listen(sock.ipv4sock, backlog);\n\tif(sock.ipv6sock >= 0)\n\t\terr = listen(sock.ipv6sock, backlog);\n\treturn err;\n}\n\nint net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a)\n{\n\tint s;\n\tsocklen_t sockaddr_len;\n\n\t*new_sock = invalid_socket;\n\n\tif(sock.ipv4sock >= 0)\n\t{\n\t\tstruct sockaddr_in addr;\n\t\tsockaddr_len = sizeof(addr);\n\n\t\ts = accept(sock.ipv4sock, (struct sockaddr *)&addr, &sockaddr_len);\n\n\t\tif (s != -1)\n\t\t{\n\t\t\tsockaddr_to_netaddr((const struct sockaddr *)&addr, a);\n\t\t\tnew_sock->type = NETTYPE_IPV4;\n\t\t\tnew_sock->ipv4sock = s;\n\t\t\treturn s;\n\t\t}\n\t}\n\n\tif(sock.ipv6sock >= 0)\n\t{\n\t\tstruct sockaddr_in6 addr;\n\t\tsockaddr_len = sizeof(addr);\n\n\t\ts = accept(sock.ipv6sock, (struct sockaddr *)&addr, &sockaddr_len);\n\n\t\tif (s != -1)\n\t\t{\n\t\t\tsockaddr_to_netaddr((const struct sockaddr *)&addr, a);\n\t\t\tnew_sock->type = NETTYPE_IPV6;\n\t\t\tnew_sock->ipv6sock = s;\n\t\t\treturn s;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nint net_tcp_connect(NETSOCKET sock, const NETADDR *a)\n{\n\tif(a->type&NETTYPE_IPV4)\n\t{\n\t\tstruct sockaddr_in addr;\n\t\tnetaddr_to_sockaddr_in(a, &addr);\n\t\treturn connect(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr));\n\t}\n\n\tif(a->type&NETTYPE_IPV6)\n\t{\n\t\tstruct sockaddr_in6 addr;\n\t\tnetaddr_to_sockaddr_in6(a, &addr);\n\t\treturn connect(sock.ipv6sock, (struct sockaddr *)&addr, sizeof(addr));\n\t}\n\n\treturn -1;\n}\n\nint net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr)\n{\n\tint res = 0;\n\n\tnet_set_non_blocking(sock);\n\tres = net_tcp_connect(sock, &bindaddr);\n\tnet_set_blocking(sock);\n\n\treturn res;\n}\n\nint net_tcp_send(NETSOCKET sock, const void *data, int size)\n{\n\tint bytes = -1;\n\n\tif(sock.ipv4sock >= 0)\n\t\tbytes = send((int)sock.ipv4sock, (const char*)data, size, 0);\n\tif(sock.ipv6sock >= 0)\n\t\tbytes = send((int)sock.ipv6sock, (const char*)data, size, 0);\n\n\treturn bytes;\n}\n\nint net_tcp_recv(NETSOCKET sock, void *data, int maxsize)\n{\n\tint bytes = -1;\n\n\tif(sock.ipv4sock >= 0)\n\t\tbytes = recv((int)sock.ipv4sock, (char*)data, maxsize, 0);\n\tif(sock.ipv6sock >= 0)\n\t\tbytes = recv((int)sock.ipv6sock, (char*)data, maxsize, 0);\n\n\treturn bytes;\n}\n\nint net_tcp_close(NETSOCKET sock)\n{\n\treturn priv_net_close_all_sockets(sock);\n}\n\nint net_errno()\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\treturn WSAGetLastError();\n#else\n\treturn errno;\n#endif\n}\n\nint net_would_block()\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\treturn net_errno() == WSAEWOULDBLOCK;\n#else\n\treturn net_errno() == EWOULDBLOCK;\n#endif\n}\n\nint net_init()\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tWSADATA wsaData;\n\tint err = WSAStartup(MAKEWORD(1, 1), &wsaData);\n\tdbg_assert(err == 0, \"network initialization failed.\");\n\treturn err==0?0:1;\n#endif\n\n\treturn 0;\n}\n\nint fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tWIN32_FIND_DATA finddata;\n\tHANDLE handle;\n\tchar buffer[1024*2];\n\tint length;\n\tstr_format(buffer, sizeof(buffer), \"%s/*\", dir);\n\n\thandle = FindFirstFileA(buffer, &finddata);\n\n\tif (handle == INVALID_HANDLE_VALUE)\n\t\treturn 0;\n\n\tstr_format(buffer, sizeof(buffer), \"%s/\", dir);\n\tlength = str_length(buffer);\n\n\t/* add all the entries */\n\tdo\n\t{\n\t\tstr_copy(buffer+length, finddata.cFileName, (int)sizeof(buffer)-length);\n\t\tif(cb(finddata.cFileName, fs_is_dir(buffer), type, user))\n\t\t\tbreak;\n\t}\n\twhile (FindNextFileA(handle, &finddata));\n\n\tFindClose(handle);\n\treturn 0;\n#else\n\tstruct dirent *entry;\n\tchar buffer[1024*2];\n\tint length;\n\tDIR *d = opendir(dir);\n\n\tif(!d)\n\t\treturn 0;\n\n\tstr_format(buffer, sizeof(buffer), \"%s/\", dir);\n\tlength = str_length(buffer);\n\n\twhile((entry = readdir(d)) != NULL)\n\t{\n\t\tstr_copy(buffer+length, entry->d_name, (int)sizeof(buffer)-length);\n\t\tif(cb(entry->d_name, fs_is_dir(buffer), type, user))\n\t\t\tbreak;\n\t}\n\n\t/* close the directory and return */\n\tclosedir(d);\n\treturn 0;\n#endif\n}\n\nint fs_storage_path(const char *appname, char *path, int max)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tchar *home = getenv(\"APPDATA\");\n\tif(!home)\n\t\treturn -1;\n\t_snprintf(path, max, \"%s/%s\", home, appname);\n\treturn 0;\n#else\n\tchar *home = getenv(\"HOME\");\n#if !defined(CONF_PLATFORM_MACOSX)\n\tint i;\n#endif\n\tif(!home)\n\t\treturn -1;\n\n#if defined(CONF_PLATFORM_MACOSX)\n\tsnprintf(path, max, \"%s/Library/Application Support/%s\", home, appname);\n#else\n\tsnprintf(path, max, \"%s/.%s\", home, appname);\n\tfor(i = strlen(home)+2; path[i]; i++)\n\t\tpath[i] = tolower(path[i]);\n#endif\n\n\treturn 0;\n#endif\n}\n\nint fs_makedir(const char *path)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tif(_mkdir(path) == 0)\n\t\t\treturn 0;\n\tif(errno == EEXIST)\n\t\treturn 0;\n\treturn -1;\n#else\n\tif(mkdir(path, 0755) == 0)\n\t\treturn 0;\n\tif(errno == EEXIST)\n\t\treturn 0;\n\treturn -1;\n#endif\n}\n\nint fs_is_dir(const char *path)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\t/* TODO: do this smarter */\n\tWIN32_FIND_DATA finddata;\n\tHANDLE handle;\n\tchar buffer[1024*2];\n\tstr_format(buffer, sizeof(buffer), \"%s/*\", path);\n\n\tif ((handle = FindFirstFileA(buffer, &finddata)) == INVALID_HANDLE_VALUE)\n\t\treturn 0;\n\n\tFindClose(handle);\n\treturn 1;\n#else\n\tstruct stat sb;\n\tif (stat(path, &sb) == -1)\n\t\treturn 0;\n\n\tif (S_ISDIR(sb.st_mode))\n\t\treturn 1;\n\telse\n\t\treturn 0;\n#endif\n}\n\nint fs_chdir(const char *path)\n{\n\tif(fs_is_dir(path))\n\t{\n\t\tif(chdir(path))\n\t\t\treturn 1;\n\t\telse\n\t\t\treturn 0;\n\t}\n\telse\n\t\treturn 1;\n}\n\nchar *fs_getcwd(char *buffer, int buffer_size)\n{\n\tif(buffer == 0)\n\t\treturn 0;\n#if defined(CONF_FAMILY_WINDOWS)\n\treturn _getcwd(buffer, buffer_size);\n#else\n\treturn getcwd(buffer, buffer_size);\n#endif\n}\n\nint fs_parent_dir(char *path)\n{\n\tchar *parent = 0;\n\tfor(; *path; ++path)\n\t{\n\t\tif(*path == '/' || *path == '\\\\')\n\t\t\tparent = path;\n\t}\n\n\tif(parent)\n\t{\n\t\t*parent = 0;\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nint fs_remove(const char *filename)\n{\n\tif(remove(filename) != 0)\n\t\treturn 1;\n\treturn 0;\n}\n\nint fs_rename(const char *oldname, const char *newname)\n{\n\tif(rename(oldname, newname) != 0)\n\t\treturn 1;\n\treturn 0;\n}\n\nvoid swap_endian(void *data, unsigned elem_size, unsigned num)\n{\n\tchar *src = (char*) data;\n\tchar *dst = src + (elem_size - 1);\n\n\twhile(num)\n\t{\n\t\tunsigned n = elem_size>>1;\n\t\tchar tmp;\n\t\twhile(n)\n\t\t{\n\t\t\ttmp = *src;\n\t\t\t*src = *dst;\n\t\t\t*dst = tmp;\n\n\t\t\tsrc++;\n\t\t\tdst--;\n\t\t\tn--;\n\t\t}\n\n\t\tsrc = src + (elem_size>>1);\n\t\tdst = src + (elem_size - 1);\n\t\tnum--;\n\t}\n}\n\nint net_socket_read_wait(NETSOCKET sock, int time)\n{\n\tstruct timeval tv;\n\tfd_set readfds;\n\tint sockid;\n\n\ttv.tv_sec = 0;\n\ttv.tv_usec = 1000*time;\n\tsockid = 0;\n\n\tFD_ZERO(&readfds);\n\tif(sock.ipv4sock >= 0)\n\t{\n\t\tFD_SET(sock.ipv4sock, &readfds);\n\t\tsockid = sock.ipv4sock;\n\t}\n\tif(sock.ipv6sock >= 0)\n\t{\n\t\tFD_SET(sock.ipv6sock, &readfds);\n\t\tif(sock.ipv6sock > sockid)\n\t\t\tsockid = sock.ipv6sock;\n\t}\n\n\t/* don't care about writefds and exceptfds */\n\tselect(sockid+1, &readfds, NULL, NULL, &tv);\n\n\tif(sock.ipv4sock >= 0 && FD_ISSET(sock.ipv4sock, &readfds))\n\t\treturn 1;\n\n\tif(sock.ipv6sock >= 0 && FD_ISSET(sock.ipv6sock, &readfds))\n\t\treturn 1;\n\n\treturn 0;\n}\n\nint time_timestamp()\n{\n\treturn time(0);\n}\n\nvoid str_append(char *dst, const char *src, int dst_size)\n{\n\tint s = strlen(dst);\n\tint i = 0;\n\twhile(s < dst_size)\n\t{\n\t\tdst[s] = src[i];\n\t\tif(!src[i]) /* check for null termination */\n\t\t\tbreak;\n\t\ts++;\n\t\ti++;\n\t}\n\n\tdst[dst_size-1] = 0; /* assure null termination */\n}\n\nvoid str_copy(char *dst, const char *src, int dst_size)\n{\n\tstrncpy(dst, src, dst_size);\n\tdst[dst_size-1] = 0; /* assure null termination */\n}\n\nint str_length(const char *str)\n{\n\treturn (int)strlen(str);\n}\n\nvoid str_format(char *buffer, int buffer_size, const char *format, ...)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\tva_list ap;\n\tva_start(ap, format);\n\t_vsnprintf(buffer, buffer_size, format, ap);\n\tva_end(ap);\n#else\n\tva_list ap;\n\tva_start(ap, format);\n\tvsnprintf(buffer, buffer_size, format, ap);\n\tva_end(ap);\n#endif\n\n\tbuffer[buffer_size-1] = 0; /* assure null termination */\n}\n\n\n\n/* makes sure that the string only contains the characters between 32 and 127 */\nvoid str_sanitize_strong(char *str_in)\n{\n\tunsigned char *str = (unsigned char *)str_in;\n\twhile(*str)\n\t{\n\t\t*str &= 0x7f;\n\t\tif(*str < 32)\n\t\t\t*str = 32;\n\t\tstr++;\n\t}\n}\n\n/* makes sure that the string only contains the characters between 32 and 255 */\nvoid str_sanitize_cc(char *str_in)\n{\n\tunsigned char *str = (unsigned char *)str_in;\n\twhile(*str)\n\t{\n\t\tif(*str < 32)\n\t\t\t*str = ' ';\n\t\tstr++;\n\t}\n}\n\n/* makes sure that the string only contains the characters between 32 and 255 + \\r\\n\\t */\nvoid str_sanitize(char *str_in)\n{\n\tunsigned char *str = (unsigned char *)str_in;\n\twhile(*str)\n\t{\n\t\tif(*str < 32 && !(*str == '\\r') && !(*str == '\\n') && !(*str == '\\t'))\n\t\t\t*str = ' ';\n\t\tstr++;\n\t}\n}\n\nchar *str_skip_to_whitespace(char *str)\n{\n\twhile(*str && (*str != ' ' && *str != '\\t' && *str != '\\n'))\n\t\tstr++;\n\treturn str;\n}\n\nchar *str_skip_whitespaces(char *str)\n{\n\twhile(*str && (*str == ' ' || *str == '\\t' || *str == '\\n' || *str == '\\r'))\n\t\tstr++;\n\treturn str;\n}\n\n/* case */\nint str_comp_nocase(const char *a, const char *b)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\treturn _stricmp(a,b);\n#else\n\treturn strcasecmp(a,b);\n#endif\n}\n\nint str_comp_nocase_num(const char *a, const char *b, const int num)\n{\n#if defined(CONF_FAMILY_WINDOWS)\n\treturn _strnicmp(a, b, num);\n#else\n\treturn strncasecmp(a, b, num);\n#endif\n}\n\nint str_comp(const char *a, const char *b)\n{\n\treturn strcmp(a, b);\n}\n\nint str_comp_num(const char *a, const char *b, const int num)\n{\n\treturn strncmp(a, b, num);\n}\n\nint str_comp_filenames(const char *a, const char *b)\n{\n\tint result;\n\n\tfor(; *a && *b; ++a, ++b)\n\t{\n\t\tif(*a >= '0' && *a <= '9' && *b >= '0' && *b <= '9')\n\t\t{\n\t\t\tresult = 0;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif(!result)\n\t\t\t\t\tresult = *a - *b;\n\t\t\t\t++a; ++b;\n\t\t\t}\n\t\t\twhile(*a >= '0' && *a <= '9' && *b >= '0' && *b <= '9');\n\n\t\t\tif(*a >= '0' && *a <= '9')\n\t\t\t\treturn 1;\n\t\t\telse if(*b >= '0' && *b <= '9')\n\t\t\t\treturn -1;\n\t\t\telse if(result)\n\t\t\t\treturn result;\n\t\t}\n\n\t\tif(tolower(*a) != tolower(*b))\n\t\t\tbreak;\n\t}\n\treturn tolower(*a) - tolower(*b);\n}\n\nconst char *str_find_nocase(const char *haystack, const char *needle)\n{\n\twhile(*haystack) /* native implementation */\n\t{\n\t\tconst char *a = haystack;\n\t\tconst char *b = needle;\n\t\twhile(*a && *b && tolower(*a) == tolower(*b))\n\t\t{\n\t\t\ta++;\n\t\t\tb++;\n\t\t}\n\t\tif(!(*b))\n\t\t\treturn haystack;\n\t\thaystack++;\n\t}\n\n\treturn 0;\n}\n\n\nconst char *str_find(const char *haystack, const char *needle)\n{\n\twhile(*haystack) /* native implementation */\n\t{\n\t\tconst char *a = haystack;\n\t\tconst char *b = needle;\n\t\twhile(*a && *b && *a == *b)\n\t\t{\n\t\t\ta++;\n\t\t\tb++;\n\t\t}\n\t\tif(!(*b))\n\t\t\treturn haystack;\n\t\thaystack++;\n\t}\n\n\treturn 0;\n}\n\nvoid str_hex(char *dst, int dst_size, const void *data, int data_size)\n{\n\tstatic const char hex[] = \"0123456789ABCDEF\";\n\tint b;\n\n\tfor(b = 0; b < data_size && b < dst_size/4-4; b++)\n\t{\n\t\tdst[b*3] = hex[((const unsigned char *)data)[b]>>4];\n\t\tdst[b*3+1] = hex[((const unsigned char *)data)[b]&0xf];\n\t\tdst[b*3+2] = ' ';\n\t\tdst[b*3+3] = 0;\n\t}\n}\n\nvoid str_timestamp(char *buffer, int buffer_size)\n{\n\ttime_t time_data;\n\tstruct tm *time_info;\n\n\ttime(&time_data);\n\ttime_info = localtime(&time_data);\n\tstrftime(buffer, buffer_size, \"%Y-%m-%d_%H-%M-%S\", time_info);\n\tbuffer[buffer_size-1] = 0;\t/* assure null termination */\n}\n\nint mem_comp(const void *a, const void *b, int size)\n{\n\treturn memcmp(a,b,size);\n}\n\nconst MEMSTATS *mem_stats()\n{\n\treturn &memory_stats;\n}\n\nvoid net_stats(NETSTATS *stats_inout)\n{\n\t*stats_inout = network_stats;\n}\n\nvoid gui_messagebox(const char *title, const char *message)\n{\n#if defined(CONF_PLATFORM_MACOSX)\n\tDialogRef theItem;\n\tDialogItemIndex itemIndex;\n\n\t/* FIXME: really needed? can we rely on glfw? */\n\t/* HACK - get events without a bundle */\n\tProcessSerialNumber psn;\n\tGetCurrentProcess(&psn);\n\tTransformProcessType(&psn,kProcessTransformToForegroundApplication);\n\tSetFrontProcess(&psn);\n\t/* END HACK */\n\n\tCreateStandardAlert(kAlertStopAlert,\n\t\t\tCFStringCreateWithCString(NULL, title, kCFStringEncodingASCII),\n\t\t\tCFStringCreateWithCString(NULL, message, kCFStringEncodingASCII),\n\t\t\tNULL,\n\t\t\t&theItem);\n\n\tRunStandardAlert(theItem, NULL, &itemIndex);\n#elif defined(CONF_FAMILY_UNIX)\n\tstatic char cmd[1024];\n\tint err;\n\t/* use xmessage which is available on nearly every X11 system */\n\tsnprintf(cmd, sizeof(cmd), \"xmessage -center -title '%s' '%s'\",\n\t\ttitle,\n\t\tmessage);\n\n\terr = system(cmd);\n\tdbg_msg(\"gui/msgbox\", \"result = %i\", err);\n#elif defined(CONF_FAMILY_WINDOWS)\n\tMessageBox(NULL,\n\t\tmessage,\n\t\ttitle,\n\t\tMB_ICONEXCLAMATION | MB_OK);\n#else\n\t/* this is not critical */\n\t#warning not implemented\n#endif\n}\n\nint str_isspace(char c) { return c == ' ' || c == '\\n' || c == '\\t'; }\n\nchar str_uppercase(char c)\n{\n\tif(c >= 'a' && c <= 'z')\n\t\treturn 'A' + (c-'a');\n\treturn c;\n}\n\nint str_toint(const char *str) { return atoi(str); }\nfloat str_tofloat(const char *str) { return atof(str); }\n\n\n\nstatic int str_utf8_isstart(char c)\n{\n\tif((c&0xC0) == 0x80) /* 10xxxxxx */\n\t\treturn 0;\n\treturn 1;\n}\n\nint str_utf8_rewind(const char *str, int cursor)\n{\n\twhile(cursor)\n\t{\n\t\tcursor--;\n\t\tif(str_utf8_isstart(*(str + cursor)))\n\t\t\tbreak;\n\t}\n\treturn cursor;\n}\n\nint str_utf8_forward(const char *str, int cursor)\n{\n\tconst char *buf = str + cursor;\n\tif(!buf[0])\n\t\treturn cursor;\n\n\tif((*buf&0x80) == 0x0)  /* 0xxxxxxx */\n\t\treturn cursor+1;\n\telse if((*buf&0xE0) == 0xC0) /* 110xxxxx */\n\t{\n\t\tif(!buf[1]) return cursor+1;\n\t\treturn cursor+2;\n\t}\n\telse  if((*buf & 0xF0) == 0xE0)\t/* 1110xxxx */\n\t{\n\t\tif(!buf[1]) return cursor+1;\n\t\tif(!buf[2]) return cursor+2;\n\t\treturn cursor+3;\n\t}\n\telse if((*buf & 0xF8) == 0xF0)\t/* 11110xxx */\n\t{\n\t\tif(!buf[1]) return cursor+1;\n\t\tif(!buf[2]) return cursor+2;\n\t\tif(!buf[3]) return cursor+3;\n\t\treturn cursor+4;\n\t}\n\n\t/* invalid */\n\treturn cursor+1;\n}\n\nint str_utf8_encode(char *ptr, int chr)\n{\n\t/* encode */\n\tif(chr <= 0x7F)\n\t{\n\t\tptr[0] = (char)chr;\n\t\treturn 1;\n\t}\n\telse if(chr <= 0x7FF)\n\t{\n\t\tptr[0] = 0xC0|((chr>>6)&0x1F);\n\t\tptr[1] = 0x80|(chr&0x3F);\n\t\treturn 2;\n\t}\n\telse if(chr <= 0xFFFF)\n\t{\n\t\tptr[0] = 0xE0|((chr>>12)&0x0F);\n\t\tptr[1] = 0x80|((chr>>6)&0x3F);\n\t\tptr[2] = 0x80|(chr&0x3F);\n\t\treturn 3;\n\t}\n\telse if(chr <= 0x10FFFF)\n\t{\n\t\tptr[0] = 0xF0|((chr>>18)&0x07);\n\t\tptr[1] = 0x80|((chr>>12)&0x3F);\n\t\tptr[2] = 0x80|((chr>>6)&0x3F);\n\t\tptr[3] = 0x80|(chr&0x3F);\n\t\treturn 4;\n\t}\n\n\treturn 0;\n}\n\nint str_utf8_decode(const char **ptr)\n{\n\tconst char *buf = *ptr;\n\tint ch = 0;\n\n\tdo\n\t{\n\t\tif((*buf&0x80) == 0x0)  /* 0xxxxxxx */\n\t\t{\n\t\t\tch = *buf;\n\t\t\tbuf++;\n\t\t}\n\t\telse if((*buf&0xE0) == 0xC0) /* 110xxxxx */\n\t\t{\n\t\t\tch  = (*buf++ & 0x3F) << 6; if(!(*buf)) break;\n\t\t\tch += (*buf++ & 0x3F);\n\t\t\tif(ch == 0) ch = -1;\n\t\t}\n\t\telse  if((*buf & 0xF0) == 0xE0)\t/* 1110xxxx */\n\t\t{\n\t\t\tch  = (*buf++ & 0x1F) << 12; if(!(*buf)) break;\n\t\t\tch += (*buf++ & 0x3F) <<  6; if(!(*buf)) break;\n\t\t\tch += (*buf++ & 0x3F);\n\t\t\tif(ch == 0) ch = -1;\n\t\t}\n\t\telse if((*buf & 0xF8) == 0xF0)\t/* 11110xxx */\n\t\t{\n\t\t\tch  = (*buf++ & 0x0F) << 18; if(!(*buf)) break;\n\t\t\tch += (*buf++ & 0x3F) << 12; if(!(*buf)) break;\n\t\t\tch += (*buf++ & 0x3F) <<  6; if(!(*buf)) break;\n\t\t\tch += (*buf++ & 0x3F);\n\t\t\tif(ch == 0) ch = -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* invalid */\n\t\t\tbuf++;\n\t\t\tbreak;\n\t\t}\n\n\t\t*ptr = buf;\n\t\treturn ch;\n\t} while(0);\n\n\t/* out of bounds */\n\t*ptr = buf;\n\treturn -1;\n\n}\n\nint str_utf8_check(const char *str)\n{\n\twhile(*str)\n\t{\n\t\tif((*str&0x80) == 0x0)\n\t\t\tstr++;\n\t\telse if((*str&0xE0) == 0xC0 && (*(str+1)&0xC0) == 0x80)\n\t\t\tstr += 2;\n\t\telse if((*str&0xF0) == 0xE0 && (*(str+1)&0xC0) == 0x80 && (*(str+2)&0xC0) == 0x80)\n\t\t\tstr += 3;\n\t\telse if((*str&0xF8) == 0xF0 && (*(str+1)&0xC0) == 0x80 && (*(str+2)&0xC0) == 0x80 && (*(str+3)&0xC0) == 0x80)\n\t\t\tstr += 4;\n\t\telse\n\t\t\treturn 0;\n\t}\n\treturn 1;\n}\n\n\nunsigned str_quickhash(const char *str)\n{\n\tunsigned hash = 5381;\n\tfor(; *str; str++)\n\t\thash = ((hash << 5) + hash) + (*str); /* hash * 33 + c */\n\treturn hash;\n}\n\n\n#if defined(__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "web/css/light.css",
    "content": "body { font-family:Molengo,\"Hiragino Sans GB\", \"Microsoft YaHei\", \"WenQuanYi Micro Hei\", sans-serif; background: #ebebeb url('../img/light.png');padding-top: 70px;padding-bottom: 30px;}\n.announcement { color: #777; border-bottom: solid 3px #d0d0d0; background-color: #fff; padding: 10px 10px; text-align: center; transition: 0.3s; }\n.announcement p { display: inline-block;font-size: 15px;margin: 0;line-height: 1;text-indent: 5px; }\n.announcement i { display: inline-block;font-size: 15px;margin: 0;line-height: 1;color: #444; }\n\n.path-announcement2 {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.path-announcement {\n\tmargin-top: 50px;\n    background-color: #fafbfc;\n    border-bottom: 1px solid #e1e4e8;\n}\n.path-announcement p {\n    color: #444444;\n    line-height: 3.5;\n    margin-left: 20px;\n}\n.path-announcement a {\n\tcolor: #666;\n    box-shadow: 0px 1px 0px 0px #999;\n\ttext-decoration: none;\n}\n.path-announcement a:focus,.path-announcement a:hover {\n\tcolor: #337ab7;\n    box-shadow: 0px 1px 0px 0px #555;\n}\n.path-announcement i {\n\tmargin-right: 10px;\n}\n.path-announcement i,.path-announcement p {\n    display: inline;\n}\n\n.navbar { min-height: 50px; }\n.navbar-top { background-color: #444 !important; }\n.navbar-brand { color: #fff; padding: 10px; font-size: 20px; }\n.dropdown .dropdown-toggle { padding-bottom: 10px; padding-top: 10px; }\n.navbar-inverse .navbar-brand { color: #fff; padding: 15px 20px 10px; font-size: 16px; font-weight: 600; }\n.content { background: #ffffff; padding: 20px; border: 1px #eee solid; -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, .1); -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, .1); box-shadow: 0 1px 10px rgba(0, 0, 0, .1); margin-bottom: 20px; margin-top: 50px; }\n.table { background: #ffffff; margin-bottom: 0; border-collapse: collapse; border-radius: 3px; }\n.table th, .table td { text-align: center; }\n.table-striped tbody > tr.even > td, .table-striped tbody > tr.even > th { background-color: #F9F9F9; }\n.table-striped tbody > tr.odd > td, .table-striped tbody > tr.odd > th { background-color: #FFF; }\n.progress { margin-bottom: 0; }\n.progress-bar { color: #000; }\n.table-hover > tbody > tr:hover > td { background: #E6E6E6; }\ntr.even.expandRow > :hover { background: #F9F9F9 !important; }\ntr.odd.expandRow > :hover { background: #FFF !important; }\n.expandRow > td { padding: 0 !important; border-top: 0px !important; }\n#cpu, #ram, #hdd, #network, #traffic { min-width: 55px; max-width: 100px; }\n\n@media only screen and (max-width: 992px) {\n\t#location, tr td:nth-child(4)\t\t{ display:none; visibility:hidden; }\n}\n@media only screen and (max-width: 720px) {\n\t#type, tr td:nth-child(3)\t\t\t{ display:none; visibility:hidden; }\n\t#location, tr td:nth-child(4)\t\t{ display:none; visibility:hidden; }\n\t#uptime, tr td:nth-child(5)\t\t\t{ display:none; visibility:hidden; }\n}\n@media only screen and (max-width: 600px) {\n\t#type, tr td:nth-child(3)\t\t\t{ display:none; visibility:hidden; }\n\t#location, tr td:nth-child(4)\t\t{ display:none; visibility:hidden; }\n\t#uptime, tr td:nth-child(5)\t\t\t{ display:none; visibility:hidden; }\n\t#load, tr td:nth-child(6)\t\t\t{ display:none; visibility:hidden; }\n}\n@media only screen and (max-width: 533px) {\n\t#type, tr td:nth-child(3)\t\t\t{ display:none; visibility:hidden; }\n\t#location, tr td:nth-child(4)\t\t{ display:none; visibility:hidden; }\n\t#uptime, tr td:nth-child(5)\t\t\t{ display:none; visibility:hidden; }\n\t#traffic, tr td:nth-child(8)\t\t{ display:none; visibility:hidden; }\n\t#load, tr td:nth-child(6)\t\t\t{ display:none; visibility:hidden; }\n}\n@media only screen and (max-width: 450px) {\n\tbody\t\t\t\t\t\t\t\t{ font-size: 10px; }\n\t.content \t\t\t\t\t\t\t{ padding: 0; }\n\t#name, tr td:nth-child(2)\t\t\t{ min-width: 20px; max-width: 60px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }\n\t#type, tr td:nth-child(3)\t\t\t{ display:none; visibility:hidden; }\n\t#location, tr td:nth-child(4)\t\t{ display:none; visibility:hidden; }\n\t#uptime, tr td:nth-child(5)\t\t\t{ display:none; visibility:hidden; }\n\t#traffic, tr td:nth-child(8)\t\t{ display:none; visibility:hidden; }\n\t#hdd, tr td:nth-child(11)\t\t{ display:none; visibility:hidden; }\n\t#cpu, #ram { min-width: 20px; max-width: 40px; }\n}\n"
  },
  {
    "path": "web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title>逗比云监控</title>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t\t<meta name=\"description\" content=\"云监控\">\n\t\t<meta name=\"author\" content=\"BotoX\">\n\t\t<link rel=\"stylesheet\" href=\"css/bootstrap.min.css\">\n\t\t<link rel=\"stylesheet\" href=\"css/bootstrap-theme.min.css\">\n\t\t<link rel=\"stylesheet\" href=\"css/light.css\">\n\t</head>\n\t<body>\n\t\t<div role=\"navigation\" class=\"navbar navbar-inverse navbar-fixed-top navbar-top\">\n\t\t\t<div class=\"navbar-inner\">\n\t\t\t\t<div class=\"container\">\n\t\t\t\t\t<div class=\"navbar-header\">\n\t\t\t\t\t\t<p class=\"navbar-brand\">逗比云监控</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"navbar-collapse collapse\">\n\t\t\t\t\t</div><!--/.nav-collapse -->\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"path-announcement navbar navbar-default navbar-fixed-top\">\n            <div class=\"path-announcement2 container\">\n              <!-- 顶部公告栏 -->\n                <p><i class=\"fa fa-volume-down\"></i>顶部公告栏</p>\n              <!-- 顶部公告栏 -->\n            </div>\n        </div>\n\t\t\n\t\t<div class=\"container content\">\n\t\t\t<div id=\"loading-notice\">\n\t\t\t\t<noscript>\n\t\t\t\t\t<div class=\"alert alert-danger\" style=\"text-align: center;\">\n\t\t\t\t\t\t<strong>Enable JavaScript</strong> you fucking autist neckbeard, it's not gonna hurt you.\n\t\t\t\t\t</div>\n\t\t\t\t</noscript>\n\t\t\t\t<div class=\"progress progress-striped active\">\n\t\t\t\t\t<div class=\"progress-bar progress-bar-warning\" style=\"width: 100%;\">加载中...</div>\n\t\t\t\t</div>\n\t\t\t\t<div style=\"text-align: center;\">\n\t\t\t\t\t如果出现此消息，请确保您已启用Javascript! <br />否则云监控主服务(服务端) 未启动或已关闭.\n\t\t\t\t</div>\n\t\t\t\t<p></p>\n\t\t\t</div>\n\t\t\t<table class=\"table table-striped table-condensed table-hover\">\n\t\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th id=\"status4\" style=\"text-align: center;\">IPv4</th>\n\t\t\t\t\t<th id=\"name\">节点名</th>\n\t\t\t\t\t<th id=\"type\">虚拟化</th>\n\t\t\t\t\t<th id=\"location\">位置</th>\n\t\t\t\t\t<th id=\"uptime\">在线时间</th>\n\t\t\t\t\t<th id=\"load\">负载</th>\n\t\t\t\t\t<th id=\"network\">网络(B/s) ↓|↑</th>\n\t\t\t\t\t<th id=\"traffic\">流量(B) ↓|↑</th>\n\t\t\t\t\t<th id=\"cpu\">CPU</th>\n\t\t\t\t\t<th id=\"ram\">内存</th>\n\t\t\t\t\t<th id=\"hdd\">硬盘</th>\n\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody id=\"servers\">\n\t\t\t\t<!-- Servers here \\o/ -->\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t\t<br />\n\t\t\t<div id=\"updated\" style=\"margin-left: 5px;\">Updating...</div>\n\t\t</div>\n\n\t\t<div class=\"container\">\n\t\t\t<p style=\"text-align: center; font-size: 10px;\"><a target=\"_blank\"href=\"https://github.com/ToyoDAdoubiBackup/ServerStatus-Toyo\">ServerStatus-Toyo</a> · <a target=\"_blank\" href=\"https://doub.io/shell-jc3/\">搭建教程</a></p>\n\t\t</div>\n\t\t<script src=\"js/jquery-1.10.2.min.js\"></script>\n\t\t<script src=\"js/bootstrap.min.js\"></script>\n\t\t<script src=\"js/serverstatus.js\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "web/js/serverstatus.js",
    "content": "// serverstatus.js\nvar error = 0;\nvar d = 0;\nvar server_status = new Array();\n\nfunction timeSince(date) {\n\tif(date == 0)\n\t\treturn \"从未.\";\n\n\tvar seconds = Math.floor((new Date() - date) / 1000);\n\tvar interval = Math.floor(seconds / 31536000);\n\n\tif (interval > 1)\n\t\treturn interval + \" 年前.\";\n\tinterval = Math.floor(seconds / 2592000);\n\tif (interval > 1)\n\t\treturn interval + \" 月前.\";\n\tinterval = Math.floor(seconds / 86400);\n\tif (interval > 1)\n\t\treturn interval + \" 日前.\";\n\tinterval = Math.floor(seconds / 3600);\n\tif (interval > 1)\n\t\treturn interval + \" 小时前.\";\n\tinterval = Math.floor(seconds / 60);\n\tif (interval > 1)\n\t\treturn interval + \" 分钟前.\";\n\t/*if(Math.floor(seconds) >= 5)\n\t\treturn Math.floor(seconds) + \" seconds\";*/\n\telse\n\t\treturn \"几秒前.\";\n}\n\nfunction bytesToSize(bytes, precision, si)\n{\n\tvar ret;\n\tsi = typeof si !== 'undefined' ? si : 0;\n\tif(si != 0) {\n\t\tvar kilobyte = 1000;\n\t\tvar megabyte = kilobyte * 1000;\n\t\tvar gigabyte = megabyte * 1000;\n\t\tvar terabyte = gigabyte * 1000;\n\t} else {\n\t\tvar kilobyte = 1024;\n\t\tvar megabyte = kilobyte * 1024;\n\t\tvar gigabyte = megabyte * 1024;\n\t\tvar terabyte = gigabyte * 1024;\n\t}\n\n\tif ((bytes >= 0) && (bytes < kilobyte)) {\n\t\treturn bytes + ' B';\n\n\t} else if ((bytes >= kilobyte) && (bytes < megabyte)) {\n\t\tret = (bytes / kilobyte).toFixed(precision) + ' K';\n\n\t} else if ((bytes >= megabyte) && (bytes < gigabyte)) {\n\t\tret = (bytes / megabyte).toFixed(precision) + ' M';\n\n\t} else if ((bytes >= gigabyte) && (bytes < terabyte)) {\n\t\tret = (bytes / gigabyte).toFixed(precision) + ' G';\n\n\t} else if (bytes >= terabyte) {\n\t\tret = (bytes / terabyte).toFixed(precision) + ' T';\n\n\t} else {\n\t\treturn bytes + ' B';\n\t}\n\tif(si != 0) {\n\t\treturn ret + 'B';\n\t} else {\n\t\treturn ret + 'iB';\n\t}\n}\n\nfunction uptime() {\n\t$.getJSON(\"json/stats.json\", function(result) {\n\t\t$(\"#loading-notice\").remove();\n\t\tif(result.reload)\n\t\t\tsetTimeout(function() { location.reload(true) }, 1000);\n\n\t\tfor (var i = 0; i < result.servers.length; i++) {\n\t\t\tvar TableRow = $(\"#servers tr#r\" + i);\n\t\t\tvar ExpandRow = $(\"#servers #rt\" + i);\n\t\t\tvar hack; // fuck CSS for making me do this\n\t\t\tif(i%2) hack=\"odd\"; else hack=\"even\";\n\t\t\tif (!TableRow.length) {\n\t\t\t\t$(\"#servers\").append(\n\t\t\t\t\t\"<tr id=\\\"r\" + i + \"\\\" data-toggle=\\\"collapse\\\" data-target=\\\"#rt\" + i + \"\\\" class=\\\"accordion-toggle \" + hack + \"\\\">\" +\n\t\t\t\t\t\t\"<td id=\\\"online4\\\"><div class=\\\"progress\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-warning\\\"><small>加载中</small></div></div></td>\" +\n\t\t\t\t\t\t\"<td id=\\\"name\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<td id=\\\"type\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<!-- td id=\\\"host\\\">加载中</td -->\" +\n\t\t\t\t\t\t\"<td id=\\\"location\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<td id=\\\"uptime\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<td id=\\\"load\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<td id=\\\"network\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<td id=\\\"traffic\\\">加载中</td>\" +\n\t\t\t\t\t\t\"<td id=\\\"cpu\\\"><div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-warning\\\"><small>加载中</small></div></div></td>\" +\n\t\t\t\t\t\t\"<td id=\\\"memory\\\"><div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-warning\\\"><small>加载中</small></div></div></td>\" +\n\t\t\t\t\t\t\"<td id=\\\"hdd\\\"><div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-warning\\\"><small>加载中</small></div></div></td>\" +\n\t\t\t\t\t\"</tr>\" +\n\t\t\t\t\t\"<tr class=\\\"expandRow \" + hack + \"\\\"><td colspan=\\\"12\\\"><div class=\\\"accordian-body collapse\\\" id=\\\"rt\" + i + \"\\\">\" +\n\t\t\t\t\t\t\"<div id=\\\"expand_mem\\\">加载中</div>\" +\n\t\t\t\t\t\t\"<div id=\\\"expand_swap\\\">加载中</div>\" +\n\t\t\t\t\t\t\"<div id=\\\"expand_hdd\\\">加载中</div>\" +\n\t\t\t\t\t\t\"<div id=\\\"expand_custom\\\">加载中</div>\" +\n\t\t\t\t\t\"</div></td></tr>\"\n\t\t\t\t);\n\t\t\t\tTableRow = $(\"#servers tr#r\" + i);\n\t\t\t\tExpandRow = $(\"#servers #rt\" + i);\n\t\t\t\tserver_status[i] = true;\n\t\t\t}\n\t\t\tTableRow = TableRow[0];\n\t\t\tif(error) {\n\t\t\t\tTableRow.setAttribute(\"data-target\", \"#rt\" + i);\n\t\t\t\tserver_status[i] = true;\n\t\t\t}\n\n\t\t\t// Online4\n\t\t\tif (result.servers[i].online4) {\n\t\t\t\tTableRow.children[\"online4\"].children[0].children[0].className = \"progress-bar progress-bar-success\";\n\t\t\t\tTableRow.children[\"online4\"].children[0].children[0].innerHTML = \"<small>开启</small>\";\n\t\t\t} else {\n\t\t\t\tTableRow.children[\"online4\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\tTableRow.children[\"online4\"].children[0].children[0].innerHTML = \"<small>关闭</small>\";\n\t\t\t}\n\n\t\t\t// Online6\n\t\t\t//if (result.servers[i].online6) {\n\t\t\t//\tTableRow.children[\"online6\"].children[0].children[0].className = \"progress-bar progress-bar-success\";\n\t\t\t//\tTableRow.children[\"online6\"].children[0].children[0].innerHTML = \"<small>开启</small>\";\n\t\t\t//} else {\n\t\t\t//\tTableRow.children[\"online6\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t//\tTableRow.children[\"online6\"].children[0].children[0].innerHTML = \"<small>关闭</small>\";\n\t\t\t//}\n\n\t\t\t// Name\n\t\t\tTableRow.children[\"name\"].innerHTML = result.servers[i].name;\n\n\t\t\t// Type\n\t\t\tTableRow.children[\"type\"].innerHTML = result.servers[i].type;\n\n\t\t\t// Host\n\t\t\t//TableRow.children[\"host\"].innerHTML = result.servers[i].host;\n\n\t\t\t// Location\n\t\t\tTableRow.children[\"location\"].innerHTML = result.servers[i].location;\n\t\t\tif (!result.servers[i].online4 && !result.servers[i].online6) {\n\t\t\t\tif (server_status[i]) {\n\t\t\t\t\tTableRow.children[\"uptime\"].innerHTML = \"–\";\n\t\t\t\t\tTableRow.children[\"load\"].innerHTML = \"–\";\n\t\t\t\t\tTableRow.children[\"network\"].innerHTML = \"–\";\n\t\t\t\t\tTableRow.children[\"traffic\"].innerHTML = \"–\";\n\t\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].style.width = \"100%\";\n\t\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].innerHTML = \"<small>关闭</small>\";\n\t\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].style.width = \"100%\";\n\t\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].innerHTML = \"<small>关闭</small>\";\n\t\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].style.width = \"100%\";\n\t\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].innerHTML = \"<small>关闭</small>\";\n\t\t\t\t\tif(ExpandRow.hasClass(\"in\")) {\n\t\t\t\t\t\tExpandRow.collapse(\"hide\");\n\t\t\t\t\t}\n\t\t\t\t\tTableRow.setAttribute(\"data-target\", \"\");\n\t\t\t\t\tserver_status[i] = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!server_status[i]) {\n\t\t\t\t\tTableRow.setAttribute(\"data-target\", \"#rt\" + i);\n\t\t\t\t\tserver_status[i] = true;\n\t\t\t\t}\n\n\t\t\t\t// Uptime\n\t\t\t\tTableRow.children[\"uptime\"].innerHTML = result.servers[i].uptime;\n\n\t\t\t\t// Load\n\t\t\t\tif(result.servers[i].load == -1) {\n\t\t\t\t\tTableRow.children[\"load\"].innerHTML = \"–\";\n\t\t\t\t} else {\n\t\t\t\t\tTableRow.children[\"load\"].innerHTML = result.servers[i].load;\n\t\t\t\t}\n\n\t\t\t\t// Network\n\t\t\t\tvar netstr = \"\";\n\t\t\t\tif(result.servers[i].network_rx < 1000)\n\t\t\t\t\tnetstr += result.servers[i].network_rx.toFixed(0) + \"B\";\n\t\t\t\telse if(result.servers[i].network_rx < 1000*1000)\n\t\t\t\t\tnetstr += (result.servers[i].network_rx/1000).toFixed(0) + \"K\";\n\t\t\t\telse\n\t\t\t\t\tnetstr += (result.servers[i].network_rx/1000/1000).toFixed(1) + \"M\";\n\t\t\t\tnetstr += \" | \"\n\t\t\t\tif(result.servers[i].network_tx < 1000)\n\t\t\t\t\tnetstr += result.servers[i].network_tx.toFixed(0) + \"B\";\n\t\t\t\telse if(result.servers[i].network_tx < 1000*1000)\n\t\t\t\t\tnetstr += (result.servers[i].network_tx/1000).toFixed(0) + \"K\";\n\t\t\t\telse\n\t\t\t\t\tnetstr += (result.servers[i].network_tx/1000/1000).toFixed(1) + \"M\";\n\t\t\t\tTableRow.children[\"network\"].innerHTML = netstr;\n\n\t\t\t\t//Traffic\n\t\t\t\tvar trafficstr = \"\";\n\t\t\t\tif(result.servers[i].network_in < 1024)\n\t\t\t\t\ttrafficstr += result.servers[i].network_in.toFixed(0) + \"B\";\n\t\t\t\telse if(result.servers[i].network_in < 1024*1024)\n\t\t\t\t\ttrafficstr += (result.servers[i].network_in/1024).toFixed(0) + \"K\";\n\t\t\t\telse if(result.servers[i].network_in < 1024*1024*1024)\n\t\t\t\t\ttrafficstr += (result.servers[i].network_in/1024/1024).toFixed(1) + \"M\";\n\t\t\t\telse if(result.servers[i].network_in < 1024*1024*1024*1024)\n\t\t\t\t\ttrafficstr += (result.servers[i].network_in/1024/1024/1024).toFixed(2) + \"G\";\n\t\t\t\telse\n\t\t\t\t\ttrafficstr += (result.servers[i].network_in/1024/1024/1024/1024).toFixed(2) + \"T\";\n\t\t\t\ttrafficstr += \" | \"\n\t\t\t\tif(result.servers[i].network_out < 1024)\n\t\t\t\t\ttrafficstr += result.servers[i].network_out.toFixed(0) + \"B\";\n\t\t\t\telse if(result.servers[i].network_out < 1024*1024)\n\t\t\t\t\ttrafficstr += (result.servers[i].network_out/1024).toFixed(0) + \"K\";\n\t\t\t\telse if(result.servers[i].network_out < 1024*1024*1024)\n\t\t\t\t\ttrafficstr += (result.servers[i].network_out/1024/1024).toFixed(1) + \"M\";\n\t\t\t\telse if(result.servers[i].network_out < 1024*1024*1024*1024)\n\t\t\t\t\ttrafficstr += (result.servers[i].network_out/1024/1024/1024).toFixed(2) + \"G\";\n\t\t\t\telse\n\t\t\t\t\ttrafficstr += (result.servers[i].network_out/1024/1024/1024/1024).toFixed(2) + \"T\";\n\t\t\t\tTableRow.children[\"traffic\"].innerHTML = trafficstr;\n\n\t\t\t\t// CPU\n\t\t\t\tif (result.servers[i].cpu >= 90)\n\t\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\telse if (result.servers[i].cpu >= 80)\n\t\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].className = \"progress-bar progress-bar-warning\";\n\t\t\t\telse\n\t\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].className = \"progress-bar progress-bar-success\";\n\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].style.width = result.servers[i].cpu + \"%\";\n\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].innerHTML = result.servers[i].cpu + \"%\";\n\n\t\t\t\t// Memory\n\t\t\t\tvar Mem = ((result.servers[i].memory_used/result.servers[i].memory_total)*100.0).toFixed(0);\n\t\t\t\tif (Mem >= 90)\n\t\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\telse if (Mem >= 80)\n\t\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].className = \"progress-bar progress-bar-warning\";\n\t\t\t\telse\n\t\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].className = \"progress-bar progress-bar-success\";\n\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].style.width = Mem + \"%\";\n\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].innerHTML = Mem + \"%\";\n\t\t\t\tExpandRow[0].children[\"expand_mem\"].innerHTML = \"内存信息: \" + bytesToSize(result.servers[i].memory_used*1024, 2) + \" / \" + bytesToSize(result.servers[i].memory_total*1024, 2);\n\t\t\t\t// Swap\n\t\t\t\tExpandRow[0].children[\"expand_swap\"].innerHTML = \"交换分区: \" + bytesToSize(result.servers[i].swap_used*1024, 2) + \" / \" + bytesToSize(result.servers[i].swap_total*1024, 2);\n\n\t\t\t\t// HDD\n\t\t\t\tvar HDD = ((result.servers[i].hdd_used/result.servers[i].hdd_total)*100.0).toFixed(0);\n\t\t\t\tif (HDD >= 90)\n\t\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].className = \"progress-bar progress-bar-danger\";\n\t\t\t\telse if (HDD >= 80)\n\t\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].className = \"progress-bar progress-bar-warning\";\n\t\t\t\telse\n\t\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].className = \"progress-bar progress-bar-success\";\n\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].style.width = HDD + \"%\";\n\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].innerHTML = HDD + \"%\";\n\t\t\t\tExpandRow[0].children[\"expand_hdd\"].innerHTML = \"硬盘信息: \" + bytesToSize(result.servers[i].hdd_used*1024*1024, 2) + \" / \" + bytesToSize(result.servers[i].hdd_total*1024*1024, 2);\n\n\t\t\t\t// Custom\n\t\t\t\tif (result.servers[i].custom) {\n\t\t\t\t\tExpandRow[0].children[\"expand_custom\"].innerHTML = result.servers[i].custom\n\t\t\t\t} else {\n\t\t\t\t\tExpandRow[0].children[\"expand_custom\"].innerHTML = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\td = new Date(result.updated*1000);\n\t\terror = 0;\n\t}).fail(function(update_error) {\n\t\tif (!error) {\n\t\t\t$(\"#servers > tr.accordion-toggle\").each(function(i) {\n\t\t\t\tvar TableRow = $(\"#servers tr#r\" + i)[0];\n\t\t\t\tvar ExpandRow = $(\"#servers #rt\" + i);\n\t\t\t\tTableRow.children[\"online4\"].children[0].children[0].className = \"progress-bar progress-bar-error\";\n\t\t\t\tTableRow.children[\"online4\"].children[0].children[0].innerHTML = \"<small>错误</small>\";\n\t\t\t\t//TableRow.children[\"online6\"].children[0].children[0].className = \"progress-bar progress-bar-error\";\n\t\t\t\t//TableRow.children[\"online6\"].children[0].children[0].innerHTML = \"<small>错误</small>\";\n\t\t\t\tTableRow.children[\"uptime\"].innerHTML = \"<div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-error\\\"><small>错误</small></div></div>\";\n\t\t\t\tTableRow.children[\"load\"].innerHTML = \"<div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-error\\\"><small>错误</small></div></div>\";\n\t\t\t\tTableRow.children[\"network\"].innerHTML = \"<div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-error\\\"><small>错误</small></div></div>\";\n\t\t\t\tTableRow.children[\"traffic\"].innerHTML = \"<div class=\\\"progress progress-striped active\\\"><div style=\\\"width: 100%;\\\" class=\\\"progress-bar progress-bar-error\\\"><small>错误</small></div></div>\";\n\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].className = \"progress-bar progress-bar-error\";\n\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].style.width = \"100%\";\n\t\t\t\tTableRow.children[\"cpu\"].children[0].children[0].innerHTML = \"<small>错误</small>\";\n\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].className = \"progress-bar progress-bar-error\";\n\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].style.width = \"100%\";\n\t\t\t\tTableRow.children[\"memory\"].children[0].children[0].innerHTML = \"<small>错误</small>\";\n\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].className = \"progress-bar progress-bar-error\";\n\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].style.width = \"100%\";\n\t\t\t\tTableRow.children[\"hdd\"].children[0].children[0].innerHTML = \"<small>错误</small>\";\n\t\t\t\tif(ExpandRow.hasClass(\"in\")) {\n\t\t\t\t\tExpandRow.collapse(\"hide\");\n\t\t\t\t}\n\t\t\t\tTableRow.setAttribute(\"data-target\", \"\");\n\t\t\t\tserver_status[i] = false;\n\t\t\t});\n\t\t}\n\t\terror = 1;\n\t\t$(\"#updated\").html(\"更新错误.\");\n\t});\n}\n\nfunction updateTime() {\n\tif (!error)\n\t\t$(\"#updated\").html(\"最后更新: \" + timeSince(d));\n}\n\nuptime();\nupdateTime();\nsetInterval(uptime, 2000);\nsetInterval(updateTime, 500);\n\n\n// styleswitcher.js\nfunction setActiveStyleSheet(title) {\n\tvar i, a, main;\n\tfor(i=0; (a = document.getElementsByTagName(\"link\")[i]); i++) {\n\t\tif(a.getAttribute(\"rel\").indexOf(\"style\") != -1 && a.getAttribute(\"title\")) {\n\t\t\ta.disabled = true;\n\t\t\tif(a.getAttribute(\"title\") == title) a.disabled = false;\n\t\t}\n\t}\n}\n\nfunction getActiveStyleSheet() {\n\tvar i, a;\n\tfor(i=0; (a = document.getElementsByTagName(\"link\")[i]); i++) {\n\t\tif(a.getAttribute(\"rel\").indexOf(\"style\") != -1 && a.getAttribute(\"title\") && !a.disabled)\n\t\t\treturn a.getAttribute(\"title\");\n\t}\n\treturn null;\n}\n\nfunction getPreferredStyleSheet() {\n\tvar i, a;\n\tfor(i=0; (a = document.getElementsByTagName(\"link\")[i]); i++) {\n\t\tif(a.getAttribute(\"rel\").indexOf(\"style\") != -1\t&& a.getAttribute(\"rel\").indexOf(\"alt\") == -1 && a.getAttribute(\"title\"))\n\t\t\treturn a.getAttribute(\"title\");\n\t}\nreturn null;\n}\n\nfunction createCookie(name,value,days) {\n\tif (days) {\n\t\tvar date = new Date();\n\t\tdate.setTime(date.getTime()+(days*24*60*60*1000));\n\t\tvar expires = \"; expires=\"+date.toGMTString();\n\t}\n\telse expires = \"\";\n\tdocument.cookie = name+\"=\"+value+expires+\"; path=/\";\n}\n\nfunction readCookie(name) {\n\tvar nameEQ = name + \"=\";\n\tvar ca = document.cookie.split(';');\n\tfor(var i=0;i < ca.length;i++) {\n\t\tvar c = ca[i];\n\t\twhile (c.charAt(0)==' ')\n\t\t\tc = c.substring(1,c.length);\n\t\tif (c.indexOf(nameEQ) == 0)\n\t\t\treturn c.substring(nameEQ.length,c.length);\n\t}\n\treturn null;\n}\n\nwindow.onload = function(e) {\n\tvar cookie = readCookie(\"style\");\n\tvar title = cookie ? cookie : getPreferredStyleSheet();\n\tsetActiveStyleSheet(title);\n}\n\nwindow.onunload = function(e) {\n\tvar title = getActiveStyleSheet();\n\tcreateCookie(\"style\", title, 365);\n}\n\nvar cookie = readCookie(\"style\");\nvar title = cookie ? cookie : getPreferredStyleSheet();\nsetActiveStyleSheet(title);\n"
  },
  {
    "path": "web/json/.gitignore",
    "content": "stats.json\nstats.json~"
  },
  {
    "path": "web/robots.txt",
    "content": "# robots.txt generated at http://tool.chinaz.com/robots/ \nUser-agent: Baiduspider\nDisallow: /\nUser-agent: Sosospider\nDisallow: /\nUser-agent: sogou spider\nDisallow: /\nUser-agent: YodaoBot\nDisallow: /\nUser-agent: Googlebot\nDisallow: /\nUser-agent: Bingbot\nDisallow: /\nUser-agent: Slurp\nDisallow: /\nUser-agent: Teoma\nDisallow: /\nUser-agent: ia_archiver\nDisallow: /\nUser-agent: twiceler\nDisallow: /\nUser-agent: MSNBot\nDisallow: /\nUser-agent: Scrubby\nDisallow: /\nUser-agent: Robozilla\nDisallow: /\nUser-agent: Gigabot\nDisallow: /\nUser-agent: googlebot-image\nDisallow: /\nUser-agent: googlebot-mobile\nDisallow: /\nUser-agent: yahoo-mmcrawler\nDisallow: /\nUser-agent: yahoo-blogs/v3.9\nDisallow: /\nUser-agent: psbot\nDisallow: /\nDisallow: /ip/\nDisallow: /qr/\nDisallow: /"
  }
]