Initial commit

This commit is contained in:
2020-01-18 05:51:45 +08:00
Unverified
commit e0da205b34
1758 changed files with 550610 additions and 0 deletions
+296
View File
@@ -0,0 +1,296 @@
<?php
namespace SakuraPanel;
class Database {
public function __construct()
{
global $_config, $conn;
$conn = mysqli_connect(
$_config['db_host'],
$_config['db_user'],
$_config['db_pass'],
$_config['db_name'],
$_config['db_port']
);
if($conn) {
mysqli_set_charset($conn, $_config['db_code']);
mysqli_select_db($conn, $_config['db_name']);
}
}
public static function query($table, $query, $mode = "AND", $raw = false)
{
global $conn;
$mode = $mode == "" ? "AND" : $mode;
if(isset($table) && $table !== "") {
if(!$raw && is_array($query)) {
$querySQL = "";
$i = 0;
$total = count($query);
foreach($query as $key => $value) {
$i++;
$key = mysqli_real_escape_string($conn, $key);
$value = mysqli_real_escape_string($conn, $value);
$querySQL .= "`{$key}`='{$value}'";
if($i < $total) {
$querySQL .= " {$mode} ";
}
}
if($total > 0) {
$querySQL = " WHERE {$querySQL}";
}
$table = mysqli_real_escape_string($conn, $table);
$querySQL = "SELECT * FROM `{$table}`{$querySQL}";
$result = mysqli_query($conn, $querySQL);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return $result;
}
} else {
$result = mysqli_query($conn, $query);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return $result;
}
}
} else {
return false;
}
}
public static function update($table, $data, $query, $mode = "AND", $raw = false)
{
global $conn;
$mode = $mode == "" ? "AND" : $mode;
if(isset($table) && $table !== "") {
if(!$raw && is_array($query) && is_array($data)) {
// 处理要更新的数据
$updateSQL = "";
$i = 0;
$total = count($data);
foreach($data as $key => $value) {
$i++;
$key = mysqli_real_escape_string($conn, $key);
$value = mysqli_real_escape_string($conn, $value);
$updateSQL .= "`{$key}`='{$value}'";
if($i < $total) {
$updateSQL .= ", ";
}
}
// 处理查询部分
$querySQL = "";
$i = 0;
$total = count($query);
foreach($query as $key => $value) {
$i++;
$key = mysqli_real_escape_string($conn, $key);
$value = mysqli_real_escape_string($conn, $value);
$querySQL .= "`{$key}`='{$value}'";
if($i < $total) {
$querySQL .= " {$mode} ";
}
}
if($total > 0) {
$querySQL = " WHERE {$querySQL}";
}
$table = mysqli_real_escape_string($conn, $table);
$querySQL = "UPDATE `{$table}` SET {$updateSQL}{$querySQL}";
mysqli_query($conn, $querySQL);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return true;
}
} else {
mysqli_query($conn, $query);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return true;
}
}
} else {
return false;
}
}
public static function delete($table, $query, $mode = "AND", $raw = false)
{
global $conn;
$mode = $mode == "" ? "AND" : $mode;
if(isset($table) && $table !== "") {
if(!$raw && is_array($query)) {
$querySQL = "";
$i = 0;
$total = count($query);
foreach($query as $key => $value) {
$i++;
$key = mysqli_real_escape_string($conn, $key);
$value = mysqli_real_escape_string($conn, $value);
$querySQL .= "`{$key}`='{$value}'";
if($i < $total) {
$querySQL .= " {$mode} ";
}
}
if($total > 0) {
$querySQL = " WHERE {$querySQL}";
}
$table = mysqli_real_escape_string($conn, $table);
$querySQL = "DELETE FROM `{$table}`{$querySQL}";
mysqli_query($conn, $querySQL);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return true;
}
} else {
mysqli_query($conn, $query);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return true;
}
}
} else {
return false;
}
}
public static function search($table, $query, $mode = "AND", $raw = false)
{
global $conn;
$mode = $mode == "" ? "AND" : $mode;
if(isset($table) && $table !== "") {
if(!$raw && is_array($query)) {
$querySQL = "";
$i = 0;
$total = count($query);
foreach($query as $key => $value) {
$i++;
$key = mysqli_real_escape_string($conn, $key);
$value = mysqli_real_escape_string($conn, $value);
$querySQL .= "POSITION('{$value}' IN `{$key}`)";
if($i < $total) {
$querySQL .= " {$mode} ";
}
}
if($total > 0) {
$querySQL = " WHERE {$querySQL}";
}
$table = mysqli_real_escape_string($conn, $table);
$querySQL = "SELECT * FROM `{$table}`{$querySQL}";
$result = mysqli_query($conn, $querySQL);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return $result;
}
} else {
$result = mysqli_query($conn, $query);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return $result;
}
}
} else {
return false;
}
}
public static function insert($table, $query, $raw = false)
{
global $conn;
if(isset($table) && $table !== "") {
if(!$raw && is_array($query)) {
$queryKey = "";
$queryValue = "";
$i = 0;
$total = count($query);
foreach($query as $key => $value) {
$i++;
$svalue = $value;
$key = mysqli_real_escape_string($conn, $key);
$value = mysqli_real_escape_string($conn, $value);
$queryKey .= "`{$key}`";
$queryValue .= $svalue === null ? "NULL" : "'{$value}'";
if($i < $total) {
$queryKey .= ", ";
$queryValue .= ", ";
}
}
$table = mysqli_real_escape_string($conn, $table);
$querySQL = "INSERT INTO `{$table}` ({$queryKey}) VALUES ({$queryValue})";
mysqli_query($conn, $querySQL);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return true;
}
} else {
mysqli_query($conn, $query);
$error = mysqli_error($conn);
if(!empty($error)) {
return $error;
} else {
return true;
}
}
} else {
return false;
}
}
public static function querySingleLine($table, $query, $raw = false)
{
global $conn;
return mysqli_fetch_array(Database::query($table, $query, $raw));
}
public static function toArray($result)
{
$data = Array();
while($rw = mysqli_fetch_row($result)) {
$data[] = $rw;
}
return $data;
}
public static function fetchError()
{
global $conn;
return mysqli_error($conn);
}
public static function escape($str)
{
global $conn;
return mysqli_real_escape_string($conn, $str);
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
namespace SakuraPanel;
use SakuraPanel;
class NodeManager {
public function closeClient($node, $token)
{
$ninfo = $this->getNodeInfo($node);
if($ninfo) {
$result = SakuraPanel\Utils::http("http://admin:{$ninfo['admin_pass']}@{$ninfo['ip']}:{$ninfo['admin_port']}/api/client/close/{$token}");
if(isset($result['body'])) {
$json = json_decode($result['body'], true);
if(is_array($json)) {
if($json['status'] == 200) {
return true;
} else {
return $json['message'];
}
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
public function getUserNode($group)
{
return Database::toArray(Database::search("nodes", Array("group" => "{$group};")));
}
public function isNodeExist($node)
{
return Database::querySingleLine("nodes", Array("id" => $node)) ? true : false;
}
public function getNodeInfo($node)
{
return Database::querySingleLine("nodes", Array("id" => $node));
}
public function updateNode($id, $data)
{
if($this->getNodeInfo($id)) {
return Database::update("nodes", $data, Array("id" => $id));
} else {
return false;
}
}
public function getTotalNodes()
{
$rs = Database::toArray(Database::query("nodes", Array()));
return count($rs);
}
public function addNode($data)
{
return Database::insert("nodes", $data);
}
public function deleteNode($data)
{
$result = false;
if(is_array($this->getNodeInfo($data))) {
$result = Database::delete("proxies", Array("node" => $data));
if($result !== true) {
return $result;
}
return Database::delete("nodes", Array("id" => $data));
} else {
return false;
}
}
}
Executable
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace SakuraPanel;
class Pages {
public function loadPage($name, $data = null)
{
if(file_exists(ROOT . "/pages/{$name}.php")) {
include(ROOT . "/pages/{$name}.php");
} else {
include(ROOT . "/pages/404.php");
}
}
public function loadModule($name, $data = null)
{
if(file_exists(ROOT . "/modules/{$name}.php")) {
include(ROOT . "/modules/{$name}.php");
} else {
include(ROOT . "/modules/404.php");
}
}
}
+1694
View File
File diff suppressed because it is too large Load Diff
+372
View File
@@ -0,0 +1,372 @@
<?php
namespace SakuraPanel;
use SakuraPanel;
class PostHandler {
public function switcher($params)
{
global $_config;
if(isset($params['action']) && preg_match("/^[A-Za-z0-9\_\-]{1,20}$/", $params['action'])) {
switch($params['action']) {
case "login":
$um = new SakuraPanel\UserManager();
$pages = new SakuraPanel\Pages();
if($_config['recaptcha']['enable']) {
if(!isset($_POST["g-recaptcha-response"]) || !Utils::reCAPTCHA($_POST["g-recaptcha-response"])) {
$data = Array("status" => false, "message" => "reCAPTCHA 验证失败,请刷新重试");
$pages->loadPage("login", $data);
exit;
}
}
$data = $um->doLogin($_POST);
if(isset($data['status']) && $data['status'] === true) {
$_SESSION['user'] = $data['username'];
$_SESSION['mail'] = $data['email'];
$_SESSION['token'] = md5(mt_rand(0, 999999) . time() . $data['username']);
exit("<script>location='?page=panel';</script>");
}
$pages->loadPage("login", $data);
break;
case "register":
$um = new SakuraPanel\UserManager();
$pages = new SakuraPanel\Pages();
if($_config['recaptcha']['enable']) {
if(!isset($_POST["g-recaptcha-response"]) || !Utils::reCAPTCHA($_POST["g-recaptcha-response"])) {
$data = Array("status" => false, "message" => "reCAPTCHA 验证失败,请刷新重试");
$pages->loadPage("register", $data);
exit;
}
}
$data = $um->doRegister($_POST);
$pages->loadPage("register", $data);
break;
case "sendmail":
$um = new SakuraPanel\UserManager();
if(!$_config['smtp']['enable']) {
exit("本站未开启 SMTP 服务!");
}
if(isset($_SESSION['reg_wait'])) {
if(time() - $_SESSION['reg_wait'] < 60) {
exit("您的操作过于频繁,请稍后再试。");
}
}
if(!isset($_POST['mail']) || $_POST['mail'] == "") {
exit("请填写邮箱!");
}
if(!$um->checkEmail($_POST['mail'])) {
exit("不正确的邮箱格式!");
}
$rand = mt_rand(100000, 999999);
$_SESSION['reg_verifycode'] = $rand;
$_SESSION['reg_wait'] = time();
$_SESSION['reg_email'] = $_POST['mail'];
$um->sendRegisterEmail($_POST['mail'], $rand);
exit("系统已发送一封邮件至您的邮箱,请查收。");
break;
case "findpass":
$um = new SakuraPanel\UserManager();
$pages = new SakuraPanel\Pages();
if($_config['recaptcha']['enable']) {
if(!isset($_POST["g-recaptcha-response"]) || !Utils::reCAPTCHA($_POST["g-recaptcha-response"])) {
$data = Array("status" => false, "message" => "reCAPTCHA 验证失败,请刷新重试");
$pages->loadPage("findpass", $data);
exit;
}
}
$data = $um->doFindpass($_POST);
$pages->loadPage("findpass", $data);
break;
case "addproxy":
$um = new SakuraPanel\UserManager();
$pm = new SakuraPanel\ProxyManager();
if($um->isLogged()) {
$result = $pm->checkRules($_POST);
if(is_array($result) && isset($result[0])) {
if($result[0]) {
if($pm->addProxy($_POST)) {
exit("隧道创建成功");
} else {
exit("隧道创建失败,请联系管理员:" . Database::fetchError());
}
} else {
$msg = $result[1] ?? "未知错误";
exit($msg);
}
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "updatepass":
$um = new SakuraPanel\UserManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
if(!isset($_POST['oldpass']) || !isset($_POST['newpass']) || !isset($_POST['newpass1'])
|| $_POST['oldpass'] == "" || $_POST['newpass'] == "" || $_POST['newpass1'] == "") {
exit("<script>alert('不完整的信息,请重新填写');location='?page=panel&module=profile';</script>");
}
$us = $um->getInfoByUser($_SESSION['user']);
if($um->checkPassword($_POST['oldpass'], $us['password'])) {
if(strlen($_POST['newpass']) < 5) exit("<script>alert('新密码不能少于 5 个字符,请重新输入');location='?page=panel&module=profile';</script>");
if($_POST['newpass'] !== $_POST['newpass1']) exit("<script>alert('两次输入的密码不一致');location='?page=panel&module=profile';</script>");
$password = $um->generatePassword($_POST['newpass']);
$token = substr(md5(sha1(md5($_SESSION['user']) . md5($password) . time() . mt_rand(0, 9999999))), 0, 16);
// 更新数据库
Database::update("users", Array("password" => $password), Array("username" => $_SESSION['user']));
Database::update("tokens", Array("token" => $token), Array("username" => $_SESSION['user']));
unset($_SESSION['user']);
unset($_SESSION['mail']);
unset($_SESSION['token']);
exit("<script>alert('密码修改成功,请重新登录。');location='?';</script>");
} else {
exit("<script>alert('旧密码错误,请检查');location='?page=panel&module=profile';</script>");
}
} else {
exit("<script>alert('登录会话已超时,请重新登录');location='?';</script>");
}
break;
case "updateuser":
$um = new SakuraPanel\UserManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
$us = $um->getInfoByUser($_SESSION['user']);
if($us['group'] == "admin") {
$valid = SakuraPanel\Regex::isValid($_POST, [
'id' => SakuraPanel\Regex::TYPE_NUMBER,
'traffic' => SakuraPanel\Regex::TYPE_NUMBER,
'proxies' => SakuraPanel\Regex::TYPE_NUMBER,
'group' => SakuraPanel\Regex::TYPE_LETTER,
'status' => SakuraPanel\Regex::TYPE_NUMBER,
]);
if($valid === true) {
$update = $um->updateUser($_POST['id'], [
'traffic' => $_POST['traffic'],
'proxies' => $_POST['proxies'],
'inbound' => $_POST['inbound'] ?? "",
'outbound' => $_POST['outbound'] ?? "",
'group' => $_POST['group'],
'status' => $_POST['status'],
]);
if($update === true) {
exit("用户资料更新成功!");
} else {
Header("HTTP/1.1 404 Not Found");
exit("该用户不存在!{$update}");
}
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!{$valid}");
}
} else {
exit("你没有足够的权限这么做");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "updatenode":
$um = new SakuraPanel\UserManager();
$nm = new SakuraPanel\NodeManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
$us = $um->getInfoByUser($_SESSION['user']);
if($us['group'] == "admin") {
$valid = SakuraPanel\Regex::isValid($_POST, [
'id' => SakuraPanel\Regex::TYPE_NUMBER,
'name' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'description' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'hostname' => SakuraPanel\Regex::TYPE_HOSTNAME,
'ip' => SakuraPanel\Regex::TYPE_IPV4_V6,
'port' => SakuraPanel\Regex::TYPE_NUMBER,
'admin_port' => SakuraPanel\Regex::TYPE_NUMBER,
'admin_pass' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'token' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'group' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'status' => SakuraPanel\Regex::TYPE_NUMBER,
]);
if($valid === true) {
$update = $nm->updateNode($_POST['id'], [
'id' => $_POST['id'],
'name' => $_POST['name'],
'description' => $_POST['description'],
'hostname' => $_POST['hostname'],
'ip' => $_POST['ip'],
'port' => $_POST['port'],
'admin_port' => $_POST['admin_port'],
'admin_pass' => $_POST['admin_pass'],
'token' => $_POST['token'],
'group' => $_POST['group'],
'status' => $_POST['status'],
]);
if($update === true) {
exit("节点信息更新成功!");
} else {
Header("HTTP/1.1 404 Not Found");
exit("该节点不存在!{$update}");
}
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!{$valid}");
}
} else {
exit("你没有足够的权限这么做");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "addnode":
$um = new SakuraPanel\UserManager();
$nm = new SakuraPanel\NodeManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
$us = $um->getInfoByUser($_SESSION['user']);
if($us['group'] == "admin") {
$valid = SakuraPanel\Regex::isValid($_POST, [
'name' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'description' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'hostname' => SakuraPanel\Regex::TYPE_HOSTNAME,
'ip' => SakuraPanel\Regex::TYPE_IPV4_V6,
'port' => SakuraPanel\Regex::TYPE_NUMBER,
'admin_port' => SakuraPanel\Regex::TYPE_NUMBER,
'admin_pass' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'token' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'group' => SakuraPanel\Regex::TYPE_NOTEMPTY,
'status' => SakuraPanel\Regex::TYPE_NUMBER,
]);
if($valid === true) {
$update = $nm->addNode([
'name' => $_POST['name'],
'description' => $_POST['description'],
'hostname' => $_POST['hostname'],
'ip' => $_POST['ip'],
'port' => $_POST['port'],
'admin_port' => $_POST['admin_port'],
'admin_pass' => $_POST['admin_pass'],
'token' => $_POST['token'],
'group' => $_POST['group'],
'status' => $_POST['status'],
]);
if($update === true) {
exit("节点添加成功!");
} else {
Header("HTTP/1.1 404 Not Found");
exit("节点添加失败!{$update}");
}
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!{$valid}");
}
} else {
exit("你没有足够的权限这么做");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "deletenode":
$um = new SakuraPanel\UserManager();
$nm = new SakuraPanel\NodeManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
$us = $um->getInfoByUser($_SESSION['user']);
if($us['group'] == "admin") {
if(SakuraPanel\Regex::isValid($_POST, [
"id" => SakuraPanel\Regex::TYPE_NUMBER
]) === true) {
$result = $nm->deleteNode($_POST['id']);
if($result === true) {
exit("节点删除成功!");
} else {
Header("HTTP/1.1 404 Not Found");
exit("节点删除失败!{$result}");
}
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!{$valid}");
}
} else {
exit("你没有足够的权限这么做");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "updatebroadcast":
$um = new SakuraPanel\UserManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
$us = $um->getInfoByUser($_SESSION['user']);
if($us['group'] == "admin") {
if(isset($_POST['data'])) {
$result = SakuraPanel\Settings::set("broadcast", $_POST['data']);
if($result === true) {
exit("公告更新成功!");
} else {
exit("数据更新失败!{$result}");
}
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!");
}
} else {
exit("你没有足够的权限这么做");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "updatehelpinfo":
$um = new SakuraPanel\UserManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
$us = $um->getInfoByUser($_SESSION['user']);
if($us['group'] == "admin") {
if(isset($_POST['data'])) {
$result = SakuraPanel\Settings::set("helpinfo", $_POST['data']);
if($result === true) {
exit("帮助更新成功!");
} else {
exit("数据更新失败!{$result}");
}
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!");
}
} else {
exit("你没有足够的权限这么做");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
case "preview":
$um = new SakuraPanel\UserManager();
if($um->isLogged()) {
SakuraPanel\Utils::checkCsrf();
include(ROOT . "/core/Parsedown.php");
$markdown = new Parsedown();
$markdown->setSafeMode(true);
$markdown->setBreaksEnabled(true);
$markdown->setUrlsLinked(true);
if(isset($_POST['data'])) {
exit($markdown->text($_POST['data']));
} else {
Header("HTTP/1.1 404 Not Found");
exit("提交的数据不合法!");
}
} else {
exit("登录会话已超时,请重新登录");
}
break;
default:
Header("HTTP/1.1 404 Not Found");
exit("Undefined action {$params['action']}");
}
}
}
}
+353
View File
@@ -0,0 +1,353 @@
<?php
namespace SakuraPanel;
use SakuraPanel;
class ProxyManager {
public function getProxyInfo($id)
{
return Database::querySingleLine("proxies", Array("id" => $id));
}
public function getUserProxies($username)
{
$rs = Database::query("proxies", Array("username" => $username));
if($rs) {
$rs = Database::toArray($rs);
return count($rs);
} else {
return -1;
}
}
public function getProxiesByNode($node)
{
$rs = Database::query("proxies", Array("node" => $node));
if($rs) {
return Database::toArray($rs);
} else {
return [];
}
}
public function getUserProxiesList($username)
{
$rs = Database::query("proxies", Array("username" => $username));
if($rs) {
return Database::toArray($rs);
} else {
return [];
}
}
public function getTotalProxies()
{
$rs = Database::toArray(Database::query("proxies", Array()));
return count($rs);
}
public function getRandomPort()
{
global $_config;
$portList = [];
for($i = $_config['proxies']['min'];$i < $_config['proxies']['max'];$i++) {
$portList[$i] = $i;
}
$rs = Database::toArray(Database::query("proxies", Array()));
foreach($rs as $proxy) {
if(!empty($proxy['remote_port'])) {
unset($portList[$proxy['remote_port']]);
}
}
$resultList = [];
foreach($portList as $port) {
$resultList[] = $port;
}
return $resultList[mt_rand(0, count($resultList) - 1)];
}
public function isPortAvailable($port, $proxy_type, $node)
{
return Database::querySingleLine("proxies", Array("remote_port" => $port,
"proxy_type" => $proxy_type, "node" => $node)) ? false : true;
}
public function isDomainAvailable($domain, $proxy_type, $node)
{
return Database::querySingleLine("proxies", Array("domain" => "[\"{$domain}\"]",
"proxy_type" => $proxy_type, "node" => $node)) ? false : true;
}
public function isProxyNameExist($name)
{
return Database::querySingleLine("proxies", Array("username" => $_SESSION['user'],
"proxy_name" => $name)) ? true : false;
}
public function addProxy($data)
{
$username = $_SESSION['user'];
$proxy_name = $data['proxy_name'] ?? "MyProxy";
$proxy_type = $data['proxy_type'] ?? "tcp";
$local_ip = $data['local_ip'] ?? "127.0.0.1";
$local_port = $data['local_port'] ?? "80";
$use_encryption = $data['use_encryption'] ?? "false";
$use_compression = $data['use_compression'] ?? "false";
$domain = $data['domain'] ? "[\"{$data['domain']}\"]" : "";
$locations = $data['locations'] ?? "";
$host_header_rewrite = $data['host_header_rewrite'] ?? "";
$header_X_From_Where = $data['header_X_From_Where'] ?? "";
$remote_port = $data['remote_port'] ?? "";
$sk = $data['sk'] ?? "";
$status = 0;
$lastupdate = time();
$node = $data['node'];
return Database::insert("proxies", Array(
"id" => null,
"username" => $username,
"proxy_name" => $proxy_name,
"proxy_type" => $proxy_type,
"local_ip" => $local_ip,
"local_port" => $local_port,
"use_encryption" => $use_encryption,
"use_compression" => $use_compression,
"domain" => $domain,
"locations" => $locations,
"host_header_rewrite" => $host_header_rewrite,
"header_X-From-Where" => $header_X_From_Where,
"remote_port" => $remote_port,
"sk" => $sk,
"status" => $status,
"lastupdate" => $lastupdate,
"node" => $node
));
}
// 基本上对所有的选项都进行了验证
// 如有需要可以自己修改验证的规则
public function checkRules($data)
{
global $_config;
$nm = new SakuraPanel\NodeManager();
$um = new SakuraPanel\UserManager();
$_list = Array("node", "proxy_name", "proxy_type", "local_ip", "local_port", "remote_port", "domain");
$_type = Array("tcp", "udp", "http", "https", "xtcp", "stcp");
$max_proxies = Intval($um->getInfoByUser($_SESSION['user'])['proxies']);
if($this->getUserProxies($_SESSION['user']) >= $max_proxies && $max_proxies !== -1) {
return Array(false, "您已经达到限制,不能再创建更多隧道了。");
}
foreach($_list as $_item) {
if(!isset($data[$_item])) {
return Array(false, "基础信息部分所有选项都是必填的。");
}
}
if(!Regex::isNumber($data['node'])) {
return Array(false, "请求不合法,无效的节点");
}
if(!$nm->isNodeExist($data['node'])) {
return Array(false, "请求不合法,节点不存在");
}
if(!Regex::isProxyName($data['proxy_name'])) {
return Array(false, "隧道名称不合法,必须是英文和数字以及下划线组成");
}
if($this->isProxyNameExist($data['proxy_name'])) {
return Array(false, "隧道 {$data['proxy_name']} 已存在,请使用其他名字");
}
if(!in_array($data['proxy_type'], $_type)) {
return Array(false, "请求不合法,无效的隧道类型");
}
if(!filter_var($data['local_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return Array(false, "本地地址不合法,不是一个有效的 IPv4 地址");
}
if(!Regex::isNumber($data['local_port']) || Intval($data['local_port']) < 0 || Intval($data['local_port']) > 65535) {
return Array(false, "本地端口不合法,必须是数字且大于 <code>0</code> 小于 <code>65535</code>。");
}
if($data['proxy_type'] !== "http" && $data['proxy_type'] !== "https") {
if(!isset($data['remote_port']) || $data['remote_port'] == "") {
return Array(false, strtoupper($data['proxy_type']) . " 类型的隧道必须要指定一个远程端口");
}
if(!Regex::isNumber($data['remote_port']) || Intval($data['remote_port']) < $_config['proxies']['min']
|| Intval($data['remote_port']) > $_config['proxies']['max']) {
return Array(false, "远程端口不合法,必须是数字且大于 <code>{$_config['proxies']['min']}</code> 小于 <code>{$_config['proxies']['max']}</code>。");
}
if(!$this->isPortAvailable($data['remote_port'], $data['proxy_type'], $data['node'])) {
return Array(false, "此远程端口已经被其他隧道使用,请更换其他的端口。");
}
if($data['proxy_type'] == "xtcp" || $data['proxy_type'] == "stcp") {
if(!isset($data['sk']) || $data['sk'] == "") {
return Array(false, "创建 XTCP 或 STCP 类型的隧道时必须要指定访问密码");
}
}
} else {
if(!isset($data['domain']) || $data["domain"] == "") {
return Array(false, "创建 HTTP 或 HTTPS 类型的隧道时必须要指定一个域名");
}
if(!$this->isDomainAvailable($data['domain'], $data['proxy_type'], $data['node'])) {
return Array(false, "此域名已经被其他隧道使用,同一协议(http/https)一个域名只能添加一次。");
}
}
if(isset($_config['proxies']['protect']) && !empty($_config['proxies']['protect'])) {
foreach($_config['proxies']['protect'] as $key => $value) {
if(Intval($data['remote_port']) > $key && Intval($data['remote_port']) < $value) {
return Array(false, "该远程端口不可用,因为它属于系统保留端口范围,详情请点击端口规则查看。");
}
}
}
if(isset($data['domain']) && $data["domain"] !== "") {
if(!Regex::isDomain($data['domain'])) {
return Array(false, "域名格式错误,请输入一个有效并且真实存在的域名");
}
}
// 剩下是高级设置
if(isset($data['use_encryption']) && $data['use_encryption'] !== "") {
if($data['use_encryption'] !== "true" && $data['use_encryption'] !== "false") {
return Array(false, "连接加密选项只能是 true 和 false,不用试了");
}
}
if(isset($data['use_compression']) && $data['use_compression'] !== "") {
if($data['use_compression'] !== "true" && $data['use_compression'] !== "false") {
return Array(false, "数据压缩选项只能是 true 和 false,不用试了");
}
}
// 这里是判断 Locations 字段的,我的设定是开头必须是 /,长度 64 字符以内,后面的不管
if(isset($data['locations']) && $data['locations'] !== "") {
if(substr($data['locations'], 0, 1) !== "/" || strlen($data['locations']) > 64) {
return Array(false, "URL 路由开头必须是 /,且最多不能超过 64 个字符");
}
}
if(isset($data['host_header_rewrite']) && $data["host_header_rewrite"] !== "") {
if(!Regex::isDomain($data['host_header_rewrite'])) {
return Array(false, "Host 重写域名格式错误,请输入一个有效的域名");
}
}
if(isset($data['header_X_From_Where']) && $data["header_X_From_Where"] !== "") {
if(!Regex::isDomain($data['header_X_From_Where'])) {
return Array(false, "请求来源必须是一个域名");
}
}
if(isset($data['sk']) && $data["sk"] !== "") {
if(strlen($data['sk']) < 5 || strlen($data['sk']) > 32) {
return Array(false, "访问密码必须大于 5 个字符,小于 32 个字符。");
}
}
return Array(true, "Check done");
}
public function getUserProxiesConfig($user, $node)
{
global $_config;
$nm = new SakuraPanel\NodeManager();
$um = new SakuraPanel\UserManager();
if(!$um->checkUserExist($user)) {
return Array(false, "用户不存在");
}
if(!$nm->isNodeExist($node)) {
return Array(false, "节点不存在");
}
// 获取节点信息和用户 Token
$ns = $nm->getNodeInfo($node);
$tk = $um->getUserToken($user);
// 客户端基础配置
$configuration = <<<EOF
[common]
server_addr = {$ns['ip']}
server_port = {$ns['port']}
tcp_mux = true
protocol = tcp
user = {$tk}
token = {$ns['token']}
dns_server = 114.114.114.114
EOF;
// 获取用户的所有隧道
$list = $this->getUserProxiesList($user);
foreach($list as $item) {
// 如果不是此节点的忽略
if(Intval($item[16]) !== Intval($node) || $item[14] !== "0") continue;
// 防止出现 Bug
$local_ip = $item[4] == "" ? "127.0.0.1" : $item[4];
$local_port = $item[5] == "" ? "80" : $item[5];
// 隧道的基本信息
$configuration .= <<<EOF
[{$item[2]}]
privilege_mode = true
type = {$item[3]}
local_ip = {$local_ip}
local_port = {$local_port}
EOF;
if($item[3] == "http" || $item[3] == "https") {
// HTTP / HTTPS
$domain = json_decode($item[8], true);
$configuration .= "custom_domains = {$domain[0]}\n";
$configuration .= $item[9] == "" ? "" : "locations = {$item[9]}\n";
$configuration .= $item[10] == "" ? "" : "host_header_rewrite = {$item[10]}\n";
$configuration .= $item[13] == "" ? "" : "header_X-From-Where = {$item[13]}\n";
} else {
// TCP / UDP / XTCP / STCP
$configuration .= "remote_port = {$item[11]}\n";
$configuration .= $item[12] == "" ? "" : "sk = {$item[12]}\n";
}
// 压缩和加密
$configuration .= $item[6] == "" ? "" : "use_encryption = {$item[6]}\n";
$configuration .= $item[7] == "" ? "" : "use_compression = {$item[7]}\n";
$configuration .= "\n";
}
return $configuration;
}
}
Executable
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace SakuraPanel;
class Regex {
const TYPE_NUMBER = 0;
const TYPE_USERNAME = 1;
const TYPE_PROXYNAME = 2;
const TYPE_EMAIL = 3;
const TYPE_DOMAIN = 4;
const TYPE_LETTER = 5;
const TYPE_NOTEMPTY = 6;
const TYPE_IPV4 = 7;
const TYPE_IPV6 = 8;
const TYPE_HOSTNAME = 9;
const TYPE_IPV4_V6 = 10;
public static function isNumber($data)
{
return preg_match("/^[0-9]+$/", $data) ? true : false;
}
public static function isProxyName($data)
{
return preg_match("/^[A-Za-z0-9\_]{3,15}$/", $data) ? true : false;
}
public static function isUserName($data)
{
return preg_match("/^[A-Za-z0-9\_\-]{3,32}$/", $data) ? true : false;
}
public static function isDomain($data)
{
return preg_match("/^(?=^.{3,255}$)[a-zA-Z0-9\x7f-\xff][-a-zA-Z0-9\x7f-\xff]{0,62}(\.[a-zA-Z0-9\x7f-\xff][-a-zA-Z0-9\x7f-\xff]{0,62})+$/", $data) ? true : false;
}
public static function isEmail($data)
{
return preg_match("/^\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,48}$/", $data) ? true : false;
}
public static function isLetter($data)
{
return preg_match("/^[A-Za-z0-9]+$/", $data) ? true : false;
}
public static function isIpv4($data)
{
return filter_var($data, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
}
public static function isIpv6($data)
{
return filter_var($data, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
}
public static function isHostName($data)
{
return (Regex::isIpv4($data) || Regex::isIpv6($data) || Regex::isDomain($data));
}
public static function isValid($var, $group)
{
$valid = true;
foreach($group as $name => $type) {
if(!isset($var[$name])) {
return "Undefined member {$name}";
} else {
switch($type) {
case (Regex::TYPE_NUMBER):
$valid = Regex::isNumber($var[$name]);
break;
case (Regex::TYPE_USERNAME):
$valid = Regex::isUserName($var[$name]);
break;
case (Regex::TYPE_EMAIL):
$valid = Regex::isEmail($var[$name]);
break;
case (Regex::TYPE_PROXYNAME):
$valid = Regex::isProxyName($var[$name]);
break;
case (Regex::TYPE_DOMAIN):
$valid = Regex::isDomain($var[$name]);
break;
case (Regex::TYPE_LETTER):
$valid = Regex::isLetter($var[$name]);
break;
case (Regex::TYPE_NOTEMPTY):
$valid = (!empty($var[$name]));
break;
case (Regex::TYPE_IPV4):
$valid = Regex::isIpv4($var[$name]);
break;
case (Regex::TYPE_IPV6):
$valid = Regex::isIpv6($var[$name]);
break;
case (Regex::TYPE_IPV4_V6):
$valid = (Regex::isIpv4($var[$name]) || Regex::isIpv6($var[$name]));
break;
case (Regex::TYPE_HOSTNAME):
$valid = Regex::isHostName($var[$name]);
break;
default:
return "Undefined type {$type}";
}
if(!$valid) {
return "Not valid {$name}";
}
}
}
return $valid;
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace SakuraPanel;
use SakuraPanel;
$pages = new SakuraPanel\Pages();
$phdle = new SakuraPanel\PostHandler();
if($_SERVER['REQUEST_METHOD'] == "POST") {
$phdle->switcher($_GET);
exit;
}
// 辣鸡 Router
if(isset($_GET['page']) && preg_match("/^[A-Za-z0-9\-\_]{1,8}$/", $_GET['page'])) {
$um = new SakuraPanel\UserManager();
if($um->isLogged()) {
if($_GET['page'] == "login" || $_GET['page'] == "register" || $_GET['page'] == "findpass") {
exit("<script>location='?page=panel';</script>");
}
$pages->loadPage($_GET['page']);
} else {
if($_GET['page'] !== "login" && $_GET['page'] !== "register" && $_GET['page'] !== "findpass") {
$pages->loadPage("login");
} else {
$pages->loadPage($_GET['page']);
}
}
} else {
$pages->loadPage("home");
}
exit;
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace SakuraPanel;
use SakuraPanel;
class Settings {
public static function get($key, $value = "")
{
$rs = Database::querySingleLine("settings", Array("key" => $key));
if($rs) {
return $rs['value'] ?? $value;
}
return $value;
}
public static function set($key, $value = "")
{
$rs = Database::querySingleLine("settings", Array("key" => $key));
if($rs) {
return Database::update("settings", Array("value" => $value), Array("key" => $key));
} else {
return Database::insert("settings", Array("key" => $key, "value" => $value));
}
}
}
Executable
+281
View File
@@ -0,0 +1,281 @@
<?php
namespace SakuraPanel;
class Smtp {
public $smtp_port;
public $time_out;
public $host_name;
public $log_file;
public $relay_host;
public $debug;
public $auth;
public $user;
public $pass;
private $sock;
public function __construct($relay_host = "", $smtp_port = 25, $auth = false, $user, $pass)
{
$this->debug = false;
$this->smtp_port = $smtp_port;
$this->relay_host = $relay_host;
$this->time_out = 30;
$this->auth = $auth;
$this->user = $user;
$this->pass = $pass;
$this->host_name = "localhost";
$this->log_file = "";
$this->sock = false;
}
public function sendMail($to, $from, $subject = "", $body = "", $mailtype, $cc = "", $bcc = "", $additional_headers = "")
{
$header = "";
$mail_from = $this->getAddress($this->stripComment($from));
$body = mb_ereg_replace("(^|(\r\n))(\\.)", "\\1.\\3", $body);
$header .= "MIME-Version:1.0\r\n";
if ($mailtype == "HTML") {
$header .= 'Content-type: text/html; charset=utf-8' . "\r\n";
}
$header .= "To: {$to}\r\n";
if ($cc != "") {
$header .= "Cc: {$cc}\r\n";
}
$header .= "From: {$from}\r\n";
$header .= "Subject: {$subject}\r\n";
$header .= $additional_headers;
$header .= "Date: " . date("r") . "\r\n";
$header .= "X-Mailer: By (SakuraPanel)\r\n";
list($msec, $sec) = explode(" ", microtime());
$header .= "Message-ID: <" . date("YmdHis", $sec) . "." . ($msec * 1000000) . ".{$mail_from}>\r\n";
$TO = explode(",", $this->stripComment($to));
if ($cc != "") {
$TO = array_merge($TO, explode(",", $this->stripComment($cc)));
}
if ($bcc != "") {
$TO = array_merge($TO, explode(",", $this->stripComment($bcc)));
}
$sent = true;
foreach ($TO as $rcpt_to) {
$rcpt_to = $this->getAddress($rcpt_to);
if (!$this->smtpSockopen($rcpt_to)) {
$this->logWrite("Error: Cannot send email to {$rcpt_to}\n");
$sent = false;
continue;
}
if ($this->smtpSend($this->host_name, $mail_from, $rcpt_to, $header, $body)) {
$this->logWrite("E-mail has been sent to <{$rcpt_to}>\n");
} else {
$this->logWrite("Error: Cannot send email to <{$rcpt_to}>\n");
$sent = false;
}
fclose($this->sock);
$this->logWrite("Disconnected from remote host\n");
}
return $sent;
}
private function smtpSend($helo, $from, $to, $header, $body = "")
{
if (!$this->smtpPutcmd("HELO", $helo)) {
return $this->smtpError("sending HELO command");
}
if ($this->auth) {
if (!$this->smtpPutcmd("AUTH LOGIN", base64_encode($this->user))) {
return $this->smtpError("sending HELO command");
}
if (!$this->smtpPutcmd("", base64_encode($this->pass))) {
return $this->smtpError("sending HELO command");
}
}
if (!$this->smtpPutcmd("MAIL", "FROM:<{$from}>")) {
return $this->smtpError("sending MAIL FROM command");
}
if (!$this->smtpPutcmd("RCPT", "TO:<{$to}>")) {
return $this->smtpError("sending RCPT TO command");
}
if (!$this->smtpPutcmd("DATA")) {
return $this->smtpError("sending DATA command");
}
if (!$this->smtpMessage($header, $body)) {
return $this->smtpError("sending message");
}
if (!$this->smtpEom()) {
return $this->smtpError("sending <CR><LF>.<CR><LF> [EOM]");
}
if (!$this->smtpPutcmd("QUIT")) {
return $this->smtpError("sending QUIT command");
}
return true;
}
private function smtpSockopen($address)
{
if ($this->relay_host == "") {
return $this->smtpSockopenMx($address);
} else {
return $this->smtpSockopenRelay();
}
}
private function smtpSockopenRelay()
{
$this->logWrite("Trying to {$this->relay_host}:{$this->smtp_port}\n");
$this->sock = @fsockopen($this->relay_host, $this->smtp_port, $errno, $errstr, $this->time_out);
if (!($this->sock && $this->smtpOk())) {
$this->logWrite("Error: Cannot connenct to relay host {$this->relay_host}\n");
$this->logWrite("Error: {$errstr} ({$errno})\n");
return false;
}
$this->logWrite("Connected to relay host {$this->relay_host}\n");
return true;;
}
private function smtpSockopenMx($address)
{
$domain = ereg_replace("^.+@([^@]+)$", "\\1", $address);
if (!@getmxrr($domain, $MXHOSTS)) {
$this->logWrite("Error: Cannot resolve MX \"{$domain}\"\n");
return false;
}
foreach ($MXHOSTS as $host) {
$this->logWrite("Trying to {$host}:{$this->smtp_port}\n");
$this->sock = @fsockopen($host, $this->smtp_port, $errno, $errstr, $this->time_out);
if (!($this->sock && $this->smtpOk())) {
$this->logWrite("Warning: Cannot connect to mx host {$host}\n");
$this->logWrite("Error: {$errstr} ({$errno})\n");
continue;
}
$this->logWrite("Connected to mx host {$host}\n");
return true;
}
$this->logWrite("Error: Cannot connect to any mx hosts (" . implode(", ", $MXHOSTS) . ")\n");
return false;
}
private function smtpMessage($header, $body)
{
fputs($this->sock, "{$header}\r\n" . $body);
$this->smtpDebug("> " . str_replace("\r\n", "\n> ", "{$header}\n> {$body}\n> "));
return true;
}
private function smtpEom()
{
fputs($this->sock, "\r\n.\r\n");
$this->smtpDebug(". [EOM]\n");
return $this->smtpOk();
}
private function smtpOk()
{
$response = str_replace("\r\n", "", fgets($this->sock, 512));
$this->smtpDebug("{$response}\n");
if (!mb_ereg("^[23]", $response)) {
fputs($this->sock, "QUIT\r\n");
fgets($this->sock, 512);
$this->logWrite("Error: Remote host returned \"{$response}\"\n");
return false;
}
return true;
}
private function smtpPutcmd($cmd, $arg = "")
{
if ($arg != "") {
$cmd = empty($cmd) ? $arg : "{$cmd} {$arg}";
}
fputs($this->sock, "{$cmd}\r\n");
$this->smtpDebug("> {$cmd}\n");
return $this->smtpOk();
}
private function smtpError($string)
{
$this->logWrite("Error: Error occurred while {$string}.\n");
return false;
}
private function logWrite($message)
{
$this->smtpDebug($message);
if ($this->log_file == "") {
return true;
}
$message = date("M d H:i:s ") . get_current_user() . "[" . getmypid() . "]: {$message}";
if (!@file_exists($this->log_file) || !($fp = @fopen($this->log_file, "a"))) {
$this->smtpDebug("Warning: Cannot open log file \"{$this->log_file}\"\n");
return false;
}
flock($fp, LOCK_EX);
fputs($fp, $message);
fclose($fp);
return true;
}
private function stripComment($address)
{
$comment = "\\([^()]*\\)";
while (mb_ereg($comment, $address)) {
$address = mb_ereg_replace($comment, "", $address);
}
return $address;
}
private function getAddress($address)
{
$address = mb_ereg_replace("([ \t\r\n])+", "", $address);
$address = mb_ereg_replace("^.*<(.+)>.*$", "\\1", $address);
return $address;
}
private function smtpDebug($message)
{
if ($this->debug) {
echo $message;
}
}
private function getAttachType($image_tag)
{
$filedata = array();
$img_file_con = fopen($image_tag, "r");
unset($image_data);
while ($tem_buffer = addslashes(fread($img_file_con, filesize($image_tag)))) {
$image_data .= $tem_buffer;
}
fclose($img_file_con);
$filedata['context'] = $image_data;
$filedata['filename'] = basename($image_tag);
$extension = substr($image_tag, strrpos($image_tag, "."), strlen($image_tag) - strrpos($image_tag, "."));
$extensions = Array(
".gif" => "image/gif",
".gz" => "application/x-gzip",
".htm" => "text/html",
".html" => "text/html",
".jpg" => "image/jpeg",
".png" => "image/png",
".bmp" => "image/bmp",
".gif" => "image/gif",
".tar" => "application/x-tar",
".txt" => "text/plain",
".zip" => "application/zip"
);
$filedata['type'] = $extensions[$extension] ?? "application/octet-stream";
return $filedata;
}
}
+370
View File
@@ -0,0 +1,370 @@
<?php
namespace SakuraPanel;
use SakuraPanel;
class UserManager {
public function isLogged()
{
return (isset($_SESSION['user']) && !empty($_SESSION['user']));
}
public function doLogin($data)
{
if(empty($data['username']) || empty($data['password'])) {
return Array("status" => false, "message" => "请将信息填写完整");
}
if(!$this->checkUserName($data['username'])) {
return Array("status" => false, "message" => "用户名不合法");
}
// 获取用户的信息(以用户名)
$rs = $this->getInfoByUser($data['username']);
if(!$rs) {
// 获取用户的信息(以邮箱)
$rs = $this->getInfoByEmail($data['username']);
if(!$rs) {
return Array("status" => false, "message" => "用户名或密码错误");
}
}
if(!$this->checkPassword($data['password'], $rs['password'])) {
return Array("status" => false, "message" => "用户名或密码错误");
}
return Array("status" => true, "message" => "登录成功", "username" => $rs['username'], "email" => $rs['email']);
}
public function doRegister($data)
{
global $_config;
if(!$_config['register']['enable']) {
return Array("status" => false, "message" => "抱歉,本站暂不开放注册");
}
if(!isset($data['username']) || !isset($data['email']) || !isset($data['password']) ||
empty($data['username']) || empty($data['email']) || empty($data['password'])) {
return Array("status" => false, "message" => "请将信息填写完整");
}
if($_config['smtp']['enable']) {
if(!isset($data['verifycode']) || empty($data['verifycode'])) {
return Array("status" => false, "message" => "请输入验证码");
} else {
if(!isset($_SESSION['reg_verifycode']) || $_SESSION['reg_verifycode'] == "") {
return Array("status" => false, "message" => "验证码已失效,请重新获取邮件");
}
if(isset($_SESSION['reg_wait'])) {
if(time() - $_SESSION['reg_wait'] > 900) {
exit("验证码已失效,请重新获取邮件");
}
}
if($_SESSION['reg_email'] !== $data['email']) {
return Array("status" => false, "message" => "请重新验证该邮箱后才能注册");
}
if(Intval($_SESSION['reg_verifycode']) !== Intval($data['verifycode'])) {
return Array("status" => false, "message" => "验证码错误,请检查");
}
}
}
if(!$this->checkUserName($data['username'])) {
return Array("status" => false, "message" => "用户名不合法");
}
if($this->checkUserExist($data['username'])) {
return Array("status" => false, "message" => "该用户名已被注册");
}
if($this->checkEmailExist($data['email'])) {
return Array("status" => false, "message" => "该邮箱已被注册");
}
// 执行注册
$this->addUser($data['username'], $data['email'], $data['password']);
return Array("status" => true, "message" => "账号注册成功!");
}
public function resetPass($link)
{
$lk = Database::querySingleLine("findpass", Array("link" => $link));
if($lk) {
if(time() - $lk['time'] < 3600) {
$newpass = $this->getUserToken($lk['username']);
$password = $this->generatePassword($newpass);
$token = substr(md5(sha1(md5($lk['username']) . md5($password) . time() . mt_rand(0, 9999999))), 0, 16);
// 更新数据库
Database::update("users", Array("password" => $password), Array("username" => $lk['username']));
Database::update("tokens", Array("token" => $token), Array("username" => $lk['username']));
Database::delete("findpass", Array("link" => $link));
return true;
}
return false;
}
return false;
}
public function doFindpass($data)
{
global $_config;
if(!$_config['smtp']['enable']) {
return Array("status" => false, "message" => "本站未开启 SMTP 服务,请联系管理员");
}
if(isset($_SESSION['sendmail']) && time() - $_SESSION['sendmail'] <= 60) return Array("status" => false, "message" => "您操作的频率太高,请稍后再试");
if($data['username'] == "") return Array("status" => false, "message" => "请填写要找回密码的账号或邮箱");
$rs = $this->getInfoByUser($data['username']);
$link = sha1(md5($rs['username'] . $rs['password'] . time() . mt_rand(0, 9999999)) . md5(mt_rand(0, 9999999)));
$found = false;
if($rs) {
$this->sendFindpassEmail($rs['username'], $rs['email'], $link);
$found = true;
} else {
$rs = $this->getInfoByEmail($data['username']);
if($rs) {
$this->sendFindpassEmail($rs['username'], $rs['email'], $link);
$found = true;
}
}
if($found) {
$ms = Database::querySingleLine("findpass", Array("username" => $rs['username']));
if($ms) {
Database::delete("findpass", Array("username" => $rs['username']));
}
Database::insert("findpass", Array(
"username" => $rs['username'],
"link" => $link,
"time" => time()
));
}
$_SESSION['sendmail'] = time();
return Array("status" => true, "message" => "我们已经尝试了发送一封邮件到该账号的邮箱,请查收。");
}
public function sendFindpassEmail($username, $email, $link)
{
global $_config;
$type = SakuraPanel\Utils::isHttps() ? "https" : "http";
$token = $this->getUserToken($username);
$url = "{$type}://{$_SERVER['SERVER_NAME']}/?page=findpass&link={$link}";
$temp = @file_get_contents(ROOT . "/assets/email/findpass.html");
$temp = str_replace("{SITENAME}", $_config['sitename'], $temp);
$temp = str_replace("{SITEDESCRIPTION}", $_config['description'], $temp);
$temp = str_replace("{USERNAME}", $username, $temp);
$temp = str_replace("{TOKEN}", $token, $temp);
$temp = str_replace("{URL}", $url, $temp);
$smtp = new SakuraPanel\Smtp(
$_config['smtp']['host'],
$_config['smtp']['port'],
true,
$_config['smtp']['user'],
$_config['smtp']['pass']
);
$smtp->debug = false;
$smtp->sendMail($email, $_config['smtp']['mail'], "找回您的 {$_config['sitename']} 密码", $temp, "HTML");
}
public function sendRegisterEmail($email, $number)
{
global $_config;
$temp = @file_get_contents(ROOT . "/assets/email/welcome.html");
$temp = str_replace("{SITENAME}", $_config['sitename'], $temp);
$temp = str_replace("{SITEDESCRIPTION}", $_config['description'], $temp);
$temp = str_replace("{NUMBER}", $number, $temp);
$smtp = new SakuraPanel\Smtp(
$_config['smtp']['host'],
$_config['smtp']['port'],
true,
$_config['smtp']['user'],
$_config['smtp']['pass']
);
$smtp->debug = false;
$smtp->sendMail($email, $_config['smtp']['mail'], "验证您的 {$_config['sitename']} 账号", $temp, "HTML");
}
public function addUser($username, $email, $password, $group = "default", $status = 0)
{
global $_config;
// 生成密码和 Token
$password = $this->generatePassword($password);
$token = substr(md5(sha1(md5($username) . md5($password) . time() . mt_rand(0, 9999999))), 0, 16);
Database::insert("users", Array(
"id" => null,
"username" => $username,
"password" => $password,
"email" => $email,
"traffic" => $_config['register']['traffic'],
"proxies" => $_config['register']['proxies'],
"group" => $group,
"regtime" => time(),
"status" => $status,
));
Database::insert("tokens", Array(
"id" => null,
"username" => $username,
"token" => $token,
"status" => 0
));
}
public function updateUser($id, $data)
{
$rs = $this->getInfoById($id);
if(is_array($rs)) {
$result = Database::update("users", Array(
"traffic" => $data['traffic'],
"proxies" => $data['proxies'],
"group" => $data['group'],
"status" => $data['status']
), Array("id" => $id));
if($result !== true) {
return $result;
}
if(isset($data['inbound'], $data['outbound']) && SakuraPanel\Regex::isNumber($data['inbound']) && SakuraPanel\Regex::isNumber($data['outbound'])) {
$ls = Database::querySingleLine("limits", Array("username" => $rs['username']));
if($ls) {
$result = Database::update("limits", Array(
"inbound" => $data['inbound'],
"outbound" => $data['outbound']
), Array("username" => $rs['username']));
} else {
$result = Database::insert("limits", Array(
"username" => $rs['username'],
"inbound" => $data['inbound'],
"outbound" => $data['outbound']
));
}
} else {
$result = Database::delete("limits", Array("username" => $rs['username']));
}
return $result;
} else {
return false;
}
}
public function getTokensToUsers()
{
$rs = Database::toArray(Database::query("tokens", Array()));
$tokens = [];
for($i = 0;$i < count($rs);$i++) {
$tokens[$rs[$i][2]] = $rs[$i][1];
}
return $tokens;
}
public function getTotalUsers()
{
$rs = Database::toArray(Database::query("users", Array()));
return count($rs);
}
public function getInfoById($id)
{
return Database::querySingleLine("users", Array("id" => $id));
}
public function getInfoByUser($username)
{
return Database::querySingleLine("users", Array("username" => $username));
}
public function getInfoByEmail($email)
{
return Database::querySingleLine("users", Array("email" => $email));
}
public function checkUserExist($username)
{
return Database::querySingleLine("users", Array("username" => $username)) ? true : false;
}
public function checkUserName($username)
{
return preg_match("/^[A-Za-z0-9\_\-]{3,32}$/", $username) ? true : false;
}
public function checkEmailExist($email)
{
return Database::querySingleLine("users", Array("email" => $email)) ? true : false;
}
public function checkEmail($email)
{
return preg_match("/^\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,48}$/", $email) ? true : false;
}
public function checkPassword($password, $encrypted)
{
return password_verify($password, $encrypted);
}
public function generatePassword($password)
{
return password_hash($password, PASSWORD_BCRYPT);
}
public function getUserToken($username)
{
$rs = Database::querySingleLine("tokens", Array("username" => $username));
return $rs['token'] ?? false;
}
/**
*
* 这里返回的 type:0 表示无设置,默认 / 1 表示该用户有独立设定 / 2 表示该用户继承组设定
*
*/
public function getLimit($username)
{
$rs = Database::querySingleLine("limits", Array("username" => $username));
if($rs) {
return Array("inbound" => $rs['inbound'], "outbound" => $rs['outbound'], "type" => 1);
} else {
$us = Database::querySingleLine("users", Array("username" => $username));
if($us) {
$gs = Database::querySingleLine("groups", Array("name" => $us['group']));
if($gs) {
return Array("inbound" => $gs['inbound'], "outbound" => $gs['outbound'], "type" => 2);
}
}
return Array("inbound" => 1024, "outbound" => 1024, "type" => 0);
}
}
public function getTodayTraffic($username)
{
$rs = Database::querySingleLine("todaytraffic", Array("user" => $username));
if($rs) {
return $rs['traffic'];
} else {
return 0;
}
}
}
Executable
+103
View File
@@ -0,0 +1,103 @@
<?php
namespace SakuraPanel;
class Utils {
const PANEL_VERSION = "1.0.0";
public static function reCAPTCHA($response)
{
global $_config;
$data = http_build_query(Array(
'secret' => $_config['recaptcha']['sitetoken'],
'response' => $response
));
$options = Array(
'http' => Array(
'method' => 'POST',
'header' => 'Content-type:application/x-www-form-urlencoded',
'content' => $data,
'timeout' => 15 * 60
)
);
$context = stream_context_create($options);
$result = @file_get_contents('https://recaptcha.net/recaptcha/api/siteverify', false, $context);
$json = json_decode($result, true);
return $json ? $json['success'] : false;
}
public static function isHttps()
{
if (!isset($_SERVER['HTTPS'])) {
return false;
}
if ($_SERVER['HTTPS'] === 1) {
return true;
} elseif ($_SERVER['HTTPS'] === 'on') {
return true;
} elseif ($_SERVER['SERVER_PORT'] == 443) {
return true;
} elseif ($_SERVER['REQUEST_SCHEME'] == "https") {
return true;
}
return false;
}
public static function http($url, $post = '', $cookie = '', $headers = '', $returnHeader = 0)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
curl_setopt($curl, CURLOPT_REFERER, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
if($post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
}
if($cookie) {
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
}
if($headers) {
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if(curl_errno($curl)) {
$httpCode = curl_error($curl);
}
curl_close($curl);
return [
'status' => $httpCode,
'body' => $data
];
}
public static function getFormatTraffic($data)
{
if($data < 1024) {
return $data . "B";
} elseif($data < 1024 * 1024) {
return round($data / 1024, 2) . "KB";
} elseif($data < 1024 * 1024 * 1024) {
return round($data / 1024 / 1024, 2) . "MB";
} else {
return round($data / 1024 / 1024 / 1024, 2) . "GB";
}
}
public static function checkCsrf()
{
if(isset($_GET['csrf'], $_SESSION['token']) && $_GET['csrf'] === $_SESSION['token']) {
return true;
} else {
ob_clean();
Header("HTTP/1.1 403 Forbidden");
exit("Invalid CSRF Token");
}
}
}