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.

1040 lines
36 KiB

3 years ago
  1. #coding: utf-8
  2. # +-------------------------------------------------------------------
  3. # | 宝塔Windows面板
  4. # +-------------------------------------------------------------------
  5. # | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
  6. # +-------------------------------------------------------------------
  7. # | Author: 沐落 <cjx@bt.cn>
  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://pv.sohu.com/cityjson?ie=utf-8'
  232. str = httpGet(url)
  233. ipaddress = re.search('\d+.\d+.\d+.\d+',str).group(0)
  234. writeFile(filename,ipaddress)
  235. ipaddress = re.search('\d+.\d+.\d+.\d+',ipaddress).group(0);
  236. return ipaddress
  237. except:
  238. try:
  239. url = 'http://www.example.com/api/getIpAddress';
  240. str = httpGet(url)
  241. writeFile(filename,ipaddress)
  242. return str
  243. except:
  244. pass
  245. def get_error_info():
  246. errorMsg = traceback.format_exc();
  247. return errorMsg
  248. def get_server_status(name):
  249. try:
  250. serviceStatus = win32serviceutil.QueryServiceStatus(name)
  251. if serviceStatus[1] == 4:
  252. return 1
  253. return 0
  254. except :
  255. return -1
  256. def start_service(name):
  257. try:
  258. timeout = 0;
  259. while get_server_status(name) == 0:
  260. try:
  261. win32serviceutil.StartService(name)
  262. time.sleep(1);
  263. except : time.sleep(1);
  264. timeout += 1
  265. if timeout > 10:break
  266. if get_server_status(name) != 0:
  267. return True,None
  268. return False,'操作失败,10秒内未完成启动服务【{}】'.format(name)
  269. except :
  270. return False,get_error_info()
  271. def stop_service(name):
  272. try:
  273. timeout = 0;
  274. while get_server_status(name) == 1:
  275. try:
  276. win32serviceutil.StopService(name)
  277. time.sleep(1);
  278. except : time.sleep(1);
  279. timeout += 1
  280. if timeout > 10:break
  281. if get_server_status(name) != 1:
  282. return True,None
  283. return False,'操作失败,10秒内未完成启动服务【{}】'.format(name)
  284. except :
  285. return False,get_error_info()
  286. def delete_server(name):
  287. try:
  288. stop_service(name)
  289. win32serviceutil.RemoveService(name)
  290. return True,''
  291. except :
  292. return False,get_error_info()
  293. def get_requests_headers():
  294. return {"Content-type":"application/x-www-form-urlencoded","User-Agent":"BT-Panel"}
  295. def downloadFile(url,filename):
  296. try:
  297. import requests
  298. res = requests.get(url,verify=False)
  299. with open(filename,"wb") as f:
  300. f.write(res.content)
  301. except:
  302. import requests
  303. res = requests.get(url,verify=False)
  304. with open(filename,"wb") as f:
  305. f.write(res.content)
  306. def downloadFileByWget(url,filename):
  307. """
  308. wget下载文件
  309. @url
  310. @filename
  311. """
  312. try:
  313. if os.path.exists(logPath): os.remove(logPath)
  314. except : pass
  315. loacl_path = '{}/script/wget.exe'.format(panelPath)
  316. if not os.path.exists(loacl_path): downloadFile(get_url()+'/win/panel/data/wget.exe',loacl_path)
  317. if os.path.getsize(loacl_path) < 10:
  318. os.remove(loacl_path)
  319. downloadFile(url,filename)
  320. else:
  321. shell = "{} {} -O {} -t 5 -T 60 --no-check-certificate --auth-no-challenge --force-directorie > {} 2>&1".format(loacl_path,url,filename,logPath)
  322. os.system(shell)
  323. num = 0
  324. re_size = 0
  325. while num <= 5:
  326. if os.path.exists(filename):
  327. cr_size = os.path.getsize(filename)
  328. if re_size > 0 and re_size == cr_size:
  329. break;
  330. else:
  331. re_size = cr_size
  332. time.sleep(0.5)
  333. num += 1
  334. if os.path.exists(filename):
  335. if os.path.getsize(filename) < 1:
  336. os.remove(filename)
  337. downloadFile(url,filename)
  338. else:
  339. downloadFile(url,filename)
  340. def writeFile(filename,s_body,mode='w+',encoding = 'utf-8'):
  341. try:
  342. fp = open(filename, mode,encoding = encoding);
  343. fp.write(s_body)
  344. fp.close()
  345. return True
  346. except:
  347. return False
  348. def readFile(filename,mode = 'r'):
  349. import os,chardet
  350. if not os.path.exists(filename): return False
  351. if not os.path.isfile(filename): return False
  352. encoding = 'utf-8'
  353. f_body = '';
  354. try:
  355. fp = open(filename, mode,encoding = encoding)
  356. f_body = fp.read()
  357. except :
  358. fp.close()
  359. try:
  360. encoding = 'gbk'
  361. fp = open(filename, mode,encoding = encoding)
  362. f_body = fp.read()
  363. except :
  364. fp.close()
  365. encoding = 'ansi'
  366. fp = open(filename, mode,encoding = encoding)
  367. f_body = fp.read()
  368. try:
  369. if f_body[0] == '\ufeff':
  370. #处理带bom格式
  371. new_code = chardet.detect(f_body.encode(encoding))["encoding"]
  372. f_body = f_body.encode(encoding).decode(new_code);
  373. except : pass
  374. fp.close()
  375. return f_body
  376. def httpGet(url,timeout = 60,headers = {}):
  377. try:
  378. import urllib.request,ssl
  379. try:
  380. ssl._create_default_https_context = ssl._create_unverified_context
  381. except:pass;
  382. req = urllib.request.Request(url,headers = headers)
  383. response = urllib.request.urlopen(req,timeout = timeout)
  384. result = response.read()
  385. if type(result) == bytes:
  386. try:
  387. result = result.decode('utf-8')
  388. except :
  389. result = result.decode('gb2312')
  390. return result
  391. except Exception as ex:
  392. if headers: return False
  393. return str(ex)
  394. def httpPost(url, data, timeout=60, headers={}):
  395. try:
  396. import urllib.request,ssl
  397. try:
  398. ssl._create_default_https_context = ssl._create_unverified_context
  399. except:pass;
  400. data2 = urllib.parse.urlencode(data).encode('utf-8')
  401. req = urllib.request.Request(url, data2,headers = headers)
  402. response = urllib.request.urlopen(req,timeout = timeout)
  403. result = response.read()
  404. if type(result) == bytes: result = result.decode('utf-8')
  405. return result
  406. except Exception as ex:
  407. return str(ex);
  408. def get_timeout(url,timeout=3):
  409. try:
  410. start = time.time()
  411. result = int(httpGet(url,timeout))
  412. return result,int((time.time() - start) * 1000 - 500)
  413. except: return 0,False
  414. def get_url(timeout = 0.5):
  415. import json
  416. try:
  417. #
  418. node_list = [{"protocol":"http://","address":"dg1.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"dg2.bt.cn","port":"80","ping":500},{"protocol":"http://","address":"node.aapanel.com","port":"80","ping":500},{"protocol":"http://","address":"download.bt.cn","port":"80","ping":500}]
  419. mnode1 = []
  420. mnode2 = []
  421. mnode3 = []
  422. for node in node_list:
  423. node['net'],node['ping'] = get_timeout(node['protocol'] + node['address'] + ':' + node['port'] + '/net_test',1)
  424. if not node['ping']: continue
  425. if node['ping'] < 100: #当响应时间<100ms且可用带宽大于1500KB时
  426. if node['net'] > 1500:
  427. mnode1.append(node)
  428. elif node['net'] > 1000:
  429. mnode3.append(node)
  430. else:
  431. if node['net'] > 1000: #当响应时间>=100ms且可用带宽大于1000KB时
  432. mnode2.append(node)
  433. if node['ping'] < 100:
  434. if node['net'] > 3000: break #有节点可用带宽大于3000时,不再检查其它节点
  435. if mnode1: #优选低延迟高带宽
  436. mnode = sorted(mnode1,key= lambda x:x['net'],reverse=True)
  437. elif mnode3: #备选低延迟,中等带宽
  438. mnode = sorted(mnode3,key= lambda x:x['net'],reverse=True)
  439. else: #终选中等延迟,中等带宽
  440. mnode = sorted(mnode2,key= lambda x:x['ping'],reverse=False)
  441. if not mnode: return 'http://download.bt.cn'
  442. #return mnode[0]['protocol'] + mnode[0]['address'] + ':' + mnode[0]['port']
  443. return "https://" + mnode[0]['address']
  444. except:
  445. return 'http://download.bt.cn'
  446. #删除文件权限
  447. def del_file_access(filename,user):
  448. try:
  449. if filename.lower() in ["c:/","c:","c:\\","c"]:
  450. return True
  451. import win32security
  452. sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
  453. dacl = sd.GetSecurityDescriptorDacl()
  454. ace_count = dacl.GetAceCount()
  455. for i in range(ace_count ,0 ,-1):
  456. try:
  457. data = {}
  458. data['rev'], data['access'], usersid = dacl.GetAce(i-1)
  459. data['user'],data['group'], data['type'] = win32security.LookupAccountSid('', usersid)
  460. if data['user'].lower() == user.lower(): dacl.DeleteAce(i-1) #删除旧的dacl
  461. if data['user'].lower() == 'users': dacl.DeleteAce(i-1) #删除旧的dacl
  462. except :
  463. try:
  464. #处理拒绝访问
  465. dacl.DeleteAce(i-1)
  466. except : pass
  467. sd.SetSecurityDescriptorDacl(1, dacl, 0)
  468. win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
  469. except :
  470. pass
  471. return True
  472. def set_file_access(filename,user,access):
  473. try:
  474. sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
  475. dacl = sd.GetSecurityDescriptorDacl()
  476. ace_count = dacl.GetAceCount()
  477. for i in range(ace_count, 0,-1):
  478. try:
  479. data = {}
  480. data['rev'], data['access'], usersid = dacl.GetAce(i-1)
  481. data['user'],data['group'], data['type'] = win32security.LookupAccountSid('', usersid)
  482. if data['user'].lower() == user.lower(): dacl.DeleteAce(i-1) #删除旧的dacl
  483. if data['user'].lower() == 'users': dacl.DeleteAce(i-1) #删除旧的dacl
  484. except :
  485. pass
  486. try:
  487. userx, domain, type = win32security.LookupAccountName("", user)
  488. except :
  489. userx, domain, type = win32security.LookupAccountName("", 'IIS APPPOOL\\' + user)
  490. if access > 0: dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 3, access, userx)
  491. sd.SetSecurityDescriptorDacl(1, dacl, 0)
  492. win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
  493. return True,None
  494. except :
  495. return False,get_error_info()
  496. def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
  497. if shell:
  498. cmdstring_list = cmdstring
  499. else:
  500. cmdstring_list = shlex.split(cmdstring)
  501. if timeout:
  502. end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
  503. sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE,shell=shell,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  504. while sub.poll() is None:
  505. time.sleep(0.1)
  506. if timeout:
  507. if end_time <= datetime.datetime.now():
  508. raise Exception("Timeout:%s"%cmdstring)
  509. a,e = sub.communicate()
  510. if type(a) == bytes:
  511. try:
  512. a = a.decode('utf-8')
  513. except :
  514. a = a.decode('gb2312','ignore')
  515. if type(e) == bytes:
  516. try:
  517. e = e.decode('utf-8')
  518. except :
  519. e = e.decode('gb2312','ignore')
  520. return a,e
  521. def GetRandomString(length):
  522. from random import Random
  523. strings = ''
  524. chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  525. chrlen = len(chars) - 1
  526. random = Random()
  527. for i in range(length):
  528. strings += chars[random.randint(0, chrlen)]
  529. return strings
  530. def GetRandomString1(length):
  531. from random import Random
  532. strings = ''
  533. chars = '0123456789'
  534. chrlen = len(chars) - 1
  535. random = Random()
  536. for i in range(length):
  537. strings += chars[random.randint(0, chrlen)]
  538. return strings
  539. def GetRandomString2(length):
  540. from random import Random
  541. strings = ''
  542. chars = '!@#$%^&*()_+.,?[]-='
  543. chrlen = len(chars) - 1
  544. random = Random()
  545. for i in range(length):
  546. strings += chars[random.randint(0, chrlen)]
  547. return strings
  548. def chdck_salt():
  549. sql = Sql()
  550. sql.table('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
  551. u_list = sql.table('users').field('id,username,password,salt').select()
  552. for u_info in u_list:
  553. salt = GetRandomString(12) #12位随机
  554. pdata = {}
  555. pdata['password'] = md5(md5(u_info['password']+'_bt.cn') + salt)
  556. pdata['salt'] = salt
  557. sql.table('users').where('id=?',(u_info['id'],)).update(pdata)
  558. def md5(strings):
  559. """
  560. MD5
  561. @strings
  562. return string(32)
  563. """
  564. import hashlib
  565. m = hashlib.md5()
  566. m.update(strings.encode('utf-8'))
  567. return m.hexdigest()
  568. def password_salt(password,username=None,uid=None):
  569. chdck_salt()
  570. sql = Sql()
  571. if not uid:
  572. if not username:
  573. raise Exception('username或uid必需传一项')
  574. uid = sql.table('users').where('username=?',(username,)).getField('id')
  575. salt = sql.table('users').where('id=?',(uid,)).getField('salt')
  576. return md5(md5(password+'_bt.cn')+salt)
  577. def check_user(username):
  578. resume = 0
  579. while True:
  580. data, total, resume = win32net.NetUserEnum(None, 3, win32netcon.FILTER_NORMAL_ACCOUNT, resume)
  581. for user in data:
  582. if user['name'] == username: return True
  583. if not resume: break
  584. return False
  585. def add_user(username,password,ps):
  586. try:
  587. if not check_user(username):
  588. d = {}
  589. d['name'] = username
  590. d['password'] = password
  591. d['comment'] = ps
  592. d['flags'] = win32netcon.UF_NORMAL_ACCOUNT | win32netcon.UF_SCRIPT
  593. d['priv'] = win32netcon.USER_PRIV_USER
  594. win32net.NetUserAdd(None, 1, d)
  595. #设置用户允许登录服务
  596. handle = win32security.LsaOpenPolicy(None, win32security.POLICY_ALL_ACCESS)
  597. sid_obj, domain, tmp = win32security.LookupAccountName(None, username)
  598. win32security.LsaAddAccountRights(handle, sid_obj, ('SeServiceLogonRight',) )
  599. win32security.LsaClose( handle)
  600. if not check_user(username): return False, '添加用户[{}]失败.'.format(username)
  601. writeFile('{}/data/{}'.format(panelPath,username),password)
  602. return True , None
  603. else:
  604. ExecShell('net user "{}" "{}"'.format(username,password))
  605. writeFile('{}/data/{}'.format(panelPath,username),password)
  606. return True , None
  607. except :
  608. return False,get_error_info()
  609. def add_user_bywww():
  610. pwd = GetRandomString(64) + GetRandomString1(32) + GetRandomString2(32)
  611. status,error = add_user('www',pwd,'用于启动宝塔安装的程序,删除后会导致部分软件无法启动,请勿删除')
  612. if not status:
  613. writeFile(error_path,error)
  614. return False
  615. return True
  616. def add_user_bymysql():
  617. pwd = GetRandomString(64) + GetRandomString1(32) + GetRandomString2(32)
  618. status,error = add_user('mysql',pwd,'用于启动宝塔安装的程序,删除后会导致部分软件无法启动,请勿删除')
  619. if not status:
  620. writeFile(error_path,error)
  621. return False
  622. return True
  623. def getIP(url):
  624. import socket,re
  625. tmp = re.search('http://(.+)\:\d*',url)
  626. if tmp:
  627. domain = tmp.groups()[0]
  628. myaddr = socket.getaddrinfo(domain, 'http')
  629. return myaddr[0][4][0]
  630. return ''
  631. def add_panel_dir():
  632. try:
  633. slist = [
  634. [panelPath , [] ],
  635. ['{}/data'.format(panelPath) , [] ],
  636. ['{}/script'.format(panelPath) , [] ],
  637. ['{}/backup'.format(panelPath) , [] ],
  638. ['{}/backup/database/sqlserver'.format(setupPath[:2]) , [ 'Authenticated Users']],
  639. ['{}/wwwroot'.format(setupPath[:2]) , [ 'IIS_IUSRS','www'] ],
  640. ['{}/wwwlogs'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  641. ['{}/php'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  642. ['{}/mysql'.format(setupPath) , [ 'mysql'] ],
  643. ['{}/temp'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  644. ['{}/temp/session'.format(setupPath) , [ 'IIS_IUSRS','www'] ],
  645. ['C:/Temp' , [ 'IIS_IUSRS','www'] ],
  646. ]
  647. is_break = False
  648. for sobj in slist:
  649. if not os.path.exists(sobj[0]):
  650. os.makedirs(sobj[0])
  651. n = 0
  652. while n < 5:
  653. if os.path.exists(sobj[0]): break
  654. os.makedirs(sobj[0])
  655. time.sleep(0.5)
  656. n += 1
  657. if not os.path.exists(sobj[0]):
  658. writeFile(error_path,"自动创建目录【{}】失败,已重试最大次数 5 次,请手动创建该目录后重新安装".format(sobj[0]))
  659. return False
  660. del_file_access(sobj[0],'users')
  661. for user in sobj[1]:
  662. n = 0
  663. while n < 3:
  664. status,error = set_file_access(sobj[0],user,2032127)
  665. if status: break
  666. time.sleep(0.5)
  667. if not status:
  668. writeFile(error_path,"目录{}设置{}权限设置错误 -> {}".format(sobj[0],user,error))
  669. break
  670. del_file_access(setupPath,'users')
  671. url = get_url()
  672. files = ['default.db','session.db','system.db','phplib.win','defaultDoc.html','404.html']
  673. for f_name in files:
  674. local_path = '{}/data/{}'.format(panelPath,f_name)
  675. download_url = '{}/win/panel/data/{}'.format(url,f_name)
  676. n = 0
  677. while n < 10:
  678. n += 1;
  679. try:
  680. if os.path.exists(local_path) and os.path.getsize(local_path) < 10: os.remove(local_path)
  681. if not os.path.exists(local_path): downloadFileByWget(download_url,local_path)
  682. if os.path.getsize(local_path) and os.path.getsize(local_path) > 10: break;
  683. writeFile(error_path,'download {} error ->> {} \r\n {}'.format(f_name,download_url,""))
  684. except :
  685. ip = getIP(url)
  686. writeFile(error_path,'download {} error ->> {} \r\n connect {} \r\n {}'.format(ip,f_name,download_url,get_error_info()))
  687. if n > 5: return False
  688. time.sleep(0.2)
  689. return True
  690. except :
  691. writeFile(error_path,get_error_info())
  692. return False
  693. def unzip(src_path,dst_path):
  694. import zipfile
  695. zip_file = zipfile.ZipFile(src_path)
  696. for names in zip_file.namelist():
  697. zip_file.extract(names,dst_path)
  698. zip_file.close()
  699. return True
  700. def to_path(path):
  701. return path.replace('/','\\')
  702. def download_panel(file_list = []):
  703. try:
  704. url = 'http://www.example.com'
  705. ExecShell("taskkill /f /t /im BtTools.exe")
  706. #下载面板
  707. loacl_path = setupPath + '/panel.zip'
  708. tmpPath = "{}/temp/panel".format(setupPath)
  709. if os.path.exists(loacl_path): os.remove(loacl_path)
  710. if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True)
  711. if not os.path.exists(tmpPath): os.makedirs(tmpPath)
  712. p_ver = sys.argv[2]
  713. downUrl = url + '/win/panel/panel_' + p_ver + '.zip';
  714. downloadFileByWget(downUrl,loacl_path);
  715. unzip(loacl_path,tmpPath)
  716. for ff_path in file_list:
  717. if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path)
  718. tcPath = '{}\class'.format(tmpPath)
  719. for name in os.listdir(tcPath):
  720. try:
  721. if name.find('win_amd64.pyd') >=0:
  722. oldName = os.path.join(tcPath,name);
  723. lName = name.split('.')[0] + '.pyd'
  724. newName = os.path.join(tcPath,lName)
  725. if not os.path.exists(newName):os.rename(oldName,newName)
  726. except :pass
  727. cPath = '{}/panel/class'.format(setupPath)
  728. if os.path.exists(cPath):
  729. os.system("del /s {}\*.pyc".format(to_path(cPath)))
  730. os.system("del /s {}\*.pyt".format(to_path(cPath)))
  731. for name in os.listdir(cPath):
  732. try:
  733. if name.find('.pyd') >=0:
  734. oldName = os.path.join(cPath,name)
  735. newName = os.path.join(cPath,GetRandomString(8) + '.pyt')
  736. os.rename(oldName,newName)
  737. except : pass
  738. os.system("del /s {}\*.pyc".format(to_path(cPath)))
  739. os.system("del /s {}\*.pyt".format(to_path(cPath)))
  740. os.system("xcopy /s /c /e /y /r {} {}".format(to_path(tmpPath),to_path(panelPath)))
  741. try:
  742. os.remove(loacl_path)
  743. except : pass
  744. try:
  745. shutil.rmtree(tmpPath,True)
  746. except : pass
  747. s_ver = platform.platform()
  748. net_v = '45'
  749. if s_ver.find('2008') >= 0: net_v = '20'
  750. writeFile('{}/data/net'.format(setupPath),net_v)
  751. not_workorder_path = '{}/data/not_workorder.pl'.format(panelPath)
  752. if not os.path.exists(not_workorder_path):
  753. writeFile(not_workorder_path,'True')
  754. bind_path = '{}/data/bind_path.pl'.format(panelPath)
  755. if os.path.exists(bind_path):
  756. os.remove(bind_path)
  757. userinfo_path = '{}/data/userInfo.json'.format(panelPath)
  758. if not os.path.exists(userinfo_path):
  759. writeFile(userinfo_path,'{"uid":1,"username":"Administrator","address":"127.0.0.1","serverid":"1","access_key":"test","secret_key":"123456","ukey":"123456","state":1}')
  760. local_path = '{}/temp/api.py'.format(setupPath)
  761. downloadFileByWget('{}/win/panel/data/api.py'.format(url),local_path)
  762. if os.path.exists(local_path):
  763. os.remove('C:/Program Files/python/Lib/site-packages/requests/api.py')
  764. shutil.move(local_path,'C:/Program Files/python/Lib/site-packages/requests')
  765. local_path = '{}/script/BtTools.exe'.format(panelPath)
  766. downloadFileByWget('{}/win/panel/BtTools{}.exe'.format(url,net_v),local_path)
  767. if os.path.getsize(local_path) > 128:
  768. return True
  769. return False
  770. downloadFileByWget('{}/win/panel/data/softList.conf'.format(url),'{}/data/softList.conf'.format(panelPath))
  771. try:
  772. from gevent import monkey
  773. except :
  774. os.system('"C:\Program Files\python\python.exe" -m pip install gevent')
  775. except :
  776. writeFile(error_path,get_error_info())
  777. def update_panel():
  778. file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json']
  779. download_panel(file_list)
  780. py_path = 'C:/Program Files/python/python.exe'
  781. ExecShell("\"{}\" {}/panel/runserver.py --startup auto install".format(py_path,setupPath))
  782. ExecShell("\"{}\" {}/panel/task.py --startup auto install".format(py_path,setupPath))
  783. print("升级成功,重启面板后生效..")
  784. def init_panel_data():
  785. try:
  786. sql = Sql()
  787. username = sql.table('users').where('id=?',(1,)).getField('username')
  788. if username == 'admin':
  789. username = GetRandomString(8)
  790. password = GetRandomString(8)
  791. writeFile(panelPath + '/data/default.pl',password)
  792. sql.table('users').where('id=?',(1,)).setField('username',username)
  793. pwd = password_salt(md5(password),uid=1)
  794. result = sql.table('users').where('id=?',(1,)).setField('password',pwd)
  795. backup_path = panelPath[:2] + '/backup'
  796. www_path = panelPath[:2] + '/wwwroot'
  797. if not os.path.exists(backup_path): os.makedirs(backup_path)
  798. if not os.path.exists(www_path): os.makedirs(www_path)
  799. sql.table('config').where('id=?',(1,)).setField('backup_path',backup_path)
  800. sql.table('config').where('id=?',(1,)).setField('sites_path',www_path)
  801. bind_path = panelPath+ '/data/bind_path.pl'
  802. if not os.path.exists(bind_path): writeFile(bind_path,'True')
  803. admin_path = panelPath+ '/data/admin_path.pl'
  804. if not os.path.exists(admin_path): writeFile(admin_path,"/" + GetRandomString(8))
  805. port_path = panelPath+ '/data/port.pl'
  806. if not os.path.exists(port_path): writeFile(port_path,'8888')
  807. recycle_bin_db = panelPath+ '/data/recycle_bin_db.pl'
  808. if not os.path.exists(recycle_bin_db): writeFile(recycle_bin_db,'True')
  809. recycle_bin = panelPath+ '/data/recycle_bin.pl'
  810. if not os.path.exists(recycle_bin): writeFile(recycle_bin,'True')
  811. conf_path = panelPath + '/config/config.json'
  812. if os.path.exists(conf_path):
  813. conf = readFile(conf_path).replace('[PATH]',setupPath.replace('\\','/'))
  814. writeFile(conf_path,conf)
  815. GetLocalIp()
  816. return True
  817. except :
  818. writeFile(error_path,get_error_info())
  819. return False
  820. def add_panel_services(num = 0):
  821. try:
  822. py_path = 'C:/Program Files/python/python.exe'
  823. delete_server('btPanel')
  824. ret = ExecShell("\"{}\" {}/panel/runserver.py --startup auto install".format(py_path,setupPath))
  825. delete_server('btTask')
  826. ExecShell("\"{}\" {}/panel/task.py --startup auto install".format(py_path,setupPath))
  827. if get_server_status('btPanel') < 0 or get_server_status('btTask') < 0:
  828. if num <= 0 :
  829. localPath = setupPath + "/temp/Time_Zones.reg";
  830. downloadFileByWget(get_url() + '/win/panel/data/Time_Zones.reg',localPath)
  831. ExecShell("regedit /s " + localPath)
  832. add_panel_services(1)
  833. else:
  834. writeFile(error_path,ret[0] + ret[1])
  835. else:
  836. os.system('sc failure btPanel reset=1800 actions=restart/60000/restart/120000/restart/30000')
  837. os.system('sc failure btTask reset=1800 actions=restart/60000/restart/120000/restart/30000')
  838. start_service('btPanel')
  839. start_service('btTask')
  840. except :
  841. writeFile(error_path,get_error_info())
  842. def add_firewall_byport():
  843. conf = ExecShell('netsh advfirewall firewall show rule "宝塔面板"')[0]
  844. if conf.lower().find('tcp') == -1:
  845. ExecShell("netsh advfirewall firewall add rule name=宝塔面板 dir=in action=allow protocol=tcp localport=8888");
  846. ExecShell("netsh advfirewall firewall add rule name=网站访问端口 dir=in action=allow protocol=tcp localport=80");
  847. ExecShell("netsh advfirewall firewall add rule name=远程桌面 dir=in action=allow protocol=tcp localport=3389");
  848. ExecShell("netsh advfirewall firewall add rule name=HTTPS端口 dir=in action=allow protocol=tcp localport=443");
  849. ExecShell("netsh advfirewall firewall add rule name=FTP主动端口 dir=in action=allow protocol=tcp localport=21");
  850. ExecShell("netsh advfirewall firewall add rule name=FTP被动端口 dir=in action=allow protocol=tcp localport=3000-4000");
  851. def get_error_log():
  852. error = readFile(error_path)
  853. try:
  854. data = {}
  855. data['msg'] = 'setup'
  856. data['os'] = 'Windows'
  857. data['error'] = error
  858. data['version'] = ''
  859. httpPost('http://www.example.com/api/wpanel/PanelBug',data)
  860. except :
  861. pass
  862. return error
  863. if __name__ == "__main__":
  864. stype = sys.argv[1];
  865. if not stype in ['get_error_log']:
  866. if os.path.exists(error_path): os.remove(error_path)
  867. result = eval('{}()'.format(stype))
  868. print(result)