全面升级第三方库:openssl/libuv/mbedtls/jsoncpp/mongoose/zlib/libssh;加入播放器的一键构建(Qt命令行构建);调整助手安装包构建脚本,使之能够发布基于Qt的软件。

pull/175/head^2
Apex Liu 2019-11-17 03:32:05 +08:00
parent 50b6eddb36
commit eae1db3edc
28 changed files with 1889 additions and 1797 deletions

1
.gitignore vendored
View File

@ -105,3 +105,4 @@ profile
/server/tp_core/testssh/Debug
/server/tp_core/testssh/Release
/external/zlib
/client/tools/qt-redist

View File

@ -13,21 +13,24 @@ class BuilderBase:
def __init__(self):
self.out_dir = ''
def build_exe(self):
pass
def build_assist(self):
cc.e("this is a pure-virtual function.")
def build_player(self):
cc.e("this is a pure-virtual function.")
def build_rdp(self):
pass
cc.e("this is a pure-virtual function.")
def build_installer(self):
pass
cc.e("this is a pure-virtual function.")
class BuilderWin(BuilderBase):
def __init__(self):
super().__init__()
def build_exe(self):
def build_assist(self):
cc.i('build tp_assist...')
sln_file = os.path.join(env.root_path, 'client', 'tp_assist_win', 'tp_assist.vs2017.sln')
out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp_assist.exe')
@ -36,6 +39,15 @@ class BuilderWin(BuilderBase):
utils.msvc_build(sln_file, 'tp_assist', ctx.target_path, ctx.bits_path, False)
utils.ensure_file_exists(out_file)
def build_player(self):
cc.i('build tp-player...')
prj_path = os.path.join(env.root_path, 'client', 'tp-player')
out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp-player.exe')
if os.path.exists(out_file):
utils.remove(out_file)
utils.qt_build_win(prj_path, 'tp-player', ctx.bits_path, ctx.target_path)
utils.ensure_file_exists(out_file)
# def build_rdp(self):
# cc.n('build tp_rdp...')
# sln_file = os.path.join(ROOT_PATH, 'client', 'tp_rdp', 'tp_rdp.2015.sln')
@ -74,12 +86,13 @@ class BuilderWin(BuilderBase):
utils.makedirs(tmp_cfg_path)
utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp_assist.exe')
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json'))
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json'))
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'cacert.cer')
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.key')
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.pem')
# assist configuration web page
utils.copy_ex(os.path.join(env.root_path, 'client', 'tp_assist_win'), tmp_app_path, 'site')
utils.makedirs(os.path.join(tmp_app_path, 'tools', 'putty'))
@ -91,13 +104,35 @@ class BuilderWin(BuilderBase):
utils.makedirs(os.path.join(tmp_app_path, 'tools', 'tprdp'))
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-client.exe')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe')
# utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'libeay32.dll')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'ssleay32.dll')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'msvcr120.dll')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools'), os.path.join(tmp_app_path, 'tools'), 'securecrt-telnet.vbs')
# tp-player
utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp-player.exe')
# qt-redist
qt_redist_path = os.path.join(env.root_path, 'client', 'tools', 'qt-redist')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Core.dll')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Gui.dll')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Network.dll')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Widgets.dll')
utils.copy_ex(os.path.join(qt_redist_path, 'platforms'), os.path.join(tmp_app_path, 'platforms'))
utils.copy_ex(os.path.join(qt_redist_path, 'styles'), os.path.join(tmp_app_path, 'styles'))
utils.copy_ex(os.path.join(qt_redist_path, 'translations'), os.path.join(tmp_app_path, 'translations'))
# zlib
suffix = 'd' if ctx.target_path == 'debug' else ''
utils.copy_file(os.path.join(env.root_path, 'external', 'zlib', 'build', ctx.target_path), tmp_app_path, 'zlib{}.dll'.format(suffix))
# openssl
utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libcrypto-1_1.dll')
utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libssl-1_1.dll')
# final build
utils.nsis_build(os.path.join(env.root_path, 'dist', 'client', 'windows', 'assist', 'installer.nsi'))
@ -105,7 +140,7 @@ class BuilderMacOS(BuilderBase):
def __init__(self):
super().__init__()
def build_exe(self):
def build_assist(self):
cc.i('build tp_assist...')
configuration = ctx.target_path.capitalize()
@ -169,7 +204,7 @@ class BuilderLinux(BuilderBase):
def __init__(self):
super().__init__()
def build_exe(self):
def build_assist(self):
cc.e('not support linux.')
# def build_rdp(self):
@ -215,7 +250,8 @@ def main():
builder = gen_builder(ctx.host_os)
if 'exe' in argv:
builder.build_exe()
builder.build_assist()
builder.build_player()
# elif 'rdp' in argv:
# builder.build_rdp()
elif 'installer' in argv:

View File

