mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-17 21:25:47 +02:00
1. Add disk IO information to the homepage 2. Add a clear list button in the upload window 3. Optimize the background task overhead of the panel 4. Optimize the disk information caching mechanism 5. Panel Pro edition is online 6. Optimize panel resource usage 7. Optimize SSL certificate renewal 8. Fix the problem of reporting an error when the security entrance is empty 9. Fix the problem of infinite recursion when copying directories in extreme cases
2867 lines
93 KiB
Python
2867 lines
93 KiB
Python
# coding: utf-8
|
||
# +-------------------------------------------------------------------
|
||
# | 宝塔Linux面板
|
||
# +-------------------------------------------------------------------
|
||
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||
# +-------------------------------------------------------------------
|
||
# | Author: hwliang <hwl@bt.cn>
|
||
# +-------------------------------------------------------------------
|
||
|
||
# --------------------------------
|
||
# 宝塔公共库
|
||
# --------------------------------
|
||
|
||
import json,os,sys,time,re,socket,importlib,binascii,base64,io,string
|
||
from random import choice
|
||
_LAN_PUBLIC = None
|
||
_LAN_LOG = None
|
||
_LAN_TEMPLATE = None
|
||
|
||
if sys.version_info[0] == 2:
|
||
reload(sys)
|
||
sys.setdefaultencoding('utf8')
|
||
else:
|
||
from importlib import reload
|
||
|
||
def M(table):
|
||
"""
|
||
@name 访问面板数据库
|
||
@author hwliang<hwl@bt.cn>
|
||
@table 被访问的表名(必需)
|
||
@return db.Sql object
|
||
|
||
ps: 默认访问data/default.db
|
||
"""
|
||
import db
|
||
with db.Sql() as sql:
|
||
#sql = db.Sql()
|
||
return sql.table(table)
|
||
|
||
def HttpGet(url,timeout = 6,headers = {}):
|
||
"""
|
||
@name 发送GET请求
|
||
@author hwliang<hwl@bt.cn>
|
||
@url 被请求的URL地址(必需)
|
||
@timeout 超时时间默认60秒
|
||
@return string
|
||
"""
|
||
if is_local(): return False
|
||
import http_requests
|
||
res = http_requests.get(url,timeout=timeout,headers = headers)
|
||
if res.status_code == 0:
|
||
if old_url.find(home) != -1: return http_get_home(old_url,timeout,res.text)
|
||
if headers: return False
|
||
s_body = res.text
|
||
return s_body
|
||
s_body = res.text
|
||
del res
|
||
return s_body
|
||
|
||
def http_get_home(url,timeout,ex):
|
||
"""
|
||
@name Get方式使用优选节点访问官网
|
||
@author hwliang<hwl@bt.cn>
|
||
@param url 当前官网URL地址
|
||
@param timeout 用于测试超时时间
|
||
@param ex 上一次错误的响应内容
|
||
@return string 响应内容
|
||
|
||
如果已经是优选节点,将直接返回ex
|
||
"""
|
||
try:
|
||
home = 'www.bt.cn'
|
||
if url.find(home) == -1: return ex
|
||
hosts_file = "config/hosts.json"
|
||
if not os.path.exists(hosts_file): return ex
|
||
hosts = json.loads(readFile(hosts_file))
|
||
headers = {"host":home}
|
||
for host in hosts:
|
||
new_url = url.replace(home,host)
|
||
res = HttpGet(new_url,timeout,headers)
|
||
if res:
|
||
writeFile("data/home_host.pl",host)
|
||
set_home_host(host)
|
||
return res
|
||
return ex
|
||
except: return ex
|
||
|
||
|
||
def set_home_host(host):
|
||
"""
|
||
@name 设置官网hosts
|
||
@author hwliang<hwl@bt.cn>
|
||
@param host IP地址
|
||
@return void
|
||
"""
|
||
ExecShell('sed -i "/www.bt.cn/d" /etc/hosts')
|
||
ExecShell("echo '' >> /etc/hosts")
|
||
ExecShell("echo '%s www.bt.cn' >> /etc/hosts" % host)
|
||
ExecShell('sed -i "/^\s*$/d" /etc/hosts')
|
||
|
||
def httpGet(url,timeout=6):
|
||
return HttpGet(url,timeout)
|
||
|
||
def HttpPost(url,data,timeout = 6,headers = {}):
|
||
"""
|
||
发送POST请求
|
||
@url 被请求的URL地址(必需)
|
||
@data POST参数,可以是字符串或字典(必需)
|
||
@timeout 超时时间默认60秒
|
||
return string
|
||
"""
|
||
if is_local(): return False
|
||
import http_requests
|
||
res = http_requests.post(url,data=data,timeout=timeout,headers = headers)
|
||
if res.status_code == 0:
|
||
if old_url.find(home) != -1: return http_post_home(old_url,data,timeout,res.text)
|
||
if headers: return False
|
||
s_body = res.text
|
||
return s_body
|
||
s_body = res.text
|
||
return s_body
|
||
|
||
|
||
def http_post_home(url,data,timeout,ex):
|
||
"""
|
||
@name POST方式使用优选节点访问官网
|
||
@author hwliang<hwl@bt.cn>
|
||
@param url(string) 当前官网URL地址
|
||
@param data(dict) POST数据
|
||
@param timeout(int) 用于测试超时时间
|
||
@param ex(string) 上一次错误的响应内容
|
||
@return string 响应内容
|
||
|
||
如果已经是优选节点,将直接返回ex
|
||
"""
|
||
try:
|
||
home = 'www.bt.cn'
|
||
if url.find(home) == -1: return ex
|
||
hosts_file = "config/hosts.json"
|
||
if not os.path.exists(hosts_file): return ex
|
||
hosts = json.loads(readFile(hosts_file))
|
||
headers = {"host": home}
|
||
for host in hosts:
|
||
new_url = url.replace(home, host)
|
||
res = HttpPost(new_url, data, timeout, headers)
|
||
if res:
|
||
writeFile("data/home_host.pl", host)
|
||
set_home_host(host)
|
||
return res
|
||
return ex
|
||
except: return ex
|
||
|
||
def httpPost(url,data,timeout=6):
|
||
"""
|
||
@name 发送POST请求
|
||
@author hwliang<hwl@bt.cn>
|
||
@param url 被请求的URL地址(必需)
|
||
@param data POST参数,可以是字符串或字典(必需)
|
||
@param timeout 超时时间默认60秒
|
||
@return string
|
||
"""
|
||
return HttpPost(url,data,timeout)
|
||
|
||
def check_home():
|
||
return True
|
||
|
||
def Md5(strings):
|
||
"""
|
||
@name 生成MD5
|
||
@author hwliang<hwl@bt.cn>
|
||
@param strings 要被处理的字符串
|
||
@return string(32)
|
||
"""
|
||
if type(strings) != bytes:
|
||
strings = strings.encode()
|
||
import hashlib
|
||
m = hashlib.md5()
|
||
m.update(strings)
|
||
return m.hexdigest()
|
||
|
||
def md5(strings):
|
||
return Md5(strings)
|
||
|
||
def FileMd5(filename):
|
||
"""
|
||
@name 生成文件的MD5
|
||
@author hwliang<hwl@bt.cn>
|
||
@param filename 文件名
|
||
@return string(32) or False
|
||
"""
|
||
if not os.path.isfile(filename): return False
|
||
import hashlib
|
||
my_hash = hashlib.md5()
|
||
f = open(filename,'rb')
|
||
while True:
|
||
b = f.read(8096)
|
||
if not b :
|
||
break
|
||
my_hash.update(b)
|
||
f.close()
|
||
return my_hash.hexdigest()
|
||
|
||
|
||
def GetRandomString(length):
|
||
"""
|
||
@name 取随机字符串
|
||
@author hwliang<hwl@bt.cn>
|
||
@param length 要获取的长度
|
||
@return string(length)
|
||
"""
|
||
from random import Random
|
||
strings = ''
|
||
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
|
||
chrlen = len(chars) - 1
|
||
random = Random()
|
||
for i in range(length):
|
||
strings += chars[random.randint(0, chrlen)]
|
||
return strings
|
||
|
||
def ReturnJson(status,msg,args=()):
|
||
"""
|
||
@name 取通用Json返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return string(json)
|
||
"""
|
||
return GetJson(ReturnMsg(status, msg, args))
|
||
|
||
def returnJson(status,msg,args=()):
|
||
"""
|
||
@name 取通用Json返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return string(json)
|
||
"""
|
||
return ReturnJson(status,msg,args)
|
||
|
||
def ReturnMsg(status,msg,args = ()):
|
||
"""
|
||
@name 取通用dict返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return dict {"status":bool,"msg":string}
|
||
"""
|
||
try:
|
||
log_message = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/public.json'))
|
||
except:
|
||
log_message = {}
|
||
keys = log_message.keys()
|
||
if type(msg) == str:
|
||
if msg in keys:
|
||
msg = log_message[msg]
|
||
for i in range(len(args)):
|
||
rep = '{'+str(i+1)+'}'
|
||
msg = msg.replace(rep,args[i])
|
||
return {'status':status,'msg':msg}
|
||
|
||
def returnMsg(status,msg,args = ()):
|
||
"""
|
||
@name 取通用dict返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return dict {"status":bool,"msg":string}
|
||
"""
|
||
return ReturnMsg(status,msg,args)
|
||
|
||
|
||
def GetFileMode(filename):
|
||
"""
|
||
@name 取文件权限字符串
|
||
@author hwliang<hwl@bt.cn>
|
||
@param filename 文件全路径
|
||
@return string 如:644/777/755
|
||
"""
|
||
stat = os.stat(filename)
|
||
accept = str(oct(stat.st_mode)[-3:])
|
||
return accept
|
||
|
||
def get_mode_and_user(path):
|
||
'''取文件或目录权限信息'''
|
||
import pwd
|
||
data = {}
|
||
if not os.path.exists(path): return None
|
||
stat = os.stat(path)
|
||
data['mode'] = str(oct(stat.st_mode)[-3:])
|
||
try:
|
||
data['user'] = pwd.getpwuid(stat.st_uid).pw_name
|
||
except:
|
||
data['user'] = str(stat.st_uid)
|
||
return data
|
||
|
||
|
||
def GetJson(data):
|
||
"""
|
||
将对象转换为JSON
|
||
@data 被转换的对象(dict/list/str/int...)
|
||
"""
|
||
from json import dumps
|
||
if data == bytes: data = data.decode('utf-8')
|
||
try:
|
||
return dumps(data,ensure_ascii=False)
|
||
except:
|
||
return dumps(returnMsg(False,"WRONG_RESPONSE", (str(data))))
|
||
|
||
def getJson(data):
|
||
return GetJson(data)
|
||
|
||
def ReadFile(filename,mode = 'r'):
|
||
"""
|
||
读取文件内容
|
||
@filename 文件名
|
||
return string(bin) 若文件不存在,则返回None
|
||
"""
|
||
import os
|
||
if not os.path.exists(filename): return False
|
||
try:
|
||
fp = open(filename, mode)
|
||
f_body = fp.read()
|
||
fp.close()
|
||
except Exception as ex:
|
||
if sys.version_info[0] != 2:
|
||
try:
|
||
fp = open(filename, mode,encoding="utf-8")
|
||
f_body = fp.read()
|
||
fp.close()
|
||
except:
|
||
fp = open(filename, mode,encoding="GBK")
|
||
f_body = fp.read()
|
||
fp.close()
|
||
else:
|
||
return False
|
||
return f_body
|
||
|
||
def readFile(filename,mode='r'):
|
||
return ReadFile(filename,mode)
|
||
|
||
def WriteFile(filename,s_body,mode='w+'):
|
||
"""
|
||
写入文件内容
|
||
@filename 文件名
|
||
@s_body 欲写入的内容
|
||
return bool 若文件不存在则尝试自动创建
|
||
"""
|
||
try:
|
||
fp = open(filename, mode)
|
||
fp.write(s_body)
|
||
fp.close()
|
||
return True
|
||
except:
|
||
try:
|
||
fp = open(filename, mode,encoding="utf-8")
|
||
fp.write(s_body)
|
||
fp.close()
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
def writeFile(filename,s_body,mode='w+'):
|
||
return WriteFile(filename,s_body,mode)
|
||
|
||
def WriteLog(type,logMsg,args=(),not_web = False):
|
||
#写日志
|
||
#try:
|
||
import time,db,json
|
||
username = 'system'
|
||
uid = 1
|
||
tmp_msg = ''
|
||
if not not_web:
|
||
try:
|
||
from BTPanel import session
|
||
if 'username' in session:
|
||
username = session['username']
|
||
uid = session['uid']
|
||
if session.get('debug') == 1: return
|
||
except:
|
||
pass
|
||
global _LAN_LOG
|
||
if not _LAN_LOG:
|
||
_LAN_LOG = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/log.json'))
|
||
keys = _LAN_LOG.keys()
|
||
if logMsg in keys:
|
||
logMsg = _LAN_LOG[logMsg]
|
||
for i in range(len(args)):
|
||
rep = '{'+str(i+1)+'}'
|
||
logMsg = logMsg.replace(rep,args[i])
|
||
if type in keys: type = _LAN_LOG[type]
|
||
sql = db.Sql()
|
||
mDate = time.strftime('%Y-%m-%d %X',time.localtime())
|
||
data = (uid,username,type,logMsg + tmp_msg,mDate)
|
||
result = sql.table('logs').add('uid,username,type,log,addtime',data)
|
||
#except:
|
||
#pass
|
||
|
||
def GetLanguage():
|
||
'''
|
||
取语言
|
||
'''
|
||
return GetConfigValue("language")
|
||
|
||
def get_language():
|
||
return GetLanguage()
|
||
|
||
def GetConfigValue(key):
|
||
'''
|
||
取配置值
|
||
'''
|
||
config = GetConfig()
|
||
if not key in config.keys(): return None
|
||
return config[key]
|
||
|
||
def SetConfigValue(key,value):
|
||
config = GetConfig()
|
||
config[key] = value
|
||
WriteConfig(config)
|
||
|
||
def GetConfig():
|
||
'''
|
||
取所有配置项
|
||
'''
|
||
path = "config/config.json"
|
||
if not os.path.exists(path): return {}
|
||
f_body = ReadFile(path)
|
||
if not f_body: return {}
|
||
return json.loads(f_body)
|
||
|
||
def WriteConfig(config):
|
||
path = "config/config.json"
|
||
WriteFile(path, json.dumps(config))
|
||
|
||
|
||
def GetLan(key):
|
||
"""
|
||
取提示消息
|
||
"""
|
||
global _LAN_TEMPLATE
|
||
if not _LAN_TEMPLATE:
|
||
_LAN_TEMPLATE = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/template.json'))
|
||
keys = _LAN_TEMPLATE.keys()
|
||
msg = None
|
||
if key in keys:
|
||
msg = _LAN_TEMPLATE[key]
|
||
return msg
|
||
def getLan(key):
|
||
return GetLan(key)
|
||
|
||
def GetMsg(key,args = ()):
|
||
try:
|
||
global _LAN_PUBLIC
|
||
if not _LAN_PUBLIC:
|
||
_LAN_PUBLIC = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/public.json'))
|
||
keys = _LAN_PUBLIC.keys()
|
||
msg = None
|
||
if key in keys:
|
||
msg = _LAN_PUBLIC[key]
|
||
for i in range(len(args)):
|
||
rep = '{'+str(i+1)+'}'
|
||
msg = msg.replace(rep,args[i])
|
||
return msg
|
||
except:
|
||
return key
|
||
def getMsg(key,args = ()):
|
||
return GetMsg(key,args)
|
||
|
||
|
||
# 获取Web服务器
|
||
def GetWebServer():
|
||
if os.path.exists('/www/server/apache/bin/apachectl'):
|
||
webserver = 'apache'
|
||
elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
|
||
webserver = 'openlitespeed'
|
||
else:
|
||
webserver = 'nginx'
|
||
return webserver
|
||
|
||
def get_webserver():
|
||
return GetWebServer()
|
||
|
||
def ServiceReload():
|
||
# 重载Web服务配置
|
||
if os.path.exists('/www/server/nginx/sbin/nginx'):
|
||
result = ExecShell('/etc/init.d/nginx reload')
|
||
if result[1].find('nginx.pid') != -1:
|
||
ExecShell('pkill -9 nginx && sleep 1')
|
||
ExecShell('/etc/init.d/nginx start')
|
||
elif os.path.exists('/www/server/apache/bin/apachectl'):
|
||
result = ExecShell('/etc/init.d/httpd reload')
|
||
else:
|
||
result = ExecShell('rm -f /tmp/lshttpd/*.sock* && /usr/local/lsws/bin/lswsctrl restart')
|
||
return result
|
||
def serviceReload():
|
||
return ServiceReload()
|
||
|
||
|
||
def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
|
||
a = ''
|
||
e = ''
|
||
import subprocess,tempfile
|
||
|
||
try:
|
||
rx = md5(cmdstring)
|
||
succ_f = tempfile.SpooledTemporaryFile(max_size=4096,mode='wb+',suffix='_succ',prefix='btex_' + rx ,dir='/dev/shm')
|
||
err_f = tempfile.SpooledTemporaryFile(max_size=4096,mode='wb+',suffix='_err',prefix='btex_' + rx ,dir='/dev/shm')
|
||
sub = subprocess.Popen(cmdstring, close_fds=True, shell=shell,bufsize=128,stdout=succ_f,stderr=err_f)
|
||
sub.wait()
|
||
err_f.seek(0)
|
||
succ_f.seek(0)
|
||
a = succ_f.read()
|
||
e = err_f.read()
|
||
if not err_f.closed: err_f.close()
|
||
if not succ_f.closed: succ_f.close()
|
||
except:
|
||
print(get_error_info())
|
||
try:
|
||
#编码修正
|
||
if type(a) == bytes: a = a.decode('utf-8')
|
||
if type(e) == bytes: e = e.decode('utf-8')
|
||
except:pass
|
||
|
||
return a,e
|
||
|
||
def GetLocalIp():
|
||
# 取本地外网IP
|
||
try:
|
||
filename = 'data/iplist.txt'
|
||
ipaddress = readFile(filename)
|
||
if not ipaddress:
|
||
url = 'http://pv.sohu.com/cityjson?ie=utf-8'
|
||
m_str = HttpGet(url)
|
||
if isinstance(m_str,bytes):
|
||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str.decode('utf-8')).group(0)
|
||
else:
|
||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str).group(0)
|
||
WriteFile(filename,ipaddress)
|
||
c_ip = check_ip(ipaddress)
|
||
if not c_ip: return GetHost()
|
||
return ipaddress
|
||
except:
|
||
try:
|
||
url = 'https://brandnew.aapanel.com/api/common/getClientIP'
|
||
return HttpGet(url)
|
||
except:
|
||
return GetHost()
|
||
|
||
def is_ipv4(ip):
|
||
try:
|
||
socket.inet_pton(socket.AF_INET, ip)
|
||
except AttributeError:
|
||
try:
|
||
socket.inet_aton(ip)
|
||
except socket.error:
|
||
return False
|
||
return ip.count('.') == 3
|
||
except socket.error:
|
||
return False
|
||
return True
|
||
|
||
|
||
def is_ipv6(ip):
|
||
try:
|
||
socket.inet_pton(socket.AF_INET6, ip)
|
||
except socket.error:
|
||
return False
|
||
return True
|
||
|
||
|
||
def check_ip(ip):
|
||
return is_ipv4(ip) or is_ipv6(ip)
|
||
|
||
def GetHost(port = False):
|
||
from flask import request
|
||
host_tmp = request.headers.get('host')
|
||
if not host_tmp:
|
||
if request.url_root:
|
||
tmp = re.findall(r"(https|http)://([\[\]\w:\.-]+)",request.url_root)
|
||
if tmp: host_tmp = tmp[0][1]
|
||
if not host_tmp:
|
||
host_tmp = GetLocalIp() + ':' + readFile('data/port.pl').strip()
|
||
try:
|
||
if host_tmp.find(':') == -1: host_tmp += ':80'
|
||
except:
|
||
host_tmp = "127.0.0.1:8888"
|
||
h = host_tmp.split(':')
|
||
if port: return h[-1]
|
||
if len(h) > 2:
|
||
h = ":".join(h[:-1])
|
||
return h
|
||
return h[0]
|
||
|
||
def GetClientIp():
|
||
from flask import request
|
||
return request.remote_addr.replace('::ffff:', '')
|
||
|
||
def phpReload(version):
|
||
# 重载PHP配置
|
||
import os
|
||
if os.path.exists('/www/server/php/' + version + '/libphp5.so'):
|
||
ExecShell('/etc/init.d/httpd reload')
|
||
else:
|
||
ExecShell('/etc/init.d/php-fpm-' + version + ' reload')
|
||
|
||
def get_timeout(url,timeout=3):
|
||
try:
|
||
start = time.time()
|
||
result = int(httpGet(url,timeout))
|
||
return result,int((time.time() - start) * 1000 - 500)
|
||
except: return 0,False
|
||
|
||
def get_url(timeout = 0.5):
|
||
import json
|
||
try:
|
||
pkey = 'node_url'
|
||
node_url = cache_get(pkey)
|
||
if node_url: return node_url
|
||
nodeFile = 'data/node.json'
|
||
node_list = json.loads(readFile(nodeFile))
|
||
mnode1 = []
|
||
mnode2 = []
|
||
mnode3 = []
|
||
new_node_list = {}
|
||
for node in node_list:
|
||
node['net'],node['ping'] = get_timeout(node['protocol'] + node['address'] + ':' + node['port'] + '/net_test',1)
|
||
new_node_list[node['address']] = node['ping']
|
||
if not node['ping']: continue
|
||
if node['ping'] < 100: #当响应时间<100ms且可用带宽大于1500KB时
|
||
if node['net'] > 1500:
|
||
mnode1.append(node)
|
||
elif node['net'] > 1000:
|
||
mnode3.append(node)
|
||
else:
|
||
if node['net'] > 1000: #当响应时间>=100ms且可用带宽大于1000KB时
|
||
mnode2.append(node)
|
||
if node['ping'] < 100:
|
||
if node['net'] > 3000: break #有节点可用带宽大于3000时,不再检查其它节点
|
||
if mnode1: #优选低延迟高带宽
|
||
mnode = sorted(mnode1,key= lambda x:x['net'],reverse=True)
|
||
elif mnode3: #备选低延迟,中等带宽
|
||
mnode = sorted(mnode3,key= lambda x:x['net'],reverse=True)
|
||
else: #终选中等延迟,中等带宽
|
||
mnode = sorted(mnode2,key= lambda x:x['ping'],reverse=False)
|
||
|
||
if not mnode: return 'https://node.aapanel.com'
|
||
|
||
new_node_keys = new_node_list.keys()
|
||
for i in range(len(node_list)):
|
||
if node_list[i]['address'] in new_node_keys:
|
||
node_list[i]['ping'] = new_node_list[node_list[i]['address']]
|
||
else:
|
||
node_list[i]['ping'] = 500
|
||
|
||
new_node_list = sorted(node_list,key=lambda x: x['ping'],reverse=False)
|
||
writeFile(nodeFile,json.dumps(new_node_list))
|
||
node_url = mnode[0]['protocol'] + mnode[0]['address'] + ':' + mnode[0]['port']
|
||
cache_set(pkey,node_url,86400)
|
||
return node_url
|
||
except:
|
||
return 'https://node.aapanel.com'
|
||
|
||
|
||
#过滤输入
|
||
def checkInput(data):
|
||
if not data: return data
|
||
if type(data) != str: return data
|
||
checkList = [
|
||
{'d':'<','r':'<'},
|
||
{'d':'>','r':'>'},
|
||
{'d':'\'','r':'‘'},
|
||
{'d':'"','r':'“'},
|
||
{'d':'&','r':'&'},
|
||
{'d':'#','r':'#'},
|
||
{'d':'<','r':'<'}
|
||
]
|
||
for v in checkList:
|
||
data = data.replace(v['d'],v['r'])
|
||
return data
|
||
|
||
#取文件指定尾行数
|
||
def GetNumLines(path,num,p=1):
|
||
pyVersion = sys.version_info[0]
|
||
max_len = 1024*128
|
||
try:
|
||
import cgi
|
||
if not os.path.exists(path): return ""
|
||
start_line = (p - 1) * num
|
||
count = start_line + num
|
||
fp = open(path,'r')
|
||
buf = ""
|
||
fp.seek(-1, 2)
|
||
if fp.read(1) == "\n": fp.seek(-1, 2)
|
||
data = []
|
||
total_len = 0
|
||
b = True
|
||
n = 0
|
||
for i in range(count):
|
||
while True:
|
||
newline_pos = str.rfind(str(buf), "\n")
|
||
pos = fp.tell()
|
||
if newline_pos != -1:
|
||
if n >= start_line:
|
||
line = buf[newline_pos + 1:]
|
||
line_len = len(line)
|
||
total_len += line_len
|
||
sp_len = total_len - max_len
|
||
if sp_len > 0:
|
||
line = line[sp_len:]
|
||
try:
|
||
data.insert(0,cgi.escape(line))
|
||
except: pass
|
||
buf = buf[:newline_pos]
|
||
n += 1
|
||
break
|
||
else:
|
||
if pos == 0:
|
||
b = False
|
||
break
|
||
to_read = min(4096, pos)
|
||
fp.seek(-to_read, 1)
|
||
t_buf = fp.read(to_read)
|
||
if pyVersion == 3:
|
||
try:
|
||
if type(t_buf) == bytes: t_buf = t_buf.decode('utf-8')
|
||
except:t_buf = str(t_buf)
|
||
buf = t_buf + buf
|
||
fp.seek(-to_read, 1)
|
||
if pos - to_read == 0:
|
||
buf = "\n" + buf
|
||
if total_len >= max_len: break
|
||
if not b: break
|
||
fp.close()
|
||
result = "\n".join(data)
|
||
if not result: raise Exception('null')
|
||
except:
|
||
result = ExecShell("tail -n {} {}".format(num,path))[0]
|
||
if len(result) > max_len:
|
||
result = result[-max_len:]
|
||
|
||
try:
|
||
try:
|
||
result = json.dumps(result)
|
||
return json.loads(result).strip()
|
||
except:
|
||
if pyVersion == 2:
|
||
result = result.decode('utf8',errors='ignore')
|
||
else:
|
||
result = result.encode('utf-8',errors='ignore').decode("utf-8",errors="ignore")
|
||
return result.strip()
|
||
except: return ""
|
||
|
||
#验证证书
|
||
def CheckCert(certPath = 'ssl/certificate.pem'):
|
||
openssl = '/usr/local/openssl/bin/openssl'
|
||
if not os.path.exists(openssl): openssl = 'openssl'
|
||
certPem = readFile(certPath)
|
||
s = "\n-----BEGIN CERTIFICATE-----"
|
||
tmp = certPem.strip().split(s)
|
||
for tmp1 in tmp:
|
||
if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: tmp1 = s + tmp1
|
||
writeFile(certPath, tmp1)
|
||
result = ExecShell(openssl + " x509 -in " + certPath + " -noout -subject")
|
||
if result[1].find('-bash:') != -1: return True
|
||
if len(result[1]) > 2: return False
|
||
if result[0].find('error:') != -1: return False
|
||
return True
|
||
|
||
|
||
# 获取面板地址
|
||
def getPanelAddr():
|
||
from flask import request
|
||
protocol = 'https://' if os.path.exists("data/ssl.pl") else 'http://'
|
||
return protocol + request.headers.get('host')
|
||
|
||
|
||
# 字节单位转换
|
||
def to_size(size):
|
||
if not size: return '0.00 b'
|
||
size = float(size)
|
||
d = ('b','KB','MB','GB','TB')
|
||
s = d[0]
|
||
for b in d:
|
||
if size < 1024: return ("%.2f" % size) + ' ' + b
|
||
size = size / 1024
|
||
s = b
|
||
return ("%.2f" % size) + ' ' + b
|
||
|
||
|
||
def checkCode(code, outime=120):
|
||
# 校验验证码
|
||
from BTPanel import session, cache
|
||
try:
|
||
codeStr = cache.get('codeStr')
|
||
cache.delete('codeStr')
|
||
if not codeStr:
|
||
session['login_error'] = GetMsg('CODE_TIMEOUT')
|
||
return False
|
||
|
||
if md5(code.lower()) != codeStr:
|
||
session['login_error'] = GetMsg('CODE_ERR')
|
||
return False
|
||
return True
|
||
except:
|
||
session['login_error'] = GetMsg('CODE_NOT_EXISTS')
|
||
return False
|
||
|
||
#写进度
|
||
def writeSpeed(title,used,total,speed = 0):
|
||
import json
|
||
if not title:
|
||
data = {'title': None, 'progress': 0, 'total': 0, 'used': 0, 'speed': 0}
|
||
else:
|
||
progress = int((100.0 * used / total))
|
||
data = {'title': title, 'progress': progress, 'total': total, 'used': used, 'speed': speed}
|
||
writeFile('/tmp/panelSpeed.pl', json.dumps(data))
|
||
return True
|
||
|
||
#取进度
|
||
def getSpeed():
|
||
import json
|
||
data = readFile('/tmp/panelSpeed.pl')
|
||
if not data:
|
||
data = json.dumps({'title': None, 'progress': 0, 'total': 0, 'used': 0, 'speed': 0})
|
||
writeFile('/tmp/panelSpeed.pl', data)
|
||
return json.loads(data)
|
||
|
||
def downloadFile(url,filename):
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
import requests
|
||
headers = {'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'}
|
||
r = requests.get(url, headers=headers)
|
||
with open(filename,"wb") as f:
|
||
f.write(r.content)
|
||
else:
|
||
import urllib.request
|
||
opener = urllib.request.build_opener()
|
||
opener.addheaders = [('User-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36')]
|
||
urllib.request.install_opener(opener)
|
||
urllib.request.urlretrieve(url, filename=filename, reporthook=downloadHook)
|
||
except:
|
||
return get_error_info()
|
||
|
||
def downloadHook(count, blockSize, totalSize):
|
||
speed = {'total':totalSize,'block':blockSize,'count':count}
|
||
#print('%02d%%'%(100.0 * count * blockSize / totalSize))
|
||
|
||
def get_error_info():
|
||
import traceback
|
||
errorMsg = traceback.format_exc()
|
||
return errorMsg
|
||
|
||
|
||
# 搜索数据中是否存在
|
||
def inArray(arrays, searchStr):
|
||
for key in arrays:
|
||
if key == searchStr: return True
|
||
|
||
return False
|
||
|
||
#格式化指定时间戳
|
||
def format_date(format="%Y-%m-%d %H:%M:%S",times = None):
|
||
if not times: times = int(time.time())
|
||
time_local = time.localtime(times)
|
||
return time.strftime(format, time_local)
|
||
|
||
|
||
# 检查Web服务器配置文件是否有错误
|
||
def checkWebConfig():
|
||
f1 = '/www/server/panel/vhost/'
|
||
f2 = '/www/server/panel/plugin/'
|
||
if not os.path.exists(f2 + 'btwaf'):
|
||
f3 = f1 + 'nginx/btwaf.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
# if not os.path.exists(f2 + 'btwaf_httpd'):
|
||
# f3 = f1 + 'apache/btwaf.conf'
|
||
# if os.path.exists(f3): os.remove(f3)
|
||
|
||
if not os.path.exists(f2 + 'total'):
|
||
f3 = f1 + 'apache/total.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
f3 = f1 + 'nginx/total.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
else:
|
||
if os.path.exists('/www/server/apache/modules/mod_lua.so'):
|
||
writeFile(f1 + 'apache/btwaf.conf', 'LoadModule lua_module modules/mod_lua.so')
|
||
writeFile(f1 + 'apache/total.conf', 'LuaHookLog /www/server/total/httpd_log.lua run_logs')
|
||
else:
|
||
f3 = f1 + 'apache/total.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
|
||
if get_webserver() == 'nginx':
|
||
result = ExecShell("ulimit -n 8192 ; /www/server/nginx/sbin/nginx -t -c /www/server/nginx/conf/nginx.conf")
|
||
searchStr = 'successful'
|
||
elif get_webserver() == 'apache':
|
||
# else:
|
||
result = ExecShell("ulimit -n 8192 ; /www/server/apache/bin/apachectl -t")
|
||
searchStr = 'Syntax OK'
|
||
else:
|
||
result = ["1","1"]
|
||
searchStr = "1"
|
||
if result[1].find(searchStr) == -1:
|
||
WriteLog("TYPE_SOFT", 'CONF_CHECK_ERR', (result[1],))
|
||
return result[1]
|
||
return True
|
||
|
||
# 检查是否为IPv4地址
|
||
def checkIp(ip):
|
||
p = re.compile(r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
|
||
if p.match(ip):
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
#检查端口是否合法
|
||
def checkPort(port):
|
||
if not re.match("^\d+$",port): return False
|
||
ports = ['21','25','443','8080','888','8888','8443']
|
||
if port in ports: return False
|
||
intport = int(port)
|
||
if intport < 1 or intport > 65535: return False
|
||
return True
|
||
|
||
#字符串取中间
|
||
def getStrBetween(startStr,endStr,srcStr):
|
||
start = srcStr.find(startStr)
|
||
if start == -1: return None
|
||
end = srcStr.find(endStr)
|
||
if end == -1: return None
|
||
return srcStr[start + 1:end]
|
||
|
||
#取CPU类型
|
||
def getCpuType():
|
||
cpuinfo = open('/proc/cpuinfo', 'r').read()
|
||
rep = "model\s+name\s+:\s+(.+)"
|
||
tmp = re.search(rep,cpuinfo,re.I)
|
||
cpuType = ''
|
||
if tmp:
|
||
cpuType = tmp.groups()[0]
|
||
else:
|
||
cpuinfo = ExecShell('LANG="en_US.UTF-8" && lscpu')[0]
|
||
rep = "Model\s+name:\s+(.+)"
|
||
tmp = re.search(rep, cpuinfo, re.I)
|
||
if tmp: cpuType = tmp.groups()[0]
|
||
return cpuType
|
||
|
||
|
||
# 检查是否允许重启
|
||
def IsRestart():
|
||
num = M('tasks').where('status!=?',('1',)).count()
|
||
if num > 0: return False
|
||
return True
|
||
|
||
#加密密码字符
|
||
def hasPwd(password):
|
||
import crypt
|
||
return crypt.crypt(password,password)
|
||
|
||
def getDate(format='%Y-%m-%d %X'):
|
||
# 取格式时间
|
||
return time.strftime(format, time.localtime())
|
||
|
||
|
||
# 处理MySQL配置文件
|
||
def CheckMyCnf():
|
||
import os
|
||
confFile = '/etc/my.cnf'
|
||
if os.path.exists(confFile):
|
||
conf = readFile(confFile)
|
||
if conf.find('[mysqld]') != -1: return True
|
||
versionFile = '/www/server/mysql/version.pl'
|
||
if not os.path.exists(versionFile): return False
|
||
|
||
versions = ['5.1','5.5','5.6','5.7','8.0','AliSQL']
|
||
version = readFile(versionFile)
|
||
for key in versions:
|
||
if key in version:
|
||
version = key
|
||
break
|
||
|
||
shellStr = '''
|
||
#!/bin/bash
|
||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||
export PATH
|
||
|
||
CF='node.aapanel.com'
|
||
HK='download.bt.cn'
|
||
HK2='103.224.251.67'
|
||
US='128.1.164.196'
|
||
sleep 0.5;
|
||
CN_PING=`ping -c 1 -w 1 $CF|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
|
||
echo "$HK_PING $HK" > ping.pl
|
||
echo "$HK2_PING $HK2" >> ping.pl
|
||
echo "$US_PING $US" >> ping.pl
|
||
echo "$CF_PING $CF" >> ping.pl
|
||
nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'`
|
||
if [ "$nodeAddr" == "" ];then
|
||
nodeAddr=$CF
|
||
fi
|
||
|
||
Download_Url=http://$nodeAddr:5880
|
||
|
||
|
||
MySQL_Opt()
|
||
{
|
||
MemTotal=`free -m | grep Mem | awk '{print $2}'`
|
||
if [[ ${MemTotal} -gt 1024 && ${MemTotal} -lt 2048 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 128#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 768K#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 768K#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 8M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 16#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 32M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 2048 && ${MemTotal} -lt 4096 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 256#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 1M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 1M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 32#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 64M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 4096 && ${MemTotal} -lt 8192 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 512#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 2M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 2M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 64#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 128M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 8192 && ${MemTotal} -lt 16384 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 1024#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 4M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 4M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 128#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 1024M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 256M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 16384 && ${MemTotal} -lt 32768 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 2048#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 8M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 8M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 256#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 2048M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 512M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 32768 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 1024M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 4096#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 512#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 4096M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 1024M#" /etc/my.cnf
|
||
fi
|
||
}
|
||
|
||
wget -O /etc/my.cnf $Download_Url/install/conf/mysql-%s.conf -T 5
|
||
chmod 644 /etc/my.cnf
|
||
MySQL_Opt
|
||
''' % (version,)
|
||
ExecShell(shellStr)
|
||
#判断是否迁移目录
|
||
if os.path.exists('data/datadir.pl'):
|
||
newPath = readFile('data/datadir.pl')
|
||
if os.path.exists(newPath):
|
||
mycnf = readFile('/etc/my.cnf')
|
||
mycnf = mycnf.replace('/www/server/data',newPath)
|
||
writeFile('/etc/my.cnf',mycnf)
|
||
WriteLog('TYPE_SOFE', 'MYSQL_CHECK_ERR')
|
||
return True
|
||
|
||
|
||
def GetSSHPort():
|
||
try:
|
||
file = '/etc/ssh/sshd_config'
|
||
conf = ReadFile(file)
|
||
rep = "#*Port\s+([0-9]+)\s*\n"
|
||
port = re.search(rep, conf).groups(0)[0]
|
||
return int(port)
|
||
except:
|
||
return 22
|
||
|
||
def GetSSHStatus():
|
||
if os.path.exists('/usr/bin/apt-get'):
|
||
status = ExecShell("service ssh status | grep -P '(dead|stop)'")
|
||
else:
|
||
import system
|
||
panelsys = system.system()
|
||
version = panelsys.GetSystemVersion()
|
||
if version.find(' 7.') != -1:
|
||
status = ExecShell("systemctl status sshd.service | grep 'dead'")
|
||
else:
|
||
status = ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'")
|
||
if len(status[0]) > 3:
|
||
status = False
|
||
else:
|
||
status = True
|
||
return status
|
||
|
||
#检查端口是否合法
|
||
def CheckPort(port,other=None):
|
||
if type(port) == str: port = int(port)
|
||
if port < 1 or port > 65535: return False
|
||
if other:
|
||
checks = [22, 20, 21, 8888, 3306, 11211, 888, 25]
|
||
if port in checks: return False
|
||
return True
|
||
|
||
#获取Token
|
||
def GetToken():
|
||
try:
|
||
from json import loads
|
||
tokenFile = 'data/token.json'
|
||
if not os.path.exists(tokenFile): return False
|
||
token = loads(readFile(tokenFile))
|
||
return token
|
||
except:
|
||
return False
|
||
|
||
def to_btint(string):
|
||
m_list = []
|
||
for s in string:
|
||
m_list.append(ord(s))
|
||
return m_list
|
||
|
||
def load_module(pluginCode):
|
||
from imp import new_module
|
||
from BTPanel import cache
|
||
p_tk = 'data/%s' % md5(pluginCode + get_uuid())
|
||
pluginInfo = None
|
||
if cache: pluginInfo = cache.get(pluginCode + 'code')
|
||
if not pluginInfo:
|
||
import panelAuth
|
||
pdata = panelAuth.panelAuth().create_serverid(None)
|
||
pdata['pid'] = pluginCode
|
||
url = GetConfigValue('home') + '/api/panel/get_py_module'
|
||
pluginTmp = httpPost(url, pdata)
|
||
try:
|
||
pluginInfo = json.loads(pluginTmp)
|
||
except:
|
||
if not os.path.exists(p_tk): return False
|
||
pluginInfo = json.loads(ReadFile(p_tk))
|
||
if pluginInfo['status'] == False: return False
|
||
WriteFile(p_tk, json.dumps(pluginInfo))
|
||
os.chmod(p_tk, 384)
|
||
if cache: cache.set(pluginCode + 'code', pluginInfo, 1800)
|
||
|
||
mod = sys.modules.setdefault(pluginCode, new_module(pluginCode))
|
||
code = compile(pluginInfo['msg'].encode('utf-8'), pluginCode, 'exec')
|
||
mod.__file__ = pluginCode
|
||
mod.__package__ = ''
|
||
exec (code, mod.__dict__)
|
||
return mod
|
||
|
||
#解密数据
|
||
def auth_decode(data):
|
||
token = GetToken()
|
||
#是否有生成Token
|
||
if not token: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#校验access_key是否正确
|
||
if token['access_key'] != data['btauth_key']: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#解码数据
|
||
import binascii,hashlib,urllib,hmac,json
|
||
tdata = binascii.unhexlify(data['data'])
|
||
|
||
#校验signature是否正确
|
||
signature = binascii.hexlify(hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest())
|
||
if signature != data['signature']: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#返回
|
||
return json.loads(urllib.unquote(tdata))
|
||
|
||
|
||
#数据加密
|
||
def auth_encode(data):
|
||
token = GetToken()
|
||
pdata = {}
|
||
|
||
#是否有生成Token
|
||
if not token: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#生成signature
|
||
import binascii,hashlib,urllib,hmac,json
|
||
tdata = urllib.quote(json.dumps(data))
|
||
#公式 hex(hmac_sha256(data))
|
||
pdata['signature'] = binascii.hexlify(hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest())
|
||
|
||
#加密数据
|
||
pdata['btauth_key'] = token['access_key']
|
||
pdata['data'] = binascii.hexlify(tdata)
|
||
pdata['timestamp'] = time.time()
|
||
|
||
#返回
|
||
return pdata
|
||
|
||
#检查Token
|
||
def checkToken(get):
|
||
tempFile = 'data/tempToken.json'
|
||
if not os.path.exists(tempFile): return False
|
||
import json, time
|
||
tempToken = json.loads(readFile(tempFile))
|
||
if time.time() > tempToken['timeout']: return False
|
||
if get.token != tempToken['token']: return False
|
||
return True
|
||
|
||
#获取识别码
|
||
def get_uuid():
|
||
import uuid
|
||
return uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||
|
||
|
||
#进程是否存在
|
||
def process_exists(pname,exe = None,cmdline = None):
|
||
try:
|
||
import psutil
|
||
pids = psutil.pids()
|
||
for pid in pids:
|
||
try:
|
||
p = psutil.Process(pid)
|
||
if p.name() == pname:
|
||
if not exe and not cmdline:
|
||
return True
|
||
else:
|
||
if exe:
|
||
if p.exe() == exe: return True
|
||
if cmdline:
|
||
if cmdline in p.cmdline(): return True
|
||
except:
|
||
pass
|
||
return False
|
||
except:
|
||
return True
|
||
|
||
|
||
# 重启面板
|
||
def restart_panel():
|
||
import system
|
||
return system.system().ReWeb(None)
|
||
|
||
#获取mac
|
||
def get_mac_address():
|
||
import uuid
|
||
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
|
||
|
||
|
||
# 转码
|
||
def to_string(lites):
|
||
if type(lites) != list: lites = [lites]
|
||
m_str = ''
|
||
for mu in lites:
|
||
if sys.version_info[0] == 2:
|
||
m_str += unichr(mu).encode('utf-8')
|
||
else:
|
||
m_str += chr(mu)
|
||
return m_str
|
||
|
||
#解码
|
||
def to_ord(string):
|
||
o = []
|
||
for s in string:
|
||
o.append(ord(s))
|
||
return o
|
||
|
||
#xss 防御
|
||
def xssencode(text):
|
||
import cgi
|
||
list = ['`', '~', '&', '#', '/', '*', '$', '@', '<', '>', '\"', '\'', ';', '%', ',', '.', '\\u']
|
||
ret = []
|
||
for i in text:
|
||
if i in list:
|
||
i = ''
|
||
ret.append(i)
|
||
str_convert = ''.join(ret)
|
||
text2 = cgi.escape(str_convert, quote=True)
|
||
return text2
|
||
|
||
# 取缓存
|
||
def cache_get(key):
|
||
from BTPanel import cache
|
||
return cache.get(key)
|
||
|
||
# 设置缓存
|
||
def cache_set(key, value, timeout=None):
|
||
from BTPanel import cache
|
||
return cache.set(key, value, timeout)
|
||
|
||
# 删除缓存
|
||
def cache_remove(key):
|
||
from BTPanel import cache
|
||
return cache.delete(key)
|
||
|
||
# 取session值
|
||
def sess_get(key):
|
||
from BTPanel import session
|
||
if key in session: return session[key]
|
||
return None
|
||
|
||
# 设置或修改session值
|
||
def sess_set(key, value):
|
||
from BTPanel import session
|
||
session[key] = value
|
||
return True
|
||
|
||
# 删除指定session值
|
||
def sess_remove(key):
|
||
from BTPanel import session
|
||
if key in session: del (session[key])
|
||
return True
|
||
|
||
# 构造分页
|
||
def get_page(count, p=1, rows=12, callback='', result='1,2,3,4,5,8'):
|
||
import page
|
||
from BTPanel import request
|
||
page = page.Page();
|
||
info = {'count': count, 'row': rows, 'p': p, 'return_js': callback, 'uri': request.full_path}
|
||
data = {'page': page.GetPage(info, result), 'shift': str(page.SHIFT), 'row': str(page.ROW)}
|
||
return data
|
||
|
||
# 取面板版本
|
||
def version():
|
||
try:
|
||
comm = ReadFile('/www/server/panel/class/common.py')
|
||
return re.search("g\.version\s*=\s*'(\d+\.\d+\.\d+)'",comm).groups()[0]
|
||
except:
|
||
return get_panel_version()
|
||
|
||
def get_panel_version():
|
||
comm = ReadFile('/www/server/panel/class/common.py')
|
||
s_key = 'g.version = '
|
||
s_len = len(s_key)
|
||
s_leff = comm.find(s_key) + s_len
|
||
version = comm[s_leff:s_leff+6].strip().strip("'")
|
||
return version
|
||
|
||
|
||
# 取文件或目录大小
|
||
def get_path_size(path):
|
||
if not os.path.exists(path): return 0
|
||
if not os.path.isdir(path): return os.path.getsize(path)
|
||
size_total = 0
|
||
for nf in os.walk(path):
|
||
for f in nf[2]:
|
||
filename = nf[0] + '/' + f
|
||
if not os.path.exists(filename): continue
|
||
if os.path.islink(filename): continue
|
||
size_total += os.path.getsize(filename)
|
||
return size_total
|
||
|
||
#写关键请求日志
|
||
def write_request_log(reques = None):
|
||
try:
|
||
from BTPanel import request,g,session
|
||
if session.get('debug') == 1: return
|
||
if request.path in ['/service_status','/favicon.ico','/task','/system','/ajax','/control','/data','/ssl']:
|
||
return False
|
||
|
||
log_path = '/www/server/panel/logs/request'
|
||
log_file = getDate(format='%Y-%m-%d') + '.json'
|
||
if not os.path.exists(log_path): os.makedirs(log_path)
|
||
|
||
log_data = []
|
||
log_data.append(getDate())
|
||
log_data.append(GetClientIp() + ':' + str(request.environ.get('REMOTE_PORT')))
|
||
log_data.append(request.method)
|
||
log_data.append(request.full_path)
|
||
log_data.append(request.headers.get('User-Agent'))
|
||
if request.method == 'POST':
|
||
args = str(request.form.to_dict())
|
||
if len(args) < 2048 and args.find('pass') == -1 and args.find('user') == -1:
|
||
log_data.append(args)
|
||
else:
|
||
log_data.append('{}')
|
||
else:
|
||
log_data.append('{}')
|
||
log_data.append(int((time.time() - g.request_time) * 1000))
|
||
WriteFile(log_path + '/' + log_file,json.dumps(log_data) + "\n",'a+')
|
||
rep_sys_path()
|
||
except: pass
|
||
|
||
# 重载模块
|
||
def mod_reload(mode):
|
||
if not mode: return False
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
reload(mode)
|
||
else:
|
||
import imp
|
||
imp.reload(mode)
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
|
||
# 设置权限
|
||
def set_mode(filename, mode):
|
||
if not os.path.exists(filename): return False
|
||
mode = int(str(mode), 8)
|
||
os.chmod(filename, mode)
|
||
return True
|
||
|
||
|
||
# 设置用户组
|
||
def set_own(filename, user, group=None):
|
||
if not os.path.exists(filename): return False
|
||
from pwd import getpwnam
|
||
try:
|
||
user_info = getpwnam(user)
|
||
user = user_info.pw_uid
|
||
if group:
|
||
user_info = getpwnam(group)
|
||
group = user_info.pw_gid
|
||
except:
|
||
# 如果指定用户或组不存在,则使用www
|
||
user_info = getpwnam('www')
|
||
user = user_info.pw_uid
|
||
group = user_info.pw_gid
|
||
os.chown(filename, user, group)
|
||
return True
|
||
|
||
#校验路径安全
|
||
def path_safe_check(path,force=True):
|
||
if len(path) > 256: return False
|
||
checks = ['..','./','\\','%','$','^','&','*','~','"',"'",';','|','{','}','`']
|
||
for c in checks:
|
||
if path.find(c) != -1: return False
|
||
if force:
|
||
rep = r"^[\w\s\.\/-]+$"
|
||
if not re.match(rep,path): return False
|
||
return True
|
||
|
||
#取数据库字符集
|
||
def get_database_character(db_name):
|
||
try:
|
||
import panelMysql
|
||
tmp = panelMysql.panelMysql().query("show create database `%s`" % db_name.strip())
|
||
c_type = str(re.findall(r"SET\s+([\w\d-]+)\s",tmp[0][1])[0])
|
||
c_types = ['utf8','utf-8','gbk','big5','utf8mb4']
|
||
if not c_type.lower() in c_types: return 'utf8'
|
||
return c_type
|
||
except:
|
||
return 'utf8'
|
||
|
||
def en_punycode(domain):
|
||
if sys.version_info[0] == 2:
|
||
domain = domain.encode('utf8')
|
||
tmp = domain.split('.')
|
||
newdomain = ''
|
||
for dkey in tmp:
|
||
if dkey == '*': continue
|
||
#匹配非ascii字符
|
||
match = re.search(u"[\x80-\xff]+",dkey)
|
||
if not match: match = re.search(u"[\u4e00-\u9fa5]+",dkey)
|
||
if not match:
|
||
newdomain += dkey + '.'
|
||
else:
|
||
if sys.version_info[0] == 2:
|
||
newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.'
|
||
else:
|
||
newdomain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.'
|
||
if tmp[0] == '*': newdomain = "*." + newdomain
|
||
return newdomain[0:-1]
|
||
|
||
|
||
|
||
#punycode 转中文
|
||
def de_punycode(domain):
|
||
tmp = domain.split('.')
|
||
newdomain = ''
|
||
for dkey in tmp:
|
||
if dkey.find('xn--') >=0:
|
||
newdomain += dkey.replace('xn--','').encode('utf-8').decode('punycode') + '.'
|
||
else:
|
||
newdomain += dkey + '.'
|
||
return newdomain[0:-1]
|
||
|
||
#取计划任务文件路径
|
||
def get_cron_path():
|
||
u_file = '/var/spool/cron/crontabs/root'
|
||
if not os.path.exists(u_file):
|
||
file = '/var/spool/cron/root'
|
||
else:
|
||
file = u_file
|
||
return file
|
||
|
||
#加密字符串
|
||
def en_crypt(key,strings):
|
||
try:
|
||
if type(strings) != bytes: strings = strings.encode('utf-8')
|
||
from cryptography.fernet import Fernet
|
||
f = Fernet(key)
|
||
result = f.encrypt(strings)
|
||
return result.decode('utf-8')
|
||
except:
|
||
#print(get_error_info())
|
||
return strings
|
||
|
||
#解密字符串
|
||
def de_crypt(key,strings):
|
||
try:
|
||
if type(strings) != bytes: strings = strings.decode('utf-8')
|
||
from cryptography.fernet import Fernet
|
||
f = Fernet(key)
|
||
result = f.decrypt(strings).decode('utf-8')
|
||
return result
|
||
except:
|
||
#print(get_error_info())
|
||
return strings
|
||
|
||
|
||
#检查IP白名单
|
||
def check_ip_panel():
|
||
ip_file = 'data/limitip.conf'
|
||
if os.path.exists(ip_file):
|
||
iplist = ReadFile(ip_file)
|
||
if iplist:
|
||
iplist = iplist.strip()
|
||
client_ip = GetClientIp()
|
||
if client_ip in ['127.0.0.1','localhost','::1']: return False
|
||
if not client_ip in iplist.split(','):
|
||
errorStr = ReadFile('./BTPanel/templates/' + GetConfigValue('template') + '/error2.html')
|
||
try:
|
||
errorStr = errorStr.format(getMsg('PAGE_ERR_TITLE'),getMsg('PAGE_ERR_IP_H1'),getMsg('PAGE_ERR_IP_P1',(GetClientIp(),)),getMsg('PAGE_ERR_IP_P2'),getMsg('PAGE_ERR_IP_P3'),getMsg('NAME'),getMsg('PAGE_ERR_HELP'))
|
||
except IndexError:pass
|
||
return errorStr
|
||
return False
|
||
|
||
#检查面板域名
|
||
def check_domain_panel():
|
||
tmp = GetHost()
|
||
domain = ReadFile('data/domain.conf')
|
||
if domain:
|
||
client_ip = GetClientIp()
|
||
if client_ip in ['127.0.0.1','localhost','::1']: return False
|
||
if tmp.strip().lower() != domain.strip().lower():
|
||
errorStr = ReadFile('./BTPanel/templates/' + GetConfigValue('template') + '/error2.html')
|
||
try:
|
||
errorStr = errorStr.format(getMsg('PAGE_ERR_TITLE'),getMsg('PAGE_ERR_DOMAIN_H1'),getMsg('PAGE_ERR_DOMAIN_P1'),getMsg('PAGE_ERR_DOMAIN_P2'),getMsg('PAGE_ERR_DOMAIN_P3'),getMsg('NAME'),getMsg('PAGE_ERR_HELP'))
|
||
except:pass
|
||
return errorStr
|
||
return False
|
||
|
||
#是否离线模式
|
||
def is_local():
|
||
s_file = '/www/server/panel/data/not_network.pl'
|
||
return os.path.exists(s_file)
|
||
|
||
|
||
#自动备份面板数据
|
||
def auto_backup_panel():
|
||
try:
|
||
panel_paeh = '/www/server/panel'
|
||
paths = panel_paeh + '/data/not_auto_backup.pl'
|
||
if os.path.exists(paths): return False
|
||
b_path = '/www/backup/panel'
|
||
backup_path = b_path + '/' + format_date('%Y-%m-%d')
|
||
if os.path.exists(backup_path): return True
|
||
if os.path.getsize(panel_paeh + '/data/default.db') > 104857600 * 2: return False
|
||
os.makedirs(backup_path,384)
|
||
import shutil
|
||
shutil.copytree(panel_paeh + '/data',backup_path + '/data')
|
||
shutil.copytree(panel_paeh + '/config',backup_path + '/config')
|
||
shutil.copytree(panel_paeh + '/vhost',backup_path + '/vhost')
|
||
ExecShell("chmod -R 600 {path};chown -R root.root {path}".format(path=b_path))
|
||
time_now = time.time() - (86400 * 15)
|
||
for f in os.listdir(b_path):
|
||
if time.mktime(time.strptime(f, "%Y-%m-%d")) < time_now:
|
||
path = b_path + '/' + f
|
||
if os.path.exists(path): shutil.rmtree(path)
|
||
except:
|
||
pass
|
||
|
||
|
||
|
||
|
||
#检查端口状态
|
||
def check_port_stat(port,localIP = '127.0.0.1'):
|
||
import socket
|
||
temp = {}
|
||
temp['port'] = port
|
||
temp['local'] = True
|
||
try:
|
||
s = socket.socket()
|
||
s.settimeout(0.15)
|
||
s.connect((localIP,port))
|
||
s.close()
|
||
except:
|
||
temp['local'] = False
|
||
|
||
result = 0
|
||
if temp['local']: result +=2
|
||
return result
|
||
|
||
|
||
#同步时间
|
||
def sync_date():
|
||
tip_file = "/dev/shm/last_sync_time.pl"
|
||
s_time = int(time.time())
|
||
try:
|
||
if os.path.exists(tip_file):
|
||
if s_time - int(readFile(tip_file)) < 60: return False
|
||
os.remove(tip_file)
|
||
time_str = HttpGet('http://www.bt.cn/api/index/get_time')
|
||
new_time = int(time_str)
|
||
time_arr = time.localtime(new_time)
|
||
date_str = time.strftime("%Y-%m-%d %H:%M:%S", time_arr)
|
||
ExecShell('date -s "%s"' % date_str)
|
||
writeFile(tip_file,str(s_time))
|
||
return True
|
||
except:
|
||
if os.path.exists(tip_file): os.remove(tip_file)
|
||
return False
|
||
|
||
|
||
#重载模块
|
||
def reload_mod(mod_name = None):
|
||
#是否重载指定模块
|
||
modules = []
|
||
if mod_name:
|
||
if type(mod_name) == str:
|
||
mod_names = mod_name.split(',')
|
||
|
||
for mod_name in mod_names:
|
||
if mod_name in sys.modules:
|
||
print(mod_name)
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
reload(sys.modules[mod_name])
|
||
else:
|
||
importlib.reload(sys.modules[mod_name])
|
||
modules.append([mod_name,True])
|
||
except:
|
||
modules.append([mod_name,False])
|
||
else:
|
||
modules.append([mod_name,False])
|
||
return modules
|
||
|
||
#重载所有模块
|
||
for mod_name in sys.modules.keys():
|
||
if mod_name in ['BTPanel']: continue
|
||
f = getattr(sys.modules[mod_name],'__file__',None)
|
||
if f:
|
||
try:
|
||
if f.find('panel/') == -1: continue
|
||
if sys.version_info[0] == 2:
|
||
reload(sys.modules[mod_name])
|
||
else:
|
||
importlib.reload(sys.modules[mod_name])
|
||
modules.append([mod_name,True])
|
||
except:
|
||
modules.append([mod_name,False])
|
||
return modules
|
||
|
||
|
||
def de_hexb(data):
|
||
if sys.version_info[0] != 2:
|
||
if type(data) == str: data = data.encode('utf-8')
|
||
pdata = base64.b64encode(data)
|
||
if sys.version_info[0] != 2:
|
||
if type(pdata) == str: pdata = pdata.encode('utf-8')
|
||
return binascii.hexlify(pdata)
|
||
|
||
def en_hexb(data):
|
||
if sys.version_info[0] != 2:
|
||
if type(data) == str: data = data.encode('utf-8')
|
||
result = base64.b64decode(binascii.unhexlify(data))
|
||
if type(result) != str: result = result.decode('utf-8')
|
||
return result
|
||
|
||
# def upload_file_url(filename):
|
||
# try:
|
||
# if os.path.exists(filename):
|
||
# data = ExecShell('/usr/bin/curl https://scanner.baidu.com/enqueue -F archive=@%s' % filename)
|
||
# data = json.loads(data[0])
|
||
# time.sleep(1)
|
||
# import requests
|
||
# default_headers = {
|
||
# 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
|
||
# }
|
||
# data_list = requests.get(url=data['url'], headers=default_headers, verify=False)
|
||
# return (data_list.json())
|
||
# else:
|
||
# return False
|
||
# except:
|
||
# return False
|
||
|
||
#直接请求到PHP-FPM
|
||
#version php版本
|
||
#uri 请求uri
|
||
#filename 要执行的php文件
|
||
#args 请求参数
|
||
#method 请求方式
|
||
def request_php(version,uri,document_root,method='GET',pdata=b''):
|
||
import panelPHP
|
||
if type(pdata) == dict: pdata = url_encode(pdata)
|
||
fpm_address = get_fpm_address(version)
|
||
p = panelPHP.FPM(fpm_address,document_root)
|
||
result = p.load_url_public(uri,pdata,method)
|
||
return result
|
||
|
||
|
||
def get_fpm_address(php_version):
|
||
'''
|
||
@name 获取FPM请求地址
|
||
@author hwliang<2020-10-23>
|
||
@param php_version string PHP版本
|
||
@return tuple or string
|
||
'''
|
||
fpm_address = '/tmp/php-cgi-{}.sock'.format(php_version)
|
||
php_fpm_file = '/www/server/php/{}/etc/php-fpm.conf'.format(php_version)
|
||
try:
|
||
fpm_conf = readFile(php_fpm_file)
|
||
tmp = re.findall(r"listen\s*=\s*(.+)",fpm_conf)
|
||
if not tmp: return fpm_address
|
||
if tmp[0].find('sock') != -1: return fpm_address
|
||
if tmp[0].find(':') != -1:
|
||
listen_tmp = tmp[0].split(':')
|
||
fpm_address = ('127.0.0.1',int(listen_tmp[1]))
|
||
else:
|
||
fpm_address = ('127.0.0.1',int(tmp[0]))
|
||
return fpm_address
|
||
except:
|
||
return fpm_address
|
||
|
||
|
||
def get_php_proxy(php_version,webserver = 'nginx'):
|
||
'''
|
||
@name 获取PHP代理地址
|
||
@author hwliang<2020-10-24>
|
||
@param php_version string php版本 (52|53|54|55|56|70|71|72|73|74)
|
||
@param webserver string web服务器类型 (nginx|apache|ols)
|
||
return string
|
||
'''
|
||
php_address = get_fpm_address(php_version)
|
||
if isinstance(php_address,str):
|
||
if webserver == 'nginx':
|
||
return 'unix:{}'.format(php_address)
|
||
elif webserver == 'apache':
|
||
return 'unix:{}|fcgi://localhost'.format(php_address)
|
||
else:
|
||
if webserver == 'nginx':
|
||
return '{}:{}'.format(php_address[0],php_address[1])
|
||
elif webserver == 'apache':
|
||
return 'fcgi://{}:{}'.format(php_address[0],php_address[1])
|
||
|
||
def get_php_version_conf(conf):
|
||
'''
|
||
@name 从指定配置文件获取PHP版本
|
||
@author hwliang<2020-10-24>
|
||
@param conf string 配置文件内容
|
||
@return string
|
||
'''
|
||
if not conf: return '00'
|
||
if conf.find('enable-php-') != -1:
|
||
rep = r"enable-php-([0-9]{2,3})\.conf"
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp: return '00'
|
||
elif conf.find('/usr/local/lsws/lsphp') != -1:
|
||
rep = r"path\s*/usr/local/lsws/lsphp(\d+)/bin/lsphp"
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp: return '00'
|
||
else:
|
||
rep = r"php-cgi-([0-9]{2,3})\.sock"
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp:
|
||
rep = r'127.0.0.1:10(\d{2,2})1'
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp:
|
||
return '00'
|
||
return tmp[0]
|
||
|
||
|
||
def get_site_php_version(siteName):
|
||
'''
|
||
@name 获取指定网站当前使用的PHP版本
|
||
@author hwliang<2020-10-24>
|
||
@param siteName string 网站名称
|
||
@return string
|
||
'''
|
||
web_server = get_webserver()
|
||
conf = readFile('/www/server/panel/vhost/'+web_server+'/'+siteName+'.conf')
|
||
if web_server == 'openlitespeed':
|
||
conf = readFile('/www/server/panel/vhost/' + web_server + '/detail/' + siteName + '.conf')
|
||
return get_php_version_conf(conf)
|
||
|
||
|
||
def sub_php_address(conf_file,rep,tsub,php_version):
|
||
'''
|
||
@name 替换新的PHP配置到配置文件
|
||
@author hwliang<2020-10-24>
|
||
@param conf_file string 配置文件全路径
|
||
@param rep string 用于查找目标替换内容的正则表达式
|
||
@param tsub string 新的内容
|
||
@param php_version string 指定PHP版本
|
||
@return bool
|
||
'''
|
||
if not os.path.isfile(conf_file): return False
|
||
if not os.path.exists(conf_file): return False
|
||
conf = readFile(conf_file)
|
||
if not conf: return False
|
||
#if conf.find('#PHP') == -1 and conf.find('pathinfo.conf') == -1: return False
|
||
phpv = get_php_version_conf(conf)
|
||
if phpv != php_version: return False
|
||
tmp = re.search(rep,conf)
|
||
if not tmp: return False
|
||
if tmp.group() == tsub: return False
|
||
conf = conf.replace(tmp.group(),tsub) #re.sub(rep,php_proxy,conf)
|
||
writeFile(conf_file,conf)
|
||
return True
|
||
|
||
|
||
def sync_all_address():
|
||
'''
|
||
@name 同步所有PHP版本配置到配置文件
|
||
@author hwliang<2020-10-24>
|
||
@return void
|
||
'''
|
||
php_versions = ['52','53','54','55','56','70','71','72','73','74','75','80','81']
|
||
for phpv in php_versions:
|
||
sync_php_address(phpv)
|
||
|
||
def sync_php_address(php_version):
|
||
'''
|
||
@name 同步PHP版本配置到所有配置文件
|
||
@author hwliang<2020-10-24>
|
||
@param php_version string PHP版本
|
||
@return void
|
||
'''
|
||
if not os.path.exists('/www/server/php/{}/bin/php'.format(php_version)): # 指定PHP版本是否安装
|
||
return False
|
||
ngx_rep = r"(unix:/tmp/php-cgi.*\.sock|127.0.0.1:\d+)"
|
||
apa_rep = r"(unix:/tmp/php-cgi.*\.sock\|fcgi://localhost|fcgi://127.0.0.1:\d+)"
|
||
ngx_proxy = get_php_proxy(php_version,'nginx')
|
||
apa_proxy = get_php_proxy(php_version,'apache')
|
||
is_write = False
|
||
|
||
#nginx的PHP配置文件
|
||
nginx_conf_path = '/www/server/nginx/conf'
|
||
|
||
if os.path.exists(nginx_conf_path):
|
||
for f_name in os.listdir(nginx_conf_path):
|
||
if f_name.find('enable-php') != -1:
|
||
conf_file = '/'.join((nginx_conf_path,f_name))
|
||
if sub_php_address(conf_file,ngx_rep,ngx_proxy,php_version):
|
||
is_write = True
|
||
#nginx的phpmyadmin
|
||
# conf_file = '/www/server/nginx/conf/nginx.conf'
|
||
# if os.path.exists(conf_file):
|
||
# if sub_php_address(conf_file,ngx_rep,ngx_proxy,php_version):
|
||
# is_write = True
|
||
|
||
#apache的网站配置文件
|
||
apache_conf_path = '/www/server/panel/vhost/apache'
|
||
if os.path.exists(apache_conf_path):
|
||
for f_name in os.listdir(apache_conf_path):
|
||
conf_file = '/'.join((apache_conf_path,f_name))
|
||
if sub_php_address(conf_file,apa_rep,apa_proxy,php_version):
|
||
is_write = True
|
||
#apache的phpmyadmin
|
||
conf_file = '/www/server/apache/conf/extra/httpd-vhosts.conf'
|
||
if os.path.exists(conf_file):
|
||
if sub_php_address(conf_file,apa_rep,apa_proxy,php_version):
|
||
is_write = True
|
||
|
||
if is_write: serviceReload()
|
||
return True
|
||
|
||
|
||
|
||
|
||
def url_encode(data):
|
||
if type(data) == str: return data
|
||
import urllib
|
||
if sys.version_info[0] != 2:
|
||
pdata = urllib.parse.urlencode(data).encode('utf-8')
|
||
else:
|
||
pdata = urllib.urlencode(data)
|
||
return pdata
|
||
|
||
def url_decode(data):
|
||
if type(data) == str: return data
|
||
import urllib
|
||
if sys.version_info[0] != 2:
|
||
pdata = urllib.parse.urldecode(data).encode('utf-8')
|
||
else:
|
||
pdata = urllib.urldecode(data)
|
||
return pdata
|
||
|
||
|
||
def unicode_encode(data):
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
result = unicode(data,errors='ignore')
|
||
else:
|
||
result = data.encode('utf8',errors='ignore')
|
||
return result
|
||
except: return data
|
||
|
||
def unicode_decode(data,charset = 'utf8'):
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
result = unicode(data,errors='ignore')
|
||
else:
|
||
result = data.decode('utf8',errors='ignore')
|
||
return result
|
||
except: return data
|
||
|
||
def import_cdn_plugin():
|
||
plugin_path = 'plugin/static_cdn'
|
||
if not os.path.exists(plugin_path): return True
|
||
try:
|
||
import static_cdn_main
|
||
except:
|
||
package_path_append(plugin_path)
|
||
import static_cdn_main
|
||
|
||
|
||
def get_cdn_hosts():
|
||
try:
|
||
if import_cdn_plugin(): return []
|
||
import static_cdn_main
|
||
return static_cdn_main.static_cdn_main().get_hosts(None)
|
||
except:
|
||
return []
|
||
|
||
def get_cdn_url():
|
||
try:
|
||
if os.path.exists('plugin/static_cdn/not_open.pl'):
|
||
return False
|
||
from BTPanel import cache
|
||
cdn_url = cache.get('cdn_url')
|
||
if cdn_url: return cdn_url
|
||
if import_cdn_plugin(): return False
|
||
import static_cdn_main
|
||
cdn_url = static_cdn_main.static_cdn_main().get_url(None)
|
||
cache.set('cdn_url',cdn_url,3)
|
||
return cdn_url
|
||
except:
|
||
return False
|
||
|
||
def set_cdn_url(cdn_url):
|
||
if not cdn_url: return False
|
||
import_cdn_plugin()
|
||
get = dict_obj()
|
||
get.cdn_url = cdn_url
|
||
import static_cdn_main
|
||
static_cdn_main.static_cdn_main().set_url(get)
|
||
return True
|
||
|
||
def get_python_bin():
|
||
bin_file = '/www/server/panel/pyenv/bin/python'
|
||
if os.path.exists(bin_file):
|
||
return bin_file
|
||
return '/usr/bin/python'
|
||
|
||
def aes_encrypt(data,key):
|
||
import panelAes
|
||
if sys.version_info[0] == 2:
|
||
aes_obj = panelAes.aescrypt_py2(key)
|
||
return aes_obj.aesencrypt(data)
|
||
else:
|
||
aes_obj = panelAes.aescrypt_py3(key)
|
||
return aes_obj.aesencrypt(data)
|
||
|
||
def aes_decrypt(data,key):
|
||
import panelAes
|
||
if sys.version_info[0] == 2:
|
||
aes_obj = panelAes.aescrypt_py2(key)
|
||
return aes_obj.aesdecrypt(data)
|
||
else:
|
||
aes_obj = panelAes.aescrypt_py3(key)
|
||
return aes_obj.aesdecrypt(data)
|
||
|
||
#清理大日志文件
|
||
def clean_max_log(log_file,max_size = 100,old_line = 100):
|
||
if not os.path.exists(log_file): return False
|
||
max_size = 1024 * 1024 * max_size
|
||
if os.path.getsize(log_file) > max_size:
|
||
try:
|
||
old_body = GetNumLines(log_file,old_line)
|
||
writeFile(log_file,old_body)
|
||
except:
|
||
print(get_error_info())
|
||
|
||
#获取证书哈希
|
||
def get_cert_data(path):
|
||
import panelSSL
|
||
get = dict_obj()
|
||
get.certPath = path
|
||
data = panelSSL.panelSSL().GetCertName(get)
|
||
return data
|
||
|
||
# 获取系统发行版
|
||
def get_linux_distribution():
|
||
distribution = 'ubuntu'
|
||
redhat_file = '/etc/redhat-release'
|
||
if os.path.exists(redhat_file):
|
||
try:
|
||
tmp = readFile(redhat_file).split()[3][0]
|
||
distribution = 'centos{}'.format(tmp)
|
||
except:
|
||
distribution = 'centos7'
|
||
return distribution
|
||
|
||
def long2ip(ips):
|
||
'''
|
||
@name 将整数转换为IP地址
|
||
@author hwliang<2020-06-11>
|
||
@param ips string(ip地址整数)
|
||
@return ipv4
|
||
'''
|
||
i1 = int(ips / (2 ** 24))
|
||
i2 = int((ips - i1 * ( 2 ** 24 )) / ( 2 ** 16 ))
|
||
i3 = int(((ips - i1 * ( 2 ** 24 )) - i2 * ( 2 ** 16 )) / ( 2 ** 8))
|
||
i4 = int(((ips - i1 * ( 2 ** 24 )) - i2 * ( 2 ** 16 )) - i3 * ( 2 ** 8))
|
||
return "{}.{}.{}.{}".format(i1,i2,i3,i4)
|
||
|
||
def ip2long(ip):
|
||
'''
|
||
@name 将IP地址转换为整数
|
||
@author hwliang<2020-06-11>
|
||
@param ip string(ipv4)
|
||
@return long
|
||
'''
|
||
ips = ip.split('.')
|
||
if len(ips) != 4: return 0
|
||
iplong = 2 ** 24 * int(ips[0]) + 2 ** 16 * int(ips[1]) + 2 ** 8 * int(ips[2]) + int(ips[3])
|
||
return iplong
|
||
|
||
def is_local_ip(ip):
|
||
'''
|
||
@name 判断是否为本地(内网)IP地址
|
||
@author hwliang<2021-03-26>
|
||
@param ip string(ipv4)
|
||
@return bool
|
||
'''
|
||
patt = r"^(192\.168|127|10|172\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31))\."
|
||
if re.match(patt,ip): return True
|
||
return False
|
||
|
||
#获取debug日志
|
||
def get_debug_log():
|
||
from BTPanel import request
|
||
return GetClientIp() +':'+ str(request.environ.get('REMOTE_PORT')) + '|' + str(int(time.time())) + '|' + get_error_info()
|
||
|
||
#获取sessionid
|
||
def get_session_id():
|
||
from BTPanel import request
|
||
session_id = request.cookies.get('SESSIONID','')
|
||
if not re.findall(r"^([\w\.-]{64,64})$",session_id): return GetRandomString(64)
|
||
return session_id
|
||
|
||
#尝试自动恢复面板数据库
|
||
def rep_default_db():
|
||
db_path = '/www/server/panel/data/'
|
||
db_file = db_path + 'default.db'
|
||
db_tmp_backup = db_path + 'default_' + format_date("%Y%m%d_%H%M%S") + ".db"
|
||
|
||
panel_backup = '/www/backup/panel'
|
||
bak_list = os.listdir(panel_backup)
|
||
if not bak_list: return False
|
||
bak_list = sorted(bak_list,reverse=True)
|
||
db_bak_file = ''
|
||
for d_name in bak_list:
|
||
db_bak_file = panel_backup + '/' + d_name + '/data/default.db'
|
||
if not os.path.exists(db_bak_file): continue
|
||
if os.path.getsize(db_bak_file) < 17408: continue
|
||
break
|
||
|
||
if not db_bak_file: return False
|
||
ExecShell("\cp -arf {} {}".format(db_file,db_tmp_backup))
|
||
ExecShell("\cp -arf {} {}".format(db_bak_file,db_file))
|
||
return True
|
||
|
||
|
||
|
||
def chdck_salt():
|
||
'''
|
||
@name 检查所有用户密码是否加盐,若没有则自动加上
|
||
@author hwliang<2020-07-08>
|
||
@return void
|
||
'''
|
||
|
||
if not M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'users','%salt%')).count():
|
||
M('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
|
||
u_list = M('users').where('salt is NULL',()).field('id,username,password,salt').select()
|
||
if isinstance(u_list,str):
|
||
if u_list.find('no such table: users') != -1:
|
||
rep_default_db()
|
||
if not M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'users','%salt%')).count():
|
||
M('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
|
||
u_list = M('users').where('salt is NULL',()).field('id,username,password,salt').select()
|
||
|
||
for u_info in u_list:
|
||
salt = GetRandomString(12) #12位随机
|
||
pdata = {}
|
||
pdata['password'] = md5(md5(u_info['password']+'_bt.cn') + salt)
|
||
pdata['salt'] = salt
|
||
M('users').where('id=?',(u_info['id'],)).update(pdata)
|
||
|
||
|
||
def get_login_token():
|
||
token_s = readFile('/www/server/panel/data/login_token.pl')
|
||
if not token_s: return GetRandomString(32)
|
||
return token_s
|
||
|
||
def get_sess_key():
|
||
from BTPanel import session
|
||
return md5(get_login_token() + session.get('request_token_head',''))
|
||
|
||
|
||
def password_salt(password,username=None,uid=None):
|
||
'''
|
||
@name 为指定密码加盐
|
||
@author hwliang<2020-07-08>
|
||
@param password string(被md5加密一次的密码)
|
||
@param username string(用户名) 可选
|
||
@param uid int(uid) 可选
|
||
@return string
|
||
'''
|
||
chdck_salt()
|
||
if not uid:
|
||
if not username:
|
||
raise Exception('username或uid必需传一项')
|
||
uid = M('users').where('username=?',(username,)).getField('id')
|
||
salt = M('users').where('id=?',(uid,)).getField('salt')
|
||
return md5(md5(password+'_bt.cn')+salt)
|
||
|
||
# 备份配置文件
|
||
def back_file(file, act=None):
|
||
"""
|
||
@name 备份配置文件
|
||
@author zhwen<zhw@bt.cn>
|
||
@param file 需要备份的文件
|
||
@param act 如果存在,则备份一份作为默认配置
|
||
"""
|
||
file_type = "_bak"
|
||
if act:
|
||
file_type = "_def"
|
||
ExecShell("/usr/bin/cp -p {0} {1}".format(file, file + file_type))
|
||
|
||
# 还原配置文件
|
||
def restore_file(file, act=None):
|
||
"""
|
||
@name 还原配置文件
|
||
@author zhwen<zhw@bt.cn>
|
||
@param file 需要还原的文件
|
||
@param act 如果存在,则还原默认配置
|
||
"""
|
||
file_type = "_bak"
|
||
if act:
|
||
file_type = "_def"
|
||
ExecShell("/usr/bin/cp -p {1} {0}".format(file, file + file_type))
|
||
|
||
|
||
def package_path_append(path):
|
||
if not path in sys.path:
|
||
sys.path.insert(0, path)
|
||
|
||
|
||
def rep_sys_path():
|
||
sys_path = []
|
||
for p in sys.path:
|
||
if p in sys_path: continue
|
||
sys_path.append(p)
|
||
sys.path = sys_path
|
||
|
||
|
||
def get_ssh_port():
|
||
'''
|
||
@name 获取本机SSH端口
|
||
@author hwliang<2020-08-07>
|
||
@return int
|
||
'''
|
||
s_file = '/etc/ssh/sshd_config'
|
||
conf = readFile(s_file)
|
||
if not conf: conf = ''
|
||
rep = r"#*Port\s+([0-9]+)\s*\n"
|
||
tmp1 = re.search(rep, conf)
|
||
ssh_port = 22
|
||
if tmp1:
|
||
ssh_port = int(tmp1.groups(0)[0])
|
||
return ssh_port
|
||
|
||
def set_error_num(key,empty = False,expire=3600):
|
||
'''
|
||
@name 设置失败次数(每调用一次+1)
|
||
@author hwliang<2020-08-21>
|
||
@param key<string> 索引
|
||
@param empty<bool> 是否清空计数
|
||
@param expire<int> 计数器生命周期(秒)
|
||
@return bool
|
||
'''
|
||
from BTPanel import cache
|
||
key = md5(key)
|
||
num = cache.get(key)
|
||
if not num:
|
||
num = 0
|
||
else:
|
||
if empty:
|
||
cache.delete(key)
|
||
return True
|
||
cache.set(key,num + 1,expire)
|
||
return True
|
||
|
||
def get_error_num(key,limit=False):
|
||
'''
|
||
@name 获取失败次数
|
||
@author hwliang<2020-08-21>
|
||
@param key<string> 索引
|
||
@param limit<False or int> 如果为False,则直接返回失败次数,否则与失败次数比较,若大于失败次数返回True,否则返回False
|
||
@return int or bool
|
||
'''
|
||
from BTPanel import cache
|
||
key = md5(key)
|
||
num = cache.get(key)
|
||
if not num: num = 0
|
||
if not limit:
|
||
return num
|
||
if limit > num:
|
||
return True
|
||
return False
|
||
|
||
|
||
def get_menus():
|
||
'''
|
||
@name 获取菜单列表
|
||
@author hwliang<2020-08-31>
|
||
@return list
|
||
'''
|
||
from BTPanel import session
|
||
data = json.loads(ReadFile('config/menu.json'))
|
||
hide_menu = ReadFile('config/hide_menu.json')
|
||
debug = session.get('debug')
|
||
if hide_menu:
|
||
hide_menu = json.loads(hide_menu)
|
||
show_menu = []
|
||
for i in range(len(data)):
|
||
if data[i]['id'] in hide_menu: continue
|
||
if data[i]['id'] == "memuAxterm":
|
||
if debug: continue
|
||
show_menu.append(data[i])
|
||
data = show_menu
|
||
del(hide_menu)
|
||
del(show_menu)
|
||
menus = sorted(data, key=lambda x: x['sort'])
|
||
return menus
|
||
|
||
|
||
#取CURL路径
|
||
def get_curl_bin():
|
||
'''
|
||
@name 取CURL执行路径
|
||
@author hwliang<2020-09-01>
|
||
@return string
|
||
'''
|
||
c_bin = ['/usr/local/curl2/bin/curl','/usr/local/curl/bin/curl','/usr/bin/curl']
|
||
for cb in c_bin:
|
||
if os.path.exists(cb): return cb
|
||
return 'curl'
|
||
|
||
|
||
#设置防跨站配置
|
||
def set_open_basedir():
|
||
try:
|
||
fastcgi_file = '/www/server/nginx/conf/fastcgi.conf'
|
||
if os.path.exists(fastcgi_file):
|
||
fastcgi_body = readFile(fastcgi_file)
|
||
if fastcgi_body.find('bt_safe_dir') == -1:
|
||
fastcgi_body = fastcgi_body + "\n"+'fastcgi_param PHP_ADMIN_VALUE "$bt_safe_dir=$bt_safe_open";'
|
||
writeFile(fastcgi_file,fastcgi_body)
|
||
|
||
proxy_file = '/www/server/nginx/conf/proxy.conf'
|
||
if os.path.exists(proxy_file):
|
||
proxy_body = readFile(proxy_file)
|
||
if proxy_body.find('bt_safe_dir') == -1:
|
||
proxy_body = proxy_body + "\n"+'''map "baota_dir" $bt_safe_dir {
|
||
default "baota_dir";
|
||
}
|
||
map "baota_open" $bt_safe_open {
|
||
default "baota_open";
|
||
} '''
|
||
writeFile(proxy_file,proxy_body)
|
||
|
||
open_basedir_path = '/www/server/panel/vhost/open_basedir/nginx'
|
||
if not os.path.exists(open_basedir_path):
|
||
os.makedirs(open_basedir_path,384)
|
||
|
||
site_list = M('sites').field('id,name,path').select()
|
||
for site_info in site_list:
|
||
set_site_open_basedir_nginx(site_info['name'])
|
||
except: return
|
||
|
||
|
||
#处理指定站点的防跨站配置 for Nginx
|
||
def set_site_open_basedir_nginx(siteName):
|
||
try:
|
||
return
|
||
open_basedir_path = '/www/server/panel/vhost/open_basedir/nginx'
|
||
if not os.path.exists(open_basedir_path):
|
||
os.makedirs(open_basedir_path,384)
|
||
config_file = '/www/server/panel/vhost/nginx/{}.conf'.format(siteName)
|
||
open_basedir_file = "/".join(
|
||
(open_basedir_path,'{}.conf'.format(siteName))
|
||
)
|
||
if not os.path.exists(config_file): return
|
||
if not os.path.exists(open_basedir_file):
|
||
writeFile(open_basedir_file,'')
|
||
config_body = readFile(config_file)
|
||
if config_body.find(open_basedir_path) == -1:
|
||
config_body = config_body.replace("include enable-php","include {};\n\t\tinclude enable-php".format(open_basedir_file))
|
||
writeFile(config_file,config_body)
|
||
|
||
root_path = re.findall(r"root\s+(.+);",config_body)[0]
|
||
if not root_path: return
|
||
userini_file = root_path + '/.user.ini'
|
||
if not os.path.exists(userini_file):
|
||
writeFile(open_basedir_file,'')
|
||
return
|
||
userini_body = readFile(userini_file)
|
||
if not userini_body: return
|
||
if userini_body.find('open_basedir') == -1:
|
||
writeFile(open_basedir_file,'')
|
||
return
|
||
|
||
open_basedir_conf = re.findall("open_basedir=(.+)",userini_body)
|
||
if not open_basedir_conf: return
|
||
open_basedir_conf = open_basedir_conf[0]
|
||
open_basedir_body = '''set $bt_safe_dir "open_basedir";
|
||
set $bt_safe_open "{}";'''.format(open_basedir_conf)
|
||
writeFile(open_basedir_file,open_basedir_body)
|
||
except: return
|
||
|
||
|
||
def run_thread(fun,args = (),daemon=False):
|
||
'''
|
||
@name 使用线程执行指定方法
|
||
@author hwliang<2020-10-27>
|
||
@param fun {def} 函数对像
|
||
@param args {tuple} 参数元组
|
||
@param daemon {bool} 是否守护线程
|
||
@return bool
|
||
'''
|
||
import threading
|
||
p = threading.Thread(target=fun,args=args)
|
||
p.setDaemon(daemon)
|
||
p.start()
|
||
return True
|
||
|
||
def check_domain_cloud(domain):
|
||
run_thread(cloud_check_domain,(domain,))
|
||
|
||
def cloud_check_domain(domain):
|
||
'''
|
||
@name 从云端验证域名的可访问性,并将结果保存到文件
|
||
@author hwliang<2020-12-10>
|
||
@param domain {string} 被验证的域名
|
||
@return void
|
||
'''
|
||
try:
|
||
check_domain_path = '/www/server/panel/data/check_domain/'
|
||
if not os.path.exists(check_domain_path):
|
||
os.makedirs(check_domain_path,384)
|
||
result = httpPost('https://www.aapanel.com/api/panel/checkDomain',{"domain":domain})
|
||
cd_file = check_domain_path + domain +'.pl'
|
||
writeFile(cd_file,result)
|
||
except:
|
||
pass
|
||
|
||
|
||
def send_file(data,fname='',mimetype = ''):
|
||
'''
|
||
@name 以文件流的形式返回
|
||
@author heliang<2020-10-27>
|
||
@param data {bytes|string} 文件数据或路径
|
||
@param mimetype {string} 文件类型
|
||
@param fname {string} 文件名
|
||
@return Response
|
||
'''
|
||
d_type = type(data)
|
||
from io import BytesIO,StringIO
|
||
from flask import send_file as send_to
|
||
if d_type == bytes:
|
||
fp = BytesIO(data)
|
||
else:
|
||
if len(data) < 128:
|
||
if os.path.exists(data):
|
||
fp = data
|
||
if not fname:
|
||
fname = os.path.basename(fname)
|
||
else:
|
||
fp = StringIO(data)
|
||
else:
|
||
fp = StringIO(data)
|
||
|
||
if not mimetype: mimetype = "application/octet-stream"
|
||
if not fname: fname = 'doan.txt'
|
||
|
||
return send_to(fp,
|
||
mimetype=mimetype,
|
||
as_attachment=True,
|
||
add_etags=True,
|
||
conditional=True,
|
||
attachment_filename=fname,
|
||
cache_timeout=0)
|
||
|
||
def gen_password(length=8,chars=string.ascii_letters+string.digits):
|
||
return ''.join([choice(chars) for i in range(length)])
|
||
|
||
def get_ipaddress():
|
||
'''
|
||
@name 获取本机IP地址
|
||
@author hwliang<2020-11-24>
|
||
@return list
|
||
'''
|
||
ipa_tmp = ExecShell("ip a |grep inet|grep -v inet6|grep -v 127.0.0.1|grep -v 'inet 192.168.'|grep -v 'inet 10.'|awk '{print $2}'|sed 's#/[0-9]*##g'")[0].strip()
|
||
iplist = ipa_tmp.split('\n')
|
||
return iplist
|
||
|
||
def get_oem_name():
|
||
'''
|
||
@name 获取OEM名称
|
||
@author hwliang<2021-03-24>
|
||
@return string
|
||
'''
|
||
oem = ''
|
||
oem_file = '/www/server/panel/data/o.pl'
|
||
if os.path.exists(oem_file):
|
||
oem = readFile(oem_file)
|
||
if oem: oem = oem.strip()
|
||
return oem
|
||
|
||
def fetch_disk_SN():
|
||
r,e = ExecShell("fdisk -l |grep 'Disk identifier' |awk {'print $3'}")
|
||
if r:
|
||
return r.split('\n')[0]
|
||
|
||
def fetch_cpu_ID():
|
||
r, e = ExecShell("cat /proc/cpuinfo|grep microcode|awk -F':' '{print $2}'")
|
||
if r:
|
||
return r.split('\n')[0]
|
||
|
||
def get_hostname():
|
||
import socket
|
||
return socket.getfqdn(socket.gethostname())
|
||
|
||
def get_platform():
|
||
import platform
|
||
return platform.platform()
|
||
|
||
def get_memory():
|
||
import psutil
|
||
return psutil.virtual_memory().total
|
||
|
||
def fetch_env_info():
|
||
import panelAuth
|
||
userInfo = panelAuth.panelAuth().create_serverid(None)
|
||
try:
|
||
return {'ip': GetLocalIp(),
|
||
'is_ipv6': 0,
|
||
'os': get_platform(),
|
||
'mac': get_mac_address(),
|
||
'hdid': fetch_disk_SN(),
|
||
'ramid': get_memory(),
|
||
'cpuid': fetch_cpu_ID(),
|
||
'server_name': get_hostname(),
|
||
'install_code': userInfo['server_id']
|
||
}
|
||
except:
|
||
return {}
|
||
|
||
def arequests(method,url,data=None,timeout=3):
|
||
import threading
|
||
if method == 'post':
|
||
method = httpPost
|
||
else:
|
||
method = httpGet
|
||
threading.Thread(target=method, args=(url,data,timeout)).start()
|
||
|
||
#取通用对象
|
||
class dict_obj:
|
||
def __contains__(self, key):
|
||
return getattr(self,key,None)
|
||
def __setitem__(self, key, value): setattr(self,key,value)
|
||
def __getitem__(self, key): return getattr(self,key,None)
|
||
def __delitem__(self,key): delattr(self,key)
|
||
def __delattr__(self, key): delattr(self,key)
|
||
def get_items(self): return self
|
||
def get(self,key,default='',format='',limit = []):
|
||
'''
|
||
@name 获取指定参数
|
||
@param key<string> 参数名称,允许在/后面限制参数格式,请参考参数值格式(format)
|
||
@param default<string> 默认值,默认空字符串
|
||
@param format<string> 参数值格式(int|str|float|json|xss|path|url|ip|ipv4|ipv6|letter|mail|phone|正则表达式|>1|<1|=1),默认为空
|
||
@param limit<list> 限制参数值内容
|
||
@param return mixed
|
||
'''
|
||
if key.find('/') != -1:
|
||
key,format = key.split('/')
|
||
result = getattr(self,key,default).strip()
|
||
if format:
|
||
if format in ['str','string','s']:
|
||
result = str(result)
|
||
elif format in ['int','d']:
|
||
try:
|
||
result = int(result)
|
||
except:
|
||
raise ValueError("Parameters: {}, requires int type data".format(key))
|
||
elif format in ['float','f']:
|
||
try:
|
||
result = float(result)
|
||
except:
|
||
raise ValueError("Parameters: {}, float type data required".format(key))
|
||
elif format in ['json','j']:
|
||
try:
|
||
result = json.loads(result)
|
||
except:
|
||
raise ValueError("Parameters: {}, requires JSON string".format(key))
|
||
elif format in ['xss','x']:
|
||
result = xssencode(result)
|
||
elif format in ['path','p']:
|
||
if not path_safe_check(result):
|
||
raise ValueError("Parameters: {}, the correct path format is required".format(key))
|
||
result = result.replace('//','/')
|
||
elif format in ['url','u']:
|
||
regex = re.compile(
|
||
r'^(?:http|ftp)s?://'
|
||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
|
||
r'localhost|'
|
||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
|
||
r'(?::\d+)?'
|
||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||
if not re.match(regex,result):
|
||
raise ValueError('Parameters: {}, the correct URL format is required'.format(key))
|
||
elif format in ['ip','ipaddr','i','ipv4','ipv6']:
|
||
if format is 'ipv4':
|
||
if not is_ipv4(result):
|
||
raise ValueError('Parameters: {}, the correct ipv4 address is required'.format(key))
|
||
elif format is 'ipv6':
|
||
if not is_ipv6(result):
|
||
raise ValueError('Parameters: {}, the correct ipv6 address is required'.format(key))
|
||
else:
|
||
if not is_ipv4(result) and not is_ipv6(result):
|
||
raise ValueError('Parameters: {}, the correct ipv4/ipv6 address is required'.format(key))
|
||
elif format in ['w','letter']:
|
||
if not re.match(r'^\w+$',result):
|
||
raise ValueError('Parameters: {}, the requirement can only be composed of English letters'.format(key))
|
||
elif format in ['email','mail','m']:
|
||
if not re.match(r"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",result):
|
||
raise ValueError("Parameters: {}, the correct email address format is required".format(key))
|
||
elif format in ['phone','mobile','m']:
|
||
if not re.match("^[0-9]{11,11}$",result):
|
||
raise ValueError("Parameters: {}, mobile phone number format required".format(key))
|
||
elif re.match(r"^[<>=]\d+$",result):
|
||
operator = format[0]
|
||
length = int(format[1:].strip())
|
||
result_len = len(result)
|
||
error_obj = ValueError("Parameters: {}, the required length is {}".format(key,format))
|
||
if operator is '=':
|
||
if result_len != length:
|
||
raise error_obj
|
||
elif operator is '>':
|
||
if result_len < length:
|
||
raise error_obj
|
||
else:
|
||
if result_len > length:
|
||
raise error_obj
|
||
elif format[0] in ['^','(','[','\\','.'] or format[-1] in ['$',')',']','+','}']:
|
||
if not re.match(format,result):
|
||
raise ValueError("The format of the specified parameter is incorrect, {}:{}".format(key,format))
|
||
|
||
if limit:
|
||
if not result in limit:
|
||
raise ValueError("The specified parameter value range is incorrect, {}:{}".format(key,limit))
|
||
return result
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#实例化定目录下的所有模块
|
||
class get_modules:
|
||
|
||
def __contains__(self, key):
|
||
return self.get_attr(key)
|
||
|
||
def __setitem__(self, key, value):
|
||
setattr(self,key,value)
|
||
|
||
def get_attr(self,key):
|
||
'''
|
||
尝试获取模块,若为字符串,则尝试实例化模块,否则直接返回模块对像
|
||
'''
|
||
res = getattr(self,key)
|
||
if isinstance(res,str):
|
||
try:
|
||
tmp_obj = __import__(key)
|
||
reload(tmp_obj)
|
||
setattr(self,key,tmp_obj)
|
||
return tmp_obj
|
||
except:
|
||
raise Exception(get_error_info())
|
||
return res
|
||
|
||
def __getitem__(self, key):
|
||
return self.get_attr(key)
|
||
|
||
def __delitem__(self,key):
|
||
delattr(self,key)
|
||
|
||
def __delattr__(self, key):
|
||
delattr(self,key)
|
||
|
||
def get_items(self):
|
||
return self
|
||
|
||
def __init__(self,path = "class",limit = None):
|
||
'''
|
||
@name 加载指定目录下的模块
|
||
@author hwliang<2020-08-03>
|
||
@param path<string> 指定目录,可指定绝对目录,也可指定相对于/www/server/panel的相对目录 默认加载class目录
|
||
@param limit<string/list/tuple> 指定限定加载的模块名称,默认加载path目录下的所有模块
|
||
@param object
|
||
|
||
@example
|
||
p = get_modules('class')
|
||
if 'public' in p:
|
||
md5_str = p.public.md5('test')
|
||
md5_str = p['public'].md5('test')
|
||
md5_str = getattr(p['public'],'md5')('test')
|
||
else:
|
||
print(p.__dict__)
|
||
'''
|
||
os.chdir('/www/server/panel')
|
||
exp_files = ['__init__.py','__pycache__']
|
||
if not path in sys.path:
|
||
sys.path.insert(0,path)
|
||
for fname in os.listdir(path):
|
||
if fname in exp_files: continue
|
||
filename = '/'.join([path,fname])
|
||
if os.path.isfile(filename):
|
||
if not fname[-3:] in ['.py','.so']: continue
|
||
mod_name = fname[:-3]
|
||
else:
|
||
c_file = '/'.join((filename,'__init__.py'))
|
||
if not os.path.exists(c_file):
|
||
continue
|
||
mod_name = fname
|
||
|
||
if limit:
|
||
if not isinstance(limit,list) and not isinstance(limit,tuple):
|
||
limit = (limit,)
|
||
if not mod_name in limit:
|
||
continue
|
||
|
||
setattr(self,mod_name,mod_name)
|
||
|
||
#检查App和小程序的绑定
|
||
def check_app(check='app'):
|
||
path='/www/server/panel/'
|
||
if check=='app':
|
||
try:
|
||
if not os.path.exists(path+'data/user.json') and os.path.exists(path+'config/api.json') and not os.path.exists(path+'plugin/app/user.json'):return False
|
||
if os.path.exists(path+'plugin/app/user.json'):
|
||
wxapp = json.loads(readFile(path+'plugin/app/user.json'))
|
||
if wxapp:return True
|
||
if os.path.exists(path+'data/user.json'):
|
||
app_info = json.loads(readFile(path+'data/user.json'))
|
||
if app_info:return True
|
||
if os.path.exists(path+'config/api.json'):
|
||
btapp_info = json.loads(readFile(path+'config/api.json'))
|
||
if not btapp_info['open']:return False
|
||
if not 'apps' in btapp_info:return False
|
||
if not btapp_info['apps']:return False
|
||
return True
|
||
return False
|
||
except:
|
||
return False
|
||
elif check=='app_bind':
|
||
if not os.path.exists(path + 'config/api.json'):return False
|
||
btapp_info = json.loads(readFile(path +'config/api.json'))
|
||
if not btapp_info: return False
|
||
if not btapp_info['open']: return False
|
||
return True
|
||
elif check=='wxapp':
|
||
if not os.path.exists(path+'plugin/app/user.json'):return False
|
||
app_info = json.loads(readFile(path+'plugin/app/user.json'))
|
||
if not app_info: return False
|
||
return True
|
||
|
||
#宝塔邮件报警
|
||
def send_mail(title,body,is_logs=False,is_type="aapanel login reminder"):
|
||
if is_logs:
|
||
try:
|
||
import send_mail
|
||
send_mail22 = send_mail.send_mail()
|
||
tongdao = send_mail22.get_settings()
|
||
if tongdao['user_mail']['mail_list']==0:return false
|
||
if not tongdao['user_mail']['info']: return false
|
||
if len(tongdao['user_mail']['mail_list'])==1:
|
||
send_mail=tongdao['user_mail']['mail_list'][0]
|
||
send_mail22.qq_smtp_send(send_mail, title=title, body=body)
|
||
else:
|
||
send_mail22.qq_smtp_send(tongdao['user_mail']['mail_list'], title=title, body=body)
|
||
if is_logs:
|
||
WriteLog2(is_type, body)
|
||
except:
|
||
return False
|
||
else:
|
||
try:
|
||
import send_mail
|
||
send_mail22 = send_mail.send_mail()
|
||
tongdao = send_mail22.get_settings()
|
||
if tongdao['user_mail']['mail_list'] == 0: return false
|
||
if not tongdao['user_mail']['info']: return false
|
||
if len(tongdao['user_mail']['mail_list']) == 1:
|
||
send_mail = tongdao['user_mail']['mail_list'][0]
|
||
return send_mail22.qq_smtp_send(send_mail, title=title, body=body)
|
||
else:
|
||
return send_mail22.qq_smtp_send(tongdao['user_mail']['mail_list'], title=title, body=body)
|
||
except:
|
||
return False
|
||
|
||
#宝塔钉钉 or 微信告警
|
||
def send_dingding(body,is_logs=False,is_type="aapanel login reminder"):
|
||
if is_logs:
|
||
try:
|
||
import send_mail
|
||
send_mail22 = send_mail.send_mail()
|
||
tongdao = send_mail22.get_settings()
|
||
if not tongdao['dingding']['info']: return false
|
||
tongdao = send_mail22.get_settings()
|
||
if is_logs:
|
||
WriteLog2(is_type,body)
|
||
return send_mail22.dingding_send(body)
|
||
except:
|
||
return False
|
||
else:
|
||
try:
|
||
import send_mail
|
||
send_mail22 = send_mail.send_mail()
|
||
tongdao = send_mail22.get_settings()
|
||
if not tongdao['dingding']['info']: return false
|
||
tongdao = send_mail22.get_settings()
|
||
return send_mail22.dingding_send(body)
|
||
except:return False
|
||
|
||
#获取服务器IP
|
||
def get_ip():
|
||
if os.path.exists('/www/server/panel/data/iplist.txt'):
|
||
data=ReadFile('/www/server/panel/data/iplist.txt')
|
||
return data.strip()
|
||
else:return '127.0.0.1'
|
||
|
||
#获取服务器内网Ip
|
||
def get_local_ip():
|
||
try:
|
||
ret=ExecShell("ip addr | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -E -v \"^127\.|^255\.|^0\.\" | head -n 1")
|
||
local_ip=ret[0].strip()
|
||
return local_ip
|
||
except:return '127.0.0.1'
|
||
|
||
def create_logs():
|
||
import db
|
||
sql = db.Sql()
|
||
if not sql.table('sqlite_master').where('type=? AND name=?', ('table', 'logs2')).count():
|
||
csql = '''CREATE TABLE `logs2` (
|
||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
`type` TEXT,
|
||
`log` TEXT,
|
||
`addtime` TEXT
|
||
, uid integer DEFAULT '1', username TEXT DEFAULT 'system')'''
|
||
sql.execute(csql, ())
|
||
|
||
def WriteLog2(type,logMsg,args=(),not_web = False):
|
||
import db
|
||
create_logs()
|
||
username = 'system'
|
||
uid = 1
|
||
tmp_msg = ''
|
||
sql = db.Sql()
|
||
mDate = time.strftime('%Y-%m-%d %X',time.localtime())
|
||
data = (uid,username,type,logMsg + tmp_msg,mDate)
|
||
result = sql.table('logs2').add('uid,username,type,log,addtime',data)
|
||
|
||
def check_ip_white(path,ip):
|
||
if os.path.exists(path):
|
||
try:
|
||
path_json=json.loads(ReadFile(path))
|
||
except:
|
||
WriteFile(path,'[]')
|
||
return False
|
||
if ip in path_json:return True
|
||
else:return False
|
||
else:
|
||
return False
|
||
|
||
#登陆告警
|
||
def login_send_body(is_type,username,login_ip,port):
|
||
if os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||
if check_ip_white('/www/server/panel/data/send_login_white.json',login_ip):return False
|
||
send_mail("aapanel login reminder","aapanel login reminder:Your server "+get_ip()+" successfully logged in via "+is_type+", account number: "+username+", login IP: "+login_ip+":"+port+", login time: "+time.strftime('%Y -%m-%d %X',time.localtime()), True)
|
||
if os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||
if check_ip_white('/www/server/panel/data/send_login_white.json',login_ip):return False
|
||
send_dingding("aapanel login reminder:Your server "+get_ip()+" successfully logged in via "+is_type+", account number: "+username+", login IP: "+login_ip+":"+port+", login time: "+time.strftime('%Y -%m-%d %X',time.localtime()), True)
|
||
|
||
#普通模式下调用发送消息【设置登陆告警后的设置】
|
||
#title= 发送的title
|
||
#body= 发送的body
|
||
#is_logs= 是否记录日志
|
||
#is_type=发送告警的类型
|
||
def send_to_body(title,body,is_logs=False,is_type="aaPanel email alert"):
|
||
if os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||
if is_logs:
|
||
send_mail(title, body,True,is_type)
|
||
send_mail(title,body)
|
||
if os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||
if is_logs:
|
||
send_dingding(body,True,is_type)
|
||
send_dingding(body)
|
||
|
||
#普通发送消息
|
||
#send_type= ["mail","dingding"]
|
||
#title =发送的头
|
||
#body= 发送消息的内容
|
||
def send_body_words(send_type,title,body):
|
||
if send_type=='mail':
|
||
return send_mail(title,body)
|
||
if send_type=='dingding':
|
||
return send_dingding(body)
|
||
|
||
def return_is_send_info():
|
||
import send_mail
|
||
send_mail22 = send_mail.send_mail()
|
||
tongdao = send_mail22.get_settings()
|
||
ret={}
|
||
ret['mail']=tongdao['user_mail']['user_name']
|
||
ret['dingding']=tongdao['dingding']['dingding']
|
||
return ret |