Merge branch 'dev'

pull/236/head
Apex Liu 2020-06-06 01:20:43 +08:00
commit 5f1a3d0f25
1136 changed files with 146003 additions and 89231 deletions

14
.gitignore vendored
View File

@ -9,6 +9,9 @@
*.aps
**/ipch
*.tpr
*.tpd
# for CMake
CMakeFiles
cmake_install.cmake
@ -28,8 +31,10 @@ __pycache__
**/.idea/misc.xml
**/.idea/dictionaries
**/.idea/watcherTasks.xml
**/.idea/dataSources.*
**/.idea/inspectionProfiles
**/.idea/codeStyles
**/.idea/dataSources
**/.idea/inspectionProfiles
**/.idea/vcs.xml
**/.idea/modules.xml
@ -65,7 +70,6 @@ __pycache__
/server/share/db
/server/share/log
/server/share/replay
/server/testssh
# for generated files.
@ -78,9 +82,7 @@ __pycache__
/client/tp_rdp
/server/tp_core/protocol/rdp
/client/tools/tprdp
/server/tp_core/testssh
/client/tp_assist_win_it_doctor
/dist/client/windows/assist-it-doctor
/client/build-tp-player-*
# for MacOS.
.DS_Store
@ -102,3 +104,7 @@ profile
*.moved-aside
/server/share/tmp
/server/tp_core/testssh/Debug
/server/tp_core/testssh/Release
/external/zlib
/client/tools/qt-redist

View File

@ -21,6 +21,7 @@
<file url="file://$PROJECT_DIR$/server/tp_core/common/ts_membuf.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/common/ts_memstream.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/core/main.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/core/tp_tpp_mgr.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_env.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_http_rpc.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_http_rpc.h" charset="GBK" />
@ -31,6 +32,7 @@
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_web_rpc.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_conn.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_conn.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_package.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_proxy.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_session.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_session.h" charset="GBK" />

View File

@ -14,15 +14,16 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
MESSAGE(STATUS "build on macOS...")
set(OS_MACOS 1)
set(OS_POSIX 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(TP_EXTERNAL_RELEASE_DIR "${PROJECT_SOURCE_DIR}/external/macos/release")
elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
set(OS_LINUX 1)
set(OS_POSIX 1)
MESSAGE(STATUS "build on Linux...")
# add_subdirectory(server/tp_web/src)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(TP_EXTERNAL_RELEASE_DIR "${PROJECT_SOURCE_DIR}/external/linux/release")
elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
# MESSAGE(FATAL_ERROR "unsupported platform: Windows")
MESSAGE(FATAL_ERROR "unsupported platform: Windows")
else ()
MESSAGE(FATAL_ERROR "unsupported platform: ${CMAKE_SYSTEM_NAME}")
endif ()

View File

@ -36,7 +36,7 @@ endif()
add_subdirectory(server/tp_core/core)
add_subdirectory(server/tp_core/protocol/ssh)
add_subdirectory(server/tp_core/protocol/telnet)
#add_subdirectory(server/testssh/testssh)
add_subdirectory(server/tp_core/testssh)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/server/tp_core/protocol/rdp")
add_subdirectory(server/tp_core/protocol/rdp)

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/builder" />
<orderEntry type="inheritedJdk" />
<content url="file://$MODULE_DIR$/builder">
<sourceFolder url="file://$MODULE_DIR$/builder" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="py37" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">

View File

@ -3,10 +3,10 @@
################################################################
# Basic settings.
################################################################
VER_PYTHON="3.7.0"
VER_PYTHON="3.7.5"
VER_PYTHON_SHORT="3.7"
VER_OPENSSL="1.0.2p"
VER_SQLITE="3250000"
VER_OPENSSL="1.1.1d"
VER_SQLITE="3300100"
VER_ZLIB="1.2.11"
VER_PYTHON_LIB="${VER_PYTHON_SHORT}m"
@ -38,15 +38,6 @@ function on_error()
exit 1
}
function setp_build_git()
{
# su -s
# yum install zlib-devel expat-devel libcurl-devel
# make prefix=/usr/local
# make prefix=/usr/local install
echo 'skip build git now.'
}
function dlfile()
{
echo -n "Downloading $1 ..."
@ -77,7 +68,7 @@ function step_download_files()
dlfile "python source tarball" "https://www.python.org/ftp/python/${VER_PYTHON}/" "Python-${VER_PYTHON}.tgz" ${PATH_DOWNLOAD}
dlfile "openssl source tarball" "https://www.openssl.org/source/" "openssl-${VER_OPENSSL}.tar.gz" ${PATH_DOWNLOAD}
dlfile "sqlite source tarball" "http://sqlite.org/2018/" "sqlite-autoconf-${VER_SQLITE}.tar.gz" ${PATH_DOWNLOAD}
dlfile "sqlite source tarball" "http://sqlite.org/2019/" "sqlite-autoconf-${VER_SQLITE}.tar.gz" ${PATH_DOWNLOAD}
dlfile "zlib source tarball" "https://www.zlib.net/" "zlib-${VER_ZLIB}.tar.gz" ${PATH_DOWNLOAD}
}
@ -121,6 +112,16 @@ function step_prepare_source()
on_error "Can not prepare source code for build sqlite3 module for Python."
fi
if [ ! -f "${PATH_FIX}/Python-${VER_PYTHON}/Modules/Setup.dist" ]; then
on_error "Can not fix source for build Python."
fi
if [ ! -f "${PATH_FIX}/Python-${VER_PYTHON}/Modules/_sqlite/cache.h" ]; then
on_error "Can not fix source for build sqlite3 module for Python."
fi
if [ ! -f "${PATH_FIX}/Python-${VER_PYTHON}/Modules/_sqlite/prepare_protocol.h" ]; then
on_error "Can not fix source for build sqlite3 module for Python."
fi
cp "${PATH_FIX}/Python-${VER_PYTHON}/Modules/Setup.dist" "${PY_PATH_SRC}/Modules/Setup.dist"
cp "${PATH_FIX}/Python-${VER_PYTHON}/Modules/Setup.dist" "${PY_PATH_SRC}/Modules/Setup"
cp "${PATH_FIX}/Python-${VER_PYTHON}/Modules/_sqlite/cache.h" "${PY_PATH_SRC}/Modules/_sqlite/cache.h"

View File

@ -64,6 +64,9 @@ def main():
elif x == 'a':
clean_everything()
continue
elif x == 'e':
clean_external()
continue
try:
x = int(x)
@ -117,6 +120,27 @@ def clean_everything():
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libuv.a'))
def clean_external():
#utils.remove(os.path.join(env.root_path, 'out'))
utils.remove(os.path.join(env.root_path, 'external', 'jsoncpp'))
utils.remove(os.path.join(env.root_path, 'external', 'libuv'))
utils.remove(os.path.join(env.root_path, 'external', 'mbedtls'))
utils.remove(os.path.join(env.root_path, 'external', 'mongoose'))
#utils.remove(os.path.join(env.root_path, 'external', 'openssl'))
#utils.remove(os.path.join(env.root_path, 'external', 'python'))
#utils.remove(os.path.join(env.root_path, 'external', 'libssh-win-static', 'lib'))
#utils.remove(os.path.join(env.root_path, 'external', 'libssh-win-static', 'src'))
#utils.remove(os.path.join(env.root_path, 'external', 'linux', 'tmp'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedcrypto.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedtls.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedx509.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libsqlite3.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libssh.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libssh_threads.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libuv.a'))
def do_opt(opt):
arg = ''
@ -155,6 +179,7 @@ def do_opt(opt):
# cmd = '"%s" -B "%s" %s' % (utils.cfg.py_exec, os.path.join(BUILDER_PATH, script), arg)
cmd = '%s -B %s %s' % (env.py_exec, os.path.join(env.builder_path, script), arg)
print(cmd)
os.system(cmd)
@ -248,6 +273,7 @@ def show_menu():
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, '%2d' % options[o]['id']), (cc.CR_NORMAL, '] ', options[o]['disp']))
cc.v(' -------------------------------------------------------')
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, ' E'), (cc.CR_NORMAL, '] clean external temp. files.'))
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, ' C'), (cc.CR_NORMAL, '] clean build and dist.'))
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, ' A'), (cc.CR_NORMAL, '] clean everything.'))

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,14 @@ 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', 'tp_assist_win', 'runtime'), tmp_app_path, 'vcruntime140.dll')
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'))
@ -90,14 +104,37 @@ class BuilderWin(BuilderBase):
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'winscp'), os.path.join(tmp_app_path, 'tools', 'winscp'), 'license.txt')
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'), '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', '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'), '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', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'wfreerdp.exe')
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 +142,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()
@ -117,6 +154,9 @@ class BuilderMacOS(BuilderBase):
utils.xcode_build(proj_file, 'TP-Assist', configuration, False)
utils.ensure_file_exists(os.path.join(out_file, 'Contents', 'Info.plist'))
def build_player(self):
cc.o('skip build tp_player now...')
def build_installer(self):
cc.i('make tp_assist dmg file...')
@ -169,7 +209,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 +255,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

