This commit is contained in:
jose
2020-08-15 18:02:15 +08:00
parent eb2e13f5a3
commit 32cc87c132
58 changed files with 6517 additions and 562 deletions
+149 -57
View File
@@ -13,10 +13,11 @@ import time
import re
import uuid
import threading
import socket
os.chdir('/www/server/panel/')
sys.path.insert(0,'class/')
import public
from flask import Flask,current_app,session,render_template,send_file,request,redirect,g,url_for,make_response,render_template_string,abort
from flask import Flask,current_app,session,render_template,send_file,request,redirect,g,url_for,make_response,render_template_string,abort,stream_with_context,Response as Resp
from flask_session import Session
try:
from werkzeug.contrib.cache import SimpleCache
@@ -79,7 +80,7 @@ except:
app.config['SESSION_PERMANENT'] = True
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_KEY_PREFIX'] = 'BT_:'
app.config['SESSION_COOKIE_NAME'] = "BT_PANEL_6"
app.config['SESSION_COOKIE_NAME'] = "SESSIONID"
app.config['PERMANENT_SESSION_LIFETIME'] = 86400
Session(app)
@@ -144,19 +145,60 @@ admin_path_checks = [
'/down',
'/api',
'/tips',
'/message'
'/message',
'/warning'
]
if admin_path in admin_path_checks: admin_path = '/bt'
@app.route('/service_status',methods = method_all)
@app.route('/service_status',methods = method_get)
def service_status():
return 'True'
@app.route('/phpmyadmin',methods = method_all)
@app.route('/phpmyadmin/',methods = method_all)
@app.route('/phpmyadmin/<path:puri>',methods = method_all)
def phpmyadmin(puri = None):
comReturn = comm.local()
if comReturn: return comReturn
import panelPHP
p = panelPHP.panelPHP()
document_root = '/www/server/phpmyadmin/pma/'
sock = p.start(puri,document_root,'/phpmyadmin/')
#如果是响应体则直接返回
if isinstance(sock,Resp):
return sock
headers_data = p.get_header_data(sock)
status,headers,bdata = p.format_header_data(headers_data)
return Response(p.resp_sock(sock,bdata),headers=headers,status=status)
@app.route('/adminer',methods = method_all)
@app.route('/adminer/',methods = method_all)
@app.route('/adminer/<path:puri>',methods = method_all)
def adminer(puri = None):
comReturn = comm.local()
if comReturn: return comReturn
import panelPHP
p = panelPHP.panelPHP()
document_root = '/www/server/adminer/'
sock = p.start(puri,document_root,'/adminer/')
#如果是响应体则直接返回
if isinstance(sock,Resp):return sock
headers_data = p.get_header_data(sock)
status,headers,bdata = p.format_header_data(headers_data)
return Response(p.resp_sock(sock,bdata),headers=headers,status=status)
@sockets.route('/webssh')
def webssh(ws):
if not check_login():
session.clear()
ws.send('server_response',"Panel session is lost, please re-login panel!")
ws.send('server_response',"SSH_ERROR")
return None
if not 'ssh_obj' in session:
import ssh_terminal
@@ -174,7 +216,7 @@ def webssh(ws):
session['ssh_obj'].run(ws,session['ssh_info'])
@app.route('/term_open',methods=method_get)
@app.route('/term_open',methods=method_all)
def term_open():
comReturn = comm.local()
if comReturn: return comReturn
@@ -183,7 +225,7 @@ def term_open():
key = 'ssh_' + args['host']
if key in session:
return public.getJson(session[key]),json_header
return public.returnMsg(False,'Acquisition failed!')
return public.returnMsg(False,'SSH_INFO_ERROR')
session['ssh_info'] = json.loads(args.data)
key = 'ssh_' + session['ssh_info']['host']
session[key] = session['ssh_info']
@@ -194,7 +236,7 @@ def term_open():
else:
if os.path.exists(s_file): os.remove(s_file)
if 'ssh_obj' in session: session['ssh_obj']._ssh_info = session['ssh_info']
return public.returnJson(True,'Successful setup!')
return public.returnJson(True,'SET_SUCCESS')
@app.route('/reload_mod',methods=method_all)
def reload_mod():
@@ -206,21 +248,28 @@ def reload_mod():
mod_name = args.mod_name
result = public.reload_mod(mod_name)
if result: return public.returnJson(True,result),json_header
return public.returnJson(False,'Reload failure!'),json_header
return public.returnJson(False,'INIT_RELOAD_ERR'),json_header
@app.before_request
def request_check():
#路由和URI长度过滤
if len(request.path) > 64: return abort(403)
if len(request.url) > 256: return abort(403)
if request.path.find('/adminer/') != -1:
return
if request.path.find('/phpmyadmin/') == -1:
if len(request.path) > 128: return abort(403)
if len(request.url) > 1024: return abort(403)
else:
if len(request.path) > 512: return abort(403)
return
if request.path in ['/service_status']: return
#POST参数过滤
if request.path in ['/login','/safe','/hook','/public','/down','/get_app_bind_status','/check_bind']:
pdata = request.form.to_dict()
for k in pdata.keys():
if len(k) > 32: return abort(403)
if len(pdata[k]) > 128: return abort(403)
if len(k) > 48: return abort(403)
if len(pdata[k]) > 256: return abort(403)
if not request.path in ['/safe','/hook','/public','/mail_sys','/down']:
ip_check = public.check_ip_panel()
@@ -235,7 +284,7 @@ def request_check():
if public.is_local():
not_networks = ['uninstall_plugin','install_plugin','UpdatePanel']
if request.args.get('action') in not_networks:
return public.returnJson(False,'This feature is not available in offline mode!'),json_header
return public.returnJson(False,'INIT_REQUEST_CHECK_LOCAL_ERR'),json_header
if app.config['BASIC_AUTH_OPEN']:
if request.path in ['/public','/download','/mail_sys','/hook','/down','/check_bind','/get_app_bind_status']: return
@@ -270,7 +319,6 @@ def home():
data['ftpCount'] = public.M('ftps').count()
data['databaseCount'] = public.M('databases').count()
data['lan'] = public.GetLan('index')
data['724'] = public.format_date("%m%d") == '0724'
public.auto_backup_panel()
return render_template( 'index.html',data = data)
@@ -305,7 +353,8 @@ def login():
for v in v_list:
pv = request.form.get(v,'').strip()
if v == 'cdn_url':
if len(pv) > 32: return public.returnMsg(False,'Wrong parameter length!')
if len(pv) > 32: return public.returnMsg(False,'INPUT_ARGS_ERR'),json_header
if not re.match(r"^[\w\.-]+$",pv): public.returnJson(False,'INPUT_ARGS_ERR'),json_header
continue
if not pv: continue
@@ -313,13 +362,14 @@ def login():
if v == 'code': p_len = 4
if v == 'vcode': p_len = 6
if len(pv) != p_len:
return public.returnJson(False,'Wrong parameter length'),json_header
if v == 'code': return public.returnJson(False,'V_CODE_LEN_ERR'),json_header
return public.returnJson(False,'INPUT_ARGS_ERR'),json_header
if not re.match(r"^\w+$",pv):
return public.returnJson(False,'Wrong parameter format'),json_header
return public.returnJson(False,'INPUT_ARGS_ERR'),json_header
for n in request.form.keys():
if not n in v_list:
return public.returnJson(False,'You cannot have extra parameters in the login parameters'),json_header
return public.returnJson(False,'EXTRA_PARAMETER'),json_header
get = get_input()
import userlogin
@@ -334,10 +384,16 @@ def login():
if session['login'] != False:
session['login'] = False
cache.set('dologin',True)
public.WriteLog('User LogOut','Client: {}, has manually exited the panel'.format(public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
public.WriteLog('TYPE_LOGOUT','MANUALLY_LOGOUT', (format(public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))),))
session.clear()
session_path = r'/dev/shm/session_py' + str(sys.version_info[0])
if os.path.exists(session_path): public.ExecShell("rm -f " + session_path + '/*')
sess_file = 'data/sess_files/' + public.get_sess_key()
if os.path.exists(sess_file):
try:
os.remove(sess_file)
except:
pass
return redirect(login_path)
if is_auth_path:
@@ -351,8 +407,6 @@ def login():
if request.method == method_post[0]:
result = userlogin.userlogin().request_post(get)
if result == "1":
return result
return is_login(result)
if request.method == method_get[0]:
@@ -474,14 +528,14 @@ def get_phpmyadmin_dir():
if session['webserver'] == 'nginx':
filename =public.GetConfigValue('setup_path') + '/nginx/conf/nginx.conf'
conf = public.readFile(filename)
rep = "listen\s+([0-9]+)\s*;"
rep = r"listen\s+([0-9]+)\s*;"
rtmp = re.search(rep,conf)
if rtmp:
phpport = rtmp.groups()[0]
if session['webserver'] == 'apache':
filename = public.GetConfigValue('setup_path') + '/apache/conf/extra/httpd-vhosts.conf'
conf = public.readFile(filename)
rep = "Listen\s+([0-9]+)\s*\n"
rep = r"Listen\s+([0-9]+)\s*\n"
rtmp = re.search(rep,conf)
if rtmp:
phpport = rtmp.groups()[0]
@@ -489,7 +543,7 @@ def get_phpmyadmin_dir():
filename = public.GetConfigValue('setup_path') + '/panel/vhost/openlitespeed/listen/888.conf'
public.writeFile('/tmp/2',filename)
conf = public.readFile(filename)
rep = "address\s*\*\:\s*(\d+)"
rep = r"address\s*\*\:\s*(\d+)"
rtmp = re.search(rep,conf)
if rtmp:
phpport = rtmp.groups()[0]
@@ -630,6 +684,16 @@ def panel_password(pdata=None):
return publicObject(dataObject, defs, None, pdata)
@app.route('/warning', methods=method_all)
def panel_warning(pdata=None):
comReturn = comm.local()
if comReturn: return comReturn
import panelWarning
dataObject = panelWarning.panelWarning()
defs = ('get_list', 'set_ignore', 'check_find')
return publicObject(dataObject, defs, None, pdata)
@app.route('/bak', methods=method_all)
def backup_bak(pdata=None):
comReturn = comm.local()
@@ -664,7 +728,7 @@ def files(pdata = None):
return render_template('files.html',data=data)
import files
filesObject = files.files()
defs = ('fix_permissions','get_all_back','restore_path_permissions','del_path_premissions','get_path_premissions','back_path_permissions',
defs = ('get_progress','restore_website','fix_permissions','get_all_back','restore_path_permissions','del_path_premissions','get_path_premissions','back_path_permissions',
'CheckExistsFiles','GetExecLog','GetSearch','ExecShell','GetExecShellMsg','exec_git','exec_composer','create_download_url',
'UploadFile','GetDir','CreateFile','CreateDir','DeleteDir','DeleteFile','get_download_url_list','remove_download_url','modify_download_url',
'CopyFile','CopyDir','MvFile','GetFileBody','SaveFileBody','Zip','UnZip','get_download_url_find',
@@ -727,8 +791,8 @@ def config(pdata = None):
if c_obj.get_ipv6_listen(None): data['ipv6'] = 'checked'
if c_obj.get_token(None)['open']: data['api'] = 'checked'
data['basic_auth'] = c_obj.get_basic_auth_stat(None)
data['basic_auth']['value'] = public.GetMsg("CLOSE")
if data['basic_auth']['open']: data['basic_auth']['value'] = public.GetMsg("OPEN")
data['basic_auth']['value'] = public.getMsg('CLOSED')
if data['basic_auth']['open']: data['basic_auth']['value'] = public.getMsg('OPENED')
data['debug'] = ''
if app.config['DEBUG']: data['debug'] = 'checked'
data['is_local'] = ''
@@ -898,7 +962,7 @@ def panel_public():
if type(checks) != bool or not checks: return public.getJson(checks),json_header
data = public.getJson(eval('pluwx.'+get.fun+'(get)'))
return data,json_header
if get.name != 'app': return abort(404)
import panelPlugin
plu = panelPlugin.panelPlugin()
@@ -941,9 +1005,9 @@ def panel_other(name=None,fun = None,stype=None):
if not name: name = 'coll'
if not public.path_safe_check("%s/%s/%s" % (name,fun,stype)): return abort(404)
if name.find('./') != -1 or not re.match("^[\w-]+$",name): return abort(404)
if not name: return public.returnJson(False,public.GetMsg("PLUGIN_INPUT_A")),json_header
p_path = '/www/server/panel/plugin/' + name
if name.find('./') != -1 or not re.match(r"^[\w-]+$",name): return abort(404)
if not name: return public.returnJson(False,'PLUGIN_INPUT_ERR'),json_header
p_path = os.path.join('/www/server/panel/plugin/', name)
if not os.path.exists(p_path): return abort(404)
#是否响插件应静态文件
@@ -951,7 +1015,7 @@ def panel_other(name=None,fun = None,stype=None):
if stype.find('./') != -1 or not os.path.exists(p_path + '/static'): return abort(404)
s_file = p_path + '/static/' + stype
if s_file.find('..') != -1: return abort(404)
if not re.match("^[\w\./-]+$",s_file): return abort(404)
if not re.match(r"^[\w\./-]+$",s_file): return abort(404)
if not public.path_safe_check(s_file): return abort(404)
if not os.path.exists(s_file): return abort(404)
return send_file(s_file,conditional=True,add_etags=True)
@@ -1067,12 +1131,16 @@ def install():
elif request.method == method_post[0]:
if not os.path.exists('install.pl'): return redirect(ret_login)
get = get_input()
if not hasattr(get,'bt_username'): return public.GetMsg("LOGIN_USER_EMPTY")
if not get.bt_username: return public.GetMsg("LOGIN_USER_EMPTY")
if not hasattr(get,'bt_password1'): return public.GetMsg("LOGIN_USER_EMPTY")
if not get.bt_password1: return public.GetMsg("LOGIN_USER_EMPTY")
if get.bt_password1 != get.bt_password2: return public.GetMsg("USER_PASSWORD_CHECK")
public.M('users').where("id=?",(1,)).save('username,password',(get.bt_username,public.md5(get.bt_password1.strip())))
if not hasattr(get,'bt_username'): return public.getMsg('INSTALL_USER_EMPTY')
if not get.bt_username: return public.getMsg('INSTALL_USER_EMPTY')
if not hasattr(get,'bt_password1'): return public.getMsg('INSTALL_PASS_EMPTY')
if not get.bt_password1: return public.getMsg('INSTALL_PASS_EMPTY')
if get.bt_password1 != get.bt_password2: return public.getMsg('INSTALL_PASS_CHECK')
public.M('users').where("id=?",(1,)).save('username,password',
(get.bt_username,
public.password_salt(public.md5(get.bt_password1.strip()),uid=1)
)
)
os.remove('install.pl')
public.M('config').where("id=?",('1',)).setField('status',1)
data = {}
@@ -1161,7 +1229,10 @@ def get_dir_down(filename,token,find):
pdata['token'] = token
pdata['src_path'] = find['filename']
pdata['to_path'] = to_path
pdata['expire'] = public.format_date(times=find['expire'])
if find['expire'] > (time.time() + (86400 * 365 * 10)):
pdata['expire'] = public.format_date(times=find['expire'])
else:
pdata['expire'] = public.getMsg('NEVER_EXPIRES')
pdata['filename'] = (find['filename'].split('/')[-1] + '/' + to_path).strip('/')
return render_template('down.html',data = pdata,to_size=public.to_size)
@@ -1188,11 +1259,11 @@ def down(token=None,fname=None):
args = get_input()
if 'file_password' in args:
if not re.match(r"^\w+$",args.file_password):
return public.ReturnJson(False,'Wrong password!'),json_header
return public.ReturnJson(False,'WRONG_PASSWD'),json_header
if re.match(r"^\d+$",args.file_password):
args.file_password += '.0'
if args.file_password != str(find['password']):
return public.ReturnJson(False,'Wrong password!'),json_header
return public.ReturnJson(False,'WRONG_PASSWD'),json_header
session[token] = 1
session['down'] = True
else:
@@ -1280,7 +1351,7 @@ def check_csrf():
def publicObject(toObject,defs,action=None,get = None):
if 'request_token' in session and 'login' in session:
if not check_csrf(): return public.ReturnJson(False,'Csrf-Token error.'),json_header
if not check_csrf(): return public.ReturnJson(False,'INIT_CSRF_ERR'),json_header
if not get: get = get_input()
if action: get.action = action
@@ -1296,8 +1367,7 @@ def publicObject(toObject,defs,action=None,get = None):
get.dfile = get.dfile.replace('//','/').replace('\\','/')
if hasattr(toObject,'site_path_check'):
if not toObject.site_path_check(get): return public.ReturnJson(False,'Excessive operation!'),json_header
if not toObject.site_path_check(get): return public.ReturnJson(False,'INIT_ACCEPT_NOT'),json_header
p = run_exec()
result = p.run(toObject,defs,get)
del p
@@ -1317,7 +1387,9 @@ def get_pd():
tmp = -1
try:
import panelPlugin
tmp1 = panelPlugin.panelPlugin().get_cloud_list()
get = public.dict_obj()
get.init = 1
tmp1 = panelPlugin.panelPlugin().get_cloud_list(get)
except:
tmp1 = None
if tmp1:
@@ -1409,17 +1481,19 @@ def get_pd():
@app.errorhandler(404)
def notfound(e):
errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error.html')
try:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_404_TITLE'),
public.getMsg('PAGE_ERR_404_H1'),
public.getMsg('PAGE_ERR_404_P1'),
public.getMsg('NAME'),
public.getMsg('PAGE_ERR_HELP'))
except IndexError: pass
return errorStr,404
errorStr = '''<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>server</center>
</body>
</html>'''
headers={
"Content-Type":"text/html"
}
return Response(errorStr,status=404,headers=headers)
@app.errorhandler(500)
#@app.errorhandler(500)
def internalerror(e):
#if str(e).find('Permanent Redirect') != -1: return e
errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error.html')
@@ -1431,7 +1505,10 @@ def internalerror(e):
public.getMsg('NAME'),
public.getMsg('PAGE_ERR_HELP'))
else:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_500_TITLE'),str(e),'<pre>'+public.get_error_info() + '</pre>','The above debugging information is only displayed in developer mode','Version: ' + public.version())
errorStr = errorStr.format(public.getMsg('PAGE_ERR_500_TITLE'),
str(e),
'<pre>'+public.get_error_info() + '</pre>',
public.getMsg('INIT_DEBUG_INFO'),public.getMsg('INIT_VERSION_LAST') + public.version())
except IndexError:pass
return errorStr,500
@@ -1443,9 +1520,17 @@ def get_input():
for key in request.args.keys():
data[key] = str(request.args.get(key,''))
try:
x_token = request.headers.get('x-http-token')
if x_token:
aes_pwd = x_token[:8] + x_token[40:48]
for key in request.form.keys():
if key in exludes: continue
data[key] = str(request.form.get(key,''))
if x_token:
if len(data[key]) > 5:
if data[key][:6] == 'BT-CRT':
data[key] = public.aes_decrypt(data[key][6:],aes_pwd)
except:
try:
post = request.form.to_dict()
@@ -1484,4 +1569,11 @@ class run_exec:
result = public.ReturnJson(False,'ARGS_ERR'),json_header
if g.is_aes:
result = public.aes_encrypt(result[0],g.aes_key),json_header
else:
if os.path.exists('pyenv/bin/python'):
if not os.path.exists('data/debug.pl'):
x_token = request.headers.get('x-http-token')
if x_token:
aes_pwd = x_token[:8] + x_token[40:48]
result = "BT-CRT"+public.aes_encrypt(result[0],aes_pwd),{'Content-Type':'text/plain; charset=utf-8'}
return result
+1 -1
View File
@@ -1 +1 @@
{"supportedModes":{"Apache_Conf":["^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"],"BatchFile":["bat|cmd"],"C_Cpp":["cpp|c|cc|cxx|h|hh|hpp|ino"],"CSharp":["cs"],"CSS":["css"],"Dockerfile":["^Dockerfile"],"golang":["go"],"HTML":["html|htm|xhtml|vue|we|wpy"],"Java":["java"],"JavaScript":["js|jsm|jsx"],"JSON":["json"],"JSP":["jsp"],"LESS":["less"],"Lua":["lua"],"Makefile":["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"],"Markdown":["md|markdown"],"MySQL":["mysql"],"Nginx":["nginx|conf"],"INI":["ini|conf|cfg|prefs"],"ObjectiveC":["m|mm"],"Perl":["pl|pm"],"Perl6":["p6|pl6|pm6"],"pgSQL":["pgsql"],"PHP_Laravel_blade":["blade.php"],"PHP":["php|inc|phtml|shtml|php3|php4|php5|phps|phpt|aw|ctp|module"],"Powershell":["ps1"],"Python":["py"],"R":["r"],"Ruby":["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"],"Rust":["rs"],"SASS":["sass"],"SCSS":["scss"],"SH":["sh|bash|^.bashrc"],"SQL":["sql"],"SQLServer":["sqlserver"],"Swift":["swift"],"Text":["txt"],"Typescript":["ts|typescript|str"],"VBScript":["vbs|vb"],"Verilog":["v|vh|sv|svh"],"XML":["xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl|xaml"],"YAML":["yaml|yml"],"Compress":["tar|zip|7z|rar|gz|arj|z"],"images":["icon|jpg|jpeg|png|bmp|gif|tif|emf"]},"nameOverrides":{"ObjectiveC":"Objective-C","CSharp":"C#","golang":"Go","C_Cpp":"C and C++","PHP_Laravel_blade":"PHP (Blade Template)","Perl6":"Perl 6"},"encodingList":["ASCII","UTF-8","GBK","GB2312","BIG5"],"themeList":["chrome","monokai"],"aceEditor":{"editorTheme":"monokai","fontSize":13,"softLabel":false,"useSoftTabs":false,"tabSize":4,"wrap":true,"enableSnippets":true,"enableLiveAutocompletion":true,"highlightActiveLine":true,"highlightSelectedWord":true,"animatedScroll":false,"showInvisibles":false,"showFoldWidgets":true,"showLineNumbers":true,"showGutter":true,"displayIndentGuides":false},"showUpdate":true}
{"supportedModes":{"Apache_Conf":["^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"],"BatchFile":["bat|cmd"],"C_Cpp":["cpp|c|cc|cxx|h|hh|hpp|ino"],"CSharp":["cs"],"CSS":["css"],"Dockerfile":["^Dockerfile"],"golang":["go"],"HTML":["html|htm|xhtml|vue|we|wpy"],"Java":["java"],"JavaScript":["js|jsm|jsx"],"JSON":["json"],"JSP":["jsp"],"LESS":["less"],"Lua":["lua"],"Makefile":["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"],"Markdown":["md|markdown"],"MySQL":["mysql"],"Nginx":["nginx|conf"],"INI":["ini|conf|cfg|prefs"],"ObjectiveC":["m|mm"],"Perl":["pl|pm"],"Perl6":["p6|pl6|pm6"],"pgSQL":["pgsql"],"PHP_Laravel_blade":["blade.php"],"PHP":["php|inc|phtml|shtml|php3|php4|php5|phps|phpt|aw|ctp|module"],"Powershell":["ps1"],"Python":["py"],"R":["r"],"Ruby":["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"],"Rust":["rs"],"SASS":["sass"],"SCSS":["scss"],"SH":["sh|bash|^.bashrc"],"SQL":["sql"],"SQLServer":["sqlserver"],"Swift":["swift"],"Text":["txt"],"Typescript":["ts|typescript|str"],"VBScript":["vbs|vb"],"Verilog":["v|vh|sv|svh"],"XML":["xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl|xaml"],"YAML":["yaml|yml"],"Compress":["tar|zip|7z|rar|gz|arj|z"],"images":["icon|jpg|jpeg|png|bmp|gif|tif|emf"]},"nameOverrides":{"ObjectiveC":"Objective-C","CSharp":"C#","golang":"Go","C_Cpp":"C and C++","PHP_Laravel_blade":"PHP (Blade Template)","Perl6":"Perl 6"},"encodingList":["ASCII","UTF-8","GBK","GB2312","BIG5"],"themeList":["chrome","monokai"],"aceEditor":{"editorTheme":"monokai","fontSize":13,"softLabel":false,"useSoftTabs":true,"tabSize":"4","wrap":true,"enableSnippets":true,"enableLiveAutocompletion":true,"highlightActiveLine":true,"highlightSelectedWord":true,"animatedScroll":false,"showInvisibles":false,"showFoldWidgets":true,"showLineNumbers":true,"showGutter":true,"displayIndentGuides":false},"showUpdate":true}
+268 -16
View File
@@ -2130,22 +2130,18 @@ html .menu .menu_exit:hover {
.soft-man-con .user_pw_tit .tit {
float: left;
line-height: 22px
line-height: 22px;
padding-right: 20px;
}
.soft-man-con .user_pw span,
.soft-man-con .user_pw_tit span {
width: 165px;
width: 135px;
display: inline-block;
text-align: left;
text-align: right;
}
.soft-man-con .user_pw span {
margin-right: 10px
}
.btswitch-p {
margin-left: 10px
.soft-man-con .user_pw .btn_password_get{
margin-left: 135px;
}
.soft-man-con .user_pw input {
@@ -4925,7 +4921,7 @@ select[disabled]{
::-webkit-scrollbar {
/*滚动条整体样式*/
width : 10px; /*高宽分别对应横竖滚动条的尺寸*/
height: 5px;
height: 10px;
}
::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
@@ -5382,7 +5378,7 @@ select[disabled]{
.ace_catalogue_list::-webkit-scrollbar {
/*滚动条整体样式*/
width: 9px; /*高宽分别对应横竖滚动条的尺寸*/
height: 1px;
height: 10px;
}
.ace_catalogue_list::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
@@ -5465,7 +5461,7 @@ select[disabled]{
}
.ace_scrollbar::-webkit-scrollbar {
/*滚动条整体样式*/
width : 15px; /*高宽分别对应横竖滚动条的尺寸*/
width : 10px; /*高宽分别对应横竖滚动条的尺寸*/
height: 10px;
}
.ace_scrollbar::-webkit-scrollbar-thumb {
@@ -5478,7 +5474,7 @@ select[disabled]{
/*滚动条里面轨道*/
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #333;
border-radius: 0;
border-radius: 10px;
}
.ace_editors.active {
display: block;
@@ -6651,6 +6647,24 @@ select[disabled]{
background-size: auto !important;
background-repeat: no-repeat;
}
.ace_config_editor_scroll ::-webkit-scrollbar {
/*滚动条整体样式*/
width : 10px; /*高宽分别对应横竖滚动条的尺寸*/
height: 10px;
}
.ace_config_editor_scroll ::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 10px;
box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2);
background : #999;
}
.ace_config_editor_scroll ::-webkit-scrollbar-track {
/*滚动条里面轨道*/
box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 10px;
background : #ededed;
border-radius: 5px;
}
/*外链分享 */
.checkbox_grourd input {
margin-right: 2px;
@@ -6769,7 +6783,7 @@ select[disabled]{
.dropUpLoadFile::-webkit-scrollbar {
/*滚动条整体样式*/
width : 15px; /*高宽分别对应横竖滚动条的尺寸*/
height: 1px;
height: 10px;
}
.dropUpLoadFile::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
@@ -6981,4 +6995,242 @@ select[disabled]{
}
.upload_warning{
color:#409EFF !important;
}
}
/*安全风险*/
.warning_scan_view{
padding: 20px;
position: relative;
color: #666;
}
.warning_scan_head {
padding: 15px 10px 20px 10px;
}
.warning_scan_head span{
display: inline-block;
width: 450px;
font-size: 18px;
margin-bottom: 5px;
}
.warning_scan_head button{
position: absolute;
right: 40px;
top: 35px;
border: none;
background: #20a53a;
border-radius: 20px;
height: 38px;
line-height: 38px;
font-size: 15px;
width: 110px;
color: #fff;
transition: all 500ms;
}
.warning_scan_head button:hover{
background:#10952a;
}
.warning_scan_head button:focus,
.warning_scan_head button:active{
outline: none;
}
.warning_scan_ps i{
font-style: inherit;
color: red;
margin: 0 5px;
}
.warning_scan_time{
font-size: 15px !important;
}
.warning_scan_body .module_head{
height: 42px;
line-height: 42px;
padding-left: 15px;
color: #555;
background: #f5f5f5;
font-size: 13px;
border-top: 1px solid #e8e8e8;
}
.module_details_list{
display: none;
}
.module_details_list.active{
display: block;
height: 400px;
overflow: auto;
transition: height 500ms;
}
.module_details_list::-webkit-scrollbar {
/*滚动条整体样式*/
width: 10px;
height: 5px;
}
.module_details_list::-webkit-scrollbar-thumb {
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #999;
}
.module_details_list ::-webkit-scrollbar-track {
/*滚动条里面轨道*/
box-shadow:inset 0 0 5px rgba(0, 0, 0, 0.2);
background:#ededed;
}
.module_details_item{
position:relative;
border-bottom: 1px solid #ececec;
}
.module_details_item:hover .module_details_head{
background: #effffa;
}
.module_details_item:hover .module_details_body{
background: #f8fffd;
}
.module_details_item:before{
/*content:'';*/
display: block;
width: 100%;
height: 6px;
position: absolute;
bottom: 0;
background: -webkit-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3));
background: -moz-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3));
background: -o-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3));
background: linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3));
clear: both;
}
.module_details_item:after{
content:'';
display: block;
width: 100%;
height: 6px;
top: 40px;
position: absolute;
background: -webkit-linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0));
background: -moz-linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0));
background: -o-linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0));
background: linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0));
clear: both;
}
.warning_scan_body .module_head:last-child{
border-top: none;
}
.module_item .module_head{
cursor: pointer;
}
.module_title{
font-size: 14px;
}
.risk .module_num,
.security .module_num,
.ignore .module_num{
display: inline-block;
height: 20px;
line-height: 21px;
width: 20px;
text-align: center;
margin-left: 5px;
border-radius: 3px;
color: #fff;
}
.risk .module_num{
background:red;
}
.security .module_num{
background:#20A53A;
}
.ignore .module_num{
background:#FFBA66;
}
.module_cut_show{
float: right;
padding-right: 10px;
height: 40px;
line-height: 40px;
}
.module_cut_show i{
color: #bbb;
font-style: initial;
margin-right: 5px;
}
.module_cut_show .glyphicon{
color: #20a53a;
}
.module_details_item{
}
.module_details_head{
height: 40px;
line-height: 40px;
font-size: 13px;
padding-left: 15px;
position: relative;
background: #fbfbfb;
clear: both;
/*border-top: 1px solid #ececec;*/
}
.module_details_head:before{
content: '';
display: inline-block;
height: 5px;
width: 5px;
border-radius: 2.5px;
position: absolute;
/* background-color: #666; */
top: 50%;
margin-top: -3.5px;
left: 15px;
}
.operate_tools{
float: right;
padding-right: 15px;
font-size: 12.5px;
}
.operate_tools a{
color: #9a9a9a;
}
.operate_tools a.btlink{
color: #20a53a;
}
.module_details_title{
font-weight: 500;
}
.module_details_title i{
color: #bbb;
font-style: initial;
font-weight: 400;
}
.module_details_body{
padding: 10px 15px;
display: none;
}
.module_details_body .module_details_line{
line-height: 25px;
margin-bottom: 5px;
clear: both;
}
.module_details_body .line_content,
.module_details_body .line_title{
display: inline-block;
font-size: 12.5px;
}
.module_details_body .line_title{
height: 100%;
width: 75px;
vertical-align: top;
color: #999;
text-align: right;
padding-right: 5px;
}
.module_details_body .line_content i{
font-style:initial;
}
.module_details_body .line_content{
width: 580px;
word-wrap:break-word;
}
.module_details_block{
display: inline-block;
}
.module_details_block .line_content{
width:250px;
}
/*END*/
+13
View File
@@ -0,0 +1,13 @@
var CryptoJS=CryptoJS||(function(Math,undefined){var crypto;if(typeof window!=="undefined"&&window.crypto){crypto=window.crypto}if(!crypto&&typeof window!=="undefined"&&window.msCrypto){crypto=window.msCrypto}if(!crypto&&typeof global!=="undefined"&&global.crypto){crypto=global.crypto}if(!crypto&&typeof require==="function"){try{crypto=require("crypto")}catch(err){}}var cryptoSecureRandomInt=function(){if(crypto){if(typeof crypto.getRandomValues==="function"){try{return crypto.getRandomValues(new Uint32Array(1))[0]}catch(err){}}if(typeof crypto.randomBytes==="function"){try{return crypto.randomBytes(4).readInt32LE()}catch(err){}}}throw new Error("Native crypto module could not be used to get secure random number.")};var create=Object.create||(function(){function F(){}return function(obj){var subtype;F.prototype=obj;subtype=new F();F.prototype=null;return subtype}}());var C={};var C_lib=C.lib={};var Base=C_lib.Base=(function(){return{extend:function(overrides){var subtype=create(this);if(overrides){subtype.mixIn(overrides)}if(!subtype.hasOwnProperty("init")||this.init===subtype.init){subtype.init=function(){subtype.$super.init.apply(this,arguments)}}subtype.init.prototype=subtype;subtype.$super=this;return subtype},create:function(){var instance=this.extend();instance.init.apply(instance,arguments);return instance},init:function(){},mixIn:function(properties){for(var propertyName in properties){if(properties.hasOwnProperty(propertyName)){this[propertyName]=properties[propertyName]}}if(properties.hasOwnProperty("toString")){this.toString=properties.toString}},clone:function(){return this.init.prototype.extend(this)}}}());var WordArray=C_lib.WordArray=Base.extend({init:function(words,sigBytes){words=this.words=words||[];if(sigBytes!=undefined){this.sigBytes=sigBytes}else{this.sigBytes=words.length*4}},toString:function(encoder){return(encoder||Hex).stringify(this)},concat:function(wordArray){var thisWords=this.words;var thatWords=wordArray.words;var thisSigBytes=this.sigBytes;
var thatSigBytes=wordArray.sigBytes;this.clamp();if(thisSigBytes%4){for(var i=0;i<thatSigBytes;i++){var thatByte=(thatWords[i>>>2]>>>(24-(i%4)*8))&255;thisWords[(thisSigBytes+i)>>>2]|=thatByte<<(24-((thisSigBytes+i)%4)*8)}}else{for(var i=0;i<thatSigBytes;i+=4){thisWords[(thisSigBytes+i)>>>2]=thatWords[i>>>2]}}this.sigBytes+=thatSigBytes;return this},clamp:function(){var words=this.words;var sigBytes=this.sigBytes;words[sigBytes>>>2]&=4294967295<<(32-(sigBytes%4)*8);words.length=Math.ceil(sigBytes/4)},clone:function(){var clone=Base.clone.call(this);clone.words=this.words.slice(0);return clone},random:function(nBytes){var words=[];for(var i=0;i<nBytes;i+=4){words.push(cryptoSecureRandomInt())}return new WordArray.init(words,nBytes)}});var C_enc=C.enc={};var Hex=C_enc.Hex={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var hexChars=[];for(var i=0;i<sigBytes;i++){var bite=(words[i>>>2]>>>(24-(i%4)*8))&255;hexChars.push((bite>>>4).toString(16));hexChars.push((bite&15).toString(16))}return hexChars.join("")},parse:function(hexStr){var hexStrLength=hexStr.length;var words=[];for(var i=0;i<hexStrLength;i+=2){words[i>>>3]|=parseInt(hexStr.substr(i,2),16)<<(24-(i%8)*4)}return new WordArray.init(words,hexStrLength/2)}};var Latin1=C_enc.Latin1={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var latin1Chars=[];for(var i=0;i<sigBytes;i++){var bite=(words[i>>>2]>>>(24-(i%4)*8))&255;latin1Chars.push(String.fromCharCode(bite))}return latin1Chars.join("")},parse:function(latin1Str){var latin1StrLength=latin1Str.length;var words=[];for(var i=0;i<latin1StrLength;i++){words[i>>>2]|=(latin1Str.charCodeAt(i)&255)<<(24-(i%4)*8)}return new WordArray.init(words,latin1StrLength)}};var Utf8=C_enc.Utf8={stringify:function(wordArray){try{return decodeURIComponent(escape(Latin1.stringify(wordArray)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(utf8Str){return Latin1.parse(unescape(encodeURIComponent(utf8Str)))
}};var BufferedBlockAlgorithm=C_lib.BufferedBlockAlgorithm=Base.extend({reset:function(){this._data=new WordArray.init();this._nDataBytes=0},_append:function(data){if(typeof data=="string"){data=Utf8.parse(data)}this._data.concat(data);this._nDataBytes+=data.sigBytes},_process:function(doFlush){var processedWords;var data=this._data;var dataWords=data.words;var dataSigBytes=data.sigBytes;var blockSize=this.blockSize;var blockSizeBytes=blockSize*4;var nBlocksReady=dataSigBytes/blockSizeBytes;if(doFlush){nBlocksReady=Math.ceil(nBlocksReady)}else{nBlocksReady=Math.max((nBlocksReady|0)-this._minBufferSize,0)}var nWordsReady=nBlocksReady*blockSize;var nBytesReady=Math.min(nWordsReady*4,dataSigBytes);if(nWordsReady){for(var offset=0;offset<nWordsReady;offset+=blockSize){this._doProcessBlock(dataWords,offset)}processedWords=dataWords.splice(0,nWordsReady);data.sigBytes-=nBytesReady}return new WordArray.init(processedWords,nBytesReady)},clone:function(){var clone=Base.clone.call(this);clone._data=this._data.clone();return clone},_minBufferSize:0});var Hasher=C_lib.Hasher=BufferedBlockAlgorithm.extend({cfg:Base.extend(),init:function(cfg){this.cfg=this.cfg.extend(cfg);this.reset()},reset:function(){BufferedBlockAlgorithm.reset.call(this);this._doReset()},update:function(messageUpdate){this._append(messageUpdate);this._process();return this},finalize:function(messageUpdate){if(messageUpdate){this._append(messageUpdate)}var hash=this._doFinalize();return hash},blockSize:512/32,_createHelper:function(hasher){return function(message,cfg){return new hasher.init(cfg).finalize(message)}},_createHmacHelper:function(hasher){return function(message,key){return new C_algo.HMAC.init(hasher,key).finalize(message)}}});var C_algo=C.algo={};return C}(Math));(function(){var C=CryptoJS;var C_lib=C.lib;var WordArray=C_lib.WordArray;var C_enc=C.enc;var Base64=C_enc.Base64={stringify:function(wordArray){var words=wordArray.words;var sigBytes=wordArray.sigBytes;var map=this._map;wordArray.clamp();
var base64Chars=[];for(var i=0;i<sigBytes;i+=3){var byte1=(words[i>>>2]>>>(24-(i%4)*8))&255;var byte2=(words[(i+1)>>>2]>>>(24-((i+1)%4)*8))&255;var byte3=(words[(i+2)>>>2]>>>(24-((i+2)%4)*8))&255;var triplet=(byte1<<16)|(byte2<<8)|byte3;for(var j=0;(j<4)&&(i+j*0.75<sigBytes);j++){base64Chars.push(map.charAt((triplet>>>(6*(3-j)))&63))}}var paddingChar=map.charAt(64);if(paddingChar){while(base64Chars.length%4){base64Chars.push(paddingChar)}}return base64Chars.join("")},parse:function(base64Str){var base64StrLength=base64Str.length;var map=this._map;var reverseMap=this._reverseMap;if(!reverseMap){reverseMap=this._reverseMap=[];for(var j=0;j<map.length;j++){reverseMap[map.charCodeAt(j)]=j}}var paddingChar=map.charAt(64);if(paddingChar){var paddingIndex=base64Str.indexOf(paddingChar);if(paddingIndex!==-1){base64StrLength=paddingIndex}}return parseLoop(base64Str,base64StrLength,reverseMap)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="};function parseLoop(base64Str,base64StrLength,reverseMap){var words=[];var nBytes=0;for(var i=0;i<base64StrLength;i++){if(i%4){var bits1=reverseMap[base64Str.charCodeAt(i-1)]<<((i%4)*2);var bits2=reverseMap[base64Str.charCodeAt(i)]>>>(6-(i%4)*2);var bitsCombined=bits1|bits2;words[nBytes>>>2]|=bitsCombined<<(24-(nBytes%4)*8);nBytes++}}return WordArray.create(words,nBytes)}}());CryptoJS.lib.Cipher||(function(undefined){var C=CryptoJS;var C_lib=C.lib;var Base=C_lib.Base;var WordArray=C_lib.WordArray;var BufferedBlockAlgorithm=C_lib.BufferedBlockAlgorithm;var C_enc=C.enc;var Utf8=C_enc.Utf8;var Base64=C_enc.Base64;var C_algo=C.algo;var EvpKDF=C_algo.EvpKDF;var Cipher=C_lib.Cipher=BufferedBlockAlgorithm.extend({cfg:Base.extend(),createEncryptor:function(key,cfg){return this.create(this._ENC_XFORM_MODE,key,cfg)},createDecryptor:function(key,cfg){return this.create(this._DEC_XFORM_MODE,key,cfg)},init:function(xformMode,key,cfg){this.cfg=this.cfg.extend(cfg);this._xformMode=xformMode;this._key=key;this.reset()},reset:function(){BufferedBlockAlgorithm.reset.call(this);
this._doReset()},process:function(dataUpdate){this._append(dataUpdate);return this._process()},finalize:function(dataUpdate){if(dataUpdate){this._append(dataUpdate)}var finalProcessedData=this._doFinalize();return finalProcessedData},keySize:128/32,ivSize:128/32,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:(function(){function selectCipherStrategy(key){if(typeof key=="string"){return PasswordBasedCipher}else{return SerializableCipher}}return function(cipher){return{encrypt:function(message,key,cfg){return selectCipherStrategy(key).encrypt(cipher,message,key,cfg)},decrypt:function(ciphertext,key,cfg){return selectCipherStrategy(key).decrypt(cipher,ciphertext,key,cfg)}}}}())});var StreamCipher=C_lib.StreamCipher=Cipher.extend({_doFinalize:function(){var finalProcessedBlocks=this._process(!!"flush");return finalProcessedBlocks},blockSize:1});var C_mode=C.mode={};var BlockCipherMode=C_lib.BlockCipherMode=Base.extend({createEncryptor:function(cipher,iv){return this.Encryptor.create(cipher,iv)},createDecryptor:function(cipher,iv){return this.Decryptor.create(cipher,iv)},init:function(cipher,iv){this._cipher=cipher;this._iv=iv}});var CBC=C_mode.CBC=(function(){var CBC=BlockCipherMode.extend();CBC.Encryptor=CBC.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;xorBlock.call(this,words,offset,blockSize);cipher.encryptBlock(words,offset);this._prevBlock=words.slice(offset,offset+blockSize)}});CBC.Decryptor=CBC.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var thisBlock=words.slice(offset,offset+blockSize);cipher.decryptBlock(words,offset);xorBlock.call(this,words,offset,blockSize);this._prevBlock=thisBlock}});function xorBlock(words,offset,blockSize){var block;var iv=this._iv;if(iv){block=iv;this._iv=undefined}else{block=this._prevBlock}for(var i=0;i<blockSize;i++){words[offset+i]^=block[i]}}return CBC}());var C_pad=C.pad={};var Pkcs7=C_pad.Pkcs7={pad:function(data,blockSize){var blockSizeBytes=blockSize*4;
var nPaddingBytes=blockSizeBytes-data.sigBytes%blockSizeBytes;var paddingWord=(nPaddingBytes<<24)|(nPaddingBytes<<16)|(nPaddingBytes<<8)|nPaddingBytes;var paddingWords=[];for(var i=0;i<nPaddingBytes;i+=4){paddingWords.push(paddingWord)}var padding=WordArray.create(paddingWords,nPaddingBytes);data.concat(padding)},unpad:function(data){var nPaddingBytes=data.words[(data.sigBytes-1)>>>2]&255;data.sigBytes-=nPaddingBytes}};var BlockCipher=C_lib.BlockCipher=Cipher.extend({cfg:Cipher.cfg.extend({mode:CBC,padding:Pkcs7}),reset:function(){var modeCreator;Cipher.reset.call(this);var cfg=this.cfg;var iv=cfg.iv;var mode=cfg.mode;if(this._xformMode==this._ENC_XFORM_MODE){modeCreator=mode.createEncryptor}else{modeCreator=mode.createDecryptor;this._minBufferSize=1}if(this._mode&&this._mode.__creator==modeCreator){this._mode.init(this,iv&&iv.words)}else{this._mode=modeCreator.call(mode,this,iv&&iv.words);this._mode.__creator=modeCreator}},_doProcessBlock:function(words,offset){this._mode.processBlock(words,offset)},_doFinalize:function(){var finalProcessedBlocks;var padding=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){padding.pad(this._data,this.blockSize);finalProcessedBlocks=this._process(!!"flush")}else{finalProcessedBlocks=this._process(!!"flush");padding.unpad(finalProcessedBlocks)}return finalProcessedBlocks},blockSize:128/32});var CipherParams=C_lib.CipherParams=Base.extend({init:function(cipherParams){this.mixIn(cipherParams)},toString:function(formatter){return(formatter||this.formatter).stringify(this)}});var C_format=C.format={};var OpenSSLFormatter=C_format.OpenSSL={stringify:function(cipherParams){var wordArray;var ciphertext=cipherParams.ciphertext;var salt=cipherParams.salt;if(salt){wordArray=WordArray.create([1398893684,1701076831]).concat(salt).concat(ciphertext)}else{wordArray=ciphertext}return wordArray.toString(Base64)},parse:function(openSSLStr){var salt;var ciphertext=Base64.parse(openSSLStr);var ciphertextWords=ciphertext.words;if(ciphertextWords[0]==1398893684&&ciphertextWords[1]==1701076831){salt=WordArray.create(ciphertextWords.slice(2,4));
ciphertextWords.splice(0,4);ciphertext.sigBytes-=16}return CipherParams.create({ciphertext:ciphertext,salt:salt})}};var SerializableCipher=C_lib.SerializableCipher=Base.extend({cfg:Base.extend({format:OpenSSLFormatter}),encrypt:function(cipher,message,key,cfg){cfg=this.cfg.extend(cfg);var encryptor=cipher.createEncryptor(key,cfg);var ciphertext=encryptor.finalize(message);var cipherCfg=encryptor.cfg;return CipherParams.create({ciphertext:ciphertext,key:key,iv:cipherCfg.iv,algorithm:cipher,mode:cipherCfg.mode,padding:cipherCfg.padding,blockSize:cipher.blockSize,formatter:cfg.format})},decrypt:function(cipher,ciphertext,key,cfg){cfg=this.cfg.extend(cfg);ciphertext=this._parse(ciphertext,cfg.format);var plaintext=cipher.createDecryptor(key,cfg).finalize(ciphertext.ciphertext);return plaintext},_parse:function(ciphertext,format){if(typeof ciphertext=="string"){return format.parse(ciphertext,this)}else{return ciphertext}}});var C_kdf=C.kdf={};var OpenSSLKdf=C_kdf.OpenSSL={execute:function(password,keySize,ivSize,salt){if(!salt){salt=WordArray.random(64/8)}var key=EvpKDF.create({keySize:keySize+ivSize}).compute(password,salt);var iv=WordArray.create(key.words.slice(keySize),ivSize*4);key.sigBytes=keySize*4;return CipherParams.create({key:key,iv:iv,salt:salt})}};var PasswordBasedCipher=C_lib.PasswordBasedCipher=SerializableCipher.extend({cfg:SerializableCipher.cfg.extend({kdf:OpenSSLKdf}),encrypt:function(cipher,message,password,cfg){cfg=this.cfg.extend(cfg);var derivedParams=cfg.kdf.execute(password,cipher.keySize,cipher.ivSize);cfg.iv=derivedParams.iv;var ciphertext=SerializableCipher.encrypt.call(this,cipher,message,derivedParams.key,cfg);ciphertext.mixIn(derivedParams);return ciphertext},decrypt:function(cipher,ciphertext,password,cfg){cfg=this.cfg.extend(cfg);ciphertext=this._parse(ciphertext,cfg.format);var derivedParams=cfg.kdf.execute(password,cipher.keySize,cipher.ivSize,ciphertext.salt);cfg.iv=derivedParams.iv;var plaintext=SerializableCipher.decrypt.call(this,cipher,ciphertext,derivedParams.key,cfg);
return plaintext}})}());(function(){var C=CryptoJS;var C_lib=C.lib;var BlockCipher=C_lib.BlockCipher;var C_algo=C.algo;var SBOX=[];var INV_SBOX=[];var SUB_MIX_0=[];var SUB_MIX_1=[];var SUB_MIX_2=[];var SUB_MIX_3=[];var INV_SUB_MIX_0=[];var INV_SUB_MIX_1=[];var INV_SUB_MIX_2=[];var INV_SUB_MIX_3=[];(function(){var d=[];for(var i=0;i<256;i++){if(i<128){d[i]=i<<1}else{d[i]=(i<<1)^283}}var x=0;var xi=0;for(var i=0;i<256;i++){var sx=xi^(xi<<1)^(xi<<2)^(xi<<3)^(xi<<4);sx=(sx>>>8)^(sx&255)^99;SBOX[x]=sx;INV_SBOX[sx]=x;var x2=d[x];var x4=d[x2];var x8=d[x4];var t=(d[sx]*257)^(sx*16843008);SUB_MIX_0[x]=(t<<24)|(t>>>8);SUB_MIX_1[x]=(t<<16)|(t>>>16);SUB_MIX_2[x]=(t<<8)|(t>>>24);SUB_MIX_3[x]=t;var t=(x8*16843009)^(x4*65537)^(x2*257)^(x*16843008);INV_SUB_MIX_0[sx]=(t<<24)|(t>>>8);INV_SUB_MIX_1[sx]=(t<<16)|(t>>>16);INV_SUB_MIX_2[sx]=(t<<8)|(t>>>24);INV_SUB_MIX_3[sx]=t;if(!x){x=xi=1}else{x=x2^d[d[d[x8^x2]]];xi^=d[d[xi]]}}}());var RCON=[0,1,2,4,8,16,32,64,128,27,54];var AES=C_algo.AES=BlockCipher.extend({_doReset:function(){var t;if(this._nRounds&&this._keyPriorReset===this._key){return}var key=this._keyPriorReset=this._key;var keyWords=key.words;var keySize=key.sigBytes/4;var nRounds=this._nRounds=keySize+6;var ksRows=(nRounds+1)*4;var keySchedule=this._keySchedule=[];for(var ksRow=0;ksRow<ksRows;ksRow++){if(ksRow<keySize){keySchedule[ksRow]=keyWords[ksRow]}else{t=keySchedule[ksRow-1];if(!(ksRow%keySize)){t=(t<<8)|(t>>>24);t=(SBOX[t>>>24]<<24)|(SBOX[(t>>>16)&255]<<16)|(SBOX[(t>>>8)&255]<<8)|SBOX[t&255];t^=RCON[(ksRow/keySize)|0]<<24}else{if(keySize>6&&ksRow%keySize==4){t=(SBOX[t>>>24]<<24)|(SBOX[(t>>>16)&255]<<16)|(SBOX[(t>>>8)&255]<<8)|SBOX[t&255]}}keySchedule[ksRow]=keySchedule[ksRow-keySize]^t}}var invKeySchedule=this._invKeySchedule=[];for(var invKsRow=0;invKsRow<ksRows;invKsRow++){var ksRow=ksRows-invKsRow;if(invKsRow%4){var t=keySchedule[ksRow]}else{var t=keySchedule[ksRow-4]}if(invKsRow<4||ksRow<=4){invKeySchedule[invKsRow]=t}else{invKeySchedule[invKsRow]=INV_SUB_MIX_0[SBOX[t>>>24]]^INV_SUB_MIX_1[SBOX[(t>>>16)&255]]^INV_SUB_MIX_2[SBOX[(t>>>8)&255]]^INV_SUB_MIX_3[SBOX[t&255]]
}}},encryptBlock:function(M,offset){this._doCryptBlock(M,offset,this._keySchedule,SUB_MIX_0,SUB_MIX_1,SUB_MIX_2,SUB_MIX_3,SBOX)},decryptBlock:function(M,offset){var t=M[offset+1];M[offset+1]=M[offset+3];M[offset+3]=t;this._doCryptBlock(M,offset,this._invKeySchedule,INV_SUB_MIX_0,INV_SUB_MIX_1,INV_SUB_MIX_2,INV_SUB_MIX_3,INV_SBOX);var t=M[offset+1];M[offset+1]=M[offset+3];M[offset+3]=t},_doCryptBlock:function(M,offset,keySchedule,SUB_MIX_0,SUB_MIX_1,SUB_MIX_2,SUB_MIX_3,SBOX){var nRounds=this._nRounds;var s0=M[offset]^keySchedule[0];var s1=M[offset+1]^keySchedule[1];var s2=M[offset+2]^keySchedule[2];var s3=M[offset+3]^keySchedule[3];var ksRow=4;for(var round=1;round<nRounds;round++){var t0=SUB_MIX_0[s0>>>24]^SUB_MIX_1[(s1>>>16)&255]^SUB_MIX_2[(s2>>>8)&255]^SUB_MIX_3[s3&255]^keySchedule[ksRow++];var t1=SUB_MIX_0[s1>>>24]^SUB_MIX_1[(s2>>>16)&255]^SUB_MIX_2[(s3>>>8)&255]^SUB_MIX_3[s0&255]^keySchedule[ksRow++];var t2=SUB_MIX_0[s2>>>24]^SUB_MIX_1[(s3>>>16)&255]^SUB_MIX_2[(s0>>>8)&255]^SUB_MIX_3[s1&255]^keySchedule[ksRow++];var t3=SUB_MIX_0[s3>>>24]^SUB_MIX_1[(s0>>>16)&255]^SUB_MIX_2[(s1>>>8)&255]^SUB_MIX_3[s2&255]^keySchedule[ksRow++];s0=t0;s1=t1;s2=t2;s3=t3}var t0=((SBOX[s0>>>24]<<24)|(SBOX[(s1>>>16)&255]<<16)|(SBOX[(s2>>>8)&255]<<8)|SBOX[s3&255])^keySchedule[ksRow++];var t1=((SBOX[s1>>>24]<<24)|(SBOX[(s2>>>16)&255]<<16)|(SBOX[(s3>>>8)&255]<<8)|SBOX[s0&255])^keySchedule[ksRow++];var t2=((SBOX[s2>>>24]<<24)|(SBOX[(s3>>>16)&255]<<16)|(SBOX[(s0>>>8)&255]<<8)|SBOX[s1&255])^keySchedule[ksRow++];var t3=((SBOX[s3>>>24]<<24)|(SBOX[(s0>>>16)&255]<<16)|(SBOX[(s1>>>8)&255]<<8)|SBOX[s2&255])^keySchedule[ksRow++];M[offset]=t0;M[offset+1]=t1;M[offset+2]=t2;M[offset+3]=t3},keySize:256/32});C.AES=BlockCipher._createHelper(AES)}());CryptoJS.pad.ZeroPadding={pad:function(data,blockSize){var blockSizeBytes=blockSize*4;data.clamp();data.sigBytes+=blockSizeBytes-((data.sigBytes%blockSizeBytes)||blockSizeBytes)},unpad:function(data){var dataWords=data.words;var i=data.sigBytes-1;for(var i=data.sigBytes-1;
i>=0;i--){if(((dataWords[i>>>2]>>>(24-(i%4)*8))&255)){data.sigBytes=i+1;break}}}};CryptoJS.mode.ECB=(function(){var ECB=CryptoJS.lib.BlockCipherMode.extend();ECB.Encryptor=ECB.extend({processBlock:function(words,offset){this._cipher.encryptBlock(words,offset)}});ECB.Decryptor=ECB.extend({processBlock:function(words,offset){this._cipher.decryptBlock(words,offset)}});return ECB}());CryptoJS.mode.CFB=(function(){var CFB=CryptoJS.lib.BlockCipherMode.extend();CFB.Encryptor=CFB.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;generateKeystreamAndEncrypt.call(this,words,offset,blockSize,cipher);this._prevBlock=words.slice(offset,offset+blockSize)}});CFB.Decryptor=CFB.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var thisBlock=words.slice(offset,offset+blockSize);generateKeystreamAndEncrypt.call(this,words,offset,blockSize,cipher);this._prevBlock=thisBlock}});function generateKeystreamAndEncrypt(words,offset,blockSize,cipher){var keystream;var iv=this._iv;if(iv){keystream=iv.slice(0);this._iv=undefined}else{keystream=this._prevBlock}cipher.encryptBlock(keystream,0);for(var i=0;i<blockSize;i++){words[offset+i]^=keystream[i]}}return CFB}());CryptoJS.mode.CTR=(function(){var CTR=CryptoJS.lib.BlockCipherMode.extend();var Encryptor=CTR.Encryptor=CTR.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var iv=this._iv;var counter=this._counter;if(iv){counter=this._counter=iv.slice(0);this._iv=undefined}var keystream=counter.slice(0);cipher.encryptBlock(keystream,0);counter[blockSize-1]=(counter[blockSize-1]+1)|0;for(var i=0;i<blockSize;i++){words[offset+i]^=keystream[i]}}});CTR.Decryptor=Encryptor;return CTR}());CryptoJS.mode.CTRGladman=(function(){var CTRGladman=CryptoJS.lib.BlockCipherMode.extend();function incWord(word){if(((word>>24)&255)===255){var b1=(word>>16)&255;var b2=(word>>8)&255;var b3=word&255;if(b1===255){b1=0;if(b2===255){b2=0;
if(b3===255){b3=0}else{++b3}}else{++b2}}else{++b1}word=0;word+=(b1<<16);word+=(b2<<8);word+=b3}else{word+=(1<<24)}return word}function incCounter(counter){if((counter[0]=incWord(counter[0]))===0){counter[1]=incWord(counter[1])}return counter}var Encryptor=CTRGladman.Encryptor=CTRGladman.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var iv=this._iv;var counter=this._counter;if(iv){counter=this._counter=iv.slice(0);this._iv=undefined}incCounter(counter);var keystream=counter.slice(0);cipher.encryptBlock(keystream,0);for(var i=0;i<blockSize;i++){words[offset+i]^=keystream[i]}}});CTRGladman.Decryptor=Encryptor;return CTRGladman}());CryptoJS.mode.OFB=(function(){var OFB=CryptoJS.lib.BlockCipherMode.extend();var Encryptor=OFB.Encryptor=OFB.extend({processBlock:function(words,offset){var cipher=this._cipher;var blockSize=cipher.blockSize;var iv=this._iv;var keystream=this._keystream;if(iv){keystream=this._keystream=iv.slice(0);this._iv=undefined}cipher.encryptBlock(keystream,0);for(var i=0;i<blockSize;i++){words[offset+i]^=keystream[i]}}});OFB.Decryptor=Encryptor;return OFB}());CryptoJS.pad.AnsiX923={pad:function(data,blockSize){var dataSigBytes=data.sigBytes;var blockSizeBytes=blockSize*4;var nPaddingBytes=blockSizeBytes-dataSigBytes%blockSizeBytes;var lastBytePos=dataSigBytes+nPaddingBytes-1;data.clamp();data.words[lastBytePos>>>2]|=nPaddingBytes<<(24-(lastBytePos%4)*8);data.sigBytes+=nPaddingBytes},unpad:function(data){var nPaddingBytes=data.words[(data.sigBytes-1)>>>2]&255;data.sigBytes-=nPaddingBytes}};CryptoJS.pad.Iso10126={pad:function(data,blockSize){var blockSizeBytes=blockSize*4;var nPaddingBytes=blockSizeBytes-data.sigBytes%blockSizeBytes;data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes-1)).concat(CryptoJS.lib.WordArray.create([nPaddingBytes<<24],1))},unpad:function(data){var nPaddingBytes=data.words[(data.sigBytes-1)>>>2]&255;data.sigBytes-=nPaddingBytes}};CryptoJS.pad.Iso97971={pad:function(data,blockSize){data.concat(CryptoJS.lib.WordArray.create([2147483648],1));
CryptoJS.pad.ZeroPadding.pad(data,blockSize)},unpad:function(data){CryptoJS.pad.ZeroPadding.unpad(data);data.sigBytes--}};CryptoJS.pad.NoPadding={pad:function(){},unpad:function(){}};
+26 -45
View File
@@ -27,51 +27,32 @@ $(".st").hover(function(){
$(".searcTime .gt").click(function(){
$(this).addClass("on").siblings().removeClass("on");
})
$(".loadbtn").click(function(){
$(this).parents(".searcTime").find("span").removeClass("on");
$('.time_range_submit').click(function(){
$(this).parents(".searcTime").find("span").removeClass("on");
$(this).parents(".searcTime").find(".st").addClass("on");
var b = (new Date($(this).parent().find(".btime").val()).getTime())/1000;
var e = (new Date($(this).parent().find(".etime").val()).getTime())/1000;
b = Math.round(b);
e = Math.round(e);
getload(b,e)
})
$(".cpubtn").click(function(){
$(this).parents(".searcTime").find("span").removeClass("on");
$(this).parents(".searcTime").find(".st").addClass("on");
var b = (new Date($(this).parent().find(".btime").val()).getTime())/1000;
var e = (new Date($(this).parent().find(".etime").val()).getTime())/1000;
b = Math.round(b);
e = Math.round(e);
cpu(b,e)
})
$(".membtn").click(function(){
$(this).parents(".searcTime").find("span").removeClass("on");
$(this).parents(".searcTime").find(".st").addClass("on");
var b = (new Date($(this).parent().find(".btime").val()).getTime())/1000;
var e = (new Date($(this).parent().find(".etime").val()).getTime())/1000;
b = Math.round(b);
e = Math.round(e);
mem(b,e)
})
$(".diskbtn").click(function(){
$(this).parents(".searcTime").find("span").removeClass("on");
$(this).parents(".searcTime").find(".st").addClass("on");
var b = (new Date($(this).parent().find(".btime").val()).getTime())/1000;
var e = (new Date($(this).parent().find(".etime").val()).getTime())/1000;
b = Math.round(b);
e = Math.round(e);
disk(b,e)
})
$(".networkbtn").click(function(){
$(this).parents(".searcTime").find("span").removeClass("on");
$(this).parents(".searcTime").find(".st").addClass("on");
var b = (new Date($(this).parent().find(".btime").val()).getTime())/1000;
var e = (new Date($(this).parent().find(".etime").val()).getTime())/1000;
b = Math.round(b);
e = Math.round(e);
network(b,e)
})
console.log(b,e);
switch ($(this).attr('data-type')) {
case 'getload':
getload(b,e);
break;
case 'cpu':
cpu(b,e);
break;
case 'mem':
mem(b,e);
break;
case 'disk':
disk(b,e);
break;
case 'network':
network(b,e);
break;
}
});
//指定天数
function Wday(day,name){
var now = (new Date().getTime())/1000;
@@ -392,8 +373,8 @@ function disk(b, e) {
//var zData = [];
for (var i = 0; i < rdata.length; i++) {
rData.push((rdata[i].read_bytes / 1024 / 60).toFixed(3));
wData.push((rdata[i].write_bytes / 1024 / 60).toFixed(3));
rData.push((rdata[i].read_bytes / 1024 / 60).toFixed(2));
wData.push((rdata[i].write_bytes / 1024 / 60).toFixed(2));
xData.push(rdata[i].addtime);
//yData.push(rdata[i].read_count);
//zData.push(rdata[i].write_count);
@@ -404,7 +385,7 @@ function disk(b, e) {
axisPointer: {
type: 'cross'
},
formatter: lan.control.time+"{b0}<br />{a0}: {c0} Kb/s<br />{a1}: {c1} Kb/s",
//formatter: lan.control.time+"{b0}<br />{a0}: {c0} Kb/s<br />{a1}: {c1} Kb/s",
},
legend: {
data: [lan.control.disk_read_bytes, lan.control.disk_write_bytes]
@@ -506,8 +487,8 @@ $.get('/ajax?action=GetNetWorkIo&start='+b+'&end='+e,function(rdata){
cData.push(rdata[i].down_packets);
dData.push(rdata[i].up_packets);
xData.push(rdata[i].addtime);
yData.push(rdata[i].up);
zData.push(rdata[i].down);
yData.push(rdata[i].up.toFixed(2));
zData.push(rdata[i].down.toFixed(2));
}
option = {
tooltip: {
+1 -1
View File
@@ -671,7 +671,7 @@ function Recycle_bin(type) {
<th>' + lan.files.recycle_bin_th2 + '</th>\
<th>' + lan.files.recycle_bin_th3 + '</th>\
<th width="150">' + lan.files.recycle_bin_th4 + '</th>\
<th style="text-align: right;" width="110">' + lan.files.recycle_bin_th5 + '</th>\
<th style="text-align: right;" width="199">' + lan.files.recycle_bin_th5 + '</th>\
</tr>\
</thead>\
<tbody id="RecycleBody" class="list-list">' + body + '</tbody>\
+244 -20
View File
@@ -3,6 +3,8 @@ bt.pub.check_install(function (rdata) {
})
var interval_stop = false;
var index = {
warning_list:[],
warning_num:0,
interval: {
limit: 10,
count: 0,
@@ -91,9 +93,9 @@ var index = {
},
get_init: function () {
var _this = this;
setTimeout(function () { _this.get_disk_list(); }, 500)
setTimeout(function () { _this.get_server_info(); }, 1000)
setTimeout(function () { _this.get_disk_list(); }, 10);
setTimeout(function () { _this.get_warning_list(); }, 20);
setTimeout(function () { _this.get_server_info(); }, 30);
bt.pub.get_user_info(function (rdata) {
if (rdata.status) {
@@ -200,13 +202,13 @@ var index = {
})
})
});
setTimeout(function () { _this.interval.start(); }, 1600)
setTimeout(function () { index.get_index_list(); }, 1200)
setTimeout(function () { _this.interval.start(); }, 40)
setTimeout(function () { index.get_index_list(); }, 50)
setTimeout(function () {
_this.net.init();
}, 200);
}, 60);
setTimeout(function () {
bt.system.check_update(function (rdata) {
@@ -222,7 +224,7 @@ var index = {
// }
}, false)
}, 1500)
}, 70)
},
get_data_info: function (callback) {
var _this = $(this);
@@ -236,7 +238,7 @@ var index = {
index.set_val(_membox, { usage: (net.mem.memRealUsed * 100 / net.mem.memTotal).toFixed(1), items: pub_arr, title: net.mem.memRealUsed + '/' + net.mem.memTotal + '(MB)' })
bt.set_cookie('memSize', net.mem.memTotal)
for (var i = 0; i < _diskbox.length; i++) {
index.set_val(_diskbox.eq(i), { usage: net.disk[i].size[3].split('%')[0], title: net.disk[i].size[1]+'/'+net.disk[0].size[0], items: pub_arr })
index.set_val(_diskbox.eq(i), { usage: net.disk[i].size[3].split('%')[0], title: net.disk[i].size[1]+'/'+net.disk[i].size[0], items: pub_arr })
}
var _lval = Math.round((net.load.one / net.load.max) * 100);
@@ -366,6 +368,7 @@ var index = {
color = '#dd2f00'
break;
}
index.set_val(_li, { usage: item.rate, color: color })
_tab.append(_li);
}
@@ -468,28 +471,34 @@ var index = {
bt.msg(rdata);
return;
}
var loading = bt.open({
var result = rdata,
is_beta = rdata.msg.is_beta,
loading = bt.open({
type: 1,
title: '[Linux' + (rdata.msg.is_beta == 1 ? lan.index.test_version : lan.index.final_version) + ']-'+lan.index.update_log,
area: '520px',
area: '550px',
shadeClose: false,
skin: 'layui-layer-dialog',
closeBtn: 2,
content: '<div class="setchmod bt-form">\
<div class="update_title"><i class="layui-layer-ico layui-layer-ico1"></i><span>'+lan.index.last_version_now+'</span></div>\
<div class="update_version">'+lan.index.this_version+'<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6-1-5-installation-tutorial/36" target="_blank" class="btlink" title="'+lan.index.check_this_version_log+'">'+lan.index.bt_linux+ (rdata.msg.is_beta == 1 ? lan.index.test_version+' ' + rdata.msg.beta.version : lan.index.final_version+' ' + rdata.msg.version) + '</a>&nbsp;&n'+ lan.index.release_time + (rdata.msg.is_beta == 1 ? rdata.msg.beta.uptime : rdata.msg.uptime) + '</div>\
<div class="update_version">'+lan.index.this_version+'<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6-1-5-installation-tutorial/36" target="_blank" class="btlink" title="'+lan.index.check_this_version_log+'">'+lan.index.bt_linux+ (rdata.msg.is_beta == 1 ? lan.index.test_version+' ' + rdata.msg.beta.version : lan.index.final_version+' ' + rdata.msg.version) + '</a>&nbsp;&nbsp;'+ lan.index.release_time + (rdata.msg.is_beta == 1 ? rdata.msg.beta.uptime : rdata.msg.uptime) + '</div>\
<div class="update_conter">\
<div class="update_tips">'+ (is_beta != 1 ? lan.index.test_version : lan.index.final_version) + lan.index.last_version_is + (result.msg.is_beta != 1 ? result.msg.beta.version : result.msg.version) + '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'+lan.index.update_time+'&nbsp;&nbsp;' + (is_beta != 1 ? result.msg.beta.uptime : result.msg.uptime) + '</div>\
'+ (is_beta !== 1 ? '<span>'+lan.index.update_verison_click+'<a href="javascript:;" onclick="index.beta_msg()" class="btlink btn_update_testPanel">'+lan.index.check_detail+'</a></span>' : '<span>'+lan.index.change_final_click+'<a href="javascript:;" onclick="index.to_not_beta()" class="btlink btn_update_testPanel">'+lan.index.change_final+'</a></span>') + '\
</div>\
<div class="bt-form-submit-btn">\
<button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">'+ lan.public.cancel + '</button>\
<button type="button" class="btn btn-success btn-sm btn-title btn_update_panel" onclick="layer.closeAll()">'+ lan.public.know + '</button>\
</div>\
</div>\
<style>\
.setchmod{padding-bottom:50px;}\
.setchmod{padding-bottom:40px;padding-top: 0;}\
.update_title{overflow: hidden;position: relative;vertical-align: middle;margin-top: 10px;}\
.update_title .layui-layer-ico{display: block;left: 60px !important;top: 1px !important;}\
.update_title span{display: inline-block;color: #333;height: 30px;margin-left: 105px;margin-top: 3px;font-size: 20px;}\
.update_title .layui-layer-ico{display: block;left: 10px !important;top: 1px !important;}\
.update_title span{display: inline-block;color: #333;height: 30px;margin-left: 45px;margin-top: 3px;font-size: 20px;}\
.update_conter{background: #f9f9f9;border-radius: 4px;padding: 20px;margin: 15px 37px;margin-top: 15px;}\
.update_version{font-size: 12px;margin:15px 0 10px 85px}\
.update_version{font-size: 12px;margin:15px 0 10px 60px}\
.update_logs{margin-bottom:10px;border-bottom:1px solid #ececec;padding-bottom:10px;}\
.update_tips{font-size: 13px;color: #666;font-weight: 600;}\
.update_tips span{padding-top: 5px;display: block;font-weight: 500;}\
@@ -518,10 +527,10 @@ var index = {
<div class="update_version">'+lan.index.last_version+'<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6-1-5-installation-tutorial/36" target="_blank" class="btlink" title="'+lan.index.check_version_log+'">'+lan.index.bt_linux+ (is_beta === 1 ? lan.index.test_version : lan.index.final_version) + rdata.version + '</a></br>'+lan.index.update_date + (result.msg.is_beta == 1 ? result.msg.beta.uptime : result.msg.uptime) + '</div>\
<div class="update_logs">'+ rdata.updateMsg + '</div>\
</div>\
<!--div class="update_conter">\
<div class="update_conter">\
<div class="update_tips">'+ (is_beta !== 1 ? lan.index.test_version : lan.index.final_version) + lan.index.last_version_is + (result.msg.is_beta != 1 ? result.msg.beta.version : result.msg.version) + '&nbsp;&nbsp;&nbsp;'+lan.index.update_time+'&nbsp;&nbsp;' + (is_beta != 1 ? result.msg.beta.uptime : result.msg.uptime) + '</div>\
'+ (is_beta !== 1 ? '<span>'+lan.index.update_verison_click+'<a href="javascript:;" onclick="index.beta_msg()" class="btlink btn_update_testPanel">'+lan.index.check_detail+'</a></span>' : '<span>'+lan.index.change_final_click+'<a href="javascript:;" onclick="index.to_not_beta()" class="btlink btn_update_testPanel">'+lan.index.change_final+'</a></span>') + '\
</div-->\
</div>\
<div class="bt-form-submit-btn">\
<button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">'+ lan.public.cancel + '</button>\
<button type="button" class="btn btn-success btn-sm btn-title btn_update_panel" onclick="index.to_update()" >'+ lan.index.update_go + '</button>\
@@ -538,7 +547,7 @@ var index = {
layer.closeAll();
bt.system.to_update(function (rdata) {
if (rdata.status) {
bt.msg({ msg: rdata.msg, icon: 1 })
bt.msg({ msg: lan.index.update_ok, icon: 1 })
$("#btversion").html(rdata.version);
$("#toUpdate").html('');
bt.system.reload_panel();
@@ -631,13 +640,14 @@ var index = {
});
$('.btn_update_panel_beta').click(function () {
bt.show_confirm(lan.index.update_test_version, lan.index.check_test_version_detail, function () {
var loading = bt.load();
bt.send('apple_beta', 'ajax/apple_beta', {}, function (rdata) {
if (rdata.status === false) {
bt.msg(rdata);
return;
}
bt.system.check_update(function (rdata) {
bt.system.check_update(function (res) {
loading.close();
index.to_update();
});
});
@@ -769,6 +779,220 @@ var index = {
$.post('/plugin?action=get_soft_list', { type: 8, p: 1, force: 1, cache: 1 }, function (rdata) {
console.log(lan.index.get_soft_list_success);
});
},
// 获取安全风险列表
get_warning_list:function(active,callback){
var that = this,obj = {};
if(active == true) obj = {force:1}
bt.send('get_list','warning/get_list',obj,function(res){
if(res.status !== false){
that.warning_list = res;
that.warning_num = res.risk.length;
$('.warning_num').css('color',(that.warning_num > 0?'red':'#20a53a')).html(that.warning_num);
$('.warning_scan_ps').html(that.warning_num>0?('This scan check <i>'+ that.warning_num +'</i> risks, please repair them in time!'):'There is no risk item detected in this scan, please keep it!');
if(callback) callback(res);
}
});
},
/**
* @description 获取时间简化缩写
* @param {Numbre} dateTimeStamp 需要转换的时间戳
* @return {String} 简化后的时间格式
*/
get_simplify_time:function(dateTimeStamp){
if(dateTimeStamp === 0) return 'Just';
if(dateTimeStamp.toString().length == 10) dateTimeStamp = dateTimeStamp * 1000
var minute = 1000 * 60,hour = minute * 60,day = hour * 24,halfamonth = day * 15,month = day * 30,now = new Date().getTime(),diffValue = now - dateTimeStamp;
if(diffValue < 0) return 'Just';
var monthC = diffValue / month,weekC = diffValue / (7 * day),dayC = diffValue / day,hourC = diffValue / hour,minC = diffValue / minute;
if(monthC >= 1) {
result = "" + parseInt(monthC) + "month ago";
} else if(weekC >= 1) {
result = "" + parseInt(weekC) + "weeks ago";
} else if(dayC >= 1) {
result = "" + parseInt(dayC) + "days ago";
} else if(hourC >= 1) {
result = "" + parseInt(hourC) + "hours ago";
} else if(minC >= 1) {
result = "" + parseInt(minC) + "minutes ago";
} else{
result = "Just";
}
return result;
},
/**
* @description 渲染安全模块视图
* @return 无返回值
*/
reader_warning_view:function(){
var that = this;
function reader_warning_list(data){
var html = '',scan_time = '',arry = [['risk','Risk'],['security','Security'],['ignore','Ignore']],level = [['Low risk','#e8d544'],['Medium risk','#E6A23C'],['High risk','red']]
bt.each(arry,function(index,item){
var data_item = data[item[0]],data_title = item[1];
html += '<li class="module_item '+ item[0] +'">'+
'<div class="module_head">'+
'<span class="module_title">'+ data_title +'</span>'+
'<span class="module_num">'+ data_item.length +'</span>'+
'<span class="module_cut_show">'+ (item[index] == 'risk' && that.warning_num > 0?'<i>Collapse</i><span class="glyphicon glyphicon-menu-up" aria-hidden="false"></span>':'<i>Details</i><span class="glyphicon glyphicon-menu-down" aria-hidden="false"></span>') +'</span>'+
'</div>'+
(function(index,item){
var htmls = '<ul class="module_details_list '+ (item[0] == 'risk' && that.warning_num > 0?'active':'') +'">';
bt.each(data_item,function(indexs,items){
scan_time = items.check_time;
htmls += '<li class="module_details_item">'+
'<div class="module_details_head">'+
'<span class="module_details_title">'+ items.ps +'<i>Checked: '+ (that.get_simplify_time(items.check_time) || 'Just') +', time: '+ ( items.taking>1?( items.taking +'Sec'):((items.taking * 1000).toFixed(2) +'ms')) +'</i></span>'+
'<span class="operate_tools">'+ (item[0] != 'security'?('<a href="javascript:;" class="btlink cut_details">Detail</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="javascript:;" data-model="'+ items.m_name +'" data-title="'+ items.title +'" '+ (item[0]=='ignore'?'class=\"btlink\"':'') +' data-type="'+item[0]+'">'+ (item[0] != 'ignore'?'Ignore':'Remove') +'</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="javascript:;" class="btlink" data-model="'+ items.m_name +'" data-title="'+ items.title +'">Check</a>'):'<a href="javascript:;" class="btlink cut_details">Detail</a>') +'</span>' +
'</div>'+
'<div class="module_details_body">'+
'<div class="module_details_line">'+
'<div class="module_details_block"><span class="line_title">Test type: </span><span class="line_content">'+ items.title +'</span></div>'+
'<div class="module_details_block"><span class="line_title">Risk level: </span><span class="line_content" style="color:'+ level[items.level-1][1] +'">'+ level[items.level-1][0] +'</span></div>'+
'</div>'+
'<div class="module_details_line"><span class="line_title">Risk detail: </span><span class="line_content">'+ items.msg +'</span></div>'+
'<div class="module_details_line"><span class="line_title">'+ (item[0] != 'security'?'Solution: ':'Suggest: ') +'</span><span class="line_content">'+
(function(){
var htmlss = '';
bt.each(items.tips,function(indexss,itemss){
htmlss +='<i>'+ (indexss+1) +'、'+ itemss +'</i></br>';
});
return htmlss;
}()) +'</span></div>'+
(items.help != ''?('<div class="module_details_line"><span class="line_title">Help: </span><span class="line_content"><a href="'+ items.help +'" target="_blank" class="btlink">'+items.help +'</span></div>'):'') +
'</div>'+
'</li>';
});
htmls += '</ul>';
return htmls;
}(index,item))
+'</li>'
});
$('.warning_scan_body').html(html);
$('.warning_scan_time').html('Checked: &nbsp;'+ bt.format_data(scan_time));
}
bt.open({
type:'1',
title:'Security risk',
area:['850px','700px'],
skin:'warning_scan_view',
content:'<div class="warning_scan_view">'+
'<div class="warning_scan_head">'+
'<span class="warning_scan_ps">'+ (that.warning_num>0?('This scan check <i>'+ that.warning_num +'</i> risks, please repair them in time!'):'This scan check no risks, please keep it!') +'</span>'+
'<span class="warning_scan_time"></span>'+
'<button class="warning_again_scan">Retest</button>'+
'</div>'+
'<ol class="warning_scan_body"></ol>'+
'</div>',
success:function(){
$('.warning_again_scan').click(function(){
var loadT = layer.msg('Re detecting security risks, please wait...',{icon:16});
that.get_warning_list(true,function(){
layer.msg('Scan succeeded',{icon:1});
reader_warning_list(that.warning_list);
});
});
$('.warning_scan_body').on('click','.module_item .module_head',function(){
var _parent = $(this).parent(),_parent_index = _parent.index(),_list = $(this).next();
if(parseInt($(this).find('.module_num').text()) > 0){
if(_list.hasClass('active')){
_list.css('height',0);
$(this).find('.module_cut_show i').text('Detail').next().removeClass('glyphicon-menu-up').addClass('glyphicon-menu-down');
setTimeout(function(){
_list.removeClass('active').removeAttr('style');
},500);
}else{
$(this).find('.module_cut_show i').text('Collapse').next().removeClass('glyphicon-menu-down').addClass('glyphicon-menu-up');
_list.addClass('active');
var details_list = _list.parent().siblings().find('.module_details_list');
details_list.removeClass('active');
details_list.prev().find('.module_cut_show i').text('Detail').next().removeClass('glyphicon-menu-up').addClass('glyphicon-menu-down')
}
}
});
$('.warning_scan_body').on('click','.operate_tools a',function(){
var index = $(this).index(),data = $(this).data();
switch(index){
case 0:
if($(this).hasClass('active')){
$(this).parents('.module_details_head').next().hide();
$(this).removeClass('active').text('Detail');
}else{
var item = $(this).parents('.module_details_item'),indexs = item.index();
$(this).addClass('active').text('Collapse');
item.siblings().find('.module_details_body').hide();
item.siblings().find('.operate_tools a:eq(0)').removeClass('active').text('Detail');
$(this).parents('.module_details_head').next().show();
$('.module_details_list').scrollTop(indexs * 41);
}
break;
case 1:
if(data.type != 'ignore'){
bt.confirm({title:'Ignore risk',msg:'Confirm to ignore【'+ data.title +'】risk?'},function(){
that.warning_set_ignore(data.model,function(res){
that.get_warning_list(false,function(){
bt.msg(res)
reader_warning_list(that.warning_list);
});
});
});
}else{
that.warning_set_ignore(data.model,function(res){
that.get_warning_list(false,function(){
bt.msg(res)
reader_warning_list(that.warning_list);
setTimeout(function(){
$('.module_item.ignore').click();
},100)
});
});
}
break;
case 2:
that.waring_check_find(data.model,function(res){
that.get_warning_list(false,function(){
bt.msg(res)
reader_warning_list(that.warning_list);
});
});
break;
}
});
reader_warning_list(that.warning_list);
}
})
},
/**
* @description 安全风险指定模块检查
* @param {String} model_name 模块名称
* @param {Function} callback 成功后的回调
* @return 无返回值
*/
waring_check_find:function(model_name,callback){
var loadT = layer.msg('Detecting the specified module, please wait...',{icon:16,time:0});
bt.send('check_find','warning/check_find',{m_name:model_name},function(res){
bt.msg(res);
if(res.status !== false){
if(callback) callback(res);
}
});
},
/**
* @description 安全风险指定模块是否忽略
* @param {String} model_name 模块名称
* @param {Function} callback 成功后的回调
* @return 无返回值
*/
warning_set_ignore:function(model_name,callback){
var loadT = layer.msg('Setting the specified module, please wait...',{icon:16,time:0});
bt.send('set_ignore','warning/set_ignore',{m_name:model_name},function(res){
bt.msg(res);
if(res.status !== false){
if(callback) callback(res);
}
});
}
}
index.get_init();
+110 -15
View File
@@ -283,12 +283,19 @@ var aceEditor = {
var _val = $(this).attr('data-value'),
_item = _this.editor['ace_editor_' + _this.ace_active];
if ($(this).parent().hasClass('tabsType')) {
_item.ace.getSession().setUseSoftTabs(_val == 'nbsp');
//_item.ace.getSession().setUseSoftTabs(_val == 'nbsp');
_this.aceConfig.aceEditor.useSoftTabs = _val == 'nbsp';
_item.softTabs = _val == 'nbsp';
} else {
_item.ace.getSession().setTabSize(_val);
//_item.ace.getSession().setTabSize(_val);
_this.aceConfig.aceEditor.tabSize = _val;
_item.tabSize = _val;
}
_this.saveAceConfig(_this.aceConfig,function(res){
if(res.status){
layer.msg('Successful setup', {icon: 1});
}
});
$(this).siblings().removeClass('active').find('.icon').remove();
$(this).addClass('active').append(_icon);
_this.currentStatusBar(_item.id);
@@ -1809,18 +1816,16 @@ var aceEditor = {
encoding:obj.encoding.toLowerCase(),
path:obj.path
},
complete:function(res,status){
if(res.status != 200){
if(error) error(res.responseJSON)
}else if(res.status == 200){
var rdata = res.responseJSON;
if(rdata.status){
if(success) success(rdata)
}else{
if(error) error(rdata)
}
if(!obj.tips) layer.msg(rdata.msg,{icon:rdata.status?1:2});
success:function(rdata){
if(rdata.status){
if(success) success(rdata)
}else{
if(error) error(rdata)
}
if(!obj.tips) layer.msg(rdata.msg,{icon:rdata.status?1:2});
},
error:function(err){
if(error) error(err)
}
});
},
@@ -2003,13 +2008,99 @@ function openEditorView(type, path) {
return false;
}
}
},
end:function(){
aceEditor.ace_active = '';
aceEditor.editor = null;
aceEditor.pathAarry = [];
aceEditor.editorLength = 0;
aceEditor.menu_path = '';
}
});
}
/**
* AES加密
* @param {string} s_text 等待加密的字符串
* @param {string} s_key 16位密钥
* @param {array} ctx 可选默认为 { mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.ZeroPadding }
* @return {string}
*/
function aes_encrypt(s_text,s_key,ctx){
if(ctx == undefined) ctx = { mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.ZeroPadding }
var key = CryptoJS.enc.Utf8.parse(s_key);
var encrypt_data = CryptoJS.AES.encrypt(s_text,key,ctx);
return encrypt_data.toString();
}
/**
* AES解密
* @param {string} s_text 等待解密的密文
* @param {string} s_key 16位密钥
* @param {array} ctx 可选默认为 { mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.ZeroPadding }
* @return {string}
*/
function aes_decrypt(s_text,s_key,ctx){
if(ctx == undefined) ctx = { mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.ZeroPadding }
var key = CryptoJS.enc.Utf8.parse(s_key);
var decrypt_data = CryptoJS.AES.decrypt(s_text,key,ctx);
return decrypt_data.toString(CryptoJS.enc.Utf8);
}
/**
* ajax内容解密
* @param {string} data 加密的响应数据
* @param {string} stype ajax中定义的数据类型
* @return {string} 解密后的响应数据
*/
function ajax_decrypt(data,stype){
if(!data) return data;
if(data.substring(0,6) == "BT-CRT"){
var token = $("#request_token_head").attr("token")
var pwd = token.substring(0,8) + token.substring(40,48)
data = aes_decrypt(data.substring(6),pwd);
if(stype == undefined){
stype = '';
}
if(stype.toLowerCase() != 'json'){
data = JSON.parse(data);
}
}
return data
}
/**
* 格式化form_data数据并加密
* @param {string} form_data 加密前的form_data数据
* @return {string} 加密后的form_data数据
*/
function format_form_data(form_data){
var data_tmp = form_data.split('&');
var form_info = {}
var token = $("#request_token_head").attr("token")
if(!token) return form_data;
var pwd = token.substring(0,8) + token.substring(40,48)
for(var i=0;i<data_tmp.length;i++){
var tmp = data_tmp[i].split('=');
if(tmp.length < 2) continue
// if(!tmp[1]) continue;
var val = decodeURIComponent(tmp[1].replace(/\+/g,'%20'));
if(val.length > 3){
form_info[tmp[0]] = 'BT-CRT' + aes_encrypt(val,pwd);
}else{
form_info[tmp[0]] = val;
}
}
return $.param(form_info);
}
function ajax_encrypt(request){
if(!this.type || !this.data || !this.contentType) return;
if($("#panel_debug").attr("data") == 'True') return;
if(this.type == 'POST' && this.data.length > 1){
this.data = format_form_data(this.data);
}
}
function ajaxSetup() {
var my_headers = {};
var request_token_ele = document.getElementById("request_token_head");
@@ -2025,7 +2116,11 @@ function ajaxSetup() {
}
if (my_headers) {
$.ajaxSetup({ headers: my_headers });
$.ajaxSetup({
headers: my_headers,
dataFilter: ajax_decrypt,
beforeSend: ajax_encrypt
});
}
}
ajaxSetup();
+97 -15
View File
@@ -681,6 +681,12 @@ var bt = {
var _width = _obj.width ? _obj.width : '330px';
_html += "<input name='" + _name + "' " + (_obj.disabled ? 'disabled' : '') + " class='bt-input-text mr5 " + _name + bs + "' " + (_placeholder ? ' placeholder="' + _placeholder + '"' : "") + " type='password' style='width:" + _width + "' value='" + (_obj.value ? _obj.value : '') + "' />";
break;
case 'div':
var _width = _obj.width ? _obj.width : '330px';
var _height = _obj.height ? _obj.height : '100px';
_html += '<div class="bt-input-text ace_config_editor_scroll mr20 ' + _name + bs + '" name="' + _name + '" style="width:' + _width + ';height:' + _height + ';line-height:22px">' + (_obj.value ? _obj.value : '') + '</div>';
if (_placeholder) _html += '<div class="placeholder c9" style="top: 15px; left: 15px; display: block;">' + _placeholder + '</div>';
break;
case 'switch':
_html += '<div style="display: inline-block;vertical-align: middle;">\
<input type="checkbox" id="' + _name + '" ' + (_obj.value==true?'checked':'') + ' class="btswitch btswitch-ios">\
@@ -1022,6 +1028,27 @@ var bt = {
} else {
ace.saveCallback(ace.ACE.getValue());
}
},
/**
* @description 遍历数组和对象
* @param {Array|Object} obj 遍历数组|对象
* @param {Function} fn 遍历对象或数组
* @return 当前对象
*/
each: function (obj, fn) {
var key, that = this;
if (typeof fn !== 'function') return that;
obj = obj || [];
if (obj.constructor === Object) {
for (key in obj) {
if (fn.call(obj[key], key, obj[key])) break;
}
} else {
for (key = 0; key < obj.length; key++) {
if (fn.call(obj[key], key, obj[key])) break;
}
}
return that;
}
};
@@ -3992,6 +4019,22 @@ bt.soft = {
}
})
},
//遍历数组和对象
each:function(obj, fn){
var key,that = this;
if(typeof fn !== 'function') return that;
obj = obj || [];
if(obj.constructor === Object){
for(key in obj){
if(fn.call(obj[key], key, obj[key])) break;
}
} else {
for(key = 0; key < obj.length; key++){
if(fn.call(obj[key], key, obj[key])) break;
}
}
return that;
},
del_make_args: function (name, args_name) {
name = bt.soft.get_name(name);
bt.confirm({ msg: 'Confirm delete[' + name + ':' + args_name + ']module', title: 'Delete[' + name + ':' + args_name + ']module!' }, function () {
@@ -4468,21 +4511,60 @@ bt.database = {
bt.msg(rdata);
})
},
open_phpmyadmin: function(name, username, password) {
if ($("#toPHPMyAdmin").attr('action').indexOf('phpmyadmin') == -1) {
layer.msg(lan.database.phpmyadmin_err, { icon: 2, shade: [0.3, '#000'] })
setTimeout(function() { window.location.href = '/soft'; }, 3000);
return;
}
var murl = $("#toPHPMyAdmin").attr('action');
$("#pma_username").val(username);
$("#pma_password").val(password);
$("#db").val(name);
layer.msg(lan.database.phpmyadmin, { icon: 16, shade: [0.3, '#000'], time: 1000 });
setTimeout(function() {
$("#toPHPMyAdmin").submit();
}, 200);
},
// open_phpmyadmin: function(name, username, password) {
// if ($("#toPHPMyAdmin").attr('action').indexOf('phpmyadmin') == -1) {
// layer.msg(lan.database.phpmyadmin_err, { icon: 2, shade: [0.3, '#000'] })
// setTimeout(function() { window.location.href = '/soft'; }, 3000);
// return;
// }
// var murl = $("#toPHPMyAdmin").attr('action');
// $("#pma_username").val(username);
// $("#pma_password").val(password);
// $("#db").val(name);
// layer.msg(lan.database.phpmyadmin, { icon: 16, shade: [0.3, '#000'], time: 1000 });
// setTimeout(function() {
// $("#toPHPMyAdmin").submit();
// }, 200);
// },
open_phpmyadmin:function(name,username,password){
if($("#toPHPMyAdmin").attr('action').indexOf('phpmyadmin') == -1){
layer.msg(lan.database.phpmyadmin_err,{icon:2,shade: [0.3, '#000']})
setTimeout(function(){ window.location.href = '/soft'; },3000);
return;
}
layer.open({
type: 1,
title: "Please select how to access phpMyAdmin",
area: '600px',
closeBtn: 2,
shadeClose: false,
content: '<div class="change-default pd20">\
<button class="btn btn-default btn-sm " onclick="bt.database.submit_phpmyadmin(\''+name+'\',\''+username+'\',\''+password+'\',false)">Secure access through panel</button>\
<button style="margin-left: 145px;" class="btn btn-default btn-sm" onclick="bt.database.submit_phpmyadmin(\''+name+'\',\''+username+'\',\''+password+'\',true)">Access by Nginx/Apche/OLS</button>\
<ul class="help-info-text c7 plr20">\
<li>Secure access by panelNo need to install Nginx/Apache. The security authentication is performed by the panel. Need to log in to the panel to access it</li>\
<li>Access by Nginx/Apche/OLSThrough the web server access, phpMyAdmin for security authentication</li>\
<li>If usedSecure access through panelthat some features cannot be used, please try another access method</li>\
</ul>\
</div>'
});
},
submit_phpmyadmin: function(name,username,password,pub){
if(pub === true){
$("#toPHPMyAdmin").attr('action',$("#toPHPMyAdmin").attr('public-data'))
}else{
$("#toPHPMyAdmin").attr('action','/phpmyadmin/index.php')
}
var murl = $("#toPHPMyAdmin").attr('action');
$("#pma_username").val(username);
$("#pma_password").val(password);
$("#db").val(name);
layer.msg(lan.database.phpmyadmin,{icon:16,shade: [0.3, '#000'],time:1000});
setTimeout(function(){
$("#toPHPMyAdmin").submit();
layer.closeAll();
},200);
},
input_sql: function(fileName, dataName) {
bt.confirm({ msg: lan.database.input_confirm, title: lan.database.input_title }, function(index) {
var loading = bt.load(lan.database.input_the);
+69 -27
View File
@@ -106,7 +106,7 @@ var site = {
var opt = '';
var _check = ' onclick="site.site_waf(\'' + item.name + '\')"';
if (bt.os == 'Linux') opt += '<a href="javascript:;" ' + _check + ' class="btlink ">' + lan.site.firewalld + '</a> | ';
//if (bt.os == 'Linux') opt += '<a href="javascript:;" ' + _check + ' class="btlink ">' + lan.site.firewalld + '</a> | ';
opt += '<a href="javascript:;" class="btlink" onclick="site.web_edit(this)">' + lan.site.set + ' </a> | ';
opt += '<a href="javascript:;" class="btlink" onclick="site.del_site(' + item.id + ',\'' + item.name + '\')" title="' + lan.site.del_site + '">' + lan.site.del + '</a>';
return opt;
@@ -232,7 +232,7 @@ var site = {
closeBtn: 2,
shift: 5,
shadeClose: false,
content: "<div class='divtable pd15 style='padding-bottom: 0'><button id='btn_data_backup' class='btn btn-success btn-sm' type='button' style='margin-bottom:10px'>" + lan.database.backup + "</button><table width='100%' id='SiteBackupList' class='table table-hover'></table><div class='page sitebackup_page'></div></div>"
content: "<div class='divtable pd15 style='padding-bottom: 0'><button id='btn_data_backup' class='btn btn-success btn-sm' type='button' style='margin-bottom:10px'>" + lan.database.backup + "</button><table width='100%' id='SiteBackupList' class='table table-hover'></table><ul class='help-info-text c7'><li>Before restoring data, all data in the root dir of the website will be moved to the panel recycle bin.</li></ul><div class='page sitebackup_page'></div></div>"
});
}
setTimeout(function() {
@@ -254,7 +254,8 @@ var site = {
title: lan.site.operate,
align: 'right',
templet: function(item) {
var _opt = '<a class="btlink" href="/download?filename=' + item.filename + '&amp;name=' + item.name + '" target="_blank">' + lan.site.download + '</a> | ';
var _opt = '<a class="btlink restore" site-id="' + id + '" backup-name="' + item.name + '">Restore</a> | ';
_opt += '<a class="btlink" href="/download?filename=' + item.filename + '&amp;name=' + item.name + '" target="_blank">' + lan.site.download + '</a> | ';
_opt += '<a class="btlink" herf="javascrpit:;" onclick="bt.site.del_backup(\'' + item.id + '\',\'' + id + '\',\'' + siteName + '\')">' + lan.site.del + '</a>'
return _opt;
}
@@ -268,10 +269,48 @@ var site = {
if (rdata.status) site.site_detail(id, siteName);
site.get_list();
});
});
$('#SiteBackupList .restore').unbind('click').click(function() {
var data = {};
data.file_name = $(this).attr('backup-name');
data.site_id = $(this).attr('site-id');
layer.confirm('Are you sure to restore backup file?', {
icon: 0,
closeBtn: 2,
title: 'Restore backup file',
}, function (index) {
$.post('/files?action=restore_website', data, function(rdata) {
layer.close(index);
site.backup_output_stop = true;
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
});
site.backup_output_logs();
});
})
}, 100)
});
},
backup_output_stop: false,
//实时显示过程
backup_output_logs: function () {
var layerT = layer.open({
type: 1,
area: '590px',
title: 'Recovering the backup...',
closeBtn: 0,
content: '<div><div><pre class="backup_logs" style="height: 390px;background: #000;color: #fff;margin-bottom: 0;"></pre></div></div>',
});
var show_output = setInterval(function(){
$.post('/files?action=get_progress', function(rdata){
if(site.backup_output_stop) {
layer.close(layerT);
clearInterval(show_output);
}
$('.backup_logs').html(rdata.msg);
$('.backup_logs').scrollTop($('.backup_logs')[0].scrollHeight);
})
}, 1000);
},
add_site: function() {
bt.site.add_site(function(rdata) {
if (rdata.siteStatus) {
@@ -1090,7 +1129,7 @@ var site = {
width: '130px',
items: arrs,
callback: function(obj) {
var spath = '/www/server/panel/rewrite/' + bt.get_cookie('serverType') + '/' + obj.val() + '.conf';
var spath = '/www/server/panel/rewrite/' + (bt.get_cookie('serverType')=='openlitespeed'?'apache':bt.get_cookie('serverType')) + '/' + obj.val() + '.conf';
bt.files.get_file_body(spath, function(sdata) {
$('.dir_config').text(sdata.data);
})
@@ -1258,7 +1297,7 @@ var site = {
]
for (var i = 0; i < dpwds.length; i++) {
var _from_pwd = bt.render_form_line(dpwds[i]);
_div.append("<div class='line'>" + _from_pwd.html + "</div>");
_div.append("<div>" + _from_pwd.html + "</div>");
bt.render_clicks(_from_pwd.clicks);
}
} else {
@@ -1516,22 +1555,26 @@ var site = {
var spath = filename;
if (obj.val() != lan.site.rewritename) spath = '/www/server/panel/rewrite/' + (webserver == 'openlitespeed'?'apache':webserver) + '/' + obj.val() + '.conf';
bt.files.get_file_body(spath, function(ret) {
editor.setValue(ret.data);
aceEditor.ACE.setValue(ret.data);
aceEditor.ACE.moveCursorTo(0, 0);
aceEditor.path = spath;
})
}
}
},
{ items: [{ name: 'config', type: 'textarea', value: rdata.data, widht: '340px', height: '200px' }] },
{ items: [{ name: 'config', type: 'div', value: rdata.data, widht: '340px', height: '200px' }] },
{
items: [{
name: 'btn_save',
text: lan.site.save,
type: 'button',
callback: function(ldata) {
bt.files.set_file_body(filename, editor.getValue(), 'utf-8', function(ret) {
if (ret.status) site.reload(4)
bt.msg(ret);
})
// bt.files.set_file_body(filename, editor.getValue(), 'utf-8', function(ret) {
// if (ret.status) site.reload(4)
// bt.msg(ret);
// })
aceEditor.path = filename;
bt.saveEditor(aceEditor);
}
},
{
@@ -1582,23 +1625,22 @@ var site = {
$('#webedit-con').append(_html);
bt.render_clicks(clicks);
$('textarea.config').attr('id', 'config_rewrite');
var editor = CodeMirror.fromTextArea(document.getElementById("config_rewrite"), {
extraKeys: { "Ctrl-Space": "autocomplete" },
lineNumbers: true,
matchBrackets: true,
});
$(".CodeMirror-scroll").css({ "height": "340px", "margin": 0, "padding": 0 });
$(".soft-man-con .CodeMirror").css({ "height": "342px" });
setTimeout(function() {
editor.refresh();
}, 250);
$('select.rewrite').trigger('change')
// $('textarea.config').attr('id', 'config_rewrite');
// var editor = CodeMirror.fromTextArea(document.getElementById("config_rewrite"), {
// extraKeys: { "Ctrl-Space": "autocomplete" },
// lineNumbers: true,
// matchBrackets: true,
// });
// $(".CodeMirror-scroll").css({ "height": "340px", "margin": 0, "padding": 0 });
// $(".soft-man-con .CodeMirror").css({ "height": "342px" });
// setTimeout(function() {
// editor.refresh();
// }, 250);
$('div.config').attr('id', 'config_rewrite').css({'height':'360px','width':'540px'})
var aceEditor = bt.aceEditor({el:'config_rewrite',content:rdata.data});
$('select.rewrite').trigger('change');
})
},
set_default_index: function(web) {
@@ -2141,7 +2183,7 @@ var site = {
{ field: 'issuer', width: '150px', title: lan.site.brand },
{
field: 'opt',
width: '75px',
width: '100px',
align: 'right',
title: lan.site.operate,
templet: function(item) {
+31 -11
View File
@@ -17,6 +17,7 @@ var soft = {
return;
}
soft.is_install = false;
console.log(type)
bt.soft.get_soft_list(page, type, search, function(rdata) {
if (rdata.pro < 0) {
$("#updata_pro_info").html('');
@@ -285,7 +286,7 @@ var soft = {
}else if (distribution=='centos7'&&item.name == 'php-5.2') {
option = '<span title="\' + lan.soft.ap2_2_not_support + \'">' + lan.soft.not_comp + '</span>';
}else{
if (is_php5) {
if (distribution!='centos7'&&is_php5) {
option = '<span title="\' + lan.soft.ap2_2_not_support + \'">' + lan.soft.not_comp + '</span>';
} else {
if (item.setup && item.task == '1') {
@@ -973,11 +974,30 @@ var soft = {
]
if (data.name == 'phpmyadmin') {
status_list = [status_list[0]];
}else{
var btns = $('<div class="sfm-opt"></div>');
for (var i = 0; i < status_list.length; i++) btns.append('<button class="btn btn-default btn-sm" onclick="bt.pub.set_server_status(\'' + data.name + '\',\'' + status_list[i].opt + '\')">' + status_list[i].title + '</button>');
tabCon.append('<p class="status">' + lan.soft.status + '<span>' + (data.status ? lan.soft.on : lan.soft.off) + '</span><span style="color: ' + (data.status ? '#20a53a;' : 'red;') + ' margin-left: 3px;" class="glyphicon ' + (data.status ? 'glyphicon glyphicon-play' : 'glyphicon-pause') + '"></span></p');
tabCon.append(btns);
}
// var btns = $('<div class="sfm-opt"></div>');
// for (var i = 0; i < status_list.length; i++) btns.append('<button class="btn btn-default btn-sm" onclick="bt.pub.set_server_status(\'' + data.name + '\',\'' + status_list[i].opt + '\')">' + status_list[i].title + '</button>');
// tabCon.append('<p class="status">' + lan.soft.status + '<span>' + (data.status ? lan.soft.running : lan.soft.stop) + '</span><span style="color: ' + (data.status ? '#20a53a;' : 'red;') + ' margin-left: 3px;" class="glyphicon ' + (data.status ? 'glyphicon glyphicon-play' : 'glyphicon-pause') + '"></span></p');
// tabCon.append(btns);
if (data.name == 'phpmyadmin') {
tabCon.append('<div style="padding-top:25px;">\
<div class="info-r "><input type="checkbox" class="status" '+(data.status?'checked':'')+' id="pma_status" name="status" onclick="bt.pub.set_server_status(\'' + data.name + '\',\'' + (data.status?'stop':'start') + '\')" style="vertical-align: top;margin-right: 10px;"><label class="mr20" for="pma_status" style="font-weight:normal;vertical-align: sub;">Enable public access</label></div>\
<p style="margin-top:5px;"><span>Public access address: </span><a class="btlink" href="' + data.ext.url + '" target="_blank">' + data.ext.url + '</a></p>\
</div>');
tabCon.append('<ul class="help-info-text c7 mtb15" style="padding-top:30px">\
<li>PhpMyAdmin enabling public access may have security risks. It is recommended not to enable it unnecessarily!</li>\
<li>The current version of phpmyadin no longer relies on Nginx / Apache without requiring public access.</li>\
<li>The service state of phpMyAdmin does not affect access to phpMyAdmin through the panel (non-public).</li>\
<li>If the public access right is not turned on, the panel will take over the access right, that is, you need to log in to the panel to access.</li>\
</ul>');
}
var btns = $('<div class="sfm-opt"></div>');
for (var i = 0; i < status_list.length; i++) btns.append('<button class="btn btn-default btn-sm" onclick="bt.pub.set_server_status(\'' + data.name + '\',\'' + status_list[i].opt + '\')">' + status_list[i].title + '</button>');
tabCon.append('<p class="status">' + lan.soft.status + '<span>' + (data.status ? lan.soft.running : lan.soft.stop) + '</span><span style="color: ' + (data.status ? '#20a53a;' : 'red;') + ' margin-left: 3px;" class="glyphicon ' + (data.status ? 'glyphicon glyphicon-play' : 'glyphicon-pause') + '"></span></p');
tabCon.append(btns);
var help = '<ul class="help-info-text c7 mtb15" style="padding-top:30px"><li>' + lan.soft.mysql_mem_err + '</li></ul>';
if (name == 'mysqld') tabCon.append(help);
@@ -1920,16 +1940,16 @@ var soft = {
<button class="btn btn-success btn-sm ssl_port_button" >Save</button>\
</div>\
<div class="user_pw_tit">\
<span class="tit">' + lan.soft.pma_pass + '</span>\
<span class="tit" style="width: 160px;padding-right: 20px;">' + lan.soft.pma_pass + '</span>\
<span class="btswitch-p"><input class="btswitch btswitch-ios" id="phpmyadminsafe" type="checkbox" ' + (sdata.ext.auth ? 'checked' : '') + '>\
<label class="btswitch-btn phpmyadmin-btn phpmyadmin_safe" for="phpmyadminsafe" ></label>\
</span>\
</div>\
<div class="user_pw">\
<p><span>' + lan.soft.pma_user + '</span><input id="username_get" class="bt-input-text" name="username_get" value="" type="text" placeholder="' + lan.soft.edit_empty + '"></p>\
<p><span>' + lan.soft.pma_pass1 + '</span><input id="password_get_1" class="bt-input-text" name="password_get_1" value="" type="password" placeholder="' + lan.soft.edit_empty + '"></p>\
<p><span>' + lan.soft.pma_pass2 + '</span><input id="password_get_2" class="bt-input-text" name="password_get_1" value="" type="password" placeholder="' + lan.soft.edit_empty + '"></p>\
<p><button class="btn btn-success btn-sm phpmyadmin_safe_save" >' + lan.public.save + '</button></p>\
<div class="user_pw" style="margin-top:5px;">\
<p><span style="width: 160px;padding-right: 20px;">' + lan.soft.pma_user + '</span><input id="username_get" class="bt-input-text" name="username_get" value="" type="text" placeholder="' + lan.soft.edit_empty + '"></p>\
<p><span style="width: 160px;padding-right: 20px;">' + lan.soft.pma_pass1 + '</span><input id="password_get_1" class="bt-input-text" name="password_get_1" value="" type="password" placeholder="' + lan.soft.edit_empty + '"></p>\
<p><span style="width: 160px;padding-right: 20px;">' + lan.soft.pma_pass2 + '</span><input id="password_get_2" class="bt-input-text" name="password_get_1" value="" type="password" placeholder="' + lan.soft.edit_empty + '"></p>\
<p><button class="btn btn-success btn-sm phpmyadmin_safe_save" style="margin-left:160px;">' + lan.public.save + '</button></p>\
</div>\
<ul class="help-info-text c7"><li>' + lan.soft.pma_ps + '</li></ul>';
+8 -1
View File
@@ -1,6 +1,7 @@
{
"TYPE_SITE":"Site manager",
"TYPE_LOGIN":"Login",
"TYPE_LOGOUT":"Logout",
"TYPE_DATABASE":"Database manager",
"TYPE_FTP":"FTP manager",
"TYPE_FILE":"File manager",
@@ -140,5 +141,11 @@
"SITE_STOP_ANTI_STEALING_LINK": "Hotlink Protection for site [{1}] disabled!",
"SITE_START_ANTI_STEALING_LINK": "Hotlink Protection for site [{1}] enabled!",
"SET_PHP_CLI":"Set the PHP-CLI version to: {1}",
"SET_BASICAUTH_STATUS":"Set the BasicAuth status to: {1}"
"SET_BASICAUTH_STATUS":"Set the BasicAuth status to: {1}",
"MANUALLY_LOGOUT": "Client: {1}, has manually exited the panel",
"GET_INFO": "Get Info",
"NGINX_LOAD_ERR": "Nginx load status acquisition failed:{1}",
"GET_DATA_ERR": "Data acquisition failed!",
"PHP_LOAD_ERR": "PHP load status acquisition failed: {1}"
}
+128 -1
View File
@@ -828,9 +828,136 @@
"NOT_INSTALL_MAIL_SERVER":"Have not installed the mail server before, please feel free to install",
"MAIL_SERVER_EXIST":"The mail server already exists, please uninstall before reinstalling",
"VERIFICATION_FAILED":"Verification failed",
"PLUGIN_INPUT_ERR": "Please pass in the plug-in name!",
"INSTALL_USER_EMPTY": "The user name cannot be empty!",
"INIT_CSRF_ERR": "CSRF calibration failed, please login again",
"EXPIRED_TOKEN":"Expired Token",
"INIT_ACCEPT_NOT": "Overstepping one's authority!",
"UNSAFE_PATH":"Unsafe path",
"PHP_NOT_INSTALL":"PHP is not installed",
"SPECIFIED_PHP_NOT_INSTALL":"Specified PHP version not installed",
"CLEARED":"Cleared!"
"CLEARED":"Cleared!",
"INIT_DEBUG_INFO": "The above debugging information is displayed only in developer mode",
"INIT_VERSION_LAST": "Version : ",
"SSH_ERROR": "Panel session lost, please login panel again!",
"SSH_INFO_ERROR": "fail to get!",
"INIT_RELOAD_ERR": "Reload failed!",
"INIT_REQUEST_CHECK_LOCAL_ERR": "This feature cannot be used in offline mode!",
"CLOSED": "Closed",
"OPENED": "Has been open",
"NEVER_EXPIRES":"Never Expires",
"WRONG_PASSWD": "Wrong password!",
"V_CODE_LEN_ERR": "Verification code length is wrong!",
"EXTRA_PARAMETER": "You cannot have extra parameters",
"ACME_MSG_ERR": "Service shutdown or internal error due to maintenance, check <a href=\"https://letsencrypt.status.io/\" target=\"_blank\" class=\"btlink\">https://letsencrypt.status.io/</a> see for more details. ",
"ACME_ACCOUNT_ERR": "The specified account does not exist",
"ACME_SUCCESS_ACCOUNT_SETUP": "Account setup succeeded!",
"ACME_ORDER_NOT_EXIST": "The specified order does not exist!",
"ACME_DEL_ODER_SUCCESS": "Order deleted successfully!",
"ACME_GET_CERT_ERR": "Certificate read failed, directory does not exist!",
"ACME_CERT_ERR": "No certificate found for the specified order!",
"ACME_CERT_READ_ERR": "Certificate read failed!",
"ACME_DOMAIN_ERR": "Need at least a domain name!",
"ACME_AUTH_ERR": "Failed to obtain authorization: {1}",
"ACME_INVALID_ORDER": "Invalid order, this order is currently in a verification failure state!",
"ACME_V_INFO_ERR": "Validation information construction failed!",
"ACME_V_DIR": "|-Verify the dir{1}",
"ACME_WRITE_V_FILE_ERR": "Writing verification file failed: {1}",
"ACME_DNS_API_ERR": "No valid DNSAPI key information found",
"ACME_QUERY_V_RESULT": "|-{1} Query verification results..",
"ACME_V_TIMES": "Error: Attempted verification {1} times. The maximum number of verifications is {2}. The verification interval is {3} seconds.",
"ACME_V_SUCCESS": "|-Verification succeeded!",
"ACME_ERR_MSG1": "CA cannot verify your domain name, please check if the domain name resolution is correct, or wait 5-10 minutes and try again.",
"ACME_ERR_MSG2": "CA server connection timed out, please try again later.",
"ACME_ERR_MSG3": "The domain name does not belong to this DNS service provider, please make sure the domain name is filled in correctly.",
"ACME_ERR_MSG4": "DNS server connection failed, please check if the key is correct.",
"ACME_ERR_MSG5": "Data validation failed and the CA was unable to get the correct captcha from the authenticated connection.",
"ACME_ERR_MSG6": "Issuing failed, the domain {1} has exceeded the limit of weekly reissues!",
"ACME_ERR_MSG7": "Issuing failed, the current server IP has reached the limit of creating up to 10 accounts every 3 hours.",
"ACME_ERR_MSG8": "Validation failed, domain name was not resolved, or resolution did not take effect!",
"ACME_ERR_MSG9": "Verification failed, domain name resolution error or verification URL cannot be accessed!",
"ACME_ERR_MSG10": "Connection to CA server failed, please try again later.",
"ACME_ERR_MSG11": "Unsupported domain name {1}, please check the domain name is correct!",
"ACME_ERR_MSG12": "No resolution record was found for domain name {1}, please check if the domain name resolution takes effect!",
"ACME_ERR_MSG13": "No valid TXT resolution record was found in the domain name {1}, please check whether the TXT record is parsed correctly. If it is applied by DNSAPI, please try again in 10 minutes!",
"ACME_ERR_MSG14": "A wrong TXT record was found on {1}: {2}, please check whether the TXT resolution is correct, if it is applied by DNSAPI, please try again in 10 minutes!",
"ACME_ERR_MSG15": "This domain name does not exist under this dnspod account, adding resolution failed!",
"ACME_ERR_MSG16": "No valid TXT resolution record was found in the domain name {1}, please check whether the TXT record is parsed correctly. If it is applied by DNSAPI, please try again in 10 minutes!",
"ACME_ERR_MSG17": "The connection timed out and the CA server was unable to access your website!",
"ACME_ERR_MSG18": "Domain name {1} is currently required to verify the CAA record, please parse the CAA record manually, or retry the application after 1 hour!",
"ACME_ERR_MSG19": "The verification timed out. Please check if the domain name is resolved correctly. If it is resolved correctly, the connection between the server and Let'sEncrypt may be abnormal. Please try again later!",
"ACME_ERR_MSG20": "Cannot issue a certificate for {1}, cannot apply for a wildcard certificate with a domain name suffix directly!",
"ACME_ERR_MSG21": "The account has more than 5 failed orders within 1 hour, please wait 1 hour and try again!",
"ACME_ERR_MSG22": "Order creation failed, please try again later!",
"ACME_ERR_MSG23": "More than 5 verification failures in 1 hour, the application is temporarily banned, please try again later!",
"ACME_ERR_MSG24": "CA server denied access, please try again later!",
"ACME_ERR_MSG25": "The DNS of the server is faulty and the domain name cannot be resolved. Please use the Linux toolbox to check the DNS configuration",
"ACME_ERR_MSG26": "Too many requests for this domain name. Please try again 3 hours later",
"ACME_SEND_CSR_ERR": "Error: Sending CSR: Response status {1} Response value: {2}",
"ACME_CERT_DOWNLOAD_ERR": "Failed to download certificate: {1}",
"ACME_CERT_REPLACE": "|-Detected that the certificate under {1} overlaps with the certificate of this application and has an earlier expiration time, and has been replaced with a new certificate!",
"ACME_CERT_FILE_ERR": "The specified certificate file does not exist!",
"ACME_CERT_GET_CERTINFO_ERR": "Certificate information acquisition failed!",
"ACME_CHECK_DNS": "|-Attempt to verify DNS records locally, domain name: {1}, type: {2} record value: {3}",
"ACME_CHECK_DNS1": "|-Number of verifications: {1}, value: {2}",
"ACME_CHECK_DNS2": "|-Local authentication succeeded!",
"ACME_CHECK_DNS3": "|-Local authentication failed!",
"ACME_BUILD_AUTH": "|-Verification type: {1}",
"ACME_REGISTERED_ERR": "Registration for ACME account failed: {1}",
"ACME_CREAT_ORDER": "|-Creating order..",
"ACME_GET_V": "|-Getting verification information..",
"ACME_V_DOMAIN": "|-Verifying domain name..",
"ACME_SEND_CSR": "|-Sending CSR..",
"ACME_DOWNLOAD_CERT": "|-Downloading certificate..",
"ACME_APPLY_SUCCESS": "Application successful!",
"ACME_APPLY_SUCCESS1": "|-Successful application, deploying to site..",
"ACME_DIR_ERR": "Invalid site directory, please check if the specified site exists!",
"ACME_PAN_DOMAIN_ERR": "Pan domain names cannot apply for a certificate using [File Verification]!",
"ACME_PROXY_ERR": "Sites with reverse proxy turned on cannot apply for SSL!",
"ACME_FORCE_SSL_ERR": "After configuring Force HTTPS, you cannot use [File Verification] to apply for a certificate!",
"ACME_DNS_ERR": "Please go to the software store to install [cloud analysis], and complete the domain name NS binding.",
"ACME_RENEW_ERR": "The specified order number does not exist and cannot be renewed!",
"ACME_NO_NEED_RENEW": "|-No SSL certificate found within 30 days!",
"ACME_NEED_RENEW": "|-A total of {1} certificates need to be renewed",
"ACME_RENEWING": "|-Renewing certificate number of {1}domain: {2}..",
"ACME_RENEW_SUCCESS": "Renewed successfully!",
"ACME_RENEW_SUCCESS1": "|-Renewed successfully!",
"ACME_USE_TIPS": "Required parameters: --domain list of domain names, multiple separated by commas!",
"ACME_USE_TIPS1": "Please specify the domain name to apply for a certificate",
"ACME_USE_TIPS2": "Please specify verification type",
"ACME_USE_TIPS3": "Please specify the website root directory",
"ACME_USE_TIPS4": "Please specify DNSAPI",
"ACME_USE_TIPS5": "Please specify DNSAPI key",
"ACME_USE_TIPS6": "Please specify DNSAPI secret",
"ACME_USE_TIPS7": "Specify the order index",
"ACME_USE_TIPS8": "renew certificate",
"ACME_USE_TIPS9": "Revoke certificate",
"ACME_USE_TIPS10": "Please enter the index of the order to be revoked in the --index parameter",
"ACME_USE_TIPS11": "Please specify the domain name for which you want to apply for a certificate in the --domain parameter, multiple separated by commas (,)",
"ACME_USE_TIPS12": "Please specify the correct authentication type in the --type parameter, supporting dns and http",
"ACME_USE_TIPS13": "Please specify the website root directory in the --path parameter!",
"ACME_USE_TIPS14": "The specified site root does not exist, please check: {1}",
"ACME_USE_TIPS15": "When applying using dnsapi, specify the dnsapi key in the --dns_key parameter!",
"ACME_USE_TIPS16": "When applying using dnsapi, specify the secret of dnsapi in the --dns_secret parameter!",
"ACME_USE_TIPS17": "|-Manual order submission is successful, please resolve DNS records according to the following tips: ",
"ACME_USE_TIPS18": "|-Order index: {1}",
"ACME_USE_TIPS19": "|-Retry the command",
"ACME_USE_TIPS20": "|-A total of \\033[36m{1}\\033[0m domain name records need to be resolved.",
"ACME_USE_TIPS21": "|-The \\033[36m{1}\\033[0m domain names are: {2}, please resolve the following information: ",
"ACME_USE_TIPS22": "|-Record Type: TXT Record Name: \\033[41m{1}\\033[0m Record Value: \\033[41m{2}\\033 [0m [Required]",
"ACME_USE_TIPS23": "|-Record type: CAA Record name: \\033[41m{1}\\033[0m Record value: \\033[41m{2}\\033[0m [Optional]",
"ACME_USE_TIPS24": "Please wait 2-3 minutes after completing the resolution and enter Y and press Enter to continue verifying the domain name: ",
"ACME_USE_TIPS25": "|-The user abandons the application and exits the program!",
"ACME_USE_TIPS26": "|-Certificate obtained successfully!",
"ACME_USE_TIPS27": "Certified Domain Name: {1}",
"ACME_USE_TIPS28": "Certificate expiration time: {1}",
"ACME_USE_TIPS29": "Certificate saved at: {}/",
"PHP_LOAD_ERR1": "PHP load status acquisition failed!",
"NOT_SUPPORT_OLS": "The current web server is openlitespeed. This function is not supported yet.",
"PHPMYADMIN_SSL_ERR": "Did not find the {1} configuration file, please try to close the ssl port settings before opening",
"PHPMYADMIN_SSL_ERR1": "Did not find the apache phpmyadmin ssl configuration file, please try to close the ssl port settings before opening",
"PHPMYADMIN_SSL_ERR2": "Did not detect the port that nginx phpmyadmin listens, please confirm whether the file has been manually modified.",
"PHPMYADMIN_SSL_ERR3": "Did not detect the port that apache phpmyadmin listens, please confirm whether the file has been manually modified.",
"PHPMYADMIN_SSL_ERR4": "The panel certificate does not exist. Please apply for the panel certificate and try again.",
"PHPMYADMIN_SSL_ERR5": "Open successfully, please manually release phpmyadmin ssl port"
}
@@ -18,6 +18,7 @@
"N1":"Site",
"N2":"DB",
"N3":"FTP",
"N4":"Security",
"T1":"Server Status",
"T2":"Connection Manager",
"T3":"Task Manager",
+5 -5
View File
@@ -42,7 +42,7 @@
<div class="time">
<span class="bt">{{data['lan']['G1']}}<input class="btime" type="text" value="2017/1/10 00:00:00"></span>
<span class="et">{{data['lan']['G2']}}<input class="etime" type="text" value="2017/1/13 00:00:00"></span>
<div class="sbtn loadbtn">{{data['lan']['G3']}}</div>
<div class="sbtn time_range_submit" data-type="getload">{{data['lan']['G3']}}</div>
</div>
</div>
</div>
@@ -63,7 +63,7 @@
<div class="time">
<span class="bt">{{data['lan']['G1']}}<input class="btime" type="text" value="2017/1/10 00:00:00"></span>
<span class="et">{{data['lan']['G2']}}<input class="etime" type="text" value="2017/1/13 00:00:00"></span>
<div class="sbtn cpubtn">{{data['lan']['G3']}}</div>
<div class="sbtn time_range_submit" data-type="cpu">{{data['lan']['G3']}}</div>
</div>
</div>
</div>
@@ -84,7 +84,7 @@
<div class="time">
<span class="bt">{{data['lan']['G1']}}<input class="btime" type="text" value="2017/1/10 00:00:00"></span>
<span class="et">{{data['lan']['G2']}}<input class="etime" type="text" value="2017/1/13 00:00:00"></span>
<div class="sbtn membtn">{{data['lan']['G3']}}</div>
<div class="sbtn time_range_submit" data-type="mem">{{data['lan']['G3']}}</div>
</div>
</div>
</div>
@@ -105,7 +105,7 @@
<div class="time">
<span class="bt">{{data['lan']['G1']}}<input class="btime" type="text" value="2017/1/10 00:00:00"></span>
<span class="et">{{data['lan']['G2']}}<input class="etime" type="text" value="2017/1/13 00:00:00"></span>
<div class="sbtn diskbtn">{{data['lan']['G3']}}</div>
<div class="sbtn time_range_submit" data-type="disk">{{data['lan']['G3']}}</div>
</div>
</div>
</div>
@@ -126,7 +126,7 @@
<div class="time">
<span class="bt">{{data['lan']['G1']}}<input class="btime" type="text" value="2017/1/10 00:00:00"></span>
<span class="et">{{data['lan']['G2']}}<input class="etime" type="text" value="2017/1/13 00:00:00"></span>
<div class="sbtn networkbtn">{{data['lan']['G3']}}</div>
<div class="sbtn time_range_submit" data-type="network">{{data['lan']['G3']}}</div>
</div>
</div>
</div>
+6 -2
View File
@@ -16,10 +16,14 @@
</div>
</div>
<div class="safe bgw mtb15 pd15">
<div class="info-title-tips">
<p><span class="glyphicon glyphicon-alert" style="color: #f39c12; margin-right: 10px;"></span> After adding the database, be sure to <a class="btlink" href="/crontab">[Cron]</a> page adds scheduled backup tasks to ensure your data security.</p>
</div>
<button onclick="database.add_database()" title="{{data['lan']['BTNT1']}}" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">{{data['lan']['BTN1']}}</button>
<button onclick="bt.database.set_root()" title="{{data['lan']['BTNT2']}}" class="btn btn-default btn-sm" type="button" style="margin-right: 5px;">{{data['lan']['BTN2']}}</button>
<button onclick="bt.database.open_phpmyadmin('','root','{{data['mysql_root']}}')" title="{{data['lan']['BTNT3']}}" class="btn btn-default btn-sm" type="button" style="margin-right: 5px;">{{data['lan']['BTN3']}}</button>
<span style="float:right">
<a href="/adminer/index.php" target="_blank" title="Lightweight database management tool that supports remote database management" class="btn btn-default btn-sm" type="button" style="margin-right: 5px;">Adminer</a>
<span style="float:right">
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="database.batch_database('del');" title="{{data['lan']['BTNT4']}}" class="btn btn-default btn-sm">{{data['lan']['BTN4']}}</button>
<button onclick="bt.recycle_bin.open_recycle_bin(6)" id="dataRecycle" title="{{data['lan']['BTNT4']}}" class="btn btn-default btn-sm" style="margin-left: 5px;"><span class="glyphicon glyphicon-trash" style="margin-right: 5px;"></span>{{data['lan']['RECYCLE_BIN']}}</button>
</span>
@@ -37,7 +41,7 @@
</div>
</div>
</div>
<form id="toPHPMyAdmin" action="{{session['phpmyadminDir']}}/index.php" method="post" style="display: none;" target="_blank">
<form id="toPHPMyAdmin" public-data="{{session['phpmyadminDir']}}/index.php" action="/phpmyadmin/index.php" method="post" style="display: none;" target="_blank">
<input type="text" name="pma_username" id="pma_username" value="" />
<input type="password" name="pma_password" id="pma_password" value="" />
<input type="text" name="server" value="1" />
+9 -4
View File
@@ -82,18 +82,22 @@
</div>
<div class="system-info-con mtb20">
<ul class="clearfix text-center">
<li class="sys-li-box col-xs-3 col-sm-3 col-md-3 col-lg-3">
<li class="sys-li-box ccol-xs-4 col-sm-4 col-md-4 col-lg-3">
<p class="name f15 c9">{{data['lan']['N1']}}</p>
<div class="val"><a class="btlink" href="/site">{{data['siteCount']}}</a></div>
</li>
<li class="sys-li-box col-xs-3 col-sm-3 col-md-3 col-lg-3">
<li class="sys-li-box ccol-xs-4 col-sm-4 col-md-4 col-lg-3">
<p class="name f15 c9">{{data['lan']['N3']}}</p>
<div class="val"><a class="btlink" href="/ftp">{{data['ftpCount']}}</a></div>
</li>
<li class="sys-li-box col-xs-3 col-sm-3 col-md-3 col-lg-3">
<li class="sys-li-box ccol-xs-4 col-sm-4 col-md-4 col-lg-3">
<p class="name f15 c9">{{data['lan']['N2']}}</p>
<div class="val"><a class="btlink" href="/database">{{data['databaseCount']}}</a></div>
</li>
<li class="sys-li-box ccol-xs-4 col-sm-4 col-md-4 col-lg-3">
<p class="name f15 c9">{{data['lan']['N4']}}</p>
<div class="val"><a class="btlink warning_num" href="javascript:;" onclick="index.reader_warning_view()">0</a></div>
</li>
</ul>
</div>
</div>
@@ -148,7 +152,8 @@
},2000);
});
});
})
});
bt.set_cookie('serverType', "{{session['webserver']}}");
</script>
{% endblock %}
+56 -14
View File
@@ -8,9 +8,9 @@
<meta name="renderer" content="webkit">
<title>{{g.title}}</title>
<link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon" />
<link href="/static/bootstrap-3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<link href="/static/css/site.css?20191127={{g['version']}}" rel="stylesheet" />
<link href="/static/codemirror/lib/codemirror.css?20191127={{g['version']}}" rel="stylesheet" />
<link href="{{g.cdn_url}}/bootstrap-3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<link href="{{g.cdn_url}}/css/site.css?20191127={{g['version']}}" rel="stylesheet" />
<link href="{{g.cdn_url}}/codemirror/lib/codemirror.css?20191127={{g['version']}}" rel="stylesheet" />
<!--[if lte IE 9]>
<script src="/static/js/requestAnimationFrame.js"></script>
<![endif]-->
@@ -68,6 +68,7 @@
<body>
<div class="bt-warp bge6">
<div class="top-tips">The current version of IE browser is too low, some functions cannot be displayed, please change to other browsers!</div>
<a style="display:none;" id="panel_debug" data="{{g['debug']}}"></a>
<a style="display:none;" id="request_token_head" token="{{session['request_token_head']}}"></a>
<div id="container" class="container-fluid">
<div class="sidebar-scroll">
@@ -89,23 +90,64 @@
<a style="display: none;" id="defaultPath">{{session['config']['sites_path']}}</a> {% block content %}{% endblock %}
<div class="footer bgw">{{session['brand']}}{{session['product']}} &copy;2014-{{session['yaer']}} {{session['brand']}} (bt.cn)
<!--<a style="margin-left:20px;color:#20a53a;" href="http://www.bt.cn/bbs" target="_blank">求助|建议请上宝塔论坛</a>-->
<a style="margin-left:20px;color:#20a53a;" href="https://forum.aapanel.com" target="_blank">{{session['bt_help']}}</a>
<a style="margin-left:20px;color:#20a53a;" href="http://forum.aapanel.com" target="_blank">{{session['bt_help']}}</a>
<a style="margin-left:20px;color:#20a53a;" href="https://doc.aapanel.com/web/#/3?page_id=117" target="_blank">User manual</a>
</div>
</div>
<script src="/static/js/jquery-1.10.2.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/language/{{session['lan']}}/lan.js?date={{g['version']}}"></script>
<script src="/static/layer/layer.js?date={{g['version']}}"></script>
<script src="/static/js/public.js?version={{g['version']}}"></script>
<script src="/static/js/public_backup.js?version={{g['version']}}"></script>
<script src="/static/codemirror/lib/codemirror.js?version={{g['version']}}"></script>
<script src="{{g.cdn_url}}/js/aes.js?version={{g['version']}}"></script>
<script src="{{g.cdn_url}}/js/jquery-1.10.2.min.js"></script>
<script src="{{g.cdn_url}}/js/bootstrap.min.js"></script>
<script src="{{g.cdn_url}}/language/{{session['lan']}}/lan.js?date={{g['version']}}"></script>
<script src="{{g.cdn_url}}/layer/layer.js?date={{g['version']}}"></script>
<script src="{{g.cdn_url}}/js/public.js?version={{g['version']}}"></script>
<script src="{{g.cdn_url}}/js/public_backup.js?version={{g['version']}}"></script>
<script src="{{g.cdn_url}}/codemirror/lib/codemirror.js?version={{g['version']}}"></script>
{% block scripts %}{% endblock %}
<script type="text/javascript">
if (navigator.appName == "Microsoft Internet Explorer" && parseInt(navigator.appVersion.split(";")[1].replace(/[ ]/g, "").replace("MSIE", "")) < 9) {
$(".main-content").css("margin-top", "50px");
$('.top-tips').show();
}
function IEVersion() {
// 取得浏览器的userAgent字符串
var userAgent = navigator.userAgent;
// 判断是否为小于IE11的浏览器
var isLessIE11 = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1;
// 判断是否为IE的Edge浏览器
var isEdge = userAgent.indexOf('Edge') > -1 && !isLessIE11;
// 判断是否为IE11浏览器
var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1;
if (isLessIE11) {
var IEReg = new RegExp('MSIE (\\d+\\.\\d+);');
// 正则表达式匹配浏览器的userAgent字符串中MSIE后的数字部分,,这一步不可省略!!!
IEReg.test(userAgent);
// 取正则表达式中第一个小括号里匹配到的值
var IEVersionNum = parseFloat(RegExp['$1']);
if (IEVersionNum === 7) {// IE7
return 7
} else if (IEVersionNum === 8) {// IE8
return 8
} else if (IEVersionNum === 9) {// IE9
return 9
} else if (IEVersionNum === 10) { // IE10
return 10
} else {// IE版本<7
return 6
}
} else if (isEdge) { // edge
return 'edge'
} else if (isIE11) {// IE11
return 11
} else {// 不是ie浏览器
return -1
}
}
if(IEVersion() != -1 && IEVersion() < 9){
$(".main-content").css("margin-top", "50px");
$('.top-tips').show();
}
$("#setBox").click(function() {
if ($(this).prop("checked")) {
$("input[name=id]").prop("checked", true);
@@ -223,9 +265,9 @@
});
}
loadScript([
'/static/laydate/laydate.js',
'/static/js/jquery.qrcode.min.js',
'/static/js/clipboard.min.js'
'{{g.cdn_url}}/laydate/laydate.js',
'{{g.cdn_url}}/js/jquery.qrcode.min.js',
'{{g.cdn_url}}/js/clipboard.min.js'
], function(e) {
});
+119 -133
View File
@@ -86,7 +86,7 @@ class acme_v2:
result = res.json()
if "type" in result:
if result['type'] == 'urn:acme:error:serverInternal':
raise Exception('Service shut down or internal error due to maintenance, check <a href="https://letsencrypt.status.io/" target="_blank" class="btlink">https://letsencrypt.status.io/</a> see for more details. ')
raise Exception(public.getMsg('ACME_MSG_ERR'))
raise Exception(res.content)
s_body = res.json()
self._apis = {}
@@ -123,14 +123,14 @@ class acme_v2:
# 设置帐户信息
def set_account_info(self, args):
if not 'account' in self._config:
return public.returnMsg(False, 'The specified account does not exist')
return public.returnMsg(False, 'ACME_ACCOUNT_ERR')
account = json.loads(args.account)
if 'email' in account:
self._config['email'] = account['email']
del(account['email'])
self._config['account'][self._mod_index[self._debug]] = account
self.save_config()
return public.returnMsg(True, 'Account setup succeeded!')
return public.returnMsg(True, 'ACME_SUCCESS_ACCOUNT_SETUP')
# 获取订单列表
def get_orders(self, args):
@@ -146,19 +146,19 @@ class acme_v2:
# 删除订单
def remove_order(self, args):
if not 'orders' in self._config:
return public.returnMsg(False, 'The specified order does not exist!')
return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST')
if not args.index in self._config['orders']:
return public.returnMsg(False, 'The specified order does not exist!')
return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST')
del(self._config['orders'][args.index])
self.save_config()
return public.returnMsg(True, 'Order deleted successfully!')
return public.returnMsg(True, 'ACME_DEL_ODER_SUCCESS')
# 取指定订单数据
def get_order_find(self, args):
if not 'orders' in self._config:
return public.returnMsg(False, 'The specified order does not exist!')
return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST')
if not args.index in self._config['orders']:
return public.returnMsg(False, 'The specified order does not exist!')
return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST')
result = self._config['orders'][args.index]
result['cert'] = self.get_cert_info(args.index)
return result
@@ -179,7 +179,7 @@ class acme_v2:
if not os.path.exists(path): # 尝试重新下载证书
self.download_cert(args.index)
if not os.path.exists(path):
return public.returnMsg(False, 'Certificate read failed, directory does not exist!')
return public.returnMsg(False, 'ACME_GET_CERT_ERR')
import panelTask
bt_task = panelTask.bt_task()
zip_file = path+'/cert.zip'
@@ -193,13 +193,13 @@ class acme_v2:
if type(index) != str:
index = index.index
if not index in self._config['orders']:
raise Exception("The specified order does not exist!")
raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST'))
cert_path = self._config['orders'][index]['save_path']
if not os.path.exists(cert_path):
raise Exception("No certificate found for the specified order!")
raise Exception(public.getMsg('ACME_CERT_ERR'))
cert = self.dump_der(cert_path)
if not cert:
raise Exception("Certificate read failed!")
raise Exception(public.getMsg('ACME_CERT_READ_ERR'))
payload = {
"certificate": self.calculate_safe_base64(cert),
"reason": 4
@@ -286,7 +286,7 @@ class acme_v2:
def create_order(self, domains, auth_type, auth_to, index=None):
domains = self.format_domains(domains)
if not domains:
raise Exception("Need at least a domain name")
raise Exception(public.getMsg('ACME_DOMAIN_ERR'))
# 构造标识
identifiers = []
for domain_name in domains:
@@ -331,7 +331,7 @@ class acme_v2:
# 获取验证信息
def get_auths(self, index):
if not index in self._config['orders']:
raise Exception('The specified order does not exist!')
raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST'))
# 检查是否已经获取过授权信息
if 'auths' in self._config['orders'][index]:
@@ -346,19 +346,19 @@ class acme_v2:
for auth_url in self._config['orders'][index]['authorizations']:
res = self.acme_request(auth_url, "")
if res.status_code not in [200, 201]:
raise Exception("Failed to obtain authorization: {}".format(res.json()))
raise Exception("ACEM_AUTH_ERR",(res.json(),))
s_body = res.json()
if 'status' in s_body:
if s_body['status'] in ['invalid']:
raise Exception("Invalid order, this order is currently in a verification failure state!")
raise Exception('ACME_INVALID_ORDER')
if s_body['status'] in ['valid']: # 跳过无需验证的域名
continue
s_body['expires'] = self.utc_to_time(s_body['expires'])
identifier_auth = self.get_identifier_auth(index, auth_url, s_body)
if not identifier_auth:
raise Exception("Validation information construction failed!{}")
raise Exception('ACME_V_INFO_ERR')
acme_keyauthorization, auth_value = self.get_keyauthorization(
identifier_auth['token'])
@@ -400,9 +400,9 @@ class acme_v2:
if not self._config['orders'][index]['auth_type'] in ['http','tls']:
return True
acme_path = '{}/.well-known/acme-challenge'.format(self._config['orders'][index]['auth_to'])
write_log("|-Verify the dir{}".format(acme_path))
if not os.path.exists(acme_path): return True
public.ExecShell("rm -f {}/*".format(acme_path))
write_log(public.getMsg('ACME_V_DIR',(acme_path,)))
if os.path.exists(acme_path):
public.ExecShell("rm -f {}/*".format(acme_path))
acme_path = '/www/server/stop/.well-known/acme-challenge'
if os.path.exists(acme_path):
public.ExecShell("rm -f {}/*".format(acme_path))
@@ -429,7 +429,7 @@ class acme_v2:
except:
err = public.get_error_info()
print(err)
raise Exception("Writing verification file failed: {}".format(err))
raise Exception(public.getMsg('ACME_WRITE_V_FILE_ERR',(err,)))
# 解析域名
def create_dns_record(self, auth_to, domain, dns_value):
@@ -460,7 +460,7 @@ class acme_v2:
key = dc['data'][0]['value']
secret = dc['data'][1]['value']
except:
raise Exception("No valid DNSAPI key information found")
raise Exception(public.getMsg('ACME_DNS_API_ERR'))
else:
key = tmp[1]
secret = tmp[2]
@@ -479,7 +479,7 @@ class acme_v2:
# 验证域名
def auth_domain(self, index):
if not index in self._config['orders']:
raise Exception('The specified order does not exist!')
raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST'))
# 开始验证
for auth in self._config['orders'][index]['auths']:
@@ -506,7 +506,7 @@ class acme_v2:
number_of_checks = 0
while True:
if desired_status == ['valid', 'invalid']:
write_log("|-{} Query verification results..".format(number_of_checks + 1))
write_log(public.getMsg('ACME_QUERY_V_RESULT',(number_of_checks + 1,)))
time.sleep(self._wait_time)
check_authorization_status_response = self.acme_request(url, "")
a_auth = check_authorization_status_response.json()
@@ -537,68 +537,69 @@ class acme_v2:
if number_of_checks == self._max_check_num:
raise StopIteration(
"Error: Attempted verification {0} times. The maximum number of verifications is {1}. The verification interval is {2} seconds.".format(
public.getMsg('ACME_V_TIMES',(
number_of_checks,
self._max_check_num,
self._wait_time
)
)
)))
if desired_status == ['valid', 'invalid']:
write_log("|-Verification succeeded!")
write_log('ACME_V_SUCCESS')
return check_authorization_status_response
# 格式化错误输出
def get_error(self, error):
if error.find("Max checks allowed") >= 0:
return "CA cannot verify your domain name, please check if the domain name resolution is correct, or wait 5-10 minutes and try again."
return public.getMsg('ACME_ERR_MSG1')
elif error.find("Max retries exceeded with") >= 0 or error.find('status_code=0 ') != -1:
return "CA server connection timed out, please try again later."
return public.getMsg('ACME_ERR_MSG2')
elif error.find("The domain name belongs") >= 0:
return "The domain name does not belong to this DNS service provider, please make sure the domain name is filled in correctly."
return public.getMsg('ACME_ERR_MSG3')
elif error.find('login token ID is invalid') >= 0:
return 'DNS server connection failed, please check if the key is correct.'
return public.getMsg('ACME_ERR_MSG4')
elif error.find('Error getting validation data') != -1:
return public.getMsg('ACME_ERR_MSG5')
elif "too many certificates already issued for exact set of domains" in error:
return 'Issuing failed, the domain %s has exceeded the limit of weekly reissues!' % re.findall("exact set of domains: (.+):", error)
return public.getMsg('ACME_ERR_MSG6',(re.findall("exact set of domains: (.+):", error),))
elif "Error creating new account :: too many registrations for this IP" in error:
return 'Issuing failed, the current server IP has reached the limit of creating up to 10 accounts every 3 hours.'
return public.getMsg('ACME_ERR_MSG7')
elif "DNS problem: NXDOMAIN looking up A for" in error:
return 'Validation failed, domain name was not resolved, or resolution did not take effect!'
return public.getMsg('ACME_ERR_MSG8')
elif "Invalid response from" in error:
return 'Verification failed, domain name resolution error or verification URL cannot be accessed!'
return public.getMsg('ACME_ERR_MSG9')
elif error.find('TLS Web Server Authentication') != -1:
return "Connection to CA server failed, please try again later."
return public.getMsg('ACME_ERR_MSG10')
elif error.find('Name does not end in a public suffix') != -1:
return "Unsupported domain name %s, please check the domain name is correct!" % re.findall("Cannot issue for \"(.+)\":", error)
return public.getMsg('ACME_ERR_MSG11',(re.findall("Cannot issue for \"(.+)\":", error),))
elif error.find('No valid IP addresses found for') != -1:
return "No resolution record was found for domain name % s, please check if the domain name resolution takes effect!" % re.findall("No valid IP addresses found for (.+)", error)
return public.getMsg('ACME_ERR_MSG12',(re.findall("No valid IP addresses found for (.+)", error),))
elif error.find('No TXT record found at') != -1:
return "No valid TXT resolution record was found in the domain name %s, please check whether the TXT record is parsed correctly. If it is applied by DNSAPI, please try again in 10 minutes!" % re.findall("No TXT record found at (.+)", error)
return public.getMsg('ACME_ERR_MSG13',(re.findall("No TXT record found at (.+)", error),))
elif error.find('Incorrect TXT record') != -1:
return "Error TXT record found on %s:, please check the TXT parsing is correct. If it is applied by DNSAPI, please try again in 10 minutes!" % (re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error))
return public.getMsg('ACME_ERR_MSG14',(re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error)))
elif error.find('Domain not under you or your user') != -1:
return "This domain name does not exist under this dnspod account, adding resolution failed!"
return public.getMsg('ACME_ERR_MSG15')
elif error.find('SERVFAIL looking up TXT for') != -1:
return "No valid TXT resolution record was found in the domain name %s, please check whether the TXT record is parsed correctly. If it is applied by DNSAPI, please try again in 10 minutes!" % re.findall("looking up TXT for (.+)", error)
return public.getMsg('ACME_ERR_MSG16',re.findall("looking up TXT for (.+)", error))
elif error.find('Timeout during connect') != -1:
return "The connection timed out and the CA server was unable to access your website!"
return public.getMsg('ACME_ERR_MSG17')
elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1:
return "Domain name %s is currently required to verify the CAA record, please parse the CAA record manually, or retry the application after 1 hour!" % re.findall("looking up CAA for (.+)", error)
return public.getMsg('ACME_ERR_MSG18',(re.findall("looking up CAA for (.+)", error),))
elif error.find("Read timed out.") != -1:
return "The verification timed out. Please check if the domain name is resolved correctly. If it is resolved correctly, the connection between the server and Let'sEncrypt may be abnormal. Please try again later!"
return public.getMsg('ACME_ERR_MSG19')
elif error.find('Cannot issue for') != -1:
return "Cannot issue a certificate for {}, cannot apply for a wildcard certificate with a domain name suffix directly!".format(re.findall(r'for\s+"(.+)"',error))
return public.getMsg('ACME_ERR_MSG20',(re.findall(r'for\s+"(.+)"',error),))
elif error.find('too many failed authorizations recently'):
return 'The account has more than 5 failed orders within 1 hour, please wait 1 hour and try again!'
return public.getMsg('ACME_ERR_MSG21')
elif error.find("Error creating new order") != -1:
return "Order creation failed, please try again later!"
return public.getMsg('ACME_ERR_MSG22')
elif error.find("Too Many Requests") != -1:
return "More than 5 verification failures in 1 hour, the application is temporarily banned, please try again later!"
return public.getMsg('ACME_ERR_MSG23')
elif error.find('HTTP Error 400: Bad Request') != -1:
return "CA server denied access, please try again later!"
return public.getMsg('ACME_ERR_MSG24')
elif error.find('Temporary failure in name resolution') != -1:
return 'The DNS of the server is faulty and the domain name cannot be resolved. Please use the Linux toolbox to check the DNS configuration'
return public.getMsg('ACME_ERR_MSG25')
elif error.find('Too Many Requests') != -1:
return '该域名请求申请次数过多,请3小时后重试'
return public.getMsg('ACME_ERR_MSG26')
else:
return error
@@ -618,10 +619,7 @@ class acme_v2:
url=self._config['orders'][index]['finalize'], payload=payload)
if send_csr_response.status_code not in [200, 201]:
raise ValueError(
"Error: Sending CSR: Response status {status_code} Response value: {response}".format(
status_code=send_csr_response.status_code,
response=send_csr_response.json(),
)
public.getMsg('ACME_SEND_CSR_ERR',(send_csr_response.status_code,send_csr_response.json()))
)
send_csr_response_json = send_csr_response.json()
certificate_url = send_csr_response_json["certificate"]
@@ -644,7 +642,7 @@ class acme_v2:
res = self.acme_request(
self._config['orders'][index]['certificate_url'], "")
if res.status_code not in [200, 201]:
raise Exception("Failed to download certificate: ()".format(res.json()))
raise Exception(public.getMsg('ACME_CERT_DOWNLOAD_ERR',(res.json(),)))
pem_certificate = res.content
if type(pem_certificate) == bytes:
@@ -747,8 +745,7 @@ fullchain.pem Paste into certificate input box
public.writeFile(
to_key_file, public.readFile(key_file, 'rb'), 'wb')
public.writeFile(to_info, json.dumps(cert_init))
write_log(
"|-Detected that the certificate under {} overlaps with the certificate of this application and has an earlier expiration time, and has been replaced with a new certificate!".format(to_path))
write_log(public.getMsg('ACME_CERT_REPLACE',(to_path,)))
# 重载web服务
public.serviceReload()
@@ -773,10 +770,10 @@ fullchain.pem Paste into certificate input box
if not os.path.exists(args.pem_file):
args.pem_file = 'vhost/cert/{}/fullchain.pem'.format(args.siteName)
if not os.path.exists(args.pem_file):
return public.returnMsg(False, 'The specified certificate file does not exist!')
return public.returnMsg(False, 'ACME_CERT_FILE_ERR')
cert_init = self.get_cert_init(args.pem_file)
if not cert_init:
return public.returnMsg(False, 'Certificate information acquisition failed!')
return public.returnMsg(False, 'ACME_CERT_GET_CERTINFO_ERR')
cert_init['dnsapi'] = json.loads(public.readFile(self._dnsapi_file))
return cert_init
@@ -866,8 +863,7 @@ fullchain.pem Paste into certificate input box
# 检查DNS记录
def check_dns(self, domain, value, s_type='TXT'):
write_log(
"|-Attempt to verify DNS records locally, domain name: {}, type: {} record value: {}".format(domain, s_type, value))
write_log(public.getMsg('ACME_CHECK_DNS',(domain, s_type, value)))
time.sleep(10)
n = 0
while n < 20:
@@ -878,9 +874,9 @@ fullchain.pem Paste into certificate input box
for j in ns.response.answer:
for i in j.items:
txt_value = i.to_text().replace('"', '').strip()
write_log("|-Number of verifications: {}, value: {}".format(n, txt_value))
write_log(public.getMsg('ACME_CHECK_DNS1',(n,txt_value)))
if txt_value == value:
write_log("|-Local authentication succeeded!")
write_log(public.getMsg('ACME_CHECK_DNS2'))
return True
except:
try:
@@ -888,7 +884,7 @@ fullchain.pem Paste into certificate input box
except:
return False
time.sleep(3)
write_log("|-Local authentication failed!")
write_log(public.getMsg('ACME_CHECK_DNS3'))
return True
# 创建CSR
@@ -939,7 +935,7 @@ fullchain.pem Paste into certificate input box
# 构造验证信息
def get_identifier_auth(self, index, url, auth_info):
s_type = self.get_auth_type(index)
write_log("|-Verification type: {}".format(s_type))
write_log(public.getMsg('ACME_BUILD_AUTH',(s_type,)))
domain = auth_info['identifier']['value']
wildcard = False
# 处理通配符
@@ -964,7 +960,7 @@ fullchain.pem Paste into certificate input box
# 获取域名验证方式
def get_auth_type(self, index):
if not index in self._config['orders']:
raise Exception('The specified order does not exist!')
raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST'))
s_type = 'http-01'
if 'auth_type' in self._config['orders'][index]:
if self._config['orders'][index]['auth_type'] == 'dns':
@@ -1040,7 +1036,7 @@ fullchain.pem Paste into certificate input box
res = self.acme_request(url=self._apis['newAccount'], payload=payload)
if res.status_code not in [201, 200, 409]:
raise Exception("Registration for ACME account failed: {}".format(res.json()))
raise Exception(public.getMsg('ACME_REGISTERED_ERR',(res.json(),)))
kid = res.headers["Location"]
return kid
@@ -1214,22 +1210,22 @@ fullchain.pem Paste into certificate input box
if 'index' in args:
index = args['index']
if not index: # 判断是否只想验证域名
write_log("|-Creating order..")
write_log(public.getMsg('ACME_CREAT_ORDER'))
index = self.create_order(domains, auth_type, auth_to)
write_log("|-Getting verification information..")
write_log('ACME_GET_V')
self.get_auths(index)
if auth_to == 'dns' and len(self._config['orders'][index]['auths']) > 0:
return self._config['orders'][index]
write_log("|-Verifying domain name..")
write_log(public.getMsg('ACME_V_DOMAIN'))
self.auth_domain(index)
self.remove_dns_record()
write_log("|-Sending CSR..")
write_log(public.getMsg('ACME_SEND_CSR'))
self.send_csr(index)
write_log("|-Downloading certificate..")
write_log(public.getMsg('ACME_DOWNLOAD_CERT'))
cert = self.download_cert(index)
cert['status'] = True
cert['msg'] = 'Application successful!'
write_log("|-Successful application, deploying to site..")
cert['msg'] = public.getMsg('ACME_APPLY_SUCCESS')
write_log(public.getMsg('ACME_APPLY_SUCCESS1'))
return cert
except Exception as ex:
self.remove_dns_record()
@@ -1254,7 +1250,7 @@ fullchain.pem Paste into certificate input box
args.auth_to = args.auth_to[:-1]
if not os.path.exists(args.auth_to):
return public.returnMsg(False, 'Invalid site directory, please check if the specified site exists!')
return public.returnMsg(False, 'ACME_DIR_ERR')
check_result = self.check_auth_env(args)
if check_result: return check_result
@@ -1268,7 +1264,7 @@ fullchain.pem Paste into certificate input box
for domain in json.loads(args.domains):
if public.checkIp(domain): continue
if domain.find('*.') >=0 and args.auth_type in ['http','tls']:
raise public.returnMsg(False, 'Pan domain names cannot apply for a certificate using [File Verification]!')
raise public.returnMsg(False, 'ACME_PAN_DOMAIN_ERR')
import panelSite
s = panelSite.panelSite()
if args.auth_type in ['http','tls']:
@@ -1283,18 +1279,17 @@ fullchain.pem Paste into certificate input box
data = s.GetProxyList(args)
if type(data) == list:
for x in data:
if s.GetProxyList(args): return public.returnMsg(False,
'Sites with reverse proxy turned on cannot apply for SSL!')
if x['open']: return public.returnMsg(False,'ACME_PROXY_ERR')
#判断是否强制HTTPS
if s.IsToHttps(args.siteName):
return public.returnMsg(False, 'After configuring Force HTTPS, you cannot use [File Verification] to apply for a certificate!')
return public.returnMsg(False, 'ACME_FORCE_SSL_ERR')
except:
return False
else:
if args.auth_to.find('Dns_com') != -1:
if not os.path.exists('plugin/dns/dns_main.py'):
return public.returnMsg(False, 'Please go to the software store to install [cloud analysis], and complete the domain name NS binding.')
return public.returnMsg(False, 'ACME_DNS_ERR')
return False
# DNS手动验证
@@ -1335,7 +1330,7 @@ fullchain.pem Paste into certificate input box
if type(index) != str:
index = index.index
if not index in self._config['orders']:
raise Exception("The specified order number does not exist and cannot be renewed!")
raise Exception(public.getMsg('ACME_RENEW_ERR'))
order_index.append(index)
else:
s_time = time.time() + (30 * 86400)
@@ -1351,17 +1346,16 @@ fullchain.pem Paste into certificate input box
order_index.append(i)
if not order_index:
write_log("|-No SSL certificate found within 30 days!")
write_log(public.getMsg('ACME_NO_NEED_RENEW'))
return
write_log("|-A total of {} certificates need to be renewed".format(len(order_index)))
write_log(public.getMsg("ACME_NEED_RENEW",(len(order_index),)))
n = 0
self.get_apis()
cert = None
for index in order_index:
n += 1
write_log("|-Renewing certificate number of {}domain: {}..".format(n,
self._config['orders'][index]['domains']))
write_log("|-Creating order...")
write_log(public.getMsg("ACME_RENEWING",(n,self._config['orders'][index]['domains'])))
write_log(public.getMsg('ACME_CREAT_ORDER'))
try:
index = self.create_order(
self._config['orders'][index]['domains'],
@@ -1369,20 +1363,20 @@ fullchain.pem Paste into certificate input box
self._config['orders'][index]['auth_to'],
index
)
write_log("|-Getting verification information..")
write_log(public.getMsg('ACME_GET_V'))
self.get_auths(index)
write_log("|-Verifying domain name..")
write_log(public.getMsg('ACME_V_DOMAIN'))
self.auth_domain(index)
write_log("|-Sending CSR..")
write_log(public.getMsg('ACME_SEND_CSR'))
self.remove_dns_record()
self.send_csr(index)
write_log("|-Downloading certificate..")
write_log(public.getMsg('ACME_DOWNLOAD_CERT'))
cert = self.download_cert(index)
self._config['orders'][index]['renew_time'] = int(time.time())
self.save_config()
cert['status'] = True
cert['msg'] = 'Renewed successfully!'
write_log("|-Renewed successfully!")
cert['msg'] = public.getMsg('ACME_RENEW_SUCCESS')
write_log(public.getMsg('ACME_RENEW_SUCCESS1'))
except Exception as e:
write_log("|-" + str(e).split('>>>>')[0])
write_log("-" * 70)
@@ -1419,23 +1413,22 @@ def write_log(log_str, mode="ab+"):
if __name__ == "__main__":
import argparse
p = argparse.ArgumentParser(usage="Required parameters: --domain list of domain names, multiple separated by commas!")
p = argparse.ArgumentParser(usage=public.getMsg('ACME_USE_TIPS'))
p.add_argument('--domain', default=None,
help="Please specify the domain name to apply for a certificate", dest="domains")
p.add_argument('--type', default=None, help="Please specify verification type", dest="auth_type")
p.add_argument('--path', default=None, help="Please specify the website root directory", dest="path")
p.add_argument('--dnsapi', default=None, help="Please specify DNSAPI", dest="dnsapi")
p.add_argument('--dns_key', default=None, help="Please specify DNSAPI key", dest="key")
p.add_argument('--dns_secret', default=None,
help="Please specify DNSAPI secret", dest="secret")
p.add_argument('--index', default=None, help="Specify the order index", dest="index")
p.add_argument('--renew', default=None, help="renew certificate", dest="renew")
p.add_argument('--revoke', default=None, help="Revoke certificate", dest="revoke")
help=public.getMsg('ACME_USE_TIPS1'), dest="domains")
p.add_argument('--type', default=None, help=public.getMsg('ACME_USE_TIPS2'), dest="auth_type")
p.add_argument('--path', default=None, help=public.getMsg('ACME_USE_TIPS3'), dest="path")
p.add_argument('--dnsapi', default=None, help=public.getMsg('ACME_USE_TIPS4'), dest="dnsapi")
p.add_argument('--dns_key', default=None, help=public.getMsg('ACME_USE_TIPS5'), dest="key")
p.add_argument('--dns_secret', default=None,help=public.getMsg('ACME_USE_TIPS6'), dest="secret")
p.add_argument('--index', default=None, help=public.getMsg('ACME_USE_TIPS7'), dest="index")
p.add_argument('--renew', default=None, help=public.getMsg('ACME_USE_TIPS8'), dest="renew")
p.add_argument('--revoke', default=None, help=public.getMsg('ACME_USE_TIPS9'), dest="revoke")
args = p.parse_args()
cert = None
if args.revoke:
if not args.index:
echo_err("Please enter the index of the order to be revoked in the --index parameter")
echo_err(public.getMsg('ACME_USE_TIPS10'))
p = acme_v2()
result = p.revoke_order(args.index)
write_log(result)
@@ -1448,25 +1441,24 @@ if __name__ == "__main__":
try:
if not args.index:
if not args.domains:
echo_err("Please specify the domain name for which you want to apply for a certificate in the --domain parameter, multiple separated by commas (,)")
echo_err(public.getMsg('ACME_USE_TIPS11'))
if not args.auth_type in ['http', 'tls', 'dns']:
echo_err("Please specify the correct authentication type in the --type parameter, supporting dns and http")
echo_err(public.getMsg('ACME_USE_TIPS12'))
auth_to = ''
if args.auth_type in ['http', 'tls']:
if not args.path:
echo_err("Please specify the website root directory in the --path parameter!")
echo_err(public.getMsg('ACME_USE_TIPS13'))
if not os.path.exists(args.path):
echo_err("The specified site root does not exist, please check: {}".format(args.path))
echo_err(public.getMsg('ACME_USE_TIPS14',(args.path,)))
auth_to = args.path
else:
if args.dnsapi == '0':
auth_to = 'dns'
else:
if not args.key:
echo_err("When applying using dnsapi, specify the dnsapi key in the --dns_key parameter!")
echo_err(public.getMsg('ACME_USE_TIPS15'))
if not args.secret:
echo_err(
"When applying using dnsapi, specify the secret of dnsapi in the --dns_secret parameter!")
echo_err(public.getMsg('ACME_USE_TIPS16'))
auth_to = "{}|{}|{}".format(
args.dnsapi, args.key, args.secret)
@@ -1478,32 +1470,27 @@ if __name__ == "__main__":
acme_txt = '_acme-challenge.'
acme_caa = '1 issue letsencrypt.org'
write_log("=" * 65)
write_log("\033[32m|-Manual order submission is successful, please resolve DNS records according to the following tips: \033[0m")
write_log("\033[32m"+public.getMsg('ACME_USE_TIPS17')+"\033[0m")
write_log("=" * 65)
write_log("|-Order index: {}".format(cert['index']))
write_log(
"|-Retry the command: ./acme_v2.py --index=\"{}\"".format(cert['index']))
write_log(
"|-A total of \033[36m{}\033[0m domain name records need to be resolved.".format(len(cert['auths'])))
write_log(public.getMsg('ACME_USE_TIPS18',(cert['index'],)))
write_log(public.getMsg('ACME_USE_TIPS19')+": ./acme_v2.py --index=\"{}\"".format(cert['index']))
write_log(public.getMsg('ACME_USE_TIPS20',(len(cert['auths']),)))
for i in range(len(cert['auths'])):
write_log('-' * 70)
write_log(
"|-The \033[36m{}\033[0m domain names are: {}, please resolve the following information: ".format(i+1, cert['auths'][i]['domain']))
write_log("|-Record Type: TXT Record Name: \033[41m{}\033[0m Record Value: \033[41m{}\033 [0m [Required]".format(
acme_txt + cert['auths'][i]['domain'].replace('*.', ''), cert['auths'][i]['auth_value']))
write_log("|-Record type: CAA Record name: \033[41m{}\033[0m Record value: \033[41m{}\033[0m [Optional]".format(
cert['auths'][i]['domain'].replace('*.', ''), acme_caa))
write_log(public.getMsg('ACME_USE_TIPS21',(i+1, cert['auths'][i]['domain'])))
write_log(public.getMsg('ACME_USE_TIPS22',(acme_txt + cert['auths'][i]['domain'].replace('*.', ''), cert['auths'][i]['auth_value'])))
write_log(public.getMsg('ACME_USE_TIPS23',(cert['auths'][i]['domain'].replace('*.', ''), acme_caa)))
write_log('-' * 70)
input_data = ""
while input_data not in ['y', 'Y', 'n', 'N']:
input_msg = "Please wait 2-3 minutes after completing the resolution and enter Y and press Enter to continue verifying the domain name: "
input_msg = public.getMsg('ACME_USE_TIPS24')
if sys.version_info[0] == 2:
input_data = raw_input(input_msg)
else:
input_data = input(input_msg)
if input_data in ['n', 'N']:
write_log("=" * 65)
write_log("|-The user abandons the application and exits the program!")
write_log(public.getMsg('ACME_USE_TIPS25'))
exit()
cert = p.apply_cert(
[], auth_type=args.auth_type, auth_to='dns', index=cert['index'])
@@ -1518,9 +1505,8 @@ if __name__ == "__main__":
if not cert:
exit()
write_log("=" * 65)
write_log("|-Certificate obtained successfully!")
write_log(public.getMsg('ACME_USE_TIPS26'))
write_log("=" * 65)
write_log("Certified Domain Name: {}".format(','.join(cert['domains'])))
write_log("Certificate expiration time: {}".format(
public.format_date(times=cert['cert_timeout'])))
write_log("Certificate saved at: {}/".format(cert['save_path']))
write_log(public.getMsg('ACME_USE_TIPS27',(','.join(cert['domains']),)))
write_log(public.getMsg('ACME_USE_TIPS28',(public.format_date(times=cert['cert_timeout']),)))
write_log(public.getMsg('ACME_USE_TIPS29',(cert['save_path'],)))
+104 -60
View File
@@ -31,10 +31,10 @@ class ajax:
for proc in psutil.process_iter():
if proc.name() == "nginx":
self.GetProcessCpuPercent(proc.pid,process_cpu)
time.sleep(0.5)
time.sleep(0.1)
#取Nginx负载状态
self.CheckStatusConf()
result = public.ExecShell('/usr/local/curl/bin/curl -Ss http://127.0.0.1/nginx_status')[0]
result = public.httpGet('http://127.0.0.1/nginx_status')
tmp = result.split()
data = {}
if "request_time" in tmp:
@@ -57,22 +57,22 @@ class ajax:
data['workermen'] = "%s%s" % (int(workermen), "MB")
return data
except Exception as ex:
public.WriteLog('Get Info',"Nginx load status acquisition failed:%s" % ex)
return public.returnMsg(False,'Data acquisition failed!')
public.WriteLog('GET_INFO','NGINX_LOAD_ERR',(ex,))
return public.returnMsg(False,'GET_DATA_ERR')
def GetPHPStatus(self,get):
#取指定PHP版本的负载状态
try:
version = get.version
uri = "/phpfpm_"+version+"_status"
result = public.request_php(version,uri,uri,'json')
uri = "/phpfpm_"+version+"_status?json"
result = public.request_php(version,uri,'')
tmp = json.loads(result)
fTime = time.localtime(int(tmp['start time']))
tmp['start time'] = time.strftime('%Y-%m-%d %H:%M:%S',fTime)
return tmp
except Exception as ex:
public.WriteLog('Get Info',"PHP load status acquisition failed: %s" % ex)
return public.returnMsg(False,'PHP load status acquisition failed!')
public.WriteLog('GET_INFO',"PHP_LOAD_ERR",(public.get_error_info(),))
return public.returnMsg(False,'PHP_LOAD_ERR1')
def CheckStatusConf(self):
if public.get_webserver() != 'nginx': return
@@ -152,7 +152,7 @@ class ajax:
if status == public.GetMsg("NOT_INSTALL"):
optStr = '<a class="link" href="javascript:InstallLib(\''+libName+'\');">'+public.GetMsg("INSTALL")+'</a>'
else:
libConfig = public.GetMsg("配置")
libConfig = public.GetMsg("CONF")
if(libName == 'beta'): libConfig = public.GetMsg("CLOSE_BETA")
optStr = '<a class="link" href="javascript:SetLibConfig(\''+libName+'\');">'+libConfig+'</a> | <a class="link" href="javascript:UninstallLib(\''+libName+'\');">'+public.GetMsg("UNINSTALL")+'</a>';
@@ -182,7 +182,7 @@ class ajax:
if result[0].find("ERROR:") == -1:
public.WriteLog("PLUG_MAM","SET_PLUG[" +info['name']+ "]AS!")
return public.returnMsg(True, 'SET_SUCCESS')
return public.returnMsg(False,'AK_SK_CONNECT_ERROR'+info['name']+',Please check if the [AK/SK/Storage] setting is correct.')
return public.returnMsg(False,'AK_SK_CONNECT_ERROR',(info['name'],))
#设置内测
def SetBeta(self,get):
@@ -383,27 +383,31 @@ class ajax:
#申请内测版
def apple_beta(self,get):
try:
userInfo = json.loads(public.ReadFile('data/userInfo.json'))
p_data = {}
p_data['uid'] = userInfo['uid']
p_data['access_key'] = userInfo['access_key']
p_data['username'] = userInfo['username']
result = public.HttpPost(public.GetConfigValue('home') + '/api/panel/apple_beta',p_data,5)
# userInfo = json.loads(public.ReadFile('data/userInfo.json'))
# p_data = {}
# p_data['uid'] = userInfo['uid']
# p_data['access_key'] = userInfo['access_key']
# p_data['username'] = userInfo['username']
# result = public.HttpPost(public.GetConfigValue('home') + '/api/panel/apple_beta',p_data,5)
public.writeFile('/www/server/panel/data/is_beta.pl','true')
try:
return json.loads(result)
return {'status': True, 'msg': "Successful application!"}
except: return public.returnMsg(False,'AJAX_CONN_ERR')
except: return public.returnMsg(False,'AJAX_USER_BINDING_ERR')
def to_not_beta(self,get):
try:
userInfo = json.loads(public.ReadFile('data/userInfo.json'))
p_data = {}
p_data['uid'] = userInfo['uid']
p_data['access_key'] = userInfo['access_key']
p_data['username'] = userInfo['username']
result = public.HttpPost(public.GetConfigValue('home') + '/api/panel/to_not_beta',p_data,5)
# userInfo = json.loads(public.ReadFile('data/userInfo.json'))
# p_data = {}
# p_data['uid'] = userInfo['uid']
# p_data['access_key'] = userInfo['access_key']
# p_data['username'] = userInfo['username']
# result = public.HttpPost(public.GetConfigValue('home') + '/api/panel/to_not_beta',p_data,5)
try:
return json.loads(result)
beta_file = '/www/server/panel/data/is_beta.pl'
if os.path.exists(beta_file):
os.remove(beta_file)
return {"status": True, "msg": "Successful application!"}
except: return public.returnMsg(False,'AJAX_CONN_ERR')
except: return public.returnMsg(False,'AJAX_USER_BINDING_ERR')
@@ -426,7 +430,8 @@ class ajax:
#获取最新的5条测试版更新日志
def get_beta_logs(self,get):
try:
data = json.loads(public.HttpGet(public.GetConfigValue('home') + '/api/panel/get_beta_logs_en'))
# data = json.loads(public.HttpGet(public.GetConfigValue('home') + '/api/panel/get_beta_logs_en'))
data = json.loads(public.HttpGet('https://console.aapanel.com/api/panel/get_beta_logs_en'))
return data
except:
return public.returnMsg(False,'AJAX_CONN_ERR')
@@ -435,28 +440,27 @@ class ajax:
other = {}
other['ds'] = []
ds = public.M('domain').field('name').select()
# public.writeFile('/tmp/1.txt',str(ds))
for d in ds:
other['ds'].append(d['name'])
return ','.join(other['ds'])
def UpdatePanel(self,get):
try:
if not public.IsRestart(): return public.returnMsg(False,'EXEC_ERR_TASK')
import json
if int(session['config']['status']) == 0:
# public.HttpGet(public.GetConfigValue('home')+'/Api/SetupCount?type=Linux')
public.HttpGet('https://www.aapanel.com/Api/SetupCount?type=Linux')
conf_status = public.M('config').where("id=?",('1',)).field('status').find()
if int(session['config']['status']) == 0 and int(conf_status['status']) == 0:
public.HttpGet(public.GetConfigValue('home')+'/Api/SetupCount?type=Linux')
public.M('config').where("id=?",('1',)).setField('status',1)
session['config']['status'] = 1
#取回远程版本信息
if 'updateInfo' in session and hasattr(get,'check') == False:
updateInfo = session['updateInfo']
else:
logs = '' #public.GetClientIp() +':'+ str(request.environ.get('REMOTE_PORT')) + '|' + str(int(time.time()))
logs = public.get_debug_log()
import psutil,system,sys
mem = psutil.virtual_memory()
import panelPlugin
@@ -480,11 +484,13 @@ class ajax:
data['o'] = ''
filename = '/www/server/panel/data/o.pl'
if os.path.exists(filename): data['o'] = str(public.readFile(filename))
sUrl = public.GetConfigValue('home') + '/api/panel/updateLinuxEn'
# sUrl = 'https://www.aapanel.com/api/panel/updateLinuxEn'
# sUrl = public.GetConfigValue('home') + '/api/panel/updateLinuxEn'
sUrl = 'https://console.aapanel.com/api/panel/updateLinuxEn'
updateInfo = json.loads(public.httpPost(sUrl,data))
if not updateInfo: return public.returnMsg(False,"CONNECT_ERR")
#updateInfo['msg'] = msg;
if os.path.exists('/www/server/panel/data/is_beta.pl'):
updateInfo['is_beta'] = 1
session['updateInfo'] = updateInfo
#检查是否需要升级
@@ -652,7 +658,7 @@ class ajax:
public.ExecShell("rm -rf " + sPath)
p_file = '/dev/shm/phpinfo.php'
public.writeFile(p_file,'<?php phpinfo(); ?>')
phpinfo = public.request_php(get.version,'/phpinfo.php',p_file,'')
phpinfo = public.request_php(get.version,'/phpinfo.php','/dev/shm')
if os.path.exists(p_file): os.remove(p_file)
return phpinfo.decode()
@@ -665,8 +671,11 @@ class ajax:
return public.returnMsg(True,'LOG_CLOSE')
def __get_webserver_conffile(self):
if public.get_webserver() == 'nginx':
webserver = public.get_webserver()
if webserver == 'nginx':
filename = public.GetConfigValue('setup_path') + '/nginx/conf/nginx.conf'
elif webserver == 'openlitespeed':
filename = public.GetConfigValue('setup_path') + "/panel/vhost/openlitespeed/detail/phpmyadmin.conf"
else:
filename = public.GetConfigValue('setup_path') + '/apache/conf/extra/httpd-vhosts.conf'
return filename
@@ -675,10 +684,10 @@ class ajax:
def get_phpmyadmin_conf(self):
if public.get_webserver() == "nginx":
conf_file = "/www/server/panel/vhost/nginx/phpmyadmin.conf"
rep = "listen\s*(\d+)"
rep = r"listen\s*(\d+)"
else:
conf_file = "/www/server/panel/vhost/apache/phpmyadmin.conf"
rep = "Listen\s*(\d+)"
rep = r"Listen\s*(\d+)"
return {"conf_file":conf_file,"rep":rep}
# 设置phpmyadmin路径
@@ -708,36 +717,36 @@ class ajax:
# 修改php ssl端口
def change_phpmyadmin_ssl_port(self,get):
if public.get_webserver() == "openlitespeed":
return public.returnMsg(False, 'The current web server is openlitespeed. This function is not supported yet.')
return public.returnMsg(False, 'NOT_SUPPORT_OLS')
import re
try:
port = int(get.port)
if 1 > port > 65535:
return public.returnMsg(False, 'Port range is incorrect')
return public.returnMsg(False, 'PORT_CHECK_RANGE')
except:
return public.returnMsg(False, 'The port format is incorrect')
return public.returnMsg(False, 'PORT_FORMAT_ERR')
for i in ["nginx","apache"]:
file = "/www/server/panel/vhost/{}/phpmyadmin.conf".format(i)
conf = public.readFile(file)
if not conf:
return public.returnMsg(False,"Did not find the {} configuration file, please try to close the ssl port settings before opening".format(i))
return public.returnMsg(False,"PHPMYADMIN_SSL_ERR",(i,))
rulePort = ['80', '443', '21', '20', '8080', '8081', '8089', '11211', '6379']
if get.port in rulePort:
return public.returnMsg(False, 'AJAX_PHPMYADMIN_PORT_ERR')
if i == "nginx":
if not os.path.exists("/www/server/panel/vhost/apache/phpmyadmin.conf"):
return public.returnMsg(False, "Did not find the apache phpmyadmin ssl configuration file, please try to close the ssl port settings before opening")
return public.returnMsg(False, "PHPMYADMIN_SSL_ERR1")
rep = "listen\s*([0-9]+)\s*.*;"
oldPort = re.search(rep, conf)
if not oldPort:
return public.returnMsg(False, 'Did not detect the port that nginx phpmyadmin listens, please confirm whether the file has been manually modified.')
return public.returnMsg(False, 'PHPMYADMIN_SSL_ERR2')
oldPort = oldPort.groups()[0]
conf = re.sub(rep, 'listen ' + get.port + ' ssl;', conf)
else:
rep = "Listen\s*([0-9]+)\s*\n"
oldPort = re.search(rep, conf)
if not oldPort:
return public.returnMsg(False, 'Did not detect the port that apache phpmyadmin listens, please confirm whether the file has been manually modified.')
return public.returnMsg(False, 'PHPMYADMIN_SSL_ERR3')
oldPort = oldPort.groups()[0]
conf = re.sub(rep, "Listen " + get.port + "\n", conf, 1)
rep = "VirtualHost\s*\*:[0-9]+"
@@ -757,13 +766,38 @@ class ajax:
fw.DelAcceptPort(get)
return public.returnMsg(True, 'SET_PORT_SUCCESS')
def _get_phpmyadmin_auth(self):
import re
nginx_conf = '/www/server/nginx/conf/nginx.conf'
reg = '#AUTH_START(.|\n)*#AUTH_END'
if os.path.exists(nginx_conf):
nginx_conf = public.readFile(nginx_conf)
auth_tmp = re.search(reg, nginx_conf)
if auth_tmp:
return True
apache_conf = '/www/server/apache/conf/extra/httpd-vhosts.conf'
if os.path.exists(apache_conf):
apache_conf = public.readFile(apache_conf)
auth_tmp = re.search(reg, apache_conf)
if auth_tmp:
return True
# 设置phpmyadmin ssl
def set_phpmyadmin_ssl(self,get):
if public.get_webserver() == "openlitespeed":
return public.returnMsg(False, 'The current web server is openlitespeed. This function is not supported yet.')
return public.returnMsg(False, 'NOT_SUPPORT_OLS')
if not os.path.exists("/www/server/panel/ssl/certificate.pem"):
return public.returnMsg(False,'The panel certificate does not exist. Please apply for the panel certificate and try again.')
return public.returnMsg(False,'PHPMYADMIN_SSL_ERR4')
if get.v == "1":
# 获取auth信息
auth = ""
if self._get_phpmyadmin_auth():
auth = """
#AUTH_START
auth_basic "Authorization";
auth_basic_user_file /www/server/pass/phpmyadmin.pass;
#AUTH_END
"""
# nginx配置文件
ssl_conf = """server
{
@@ -782,6 +816,7 @@ class ajax:
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
#SSL-END
%s
include enable-php.conf;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
@@ -796,12 +831,20 @@ class ajax:
deny all;
}
access_log /www/wwwlogs/access.log;
}"""
}""" % auth
public.writeFile("/www/server/panel/vhost/nginx/phpmyadmin.conf",ssl_conf)
import panelPlugin
get.sName = "phpmyadmin"
v = panelPlugin.panelPlugin().get_soft_find(get)
public.writeFile("/tmp/2",str(v["ext"]["phpversion"]))
if self._get_phpmyadmin_auth():
auth = """
#AUTH_START
AuthType basic
AuthName "Authorization "
AuthUserFile /www/server/pass/phpmyadmin.pass
Require user jose
#AUTH_END
"""
# apache配置
ssl_conf = '''Listen 887
<VirtualHost *:887>
@@ -833,13 +876,14 @@ class ajax:
#PATH
<Directory "/www/wwwroot/bt.youbadbad.cn/">
{}
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
Require all granted
DirectoryIndex index.php index.html index.htm default.php default.html default.htm
</Directory>
</VirtualHost>'''.format(v["ext"]["phpversion"])
</VirtualHost>'''.format(v["ext"]["phpversion"],auth)
public.writeFile("/www/server/panel/vhost/apache/phpmyadmin.conf", ssl_conf)
else:
if os.path.exists("/www/server/panel/vhost/nginx/phpmyadmin.conf"):
@@ -847,9 +891,9 @@ class ajax:
if os.path.exists("/www/server/panel/vhost/apache/phpmyadmin.conf"):
os.remove("/www/server/panel/vhost/apache/phpmyadmin.conf")
public.serviceReload()
return public.returnMsg(True, 'Closed successfully')
return public.returnMsg(True, 'SET_SUCCESS')
public.serviceReload()
return public.returnMsg(True,'Open successfully, please manually release phpmyadmin ssl port')
return public.returnMsg(True,'PHPMYADMIN_SSL_ERR5')
#设置PHPMyAdmin
@@ -872,15 +916,15 @@ class ajax:
oldPort = re.search(rep,conf).groups()[0]
conf = re.sub(rep,'listen ' + get.port + ';\n',conf)
elif public.get_webserver() == 'apache':
rep = "Listen\s+([0-9]+)\s*\n"
rep = r"Listen\s+([0-9]+)\s*\n"
oldPort = re.search(rep,conf).groups()[0]
conf = re.sub(rep,"Listen " + get.port + "\n",conf,1)
rep = "VirtualHost\s+\*:[0-9]+"
rep = r"VirtualHost\s+\*:[0-9]+"
conf = re.sub(rep,"VirtualHost *:" + get.port,conf,1)
else:
filename = '/www/server/panel/vhost/openlitespeed/listen/888.conf'
conf = public.readFile(filename)
reg = "address\s+\*:(\d+)"
reg = r"address\s+\*:(\d+)"
tmp = re.search(reg,conf)
if tmp:
oldPort = tmp.groups(1)
@@ -903,13 +947,13 @@ class ajax:
if public.get_webserver() == 'nginx':
filename = public.GetConfigValue('setup_path') + '/nginx/conf/enable-php.conf'
conf = public.readFile(filename)
rep = "php-cgi.*\.sock"
rep = r"php-cgi.*\.sock"
conf = re.sub(rep,'php-cgi-' + get.phpversion + '.sock',conf,1)
elif public.get_webserver() == 'apache':
rep = "php-cgi.*\.sock"
rep = r"php-cgi.*\.sock"
conf = re.sub(rep,'php-cgi-' + get.phpversion + '.sock',conf,1)
else:
reg = '/usr/local/lsws/lsphp\d+/bin/lsphp'
reg = r'/usr/local/lsws/lsphp\d+/bin/lsphp'
conf = re.sub(reg,'/usr/local/lsws/lsphp{}/bin/lsphp'.format(get.phpversion),conf)
public.writeFile(filename,conf)
public.serviceReload()
@@ -917,7 +961,7 @@ class ajax:
return public.returnMsg(True,'SOFT_PHPVERSION_SET')
if hasattr(get,'password'):
import panelSite;
import panelSite
if(get.password == 'close'):
return panelSite.panelSite().CloseHasPwd(get)
else:
@@ -1150,7 +1194,7 @@ class ajax:
#取指定行
def get_lines(self,args):
if not os.path.exists(args.filename): return public.returnMsg(False,'The specified log file does not exist!')
if not os.path.exists(args.filename): return public.returnMsg(False,'LOG_EMPTY')
s_body = public.ExecShell("tail -n {} {}".format(args.num,args.filename))[0]
return public.returnMsg(True,s_body)
+10 -4
View File
@@ -29,15 +29,16 @@ class dict_obj:
class panelSetup:
def init(self):
ua = request.headers.get('User-Agent')
ua = request.headers.get('User-Agent','')
if ua:
ua = ua.lower()
if ua.find('spider') != -1 or ua.find('bot') != -1:
return redirect('https://www.google.com')
g.version = '6.7.6'
g.version = '6.8.2'
g.title = public.GetConfigValue('title')
g.uri = request.path
if not os.path.exists('data/debug.pl'):
g.debug = os.path.exists('data/debug.pl')
if not g.debug:
g.cdn_url = public.get_cdn_url()
if not g.cdn_url:
g.cdn_url = '/static'
@@ -98,7 +99,7 @@ class panelAdmin(panelSetup):
if not 'lan' in session:
session['lan'] = public.GetLanguage()
if not 'home' in session:
session['home'] = 'http://www.aapanel.com';
session['home'] = 'http://www.aapanel.com'
return None
# 检查Web服务器类型
@@ -159,6 +160,11 @@ class panelAdmin(panelSetup):
if session['login_token'] != token:
session.clear()
return redirect('/login?dologin=True')
if api_check:
filename = 'data/sess_files/' + public.get_sess_key()
if not os.path.exists(filename):
session.clear()
return redirect('/login?dologin=True')
except:
return public.returnMsg(False,public.get_error_info())
session.clear()
+35 -2
View File
@@ -12,7 +12,7 @@ try:
except:
public.ExecShell("pip install pyotp &")
try:
from BTPanel import session,admin_path_checks
from BTPanel import session,admin_path_checks,g
from flask import request
import send_mail
except:pass
@@ -168,8 +168,41 @@ class config:
userInfo = public.M('users').where("id=?",(1,)).field('username,password').find()
token = public.Md5(userInfo['username'] + '/' + userInfo['password'])
public.writeFile('/www/server/panel/data/login_token.pl',token)
sess_path = 'data/sess_files'
if not os.path.exists(sess_path):
os.makedirs(sess_path,384)
self.clean_sess_files(sess_path)
sess_key = public.get_sess_key()
sess_file = os.path.join(sess_path,sess_key)
public.writeFile(sess_file,str(int(time.time()+86400)))
public.set_mode(sess_file,'600')
session['login_token'] = token
def clean_sess_files(self,sess_path):
'''
@name 清理过期的sess_file
@auther hwliang<2020-07-25>
@param sess_path(string) sess_files目录
@return void
'''
s_time = time.time()
for fname in os.listdir(sess_path):
try:
if len(fname) != 32: continue
sess_file = os.path.join(sess_path,fname)
if not os.path.isfile(sess_file): continue
sess_tmp = public.ReadFile(sess_file)
if not sess_tmp:
if os.path.exists(sess_file):
os.remove(sess_file)
if s_time > int(sess_tmp):
os.remove(sess_file)
except:
pass
def setPassword(self,get):
if get.password1 != get.password2: return public.returnMsg(False,'USER_PASSWORD_CHECK')
if len(get.password1) < 5: return public.returnMsg(False,'USER_PASSWORD_LEN')
@@ -837,7 +870,7 @@ class config:
ols_php_path = '/usr/local/lsws/lsphp{}/etc/php/{}.{}/litespeed/php.ini'.format(get.version, get.version[0],get.version[1])
if os.path.exists('/etc/redhat-release'):
ols_php_path = '/usr/local/lsws/lsphp' + get.version + '/etc/php.ini'
reload_ols_str = '/usr/local/lsws/bin/lswsctl reload'
reload_ols_str = '/usr/local/lsws/bin/lswsctrl restart'
for p in [filename,ols_php_path]:
if not p:
continue
+5 -1
View File
@@ -333,10 +333,14 @@ class crontab:
shell=param.sFile
else :
head="#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
log='-access_log'
python_bin = public.get_python_bin()
if public.get_webserver()=='nginx':
log='.log'
elif public.get_webserver()=='apache':
log = '-access_log'
else:
log = '_ols.access_log'
if type in ['site','path'] and param['sBody'] != 'undefined' and len(param['sBody']) > 1:
exports = param['sBody'].replace("\r\n","\n").replace("\n",",")
head += "BT_EXCLUDE=\"" + exports.strip() + "\"\nexport BT_EXCLUDE\n"
-3
View File
@@ -275,6 +275,3 @@ class data:
return fields[tableName]
except:
return ''
+2
View File
@@ -867,6 +867,8 @@ SetLink
#修复表信息
def ReTable(self,get):
m_version = public.readFile(public.GetConfigValue('setup_path') + '/mysql/version.pl')
if m_version.find('5.1.')!=-1:return public.returnMsg(False,"nonsupport mysql5.1!")
info=self.RepairTable(get)
if info:
+399
View File
@@ -0,0 +1,399 @@
# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $Id$
#
# Copyright (c) 2011 Vladimir Rusinov <vladimir@greenmice.info>
__author__ = 'Allan Saddi <allan@saddi.com>'
__version__ = '$Revision$'
import sys
import select
import struct
import socket
import errno
import types
__all__ = ['FCGIApp']
# Constants from the spec.
FCGI_LISTENSOCK_FILENO = 0
FCGI_HEADER_LEN = 8
FCGI_VERSION_1 = 1
FCGI_BEGIN_REQUEST = 1
FCGI_ABORT_REQUEST = 2
FCGI_END_REQUEST = 3
FCGI_PARAMS = 4
FCGI_STDIN = 5
FCGI_STDOUT = 6
FCGI_STDERR = 7
FCGI_DATA = 8
FCGI_GET_VALUES = 9
FCGI_GET_VALUES_RESULT = 10
FCGI_UNKNOWN_TYPE = 11
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
FCGI_NULL_REQUEST_ID = 0
FCGI_KEEP_CONN = 1
FCGI_RESPONDER = 1
FCGI_AUTHORIZER = 2
FCGI_FILTER = 3
FCGI_REQUEST_COMPLETE = 0
FCGI_CANT_MPX_CONN = 1
FCGI_OVERLOADED = 2
FCGI_UNKNOWN_ROLE = 3
FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
FCGI_MAX_REQS = 'FCGI_MAX_REQS'
FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
FCGI_Header = '!BBHHBx'
FCGI_BeginRequestBody = '!HB5x'
FCGI_EndRequestBody = '!LB3x'
FCGI_UnknownTypeBody = '!B7x'
FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
if __debug__:
import time
# Set non-zero to write debug output to a file.
DEBUG = 0
DEBUGLOG = '/www/server/panel/logs/fastcgi.log'
def _debug(level, msg):
if DEBUG < level:
return
try:
f = open(DEBUGLOG, 'a')
f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
f.close()
except:
pass
def decode_pair(s, pos=0):
"""
Decodes a name/value pair.
The number of bytes decoded as well as the name/value pair
are returned.
"""
nameLength = ord(s[pos])
if nameLength & 128:
nameLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
valueLength = ord(s[pos])
if valueLength & 128:
valueLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
name = s[pos:pos+nameLength]
pos += nameLength
value = s[pos:pos+valueLength]
pos += valueLength
return (pos, (name, value))
def encode_pair(name, value):
"""
Encodes a name/value pair.
The encoded string is returned.
"""
nameLength = len(name)
if nameLength < 128:
s = chr(nameLength).encode()
else:
s = struct.pack('!L', nameLength | 0x80000000)
valueLength = len(value)
if valueLength < 128:
s += chr(valueLength).encode()
else:
s += struct.pack('!L', valueLength | 0x80000000)
return s + name + value
class Record(object):
"""
A FastCGI Record.
Used for encoding/decoding records.
"""
def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
self.version = FCGI_VERSION_1
self.type = type
self.requestId = requestId
self.contentLength = 0
self.paddingLength = 0
self.contentData = ''
def _recvall(sock, length):
"""
Attempts to receive length bytes from a socket, blocking if necessary.
(Socket may be blocking or non-blocking.)
"""
dataList = []
recvLen = 0
while length:
try:
data = sock.recv(length)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([sock], [], [])
continue
else:
raise
if not data: # EOF
break
dataList.append(data)
dataLen = len(data)
recvLen += dataLen
length -= dataLen
return b''.join(dataList), recvLen
_recvall = staticmethod(_recvall)
def read(self, sock):
"""Read and decode a Record from a socket."""
try:
header, length = self._recvall(sock, FCGI_HEADER_LEN)
except:
raise EOFError
if length < FCGI_HEADER_LEN:
raise EOFError
self.version, self.type, self.requestId, self.contentLength, \
self.paddingLength = struct.unpack(FCGI_Header, header)
if __debug__: _debug(9, 'read: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
if self.contentLength:
try:
self.contentData, length = self._recvall(sock,
self.contentLength)
except:
raise EOFError
if length < self.contentLength:
raise EOFError
if self.paddingLength:
try:
self._recvall(sock, self.paddingLength)
except:
raise EOFError
def _sendall(sock, data):
"""
Writes data to a socket and does not return until all the data is sent.
"""
length = len(data)
while length:
try:
sent = sock.send(data)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([], [sock], [])
continue
else:
raise
data = data[sent:]
length -= sent
_sendall = staticmethod(_sendall)
def write(self, sock):
"""Encode and write a Record to a socket."""
self.paddingLength = -self.contentLength & 7
if __debug__: _debug(9, 'write: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
header = struct.pack(FCGI_Header, self.version, self.type,
self.requestId, self.contentLength,
self.paddingLength)
self._sendall(sock, header)
if self.contentLength:
self._sendall(sock, self.contentData)
if self.paddingLength:
self._sendall(sock, b'\x00'*self.paddingLength)
class FCGIApp(object):
def __init__(self, connect=None, host=None, port=None, filterEnviron=True):
if host is not None:
assert port is not None
connect=(host, port)
self._connect = connect
self._filterEnviron = filterEnviron
def __call__(self, environ, io, start_response=None):
# For sanity's sake, we don't care about FCGI_MPXS_CONN
# (connection multiplexing). For every request, we obtain a new
# transport socket, perform the request, then discard the socket.
# This is, I believe, how mod_fastcgi does things...
sock = self._getConnection()
# Since this is going to be the only request on this connection,
# set the request ID to 1.
requestId = 1
# Begin the request
rec = Record(FCGI_BEGIN_REQUEST, requestId)
rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
rec.contentLength = FCGI_BeginRequestBody_LEN
rec.write(sock)
# Filter WSGI environ and send it as FCGI_PARAMS
if self._filterEnviron:
params = self._defaultFilterEnviron(environ)
else:
params = self._lightFilterEnviron(environ)
# TODO: Anything not from environ that needs to be sent also?
#return '200 OK',[],str(params),''
self._fcgiParams(sock, requestId, params)
self._fcgiParams(sock, requestId, {})
# Transfer wsgi.input to FCGI_STDIN
content_length = int(environ.get('CONTENT_LENGTH') or 0)
s = ''
#io = StringIO(stdin)
while True:
if not io: break
chunk_size = min(content_length, 4096)
s = io.read(chunk_size)
content_length -= len(s)
rec = Record(FCGI_STDIN, requestId)
rec.contentData = s
rec.contentLength = len(s)
rec.write(sock)
if not s: break
# Empty FCGI_DATA stream
rec = Record(FCGI_DATA, requestId)
rec.write(sock)
return sock
def _getConnection(self):
if self._connect is not None:
# The simple case. Create a socket and connect to the
# application.
if isinstance(self._connect, str):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self._connect)
elif hasattr(socket, 'create_connection'):
sock = socket.create_connection(self._connect)
else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self._connect)
return sock
# To be done when I have more time...
raise NotImplementedError #, 'Launching and managing FastCGI programs not yet implemented'
def _fcgiGetValues(self, sock, vars):
# Construct FCGI_GET_VALUES record
outrec = Record(FCGI_GET_VALUES)
data = []
for name in vars:
data.append(encode_pair(name, ''))
data = ''.join(data)
outrec.contentData = data
outrec.contentLength = len(data)
outrec.write(sock)
# Await response
inrec = Record()
inrec.read(sock)
result = {}
if inrec.type == FCGI_GET_VALUES_RESULT:
pos = 0
while pos < inrec.contentLength:
pos, (name, value) = decode_pair(inrec.contentData, pos)
result[name] = value
return result
def _fcgiParams(self, sock, requestId, params):
rec = Record(FCGI_PARAMS, requestId)
data = []
for name,value in params.items():
data.append(encode_pair(name.encode('latin-1'), value.encode('latin-1')))
data = b''.join(data)
rec.contentData = data
rec.contentLength = len(data)
rec.write(sock)
_environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
'CONTENT_', 'DOCUMENT_', 'SCRIPT_']
_environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
_environRenames = []
def _defaultFilterEnviron(self, environ):
result = {}
for n in environ.keys():
iv = False
for p in self._environPrefixes:
if n.startswith(p):
result[n] = environ[n]
iv = True
if n in self._environCopies:
result[n] = environ[n]
iv = True
if n in self._environRenames:
result[self._environRenames[n]] = environ[n]
iv = True
if not iv:
result[n] = environ[n]
return result
def _lightFilterEnviron(self, environ):
result = {}
for n in environ.keys():
if n.upper() == n:
result[n] = environ[n]
return result
+84 -34
View File
@@ -372,17 +372,17 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
if get.reverse == 'False':
reverse = False
for file_info in self.__list_dir(get.path, get.sort, reverse):
filename = os.path.join(get.path, file_info['name'])
if not os.path.exists(filename):
continue
filename = os.path.join(get.path, file_info[0])
if search:
if file_info['name'].lower().find(search) == -1:
if file_info[0].lower().find(search) == -1:
continue
i += 1
if n >= page.ROW:
break
if i < page.SHIFT:
continue
if not os.path.exists(filename): continue
file_info = self.__format_stat(filename, get.path)
r_file = file_info['name'] + ';' + str(file_info['size']) + ';' + str(file_info['mtime']) + ';' + str(
file_info['accept']) + ';' + file_info['user'] + ';' + file_info['link']+';' + self.get_download_id(filename) + ';' + self.is_composer_json(filename)
if os.path.isdir(filename):
@@ -402,34 +402,46 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
return data
def __list_dir(self, path, my_sort='name', reverse=False):
'''
@name 获取文件列表并排序
@author hwliang<2020-08-01>
@param path<string> 路径
@param my_sort<string> 排序字段
@param reverse<bool> 是否降序
@param list
'''
if not os.path.exists(path):
return []
py_v = sys.version_info[0]
tmp_files = []
tmp_dirs = []
for f_name in os.listdir(path):
if py_v == 2:
f_name = f_name.encode('utf-8')
#使用.join拼接效率更高
filename = "/".join((path,f_name))
sort_key = 1
sort_val = None
#此处直接做异常处理比先判断文件是否存在更高效
try:
if py_v == 2:
f_name = f_name.encode('utf-8')
filename = os.path.join(path, f_name)
if not os.path.exists(filename):
continue
file_info = self.__format_stat(filename, path)
if not file_info:
continue
if os.path.isdir(filename):
tmp_dirs.append(file_info)
else:
tmp_files.append(file_info)
if my_sort == 'name':
sort_key = 0
elif my_sort == 'size':
sort_val = os.stat(filename).st_size
elif my_sort == 'mtime':
sort_val = os.stat(filename).st_mtime
elif my_sort == 'accept':
sort_val = os.stat(filename).st_mode
elif my_sort == 'user':
sort_val = os.stat(filename).st_uid
except:
continue
tmp_dirs = sorted(tmp_dirs, key=lambda x: x[my_sort], reverse=reverse)
tmp_files = sorted(
tmp_files, key=lambda x: x[my_sort], reverse=reverse)
#使用list[tuple]排序效率更高
tmp_files.append((f_name,sort_val))
for f in tmp_files:
tmp_dirs.append(f)
return tmp_dirs
tmp_files = sorted(tmp_files, key=lambda x: x[sort_key], reverse=reverse)
return tmp_files
def __format_stat(self, filename, path):
try:
@@ -923,7 +935,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
return public.returnMsg(False,'The file format does not support online editing!')
if os.path.getsize(get.path) > 3145928:
return public.returnMsg(False,'CANT_EDIT_ONLINE_FILE')
if not os.path.isfile(get.path):
if os.path.isdir(get.path):
return public.returnMsg(False,'This is not a file!')
fp = open(get.path,'rb')
data = {}
@@ -1856,8 +1868,10 @@ cd %s
"password":str(get.password), #提取密码
"addtime": mtime #添加时间
}
# 更新 or 插入
token = public.M(my_table).where('filename=?', (get.filename,)).getField('token')
if len(pdata['password']) < 4 and len(pdata['password']) > 0:
return public.returnMsg(False,'The extract password length cannot be less than 4 bits')
#更新 or 插入
token = public.M(my_table).where('filename=?',(get.filename,)).getField('token')
if token:
return public.returnMsg(False, 'Already shared!')
# pdata['token'] = token
@@ -2001,13 +2015,17 @@ cd %s
# 操作数据库
def _operate_db(self,q_sql,permissions_tb=None):
self._get_sqlite_connect()
c = self.sqlite_connection.cursor()
table = "index_tb"
if permissions_tb:
table = permissions_tb
sql_data = q_sql.replace("TB_NAME",table)
return c.execute(sql_data)
try:
self._get_sqlite_connect()
c = self.sqlite_connection.cursor()
table = "index_tb"
if permissions_tb:
table = permissions_tb
sql_data = q_sql.replace("TB_NAME",table)
return c.execute(sql_data)
except:
self._create_index_tb()
self._operate_db(q_sql,permissions_tb)
# 判断文件个数
def _get_file_total(self,path,num,date):
@@ -2039,6 +2057,18 @@ CREATE TABLE {}(
);""".format(tb_name)
self.sqlite_connection.execute(sql)
def _create_index_tb(self):
self._get_sqlite_connect()
sql = """
CREATE TABLE index_tb(
id INTEGER PRIMARY KEY AUTOINCREMENT,
permissions_tb CHAR ,
date CHAR,
remark CHAR,
first_path CHAR
);"""
self.sqlite_connection.execute(sql)
# 获取权限表名
def _get_permissions_tb_name(self,get_all_tb=None):
sql = 'select permissions_tb from TB_NAME'
@@ -2301,4 +2331,24 @@ CREATE TABLE {}(
os.chmod(filename,0o644)
except:
print(public.get_error_info())
return public.returnMsg(True,"Permission repair succeeded")
return public.returnMsg(True,"Permission repair succeeded")
def restore_website(self,args):
"""
@name 恢复站点文件
@author zhwen<zhw@bt.cn>
@parma file_name 备份得文件名
@parma site_id 网站id
"""
import panel_restore
pr=panel_restore.panel_restore()
return pr.restore_website_backup(args)
def get_progress(self,args):
"""
@name 获取进度日志
@author zhwen<zhw@bt.cn>
"""
import panel_restore
pr=panel_restore.panel_restore()
return pr.get_progress(args)
+4 -10
View File
@@ -26,10 +26,7 @@ class http:
from requests import get as req_get
return req_get(url,timeout=timeout,headers=get_headers(headers),verify=verify)
except:
if sys.version_info[0] == 2:
result = self._get_py2(url,timeout,headers,verify)
else:
result = self._get_py3(url,timeout,headers,verify)
result = self._get_curl(url,timeout,headers,verify)
elif type == 'curl':
result = self._get_curl(url,timeout,headers,verify)
elif type == 'php':
@@ -43,10 +40,7 @@ class http:
from requests import post as req_post
return req_post(url,data,timeout=timeout,headers=headers,verify=verify)
except:
if sys.version_info[0] == 2:
result = self._post_py2(url,data,timeout,headers,verify)
else:
result = self._post_py3(url,data,timeout,headers,verify)
result = self._post_curl(url,data,timeout,headers,verify)
elif type == 'curl':
result = self._post_curl(url,data,timeout,headers,verify)
elif type == 'php':
@@ -138,7 +132,7 @@ exit($header."\r\n\r\n".json_encode($body));
if php_version.find('/www/server/php') != -1:
result = public.ExecShell(php_version + ' ' + tmp_file + " --post='" + data + "'" )[0]
else:
result = public.request_php(php_version,'/http.php',tmp_file,'','POST',{"data":data})
result = public.request_php(php_version,'/http.php','/dev/shm','POST',{"data":data})
if os.path.exists(tmp_file): os.remove(tmp_file)
r_body,r_headers,r_status_code = self._curl_format(result)
@@ -240,7 +234,7 @@ exit($header."\r\n\r\n".json_encode($body));
if php_version.find('/www/server/php') != -1:
result = public.ExecShell(php_version + ' ' + tmp_file + " --post='" + data + "'" )[0]
else:
result = public.request_php(php_version,'/http.php',tmp_file,'','POST',{"data":data})
result = public.request_php(php_version,'/http.php','/dev/shm','POST',{"data":data})
if os.path.exists(tmp_file): os.remove(tmp_file)
r_body,r_headers,r_status_code = self._curl_format(result)
return response(json.loads(r_body).strip(),r_status_code,r_headers)
+58 -1
View File
@@ -105,11 +105,19 @@ def control_init():
public.ExecShell(c)
p_file = 'class/plugin2.so'
if os.path.exists(p_file): public.ExecShell("rm -f class/*.so")
public.ExecShell("chmod -R 600 /www/server/panel/data;chmod -R 600 /www/server/panel/config;chmod -R 700 /www/server/cron;chmod -R 600 /www/server/cron/*.log;chown -R root:root /www/server/panel/data;chown -R root:root /www/server/panel/config;chown -R root:root /www/server/phpmyadmin;chmod -R 755 /www/server/phpmyadmin")
# public.ExecShell("chmod -R 600 /www/server/panel/data;chmod -R 600 /www/server/panel/config;chmod -R 700 /www/server/cron;chmod -R 600 /www/server/cron/*.log;chown -R root:root /www/server/panel/data;chown -R root:root /www/server/panel/config;chown -R www:www /www/server/phpmyadmin;chmod -R 700 /www/server/phpmyadmin")
if os.path.exists("/www/server/mysql"):
public.ExecShell("chown mysql:mysql /etc/my.cnf;chmod 600 /etc/my.cnf")
stop_path = '/www/server/stop'
if not os.path.exists(stop_path):
os.makedirs(stop_path)
public.ExecShell("chown -R root:root {path};chmod -R 755 {path}".format(path=stop_path))
public.ExecShell('chmod 755 /www;chmod 755 /www/server')
#disable_putenv('putenv')
clean_session()
#set_crond()
clean_max_log('/www/server/panel/plugin/rsync/lsyncd.log')
clean_max_log('/var/log/rsyncd.log',1024*1024*10)
clean_max_log('/root/.pm2/pm2.log',1024*1024*20)
remove_tty1()
clean_hook_log()
@@ -119,6 +127,55 @@ def control_init():
check_dnsapi()
clean_php_log()
#update_py37()
files_set_mode()
#设置文件权限
def files_set_mode():
rr = {True:'-R',False:''}
m_paths = [
["/www/server/total","/*.lua","root",755,False],
["/www/server/total","/*.json","root",755,False],
["/www/server/total/logs","","www",755,True],
["/www/server/total/total","","www",755,True],
["/www/server/speed","/*.lua","root",755,False],
["/www/server/speed/total","","www",755,True],
["/www/server/btwaf","/*.lua","root",755,False],
["/www/server/panel/class","","root",600,True],
["/www/server/panel/data","","root",600,True],
["/www/server/panel/plugin","","root",600,False],
["/www/server/panel/BTPanel","","root",600,True],
["/www/server/panel/vhost","","root",600,True],
["/www/server/panel/rewrite","","root",600,True],
["/www/server/panel/config","","root",600,True],
["/www/server/panel/backup","","root",600,True],
["/www/server/panel/package","","root",600,True],
["/www/server/panel/script","","root",700,True],
["/www/server/panel/temp","","root",600,True],
["/www/server/panel/tmp","","root",600,True],
["/www/server/panel/ssl","","root",600,True],
["/www/server/panel/install","","root",600,True],
["/www/server/panel/logs","","root",600,True],
["/www/server/panel/BT-Panel","","root",700,False],
["/www/server/panel/BT-Task","","root",700,False],
["/www/server/panel","/*.py","root",600,False],
["/dev/shm/session.db","","root",600,False],
["/dev/shm/session_py3","","root",600,True],
["/dev/shm/session_py2","","root",600,True],
["/www/server/adminer","","www",700,True]
]
for m in m_paths:
if not os.path.exists(m[0]): continue
path = m[0] + m[1]
public.ExecShell("chown {R} {U}:{U} {P}".format(P=path,U=m[2],R=rr[m[4]]))
public.ExecShell("chmod {R} {M} {P}".format(P=path,M=m[3],R=rr[m[4]]))
if m[1]:
public.ExecShell("chown {U}:{U} {P}".format(P=m[0],U=m[2],R=rr[m[4]]))
public.ExecShell("chmod {M} {P}".format(P=m[0],M=m[3],R=rr[m[4]]))
#尝试升级到独立环境
+532 -12
View File
@@ -11,16 +11,31 @@
# | PHP插件兼容模块
# +-------------------------------------------------------------------
import json,os,public,time,re
from BTPanel import request
import json,os,public,time,re,sys
if __name__ != "__main__":
from BTPanel import request,abort,send_file,Resp,cache
import time
import fastcgiClient as fcgi_client
import struct
FCGI_Header = '!BBHHBx'
if sys.version_info[0] == 2:
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
else:
from io import BytesIO as StringIO
class panelPHP:
def __init__(self,plugin_name):
self.__plugin_name = plugin_name
self.__plugin_path = "/www/server/panel/plugin/%s" % plugin_name
self.__args_dir = self.__plugin_path + '/args'
self.__args_tmp = self.__args_dir + '/' + public.GetRandomString(32)
if not os.path.exists(self.__args_dir): os.makedirs(self.__args_dir, 384)
re_io = None
def __init__(self,plugin_name = None):
if plugin_name:
self.__plugin_name = plugin_name
self.__plugin_path = "/www/server/panel/plugin/%s" % plugin_name
self.__args_dir = self.__plugin_path + '/args'
self.__args_tmp = self.__args_dir + '/' + public.GetRandomString(32)
if not os.path.exists(self.__args_dir): os.makedirs(self.__args_dir, 384)
#调用PHP插件
def exec_php_script(self,args):
@@ -94,6 +109,511 @@ class panelPHP:
php_ini_body = re.sub(r".*bt_filter.+","",php_ini_body)
public.writeFile(php_ini,php_ini_body)
return php_path + php_v + '/bin/php -c ' + php_ini
def get_php_version(self,php_version):
if php_version:
if not isinstance(php_version,list):
php_vs = [php_version]
else:
php_vs = sorted(php_version,reverse=True)
else:
php_vs = ["80","74","73","72","71","70","56","55","54","53","52"]
php_path = "/www/server/php/"
php_v = None
for pv in php_vs:
php_bin = php_path + pv + "/bin/php"
if os.path.exists(php_bin) and os.path.exists("/tmp/php-cgi-{}.sock".format(pv)):
php_v = pv
break
return php_v
def get_phpmyadmin_phpversion(self):
'''
@name 获取当前phpmyadmin设置的PHP版本
@author hwliang<2020-07-13>
@return string
'''
ikey = 'pma_phpv'
phpv = cache.get(ikey)
if phpv: return phpv
webserver = public.get_webserver()
if webserver == 'nginx':
filename = public.GetConfigValue('setup_path') + '/nginx/conf/enable-php.conf'
conf = public.readFile(filename)
if not conf: return None
rep = r"php-cgi-(\d+)\.sock"
phpv = re.findall(rep,conf)
elif webserver == 'openlitespeed':
filename = public.GetConfigValue('setup_path') + "/panel/vhost/openlitespeed/detail/phpmyadmin.conf"
conf = public.readFile(filename)
if not conf: return None
rep = r"/usr/local/lsws/lsphp(\d+)/bin/lsphp"
phpv = re.findall(rep,conf)
else:
filename = public.GetConfigValue('setup_path') + '/apache/conf/extra/httpd-vhosts.conf'
conf = public.readFile(filename)
if not conf: return None
rep = r"php-cgi-(\d+)\.sock"
phpv = re.findall(rep,conf)
if not phpv: return None
cache.set(ikey,phpv[0],3)
return phpv[0]
def get_pma_root(self):
'''
@name 获取phpmyadmin根目录
@author hwliang<2020-07-13>
@return string
'''
pma_path = '/www/server/phpmyadmin/'
if not os.path.exists(pma_path):
os.makedirs(pma_path)
for dname in os.listdir(pma_path):
if dname.find('phpmyadmin_') != -1:
return os.path.join(pma_path,dname)
return None
def check_phpmyadmin_phpversion(self):
'''
@name 检查当前phpmyadmin版本可用的php版本列表
@author hwliang<2020-07-13>
@return list
'''
pma_path = '/www/server/phpmyadmin/'
pma_version_f1 = os.path.join(pma_path,'version_check.pl')
pma_root = os.path.join(pma_path,'pma')
pma_version_f2 = os.path.join(pma_root,'version_check.pl')
if not os.path.exists(pma_version_f1):
src_vfile = os.path.join(pma_path,'version.pl')
if os.path.exists(src_vfile):
public.writeFile(pma_version_f1,public.readFile(src_vfile))
v_sync = public.readFile(pma_version_f1) == public.readFile(pma_version_f2)
if not os.path.exists(pma_root + '/index.php') or not v_sync:
o_pma_root = self.get_pma_root()
if o_pma_root:
if not os.path.exists(pma_root):
os.makedirs(pma_root)
public.ExecShell("\cp -arf {}/* {}/".format(o_pma_root,pma_root))
public.ExecShell("chown -R www:www {}".format(pma_root))
public.ExecShell("chmod -R 700 {}".format(pma_root))
public.ExecShell("\cp -arf {} {}".format(pma_version_f1,pma_version_f2))
index = public.readFile(pma_root + '/index.php')
if index:
if index.find("use PhpMyAdmin\\Util") != -1:
resp = "use PhpMyAdmin\\Util;\nif(function_exists('opcache_invalidate')) opcache_invalidate('/www/server/phpmyadmin/pma/config.inc.php');"
index = index.replace("use PhpMyAdmin\\Util;",resp)
elif index.find("use PMA\libraries\LanguageManager;") != -1:
resp = "use PMA\libraries\LanguageManager;\nif(function_exists('opcache_invalidate')) opcache_invalidate('/www/server/phpmyadmin/pma/config.inc.php');"
index = index.replace("use PMA\libraries\LanguageManager;",resp)
elif index.find("require_once 'libraries/common.inc.php';") != -1:
resp = "if(function_exists('opcache_invalidate')) opcache_invalidate('/www/server/phpmyadmin/pma/config.inc.php');\nrequire_once 'libraries/common.inc.php';"
index = index.replace("require_once 'libraries/common.inc.php';",resp)
public.writeFile(pma_root + '/index.php',index)
if not os.path.exists(pma_version_f2):
return False
pma_version = public.readFile(pma_version_f2)
self.pma_version = pma_version
if pma_version:
pma_version = pma_version[:3]
if pma_version == '4.4':
return ['53','54','55','56']
elif pma_version == '4.0':
return ['52','53']
elif pma_version == '4.6':
return None
elif pma_version == '4.7':
return ['55','56','70','71','72']
elif pma_version in ['4.8','4.9','5.0']:
return ['70','71','72','73','74']
else:
return ['55','56','70','71','72']
def get_mysql_port(self):
'''
@name 获取mysql当前端口号
@author hwliang<2020-07-13>
@return int
'''
try:
myconf = public.readFile('/etc/my.cnf')
rep = r"port\s*=\s*([0-9]+)"
port = int(re.search(rep,myconf).groups()[0])
if not port: port = 3306
return port
except:
return 3306
def write_pma_passwd(self,username,password):
'''
@name 写入mysql帐号密码到配置文件
@author hwliang<2020-07-13>
@param username string(用户名)
@param password string(密码)
@return bool
'''
self.check_phpmyadmin_phpversion()
pconfig = 'cookie'
if username:
pconfig = 'config'
pma_path = '/www/server/phpmyadmin/'
pma_config_file = os.path.join(pma_path,'pma/config.inc.php')
conf = public.readFile(pma_config_file)
if not conf: return False
rep = r"/\* Authentication type \*/(.|\n)+/\* Server parameters \*/"
rstr = '''/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = '{}';
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['port'] = '{}';
$cfg['Servers'][$i]['user'] = '{}';
$cfg['Servers'][$i]['password'] = '{}';
/* Server parameters */'''.format(pconfig,self.get_mysql_port(),username,password)
conf = re.sub(rep,rstr,conf)
public.writeFile(pma_config_file,conf)
return True
def request_php(self,uri):
'''
@name 发起fastcgi请求到PHP-FPM
@author hwliang<2020-07-11>
@param puri string(URI地址)
@return socket
'''
php_unix_socket = '/tmp/php-cgi-{}.sock'.format(self.php_version)
f = FPM(php_unix_socket,self.document_root,self.last_path)
if request.full_path.find('?') != -1:
uri = request.full_path[request.full_path.find(uri):]
if self.re_io:
sock = f.load_url(uri,content=self.re_io)
else:
sock = f.load_url(uri,content=request.stream)
return sock
def start(self,puri,document_root,last_path = ''):
'''
@name 开始处理PHP请求
@author hwliang<2020-07-11>
@param puri string(URI地址)
@return socket or Response
'''
if puri in ['/','',None]: puri = 'index.php'
if puri[0] == '/': puri = puri[1:]
self.document_root = document_root
self.last_path = last_path
filename = document_root + puri
#如果是PHP文件
if puri[-4:] == '.php':
if request.path.find('/phpmyadmin/') != -1:
ikey = 'pma_php_version'
self.php_version = cache.get(ikey)
if not self.php_version:
php_version = self.get_phpmyadmin_phpversion()
php_versions = self.check_phpmyadmin_phpversion()
if not php_versions:
if php_versions == False:
return Resp(
'Phpmyadmin is not installed, or support for phpMyAdmin4.6 has been discontinued due to security issues, uninstall and install other secure versions in the software store!')
else:
return Resp('phpmyadmin is not installed')
if not php_version or not php_version in php_versions:
php_version = php_versions
self.php_version = self.get_php_version(php_version)
if not self.php_version:
php_version = self.check_phpmyadmin_phpversion()
self.php_version = self.get_php_version(php_version)
if not php_version:
return Resp('No supported PHP version found: {}'.format(php_versions))
if not self.php_version in php_versions:
self.php_version = self.get_php_version(php_versions)
if not self.php_version:
return Resp('No supported PHP version found: {}'.format(php_versions))
cache.set(ikey,self.php_version,1)
if request.method == 'POST':
#登录phpmyadmin
if puri in ['index.php','/index.php']:
content = public.url_encode(request.form.to_dict())
if not isinstance(content,bytes):
content = content.encode()
self.re_io = StringIO(content)
username = request.form.get('pma_username')
if username:
password = request.form.get('pma_password')
if not self.write_pma_passwd(username,password):
return Resp('Phpmyadmin is not installed')
if puri in ['logout.php', '/logout.php']:
self.write_pma_passwd(None, None)
else:
src_path = '/www/server/panel/adminer'
dst_path = '/www/server/adminer'
if os.path.exists(src_path):
if not os.path.exists(dst_path): os.makedirs(dst_path)
public.ExecShell("\cp -arf {}/* {}/".format(src_path, dst_path))
public.ExecShell("chown -R www:www {}".format(dst_path))
public.ExecShell("chmod -R 700 {}".format(dst_path))
public.ExecShell("rm -rf {}".format(src_path))
if not os.path.exists(dst_path + '/index.php'):
return Resp("The AdMiner file is missing. Please try again after the [Fix] panel on the first page!")
ikey = 'aer_php_version'
self.php_version = cache.get(ikey)
if not self.php_version:
self.php_version = self.get_php_version(None)
cache.set(ikey, self.php_version, 10)
if not self.php_version:
return Resp('没有找到可用的PHP版本')
#文件是否存在?
if not os.path.exists(filename):
return abort(404)
#发送到FPM
try:
return self.request_php(puri)
except Exception as ex:
if str(ex).find('No such file or directory') != -1:
return Resp('Specify PHP version: {}, not started, or unable to connect!'.format(self.php_version))
return Resp(str(ex))
if not os.path.exists(filename):
return abort(404)
#如果是静态文件
return send_file(filename)
#获取头部128KB数据
def get_header_data(self,sock):
'''
@name 获取头部32KB数据
@author hwliang<2020-07-11>
@param sock socketobject(fastcgi套接字对象)
@return bytes
'''
headers_data = b''
total_len = 0
header_len = 1024 * 128
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
headers_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3: break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd: break
headers_data += sd
tlen -= len(sd)
total_len += fast_pack[3]
if fast_pack[4]:
sock.recv(fast_pack[4])
if total_len > header_len: break
return headers_data
#格式化响应头
def format_header_data(self,headers_data):
'''
@name 格式化响应头
@author hwliang<2020-07-11>
@param headers_data bytes(fastcgi头部32KB数据)
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
'''
status = '200 OK'
headers = {}
pos = 0
while True:
eolpos = headers_data.find(b'\n', pos)
if eolpos < 0: break
line = headers_data[pos:eolpos-1]
pos = eolpos + 1
line = line.strip()
if len(line) < 2: break
if line.find(b':') == -1: continue
header, value = line.split(b':', 1)
header = header.strip()
value = value.strip()
if isinstance(header,bytes):
header = header.decode()
value = value.decode()
if header == 'Status':
status = value
if status.find(' ') < 0:
status += ' BTPanel'
else:
headers[header] = value
bdata = headers_data[pos:]
status = int(status.split(' ')[0])
return status,headers,bdata
#以流的方式发送剩余数据
def resp_sock(self,sock,bdata):
'''
@name 以流的方式发送剩余数据
@author hwliang<2020-07-11>
@param sock socketobject(fastcgi套接字对象)
@param bdata bytes(格式化响应头后的多余数据)
@return yield bytes
'''
#发送除响应头以外的多余头部数据
yield bdata
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
yield fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3: break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd: break
tlen -= len(sd)
if sd:
yield sd
if fast_pack[4]:
sock.recv(fast_pack[4])
sock.close()
class FPM(object):
def __init__(self,sock=None, document_root='',last_path = ''):
'''
@name 实例化FPM对象
@author hwliang<2020-07-11>
@param sock string(unixsocket路径)
@param document_root string(PHP文档根目录)
@return FPM
'''
if sock:
self.fcgi_sock = sock
if document_root[-1:] != '/':
document_root += '/'
self.document_root = document_root
self.last_path = last_path
def load_url(self, url, content=b''):
'''
@name 转发URL到PHP-FPM
@author hwliang<2020-07-11>
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': request.method,
'SCRIPT_NAME': self.last_path + script_name,
'REQUEST_URI': self.last_path + url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'BT-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': request.headers.get('Content-Type','application/x-www-form-urlencoded'),
'CONTENT_LENGTH': str(request.headers.get('Content-Length','0')),
'DOCUMENT_URI': request.path,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL' : 'HTTP/1.1',
'REMOTE_ADDR': request.remote_addr.replace('::ffff:',''),
'REMOTE_PORT': str(request.environ.get('REMOTE_PORT')),
'SERVER_ADDR': request.headers.get('host'),
'SERVER_PORT': '80',
'SERVER_NAME': 'BT-Panel',
}
for k in request.headers.keys():
key = 'HTTP_' + k.replace('-','_').upper()
env[key] = request.headers[k]
fpm_sock = fcgi(env, content)
return fpm_sock
def load_url_public(self,url,content = b'',method='GET',content_type='application/x-www-form-urlencoded'):
'''
@name 转发URL到PHP-FPM 公共
@author hwliang<2020-07-11>
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
content_length = len(content)
if content:
content = StringIO(content)
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': method,
'SCRIPT_NAME': self.last_path + script_name,
'REQUEST_URI': url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'BT-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': content_type,
'CONTENT_LENGTH': str(content_length),
'DOCUMENT_URI': script_name,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL' : 'HTTP/1.1',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '8888',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': 'BT-Panel'
}
fpm_sock = fcgi(env, content)
_data = b''
while True:
fastcgi_header = fpm_sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3: break
tlen = fast_pack[3]
while tlen > 0:
sd = fpm_sock.recv(tlen)
if not sd: break
tlen -= len(sd)
_data += sd
if fast_pack[4]:
fpm_sock.recv(fast_pack[4])
status,headers,data = panelPHP().format_header_data(_data)
return data
+26 -12
View File
@@ -297,6 +297,9 @@ class panelPlugin:
except:
if os.path.exists(lcoalTmp): os.remove(lcoalTmp)
if 'init' in get:
if softList: return softList
focre = 0
if hasattr(get,'force'): focre = int(get.force)
if 'focre_cloud' in session:
@@ -310,7 +313,8 @@ class panelPlugin:
if not softList or focre > 0:
self.clean_panel_log()
cloudUrl = 'http://www.bt.cn/api/panel/get_soft_list_en?v=6.6.6'
# cloudUrl = public.GetConfigValue('home') + '/api/panel/get_soft_list'
cloudUrl = 'https://console.aapanel.com/api/panel/get_soft_list'
import panelAuth
pdata = panelAuth.panelAuth().create_serverid(None)
listTmp = public.httpPost(cloudUrl,pdata,5)
@@ -638,7 +642,7 @@ class panelPlugin:
#取软件列表
def get_soft_list(self,get = None):
softList = self.get_cloud_list(get)
if not softList:
if not softList:
get.force = 1
softList = self.get_cloud_list(get)
if not softList: return public.returnMsg(False,'GET_SOFTLIST_FAIL',"401")
@@ -691,6 +695,7 @@ class panelPlugin:
if sName in indexList: return public.returnMsg(False,'DONT_ADD_AGAIN')
if len(indexList) >= 12:
softList = self.get_cloud_list(get)['list']
softList = self.set_coexist(softList)
for softInfo in softList:
# return softList
if softInfo['name'] == 'php':
@@ -822,12 +827,16 @@ class panelPlugin:
else:
softInfo['version'] = ""
if softInfo['version_coexist'] == 1:
self.get_icon(softInfo['name'].split('-')[0])
if softInfo['id'] != 10000:
self.get_icon(softInfo['name'].split('-')[0])
else:
if 'min_image' in softInfo:
self.get_icon(softInfo['name'],softInfo['min_image'])
if softInfo['id'] != 10000:
self.get_icon(softInfo['name'],softInfo['min_image'])
else:
self.get_icon(softInfo['name'])
if softInfo['id'] != 10000:
self.get_icon(softInfo['name'])
if softInfo['name'].find('php-') != -1:
v2= softInfo['versions'][0]['m_version'].replace('.','')
softInfo['fpm'] = os.path.exists('/www/server/php/' + v2 + '/sbin/php-fpm')
@@ -877,7 +886,11 @@ class panelPlugin:
for softInfo in softList:
if softInfo['name'] == sName:
if sName == 'phpmyadmin':
from BTPanel import get_phpmyadmin_dir
pmd = get_phpmyadmin_dir()
softInfo['ext'] = self.getPHPMyAdminStatus()
if softInfo['ext'] and pmd:
softInfo['ext']['url'] = 'http://' + public.GetHost() + ':'+ pmd[1] + '/' + pmd[0]
if "php-" in sName:
v = softInfo["versions"][0]["m_version"]
v1 = v.replace(".", "")
@@ -973,7 +986,7 @@ class panelPlugin:
def get_pids(self):
pids = []
for pid in os.listdir('/proc'):
if re.match("^\d+$",pid): pids.append(pid)
if re.match(r"^\d+$",pid): pids.append(pid)
return pids
@@ -1094,7 +1107,8 @@ class panelPlugin:
tmp = []
for d in data:
self.get_icon(d['name'])
if d['id'] != 10000:
self.get_icon(d['name'])
if display:
if d['display'] == 0: continue
i=0
@@ -1118,7 +1132,6 @@ class panelPlugin:
#获取图标
def get_icon(self,name,downFile = None):
iconFile = 'BTPanel/static/img/soft_ico/ico-' + name + '.png'
if not os.path.exists(iconFile):
self.download_icon(name,iconFile,downFile)
else:
@@ -1134,9 +1147,9 @@ class panelPlugin:
public.ExecShell(r"\cp -a -r " + srcIcon + " " + iconFile)
else:
if downFile:
public.ExecShell('wget -O ' + iconFile + ' ' + public.GetConfigValue('home') + downFile + '&')
public.ExecShell('wget -O ' + iconFile + ' ' + public.GetConfigValue('home') + downFile)
else:
public.ExecShell('wget -O ' + iconFile + ' ' + public.get_url() + '/install/plugin/' + name + '/icon.png &')
public.ExecShell('wget -O ' + iconFile + ' ' + public.get_url() + '/install/plugin/' + name + '/icon.png')
cache.set(skey,1,86400)
@@ -1878,7 +1891,8 @@ class panelPlugin:
return panelPHP.panelPHP(get.name).exec_php_script(get)
return public.returnMsg(False,'PLUGIN_INPUT_B')
if not self.check_accept(get):return public.returnMsg(False,public.to_string([24744, 26410, 36141, 20080, 91, 37, 115, 93, 25110, 25480, 26435, 24050, 21040, 26399, 33]) % (self.get_title_byname(get),))
sys.path.append(path)
if not path in sys.path:
sys.path.insert(0,path)
plugin_main = __import__(get.name+'_main')
try:
reload(plugin_main)
@@ -1887,7 +1901,7 @@ class panelPlugin:
if not hasattr(pluginObject,get.s): return public.returnMsg(False,'PLUGIN_INPUT_C',(get.s,))
execStr = 'pluginObject.' + get.s + '(get)'
return eval(execStr)
except Exception as ex:
except:
import traceback
errorMsg = traceback.format_exc()
public.writeFile('logs/done.log',errorMsg)
+189
View File
@@ -0,0 +1,189 @@
#coding: utf-8
#-------------------------------------------------------------------
# 宝塔Linux面板
#-------------------------------------------------------------------
# Copyright (c) 2015-2019 宝塔软件(http:#bt.cn) All rights reserved.
#-------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
#-------------------------------------------------------------------
import public,os
from BTPanel import request,abort,send_file
_PMD_PATH = '/www/server/phpmyadmin/phpmyadmin_b313c665284f1718/'
import time
import fastcgiClient as fcgi_client
import struct
FCGI_Header = '!BBHHBx'
class FPM(object):
def __init__(self,sock=None, document_root='/www/server/phpmyadmin/phpmyadmin_b313c665284f1718/'):
'''
@name 实例化FPM对象
@author hwliang<2020-07-11>
@param sock string(unixsocket路径)
@param document_root string(PHP文档根目录)
@return FPM
'''
if sock:
self.fcgi_sock = sock
if document_root[-1:] != '/':
document_root += '/'
self.document_root = document_root
def load_url(self, url, content=b''):
'''
@name 转发URL到PHP-FPM
@author hwliang<2020-07-11>
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': request.method,
'SCRIPT_NAME': '/phpmyadmin/'+script_name,
'REQUEST_URI': url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'BT-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': request.headers.get('Content-Type','application/x-www-form-urlencoded'),
'CONTENT_LENGTH': str(request.headers.get('Content-Length','0')),
'DOCUMENT_URI': request.path,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL' : 'HTTP/1.1',
'REMOTE_ADDR': request.remote_addr.replace('::ffff:',''),
'REMOTE_PORT': str(request.environ.get('REMOTE_PORT')),
'SERVER_ADDR': request.headers.get('host'),
'SERVER_PORT': '80',
'SERVER_NAME': 'BT-Panel',
}
for k in request.headers.keys():
key = 'HTTP_' + k.replace('-','_').upper()
env[key] = request.headers[k]
fpm_sock = fcgi(env, content)
return fpm_sock
def request_php(uri):
'''
@name 发起fastcgi请求到PHP-FPM
@author hwliang<2020-07-11>
param puri string(URI地址)
@return socket
'''
f = FPM(sock='/tmp/php-cgi-72.sock')
if request.full_path.find('?') != -1:
uri = request.full_path[request.full_path.find(uri):]
sock = f.load_url(uri,content=request.stream)
return sock
def start(puri):
'''
@name 开始处理PHP请求
@author hwliang<2020-07-11>
param puri string(URI地址)
@return socket or Response
'''
if puri in ['/','',None]: puri = '/index.php'
filename = _PMD_PATH + puri
if not os.path.exists(filename):
return abort(404)
#如果是PHP文件
if puri[-4:] == '.php':
return request_php(puri)
#如果是静态文件
return send_file(filename)
#获取头部32KB数据
def get_header_data(sock):
'''
@name 获取头部32KB数据
@author hwliang<2020-07-11>
param sock socketobject(fastcgi套接字对象)
@return bytes
'''
headers_data = b''
total_len = 0
header_len = 1024 * 32
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
headers_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
headers_data += sock.recv(fast_pack[3])
total_len += fast_pack[3]
if fast_pack[4]:
sock.recv(fast_pack[4])
if total_len > header_len: break
return headers_data
#格式化响应头
def format_header_data(headers_data):
'''
@name 格式化响应头
@author hwliang<2020-07-11>
@param headers_data bytes(fastcgi头部32KB数据)
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
'''
status = '200 OK'
headers = {}
pos = 0
while True:
eolpos = headers_data.find(b'\n', pos)
if eolpos < 0: break
line = headers_data[pos:eolpos-1]
pos = eolpos + 1
line = line.strip()
if len(line) < 2: break
header, value = line.split(b':', 1)
header = header.strip()
value = value.strip()
if header == 'Status':
status = value
if status.find(' ') < 0:
status += ' BTPanel'
else:
headers[header] = value
bdata = headers_data[pos:]
headers['Transfer-Encoding'] = 'chunked'
status = int(status.split(' ')[0])
return status,headers,bdata
#以流的方式发送剩余数据
def resp_sock(sock,bdata):
'''
@name 以流的方式发送剩余数据
@author hwliang<2020-07-11>
@param sock socketobject(fastcgi套接字对象)
@param bdata bytes(格式化响应头后的多余数据)
@return yield bytes
'''
#发送除响应头以外的多余头部数据
yield bdata
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
yield fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
data = sock.recv(fast_pack[3])
if fast_pack[4]:
sock.recv(fast_pack[4])
if not data: break
yield data
sock.close()
+1 -1
View File
@@ -194,7 +194,7 @@ class panelRedirect:
for d in json.loads(get.redirectdomain):
tu = self.GetToDomain(get.tourl)
if d == tu:
return public.GetMsg("DOMAIN_SAMEAS_URL",(d,))
return public.returnMsg(False,public.GetMsg("DOMAIN_SAMEAS_URL",(d,)))
if get.domainorpath == "path":
domains = self.GetAllDomain(get.sitename)
+35 -28
View File
@@ -264,6 +264,7 @@ setUIDMode 0
}
#VHOST_TYPE BT_SITENAME END
"""
self.old_name = self.siteName
if hasattr(get,"dirName"):
self.siteName = self.siteName + "_" + get.dirName
# sub_dir = self.sitePath + "/" + get.dirName
@@ -311,8 +312,8 @@ scripthandler {
extprocessor BTSITENAME {
type lsapi
address UDS://tmp/lshttpd/BT_EXTP_NAME.sock
maxConns 10
env LSAPI_CHILDREN=10
maxConns 20
env LSAPI_CHILDREN=20
initTimeout 600
retryTimeout 0
persistConn 1
@@ -364,10 +365,10 @@ include /www/server/panel/vhost/openlitespeed/proxy/BTSITENAME/*.conf
public.writeFile(file, conf)
# 生成伪静态文件
urlrewritePath = self.setupPath + '/panel/vhost/rewrite'
urlrewriteFile = urlrewritePath + '/' + self.siteName + '.conf'
if not os.path.exists(urlrewritePath): os.makedirs(urlrewritePath)
open(urlrewriteFile, 'w+').close()
# urlrewritePath = self.setupPath + '/panel/vhost/rewrite'
# urlrewriteFile = urlrewritePath + '/' + self.siteName + '.conf'
# if not os.path.exists(urlrewritePath): os.makedirs(urlrewritePath)
# open(urlrewriteFile, 'w+').close()
return True
#添加站点
@@ -442,7 +443,7 @@ include /www/server/panel/vhost/openlitespeed/proxy/BTSITENAME/*.conf
self.DelUserInI(self.sitePath)
userIni = self.sitePath+'/.user.ini'
if not os.path.exists(userIni):
public.writeFile(userIni, 'open_basedir='+self.sitePath+'/:/tmp/:/proc/')
public.writeFile(userIni, 'open_basedir='+self.sitePath+'/:/tmp/')
public.ExecShell('chmod 644 ' + userIni)
public.ExecShell('chown root:root ' + userIni)
public.ExecShell('chattr +i '+userIni)
@@ -1132,7 +1133,7 @@ listener Default%s{
result = self.GetSiteRunPath(get)
if 'runPath' in result:
return result['runPath']
return result
return False
# 创建Let's Encrypt免费证书
@@ -2359,15 +2360,14 @@ server
public.serviceReload()
public.WriteLog('TYPE_SITE', 'SITE_BINDING_DEL_SUCCESS',(siteName,binding['path']))
return public.returnMsg(True,'DEL_SUCCESS')
#取默认文档
#取子目录Rewrite
def GetDirRewrite(self,get):
id = get.id
find = public.M('binding').where("id=?",(id,)).field('id,pid,domain,path').find()
site = public.M('sites').where("id=?",(find['pid'],)).field('id,name,path').find()
if(public.get_webserver() == 'apache'):
if(public.get_webserver() != 'nginx'):
filename = site['path']+'/'+find['path']+'/.htaccess'
else:
filename = self.setupPath + '/panel/vhost/rewrite/'+site['name']+'_'+find['path']+'.conf'
@@ -2389,7 +2389,10 @@ server
data['status'] = True
data['data'] = public.readFile(filename)
data['rlist'] = []
for ds in os.listdir('rewrite/' + public.get_webserver()):
webserver = public.get_webserver()
if webserver == "openlitespeed":
webserver = "apache"
for ds in os.listdir('rewrite/' + webserver):
if ds == 'list.txt': continue
data['rlist'].append(ds[0:len(ds)-5])
data['filename'] = filename
@@ -2590,9 +2593,10 @@ server
conf = public.readFile(file)
if conf:
rep = 'lsphp\d+'
tmp = re.search(rep, conf).group()
conf = conf.replace(tmp, 'lsphp' + version)
public.writeFile(file, conf)
tmp = re.search(rep, conf)
if tmp:
conf = conf.replace(tmp.group(), 'lsphp' + version)
public.writeFile(file, conf)
public.serviceReload()
public.WriteLog("TYPE_SITE", "SITE_PHPVERSION_SUCCESS",(siteName,version))
return public.returnMsg(True,'SITE_PHPVERSION_SUCCESS',(siteName,version))
@@ -2661,15 +2665,15 @@ server
if conf and "session.save_path" in conf:
rep = "session.save_path\s*=\s*(.*)"
s_path = re.search(rep,conf).groups(1)[0]
public.writeFile(filename, conf + '\nopen_basedir={}/:/tmp/:/proc/:{}'.format(path,s_path))
public.writeFile(filename, conf + '\nopen_basedir={}/:/tmp/:{}'.format(path,s_path))
else:
public.writeFile(filename,'open_basedir={}/:/tmp/:/proc/'.format(path))
public.writeFile(filename,'open_basedir={}/:/tmp/'.format(path))
public.ExecShell("chattr +i " + filename)
public.serviceReload()
return public.returnMsg(True,'SITE_BASEDIR_OPEN_SUCCESS')
except Exception as e:
public.ExecShell("chattr +i " + filename)
return e
return str(e)
def _set_ols_open_basedir(self,get):
# 设置ols
@@ -2924,7 +2928,8 @@ server
self.CheckProxy(get)
ng_conf = public.readFile(ng_file)
if not p_conf:
rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.+[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
# rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.+[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{1,66}[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
ng_conf = re.sub(rep, 'include enable-php-', ng_conf)
oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
@@ -2938,7 +2943,8 @@ server
error_log off;
access_log off;
}'''
ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log")
if "(gif|jpg|jpeg|png|bmp|swf)$" not in ng_conf:
ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log")
public.writeFile(ng_file, ng_conf)
return
sitenamelist = []
@@ -2954,7 +2960,7 @@ server
public.writeFile(ng_file,ng_conf)
else:
rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{66,66}\n+[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{1,66}[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
ng_conf = re.sub(rep,'include enable-php-',ng_conf)
oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
@@ -2968,7 +2974,8 @@ server
error_log off;
access_log off;
}'''
ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log")
if "(gif|jpg|jpeg|png|bmp|swf)$" not in ng_conf:
ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log")
public.writeFile(ng_file, ng_conf)
# 设置apache配置
@@ -3143,7 +3150,7 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s]
ng_conf = re.sub("location\s+%s" % conf[i]["proxydir"],"location "+get.proxydir,ng_conf)
ng_conf = re.sub("proxy_pass\s+%s" % conf[i]["proxysite"],"proxy_pass "+get.proxysite,ng_conf)
ng_conf = re.sub("\sHost\s+%s" % conf[i]["todomain"]," Host "+get.todomain,ng_conf)
cache_rep = "proxy_cache_valid\s+200\s+304\s+301\s+302\s+\d+m;((\n|.)+expires\s+\d+m;)*"
cache_rep = r"proxy_cache_valid\s+200\s+304\s+301\s+302\s+\d+m;((\n|.)+expires\s+\d+m;)*"
if int(get.cache) == 1:
if re.search(cache_rep,ng_conf):
expires_rep = "\{\n\s+expires\s+12h;"
@@ -3160,16 +3167,16 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s]
ng_conf = re.sub(cache_rep,'\n\t#Set Nginx Cache\n'+ng_cache,ng_conf)
else:
# cache_rep = '#proxy_set_header\s+Connection\s+"upgrade";'
cache_rep = 'proxy_set_header\s+REMOTE-HOST\s+\$remote_addr;'
ng_conf = re.sub(cache_rep, '\n\tproxy_set_header\s+REMOTE-HOST\s+\$remote_addr;\n\t#Set Nginx Cache' + ng_cache,
cache_rep = r"proxy_set_header\s+REMOTE-HOST\s+\$remote_addr;"
ng_conf = re.sub(cache_rep, r"\n\tproxy_set_header\s+REMOTE-HOST\s+\$remote_addr;\n\t#Set Nginx Cache" + ng_cache,
ng_conf)
else:
if self.check_annotate(ng_conf):
rep = '\n\s*#Set\s*Nginx\s*Cache(.|\n)*\d+m;'
rep = r'\n\s*#Set\s*Nginx\s*Cache(.|\n)*\d+m;'
ng_conf = re.sub(rep, "\n\t#Set Nginx Cache\n\tproxy_ignore_headers Set-Cookie Cache-Control expires;\n\tadd_header Cache-Control no-cache;", ng_conf)
else:
rep = '\s+proxy_cache\s+cache_one.*[\n\s\w\_\";\$]+m;'
ng_conf = re.sub(rep, '\n\t#Set Nginx Cache\n\tproxy_ignore_headers Set-Cookie Cache-Control expires;\n\tadd_header Cache-Control no-cache;', ng_conf)
rep = r"\s+proxy_cache\s+cache_one.*[\n\s\w\_\";\$]+m;"
ng_conf = re.sub(rep, r"\n\t#Set Nginx Cache\n\tproxy_ignore_headers Set-Cookie Cache-Control expires;\n\tadd_header Cache-Control no-cache;", ng_conf)
sub_rep = "sub_filter"
subfilter = json.loads(get.subfilter)
+8 -2
View File
@@ -473,8 +473,14 @@ class bt_task:
# 设置权限
def set_file_accept(self, filename):
public.ExecShell('chown -R www:www ' + filename)
public.ExecShell('chmod -R 755 ' + filename)
# public.ExecShell('chown -R www:www ' + filename)
# public.ExecShell('chmod -R 755 ' + filename)
import files
from collections import namedtuple
get = namedtuple('get',['path'])
get.path = filename
public.writeFile('/tmp/2',str(get.path))
files.files().fix_permissions(get)
# 检查敏感目录
def check_dir(self, path):
+174
View File
@@ -0,0 +1,174 @@
#coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Linux面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: hwliang <2020-08-04>
# +-------------------------------------------------------------------
import os,sys,json,time,public
class panelWarning:
__path = '/www/server/panel/data/warning'
__ignore = __path + '/ignore'
__result = __path + '/result'
def __init__(self):
if not os.path.exists(self.__ignore):
os.makedirs(self.__ignore,384)
if not os.path.exists(self.__result):
os.makedirs(self.__result,384)
def get_list(self,args):
self.sync_rule()
p = public.get_modules('class/safe_warning')
data = {
'security':[],
'risk':[],
'ignore':[]
}
for m_name in p.__dict__.keys():
m_info = {
'title': p[m_name]._title,
'm_name': m_name,
'ps': p[m_name]._ps,
'version': p[m_name]._version,
'level': p[m_name]._level,
'ignore': p[m_name]._ignore,
'date': p[m_name]._date,
'tips': p[m_name]._tips,
'help': p[m_name]._help
}
result_file = self.__result + '/' + m_name + '.pl'
not_force = True
if 'force' in args:
not_force = m_info['ignore']
if os.path.exists(result_file) and not_force:
m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking'] = json.loads(public.readFile(result_file))
else:
try:
s_time = time.time()
m_info['status'],m_info['msg'] = p[m_name].check_run()
m_info['taking'] = round(time.time() - s_time,6)
m_info['check_time'] = int(time.time())
public.writeFile(result_file,json.dumps([m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking']],))
except:
continue
if m_info['ignore']:
data['ignore'].append(m_info)
else:
if m_info['status']:
data['security'].append(m_info)
else:
data['risk'].append(m_info)
data['risk'] = sorted(data['risk'],key=lambda x: x['level'],reverse=True)
data['security'] = sorted(data['security'],key=lambda x: x['level'],reverse=True)
data['ignore'] = sorted(data['ignore'],key=lambda x: x['level'],reverse=True)
return data
def sync_rule(self):
'''
@name 从云端同步规则
@author hwliang<2020-08-05>
@return void
'''
try:
dep_path = '/www/server/panel/class/safe_warning'
local_version_file = self.__path + '/version.pl'
last_sync_time = local_version_file = self.__path + '/last_sync.pl'
if os.path.exists(dep_path):
if os.path.exists(last_sync_time):
if int(public.readFile(last_sync_time)) > time.time():
return
else:
if os.path.exists(local_version_file): os.remove(local_version_file)
download_url = public.get_url()
version_url = download_url + '/install/warning/version.txt'
cloud_version = public.httpGet(version_url)
if cloud_version: cloud_version = cloud_version.strip()
local_version = public.readFile(local_version_file)
if local_version:
if cloud_version == local_version:
return
tmp_file = '/tmp/bt_safe_warning.zip'
public.ExecShell('wget -O {} {} -T 5'.format(tmp_file,download_url + '/install/warning/safe_warning_en.zip'))
if not os.path.exists(tmp_file):
return
if os.path.getsize(tmp_file) < 2129:
os.remove(tmp_file)
return
if not os.path.exists(dep_path):
os.makedirs(dep_path,384)
public.ExecShell("unzip -o {} -d {}/ >/dev/null".format(tmp_file,dep_path))
public.writeFile(local_version_file,cloud_version)
public.writeFile(last_sync_time,str(int(time.time() + 7200)))
if os.path.exists(tmp_file): os.remove(tmp_file)
public.ExecShell("chmod -R 600 {}".format(dep_path))
except:
pass
def set_ignore(self,args):
'''
@name 设置指定项忽略状态
@author hwliang<2020-08-04>
@param dict_obj {
m_name<string> 模块名称
}
@return dict
'''
m_name = args.m_name.strip()
ignore_file = self.__ignore + '/' + m_name + '.pl'
if os.path.exists(ignore_file):
os.remove(ignore_file)
else:
public.writeFile(ignore_file,'1')
return public.returnMsg(True,'Setup Successfully!')
def check_find(self,args):
'''
@name 检测指定项
@author hwliang<2020-08-04>
@param dict_obj {
m_name<string> 模块名称
}
@return dict
'''
try:
m_name = args.m_name.strip()
p = public.get_modules('class/safe_warning')
m_info = {
'title': p[m_name]._title,
'm_name': m_name,
'ps': p[m_name]._ps,
'version': p[m_name]._version,
'level': p[m_name]._level,
'ignore': p[m_name]._ignore,
'date': p[m_name]._date,
'tips': p[m_name]._tips,
'help': p[m_name]._help
}
result_file = self.__result + '/' + m_name + '.pl'
s_time = time.time()
m_info['status'],m_info['msg'] = p[m_name].check_run()
m_info['taking'] = round(time.time() - s_time,4)
m_info['check_time'] = int(time.time())
public.writeFile(result_file,json.dumps([m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking']]))
return public.returnMsg(True,'Retested')
except:
return public.returnMsg(False,'Bad module name')
+184
View File
@@ -0,0 +1,184 @@
# coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http:#bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: zhwwen <zhw@bt.cn>
# -------------------------------------------------------------------
#
# ------------------------------
# 网站恢复
# ------------------------------
import public,os,files,sys
from time import sleep
class panel_restore:
_local_file = '/tmp/{}'
_progress_file = '/tmp/restore_site.log'
# def __init__(self):
# # 清空日志文件
def _progress_rewrite(self,content,mothed='a+'):
sleep(2)
public.writeFile(self._progress_file,content+'\n',mothed)
def _get_local_backup_path(self):
local_backdir = public.M('config').field('backup_path').find()['backup_path']
return local_backdir
def _build_aws_backup_path(self,file_name,domain):
config_file = "/www/server/panel/plugin/aws_s3/config.conf"
conf = public.readFile(config_file)
backup_path = conf.split('|')[-1].strip()+'site/'+ domain + '/' + file_name
return backup_path
def _build_google_backup_path(self,file_name,domain):
object_name = 'bt_backup/site/{}/{}'.format(domain,file_name)
return object_name
def _get_backfile_method(self,filename):
backup_info = public.M('backup').where("name=?", (filename,)).getField('filename')
backup_info = backup_info.split('|')
if len(backup_info) >= 3:
method = backup_info[1]
else:
method = 'local'
return method
def _remove_old_website_file_to_trush(self,args):
# 将原来目录移至回收站
files.files().DeleteDir(args)
def _get_website_info(self,site_id):
site_name = public.M('sites').where("id=?",(site_id,)).getField('name')
site_path = public.M('sites').where("id=?",(site_id,)).getField('path')
return {'site_name':site_name,'site_path':site_path}
def _restore_backup(self,local_backup_file_path,site_info,args):
# 判断备份文件是否存在,如果不存在继续检查是否远程备份
if not os.path.exists(local_backup_file_path):
self._progress_rewrite('No backup file found: {}'.format(str(local_backup_file_path)))
return public.returnMsg(False, 'Panel does not find the backup file: {}'.format(local_backup_file_path))
# 将网站目录移至回收站
self._progress_rewrite('Move the current website directory to the recycle bin: {}'.format(str(args.path)))
self._remove_old_website_file_to_trush(args)
if not os.path.exists(args.path):
self._progress_rewrite('Create an empty directory for the site: {}'.format(str(args.path)))
os.makedirs(site_info['site_path'])
if 'zip' in args.file_name:
uncompress_comand = 'unzip'
else:
uncompress_comand = 'tar -zxvf'
self._progress_rewrite('The decompression command is: {}'.format(str(uncompress_comand)))
self._progress_rewrite('Start to restore data......')
public.ExecShell('cd {} && {} {} >> /tmp/restore_site.log'.format(site_info['site_path'], uncompress_comand, local_backup_file_path))
if len(os.listdir(site_info['site_path'])) == 2:
public.ExecShell('cd {s} && mv {s}/{d}/* .'.format(s=site_info['site_path'],d=args.site_name))
public.ExecShell('cd {s} && rmdir {d}'.format(s=site_info['site_path'],d=args.site_name))
# 将文件全新设置为644,文件夹设置为755
self._progress_rewrite('Setting site permissions......')
files.files().fix_permissions(args)
def _download_aws_file(self,args):
sys.path.append('/www/server/panel/plugin/aws_s3')
import aws_s3_main
aws3 = aws_s3_main.aws_s3_main()
self._progress_rewrite('Building S3 download path...')
download_file = self._build_aws_backup_path(args.file_name,args.site_name)
self._progress_rewrite('The download path is:{}'.format(download_file))
self._local_file = self._local_file.format(args.file_name)
self._progress_rewrite('Backup file will be downloaded to:{}'.format(self._local_file))
self._progress_rewrite('Starting to download file:{}'.format(self._local_file))
args.object_name = download_file
args.local_file = self._local_file
aws3.download_file(args)
self._progress_rewrite('Download completed:{}'.format(self._local_file))
return self._local_file
def _download_google_cloud_file(self,args):
sys.path.append('/www/server/panel/plugin/gcloud_storage')
import gcloud_storage_main
gs = gcloud_storage_main.gcloud_storage_main()
self._progress_rewrite('Building Google Store download path...')
download_file = self._build_google_backup_path(args.file_name,args.site_name)
self._progress_rewrite('The download path is:{}'.format(download_file))
self._local_file = self._local_file.format(args.file_name)
self._progress_rewrite('Backup file will be downloaded to:{}'.format(self._local_file))
self._progress_rewrite('Starting to download file:{}'.format(self._local_file))
args.source_blob_name = download_file
args.destination_file_name = self._local_file
gs.download_blob(args)
self._progress_rewrite('Download completed:{}'.format(self._local_file))
return self._local_file
def _download_google_drive_file(self,args):
sys.path.append('/www/server/panel/plugin/gdrive')
import gdrive_main
gd = gdrive_main.gdrive_main()
# self._progress_rewrite('Building Google Store download path...')
# download_file = self._build_google_backup_path(args.file_name,args.site_name)
# self._progress_rewrite('The download path is:{}'.format(download_file))
self._local_file = self._local_file.format(args.file_name)
self._progress_rewrite('Backup file will be downloaded to:{}'.format(self._local_file))
self._progress_rewrite('Starting to download file:{}'.format(self._local_file))
# args.source_blob_name = download_file
# args.destination_file_name = self._local_file
gd.download_file(args.file_name)
self._progress_rewrite('Download completed:{}'.format(self._local_file))
return self._local_file
def restore_website_backup(self,args):
"""
@name 恢复站点文件
@author zhwen<zhw@bt.cn>
@parma file_name 备份得文件名
@parma site_id 网站id
"""
self._progress_rewrite('','w')
site_info = self._get_website_info(args.site_id)
self._progress_rewrite('Get site information:{}'.format(str(site_info)))
args.path = site_info['site_path']
args.site_name = site_info['site_name']
self._progress_rewrite('Get the site path:{}'.format(str(site_info['site_path'])))
local_backup_path = self._get_local_backup_path()
local_backup_file_path = local_backup_path +'/site/'+ args.file_name
self._progress_rewrite('Get the local backup file path: {}'.format(str(local_backup_path)))
backup_method = self._get_backfile_method(args.file_name)
self._progress_rewrite('Get the backup method: {}'.format(str(backup_method)))
if backup_method == 'local':
self._progress_rewrite('Start to restore local backup files: {}'.format(str(local_backup_file_path)))
result = self._restore_backup(local_backup_file_path,site_info,args)
if result:
self._progress_rewrite('Recovery failed: {}'.format(str(site_info['site_path'])))
return result
elif backup_method == 'aws_s3':
self._download_aws_file(args)
result = self._restore_backup(self._local_file, site_info, args)
elif backup_method == 'Google Cloud':
self._download_google_cloud_file(args)
result = self._restore_backup(self._local_file, site_info, args)
elif backup_method == 'Google Drive':
self._download_google_drive_file(args)
result = self._restore_backup(self._local_file, site_info, args)
else:
return public.ExecShell(False,'Currently only supports restoring local, Google storage and AWS S3 backups')
os.remove(self._local_file)
if result:
self._progress_rewrite('Recovery failed: {}'.format(str(site_info['site_path'])))
return result
self._progress_rewrite('Successful recovery: {}'.format(str(site_info['site_path'])))
return public.returnMsg(True,'Restore Successful')
# 取任务进度
def get_progress(self, get):
"""
@name 获取进度日志
@author zhwen<zhw@bt.cn>
"""
# result = public.GetNumLines(self._progress_file, 20)
result = public.ExecShell('tail -n 20 {}'.format(self._progress_file))[0]
if len(result) < 1:
return {'msg':"Wait for the restore to start"}
return {'msg':result}
+148 -23
View File
@@ -19,6 +19,8 @@ _LAN_TEMPLATE = None
if sys.version_info[0] == 2:
reload(sys)
sys.setdefaultencoding('utf8')
else:
from importlib import reload
def M(table):
"""
@@ -125,7 +127,7 @@ def HttpPost(url,data,timeout = 6,headers = {}):
import http_requests
res = http_requests.post(url,data=data,timeout=timeout,headers = headers)
if res.status_code == 0:
WriteLog('Request error',res.text)
# WriteLog('Request error',res.text)
if old_url.find(home) != -1: return http_post_home(old_url,data,timeout,res.text)
if headers: return False
s_body = res.text
@@ -185,7 +187,7 @@ def Md5(strings):
@param strings 要被处理的字符串
@return string(32)
"""
if type(strings) == str:
if type(strings) != bytes:
strings = strings.encode()
import hashlib
m = hashlib.md5()
@@ -854,9 +856,9 @@ def checkWebConfig():
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 + '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'
@@ -1554,9 +1556,8 @@ def auto_backup_panel():
#检查端口状态
def check_port_stat(port):
def check_port_stat(port,localIP = '127.0.0.1'):
import socket
localIP = '127.0.0.1'
temp = {}
temp['port'] = port
temp['local'] = True
@@ -1671,22 +1672,11 @@ def en_hexb(data):
#filename 要执行的php文件
#args 请求参数
#method 请求方式
def request_php(version,uri,filename,args,method='GET',pdata='',timeout=3000):
import fastcgi_client
client= fastcgi_client.fastcgi_client('/tmp/php-cgi-'+version+'.sock',None, timeout, 0)
if type(args) == dict: args = url_encode(args)
def request_php(version,uri,document_root,method='GET',pdata=b''):
import panelPHP
if type(pdata) == dict: pdata = url_encode(pdata)
params = {
'REQUEST_METHOD': method,
'SCRIPT_FILENAME': filename,
'SCRIPT_NAME': uri,
'SERVER_PROTOCOL': 'HTTP/1.1',
'GATEWAY_INTERFACE': 'CGI/1.1',
'QUERY_STRING': args,
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(pdata)
}
result = client.request(params,pdata)
p = panelPHP.FPM('/tmp/php-cgi-'+version+'.sock',document_root)
result = p.load_url_public(uri,pdata,method)
return result
@@ -1699,6 +1689,15 @@ def url_encode(data):
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:
@@ -1810,6 +1809,8 @@ def get_linux_distribution():
tmp = readFile(redhat_file).split()[3][0]
if int(tmp) > 7:
distribution = 'centos8'
else:
distribution = 'centos7'
except:
distribution = 'centos7'
return distribution
@@ -1839,10 +1840,15 @@ def ip2long(ip):
iplong = 2 ** 24 * int(ips[0]) + 2 ** 16 * int(ips[1]) + 2 ** 8 * int(ips[2]) + int(ips[3])
return iplong
#获取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
return request.cookies.get('BT_PANEL_6')
return request.cookies.get('SESSIONID','')
def chdck_salt():
'''
@@ -1862,6 +1868,16 @@ def chdck_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 request
return md5(get_login_token() + request.headers.get('User-Agent',''))
def password_salt(password,username=None,uid=None):
'''
@name 为指定密码加盐
@@ -1879,6 +1895,32 @@ def password_salt(password,username=None,uid=None):
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))
#取通用对象
class dict_obj:
def __contains__(self, key):
@@ -1889,3 +1931,86 @@ class dict_obj:
def __delattr__(self, key): delattr(self,key)
def get_items(self): return self
#实例化定目录下的所有模块
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)
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 数据库备份检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Database backup'
_version = 1.0 # 版本
_ps = "Checks whether all databases are set up for periodic backup" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-04' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_database_backup.pl")
_tips = [
"On the [ Cron ] page, set the database that is not backed up, or set all databases to be backed up",
"Tip: if the database is not set up for regular backup, once the data is lost accidentally and cannot be recovered, the loss will be huge"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-03>
@return tuple (status<bool>,msg<string>)
'''
if public.M('crontab').where('sType=? AND sName=?',('database','ALL')).count():
return True,'无风险'
db_list = public.M('databases').field('name').select()
not_backups = []
sql = public.M('crontab')
for db in db_list:
if sql.where('sType=? AND sName=?',('database',db['name'])).count():
continue
not_backups.append(db['name'])
if not_backups:
return False ,'The following databases are not set up for regular backup: <br />' + ('<br />'.join(not_backups))
return True,'Risk-free'
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测是否开debug模式
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Developer Mode'
_version = 1.0 # 版本
_ps = "Checks whether panel developer mode is enabled" # 描述
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_debug_mode.pl")
_tips = [
"Turn off developer mode on the [ Settings ] page",
"Note: Developer mode is only used for panel plug-in or API development, do not use in production environment"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-05>
@return tuple (status<bool>,msg<string>)
'''
if os.path.exists('/www/server/panel/data/debug.pl'):
return False,'[Developer mode] has been opened, and risks such as data communication and information leakage exist'
return True,'Risk-free'
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测关键目录权限是否正确
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'System directory permissions'
_version = 1.0 # 版本
_ps = "Checks if the System directory permissions are correct" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_dir_mode.pl")
_tips = [
"On the [ File ] page, set the correct permissions and owner for the specified directory or file",
"Note 1: When setting directory permissions through the [File] page, please cancel the [Apply to subdirectories] option",
"Note 2: Incorrect file permissions not only pose a security risk, but also may cause some software on the server to fail to work properly"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-05>
@return tuple (status<bool>,msg<string>)
'''
dir_list = [
['/usr',755,'root'],
['/usr/bin',555,'root'],
['/usr/sbin',555,'root'],
['/usr/lib',555,'root'],
['/usr/lib64',555,'root'],
['/usr/local',755,'root'],
['/etc',755,'root'],
['/etc/passwd',644,'root'],
['/etc/shadow',000,'root'],
['/etc/gshadow',000,'root'],
['/etc/cron.deny',600,'root'],
['/etc/anacrontab',600,'root'],
['/var',755,'root'],
['/var/spool',755,'root'],
['/var/spool/cron',700,'root'],
['/var/spool/cron/root',600,'root'],
['/var/spool/cron/crontabs/root',600,'root'],
['/www',755,'root'],
['/www/server',755,'root'],
['/www/wwwroot',755,'root'],
['/root',550,'root'],
['/mnt',755,'root'],
['/home',755,'root'],
['/dev',755,'root'],
['/opt',755,'root'],
['/sys',555,'root'],
['/run',755,'root'],
['/tmp',777,'root']
]
not_mode_list = []
for d in dir_list:
if not os.path.exists(d[0]): continue
u_mode = public.get_mode_and_user(d[0])
if u_mode['user'] != d[2]:
not_mode_list.append("{} Current permissions: {} : {} Security permissions: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
if int(u_mode['mode']) != d[1]:
not_mode_list.append("{} Current permissions: {} : {} Security permissions: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
if not_mode_list:
return False,'The following system file or directory permissions are incorrect: <br />' + ("<br />".join(not_mode_list))
return True,'Risk-free'
@@ -0,0 +1,39 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测是否开启文件回收站
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'File Recycle Bin'
_version = 1.0 # 版本
_ps = "Check whether the file recycle bin is open" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_files_recycle_bin.pl")
_tips = [
"On the [File] page, [Recycle Bin] - opens the [File Recycle Bin] function"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-05>
@return tuple (status<bool>,msg<string>)
'''
if not os.path.exists('/www/server/panel/data/recycle_bin.pl'):
return False,'The function of [File Recycle Station] is not enabled at present. There is a risk that files cannot be retrieved in case of being deleted by mistake'
return True,'Risk-free'
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 系统防火墙检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'System firewall'
_version = 1.0 # 版本
_ps = "Check whether the system firewall is enable" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_firewall_open.pl")
_tips = [
"It is recommended to enable the system firewall to prevent all server ports from being exposed to the Internet. If the server has [security group] function, please ignore this prompt",
"Note: To open the system firewall, the ports that need to be opened, especially SSH and panel ports, should be added to the release list in advance, otherwise the server may not be able to access them"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-04>
@return tuple (status<bool>,msg<string>)
'''
if os.path.exists('/usr/sbin/firewalld'):
if public.ExecShell("systemctl status firewalld|grep 'active (running)'")[0]:
return True,'Risk-free'
elif os.path.exists('/usr/sbin/ufw'):
if public.ExecShell("ufw status|grep 'Status: active'")[0]:
return True,'Risk-free'
else:
if public.ExecShell("service iptables status|grep 'Table: filter'")[0]:
return True,'Risk-free'
return False,'The system firewall is not opened, and there is a security risk'
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测用户登录通知
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'SSH user login notification'
_version = 1.0 # 版本
_ps = "Check whether SSH user login notification is enabled" # 描述
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_login_message.pl")
_tips = [
"On the [Security] page, [SSH security management] - [login alarm] enable the [monitor root login] function"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-04>
@return tuple (status<bool>,msg<string>)
'''
data = public.ReadFile('/etc/bashrc')
if not data: return True,'Risk-free'
if re.search('python /www/server/panel/class/ssh_security.py login', data):
return True,'Risk-free'
else:
return False,'SSH user login notification is not configured, so it is impossible to know whether the server has been illegally logged in in the first place'
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测风险用户
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Risk User'
_version = 1.0 # 版本
_ps = "Detect if there is a risk user in the system user list" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_login_user.pl")
_tips = [
"If these users are not added by the server administrator, the system may have been compromised and should be dealt with as soon as possible."
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-04>
@return tuple (status<bool>,msg<string>)
'''
u_list = get_ulist()
try_users = []
for u_info in u_list:
if u_info['user'] == 'root': continue
if u_info['pass'] == '*': continue
if u_info['uid'] == 0:
try_users.append(u_info['user'] + ' > Unknown administrator user [high risk]')
if u_info['login'] in ['/bin/bash','/bin/sh']:
try_users.append(u_info['user'] + ' > Logged-in user [medium risk]')
if try_users:
return False, 'There are security risks for the following users: <br />' + ('<br />'.join(try_users))
return True,'Risk-free'
#获取用户列表
def get_ulist():
u_data = public.readFile('/etc/passwd')
u_list = []
for i in u_data.split("\n"):
u_tmp = i.split(':')
if len(u_tmp) < 3: continue
u_info = {}
u_info['user'],u_info['pass'],u_info['uid'],u_info['gid'],u_info['user_msg'],u_info['home'],u_info['login'] = u_tmp
u_list.append(u_info)
return u_list
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# Memcached安全检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Memcached security'
_version = 1.0 # 版本
_ps = "Check whether the current Memcached is safe" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-04' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_memcached_port.pl")
_tips = [
"Do not configure bindIP for Memcached as 0.0.0.0 unless necessary",
"If bindIP is 0.0.0.0, be sure to set IP access restrictions through the [SYS firewall] plugin or the Security group"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-03>
@return tuple (status<bool>,msg<string>)
'''
p_file = '/etc/init.d/memcached'
p_body = public.readFile(p_file)
if not p_body: return True,'Risk-free'
tmp = re.findall(r"^\s*IP=(0\.0\.0\.0)",p_body,re.M)
if not tmp: return True,'Risk-free'
tmp = re.findall(r"^\s*PORT=(\d+)",p_body,re.M)
result = public.check_port_stat(int(tmp[0]),public.GetClientIp())
if result == 0:
return True,'Risk-free'
return False,'The current Memcached port: {} allows arbitrary client access, which can lead to data leakage'.format(tmp[0])
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# MySQL端口安全检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'MySQL security'
_version = 1.0 # 版本
_ps = "Checks whether the current server's MySQL port is secure" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-03' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_mysql_port.pl")
_tips = [
"If not necessary, remove the MySQL port release from the [Security] page",
"Restrict IP access to MySQL port through the [System firewall] plug-in to enhance security",
"Use [ Fail2ban ] plug-in to protect MySQL service"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-03>
@return tuple (status<bool>,msg<string>)
@example
status, msg = check_run()
if status:
print('OK')
else:
print('Warning: {}'.format(msg))
'''
mycnf_file = '/etc/my.cnf'
if not os.path.exists(mycnf_file):
return True,'MySQL is not installed'
mycnf = public.readFile(mycnf_file)
port_tmp = re.findall(r"port\s*=\s*(\d+)",mycnf)
if not port_tmp:
return True,'MySQL is not installed'
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
return True,'MySQL is not installed'
result = public.check_port_stat(int(port_tmp[0]),public.GetClientIp())
if result == 0:
return True,'Risk-free'
return False,'The current MySQL port: {}, which can be accessed by any server, which may cause MySQL to be cracked by brute force, posing security risks'.format(port_tmp[0])
File diff suppressed because it is too large Load Diff
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 面板安全入口检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Safe entrance'
_version = 1.0 # 版本
_ps = "Check the security entrance security of the panel" # 描述
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-04' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_panel_path.pl")
_tips = [
"Please modify the security entrance on the [Settings] page",
"Set the binding domain name on the [Settings] page, or set authorized IP restrictions",
"Note: Please do not set up too simple safety entrance, which may cause safety hazards"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-03>
@return tuple (status<bool>,msg<string>)
'''
p_file = '/www/server/panel/data/domain.conf'
if public.readFile(p_file):
return True,'Risk-free'
p_file = '/www/server/panel/data/limitip.conf'
if public.readFile(p_file):
return True,'Risk-free'
p_file = '/www/server/panel/data/admin_path.pl'
p_body = public.readFile(p_file)
if not p_body: return False,'No security entrance is set, the panel is at risk of being scanned'
p_body = p_body.strip('/').lower()
if p_body == '': return False,'No security entrance is set, the panel is at risk of being scanned'
lower_path = ['root','admin','123456','123','12','1234567','12345','1234','12345678','123456789','abc','bt']
if p_body in lower_path:
return False,'The security entrance is: {}, too simple, there are potential safety hazards'.format(p_body)
lower_rule = 'qwertyuiopasdfghjklzxcvbnm1234567890'
for s in lower_rule:
for i in range(12):
if not i: continue
lp = s * i
if p_body == lp:
return False,'The security entrance is: {}, too simple, there are potential safety hazards'.format(p_body)
return True,'Risk-free'
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 面板端口检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Panel port'
_version = 1.0 # 版本
_ps = "Check whether the current panel port is safe" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-03' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_panel_port.pl")
_tips = [
"Please modify the default panel port on the [Settings] page",
"Note: Servers with [Security Group] should release the new port in the [Security Group] in advance to prevent the new port cannot be opened"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-03>
@return tuple (status<bool>,msg<string>)
'''
port_file = '/www/server/panel/data/port.pl'
port = public.readFile(port_file)
if not port: return True,'Rick-free'
port = int(port)
if port != 8888:
return True,'Rick-free'
return False,'The panel port is the default port ({}), which may cause unnecessary security risks'.format(port)
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测是否禁ping
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'ICMP detection'
_version = 1.0 # 版本
_ps = "Check whether ICMP access is allowed (Block ICMP)" # 描述
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_ping.pl")
_tips = [
"Turn on the [Block ICMP] function in the [Security] page",
"Note: The server IP or domain name cannot be Ping after it is turned on, please set according to actual needs"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-05>
@return tuple (status<bool>,msg<string>)
'''
cfile = '/etc/sysctl.conf'
conf = public.readFile(cfile)
rep = r"#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)"
tmp = re.search(rep,conf)
if tmp:
if tmp.groups(0)[0] == '1':
return True,'Rick-free'
return False,'If the [Block ICMP] function is not enabled, there is a risk that the server will be attacked or scanned by ICMP'
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 检测网站是否开启防跨站
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Website anti-cross-site detection'
_version = 1.0 # 版本
_ps = "Check the website to prevent cross-site" # 描述
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-05' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_site_spath.pl")
_tips = [
"On the [WebSite] page, [Settings]-[Site Directory], turn on the [Anti-cross-site attack (open_basedir)] function"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-05>
@return tuple (status<bool>,msg<string>)
'''
not_uini = []
site_list = public.M('sites').field('name,path').select()
for s in site_list:
path = get_site_run_path(s['name'],s['path'])
user_ini = path + '/.user.ini'
if os.path.exists(user_ini): continue
not_uini.append(s['name'])
if not_uini:
return False,'The following websites are not enabled for cross-site prevention:<br />' + ('<br />'.join(not_uini))
return True,'Rick-free'
webserver_type = None
setupPath = '/www/server'
def get_site_run_path(siteName,sitePath):
'''
@name 获取网站运行目录
@author hwliang<2020-08-05>
@param siteName(string) 网站名称
@param sitePath(string) 网站根目录
@return string
'''
global webserver_type,setupPath
if not webserver_type:
webserver_type = public.get_webserver()
path = None
if webserver_type == 'nginx':
filename = setupPath + '/panel/vhost/nginx/' + siteName + '.conf'
if os.path.exists(filename):
conf = public.readFile(filename)
rep = r'\s*root\s+(.+);'
tmp1 = re.search(rep,conf)
if tmp1: path = tmp1.groups()[0]
elif webserver_type == 'apache':
filename = setupPath + '/panel/vhost/apache/' + siteName + '.conf'
if os.path.exists(filename):
conf = public.readFile(filename)
rep = r'\s*DocumentRoot\s*"(.+)"\s*\n'
tmp1 = re.search(rep,conf)
if tmp1: path = tmp1.groups()[0]
else:
filename = setupPath + '/panel/vhost/openlitespeed/' + siteName + '.conf'
if os.path.exists(filename):
conf = public.readFile(filename)
rep = r"vhRoot\s*(.*)"
path = re.search(rep,conf)
if not path:
path = None
else:
path = path.groups()[0]
if not path:
path = sitePath
return path
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 网站证书检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'Website certificate (SSL)'
_version = 1.0 # 版本
_ps = "Check whether all websites deploy SSL" # 描述
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-04' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_site_ssl.pl")
_tips = [
"Please consider deploying an SSL certificate for your website to improve its security"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-04>
@return tuple (status<bool>,msg<string>)
'''
site_list = public.M('sites').field('id,name').select()
not_ssl_list = []
for site_info in site_list:
ng_conf_file = '/www/server/panel/vhost/nginx/' + site_info['name'] + '.conf'
if not os.path.exists(ng_conf_file): continue
s_body = public.readFile(ng_conf_file)
if not s_body: continue
if s_body.find('ssl_certificate') == -1:
not_ssl_list.append(site_info['name'])
if not_ssl_list:
return False ,'The following sites do not deploy SSL certificates: <br />' + ('<br />'.join(not_ssl_list))
return True,'Rick-free'
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# 网站证书过期检测
# -------------------------------------------------------------------
import os,sys,re,public,OpenSSL,time
_title = 'Website certificate expired'
_version = 1.0 # 版本
_ps = "Check whether the websites SSL has expired" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-04' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_site_ssl_expire.pl")
_tips = [
"Please renew or replace with a new SSL certificate for your site to avoid affecting normal website access",
"After the SSL certificate expires, the user will be prompted by the browser to access the website as insecure, and most browsers will block access, seriously affecting online business"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-04>
@return tuple (status<bool>,msg<string>)
'''
site_list = public.M('sites').field('id,name').select()
not_ssl_list = []
s_time = time.time()
for site_info in site_list:
ng_conf_file = '/www/server/panel/vhost/nginx/' + site_info['name'] + '.conf'
if not os.path.exists(ng_conf_file): continue
s_body = public.readFile(ng_conf_file)
if not s_body: continue
if s_body.find('ssl_certificate') == -1: continue
cert_file = '/www/server/panel/vhost/cert/{}/fullchain.pem'.format(site_info['name'])
if not os.path.exists(cert_file): continue
cert_timeout = get_cert_timeout(cert_file)
if s_time > cert_timeout:
not_ssl_list.append(site_info['name'] + ' Expiration: ' + public.format_date("%Y-%m-%d",cert_timeout))
if not_ssl_list:
return False ,'The following sites SSL certificate has expired: <br />' + ('<br />'.join(not_ssl_list))
return True,'Rick-free'
# 获取证书到期时间
def get_cert_timeout(cert_file):
try:
cert = split_ca_data(public.readFile(cert_file))
x509 = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
cert_timeout = bytes.decode(x509.get_notAfter())[:-1]
return int(time.mktime(time.strptime(cert_timeout, '%Y%m%d%H%M%S')))
except:
return time.time() + 86400
# 拆分根证书
def split_ca_data(cert):
datas = cert.split('-----END CERTIFICATE-----')
return datas[0] + "-----END CERTIFICATE-----\n"
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/python
#coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# SSH安全检测
# -------------------------------------------------------------------
import os,sys,re,public
_title = 'SSH security'
_version = 1.0 # 版本
_ps = "Check whether the SSH port of the current server is safe" # 描述
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
_date = '2020-08-04' # 最后更新时间
_ignore = os.path.exists("data/warning/ignore/sw_ssh_port.pl")
_tips = [
"Modify the SSH port on the [Security] page, and consider turning off [SSH password login] in [SSH security management], and turning on [SSH key login]",
"If SSH connection service is not required, it is recommended to disable SSH service on the [Security] page",
"Through the [System Firewall] plug-in or in the [Security Group] modify the release behavior of the SSH port to limit the IP to enhance security",
"Use [Fail2ban] plug-in to protect SSH service"
]
_help = ''
def check_run():
'''
@name 开始检测
@author hwliang<2020-08-03>
@return tuple (status<bool>,msg<string>)
@example
status, msg = check_run()
if status:
print('OK')
else:
print('Warning: {}'.format(msg))
'''
file = '/etc/ssh/sshd_config'
conf = public.readFile(file)
if not conf: conf = ''
rep = r"#*Port\s+([0-9]+)\s*\n"
tmp1 = re.search(rep,conf)
port = '22'
if tmp1:
port = tmp1.groups(0)[0]
version = public.readFile('/etc/redhat-release')
if not version:
version = public.readFile('/etc/issue').strip().split("\n")[0].replace('\\n','').replace('\l','').strip()
else:
version = version.replace('release ','').replace('Linux','').replace('(Core)','').strip()
if os.path.exists('/usr/bin/apt-get'):
if os.path.exists('/etc/init.d/sshd'):
status = public.ExecShell("service sshd status | grep -P '(dead|stop)'|grep -v grep")
else:
status = public.ExecShell("service ssh status | grep -P '(dead|stop)'|grep -v grep")
else:
if version.find(' 7.') != -1 or version.find(' 8.') != -1 or version.find('Fedora') != -1:
status = public.ExecShell("systemctl status sshd.service | grep 'dead'|grep -v grep")
else:
status = public.ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'|grep -v grep")
if len(status[0]) > 3:
status = False
else:
status = True
if not status:
return True,'SSH service is not enabled'
if port != '22':
return True,'The default SSH port has been modified'
result = public.check_port_stat(int(port),public.GetClientIp())
if result == 0:
return True,'Rick-free'
return False,'The default SSH port ({}) has not been modified, and the access IP limit configuration has not been done, there is a risk of SSH breaching'.format(port)
+1 -1
View File
@@ -363,7 +363,7 @@ class system:
#取磁盘分区信息
diskIo = psutil.disk_partitions()
diskInfo = []
cuts = ['/mnt/cdrom','/boot','/boot/efi','/dev','/dev/shm','/run/lock','/run','/run/shm','/run/user'];
cuts = ['/mnt/cdrom','/boot','/boot/efi','/dev','/dev/shm','/run/lock','/run','/run/shm','/run/user']
for disk in diskIo:
if not cuts: continue
tmp = {}
+10
View File
@@ -45,6 +45,10 @@ class ScanLogin(object):
cache.set(public.get_session_id(),tid,360)
return public.returnMsg(True, qrcode_str)
#生成request_token
def set_request_token(self):
session['request_token_head'] = public.GetRandomString(48)
# 设置登录状态
def set_login(self, get):
session_id = public.get_session_id()
@@ -66,6 +70,9 @@ class ScanLogin(object):
public.WriteLog('TYPE_LOGIN', 'LOGIN_SUCCESS',
('WeChat scan code login', public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
login_type = 'data/app_login.pl'
self.set_request_token()
import config
config.config().reload_session()
public.writeFile(login_type,'True')
return public.returnMsg(True, 'login successful')
return public.returnMsg(False, 'Login failed')
@@ -87,6 +94,9 @@ class ScanLogin(object):
sess_input_path = 'data/session_last.pl'
public.writeFile(sess_input_path,str(int(time.time())))
login_type = 'data/app_login.pl'
self.set_request_token()
import config
config.config().reload_session()
public.writeFile(login_type,'True')
return public.returnMsg(True,'login successful!')