@ -27,19 +27,25 @@ class BuilderBase:
def build_jsoncpp(self):
file_name = 'jsoncpp-{}.zip'.format(env.ver_jsoncpp)
if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name):
# return
self._build_jsoncpp(file_name)
def _download_jsoncpp(self, file_name):
return utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name)
def _build_jsoncpp(self, file_name):
cc.e("this is a pure-virtual function.")
def build_mongoose(self):
file_name = 'mongoose-{}.zip'.format(env.ver_mongoose)
if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name):
# return
self._build_mongoose(file_name)
def _download_mongoose(self, file_name):
return utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name)
def _build_mongoose(self, file_name):
cc.e("this is a pure-virtual function.")
@ -47,38 +53,64 @@ class BuilderBase:
file_name = 'openssl-{}.zip'.format(env.ver_ossl)
self._build_openssl(file_name)
def _build_openssl(self, file_name):
def _download_openssl(self, file_name):
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
cc.e("can not download openssl source tarball.")
return False
else:
return True
return utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name)
def _build_openssl(self, file_name):
cc.e("this is a pure-virtual function.")
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
# if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
# cc.e("can not download openssl source tarball.")
# return False
# else:
# return True
def build_libuv(self):
file_name = 'libuv-{}.zip'.format(env.ver_libuv)
if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name):
# return
self._build_libuv(file_name)
def _download_libuv(self, file_name):
return utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name)
def _build_libuv(self, file_name):
cc.e("this is a pure-virtual function.")
def build_mbedtls(self):
file_name = 'mbedtls-mbedtls-{}.zip'.format(env.ver_mbedtls)
if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
# return
self._build_mbedtls(file_name)
def _download_mbedtls(self, file_name):
return utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name)
def _build_mbedtls(self, file_name):
cc.e("this is a pure-virtual function.")
def build_zlib(self):
file_name = 'zlilb{}.zip'.format(env.ver_zlib_number)
# if not utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name):
# return
self._build_zlib(file_name)
def _download_zlib(self, file_name):
return utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name)
def _build_zlib(self, file_name):
cc.e("this is a pure-virtual function.")
def build_libssh(self):
file_name = 'libssh-{}.zip'.format(env.ver_libssh)
if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name):
# return
self._build_libssh(file_name)
def _download_libssh(self, file_name):
return utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name)
def _build_libssh(self, file_name):
cc.e("this is a pure-virtual function.")
@ -103,9 +135,10 @@ class BuilderWin(BuilderBase):
self.MBEDTLS_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mbedtls')
self.LIBUV_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libuv')
self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh')
self.ZLIB_PATH_SRC = os.path.join(PATH_EXTERNAL, 'zlib')
def _prepare_python(self):
cc.n('prepare python header files ...', end='')
cc.n('prepare python header files ... ', end='')
if os.path.exists(os.path.join(PATH_EXTERNAL, 'python', 'include', 'Python.h')):
cc.w('already exists, skip.')
@ -125,53 +158,85 @@ class BuilderWin(BuilderBase):
utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include'))
def _build_openssl(self, file_name):
cc.n('build openssl static library from source code... ')
if not super()._build_openssl(file_name):
return
_chk_output = [
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
]
need_build = False
for f in _chk_output:
if not os.path.exists(f):
need_build = True
break
if not need_build:
cc.n('build openssl static library from source code... ', end='')
cc.n('prepare OpenSSL pre-built package ... ', end='')
if os.path.exists(self.OPENSSL_PATH_SRC):
cc.w('already exists, skip.')
return
cc.v('')
cc.n('prepare openssl source code...')
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not os.path.exists(self.OPENSSL_PATH_SRC):
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
if not os.path.exists(self.OPENSSL_PATH_SRC):
raise RuntimeError('can not prepare openssl source code.')
else:
cc.w('already exists, skip.')
os.chdir(self.OPENSSL_PATH_SRC)
os.system('""{}" Configure VC-WIN32"'.format(env.perl))
os.system(r'ms\do_nasm')
# for vs2015
# utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
# for vs2017 community
utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
file_name = 'Win32OpenSSL-{}.msi'.format(_alt_ver)
installer = os.path.join(PATH_DOWNLOAD, file_name)
for f in _chk_output:
if not os.path.exists(f):
raise RuntimeError('build openssl static library from source code failed.')
if not os.path.exists(installer):
if not utils.download_file('openssl installer', 'http://slproweb.com/download/{}'.format(filename), PATH_DOWNLOAD, file_name):
cc.e('can not download pre-built installer of OpenSSL.')
return
utils.ensure_file_exists(installer)
cc.w('On Windows, we use pre-built package of OpenSSL.')
cc.w('The installer have been downloaded at "{}".'.format(installer))
cc.w('please install OpenSSL into "{}".'.format(self.OPENSSL_PATH_SRC))
cc.w('\nOnce the OpenSSL installed, press Enter to continue or Q to quit...', end='')
try:
x = env.input()
except EOFError:
x = 'q'
if x == 'q':
return
# cc.n('build openssl static library from source code... ')
# if not super()._build_openssl(file_name):
# return
# _chk_output = [
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
# os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
# ]
# need_build = False
# for f in _chk_output:
# if not os.path.exists(f):
# need_build = True
# break
# if not need_build:
# cc.n('build openssl static library from source code... ', end='')
# cc.w('already exists, skip.')
# return
# cc.v('')
# cc.n('prepare openssl source code...')
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
# if not os.path.exists(self.OPENSSL_PATH_SRC):
# utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
# os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
# if not os.path.exists(self.OPENSSL_PATH_SRC):
# raise RuntimeError('can not prepare openssl source code.')
# else:
# cc.w('already exists, skip.')
# os.chdir(self.OPENSSL_PATH_SRC)
# os.system('""{}" Configure VC-WIN32"'.format(env.perl))
# os.system(r'ms\do_nasm')
# # for vs2015
# # utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
# # for vs2017 community
# utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
# for f in _chk_output:
# if not os.path.exists(f):
# raise RuntimeError('build openssl static library from source code failed.')
def _build_libssh(self, file_name):
cc.n('build libssh static library from source code... ', end='')
if not self._download_libssh(file_name):
return
cc.n('build libssh library from source code... ', end='')
if not os.path.exists(self.LIBSSH_PATH_SRC):
cc.v('')
@ -210,7 +275,7 @@ class BuilderWin(BuilderBase):
cc.i('build libssh...')
sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'build', 'libssh.sln')
utils.msvc_build(sln_file, 'ssh_shared', ctx.target_path, 'win32', False)
utils.msvc_build(sln_file, 'ssh', ctx.target_path, 'win32', False)
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.lib'))
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.dll'))
utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.lib')
@ -218,7 +283,53 @@ class BuilderWin(BuilderBase):
utils.ensure_file_exists(out_file_lib)
utils.ensure_file_exists(out_file_dll)
def _build_zlib(self, file_name):
if not self._download_zlib(file_name):
return
cc.n('build zlib library from source code... ', end='')
if not os.path.exists(self.ZLIB_PATH_SRC):
cc.v('')
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
os.rename(os.path.join(PATH_EXTERNAL, 'zlib-{}'.format(env.ver_zlib)), self.ZLIB_PATH_SRC)
if ctx.target_path == 'debug':
olib = 'zlibd.lib'
odll = 'zlibd.dll'
else:
olib = 'zlib.lib'
odll = 'zlib.dll'
out_file_lib = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, olib)
out_file_dll = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, odll)
if os.path.exists(out_file_lib) and os.path.exists(out_file_dll):
cc.w('already exists, skip.')
return
cc.v('')
cc.w('On Windows, when build zlib, need you use cmake-gui.exe to generate solution file')
cc.w('for Visual Studio 2017. Visit https://docs.tp4a.com for more details.')
cc.w('\nOnce the zlib.sln generated, press Enter to continue or Q to quit...', end='')
try:
x = env.input()
except EOFError:
x = 'q'
if x == 'q':
return
cc.i('build zlib...')
sln_file = os.path.join(self.ZLIB_PATH_SRC, 'build', 'zlib.sln')
utils.msvc_build(sln_file, 'zlib', ctx.target_path, 'win32', False)
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.lib'))
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.dll'))
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.lib')
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.dll')
utils.ensure_file_exists(out_file_lib)
utils.ensure_file_exists(out_file_dll)
def _build_jsoncpp(self, file_name):
if not self._download_jsoncpp(file_name):
return
cc.n('prepare jsoncpp source code... ', end='')
if not os.path.exists(self.JSONCPP_PATH_SRC):
cc.v('')
@ -228,6 +339,8 @@ class BuilderWin(BuilderBase):
cc.w('already exists, skip.')
def _build_mongoose(self, file_name):
if not self._download_mongoose(file_name):
return
cc.n('prepare mongoose source code... ', end='')
if not os.path.exists(self.MONGOOSE_PATH_SRC):
cc.v('')
@ -237,6 +350,8 @@ class BuilderWin(BuilderBase):
cc.w('already exists, skip.')
def _build_mbedtls(self, file_name):
if not self._download_mbedtls(file_name):
return
cc.n('prepare mbedtls source code... ', end='')
if not os.path.exists(self.MBEDTLS_PATH_SRC):
cc.v('')
@ -254,6 +369,8 @@ class BuilderWin(BuilderBase):
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
def _build_libuv(self, file_name):
if not self._download_libuv(file_name):
return
cc.n('prepare libuv source code... ', end='')
if not os.path.exists(self.LIBUV_PATH_SRC):
cc.v('')
@ -674,6 +791,7 @@ def main():
builder.build_openssl()
builder.build_libuv()
builder.build_mbedtls()
builder.build_zlib()
builder.build_libssh()
builder.fix_output()

