mirror of
https://github.com/ZeroDream-CN/SakuraPanel.git
synced 2026-05-19 03:39:51 +08:00
Initial commit
This commit is contained in:
Executable
+296
@@ -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);
|
||||
}
|
||||
}
|
||||
Executable
+83
@@ -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
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+1694
File diff suppressed because it is too large
Load Diff
Executable
+372
@@ -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']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+353
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+31
@@ -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;
|
||||
Executable
+26
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+370
@@ -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
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user