mirror of https://github.com/flucont/btcloud.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1039 lines
35 KiB
1039 lines
35 KiB
#coding: utf-8
|
|
# +-------------------------------------------------------------------
|
|
# | 宝塔Windows面板
|
|
# +-------------------------------------------------------------------
|
|
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
|
# +-------------------------------------------------------------------
|
|
# | Author: 沐落 <cjx@bt.cn>
|
|
# +-------------------------------------------------------------------
|
|
|
|
import os,chardet,time,sys,re
|
|
import win32net, win32api, win32netcon,win32security,win32serviceutil
|
|
import traceback,shlex,datetime,subprocess,platform
|
|
import sqlite3,shutil
|
|
|
|
def readReg(path,key):
|
|
import winreg
|
|
try:
|
|
newKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE ,path)
|
|
value,type = winreg.QueryValueEx(newKey, key)
|
|
return value
|
|
except :
|
|
return False
|
|
|
|
panelPath = readReg(r'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\宝塔面板','PanelPath')
|
|
if not panelPath:
|
|
panelPath = os.getenv('BT_PANEL')
|
|
if not panelPath: exit();
|
|
|
|
setupPath = os.path.dirname(panelPath)
|
|
|
|
error_path = '{}/error.log'.format(setupPath)
|
|
logPath = panelPath + '/data/panelExec.log'
|
|
|
|
class Sql():
|
|
#------------------------------
|
|
# 数据库操作类 For sqlite3
|
|
#------------------------------
|
|
__DB_FILE = None # 数据库文件
|
|
__DB_CONN = None # 数据库连接对象
|
|
__DB_TABLE = "" # 被操作的表名称
|
|
__OPT_WHERE = "" # where条件
|
|
__OPT_LIMIT = "" # limit条件
|
|
__OPT_ORDER = "" # order条件
|
|
__OPT_FIELD = "*" # field条件
|
|
__OPT_PARAM = () # where值
|
|
__LOCK = panelPath + '/data/sqlite_lock.pl'
|
|
|
|
def __init__(self):
|
|
self.__DB_FILE = panelPath + '/data/default.db'
|
|
|
|
def __GetConn(self):
|
|
#取数据库对象
|
|
try:
|
|
if self.__DB_CONN == None:
|
|
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
|
|
self.__DB_CONN.text_factory = str
|
|
except Exception as ex:
|
|
print(str(ex))
|
|
return "error: " + str(ex)
|
|
|
|
def table(self,table):
|
|
#设置表名
|
|
self.__DB_TABLE = table
|
|
return self
|
|
|
|
|
|
def where(self,where,param):
|
|
#WHERE条件
|
|
if where:
|
|
self.__OPT_WHERE = " WHERE " + where
|
|
self.__OPT_PARAM = self.__to_tuple(param)
|
|
return self
|
|
|
|
def __to_tuple(self,param):
|
|
#将参数转换为tuple
|
|
if type(param) != tuple:
|
|
if type(param) == list:
|
|
param = tuple(param)
|
|
else:
|
|
param = (param,)
|
|
return param
|
|
|
|
#更新数据
|
|
def update(self,pdata):
|
|
if not pdata: return False
|
|
keys,param = self.__format_pdata(pdata)
|
|
return self.save(keys,param)
|
|
|
|
#构造数据
|
|
def __format_pdata(self,pdata):
|
|
keys = pdata.keys()
|
|
keys_str = ','.join(keys)
|
|
param = []
|
|
for k in keys: param.append(pdata[k])
|
|
return keys_str,tuple(param)
|
|
|
|
def field(self,field):
|
|
#FIELD条件
|
|
if len(field):
|
|
self.__OPT_FIELD = field
|
|
return self
|
|
|
|
def getField(self,keyName):
|
|
#取回指定字段
|
|
|
|
result = self.field(keyName).select()
|
|
print(result)
|
|
if len(result) != 0:
|
|
return result[0][keyName]
|
|
return result
|
|
|
|
def __format_field(self,field):
|
|
import re
|
|
fields = []
|
|
for key in field:
|
|
s_as = re.search(r'\s+as\s+',key,flags=re.IGNORECASE)
|
|
if s_as:
|
|
as_tip = s_as.group()
|
|
key = key.split(as_tip)[1]
|
|
fields.append(key)
|
|
return fields
|
|
|
|
def __get_columns(self):
|
|
if self.__OPT_FIELD == '*':
|
|
tmp_cols = self.query('PRAGMA table_info('+self.__DB_TABLE+')',())
|
|
cols = []
|
|
for col in tmp_cols:
|
|
if len(col) > 2: cols.append('`' + col[1] + '`')
|
|
if len(cols) > 0: self.__OPT_FIELD = ','.join(cols)
|
|
|
|
def select(self):
|
|
#查询数据集
|
|
self.__GetConn()
|
|
try:
|
|
self.__get_columns()
|
|
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + self.__OPT_WHERE + self.__OPT_ORDER + self.__OPT_LIMIT
|
|
result = self.__DB_CONN.execute(sql,self.__OPT_PARAM)
|
|
data = result.fetchall()
|
|
#构造字典系列
|
|
if self.__OPT_FIELD != "*":
|
|
fields = self.__format_field(self.__OPT_FIELD.split(','))
|
|
tmp = []
|
|
for row in data:
|
|
i=0
|
|
tmp1 = {}
|
|
for key in fields:
|
|
tmp1[key.strip('`')] = row[i]
|
|
i += 1
|
|
tmp.append(tmp1)
|
|
del(tmp1)
|
|
data = tmp
|
|
del(tmp)
|
|
else:
|
|
#将元组转换成列表
|
|
tmp = list(map(list,data))
|
|
data = tmp
|
|
del(tmp)
|
|
self.__close()
|
|
return data
|
|
except Exception as ex:
|
|
return "error: " + str(ex)
|
|
|
|
def setField(self,keyName,keyValue):
|
|
#更新指定字段
|
|
return self.save(keyName,(keyValue,))
|
|
|
|
def commit(self):
|
|
self.__close()
|
|
self.__DB_CONN.commit()
|
|
|
|
|
|
def save(self,keys,param):
|
|
#更新数据
|
|
self.write_lock()
|
|
self.__GetConn()
|
|
self.__DB_CONN.text_factory = str
|
|
try:
|
|
opt = ""
|
|
for key in keys.split(','):
|
|
opt += key + "=?,"
|
|
opt = opt[0:len(opt)-1]
|
|
sql = "UPDATE " + self.__DB_TABLE + " SET " + opt+self.__OPT_WHERE
|
|
|
|
#处理拼接WHERE与UPDATE参数
|
|
tmp = list(self.__to_tuple(param))
|
|
for arg in self.__OPT_PARAM:
|
|
tmp.append(arg)
|
|
self.__OPT_PARAM = tuple(tmp)
|
|
result = self.__DB_CONN.execute(sql,self.__OPT_PARAM)
|
|
self.__close()
|
|
self.__DB_CONN.commit()
|
|
self.rm_lock()
|
|
return result.rowcount
|
|
except Exception as ex:
|
|
return "error: " + str(ex)
|
|
|
|
|
|
def execute(self,sql,param = ()):
|
|
#执行SQL语句返回受影响行
|
|
self.write_lock()
|
|
self.__GetConn()
|
|
try:
|
|
result = self.__DB_CONN.execute(sql,self.__to_tuple(param))
|
|
self.__DB_CONN.commit()
|
|
self.rm_lock()
|
|
return result.rowcount
|
|
except Exception as ex:
|
|
return "error: " + str(ex)
|
|
|
|
#是否有锁
|
|
def is_lock(self):
|
|
n = 0
|
|
while os.path.exists(self.__LOCK):
|
|
n+=1
|
|
if n > 100:
|
|
self.rm_lock()
|
|
break
|
|
time.sleep(0.01)
|
|
#写锁
|
|
def write_lock(self):
|
|
self.is_lock()
|
|
open(self.__LOCK,'wb+').close()
|
|
|
|
#解锁
|
|
def rm_lock(self):
|
|
if os.path.exists(self.__LOCK):
|
|
os.remove(self.__LOCK)
|
|
|
|
def query(self,sql,param = ()):
|
|
#执行SQL语句返回数据集
|
|
self.__GetConn()
|
|
try:
|
|
result = self.__DB_CONN.execute(sql,self.__to_tuple(param))
|
|
#将元组转换成列表
|
|
data = list(map(list,result))
|
|
return data
|
|
except Exception as ex:
|
|
return "error: " + str(ex)
|
|
|
|
def __close(self):
|
|
#清理条件属性
|
|
self.__OPT_WHERE = ""
|
|
self.__OPT_FIELD = "*"
|
|
self.__OPT_ORDER = ""
|
|
self.__OPT_LIMIT = ""
|
|
self.__OPT_PARAM = ()
|
|
|
|
|
|
def close(self):
|
|
#释放资源
|
|
try:
|
|
self.__DB_CONN.close()
|
|
self.__DB_CONN = None
|
|
except:
|
|
pass
|
|
|
|
|
|
def GetLocalIp():
|
|
"""
|
|
取本地外网IP
|
|
|
|
"""
|
|
try:
|
|
filename = panelPath + '/data/iplist.txt'
|
|
ipaddress = readFile(filename)
|
|
if not ipaddress:
|
|
|
|
url = 'http://www.example.com/api/getIpAddress';
|
|
str = httpGet(url)
|
|
writeFile(filename,ipaddress)
|
|
|
|
ipaddress = re.search('\d+.\d+.\d+.\d+',ipaddress).group(0);
|
|
return ipaddress
|
|
except:
|
|
try:
|
|
url = 'https://www.bt.cn/Api/getIpAddress';
|
|
str = httpGet(url)
|
|
writeFile(filename,ipaddress)
|
|
return str
|
|
except:
|
|
pass
|
|
|
|
def get_error_info():
|
|
errorMsg = traceback.format_exc();
|
|
return errorMsg
|
|
|
|
|
|
def get_server_status(name):
|
|
try:
|
|
serviceStatus = win32serviceutil.QueryServiceStatus(name)
|
|
if serviceStatus[1] == 4:
|
|
return 1
|
|
return 0
|
|
except :
|
|
return -1
|
|
|
|
def start_service(name):
|
|
|
|
try:
|
|
timeout = 0;
|
|
while get_server_status(name) == 0:
|
|
try:
|
|
win32serviceutil.StartService(name)
|
|
time.sleep(1);
|
|
except : time.sleep(1);
|
|
timeout += 1
|
|
if timeout > 10:break
|
|
|
|
if get_server_status(name) != 0:
|
|
return True,None
|
|
return False,'操作失败,10秒内未完成启动服务【{}】'.format(name)
|
|
except :
|
|
return False,get_error_info()
|
|
|
|
def stop_service(name):
|
|
try:
|
|
timeout = 0;
|
|
while get_server_status(name) == 1:
|
|
try:
|
|
win32serviceutil.StopService(name)
|
|
time.sleep(1);
|
|
except : time.sleep(1);
|
|
timeout += 1
|
|
if timeout > 10:break
|
|
|
|
if get_server_status(name) != 1:
|
|
return True,None
|
|
return False,'操作失败,10秒内未完成启动服务【{}】'.format(name)
|
|
except :
|
|
return False,get_error_info()
|
|
|
|
def delete_server(name):
|
|
try:
|
|
stop_service(name)
|
|
win32serviceutil.RemoveService(name)
|
|
return True,''
|
|
except :
|
|
return False,get_error_info()
|
|
|
|
def get_requests_headers():
|
|
return {"Content-type":"application/x-www-form-urlencoded","User-Agent":"BT-Panel"}
|
|
|
|
def downloadFile(url,filename):
|
|
try:
|
|
import requests
|
|
res = requests.get(url,verify=False)
|
|
with open(filename,"wb") as f:
|
|
f.write(res.content)
|
|
except:
|
|
import requests
|
|
res = requests.get(url,verify=False)
|
|
with open(filename,"wb") as f:
|
|
f.write(res.content)
|
|
|
|
|
|
def downloadFileByWget(url,filename):
|
|
"""
|
|
wget下载文件
|
|
@url 下载地址
|
|
@filename 本地文件路径
|
|
"""
|
|
try:
|
|
if os.path.exists(logPath): os.remove(logPath)
|
|
except : pass
|
|
loacl_path = '{}/script/wget.exe'.format(panelPath)
|
|
if not os.path.exists(loacl_path): downloadFile(get_url()+'/win/panel/data/wget.exe',loacl_path)
|
|
|
|
if os.path.getsize(loacl_path) < 10:
|
|
os.remove(loacl_path)
|
|
downloadFile(url,filename)
|
|
else:
|
|
shell = "{} {} -O {} -t 5 -T 60 --no-check-certificate --auth-no-challenge --force-directorie > {} 2>&1".format(loacl_path,url,filename,logPath)
|
|
os.system(shell)
|
|
|
|
num = 0
|
|
re_size = 0
|
|
while num <= 5:
|
|
if os.path.exists(filename):
|
|
cr_size = os.path.getsize(filename)
|
|
if re_size > 0 and re_size == cr_size:
|
|
break;
|
|
else:
|
|
re_size = cr_size
|
|
time.sleep(0.5)
|
|
num += 1
|
|
|
|
if os.path.exists(filename):
|
|
if os.path.getsize(filename) < 1:
|
|
os.remove(filename)
|
|
downloadFile(url,filename)
|
|
else:
|
|
downloadFile(url,filename)
|
|
|
|
def writeFile(filename,s_body,mode='w+',encoding = 'utf-8'):
|
|
try:
|
|
fp = open(filename, mode,encoding = encoding);
|
|
fp.write(s_body)
|
|
fp.close()
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
def readFile(filename,mode = 'r'):
|
|
|
|
import os,chardet
|
|
if not os.path.exists(filename): return False
|
|
if not os.path.isfile(filename): return False
|
|
|
|
encoding = 'utf-8'
|
|
f_body = '';
|
|
try:
|
|
fp = open(filename, mode,encoding = encoding)
|
|
f_body = fp.read()
|
|
except :
|
|
fp.close()
|
|
|
|
try:
|
|
encoding = 'gbk'
|
|
fp = open(filename, mode,encoding = encoding)
|
|
f_body = fp.read()
|
|
except :
|
|
fp.close()
|
|
|
|
encoding = 'ansi'
|
|
fp = open(filename, mode,encoding = encoding)
|
|
f_body = fp.read()
|
|
|
|
try:
|
|
if f_body[0] == '\ufeff':
|
|
#处理带bom格式
|
|
new_code = chardet.detect(f_body.encode(encoding))["encoding"]
|
|
f_body = f_body.encode(encoding).decode(new_code);
|
|
except : pass
|
|
|
|
fp.close()
|
|
return f_body
|
|
|
|
def httpGet(url,timeout = 60,headers = {}):
|
|
try:
|
|
import urllib.request,ssl
|
|
try:
|
|
ssl._create_default_https_context = ssl._create_unverified_context
|
|
except:pass;
|
|
req = urllib.request.Request(url,headers = headers)
|
|
response = urllib.request.urlopen(req,timeout = timeout)
|
|
result = response.read()
|
|
if type(result) == bytes:
|
|
try:
|
|
result = result.decode('utf-8')
|
|
except :
|
|
result = result.decode('gb2312')
|
|
return result
|
|
except Exception as ex:
|
|
if headers: return False
|
|
return str(ex)
|
|
|
|
def httpPost(url, data, timeout=60, headers={}):
|
|
|
|
try:
|
|
import urllib.request,ssl
|
|
try:
|
|
ssl._create_default_https_context = ssl._create_unverified_context
|
|
except:pass;
|
|
data2 = urllib.parse.urlencode(data).encode('utf-8')
|
|
req = urllib.request.Request(url, data2,headers = headers)
|
|
response = urllib.request.urlopen(req,timeout = timeout)
|
|
result = response.read()
|
|
if type(result) == bytes: result = result.decode('utf-8')
|
|
|
|
return result
|
|
except Exception as ex:
|
|
|
|
return str(ex);
|
|
|
|
|
|
def get_timeout(url,timeout=3):
|
|
|
|
try:
|
|
start = time.time()
|
|
result = int(httpGet(url,timeout))
|
|
return result,int((time.time() - start) * 1000 - 500)
|
|
except: return 0,False
|
|
|
|
def get_url(timeout = 0.5):
|
|
import json
|
|
try:
|
|
#
|
|
node_list = [{"protocol":"http://","address":"dg2.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"dg1.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"download.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"hk1-node.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"na1-node.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"jp1-node.bt.cn","port":"80","ping":500}]
|
|
|
|
mnode1 = []
|
|
mnode2 = []
|
|
mnode3 = []
|
|
for node in node_list:
|
|
node['net'],node['ping'] = get_timeout(node['protocol'] + node['address'] + ':' + node['port'] + '/net_test',1)
|
|
if not node['ping']: continue
|
|
if node['ping'] < 100: #当响应时间<100ms且可用带宽大于1500KB时
|
|
if node['net'] > 1500:
|
|
mnode1.append(node)
|
|
elif node['net'] > 1000:
|
|
mnode3.append(node)
|
|
else:
|
|
if node['net'] > 1000: #当响应时间>=100ms且可用带宽大于1000KB时
|
|
mnode2.append(node)
|
|
if node['ping'] < 100:
|
|
if node['net'] > 3000: break #有节点可用带宽大于3000时,不再检查其它节点
|
|
if mnode1: #优选低延迟高带宽
|
|
mnode = sorted(mnode1,key= lambda x:x['net'],reverse=True)
|
|
elif mnode3: #备选低延迟,中等带宽
|
|
mnode = sorted(mnode3,key= lambda x:x['net'],reverse=True)
|
|
else: #终选中等延迟,中等带宽
|
|
mnode = sorted(mnode2,key= lambda x:x['ping'],reverse=False)
|
|
|
|
if not mnode: return 'https://download.bt.cn'
|
|
#return mnode[0]['protocol'] + mnode[0]['address'] + ':' + mnode[0]['port']
|
|
return "https://" + mnode[0]['address']
|
|
except:
|
|
return 'https://download.bt.cn'
|
|
|
|
|
|
|
|
#删除文件权限
|
|
def del_file_access(filename,user):
|
|
try:
|
|
|
|
if filename.lower() in ["c:/","c:","c:\\","c"]:
|
|
return True
|
|
import win32security
|
|
sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
|
|
dacl = sd.GetSecurityDescriptorDacl()
|
|
ace_count = dacl.GetAceCount()
|
|
|
|
for i in range(ace_count ,0 ,-1):
|
|
try:
|
|
data = {}
|
|
data['rev'], data['access'], usersid = dacl.GetAce(i-1)
|
|
data['user'],data['group'], data['type'] = win32security.LookupAccountSid('', usersid)
|
|
if data['user'].lower() == user.lower(): dacl.DeleteAce(i-1) #删除旧的dacl
|
|
if data['user'].lower() == 'users': dacl.DeleteAce(i-1) #删除旧的dacl
|
|
|
|
except :
|
|
try:
|
|
#处理拒绝访问
|
|
dacl.DeleteAce(i-1)
|
|
except : pass
|
|
sd.SetSecurityDescriptorDacl(1, dacl, 0)
|
|
win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
|
|
except :
|
|
pass
|
|
return True
|
|
|
|
def set_file_access(filename,user,access):
|
|
try:
|
|
sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
|
|
dacl = sd.GetSecurityDescriptorDacl()
|
|
ace_count = dacl.GetAceCount()
|
|
|
|
for i in range(ace_count, 0,-1):
|
|
try:
|
|
data = {}
|
|
data['rev'], data['access'], usersid = dacl.GetAce(i-1)
|
|
data['user'],data['group'], data['type'] = win32security.LookupAccountSid('', usersid)
|
|
if data['user'].lower() == user.lower(): dacl.DeleteAce(i-1) #删除旧的dacl
|
|
if data['user'].lower() == 'users': dacl.DeleteAce(i-1) #删除旧的dacl
|
|
|
|
except :
|
|
pass
|
|
try:
|
|
userx, domain, type = win32security.LookupAccountName("", user)
|
|
except :
|
|
userx, domain, type = win32security.LookupAccountName("", 'IIS APPPOOL\\' + user)
|
|
if access > 0: dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 3, access, userx)
|
|
|
|
sd.SetSecurityDescriptorDacl(1, dacl, 0)
|
|
win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
|
|
return True,None
|
|
except :
|
|
return False,get_error_info()
|
|
|
|
def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
|
|
if shell:
|
|
cmdstring_list = cmdstring
|
|
else:
|
|
cmdstring_list = shlex.split(cmdstring)
|
|
|
|
if timeout:
|
|
end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
|
|
|
|
sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE,shell=shell,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
while sub.poll() is None:
|
|
time.sleep(0.1)
|
|
if timeout:
|
|
if end_time <= datetime.datetime.now():
|
|
raise Exception("Timeout:%s"%cmdstring)
|
|
a,e = sub.communicate()
|
|
if type(a) == bytes:
|
|
try:
|
|
a = a.decode('utf-8')
|
|
except :
|
|
a = a.decode('gb2312','ignore')
|
|
|
|
if type(e) == bytes:
|
|
try:
|
|
e = e.decode('utf-8')
|
|
except :
|
|
e = e.decode('gb2312','ignore')
|
|
return a,e
|
|
|
|
def GetRandomString(length):
|
|
from random import Random
|
|
strings = ''
|
|
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
|
|
chrlen = len(chars) - 1
|
|
random = Random()
|
|
for i in range(length):
|
|
strings += chars[random.randint(0, chrlen)]
|
|
return strings
|
|
|
|
def GetRandomString1(length):
|
|
from random import Random
|
|
strings = ''
|
|
chars = '0123456789'
|
|
chrlen = len(chars) - 1
|
|
random = Random()
|
|
for i in range(length):
|
|
strings += chars[random.randint(0, chrlen)]
|
|
return strings
|
|
|
|
def GetRandomString2(length):
|
|
from random import Random
|
|
strings = ''
|
|
chars = '!@#$%^&*()_+.,?[]-='
|
|
chrlen = len(chars) - 1
|
|
random = Random()
|
|
for i in range(length):
|
|
strings += chars[random.randint(0, chrlen)]
|
|
return strings
|
|
|
|
def chdck_salt():
|
|
|
|
sql = Sql()
|
|
sql.table('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
|
|
|
|
u_list = sql.table('users').field('id,username,password,salt').select()
|
|
for u_info in u_list:
|
|
salt = GetRandomString(12) #12位随机
|
|
pdata = {}
|
|
pdata['password'] = md5(md5(u_info['password']+'_bt.cn') + salt)
|
|
pdata['salt'] = salt
|
|
sql.table('users').where('id=?',(u_info['id'],)).update(pdata)
|
|
|
|
def md5(strings):
|
|
"""
|
|
生成MD5
|
|
@strings 要被处理的字符串
|
|
return string(32)
|
|
"""
|
|
import hashlib
|
|
m = hashlib.md5()
|
|
|
|
m.update(strings.encode('utf-8'))
|
|
return m.hexdigest()
|
|
|
|
def password_salt(password,username=None,uid=None):
|
|
|
|
chdck_salt()
|
|
sql = Sql()
|
|
|
|
if not uid:
|
|
if not username:
|
|
raise Exception('username或uid必需传一项')
|
|
uid = sql.table('users').where('username=?',(username,)).getField('id')
|
|
salt = sql.table('users').where('id=?',(uid,)).getField('salt')
|
|
return md5(md5(password+'_bt.cn')+salt)
|
|
|
|
def check_user(username):
|
|
resume = 0
|
|
while True:
|
|
data, total, resume = win32net.NetUserEnum(None, 3, win32netcon.FILTER_NORMAL_ACCOUNT, resume)
|
|
for user in data:
|
|
if user['name'] == username: return True
|
|
if not resume: break
|
|
return False
|
|
|
|
def add_user(username,password,ps):
|
|
try:
|
|
if not check_user(username):
|
|
d = {}
|
|
d['name'] = username
|
|
d['password'] = password
|
|
d['comment'] = ps
|
|
d['flags'] = win32netcon.UF_NORMAL_ACCOUNT | win32netcon.UF_SCRIPT
|
|
d['priv'] = win32netcon.USER_PRIV_USER
|
|
win32net.NetUserAdd(None, 1, d)
|
|
|
|
#设置用户允许登录服务
|
|
handle = win32security.LsaOpenPolicy(None, win32security.POLICY_ALL_ACCESS)
|
|
sid_obj, domain, tmp = win32security.LookupAccountName(None, username)
|
|
win32security.LsaAddAccountRights(handle, sid_obj, ('SeServiceLogonRight',) )
|
|
win32security.LsaClose( handle)
|
|
|
|
if not check_user(username): return False, '添加用户[{}]失败.'.format(username)
|
|
writeFile('{}/data/{}'.format(panelPath,username),password)
|
|
return True , None
|
|
else:
|
|
ExecShell('net user "{}" "{}"'.format(username,password))
|
|
writeFile('{}/data/{}'.format(panelPath,username),password)
|
|
return True , None
|
|
except :
|
|
return False,get_error_info()
|
|
|
|
def add_user_bywww():
|
|
|
|
pwd = GetRandomString(64) + GetRandomString1(32) + GetRandomString2(32)
|
|
status,error = add_user('www',pwd,'用于启动宝塔安装的程序,删除后会导致部分软件无法启动,请勿删除')
|
|
if not status:
|
|
writeFile(error_path,error)
|
|
return False
|
|
return True
|
|
|
|
def add_user_bymysql():
|
|
|
|
pwd = GetRandomString(64) + GetRandomString1(32) + GetRandomString2(32)
|
|
status,error = add_user('mysql',pwd,'用于启动宝塔安装的程序,删除后会导致部分软件无法启动,请勿删除')
|
|
if not status:
|
|
writeFile(error_path,error)
|
|
return False
|
|
return True
|
|
|
|
def getIP(url):
|
|
import socket,re
|
|
|
|
tmp = re.search('http://(.+)\:\d*',url)
|
|
if tmp:
|
|
domain = tmp.groups()[0]
|
|
myaddr = socket.getaddrinfo(domain, 'http')
|
|
return myaddr[0][4][0]
|
|
return ''
|
|
|
|
|
|
def add_panel_dir():
|
|
try:
|
|
slist = [
|
|
[panelPath , [] ],
|
|
['{}/data'.format(panelPath) , [] ],
|
|
['{}/script'.format(panelPath) , [] ],
|
|
['{}/backup'.format(panelPath) , [] ],
|
|
['{}/backup/database/sqlserver'.format(setupPath[:2]) , [ 'Authenticated Users']],
|
|
['{}/wwwroot'.format(setupPath[:2]) , [ 'IIS_IUSRS','www'] ],
|
|
['{}/wwwlogs'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
|
|
['{}/php'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
|
|
['{}/mysql'.format(setupPath) , [ 'mysql'] ],
|
|
['{}/temp'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
|
|
['{}/temp/session'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
|
|
['C:/Temp' , [ 'IIS_IUSRS','www'] ],
|
|
]
|
|
|
|
is_break = False
|
|
for sobj in slist:
|
|
if not os.path.exists(sobj[0]):
|
|
os.makedirs(sobj[0])
|
|
n = 0
|
|
while n < 5:
|
|
if os.path.exists(sobj[0]): break
|
|
|
|
os.makedirs(sobj[0])
|
|
time.sleep(0.5)
|
|
n += 1
|
|
|
|
if not os.path.exists(sobj[0]):
|
|
writeFile(error_path,"自动创建目录【{}】失败,已重试最大次数 5 次,请手动创建该目录后重新安装".format(sobj[0]))
|
|
return False
|
|
|
|
del_file_access(sobj[0],'users')
|
|
|
|
for user in sobj[1]:
|
|
n = 0
|
|
while n < 3:
|
|
status,error = set_file_access(sobj[0],user,2032127)
|
|
if status: break
|
|
time.sleep(0.5)
|
|
|
|
if not status:
|
|
writeFile(error_path,"目录{}设置{}权限设置错误 -> {}".format(sobj[0],user,error))
|
|
break
|
|
|
|
del_file_access(setupPath,'users')
|
|
url = get_url()
|
|
|
|
files = ['default.db','session.db','system.db','phplib.win','defaultDoc.html','404.html']
|
|
for f_name in files:
|
|
local_path = '{}/data/{}'.format(panelPath,f_name)
|
|
download_url = '{}/win/panel/data/{}'.format(url,f_name)
|
|
|
|
n = 0
|
|
while n < 10:
|
|
n += 1;
|
|
|
|
try:
|
|
if os.path.exists(local_path) and os.path.getsize(local_path) < 10: os.remove(local_path)
|
|
if not os.path.exists(local_path): downloadFileByWget(download_url,local_path)
|
|
if os.path.getsize(local_path) and os.path.getsize(local_path) > 10: break;
|
|
|
|
writeFile(error_path,'download {} error ->> {} \r\n {}'.format(f_name,download_url,""))
|
|
except :
|
|
ip = getIP(url)
|
|
writeFile(error_path,'download {} error ->> {} \r\n connect {} \r\n {}'.format(ip,f_name,download_url,get_error_info()))
|
|
|
|
if n > 5: return False
|
|
time.sleep(0.2)
|
|
|
|
return True
|
|
except :
|
|
writeFile(error_path,get_error_info())
|
|
return False
|
|
|
|
def unzip(src_path,dst_path):
|
|
import zipfile
|
|
zip_file = zipfile.ZipFile(src_path)
|
|
for names in zip_file.namelist():
|
|
zip_file.extract(names,dst_path)
|
|
zip_file.close()
|
|
return True
|
|
|
|
def to_path(path):
|
|
return path.replace('/','\\')
|
|
|
|
def download_panel(file_list = []):
|
|
try:
|
|
url = 'http://www.example.com'
|
|
|
|
ExecShell("taskkill /f /t /im BtTools.exe")
|
|
|
|
#下载面板
|
|
loacl_path = setupPath + '/panel.zip'
|
|
tmpPath = "{}/temp/panel".format(setupPath)
|
|
if os.path.exists(loacl_path): os.remove(loacl_path)
|
|
if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True)
|
|
if not os.path.exists(tmpPath): os.makedirs(tmpPath)
|
|
|
|
p_ver = sys.argv[2]
|
|
downUrl = url + '/win/panel/panel_' + p_ver + '.zip';
|
|
downloadFileByWget(downUrl,loacl_path);
|
|
unzip(loacl_path,tmpPath)
|
|
|
|
for ff_path in file_list:
|
|
if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path)
|
|
|
|
tcPath = '{}\class'.format(tmpPath)
|
|
for name in os.listdir(tcPath):
|
|
try:
|
|
if name.find('win_amd64.pyd') >=0:
|
|
oldName = os.path.join(tcPath,name);
|
|
lName = name.split('.')[0] + '.pyd'
|
|
newName = os.path.join(tcPath,lName)
|
|
if not os.path.exists(newName):os.rename(oldName,newName)
|
|
except :pass
|
|
|
|
cPath = '{}/panel/class'.format(setupPath)
|
|
|
|
if os.path.exists(cPath):
|
|
os.system("del /s {}\*.pyc".format(to_path(cPath)))
|
|
os.system("del /s {}\*.pyt".format(to_path(cPath)))
|
|
for name in os.listdir(cPath):
|
|
try:
|
|
if name.find('.pyd') >=0:
|
|
oldName = os.path.join(cPath,name)
|
|
newName = os.path.join(cPath,GetRandomString(8) + '.pyt')
|
|
os.rename(oldName,newName)
|
|
except : pass
|
|
os.system("del /s {}\*.pyc".format(to_path(cPath)))
|
|
os.system("del /s {}\*.pyt".format(to_path(cPath)))
|
|
|
|
os.system("xcopy /s /c /e /y /r {} {}".format(to_path(tmpPath),to_path(panelPath)))
|
|
try:
|
|
os.remove(loacl_path)
|
|
except : pass
|
|
|
|
try:
|
|
shutil.rmtree(tmpPath,True)
|
|
except : pass
|
|
|
|
s_ver = platform.platform()
|
|
net_v = '45'
|
|
if s_ver.find('2008') >= 0: net_v = '20'
|
|
writeFile('{}/data/net'.format(setupPath),net_v)
|
|
|
|
not_workorder_path = '{}/data/not_workorder.pl'.format(panelPath)
|
|
if not os.path.exists(not_workorder_path):
|
|
writeFile(not_workorder_path,'True')
|
|
bind_path = '{}/data/bind_path.pl'.format(panelPath)
|
|
if os.path.exists(bind_path):
|
|
os.remove(bind_path)
|
|
userinfo_path = '{}/data/userInfo.json'.format(panelPath)
|
|
if not os.path.exists(userinfo_path):
|
|
writeFile(userinfo_path,'{"uid":1,"username":"Administrator","address":"127.0.0.1","serverid":"1","access_key":"test","secret_key":"123456","ukey":"123456","state":1}')
|
|
|
|
local_path = '{}/temp/api.py'.format(setupPath)
|
|
downloadFileByWget('{}/win/panel/data/api.py'.format(url),local_path)
|
|
if os.path.exists(local_path):
|
|
os.remove('C:/Program Files/python/Lib/site-packages/requests/api.py')
|
|
shutil.move(local_path,'C:/Program Files/python/Lib/site-packages/requests')
|
|
|
|
local_path = '{}/script/BtTools.exe'.format(panelPath)
|
|
downloadFileByWget('{}/win/panel/BtTools{}.exe'.format(url,net_v),local_path)
|
|
if os.path.getsize(local_path) > 128:
|
|
return True
|
|
return False
|
|
downloadFileByWget('{}/win/panel/data/softList.conf'.format(url),'{}/data/softList.conf'.format(panelPath))
|
|
try:
|
|
from gevent import monkey
|
|
except :
|
|
os.system('"C:\Program Files\python\python.exe" -m pip install gevent')
|
|
except :
|
|
writeFile(error_path,get_error_info())
|
|
|
|
def update_panel():
|
|
|
|
file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json']
|
|
download_panel(file_list)
|
|
|
|
py_path = 'C:/Program Files/python/python.exe'
|
|
|
|
ExecShell("\"{}\" {}/panel/runserver.py --startup auto install".format(py_path,setupPath))
|
|
ExecShell("\"{}\" {}/panel/task.py --startup auto install".format(py_path,setupPath))
|
|
|
|
print("升级成功,重启面板后生效..")
|
|
|
|
def init_panel_data():
|
|
try:
|
|
sql = Sql()
|
|
username = sql.table('users').where('id=?',(1,)).getField('username')
|
|
if username == 'admin':
|
|
username = GetRandomString(8)
|
|
password = GetRandomString(8)
|
|
writeFile(panelPath + '/data/default.pl',password)
|
|
|
|
sql.table('users').where('id=?',(1,)).setField('username',username)
|
|
pwd = password_salt(md5(password),uid=1)
|
|
|
|
result = sql.table('users').where('id=?',(1,)).setField('password',pwd)
|
|
|
|
backup_path = panelPath[:2] + '/backup'
|
|
www_path = panelPath[:2] + '/wwwroot'
|
|
|
|
if not os.path.exists(backup_path): os.makedirs(backup_path)
|
|
if not os.path.exists(www_path): os.makedirs(www_path)
|
|
|
|
sql.table('config').where('id=?',(1,)).setField('backup_path',backup_path)
|
|
sql.table('config').where('id=?',(1,)).setField('sites_path',www_path)
|
|
|
|
bind_path = panelPath+ '/data/bind_path.pl'
|
|
if not os.path.exists(bind_path): writeFile(bind_path,'True')
|
|
|
|
admin_path = panelPath+ '/data/admin_path.pl'
|
|
if not os.path.exists(admin_path): writeFile(admin_path,"/" + GetRandomString(8))
|
|
|
|
port_path = panelPath+ '/data/port.pl'
|
|
if not os.path.exists(port_path): writeFile(port_path,'8888')
|
|
|
|
recycle_bin_db = panelPath+ '/data/recycle_bin_db.pl'
|
|
if not os.path.exists(recycle_bin_db): writeFile(recycle_bin_db,'True')
|
|
|
|
recycle_bin = panelPath+ '/data/recycle_bin.pl'
|
|
if not os.path.exists(recycle_bin): writeFile(recycle_bin,'True')
|
|
|
|
conf_path = panelPath + '/config/config.json'
|
|
if os.path.exists(conf_path):
|
|
conf = readFile(conf_path).replace('[PATH]',setupPath.replace('\\','/'))
|
|
writeFile(conf_path,conf)
|
|
|
|
GetLocalIp()
|
|
|
|
return True
|
|
except :
|
|
writeFile(error_path,get_error_info())
|
|
return False
|
|
|
|
def add_panel_services(num = 0):
|
|
try:
|
|
py_path = 'C:/Program Files/python/python.exe'
|
|
|
|
delete_server('btPanel')
|
|
ret = ExecShell("\"{}\" {}/panel/runserver.py --startup auto install".format(py_path,setupPath))
|
|
|
|
delete_server('btTask')
|
|
ret1 = ExecShell("\"{}\" {}/panel/task.py --startup auto install".format(py_path,setupPath))
|
|
|
|
if get_server_status('btPanel') < 0 or get_server_status('btTask') < 0:
|
|
if num <= 0 :
|
|
localPath = setupPath + "/temp/Time_Zones.reg";
|
|
downloadFileByWget(get_url() + '/win/panel/data/Time_Zones.reg',localPath)
|
|
ExecShell("regedit /s " + localPath)
|
|
|
|
add_panel_services(1)
|
|
else:
|
|
writeFile(error_path,ret[0] + ret[1] + ret1[0] + ret1[1])
|
|
else:
|
|
os.system('sc failure btPanel reset=1800 actions=restart/60000/restart/120000/restart/30000')
|
|
os.system('sc failure btTask reset=1800 actions=restart/60000/restart/120000/restart/30000')
|
|
start_service('btPanel')
|
|
start_service('btTask')
|
|
except :
|
|
writeFile(error_path,get_error_info())
|
|
|
|
|
|
def add_firewall_byport():
|
|
|
|
conf = ExecShell('netsh advfirewall firewall show rule "宝塔面板"')[0]
|
|
if conf.lower().find('tcp') == -1:
|
|
ExecShell("netsh advfirewall firewall add rule name=宝塔面板 dir=in action=allow protocol=tcp localport=8888");
|
|
ExecShell("netsh advfirewall firewall add rule name=网站访问端口 dir=in action=allow protocol=tcp localport=80");
|
|
ExecShell("netsh advfirewall firewall add rule name=远程桌面 dir=in action=allow protocol=tcp localport=3389");
|
|
ExecShell("netsh advfirewall firewall add rule name=HTTPS端口 dir=in action=allow protocol=tcp localport=443");
|
|
ExecShell("netsh advfirewall firewall add rule name=FTP主动端口 dir=in action=allow protocol=tcp localport=21");
|
|
ExecShell("netsh advfirewall firewall add rule name=FTP被动端口 dir=in action=allow protocol=tcp localport=3000-4000");
|
|
|
|
def get_error_log():
|
|
error = readFile(error_path)
|
|
try:
|
|
data = {}
|
|
data['msg'] = 'setup'
|
|
data['os'] = 'Windows'
|
|
data['error'] = error
|
|
data['version'] = ''
|
|
httpPost('http://www.example.com/api/wpanel/PanelBug',data)
|
|
except :
|
|
pass
|
|
return error
|
|
|
|
if __name__ == "__main__":
|
|
stype = sys.argv[1];
|
|
if not stype in ['get_error_log']:
|
|
if os.path.exists(error_path): os.remove(error_path)
|
|
result = eval('{}()'.format(stype))
|
|
print(result)
|
|
|
|
|
|
|
|
|