View File

@ -148,6 +148,11 @@ class Env(object):
if warn_miss_tool:
cc.w(' - can not locate `nsis`, so I can not make installer.')
if 'qt' in _tmp:
self.qt = _tmp['qt']
else:
self.qt = None
elif self.is_linux or self.is_macos:
if 'cmake' in _tmp:
self.cmake = _tmp['cmake']
@ -178,6 +183,10 @@ class Env(object):
self.ver_ossl = _v_openssl[0].strip()
self.ver_ossl_number = _v_openssl[1].strip()
_v_zlib = _tmp['zlib'].split(',')
self.ver_zlib = _v_zlib[0].strip()
self.ver_zlib_number = _v_zlib[1].strip()
self.ver_libuv = _tmp['libuv']
self.ver_mbedtls = _tmp['mbedtls']
# self.ver_sqlite = _tmp['sqlite']

View File

@ -320,6 +320,21 @@ def msvc_build(sln_file, proj_name, target, platform, force_rebuild):
raise RuntimeError('build MSVC project `{}` failed.'.format(proj_name))
def qt_build_win(prj_path, prj_name, bit_path, target_path):
cc.n(env.visual_studio_path)
if env.qt is None:
raise RuntimeError('where is `qt`?')
if env.is_win:
tmp_path = os.path.join(env.root_path, 'out', '_tmp_', prj_name, bit_path)
# C:\Windows\System32\cmd.exe /A /Q /K C:\Qt\Qt5.12.0\5.12.0\msvc2017\bin\qtenv2.bat
cmd = 'C:\\Windows\\System32\\cmd.exe /A /Q /C ""{}\qt-helper.bat" "{}\\bin\\qtenv2.bat" "{}VC\\Auxiliary\\Build\\vcvarsall.bat" {} "{}" "{}" {}"'.format(env.build_path, env.qt, env.visual_studio_path, bit_path, tmp_path, prj_path, target_path)
ret, _ = sys_exec(cmd, direct_output=True)
if ret != 0:
raise RuntimeError('build XCode project `{}` failed.'.format(proj_name))
def xcode_build(proj_file, proj_name, target, force_rebuild):
if force_rebuild:
cmd = 'xcodebuild -project "{}" -target {} -configuration {} clean'.format(proj_file, proj_name, target)

View File

@ -1,3 +1,3 @@
# -*- coding: utf8 -*-
VER_TP_SERVER = "3.3.1"
VER_TP_ASSIST = "3.3.1"
VER_TP_SERVER = "3.5.1"
VER_TP_ASSIST = "3.5.1"

View File

@ -14,14 +14,10 @@
// tp-player.exe path/contains/tp-rdp.tpr 包含 .tpr 文件的路径
//
// ## 从TP服务器上下载
// (废弃) tp-player.exe "http://127.0.0.1:7190" 1234 "tp_1491560510_ca67fceb75a78c9d" "000000256-admin-administrator-218.244.140.14-20171209-020047"
// (废弃) TP服务器地址 记录编号 session-id仅授权用户可下载 合成的名称,用于本地生成路径来存放下载的文件
//
// ## 从TP服务器上下载
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
// TP服务器地址(可能包含子路径,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
// 按 “/” 进行分割后去掉最后两个项剩下部分是TP服务器的WEB地址用于合成后续的文件下载URL。
// 根据下载的.tpr文件内容本地合成类似于 "000000256-admin-administrator-218.244.140.14-20171209-020047" 的路径来存放下载的文件
// 根据下载的.tpr文件内容本地合成类似于 "000000256-admin-administrator-123.45.77.88-20191109-020047" 的路径来存放下载的文件
// 特别注意,如果账号是 domain\user 这种形式,需要将 "\" 替换为下划线,否则此符号作为路径分隔符,会导致路径不存在而无法保存下载的文件。
// - 获取文件大小: http://127.0.0.1:7190/audit/get-file?act=size&type=rdp&rid=yyyyy&f=file-name
// - 'act'为`size`表示获取文件大小(返回一个数字字符串,就是指定的文件大小)
@ -41,9 +37,9 @@ void show_usage(QCommandLineParser& parser) {
+ parser.helpText()
+ "\n\n"
+ "RESOURCE could be:\n"
+ " teleport record file (.tpr).\n"
+ " a directory contains .tpr file.\n"
+ " an URL to download teleport record file."
+ " - teleport record file (.tpr).\n"
+ " - a directory contains .tpr file.\n"
+ " - an URL to download teleport record file."
+ "</pre></body></html>");
}
@ -82,15 +78,6 @@ int main(int argc, char *argv[])
if(parser.isSet(opt_help)) {
show_usage(parser);
// QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
// "<html><head/><body><pre>"
// + parser.helpText()
// + "\n\n"
// + "RESOURCE could be:\n"
// + " teleport record file (.tpr).\n"
// + " a directory contains .tpr file.\n"
// + " an URL for download teleport record file."
// + "</pre></body></html>");
return 2;
}
@ -104,10 +91,6 @@ int main(int argc, char *argv[])
qDebug() << resource;
// QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));
// QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
// QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
MainWindow w;
w.set_resource(resource);
w.show();

View File

@ -3,8 +3,6 @@ TARGET = tp-player
QT += core gui widgets network
#DEFINES += QT_NO_DEBUG_OUTPUT
HEADERS += \
mainwindow.h \
bar.h \
@ -40,12 +38,13 @@ FORMS += \
win32:CONFIG(release, debug|release): {
LIBS += -L$$PWD/../../external/zlib/build/release/ -lzlibstatic
DESTDIR = $$PWD/../../out/client/x86/Release/tools/player
DEFINES += QT_NO_DEBUG_OUTPUT
LIBS += -L$$PWD/../../external/zlib/build/release/ -lzlib
DESTDIR = $$PWD/../../out/client/x86/Release
}
else:win32:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../../external/zlib/build/debug/ -lzlibstaticd
DESTDIR = $$PWD/../../out/client/x86/Debug/tools/player
LIBS += -L$$PWD/../../external/zlib/build/debug/ -lzlibd
DESTDIR = $$PWD/../../out/client/x86/Debug
}
INCLUDEPATH += $$PWD/../../external/zlib

