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

1 year ago
  1. #coding: utf-8
  2. # +-------------------------------------------------------------------
  3. # | 宝塔Windows面板
  4. # +-------------------------------------------------------------------
  5. # | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
  6. # +-------------------------------------------------------------------
  7. # | Author: 沐落 <[email protected]>
  8. # +-------------------------------------------------------------------
  9. import os,chardet,time,sys,re
  10. import win32net, win32api, win32netcon,win32security,win32serviceutil
  11. import traceback,shlex,datetime,subprocess,platform
  12. import sqlite3,shutil
  13. def readReg(path,key):
  14. import winreg
  15. try:
  16. newKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE ,path)
  17. value,type = winreg.QueryValueEx(newKey, key)
  18. return value
  19. except :
  20. return False
  21. panelPath = readReg(r'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\宝塔面板','PanelPath')
  22. if not panelPath:
  23. panelPath = os.getenv('BT_PANEL')
  24. if not panelPath: exit();
  25. setupPath = os.path.dirname(panelPath)
  26. error_path = '{}/error.log'.format(setupPath)
  27. logPath = panelPath + '/data/panelExec.log'
  28. class Sql():
  29. #------------------------------
  30. # 数据库操作类 For sqlite3
  31. #------------------------------
  32. __DB_FILE = None # 数据库文件
  33. __DB_CONN = None # 数据库连接对象
  34. __DB_TABLE = "" # 被操作的表名称
  35. __OPT_WHERE = "" # where条件
  36. __OPT_LIMIT = "" # limit条件
  37. __OPT_ORDER = "" # order条件
  38. __OPT_FIELD = "*" # field条件
  39. __OPT_PARAM = () # where值
  40. __LOCK = panelPath + '/data/sqlite_lock.pl'
  41. def __init__(self):
  42. self.__DB_FILE = panelPath + '/data/default.db'
  43. def __GetConn(self):
  44. #取数据库对象
  45. try:
  46. if self.__DB_CONN == None:
  47. self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
  48. self.__DB_CONN.text_factory = str
  49. except Exception as ex:
  50. print(str(ex))
  51. return "error: " + str(ex)
  52. def table(self,table):
  53. #设置表名
  54. self.__DB_TABLE = table
  55. return self
  56. def where(self,where,param):
  57. #WHERE条件
  58. if where:
  59. self.__OPT_WHERE = " WHERE " + where
  60. self.__OPT_PARAM = self.__to_tuple(param)
  61. return self
  62. def __to_tuple(self,param):
  63. #将参数转换为tuple
  64. if type(param) != tuple:
  65. if type(param) == list:
  66. param = tuple(param)
  67. else:
  68. param = (param,)
  69. return param
  70. #更新数据
  71. def update(self,pdata):
  72. if not pdata: return False
  73. keys,param = self.__format_pdata(pdata)
  74. return self.save(keys,param)
  75. #构造数据
  76. def __format_pdata(self,pdata):
  77. keys = pdata.keys()
  78. keys_str = ','.join(keys)
  79. param = []
  80. for k in keys: param.append(pdata[k])
  81. return keys_str,tuple(param)
  82. def field(self,field):
  83. #FIELD条件
  84. if len(field):
  85. self.__OPT_FIELD = field
  86. return self
  87. def getField(self,keyName):
  88. #取回指定字段
  89. result = self.field(keyName).select()
  90. print(result)
  91. if len(result) != 0:
  92. return result[0][keyName]
  93. return result
  94. def __format_field(self,field):
  95. import re
  96. fields = []
  97. for key in field:
  98. s_as = re.search(r'\s+as\s+',key,flags=re.IGNORECASE)
  99. if s_as:
  100. as_tip = s_as.group()
  101. key = key.split(as_tip)[1]
  102. fields.append(key)
  103. return fields
  104. def __get_columns(self):
  105. if self.__OPT_FIELD == '*':
  106. tmp_cols = self.query('PRAGMA table_info('+self.__DB_TABLE+')',())
  107. cols = []
  108. for col in tmp_cols:
  109. if len(col) > 2: cols.append('`' + col[1] + '`')
  110. if len(cols) > 0: self.__OPT_FIELD = ','.join(cols)
  111. def select(self):
  112. #查询数据集
  113. self.__GetConn()
  114. try:
  115. self.__get_columns()
  116. sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + self.__OPT_WHERE + self.__OPT_ORDER + self.__OPT_LIMIT
  117. result = self.__DB_CONN.execute(sql,self.__OPT_PARAM)
  118. data = result.fetchall()
  119. #构造字典系列
  120. if self.__OPT_FIELD != "*":
  121. fields = self.__format_field(self.__OPT_FIELD.split(','))
  122. tmp = []
  123. for row in data:
  124. i=0
  125. tmp1 = {}
  126. for key in fields:
  127. tmp1[key.strip('`')] = row[i]
  128. i += 1
  129. tmp.append(tmp1)
  130. del(tmp1)
  131. data = tmp
  132. del(tmp)
  133. else:
  134. #将元组转换成列表
  135. tmp = list(map(list,data))
  136. data = tmp
  137. del(tmp)
  138. self.__close()
  139. return data
  140. except Exception as ex:
  141. return "error: " + str(ex)
  142. def setField(self,keyName,keyValue):
  143. #更新指定字段
  144. return self.save(keyName,(keyValue,))
  145. def commit(self):
  146. self.__close()
  147. self.__DB_CONN.commit()
  148. def save(self,keys,param):
  149. #更新数据
  150. self.write_lock()
  151. self.__GetConn()
  152. self.__DB_CONN.text_factory = str
  153. try:
  154. opt = ""
  155. for key in keys.split(','):
  156. opt += key + "=?,"
  157. opt = opt[0:len(opt)-1]
  158. sql = "UPDATE " + self.__DB_TABLE + " SET " + opt+self.__OPT_WHERE
  159. #处理拼接WHERE与UPDATE参数
  160. tmp = list(self.__to_tuple(param))
  161. for arg in self.__OPT_PARAM:
  162. tmp.append(arg)
  163. self.__OPT_PARAM = tuple(tmp)
  164. result = self.__DB_CONN.execute(sql,self.__OPT_PARAM)
  165. self.__close()
  166. self.__DB_CONN.commit()
  167. self.rm_lock()
  168. return result.rowcount
  169. except Exception as ex:
  170. return "error: " + str(ex)
  171. def execute(self,sql,param = ()):
  172. #执行SQL语句返回受影响行
  173. self.write_lock()
  174. self.__GetConn()
  175. try:
  176. result = self.__DB_CONN.execute(sql,self.__to_tuple(param))
  177. self.__DB_CONN.commit()
  178. self.rm_lock()
  179. return result.rowcount
  180. except Exception as ex:
  181. return "error: " + str(ex)
  182. #是否有锁
  183. def is_lock(self):
  184. n = 0
  185. while os.path.exists(self.__LOCK):
  186. n+=1
  187. if n > 100:
  188. self.rm_lock()
  189. break
  190. time.sleep(0.01)
  191. #写锁
  192. def write_lock(self):
  193. self.is_lock()
  194. open(self.__LOCK,'wb+').close()
  195. #解锁
  196. def rm_lock(self):
  197. if os.path.exists(self.__LOCK):
  198. os.remove(self.__LOCK)
  199. def query(self,sql,param = ()):
  200. #执行SQL语句返回数据集
  201. self.__GetConn()
  202. try:
  203. result = self.__DB_CONN.execute(sql,self.__to_tuple(param))
  204. #将元组转换成列表
  205. data = list(map(list,result))
  206. return data
  207. except Exception as ex:
  208. return "error: " + str(ex)
  209. def __close(self):
  210. #清理条件属性
  211. self.__OPT_WHERE = ""
  212. self.__OPT_FIELD = "*"
  213. self.__OPT_ORDER = ""
  214. self.__OPT_LIMIT = ""
  215. self.__OPT_PARAM = ()
  216. def close(self):
  217. #释放资源
  218. try:
  219. self.__DB_CONN.close()
  220. self.__DB_CONN = None
  221. except:
  222. pass
  223. def GetLocalIp():
  224. """
  225. IP
  226. """
  227. try:
  228. filename = panelPath + '/data/iplist.txt'
  229. ipaddress = readFile(filename)
  230. if not ipaddress:
  231. url = 'http://www.example.com/api/getIpAddress';
  232. str = httpGet(url)
  233. writeFile(filename,ipaddress)
  234. ipaddress = re.search('\d+.\d+.\d+.\d+',ipaddress).group(0);
  235. return ipaddress
  236. except:
  237. try:
  238. url = 'https://www.bt.cn/Api/getIpAddress';
  239. str = httpGet(url)
  240. writeFile(filename,ipaddress)
  241. return str
  242. except:
  243. pass
  244. def get_error_info():
  245. errorMsg = traceback.format_exc();
  246. return errorMsg
  247. def get_server_status(name):
  248. try:
  249. serviceStatus = win32serviceutil.QueryServiceStatus(name)
  250. if serviceStatus[1] == 4:
  251. return 1
  252. return 0
  253. except :
  254. return -1
  255. def start_service(name):
  256. try:
  257. timeout = 0;
  258. while get_server_status(name) == 0:
  259. try:
  260. win32serviceutil.StartService(name)
  261. time.sleep(1);
  262. except : time.sleep(1);
  263. timeout += 1
  264. if timeout > 10:break
  265. if get_server_status(name) != 0:
  266. return True,None
  267. return False,'操作失败,10秒内未完成启动服务【{}】'.format(name)
  268. except :
  269. return False,get_error_info()
  270. def stop_service(name):
  271. try:
  272. timeout = 0;
  273. while get_server_status(name) == 1:
  274. try:
  275. win32serviceutil.StopService(name)
  276. time.sleep(1);
  277. except : time.sleep(1);
  278. timeout += 1
  279. if timeout > 10:break
  280. if get_server_status(name) != 1:
  281. return True,None
  282. return False,'操作失败,10秒内未完成启动服务【{}】'.format(name)
  283. except :
  284. return False,get_error_info()
  285. def delete_server(name):
  286. try:
  287. stop_service(name)
  288. win32serviceutil.RemoveService(name)
  289. return True,''
  290. except :
  291. return False,get_error_info()
  292. def get_requests_headers():
  293. return {"Content-type":"application/x-www-form-urlencoded","User-Agent":"BT-Panel"}
  294. def downloadFile(url,filename):
  295. try:
  296. import requests
  297. res = requests.get(url,verify=False)
  298. with open(filename,"wb") as f:
  299. f.write(res.content)
  300. except:
  301. import requests
  302. res = requests.get(url,verify=False)
  303. with open(filename,"wb") as f:
  304. f.write(res.content)
  305. def downloadFileByWget(url,filename):
  306. """
  307. wget下载文件
  308. @url
  309. @filename
  310. """
  311. try:
  312. if os.path.exists(logPath): os.remove(logPath)
  313. except : pass
  314. loacl_path = '{}/script/wget.exe'.format(panelPath)
  315. if not os.path.exists(loacl_path): downloadFile(get_url()+'/win/panel/data/wget.exe',loacl_path)
  316. if os.path.getsize(loacl_path) < 10:
  317. os.remove(loacl_path)
  318. downloadFile(url,filename)
  319. else:
  320. shell = "{} {} -O {} -t 5 -T 60 --no-check-certificate --auth-no-challenge --force-directorie > {} 2>&1".format(loacl_path,url,filename,logPath)
  321. os.system(shell)
  322. num = 0
  323. re_size = 0
  324. while num <= 5:
  325. if os.path.exists(filename):
  326. cr_size = os.path.getsize(filename)
  327. if re_size > 0 and re_size == cr_size:
  328. break;
  329. else:
  330. re_size = cr_size
  331. time.sleep(0.5)
  332. num += 1
  333. if os.path.exists(filename):
  334. if os.path.getsize(filename) < 1:
  335. os.remove(filename)
  336. downloadFile(url,filename)
  337. else:
  338. downloadFile(url,filename)
  339. def writeFile(filename,s_body,mode='w+',encoding = 'utf-8'):
  340. try:
  341. fp = open(filename, mode,encoding = encoding);
  342. fp.write(s_body)
  343. fp.close()
  344. return True
  345. except:
  346. return False
  347. def readFile(filename,mode = 'r'):
  348. import os,chardet
  349. if not os.path.exists(filename): return False
  350. if not os.path.isfile(filename): return False
  351. encoding = 'utf-8'
  352. f_body = '';
  353. try:
  354. fp = open(filename, mode,encoding = encoding)
  355. f_body = fp.read()
  356. except :
  357. fp.close()
  358. try:
  359. encoding = 'gbk'
  360. fp = open(filename, mode,encoding = encoding)
  361. f_body = fp.read()
  362. except :
  363. fp.close()
  364. encoding = 'ansi'
  365. fp = open(filename, mode,encoding = encoding)
  366. f_body = fp.read()
  367. try:
  368. if f_body[0] == '\ufeff':
  369. #处理带bom格式
  370. new_code = chardet.detect(f_body.encode(encoding))["encoding"]
  371. f_body = f_body.encode(encoding).decode(new_code);
  372. except : pass
  373. fp.close()
  374. return f_body
  375. def httpGet(url,timeout = 60,headers = {}):
  376. try:
  377. import urllib.request,ssl
  378. try:
  379. ssl._create_default_https_context = ssl._create_unverified_context
  380. except:pass;
  381. req = urllib.request.Request(url,headers = headers)
  382. response = urllib.request.urlopen(req,timeout = timeout)
  383. result = response.read()
  384. if type(result) == bytes:
  385. try:
  386. result = result.decode('utf-8')
  387. except :
  388. result = result.decode('gb2312')
  389. return result
  390. except Exception as ex:
  391. if headers: return False
  392. return str(ex)
  393. def httpPost(url, data, timeout=60, headers={}):
  394. try:
  395. import urllib.request,ssl
  396. try:
  397. ssl._create_default_https_context = ssl._create_unverified_context
  398. except:pass;
  399. data2 = urllib.parse.urlencode(data).encode('utf-8')
  400. req = urllib.request.Request(url, data2,headers = headers)
  401. response = urllib.request.urlopen(req,timeout = timeout)
  402. result = response.read()
  403. if type(result) == bytes: result = result.decode('utf-8')
  404. return result
  405. except Exception as ex:
  406. return str(ex);
  407. def get_timeout(url,timeout=3):
  408. try:
  409. start = time.time()
  410. result = int(httpGet(url,timeout))
  411. return result,int((time.time() - start) * 1000 - 500)
  412. except: return 0,False
  413. def get_url(timeout = 0.5):
  414. import json
  415. try:
  416. #
  417. 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}]
  418. mnode1 = []
  419. mnode2 = []
  420. mnode3 = []
  421. for node in node_list:
  422. node['net'],node['ping'] = get_timeout(node['protocol'] + node['address'] + ':' + node['port'] + '/net_test',1)
  423. if not node['ping']: continue
  424. if node['ping'] < 100: #当响应时间<100ms且可用带宽大于1500KB时
  425. if node['net'] > 1500:
  426. mnode1.append(node)
  427. elif node['net'] > 1000:
  428. mnode3.append(node)
  429. else:
  430. if node['net'] > 1000: #当响应时间>=100ms且可用带宽大于1000KB时
  431. mnode2.append(node)
  432. if node['ping'] < 100:
  433. if node['net'] > 3000: break #有节点可用带宽大于3000时,不再检查其它节点
  434. if mnode1: #优选低延迟高带宽
  435. mnode = sorted(mnode1,key= lambda x:x['net'],reverse=True)
  436. elif mnode3: #备选低延迟,中等带宽
  437. mnode = sorted(mnode3,key= lambda x:x['net'],reverse=True)
  438. else: #终选中等延迟,中等带宽
  439. mnode = sorted(mnode2,key= lambda x:x['ping'],reverse=False)
  440. if not mnode: return 'https://download.bt.cn'
  441. #return mnode[0]['protocol'] + mnode[0]['address'] + ':' + mnode[0]['port']
  442. return "https://" + mnode[0]['address']
  443. except:
  444. return 'https://download.bt.cn'
  445. #删除文件权限
  446. def del_file_access(filename,user):
  447. try:
  448. if filename.lower() in ["c:/","c:","c:\\","c"]:
  449. return True
  450. import win32security
  451. sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
  452. dacl = sd.GetSecurityDescriptorDacl()
  453. ace_count = dacl.GetAceCount()
  454. for i in range(ace_count ,0 ,-1):
  455. try:
  456. data = {}
  457. data['rev'], data['access'], usersid = dacl.GetAce(i-1)
  458. data['user'],data['group'], data['type'] = win32security.LookupAccountSid('', usersid)
  459. if data['user'].lower() == user.lower(): dacl.DeleteAce(i-1) #删除旧的dacl
  460. if data['user'].lower() == 'users': dacl.DeleteAce(i-1) #删除旧的dacl
  461. except :
  462. try:
  463. #处理拒绝访问
  464. dacl.DeleteAce(i-1)
  465. except : pass
  466. sd.SetSecurityDescriptorDacl(1, dacl, 0)
  467. win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
  468. except :
  469. pass
  470. return True
  471. def set_file_access(filename,user,access):
  472. try:
  473. sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
  474. dacl = sd.GetSecurityDescriptorDacl()
  475. ace_count = dacl.GetAceCount()
  476. for i in range(ace_count, 0,-1):
  477. try:
  478. data = {}
  479. data['rev'], data['access'], usersid = dacl.GetAce(i-1)
  480. data['user'],data['group'], data['type'] = win32security.LookupAccountSid('', usersid)
  481. if data['user'].lower() == user.lower(): dacl.DeleteAce(i-1) #删除旧的dacl
  482. if data['user'].lower() == 'users': dacl.DeleteAce(i-1) #删除旧的dacl
  483. except :
  484. pass
  485. try:
  486. userx, domain, type = win32security.LookupAccountName("", user)
  487. except :
  488. userx, domain, type = win32security.LookupAccountName("", 'IIS APPPOOL\\' + user)
  489. if access > 0: dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 3, access, userx)
  490. sd.SetSecurityDescriptorDacl(1, dacl, 0)
  491. win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
  492. return True,None
  493. except :
  494. return False,get_error_info()
  495. def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
  496. if shell:
  497. cmdstring_list = cmdstring
  498. else:
  499. cmdstring_list = shlex.split(cmdstring)
  500. if timeout:
  501. end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
  502. sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE,shell=shell,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  503. while sub.poll() is None:
  504. time.sleep(0.1)
  505. if timeout:
  506. if end_time <= datetime.datetime.now():
  507. raise Exception("Timeout:%s"%cmdstring)
  508. a,e = sub.communicate()
  509. if type(a) == bytes:
  510. try:
  511. a = a.decode('utf-8')
  512. except :
  513. a = a.decode('gb2312','ignore')
  514. if type(e) == bytes:
  515. try:
  516. e = e.decode('utf-8')
  517. except :
  518. e = e.decode('gb2312','ignore')
  519. return a,e
  520. def GetRandomString(length):
  521. from random import Random
  522. strings = ''
  523. chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  524. chrlen = len(chars) - 1
  525. random = Random()
  526. for i in range(length):
  527. strings += chars[random.randint(0, chrlen)]
  528. return strings
  529. def GetRandomString1(length):
  530. from random import Random
  531. strings = ''
  532. chars = '0123456789'
  533. chrlen = len(chars) - 1
  534. random = Random()
  535. for i in range(length):
  536. strings += chars[random.randint(0, chrlen)]
  537. return strings
  538. def GetRandomString2(length):
  539. from random import Random
  540. strings = ''
  541. chars = '!@#$%^&*()_+.,?[]-='
  542. chrlen = len(chars) - 1
  543. random = Random()
  544. for i in range(length):
  545. strings += chars[random.randint(0, chrlen)]
  546. return strings
  547. def chdck_salt():
  548. sql = Sql()
  549. sql.table('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
  550. u_list = sql.table('users').field('id,username,password,salt').select()
  551. for u_info in u_list:
  552. salt = GetRandomString(12) #12位随机
  553. pdata = {}
  554. pdata['password'] = md5(md5(u_info['password']+'_bt.cn') + salt)
  555. pdata['salt'] = salt
  556. sql.table('users').where('id=?',(u_info['id'],)).update(pdata)
  557. def md5(strings):
  558. """
  559. MD5
  560. @strings
  561. return string(32)
  562. """
  563. import hashlib
  564. m = hashlib.md5()
  565. m.update(strings.encode('utf-8'))
  566. return m.hexdigest()
  567. def password_salt(password,username=None,uid=None):
  568. chdck_salt()
  569. sql = Sql()
  570. if not uid:
  571. if not username:
  572. raise Exception('username或uid必需传一项')
  573. uid = sql.table('users').where('username=?',(username,)).getField('id')
  574. salt = sql.table('users').where('id=?',(uid,)).getField('salt')
  575. return md5(md5(password+'_bt.cn')+salt)
  576. def check_user(username):
  577. resume = 0
  578. while True:
  579. data, total, resume = win32net.NetUserEnum(None, 3, win32netcon.FILTER_NORMAL_ACCOUNT, resume)
  580. for user in data:
  581. if user['name'] == username: return True
  582. if not resume: break
  583. return False
  584. def add_user(username,password,ps):
  585. try:
  586. if not check_user(username):
  587. d = {}
  588. d['name'] = username
  589. d['password'] = password
  590. d['comment'] = ps
  591. d['flags'] = win32netcon.UF_NORMAL_ACCOUNT | win32netcon.UF_SCRIPT
  592. d['priv'] = win32netcon.USER_PRIV_USER
  593. win32net.NetUserAdd(None, 1, d)
  594. #设置用户允许登录服务
  595. handle = win32security.LsaOpenPolicy(None, win32security.POLICY_ALL_ACCESS)
  596. sid_obj, domain, tmp = win32security.LookupAccountName(None, username)
  597. win32security.LsaAddAccountRights(handle, sid_obj, ('SeServiceLogonRight',) )
  598. win32security.LsaClose( handle)
  599. if not check_user(username): return False, '添加用户[{}]失败.'.format(username)
  600. writeFile('{}/data/{}'.format(panelPath,username),password)
  601. return True , None
  602. else:
  603. ExecShell('net user "{}" "{}"'.format(username,password))
  604. writeFile('{}/data/{}'.format(panelPath,username),password)
  605. return True , None
  606. except :
  607. return False,get_error_info()
  608. def add_user_bywww():
  609. pwd = GetRandomString(64) + GetRandomString1(32) + GetRandomString2(32)
  610. status,error = add_user('www',pwd,'用于启动宝塔安装的程序,删除后会导致部分软件无法启动,请勿删除')
  611. if not status:
  612. writeFile(error_path,error)
  613. return False
  614. return True
  615. def add_user_bymysql():
  616. pwd = GetRandomString(64) + GetRandomString1(32) + GetRandomString2(32)
  617. status,error = add_user('mysql',pwd,'用于启动宝塔安装的程序,删除后会导致部分软件无法启动,请勿删除')
  618. if not status:
  619. writeFile(error_path,error)
  620. return False
  621. return True
  622. def getIP(url):
  623. import socket,re
  624. tmp = re.search('http://(.+)\:\d*',url)
  625. if tmp:
  626. domain = tmp.groups()[0]
  627. myaddr = socket.getaddrinfo(domain, 'http')
  628. return myaddr[0][4][0]
  629. return ''
  630. def add_panel_dir():
  631. try:
  632. slist = [
  633. [panelPath , [] ],
  634. ['{}/data'.format(panelPath) , [] ],
  635. ['{}/script'.format(panelPath) , [] ],
  636. ['{}/backup'.format(panelPath) , [] ],
  637. ['{}/backup/database/sqlserver'.format(setupPath[:2]) , [ 'Authenticated Users']],
  638. ['{}/wwwroot'.format(setupPath[:2]) , [ 'IIS_IUSRS','www'] ],
  639. ['{}/wwwlogs'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  640. ['{}/php'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  641. ['{}/mysql'.format(setupPath) , [ 'mysql'] ],
  642. ['{}/temp'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  643. ['{}/temp/session'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  644. ['C:/Temp' , [ 'IIS_IUSRS','www'] ],
  645. ]
  646. is_break = False
  647. for sobj in slist:
  648. if not os.path.exists(sobj[0]):
  649. os.makedirs(sobj[0])
  650. n = 0
  651. while n < 5:
  652. if os.path.exists(sobj[0]): break
  653. os.makedirs(sobj[0])
  654. time.sleep(0.5)
  655. n += 1
  656. if not os.path.exists(sobj[0]):
  657. writeFile(error_path,"自动创建目录【{}】失败,已重试最大次数 5 次,请手动创建该目录后重新安装".format(sobj[0]))
  658. return False
  659. del_file_access(sobj[0],'users')
  660. for user in sobj[1]:
  661. n = 0
  662. while n < 3:
  663. status,error = set_file_access(sobj[0],user,2032127)
  664. if status: break
  665. time.sleep(0.5)
  666. if not status:
  667. writeFile(error_path,"目录{}设置{}权限设置错误 -> {}".format(sobj[0],user,error))
  668. break
  669. del_file_access(setupPath,'users')
  670. url = get_url()
  671. files = ['default.db','session.db','system.db','phplib.win','defaultDoc.html','404.html']
  672. for f_name in files:
  673. local_path = '{}/data/{}'.format(panelPath,f_name)
  674. download_url = '{}/win/panel/data/{}'.format(url,f_name)
  675. n = 0
  676. while n < 10:
  677. n += 1;
  678. try:
  679. if os.path.exists(local_path) and os.path.getsize(local_path) < 10: os.remove(local_path)
  680. if not os.path.exists(local_path): downloadFileByWget(download_url,local_path)
  681. if os.path.getsize(local_path) and os.path.getsize(local_path) > 10: break;
  682. writeFile(error_path,'download {} error ->> {} \r\n {}'.format(f_name,download_url,""))
  683. except :
  684. ip = getIP(url)
  685. writeFile(error_path,'download {} error ->> {} \r\n connect {} \r\n {}'.format(ip,f_name,download_url,get_error_info()))
  686. if n > 5: return False
  687. time.sleep(0.2)
  688. return True
  689. except :
  690. writeFile(error_path,get_error_info())
  691. return False
  692. def unzip(src_path,dst_path):
  693. import zipfile
  694. zip_file = zipfile.ZipFile(src_path)
  695. for names in zip_file.namelist():
  696. zip_file.extract(names,dst_path)
  697. zip_file.close()
  698. return True
  699. def to_path(path):
  700. return path.replace('/','\\')
  701. def download_panel(file_list = []):
  702. try:
  703. url = 'http://www.example.com'
  704. ExecShell("taskkill /f /t /im BtTools.exe")
  705. #下载面板
  706. loacl_path = setupPath + '/panel.zip'
  707. tmpPath = "{}/temp/panel".format(setupPath)
  708. if os.path.exists(loacl_path): os.remove(loacl_path)
  709. if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True)
  710. if not os.path.exists(tmpPath): os.makedirs(tmpPath)
  711. p_ver = sys.argv[2]
  712. downUrl = url + '/win/panel/panel_' + p_ver + '.zip';
  713. downloadFileByWget(downUrl,loacl_path);
  714. unzip(loacl_path,tmpPath)
  715. for ff_path in file_list:
  716. if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path)
  717. tcPath = '{}\class'.format(tmpPath)
  718. for name in os.listdir(tcPath):
  719. try:
  720. if name.find('win_amd64.pyd') >=0:
  721. oldName = os.path.join(tcPath,name);
  722. lName = name.split('.')[0] + '.pyd'
  723. newName = os.path.join(tcPath,lName)
  724. if not os.path.exists(newName):os.rename(oldName,newName)
  725. except :pass
  726. cPath = '{}/panel/class'.format(setupPath)
  727. if os.path.exists(cPath):
  728. os.system("del /s {}\*.pyc".format(to_path(cPath)))
  729. os.system("del /s {}\*.pyt".format(to_path(cPath)))
  730. for name in os.listdir(cPath):
  731. try:
  732. if name.find('.pyd') >=0:
  733. oldName = os.path.join(cPath,name)
  734. newName = os.path.join(cPath,GetRandomString(8) + '.pyt')
  735. os.rename(oldName,newName)
  736. except : pass
  737. os.system("del /s {}\*.pyc".format(to_path(cPath)))
  738. os.system("del /s {}\*.pyt".format(to_path(cPath)))
  739. os.system("xcopy /s /c /e /y /r {} {}".format(to_path(tmpPath),to_path(panelPath)))
  740. try:
  741. os.remove(loacl_path)
  742. except : pass
  743. try:
  744. shutil.rmtree(tmpPath,True)
  745. except : pass
  746. s_ver = platform.platform()
  747. net_v = '45'
  748. if s_ver.find('2008') >= 0: net_v = '20'
  749. writeFile('{}/data/net'.format(setupPath),net_v)
  750. not_workorder_path = '{}/data/not_workorder.pl'.format(panelPath)
  751. if not os.path.exists(not_workorder_path):
  752. writeFile(not_workorder_path,'True')
  753. bind_path = '{}/data/bind_path.pl'.format(panelPath)
  754. if os.path.exists(bind_path):
  755. os.remove(bind_path)
  756. userinfo_path = '{}/data/userInfo.json'.format(panelPath)
  757. if not os.path.exists(userinfo_path):
  758. writeFile(userinfo_path,'{"uid":1,"username":"Administrator","address":"127.0.0.1","serverid":"1","access_key":"test","secret_key":"123456","ukey":"123456","state":1}')
  759. local_path = '{}/temp/api.py'.format(setupPath)
  760. downloadFileByWget('{}/win/panel/data/api.py'.format(url),local_path)
  761. if os.path.exists(local_path):
  762. os.remove('C:/Program Files/python/Lib/site-packages/requests/api.py')
  763. shutil.move(local_path,'C:/Program Files/python/Lib/site-packages/requests')
  764. local_path = '{}/script/BtTools.exe'.format(panelPath)
  765. downloadFileByWget('{}/win/panel/BtTools{}.exe'.format(url,net_v),local_path)
  766. if os.path.getsize(local_path) > 128:
  767. return True
  768. return False
  769. downloadFileByWget('{}/win/panel/data/softList.conf'.format(url),'{}/data/softList.conf'.format(panelPath))
  770. try:
  771. from gevent import monkey
  772. except :
  773. os.system('"C:\Program Files\python\python.exe" -m pip install gevent')
  774. except :
  775. writeFile(error_path,get_error_info())
  776. def update_panel():
  777. file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json']
  778. download_panel(file_list)
  779. py_path = 'C:/Program Files/python/python.exe'
  780. ExecShell("\"{}\" {}/panel/runserver.py --startup auto install".format(py_path,setupPath))
  781. ExecShell("\"{}\" {}/panel/task.py --startup auto install".format(py_path,setupPath))
  782. print("升级成功,重启面板后生效..")
  783. def init_panel_data():
  784. try:
  785. sql = Sql()
  786. username = sql.table('users').where('id=?',(1,)).getField('username')
  787. if username == 'admin':
  788. username = GetRandomString(8)
  789. password = GetRandomString(8)
  790. writeFile(panelPath + '/data/default.pl',password)
  791. sql.table('users').where('id=?',(1,)).setField('username',username)
  792. pwd = password_salt(md5(password),uid=1)
  793. result = sql.table('users').where('id=?',(1,)).setField('password',pwd)
  794. backup_path = panelPath[:2] + '/backup'
  795. www_path = panelPath[:2] + '/wwwroot'
  796. if not os.path.exists(backup_path): os.makedirs(backup_path)
  797. if not os.path.exists(www_path): os.makedirs(www_path)
  798. sql.table('config').where('id=?',(1,)).setField('backup_path',backup_path)
  799. sql.table('config').where('id=?',(1,)).setField('sites_path',www_path)
  800. bind_path = panelPath+ '/data/bind_path.pl'
  801. if not os.path.exists(bind_path): writeFile(bind_path,'True')
  802. admin_path = panelPath+ '/data/admin_path.pl'
  803. if not os.path.exists(admin_path): writeFile(admin_path,"/" + GetRandomString(8))
  804. port_path = panelPath+ '/data/port.pl'
  805. if not os.path.exists(port_path): writeFile(port_path,'8888')
  806. recycle_bin_db = panelPath+ '/data/recycle_bin_db.pl'
  807. if not os.path.exists(recycle_bin_db): writeFile(recycle_bin_db,'True')
  808. recycle_bin = panelPath+ '/data/recycle_bin.pl'
  809. if not os.path.exists(recycle_bin): writeFile(recycle_bin,'True')
  810. conf_path = panelPath + '/config/config.json'
  811. if os.path.exists(conf_path):
  812. conf = readFile(conf_path).replace('[PATH]',setupPath.replace('\\','/'))
  813. writeFile(conf_path,conf)
  814. GetLocalIp()
  815. return True
  816. except :
  817. writeFile(error_path,get_error_info())
  818. return False
  819. def add_panel_services(num = 0):
  820. try:
  821. py_path = 'C:/Program Files/python/python.exe'
  822. delete_server('btPanel')
  823. ret = ExecShell("\"{}\" {}/panel/runserver.py --startup auto install".format(py_path,setupPath))
  824. delete_server('btTask')
  825. ret1 = ExecShell("\"{}\" {}/panel/task.py --startup auto install".format(py_path,setupPath))
  826. if get_server_status('btPanel') < 0 or get_server_status('btTask') < 0:
  827. if num <= 0 :
  828. localPath = setupPath + "/temp/Time_Zones.reg";
  829. downloadFileByWget(get_url() + '/win/panel/data/Time_Zones.reg',localPath)
  830. ExecShell("regedit /s " + localPath)
  831. add_panel_services(1)
  832. else:
  833. writeFile(error_path,ret[0] + ret[1] + ret1[0] + ret1[1])
  834. else:
  835. os.system('sc failure btPanel reset=1800 actions=restart/60000/restart/120000/restart/30000')
  836. os.system('sc failure btTask reset=1800 actions=restart/60000/restart/120000/restart/30000')
  837. start_service('btPanel')
  838. start_service('btTask')
  839. except :
  840. writeFile(error_path,get_error_info())
  841. def add_firewall_byport():
  842. conf = ExecShell('netsh advfirewall firewall show rule "宝塔面板"')[0]
  843. if conf.lower().find('tcp') == -1:
  844. ExecShell("netsh advfirewall firewall add rule name=宝塔面板 dir=in action=allow protocol=tcp localport=8888");
  845. ExecShell("netsh advfirewall firewall add rule name=网站访问端口 dir=in action=allow protocol=tcp localport=80");
  846. ExecShell("netsh advfirewall firewall add rule name=远程桌面 dir=in action=allow protocol=tcp localport=3389");
  847. ExecShell("netsh advfirewall firewall add rule name=HTTPS端口 dir=in action=allow protocol=tcp localport=443");
  848. ExecShell("netsh advfirewall firewall add rule name=FTP主动端口 dir=in action=allow protocol=tcp localport=21");
  849. ExecShell("netsh advfirewall firewall add rule name=FTP被动端口 dir=in action=allow protocol=tcp localport=3000-4000");
  850. def get_error_log():
  851. error = readFile(error_path)
  852. try:
  853. data = {}
  854. data['msg'] = 'setup'
  855. data['os'] = 'Windows'
  856. data['error'] = error
  857. data['version'] = ''
  858. httpPost('http://www.example.com/api/wpanel/PanelBug',data)
  859. except :
  860. pass
  861. return error
  862. if __name__ == "__main__":
  863. stype = sys.argv[1];
  864. if not stype in ['get_error_log']:
  865. if os.path.exists(error_path): os.remove(error_path)
  866. result = eval('{}()'.format(stype))
  867. print(result)