@ -23,70 +23,90 @@ class BuilderBase:
self._init_path()
def _init_path(self):
cc.e("this is a pure-virtual function.")
cc.e("_init_path() pure-virtual function.")
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
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.")
cc.e("_build_jsoncpp() 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
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.")
cc.e("_build_mongoose() pure-virtual function.")
def build_openssl(self):
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("_build_openssl() 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
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
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)
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("_build_zlib() 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
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.")
cc.e("_build_libssh() pure-virtual function.")
def prepare_python(self):
self._prepare_python()
def _prepare_python(self):
cc.e("this is a pure-virtual function.")
cc.e("_prepare_python() pure-virtual function.")
def fix_output(self):
pass
@ -103,9 +123,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,69 +146,98 @@ 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('')
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
os.rename(os.path.join(PATH_EXTERNAL, 'libssh-{}'.format(env.ver_libssh)), self.LIBSSH_PATH_SRC)
# cc.n('fix libssh source code... ', end='')
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', 'src', 'sftp.c'))
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'sftp.c')
cc.n('fix libssh source code... ', end='')
s_name = 'libssh-{}'.format(env.ver_libssh)
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'session.c'))
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto.c'))
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto.c'))
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto-compat.c'))
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'session.c')
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto.c')
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto.c')
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto-compat.c')
out_file_lib = os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path, 'ssh.lib')
@ -210,7 +260,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 +268,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 +324,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 +335,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 +354,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('')
@ -277,6 +379,7 @@ class BuilderLinux(BuilderBase):
self.LIBUV_PATH_SRC = os.path.join(self.PATH_TMP, 'libuv-{}'.format(env.ver_libuv))
self.MBEDTLS_PATH_SRC = os.path.join(self.PATH_TMP, 'mbedtls-mbedtls-{}'.format(env.ver_mbedtls))
self.LIBSSH_PATH_SRC = os.path.join(self.PATH_TMP, 'libssh-{}'.format(env.ver_libssh))
self.ZLIB_PATH_SRC = os.path.join(self.PATH_TMP, 'zlib-{}'.format(env.ver_zlib))
self.JSONCPP_PATH_SRC = os.path.join(PATH_EXTERNAL, 'jsoncpp')
self.MONGOOSE_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mongoose')
@ -288,7 +391,7 @@ class BuilderLinux(BuilderBase):
cc.n('prepare python header and lib files ...')
if os.path.exists(os.path.join(self.PATH_RELEASE, 'include', 'python', 'Python.h')):
cc.w(' - header file already exists, skip.')
cc.w('python header file already exists, skip.')
else:
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'include', 'python{}m'.format(ctx.py_dot_ver), 'Python.h'))
utils.sys_exec('ln -s "{}" "{}"'.format(
@ -300,6 +403,8 @@ class BuilderLinux(BuilderBase):
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', lib_file))
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('')
@ -309,6 +414,8 @@ class BuilderLinux(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('')
@ -318,9 +425,12 @@ class BuilderLinux(BuilderBase):
cc.w('already exists, skip.')
def _build_openssl(self, file_name):
pass # we do not need build openssl anymore, because first time run build.sh we built Python, it include openssl.
# we do not need build openssl anymore, because first time run build.sh we built Python with openssl included.
cc.w('skip build openssl again.')
def _build_libuv(self, file_name):
if not self._download_libuv(file_name):
return
if not os.path.exists(self.LIBUV_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
@ -347,7 +457,11 @@ class BuilderLinux(BuilderBase):
# use os.unlink() because some file should be a link.
os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', 'libuv.a'))
def _build_mbedtls(self, file_name):
if not self._download_mbedtls(file_name):
return
if not os.path.exists(self.MBEDTLS_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
@ -382,8 +496,6 @@ class BuilderLinux(BuilderBase):
# fix source file
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'include', 'mbedtls', 'config.h'))
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'include', 'mbedtls'), os.path.join(self.MBEDTLS_PATH_SRC, 'include', 'mbedtls'), 'config.h')
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library', 'rsa.c'))
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
old_p = os.getcwd()
os.chdir(self.MBEDTLS_PATH_SRC)
@ -392,11 +504,14 @@ class BuilderLinux(BuilderBase):
os.chdir(old_p)
def _build_libssh(self, file_name):
if not self._download_libssh(file_name):
return
if not os.path.exists(self.LIBSSH_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
cc.n('build libssh...', end='')
if os.path.exists(os.path.join(self.PATH_RELEASE, 'lib', 'libssh.a')):
out_file = os.path.join(self.PATH_RELEASE, 'lib64', 'libssh.a')
if os.path.exists(out_file):
cc.w('already exists, skip.')
return
cc.v('')
@ -404,10 +519,10 @@ class BuilderLinux(BuilderBase):
cc.n('fix libssh source code... ', end='')
s_name = 'libssh-{}'.format(env.ver_libssh)
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'session.c'))
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto.c'))
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto.c'))
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto-compat.c'))
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'session.c')
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto.c')
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto.c')
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto-compat.c')
build_path = os.path.join(self.LIBSSH_PATH_SRC, 'build')
@ -417,14 +532,52 @@ class BuilderLinux(BuilderBase):
' -DOPENSSL_LIBRARIES={path_release}/lib' \
' -DWITH_SFTP=ON' \
' -DWITH_SERVER=ON' \
' -DWITH_STATIC_LIB=ON' \
' -DWITH_GSSAPI=OFF' \
' -DWITH_ZLIB=OFF' \
' -DWITH_ZLIB=ON' \
' -DWITH_PCAP=OFF' \
' -DBUILD_SHARED_LIBS=OFF' \
' -DUNIT_TESTING=OFF' \
' -DWITH_EXAMPLES=OFF' \
' -DWITH_BENCHMARKS=OFF' \
' -DWITH_NACL=OFF' \
''.format(path_release=self.PATH_RELEASE)
# ' -DWITH_STATIC_LIB=ON'
old_p = os.getcwd()
try:
utils.cmake(build_path, 'Release', False, cmake_define=cmake_define, cmake_pre_define='CFLAGS="-fPIC"')
os.chdir(build_path)
utils.sys_exec('make install')
except:
pass
os.chdir(old_p)
utils.ensure_file_exists(out_file)
# files = os.listdir(os.path.join(self.PATH_RELEASE, 'lib'))
# for i in files:
# if i.startswith('libssh.so'):
# # use os.unlink() because some file should be a link.
# os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
def _build_zlib(self, file_name):
# cc.w('skip build zlib again.')
if not self._download_zlib(file_name):
return
if not os.path.exists(self.ZLIB_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
cc.n('build zlib...', end='')
out_file = os.path.join(self.PATH_RELEASE, 'lib', 'libz.a')
if os.path.exists(out_file):
cc.w('already exists, skip.')
return
cc.v('')
build_path = os.path.join(self.ZLIB_PATH_SRC, 'build')
cmake_define = ' -DCMAKE_INSTALL_PREFIX={path_release}' \
' ..'.format(path_release=self.PATH_RELEASE)
old_p = os.getcwd()
@ -436,10 +589,10 @@ class BuilderLinux(BuilderBase):
pass
os.chdir(old_p)
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', 'libssh.a'))
utils.ensure_file_exists(out_file)
files = os.listdir(os.path.join(self.PATH_RELEASE, 'lib'))
for i in files:
if i.startswith('libssh.so'):
if i.startswith('libz.so'):
# use os.unlink() because some file should be a link.
os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
@ -458,6 +611,7 @@ class BuilderMacOS(BuilderBase):
self.LIBUV_PATH_SRC = os.path.join(self.PATH_TMP, 'libuv-{}'.format(env.ver_libuv))
self.MBEDTLS_PATH_SRC = os.path.join(self.PATH_TMP, 'mbedtls-mbedtls-{}'.format(env.ver_mbedtls))
self.LIBSSH_PATH_SRC = os.path.join(self.PATH_TMP, 'libssh-{}'.format(env.ver_libssh))
self.ZLIB_PATH_SRC = os.path.join(self.PATH_TMP, 'zlib-{}'.format(env.ver_zlib))
self.JSONCPP_PATH_SRC = os.path.join(PATH_EXTERNAL, 'jsoncpp')
self.MONGOOSE_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mongoose')
@ -466,6 +620,8 @@ class BuilderMacOS(BuilderBase):
utils.makedirs(self.PATH_TMP)
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('')
@ -475,6 +631,8 @@ class BuilderMacOS(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('')
@ -484,7 +642,10 @@ class BuilderMacOS(BuilderBase):
cc.w('already exists, skip.')
def _build_openssl(self, file_name):
if not super()._build_openssl(file_name):
cc.w('skip build openssl again.')
return
if not self._download_openssl(file_name):
return
cc.n('prepare openssl source code...', end='')
@ -512,6 +673,8 @@ class BuilderMacOS(BuilderBase):
os.chdir(old_p)
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):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
@ -534,6 +697,8 @@ class BuilderMacOS(BuilderBase):
os.chdir(old_p)
def _build_mbedtls(self, file_name):
if not self._download_mbedtls(file_name):
return
if not os.path.exists(self.MBEDTLS_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
@ -578,6 +743,11 @@ class BuilderMacOS(BuilderBase):
os.chdir(old_p)
def _build_libssh(self, file_name):
# cc.n('skip build libssh on macOS.')
# return
if not self._download_libssh(file_name):
return
if not os.path.exists(self.LIBSSH_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
@ -591,20 +761,24 @@ class BuilderMacOS(BuilderBase):
build_path = os.path.join(self.LIBSSH_PATH_SRC, 'build')
cmake_define = ' -DCMAKE_INSTALL_PREFIX={path_release}' \
' -DOPENSSL_INCLUDE_DIR={path_release}/include' \
' -DOPENSSL_LIBRARIES={path_release}/lib' \
' -DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl/include' \
' -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib' \
' -DWITH_GCRYPT=OFF' \
' -DWITH_GEX=OFF' \
' -DWITH_SFTP=ON' \
' -DWITH_SERVER=ON' \
' -DWITH_STATIC_LIB=ON' \
' -DWITH_GSSAPI=OFF' \
' -DWITH_ZLIB=OFF' \
' -DWITH_ZLIB=ON' \
' -DWITH_PCAP=OFF' \
' -DBUILD_SHARED_LIBS=OFF' \
' -DUNIT_TESTING=OFF' \
' -DWITH_EXAMPLES=OFF' \
' -DWITH_BENCHMARKS=OFF' \
' -DWITH_NACL=OFF' \
''.format(path_release=self.PATH_RELEASE)
# ' -DWITH_STATIC_LIB=ON'
try:
utils.cmake(build_path, 'Release', False, cmake_define)
except:
@ -618,6 +792,41 @@ class BuilderMacOS(BuilderBase):
# utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'threads'), os.path.join(self.PATH_RELEASE, 'lib'), 'libssh_threads.a')
utils.copy_ex(os.path.join(self.LIBSSH_PATH_SRC, 'include'), os.path.join(self.PATH_RELEASE, 'include'), 'libssh')
def _build_zlib(self, file_name):
# cc.w('skip build zlib again.')
if not self._download_zlib(file_name):
return
if not os.path.exists(self.ZLIB_PATH_SRC):
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
cc.n('build zlib...', end='')
out_file = os.path.join(self.PATH_RELEASE, 'lib', 'libz.a')
if os.path.exists(out_file):
cc.w('already exists, skip.')
return
cc.v('')
build_path = os.path.join(self.ZLIB_PATH_SRC, 'build')
cmake_define = ' -DCMAKE_INSTALL_PREFIX={path_release}' \
' ..'.format(path_release=self.PATH_RELEASE)
old_p = os.getcwd()
try:
utils.cmake(build_path, 'Release', False, cmake_define=cmake_define, cmake_pre_define='CFLAGS="-fPIC"')
os.chdir(build_path)
utils.sys_exec('make install')
except:
pass
os.chdir(old_p)
utils.ensure_file_exists(out_file)
files = os.listdir(os.path.join(self.PATH_RELEASE, 'lib'))
for i in files:
if i.startswith('libz.so'):
# use os.unlink() because some file should be a link.
os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
def _prepare_python(self):
pass
@ -673,6 +882,7 @@ def main():
builder.build_openssl()
builder.build_libuv()
builder.build_mbedtls()
builder.build_zlib()
builder.build_libssh()
builder.fix_output()

View File

@ -13,11 +13,11 @@ ctx = BuildContext()
MODULES_WIN = ['_asyncio', '_bz2', '_ctypes', '_hashlib', '_lzma', '_overlapped', '_socket', '_sqlite3', '_ssl', 'select', 'sqlite3',
'libcrypto-1_1', 'libssl-1_1', 'unicodedata']
PY_LIB_REMOVE_WIN = ['ctypes/test', 'curses', 'dbm', 'distutils', 'email/test', 'ensurepip', 'idlelib', 'lib2to3',
PY_LIB_REMOVE_WIN = ['ctypes/test', 'curses', 'dbm', 'distutils/test', 'email/tests', 'ensurepip', 'idlelib', 'lib2to3',
'lib-dynload', 'pydoc_data', 'site-packages', 'sqlite3/test', 'test', 'tkinter', 'turtledemo',
'unittest', 'venv', 'wsgiref', 'doctest.py', 'pdb.py', 'py_compile.py', 'pydoc.py',
'this.py', 'wave.py', 'webbrowser.py', 'zipapp.py']
PY_LIB_REMOVE_LINUX = ['ctypes/test', 'curses', 'dbm', 'distutils', 'ensurepip', 'idlelib', 'lib2to3',
PY_LIB_REMOVE_LINUX = ['ctypes/test', 'curses', 'dbm', 'distutils/tests', 'ensurepip', 'idlelib', 'lib2to3',
'lib-dynload', 'pydoc_data', 'site-packages', 'sqlite3/test', 'test', 'tkinter', 'turtledemo', 'unittest', 'venv',
'wsgiref', 'doctest.py', 'pdb.py', 'py_compile.py', 'pydoc.py', 'this.py', 'wave.py', 'webbrowser.py', 'zipapp.py']
PY_MODULE_REMOVE_LINUX = ['_ctypes_test', '_testbuffer', '_testcapi', '_testimportmultiple', '_testmultiphase', '_xxtestfuzz']
@ -46,7 +46,7 @@ class PYSBase:
utils.sys_exec('{} -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip --upgrade'.format(env.py_exec))
pip = self._get_pip()
pypi_modules = ['ldap3', 'mako', 'Pillow', 'psutil', 'pymysql', 'qrcode', 'tornado', 'wheezy.captcha']
pypi_modules = ['cffi', 'cryptography', 'ldap3', 'mako', 'Pillow', 'psutil', 'pyasn1', 'pymysql', 'qrcode', 'tornado', 'wheezy.captcha']
for p in pypi_modules:
cc.n('install {} ...'.format(p))
utils.sys_exec('{} install -i https://pypi.tuna.tsinghua.edu.cn/simple {}'.format(pip, p), direct_output=True)
@ -111,7 +111,7 @@ class PYSBase:
utils.ensure_file_exists(out_file)
cc.v('remove temp folder...')
utils.remove(_tmp_)
# utils.remove(_tmp_)
def _make_py_ver_file(self):
pass
@ -250,7 +250,7 @@ class PYSLinux(PYSBase):
def _get_pip(self):
_exec_path = os.path.dirname(env.py_exec)
return os.path.join(_exec_path, 'pip')
return os.path.join(_exec_path, 'pip3.7')
def _make_py_ver_file(self):
# do nothing.

View File

@ -18,6 +18,7 @@ class Builder:
self.VER_TP_TPCORE = ''
self.VER_TP_TPWEB = ''
self.VER_TP_ASSIST = ''
self.VER_TP_ASSIST_REQUIRE = ''
def build(self):
cc.n('update version...')
@ -43,12 +44,17 @@ class Builder:
x = l.split(' ')
self.VER_TP_ASSIST = x[1].strip()
# self.VER_TP_ASSIST += '.0'
elif l.startswith('TP_ASSIST_REQUIRE '):
x = l.split(' ')
self.VER_TP_ASSIST_REQUIRE = x[1].strip()
# self.VER_TP_ASSIST += '.0'
cc.v('new version:')
cc.v(' Server : ', self.VER_TP_SERVER)
cc.v(' - tp_core : ', self.VER_TP_TPCORE)
cc.v(' - tp_web : ', self.VER_TP_TPWEB)
cc.v(' Assist : ', self.VER_TP_ASSIST)
cc.v(' - Require : ', self.VER_TP_ASSIST_REQUIRE)
cc.v('')
self.make_builder_ver()
@ -100,7 +106,12 @@ class Builder:
def make_server_ver(self):
ver_file = os.path.join(env.root_path, 'server', 'www', 'teleport', 'webroot', 'app', 'app_ver.py')
# ver_content = '# -*- coding: utf8 -*-\n\nTS_VER = "{}"\n'.format(self.VER_TELEPORT_SERVER)
ver_content = '# -*- coding: utf8 -*-\nTP_SERVER_VER = "{}"\n'.format(self.VER_TP_SERVER)
# ver_content = '# -*- coding: utf8 -*-\nTP_SERVER_VER = "{}"\n'.format(self.VER_TP_SERVER)
ver_content = '' \
'# -*- coding: utf8 -*-\n' \
'TP_SERVER_VER = "{}"\n' \
'TP_ASSIST_REQUIRE_VER = "{}"\n' \
''.format(self.VER_TP_SERVER, self.VER_TP_ASSIST_REQUIRE)
rewrite = False
if not os.path.exists(ver_file):

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.2.0"
VER_TP_ASSIST = "3.2.0"
# -*- coding: utf8 -*-
VER_TP_SERVER = "3.5.5"
VER_TP_ASSIST = "3.5.5"

View File

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxI1ZDRvuNnkVB
JgTZmnwF97d7Ace+R0gSSkWi2l2oezakLSdUUkiysr1wx45u2Du36FNqMGg7LiCZ
SX1e2Zba96PI6vwNGnlprCfXTe2eV3W8kMPKA6c9X8BTktMZINNHO3K591jGx+uM
fyrl6/CFVPHNkl73Ium9u91JXIX9BOata4RTlphmHADc+hPXuC6oeN8qayZvV2rV
Jfx1wMlWCMiGJM36JJO5pywteBCKQkVJuJ7y29XF2wT690o+i6ugk+yI2/2OpiET
2E5SYdvyhlbcU+iBERsnY3X7IvFY8/m00YIjIc3reGSEwt9M5WTPRCjgonnpQGAx
9xWXwqkzAgMBAAECggEAT9b2YdInye0EWxy+cFoBBGzPeE/PlcW+LCghRFlutzEM
l3FH21hfL6OUq7m3BCZeJ3cp3zfl2upb6sT1WKlMlHV36jc7ew8v8fgJPPVVXp7w
oZ2A5estvVltsX4knOZMbgJV6xLldvOMnvkf9/6VpV/Jq9nxzXvmzmZcT0TuLCaF
uPk/g/yD5qQ8LkWXDVJeBiDrrOZYo5F+T8bveYKKIEZV0ZAlXwJqVOUFnhffIaDF
fZVDOv4K3+q0aRDLTY2hxptHZiKzpLXgU634nBN3fiy0Fj88upNIus22gjaz+Jfx
2pYv22iGNXAMFQwGaeuT7d4+qhgxze8C7YlLJsJWCQKBgQD8kkXbgYG+8NoKmovz
ki9nuK1R6On5pNjZ344SJm6t/s4FaxQhE/4oHvODwgolqKyT2Sq1K8/5NInRGA29
xPqqkkhwWk3Zf9VTXgmuXsOikPhbCOuiehO+6/ZthmHYy1jBMqkAIWYaL9Ytn2qb
dKMHwzNdnppQNdQnwmXI2ZdRBQKBgQD0aVTSOmKfKdIxH9qFLdbi2CoyJMzjAjm9
Ss5M0OhI9wZnCXyjPBx4hOs+M/BKx4lQ296u2Dh+gSK3L8K3x8lVqqx8gd614qaC
EWzXZpAbd1S835o2vVYEWXU0iI9s0jkj+VnILEWBMRPYManRUATB2phwRPulimdu
o+BWN0GG1wKBgCYBxO1hMasQB1+tHf5LM0MCcWJwEDV27wLqNzDYA7O/MjVyhZbs
sURMVAyxuGEuXrno5hpZO3SeyVZjrj2uVKIyXSA7FpfyOqHO9tn8fKgL9LOORhcv
E6WZUH3uyO6cuwBnpTLV082BAVPgN2SpSpcycppV8Za8Yu6QvExbIgAZAoGBALcq
ANETxDj3hHggIQlRkwqpaOXvQkSVtGOxne1fWdTkmz24lFlYgRWotwsErX29D6Ez
RSzPCXd0m2mhN1G3PaEfqOgeA6NXWeV73Y+HY1PSGAT7pXyEY+QajoVyGdo5qWzW
P3yOAQCSoQaSIWulhgspILhyWgxzLpRx53t1KXw9AoGBAOxsrIrx/S6onTz58ncZ
m99OWwJX4WmY5KKhc5dWrfgHrNfldSbhjRhjALy6hSPzkaVy01wXKeeIZl64rUbd
S/r58yALQ5wuIHAi53BLStxgqEdHQHLg16GqL3b/+Waaf+Fy9y5eoUQ976HPr33G
uDJ1AAnWjX3KvcyZeWLFTU2/
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+xdLVfN5IErS1
UplYesMvkFZVBWlH2AojfJ8pSnaqfE6XeVWc/hoCpiqTSkGdoX4NLgy8cjAcvI9Z
1E4Xdcuch161F4HH68V0CZsSK7LHHjXI9SLbjSyoUL0BvDIEGG9D2Zyqy8xTiVc6
gBZBvR1pGRsh59KiRMNCPN67lT6PGTt+OxMJmh2laYBWh28Lbqx4R1nBl8/m1wZa
QFhTTz0WBrBm4/3j95bQXIUjP0kW8uFcaIg0oA3/1EM5DrVQqJfp7ePEWevToP3H
4Ny1/Wg/gWTpSiB/dgN8c3vXokWxabGJG/Oq5CWjtw9gWFyR0I/OmFh8cnWPf2vf
QFVYQnv1AgMBAAECggEAeGs2ojuns6bbGnmBAjC7dBKP7Cr2QbtE6xGHBfFS5lqA
4WxddjOPB40L4t1EfdOqVXdz4p/RbtI3SmSQxo48cBmi1nx4F1Hj2VMW52ld+AJB
wQ+7aQq73aLZK3c3uw4Rbaq3EbiCyVgwD2U6p1RQdD68ubIzauostmrlzVJvorMZ
1J0hz1gsJuH87WpkgRdp910hEYiM7eUBrOKG+K0trohVeStsjjJyV47LKxXDtf2F
yUQvpbbIgHh2mXe29+d42hio2VrB5y1/+dc7wMiPlwBPG5xpv4eW1aaIGNTsHYCO
1dy8KQirOsrGLIp0GzEej2XL/wTlHJfv3nSpYR2gIQKBgQD4I3lySzFqFUlrvZoX
F9gYKbbH81gQakoAyk68qy4ENve5g+cChHTI6cO4fW8zIeEtLs/kDdpGuV20NmaD
Pb8lcd7nONGhQ1l75aNAy978e2WuAYQ4xfMLR+8jKdTDG9ttIqhGFSNXetMGINLJ
GkCl5fWAJ4p6oKBsUy2FgESECwKBgQDE0RWNQofR0UmxMPeHtD6i3pX2j3bb0GdM
1yh8vqE1IqXJesVM//gIgSZ4n3hd93AXDQIvJ6xkdtNKbnGi5wJLf1OiYW3iSkfC
l+Lgup10CVHJpOrBLxUGYZWjY4LsEX3z3MBNW4DQ6SNmIJN7xJAAewzq0UMMGk6P
IIQ7rvT//wKBgQCqiDa+xc6ACYEb+oIbvNdWQ9TKNgMfxOx2/pJ+N2a4ns5BQNVS
dZWNPpq0AACcM3x9gN5+7MZGNL6hS4HIUHc9VLTMU9A98/tbmsZHkdT90BBhNcmY
+vG9nwJKOEVwkYSLzHW5NG3FgTPl0kkKzHABk7jVClexTxLxX3i5dx2fYQKBgCla
bRbTJcp2GO+8BCZlPsvlzMiTeDvTXAEPLBiZzTFm6EKfIxl8ptbSnAy4JQhJVyng
t9bElTo+pUJ8VjAOLbNDO4Vgxz/Gr7E5TJg/XZnl42Nk3VZd2CMRGenMnNOREU/N
0DHwye4bLi7lJVfaAw+2yw4DjfzbAiqcgGwx5JRtAoGBAPFqMyLgZGtCBLrrJVxD
kswm0gABU7l/UXS7OfLTWmfr0vDzoZEcVeBcabwmpRnsTaj1+EHcpl8kZogO4mcg
0RiT+lc2E8TfZL5c4HEr4wSLbz8FEeKwhFa6ScNUOj5vVSnsFzW1xkVEBIM8akMR
UI4+yvEjUIpuQt35cyE9K/nx
-----END PRIVATE KEY-----

View File

@ -1,24 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEGTCCAwGgAwIBAgIEASUKPDANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJD
MIIEHzCCAwegAwIBAgIEASUKQDANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJD
TjENMAsGA1UECgwEVFA0QTEZMBcGA1UECwwQVFA0QSBUZWxlcG9ydCBDQTEZMBcG
A1UEAwwQVFA0QSBUZWxlcG9ydCBDQTAgFw0xODExMDgxNzMyMjdaGA8yMTE4MTAx
NTE3MzIyN1owXzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQHDAJ0
A1UEAwwQVFA0QSBUZWxlcG9ydCBDQTAgFw0xOTAxMjUxMDM0MTVaGA8yMTE5MDEw
MTEwMzQxNVowXzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQHDAJ0
cDERMA8GA1UECgwIVGVsZXBvcnQxDzANBgNVBAsMBkFzc2lzdDESMBAGA1UEAwwJ
bG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8SNWQ0b7
jZ5FQSYE2Zp8Bfe3ewHHvkdIEkpFotpdqHs2pC0nVFJIsrK9cMeObtg7t+hTajBo
Oy4gmUl9XtmW2vejyOr8DRp5aawn103tnld1vJDDygOnPV/AU5LTGSDTRztyufdY
xsfrjH8q5evwhVTxzZJe9yLpvbvdSVyF/QTmrWuEU5aYZhwA3PoT17guqHjfKmsm
b1dq1SX8dcDJVgjIhiTN+iSTuacsLXgQikJFSbie8tvVxdsE+vdKPouroJPsiNv9
jqYhE9hOUmHb8oZW3FPogREbJ2N1+yLxWPP5tNGCIyHN63hkhMLfTOVkz0Qo4KJ5
6UBgMfcVl8KpMwIDAQABo4HnMIHkMB0GA1UdDgQWBBRc5d0h39QISTM55kCqPyy1
dohEHTB6BgNVHSMEczBxgBSh6jvPH2KfGq3ekij4vF+Bqa/roqFWpFQwUjELMAkG
MTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvsXS1Xze
SBK0tVKZWHrDL5BWVQVpR9gKI3yfKUp2qnxOl3lVnP4aAqYqk0pBnaF+DS4MvHIw
HLyPWdROF3XLnIdetReBx+vFdAmbEiuyxx41yPUi240sqFC9AbwyBBhvQ9mcqsvM
U4lXOoAWQb0daRkbIefSokTDQjzeu5U+jxk7fjsTCZodpWmAVodvC26seEdZwZfP
5tcGWkBYU089FgawZuP94/eW0FyFIz9JFvLhXGiINKAN/9RDOQ61UKiX6e3jxFnr
06D9x+Dctf1oP4Fk6Uogf3YDfHN716JFsWmxiRvzquQlo7cPYFhckdCPzphYfHJ1
j39r30BVWEJ79QIDAQABo4HtMIHqMB0GA1UdDgQWBBQHRB+sP9RolTsf34gPFAJw
6UKn2zB6BgNVHSMEczBxgBSh6jvPH2KfGq3ekij4vF+Bqa/roqFWpFQwUjELMAkG
A1UEBhMCQ04xDTALBgNVBAoMBFRQNEExGTAXBgNVBAsMEFRQNEEgVGVsZXBvcnQg
Q0ExGTAXBgNVBAMMEFRQNEEgVGVsZXBvcnQgQ0GCAQAwDAYDVR0TAQH/BAIwADAO
BgNVHQ8BAf8EBAMCA4gwEwYDVR0lBAwwCgYIKwYBBQUHAwEwFAYDVR0RBA0wC4IJ
bG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4IBAQAfj/CpFDhv5CrnN2kxhtRAmesJ
q6/KxxkBaimjbS/BpfvqfC9RxGH7MIqGUkbC4/ADkEt2OmVU4+f2R3+rCl+x+r1t
9+3r/JSYYVBxFnF1GbDhiY9sKahgb4HoFjE2Fj8eVODcEzdApLr198p5IIIyfBys
WHV4CYFMvq5qCKbSR/JMfrm9GArAh1J+B+JMIfm8xwerFi0tfK2YT+N4QkvbidjG
sd+RKlR51GHo9m4iEQ7mDd9H8joVrVs2MVLGf2EoVU5y/Ahee4g7k3SKrn3GI/Ec
6BRCht+INCLI3bnC3MtJHJRzv5Vmu4pSh3cwnVHfe+VWLGvGlp2+KeC02xZ2
BgNVHQ8BAf8EBAMCA4gwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGgYDVR0RBBMwEYIJ
bG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCagioxwrTdc9N5IVSH
qbOXTGpUE4R7dvfCKatNJrGen7lAGdfqgomwM+fjRO5Jt0Kc15q8gxvQ3kePwaBY
11f1FJ8iDMqxX7Hmb3KT0FeWKmUPgH3YtlitSAD7DrqMxBh5sr/28zN8XIjWWhY1
huv7APQbuicxl/YZumPKa3r8FI1ca4pn4TKsm+YMN6Buy6k9CQV6POtNmawLNgNP
axErEeTzNsis1JalHFAdr6mPWY0xaZsdrMeJHMx/7lvM7Qo+odEyswguoCS8Bc1Y
6ZlEYZUev7lN0amqnoh25KrrGqpyHCXtXAEEwVyTmdpYDtqsetDYv7aCrfeITPBm
GAyD
-----END CERTIFICATE-----

View File

@ -102,7 +102,7 @@
"name": "FreeRDP",
"display": "FreeRDP",
"app": "",
"cmdline": "",
"cmdline": "/u:{user_name} /v:{host_ip} /port:{host_port}",
"desc": [
"建议使用homebrew安装freerdp安装后freerdp默认路径在/usr/local/Cellar/freerdp/x.y.z/bin/xfreerdp",
"首次安装freerdp后需要重新启动计算机"

View File

@ -1,9 +1,8 @@
{
"ssh": {
"selected": "putty",
"available": [
{
"name":"putty",
"available": [{
"name": "putty",
"display": "PuTTY内置",
"app": "{assist_tools_path}\\putty\\putty.exe",
"cmdline": "-ssh -pw **** -P {host_port} -l {user_name} {host_ip}"
@ -30,9 +29,8 @@
},
"scp": {
"selected": "winscp",
"available": [
{
"name":"winscp",
"available": [{
"name": "winscp",
"display": "WinSCP内置",
"app": "{assist_tools_path}\\winscp\\winscp.exe",
"cmdline": "/sessionname=\"TP#{real_ip}\" {user_name}:****@{host_ip}:{host_port}"
@ -47,9 +45,8 @@
},
"telnet": {
"selected": "putty",
"available": [
{
"name":"putty",
"available": [{
"name": "putty",
"display": "PuTTY内置",
"app": "{assist_tools_path}\\putty\\putty.exe",
"cmdline": "telnet://{user_name}@{host_ip}:{host_port}"
@ -68,15 +65,20 @@
}
]
},
"rdp" : {
"available" : [
{
"app" : "{assist_tools_path}\\tprdp\\tprdp-client.exe",
"cmdline" : "/v:{host_ip}:{host_port} /u:{user_name} /t:\"TP#{real_ip}\"",
"display" : "FreeRDP内置",
"name" : "freerdp"
}
],
"selected" : "freerdp"
}
}
"rdp": {
"available": [{
"app": "mstsc.exe",
"cmdline": "\"{tmp_rdp_file}\"",
"display": "微软RDP客户端系统自带",
"name": "mstsc"
},
{
"app": "{assist_tools_path}\\tprdp\\wfreerdp.exe",
"cmdline": "/v:{host_ip}:{host_port} /u:{user_name} /t:\"TP#{real_ip}\"",
"display": "FreeRDP内置",
"name": "freerdp"
}
],
"selected": "mstsc"
}
}

718
client/tp-player/bar.cpp Normal file
View File

@ -0,0 +1,718 @@
#include "bar.h"
#include <QPainter>
#include <QDebug>
#include "mainwindow.h"
#define FONT_SIZE_DEFAULT 12
#define FONT_SIZE_TIME 14
#define TEXT_COLOR QColor(255,255,255,153)
#define SPEED_BTN_WIDTH 42
#define CHKBOX_RIGHT_PADDING 6
#define PADDING_TIME_PROGRESS_BAR 10
#define SPEED_BTN_PADDING_TOP 8
#define SPEED_BTN_PADDING_RIGHT 8
#define SKIP_PADDING_TOP 10
#define BAR_ALIGN_TOP 10
#define BAR_PADDING_TOP 18
#define BAR_PADDING_LEFT 15
#define BAR_PADDING_RIGHT 15
typedef struct RES_MAP {
RES_ID id;
const char* name;
}RES_MAP;
static RES_MAP img_res[res__max] = {
{res_bg_left, "bg-left"},
{res_bg_mid, "bg-mid"},
{res_bg_right, "bg-right"},
{res_btn_normal_left, "btn-normal-left"},
{res_btn_normal_mid, "btn-normal-mid"},
{res_btn_normal_right, "btn-normal-right"},
{res_btn_sel_left, "btn-sel-left"},
{res_btn_sel_mid, "btn-sel-mid"},
{res_btn_sel_right, "btn-sel-right"},
{res_btn_hover_left, "btn-hover-left"},
{res_btn_hover_mid, "btn-hover-mid"},
{res_btn_hover_right, "btn-hover-right"},
{res_prgbarh_left, "prgbarh-left"},
{res_prgbarh_mid, "prgbarh-mid"},
{res_prgbar_mid, "prgbar-mid"},
{res_prgbar_right, "prgbar-right"},
{res_chkbox_normal, "chkbox-normal"},
{res_chkbox_hover, "chkbox-hover"},
{res_chkbox_sel_normal, "chkbox-sel-normal"},
{res_chkbox_sel_hover, "chkbox-sel-hover"},
};
typedef struct SPEED_MAP {
int id;
const char* title;
}SPEED_MAP;
static SPEED_MAP speed[speed_count] = {
{speed_1x, "1x"},
{speed_2x, "2x"},
{speed_4x, "4x"},
{speed_8x, "8x"},
};
static inline int min(int a, int b){
return a < b ? a : b;
}
static inline int max(int a, int b){
return a > b ? a : b;
}
Bar::Bar() {
m_img_ready = false;
m_width = 0;
m_height = 0;
m_str_total_time = "00:00";
m_str_passed_time = "00:00";
m_str_passed_time_last_draw = "--:--";
m_percent = 0;
m_percent_last_draw = -1;
m_play_hover = false;
m_playing = true; // false=paused
m_speed_selected = speed_1x;
m_speed_hover = speed_count; // speed_count=no-hover
m_skip_selected = false;
m_skip_hover = false;
m_progress_hover = false;
m_progress_pressed = false;
m_resume_ms = 0;
}
Bar::~Bar() {
}
bool Bar::init(MainWindow* owner) {
m_owner = owner;
// 加载所需的图像资源
int i = 0;
for(i = 0; i < res__max; ++i) {
QString name;
name.sprintf(":/tp-player/res/bar/%s.png", img_res[i].name);
if(!m_res[i].load(name))
return false;
}
// 无需合成的图像
if(!m_img_btn_play[play_running][widget_normal].load(":/tp-player/res/bar/play-normal.png")
|| !m_img_btn_play[play_running][widget_hover].load(":/tp-player/res/bar/play-hover.png")
|| !m_img_btn_play[play_paused][widget_normal].load(":/tp-player/res/bar/pause-normal.png")
|| !m_img_btn_play[play_paused][widget_hover].load(":/tp-player/res/bar/pause-hover.png")
|| !m_img_progress_pointer[widget_normal].load(":/tp-player/res/bar/prgpt-normal.png")
|| !m_img_progress_pointer[widget_hover].load(":/tp-player/res/bar/prgpt-hover.png")
) {
return false;
}
m_height = m_res[res_bg_left].height();
return true;
}
void Bar::start(uint32_t total_ms, int width) {
bool is_first_start = (m_width == 0);
m_width = width;
m_total_ms = total_ms;
_ms_to_str(total_ms, m_str_total_time);
// 首次播放时调整位置左右居中距窗口顶部10点处。
if(is_first_start) {
_init_imgages();
QRect rc = m_owner->rect();
m_rc = QRect(0, 0, m_width, m_height);
m_rc.moveTo((rc.width() - m_width)/2, BAR_ALIGN_TOP);
}
}
void Bar::end() {
if(m_played_ms != m_total_ms)
update_passed_time(m_total_ms);
m_playing = false;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
}
void Bar::_init_imgages() {
m_img_bg = QPixmap(m_width, m_height);
m_img_bg.fill(Qt::transparent);//用透明色填充
QPainter pp(&m_img_bg);
QFont font = pp.font();
// 合成背景图像
{
pp.drawPixmap(0, 0, m_res[res_bg_left].width(), m_res[res_bg_left].height(), m_res[res_bg_left]);
pp.drawPixmap(m_res[res_bg_left].width(), 0, m_width - m_res[res_bg_left].width() - m_res[res_bg_right].width(), m_height, m_res[res_bg_mid]);
pp.drawPixmap(m_width-m_res[res_bg_right].width(), 0, m_res[res_bg_right].width(), m_height, m_res[res_bg_right]);
}
{
m_rc_btn_play = QRect(BAR_PADDING_LEFT, (m_height - m_img_btn_play[play_running][widget_normal].height())/2 , m_img_btn_play[play_running][widget_normal].width(), m_img_btn_play[play_running][widget_normal].height());
}
// 合成速度按钮
{
int w = SPEED_BTN_WIDTH, h = m_res[res_btn_normal_left].height();
QRect rc(0, 0, w, h);
QPixmap btn[btnspd_state_count];
// 未选中状态
btn[btnspd_normal] = QPixmap(w, h);
btn[btnspd_normal].fill(Qt::transparent);//用透明色填充
QPainter pn(&btn[btnspd_normal]);
pn.drawPixmap(0, 0, m_res[res_btn_normal_left].width(), m_res[res_btn_normal_left].height(), m_res[res_btn_normal_left]);
pn.drawPixmap(m_res[res_btn_normal_left].width(), 0, w - m_res[res_btn_normal_left].width() - m_res[res_btn_normal_right].width(), h, m_res[res_btn_normal_mid]);
pn.drawPixmap(w-m_res[res_btn_normal_right].width(), 0, m_res[res_btn_normal_right].width(), h, m_res[res_btn_normal_right]);
// 选中状态
btn[btnspd_sel] = QPixmap(w, h);
btn[btnspd_sel].fill(Qt::transparent);//用透明色填充
QPainter ps(&btn[btnspd_sel]);
ps.drawPixmap(0, 0, m_res[res_btn_sel_left].width(), m_res[res_btn_sel_left].height(), m_res[res_btn_sel_left]);
ps.drawPixmap(m_res[res_btn_sel_left].width(), 0, w - m_res[res_btn_sel_left].width() - m_res[res_btn_sel_right].width(), h, m_res[res_btn_sel_mid]);
ps.drawPixmap(w-m_res[res_btn_sel_right].width(), 0, m_res[res_btn_sel_right].width(), h, m_res[res_btn_sel_right]);
// 鼠标滑过状态
btn[btnspd_hover] = QPixmap(w, h);
btn[btnspd_hover].fill(Qt::transparent);//用透明色填充
QPainter ph(&btn[btnspd_hover]);
ph.drawPixmap(0, 0, m_res[res_btn_hover_left].width(), m_res[res_btn_hover_left].height(), m_res[res_btn_hover_left]);
ph.drawPixmap(m_res[res_btn_hover_left].width(), 0, w - m_res[res_btn_hover_left].width() - m_res[res_btn_hover_right].width(), h, m_res[res_btn_hover_mid]);
ph.drawPixmap(w-m_res[res_btn_hover_right].width(), 0, m_res[res_btn_hover_right].width(), h, m_res[res_btn_hover_right]);
for(int i = 0; i < btnspd_state_count; ++i) {
for(int j = 0; j < speed_count; ++j) {
m_img_btn_speed[j][i] = QPixmap(w, h);
m_img_btn_speed[j][i].fill(Qt::transparent);
QPainter ps(&m_img_btn_speed[j][i]);
ps.setPen(TEXT_COLOR);
QFont font = ps.font();
font.setFamily("consolas");
font.setPixelSize(FONT_SIZE_DEFAULT);
ps.setFont(font);
ps.drawPixmap(0, 0, w, h, btn[i]);
ps.drawText(rc, Qt::AlignCenter, speed[j].title);
}
}
}
// 合成跳过无操作选项
{
// 计算显示跳过无操作选项字符串的宽高
font.setFamily("微软雅黑");
font.setBold(false);
font.setPixelSize(FONT_SIZE_DEFAULT);
pp.setFont(font);
QFontMetrics fm = pp.fontMetrics();
{
int h = fm.height();
if(h < m_res[res_chkbox_normal].height())
h = m_res[res_chkbox_normal].height();
m_rc_skip = QRect(0, 0, fm.width(LOCAL8BIT("无操作则跳过")) + CHKBOX_RIGHT_PADDING + m_res[res_chkbox_normal].width(), h);
}
int w = m_rc_skip.width();
int h = m_rc_skip.height();
int chkbox_top = (m_rc_skip.height() - m_res[res_chkbox_normal].height()) / 2;
int text_left = m_res[res_chkbox_normal].width() + CHKBOX_RIGHT_PADDING;
int text_top = (m_rc_skip.height() - fm.height()) / 2;
for(int i = 0; i < chkbox_state_count; ++i) {
for(int j = 0; j < widget_state_count; ++j) {
m_img_skip[i][j] = QPixmap(w,h);
m_img_skip[i][j].fill(Qt::transparent);
QPainter ps(&m_img_skip[i][j]);
ps.setPen(TEXT_COLOR);
QFont font = ps.font();
font.setFamily("微软雅黑");
font.setPixelSize(FONT_SIZE_DEFAULT);
ps.setFont(font);
QPixmap* img = nullptr;
if(i == chkbox_normal && j == widget_normal)
img = &m_res[res_chkbox_normal];
else if(i == chkbox_normal && j == widget_hover)
img = &m_res[res_chkbox_hover];
else if(i == chkbox_selected && j == widget_normal)
img = &m_res[res_chkbox_sel_normal];
else if(i == chkbox_selected && j == widget_hover)
img = &m_res[res_chkbox_sel_hover];
if(img == nullptr) {
qDebug("ERROR: can not load image for check-box.");
img = &m_res[res_chkbox_normal];
}
ps.drawPixmap(0, chkbox_top, img->width(), img->height(), *img);
ps.drawText(QRect(text_left, text_top, w-text_left, h-text_top), Qt::AlignCenter, LOCAL8BIT("无操作则跳过"));
}
}
}
// 定位进度条
{
// 计算显示时间所需的宽高
font.setFamily("consolas");
font.setBold(true);
font.setPixelSize(FONT_SIZE_TIME);
pp.setFont(font);
{
QFontMetrics fm = pp.fontMetrics();
m_rc_time_passed = QRect(0, 0, fm.width("00:00:00"), fm.height());
m_rc_time_total = m_rc_time_passed;
}
m_img_time_total = QPixmap(m_rc_time_total.width(), m_rc_time_total.height());
m_img_time_total.fill(Qt::transparent);
QPainter pp(&m_img_time_total);
pp.setPen(TEXT_COLOR);
QFont font = pp.font();
font.setFamily("consolas");
font.setBold(true);
font.setPixelSize(FONT_SIZE_TIME);
pp.setFont(font);
pp.drawText(m_rc_time_total, Qt::AlignLeft, m_str_total_time);
// 定位时间字符串的位置
m_rc_time_passed.moveTo(BAR_PADDING_LEFT+m_img_btn_play[play_running][widget_normal].width()+PADDING_TIME_PROGRESS_BAR, BAR_PADDING_TOP);
m_rc_time_total.moveTo(m_width - BAR_PADDING_RIGHT - m_rc_time_total.width(), BAR_PADDING_TOP);
int prog_width = m_rc_time_total.left() - PADDING_TIME_PROGRESS_BAR - PADDING_TIME_PROGRESS_BAR - m_rc_time_passed.right();
int prog_height = max(m_res[res_prgbarh_left].height(), m_img_progress_pointer->height());
m_rc_progress = QRect(0, 0, prog_width, prog_height);
m_rc_progress.moveTo(m_rc_time_passed.right() + PADDING_TIME_PROGRESS_BAR, m_rc_time_passed.top() + (m_rc_time_passed.height() - prog_height)/2);
}
// 定位速度按钮
{
int left = m_rc_time_passed.right() + PADDING_TIME_PROGRESS_BAR;
int top = m_rc_time_passed.bottom() + SPEED_BTN_PADDING_TOP;
for(int i = 0; i < speed_count; i++) {
m_rc_btn_speed[i] = QRect(left, top, m_img_btn_speed[i][widget_normal].width(), m_img_btn_speed[i][widget_normal].height());
left += m_img_btn_speed[i][widget_normal].width() + SPEED_BTN_PADDING_RIGHT;
}
}
// 定位跳过选项
{
int left = m_rc_time_total.left() - m_rc_skip.width() - PADDING_TIME_PROGRESS_BAR;
int top = m_rc_time_passed.bottom() + SKIP_PADDING_TOP;//m_rc_btn_speed[0].top() + (m_rc_btn_speed[0].height() - m_rc_skip.height())/2;
m_rc_skip.moveTo(left, top);
}
m_img_ready = true;
}
void Bar::_ms_to_str(uint32_t ms, QString& str) {
int h = 0, m = 0, s = 0;
s = ms / 1000;
if(ms % 1000 > 500)
s += 1;
h = s / 3600;
s = s % 3600;
m = s / 60;
s = s % 60;
if(h > 0)
str.sprintf("%02d:%02d:%02d", h, m, s);
else
str.sprintf("%02d:%02d", m, s);
}
void Bar::update_passed_time(uint32_t ms) {
QString str_passed;
_ms_to_str(ms, str_passed);
if(m_str_passed_time != str_passed)
{
m_str_passed_time = str_passed;
m_owner->update(m_rc.left()+m_rc_time_passed.left(), m_rc.top()+m_rc_time_passed.top(), m_rc_time_passed.width(), m_rc_time_passed.height());
}
int percent = 0;
if(ms >= m_total_ms) {
percent = 100;
m_played_ms = m_total_ms;
}
else {
m_played_ms = ms;
//percent = (int)(((double)m_played_ms / (double)m_total_ms) * 100);
percent = m_played_ms * 100 / m_total_ms;
}
if(percent != m_percent) {
m_percent = percent;
m_owner->update(m_rc.left()+m_rc_progress.left(), m_rc.top()+m_rc_progress.top(), m_rc_progress.width(), m_rc_progress.height());
}
}
void Bar::onMouseMove(int x, int y) {
// 映射鼠标坐标点到本浮动窗内部的相对位置
QPoint pt(x-m_rc.left(), y-m_rc.top());
if(m_progress_pressed) {
// 重新设置进度条指示器位置
int percent = 0;
if(pt.x() < m_rc_progress.left()) {
percent = 0;
m_resume_ms = 1;
}
else if(pt.x() > m_rc_progress.right()) {
percent = 100;
m_resume_ms = m_total_ms;
}
else {
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
m_resume_ms = m_total_ms * percent / 100;
}
update_passed_time(m_resume_ms);
return;
}
bool play_hover = m_rc_btn_play.contains(pt);
if(play_hover != m_play_hover) {
m_play_hover = play_hover;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
}
if(play_hover)
return;
int speed_hover = speed_count;
for(int i = 0; i < speed_count; ++i) {
if(m_rc_btn_speed[i].contains(pt)) {
speed_hover = i;
break;
}
}
if(m_speed_hover != speed_hover) {
if(m_speed_hover != speed_count) {
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
}
m_speed_hover = speed_hover;
if(m_speed_hover != speed_count) {
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
}
}
bool skip_hover = m_rc_skip.contains(pt);
if(skip_hover != m_skip_hover) {
m_skip_hover = skip_hover;
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
}
if(skip_hover)
return;
}
void Bar::onMousePress(int x, int y, Qt::MouseButton button) {
// 我们只关心左键按下
if(button != Qt::LeftButton)
return;
// 映射鼠标坐标点到本浮动窗内部的相对位置
QPoint pt(x-m_rc.left(), y-m_rc.top());
if(m_rc_btn_play.contains(pt)) {
if(m_playing)
m_owner->pause();
else
m_owner->resume(false, 0);
m_playing = !m_playing;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
return;
}
int speed_sel = speed_count;
for(int i = 0; i < speed_count; ++i) {
if(m_rc_btn_speed[i].contains(pt)) {
speed_sel = i;
break;
}
}
if(m_speed_selected != speed_sel && speed_sel != speed_count) {
int old_sel = m_speed_selected;
m_speed_selected = speed_sel;
m_owner->set_speed(get_speed());
m_owner->update(m_rc.left()+m_rc_btn_speed[old_sel].left(), m_rc.top()+m_rc_btn_speed[old_sel].top(), m_rc_btn_speed[old_sel].width(), m_rc_btn_speed[old_sel].height());
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
return;
}
if(m_rc_skip.contains(pt)) {
m_skip_selected = !m_skip_selected;
m_owner->set_skip(m_skip_selected);
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
return;
}
//
if(m_rc_progress.contains(pt)) {
m_progress_pressed = true;
// TODO: 暂停播放,按比例计算出点击位置占整个录像时长的百分比,定位到此位置准备播放。
// TODO: 如果点击的位置是进度条指示标志,则仅暂停播放
m_owner->pause();
m_playing = false;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
int percent = 0;
if(pt.x() < m_rc_progress.left()) {
percent = 0;
m_resume_ms = 0;
}
else if(pt.x() > m_rc_progress.right()) {
percent = 100;
m_resume_ms = m_total_ms;
}
else {
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
m_resume_ms = m_total_ms * percent / 100;
}
update_passed_time(m_resume_ms);
}
}
void Bar::onMouseRelease(int, int, Qt::MouseButton button) {
// 我们只关心左键释放
if(button != Qt::LeftButton)
return;
if(m_progress_pressed) {
m_progress_pressed = false;
qDebug("resume at %dms.", m_resume_ms);
m_owner->resume(true, m_resume_ms);
m_playing = true;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
}
}
int Bar::get_speed() {
switch (m_speed_selected) {
case speed_1x:
return 1;
case speed_2x:
return 2;
case speed_4x:
return 4;
case speed_8x:
return 8;
default:
return 1;
}
}
void Bar::draw(QPainter& painter, const QRect& rc_draw){
if(!m_width)
return;
if(!rc_draw.intersects(m_rc))
return;
// 绘制背景
{
QRect rc(m_rc);
//rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
int from_x = max(rc_draw.left(), m_rc.left()) - m_rc.left();
int from_y = max(rc_draw.top(), m_rc.top()) - m_rc.top();
int w = min(m_rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(m_rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = m_rc.left() + from_x;
int to_y = m_rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_bg, from_x, from_y, w, h);
}
// 绘制播放按钮
{
QRect rc(m_rc_btn_play);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
if(m_playing){
if(m_play_hover)
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_paused][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_paused][widget_normal], from_x, from_y, w, h);
} else {
if(m_play_hover)
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_running][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_running][widget_normal], from_x, from_y, w, h);
}
}
}
// 绘制已播放时间
{
QRect rc(m_rc_time_passed);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
if(m_str_passed_time != m_str_passed_time_last_draw) {
m_img_time_passed = QPixmap(m_rc_time_passed.width(), m_rc_time_passed.height());
m_img_time_passed.fill(Qt::transparent);
QPainter pp(&m_img_time_passed);
pp.setPen(TEXT_COLOR);
QFont font = pp.font();
font.setFamily("consolas");
font.setBold(true);
font.setPixelSize(FONT_SIZE_TIME);
pp.setFont(font);
pp.drawText(QRect(0,0,m_rc_time_passed.width(), m_rc_time_passed.height()), Qt::AlignRight, m_str_passed_time);
m_str_passed_time_last_draw = m_str_passed_time;
}
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_time_passed, from_x, from_y, w, h);
}
}
// 绘制总时间
{
QRect rc(m_rc_time_total);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_time_total, from_x, from_y, w, h);
}
}
// 绘制进度条
{
QRect rc(m_rc_progress);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
if(m_percent_last_draw != m_percent) {
m_img_progress = QPixmap(m_rc_progress.width(), m_rc_progress.height());
m_img_progress.fill(Qt::transparent);
QPainter pp(&m_img_progress);
// 进度条
int top = (rc.height() - m_res[res_prgbarh_left].height())/2;
int passed_width = rc.width() * m_percent / 100; // 已经播放的进度条宽度
int remain_width = rc.width() - passed_width; // 剩下未播放的进度条宽度
if(passed_width >= m_res[res_prgbarh_left].width())
pp.drawPixmap(0, top , m_res[res_prgbarh_left].width(), m_res[res_prgbarh_left].height(), m_res[res_prgbarh_left]);
if(passed_width > 0) {
//pp.drawPixmap(m_res[res_pbh_left].width(), top, passed_width - m_res[res_pbh_left].width(), m_res[res_pbh_mid].height(), m_res[res_pbh_mid]);
if(remain_width > m_res[res_prgbar_right].width())
pp.drawPixmap(m_res[res_prgbarh_left].width(), top, passed_width - m_res[res_prgbarh_left].width(), m_res[res_prgbarh_mid].height(), m_res[res_prgbarh_mid]);
else
pp.drawPixmap(m_res[res_prgbarh_left].width(), top, passed_width - m_res[res_prgbarh_left].width() - m_res[res_prgbar_right].width(), m_res[res_prgbarh_mid].height(), m_res[res_prgbarh_mid]);
}
if(remain_width > 0)
pp.drawPixmap(passed_width, top, remain_width - m_res[res_prgbar_right].width(), m_res[res_prgbar_mid].height(), m_res[res_prgbar_mid]);
if(remain_width >= m_res[res_prgbar_right].width())
pp.drawPixmap(rc.width() - m_res[res_prgbar_right].width(), top , m_res[res_prgbar_right].width(), m_res[res_prgbar_right].height(), m_res[res_prgbar_right]);
// 进度位置指示
int left = passed_width - m_img_progress_pointer->width() / 2;
if(left < 0)
left = 0;
if(left + m_img_progress_pointer->width() > rc.width())
left = rc.width() - m_img_progress_pointer->width();
top = (rc.height() - m_img_progress_pointer[widget_normal].height())/2;
pp.drawPixmap(left, top , m_img_progress_pointer[widget_normal].width(), m_img_progress_pointer[widget_normal].height(), m_img_progress_pointer[widget_normal]);
m_percent_last_draw = m_percent;
}
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_progress, from_x, from_y, w, h);
}
}
// 绘制速度按钮
{
for(int i = 0; i < speed_count; i++) {
QRect rc(m_rc_btn_speed[i]);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
if(m_speed_hover == i)
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_hover], from_x, from_y, w, h);
else if(m_speed_selected == i)
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_sel], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_normal], from_x, from_y, w, h);
}
}
}
// 绘制跳过选项
{
QRect rc(m_rc_skip);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
// painter.fillRect(rc, QColor(255, 255, 255));
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
//qDebug("skip (%d,%d), (%d,%d)/(%d,%d)", to_x, to_y, from_x, from_y, w, h);
if(m_skip_selected) {
if(m_skip_hover)
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_selected][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_selected][widget_normal], from_x, from_y, w, h);
}
else {
if(m_skip_hover)
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_normal][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_normal][widget_normal], from_x, from_y, w, h);
}
}
}
}