View File

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.3.1</string>
<string>3.5.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>3.3.1</string>
<string>3.5.1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>

View File

@ -1,6 +1,6 @@
#ifndef __TS_ASSIST_VER_H__
#define __TS_ASSIST_VER_H__
#define TP_ASSIST_VER L"3.3.1"
#define TP_ASSIST_VER L"3.5.1"
#endif // __TS_ASSIST_VER_H__

View File

@ -1 +1,14 @@
#include "stdafx.h"
#include "stdafx.h"
#include <ex.h>
// #ifdef EX_DEBUG
// // # pragma comment(lib, "libssl32MTd.lib")
// // # pragma comment(lib, "libcrypto32MTd.lib")
// #else
// # pragma comment(lib, "libssl32MT.lib")
// # pragma comment(lib, "libcrypto32MT.lib")
// #endif
# pragma comment(lib, "libssl.lib")
# pragma comment(lib, "libcrypto.lib")

Binary file not shown.

View File

@ -61,13 +61,14 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;_DEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -79,7 +80,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;NDEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@ -87,7 +88,8 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "ts_cfg.h"
#include "ts_env.h"
@ -29,8 +29,12 @@ bool TsCfg::save(const ex_astr& new_value)
if (!_load(new_value))
return false;
Json::StyledWriter jwriter;
ex_astr val = jwriter.write(m_root);
//Json::StyledWriter jwriter;
Json::StreamWriterBuilder jwb;
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
ex_aoss os;
jwriter->write(m_root, &os);
ex_astr val = os.str();
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
EXLOGE("can not save config file.\n");
@ -41,10 +45,14 @@ bool TsCfg::save(const ex_astr& new_value)
}
bool TsCfg::_load(const ex_astr& str_json) {
Json::Reader jreader;
//Json::Reader jreader;
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = str_json.c_str();
if (!jreader.parse(str_json.c_str(), m_root)) {
EXLOGE("can not parse new config data, not in json format? %s\n", jreader.getFormattedErrorMessages().c_str());
ex_astr err;
if (!jreader->parse(str_json_begin, str_json_begin + str_json.length(), &m_root, &err)) {
EXLOGE("can not parse new config data, not in json format? %s\n", err.c_str());
return false;
}

View File

@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "ts_env.h"
#include <time.h>
@ -40,8 +40,8 @@ bool TsEnv::init(void)
m_site_path = m_exec_path;
ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL);
m_tools_path = m_exec_path;
ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
// m_tools_path = m_exec_path;
// ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
cfg_default = m_exec_path;
ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL);
@ -50,13 +50,16 @@ bool TsEnv::init(void)
m_site_path = m_exec_path;
ex_path_join(m_site_path, false, L"site", NULL);
m_tools_path = m_exec_path;
ex_path_join(m_tools_path, false, L"tools", NULL);
// m_tools_path = m_exec_path;
// ex_path_join(m_tools_path, false, L"tools", NULL);
cfg_default = m_exec_path;
ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL);
#endif
m_tools_path = m_exec_path;
ex_path_join(m_tools_path, false, L"tools", NULL);
if (!ex_is_file_exists(m_cfg_file.c_str())) {
ex_wstr cfg_path = m_exec_path;
ex_path_join(cfg_path, false, L"cfg", NULL);

View File

@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#pragma warning(disable:4091)
@ -17,22 +17,22 @@
/*
1.
SecureCRT /N "tab name"
SecureCRT /N "tab name"
Example:
To launch a new Telnet session, displaying the name "Houston, TX" on the tab, use the following:
/T /N "Houston, TX" /TELNET 192.168.0.6
2.
SecureCRT使 /T
SecureCRT使 /T
SecureCRT.exe /T /N "TP#ssh://192.168.1.3" /SSH2 /L root /PASSWORD 1234 120.26.109.25
3.
telnet
telnet
putty.exe telnet://administrator@127.0.0.1:52389
SecureCRT
SecureCRT
SecureCRT.exe /T /N "TP#telnet://192.168.1.3" /SCRIPT X:\path\to\startup.vbs /TELNET 127.0.0.1 52389
startup.vbs
------------------
startup.vbs
------------------
#$language = "VBScript"
#$interface = "1.0"
Sub main
@ -41,11 +41,11 @@ Sub main
crt.Screen.Send "SESSION-ID" & VbCr
crt.Screen.Synchronous = False
End Sub
------------------
------------------
4. puttyIP
4. puttyIP
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@192.168.1.2: \w\a\]$PS1"
ubuntuLinuxSecureCRT
ubuntuLinuxSecureCRT
*/
//#define RDP_CLIENT_SYSTEM_BUILTIN
@ -67,14 +67,16 @@ desktopwidth:i:%d\n\
desktopheight:i:%d\n\
session bpp:i:16\n\
winposstr:s:0,1,%d,%d,%d,%d\n\
compression:i:1\n\
bitmapcachepersistenable:i:1\n\
bitmapcachesize:i:32000\n\
compression:i:1\n\
keyboardhook:i:2\n\
audiocapturemode:i:0\n\
videoplaybackmode:i:1\n\
connection type:i:7\n\
networkautodetect:i:1\n\
bandwidthautodetect:i:1\n\
disableclipboardredirection:i:0\n\
displayconnectionbar:i:1\n\
enableworkspacereconnect:i:0\n\
disable wallpaper:i:1\n\
@ -112,6 +114,17 @@ username:s:%s\n\
password 51:b:%s\n\
";
// https://www.donkz.nl/overview-rdp-file-settings/
//
// authentication level:i:2\n
//
//
// negotiate security layer:i:1\n
// 0 = negotiation is not enabled and the session is started by using Secure Sockets Layer (SSL).
// 1 = negotiation is enabled and the session is started by using x.224 encryption.
//redirectdirectx:i:0\n\
//prompt for credentials on client:i:0\n\
@ -210,7 +223,7 @@ bool calc_psw51b(const char* password, std::string& ret) {
bool isDegital(std::string str) {
for (int i = 0; i < str.size(); i++) {
if (str.at(i) == '-' && str.size() > 1) // 有可能出现负数
if (str.at(i) == '-' && str.size() > 1) // 有可能出现负数
continue;
if (str.at(i) > '9' || str.at(i) < '0')
return false;
@ -377,7 +390,7 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
bool b_is_html = false;
// if (uri == "/") {
// ex_wstr page = L"<html lang=\"zh_CN\"><head><meta charset=\"utf-8\"/><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>Teleport助手</title>\n<style type=\"text/css\">\n.box{padding:20px;margin:40px;border:1px solid #78b17c;background-color:#e4ffe5;}\n</style>\n</head><body><div class=\"box\">Teleport助手工作正常</div></body></html>";
// ex_wstr page = L"<html lang=\"zh_CN\"><head><meta charset=\"utf-8\"/><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>Teleport助手</title>\n<style type=\"text/css\">\n.box{padding:20px;margin:40px;border:1px solid #78b17c;background-color:#e4ffe5;}\n</style>\n</head><body><div class=\"box\">Teleport助手工作正常</div></body></html>";
// ex_wstr2astr(page, ret_buf, EX_CODEPAGE_UTF8);
//
// mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n%s", ret_buf.size() - 1, &ret_buf[0]);
@ -410,7 +423,7 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
_this->_process_js_request(method, json_param, ret_buf);
}
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: application/json\r\n\r\n%s", ret_buf.size() - 1, &ret_buf[0]);
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: application/json\r\n\r\n%s", ret_buf.length(), &ret_buf[0]);
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
@ -478,7 +491,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
ex_astrs strs;
size_t pos_start = 1; // 跳过第一个字节,一定是 '/'
size_t pos_start = 1; // 跳过第一个字节,一定是 '/'
size_t i = 0;
for (i = pos_start; i < req->uri.len; ++i) {
@ -488,7 +501,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
tmp_uri.assign(req->uri.p + pos_start, i - pos_start);
strs.push_back(tmp_uri);
}
pos_start = i + 1; // 跳过当前找到的分隔符
pos_start = i + 1; // 跳过当前找到的分隔符
}
}
if (pos_start < req->uri.len) {
@ -522,7 +535,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
}
if (func_args.length() > 0) {
// 将参数进行 url-decode 解码
// 将参数进行 url-decode 解码
int len = func_args.length() * 2;
ex_chars sztmp;
sztmp.resize(len);
@ -558,24 +571,33 @@ void TsHttpRpc::_process_js_request(const ex_astr& func_cmd, const ex_astr& func
}
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) {
// 返回: {"code":123}
// 返回: {"code":123}
Json::FastWriter jr_writer;
Json::Value jr_root;
jr_root["code"] = errcode;
buf = jr_writer.write(jr_root);
// buf = jr_writer.write(jr_root);
Json::StreamWriterBuilder jwb;
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
ex_aoss os;
jwriter->write(jr_root, &os);
buf = os.str();
}
void TsHttpRpc::_create_json_ret(ex_astr& buf, Json::Value& jr_root) {
Json::FastWriter jr_writer;
buf = jr_writer.write(jr_root);
// Json::FastWriter jr_writer;
// buf = jr_writer.write(jr_root);
Json::StreamWriterBuilder jwb;
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
ex_aoss os;
jwriter->write(jr_root, &os);
buf = os.str();
}
void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf)
{
//处理urlprotocol调用访式
// 将参数进行 url-decode 解码
//处理urlprotocol调用访式
// 将参数进行 url-decode 解码
std::string func_args = args;
if (func_args.length() > 0)
{
@ -589,17 +611,17 @@ void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf)
func_args = &sztmp[0];
}
EXLOGD(("%s\n"), func_args.c_str());
//处理传参过来的teleport://{}/,只保留参数部份
//处理传参过来的teleport://{}/,只保留参数部份
std::string urlproto_appname = TP_URLPROTO_APP_NAME;
urlproto_appname += "://{";
func_args.erase(0, urlproto_appname.length());//去除第一个URLPROTO_APP_NAME以及://字符
func_args.erase(0, urlproto_appname.length());//去除第一个URLPROTO_APP_NAME以及://字符
int pos = func_args.length() - 1;
if (func_args.substr(pos, 1) == "/")
func_args.erase(pos - 1, 2);//去除最后一个}/字符
func_args.erase(pos - 1, 2);//去除最后一个}/字符
else
func_args.erase(pos, 1);
//由于命令行、ie浏览器参数传递时会把原来json结构中的"号去掉需要重新格式化参数为json格式
//由于命令行、ie浏览器参数传递时会把原来json结构中的"号去掉需要重新格式化参数为json格式
if (func_args.find("\"", 0) == std::string::npos) {
std::vector<std::string> strv;
SplitString(func_args, strv, ",");
@ -620,21 +642,27 @@ void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf)
}
func_args = "{" + func_args + "}";
EXLOGD(("%s\n"), func_args.c_str());
//调用TsHttpRpc类里的_rpc_func_run_client启动客户端
//调用TsHttpRpc类里的_rpc_func_run_client启动客户端
_rpc_func_run_client(func_args, buf);
}
void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
// 入参:{"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2}
// 入参:{"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2}
// authmode: 1=password, 2=private-key
// protocol: 1=rdp, 2=ssh
// SSH返回 {"code":0, "data":{"sid":"0123abcde"}}
// RDP返回 {"code":0, "data":{"sid":"0123abcde0A"}}
// SSH返回 {"code":0, "data":{"sid":"0123abcde"}}
// RDP返回 {"code":0, "data":{"sid":"0123abcde0A"}}
Json::Reader jreader;
//Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot)) {
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = func_args.c_str();
ex_astr err;
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
@ -643,7 +671,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
return;
}
// 判断参数是否正确
// 判断参数是否正确
if (!jsRoot["teleport_ip"].isString()
|| !jsRoot["teleport_port"].isNumeric() || !jsRoot["remote_host_ip"].isString()
|| !jsRoot["session_id"].isString() || !jsRoot["protocol_type"].isNumeric() || !jsRoot["protocol_sub_type"].isNumeric()
@ -756,7 +784,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
int iHeight = GetSystemMetrics(SM_CYSCREEN);
if (rdp_w == 0 || rdp_h == 0) {
//全屏
//全屏
width = iWidth;
higth = iHeight;
display = 2;
@ -825,7 +853,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
fclose(f);
ex_astr2wstr(sz_file_name, tmp_rdp_file);
// 变量替换
// 变量替换
ex_replace_all(w_exe_path, _T("{tmp_rdp_file}"), tmp_rdp_file);
} else if (g_cfg.rdp_name == L"freerdp") {
w_exe_path += L"{size} {console} {clipboard} {drives} ";
@ -834,7 +862,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
ex_wstr w_screen;
if (rdp_w == 0 || rdp_h == 0) {
//全屏
//全屏
w_screen = _T("/f");
} else {
char sz_size[64] = { 0 };
@ -860,10 +888,10 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
w_sid = L"02" + w_sid;
w_exe_path += L" /gdi:sw"; // 使用软件渲染gdi:hw使用硬件加速但是会出现很多黑块录像回放时又是正常的
w_exe_path += L" -grab-keyboard"; // [new style] 防止启动FreeRDP后失去本地键盘响应必须得先最小化一下FreeRDP窗口不过貌似不起作用
w_exe_path += L" /gdi:sw"; // 使用软件渲染gdi:hw使用硬件加速但是会出现很多黑块录像回放时又是正常的
w_exe_path += L" -grab-keyboard"; // [new style] 防止启动FreeRDP后失去本地键盘响应必须得先最小化一下FreeRDP窗口不过貌似不起作用
// 变量替换
// 变量替换
ex_replace_all(w_exe_path, _T("{size}"), w_screen);
if (flag_console && rdp_console)
@ -941,22 +969,24 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
}
void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
Json::Reader jreader;
//Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot)) {
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = func_args.c_str();
ex_astr err;
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
// 判断参数是否正确
// 判断参数是否正确
if (!jsRoot["rid"].isInt()
|| !jsRoot["web"].isString()
|| !jsRoot["sid"].isString()
|| !jsRoot["user"].isString()
|| !jsRoot["acc"].isString()
|| !jsRoot["host"].isString()
|| !jsRoot["start"].isString()
) {
_create_json_ret(buf, TPE_PARAM);
return;
@ -965,67 +995,9 @@ void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
int rid = jsRoot["rid"].asInt();
ex_astr a_url_base = jsRoot["web"].asCString();
ex_astr a_sid = jsRoot["sid"].asCString();
ex_astr a_user = jsRoot["user"].asCString();
ex_astr a_acc = jsRoot["acc"].asCString();
ex_astr a_host = jsRoot["host"].asCString();
ex_astr a_start = jsRoot["start"].asCString();
char cmd_args[1024] = { 0 };
ex_strformat(cmd_args, 1023, "%d \"%s\" \"%09d-%s-%s-%s-%s\"", rid, a_sid.c_str(), rid, a_user.c_str(), a_acc.c_str(), a_host.c_str(), a_start.c_str());
// TODO: 理论上不应该由助手来提前做域名转为IP这样的操作而是应该将域名发送给播放器由播放器自己去处理
// 但是在改造FreeRDP制作的播放器时为了从服务器上下载文件使用了Mongoose库如果传入的是域名会出现问题貌似是异步查询DNS的问题
// 所以暂时先由助手进行域名IP转换。
{
unsigned int port_i = 0;
struct mg_str scheme, query, fragment, user_info, host, path;
if (mg_parse_uri(mg_mk_str(a_url_base.c_str()), &scheme, &user_info, &host, &port_i, &path, &query, &fragment) != 0) {
EXLOGE(_T("parse url failed.\n"));
Json::Value root_ret;
root_ret["code"] = TPE_PARAM;
_create_json_ret(buf, root_ret);
return;
}
ex_astr _scheme;
_scheme.assign(scheme.p, scheme.len);
// 将host从域名转换为IP
ex_astr str_tp_host;
str_tp_host.assign(host.p, host.len);
struct hostent *tp_host = gethostbyname(str_tp_host.c_str());
if (NULL == tp_host) {
EXLOGE(_T("resolve host name failed.\n"));
Json::Value root_ret;
root_ret["code"] = TPE_PARAM;
_create_json_ret(buf, root_ret);
return;
}
int i = 0;
char* _ip = NULL;
if (tp_host->h_addrtype == AF_INET) {
struct in_addr addr;
while (tp_host->h_addr_list[i] != 0) {
addr.s_addr = *(u_long *)tp_host->h_addr_list[i++];
_ip = inet_ntoa(addr);
break;
}
}
if (NULL == _ip) {
EXLOGE(_T("resolve host name failed.\n"));
Json::Value root_ret;
root_ret["code"] = TPE_PARAM;
_create_json_ret(buf, root_ret);
return;
}
char _url_base[256];
ex_strformat(_url_base, 255, "%s://%s:%d", _scheme.c_str(), _ip, port_i);
a_url_base = _url_base;
}
ex_strformat(cmd_args, 1023, "%s/%d", a_sid.c_str(), rid);
ex_wstr w_url_base;
ex_astr2wstr(a_url_base, w_url_base);
@ -1034,10 +1006,10 @@ void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
ex_wstr w_exe_path;
w_exe_path = _T("\"");
w_exe_path += g_env.m_tools_path + _T("\\tprdp\\tprdp-replay.exe\"");
w_exe_path += g_env.m_exec_path + _T("\\tp-player.exe\"");
w_exe_path += _T(" \"");
w_exe_path += w_url_base;
w_exe_path += _T("\" ");
w_exe_path += _T("/");
w_exe_path += w_cmd_args;
Json::Value root_ret;
@ -1073,9 +1045,15 @@ void TsHttpRpc::_rpc_func_get_config(const ex_astr& func_args, ex_astr& buf) {
}
void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf) {
Json::Reader jreader;
//Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot)) {
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = func_args.c_str();
ex_astr err;
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
@ -1088,14 +1066,20 @@ void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf) {
void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
Json::Reader jreader;
//Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot)) {
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = func_args.c_str();
ex_astr err;
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
// 判断参数是否正确
// 判断参数是否正确
if (!jsRoot["action"].isNumeric()) {
_create_json_ret(buf, TPE_PARAM);
return;
@ -1118,9 +1102,9 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrTitle = _T("选择文件");
ofn.lpstrTitle = _T("选择文件");
ofn.hwndOwner = hParent;
ofn.lpstrFilter = _T("可执行程序 (*.exe)\0*.exe\0");
ofn.lpstrFilter = _T("可执行程序 (*.exe)\0*.exe\0");
ofn.lpstrFile = wszReturnPath;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = wsDefaultPath.c_str();
@ -1138,12 +1122,12 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
ZeroMemory(&bi, sizeof(BROWSEINFO));
bi.hwndOwner = NULL;
bi.pidlRoot = NULL;
bi.pszDisplayName = wszReturnPath; //此参数如为NULL则不能显示对话框
bi.lpszTitle = _T("选择目录");
bi.pszDisplayName = wszReturnPath; //此参数如为NULL则不能显示对话框
bi.lpszTitle = _T("选择目录");
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.iImage = 0; //初始化入口参数bi结束
LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//调用显示选择对话框
bi.iImage = 0; //初始化入口参数bi结束
LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//调用显示选择对话框
if (pIDList) {
ret = true;
SHGetPathFromIDList(pIDList, wszReturnPath);

View File

@ -1,6 +1,6 @@
#ifndef __TS_ASSIST_VER_H__
#define __TS_ASSIST_VER_H__
#define TP_ASSIST_VER L"3.3.1"
#define TP_ASSIST_VER L"3.5.1"
#endif // __TS_ASSIST_VER_H__

View File

@ -55,9 +55,12 @@ int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...);
#include <string>
#include <vector>
#include <ostream>
typedef std::string ex_astr;
typedef std::wstring ex_wstr;
typedef std::ostringstream ex_aoss;
typedef std::wostringstream ex_woss;
typedef std::vector<ex_astr> ex_astrs;
typedef std::vector<ex_wstr> ex_wstrs;

View File

@ -22,6 +22,8 @@ wget = C:\Program Files (x86)\wget\wget.exe
# if not set msbuild path, default to get it by register.
#msbuild = C:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe
# need qt to build tp-player.
qt = C:\Qt\Qt5.12.0\5.12.0\msvc2017
# ============================================
# for Linux and macOS

Binary file not shown.

21
external/version.ini vendored
View File

@ -1,9 +1,16 @@
[external_ver]
openssl = 1.0.2s,1000210f
libuv = 1.28.0
mbedtls = 2.12.0
libssh = 0.9.0
jsoncpp = 0.10.6
mongoose = 6.12
; https://github.com/openssl/openssl/releases
; http://slproweb.com/download/Win32OpenSSL-1_1_1d.exe
openssl = 1.1.1d,1010104f
; https://github.com/libuv/libuv/releases
libuv = 1.33.1
; https://github.com/ARMmbed/mbedtls/releases
mbedtls = 2.16.3
; https://github.com/open-source-parsers/jsoncpp/releases
jsoncpp = 1.9.2
; https://github.com/cesanta/mongoose/releases
mongoose = 6.16
; https://www.zlib.net/zlib1211.zip
zlilb = 1211,1.2.11
zlib = 1.2.11,1211
; https://git.libssh.org/projects/libssh.git/
libssh = 0.9.2

Binary file not shown.

View File

@ -1,6 +1,6 @@
#ifndef __TS_SERVER_VER_H__
#define __TS_SERVER_VER_H__
#define TP_SERVER_VER L"3.3.0"
#define TP_SERVER_VER L"3.5.0"
#endif // __TS_SERVER_VER_H__

View File

@ -157,7 +157,7 @@ $app.on_table_host_cell_created = function (tbl, row_id, col_key, cell_obj) {
cell_obj.find('[data-action]').click(function () {
var row_data = tbl.get_row(row_id);
console.log('---', row_data);
// console.log('---', row_data);
var action = $(this).attr('data-action');
if (action === 'replay') {
@ -363,83 +363,26 @@ $app.on_table_host_render_created = function (render) {
};
$app.do_replay_rdp = function (record_id, user_username, acc_username, host_ip, time_begin) {
$assist.do_rdp_replay(
{
rid: record_id
// , web: $tp.web_server // + '/audit/get_rdp_record/' + record_id // 'http://' + ip + ':' + port + '/log/replay/rdp/' + record_id;
// , sid: Cookies.get('_sid')
, user: user_username
, acc: acc_username
, host: host_ip
, start: time_begin//tp_format_datetime(tp_utc2local(time_begin), 'yyyyMMdd-HHmmss')
if(!$app.options.core_running) {
$tp.notify_error(tp_error_msg(TPE_NO_CORE_SERVER), '无法播放。');
return;
}
if(!$assist.check())
return;
$assist.do_rdp_replay(
record_id
, function () {
// func_success
}
, function (code, message) {
if (code === TPE_NO_ASSIST)
if (code === TPE_NO_ASSIST) {
$assist.errcode = TPE_NO_ASSIST;
$assist.alert_assist_not_found();
}
else
$tp.notify_error('播放RDP操作录像失败' + tp_error_msg(code, message));
}
);
};
// $app.on_table_host_header_created = function (header) {
// $('#' + header._table_ctrl.dom_id + ' a[data-reset-filter]').click(function () {
// CALLBACK_STACK.create()
// .add(header._table_ctrl.load_data)
// .add(header._table_ctrl.reset_filters)
// .exec();
// });
//
// // 表格内嵌过滤器的事件绑定在这时进行(也可以延期到整个表格创建完成时进行)
// header._table_ctrl.get_filter_ctrl('search').on_created();
// };
// $app.get_selected_record = function (tbl) {
// var records = [];
// var _objs = $('#' + $app.table_record.dom_id + ' tbody tr td input[data-check-box]');
// $.each(_objs, function (i, _obj) {
// if ($(_obj).is(':checked')) {
// var _row_data = tbl.get_row(_obj);
// records.push(_row_data.id);
// }
// });
// return records;
// };
// $app.on_btn_remove_record_click = function () {
// var records = $app.get_selected_record($app.table_record);
// if (records.length === 0) {
// $tp.notify_error('请选择要删除的会话记录!');
// return;
// }
//
// var _fn_sure = function (cb_stack, cb_args) {
// $tp.ajax_post_json('/user/remove-user', {users: users},
// function (ret) {
// if (ret.code === TPE_OK) {
// cb_stack.add($app.check_host_all_selected);
// cb_stack.add($app.table_record.load_data);
// $tp.notify_success('删除用户账号操作成功!');
// } else {
// $tp.notify_error('删除用户账号操作失败:' + tp_error_msg(ret.code, ret.message));
// }
//
// cb_stack.exec();
// },
// function () {
// $tp.notify_error('网络故障,删除用户账号操作失败!');
// cb_stack.exec();
// }
// );
// };
//
// var cb_stack = CALLBACK_STACK.create();
// $tp.dlg_confirm(cb_stack, {
// msg: '<div class="alert alert-danger"><p><strong>注意:删除操作不可恢复!!</strong></p><p>删除用户账号将同时将其从所在用户组中移除,并且删除所有分配给此用户的授权!</p></div><p>如果您希望禁止某个用户登录本系统,可对其进行“禁用”操作!</p><p>您确定要移除所有选定的 <strong>' + user_list.length + '个</strong> 用户账号吗?</p>',
// fn_yes: _fn_sure
// });
// };

View File

@ -66,16 +66,29 @@ $assist.init = function (cb_stack) {
cb_stack.exec();
};
$assist.check = function() {
if (!$assist.running) {
$assist.errcode = TPE_NO_ASSIST;
$assist.alert_assist_not_found();
return false;
} else if (!$assist._version_compare()) {
$assist.errcode = TPE_OLD_ASSIST;
$assist.alert_assist_not_found();
return false;
}
return true;
};
$assist.alert_assist_not_found = function () {
console.log($assist.errcode);
if($assist.errcode === TPE_NO_ASSIST) {
$assist.dom.msg_box_title.html('未检测到TELEPORT助手');
$assist.dom.msg_box_info.html('需要TELEPORT助手来辅助远程连接请确认本机运行了TELEPORT助手');
$assist.dom.msg_box_desc.html('如果您尚未运行TELEPORT助手请 <a href="http://tp4a.com/download" target="_blank"><strong>下载最新版TELEPORT助手安装包</strong></a> 并安装。一旦运行了TELEPORT助手即可重新进行远程连接。');
$assist.dom.msg_box_desc.html('如果您尚未运行TELEPORT助手请 <a href="http://tp4a.com/download" target="_blank"><strong>下载最新版TELEPORT助手安装包</strong></a> 并安装。一旦运行了TELEPORT助手即可刷新页面,重新进行远程连接。');
} else if($assist.errcode === TPE_OLD_ASSIST) {
$assist.dom.msg_box_title.html('TELEPORT助手需要升级');
$assist.dom.msg_box_info.html('检测到TELEPORT助手版本 v'+ $assist.version +',但需要最低版本 v'+ $assist.ver_require+'。');
$assist.dom.msg_box_desc.html('请 <a href="http://tp4a.com/download" target="_blank"><strong>下载最新版TELEPORT助手安装包</strong></a> 并安装。一旦升级了TELEPORT助手即可重新进行远程连接。');
$assist.dom.msg_box_desc.html('请 <a href="http://tp4a.com/download" target="_blank"><strong>下载最新版TELEPORT助手安装包</strong></a> 并安装。一旦升级了TELEPORT助手即可刷新页面,重新进行远程连接。');
}
$('#dialog-need-assist').modal();
@ -134,15 +147,8 @@ $assist._make_message_box = function () {
};
$assist.do_teleport = function (args, func_success, func_error) {
if(!$assist.running) {
$assist.errcode = TPE_NO_ASSIST;
func_error(TPE_NO_ASSIST, '');
if(!$assist.check())
return;
} else if(!$assist._version_compare()) {
$assist.errcode = TPE_OLD_ASSIST;
func_error(TPE_NO_ASSIST, '');
return;
}
// 第一步将参数传递给web服务准备获取一个远程连接会话ID
var args_ = JSON.stringify(args);
@ -226,7 +232,7 @@ $assist.do_teleport = function (args, func_success, func_error) {
});
};
$assist.do_rdp_replay = function (args, func_success, func_error) {
$assist.do_rdp_replay = function (rid, func_success, func_error) {
// ==================================================
// args is dict with fields shown below:
// rid: (int) - record-id in database.
@ -236,10 +242,11 @@ $assist.do_rdp_replay = function (args, func_success, func_error) {
// start: (string) - when start the RDP connection, should be a UTC timestamp.
// ==================================================
// now fix the args.
// now make the args.
var args = {rid: rid};
args.web = $tp.web_server; // (string) - teleport server base address, like "http://127.0.0.1:7190", without end-slash.
args.sid = Cookies.get('_sid'); // (string) - current login user's session-id.
args.start = tp_format_datetime(tp_utc2local(args.start), 'yyyyMMdd-HHmmss'); // (string) - convert UTC timestamp to local human-readable string.
// args.start = tp_format_datetime(tp_utc2local(args.start), 'yyyyMMdd-HHmmss'); // (string) - convert UTC timestamp to local human-readable string.
console.log('do-rdp-replay:', args);
@ -264,34 +271,3 @@ $assist.do_rdp_replay = function (args, func_success, func_error) {
}
});
};
/*
var version_compare = function () {
var cur_version = parseInt(g_current_version.split(".")[2]);
var req_version = parseInt(g_req_version.split(".")[2]);
return cur_version >= req_version;
};
var start_rdp_replay = function (args, func_success, func_error) {
var args_ = encodeURIComponent(JSON.stringify(args));
$.ajax({
type: 'GET',
timeout: 6000,
url: $assist.api_url + '/rdp_play/' + args_,
jsonp: 'callback',
dataType: 'json',
success: function (ret) {
if (ret.code === TPE_OK) {
error_process(ret, func_success, func_error);
} else {
func_error(ret.code, '查看录像失败!');
}
console.log('ret', ret);
},
error: function () {
func_error(TPE_NETWORK, '与助手的络通讯失败!');
}
});
};
*/

View File

@ -1,3 +1,3 @@
# -*- coding: utf8 -*-
TP_SERVER_VER = "3.3.1"
TP_ASSIST_REQUIRE_VER = "3.3.1"
TP_SERVER_VER = "3.5.1"
TP_ASSIST_REQUIRE_VER = "3.5.1"

View File

@ -411,12 +411,15 @@ class RecordHandler(TPBaseHandler):
return
if not tp_cfg().core.detected:
core_running = False
total_size = 0
free_size = 0
else:
core_running = True
total_size, free_size = get_free_space_bytes(tp_cfg().core.replay_path)
param = {
'core_running': core_running,
'total_size': total_size,
'free_size': free_size,
}
@ -659,25 +662,12 @@ class DoGetFileHandler(TPBaseHandler):
require_privilege = TP_PRIVILEGE_OPS_AUZ | TP_PRIVILEGE_AUDIT_AUZ | TP_PRIVILEGE_AUDIT
# sid = self.get_argument('sid', None)
# if sid is None:
# self.set_status(403)
# return self.write('need login first.')
#
# self._s_id = sid
# _user = self.get_session('user')
# if _user is None:
# self.set_status(403)
# return self.write('need login first.')
# self._user = _user
# when test, disable auth.
# if not self._user['_is_login']:
# self.set_status(401) # 401=未授权, 要求身份验证
# return self.write('need login first.')
# if (self._user['privilege'] & require_privilege) == 0:
# self.set_status(403) # 403=禁止
# return self.write('you have no such privilege.')
if not self._user['_is_login']:
self.set_status(401) # 401=未授权, 要求身份验证
return self.write('need login first.')
if (self._user['privilege'] & require_privilege) == 0:
self.set_status(403) # 403=禁止
return self.write('you have no such privilege.')
act = self.get_argument('act', None)
_type = self.get_argument('type', None)

View File

@ -10,8 +10,8 @@ Minor: 次版本号。如果两个程序集的名称和主版本号相同,而
Revision: 修订号。主版本号和次版本号都相同但修订号不同的程序集应是完全可互换的。
这适用于修复以前发布的程序集中的错误或安全漏洞。
TP_SERVER 3.3.1 # 整个服务端打包的版本
TP_TPCORE 3.3.0 # 核心服务 tp_core 的版本
TP_SERVER 3.5.1 # 整个服务端打包的版本
TP_TPCORE 3.5.0 # 核心服务 tp_core 的版本
TP_TPWEB 3.1.0 # web服务 tp_web 的版本一般除非升级Python否则不会变化
TP_ASSIST 3.3.1 # 助手版本
TP_ASSIST_REQUIRE 3.3.1 # 适配的助手最低版本
TP_ASSIST 3.5.1 # 助手版本
TP_ASSIST_REQUIRE 3.5.1 # 适配的助手最低版本