149
client/tp-player/bar.h Normal file
View File

@ -0,0 +1,149 @@
#ifndef BAR_H
#define BAR_H
#include <QPainter>
#include <QPixmap>
#include <QWidget>
typedef enum {
res_bg_left = 0, // 背景
res_bg_mid,
res_bg_right,
res_btn_normal_left, // 按钮(速度选择),普通状态
res_btn_normal_mid,
res_btn_normal_right,
res_btn_sel_left, // 按钮(速度选择),已选中
res_btn_sel_mid,
res_btn_sel_right,
res_btn_hover_left, // 按钮(速度选择),鼠标滑过
res_btn_hover_mid,
res_btn_hover_right,
res_prgbarh_left, // 进度条(已经过)左侧
res_prgbarh_mid, // 进度条(已经过)中间,拉伸填充
res_prgbar_mid, // 进度条(未到达)中间,拉伸填充
res_prgbar_right, // 进度条(未到达)右侧
res_chkbox_normal, // 复选框
res_chkbox_hover,
res_chkbox_sel_normal,
res_chkbox_sel_hover,
res__max
}RES_ID;
//typedef enum {
// widget_normal = 0,
// widget_hover,
// widget__max
//}WIDGET_STAT;
#define widget_normal 0
#define widget_hover 1
#define widget_state_count 2
//typedef enum {
// play_running = 0,
// play_paused,
// play__max
//}PLAY_STAT;
#define play_running 0
#define play_paused 1
#define play_state_count 2
#define speed_1x 0
#define speed_2x 1
#define speed_4x 2
#define speed_8x 3
#define speed_count 4
#define btnspd_normal 0
#define btnspd_sel 1
#define btnspd_hover 2
#define btnspd_state_count 3
#define chkbox_normal 0
#define chkbox_selected 1
#define chkbox_state_count 2
class MainWindow;
class Bar {
public:
Bar();
~Bar();
bool init(MainWindow* owner);
void start(uint32_t total_ms, int width);
void end();
void draw(QPainter& painter, const QRect& rc);
void update_passed_time(uint32_t ms);
int get_speed();
QRect rc(){return m_rc;}
void onMouseMove(int x, int y);
void onMousePress(int x, int y, Qt::MouseButton button);
void onMouseRelease(int x, int y, Qt::MouseButton button);
private:
void _init_imgages();
void _ms_to_str(uint32_t ms, QString& str);
private:
MainWindow* m_owner;
uint32_t m_total_ms; // 录像的总时长
uint32_t m_played_ms; // 已经播放了的时长
int m_percent; // 已经播放了的百分比0~100
int m_percent_last_draw;
QString m_str_total_time;
QString m_str_passed_time;
QString m_str_passed_time_last_draw;
bool m_img_ready;
// 从资源中加载的原始图像
QPixmap m_res[res__max];
QPixmap m_img_progress_pointer[widget_state_count];
int m_width;
int m_height;
// 此浮动窗相对于父窗口的坐标和大小
QRect m_rc;
// 尺寸和定位(此浮动窗内部的相对坐标)
QRect m_rc_btn_play;
QRect m_rc_btn_speed[speed_count];
QRect m_rc_time_passed;
QRect m_rc_time_total;
QRect m_rc_progress;
QRect m_rc_skip;
// 画布,最终输出的图像
//QPixmap m_canvas;
// 合成的图像
QPixmap m_img_bg;
QPixmap m_img_btn_play[play_state_count][widget_state_count];
QPixmap m_img_btn_speed[speed_count][btnspd_state_count];
QPixmap m_img_progress;
QPixmap m_img_skip[chkbox_state_count][widget_state_count];
QPixmap m_img_time_passed;
QPixmap m_img_time_total;
// 各种状态
bool m_playing; // 0=play, 2=pause
bool m_play_hover;
int m_speed_selected;
int m_speed_hover; // speed__max=no-hover
bool m_skip_selected;
bool m_skip_hover;
bool m_progress_hover;
bool m_progress_pressed;
uint32_t m_resume_ms; // after drag progress-pointer, resume play from here.
};
#endif // BAR_H

View File

@ -0,0 +1,120 @@
#include "downloader.h"
#include "record_format.h"
#include <QEventLoop>
#include <QNetworkReply>
#include <qelapsedtimer.h>
Downloader::Downloader() : QObject () {
m_data = nullptr;
m_reply = nullptr;
m_result = false;
}
Downloader::~Downloader() {
}
bool Downloader::request(const QString& url, const QString& sid, const QString& filename) {
return _request(url, sid, filename, nullptr);
}
bool Downloader::request(const QString& url, const QString& sid, QByteArray* data) {
QString fname;
return _request(url, sid, fname, data);
}
bool Downloader::_request(const QString& url, const QString& sid, const QString& filename, QByteArray* data) {
if(filename.isEmpty() && data == nullptr)
return false;
if(!filename.isEmpty() && data != nullptr)
return false;
m_data = data;
if(!filename.isEmpty()) {
m_file.setFileName(filename);
if(!m_file.open(QIODevice::WriteOnly | QFile::Truncate)){
qDebug("open file for write failed.");
return false;
}
}
QString cookie = QString("_sid=%1\r\n").arg(sid);
QNetworkRequest req;
req.setUrl(QUrl(url));
req.setRawHeader("Cookie", cookie.toLatin1());
QNetworkAccessManager* nam = new QNetworkAccessManager();
QEventLoop eloop;
m_reply = nam->get(req);
connect(m_reply, &QNetworkReply::finished, &eloop, &QEventLoop::quit);
connect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
connect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
// qDebug("before eventLoop.exec(%p)", &eloop);
eloop.exec();
// qDebug("after eventLoop.exec()");
disconnect(m_reply, &QNetworkReply::finished, &eloop, &QEventLoop::quit);
disconnect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
disconnect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
delete m_reply;
m_reply = nullptr;
delete nam;
qDebug("Downloader::_request() end.");
return m_result;
}
void Downloader::_on_data_ready() {
// qDebug("Downloader::_on_data_ready(%p).", this);
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
if(m_data != nullptr) {
m_data->push_back(reply->readAll());
}
else {
m_file.write(reply->readAll());
}
}
void Downloader::abort() {
if(m_reply) {
qDebug("Downloader::abort().");
m_reply->abort();
}
}
void Downloader::_on_finished() {
// qDebug("Downloader::_on_finished(%p).", this);
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (reply->error() != QNetworkReply::NoError) {
// reply->abort() got "Operation canceled"
//QString strError = reply->errorString();
qDebug() << "ERROR:" << reply->errorString();
if(m_data == nullptr) {
m_file.flush();
m_file.close();
}
m_result = false;
return;
}
if(m_data != nullptr) {
m_data->push_back(reply->readAll());
}
else {
m_file.write(reply->readAll());
m_file.flush();
m_file.close();
}
reply->deleteLater();
m_result = true;
}

View File

@ -0,0 +1,40 @@
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QFile>
#include <QNetworkAccessManager>
class Downloader : public QObject {
Q_OBJECT
public:
// 从url下载数据写入到filename文件中或放入data中。
Downloader();
~Downloader();
bool request(const QString& url, const QString& sid, const QString& filename);
bool request(const QString& url, const QString& sid, QByteArray* data);
void abort();
private:
bool _request(const QString& url, const QString& sid, const QString& filename, QByteArray* data);
private slots:
void _on_data_ready(); // 有数据可读了,读取并写入文件
void _on_finished(); // 下载结束了
private:
QFile m_file;
QByteArray* m_data;
bool m_result;
QNetworkReply* m_reply;
};
typedef struct DownloadParam {
QString url;
QString sid;
QString fname;
}DownloadParam;
#endif

98
client/tp-player/main.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "mainwindow.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QDebug>
#include <QMessageBox>
#include <QTextCodec>
// 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名
// 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。
// 命令行参数格式:
// ## 本地文件或目录
// tp-player.exe path/of/tp-rdp.tpr 一个 .tpr 文件的文件名
// tp-player.exe path/contains/tp-rdp.tpr 包含 .tpr 文件的路径
//
// ## 从TP服务器上下载
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
// 按 “/” 进行分割后去掉最后两个项剩下部分是TP服务器的WEB地址用于合成后续的文件下载URL。
// 根据下载的.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`表示获取文件大小(返回一个数字字符串,就是指定的文件大小)
// - 'type'可以是`rdp`或`ssh`,目前仅用了`rdp`
// - 'rid'是录像会话编号(在服务端,一个会话的录像文件均放在录像会话编号命名的目录下)
// - 'f' 是文件名,即会话编号目录下的指定文件,例如 'tp-rdp.tpr'
// - 读取文件内容: http://127.0.0.1:7190/audit/get-file?act=read&type=rdp&rid=yyyyy&f=file-name&offset=1234&length=1024
// - 'act'为`read`表示获取文件内容
// - 'offset'表示要读取的偏移,如果未指定,则表示从文件起始处开始读取,即默认为 offset=0
// - 'length'表示要读取的大小,如果未指定,表示读取整个文件,即默认为 length=-1服务端对length=-1做完全读取处理
// - 搭配使用 offst 和 length 可以做到分块下载、断点续传。
void show_usage(QCommandLineParser& 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 to download teleport record file."
+ "</pre></body></html>");
}
int main(int argc, char *argv[])
{
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
//#endif
QApplication a(argc, argv);
//#ifdef __APPLE__
// QString data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
// data_path_base += "/tp-testdata/";
//#else
// QString data_path_base = QCoreApplication::applicationDirPath() + "/record";
//#endif
// qDebug("data-path-base: %s", data_path_base.toStdString().c_str());
// return 0;
QGuiApplication::setApplicationDisplayName(LOCAL8BIT("[Teleport播放器]"));
QCommandLineParser parser;
const QCommandLineOption opt_help = parser.addHelpOption();
parser.addPositionalArgument("RESOURCE", "teleport record resource to be play.");
if(!parser.parse(QCoreApplication::arguments())) {
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
//"<html><head/><body><h2 style=\"color:#ff0000;\">" + parser.errorText() + "</h2><pre>"
"<html><head/><body><h2>" + parser.errorText() + "</h2><pre>"
+ parser.helpText() + "</pre></body></html>");
return 1;
}
if(parser.isSet(opt_help)) {
show_usage(parser);
return 2;
}
const QStringList args = parser.positionalArguments();
if(0 == args.size()) {
show_usage(parser);
return 2;
}
QString resource = args.at(0);
qDebug() << resource;
MainWindow w;
w.set_resource(resource);
w.show();
return a.exec();
}

View File

@ -0,0 +1,433 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMatrix>
#include <QDebug>
#include <QPainter>
#include <QDesktopWidget>
#include <QPaintEvent>
#include <QMessageBox>
#include <QDialogButtonBox>
static inline int min(int a, int b){
return a < b ? a : b;
}
static inline int max(int a, int b){
return a > b ? a : b;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
m_show_default = true;
m_bar_shown = false;
m_bar_fade_in = false;
m_bar_fading = false;
m_bar_opacity = 1.0;
m_show_message = false;
memset(&m_pt, 0, sizeof(TS_RECORD_RDP_POINTER));
m_thr_play = nullptr;
m_play_state = PLAY_STATE_UNKNOWN;
m_thr_data = nullptr;
m_disable_draw = false;
ui->setupUi(this);
ui->centralWidget->setMouseTracking(true);
setMouseTracking(true);
// frame-less window.
//#ifdef __APPLE__
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | Qt::Window);
// OSXCode::fixWin(winId());
//#else
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | windowFlags());
//#endif //__APPLE__
m_pt_normal.load(":/tp-player/res/cursor.png");
m_default_bg.load(":/tp-player/res/bg.png");
m_canvas = QPixmap(m_default_bg.width(), m_default_bg.height());
QPainter pp(&m_canvas);
pp.drawPixmap(0, 0, m_default_bg, 0, 0, m_default_bg.width(), m_default_bg.height());
setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint); // 禁止最大化按钮
setFixedSize(m_default_bg.width(), m_default_bg.height()); // 禁止拖动窗口大小
if(!m_bar.init(this)) {
qDebug("bar init failed.");
return;
}
connect(&m_timer_first_run, SIGNAL(timeout()), this, SLOT(_do_first_run()));
connect(&m_timer_bar_fade, SIGNAL(timeout()), this, SLOT(_do_bar_fade()));
connect(&m_timer_bar_delay_hide, SIGNAL(timeout()), this, SLOT(_do_bar_delay_hide()));
m_timer_first_run.setSingleShot(true);
m_timer_first_run.start(500);
}
MainWindow::~MainWindow()
{
if(m_thr_play) {
m_thr_play->stop();
disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_play;
m_thr_play = nullptr;
}
if(m_thr_data) {
m_thr_data->stop();
disconnect(m_thr_data, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_data;
m_thr_data = nullptr;
}
delete ui;
}
void MainWindow::set_resource(const QString &res) {
m_res = res;
}
void MainWindow::_do_first_run() {
m_thr_data = new ThrData(this, m_res);
connect(m_thr_data, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
m_thr_data->start(QThread::TimeCriticalPriority);
m_thr_play = new ThrPlay(this);
connect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
m_thr_play->speed(m_bar.get_speed());
m_thr_play->start();
}
void MainWindow::set_speed(int s) {
if(m_thr_play)
m_thr_play->speed(s);
}
void MainWindow::set_skip(bool s) {
if(m_thr_play)
m_thr_play->skip(s);
}
void MainWindow::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
if(m_show_default) {
painter.drawPixmap(e->rect(), m_default_bg, e->rect());
}
else {
painter.drawPixmap(e->rect(), m_canvas, e->rect());
QRect rcpt(m_pt_normal.rect());
rcpt.moveTo(m_pt.x - m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2);
if(e->rect().intersects(rcpt)) {
painter.drawPixmap(m_pt.x-m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2, m_pt_normal);
}
// 绘制浮动控制窗
if(m_bar_fading) {
painter.setOpacity(m_bar_opacity);
m_bar.draw(painter, e->rect());
}
else if(m_bar_shown) {
m_bar.draw(painter, e->rect());
}
}
if(m_show_message) {
QRect rc_draw = e->rect();
QRect rc(m_rc_message);
if(e->rect().intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h);
}
}
}
void MainWindow::pause() {
if(m_play_state != PLAY_STATE_RUNNING)
return;
m_thr_play->pause();
m_play_state = PLAY_STATE_PAUSE;
}
void MainWindow::resume(bool relocate, uint32_t ms) {
if(m_play_state == PLAY_STATE_PAUSE) {
if(relocate)
m_thr_data->restart(ms);
m_thr_play->resume(relocate, ms);
}
else if(m_play_state == PLAY_STATE_STOP) {
m_thr_data->restart(ms);
m_thr_play->resume(true, ms);
}
m_play_state = PLAY_STATE_RUNNING;
}
void MainWindow::_do_update_data(UpdateData* dat) {
if(!dat)
return;
UpdateDataHelper data_helper(dat);
if(dat->data_type() == TYPE_POINTER) {
TS_RECORD_RDP_POINTER pt;
memcpy(&pt, &m_pt, sizeof(TS_RECORD_RDP_POINTER));
// 更新虚拟鼠标信息,这样下一次绘制界面时就会在新的位置绘制出虚拟鼠标
memcpy(&m_pt, dat->get_pointer(), sizeof(TS_RECORD_RDP_POINTER));
update(m_pt.x - m_pt_normal.width()/2, m_pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height());
update(pt.x - m_pt_normal.width()/2, pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height());
return;
}
else if(dat->data_type() == TYPE_IMAGE) {
const UpdateImages uimgs = dat->get_images();
if(uimgs.size() == 0)
return;
if(uimgs.size() > 1 && !m_disable_draw) {
// 禁止界面更新
setUpdatesEnabled(false);
}
QPainter pp(&m_canvas);
for(int i = 0; i < uimgs.size(); ++i) {
pp.drawImage(uimgs[i].x, uimgs[i].y, *(uimgs[i].img), 0, 0, uimgs[i].w, uimgs[i].h, Qt::AutoColor);
if(!m_disable_draw)
update(uimgs[i].x, uimgs[i].y, uimgs[i].w, uimgs[i].h);
}
if(uimgs.size() > 1 && !m_disable_draw) {
// 允许界面更新
setUpdatesEnabled(true);
}
return;
}
else if(dat->data_type() == TYPE_PLAYED_MS) {
m_bar.update_passed_time(dat->played_ms());
return;
}
else if(dat->data_type() == TYPE_DISABLE_DRAW) {
// 禁止界面更新
m_disable_draw = true;
setUpdatesEnabled(false);
return;
}
else if(dat->data_type() == TYPE_ENABLE_DRAW) {
// 允许界面更新
m_disable_draw = false;
setUpdatesEnabled(true);
update();
return;
}
else if(dat->data_type() == TYPE_MESSAGE) {
if(dat->message().isEmpty()) {
m_show_message = false;
return;
}
m_show_message = true;
qDebug("1message, w=%d, h=%d", m_canvas.width(), m_canvas.height());
QPainter pp(&m_canvas);
QRect rcWin(0, 0, m_canvas.width(), m_canvas.height());
pp.drawText(rcWin, Qt::AlignLeft|Qt::TextDontPrint, dat->message(), &m_rc_message);
qDebug("2message, w=%d, h=%d", m_rc_message.width(), m_rc_message.height());
m_rc_message.setWidth(m_rc_message.width()+60);
m_rc_message.setHeight(m_rc_message.height()+60);
m_img_message = QPixmap(m_rc_message.width(), m_rc_message.height());
m_img_message.fill(Qt::transparent);
QPainter pm(&m_img_message);
pm.setPen(QColor(255,255,255,153));
pm.fillRect(m_rc_message, QColor(0,0,0,190));
QRect rcRect(m_rc_message);
rcRect.setWidth(rcRect.width()-1);
rcRect.setHeight(rcRect.height()-1);
pm.drawRect(rcRect);
QRect rcText(m_rc_message);
rcText.setLeft(30);
rcText.setTop(30);
pm.drawText(rcText, Qt::AlignLeft, dat->message());
m_rc_message.moveTo(
(m_canvas.width() - m_rc_message.width())/2,
(m_canvas.height() - m_rc_message.height())/3
);
update(m_rc_message.x(), m_rc_message.y(), m_rc_message.width(), m_rc_message.height());
return;
}
else if(dat->data_type() == TYPE_ERROR) {
QMessageBox::critical(this, QGuiApplication::applicationDisplayName(), dat->message());
QApplication::instance()->exit(0);
return;
}
// 这是播放开始时收到的第一个数据包
else if(dat->data_type() == TYPE_HEADER_INFO) {
TS_RECORD_HEADER* hdr = dat->get_header();
if(hdr == nullptr)
return;
memcpy(&m_rec_hdr, hdr, sizeof(TS_RECORD_HEADER));
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")";
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以
qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height());
move(10, (desktop->height() - m_rec_hdr.basic.height)/2);
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
m_canvas.fill(QColor(38, 73, 111));
m_show_default = false;
repaint();
m_bar.start(m_rec_hdr.info.time_ms, 640);
m_bar_shown = true;
m_play_state = PLAY_STATE_RUNNING;
update(m_bar.rc());
m_bar_fade_in = false;
m_bar_fading = true;
m_timer_bar_delay_hide.start(2000);
QString title;
if (m_rec_hdr.basic.conn_port == 3389) {
title = QString(LOCAL8BIT("用户 %1 访问 %2 的 %3 账号").arg(m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, m_rec_hdr.basic.acc_username));
}
else {
QString _port;
_port.sprintf("%d", m_rec_hdr.basic.conn_port);
title = QString(LOCAL8BIT("用户 %1 访问 %2:%3 的 %4 账号").arg(m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, _port, m_rec_hdr.basic.acc_username));
}
setWindowTitle(title);
return;
}
else if(dat->data_type() == TYPE_END) {
m_bar.end();
m_play_state = PLAY_STATE_STOP;
return;
}
}
void MainWindow::_do_bar_delay_hide() {
m_bar_fading = true;
m_timer_bar_delay_hide.stop();
m_timer_bar_fade.stop();
m_timer_bar_fade.start(50);
}
void MainWindow::_do_bar_fade() {
if(m_bar_fade_in) {
if(m_bar_opacity < 1.0)
m_bar_opacity += 0.3;
if(m_bar_opacity >= 1.0) {
m_bar_opacity = 1.0;
m_bar_shown = true;
m_bar_fading = false;
m_timer_bar_fade.stop();
}
}
else {
if(m_bar_opacity > 0.0)
m_bar_opacity -= 0.2;
if(m_bar_opacity <= 0.0) {
m_bar_opacity = 0.0;
m_bar_shown = false;
m_bar_fading = false;
m_timer_bar_fade.stop();
}
}
update(m_bar.rc());
}
void MainWindow::mouseMoveEvent(QMouseEvent *e) {
if(!m_show_default) {
QRect rc = m_bar.rc();
if(e->y() > rc.top() - 20 && e->y() < rc.bottom() + 20) {
if((!m_bar_shown && !m_bar_fading) || (m_bar_fading && !m_bar_fade_in)) {
m_bar_fade_in = true;
m_bar_fading = true;
m_timer_bar_delay_hide.stop();
m_timer_bar_fade.stop();
m_timer_bar_fade.start(50);
}
if(rc.contains(e->pos()))
m_bar.onMouseMove(e->x(), e->y());
}
else {
if((m_bar_shown && !m_bar_fading) || (m_bar_fading && m_bar_fade_in)) {
m_bar_fade_in = false;
m_bar_fading = true;
m_timer_bar_fade.stop();
m_timer_bar_delay_hide.stop();
if(m_bar_opacity != 1.0)
m_timer_bar_fade.start(50);
else
m_timer_bar_delay_hide.start(1000);
}
}
}
}
void MainWindow::mousePressEvent(QMouseEvent *e) {
if(!m_show_default) {
QRect rc = m_bar.rc();
if(rc.contains(e->pos())) {
m_bar.onMousePress(e->x(), e->y(), e->button());
}
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *e) {
m_bar.onMouseRelease(e->x(), e->y(), e->button());
}

View File

@ -0,0 +1,90 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMessageBox>
#include <QTimer>
#include "bar.h"
#include "thr_play.h"
#include "thr_data.h"
#include "update_data.h"
#include "record_format.h"
#include "util.h"
#include "downloader.h"
#define PLAY_STATE_UNKNOWN 0
#define PLAY_STATE_RUNNING 1
#define PLAY_STATE_PAUSE 2
#define PLAY_STATE_STOP 3
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void set_resource(const QString& res);
void pause();
void resume(bool relocate, uint32_t ms);
void restart();
void set_speed(int s);
void set_skip(bool s);
// TODO: 将thr_data移动到thr_play线程由play线程进行管理
ThrData* get_thr_data() {return m_thr_data;}
private:
void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
private slots:
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)
void _do_update_data(UpdateData*);
void _do_bar_fade();
void _do_bar_delay_hide();
private:
Ui::MainWindow *ui;
bool m_show_default;
bool m_bar_shown;
QPixmap m_default_bg;
QString m_res;
ThrPlay* m_thr_play;
ThrData* m_thr_data;
QPixmap m_canvas;
Bar m_bar;
TS_RECORD_HEADER m_rec_hdr;
QPixmap m_pt_normal;
TS_RECORD_RDP_POINTER m_pt;
QTimer m_timer_first_run;
QTimer m_timer_bar_fade;
QTimer m_timer_bar_delay_hide;
bool m_bar_fade_in;
bool m_bar_fading;
qreal m_bar_opacity;
int m_play_state;
bool m_show_message;
QPixmap m_img_message;
QRect m_rc_message;
bool m_disable_draw;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>360</height>
</rect>
</property>
<property name="font">
<font>
<family>微软雅黑 Light</family>
</font>
</property>
<property name="windowTitle">
<string>Teleport Replayer</string>
</property>
<widget class="QWidget" name="centralWidget"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,106 @@
#ifndef RECORD_FORMAT_H
#define RECORD_FORMAT_H
#include <Qt>
#define TS_TPPR_TYPE_UNKNOWN 0x0000
#define TS_TPPR_TYPE_SSH 0x0001
#define TS_TPPR_TYPE_RDP 0x0101
#define TS_RECORD_TYPE_RDP_POINTER 0x12 // 鼠标坐标位置改变,用于绘制虚拟鼠标
#define TS_RECORD_TYPE_RDP_IMAGE 0x13 // 服务端返回的图像,用于展示
#define TS_RECORD_TYPE_RDP_KEYFRAME 0x14 //
#define TS_RDP_BTN_FREE 0
#define TS_RDP_BTN_PRESSED 1
#define TS_RDP_IMG_RAW 0 // 未压缩原始数据根据bitsPerPixel多个字节对应一个点的颜色
#define TS_RDP_IMG_BMP 1 // 压缩的BMP数据
#define TS_RDP_IMG_ALT 2
#pragma pack(push,1)
// 录像文件头(随着录像数据写入,会改变的部分)
typedef struct TS_RECORD_HEADER_INFO {
uint32_t magic; // "TPPR" 标志 TelePort Protocol Record
uint16_t ver; // 录像文件版本从3.5.0开始为4
uint16_t type; //
// uint32_t packages; // 总包数
uint32_t time_ms; // 总耗时(毫秒)
uint32_t dat_file_count; // 数据文件数量
}TS_RECORD_HEADER_INFO;
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
// 录像文件头(固定不变部分)
typedef struct TS_RECORD_HEADER_BASIC {
uint16_t protocol_type; // 协议1=RDP, 2=SSH, 3=Telnet
uint16_t protocol_sub_type; // 子协议100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
uint64_t timestamp; // 本次录像的起始时间UTC时间戳
uint16_t width; // 初始屏幕尺寸:宽
uint16_t height; // 初始屏幕尺寸:高
char user_username[64]; // teleport账号
char acc_username[64]; // 远程主机用户名
char host_ip[40]; // 远程主机IP
char conn_ip[40]; // 远程主机IP
uint16_t conn_port; // 远程主机端口
char client_ip[40]; // 客户端IP
// // RDP专有
// uint8_t rdp_security; // 0 = RDP, 1 = TLS
}TS_RECORD_HEADER_BASIC;
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
typedef struct TS_RECORD_HEADER {
TS_RECORD_HEADER_INFO info;
uint8_t _reserve1[64 - ts_record_header_info_size];
TS_RECORD_HEADER_BASIC basic;
uint8_t _reserve2[512 - 64 - ts_record_header_basic_size];
}TS_RECORD_HEADER;
// header部分header-info + header-basic = 512B
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
// 一个数据包的头
typedef struct TS_RECORD_PKG {
uint8_t type; // 包的数据类型
uint32_t size; // 这个包的总大小(不含包头)
uint32_t time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
uint8_t _reserve[3]; // 保留
}TS_RECORD_PKG;
typedef struct TS_RECORD_RDP_POINTER {
uint16_t x;
uint16_t y;
uint8_t button;
uint8_t pressed;
}TS_RECORD_RDP_POINTER;
// RDP图像更新
typedef struct TS_RECORD_RDP_IMAGE_INFO {
uint16_t destLeft;
uint16_t destTop;
uint16_t destRight;
uint16_t destBottom;
uint16_t width;
uint16_t height;
uint16_t bitsPerPixel;
uint8_t format;
uint8_t _reserved;
uint32_t dat_len;
uint32_t zip_len;
}TS_RECORD_RDP_IMAGE_INFO;
// 关键帧索引
typedef struct TS_RECORD_RDP_KEYFRAME_INFO {
uint32_t time_ms; // 此关键帧的时间点
uint32_t file_index; // 此关键帧图像数据位于哪一个数据文件中
uint32_t offset; // 此关键帧图像数据在数据文件中的偏移
}TS_RECORD_RDP_KEYFRAME_INFO;
#pragma pack(pop)
#endif // RECORD_FORMAT_H

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
client/tp-player/res/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

974
client/tp-player/rle.c Normal file
View File

@ -0,0 +1,974 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Bitmap decompression routines
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* three seperate function for speed when decompressing the bitmaps
when modifing one function make the change in the others
jay.sorg@gmail.com */
/* indent is confused by this file */
/* *INDENT-OFF* */
#include <stdlib.h>
#include "rle.h"
/* Specific rename for RDPY integration */
#define unimpl(str, code)
//#define RD_BOOL int
//#define False 0
//#define True 1
/* end specific rename */
#define CVAL(p) (*(p++))
//#ifdef NEED_ALIGN
//#ifdef L_ENDIAN
#define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; }
//#else
//#define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); }
//#endif /* L_ENDIAN */
//#else
//#define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; }
//#endif /* NEED_ALIGN */
#define UNROLL8(exp) { exp exp exp exp exp exp exp exp }
#define REPEAT(statement) \
{ \
while((count & ~0x7) && ((x+8) < width)) \
UNROLL8( statement; count--; x++; ); \
\
while((count > 0) && (x < width)) \
{ \
statement; \
count--; \
x++; \
} \
}
#define MASK_UPDATE() \
{ \
mixmask <<= 1; \
if (mixmask == 0) \
{ \
mask = fom_mask ? fom_mask : CVAL(input); \
mixmask = 1; \
} \
}
/* 1 byte bitmap decompress */
RD_BOOL
bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size)
{
const uint8 *end = input + size;
uint8 *prevline = NULL, *line = NULL;
int opcode, count, offset, isfillormix, x = width;
int lastopcode = -1, insertmix = False, bicolour = False;
uint8 code;
uint8 colour1 = 0, colour2 = 0;
uint8 mixmask, mask = 0;
uint8 mix = 0xff;
int fom_mask = 0;
while (input < end)
{
fom_mask = 0;
code = CVAL(input);
opcode = code >> 4;
/* Handle different opcode forms */
switch (opcode)
{
case 0xc:
case 0xd:
case 0xe:
opcode -= 6;
count = code & 0xf;
offset = 16;
break;
case 0xf:
opcode = code & 0xf;
if (opcode < 9)
{
count = CVAL(input);
count |= CVAL(input) << 8;
}
else
{
count = (opcode < 0xb) ? 8 : 1;
}
offset = 0;
break;
default:
opcode >>= 1;
count = code & 0x1f;
offset = 32;
break;
}
/* Handle strange cases for counts */
if (offset != 0)
{
isfillormix = ((opcode == 2) || (opcode == 7));
if (count == 0)
{
if (isfillormix)
count = CVAL(input) + 1;
else
count = CVAL(input) + offset;
}
else if (isfillormix)
{
count <<= 3;
}
}
/* Read preliminary data */
switch (opcode)
{
case 0: /* Fill */
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
insertmix = True;
break;
case 8: /* Bicolour */
colour1 = CVAL(input);
case 3: /* Colour */
colour2 = CVAL(input);
break;
case 6: /* SetMix/Mix */
case 7: /* SetMix/FillOrMix */
mix = CVAL(input);
opcode -= 5;
break;
case 9: /* FillOrMix_1 */
mask = 0x03;
opcode = 0x02;
fom_mask = 3;
break;
case 0x0a: /* FillOrMix_2 */
mask = 0x05;
opcode = 0x02;
fom_mask = 5;
break;
}
lastopcode = opcode;
mixmask = 0;
/* Output body */
while (count > 0)
{
if (x >= width)
{
if (height <= 0)
return False;
x = 0;
height--;
prevline = line;
line = output + height * width;
}
switch (opcode)
{
case 0: /* Fill */
if (insertmix)
{
if (prevline == NULL)
line[x] = mix;
else
line[x] = prevline[x] ^ mix;
insertmix = False;
count--;
x++;
}
if (prevline == NULL)
{
REPEAT(line[x] = 0)
}
else
{
REPEAT(line[x] = prevline[x])
}
break;
case 1: /* Mix */
if (prevline == NULL)
{
REPEAT(line[x] = mix)
}
else
{
REPEAT(line[x] = prevline[x] ^ mix)
}
break;
case 2: /* Fill or Mix */
if (prevline == NULL)
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = mix;
else
line[x] = 0;
)
}
else
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = prevline[x] ^ mix;
else
line[x] = prevline[x];
)
}
break;
case 3: /* Colour */
REPEAT(line[x] = colour2)
break;
case 4: /* Copy */
REPEAT(line[x] = CVAL(input))
break;
case 8: /* Bicolour */
REPEAT
(
if (bicolour)
{
line[x] = colour2;
bicolour = False;
}
else
{
line[x] = colour1;
bicolour = True; count++;
}
)
break;
case 0xd: /* White */
REPEAT(line[x] = 0xff)
break;
case 0xe: /* Black */
REPEAT(line[x] = 0)
break;
default:
unimpl("bitmap opcode 0x%x\n", opcode);
return False;
}
}
}
return True;
}
/* 2 byte bitmap decompress */
RD_BOOL
bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size)
{
const uint8 *end = input + size;
uint16 *prevline = NULL, *line = NULL;
int opcode, count, offset, isfillormix, x = width;
int lastopcode = -1, insertmix = False, bicolour = False;
uint8 code;
uint16 colour1 = 0, colour2 = 0;
uint8 mixmask, mask = 0;
uint16 mix = 0xffff;
int fom_mask = 0;
while (input < end)
{
fom_mask = 0;
code = CVAL(input);
opcode = code >> 4;
/* Handle different opcode forms */
switch (opcode)
{
case 0xc:
case 0xd:
case 0xe:
opcode -= 6;
count = code & 0xf;
offset = 16;
break;
case 0xf:
opcode = code & 0xf;
if (opcode < 9)
{
count = CVAL(input);
count |= CVAL(input) << 8;
}
else
{
count = (opcode < 0xb) ? 8 : 1;
}
offset = 0;
break;
default:
opcode >>= 1;
count = code & 0x1f;
offset = 32;
break;
}
/* Handle strange cases for counts */
if (offset != 0)
{
isfillormix = ((opcode == 2) || (opcode == 7));
if (count == 0)
{
if (isfillormix)
count = CVAL(input) + 1;
else
count = CVAL(input) + offset;
}
else if (isfillormix)
{
count <<= 3;
}
}
/* Read preliminary data */
switch (opcode)
{
case 0: /* Fill */
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
insertmix = True;
break;
case 8: /* Bicolour */
CVAL2(input, colour1);
case 3: /* Colour */
CVAL2(input, colour2);
break;
case 6: /* SetMix/Mix */
case 7: /* SetMix/FillOrMix */
CVAL2(input, mix);
opcode -= 5;
break;
case 9: /* FillOrMix_1 */
mask = 0x03;
opcode = 0x02;
fom_mask = 3;
break;
case 0x0a: /* FillOrMix_2 */
mask = 0x05;
opcode = 0x02;
fom_mask = 5;
break;
}
lastopcode = opcode;
mixmask = 0;
/* Output body */
while (count > 0)
{
if (x >= width)
{
if (height <= 0)
return False;
x = 0;
height--;
prevline = line;
line = ((uint16 *) output) + height * width;
}
switch (opcode)
{
case 0: /* Fill */
if (insertmix)
{
if (prevline == NULL)
line[x] = mix;
else
line[x] = prevline[x] ^ mix;
insertmix = False;
count--;
x++;
}
if (prevline == NULL)
{
REPEAT(line[x] = 0)
}
else
{
REPEAT(line[x] = prevline[x])
}
break;
case 1: /* Mix */
if (prevline == NULL)
{
REPEAT(line[x] = mix)
}
else
{
REPEAT(line[x] = prevline[x] ^ mix)
}
break;
case 2: /* Fill or Mix */
if (prevline == NULL)
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = mix;
else
line[x] = 0;
)
}
else
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = prevline[x] ^ mix;
else
line[x] = prevline[x];
)
}
break;
case 3: /* Colour */
REPEAT(line[x] = colour2)
break;
case 4: /* Copy */
REPEAT(CVAL2(input, line[x]))
break;
case 8: /* Bicolour */
REPEAT
(
if (bicolour)
{
line[x] = colour2;
bicolour = False;
}
else
{
line[x] = colour1;
bicolour = True;
count++;
}
)
break;
case 0xd: /* White */
REPEAT(line[x] = 0xffff)
break;
case 0xe: /* Black */
REPEAT(line[x] = 0)
break;
default:
unimpl("bitmap opcode 0x%x\n", opcode);
return False;
}
}
}
return True;
}
/* 3 byte bitmap decompress */
RD_BOOL
bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size)
{
uint8 *end = input + size;
uint8 *prevline = NULL, *line = NULL;
int opcode, count, offset, isfillormix, x = width;
int lastopcode = -1, insertmix = False, bicolour = False;
uint8 code;
uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0};
uint8 mixmask, mask = 0;
uint8 mix[3] = {0xff, 0xff, 0xff};
int fom_mask = 0;
while (input < end)
{
fom_mask = 0;
code = CVAL(input);
opcode = code >> 4;
/* Handle different opcode forms */
switch (opcode)
{
case 0xc:
case 0xd:
case 0xe:
opcode -= 6;
count = code & 0xf;
offset = 16;
break;
case 0xf:
opcode = code & 0xf;
if (opcode < 9)
{
count = CVAL(input);
count |= CVAL(input) << 8;
}
else
{
count = (opcode <
0xb) ? 8 : 1;
}
offset = 0;
break;
default:
opcode >>= 1;
count = code & 0x1f;
offset = 32;
break;
}
/* Handle strange cases for counts */
if (offset != 0)
{
isfillormix = ((opcode == 2) || (opcode == 7));
if (count == 0)
{
if (isfillormix)
count = CVAL(input) + 1;
else
count = CVAL(input) + offset;
}
else if (isfillormix)
{
count <<= 3;
}
}
/* Read preliminary data */
switch (opcode)
{
case 0: /* Fill */
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
insertmix = True;
break;
case 8: /* Bicolour */
colour1[0] = CVAL(input);
colour1[1] = CVAL(input);
colour1[2] = CVAL(input);
case 3: /* Colour */
colour2[0] = CVAL(input);
colour2[1] = CVAL(input);
colour2[2] = CVAL(input);
break;
case 6: /* SetMix/Mix */
case 7: /* SetMix/FillOrMix */
mix[0] = CVAL(input);
mix[1] = CVAL(input);
mix[2] = CVAL(input);
opcode -= 5;
break;
case 9: /* FillOrMix_1 */
mask = 0x03;
opcode = 0x02;
fom_mask = 3;
break;
case 0x0a: /* FillOrMix_2 */
mask = 0x05;
opcode = 0x02;
fom_mask = 5;
break;
}
lastopcode = opcode;
mixmask = 0;
/* Output body */
while (count > 0)
{
if (x >= width)
{
if (height <= 0)
return False;
x = 0;
height--;
prevline = line;
line = output + height * (width * 3);
}
switch (opcode)
{
case 0: /* Fill */
if (insertmix)
{
if (prevline == NULL)
{
line[x * 3] = mix[0];
line[x * 3 + 1] = mix[1];
line[x * 3 + 2] = mix[2];
}
else
{
line[x * 3] =
prevline[x * 3] ^ mix[0];
line[x * 3 + 1] =
prevline[x * 3 + 1] ^ mix[1];
line[x * 3 + 2] =
prevline[x * 3 + 2] ^ mix[2];
}
insertmix = False;
count--;
x++;
}
if (prevline == NULL)
{
REPEAT
(
line[x * 3] = 0;
line[x * 3 + 1] = 0;
line[x * 3 + 2] = 0;
)
}
else
{
REPEAT
(
line[x * 3] = prevline[x * 3];
line[x * 3 + 1] = prevline[x * 3 + 1];
line[x * 3 + 2] = prevline[x * 3 + 2];
)
}
break;
case 1: /* Mix */
if (prevline == NULL)
{
REPEAT
(
line[x * 3] = mix[0];
line[x * 3 + 1] = mix[1];
line[x * 3 + 2] = mix[2];
)
}
else
{
REPEAT
(
line[x * 3] =
prevline[x * 3] ^ mix[0];
line[x * 3 + 1] =
prevline[x * 3 + 1] ^ mix[1];
line[x * 3 + 2] =
prevline[x * 3 + 2] ^ mix[2];
)
}
break;
case 2: /* Fill or Mix */
if (prevline == NULL)
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
{
line[x * 3] = mix[0];
line[x * 3 + 1] = mix[1];
line[x * 3 + 2] = mix[2];
}
else
{
line[x * 3] = 0;
line[x * 3 + 1] = 0;
line[x * 3 + 2] = 0;
}
)
}
else
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
{
line[x * 3] =
prevline[x * 3] ^ mix [0];
line[x * 3 + 1] =
prevline[x * 3 + 1] ^ mix [1];
line[x * 3 + 2] =
prevline[x * 3 + 2] ^ mix [2];
}
else
{
line[x * 3] =
prevline[x * 3];
line[x * 3 + 1] =
prevline[x * 3 + 1];
line[x * 3 + 2] =
prevline[x * 3 + 2];
}
)
}
break;
case 3: /* Colour */
REPEAT
(
line[x * 3] = colour2 [0];
line[x * 3 + 1] = colour2 [1];
line[x * 3 + 2] = colour2 [2];
)
break;
case 4: /* Copy */
REPEAT
(
line[x * 3] = CVAL(input);
line[x * 3 + 1] = CVAL(input);
line[x * 3 + 2] = CVAL(input);
)
break;
case 8: /* Bicolour */
REPEAT
(
if (bicolour)
{
line[x * 3] = colour2[0];
line[x * 3 + 1] = colour2[1];
line[x * 3 + 2] = colour2[2];
bicolour = False;
}
else
{
line[x * 3] = colour1[0];
line[x * 3 + 1] = colour1[1];
line[x * 3 + 2] = colour1[2];
bicolour = True;
count++;
}
)
break;
case 0xd: /* White */
REPEAT
(
line[x * 3] = 0xff;
line[x * 3 + 1] = 0xff;
line[x * 3 + 2] = 0xff;
)
break;
case 0xe: /* Black */
REPEAT
(
line[x * 3] = 0;
line[x * 3 + 1] = 0;
line[x * 3 + 2] = 0;
)
break;
default:
unimpl("bitmap opcode 0x%x\n", opcode);
return False;
}
}
}
return True;
}
/* decompress a colour plane */
static int
process_plane(uint8 * in, int width, int height, uint8 * out, int size)
{
int indexw;
int indexh;
int code;
int collen;
int replen;
int color;
int x;
int revcode;
uint8 * last_line;
uint8 * this_line;
uint8 * org_in;
uint8 * org_out;
org_in = in;
org_out = out;
last_line = 0;
indexh = 0;
while (indexh < height)
{
out = (org_out + width * height * 4) - ((indexh + 1) * width * 4);
color = 0;
this_line = out;
indexw = 0;
if (last_line == 0)
{
while (indexw < width)
{
code = CVAL(in);
replen = code & 0xf;
collen = (code >> 4) & 0xf;
revcode = (replen << 4) | collen;
if ((revcode <= 47) && (revcode >= 16))
{
replen = revcode;
collen = 0;
}
while (collen > 0)
{
color = CVAL(in);
*out = color;
out += 4;
indexw++;
collen--;
}
while (replen > 0)
{
*out = color;
out += 4;
indexw++;
replen--;
}
}
}
else
{
while (indexw < width)
{
code = CVAL(in);
replen = code & 0xf;
collen = (code >> 4) & 0xf;
revcode = (replen << 4) | collen;
if ((revcode <= 47) && (revcode >= 16))
{
replen = revcode;
collen = 0;
}
while (collen > 0)
{
x = CVAL(in);
if (x & 1)
{
x = x >> 1;
x = x + 1;
color = -x;
}
else
{
x = x >> 1;
color = x;
}
x = last_line[indexw * 4] + color;
*out = x;
out += 4;
indexw++;
collen--;
}
while (replen > 0)
{
x = last_line[indexw * 4] + color;
*out = x;
out += 4;
indexw++;
replen--;
}
}
}
indexh++;
last_line = this_line;
}
return (int) (in - org_in);
}
/* 4 byte bitmap decompress */
RD_BOOL
bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size)
{
int code;
int bytes_pro;
int total_pro;
code = CVAL(input);
if (code != 0x10)
{
return False;
}
total_pro = 1;
bytes_pro = process_plane(input, width, height, output + 3, size - total_pro);
total_pro += bytes_pro;
input += bytes_pro;
bytes_pro = process_plane(input, width, height, output + 2, size - total_pro);
total_pro += bytes_pro;
input += bytes_pro;
bytes_pro = process_plane(input, width, height, output + 1, size - total_pro);
total_pro += bytes_pro;
input += bytes_pro;
bytes_pro = process_plane(input, width, height, output + 0, size - total_pro);
total_pro += bytes_pro;
return size == total_pro;
}
int
bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint16 a = ((uint16*)temp)[y * input_width + x];
uint8 r = (a & 0x7c00) >> 10;
uint8 g = (a & 0x03e0) >> 5;
uint8 b = (a & 0x001f);
r = r * 255 / 31;
g = g * 255 / 31;
b = b * 255 / 31;
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
}
free(temp);
return rv;
}
int
bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint16 a = ((uint16*)temp)[y * input_width + x];
uint8 r = (a & 0xf800) >> 11;
uint8 g = (a & 0x07e0) >> 5;
uint8 b = (a & 0x001f);
r = r * 255 / 31;
g = g * 255 / 63;
b = b * 255 / 31;
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
}
free(temp);
return rv;
}
int
bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 3);
RD_BOOL rv = bitmap_decompress3(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint8 r = temp[(y * input_width + x) * 3];
uint8 g = temp[(y * input_width + x) * 3 + 1];
uint8 b = temp[(y * input_width + x) * 3 + 2];
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
}
free(temp);
return rv;
}
int
bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 4);
RD_BOOL rv = bitmap_decompress4(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint8 r = temp[(y * input_width + x) * 4];
uint8 g = temp[(y * input_width + x) * 4 + 1];
uint8 b = temp[(y * input_width + x) * 4 + 2];
uint8 a = temp[(y * input_width + x) * 4 + 3];
((uint32*)output)[y * output_width + x] = 0xff << 24 | r << 16 | g << 8 | b;
}
}
free(temp);
return rv;
}

31
client/tp-player/rle.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef RLE_H
#define RLE_H
#define RD_BOOL int
#define False 0
#define True 1
#define uint8 unsigned char
#define uint16 unsigned short
#define uint32 unsigned int
#ifdef __cplusplus
extern "C" {
#endif
RD_BOOL bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size);
RD_BOOL bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size);
RD_BOOL bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size);
RD_BOOL bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size);
int bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
int bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
int bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
int bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
#ifdef __cplusplus
}
#endif
#endif // RLE_H

View File

@ -0,0 +1,709 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QNetworkCookie>
#include <QStandardPaths>
#include <qcoreapplication.h>
#include <inttypes.h>
#include <zlib.h>
#include "thr_play.h"
#include "thr_data.h"
#include "util.h"
#include "downloader.h"
#include "record_format.h"
#include "mainwindow.h"
#include "rle.h"
static QImage* _rdpimg2QImage(int w, int h, int bitsPerPixel, bool isCompressed, const uint8_t* dat, uint32_t len) {
QImage* out;
switch(bitsPerPixel) {
case 15:
if(isCompressed) {
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
if(!bitmap_decompress1(_dat, w, h, dat, len)) {
free(_dat);
return nullptr;
}
out = new QImage(_dat, w, h, QImage::Format_RGB555);
free(_dat);
}
else {
out = new QImage(QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
}
return out;
case 16:
if(isCompressed) {
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
if(!bitmap_decompress2(_dat, w, h, dat, len)) {
free(_dat);
qDebug() << "22------------------DECOMPRESS2 failed.";
return nullptr;
}
// TODO: 这里需要进一步优化直接操作QImage的buffer。
out = new QImage(w, h, QImage::Format_RGB16);
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
uint16 a = ((uint16*)_dat)[y * w + x];
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
uint8 b = (a & 0x001f) * 255 / 31;
out->setPixelColor(x, y, QColor(r,g,b));
}
}
free(_dat);
return out;
}
else {
out = new QImage(QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
}
return out;
case 24:
case 32:
default:
qDebug() << "--------NOT support " << bitsPerPixel << " bitsPerPix.";
return nullptr;
}
}
static QImage* _raw2QImage(int w, int h, const uint8_t* dat, uint32_t len) {
QImage* out;
// TODO: 这里需要进一步优化直接操作QImage的buffer。
out = new QImage(w, h, QImage::Format_RGB16);
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
uint16 a = ((uint16*)dat)[y * w + x];
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
uint8 b = (a & 0x001f) * 255 / 31;
out->setPixelColor(x, y, QColor(r,g,b));
}
}
return out;
}
//=================================================================
// ThrData
//=================================================================
ThrData::ThrData(MainWindow* mainwin, const QString& res) {
m_mainwin = mainwin;
m_res = res;
m_need_download = false;
m_need_stop = false;
m_need_restart = false;
m_wait_restart = false;
m_need_show_kf = false;
m_file_idx = 0;
m_offset = 0;
#ifdef __APPLE__
m_data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
m_data_path_base += "/tp-testdata/";
#else
m_data_path_base = QCoreApplication::applicationDirPath() + "/record";
#endif
qDebug("data-path-base: %s", m_data_path_base.toStdString().c_str());
// qDebug() << "AppConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
// qDebug() << "AppDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// qDebug() << "AppLocalDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
// qDebug() << "ConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
// qDebug() << "CacheLocation:" << QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
// qDebug() << "GenericCacheLocation:" << QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
/*
AppConfigLocation: "C:/Users/apex/AppData/Local/tp-player"
AppDataLocation: "C:/Users/apex/AppData/Roaming/tp-player"
AppLocalDataLocation: "C:/Users/apex/AppData/Local/tp-player"
ConfigLocation: "C:/Users/apex/AppData/Local/tp-player"
CacheLocation: "C:/Users/apex/AppData/Local/tp-player/cache"
GenericCacheLocation: "C:/Users/apex/AppData/Local/cache"
*/
}
ThrData::~ThrData() {
_clear_data();
}
void ThrData::stop() {
if(!isRunning())
return;
m_need_stop = true;
wait();
qDebug("data thread stop() end.");
}
void ThrData::_notify_message(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrData::_notify_error(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_ERROR);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrData::run() {
_run();
qDebug("ThrData thread run() end.");
}
void ThrData::_run() {
QString _tmp_res = m_res.toLower();
if(_tmp_res.startsWith("http")) {
m_need_download = true;
_notify_message(LOCAL8BIT("正在准备录像数据,请稍候..."));
if(!m_thr_download.init(m_data_path_base, m_res)) {
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法下载录像文件!\n\n"), m_res));
return;
}
m_thr_download.start();
msleep(100);
for(;;) {
if(m_need_stop)
return;
if(!m_thr_download.is_running() || m_thr_download.is_tpk_downloaded())
break;
msleep(100);
}
if(!m_thr_download.is_tpk_downloaded()) {
_notify_error(QString("%1\n%2").arg(LOCAL8BIT("无法下载录像文件!"), m_res));
return;
}
m_thr_download.get_data_path(m_data_path);
}
else {
QFileInfo fi_chk_link(m_res);
if(fi_chk_link.isSymLink())
m_res = fi_chk_link.symLinkTarget();
QFileInfo fi(m_res);
if(!fi.exists()) {
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("指定的文件或目录不存在!"), m_res));
return;
}
if(fi.isFile()) {
m_data_path = fi.path();
}
else if(fi.isDir()) {
m_data_path = m_res;
}
m_data_path = QDir::toNativeSeparators(m_data_path);
}
// 到这里,.tpr和.tpk文件均已经下载完成了。
if(!_load_header())
return;
if(!_load_keyframe())
return;
UpdateData* dat = new UpdateData(m_hdr);
emit signal_update_data(dat);
QFile* fdata = nullptr;
qint64 file_size = 0;
qint64 file_processed = 0;
qint64 read_len = 0;
QString str_fidx;
for(;;) {
// 任何时候确保第一时间响应退出操作
if(m_need_stop)
return;
if(m_need_restart) {
if(fdata) {
fdata->close();
delete fdata;
fdata = nullptr;
}
m_wait_restart = true;
msleep(50);
continue;
}
// 如果所有文件都已经处理完了,则等待(可能用户会拖动滚动条,或者重新播放)
if(m_file_idx >= m_hdr.info.dat_file_count) {
msleep(500);
continue;
}
// 看看待播放队列中还有多少个数据包
int pkg_count_in_queue = 0;
int pkg_need_add = 0;
m_locker.lock();
pkg_count_in_queue = m_data.size();
m_locker.unlock();
// 少于1000个的话补足到2000个
if(m_data.size() < 1000)
pkg_need_add = 2000 - pkg_count_in_queue;
if(pkg_need_add == 0) {
msleep(100);
continue;
}
for(int i = 0; i < pkg_need_add; ++i) {
if(m_need_stop)
return;
if(m_need_restart)
break;
// 如果数据文件尚未打开,则打开它
if(fdata == nullptr) {
str_fidx.sprintf("%d", m_file_idx+1);
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
tpd_fname = QDir::toNativeSeparators(tpd_fname);
QFileInfo fi_tpd(tpd_fname);
if(!fi_tpd.exists()) {
if(m_need_download) {
// 此文件尚未下载完成,等待
for(;;) {
if(m_need_stop)
return;
if(!m_thr_download.is_running() || m_thr_download.is_tpd_downloaded(m_file_idx))
break;
msleep(100);
}
// 下载失败了
if(!m_thr_download.is_tpd_downloaded(m_file_idx))
return;
}
}
fdata = new QFile(tpd_fname);
if(!fdata->open(QFile::ReadOnly)) {
qDebug() << "Can not open " << tpd_fname << " for read.";
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法打开录像数据文件!"), tpd_fname));
return;
}
file_size = fdata->size();
file_processed = 0;
qDebug("Open file tp-rdp-%d.tpd, processed: %" PRId64 ", size: %" PRId64, m_file_idx+1, file_processed, file_size);
}
// 如果指定了起始偏移,则跳过这部分数据
if(m_offset > 0) {
fdata->seek(m_offset);
file_processed = m_offset;
m_offset = 0;
}
//----------------------------------
// 读取一个数据包
//----------------------------------
if(file_size - file_processed < sizeof(TS_RECORD_PKG)) {
qDebug("invaid tp-rdp-%d.tpd file, filesize=%" PRId64 ", processed=%" PRId64 ", need=%d.", m_file_idx+1, file_size, file_processed, sizeof(TS_RECORD_PKG));
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
TS_RECORD_PKG pkg;
read_len = fdata->read(reinterpret_cast<char*>(&pkg), sizeof(TS_RECORD_PKG));
if(read_len != sizeof(TS_RECORD_PKG)) {
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (1).", m_file_idx+1, read_len);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
file_processed += sizeof(TS_RECORD_PKG);
if(file_size - file_processed < pkg.size) {
qDebug("invaid tp-rdp-%d.tpd file (2).", m_file_idx+1);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
if(pkg.size == 0) {
qDebug("################## too bad.");
}
QByteArray pkg_data = fdata->read(pkg.size);
if(pkg_data.size() != static_cast<int>(pkg.size)) {
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (3).", m_file_idx+1, read_len);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
file_processed += pkg.size;
UpdateData* dat = _parse(pkg, pkg_data);
if(dat == nullptr) {
qDebug("invaid tp-rdp-%d.tpd file (4).", m_file_idx+1);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
// 遇到关键帧,需要清除自上一个关键帧以来保存的缓存图像数据
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
for(size_t ci = 0; ci < m_cache_imgs.size(); ++ci) {
if(m_cache_imgs[ci] != nullptr)
delete m_cache_imgs[ci];
}
m_cache_imgs.clear();
}
// 拖动滚动条后,需要显示一次关键帧数据,然后跳过后续关键帧。
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
qDebug("----key frame: %ld, processed=%" PRId64 ", pkg.size=%d", pkg.time_ms, file_processed, pkg.size);
if(m_need_show_kf) {
m_need_show_kf = false;
qDebug("++ show keyframe.");
}
else {
qDebug("-- skip keyframe.");
delete dat;
dat = nullptr;
}
}
// 数据放到待播放列表中
if(dat) {
m_locker.lock();
m_data.enqueue(dat);
m_locker.unlock();
}
// 让线程调度器让播放线程有机会执行
// msleep(1);
// 如果此文件已经处理完毕,则关闭文件,这样下次处理一个新的文件
if(file_processed >= file_size) {
fdata->close();
delete fdata;
fdata = nullptr;
m_file_idx++;
}
if(m_file_idx >= m_hdr.info.dat_file_count) {
UpdateData* dat = new UpdateData(TYPE_END);
m_locker.lock();
m_data.enqueue(dat);
m_locker.unlock();
break;
}
}
}
}
UpdateData* ThrData::_parse(const TS_RECORD_PKG& pkg, const QByteArray& data) {
if(pkg.type == TS_RECORD_TYPE_RDP_POINTER) {
if(data.size() != sizeof(TS_RECORD_RDP_POINTER))
return nullptr;
UpdateData* ud = new UpdateData();
ud->set_pointer(pkg.time_ms, reinterpret_cast<const TS_RECORD_RDP_POINTER*>(data.data()));
return ud;
}
else if(pkg.type == TS_RECORD_TYPE_RDP_IMAGE) {
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
if(data.size() < static_cast<int>(sizeof(uint16_t) + sizeof(TS_RECORD_RDP_IMAGE_INFO))) {
delete ud;
return nullptr;
}
const uint8_t* dat_ptr = reinterpret_cast<const uint8_t*>(data.data());
uint16_t count = (reinterpret_cast<const uint16_t*>(dat_ptr))[0];
uint32_t offset = sizeof(uint16_t);
UpdateImages& imgs = ud->get_images();
for(uint16_t i = 0; i < count; ++i) {
const TS_RECORD_RDP_IMAGE_INFO* info = reinterpret_cast<const TS_RECORD_RDP_IMAGE_INFO*>(dat_ptr+offset);
offset += sizeof(TS_RECORD_RDP_IMAGE_INFO);
if(info->format != TS_RDP_IMG_ALT) {
const uint8_t* img_dat = dat_ptr + offset;
const uint8_t* real_img_dat = nullptr;
QByteArray unzip_data;
if(info->zip_len > 0) {
// 数据被压缩了,需要解压缩
unzip_data.resize(static_cast<int>(info->dat_len));
uLong u_len = info->dat_len;
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, img_dat, info->zip_len);
if(err != Z_OK || u_len != info->dat_len) {
qDebug("image uncompress failed. err=%d.", err);
}
else {
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
}
offset += info->zip_len;
}
else {
real_img_dat = img_dat;
offset += info->dat_len;
}
UPDATE_IMAGE uimg;
uimg.x = info->destLeft;
uimg.y = info->destTop;
uimg.w = info->destRight - info->destLeft + 1;
uimg.h = info->destBottom - info->destTop + 1;
if(real_img_dat)
uimg.img = _rdpimg2QImage(info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, real_img_dat, info->dat_len);
else
uimg.img = nullptr;
imgs.push_back(uimg);
QImage* cache_img = nullptr;
if(uimg.img != nullptr)
cache_img = new QImage(*uimg.img);
m_cache_imgs.push_back(cache_img);
}
else {
UPDATE_IMAGE uimg;
uimg.x = info->destLeft;
uimg.y = info->destTop;
uimg.w = info->destRight - info->destLeft + 1;
uimg.h = info->destBottom - info->destTop + 1;
size_t cache_idx = info->dat_len;
if(cache_idx >= m_cache_imgs.size() || m_cache_imgs[cache_idx] == nullptr) {
uimg.img = nullptr;
}
else {
uimg.img = new QImage(*m_cache_imgs[cache_idx]);
}
imgs.push_back(uimg);
}
}
return ud;
}
else if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
const TS_RECORD_RDP_KEYFRAME_INFO* info = reinterpret_cast<const TS_RECORD_RDP_KEYFRAME_INFO*>(data.data());
const uint8_t* data_buf = reinterpret_cast<const uint8_t*>(data.data() + sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
uint32_t data_len = data.size() - sizeof(TS_RECORD_RDP_KEYFRAME_INFO);
UpdateImages& imgs = ud->get_images();
UPDATE_IMAGE uimg;
uimg.x = 0;
uimg.y = 0;
uimg.w = m_hdr.basic.width;
uimg.h = m_hdr.basic.height;
const uint8_t* real_img_dat = nullptr;
uint32_t real_img_len = m_hdr.basic.width * m_hdr.basic.height * 2;
QByteArray unzip_data;
if(data_len != real_img_len) {
// 数据被压缩了,需要解压缩
unzip_data.resize(static_cast<int>(real_img_len));
uLong u_len = real_img_len;
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, data_buf, data_len);
if(err != Z_OK || u_len != real_img_len) {
qDebug("keyframe uncompress failed. err=%d.", err);
}
else {
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
}
}
else {
real_img_dat = data_buf;
}
if(real_img_dat != nullptr)
uimg.img = _raw2QImage(m_hdr.basic.width, m_hdr.basic.height, real_img_dat, real_img_len);
else
uimg.img = nullptr;
imgs.push_back(uimg);
return ud;
}
return nullptr;
}
void ThrData::restart(uint32_t start_ms) {
qDebug("restart at %ld ms", start_ms);
// 让处理线程处理完当前循环,然后等待
m_need_restart = true;
// 确保处理线程已经处理完当前循环
for(;;) {
msleep(50);
if(m_need_stop)
return;
if(m_wait_restart)
break;
}
// 清空待播放队列
_clear_data();
if(start_ms == 0) {
m_offset = 0;
m_file_idx = 0;
m_need_show_kf = false;
}
else {
// 找到最接近 start_ms 但小于它的关键帧
size_t i = 0;
for(i = 0; i < m_kf.size(); ++i) {
if(m_kf[i].time_ms > start_ms) {
break;
}
}
if(i > 0)
i--;
qDebug("restart acturelly at %ld ms, kf: %d", m_kf[i].time_ms, i);
// 指定要播放的数据的开始位置
m_offset = m_kf[i].offset;
m_file_idx = m_kf[i].file_index;
if(m_file_idx == (uint32_t)-1)
m_file_idx = 0;
m_need_show_kf = true;
}
qDebug("RESTART: offset=%d, file_idx=%d", m_offset, m_file_idx);
// 让处理线程继续
m_wait_restart = false;
m_need_restart = false;
}
bool ThrData::_load_header() {
QString msg;
qDebug() << "PATH_BASE: " << m_data_path;
QString filename = QString("%1/tp-rdp.tpr").arg(m_data_path);
filename = QDir::toNativeSeparators(filename);
qDebug() << "TPR: " << filename;
QFile f(filename);
if(!f.open(QFile::ReadOnly)) {
qDebug() << "Can not open " << filename << " for read.";
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法打开录像信息文件!"), filename));
return false;
}
memset(&m_hdr, 0, sizeof(TS_RECORD_HEADER));
qint64 read_len = 0;
read_len = f.read(reinterpret_cast<char*>(&m_hdr), sizeof(TS_RECORD_HEADER));
if(read_len != sizeof(TS_RECORD_HEADER)) {
qDebug() << "invaid .tpr file.";
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("错误的录像信息文件!"), filename));
return false;
}
if(m_hdr.info.ver != 4) {
qDebug() << "invaid .tpr file.";
_notify_error(QString("%1 %2%3").arg(LOCAL8BIT("不支持的录像文件版本 "), QString(m_hdr.info.ver), LOCAL8BIT("\n\n此播放器支持录像文件版本 4。")));
return false;
}
if(m_hdr.basic.width == 0 || m_hdr.basic.height == 0) {
_notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
return false;
}
if(m_hdr.info.dat_file_count == 0) {
_notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
return false;
}
return true;
}
bool ThrData::_load_keyframe() {
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path);
tpk_fname = QDir::toNativeSeparators(tpk_fname);
qDebug() << "TPK: " << tpk_fname;
QFile f_kf(tpk_fname);
if(!f_kf.open(QFile::ReadOnly)) {
qDebug() << "Can not open " << tpk_fname << " for read.";
_notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("无法打开关键帧信息文件!"), tpk_fname));
return false;
}
qint64 fsize = f_kf.size();
if(!fsize || fsize % sizeof(TS_RECORD_RDP_KEYFRAME_INFO) != 0) {
qDebug() << "Can not open " << tpk_fname << " for read.";
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
return false;
}
qint64 read_len = 0;
int kf_count = static_cast<int>(fsize / sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
for(int i = 0; i < kf_count; ++i) {
TS_RECORD_RDP_KEYFRAME_INFO kf;
memset(&kf, 0, sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
read_len = f_kf.read(reinterpret_cast<char*>(&kf), sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
if(read_len != sizeof(TS_RECORD_RDP_KEYFRAME_INFO)) {
qDebug() << "invaid .tpk file.";
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
return false;
}
m_kf.push_back(kf);
}
return true;
}
UpdateData* ThrData::get_data() {
UpdateData* d = nullptr;
m_locker.lock();
if(m_data.size() > 0) {
d = m_data.dequeue();
}
m_locker.unlock();
return d;
}
void ThrData::_clear_data() {
m_locker.lock();
while(m_data.size() > 0) {
UpdateData* d = m_data.dequeue();
delete d;
}
m_locker.unlock();
}

107
client/tp-player/thr_data.h Normal file
View File

@ -0,0 +1,107 @@
#ifndef THR_DATA_H
#define THR_DATA_H
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QNetworkReply>
#include <QFile>
#include <QEventLoop>
#include <QImage>
#include "update_data.h"
#include "record_format.h"
#include "thr_download.h"
/*
4MB
tp-rdp.tpr
tp-rdp.tpk (v3.5.1)
tp-rdp-1.tpd, tp-rdp-2.tpd, tp-rdp-3.tpd, ...
线
线
.tpk
tp-rdp.tpk.tmp, tp-rdp.tpk.len
tp-rdp-1.tpd.tmp, tp-rdp-1.tpd.len, ...
*/
typedef std::vector<TS_RECORD_RDP_KEYFRAME_INFO> KeyFrames;
typedef std::vector<QImage*> CachedImages;
class MainWindow;
// 下载必要的文件解析文件数据生成图像数据QImage*),将数据包放入待显示队列中,等待 ThrPlay 线程使用
// 注意无需将所有数据解析并放入待显示队列此队列有数量限制例如1000个避免过多占用内存
class ThrData : public QThread {
Q_OBJECT
public:
ThrData(MainWindow* mainwin, const QString& url);
~ThrData();
virtual void run();
void stop();
void restart(uint32_t start_ms); // 重新从指定时间开始播放
bool have_more_data();
UpdateData* get_data();
private:
void _run();
bool _load_header();
bool _load_keyframe();
void _clear_data();
// void _prepare();
UpdateData* _parse(const TS_RECORD_PKG& pkg, const QByteArray& data);
void _notify_message(const QString& msg);
void _notify_error(const QString& err_msg);
signals:
void signal_update_data(UpdateData*);
private:
MainWindow* m_mainwin;
QQueue<UpdateData*> m_data;
QMutex m_locker;
ThrDownload m_thr_download;
bool m_need_stop;
bool m_need_download;
QString m_res;
QString m_data_path_base;
QString m_url_base;
QString m_sid;
QString m_rid;
QString m_data_path;
TS_RECORD_HEADER m_hdr;
KeyFrames m_kf;
bool m_need_restart;
bool m_wait_restart;
bool m_need_show_kf;
uint32_t m_file_idx;
uint32_t m_offset;
CachedImages m_cache_imgs;
};
#endif // THR_DATA_H

View File

@ -0,0 +1,292 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QNetworkCookie>
#include "thr_download.h"
#include "util.h"
#include "downloader.h"
#include "record_format.h"
//=================================================================
// ThrDownload
//=================================================================
ThrDownload::ThrDownload() {
m_need_stop = false;
m_have_tpr = false;
m_have_tpk = false;
m_have_tpd = nullptr;
m_need_tpk = false;
m_running = true;
}
ThrDownload::~ThrDownload() {
if(m_have_tpd)
delete[] m_have_tpd;
}
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
bool ThrDownload::init(const QString& local_data_path_base, const QString &res) {
m_data_path_base = local_data_path_base;
QString _tmp_res = res.toLower();
if(!_tmp_res.startsWith("http")) {
return false;
}
QStringList _uris = res.split('/');
if(_uris.size() < 3) {
return false;
}
m_sid = _uris[_uris.size()-2];
m_rid = _uris[_uris.size()-1];
m_url_base = res.left(res.length() - m_sid.length() - m_rid.length() - 2);
if(m_sid.length() == 0 || m_rid.length() == 0 || m_url_base.length() == 0)
return false;
return true;
}
void ThrDownload::stop() {
if(!m_running)
return;
// if(!isRunning())
// return;
m_need_stop = true;
wait();
qDebug("data thread stop() end.");
}
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
void ThrDownload::run() {
_run();
m_running = false;
qDebug("ThrDownload thread run() end.");
}
void ThrDownload::_run() {
// m_state = statDownloading;
if(!_download_tpr()) {
// m_state = statFailDone;
return;
}
m_have_tpr = true;
m_have_tpd = new bool[m_tpd_count];
for(uint32_t i = 0; i < m_tpd_count; ++i) {
m_have_tpd[i] = false;
}
if(m_need_tpk) {
if(!_download_tpk()) {
// m_state = statFailDone;
return;
}
m_have_tpk = true;
}
uint32_t file_idx = 0;
for(;;) {
if(m_need_stop)
break;
QString str_fidx;
str_fidx.sprintf("%d", file_idx+1);
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
tpd_fname = QDir::toNativeSeparators(tpd_fname);
QString tmp_fname = QString("%1/tp-rdp-%2.tpd.downloading").arg(m_data_path, str_fidx);
tmp_fname = QDir::toNativeSeparators(tmp_fname);
QFileInfo fi_tmp(tmp_fname);
if(fi_tmp.isFile()) {
QFile::remove(tmp_fname);
}
QFileInfo fi_tpd(tpd_fname);
if(!fi_tpd.exists()) {
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp-%3.tpd").arg(m_url_base, m_rid, str_fidx);
qDebug() << "URL : " << url;
qDebug() << "TPD : " << tmp_fname;
if(!_download_file(url, tmp_fname)) {
// m_state = statFailDone;
return;
}
if(!QFile::rename(tmp_fname, tpd_fname)) {
// m_state = statFailDone;
return;
}
}
m_have_tpd[file_idx] = true;
file_idx += 1;
if(file_idx >= m_tpd_count)
break;
}
// m_state = statSuccessDone;
}
bool ThrDownload::_download_tpr() {
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp.tpr").arg(m_url_base, m_rid);
QByteArray data;
if(!_download_file(url, data))
return false;
if(data.size() != sizeof(TS_RECORD_HEADER)) {
qDebug("invalid header data. %d", data.size());
m_error = QString(LOCAL8BIT("录像信息文件数据错误!"));
return false;
}
TS_RECORD_HEADER* hdr = reinterpret_cast<TS_RECORD_HEADER*>(data.data());
// if(hdr->info.ver != 4) {
// qDebug() << "invaid .tpr file.";
// m_last_error = QString("%1 %2%3").arg(LOCAL8BIT("不支持的录像文件版本 "), QString(hdr->info.ver), LOCAL8BIT("\n\n此播放器支持录像文件版本 4。"));
// return false;
// }
// if(m_hdr.basic.width == 0 || m_hdr.basic.height == 0) {
// _notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
// return false;
// }
// if(m_hdr.info.dat_file_count == 0) {
// _notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
// return false;
// }
// 下载得到的数据应该是一个TS_RECORD_HEADER解析此数据生成本地文件路径并保存之。
QDateTime timeUTC;
// timeUTC.setTimeSpec(Qt::UTC);
// timeUTC.setTime_t(m_hdr.basic.timestamp);
timeUTC.setSecsSinceEpoch(hdr->basic.timestamp);
QString strUTC = timeUTC.toString("yyyyMMdd-hhmmss");
QString strAcc(hdr->basic.acc_username);
int idx = strAcc.indexOf('\\');
if(-1 != idx) {
QString _domain = strAcc.left(idx);
QString _user = strAcc.right(strAcc.length() - idx - 1);
strAcc = _user + "@" + _domain;
}
QString strType;
if(hdr->info.type == TS_TPPR_TYPE_SSH) {
strType = "SSH";
}
else if(hdr->info.type == TS_TPPR_TYPE_RDP) {
strType = "RDP";
m_need_tpk = true;
}
else {
strType = "UNKNOWN";
}
// .../record/RDP-211-admin-user@domain-192.168.0.68-20191015-020243
m_data_path = QString("%1/%2-%3-%4-%5-%6-%7").arg(m_data_path_base, strType, m_rid, hdr->basic.user_username, strAcc, hdr->basic.host_ip, strUTC);
m_data_path = QDir::toNativeSeparators(m_data_path);
qDebug() << "PATH_BASE: " << m_data_path;
QDir dir;
dir.mkpath(m_data_path);
QFileInfo fi;
fi.setFile(m_data_path);
if(!fi.isDir()) {
qDebug("can not create folder to save downloaded file.");
return false;
}
QString filename = QString("%1/tp-rdp.tpr").arg(m_data_path);
filename = QDir::toNativeSeparators(filename);
qDebug() << "TPR: " << filename;
QFile f;
f.setFileName(filename);
if(!f.open(QIODevice::WriteOnly | QFile::Truncate)){
qDebug("open file for write failed.");
return false;
}
qint64 written = f.write(reinterpret_cast<const char*>(hdr), sizeof(TS_RECORD_HEADER));
f.flush();
f.close();
if(written != sizeof(TS_RECORD_HEADER)) {
qDebug("save header file failed.");
return false;
}
m_tpd_count = hdr->info.dat_file_count;
return true;
}
bool ThrDownload::_download_tpk() {
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path);
tpk_fname = QDir::toNativeSeparators(tpk_fname);
QString tmp_fname = QString("%1/tp-rdp.tpk.downloading").arg(m_data_path);
tmp_fname = QDir::toNativeSeparators(tmp_fname);
QFileInfo fi_tmp(tmp_fname);
if(fi_tmp.isFile()) {
QFile::remove(tmp_fname);
}
QFileInfo fi_tpk(tpk_fname);
if(!fi_tpk.exists()) {
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp.tpk").arg(m_url_base, m_rid);
qDebug() << "TPK: " << tmp_fname;
if(!_download_file(url, tmp_fname))
return false;
if(!QFile::rename(tmp_fname, tpk_fname))
return false;
}
return true;
}
bool ThrDownload::_download_file(const QString& url, const QString filename) {
Downloader dl;
if(!dl.request(url, m_sid, filename)) {
qDebug() << "download failed.";
m_error = QString("%1").arg(LOCAL8BIT("下载文件失败!"));
return false;
}
return true;
}
bool ThrDownload::_download_file(const QString& url, QByteArray& data) {
Downloader dl;
if(!dl.request(url, m_sid, &data)) {
qDebug() << "download failed.";
m_error = QString("%1").arg(LOCAL8BIT("下载文件失败!"));
return false;
}
return true;
}
bool ThrDownload::is_tpd_downloaded(uint32_t file_idx) const {
if(!m_have_tpd)
return false;
if(file_idx >= m_tpd_count)
return false;
return m_have_tpd[file_idx];
}

View File

@ -0,0 +1,72 @@
#ifndef THR_DOWNLOAD_H
#define THR_DOWNLOAD_H
#include <QThread>
#include <QNetworkReply>
#include <QFile>
#include <QEventLoop>
class ThrDownload : public QThread {
Q_OBJECT
//public:
// enum State {
// statStarting,
// statDownloading,
// statInvalidParam,
// statFailDone,
// statSuccessDone
// };
public:
ThrDownload();
~ThrDownload();
bool init(const QString& local_data_path_base, const QString& res);
virtual void run();
void stop();
bool is_running() const {return m_running;}
bool is_tpr_downloaded() const {return m_have_tpr;}
bool is_tpk_downloaded() const {return m_have_tpk;}
bool is_tpd_downloaded(uint32_t file_idx) const;
bool get_data_path(QString& path) const {
if(m_data_path.isEmpty())
return false;
path = m_data_path;
return true;
}
private:
void _run();
bool _download_tpr();
bool _download_tpk();
bool _download_file(const QString& url, const QString filename);
bool _download_file(const QString& url, QByteArray& data);
private:
bool m_need_stop;
QString m_data_path_base;
QString m_url_base;
QString m_sid;
QString m_rid;
QString m_data_path;
bool m_running;
bool m_have_tpr;
bool m_have_tpk;
bool m_need_tpk;
uint32_t m_tpd_count;
bool* m_have_tpd;
QString m_error;
};
#endif // THR_DOWNLOAD_H

View File

@ -0,0 +1,182 @@
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include "thr_play.h"
#include "thr_data.h"
#include "mainwindow.h"
#include "record_format.h"
#include "util.h"
/*
*
* - 线线
* + 线500100020ms500
* - 线UI
* + if( * ( - ) >= ( - ))
* + 33
*/
ThrPlay::ThrPlay(MainWindow* mainwnd) {
m_mainwnd = mainwnd;
m_need_stop = false;
m_need_pause = false;
m_speed = 1;
m_skip = false;
m_start_ms = 0;
}
ThrPlay::~ThrPlay() {
stop();
}
void ThrPlay::stop() {
if(!isRunning())
return;
m_need_stop = true;
wait();
qDebug() << "play-thread end.";
}
void ThrPlay::_notify_message(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrPlay::_notify_error(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_ERROR);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrPlay::resume(bool relocate, uint32_t start_ms) {
if(relocate) {
m_start_ms = start_ms;
m_first_run = true;
}
m_need_pause = false;
}
void ThrPlay::run() {
ThrData* thr_data = m_mainwnd->get_thr_data();
m_first_run = true;
uint32_t last_time_ms = 0;
uint32_t last_pass_ms = 0;
UpdateData* dat = nullptr;
for(;;) {
if(m_need_stop)
break;
// 1. 从ThrData的待播放队列中取出一个数据
dat = thr_data->get_data();
if(dat == nullptr) {
msleep(20);
continue;
}
if(m_first_run) {
m_first_run = false;
_notify_message("");
}
if(m_start_ms > 0) {
if(dat->get_time() < m_start_ms) {
emit signal_update_data(dat);
continue;
}
last_time_ms = m_start_ms;
m_start_ms = 0;
UpdateData* _enable = new UpdateData(TYPE_ENABLE_DRAW);
emit signal_update_data(_enable);
}
// 2. 根据数据包的信息,等待到播放时间点
uint32_t need_wait_ms = 0;
uint32_t this_time_ms = dat->get_time();
uint32_t this_pass_ms = last_time_ms;
if(this_time_ms > 0) {
if(this_time_ms >= last_time_ms)
need_wait_ms = this_time_ms - last_time_ms;
else
need_wait_ms = 0;
if(need_wait_ms > 0) {
uint32_t time_wait = 0;
// 如果设置了跳过无操作区间将超过1秒的等待时间压缩至1秒。
if(m_skip) {
if(need_wait_ms > 1000)
need_wait_ms = 1000;
}
for(;;) {
time_wait = need_wait_ms > 10 ? 10 : need_wait_ms;
msleep(time_wait);
if(m_need_pause) {
while(m_need_pause) {
msleep(50);
if(m_need_stop)
break;
}
}
if(m_need_stop)
break;
if(m_start_ms > 0) {
delete dat;
dat = nullptr;
UpdateData* _disable = new UpdateData(TYPE_DISABLE_DRAW);
msleep(500);
emit signal_update_data(_disable);
break;
}
time_wait *= m_speed;
// 如果已经在等待长时间无操作区间内用户设置了跳过无操作区间则将超过0.5秒的等待时间压缩至0.5秒。
if(m_skip) {
if(need_wait_ms > 500)
need_wait_ms = 500;
}
this_pass_ms += time_wait;
if(this_pass_ms - last_pass_ms > 100) {
UpdateData* _passed_ms = new UpdateData(TYPE_PLAYED_MS);
_passed_ms->played_ms(this_pass_ms);
emit signal_update_data(_passed_ms);
last_pass_ms = this_pass_ms;
}
if(need_wait_ms <= time_wait)
break;
else
need_wait_ms -= time_wait;
}
if(m_need_stop)
break;
}
}
last_time_ms = this_time_ms;
// 3. 将数据包发送给主UI界面进行显示
if(dat != nullptr) {
if(dat->data_type() == TYPE_END) {
_notify_message(LOCAL8BIT("播放结束"));
}
emit signal_update_data(dat);
}
}
if(dat != nullptr)
delete dat;
}

View File

@ -0,0 +1,43 @@
#ifndef THR_PLAY_H
#define THR_PLAY_H
#include <QThread>
#include "update_data.h"
#include "downloader.h"
class MainWindow;
// 根据播放规则将要播放的图像发送给主UI线程进行显示
class ThrPlay : public QThread
{
Q_OBJECT
friend class ThrData;
public:
ThrPlay(MainWindow* mainwnd);
~ThrPlay();
virtual void run();
void stop();
void pause() {m_need_pause = true;}
void resume(bool relocate, uint32_t start_ms);
void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;}
void skip(bool s) {m_skip = s;}
private:
void _notify_message(const QString& msg);
void _notify_error(const QString& err_msg);
signals:
void signal_update_data(UpdateData*);
private:
MainWindow* m_mainwnd;
bool m_need_stop;
bool m_need_pause;
int m_speed;
bool m_skip;
bool m_first_run;
uint32_t m_start_ms;
};
#endif // THR_PLAY_H

View File

@ -0,0 +1,70 @@
TEMPLATE = app
TARGET = tp-player
QT += core gui widgets network
HEADERS += \
mainwindow.h \
bar.h \
thr_play.h \
thr_data.h \
update_data.h \
record_format.h \
rle.h \
util.h \
downloader.h \
thr_download.h
SOURCES += \
main.cpp \
mainwindow.cpp \
bar.cpp \
thr_play.cpp \
thr_data.cpp \
update_data.cpp \
rle.c \
util.cpp \
downloader.cpp \
thr_download.cpp
RESOURCES += \
tp-player.qrc
RC_FILE += \
tp-player.rc
FORMS += \
mainwindow.ui
win32:CONFIG(release, debug|release): {
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/ -lzlibd
DESTDIR = $$PWD/../../out/client/x86/Debug
}
macx:CONFIG(release, debug|release): {
DEFINES += QT_NO_DEBUG_OUTPUT
LIBS += -L$$PWD/../../external/zlib/build/release/ -lzlib
DESTDIR = $$PWD/../../out/client/x86/Release
}
else:macx:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../../external/zlib/build/debug/ -lzlibd
DESTDIR = $$PWD/../../out/client/x86/Debug
}
INCLUDEPATH += $$PWD/../../external/zlib
INCLUDEPATH += $$PWD/../../external/zlib/build
DEPENDPATH += $$PWD/../../external/zlib
DEPENDPATH += $$PWD/../../external/zlib/build
#win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/release/libzlibstatic.a
#else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/debug/libzlibstaticd.a
#else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/release/zlibstatic.lib
#else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/debug/zlibstaticd.lib

View File

@ -0,0 +1,38 @@
<RCC>
<qresource prefix="/tp-player">
<file>res/bg.png</file>
<file>res/cursor.png</file>
<file>res/bar/bg-left.png</file>
<file>res/bar/bg-mid.png</file>
<file>res/bar/bg-right.png</file>
<file>res/bar/btn-normal-left.png</file>
<file>res/bar/btn-normal-mid.png</file>
<file>res/bar/btn-normal-right.png</file>
<file>res/bar/btn-sel-left.png</file>
<file>res/bar/btn-sel-mid.png</file>
<file>res/bar/btn-sel-right.png</file>
<file>res/bar/btn-hover-left.png</file>
<file>res/bar/btn-hover-mid.png</file>
<file>res/bar/btn-hover-right.png</file>
<file>res/bar/play-hover.png</file>
<file>res/bar/play-normal.png</file>
<file>res/bar/pause-hover.png</file>
<file>res/bar/pause-normal.png</file>
<file>res/bar/prgbar-mid.png</file>
<file>res/bar/prgbar-right.png</file>
<file>res/bar/prgbarh-left.png</file>
<file>res/bar/prgbarh-mid.png</file>
<file>res/bar/prgpt-normal.png</file>
<file>res/bar/prgpt-hover.png</file>
<file>res/bar/chkbox-normal.png</file>
<file>res/bar/chkbox-hover.png</file>
<file>res/bar/chkbox-sel-normal.png</file>
<file>res/bar/chkbox-sel-hover.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,2 @@
IDI_ICON1 ICON DISCARDABLE "res\\tp-player.ico"

View File

@ -0,0 +1,79 @@
#include "update_data.h"
#include <QImage>
#include <QDebug>
UpdateData::UpdateData() : QObject(nullptr)
{
_init();
}
UpdateData::UpdateData(int data_type) : QObject(nullptr)
{
_init();
m_data_type = data_type;
}
UpdateData::UpdateData(int data_type, uint32_t time_ms) : QObject(nullptr)
{
_init();
m_data_type = data_type;
m_time_ms = time_ms;
}
UpdateData::UpdateData(const TS_RECORD_HEADER& hdr) : QObject(nullptr)
{
_init();
m_data_type = TYPE_HEADER_INFO;
m_hdr = new TS_RECORD_HEADER;
memcpy(m_hdr, &hdr, sizeof(TS_RECORD_HEADER));
}
void UpdateData::_init() {
m_data_type = TYPE_UNKNOWN;
m_hdr = nullptr;
m_pointer = nullptr;
m_data_buf = nullptr;
m_data_len = 0;
m_time_ms = 0;
}
UpdateData::~UpdateData() {
if(m_hdr)
delete m_hdr;
if(m_pointer)
delete m_pointer;
for(int i = 0; i < m_images.size(); ++i) {
delete m_images[i].img;
}
m_images.clear();
if(m_data_buf)
delete m_data_buf;
}
void UpdateData::set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p) {
m_data_type = TYPE_POINTER;
m_time_ms = ts;
m_pointer = new TS_RECORD_RDP_POINTER;
memcpy(m_pointer, p, sizeof(TS_RECORD_RDP_POINTER));
}
void UpdateData::alloc_data(uint32_t len) {
if(m_data_buf)
delete m_data_buf;
m_data_buf = new uint8_t[len];
memset(m_data_buf, 0, len);
m_data_len = len;
}
void UpdateData::attach_data(const uint8_t* dat, uint32_t len) {
if(m_data_buf)
delete m_data_buf;
m_data_buf = new uint8_t[len];
memcpy(m_data_buf, dat, len);
m_data_len = len;
}

View File

@ -0,0 +1,106 @@
#ifndef UPDATE_DATA_H
#define UPDATE_DATA_H
#include <QObject>
#include <QVector>
#include "record_format.h"
#define TYPE_UNKNOWN 0
#define TYPE_HEADER_INFO 1
#define TYPE_DISABLE_DRAW 5
#define TYPE_ENABLE_DRAW 6
#define TYPE_POINTER 10
#define TYPE_IMAGE 11
#define TYPE_KEYFRAME 12
#define TYPE_PLAYED_MS 20
#define TYPE_DOWNLOAD_PERCENT 21
#define TYPE_END 50
#define TYPE_MESSAGE 90
#define TYPE_ERROR 91
typedef struct UPDATE_IMAGE {
int x;
int y;
int w;
int h;
QImage* img;
}UPDATE_IMAGE;
typedef QVector<UPDATE_IMAGE> UpdateImages;
class UpdateData : public QObject
{
Q_OBJECT
public:
explicit UpdateData();
explicit UpdateData(int data_type);
explicit UpdateData(int data_type, uint32_t time_ms);
explicit UpdateData(const TS_RECORD_HEADER& hdr);
virtual ~UpdateData();
void set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p);
TS_RECORD_HEADER* get_header() {return m_hdr;}
TS_RECORD_RDP_POINTER* get_pointer() {return m_pointer;}
UpdateImages& get_images() {return m_images;}
const UpdateImages& get_images() const {return m_images;}
uint32_t get_time() {return m_time_ms;}
void alloc_data(uint32_t len);
void attach_data(const uint8_t* dat, uint32_t len);
int data_type() const {return m_data_type;}
uint8_t* data_buf() {return m_data_buf;}
uint32_t data_len() const {return m_data_len;}
void played_ms(uint32_t ms) {m_played_ms = ms;}
uint32_t played_ms() {return m_played_ms;}
void message(const QString& msg) {m_msg = msg;}
const QString message(){return m_msg;}
private:
void _init(void);
signals:
public slots:
private:
int m_data_type;
uint32_t m_time_ms;
uint8_t* m_data_buf;
uint32_t m_data_len;
uint32_t m_played_ms;
QString m_msg;
// for HEADER
TS_RECORD_HEADER* m_hdr;
// for POINTER
TS_RECORD_RDP_POINTER* m_pointer;
// for IMAGE
UpdateImages m_images;
};
class UpdateDataHelper {
public:
UpdateDataHelper(UpdateData* data) {
m_data = data;
}
~UpdateDataHelper() {
if(m_data)
delete m_data;
}
private:
UpdateData* m_data;
};
#endif // UPDATE_DATA_H

View File

33
client/tp-player/util.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef TP_PLAYER_UTIL_H
#define TP_PLAYER_UTIL_H
#include <QTime>
class TimeUseTest {
public:
TimeUseTest() {
m_used_ms = 0;
m_count = 0;
}
~TimeUseTest() {}
void begin() {
m_time.start();
}
void end() {
m_count++;
m_used_ms += m_time.elapsed();
}
uint32_t used() const {return m_used_ms;}
uint32_t count() const {return m_count;}
private:
QTime m_time;
uint32_t m_used_ms;
uint32_t m_count;
};
#define LOCAL8BIT(x) QString::fromLocal8Bit(x)
#endif // TP_PLAYER_UTIL_H

View File

@ -37,8 +37,9 @@
7AA2CD541F6AB9F10074C92B /* json_writer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AA2CD501F6AB9F10074C92B /* json_writer.cpp */; };
7AA2CD571F6ABA2E0074C92B /* mongoose.c in Sources */ = {isa = PBXBuildFile; fileRef = 7AA2CD561F6ABA2E0074C92B /* mongoose.c */; };
7AA2CD591F6AC0DA0074C92B /* site in Resources */ = {isa = PBXBuildFile; fileRef = 7AA2CD581F6AC0DA0074C92B /* site */; };
7AF9BF272199E3DE00BE5DBC /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF9BF1F2199E31A00BE5DBC /* libssl.a */; };
7AF9BF292199E3DF00BE5DBC /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF9BF282199E3DF00BE5DBC /* libcrypto.a */; };
7AAE4B242390EE5C007EDDE7 /* libmbedtls.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF9BF222199E32B00BE5DBC /* libmbedtls.a */; };
7AAE4B252390EE7D007EDDE7 /* libmbedcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF9BF232199E32B00BE5DBC /* libmbedcrypto.a */; };
7AAE4B262390EE7D007EDDE7 /* libmbedx509.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF9BF212199E32B00BE5DBC /* libmbedx509.a */; };
A1B7B9DD1DB53ED200809327 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1B7B9DF1DB53ED200809327 /* Localizable.strings */; };
A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D700061A5DCE8D003563E4 /* AboutWindowController.m */; };
C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C149EBFD15D5214600B1F558 /* Cocoa.framework */; };
@ -107,11 +108,10 @@
7AA2CD501F6AB9F10074C92B /* json_writer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = json_writer.cpp; path = ../../../../external/jsoncpp/src/lib_json/json_writer.cpp; sourceTree = "<group>"; };
7AA2CD561F6ABA2E0074C92B /* mongoose.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mongoose.c; path = ../../../../external/mongoose/mongoose.c; sourceTree = "<group>"; };
7AA2CD581F6AC0DA0074C92B /* site */ = {isa = PBXFileReference; lastKnownFileType = folder; path = site; sourceTree = "<group>"; };
7AF9BF1F2199E31A00BE5DBC /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssl.a; path = ../../external/macos/release/lib/libssl.a; sourceTree = "<group>"; };
7AAE4B232390E642007EDDE7 /* mongoose.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mongoose.h; path = ../../../../external/mongoose/mongoose.h; sourceTree = "<group>"; };
7AF9BF212199E32B00BE5DBC /* libmbedx509.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbedx509.a; path = ../../external/macos/release/lib/libmbedx509.a; sourceTree = "<group>"; };
7AF9BF222199E32B00BE5DBC /* libmbedtls.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbedtls.a; path = ../../external/macos/release/lib/libmbedtls.a; sourceTree = "<group>"; };
7AF9BF232199E32B00BE5DBC /* libmbedcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbedcrypto.a; path = ../../external/macos/release/lib/libmbedcrypto.a; sourceTree = "<group>"; };
7AF9BF282199E3DF00BE5DBC /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcrypto.a; path = ../../external/macos/release/lib/libcrypto.a; sourceTree = "<group>"; };
A1B7B9D31DB5361700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
A1B7B9DE1DB53ED200809327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
A1B7B9E01DB53ED700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -143,8 +143,9 @@
buildActionMask = 2147483647;
files = (
C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */,
7AF9BF292199E3DF00BE5DBC /* libcrypto.a in Frameworks */,
7AF9BF272199E3DE00BE5DBC /* libssl.a in Frameworks */,
7AAE4B262390EE7D007EDDE7 /* libmbedx509.a in Frameworks */,
7AAE4B252390EE7D007EDDE7 /* libmbedcrypto.a in Frameworks */,
7AAE4B242390EE5C007EDDE7 /* libmbedtls.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -225,6 +226,7 @@
7AA2CD551F6ABA000074C92B /* mongoose */ = {
isa = PBXGroup;
children = (
7AAE4B232390E642007EDDE7 /* mongoose.h */,
7AA2CD561F6ABA2E0074C92B /* mongoose.c */,
);
name = mongoose;
@ -248,13 +250,6 @@
path = csrc;
sourceTree = "<group>";
};
7AF9BF1E2199E0DD00BE5DBC /* mbedtls */ = {
isa = PBXGroup;
children = (
);
name = mbedtls;
sourceTree = "<group>";
};
A12D9BE61BCF2C72004F52A6 /* apple-scpt */ = {
isa = PBXGroup;
children = (
@ -286,11 +281,9 @@
C149EBFC15D5214600B1F558 /* Frameworks */ = {
isa = PBXGroup;
children = (
7AF9BF282199E3DF00BE5DBC /* libcrypto.a */,
7AF9BF232199E32B00BE5DBC /* libmbedcrypto.a */,
7AF9BF222199E32B00BE5DBC /* libmbedtls.a */,
7AF9BF212199E32B00BE5DBC /* libmbedx509.a */,
7AF9BF1F2199E31A00BE5DBC /* libssl.a */,
C149EBFD15D5214600B1F558 /* Cocoa.framework */,
C149EBFF15D5214600B1F558 /* Other Frameworks */,
);
@ -310,7 +303,6 @@
C149EC0315D5214600B1F558 /* src */ = {
isa = PBXGroup;
children = (
7AF9BF1E2199E0DD00BE5DBC /* mbedtls */,
7A45423D2196E32800FEB5B4 /* cfg */,
7AD3E8741F6A7CC600D2EB48 /* csrc */,
A12D9BE61BCF2C72004F52A6 /* apple-scpt */,
@ -367,7 +359,7 @@
C149EBF015D5214600B1F558 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0930;
LastUpgradeCheck = 1120;
ORGANIZATIONNAME = TP4A;
TargetAttributes = {
C149EBF815D5214600B1F558 = {
@ -381,7 +373,7 @@
};
buildConfigurationList = C149EBF315D5214600B1F558 /* Build configuration list for PBXProject "TP-Assist" */;
compatibilityVersion = "Xcode 10.0";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@ -606,13 +598,14 @@
C149EC1815D5214600B1F558 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "src/TP-Assist-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
MG_ENABLE_SSL,
"MG_SSL_IF=MG_SSL_IF_MBEDTLS",
);
HEADER_SEARCH_PATHS = (
../../common/teleport,
@ -634,11 +627,14 @@
C149EC1915D5214600B1F558 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "src/TP-Assist-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = MG_ENABLE_SSL;
GCC_PREPROCESSOR_DEFINITIONS = (
MG_ENABLE_SSL,
"MG_SSL_IF=MG_SSL_IF_MBEDTLS",
);
HEADER_SEARCH_PATHS = (
../../common/teleport,
../../common/libex/include,

View File

@ -6,58 +6,17 @@ on scriptRun(argsCmd, argsProfile, argsTitle)
end scriptRun
on CommandRun(theCmd, theProfile, theTitle)
tell application "iTerm"
if it is not running then
tell application "iTerm"
activate
delay 0.5
try
close first window
end try
end tell
tell application "iTerm"
try
create window with profile theProfile
on error msg
create window with profile "Default"
end try
tell the current window
tell the current session
delay 0.5
set name to theTitle
set profile to theProfile
write text theCmd
delay 0.5
write text ""
end tell
end tell
end tell
else
--assume that iTerm is open and open a new tab
try
try
tell application "iTerm"
if it is not running then
tell application "iTerm"
activate
tell the current window
try
create tab with profile theProfile
on error msg
create tab with profile "Default"
end try
tell the current tab
tell the current session
delay 0.5
set name to theTitle
write text theCmd
delay 0.5
write text ""
end tell
end tell
end tell
delay 0.5
try
close first window
end try
end tell
on error msg
-- if all iTerm windows are closed the app stays open. In this scenario iTerm has
-- no "current window" and will give an error when trying to create the new tab.
tell application "iTerm"
try
create window with profile theProfile
@ -68,13 +27,59 @@ on CommandRun(theCmd, theProfile, theTitle)
tell the current session
delay 0.5
set name to theTitle
set profile to theProfile
write text theCmd
delay 0.5
write text ""
end tell
end tell
end tell
end try
end if
end tell
else
--assume that iTerm is open and open a new tab
try
tell application "iTerm"
activate
tell the current window
try
create tab with profile theProfile
on error msg
create tab with profile "Default"
end try
tell the current tab
tell the current session
delay 0.5
set name to theTitle
write text theCmd
delay 0.5
write text ""
end tell
end tell
end tell
end tell
on error msg
-- if all iTerm windows are closed the app stays open. In this scenario iTerm has
-- no "current window" and will give an error when trying to create the new tab.
tell application "iTerm"
try
create window with profile theProfile
on error msg
create window with profile "Default"
end try
tell the current window
tell the current session
delay 0.5
set name to theTitle
write text theCmd
delay 0.5
write text ""
end tell
end tell
end tell
end try
end if
end tell
on error msg
display dialog "ERROR: " & msg
end try
end CommandRun

View File

@ -1,76 +1,83 @@
on scriptRun(argsCmd, argsProfile, argsTitle)
set theCmd to (argsCmd)
set theCmd to (argsCmd)
set theProfile to (argsProfile)
set theTitle to (argsTitle)
CommandRun(theCmd, theProfile, theTitle)
end scriptRun
on CommandRun(theCmd, theProfile, theTitle)
tell application "Terminal"
if it is not running then
--if this is the first time Terminal is running you have specify window 1
--if you dont do this you will get two windows and the title wont be set
activate
delay 1.0
set newTerm to do script theCmd in window 1
set newTerm's current settings to settings set theProfile
set custom title of front window to theTitle
delay 1.0
reopen
activate
tell application "System Events" to key code 36
else
--Terminal is running get the window count
set windowCount to (count every window)
if windowCount = 0 then
--Terminal is running but no windows are open
--run our script in a new window
try
tell application "Terminal"
if it is not running then
--if this is the first time Terminal is running you have specify window 1
--if you dont do this you will get two windows and the title wont be set
activate
delay 3.0
set newTerm to do script theCmd in window 1
set newTerm's current settings to settings set theProfile
set custom title of front window to theTitle
delay 1.0
reopen
activate
do script theCmd in window 1
set current settings of selected tab of front window to settings set theProfile
set title displays custom title of front window to true
set custom title of selected tab of front window to theTitle
delay 1.0
reopen
activate
tell application "System Events" to key code 36
tell application "System Events" to key code 36
else
--Terminal is running and we have a window run in a new tab
reopen
activate
tell application "System Events"
tell process "Terminal"
delay 0.5
keystroke "t" using {command down}
--Terminal is running get the window count
set windowCount to (count every window)
if windowCount = 0 then
--Terminal is running but no windows are open
--run our script in a new window
reopen
activate
do script theCmd in window 1
set current settings of selected tab of front window to settings set theProfile
set title displays custom title of front window to true
set custom title of selected tab of front window to theTitle
delay 1.0
reopen
activate
tell application "System Events" to key code 36
else
--Terminal is running and we have a window run in a new tab
reopen
activate
tell application "System Events"
tell process "Terminal"
delay 0.5
keystroke "t" using {command down}
end tell
end tell
end tell
reopen
activate
do script theCmd in front window
set current settings of selected tab of front window to settings set theProfile
set title displays custom title of front window to true
set custom title of selected tab of front window to theTitle
delay 1.0
reopen
activate
tell application "System Events" to key code 36
reopen
activate
do script theCmd in front window
set current settings of selected tab of front window to settings set theProfile
set title displays custom title of front window to true
set custom title of selected tab of front window to theTitle
delay 1.0
reopen
activate
tell application "System Events" to key code 36
end if
--set current settings of selected tab of front window to settings set theProfile
--set title displays custom title of front window to true
--set custom title of selected tab of front window to theTitle
end if
# set current settings of selected tab of front window to settings set theProfile
# set title displays custom title of front window to true
# set custom title of selected tab of front window to theTitle
end if
end tell
end tell
on error msg
display dialog "ERROR: " & msg
end try
end CommandRun

View File

@ -32,7 +32,7 @@
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2018,保留所有权利。</p>
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020,保留所有权利。</p>
</div>
</div>

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<!--[if IE 8]>
<html lang="en" class="ie8"><![endif]-->
<!--[if !IE]><!-->
<html lang="zh_CN">
<!--<![endif]-->
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>TELEPORT助手</title>
<link rel="shortcut icon" href="favicon.png">
<link href="plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.info-box {
padding:20px;
margin:40px;
border:1px solid #78b17c;
background-color:#e4ffe5;
font-size: 140%;
}
.warning-box {
padding:20px;
margin:40px;
border:1px solid #eac781;
background-color: #f9f5d7;
}
</style>
</head>
<body>
<div class="header">
<div class="container">
<span class="title"><i class="fa fa-cog fa-fw"></i> Teleport 助手</span>
<span class="sub-title" id="version"></span>
</div>
</div>
<div class="header-fix"></div>
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020保留所有权利。</p>
</div>
</div>
<div class="container">
<div class="content">
<div class="info-box">
Teleport助手工作正常
</div>
<div class="warning-box">
<p>如果在使用 HTTPS 方式访问 teleport 的 web 服务时检测不到助手,请<a href="https://127.0.0.1:50023" target="_blank">点击这里</a>,查看页面是否能够正常显示。</p>
<p>因为助手在配合HTTPS访问时使用了<strong>自签名证书</strong>,而自签名证书的颁发机构的根证书<strong>默认不被浏览器信任</strong>,因此,还<strong>需要将其设置为浏览器信任的根证书</strong>才行,根据浏览器的不同,具体设置方法有两种:</p>
<p><strong>Chrome/IE/Edge/Opera 等浏览器</strong></p>
<ol>
<li>在桌面的助手快捷方式上点击右键,然后选择“打开文件所在的位置”;</li>
<li>右键点击 <strong>cacert.cer</strong>,在弹出菜单中选择“安装证书”;</li>
<li>在打开的“证书导入向导”对话框中选择“当前用户”,点击下一步;</li>
<li>选择“将所有的证书都放入下列存储”,然后点击“浏览”按钮;</li>
<li>在打开的“选择证书存储”对话框中选择<strong>“受信任的根证书颁发机构”</strong>,点击确定;</li>
<li>点击“下一步”,然后点击“完成”;</li>
<li>系统提示“导入成功”,大功告成。</li>
</ol>
<p><strong>FireFox火狐浏览器</strong></p>
<ol>
<li>打开火狐浏览器的选项页面;</li>
<li>点击左侧的“隐私与安全”,然后滚动页面到底部,点击“查看证书”按钮;</li>
<li>在打开的“证书管理器”对话框中选择“证书颁发机构”选项卡;</li>
<li>点击对话框底部的“导入”按钮,然后选择 <strong>cacert.cer</strong> 文件并点击“打开”按钮;</li>
<li>在“下载证书”对话框中,勾选“信任由此证书颁发机构来标识网站”,然后点击“确定”;</li>
<li>点击“确定”来关闭证书管理器对话框,大功告成。</li>
</ol>
<p><strong>注意:</strong>导入证书后,请再次<a href="https://127.0.0.1:50023" target="_blank">点击这里</a>,查看页面是否能够正常显示。</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -19,61 +19,61 @@
<window title="About Teleport Assist" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="750" y="574" width="315" height="204"/>
<rect key="screenRect" x="0.0" y="0.0" width="2048" height="1129"/>
<rect key="contentRect" x="750" y="574" width="329" height="217"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="315" height="204"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6kW-hd-spe">
<rect key="frame" x="125" y="126" width="64" height="64"/>
<rect key="frame" x="125" y="133" width="64" height="64"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageAlignment="top" imageScaling="proportionallyDown" image="teleport" id="VBv-EA-L5r"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="APf-vF-1w0">
<rect key="frame" x="18" y="99" width="279" height="17"/>
<rect key="frame" x="18" y="91" width="293" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Program Name and Tag Line" id="tb3-eK-HAT">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="Program Name and Tag Line" id="tb3-eK-HAT">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LTk-GB-fgJ">
<rect key="frame" x="18" y="73" width="279" height="17"/>
<rect key="frame" x="18" y="76" width="293" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Version" id="6fl-Xm-UPS">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rfT-bT-B7g">
<rect key="frame" x="18" y="40" width="293" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Copyright" id="Cmu-CQ-3V9">
<font key="font" metaFont="system" size="10"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="soF-wq-5hG">
<rect key="frame" x="14" y="13" width="287" height="32"/>
<rect key="frame" x="14" y="13" width="301" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Homepage" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uUM-88-32s">
<buttonCell key="cell" type="push" title="url" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uUM-88-32s">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
<font key="font" metaFont="system" size="12"/>
</buttonCell>
<connections>
<action selector="btnHomepage:" target="-2" id="ZGb-bg-pCR"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rfT-bT-B7g">
<rect key="frame" x="18" y="51" width="279" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Copyright" id="Cmu-CQ-3V9">
<font key="font" metaFont="system" size="10"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="50.5" y="233"/>
<point key="canvasLocation" x="57.5" y="239.5"/>
</window>
</objects>
<resources>

View File

@ -3,12 +3,12 @@
TPAssist
*/
"app_name" = "Teleport助手";
"app_name" = "Teleport Assist";
//=============================================
// for About Window
//=============================================
"version" = "Version: ";
"app_full_name" = "Teleport Assist for macOS";
"copyright" = "Copyright © 2017~2018 TP4A. All rights reserved.";
"version" = "";
"app_full_name" = "Teleport Assist";
"copyright" = "Copyright © 2017~2019, tp4a.com. All rights reserved.";
"visit_tp4a_website" = "Visit Teleport Website"

View File

@ -17,19 +17,21 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.2.0</string>
<string>3.5.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>3.2.0</string>
<string>3.5.5</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSUIElement</key>
<true/>
<key>NSAppleEventsUsageDescription</key>
<string></string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017~2018 TP4A. All rights reserved.</string>
<string>Copyright © 2017~2019, tp4a.com. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@ -41,9 +41,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::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");
return false;
@ -123,12 +126,21 @@ bool TsCfg::_parse_app(const Json::Value& m_root, const ex_astr& str_app, APP_CO
bool TsCfg::_load(const ex_astr& str_json) {
Json::Reader jreader;
// Json::Reader jreader;
//
// if (!jreader.parse(str_json.c_str(), m_root)) {
// EXLOGE("can not parse new config data, not in json format?\n");
// return false;
// }
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?\n");
return false;
}
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;
}
//===================================
// check ssh config

View File

@ -32,3 +32,11 @@ bool TsEnv::init(const char* cfg_file, const char* res_path)
return true;
}
extern "C" {
int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) {
(void) ctx;
while (len--) *buf++ = (arc4random() % 255);
return 0;
}
}

View File

@ -32,9 +32,9 @@ int http_rpc_start(void* app) {
EXLOGE("[ERROR] can not start HTTP-RPC listener, maybe port %d is already in use.\n", TS_HTTP_RPC_PORT);
return -1;
}
EXLOGW("[rpc] TeleportAssist-HTTP-RPC ready on localhost:%d\n", TS_HTTP_RPC_PORT);
EXLOGW("[rpc] TeleportAssist-HTTP-RPC ready on 127.0.0.1:%d\n", TS_HTTP_RPC_PORT);
if(!g_http_interface.start())
return -2;
@ -43,9 +43,9 @@ int http_rpc_start(void* app) {
EXLOGE("[ERROR] can not start HTTPS-RPC listener, maybe port %d is already in use.\n", TS_HTTPS_RPC_PORT);
return -1;
}
EXLOGW("[rpc] TeleportAssist-HTTPS-RPC ready on localhost:%d\n", TS_HTTPS_RPC_PORT);
EXLOGW("[rpc] TeleportAssist-HTTPS-RPC ready on 127.0.0.1:%d\n", TS_HTTPS_RPC_PORT);
if(!g_https_interface.start())
return -2;
@ -106,22 +106,21 @@ TsHttpRpc::~TsHttpRpc()
mg_mgr_free(&m_mg_mgr);
}
bool TsHttpRpc::init_http()
{
char addr[128] = { 0 };
ex_strformat(addr, 128, "tcp://localhost:%d", TS_HTTP_RPC_PORT);
bool TsHttpRpc::init_http() {
struct mg_connection* nc = NULL;
char addr[128] = { 0 };
ex_strformat(addr, 128, "tcp://127.0.0.1:%d", TS_HTTP_RPC_PORT);
nc = mg_bind(&m_mg_mgr, addr, _mg_event_handler);
if (nc == NULL) {
EXLOGE("[rpc] TsHttpRpc::init_http() localhost:%d\n", TS_HTTP_RPC_PORT);
if (!nc) {
EXLOGE("[rpc] TsHttpRpc::init 127.0.0.1:%d\n", TS_HTTP_RPC_PORT);
return false;
}
nc->user_data = this;
mg_set_protocol_http_websocket(nc);
return _on_init();
}
@ -135,27 +134,27 @@ bool TsHttpRpc::init_https()
ex_wstr2astr(file_ssl_cert, _ssl_cert);
ex_astr _ssl_key;
ex_wstr2astr(file_ssl_key, _ssl_key);
const char *err = NULL;
struct mg_bind_opts bind_opts;
memset(&bind_opts, 0, sizeof(bind_opts));
bind_opts.ssl_cert = _ssl_cert.c_str();
bind_opts.ssl_key = _ssl_key.c_str();
bind_opts.error_string = &err;
char addr[128] = { 0 };
ex_strformat(addr, 128, "tcp://localhost:%d", TS_HTTPS_RPC_PORT);
ex_strformat(addr, 128, "tcp://127.0.0.1:%d", TS_HTTPS_RPC_PORT);
struct mg_connection* nc = NULL;
nc = mg_bind_opt(&m_mg_mgr, addr, _mg_event_handler, bind_opts);
if (nc == NULL) {
EXLOGE("[rpc] TsHttpRpc::init_https() localhost:%d\n", TS_HTTPS_RPC_PORT);
if (!nc) {
EXLOGE("[rpc] TsHttpRpc::init 127.0.0.1:%d\n", TS_HTTPS_RPC_PORT);
return false;
}
nc->user_data = this;
mg_set_protocol_http_websocket(nc);
return _on_init();
}
@ -182,7 +181,7 @@ void TsHttpRpc::_thread_loop(void)
{
mg_mgr_poll(&m_mg_mgr, 500);
}
EXLOGV("[core] rpc main loop end.\n");
}
@ -225,23 +224,25 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
EXLOGV("[rpc] got %s request: %s\n", dbg_method, uri.c_str());
#endif
ex_astr ret_buf;
bool b_is_index = false;
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 Assistor</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 Assistor works fine.</div></body></html>";
ex_wstr2astr(page, ret_buf, EX_CODEPAGE_UTF8);
// 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 Assistor works fine.</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]);
// nc->flags |= MG_F_SEND_AND_CLOSE;
// return;
// }
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: text/html\r\n\r\n%s", ret_buf.size() - 1, &ret_buf[0]);
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
if (uri == "/config")
{
uri = "/index.html";
b_is_index = true;
}
if (uri == "/") {
uri = "/status.html";
b_is_html = true;
}
else if (uri == "/config") {
uri = "/index.html";
b_is_html = true;
}
ex_astr temp;
size_t offset = uri.find("/", 1);
@ -262,24 +263,24 @@ 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.c_str());
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
}
ex_astr file_suffix;
offset = uri.rfind(".");
if (offset > 0)
{
file_suffix = uri.substr(offset, uri.length());
}
ex_wstr2astr(g_env.m_site_path, temp);
ex_astr index_path = temp + uri;
FILE* file = ex_fopen(index_path.c_str(), "rb");
if (file)
@ -295,25 +296,23 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
fseek(file, 0, SEEK_SET);
ret = fread(buf, 1, file_size, file);
fclose(file);
ex_astr content_type = _this->get_content_type(file_suffix);
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: %s\r\n\r\n", file_size, content_type.c_str());
mg_send(nc, buf, (int)file_size);
delete []buf;
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
else if (b_is_index)
{
} else if (b_is_html) {
ex_wstr page = L"<html lang=\"zh_CN\"><html><head><title>404 Not Found</title></head><body bgcolor=\"white\"><center><h1>404 Not Found</h1></center><hr><center><p>Teleport Assistor configuration page not found.</p></center></body></html>";
ex_wstr2astr(page, ret_buf, EX_CODEPAGE_UTF8);
mg_printf(nc, "HTTP/1.0 404 File Not Found\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: text/html\r\n\r\n%s", ret_buf.size() - 1, &ret_buf[0]);
mg_printf(nc, "HTTP/1.0 404 File Not Found\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: text/html\r\n\r\n%s", ret_buf.length(), ret_buf.c_str());
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
}
break;
default:
@ -336,7 +335,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; // skip first charactor, it must be '/'
size_t i = 0;
for (i = pos_start; i < req->uri.len; ++i)
@ -349,7 +348,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; // skip current split chactor.
}
}
if (pos_start < req->uri.len)
@ -397,7 +396,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
if (func_args.length() > 0)
{
// 将参数进行 url-decode 解码
// decode param with url-decode.
size_t len = func_args.length() * 2;
ex_chars sztmp;
sztmp.resize(len);
@ -448,44 +447,50 @@ 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}
// return {"code":123}
Json::FastWriter jr_writer;
Json::Value jr_root;
jr_root["code"] = errcode;
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::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_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}
void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
// param: {"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 return {"code":0, "data":{"sid":"0123abcde"}}
// RDP return {"code":0, "data":{"sid":"0123abcde0A"}}
Json::Reader jreader;
Json::Value jsRoot;
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = func_args.c_str();
if (!jreader.parse(func_args.c_str(), jsRoot))
{
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
Json::Value jsRoot;
ex_astr err;
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
if (!jsRoot.isObject())
{
_create_json_ret(buf, TPE_PARAM);
return;
}
// 判断参数是否正确
// check param
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()
@ -512,10 +517,9 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
ex_astr s_exec;
ex_astr s_arg;
ex_astrs s_argv;
if (pro_type == TP_PROTOCOL_TYPE_RDP)
{
if (pro_type == TP_PROTOCOL_TYPE_RDP) {
//==============================================
// RDP
//==============================================
@ -524,10 +528,10 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
_create_json_ret(buf, TPE_NOT_EXISTS);
return;
}
bool flag_clipboard = (protocol_flag & TP_FLAG_RDP_CLIPBOARD);
bool flag_disk = (protocol_flag & TP_FLAG_RDP_DISK);
bool flag_console = (protocol_flag & TP_FLAG_RDP_CONSOLE);
bool flag_clipboard = ((protocol_flag & TP_FLAG_RDP_CLIPBOARD) == TP_FLAG_RDP_CLIPBOARD);
bool flag_disk = ((protocol_flag & TP_FLAG_RDP_DISK) == TP_FLAG_RDP_DISK);
bool flag_console = ((protocol_flag & TP_FLAG_RDP_CONSOLE) == TP_FLAG_RDP_CONSOLE);
int rdp_w = 800;
int rdp_h = 640;
@ -578,9 +582,9 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
{
szPwd[i] = '*';
}
//ex_astr2wstr(real_sid, w_sid);
//w_exe_path = _T("\"");
//w_exe_path += g_cfg.rdp_app + _T("\" ");
//w_exe_path += g_cfg.rdp_cmdline;
@ -589,24 +593,49 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
//w_exe_path = _T("xfreerdp -u {user_name} {size} {console} ");
//s_exec = "/usr/local/Cellar/freerdp/1.0.2_1/bin/xfreerdp";
s_exec = g_cfg.rdp.application;
s_argv.push_back(s_exec.c_str());
s_arg = g_cfg.rdp.cmdline;
sid = "02" + real_sid;
// s_argv.push_back("/f");
s_argv.push_back("/sec:tls");
s_argv.push_back("-wallpaper");
s_argv.push_back("-themes");
// Ignore certificate
s_argv.push_back("/cert-ignore");
// Automatically accept certificate on first connect
s_argv.push_back("/cert-tofu");
ex_astr _tmp_pass = "/p:PLACEHOLDER";
//_tmp_pass += szPwd;
s_argv.push_back(_tmp_pass);
//#if 0
//s_argv.push_back(s_exec.c_str());
{
ex_astr username = "02" + real_sid;
s_argv.push_back("-u");
s_argv.push_back(username.c_str());
// ex_astr username = "02" + real_sid;
// s_argv.push_back("/u:");
// s_argv.push_back(username.c_str());
if (rdp_w == 0 || rdp_h == 0) {
s_argv.push_back("-f");
s_argv.push_back("/f");
}
else {
char sz_size[64] = {0};
ex_strformat(sz_size, 63, "%dx%d", rdp_w, rdp_h);
s_argv.push_back("-g");
s_argv.push_back(sz_size);
}
// char sz_size[64] = {0};
// ex_strformat(sz_size, 63, "%dx%d", rdp_w, rdp_h);
// s_argv.push_back("-g");
// s_argv.push_back(sz_size);
char sz_width[64] = {0};
ex_strformat(sz_width, 63, "/w:%d", rdp_w);
s_argv.push_back(sz_width);
char sz_height[64] = {0};
ex_strformat(sz_height, 63, "/h:%d", rdp_h);
s_argv.push_back(sz_height);
}
if (flag_console && rdp_console)
s_argv.push_back("/admin");
@ -619,17 +648,16 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
// s_argv.push_back("+drives");
// else
// s_argv.push_back("-drives");
{
char sz_temp[128] = {0};
ex_strformat(sz_temp, 127, "%s:%d", teleport_ip.c_str(), teleport_port);
s_argv.push_back(sz_temp);
}
//
// {
// char sz_temp[128] = {0};
// ex_strformat(sz_temp, 127, "%s:%d", teleport_ip.c_str(), teleport_port);
// s_argv.push_back(sz_temp);
// }
}
//#endif
}
else if (pro_type == TP_PROTOCOL_TYPE_SSH)
{
else if (pro_type == TP_PROTOCOL_TYPE_SSH) {
//==============================================
// SSH
//==============================================
@ -638,11 +666,11 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
{
if(g_cfg.ssh.name == "terminal" || g_cfg.ssh.name == "iterm2") {
char szCmd[1024] = {0};
ex_strformat(szCmd, 1023, "ssh %s@%s -p %d", sid.c_str(), teleport_ip.c_str(), teleport_port);
ex_strformat(szCmd, 1023, "ssh %s@%s -p %d -o \"StrictHostKeyChecking no\"", sid.c_str(), teleport_ip.c_str(), teleport_port);
char szTitle[128] = {0};
ex_strformat(szTitle, 127, "TP#%s", real_host_ip.c_str());
int ret = AppDelegate_start_ssh_client(g_app, szCmd, g_cfg.ssh.name.c_str(), g_cfg.ssh.cmdline.c_str(), szTitle);
if(ret == 0)
_create_json_ret(buf, TPE_OK);
@ -650,20 +678,20 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
_create_json_ret(buf, TPE_FAILED);
return;
}
if(g_cfg.ssh.application.length() == 0) {
_create_json_ret(buf, TPE_NOT_EXISTS);
return;
}
s_exec = g_cfg.ssh.application;
s_argv.push_back(s_exec.c_str());
s_arg = g_cfg.ssh.cmdline;
}
else
{
// sorry, SFTP not supported yet for macOS.
// _create_json_ret(buf, TPE_NOT_IMPLEMENT);
// return;
@ -672,16 +700,14 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
_create_json_ret(buf, TPE_NOT_EXISTS);
return;
}
s_exec = g_cfg.sftp.application;
s_argv.push_back(s_exec.c_str());
s_arg = g_cfg.sftp.cmdline;
s_arg = g_cfg.sftp.cmdline;
}
}
else if (pro_type == TP_PROTOCOL_TYPE_TELNET)
{
else if (pro_type == TP_PROTOCOL_TYPE_TELNET) {
//==============================================
// TELNET
//==============================================
@ -716,7 +742,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
// s_arg = g_cfg.telnet.cmdline;
}
//---- split s_arg and push to s_argv ---
ex_astr::size_type p1 = 0;
ex_astr::size_type p2 = 0;
@ -739,7 +765,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
ex_astr _t;
_t.assign(tmp, p1, p2 - p1);
tmp.erase(0, p2 + 2);
s_argv.push_back(_t);
} else {
p1 = 0;
@ -754,12 +780,12 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
ex_astr _t;
_t.assign(tmp, p1, p2 - p1);
tmp.erase(0, p2 + 1);
s_argv.push_back(_t);
}
}
Json::Value root_ret;
ex_astr utf8_path = s_exec;
@ -774,18 +800,18 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
utf8_path += " ";
utf8_path += (*it);
}
root_ret["path"] = utf8_path;
// for macOS, Create Process should be fork()/exec()...
pid_t processId;
if ((processId = fork()) == 0) {
int i = 0;
char** _argv = (char**)calloc(s_argv.size()+1, sizeof(char*));
if (!_argv)
return;
for (i = 0; i < s_argv.size(); ++i)
{
_argv[i] = ex_strdup(s_argv[i].c_str());
@ -800,42 +826,47 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
}
}
free(_argv);
} else if (processId < 0) {
root_ret["code"] = TPE_FAILED;
} else {
root_ret["code"] = TPE_OK;
}
// root_ret["code"] = TPE_OK;
_create_json_ret(buf, root_ret);
}
void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf)
{
void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
_create_json_ret(buf, TPE_NOT_IMPLEMENT);
}
void TsHttpRpc::_rpc_func_get_config(const ex_astr& func_args, ex_astr& buf)
{
void TsHttpRpc::_rpc_func_get_config(const ex_astr& func_args, ex_astr& buf) {
Json::Value jr_root;
jr_root["code"] = 0;
jr_root["data"] = g_cfg.get_root();
_create_json_ret(buf, jr_root);
}
void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf)
{
Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot))
{
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf) {
// Json::Reader jreader;
// Json::Value jsRoot;
// if (!jreader.parse(func_args.c_str(), jsRoot))
// {
// _create_json_ret(buf, TPE_JSON_FORMAT);
// return;
// }
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = func_args.c_str();
Json::Value jsRoot;
ex_astr err;
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
if(!g_cfg.save(func_args))
_create_json_ret(buf, TPE_FAILED);
@ -848,7 +879,7 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
#if 0
Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
@ -859,7 +890,7 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
// return;
// }
// int action = jsRoot["action"].asUInt();
AppDelegate_select_app(g_app);
_create_json_ret(buf, TPE_FAILED);
@ -884,8 +915,7 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
#endif
}
void TsHttpRpc::_rpc_func_get_version(const ex_astr& func_args, ex_astr& buf)
{
void TsHttpRpc::_rpc_func_get_version(const ex_astr& func_args, ex_astr& buf) {
Json::Value root_ret;
ex_wstr w_version = TP_ASSIST_VER;
ex_astr version;

View File

@ -1,6 +1,6 @@
#ifndef __TS_ASSIST_VER_H__
#define __TS_ASSIST_VER_H__
#define TP_ASSIST_VER L"3.2.0"
#endif // __TS_ASSIST_VER_H__
#ifndef __TS_ASSIST_VER_H__
#define __TS_ASSIST_VER_H__
#define TP_ASSIST_VER L"3.5.5"
#endif // __TS_ASSIST_VER_H__

View File

@ -13,7 +13,7 @@
// for About Window
//=============================================
"about " = "关于 ";
"version" = "版本:";
"app_full_name" = "Teleport助手 - macOS";
"copyright" = "© 2017~2018 TP4A保留所有权利。";
"version" = "";
"app_full_name" = "Teleport助手";
"copyright" = "© 2017~2019tp4a.com。保留所有权利。";
"visit_tp4a_website" = "访问 Teleport 网站";

View File

@ -96,7 +96,7 @@ INT_PTR CALLBACK eomDlgMainProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARA
case IDM_OPEN_CONFIG:
{
ShellExecute(nullptr, _T("open"), _T("http://localhost:50022/config"), nullptr, nullptr, SW_SHOW);
ShellExecute(nullptr, _T("open"), _T("http://127.0.0.1:50022/config"), nullptr, nullptr, SW_SHOW);
return TRUE;
}break;

View File

@ -32,7 +32,7 @@
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2018,保留所有权利。</p>
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020,保留所有权利。</p>
</div>
</div>
@ -47,6 +47,7 @@
<ul>
<li><span class="arg-varb">{host_ip}</span> 替换主机IP地址</li>
<li><span class="arg-varb">{host_port}</span> 替换主机端口号</li>
<li><span class="arg-varb">{host_name}</span> 替换主机名称</li>
<li><span class="arg-varb">{user_name}</span> 替换用户名</li>
<li><span class="arg-varb">{real_ip}</span> 替换为远程主机真实IP仅用于显示例如客户端的窗口标题或标签页标题等</li>
<li><span class="arg-varb">{assist_tools_path}</span> 替换为助手工具所在的tools目录的绝对路径</li>

View File

@ -1,6 +1,7 @@
"use strict";
var g_url_base = 'http://localhost:50022';
//var g_url_base = 'http://localhost:50022';
var g_url_base = 'http://127.0.0.1:50022';
var g_cfg = null;

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<!--[if IE 8]>
<html lang="en" class="ie8"><![endif]-->
<!--[if !IE]><!-->
<html lang="zh_CN">
<!--<![endif]-->
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>TELEPORT助手</title>
<link rel="shortcut icon" href="favicon.png">
<link href="plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.info-box {
padding:20px;
margin:40px;
border:1px solid #78b17c;
background-color:#e4ffe5;
font-size: 140%;
}
.warning-box {
padding:20px;
margin:40px;
border:1px solid #eac781;
background-color: #f9f5d7;
}
</style>
</head>
<body>
<div class="header">
<div class="container">
<span class="title"><i class="fa fa-cog fa-fw"></i> Teleport 助手</span>
<span class="sub-title" id="version"></span>
</div>
</div>
<div class="header-fix"></div>
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020保留所有权利。</p>
</div>
</div>
<div class="container">
<div class="content">
<div class="info-box">
Teleport助手工作正常
</div>
<div class="warning-box">
<p>如果在使用 HTTPS 方式访问 teleport 的 web 服务时检测不到助手,请<a href="https://127.0.0.1:50023" target="_blank">点击这里</a>,查看页面是否能够正常显示。</p>
<p>因为助手在配合HTTPS访问时使用了<strong>自签名证书</strong>,而自签名证书的颁发机构的根证书<strong>默认不被浏览器信任</strong>,因此,还<strong>需要将其设置为浏览器信任的根证书</strong>才行,根据浏览器的不同,具体设置方法有两种:</p>
<p><strong>Chrome/IE/Edge/Opera 等浏览器</strong></p>
<ol>
<li>在桌面的助手快捷方式上点击右键,然后选择“打开文件所在的位置”;</li>
<li>右键点击 <strong>cacert.cer</strong>,在弹出菜单中选择“安装证书”;</li>
<li>在打开的“证书导入向导”对话框中选择“当前用户”,点击下一步;</li>
<li>选择“将所有的证书都放入下列存储”,然后点击“浏览”按钮;</li>
<li>在打开的“选择证书存储”对话框中选择<strong>“受信任的根证书颁发机构”</strong>,点击确定;</li>
<li>点击“下一步”,然后点击“完成”;</li>
<li>系统提示“导入成功”,大功告成。</li>
</ol>
<p><strong>FireFox火狐浏览器</strong></p>
<ol>
<li>打开火狐浏览器的选项页面;</li>
<li>点击左侧的“隐私与安全”,然后滚动页面到底部,点击“查看证书”按钮;</li>
<li>在打开的“证书管理器”对话框中选择“证书颁发机构”选项卡;</li>
<li>点击对话框底部的“导入”按钮,然后选择 <strong>cacert.cer</strong> 文件并点击“打开”按钮;</li>
<li>在“下载证书”对话框中,勾选“信任由此证书颁发机构来标识网站”,然后点击“确定”;</li>
<li>点击“确定”来关闭证书管理器对话框,大功告成。</li>
</ol>
<p><strong>注意:</strong>导入证书后,请再次<a href="https://127.0.0.1:50023" target="_blank">点击这里</a>,查看页面是否能够正常显示。</p>
</div>
</div>
</div>
</body>
</html>

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

@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tp_assist", "tp_assist.vs2015.vcxproj", "{63B7A8F2-9722-487C-A92A-3DB5D8CA1473}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63B7A8F2-9722-487C-A92A-3DB5D8CA1473}.Debug|x86.ActiveCfg = Debug|Win32
{63B7A8F2-9722-487C-A92A-3DB5D8CA1473}.Debug|x86.Build.0 = Debug|Win32
{63B7A8F2-9722-487C-A92A-3DB5D8CA1473}.Release|x86.ActiveCfg = Release|Win32
{63B7A8F2-9722-487C-A92A-3DB5D8CA1473}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -1,185 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{63B7A8F2-9722-487C-A92A-3DB5D8CA1473}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>tp_assist</RootNamespace>
<ProjectName>tp_assist</ProjectName>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\out\client\$(PlatformTarget)\$(Configuration)\</OutDir>
<IntDir>..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\</IntDir>
<IncludePath>C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\out\client\$(PlatformTarget)\$(Configuration)\</OutDir>
<IntDir>..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<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>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<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>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\common\libex\include\ex.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_const.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_ini.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_log.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_path.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_platform.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_str.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_thread.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_types.h" />
<ClInclude Include="..\..\common\libex\include\ex\ex_util.h" />
<ClInclude Include="..\..\common\teleport\teleport_const.h" />
<ClInclude Include="..\..\external\mongoose\mongoose.h" />
<ClInclude Include="dlg_main.h" />
<ClInclude Include="msocketx.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="tp_assist.h" />
<ClInclude Include="ts_cfg.h" />
<ClInclude Include="ts_const.h" />
<ClInclude Include="ts_env.h" />
<ClInclude Include="ts_http_rpc.h" />
<ClInclude Include="ts_network.h" />
<ClInclude Include="ts_ver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\common\libex\src\ex_ini.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_log.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_path.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_str.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_thread.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_util.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\external\jsoncpp\src\lib_json\json_reader.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\external\jsoncpp\src\lib_json\json_value.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\external\jsoncpp\src\lib_json\json_writer.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\external\mongoose\mongoose.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="dlg_main.cpp" />
<ClCompile Include="msocketx.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="tp_assist.cpp" />
<ClCompile Include="ts_cfg.cpp" />
<ClCompile Include="ts_env.cpp" />
<ClCompile Include="ts_http_rpc.cpp" />
<ClCompile Include="ts_network.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="tp_assist.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="res\tp.ico" />
<Image Include="res\tp_small.ico" />
<Image Include="res\tray_normal.ico" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\external\jsoncpp\src\lib_json\json_valueiterator.inl" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,180 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="dlg_main.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="tp_assist.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="ts_http_rpc.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="ts_network.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="msocketx.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="ts_env.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="ts_cfg.cpp">
<Filter>main app</Filter>
</ClCompile>
<ClCompile Include="..\..\external\jsoncpp\src\lib_json\json_reader.cpp">
<Filter>jsoncpp</Filter>
</ClCompile>
<ClCompile Include="..\..\external\jsoncpp\src\lib_json\json_value.cpp">
<Filter>jsoncpp</Filter>
</ClCompile>
<ClCompile Include="..\..\external\jsoncpp\src\lib_json\json_writer.cpp">
<Filter>jsoncpp</Filter>
</ClCompile>
<ClCompile Include="..\..\external\mongoose\mongoose.c">
<Filter>mongoose</Filter>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_str.cpp">
<Filter>libex\src</Filter>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_util.cpp">
<Filter>libex\src</Filter>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_path.cpp">
<Filter>libex\src</Filter>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_ini.cpp">
<Filter>libex\src</Filter>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_log.cpp">
<Filter>libex\src</Filter>
</ClCompile>
<ClCompile Include="..\..\common\libex\src\ex_thread.cpp">
<Filter>libex\src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Resource.h">
<Filter>resource</Filter>
</ClInclude>
<ClInclude Include="dlg_main.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="tp_assist.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="ts_http_rpc.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="ts_network.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="ts_env.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="ts_cfg.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="..\..\external\mongoose\mongoose.h">
<Filter>mongoose</Filter>
</ClInclude>
<ClInclude Include="msocketx.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_const.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_path.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_platform.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_str.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_types.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_util.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="ts_const.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="ts_ver.h">
<Filter>main app</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_ini.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_log.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\libex\include\ex\ex_thread.h">
<Filter>libex\header</Filter>
</ClInclude>
<ClInclude Include="..\..\common\teleport\teleport_const.h">
<Filter>teleport</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="res\tp.ico">
<Filter>resource</Filter>
</Image>
<Image Include="res\tp_small.ico">
<Filter>resource</Filter>
</Image>
<Image Include="res\tray_normal.ico">
<Filter>resource</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<Filter Include="resource">
<UniqueIdentifier>{52b425b1-8aa9-4e08-acbd-c88387350530}</UniqueIdentifier>
</Filter>
<Filter Include="jsoncpp">
<UniqueIdentifier>{adabe93d-3938-4b11-9352-5b67a1efd7e3}</UniqueIdentifier>
</Filter>
<Filter Include="mongoose">
<UniqueIdentifier>{35a345a0-6147-4c87-97c9-3b0b2a57e348}</UniqueIdentifier>
</Filter>
<Filter Include="main app">
<UniqueIdentifier>{0942cec3-67df-4d19-bbc1-e962145e496f}</UniqueIdentifier>
</Filter>
<Filter Include="libex">
<UniqueIdentifier>{a88e05d3-51f4-463f-84cc-c3bc86f07aac}</UniqueIdentifier>
</Filter>
<Filter Include="libex\header">
<UniqueIdentifier>{e3e7a811-5905-4ad5-86a7-9721af5d015a}</UniqueIdentifier>
</Filter>
<Filter Include="libex\src">
<UniqueIdentifier>{d7d49fa4-5192-42c5-bc70-5584d9d646c6}</UniqueIdentifier>
</Filter>
<Filter Include="teleport">
<UniqueIdentifier>{1291a5cf-cb08-4ad6-8a86-8a0486297c63}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="tp_assist.rc">
<Filter>resource</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\external\jsoncpp\src\lib_json\json_valueiterator.inl">
<Filter>jsoncpp</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -46,8 +46,8 @@
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\out\client\$(PlatformTarget)\$(Configuration)\</OutDir>
<IntDir>..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\</IntDir>
<IncludePath>C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
<IncludePath>C:\apps\vld\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\apps\vld\lib\Win32;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
@ -61,12 +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'">
@ -78,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>
@ -86,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,266 +1,274 @@
#include "stdafx.h"
#include "ts_cfg.h"
#include "ts_env.h"
TsCfg g_cfg;
TsCfg::TsCfg()
{}
TsCfg::~TsCfg()
{}
bool TsCfg::init(void) {
ex_astr file_content;
if (!ex_read_text_file(g_env.m_cfg_file, file_content)) {
EXLOGE("can not load config file.\n");
return false;
}
if (!_load(file_content))
return false;
return true;
}
bool TsCfg::save(const ex_astr& new_value)
{
if (!_load(new_value))
return false;
Json::StyledWriter jwriter;
ex_astr val = jwriter.write(m_root);
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
EXLOGE("can not save config file.\n");
return false;
}
return true;
}
bool TsCfg::_load(const ex_astr& str_json) {
Json::Reader jreader;
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());
return false;
}
ex_astr sel_name;
size_t i = 0;
ex_astr tmp;
//===================================
// check ssh config
//===================================
if (!m_root["ssh"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["ssh"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["ssh"]["selected"].asCString();
if (!m_root["ssh"]["available"].isArray() || m_root["ssh"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["ssh"]["available"].size(); ++i) {
if (
!m_root["ssh"]["available"][i]["name"].isString()
|| !m_root["ssh"]["available"][i]["app"].isString()
|| !m_root["ssh"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["ssh"]["available"][i]["display"].isNull()) {
m_root["ssh"]["available"][i]["display"] = m_root["ssh"]["available"][i]["name"];
}
if (m_root["ssh"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["ssh"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, ssh_app, EX_CODEPAGE_UTF8);
tmp = m_root["ssh"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, ssh_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (ssh_app.length() == 0 || ssh_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check sftp config
//===================================
if (!m_root["scp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["scp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["scp"]["selected"].asCString();
if (!m_root["scp"]["available"].isArray() || m_root["scp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["scp"]["available"].size(); ++i) {
if (
!m_root["scp"]["available"][i]["name"].isString()
|| !m_root["scp"]["available"][i]["app"].isString()
|| !m_root["scp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["scp"]["available"][i]["display"].isNull()) {
m_root["scp"]["available"][i]["display"] = m_root["scp"]["available"][i]["name"];
}
if (m_root["scp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["scp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, scp_app, EX_CODEPAGE_UTF8);
tmp = m_root["scp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, scp_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (scp_app.length() == 0 || scp_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check telnet config
//===================================
if (!m_root["telnet"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["telnet"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["telnet"]["selected"].asCString();
if (!m_root["telnet"]["available"].isArray() || m_root["telnet"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["telnet"]["available"].size(); ++i) {
if (
!m_root["telnet"]["available"][i]["name"].isString()
|| !m_root["telnet"]["available"][i]["app"].isString()
|| !m_root["telnet"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["telnet"]["available"][i]["display"].isNull()) {
m_root["telnet"]["available"][i]["display"] = m_root["telnet"]["available"][i]["name"];
}
if (m_root["telnet"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["telnet"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, telnet_app, EX_CODEPAGE_UTF8);
tmp = m_root["telnet"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, telnet_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (telnet_app.length() == 0 || telnet_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check rdp config
//===================================
if (!m_root["rdp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["rdp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["rdp"]["selected"].asCString();
if (!m_root["rdp"]["available"].isArray() || m_root["rdp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["rdp"]["available"].size(); ++i) {
if (
!m_root["rdp"]["available"][i]["name"].isString()
|| !m_root["rdp"]["available"][i]["app"].isString()
|| !m_root["rdp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["rdp"]["available"][i]["display"].isNull()) {
m_root["rdp"]["available"][i]["display"] = m_root["rdp"]["available"][i]["name"];
}
if (m_root["rdp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["rdp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, rdp_app, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, rdp_cmdline, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["name"].asCString();
ex_astr2wstr(tmp, rdp_name, EX_CODEPAGE_UTF8);
break;
}
if (rdp_app.length() == 0 || rdp_cmdline.length() == 0 || rdp_name.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
return true;
}
#include "stdafx.h"
#include "ts_cfg.h"
#include "ts_env.h"
TsCfg g_cfg;
TsCfg::TsCfg()
{}
TsCfg::~TsCfg()
{}
bool TsCfg::init(void) {
ex_astr file_content;
if (!ex_read_text_file(g_env.m_cfg_file, file_content)) {
EXLOGE("can not load config file.\n");
return false;
}
if (!_load(file_content))
return false;
return true;
}
bool TsCfg::save(const ex_astr& new_value)
{
if (!_load(new_value))
return false;
//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");
return false;
}
return true;
}
bool TsCfg::_load(const ex_astr& str_json) {
//Json::Reader jreader;
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = str_json.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;
}
ex_astr sel_name;
size_t i = 0;
ex_astr tmp;
//===================================
// check ssh config
//===================================
if (!m_root["ssh"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["ssh"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["ssh"]["selected"].asCString();
if (!m_root["ssh"]["available"].isArray() || m_root["ssh"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["ssh"]["available"].size(); ++i) {
if (
!m_root["ssh"]["available"][i]["name"].isString()
|| !m_root["ssh"]["available"][i]["app"].isString()
|| !m_root["ssh"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["ssh"]["available"][i]["display"].isNull()) {
m_root["ssh"]["available"][i]["display"] = m_root["ssh"]["available"][i]["name"];
}
if (m_root["ssh"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["ssh"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, ssh_app, EX_CODEPAGE_UTF8);
tmp = m_root["ssh"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, ssh_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (ssh_app.length() == 0 || ssh_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check sftp config
//===================================
if (!m_root["scp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["scp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["scp"]["selected"].asCString();
if (!m_root["scp"]["available"].isArray() || m_root["scp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["scp"]["available"].size(); ++i) {
if (
!m_root["scp"]["available"][i]["name"].isString()
|| !m_root["scp"]["available"][i]["app"].isString()
|| !m_root["scp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["scp"]["available"][i]["display"].isNull()) {
m_root["scp"]["available"][i]["display"] = m_root["scp"]["available"][i]["name"];
}
if (m_root["scp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["scp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, scp_app, EX_CODEPAGE_UTF8);
tmp = m_root["scp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, scp_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (scp_app.length() == 0 || scp_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check telnet config
//===================================
if (!m_root["telnet"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["telnet"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["telnet"]["selected"].asCString();
if (!m_root["telnet"]["available"].isArray() || m_root["telnet"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["telnet"]["available"].size(); ++i) {
if (
!m_root["telnet"]["available"][i]["name"].isString()
|| !m_root["telnet"]["available"][i]["app"].isString()
|| !m_root["telnet"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["telnet"]["available"][i]["display"].isNull()) {
m_root["telnet"]["available"][i]["display"] = m_root["telnet"]["available"][i]["name"];
}
if (m_root["telnet"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["telnet"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, telnet_app, EX_CODEPAGE_UTF8);
tmp = m_root["telnet"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, telnet_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (telnet_app.length() == 0 || telnet_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check rdp config
//===================================
if (!m_root["rdp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["rdp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["rdp"]["selected"].asCString();
if (!m_root["rdp"]["available"].isArray() || m_root["rdp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["rdp"]["available"].size(); ++i) {
if (
!m_root["rdp"]["available"][i]["name"].isString()
|| !m_root["rdp"]["available"][i]["app"].isString()
|| !m_root["rdp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["rdp"]["available"][i]["display"].isNull()) {
m_root["rdp"]["available"][i]["display"] = m_root["rdp"]["available"][i]["name"];
}
if (m_root["rdp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["rdp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, rdp_app, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, rdp_cmdline, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["name"].asCString();
ex_astr2wstr(tmp, rdp_name, EX_CODEPAGE_UTF8);
break;
}
if (rdp_app.length() == 0 || rdp_cmdline.length() == 0 || rdp_name.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
return true;
}

Some files were not shown because too many files have changed in this diff Show More