diff --git a/.gitignore b/.gitignore index d6ddb82..8e3a1fd 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,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 @@ -100,3 +102,7 @@ profile *.moved-aside /server/share/tmp +/server/tp_core/testssh/Debug +/server/tp_core/testssh/Release +/external/zlib +/client/tools/qt-redist diff --git a/build/build-py-static.sh b/build/build-py-static.sh index b501055..296612b 100755 --- a/build/build-py-static.sh +++ b/build/build-py-static.sh @@ -3,10 +3,10 @@ ################################################################ # Basic settings. ################################################################ -VER_PYTHON="3.7.0" +VER_PYTHON="3.7.4" VER_PYTHON_SHORT="3.7" -VER_OPENSSL="1.0.2p" -VER_SQLITE="3250000" +VER_OPENSSL="1.0.2s" +VER_SQLITE="3290000" VER_ZLIB="1.2.11" VER_PYTHON_LIB="${VER_PYTHON_SHORT}m" @@ -77,7 +77,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} } diff --git a/build/builder/build-assist.py b/build/builder/build-assist.py index da84c60..06cad85 100644 --- a/build/builder/build-assist.py +++ b/build/builder/build-assist.py @@ -13,21 +13,24 @@ class BuilderBase: def __init__(self): self.out_dir = '' - def build_exe(self): - pass + def build_assist(self): + cc.e("this is a pure-virtual function.") + + def build_player(self): + cc.e("this is a pure-virtual function.") def build_rdp(self): - pass + cc.e("this is a pure-virtual function.") def build_installer(self): - pass + cc.e("this is a pure-virtual function.") class BuilderWin(BuilderBase): def __init__(self): super().__init__() - def build_exe(self): + def build_assist(self): cc.i('build tp_assist...') sln_file = os.path.join(env.root_path, 'client', 'tp_assist_win', 'tp_assist.vs2017.sln') out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp_assist.exe') @@ -36,6 +39,15 @@ class BuilderWin(BuilderBase): utils.msvc_build(sln_file, 'tp_assist', ctx.target_path, ctx.bits_path, False) utils.ensure_file_exists(out_file) + def build_player(self): + cc.i('build tp-player...') + prj_path = os.path.join(env.root_path, 'client', 'tp-player') + out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp-player.exe') + if os.path.exists(out_file): + utils.remove(out_file) + utils.qt_build_win(prj_path, 'tp-player', ctx.bits_path, ctx.target_path) + utils.ensure_file_exists(out_file) + # def build_rdp(self): # cc.n('build tp_rdp...') # sln_file = os.path.join(ROOT_PATH, 'client', 'tp_rdp', 'tp_rdp.2015.sln') @@ -74,12 +86,13 @@ class BuilderWin(BuilderBase): utils.makedirs(tmp_cfg_path) utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp_assist.exe') - utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json')) + utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json')) utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'cacert.cer') utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.key') utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.pem') + # assist configuration web page utils.copy_ex(os.path.join(env.root_path, 'client', 'tp_assist_win'), tmp_app_path, 'site') utils.makedirs(os.path.join(tmp_app_path, 'tools', 'putty')) @@ -91,13 +104,35 @@ class BuilderWin(BuilderBase): utils.makedirs(os.path.join(tmp_app_path, 'tools', 'tprdp')) utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-client.exe') - utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe') + # utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe') utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'libeay32.dll') utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'ssleay32.dll') utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'msvcr120.dll') utils.copy_file(os.path.join(env.root_path, 'client', 'tools'), os.path.join(tmp_app_path, 'tools'), 'securecrt-telnet.vbs') + # tp-player + utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp-player.exe') + + # qt-redist + qt_redist_path = os.path.join(env.root_path, 'client', 'tools', 'qt-redist') + utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Core.dll') + utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Gui.dll') + utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Network.dll') + utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Widgets.dll') + utils.copy_ex(os.path.join(qt_redist_path, 'platforms'), os.path.join(tmp_app_path, 'platforms')) + utils.copy_ex(os.path.join(qt_redist_path, 'styles'), os.path.join(tmp_app_path, 'styles')) + utils.copy_ex(os.path.join(qt_redist_path, 'translations'), os.path.join(tmp_app_path, 'translations')) + + # zlib + suffix = 'd' if ctx.target_path == 'debug' else '' + utils.copy_file(os.path.join(env.root_path, 'external', 'zlib', 'build', ctx.target_path), tmp_app_path, 'zlib{}.dll'.format(suffix)) + + # openssl + utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libcrypto-1_1.dll') + utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libssl-1_1.dll') + + # final build utils.nsis_build(os.path.join(env.root_path, 'dist', 'client', 'windows', 'assist', 'installer.nsi')) @@ -105,7 +140,7 @@ class BuilderMacOS(BuilderBase): def __init__(self): super().__init__() - def build_exe(self): + def build_assist(self): cc.i('build tp_assist...') configuration = ctx.target_path.capitalize() @@ -169,7 +204,7 @@ class BuilderLinux(BuilderBase): def __init__(self): super().__init__() - def build_exe(self): + def build_assist(self): cc.e('not support linux.') # def build_rdp(self): @@ -215,7 +250,8 @@ def main(): builder = gen_builder(ctx.host_os) if 'exe' in argv: - builder.build_exe() + builder.build_assist() + builder.build_player() # elif 'rdp' in argv: # builder.build_rdp() elif 'installer' in argv: diff --git a/build/builder/build-external.py b/build/builder/build-external.py index e3f9c7a..491e4b0 100644 --- a/build/builder/build-external.py +++ b/build/builder/build-external.py @@ -27,19 +27,25 @@ class BuilderBase: def build_jsoncpp(self): file_name = 'jsoncpp-{}.zip'.format(env.ver_jsoncpp) - if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name): - return + # if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name): + # return self._build_jsoncpp(file_name) + def _download_jsoncpp(self, file_name): + return utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name) + def _build_jsoncpp(self, file_name): cc.e("this is a pure-virtual function.") def build_mongoose(self): file_name = 'mongoose-{}.zip'.format(env.ver_mongoose) - if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name): - return + # if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name): + # return self._build_mongoose(file_name) + def _download_mongoose(self, file_name): + return utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name) + def _build_mongoose(self, file_name): cc.e("this is a pure-virtual function.") @@ -47,38 +53,64 @@ class BuilderBase: file_name = 'openssl-{}.zip'.format(env.ver_ossl) self._build_openssl(file_name) - def _build_openssl(self, file_name): + def _download_openssl(self, file_name): _alt_ver = '_'.join(env.ver_ossl.split('.')) - if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name): - cc.e("can not download openssl source tarball.") - return False - else: - return True + return utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name) + + def _build_openssl(self, file_name): + cc.e("this is a pure-virtual function.") + # _alt_ver = '_'.join(env.ver_ossl.split('.')) + # if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name): + # cc.e("can not download openssl source tarball.") + # return False + # else: + # return True def build_libuv(self): file_name = 'libuv-{}.zip'.format(env.ver_libuv) - if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name): - return + # if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name): + # return self._build_libuv(file_name) + def _download_libuv(self, file_name): + return utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name) + def _build_libuv(self, file_name): cc.e("this is a pure-virtual function.") def build_mbedtls(self): file_name = 'mbedtls-mbedtls-{}.zip'.format(env.ver_mbedtls) - if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name): - return + # if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name): + # return self._build_mbedtls(file_name) + def _download_mbedtls(self, file_name): + return utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name) + def _build_mbedtls(self, file_name): cc.e("this is a pure-virtual function.") + def build_zlib(self): + file_name = 'zlilb{}.zip'.format(env.ver_zlib_number) + # if not utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name): + # return + self._build_zlib(file_name) + + def _download_zlib(self, file_name): + return utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name) + + def _build_zlib(self, file_name): + cc.e("this is a pure-virtual function.") + def build_libssh(self): file_name = 'libssh-{}.zip'.format(env.ver_libssh) - if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name): - return + # if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name): + # return self._build_libssh(file_name) + def _download_libssh(self, file_name): + return utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name) + def _build_libssh(self, file_name): cc.e("this is a pure-virtual function.") @@ -103,9 +135,10 @@ class BuilderWin(BuilderBase): self.MBEDTLS_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mbedtls') self.LIBUV_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libuv') self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh') + self.ZLIB_PATH_SRC = os.path.join(PATH_EXTERNAL, 'zlib') def _prepare_python(self): - cc.n('prepare python header files ...', end='') + cc.n('prepare python header files ... ', end='') if os.path.exists(os.path.join(PATH_EXTERNAL, 'python', 'include', 'Python.h')): cc.w('already exists, skip.') @@ -125,53 +158,85 @@ class BuilderWin(BuilderBase): utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include')) def _build_openssl(self, file_name): - cc.n('build openssl static library from source code... ') - - if not super()._build_openssl(file_name): - return - - _chk_output = [ - os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'), - os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'), - os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'), - ] - - need_build = False - for f in _chk_output: - if not os.path.exists(f): - need_build = True - break - - if not need_build: - cc.n('build openssl static library from source code... ', end='') + cc.n('prepare OpenSSL pre-built package ... ', end='') + if os.path.exists(self.OPENSSL_PATH_SRC): cc.w('already exists, skip.') return cc.v('') - cc.n('prepare openssl source code...') _alt_ver = '_'.join(env.ver_ossl.split('.')) - if not os.path.exists(self.OPENSSL_PATH_SRC): - utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL) - os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC) - if not os.path.exists(self.OPENSSL_PATH_SRC): - raise RuntimeError('can not prepare openssl source code.') - else: - cc.w('already exists, skip.') - os.chdir(self.OPENSSL_PATH_SRC) - os.system('""{}" Configure VC-WIN32"'.format(env.perl)) - os.system(r'ms\do_nasm') - # for vs2015 - # utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True) - # for vs2017 community - utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True) + file_name = 'Win32OpenSSL-{}.msi'.format(_alt_ver) + installer = os.path.join(PATH_DOWNLOAD, file_name) - for f in _chk_output: - if not os.path.exists(f): - raise RuntimeError('build openssl static library from source code failed.') + if not os.path.exists(installer): + if not utils.download_file('openssl installer', 'http://slproweb.com/download/{}'.format(filename), PATH_DOWNLOAD, file_name): + cc.e('can not download pre-built installer of OpenSSL.') + return + + utils.ensure_file_exists(installer) + + cc.w('On Windows, we use pre-built package of OpenSSL.') + cc.w('The installer have been downloaded at "{}".'.format(installer)) + cc.w('please install OpenSSL into "{}".'.format(self.OPENSSL_PATH_SRC)) + cc.w('\nOnce the OpenSSL installed, press Enter to continue or Q to quit...', end='') + try: + x = env.input() + except EOFError: + x = 'q' + if x == 'q': + return + + + # cc.n('build openssl static library from source code... ') + + # if not super()._build_openssl(file_name): + # return + + # _chk_output = [ + # os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'), + # os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'), + # os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'), + # ] + + # need_build = False + # for f in _chk_output: + # if not os.path.exists(f): + # need_build = True + # break + + # if not need_build: + # cc.n('build openssl static library from source code... ', end='') + # cc.w('already exists, skip.') + # return + # cc.v('') + + # cc.n('prepare openssl source code...') + # _alt_ver = '_'.join(env.ver_ossl.split('.')) + # if not os.path.exists(self.OPENSSL_PATH_SRC): + # utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL) + # os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC) + # if not os.path.exists(self.OPENSSL_PATH_SRC): + # raise RuntimeError('can not prepare openssl source code.') + # else: + # cc.w('already exists, skip.') + + # os.chdir(self.OPENSSL_PATH_SRC) + # os.system('""{}" Configure VC-WIN32"'.format(env.perl)) + # os.system(r'ms\do_nasm') + # # for vs2015 + # # utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True) + # # for vs2017 community + # utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True) + + # for f in _chk_output: + # if not os.path.exists(f): + # raise RuntimeError('build openssl static library from source code failed.') def _build_libssh(self, file_name): - cc.n('build libssh static library from source code... ', end='') + if not self._download_libssh(file_name): + return + cc.n('build libssh library from source code... ', end='') if not os.path.exists(self.LIBSSH_PATH_SRC): cc.v('') @@ -210,7 +275,7 @@ class BuilderWin(BuilderBase): cc.i('build libssh...') sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'build', 'libssh.sln') - utils.msvc_build(sln_file, 'ssh_shared', ctx.target_path, 'win32', False) + utils.msvc_build(sln_file, 'ssh', ctx.target_path, 'win32', False) utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.lib')) utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.dll')) utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.lib') @@ -218,7 +283,53 @@ class BuilderWin(BuilderBase): utils.ensure_file_exists(out_file_lib) utils.ensure_file_exists(out_file_dll) + def _build_zlib(self, file_name): + if not self._download_zlib(file_name): + return + cc.n('build zlib library from source code... ', end='') + + if not os.path.exists(self.ZLIB_PATH_SRC): + cc.v('') + utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL) + os.rename(os.path.join(PATH_EXTERNAL, 'zlib-{}'.format(env.ver_zlib)), self.ZLIB_PATH_SRC) + + if ctx.target_path == 'debug': + olib = 'zlibd.lib' + odll = 'zlibd.dll' + else: + olib = 'zlib.lib' + odll = 'zlib.dll' + out_file_lib = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, olib) + out_file_dll = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, odll) + + if os.path.exists(out_file_lib) and os.path.exists(out_file_dll): + cc.w('already exists, skip.') + return + cc.v('') + + cc.w('On Windows, when build zlib, need you use cmake-gui.exe to generate solution file') + cc.w('for Visual Studio 2017. Visit https://docs.tp4a.com for more details.') + cc.w('\nOnce the zlib.sln generated, press Enter to continue or Q to quit...', end='') + try: + x = env.input() + except EOFError: + x = 'q' + if x == 'q': + return + + cc.i('build zlib...') + sln_file = os.path.join(self.ZLIB_PATH_SRC, 'build', 'zlib.sln') + utils.msvc_build(sln_file, 'zlib', ctx.target_path, 'win32', False) + # utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.lib')) + # utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.dll')) + # utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.lib') + # utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.dll') + utils.ensure_file_exists(out_file_lib) + utils.ensure_file_exists(out_file_dll) + def _build_jsoncpp(self, file_name): + if not self._download_jsoncpp(file_name): + return cc.n('prepare jsoncpp source code... ', end='') if not os.path.exists(self.JSONCPP_PATH_SRC): cc.v('') @@ -228,6 +339,8 @@ class BuilderWin(BuilderBase): cc.w('already exists, skip.') def _build_mongoose(self, file_name): + if not self._download_mongoose(file_name): + return cc.n('prepare mongoose source code... ', end='') if not os.path.exists(self.MONGOOSE_PATH_SRC): cc.v('') @@ -237,6 +350,8 @@ class BuilderWin(BuilderBase): cc.w('already exists, skip.') def _build_mbedtls(self, file_name): + if not self._download_mbedtls(file_name): + return cc.n('prepare mbedtls source code... ', end='') if not os.path.exists(self.MBEDTLS_PATH_SRC): cc.v('') @@ -254,6 +369,8 @@ class BuilderWin(BuilderBase): # utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c') def _build_libuv(self, file_name): + if not self._download_libuv(file_name): + return cc.n('prepare libuv source code... ', end='') if not os.path.exists(self.LIBUV_PATH_SRC): cc.v('') @@ -674,6 +791,7 @@ def main(): builder.build_openssl() builder.build_libuv() builder.build_mbedtls() + builder.build_zlib() builder.build_libssh() builder.fix_output() diff --git a/build/builder/core/env.py b/build/builder/core/env.py index 82a907e..4a89e73 100644 --- a/build/builder/core/env.py +++ b/build/builder/core/env.py @@ -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'] diff --git a/build/builder/core/utils.py b/build/builder/core/utils.py index c6aa97f..3aacf62 100644 --- a/build/builder/core/utils.py +++ b/build/builder/core/utils.py @@ -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) diff --git a/build/builder/core/ver.py b/build/builder/core/ver.py index 31996dd..30abc5a 100644 --- a/build/builder/core/ver.py +++ b/build/builder/core/ver.py @@ -1,3 +1,3 @@ # -*- coding: utf8 -*- -VER_TP_SERVER = "3.3.1" -VER_TP_ASSIST = "3.3.1" +VER_TP_SERVER = "3.5.1" +VER_TP_ASSIST = "3.5.1" diff --git a/client/cfg/tp-assist.windows.json b/client/cfg/tp-assist.windows.json index f4fc876..0f9baea 100755 --- a/client/cfg/tp-assist.windows.json +++ b/client/cfg/tp-assist.windows.json @@ -71,12 +71,18 @@ "rdp" : { "available" : [ { + "app" : "mstsc.exe", + "cmdline" : "\"{tmp_rdp_file}\"", + "display" : "微软RDP客户端(系统自带)", + "name" : "mstsc" + }, + { "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" + "selected" : "mstsc" } } diff --git a/client/tp-player/bar.cpp b/client/tp-player/bar.cpp new file mode 100644 index 0000000..5ff3f28 --- /dev/null +++ b/client/tp-player/bar.cpp @@ -0,0 +1,718 @@ +#include "bar.h" +#include +#include +#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); + } + } + } +} + + diff --git a/client/tp-player/bar.h b/client/tp-player/bar.h new file mode 100644 index 0000000..7141cb3 --- /dev/null +++ b/client/tp-player/bar.h @@ -0,0 +1,149 @@ +#ifndef BAR_H +#define BAR_H + +#include +#include +#include + +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 diff --git a/client/tp-player/downloader.cpp b/client/tp-player/downloader.cpp new file mode 100644 index 0000000..0f5a10a --- /dev/null +++ b/client/tp-player/downloader.cpp @@ -0,0 +1,120 @@ +#include "downloader.h" +#include "record_format.h" + +#include +#include +#include + +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(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(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; +} diff --git a/client/tp-player/downloader.h b/client/tp-player/downloader.h new file mode 100644 index 0000000..5f5a123 --- /dev/null +++ b/client/tp-player/downloader.h @@ -0,0 +1,40 @@ +#ifndef DOWNLOADER_H +#define DOWNLOADER_H + +#include +#include + +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 diff --git a/client/tp-player/main.cpp b/client/tp-player/main.cpp new file mode 100644 index 0000000..82e2531 --- /dev/null +++ b/client/tp-player/main.cpp @@ -0,0 +1,98 @@ +#include "mainwindow.h" +#include +#include +#include +#include +#include + +// 编译出来的可执行程序复制到单独目录,然后执行 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(), + "
"
+                         + 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."
+                         + "
"); +} + +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(), + //"

" + parser.errorText() + "

"
+                             "

" + parser.errorText() + "

"
+                             + parser.helpText() + "
"); + 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(); +} diff --git a/client/tp-player/mainwindow.cpp b/client/tp-player/mainwindow.cpp new file mode 100644 index 0000000..2e2e458 --- /dev/null +++ b/client/tp-player/mainwindow.cpp @@ -0,0 +1,433 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include + +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()); +} + diff --git a/client/tp-player/mainwindow.h b/client/tp-player/mainwindow.h new file mode 100644 index 0000000..174487f --- /dev/null +++ b/client/tp-player/mainwindow.h @@ -0,0 +1,90 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#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 diff --git a/client/tp-player/mainwindow.ui b/client/tp-player/mainwindow.ui new file mode 100644 index 0000000..b296c52 --- /dev/null +++ b/client/tp-player/mainwindow.ui @@ -0,0 +1,26 @@ + + + MainWindow + + + + 0 + 0 + 500 + 360 + + + + + 微软雅黑 Light + + + + Teleport Replayer + + + + + + + diff --git a/client/tp-player/record_format.h b/client/tp-player/record_format.h new file mode 100644 index 0000000..42b8ff3 --- /dev/null +++ b/client/tp-player/record_format.h @@ -0,0 +1,109 @@ +#ifndef RECORD_FORMAT_H +#define RECORD_FORMAT_H + +#include + +#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; // 数据文件数量 + uint8_t _reserve[64-4-2-2-4-4]; +}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 + +// uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size]; + uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - ts_record_header_info_size]; +}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; + TS_RECORD_HEADER_BASIC basic; +}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; // 包的数据类型 + uint8_t _reserve[3]; // 保留 + uint32_t size; // 这个包的总大小(不含包头) + uint32_t time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天) + // uint32_t index; // 这个包的序号(最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配) +}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 diff --git a/client/tp-player/res/bar.psd b/client/tp-player/res/bar.psd new file mode 100644 index 0000000..a2defc8 Binary files /dev/null and b/client/tp-player/res/bar.psd differ diff --git a/client/tp-player/res/bar/bg-left.png b/client/tp-player/res/bar/bg-left.png new file mode 100644 index 0000000..e20e24c Binary files /dev/null and b/client/tp-player/res/bar/bg-left.png differ diff --git a/client/tp-player/res/bar/bg-mid.png b/client/tp-player/res/bar/bg-mid.png new file mode 100644 index 0000000..974129a Binary files /dev/null and b/client/tp-player/res/bar/bg-mid.png differ diff --git a/client/tp-player/res/bar/bg-right.png b/client/tp-player/res/bar/bg-right.png new file mode 100644 index 0000000..c6b31a0 Binary files /dev/null and b/client/tp-player/res/bar/bg-right.png differ diff --git a/client/tp-player/res/bar/btn-hover-left.png b/client/tp-player/res/bar/btn-hover-left.png new file mode 100644 index 0000000..7914efb Binary files /dev/null and b/client/tp-player/res/bar/btn-hover-left.png differ diff --git a/client/tp-player/res/bar/btn-hover-mid.png b/client/tp-player/res/bar/btn-hover-mid.png new file mode 100644 index 0000000..a1db349 Binary files /dev/null and b/client/tp-player/res/bar/btn-hover-mid.png differ diff --git a/client/tp-player/res/bar/btn-hover-right.png b/client/tp-player/res/bar/btn-hover-right.png new file mode 100644 index 0000000..cfd1348 Binary files /dev/null and b/client/tp-player/res/bar/btn-hover-right.png differ diff --git a/client/tp-player/res/bar/btn-normal-left.png b/client/tp-player/res/bar/btn-normal-left.png new file mode 100644 index 0000000..eb9b22a Binary files /dev/null and b/client/tp-player/res/bar/btn-normal-left.png differ diff --git a/client/tp-player/res/bar/btn-normal-mid.png b/client/tp-player/res/bar/btn-normal-mid.png new file mode 100644 index 0000000..06608db Binary files /dev/null and b/client/tp-player/res/bar/btn-normal-mid.png differ diff --git a/client/tp-player/res/bar/btn-normal-right.png b/client/tp-player/res/bar/btn-normal-right.png new file mode 100644 index 0000000..53f68d8 Binary files /dev/null and b/client/tp-player/res/bar/btn-normal-right.png differ diff --git a/client/tp-player/res/bar/btn-sel-left.png b/client/tp-player/res/bar/btn-sel-left.png new file mode 100644 index 0000000..e26b79e Binary files /dev/null and b/client/tp-player/res/bar/btn-sel-left.png differ diff --git a/client/tp-player/res/bar/btn-sel-mid.png b/client/tp-player/res/bar/btn-sel-mid.png new file mode 100644 index 0000000..f69acf2 Binary files /dev/null and b/client/tp-player/res/bar/btn-sel-mid.png differ diff --git a/client/tp-player/res/bar/btn-sel-right.png b/client/tp-player/res/bar/btn-sel-right.png new file mode 100644 index 0000000..ccfab12 Binary files /dev/null and b/client/tp-player/res/bar/btn-sel-right.png differ diff --git a/client/tp-player/res/bar/chkbox-hover.png b/client/tp-player/res/bar/chkbox-hover.png new file mode 100644 index 0000000..485711a Binary files /dev/null and b/client/tp-player/res/bar/chkbox-hover.png differ diff --git a/client/tp-player/res/bar/chkbox-normal.png b/client/tp-player/res/bar/chkbox-normal.png new file mode 100644 index 0000000..1ac352d Binary files /dev/null and b/client/tp-player/res/bar/chkbox-normal.png differ diff --git a/client/tp-player/res/bar/chkbox-sel-hover.png b/client/tp-player/res/bar/chkbox-sel-hover.png new file mode 100644 index 0000000..47970ca Binary files /dev/null and b/client/tp-player/res/bar/chkbox-sel-hover.png differ diff --git a/client/tp-player/res/bar/chkbox-sel-normal.png b/client/tp-player/res/bar/chkbox-sel-normal.png new file mode 100644 index 0000000..872fe0c Binary files /dev/null and b/client/tp-player/res/bar/chkbox-sel-normal.png differ diff --git a/client/tp-player/res/bar/pause-hover.png b/client/tp-player/res/bar/pause-hover.png new file mode 100644 index 0000000..a220452 Binary files /dev/null and b/client/tp-player/res/bar/pause-hover.png differ diff --git a/client/tp-player/res/bar/pause-normal.png b/client/tp-player/res/bar/pause-normal.png new file mode 100644 index 0000000..455497e Binary files /dev/null and b/client/tp-player/res/bar/pause-normal.png differ diff --git a/client/tp-player/res/bar/play-hover.png b/client/tp-player/res/bar/play-hover.png new file mode 100644 index 0000000..1bb55a9 Binary files /dev/null and b/client/tp-player/res/bar/play-hover.png differ diff --git a/client/tp-player/res/bar/play-normal.png b/client/tp-player/res/bar/play-normal.png new file mode 100644 index 0000000..430f6f4 Binary files /dev/null and b/client/tp-player/res/bar/play-normal.png differ diff --git a/client/tp-player/res/bar/prgbar-mid.png b/client/tp-player/res/bar/prgbar-mid.png new file mode 100644 index 0000000..4e7d3a0 Binary files /dev/null and b/client/tp-player/res/bar/prgbar-mid.png differ diff --git a/client/tp-player/res/bar/prgbar-right.png b/client/tp-player/res/bar/prgbar-right.png new file mode 100644 index 0000000..8897c17 Binary files /dev/null and b/client/tp-player/res/bar/prgbar-right.png differ diff --git a/client/tp-player/res/bar/prgbarh-left.png b/client/tp-player/res/bar/prgbarh-left.png new file mode 100644 index 0000000..9a6ba68 Binary files /dev/null and b/client/tp-player/res/bar/prgbarh-left.png differ diff --git a/client/tp-player/res/bar/prgbarh-mid.png b/client/tp-player/res/bar/prgbarh-mid.png new file mode 100644 index 0000000..baeb2f6 Binary files /dev/null and b/client/tp-player/res/bar/prgbarh-mid.png differ diff --git a/client/tp-player/res/bar/prgpt-hover.png b/client/tp-player/res/bar/prgpt-hover.png new file mode 100644 index 0000000..e98d208 Binary files /dev/null and b/client/tp-player/res/bar/prgpt-hover.png differ diff --git a/client/tp-player/res/bar/prgpt-normal.png b/client/tp-player/res/bar/prgpt-normal.png new file mode 100644 index 0000000..59e1faa Binary files /dev/null and b/client/tp-player/res/bar/prgpt-normal.png differ diff --git a/client/tp-player/res/bar/select.png b/client/tp-player/res/bar/select.png new file mode 100644 index 0000000..38a1052 Binary files /dev/null and b/client/tp-player/res/bar/select.png differ diff --git a/client/tp-player/res/bar/selected.png b/client/tp-player/res/bar/selected.png new file mode 100644 index 0000000..c0067f5 Binary files /dev/null and b/client/tp-player/res/bar/selected.png differ diff --git a/client/tp-player/res/bg.png b/client/tp-player/res/bg.png new file mode 100644 index 0000000..e35d671 Binary files /dev/null and b/client/tp-player/res/bg.png differ diff --git a/client/tp-player/res/cursor.png b/client/tp-player/res/cursor.png new file mode 100644 index 0000000..5728f4f Binary files /dev/null and b/client/tp-player/res/cursor.png differ diff --git a/client/tp-player/res/tp-player.ico b/client/tp-player/res/tp-player.ico new file mode 100644 index 0000000..d3fce7a Binary files /dev/null and b/client/tp-player/res/tp-player.ico differ diff --git a/client/tp-player/rle.c b/client/tp-player/rle.c new file mode 100644 index 0000000..7aa0b49 --- /dev/null +++ b/client/tp-player/rle.c @@ -0,0 +1,974 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Bitmap decompression routines + Copyright (C) Matthew Chapman 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 . +*/ + +/* 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 +#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; +} diff --git a/client/tp-player/rle.h b/client/tp-player/rle.h new file mode 100644 index 0000000..6de7624 --- /dev/null +++ b/client/tp-player/rle.h @@ -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 diff --git a/client/tp-player/thr_data.cpp b/client/tp-player/thr_data.cpp new file mode 100644 index 0000000..0bcc255 --- /dev/null +++ b/client/tp-player/thr_data.cpp @@ -0,0 +1,709 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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(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(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 UNKNOWN bitsPerPix" << bitsPerPixel; + 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(&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(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(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(sizeof(uint16_t) + sizeof(TS_RECORD_RDP_IMAGE_INFO))) { + delete ud; + return nullptr; + } + + const uint8_t* dat_ptr = reinterpret_cast(data.data()); + + uint16_t count = (reinterpret_cast(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(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(info->dat_len)); + + uLong u_len = info->dat_len; + int err = uncompress(reinterpret_cast(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(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(data.data()); + const uint8_t* data_buf = reinterpret_cast(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(real_img_len)); + + uLong u_len = real_img_len; + int err = uncompress(reinterpret_cast(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(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(&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(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(&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(); +} diff --git a/client/tp-player/thr_data.h b/client/tp-player/thr_data.h new file mode 100644 index 0000000..92ad518 --- /dev/null +++ b/client/tp-player/thr_data.h @@ -0,0 +1,107 @@ +#ifndef THR_DATA_H +#define THR_DATA_H + +#include +#include +#include +#include +#include +#include +#include +#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 KeyFrames; + +typedef std::vector 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 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 diff --git a/client/tp-player/thr_download.cpp b/client/tp-player/thr_download.cpp new file mode 100644 index 0000000..cbfe0f7 --- /dev/null +++ b/client/tp-player/thr_download.cpp @@ -0,0 +1,292 @@ +#include +#include +#include +#include + +#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(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(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]; +} + diff --git a/client/tp-player/thr_download.h b/client/tp-player/thr_download.h new file mode 100644 index 0000000..e5478e0 --- /dev/null +++ b/client/tp-player/thr_download.h @@ -0,0 +1,72 @@ +#ifndef THR_DOWNLOAD_H +#define THR_DOWNLOAD_H + +#include +#include +#include +#include + +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 diff --git a/client/tp-player/thr_play.cpp b/client/tp-player/thr_play.cpp new file mode 100644 index 0000000..424f7bd --- /dev/null +++ b/client/tp-player/thr_play.cpp @@ -0,0 +1,182 @@ +#include +#include +#include + +#include "thr_play.h" +#include "thr_data.h" +#include "mainwindow.h" +#include "record_format.h" +#include "util.h" + + +/* + * 录像播放流程: + * - 数据处理线程,该线程负责(下载)文件、解析文件,将数据准备成待播放队列; + * + 数据处理线程维护待播放队列,少于500个则填充至1000个,每20ms检查一次队列是否少于500个。 + * - 播放线程从队列中取出一个数据,判断当前时间是否应该播放此数据,如果应该,则将此数据发送给主UI + * + if( 播放速率 * (当前时间 - 播放时间) >= (当前数据包偏移时间 - 上个数据包偏移时间)) 则 播放 + * + 如选择“跳过无操作时间”,则数据包偏移时间差超过3秒的,视为3秒。 + */ + + +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; +} diff --git a/client/tp-player/thr_play.h b/client/tp-player/thr_play.h new file mode 100644 index 0000000..e8ea3d8 --- /dev/null +++ b/client/tp-player/thr_play.h @@ -0,0 +1,43 @@ +#ifndef THR_PLAY_H +#define THR_PLAY_H + +#include +#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 diff --git a/client/tp-player/tp-player.pro b/client/tp-player/tp-player.pro new file mode 100644 index 0000000..3b95bae --- /dev/null +++ b/client/tp-player/tp-player.pro @@ -0,0 +1,58 @@ +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 +} + +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 diff --git a/client/tp-player/tp-player.qrc b/client/tp-player/tp-player.qrc new file mode 100644 index 0000000..f79019d --- /dev/null +++ b/client/tp-player/tp-player.qrc @@ -0,0 +1,38 @@ + + + res/bg.png + res/cursor.png + + res/bar/bg-left.png + res/bar/bg-mid.png + res/bar/bg-right.png + + res/bar/btn-normal-left.png + res/bar/btn-normal-mid.png + res/bar/btn-normal-right.png + res/bar/btn-sel-left.png + res/bar/btn-sel-mid.png + res/bar/btn-sel-right.png + res/bar/btn-hover-left.png + res/bar/btn-hover-mid.png + res/bar/btn-hover-right.png + + res/bar/play-hover.png + res/bar/play-normal.png + res/bar/pause-hover.png + res/bar/pause-normal.png + + res/bar/prgbar-mid.png + res/bar/prgbar-right.png + res/bar/prgbarh-left.png + res/bar/prgbarh-mid.png + + res/bar/prgpt-normal.png + res/bar/prgpt-hover.png + + res/bar/chkbox-normal.png + res/bar/chkbox-hover.png + res/bar/chkbox-sel-normal.png + res/bar/chkbox-sel-hover.png + + diff --git a/client/tp-player/tp-player.rc b/client/tp-player/tp-player.rc new file mode 100644 index 0000000..df678b3 --- /dev/null +++ b/client/tp-player/tp-player.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "res\\tp-player.ico" + diff --git a/client/tp-player/update_data.cpp b/client/tp-player/update_data.cpp new file mode 100644 index 0000000..b9cfdfd --- /dev/null +++ b/client/tp-player/update_data.cpp @@ -0,0 +1,79 @@ +#include "update_data.h" + +#include +#include + + +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; +} diff --git a/client/tp-player/update_data.h b/client/tp-player/update_data.h new file mode 100644 index 0000000..f142629 --- /dev/null +++ b/client/tp-player/update_data.h @@ -0,0 +1,106 @@ +#ifndef UPDATE_DATA_H +#define UPDATE_DATA_H + +#include +#include +#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 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 diff --git a/client/tp-player/util.cpp b/client/tp-player/util.cpp new file mode 100644 index 0000000..e69de29 diff --git a/client/tp-player/util.h b/client/tp-player/util.h new file mode 100644 index 0000000..8ff938b --- /dev/null +++ b/client/tp-player/util.h @@ -0,0 +1,33 @@ +#ifndef TP_PLAYER_UTIL_H +#define TP_PLAYER_UTIL_H + +#include + +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 diff --git a/client/tp_assist_macos/src/TP-Assist-Info.plist b/client/tp_assist_macos/src/TP-Assist-Info.plist index 3231d75..4b86b89 100644 --- a/client/tp_assist_macos/src/TP-Assist-Info.plist +++ b/client/tp_assist_macos/src/TP-Assist-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.3.1 + 3.5.1 CFBundleSignature ???? CFBundleVersion - 3.3.1 + 3.5.1 LSApplicationCategoryType public.app-category.productivity LSMinimumSystemVersion diff --git a/client/tp_assist_macos/src/csrc/ts_ver.h b/client/tp_assist_macos/src/csrc/ts_ver.h index 6aa10dc..9c55de2 100644 --- a/client/tp_assist_macos/src/csrc/ts_ver.h +++ b/client/tp_assist_macos/src/csrc/ts_ver.h @@ -1,6 +1,6 @@ #ifndef __TS_ASSIST_VER_H__ #define __TS_ASSIST_VER_H__ -#define TP_ASSIST_VER L"3.3.1" +#define TP_ASSIST_VER L"3.5.1" #endif // __TS_ASSIST_VER_H__ diff --git a/client/tp_assist_win/stdafx.cpp b/client/tp_assist_win/stdafx.cpp index fd4f341..6c5f035 100644 --- a/client/tp_assist_win/stdafx.cpp +++ b/client/tp_assist_win/stdafx.cpp @@ -1 +1,14 @@ -#include "stdafx.h" +#include "stdafx.h" + +#include + +// #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") diff --git a/client/tp_assist_win/tp_assist.rc b/client/tp_assist_win/tp_assist.rc index 8dc73d0..b1c8832 100644 Binary files a/client/tp_assist_win/tp_assist.rc and b/client/tp_assist_win/tp_assist.rc differ diff --git a/client/tp_assist_win/tp_assist.vs2017.vcxproj b/client/tp_assist_win/tp_assist.vs2017.vcxproj index 0ebbf9c..c5ef904 100644 --- a/client/tp_assist_win/tp_assist.vs2017.vcxproj +++ b/client/tp_assist_win/tp_assist.vs2017.vcxproj @@ -46,8 +46,8 @@ true ..\..\out\client\$(PlatformTarget)\$(Configuration)\ ..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ - C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) - C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + C:\apps\vld\include;$(IncludePath) + C:\apps\vld\lib\Win32;$(LibraryPath) false @@ -61,13 +61,14 @@ Disabled 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) true - ..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32 + ..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include MultiThreadedDebug Windows true - ..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies) + %(AdditionalDependencies) + ..\..\external\openssl\lib;%(AdditionalLibraryDirectories) @@ -79,7 +80,7 @@ true 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) true - ..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32 + ..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include MultiThreaded @@ -87,7 +88,8 @@ true true true - ..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies) + %(AdditionalDependencies) + ..\..\external\openssl\lib;%(AdditionalLibraryDirectories) diff --git a/client/tp_assist_win/ts_cfg.cpp b/client/tp_assist_win/ts_cfg.cpp index 722a7b5..f9214f6 100644 --- a/client/tp_assist_win/ts_cfg.cpp +++ b/client/tp_assist_win/ts_cfg.cpp @@ -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 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 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; +} diff --git a/client/tp_assist_win/ts_env.cpp b/client/tp_assist_win/ts_env.cpp index db08129..767ef7b 100644 --- a/client/tp_assist_win/ts_env.cpp +++ b/client/tp_assist_win/ts_env.cpp @@ -1,71 +1,74 @@ -#include "stdafx.h" -#include "ts_env.h" - -#include -#ifdef EX_OS_WIN32 -# include -//# include -#endif - -TsEnv g_env; - -//======================================================= -// -//======================================================= - -TsEnv::TsEnv() -{} - -TsEnv::~TsEnv() -{} - -bool TsEnv::init(void) -{ - if (!ex_exec_file(m_exec_file)) - return false; - - m_exec_path = m_exec_file; - if (!ex_dirname(m_exec_path)) - return false; - - m_cfg_file = m_exec_path; - ex_path_join(m_cfg_file, false, L"cfg", L"tp-assist.json", NULL); - - m_log_path = m_exec_path; - ex_path_join(m_log_path, false, L"log", NULL); - - ex_wstr cfg_default; - -#ifdef _DEBUG - m_site_path = m_exec_path; - ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL); - - m_tools_path = m_exec_path; - ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL); - - cfg_default = m_exec_path; - ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL); - -#else - m_site_path = m_exec_path; - ex_path_join(m_site_path, false, L"site", NULL); - - m_tools_path = m_exec_path; - ex_path_join(m_tools_path, false, L"tools", NULL); - - cfg_default = m_exec_path; - ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL); -#endif - - if (!ex_is_file_exists(m_cfg_file.c_str())) { - ex_wstr cfg_path = m_exec_path; - ex_path_join(cfg_path, false, L"cfg", NULL); - - ex_mkdirs(cfg_path); - - if (!ex_copy_file(cfg_default.c_str(), m_cfg_file.c_str())) - return false; -} - - return true; -} +#include "stdafx.h" +#include "ts_env.h" + +#include +#ifdef EX_OS_WIN32 +# include +//# include +#endif + +TsEnv g_env; + +//======================================================= +// +//======================================================= + +TsEnv::TsEnv() +{} + +TsEnv::~TsEnv() +{} + +bool TsEnv::init(void) +{ + if (!ex_exec_file(m_exec_file)) + return false; + + m_exec_path = m_exec_file; + if (!ex_dirname(m_exec_path)) + return false; + + m_cfg_file = m_exec_path; + ex_path_join(m_cfg_file, false, L"cfg", L"tp-assist.json", NULL); + + m_log_path = m_exec_path; + ex_path_join(m_log_path, false, L"log", NULL); + + ex_wstr cfg_default; + +#ifdef _DEBUG + m_site_path = m_exec_path; + ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL); + +// m_tools_path = m_exec_path; +// ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL); + + cfg_default = m_exec_path; + ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL); + +#else + m_site_path = m_exec_path; + ex_path_join(m_site_path, false, L"site", NULL); + +// m_tools_path = m_exec_path; +// ex_path_join(m_tools_path, false, L"tools", NULL); + + cfg_default = m_exec_path; + ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL); +#endif + + m_tools_path = m_exec_path; + ex_path_join(m_tools_path, false, L"tools", NULL); + + if (!ex_is_file_exists(m_cfg_file.c_str())) { + ex_wstr cfg_path = m_exec_path; + ex_path_join(cfg_path, false, L"cfg", NULL); + + ex_mkdirs(cfg_path); + + if (!ex_copy_file(cfg_default.c_str(), m_cfg_file.c_str())) + return false; +} + + return true; +} diff --git a/client/tp_assist_win/ts_http_rpc.cpp b/client/tp_assist_win/ts_http_rpc.cpp index 8c57c25..3cebf9c 100644 --- a/client/tp_assist_win/ts_http_rpc.cpp +++ b/client/tp_assist_win/ts_http_rpc.cpp @@ -1,1207 +1,1191 @@ -#include "stdafx.h" - -#pragma warning(disable:4091) - -#include -#include -#include - -#pragma comment(lib, "Crypt32.lib") - -#include - -#include "ts_http_rpc.h" -#include "dlg_main.h" -#include "ts_ver.h" -#include "ts_env.h" - -/* -1. -SecureCRT֧ñǩҳı⣬в /N "tab name"Ϳ -Example: -To launch a new Telnet session, displaying the name "Houston, TX" on the tab, use the following: -/T /N "Houston, TX" /TELNET 192.168.0.6 - -2. -SecureCRTŵһڵIJͬǩҳУʹò /T - SecureCRT.exe /T /N "TP#ssh://192.168.1.3" /SSH2 /L root /PASSWORD 1234 120.26.109.25 - -3. -telnetͻ˵ - putty.exe telnet://administrator@127.0.0.1:52389 -SecureCRTҪ - SecureCRT.exe /T /N "TP#telnet://192.168.1.3" /SCRIPT X:\path\to\startup.vbs /TELNET 127.0.0.1 52389 -Уstartup.vbsΪ ----------ļʼ--------- -#$language = "VBScript" -#$interface = "1.0" -Sub main - crt.Screen.Synchronous = True - crt.Screen.WaitForString "ogin: " - crt.Screen.Send "SESSION-ID" & VbCr - crt.Screen.Synchronous = False -End Sub ----------ļ--------- - -4. ΪputtyĴڱǩʾIPԳӳɹ˷ - PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@192.168.1.2: \w\a\]$PS1" -ֹˣubuntuԣ֪Ƿܹ֧еLinuxSecureCRTԴ˱ʾԡ -*/ - -//#define RDP_CLIENT_SYSTEM_BUILTIN -// #define RDP_CLIENT_SYSTEM_ACTIVE_CONTROL -//#define RDP_CLIENT_FREERDP - - -//#ifdef RDP_CLIENT_SYSTEM_BUILTIN - -//connect to console:i:%d -//compression:i:1 -//bitmapcachepersistenable:i:1 - -std::string rdp_content = "\ -administrative session:i:%d\n\ -screen mode id:i:%d\n\ -use multimon:i:0\n\ -desktopwidth:i:%d\n\ -desktopheight:i:%d\n\ -session bpp:i:16\n\ -winposstr:s:0,1,%d,%d,%d,%d\n\ -compression:i:1\n\ -bitmapcachepersistenable:i:1\n\ -keyboardhook:i:2\n\ -audiocapturemode:i:0\n\ -videoplaybackmode:i:1\n\ -connection type:i:7\n\ -networkautodetect:i:1\n\ -bandwidthautodetect:i:1\n\ -displayconnectionbar:i:1\n\ -enableworkspacereconnect:i:0\n\ -disable wallpaper:i:1\n\ -allow font smoothing:i:0\n\ -allow desktop composition:i:0\n\ -disable full window drag:i:1\n\ -disable menu anims:i:1\n\ -disable themes:i:1\n\ -disable cursor setting:i:1\n\ -full address:s:%s:%d\n\ -audiomode:i:0\n\ -redirectprinters:i:0\n\ -redirectcomports:i:0\n\ -redirectsmartcards:i:0\n\ -redirectclipboard:i:%d\n\ -redirectposdevices:i:0\n\ -autoreconnection enabled:i:0\n\ -authentication level:i:2\n\ -prompt for credentials:i:0\n\ -negotiate security layer:i:1\n\ -remoteapplicationmode:i:0\n\ -alternate shell:s:\n\ -shell working directory:s:\n\ -gatewayhostname:s:\n\ -gatewayusagemethod:i:4\n\ -gatewaycredentialssource:i:4\n\ -gatewayprofileusagemethod:i:0\n\ -promptcredentialonce:i:0\n\ -gatewaybrokeringtype:i:0\n\ -use redirection server name:i:0\n\ -rdgiskdcproxy:i:0\n\ -kdcproxyname:s:\n\ -drivestoredirect:s:%s\n\ -username:s:%s\n\ -password 51:b:%s\n\ -"; - -//redirectdirectx:i:0\n\ -//prompt for credentials on client:i:0\n\ - -//#endif - - -TsHttpRpc g_http_interface; -TsHttpRpc g_https_interface; - -void http_rpc_main_loop(bool is_https) { - if (is_https) { - if (!g_https_interface.init_https()) { - EXLOGE("[ERROR] can not start HTTPS-RPC listener, maybe port %d is already in use.\n", TS_HTTPS_RPC_PORT); - return; - } - - EXLOGW("======================================================\n"); - EXLOGW("[rpc] TeleportAssist-HTTPS-RPC ready on 127.0.0.1:%d\n", TS_HTTPS_RPC_PORT); - - g_https_interface.run(); - - EXLOGW("[rpc] HTTPS-Server main loop end.\n"); - } else { - if (!g_http_interface.init_http()) { - EXLOGE("[ERROR] can not start HTTP-RPC listener, maybe port %d is already in use.\n", TS_HTTP_RPC_PORT); - return; - } - - EXLOGW("======================================================\n"); - EXLOGW("[rpc] TeleportAssist-HTTP-RPC ready on 127.0.0.1:%d\n", TS_HTTP_RPC_PORT); - - g_http_interface.run(); - - EXLOGW("[rpc] HTTP-Server main loop end.\n"); - } -} - -void http_rpc_stop(bool is_https) { - if (is_https) - g_https_interface.stop(); - else - g_http_interface.stop(); -} - -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') - -int ts_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) { - int i, j, a, b; - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (src[i] == '%') { - if (i < src_len - 2 && isxdigit(*(const unsigned char *)(src + i + 1)) && - isxdigit(*(const unsigned char *)(src + i + 2))) { - a = tolower(*(const unsigned char *)(src + i + 1)); - b = tolower(*(const unsigned char *)(src + i + 2)); - dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } else { - return -1; - } - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; /* Null-terminate the destination */ - - return i >= src_len ? j : -1; -} - -bool calc_psw51b(const char* password, std::string& ret) { - DATA_BLOB DataIn; - DATA_BLOB DataOut; - - ex_wstr w_pswd; - ex_astr2wstr(password, w_pswd, EX_CODEPAGE_ACP); - - DataIn.cbData = w_pswd.length() * sizeof(wchar_t); - DataIn.pbData = (BYTE*)w_pswd.c_str(); - - - if (!CryptProtectData(&DataIn, L"psw", nullptr, nullptr, nullptr, 0, &DataOut)) - return false; - - char szRet[5] = { 0 }; - for (DWORD i = 0; i < DataOut.cbData; ++i) { - sprintf_s(szRet, 5, "%02X", DataOut.pbData[i]); - ret += szRet; - } - - LocalFree(DataOut.pbData); - return true; -} - -bool isDegital(std::string str) { - for (int i = 0; i < str.size(); i++) { - if (str.at(i) == '-' && str.size() > 1) // пָܳ - continue; - if (str.at(i) > '9' || str.at(i) < '0') - return false; - } - return true; -} - -std::string strtolower(std::string str) { - for (int i = 0; i < str.size(); i++) - { - str[i] = tolower(str[i]); - } - return str; -} - -void SplitString(const std::string& s, std::vector& v, const std::string& c) -{ - std::string::size_type pos1, pos2; - pos2 = s.find(c); - pos1 = 0; - while (std::string::npos != pos2) - { - v.push_back(s.substr(pos1, pos2 - pos1)); - - pos1 = pos2 + c.size(); - pos2 = s.find(c, pos1); - } - if (pos1 != s.length()) - v.push_back(s.substr(pos1)); -} - -TsHttpRpc::TsHttpRpc() { - m_stop = false; - mg_mgr_init(&m_mg_mgr, nullptr); -} - -TsHttpRpc::~TsHttpRpc() { - mg_mgr_free(&m_mg_mgr); -} - -bool TsHttpRpc::init_http() { - struct mg_connection* nc = nullptr; - - 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) { - 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(); -} - -bool TsHttpRpc::init_https() { - ex_wstr file_ssl_cert = g_env.m_exec_path; - ex_path_join(file_ssl_cert, true, L"cfg", L"localhost.pem", NULL); - ex_wstr file_ssl_key = g_env.m_exec_path; - ex_path_join(file_ssl_key, true, L"cfg", L"localhost.key", NULL); - ex_astr _ssl_cert; - 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://127.0.0.1:%d", TS_HTTPS_RPC_PORT); - - struct mg_connection* nc = nullptr; - nc = mg_bind_opt(&m_mg_mgr, addr, _mg_event_handler, bind_opts); - 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(); -} - -bool TsHttpRpc::_on_init() { - char file_name[MAX_PATH] = { 0 }; - if (!GetModuleFileNameA(nullptr, file_name, MAX_PATH)) - return false; - - int len = strlen(file_name); - - if (file_name[len] == '\\') - file_name[len] = '\0'; - - char* match = strrchr(file_name, '\\'); - if (match) - *match = '\0'; - - m_content_type_map[".js"] = "application/javascript"; - m_content_type_map[".png"] = "image/png"; - m_content_type_map[".jpeg"] = "image/jpeg"; - m_content_type_map[".jpg"] = "image/jpeg"; - m_content_type_map[".gif"] = "image/gif"; - m_content_type_map[".ico"] = "image/x-icon"; - m_content_type_map[".json"] = "image/json"; - m_content_type_map[".html"] = "text/html"; - m_content_type_map[".css"] = "text/css"; - m_content_type_map[".tif"] = "image/tiff"; - m_content_type_map[".tiff"] = "image/tiff"; - m_content_type_map[".svg"] = "text/html"; - - return true; -} - -void TsHttpRpc::run(void) { - while (!m_stop) { - mg_mgr_poll(&m_mg_mgr, 500); - } -} - -void TsHttpRpc::stop(void) { - m_stop = true; -} - -void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_data) { - struct http_message *hm = (struct http_message*)ev_data; - - TsHttpRpc* _this = (TsHttpRpc*)nc->user_data; - if (!_this) { - EXLOGE("[ERROR] invalid http request.\n"); - return; - } - - switch (ev) { - case MG_EV_HTTP_REQUEST: - { - ex_astr uri; - ex_chars _uri; - _uri.resize(hm->uri.len + 1); - memset(&_uri[0], 0, hm->uri.len + 1); - memcpy(&_uri[0], hm->uri.p, hm->uri.len); - uri = &_uri[0]; - -#ifdef EX_DEBUG - char* dbg_method = nullptr; - if (hm->method.len == 3 && 0 == memcmp(hm->method.p, "GET", hm->method.len)) - dbg_method = "GET"; - else if (hm->method.len == 4 && 0 == memcmp(hm->method.p, "POST", hm->method.len)) - dbg_method = "POST"; - else - dbg_method = "UNSUPPORTED-HTTP-METHOD"; - - EXLOGV("[rpc] got %s request: %s\n", dbg_method, uri.c_str()); -#endif - ex_astr ret_buf; - bool b_is_html = false; - -// if (uri == "/") { -// ex_wstr page = L"Teleport\n\n
Teleportֹ
"; -// 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; -// } - - if (uri == "/") { - uri = "/status.html"; - b_is_html = true; - } - else if (uri == "/config") { - uri = "/index.html"; - b_is_html = true; - } - - ex_astr temp; - int offset = uri.find("/", 1); - if (offset > 0) { - temp = uri.substr(1, offset - 1); - - if (temp == "api") { - ex_astr method; - ex_astr json_param; - int rv = _this->_parse_request(hm, method, json_param); - if (0 != rv) { - EXLOGE("[ERROR] http-rpc got invalid request.\n"); - _this->_create_json_ret(ret_buf, rv); - } else { - _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]); - 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) { - unsigned long file_size = 0; - char* buf = nullptr; - size_t ret = 0; - - fseek(file, 0, SEEK_END); - file_size = ftell(file); - buf = new char[file_size]; - memset(buf, 0, file_size); - 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_html) { - ex_wstr page = L"404 Not Found

404 Not Found


Teleport Assistor configuration page not found.

"; - 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]); - nc->flags |= MG_F_SEND_AND_CLOSE; - return; - } - - } - break; - default: - break; - } -} - -int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_astr& func_args) { - if (!req) - return TPE_FAILED; - - bool is_get = true; - if (req->method.len == 3 && 0 == memcmp(req->method.p, "GET", req->method.len)) - is_get = true; - else if (req->method.len == 4 && 0 == memcmp(req->method.p, "POST", req->method.len)) - is_get = false; - else - return TPE_HTTP_METHOD; - - ex_astrs strs; - - size_t pos_start = 1; // һֽڣһ '/' - - size_t i = 0; - for (i = pos_start; i < req->uri.len; ++i) { - if (req->uri.p[i] == '/') { - if (i - pos_start > 0) { - ex_astr tmp_uri; - tmp_uri.assign(req->uri.p + pos_start, i - pos_start); - strs.push_back(tmp_uri); - } - pos_start = i + 1; // ǰҵķָ - } - } - if (pos_start < req->uri.len) { - ex_astr tmp_uri; - tmp_uri.assign(req->uri.p + pos_start, req->uri.len - pos_start); - strs.push_back(tmp_uri); - } - - if (strs.empty() || strs[0] != "api") - return TPE_PARAM; - - if (is_get) { - if (2 == strs.size()) { - func_cmd = strs[1]; - } else if (3 == strs.size()) { - func_cmd = strs[1]; - func_args = strs[2]; - } else { - return TPE_PARAM; - } - } else { - if (2 == strs.size()) { - func_cmd = strs[1]; - } else { - return TPE_PARAM; - } - - if (req->body.len > 0) { - func_args.assign(req->body.p, req->body.len); - } - } - - if (func_args.length() > 0) { - // url-decode - int len = func_args.length() * 2; - ex_chars sztmp; - sztmp.resize(len); - memset(&sztmp[0], 0, len); - if (-1 == ts_url_decode(func_args.c_str(), func_args.length(), &sztmp[0], len, 0)) - return TPE_HTTP_URL_ENCODE; - - func_args = &sztmp[0]; - } - - EXLOGV("[rpc] method=%s, json_param=%s\n", func_cmd.c_str(), func_args.c_str()); - - return TPE_OK; -} - -void TsHttpRpc::_process_js_request(const ex_astr& func_cmd, const ex_astr& func_args, ex_astr& buf) { - if (func_cmd == "get_version") { - _rpc_func_get_version(func_args, buf); - } else if (func_cmd == "run") { - _rpc_func_run_client(func_args, buf); - } else if (func_cmd == "rdp_play") { - _rpc_func_rdp_play(func_args, buf); - } else if (func_cmd == "get_config") { - _rpc_func_get_config(func_args, buf); - } else if (func_cmd == "set_config") { - _rpc_func_set_config(func_args, buf); - } else if (func_cmd == "file_action") { - _rpc_func_file_action(func_args, buf); - } else { - EXLOGE("[rpc] got unknown command: %s\n", func_cmd.c_str()); - _create_json_ret(buf, TPE_UNKNOWN_CMD); - } -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) { - // أ {"code":123} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, Json::Value& jr_root) { - Json::FastWriter jr_writer; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf) -{ - //urlprotocol÷ʽ - // url-decode - std::string func_args = args; - if (func_args.length() > 0) - { - int len = func_args.length() * 2; - ex_chars sztmp; - sztmp.resize(len); - memset(&sztmp[0], 0, len); - if (-1 == ts_url_decode(func_args.c_str(), func_args.length(), &sztmp[0], len, 0)) - return ; - - func_args = &sztmp[0]; - } - EXLOGD(("%s\n"), func_args.c_str()); - //ιteleport://{}/,ֻ - std::string urlproto_appname = TP_URLPROTO_APP_NAME; - urlproto_appname += "://{"; - func_args.erase(0, urlproto_appname.length());//ȥһURLPROTO_APP_NAMEԼ://ַ - int pos = func_args.length() - 1; - if (func_args.substr(pos, 1) == "/") - func_args.erase(pos - 1, 2);//ȥһ}/ַ - else - func_args.erase(pos, 1); - - //Сieʱԭjsonṹе"ȥҪ¸ʽΪjsonʽ - if (func_args.find("\"", 0) == std::string::npos) { - std::vector strv; - SplitString(func_args, strv, ","); - func_args = ""; - for (std::vector::size_type i = 0; i < strv.size(); i++) { - std::vector strv1; - SplitString(strv[i], strv1, ":"); - strv1[0] = "\"" + strv1[0] + "\""; - if (!isDegital(strv1[1]) && strtolower(strv1[1]) != "true" && strtolower(strv1[1]) != "false") - strv1[1] = "\"" + strv1[1] + "\""; - - strv[i] = strv1[0] + ":" + strv1[1]; - if (i == 0) - func_args = strv[i]; - else - func_args += "," + strv[i]; - } - } - func_args = "{" + func_args + "}"; - EXLOGD(("%s\n"), func_args.c_str()); - //TsHttpRpc_rpc_func_run_clientͻ - _rpc_func_run_client(func_args, buf); -} - -void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) { - // Σ{"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2} - // authmode: 1=password, 2=private-key - // protocol: 1=rdp, 2=ssh - // SSHأ {"code":0, "data":{"sid":"0123abcde"}} - // RDPأ {"code":0, "data":{"sid":"0123abcde0A"}} - - Json::Reader jreader; - Json::Value jsRoot; - - if (!jreader.parse(func_args.c_str(), jsRoot)) { - _create_json_ret(buf, TPE_JSON_FORMAT); - return; - } - if (!jsRoot.isObject()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - // жϲǷȷ - if (!jsRoot["teleport_ip"].isString() - || !jsRoot["teleport_port"].isNumeric() || !jsRoot["remote_host_ip"].isString() - || !jsRoot["session_id"].isString() || !jsRoot["protocol_type"].isNumeric() || !jsRoot["protocol_sub_type"].isNumeric() - || !jsRoot["protocol_flag"].isNumeric() - ) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - int pro_type = jsRoot["protocol_type"].asUInt(); - int pro_sub = jsRoot["protocol_sub_type"].asInt(); - ex_u32 protocol_flag = jsRoot["protocol_flag"].asUInt(); - - ex_astr teleport_ip = jsRoot["teleport_ip"].asCString(); - int teleport_port = jsRoot["teleport_port"].asUInt(); - - ex_astr remote_host_name = jsRoot["remote_host_name"].asCString(); - - ex_astr real_host_ip = jsRoot["remote_host_ip"].asCString(); - ex_astr sid = jsRoot["session_id"].asCString(); - - ex_wstr w_exe_path; - WCHAR w_szCommandLine[MAX_PATH] = { 0 }; - - - ex_wstr w_sid; - ex_astr2wstr(sid, w_sid); - ex_wstr w_teleport_ip; - ex_astr2wstr(teleport_ip, w_teleport_ip); - ex_wstr w_real_host_ip; - ex_astr2wstr(real_host_ip, w_real_host_ip); - ex_wstr w_remote_host_name; - ex_astr2wstr(remote_host_name, w_remote_host_name); - WCHAR w_port[32] = { 0 }; - swprintf_s(w_port, _T("%d"), teleport_port); - - ex_wstr tmp_rdp_file; // for .rdp file - - if (pro_type == TP_PROTOCOL_TYPE_RDP) { - //============================================== - // RDP - //============================================== - - 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; - bool rdp_console = false; - - if (!jsRoot["rdp_width"].isNull()) { - if (jsRoot["rdp_width"].isNumeric()) { - rdp_w = jsRoot["rdp_width"].asUInt(); - } else { - _create_json_ret(buf, TPE_PARAM); - return; - } - } - - if (!jsRoot["rdp_height"].isNull()) { - if (jsRoot["rdp_height"].isNumeric()) { - rdp_h = jsRoot["rdp_height"].asUInt(); - } else { - _create_json_ret(buf, TPE_PARAM); - return; - } - } - - if (!jsRoot["rdp_console"].isNull()) { - if (jsRoot["rdp_console"].isBool()) { - rdp_console = jsRoot["rdp_console"].asBool(); - } else { - _create_json_ret(buf, TPE_PARAM); - return; - } - } - - if (!flag_console) - rdp_console = false; - - - int split_pos = sid.length() - 2; - ex_astr real_sid = sid.substr(0, split_pos); - ex_astr str_pwd_len = sid.substr(split_pos, sid.length()); - int n_pwd_len = strtol(str_pwd_len.c_str(), nullptr, 16); - n_pwd_len -= real_sid.length(); - n_pwd_len -= 2; - char szPwd[256] = { 0 }; - for (int i = 0; i < n_pwd_len; i++) { - szPwd[i] = '*'; - } - - ex_astr2wstr(real_sid, w_sid); - - w_exe_path = _T("\""); - w_exe_path += g_cfg.rdp_app + _T("\" "); - - ex_wstr rdp_name = g_cfg.rdp_name; - if (rdp_name == L"mstsc") { - w_exe_path += g_cfg.rdp_cmdline; - - int width = 0; - int higth = 0; - int cx = 0; - int cy = 0; - - int display = 1; - int iWidth = GetSystemMetrics(SM_CXSCREEN); - int iHeight = GetSystemMetrics(SM_CYSCREEN); - - if (rdp_w == 0 || rdp_h == 0) { - //ȫ - width = iWidth; - higth = iHeight; - display = 2; - } else { - width = rdp_w; - higth = rdp_h; - display = 1; - } - - cx = (iWidth - width) / 2; - cy = (iHeight - higth) / 2; - if (cx < 0) { - cx = 0; - } - if (cy < 0) { - cy = 0; - } - - // int console_mode = 0; - // if (rdp_console) - // console_mode = 1; - - std::string psw51b; - if (!calc_psw51b(szPwd, psw51b)) { - EXLOGE("calc password failed.\n"); - _create_json_ret(buf, TPE_FAILED); - return; - } - - real_sid = "01" + real_sid; - - char sz_rdp_file_content[4096] = { 0 }; - sprintf_s(sz_rdp_file_content, 4096, rdp_content.c_str() - , (flag_console && rdp_console) ? 1 : 0 - , display, width, higth - , cx, cy, cx + width + 100, cy + higth + 100 - , teleport_ip.c_str(), teleport_port - , flag_clipboard ? 1 : 0 - , flag_disk ? "*" : "" - , real_sid.c_str() - , psw51b.c_str() - ); - - char sz_file_name[MAX_PATH] = { 0 }; - char temp_path[MAX_PATH] = { 0 }; - DWORD ret = GetTempPathA(MAX_PATH, temp_path); - if (ret <= 0) { - EXLOGE("fopen failed (%d).\n", GetLastError()); - _create_json_ret(buf, TPE_FAILED); - return; - } - - ex_astr temp_host_ip = real_host_ip; - ex_replace_all(temp_host_ip, ".", "-"); - - sprintf_s(sz_file_name, MAX_PATH, ("%s%s.rdp"), temp_path, temp_host_ip.c_str()); - - FILE* f = NULL; - if (fopen_s(&f, sz_file_name, "wt") != 0) { - EXLOGE("fopen failed (%d).\n", GetLastError()); - _create_json_ret(buf, TPE_OPENFILE); - return; - } - // Write a string into the file. - fwrite(sz_rdp_file_content, strlen(sz_rdp_file_content), 1, f); - fclose(f); - ex_astr2wstr(sz_file_name, tmp_rdp_file); - - // 滻 - ex_replace_all(w_exe_path, _T("{tmp_rdp_file}"), tmp_rdp_file); - } else if (g_cfg.rdp_name == L"freerdp") { - w_exe_path += L"{size} {console} {clipboard} {drives} "; - w_exe_path += g_cfg.rdp_cmdline; - - ex_wstr w_screen; - - if (rdp_w == 0 || rdp_h == 0) { - //ȫ - w_screen = _T("/f"); - } else { - char sz_size[64] = { 0 }; - ex_strformat(sz_size, 63, "/size:%dx%d", rdp_w, rdp_h); - ex_astr2wstr(sz_size, w_screen); - } - - // wchar_t* w_console = NULL; - // - // if (flag_console && rdp_console) - // { - // w_console = L"/admin"; - // } - // else - // { - // w_console = L""; - // } - - ex_wstr w_password; - ex_astr2wstr(szPwd, w_password); - w_exe_path += L" /p:"; - w_exe_path += w_password; - - w_sid = L"02" + w_sid; - - w_exe_path += L" /gdi:sw"; // ʹȾgdi:hwʹӲ٣ǻֺܶڿ飨¼طʱģ - w_exe_path += L" -grab-keyboard"; // [new style] ֹFreeRDPʧȥؼӦСһFreeRDPڣòƲã - - // 滻 - ex_replace_all(w_exe_path, _T("{size}"), w_screen); - - if (flag_console && rdp_console) - ex_replace_all(w_exe_path, _T("{console}"), L"/admin"); - else - ex_replace_all(w_exe_path, _T("{console}"), L""); - - //ex_replace_all(w_exe_path, _T("{clipboard}"), L"+clipboard"); - - if (flag_clipboard) - ex_replace_all(w_exe_path, _T("{clipboard}"), L"/clipboard"); - else - ex_replace_all(w_exe_path, _T("{clipboard}"), L"-clipboard"); - - if (flag_disk) - ex_replace_all(w_exe_path, _T("{drives}"), L"/drives"); - else - ex_replace_all(w_exe_path, _T("{drives}"), L"-drives"); - } else { - _create_json_ret(buf, TPE_FAILED); - return; - } - } else if (pro_type == TP_PROTOCOL_TYPE_SSH) { - //============================================== - // SSH - //============================================== - - if (pro_sub == TP_PROTOCOL_TYPE_SSH_SHELL) { - w_exe_path = _T("\""); - w_exe_path += g_cfg.ssh_app + _T("\" "); - w_exe_path += g_cfg.ssh_cmdline; - } else { - w_exe_path = _T("\""); - w_exe_path += g_cfg.scp_app + _T("\" "); - w_exe_path += g_cfg.scp_cmdline; - } - } else if (pro_type == TP_PROTOCOL_TYPE_TELNET) { - //============================================== - // TELNET - //============================================== - w_exe_path = _T("\""); - w_exe_path += g_cfg.telnet_app + _T("\" "); - w_exe_path += g_cfg.telnet_cmdline; - } - - ex_replace_all(w_exe_path, _T("{host_ip}"), w_teleport_ip.c_str()); - ex_replace_all(w_exe_path, _T("{host_port}"), w_port); - ex_replace_all(w_exe_path, _T("{user_name}"), w_sid.c_str()); - ex_replace_all(w_exe_path, _T("{host_name}"), w_remote_host_name.c_str()); - ex_replace_all(w_exe_path, _T("{real_ip}"), w_real_host_ip.c_str()); - ex_replace_all(w_exe_path, _T("{assist_tools_path}"), g_env.m_tools_path.c_str()); - - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - ZeroMemory(&pi, sizeof(pi)); - - Json::Value root_ret; - ex_astr utf8_path; - ex_wstr2astr(w_exe_path, utf8_path, EX_CODEPAGE_UTF8); - root_ret["path"] = utf8_path; - - if (!CreateProcess(NULL, (wchar_t *)w_exe_path.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { - EXLOGE(_T("CreateProcess() failed. Error=0x%08X.\n %s\n"), GetLastError(), w_exe_path.c_str()); - root_ret["code"] = TPE_START_CLIENT; - _create_json_ret(buf, root_ret); - return; - } - - 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) { - Json::Reader jreader; - Json::Value jsRoot; - - if (!jreader.parse(func_args.c_str(), jsRoot)) { - _create_json_ret(buf, TPE_JSON_FORMAT); - return; - } - - // жϲǷȷ - if (!jsRoot["rid"].isInt() - || !jsRoot["web"].isString() - || !jsRoot["sid"].isString() - || !jsRoot["user"].isString() - || !jsRoot["acc"].isString() - || !jsRoot["host"].isString() - || !jsRoot["start"].isString() - ) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - int rid = jsRoot["rid"].asInt(); - ex_astr a_url_base = jsRoot["web"].asCString(); - ex_astr a_sid = jsRoot["sid"].asCString(); - ex_astr a_user = jsRoot["user"].asCString(); - ex_astr a_acc = jsRoot["acc"].asCString(); - ex_astr a_host = jsRoot["host"].asCString(); - ex_astr a_start = jsRoot["start"].asCString(); - - char cmd_args[1024] = { 0 }; - ex_strformat(cmd_args, 1023, "%d \"%s\" \"%09d-%s-%s-%s-%s\"", rid, a_sid.c_str(), rid, a_user.c_str(), a_acc.c_str(), a_host.c_str(), a_start.c_str()); - - // TODO: ϲӦǰתΪIPIJӦý͸ɲԼȥ - // ڸFreeRDPIJʱΪ˴ӷļʹMongoose⣬⣨ò첽ѯDNS⣩ - // ʱֽIPת - { - unsigned int port_i = 0; - struct mg_str scheme, query, fragment, user_info, host, path; - - if (mg_parse_uri(mg_mk_str(a_url_base.c_str()), &scheme, &user_info, &host, &port_i, &path, &query, &fragment) != 0) { - EXLOGE(_T("parse url failed.\n")); - Json::Value root_ret; - root_ret["code"] = TPE_PARAM; - _create_json_ret(buf, root_ret); - return; - } - - ex_astr _scheme; - _scheme.assign(scheme.p, scheme.len); - - // hostתΪIP - ex_astr str_tp_host; - str_tp_host.assign(host.p, host.len); - struct hostent *tp_host = gethostbyname(str_tp_host.c_str()); - if (NULL == tp_host) { - EXLOGE(_T("resolve host name failed.\n")); - Json::Value root_ret; - root_ret["code"] = TPE_PARAM; - _create_json_ret(buf, root_ret); - return; - } - - int i = 0; - char* _ip = NULL; - if (tp_host->h_addrtype == AF_INET) { - struct in_addr addr; - while (tp_host->h_addr_list[i] != 0) { - addr.s_addr = *(u_long *)tp_host->h_addr_list[i++]; - _ip = inet_ntoa(addr); - break; - } - } - - if (NULL == _ip) { - EXLOGE(_T("resolve host name failed.\n")); - Json::Value root_ret; - root_ret["code"] = TPE_PARAM; - _create_json_ret(buf, root_ret); - return; - } - - char _url_base[256]; - ex_strformat(_url_base, 255, "%s://%s:%d", _scheme.c_str(), _ip, port_i); - a_url_base = _url_base; - } - - ex_wstr w_url_base; - ex_astr2wstr(a_url_base, w_url_base); - ex_wstr w_cmd_args; - ex_astr2wstr(cmd_args, w_cmd_args); - - ex_wstr w_exe_path; - w_exe_path = _T("\""); - w_exe_path += g_env.m_tools_path + _T("\\tprdp\\tprdp-replay.exe\""); - w_exe_path += _T(" \""); - w_exe_path += w_url_base; - w_exe_path += _T("\" "); - w_exe_path += w_cmd_args; - - Json::Value root_ret; - ex_astr utf8_path; - ex_wstr2astr(w_exe_path, utf8_path, EX_CODEPAGE_UTF8); - root_ret["cmdline"] = utf8_path; - - EXLOGD(w_exe_path.c_str()); - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - ZeroMemory(&pi, sizeof(pi)); - if (!CreateProcess(NULL, (wchar_t *)w_exe_path.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { - EXLOGE(_T("CreateProcess() failed. Error=0x%08X.\n %s\n"), GetLastError(), w_exe_path.c_str()); - root_ret["code"] = TPE_START_CLIENT; - _create_json_ret(buf, root_ret); - return; - } - - root_ret["code"] = TPE_OK; - _create_json_ret(buf, root_ret); - return; -} - -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; - } - - if (!g_cfg.save(func_args)) - _create_json_ret(buf, TPE_FAILED); - else - _create_json_ret(buf, TPE_OK); -} - -void TsHttpRpc::_rpc_func_file_action(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; - } - // жϲǷȷ - if (!jsRoot["action"].isNumeric()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - int action = jsRoot["action"].asUInt(); - - HWND hParent = GetForegroundWindow(); - if (NULL == hParent) - hParent = g_hDlgMain; - - BOOL ret = FALSE; - wchar_t wszReturnPath[MAX_PATH] = _T(""); - - if (action == 1 || action == 2) { - OPENFILENAME ofn; - ex_wstr wsDefaultName; - ex_wstr wsDefaultPath; - StringCchCopy(wszReturnPath, MAX_PATH, wsDefaultName.c_str()); - - ZeroMemory(&ofn, sizeof(ofn)); - - ofn.lStructSize = sizeof(ofn); - ofn.lpstrTitle = _T("ѡļ"); - ofn.hwndOwner = hParent; - ofn.lpstrFilter = _T("ִг (*.exe)\0*.exe\0"); - ofn.lpstrFile = wszReturnPath; - ofn.nMaxFile = MAX_PATH; - ofn.lpstrInitialDir = wsDefaultPath.c_str(); - ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST; - - if (action == 1) { - ofn.Flags |= OFN_FILEMUSTEXIST; - ret = GetOpenFileName(&ofn); - } else { - ofn.Flags |= OFN_OVERWRITEPROMPT; - ret = GetSaveFileName(&ofn); - } - } else if (action == 3) { - BROWSEINFO bi; - ZeroMemory(&bi, sizeof(BROWSEINFO)); - bi.hwndOwner = NULL; - bi.pidlRoot = NULL; - bi.pszDisplayName = wszReturnPath; //˲ΪNULLʾԻ - bi.lpszTitle = _T("ѡĿ¼"); - bi.ulFlags = BIF_RETURNONLYFSDIRS; - bi.lpfn = NULL; - bi.iImage = 0; //ʼڲbi - LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//ʾѡԻ - if (pIDList) { - ret = true; - SHGetPathFromIDList(pIDList, wszReturnPath); - } else { - ret = false; - } - } else if (action == 4) { - ex_wstr wsDefaultName; - ex_wstr wsDefaultPath; - - if (wsDefaultPath.length() == 0) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_wstr::size_type pos = 0; - - while (ex_wstr::npos != (pos = wsDefaultPath.find(L"/", pos))) { - wsDefaultPath.replace(pos, 1, L"\\"); - pos += 1; - } - - ex_wstr wArg = L"/select, \""; - wArg += wsDefaultPath; - wArg += L"\""; - if ((int)ShellExecute(hParent, _T("open"), _T("explorer"), wArg.c_str(), NULL, SW_SHOW) > 32) - ret = true; - else - ret = false; - } - - if (ret) { - if (action == 1 || action == 2 || action == 3) { - ex_astr utf8_path; - ex_wstr2astr(wszReturnPath, utf8_path, EX_CODEPAGE_UTF8); - Json::Value root; - root["code"] = TPE_OK; - root["path"] = utf8_path; - _create_json_ret(buf, root); - - return; - } else { - _create_json_ret(buf, TPE_OK); - return; - } - } else { - _create_json_ret(buf, TPE_DATA); - return; - } -} - -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; - ex_wstr2astr(w_version, version, EX_CODEPAGE_UTF8); - root_ret["version"] = version; - root_ret["code"] = TPE_OK; - _create_json_ret(buf, root_ret); - return; -} +#include "stdafx.h" + +#pragma warning(disable:4091) + +#include +#include +#include + +#pragma comment(lib, "Crypt32.lib") + +#include + +#include "ts_http_rpc.h" +#include "dlg_main.h" +#include "ts_ver.h" +#include "ts_env.h" + +/* +1. +SecureCRT支持设置标签页的标题,命令行参数 /N "tab name"就可以 +Example: +To launch a new Telnet session, displaying the name "Houston, TX" on the tab, use the following: +/T /N "Houston, TX" /TELNET 192.168.0.6 + +2. +多次启动的SecureCRT放到一个窗口的不同标签页中,使用参数: /T + SecureCRT.exe /T /N "TP#ssh://192.168.1.3" /SSH2 /L root /PASSWORD 1234 120.26.109.25 + +3. +telnet客户端的启动: + putty.exe telnet://administrator@127.0.0.1:52389 +如果是SecureCRT,则需要 + SecureCRT.exe /T /N "TP#telnet://192.168.1.3" /SCRIPT X:\path\to\startup.vbs /TELNET 127.0.0.1 52389 +其中,startup.vbs的内容为: +---------文件开始--------- +#$language = "VBScript" +#$interface = "1.0" +Sub main + crt.Screen.Synchronous = True + crt.Screen.WaitForString "ogin: " + crt.Screen.Send "SESSION-ID" & VbCr + crt.Screen.Synchronous = False +End Sub +---------文件结束--------- + +4. 为了让putty的窗口标签显示正常的IP,可以尝试在连接成功后,主动向服务端发送下列命令: + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@192.168.1.2: \w\a\]$PS1" +手工测试了,ubuntu服务器可以,不知道是否能够支持所有的Linux。SecureCRT对此表示忽略。 +*/ + +//#define RDP_CLIENT_SYSTEM_BUILTIN +// #define RDP_CLIENT_SYSTEM_ACTIVE_CONTROL +//#define RDP_CLIENT_FREERDP + + +//#ifdef RDP_CLIENT_SYSTEM_BUILTIN + +//connect to console:i:%d +//compression:i:1 +//bitmapcachepersistenable:i:1 + +std::string rdp_content = "\ +administrative session:i:%d\n\ +screen mode id:i:%d\n\ +use multimon:i:0\n\ +desktopwidth:i:%d\n\ +desktopheight:i:%d\n\ +session bpp:i:16\n\ +winposstr:s:0,1,%d,%d,%d,%d\n\ +bitmapcachepersistenable:i:1\n\ +bitmapcachesize:i:32000\n\ +compression:i:1\n\ +keyboardhook:i:2\n\ +audiocapturemode:i:0\n\ +videoplaybackmode:i:1\n\ +connection type:i:7\n\ +networkautodetect:i:1\n\ +bandwidthautodetect:i:1\n\ +disableclipboardredirection:i:0\n\ +displayconnectionbar:i:1\n\ +enableworkspacereconnect:i:0\n\ +disable wallpaper:i:1\n\ +allow font smoothing:i:0\n\ +allow desktop composition:i:0\n\ +disable full window drag:i:1\n\ +disable menu anims:i:1\n\ +disable themes:i:1\n\ +disable cursor setting:i:1\n\ +full address:s:%s:%d\n\ +audiomode:i:0\n\ +redirectprinters:i:0\n\ +redirectcomports:i:0\n\ +redirectsmartcards:i:0\n\ +redirectclipboard:i:%d\n\ +redirectposdevices:i:0\n\ +autoreconnection enabled:i:0\n\ +authentication level:i:2\n\ +prompt for credentials:i:0\n\ +negotiate security layer:i:1\n\ +remoteapplicationmode:i:0\n\ +alternate shell:s:\n\ +shell working directory:s:\n\ +gatewayhostname:s:\n\ +gatewayusagemethod:i:4\n\ +gatewaycredentialssource:i:4\n\ +gatewayprofileusagemethod:i:0\n\ +promptcredentialonce:i:0\n\ +gatewaybrokeringtype:i:0\n\ +use redirection server name:i:0\n\ +rdgiskdcproxy:i:0\n\ +kdcproxyname:s:\n\ +drivestoredirect:s:%s\n\ +username:s:%s\n\ +password 51:b:%s\n\ +"; + +// https://www.donkz.nl/overview-rdp-file-settings/ +// +// authentication level:i:2\n +// +// +// negotiate security layer:i:1\n +// 0 = negotiation is not enabled and the session is started by using Secure Sockets Layer (SSL). +// 1 = negotiation is enabled and the session is started by using x.224 encryption. + + + +//redirectdirectx:i:0\n\ +//prompt for credentials on client:i:0\n\ + +//#endif + + +TsHttpRpc g_http_interface; +TsHttpRpc g_https_interface; + +void http_rpc_main_loop(bool is_https) { + if (is_https) { + if (!g_https_interface.init_https()) { + EXLOGE("[ERROR] can not start HTTPS-RPC listener, maybe port %d is already in use.\n", TS_HTTPS_RPC_PORT); + return; + } + + EXLOGW("======================================================\n"); + EXLOGW("[rpc] TeleportAssist-HTTPS-RPC ready on 127.0.0.1:%d\n", TS_HTTPS_RPC_PORT); + + g_https_interface.run(); + + EXLOGW("[rpc] HTTPS-Server main loop end.\n"); + } else { + if (!g_http_interface.init_http()) { + EXLOGE("[ERROR] can not start HTTP-RPC listener, maybe port %d is already in use.\n", TS_HTTP_RPC_PORT); + return; + } + + EXLOGW("======================================================\n"); + EXLOGW("[rpc] TeleportAssist-HTTP-RPC ready on 127.0.0.1:%d\n", TS_HTTP_RPC_PORT); + + g_http_interface.run(); + + EXLOGW("[rpc] HTTP-Server main loop end.\n"); + } +} + +void http_rpc_stop(bool is_https) { + if (is_https) + g_https_interface.stop(); + else + g_http_interface.stop(); +} + +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + +int ts_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) { + int i, j, a, b; + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (src[i] == '%') { + if (i < src_len - 2 && isxdigit(*(const unsigned char *)(src + i + 1)) && + isxdigit(*(const unsigned char *)(src + i + 2))) { + a = tolower(*(const unsigned char *)(src + i + 1)); + b = tolower(*(const unsigned char *)(src + i + 2)); + dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else { + return -1; + } + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +bool calc_psw51b(const char* password, std::string& ret) { + DATA_BLOB DataIn; + DATA_BLOB DataOut; + + ex_wstr w_pswd; + ex_astr2wstr(password, w_pswd, EX_CODEPAGE_ACP); + + DataIn.cbData = w_pswd.length() * sizeof(wchar_t); + DataIn.pbData = (BYTE*)w_pswd.c_str(); + + + if (!CryptProtectData(&DataIn, L"psw", nullptr, nullptr, nullptr, 0, &DataOut)) + return false; + + char szRet[5] = { 0 }; + for (DWORD i = 0; i < DataOut.cbData; ++i) { + sprintf_s(szRet, 5, "%02X", DataOut.pbData[i]); + ret += szRet; + } + + LocalFree(DataOut.pbData); + return true; +} + +bool isDegital(std::string str) { + for (int i = 0; i < str.size(); i++) { + if (str.at(i) == '-' && str.size() > 1) // 有可能出现负数 + continue; + if (str.at(i) > '9' || str.at(i) < '0') + return false; + } + return true; +} + +std::string strtolower(std::string str) { + for (int i = 0; i < str.size(); i++) + { + str[i] = tolower(str[i]); + } + return str; +} + +void SplitString(const std::string& s, std::vector& v, const std::string& c) +{ + std::string::size_type pos1, pos2; + pos2 = s.find(c); + pos1 = 0; + while (std::string::npos != pos2) + { + v.push_back(s.substr(pos1, pos2 - pos1)); + + pos1 = pos2 + c.size(); + pos2 = s.find(c, pos1); + } + if (pos1 != s.length()) + v.push_back(s.substr(pos1)); +} + +TsHttpRpc::TsHttpRpc() { + m_stop = false; + mg_mgr_init(&m_mg_mgr, nullptr); +} + +TsHttpRpc::~TsHttpRpc() { + mg_mgr_free(&m_mg_mgr); +} + +bool TsHttpRpc::init_http() { + struct mg_connection* nc = nullptr; + + 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) { + 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(); +} + +bool TsHttpRpc::init_https() { + ex_wstr file_ssl_cert = g_env.m_exec_path; + ex_path_join(file_ssl_cert, true, L"cfg", L"localhost.pem", NULL); + ex_wstr file_ssl_key = g_env.m_exec_path; + ex_path_join(file_ssl_key, true, L"cfg", L"localhost.key", NULL); + ex_astr _ssl_cert; + 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://127.0.0.1:%d", TS_HTTPS_RPC_PORT); + + struct mg_connection* nc = nullptr; + nc = mg_bind_opt(&m_mg_mgr, addr, _mg_event_handler, bind_opts); + 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(); +} + +bool TsHttpRpc::_on_init() { + char file_name[MAX_PATH] = { 0 }; + if (!GetModuleFileNameA(nullptr, file_name, MAX_PATH)) + return false; + + int len = strlen(file_name); + + if (file_name[len] == '\\') + file_name[len] = '\0'; + + char* match = strrchr(file_name, '\\'); + if (match) + *match = '\0'; + + m_content_type_map[".js"] = "application/javascript"; + m_content_type_map[".png"] = "image/png"; + m_content_type_map[".jpeg"] = "image/jpeg"; + m_content_type_map[".jpg"] = "image/jpeg"; + m_content_type_map[".gif"] = "image/gif"; + m_content_type_map[".ico"] = "image/x-icon"; + m_content_type_map[".json"] = "image/json"; + m_content_type_map[".html"] = "text/html"; + m_content_type_map[".css"] = "text/css"; + m_content_type_map[".tif"] = "image/tiff"; + m_content_type_map[".tiff"] = "image/tiff"; + m_content_type_map[".svg"] = "text/html"; + + return true; +} + +void TsHttpRpc::run(void) { + while (!m_stop) { + mg_mgr_poll(&m_mg_mgr, 500); + } +} + +void TsHttpRpc::stop(void) { + m_stop = true; +} + +void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message*)ev_data; + + TsHttpRpc* _this = (TsHttpRpc*)nc->user_data; + if (!_this) { + EXLOGE("[ERROR] invalid http request.\n"); + return; + } + + switch (ev) { + case MG_EV_HTTP_REQUEST: + { + ex_astr uri; + ex_chars _uri; + _uri.resize(hm->uri.len + 1); + memset(&_uri[0], 0, hm->uri.len + 1); + memcpy(&_uri[0], hm->uri.p, hm->uri.len); + uri = &_uri[0]; + +#ifdef EX_DEBUG + char* dbg_method = nullptr; + if (hm->method.len == 3 && 0 == memcmp(hm->method.p, "GET", hm->method.len)) + dbg_method = "GET"; + else if (hm->method.len == 4 && 0 == memcmp(hm->method.p, "POST", hm->method.len)) + dbg_method = "POST"; + else + dbg_method = "UNSUPPORTED-HTTP-METHOD"; + + EXLOGV("[rpc] got %s request: %s\n", dbg_method, uri.c_str()); +#endif + ex_astr ret_buf; + bool b_is_html = false; + +// if (uri == "/") { +// ex_wstr page = L"Teleport助手\n\n
Teleport助手工作正常!
"; +// 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; +// } + + if (uri == "/") { + uri = "/status.html"; + b_is_html = true; + } + else if (uri == "/config") { + uri = "/index.html"; + b_is_html = true; + } + + ex_astr temp; + int offset = uri.find("/", 1); + if (offset > 0) { + temp = uri.substr(1, offset - 1); + + if (temp == "api") { + ex_astr method; + ex_astr json_param; + int rv = _this->_parse_request(hm, method, json_param); + if (0 != rv) { + EXLOGE("[ERROR] http-rpc got invalid request.\n"); + _this->_create_json_ret(ret_buf, rv); + } else { + _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.length(), &ret_buf[0]); + 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) { + unsigned long file_size = 0; + char* buf = nullptr; + size_t ret = 0; + + fseek(file, 0, SEEK_END); + file_size = ftell(file); + buf = new char[file_size]; + memset(buf, 0, file_size); + 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_html) { + ex_wstr page = L"404 Not Found

404 Not Found


Teleport Assistor configuration page not found.

"; + 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]); + nc->flags |= MG_F_SEND_AND_CLOSE; + return; + } + + } + break; + default: + break; + } +} + +int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_astr& func_args) { + if (!req) + return TPE_FAILED; + + bool is_get = true; + if (req->method.len == 3 && 0 == memcmp(req->method.p, "GET", req->method.len)) + is_get = true; + else if (req->method.len == 4 && 0 == memcmp(req->method.p, "POST", req->method.len)) + is_get = false; + else + return TPE_HTTP_METHOD; + + ex_astrs strs; + + size_t pos_start = 1; // 跳过第一个字节,一定是 '/' + + size_t i = 0; + for (i = pos_start; i < req->uri.len; ++i) { + if (req->uri.p[i] == '/') { + if (i - pos_start > 0) { + ex_astr tmp_uri; + tmp_uri.assign(req->uri.p + pos_start, i - pos_start); + strs.push_back(tmp_uri); + } + pos_start = i + 1; // 跳过当前找到的分隔符 + } + } + if (pos_start < req->uri.len) { + ex_astr tmp_uri; + tmp_uri.assign(req->uri.p + pos_start, req->uri.len - pos_start); + strs.push_back(tmp_uri); + } + + if (strs.empty() || strs[0] != "api") + return TPE_PARAM; + + if (is_get) { + if (2 == strs.size()) { + func_cmd = strs[1]; + } else if (3 == strs.size()) { + func_cmd = strs[1]; + func_args = strs[2]; + } else { + return TPE_PARAM; + } + } else { + if (2 == strs.size()) { + func_cmd = strs[1]; + } else { + return TPE_PARAM; + } + + if (req->body.len > 0) { + func_args.assign(req->body.p, req->body.len); + } + } + + if (func_args.length() > 0) { + // 将参数进行 url-decode 解码 + int len = func_args.length() * 2; + ex_chars sztmp; + sztmp.resize(len); + memset(&sztmp[0], 0, len); + if (-1 == ts_url_decode(func_args.c_str(), func_args.length(), &sztmp[0], len, 0)) + return TPE_HTTP_URL_ENCODE; + + func_args = &sztmp[0]; + } + + EXLOGV("[rpc] method=%s, json_param=%s\n", func_cmd.c_str(), func_args.c_str()); + + return TPE_OK; +} + +void TsHttpRpc::_process_js_request(const ex_astr& func_cmd, const ex_astr& func_args, ex_astr& buf) { + if (func_cmd == "get_version") { + _rpc_func_get_version(func_args, buf); + } else if (func_cmd == "run") { + _rpc_func_run_client(func_args, buf); + } else if (func_cmd == "rdp_play") { + _rpc_func_rdp_play(func_args, buf); + } else if (func_cmd == "get_config") { + _rpc_func_get_config(func_args, buf); + } else if (func_cmd == "set_config") { + _rpc_func_set_config(func_args, buf); + } else if (func_cmd == "file_action") { + _rpc_func_file_action(func_args, buf); + } else { + EXLOGE("[rpc] got unknown command: %s\n", func_cmd.c_str()); + _create_json_ret(buf, TPE_UNKNOWN_CMD); + } +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) { + // 返回: {"code":123} + + Json::Value jr_root; + jr_root["code"] = errcode; + + // buf = jr_writer.write(jr_root); + Json::StreamWriterBuilder jwb; + std::unique_ptr 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 jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jr_root, &os); + buf = os.str(); +} + +void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf) +{ + //处理urlprotocol调用访式 + // 将参数进行 url-decode 解码 + std::string func_args = args; + if (func_args.length() > 0) + { + int len = func_args.length() * 2; + ex_chars sztmp; + sztmp.resize(len); + memset(&sztmp[0], 0, len); + if (-1 == ts_url_decode(func_args.c_str(), func_args.length(), &sztmp[0], len, 0)) + return ; + + func_args = &sztmp[0]; + } + EXLOGD(("%s\n"), func_args.c_str()); + //处理传参过来的teleport://{}/,只保留参数部份 + std::string urlproto_appname = TP_URLPROTO_APP_NAME; + urlproto_appname += "://{"; + func_args.erase(0, urlproto_appname.length());//去除第一个URLPROTO_APP_NAME以及://字符 + int pos = func_args.length() - 1; + if (func_args.substr(pos, 1) == "/") + func_args.erase(pos - 1, 2);//去除最后一个}/字符 + else + func_args.erase(pos, 1); + + //由于命令行、ie浏览器参数传递时会把原来json结构中的"号去掉,需要重新格式化参数为json格式 + if (func_args.find("\"", 0) == std::string::npos) { + std::vector strv; + SplitString(func_args, strv, ","); + func_args = ""; + for (std::vector::size_type i = 0; i < strv.size(); i++) { + std::vector strv1; + SplitString(strv[i], strv1, ":"); + strv1[0] = "\"" + strv1[0] + "\""; + if (!isDegital(strv1[1]) && strtolower(strv1[1]) != "true" && strtolower(strv1[1]) != "false") + strv1[1] = "\"" + strv1[1] + "\""; + + strv[i] = strv1[0] + ":" + strv1[1]; + if (i == 0) + func_args = strv[i]; + else + func_args += "," + strv[i]; + } + } + func_args = "{" + func_args + "}"; + EXLOGD(("%s\n"), func_args.c_str()); + //调用TsHttpRpc类里的_rpc_func_run_client启动客户端 + _rpc_func_run_client(func_args, buf); +} + +void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) { + // 入参:{"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2} + // authmode: 1=password, 2=private-key + // protocol: 1=rdp, 2=ssh + // SSH返回: {"code":0, "data":{"sid":"0123abcde"}} + // RDP返回: {"code":0, "data":{"sid":"0123abcde0A"}} + + //Json::Reader jreader; + Json::Value jsRoot; + + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = func_args.c_str(); + ex_astr err; + + //if (!jreader.parse(func_args.c_str(), jsRoot)) { + if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) { + _create_json_ret(buf, TPE_JSON_FORMAT); + return; + } + if (!jsRoot.isObject()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + // 判断参数是否正确 + if (!jsRoot["teleport_ip"].isString() + || !jsRoot["teleport_port"].isNumeric() || !jsRoot["remote_host_ip"].isString() + || !jsRoot["session_id"].isString() || !jsRoot["protocol_type"].isNumeric() || !jsRoot["protocol_sub_type"].isNumeric() + || !jsRoot["protocol_flag"].isNumeric() + ) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + int pro_type = jsRoot["protocol_type"].asUInt(); + int pro_sub = jsRoot["protocol_sub_type"].asInt(); + ex_u32 protocol_flag = jsRoot["protocol_flag"].asUInt(); + + ex_astr teleport_ip = jsRoot["teleport_ip"].asCString(); + int teleport_port = jsRoot["teleport_port"].asUInt(); + + ex_astr remote_host_name = jsRoot["remote_host_name"].asCString(); + + ex_astr real_host_ip = jsRoot["remote_host_ip"].asCString(); + ex_astr sid = jsRoot["session_id"].asCString(); + + ex_wstr w_exe_path; + WCHAR w_szCommandLine[MAX_PATH] = { 0 }; + + + ex_wstr w_sid; + ex_astr2wstr(sid, w_sid); + ex_wstr w_teleport_ip; + ex_astr2wstr(teleport_ip, w_teleport_ip); + ex_wstr w_real_host_ip; + ex_astr2wstr(real_host_ip, w_real_host_ip); + ex_wstr w_remote_host_name; + ex_astr2wstr(remote_host_name, w_remote_host_name); + WCHAR w_port[32] = { 0 }; + swprintf_s(w_port, _T("%d"), teleport_port); + + ex_wstr tmp_rdp_file; // for .rdp file + + if (pro_type == TP_PROTOCOL_TYPE_RDP) { + //============================================== + // RDP + //============================================== + + 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; + bool rdp_console = false; + + if (!jsRoot["rdp_width"].isNull()) { + if (jsRoot["rdp_width"].isNumeric()) { + rdp_w = jsRoot["rdp_width"].asUInt(); + } else { + _create_json_ret(buf, TPE_PARAM); + return; + } + } + + if (!jsRoot["rdp_height"].isNull()) { + if (jsRoot["rdp_height"].isNumeric()) { + rdp_h = jsRoot["rdp_height"].asUInt(); + } else { + _create_json_ret(buf, TPE_PARAM); + return; + } + } + + if (!jsRoot["rdp_console"].isNull()) { + if (jsRoot["rdp_console"].isBool()) { + rdp_console = jsRoot["rdp_console"].asBool(); + } else { + _create_json_ret(buf, TPE_PARAM); + return; + } + } + + if (!flag_console) + rdp_console = false; + + + int split_pos = sid.length() - 2; + ex_astr real_sid = sid.substr(0, split_pos); + ex_astr str_pwd_len = sid.substr(split_pos, sid.length()); + int n_pwd_len = strtol(str_pwd_len.c_str(), nullptr, 16); + n_pwd_len -= real_sid.length(); + n_pwd_len -= 2; + char szPwd[256] = { 0 }; + for (int i = 0; i < n_pwd_len; i++) { + szPwd[i] = '*'; + } + + ex_astr2wstr(real_sid, w_sid); + + w_exe_path = _T("\""); + w_exe_path += g_cfg.rdp_app + _T("\" "); + + ex_wstr rdp_name = g_cfg.rdp_name; + if (rdp_name == L"mstsc") { + w_exe_path += g_cfg.rdp_cmdline; + + int width = 0; + int higth = 0; + int cx = 0; + int cy = 0; + + int display = 1; + int iWidth = GetSystemMetrics(SM_CXSCREEN); + int iHeight = GetSystemMetrics(SM_CYSCREEN); + + if (rdp_w == 0 || rdp_h == 0) { + //全屏 + width = iWidth; + higth = iHeight; + display = 2; + } else { + width = rdp_w; + higth = rdp_h; + display = 1; + } + + cx = (iWidth - width) / 2; + cy = (iHeight - higth) / 2; + if (cx < 0) { + cx = 0; + } + if (cy < 0) { + cy = 0; + } + + // int console_mode = 0; + // if (rdp_console) + // console_mode = 1; + + std::string psw51b; + if (!calc_psw51b(szPwd, psw51b)) { + EXLOGE("calc password failed.\n"); + _create_json_ret(buf, TPE_FAILED); + return; + } + + real_sid = "01" + real_sid; + + char sz_rdp_file_content[4096] = { 0 }; + sprintf_s(sz_rdp_file_content, 4096, rdp_content.c_str() + , (flag_console && rdp_console) ? 1 : 0 + , display, width, higth + , cx, cy, cx + width + 100, cy + higth + 100 + , teleport_ip.c_str(), teleport_port + , flag_clipboard ? 1 : 0 + , flag_disk ? "*" : "" + , real_sid.c_str() + , psw51b.c_str() + ); + + char sz_file_name[MAX_PATH] = { 0 }; + char temp_path[MAX_PATH] = { 0 }; + DWORD ret = GetTempPathA(MAX_PATH, temp_path); + if (ret <= 0) { + EXLOGE("fopen failed (%d).\n", GetLastError()); + _create_json_ret(buf, TPE_FAILED); + return; + } + + ex_astr temp_host_ip = real_host_ip; + ex_replace_all(temp_host_ip, ".", "-"); + + sprintf_s(sz_file_name, MAX_PATH, ("%s%s.rdp"), temp_path, temp_host_ip.c_str()); + + FILE* f = NULL; + if (fopen_s(&f, sz_file_name, "wt") != 0) { + EXLOGE("fopen failed (%d).\n", GetLastError()); + _create_json_ret(buf, TPE_OPENFILE); + return; + } + // Write a string into the file. + fwrite(sz_rdp_file_content, strlen(sz_rdp_file_content), 1, f); + fclose(f); + ex_astr2wstr(sz_file_name, tmp_rdp_file); + + // 变量替换 + ex_replace_all(w_exe_path, _T("{tmp_rdp_file}"), tmp_rdp_file); + } else if (g_cfg.rdp_name == L"freerdp") { + w_exe_path += L"{size} {console} {clipboard} {drives} "; + w_exe_path += g_cfg.rdp_cmdline; + + ex_wstr w_screen; + + if (rdp_w == 0 || rdp_h == 0) { + //全屏 + w_screen = _T("/f"); + } else { + char sz_size[64] = { 0 }; + ex_strformat(sz_size, 63, "/size:%dx%d", rdp_w, rdp_h); + ex_astr2wstr(sz_size, w_screen); + } + + // wchar_t* w_console = NULL; + // + // if (flag_console && rdp_console) + // { + // w_console = L"/admin"; + // } + // else + // { + // w_console = L""; + // } + + ex_wstr w_password; + ex_astr2wstr(szPwd, w_password); + w_exe_path += L" /p:"; + w_exe_path += w_password; + + w_sid = L"02" + w_sid; + + w_exe_path += L" /gdi:sw"; // 使用软件渲染,gdi:hw使用硬件加速,但是会出现很多黑块(录像回放时又是正常的!) + w_exe_path += L" -grab-keyboard"; // [new style] 防止启动FreeRDP后,失去本地键盘响应,必须得先最小化一下FreeRDP窗口(不过貌似不起作用) + + // 变量替换 + ex_replace_all(w_exe_path, _T("{size}"), w_screen); + + if (flag_console && rdp_console) + ex_replace_all(w_exe_path, _T("{console}"), L"/admin"); + else + ex_replace_all(w_exe_path, _T("{console}"), L""); + + //ex_replace_all(w_exe_path, _T("{clipboard}"), L"+clipboard"); + + if (flag_clipboard) + ex_replace_all(w_exe_path, _T("{clipboard}"), L"/clipboard"); + else + ex_replace_all(w_exe_path, _T("{clipboard}"), L"-clipboard"); + + if (flag_disk) + ex_replace_all(w_exe_path, _T("{drives}"), L"/drives"); + else + ex_replace_all(w_exe_path, _T("{drives}"), L"-drives"); + } else { + _create_json_ret(buf, TPE_FAILED); + return; + } + } else if (pro_type == TP_PROTOCOL_TYPE_SSH) { + //============================================== + // SSH + //============================================== + + if (pro_sub == TP_PROTOCOL_TYPE_SSH_SHELL) { + w_exe_path = _T("\""); + w_exe_path += g_cfg.ssh_app + _T("\" "); + w_exe_path += g_cfg.ssh_cmdline; + } else { + w_exe_path = _T("\""); + w_exe_path += g_cfg.scp_app + _T("\" "); + w_exe_path += g_cfg.scp_cmdline; + } + } else if (pro_type == TP_PROTOCOL_TYPE_TELNET) { + //============================================== + // TELNET + //============================================== + w_exe_path = _T("\""); + w_exe_path += g_cfg.telnet_app + _T("\" "); + w_exe_path += g_cfg.telnet_cmdline; + } + + ex_replace_all(w_exe_path, _T("{host_ip}"), w_teleport_ip.c_str()); + ex_replace_all(w_exe_path, _T("{host_port}"), w_port); + ex_replace_all(w_exe_path, _T("{user_name}"), w_sid.c_str()); + ex_replace_all(w_exe_path, _T("{host_name}"), w_remote_host_name.c_str()); + ex_replace_all(w_exe_path, _T("{real_ip}"), w_real_host_ip.c_str()); + ex_replace_all(w_exe_path, _T("{assist_tools_path}"), g_env.m_tools_path.c_str()); + + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + Json::Value root_ret; + ex_astr utf8_path; + ex_wstr2astr(w_exe_path, utf8_path, EX_CODEPAGE_UTF8); + root_ret["path"] = utf8_path; + + if (!CreateProcess(NULL, (wchar_t *)w_exe_path.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + EXLOGE(_T("CreateProcess() failed. Error=0x%08X.\n %s\n"), GetLastError(), w_exe_path.c_str()); + root_ret["code"] = TPE_START_CLIENT; + _create_json_ret(buf, root_ret); + return; + } + + 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) { + //Json::Reader jreader; + Json::Value jsRoot; + + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = func_args.c_str(); + ex_astr err; + + //if (!jreader.parse(func_args.c_str(), jsRoot)) { + if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) { + _create_json_ret(buf, TPE_JSON_FORMAT); + return; + } + + // 判断参数是否正确 + if (!jsRoot["rid"].isInt() + || !jsRoot["web"].isString() + || !jsRoot["sid"].isString() + ) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + int rid = jsRoot["rid"].asInt(); + ex_astr a_url_base = jsRoot["web"].asCString(); + ex_astr a_sid = jsRoot["sid"].asCString(); + + char cmd_args[1024] = { 0 }; + ex_strformat(cmd_args, 1023, "%s/%d", a_sid.c_str(), rid); + + ex_wstr w_url_base; + ex_astr2wstr(a_url_base, w_url_base); + ex_wstr w_cmd_args; + ex_astr2wstr(cmd_args, w_cmd_args); + + ex_wstr w_exe_path; + w_exe_path = _T("\""); + w_exe_path += g_env.m_exec_path + _T("\\tp-player.exe\""); + w_exe_path += _T(" \""); + w_exe_path += w_url_base; + w_exe_path += _T("/"); + w_exe_path += w_cmd_args; + + Json::Value root_ret; + ex_astr utf8_path; + ex_wstr2astr(w_exe_path, utf8_path, EX_CODEPAGE_UTF8); + root_ret["cmdline"] = utf8_path; + + EXLOGD(w_exe_path.c_str()); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + if (!CreateProcess(NULL, (wchar_t *)w_exe_path.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + EXLOGE(_T("CreateProcess() failed. Error=0x%08X.\n %s\n"), GetLastError(), w_exe_path.c_str()); + root_ret["code"] = TPE_START_CLIENT; + _create_json_ret(buf, root_ret); + return; + } + + root_ret["code"] = TPE_OK; + _create_json_ret(buf, root_ret); + return; +} + +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; + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = func_args.c_str(); + ex_astr err; + + //if (!jreader.parse(func_args.c_str(), jsRoot)) { + if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) { + _create_json_ret(buf, TPE_JSON_FORMAT); + return; + } + + if (!g_cfg.save(func_args)) + _create_json_ret(buf, TPE_FAILED); + else + _create_json_ret(buf, TPE_OK); +} + +void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) { + + //Json::Reader jreader; + Json::Value jsRoot; + + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = func_args.c_str(); + ex_astr err; + + //if (!jreader.parse(func_args.c_str(), jsRoot)) { + if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) { + _create_json_ret(buf, TPE_JSON_FORMAT); + return; + } + // 判断参数是否正确 + if (!jsRoot["action"].isNumeric()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + int action = jsRoot["action"].asUInt(); + + HWND hParent = GetForegroundWindow(); + if (NULL == hParent) + hParent = g_hDlgMain; + + BOOL ret = FALSE; + wchar_t wszReturnPath[MAX_PATH] = _T(""); + + if (action == 1 || action == 2) { + OPENFILENAME ofn; + ex_wstr wsDefaultName; + ex_wstr wsDefaultPath; + StringCchCopy(wszReturnPath, MAX_PATH, wsDefaultName.c_str()); + + ZeroMemory(&ofn, sizeof(ofn)); + + ofn.lStructSize = sizeof(ofn); + ofn.lpstrTitle = _T("选择文件"); + ofn.hwndOwner = hParent; + ofn.lpstrFilter = _T("可执行程序 (*.exe)\0*.exe\0"); + ofn.lpstrFile = wszReturnPath; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrInitialDir = wsDefaultPath.c_str(); + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST; + + if (action == 1) { + ofn.Flags |= OFN_FILEMUSTEXIST; + ret = GetOpenFileName(&ofn); + } else { + ofn.Flags |= OFN_OVERWRITEPROMPT; + ret = GetSaveFileName(&ofn); + } + } else if (action == 3) { + BROWSEINFO bi; + ZeroMemory(&bi, sizeof(BROWSEINFO)); + bi.hwndOwner = NULL; + bi.pidlRoot = NULL; + bi.pszDisplayName = wszReturnPath; //此参数如为NULL则不能显示对话框 + bi.lpszTitle = _T("选择目录"); + bi.ulFlags = BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.iImage = 0; //初始化入口参数bi结束 + LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//调用显示选择对话框 + if (pIDList) { + ret = true; + SHGetPathFromIDList(pIDList, wszReturnPath); + } else { + ret = false; + } + } else if (action == 4) { + ex_wstr wsDefaultName; + ex_wstr wsDefaultPath; + + if (wsDefaultPath.length() == 0) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_wstr::size_type pos = 0; + + while (ex_wstr::npos != (pos = wsDefaultPath.find(L"/", pos))) { + wsDefaultPath.replace(pos, 1, L"\\"); + pos += 1; + } + + ex_wstr wArg = L"/select, \""; + wArg += wsDefaultPath; + wArg += L"\""; + if ((int)ShellExecute(hParent, _T("open"), _T("explorer"), wArg.c_str(), NULL, SW_SHOW) > 32) + ret = true; + else + ret = false; + } + + if (ret) { + if (action == 1 || action == 2 || action == 3) { + ex_astr utf8_path; + ex_wstr2astr(wszReturnPath, utf8_path, EX_CODEPAGE_UTF8); + Json::Value root; + root["code"] = TPE_OK; + root["path"] = utf8_path; + _create_json_ret(buf, root); + + return; + } else { + _create_json_ret(buf, TPE_OK); + return; + } + } else { + _create_json_ret(buf, TPE_DATA); + return; + } +} + +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; + ex_wstr2astr(w_version, version, EX_CODEPAGE_UTF8); + root_ret["version"] = version; + root_ret["code"] = TPE_OK; + _create_json_ret(buf, root_ret); + return; +} diff --git a/client/tp_assist_win/ts_ver.h b/client/tp_assist_win/ts_ver.h index 6aa10dc..9c55de2 100644 --- a/client/tp_assist_win/ts_ver.h +++ b/client/tp_assist_win/ts_ver.h @@ -1,6 +1,6 @@ #ifndef __TS_ASSIST_VER_H__ #define __TS_ASSIST_VER_H__ -#define TP_ASSIST_VER L"3.3.1" +#define TP_ASSIST_VER L"3.5.1" #endif // __TS_ASSIST_VER_H__ diff --git a/common/libex/include/ex.h b/common/libex/include/ex.h index 68166fb..2c668d3 100644 --- a/common/libex/include/ex.h +++ b/common/libex/include/ex.h @@ -1,4 +1,4 @@ -#ifndef __LIB_EX_H__ +#ifndef __LIB_EX_H__ #define __LIB_EX_H__ #ifdef EX_HAVE_CONFIG diff --git a/common/libex/include/ex/ex_const.h b/common/libex/include/ex/ex_const.h index 30604a7..699bf10 100644 --- a/common/libex/include/ex/ex_const.h +++ b/common/libex/include/ex/ex_const.h @@ -1,4 +1,4 @@ -#ifndef __LIB_EX_CONST_H__ +#ifndef __LIB_EX_CONST_H__ #define __LIB_EX_CONST_H__ #include "ex_platform.h" @@ -43,8 +43,8 @@ // error code. //==================================================== #define EXRV_OK 0 -#define EXRV_SYS_ERR 1 // ϵͳ󣬿ʹGetLastErrorerrnoȡֵ -#define EXRV_FAILED 2 // ʧ +#define EXRV_SYS_ERR 1 // 系统错误,可以使用GetLastError或者errno来获取具体错误值 +#define EXRV_FAILED 2 // 操作失败 //#define EXRV_CANNOT_FOUND 9 #define EXRV_CANNOT_CREATE 10 diff --git a/common/libex/include/ex/ex_ini.h b/common/libex/include/ex/ex_ini.h index 38749f5..681f05f 100644 --- a/common/libex/include/ex/ex_ini.h +++ b/common/libex/include/ex/ex_ini.h @@ -1,4 +1,4 @@ -#ifndef __EX_INI_H__ +#ifndef __EX_INI_H__ #define __EX_INI_H__ /* diff --git a/common/libex/include/ex/ex_log.h b/common/libex/include/ex/ex_log.h index 29a1b41..1cd89ab 100644 --- a/common/libex/include/ex/ex_log.h +++ b/common/libex/include/ex/ex_log.h @@ -1,4 +1,4 @@ -#ifndef __EX_LOG_H__ +#ifndef __EX_LOG_H__ #define __EX_LOG_H__ #include "ex_types.h" @@ -27,7 +27,7 @@ public: protected: bool _open_file(); - bool _rotate_file(void); // ־ļݣȻ¿һ־ļ + bool _rotate_file(void); // 将现有日志文件改名备份,然后新开一个日志文件 public: ExThreadLock lock; @@ -63,7 +63,7 @@ void EXLOG_USE_LOGGER(ExLogger* logger); void EXLOG_LEVEL(int min_level); void EXLOG_DEBUG(bool debug_mode); -// 趨־ļ·δָ·ΪִгĿ¼µlogĿ¼ +// 设定日志文件名及路径,如未指定路径,则为可执行程序所在目录下的log目录。 void EXLOG_FILE(const wchar_t* log_file, const wchar_t* log_path = NULL, ex_u32 max_filesize = EX_LOG_FILE_MAX_SIZE, ex_u8 max_filecount = EX_LOG_FILE_MAX_COUNT); void EXLOG_CONSOLE(bool output_to_console); diff --git a/common/libex/include/ex/ex_path.h b/common/libex/include/ex/ex_path.h index 2f1fa19..20ac79e 100644 --- a/common/libex/include/ex/ex_path.h +++ b/common/libex/include/ex/ex_path.h @@ -1,4 +1,4 @@ -#ifndef __LIB_EX_PATH_H__ +#ifndef __LIB_EX_PATH_H__ #define __LIB_EX_PATH_H__ #include "ex_platform.h" @@ -39,7 +39,7 @@ bool ex_path_join(ex_wstr& inout_path, EX_BOOL auto_abspath, ...); bool ex_abspath_to(const ex_wstr& base_abs_path, const ex_wstr& relate_path, ex_wstr& out_path); bool ex_mkdirs(const ex_wstr& in_path); -// ȡļеչ֣.abc.py py +// 获取文件名中的扩展名部分(不包括.,例如abc.py,返回 py) bool ex_path_ext_name(const ex_wstr& in_filename, ex_wstr& out_ext); #endif diff --git a/common/libex/include/ex/ex_platform.h b/common/libex/include/ex/ex_platform.h index 3000efe..6a23715 100644 --- a/common/libex/include/ex/ex_platform.h +++ b/common/libex/include/ex/ex_platform.h @@ -1,4 +1,4 @@ -#ifndef __LIB_EX_PLATFORM_H__ +#ifndef __LIB_EX_PLATFORM_H__ #define __LIB_EX_PLATFORM_H__ #if defined(_WIN32) || defined(WIN32) diff --git a/common/libex/include/ex/ex_str.h b/common/libex/include/ex/ex_str.h index b5c4a43..330d8e8 100644 --- a/common/libex/include/ex/ex_str.h +++ b/common/libex/include/ex/ex_str.h @@ -1,86 +1,89 @@ -#ifndef __LIB_EX_STR_H__ -#define __LIB_EX_STR_H__ - -#include "ex_types.h" - -#define EX_CODEPAGE_ACP 0 -#define EX_CODEPAGE_UTF8 1 -#ifdef EX_OS_WIN32 -# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_ACP -#else -# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_UTF8 -#endif - -#define EX_RSC_BEGIN 0x01 -#define EX_RSC_END 0x02 -#define EX_RSC_ALL EX_RSC_BEGIN | EX_RSC_END - -//================================================= -// C Interface -//================================================= - -// copy a string from `source` to `target`. -// `size` is size of target buffer. -// if buffer is to small, NULL will return, but `size-1` characters have been copied. -char* ex_strcpy(char* target, size_t size, const char* source); -wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source); - - -// dupilicate a string. -// must use ex_free() to release the returned value. -char* ex_strdup(const char* src); -wchar_t* ex_wcsdup(const wchar_t* src); - -// convert between mutli-bytes and wide char string. -// must use ex_free() to release the returned value. -wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page); -char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page); - -// convert char** argv to wchar_t** argv. -// must use ex_free_argv() to release the returned value. -wchar_t** ex_make_wargv(int argc, char** argv); -void ex_free_wargv(int argc, wchar_t** argv); - -EX_BOOL ex_str_only_white_space(const wchar_t* src); -EX_BOOL ex_wcs_only_white_space(const char* src); - - -int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...); -int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...); - -//================================================= -// C++ Interface -//================================================= -#ifdef __cplusplus - -#include -#include - -typedef std::string ex_astr; -typedef std::wstring ex_wstr; - -typedef std::vector ex_astrs; -typedef std::vector ex_wstrs; -typedef std::vector ex_str_utf16le; - -bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT); -bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT); -bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT); -bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT); - -bool ex_only_white_space(const ex_astr& str_check); -bool ex_only_white_space(const ex_wstr& str_check); - -void ex_remove_white_space(ex_astr& str_fix, int ulFlag = EX_RSC_ALL); -void ex_remove_white_space(ex_wstr& str_fix, int ulFlag = EX_RSC_ALL); - -ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value); -ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value); - -// UTF8ַתΪUTF16-LEַ\0 -bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to); - -#endif - - -#endif // __LIB_EX_STR_H__ +#ifndef __LIB_EX_STR_H__ +#define __LIB_EX_STR_H__ + +#include "ex_types.h" + +#define EX_CODEPAGE_ACP 0 +#define EX_CODEPAGE_UTF8 1 +#ifdef EX_OS_WIN32 +# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_ACP +#else +# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_UTF8 +#endif + +#define EX_RSC_BEGIN 0x01 +#define EX_RSC_END 0x02 +#define EX_RSC_ALL EX_RSC_BEGIN | EX_RSC_END + +//================================================= +// C Interface +//================================================= + +// copy a string from `source` to `target`. +// `size` is size of target buffer. +// if buffer is to small, NULL will return, but `size-1` characters have been copied. +char* ex_strcpy(char* target, size_t size, const char* source); +wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source); + + +// dupilicate a string. +// must use ex_free() to release the returned value. +char* ex_strdup(const char* src); +wchar_t* ex_wcsdup(const wchar_t* src); + +// convert between mutli-bytes and wide char string. +// must use ex_free() to release the returned value. +wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page); +char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page); + +// convert char** argv to wchar_t** argv. +// must use ex_free_argv() to release the returned value. +wchar_t** ex_make_wargv(int argc, char** argv); +void ex_free_wargv(int argc, wchar_t** argv); + +EX_BOOL ex_str_only_white_space(const wchar_t* src); +EX_BOOL ex_wcs_only_white_space(const char* src); + + +int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...); +int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...); + +//================================================= +// C++ Interface +//================================================= +#ifdef __cplusplus + +#include +#include +#include + +typedef std::string ex_astr; +typedef std::wstring ex_wstr; +typedef std::ostringstream ex_aoss; +typedef std::wostringstream ex_woss; + +typedef std::vector ex_astrs; +typedef std::vector ex_wstrs; +typedef std::vector ex_str_utf16le; + +bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT); +bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT); +bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT); +bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT); + +bool ex_only_white_space(const ex_astr& str_check); +bool ex_only_white_space(const ex_wstr& str_check); + +void ex_remove_white_space(ex_astr& str_fix, int ulFlag = EX_RSC_ALL); +void ex_remove_white_space(ex_wstr& str_fix, int ulFlag = EX_RSC_ALL); + +ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value); +ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value); + +// 将UTF8字符串转换为UTF16-LE字符串(输出结果包含\0结束符) +bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to); + +#endif + + +#endif // __LIB_EX_STR_H__ diff --git a/common/libex/include/ex/ex_thread.h b/common/libex/include/ex/ex_thread.h index f3d4170..3948749 100644 --- a/common/libex/include/ex/ex_thread.h +++ b/common/libex/include/ex/ex_thread.h @@ -1,4 +1,4 @@ -#ifndef __EX_THREAD_H__ +#ifndef __EX_THREAD_H__ #define __EX_THREAD_H__ #include "ex_str.h" @@ -23,11 +23,11 @@ public: bool is_running(void) { return m_is_running; } - // ִ̣߳б˵run() + // 创建并启动线程(执行被重载了的run()函数) bool start(void); - // ̣߳ȴwait_timeout_ms룬wait_timeout_msΪ0޵ȴ + // 结束线程(等待wait_timeout_ms毫秒,如果wait_timeout_ms为0,则无限等待) bool stop(void); - // ֱӽ̣߳ǿɱʹã + // 直接结束线程(强杀,不建议使用) bool terminate(void); protected: @@ -52,7 +52,7 @@ protected: }; -// ߳ʹã +// 线程锁(进程内使用) class ExThreadLock { public: @@ -70,7 +70,7 @@ private: #endif }; -// ߳ +// 线程锁辅助类 class ExThreadSmartLock { public: @@ -109,12 +109,12 @@ private: }; -// ԭӲ +// 原子操作 int ex_atomic_add(volatile int* pt, int t); int ex_atomic_inc(volatile int* pt); int ex_atomic_dec(volatile int* pt); -// ߳ز +// 线程相关操作 ex_u64 ex_get_thread_id(void); #endif // __EX_THREAD_H__ diff --git a/common/libex/include/ex/ex_types.h b/common/libex/include/ex/ex_types.h index 0187b0a..3ad1a01 100644 --- a/common/libex/include/ex/ex_types.h +++ b/common/libex/include/ex/ex_types.h @@ -1,44 +1,44 @@ -#ifndef __LIB_EX_TYPE_H__ -#define __LIB_EX_TYPE_H__ - -#include "ex_platform.h" - -#include - -typedef signed char ex_i8; -typedef signed short ex_i16; - -typedef unsigned char ex_u8; -typedef unsigned short ex_u16; -typedef unsigned int ex_u32; -typedef unsigned long ex_ulong; - -#if defined(EX_OS_WIN32) -typedef unsigned __int64 ex_u64; -typedef signed __int64 ex_i64; -typedef wchar_t ex_utf16; -#else -typedef unsigned long long ex_u64; -typedef signed long long ex_i64; -typedef ex_i16 ex_utf16; -#endif - -typedef int EX_BOOL; -#define EX_TRUE 1 -#define EX_FALSE 0 - - -typedef std::vector ex_bin; -typedef std::vector ex_chars; - -typedef ex_u32 ex_rv; - - -#if defined(EX_OS_WIN32) -# define EX_DYLIB_HANDLE HINSTANCE -#else -# define EX_DYLIB_HANDLE void* -#endif - - -#endif // __LIB_EX_TYPE_H__ +#ifndef __LIB_EX_TYPE_H__ +#define __LIB_EX_TYPE_H__ + +#include "ex_platform.h" + +#include + +typedef signed char ex_i8; +typedef signed short ex_i16; + +typedef unsigned char ex_u8; +typedef unsigned short ex_u16; +typedef unsigned int ex_u32; +typedef unsigned long ex_ulong; + +#if defined(EX_OS_WIN32) +typedef unsigned __int64 ex_u64; +typedef signed __int64 ex_i64; +typedef wchar_t ex_utf16; +#else +typedef unsigned long long ex_u64; +typedef signed long long ex_i64; +typedef ex_i16 ex_utf16; +#endif + +typedef int EX_BOOL; +#define EX_TRUE 1 +#define EX_FALSE 0 + + +typedef std::vector ex_bin; +typedef std::vector ex_chars; + +typedef ex_u32 ex_rv; + + +#if defined(EX_OS_WIN32) +# define EX_DYLIB_HANDLE HINSTANCE +#else +# define EX_DYLIB_HANDLE void* +#endif + + +#endif // __LIB_EX_TYPE_H__ diff --git a/common/libex/include/ex/ex_util.h b/common/libex/include/ex/ex_util.h index 06b78ec..17b2556 100644 --- a/common/libex/include/ex/ex_util.h +++ b/common/libex/include/ex/ex_util.h @@ -1,55 +1,55 @@ -#ifndef __LIB_EX_UTIL_H__ -#define __LIB_EX_UTIL_H__ - -#include "ex_types.h" -#include "ex_str.h" - -#ifdef EX_OS_WIN32 -# include -//# include -//# include -// #include -#pragma comment(lib, "ws2_32.lib") -#else -// #include -# include -# include -#endif - -EX_BOOL ex_initialize(const char* lc_ctype); - -void ex_free(void* buffer); - -// haystackΪhaystacklenֽڣвneedleΪneedlelenʼַNULLʾûҵ -const ex_u8* ex_memmem(const ex_u8* haystack, size_t haystacklen, const ex_u8* needle, size_t needlelen); -void ex_mem_reverse(ex_u8* p, size_t l); - -void ex_printf(const char* fmt, ...); -void ex_wprintf(const wchar_t* fmt, ...); - -ex_u64 ex_get_tick_count(void); -void ex_sleep_ms(int ms); - -EX_BOOL ex_localtime_now(int* t, struct tm* dt); - - -FILE* ex_fopen(const ex_wstr& filename, const wchar_t* mode); -FILE* ex_fopen(const ex_astr& filename, const char* mode); - -// open a text file and read all content. -bool ex_read_text_file(const ex_wstr& file_name, ex_astr& file_content); -// open a file and write content. -bool ex_write_text_file(const ex_wstr& file_name, const ex_astr& file_content); - -EX_DYLIB_HANDLE ex_dlopen(const wchar_t* dylib_path); -void ex_dlclose(EX_DYLIB_HANDLE dylib); - - -// inet... -int ex_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); - -#define EX_IPV4_NAME_LEN 16 -#define EX_IPV6_NAME_LEN 46 -const char* ex_inet_ntop(int af, const void *src, char *dst, size_t size); - -#endif // __LIB_EX_UTIL_H__ +#ifndef __LIB_EX_UTIL_H__ +#define __LIB_EX_UTIL_H__ + +#include "ex_types.h" +#include "ex_str.h" + +#ifdef EX_OS_WIN32 +# include +//# include +//# include +// #include +#pragma comment(lib, "ws2_32.lib") +#else +// #include +# include +# include +#endif + +EX_BOOL ex_initialize(const char* lc_ctype); + +void ex_free(void* buffer); + +// 在haystack(长度为haystacklen字节)中查找needle(长度为needlelen)的起始地址,返回NULL表示没有找到 +const ex_u8* ex_memmem(const ex_u8* haystack, size_t haystacklen, const ex_u8* needle, size_t needlelen); +void ex_mem_reverse(ex_u8* p, size_t l); + +void ex_printf(const char* fmt, ...); +void ex_wprintf(const wchar_t* fmt, ...); + +ex_u64 ex_get_tick_count(void); +void ex_sleep_ms(int ms); + +EX_BOOL ex_localtime_now(int* t, struct tm* dt); + + +FILE* ex_fopen(const ex_wstr& filename, const wchar_t* mode); +FILE* ex_fopen(const ex_astr& filename, const char* mode); + +// open a text file and read all content. +bool ex_read_text_file(const ex_wstr& file_name, ex_astr& file_content); +// open a file and write content. +bool ex_write_text_file(const ex_wstr& file_name, const ex_astr& file_content); + +EX_DYLIB_HANDLE ex_dlopen(const wchar_t* dylib_path); +void ex_dlclose(EX_DYLIB_HANDLE dylib); + + +// inet... +int ex_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); + +#define EX_IPV4_NAME_LEN 16 +#define EX_IPV6_NAME_LEN 46 +const char* ex_inet_ntop(int af, const void *src, char *dst, size_t size); + +#endif // __LIB_EX_UTIL_H__ diff --git a/common/libex/include/ex/ex_winsrv.h b/common/libex/include/ex/ex_winsrv.h index 91938ec..15ac6ee 100644 --- a/common/libex/include/ex/ex_winsrv.h +++ b/common/libex/include/ex/ex_winsrv.h @@ -1,4 +1,4 @@ -#ifndef __EX_WINSRV_H__ +#ifndef __EX_WINSRV_H__ #define __EX_WINSRV_H__ #include "ex_str.h" diff --git a/common/libex/src/ex_ini.cpp b/common/libex/src/ex_ini.cpp index 669b890..e471752 100644 --- a/common/libex/src/ex_ini.cpp +++ b/common/libex/src/ex_ini.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -241,7 +241,7 @@ bool ExIniFile::LoadFromFile(const ex_wstr& strFileName, bool bClearOld) { pOffset += 3; } - // ļʹUTF8 + // 配置文件均使用UTF8编码 ex_wstr fileData; if (!ex_astr2wstr(pOffset, fileData, EX_CODEPAGE_UTF8)) return false; @@ -357,7 +357,7 @@ void ExIniFile::Save(int codepage/* = EX_CODEPAGE_UTF8*/) return; } - // вκСڵֵԣȱ֮ + // 如果有不属于任何小节的值对,先保存之 if (m_dumy_sec.Count() > 0) m_dumy_sec.Save(file, codepage); @@ -415,16 +415,16 @@ ExIniSection* ExIniFile::GetSection(const ex_wstr& strName, bool bCreateIfNotExi } // static function. -// һУֵΪ [/ֵ/ע/ʲôҲ/] -// => strKey = [section_name] -// ֵ => strKey = strValue +// 解析一行,返回值为 [节名/值对/注释/什么也不是/出错了] +// 节名 => strKey = [section_name] +// 值对 => strKey = strValue ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& strKey, ex_wstr& strValue) { - // ȥ׵Ŀո TAB + // 首先去掉行首的空格或者 TAB 控制 ex_wstr strLine(strOrigLine); ex_remove_white_space(strLine, EX_RSC_BEGIN); - // жǷΪע͡ .ini ļ ֺ';'/'#' Ϊעеĵһַ + // 判断是否为注释。 .ini 文件以 分号';'/'#' 作为注释行的第一个字符 if (';' == strLine[0] || '#' == strLine[0]) { return PARSE_COMMENT; @@ -432,7 +432,7 @@ ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& s if ('[' == strLine[0]) { - // һ(section) + // 这是一个节(section) ex_wstr::size_type startPos = strLine.find('['); ex_wstr::size_type endPos = strLine.rfind(']'); strLine.erase(endPos); @@ -443,23 +443,23 @@ ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& s } else { - // ܷҵȺ(=) key=value б𷽷 + // 看看能否找到等号(=),这是 key=value 的判别方法 ex_wstr::size_type pos = strLine.find('='); if (ex_wstr::npos == pos) { - //return PARSE_OTHER; // ûеȺ + //return PARSE_OTHER; // 没有等号 ex_remove_white_space(strLine); strKey = strLine; strValue.clear(); return PARSE_KEYVALUE; } - // ȺǰȺźķָ + // 将等号前面的与等号后面的分割 strKey.assign(strLine, 0, pos); strValue.assign(strLine, pos + 1, strLine.length() - pos); ex_remove_white_space(strKey); - // ȺźӦԭⲻӦƳհַ + // 等号后面的应该原封不动,不应该移除空白字符 ex_remove_white_space(strValue, EX_RSC_BEGIN); return PARSE_KEYVALUE; @@ -489,7 +489,7 @@ bool ExIniFile::_ProcessLine(const ex_wstr strLine, ExIniSection** pCurSection) break; case PARSE_SECTION: { - // һ + // 创建一个节 ExIniSection* pSection = GetSection(strKey, true); if (NULL == pSection) { @@ -508,7 +508,7 @@ bool ExIniFile::_ProcessLine(const ex_wstr strLine, ExIniSection** pCurSection) *pCurSection = &m_dumy_sec; } - // һֵ + // 创建一个值对 if (!(*pCurSection)->SetValue(strKey, strValue, true)) { bError = true; diff --git a/common/libex/src/ex_log.cpp b/common/libex/src/ex_log.cpp index 7a2f9fa..4810a37 100644 --- a/common/libex/src/ex_log.cpp +++ b/common/libex/src/ex_log.cpp @@ -1,517 +1,517 @@ -#include -#include -//#include -//#include -//#include -//#include - -#ifdef EX_OS_WIN32 -# include -# include -# include -#else -//# include -//# include -#endif - -#define EX_LOG_CONTENT_MAX_LEN 2048 - -//typedef std::deque log_file_deque; - -static ExLogger* g_exlog = NULL; - -void EXLOG_USE_LOGGER(ExLogger* logger) -{ - g_exlog = logger; -} - -void EXLOG_LEVEL(int min_level) -{ - if(NULL != g_exlog) - g_exlog->min_level = min_level; -} - -void EXLOG_DEBUG(bool debug_mode) -{ - if (NULL != g_exlog) - g_exlog->debug_mode = debug_mode; -} - -void EXLOG_CONSOLE(bool output_to_console) -{ - if(NULL != g_exlog) - g_exlog->to_console = output_to_console; -} - -void EXLOG_FILE(const wchar_t* log_file, const wchar_t* log_path /*= NULL*/, ex_u32 max_filesize /*= EX_LOG_FILE_MAX_SIZE*/, ex_u8 max_filecount /*= EX_LOG_FILE_MAX_COUNT*/) -{ - if(NULL == g_exlog) - return; - - ex_wstr _path; - if (NULL == log_path) - { - ex_exec_file(_path); - ex_dirname(_path); - ex_path_join(_path, false, L"log", NULL); - } - else - { - _path = log_path; - } - - g_exlog->set_log_file(_path, log_file, max_filesize, max_filecount); -} - -ExLogger::ExLogger() -{ -#ifdef EX_OS_WIN32 - console_handle = GetStdHandle(STD_OUTPUT_HANDLE); -#endif - - min_level = EX_LOG_LEVEL_INFO; - debug_mode = false; - to_console = true; - - m_file = NULL; - m_filesize = 0; -} - -ExLogger::~ExLogger() -{ - if (NULL != m_file) - { -#ifdef EX_OS_WIN32 - CloseHandle(m_file); -#else - fclose(m_file); -#endif - m_file = NULL; - } -} - -void ExLogger::log_a(int level, const char* fmt, va_list valist) -{ - if (NULL == fmt) - return; - - if (0 == strlen(fmt)) - return; - - char szTmp[4096] = { 0 }; - size_t offset = 0; - - if (level == EX_LOG_LEVEL_ERROR) - { - szTmp[0] = '['; - szTmp[1] = 'E'; - szTmp[2] = ']'; - szTmp[3] = ' '; - offset = 4; - } - -#ifdef EX_OS_WIN32 - vsnprintf_s(szTmp+offset, 4096-offset, 4095-offset, fmt, valist); - if(to_console) - { - if (NULL != console_handle) - { - printf_s("%s", szTmp); - fflush(stdout); - } - else - { - if(debug_mode) - OutputDebugStringA(szTmp); - } - } -#else - vsnprintf(szTmp+offset, 4095-offset, fmt, valist); - if(to_console) - { - // On linux, the stdout only output the first time output format (char or wchar_t). - // e.g.: first time you use printf(), then after that, every wprintf() not work, and vice versa. - // so we always use wprintf() to fix that. - - ex_astr tmp(szTmp); - ex_wstr _tmp; - ex_astr2wstr(tmp, _tmp); - wprintf(L"%ls", _tmp.c_str()); - fflush(stdout); - -// printf("%s", szTmp); -// fflush(stdout); - } -#endif - - write_a(szTmp); -} - -void ExLogger::log_w(int level, const wchar_t* fmt, va_list valist) -{ - if (NULL == fmt || 0 == wcslen(fmt)) - return; - - wchar_t szTmp[4096] = { 0 }; - size_t offset = 0; - - if (level == EX_LOG_LEVEL_ERROR) - { - szTmp[0] = L'['; - szTmp[1] = L'E'; - szTmp[2] = L']'; - szTmp[3] = L' '; - offset = 4; - } - -#ifdef EX_OS_WIN32 - _vsnwprintf_s(szTmp+offset, 4096-offset, 4095-offset, fmt, valist); - if(to_console) - { - if (NULL != console_handle) - { - wprintf_s(_T("%s"), szTmp); - fflush(stdout); - } - else - { - if(debug_mode) - OutputDebugStringW(szTmp); - } - } -#else - vswprintf(szTmp+offset, 4095-offset, fmt, valist); - if(to_console) - { - wprintf(L"%ls", szTmp); - fflush(stdout); - } -#endif - - write_w(szTmp); -} - -#define EX_PRINTF_XA(fn, level) \ -void fn(const char* fmt, ...) \ -{ \ - if(NULL == g_exlog) \ - return; \ - if (g_exlog->min_level > level) \ - return; \ - ExThreadSmartLock locker(g_exlog->lock); \ - va_list valist; \ - va_start(valist, fmt); \ - g_exlog->log_a(level, fmt, valist); \ - va_end(valist); \ -} - -#define EX_PRINTF_XW(fn, level) \ -void fn(const wchar_t* fmt, ...) \ -{ \ - if(NULL == g_exlog) \ - return; \ - if (g_exlog->min_level > level) \ - return; \ - ExThreadSmartLock locker(g_exlog->lock); \ - va_list valist; \ - va_start(valist, fmt); \ - g_exlog->log_w(level, fmt, valist); \ - va_end(valist); \ -} - -EX_PRINTF_XA(ex_printf_d, EX_LOG_LEVEL_DEBUG) -EX_PRINTF_XA(ex_printf_v, EX_LOG_LEVEL_VERBOSE) -EX_PRINTF_XA(ex_printf_i, EX_LOG_LEVEL_INFO) -EX_PRINTF_XA(ex_printf_w, EX_LOG_LEVEL_WARN) -EX_PRINTF_XA(ex_printf_e, EX_LOG_LEVEL_ERROR) - -EX_PRINTF_XW(ex_printf_d, EX_LOG_LEVEL_DEBUG) -EX_PRINTF_XW(ex_printf_v, EX_LOG_LEVEL_VERBOSE) -EX_PRINTF_XW(ex_printf_i, EX_LOG_LEVEL_INFO) -EX_PRINTF_XW(ex_printf_w, EX_LOG_LEVEL_WARN) -EX_PRINTF_XW(ex_printf_e, EX_LOG_LEVEL_ERROR) - - -#ifdef EX_OS_WIN32 -void ex_printf_e_lasterror(const char* fmt, ...) -{ - ExThreadSmartLock locker(g_exlog->lock); - - va_list valist; - va_start(valist, fmt); - g_exlog->log_a(EX_LOG_LEVEL_ERROR, fmt, valist); - va_end(valist); - - //========================================= - - LPVOID lpMsgBuf; - DWORD dw = GetLastError(); - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&lpMsgBuf, 0, NULL); - - ex_printf_e(" - WinErr(%d): %s\n", dw, (LPSTR)lpMsgBuf); - LocalFree(lpMsgBuf); -} - -void ex_printf_e_lasterror(const wchar_t* fmt, ...) -{ - ExThreadSmartLock locker(g_exlog->lock); - - va_list valist; - va_start(valist, fmt); - g_exlog->log_w(EX_LOG_LEVEL_ERROR, fmt, valist); - va_end(valist); - - //========================================= - - LPVOID lpMsgBuf; - DWORD dw = GetLastError(); - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&lpMsgBuf, 0, NULL); - - ex_printf_e(" - WinErr(%d): %s\n", dw, (LPSTR)lpMsgBuf); - LocalFree(lpMsgBuf); -} -#endif - -void ex_printf_bin(const ex_u8* bin_data, size_t bin_size, const char* fmt, ...) -{ - if(NULL == g_exlog) - return; - if (!g_exlog->debug_mode) - return; - - ExThreadSmartLock locker(g_exlog->lock); - - va_list valist; - va_start(valist, fmt); - g_exlog->log_a(EX_LOG_LEVEL_DEBUG, fmt, valist); - va_end(valist); - - ex_printf_d(" (%d/0x%02x Bytes)\n", bin_size, bin_size); - - const ex_u8* line = bin_data; - size_t thisline = 0; - size_t offset = 0; - unsigned int i = 0; - - char szTmp[128] = { 0 }; - size_t _offset = 0; - - while (offset < bin_size) - { - memset(szTmp, 0, 128); - _offset = 0; - - snprintf(szTmp + _offset, 128 - _offset, "%06x ", (int)offset); - _offset += 8; - - thisline = bin_size - offset; - if (thisline > 16) - thisline = 16; - - for (i = 0; i < thisline; i++) - { - snprintf(szTmp + _offset, 128 - _offset, "%02x ", line[i]); - _offset += 3; - } - - snprintf(szTmp + _offset, 128 - _offset, " "); - _offset += 2; - - for (; i < 16; i++) - { - snprintf(szTmp + _offset, 128 - _offset, " "); - _offset += 3; - } - - for (i = 0; i < thisline; i++) - { - snprintf(szTmp + _offset, 128 - _offset, "%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.'); - _offset += 1; - } - - snprintf(szTmp + _offset, 128 - _offset, "\n"); - _offset += 1; - - ex_printf_d("%s", szTmp); - - offset += thisline; - line += thisline; - } - - fflush(stdout); -} - -bool ExLogger::set_log_file(const ex_wstr& log_path, const ex_wstr& log_name, ex_u32 max_filesize, ex_u8 max_count) -{ - m_max_filesize = max_filesize; - m_max_count = max_count; - - m_filename = log_name; - - m_path = log_path; - ex_abspath(m_path); - - ex_mkdirs(m_path); - - m_fullname = m_path; - ex_path_join(m_fullname, false, log_name.c_str(), NULL); - - return _open_file(); -} - - -bool ExLogger::_open_file() -{ - if (m_file) - { -#ifdef EX_OS_WIN32 - CloseHandle(m_file); -#else - fclose(m_file); -#endif - m_file = NULL; - } - -#ifdef EX_OS_WIN32 - // ע⣺ʹ CreateFile() ־ļʹFILEָ޷ݸ̬в - m_file = CreateFileW(m_fullname.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == m_file) - { - m_file = NULL; - return false; - } - - SetFilePointer(m_file, 0, NULL, FILE_END); - m_filesize = GetFileSize(m_file, NULL); -#else - ex_astr _fullname; - ex_wstr2astr(m_fullname, _fullname); - m_file = fopen(_fullname.c_str(), "a"); - - if (NULL == m_file) - { - return false; - } - - fseek(m_file, 0, SEEK_END); - m_filesize = (ex_u32)ftell(m_file); -#endif - - return _rotate_file(); -} - -bool ExLogger::_rotate_file(void) -{ - if (m_filesize < m_max_filesize) - return true; - - if (m_file) - { -#ifdef EX_OS_WIN32 - CloseHandle(m_file); -#else - fclose(m_file); -#endif - m_file = NULL; - } - - // make a name for backup file. - wchar_t _tmpname[64] = { 0 }; -#ifdef EX_OS_WIN32 - SYSTEMTIME st; - GetLocalTime(&st); - swprintf_s(_tmpname, 64, L"%s.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); -#else - time_t timep; - time(&timep); - struct tm *p = localtime(&timep); - if (p == NULL) - return false; - - ex_wcsformat(_tmpname, 64, L"%ls.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec); -#endif - - ex_wstr _new_fullname(m_path); - ex_path_join(_new_fullname, false, _tmpname, NULL); - -#ifdef EX_OS_WIN32 - if (!MoveFileW(m_fullname.c_str(), _new_fullname.c_str())) - { - EXLOGE_WIN("can not rename log file, remove old one and try again."); - DeleteFileW(_new_fullname.c_str()); - if (!MoveFileW(m_fullname.c_str(), _new_fullname.c_str())) - return false; - } -#else - ex_astr _a_fullname; - ex_astr _a_new_fullname; - ex_wstr2astr(m_fullname, _a_fullname); - ex_wstr2astr(_new_fullname, _a_new_fullname); - - if (rename(_a_fullname.c_str(), _a_new_fullname.c_str()) != 0) - { - remove(_a_new_fullname.c_str()); - if (0 != (rename(_a_fullname.c_str(), _a_new_fullname.c_str()))) - return false; - } -#endif - - return _open_file(); -} - -bool ExLogger::write_a(const char* buf) -{ - if (NULL == m_file) - return false; - - size_t len = strlen(buf); - - if (len > EX_LOG_CONTENT_MAX_LEN) - return false; - - char szTime[100] = { 0 }; -#ifdef EX_OS_WIN32 - SYSTEMTIME st; - GetLocalTime(&st); - sprintf_s(szTime, 100, "[%04d-%02d-%02d %02d:%02d:%02d] ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); - - int lenTime = strlen(szTime); - DWORD dwWritten = 0; - WriteFile(m_file, szTime, lenTime, &dwWritten, NULL); - m_filesize += lenTime; - WriteFile(m_file, buf, len, &dwWritten, NULL); - m_filesize += len; - FlushFileBuffers(m_file); -#else - time_t timep; - struct tm *p; - time(&timep); - p = localtime(&timep); - if (p == NULL) - return false; - sprintf(szTime, "[%04d-%02d-%02d %02d:%02d:%02d] ", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec); - - size_t lenTime = strlen(szTime); - fwrite(szTime, lenTime, 1, m_file); - m_filesize += lenTime; - fwrite(buf, len, 1, m_file); - m_filesize += len; - fflush(m_file); -#endif - - - return _rotate_file(); -} - -bool ExLogger::write_w(const wchar_t* buf) -{ - ex_astr _buf; - ex_wstr2astr(buf, _buf, EX_CODEPAGE_UTF8); - return write_a(_buf.c_str()); -} +#include +#include +//#include +//#include +//#include +//#include + +#ifdef EX_OS_WIN32 +# include +# include +# include +#else +//# include +//# include +#endif + +#define EX_LOG_CONTENT_MAX_LEN 2048 + +//typedef std::deque log_file_deque; + +static ExLogger* g_exlog = NULL; + +void EXLOG_USE_LOGGER(ExLogger* logger) +{ + g_exlog = logger; +} + +void EXLOG_LEVEL(int min_level) +{ + if(NULL != g_exlog) + g_exlog->min_level = min_level; +} + +void EXLOG_DEBUG(bool debug_mode) +{ + if (NULL != g_exlog) + g_exlog->debug_mode = debug_mode; +} + +void EXLOG_CONSOLE(bool output_to_console) +{ + if(NULL != g_exlog) + g_exlog->to_console = output_to_console; +} + +void EXLOG_FILE(const wchar_t* log_file, const wchar_t* log_path /*= NULL*/, ex_u32 max_filesize /*= EX_LOG_FILE_MAX_SIZE*/, ex_u8 max_filecount /*= EX_LOG_FILE_MAX_COUNT*/) +{ + if(NULL == g_exlog) + return; + + ex_wstr _path; + if (NULL == log_path) + { + ex_exec_file(_path); + ex_dirname(_path); + ex_path_join(_path, false, L"log", NULL); + } + else + { + _path = log_path; + } + + g_exlog->set_log_file(_path, log_file, max_filesize, max_filecount); +} + +ExLogger::ExLogger() +{ +#ifdef EX_OS_WIN32 + console_handle = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + + min_level = EX_LOG_LEVEL_INFO; + debug_mode = false; + to_console = true; + + m_file = NULL; + m_filesize = 0; +} + +ExLogger::~ExLogger() +{ + if (NULL != m_file) + { +#ifdef EX_OS_WIN32 + CloseHandle(m_file); +#else + fclose(m_file); +#endif + m_file = NULL; + } +} + +void ExLogger::log_a(int level, const char* fmt, va_list valist) +{ + if (NULL == fmt) + return; + + if (0 == strlen(fmt)) + return; + + char szTmp[4096] = { 0 }; + size_t offset = 0; + + if (level == EX_LOG_LEVEL_ERROR) + { + szTmp[0] = '['; + szTmp[1] = 'E'; + szTmp[2] = ']'; + szTmp[3] = ' '; + offset = 4; + } + +#ifdef EX_OS_WIN32 + vsnprintf_s(szTmp+offset, 4096-offset, 4095-offset, fmt, valist); + if(to_console) + { + if (NULL != console_handle) + { + printf_s("%s", szTmp); + fflush(stdout); + } + else + { + if(debug_mode) + OutputDebugStringA(szTmp); + } + } +#else + vsnprintf(szTmp+offset, 4095-offset, fmt, valist); + if(to_console) + { + // On linux, the stdout only output the first time output format (char or wchar_t). + // e.g.: first time you use printf(), then after that, every wprintf() not work, and vice versa. + // so we always use wprintf() to fix that. + + ex_astr tmp(szTmp); + ex_wstr _tmp; + ex_astr2wstr(tmp, _tmp); + wprintf(L"%ls", _tmp.c_str()); + fflush(stdout); + +// printf("%s", szTmp); +// fflush(stdout); + } +#endif + + write_a(szTmp); +} + +void ExLogger::log_w(int level, const wchar_t* fmt, va_list valist) +{ + if (NULL == fmt || 0 == wcslen(fmt)) + return; + + wchar_t szTmp[4096] = { 0 }; + size_t offset = 0; + + if (level == EX_LOG_LEVEL_ERROR) + { + szTmp[0] = L'['; + szTmp[1] = L'E'; + szTmp[2] = L']'; + szTmp[3] = L' '; + offset = 4; + } + +#ifdef EX_OS_WIN32 + _vsnwprintf_s(szTmp+offset, 4096-offset, 4095-offset, fmt, valist); + if(to_console) + { + if (NULL != console_handle) + { + wprintf_s(_T("%s"), szTmp); + fflush(stdout); + } + else + { + if(debug_mode) + OutputDebugStringW(szTmp); + } + } +#else + vswprintf(szTmp+offset, 4095-offset, fmt, valist); + if(to_console) + { + wprintf(L"%ls", szTmp); + fflush(stdout); + } +#endif + + write_w(szTmp); +} + +#define EX_PRINTF_XA(fn, level) \ +void fn(const char* fmt, ...) \ +{ \ + if(NULL == g_exlog) \ + return; \ + if (g_exlog->min_level > level) \ + return; \ + ExThreadSmartLock locker(g_exlog->lock); \ + va_list valist; \ + va_start(valist, fmt); \ + g_exlog->log_a(level, fmt, valist); \ + va_end(valist); \ +} + +#define EX_PRINTF_XW(fn, level) \ +void fn(const wchar_t* fmt, ...) \ +{ \ + if(NULL == g_exlog) \ + return; \ + if (g_exlog->min_level > level) \ + return; \ + ExThreadSmartLock locker(g_exlog->lock); \ + va_list valist; \ + va_start(valist, fmt); \ + g_exlog->log_w(level, fmt, valist); \ + va_end(valist); \ +} + +EX_PRINTF_XA(ex_printf_d, EX_LOG_LEVEL_DEBUG) +EX_PRINTF_XA(ex_printf_v, EX_LOG_LEVEL_VERBOSE) +EX_PRINTF_XA(ex_printf_i, EX_LOG_LEVEL_INFO) +EX_PRINTF_XA(ex_printf_w, EX_LOG_LEVEL_WARN) +EX_PRINTF_XA(ex_printf_e, EX_LOG_LEVEL_ERROR) + +EX_PRINTF_XW(ex_printf_d, EX_LOG_LEVEL_DEBUG) +EX_PRINTF_XW(ex_printf_v, EX_LOG_LEVEL_VERBOSE) +EX_PRINTF_XW(ex_printf_i, EX_LOG_LEVEL_INFO) +EX_PRINTF_XW(ex_printf_w, EX_LOG_LEVEL_WARN) +EX_PRINTF_XW(ex_printf_e, EX_LOG_LEVEL_ERROR) + + +#ifdef EX_OS_WIN32 +void ex_printf_e_lasterror(const char* fmt, ...) +{ + ExThreadSmartLock locker(g_exlog->lock); + + va_list valist; + va_start(valist, fmt); + g_exlog->log_a(EX_LOG_LEVEL_ERROR, fmt, valist); + va_end(valist); + + //========================================= + + LPVOID lpMsgBuf; + DWORD dw = GetLastError(); + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, 0, NULL); + + ex_printf_e(" - WinErr(%d): %s\n", dw, (LPSTR)lpMsgBuf); + LocalFree(lpMsgBuf); +} + +void ex_printf_e_lasterror(const wchar_t* fmt, ...) +{ + ExThreadSmartLock locker(g_exlog->lock); + + va_list valist; + va_start(valist, fmt); + g_exlog->log_w(EX_LOG_LEVEL_ERROR, fmt, valist); + va_end(valist); + + //========================================= + + LPVOID lpMsgBuf; + DWORD dw = GetLastError(); + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, 0, NULL); + + ex_printf_e(" - WinErr(%d): %s\n", dw, (LPSTR)lpMsgBuf); + LocalFree(lpMsgBuf); +} +#endif + +void ex_printf_bin(const ex_u8* bin_data, size_t bin_size, const char* fmt, ...) +{ + if(NULL == g_exlog) + return; + if (!g_exlog->debug_mode) + return; + + ExThreadSmartLock locker(g_exlog->lock); + + va_list valist; + va_start(valist, fmt); + g_exlog->log_a(EX_LOG_LEVEL_DEBUG, fmt, valist); + va_end(valist); + + ex_printf_d(" (%d/0x%02x Bytes)\n", bin_size, bin_size); + + const ex_u8* line = bin_data; + size_t thisline = 0; + size_t offset = 0; + unsigned int i = 0; + + char szTmp[128] = { 0 }; + size_t _offset = 0; + + while (offset < bin_size) + { + memset(szTmp, 0, 128); + _offset = 0; + + snprintf(szTmp + _offset, 128 - _offset, "%06x ", (int)offset); + _offset += 8; + + thisline = bin_size - offset; + if (thisline > 16) + thisline = 16; + + for (i = 0; i < thisline; i++) + { + snprintf(szTmp + _offset, 128 - _offset, "%02x ", line[i]); + _offset += 3; + } + + snprintf(szTmp + _offset, 128 - _offset, " "); + _offset += 2; + + for (; i < 16; i++) + { + snprintf(szTmp + _offset, 128 - _offset, " "); + _offset += 3; + } + + for (i = 0; i < thisline; i++) + { + snprintf(szTmp + _offset, 128 - _offset, "%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.'); + _offset += 1; + } + + snprintf(szTmp + _offset, 128 - _offset, "\n"); + _offset += 1; + + ex_printf_d("%s", szTmp); + + offset += thisline; + line += thisline; + } + + fflush(stdout); +} + +bool ExLogger::set_log_file(const ex_wstr& log_path, const ex_wstr& log_name, ex_u32 max_filesize, ex_u8 max_count) +{ + m_max_filesize = max_filesize; + m_max_count = max_count; + + m_filename = log_name; + + m_path = log_path; + ex_abspath(m_path); + + ex_mkdirs(m_path); + + m_fullname = m_path; + ex_path_join(m_fullname, false, log_name.c_str(), NULL); + + return _open_file(); +} + + +bool ExLogger::_open_file() +{ + if (m_file) + { +#ifdef EX_OS_WIN32 + CloseHandle(m_file); +#else + fclose(m_file); +#endif + m_file = NULL; + } + +#ifdef EX_OS_WIN32 + // 注意:这里必须使用 CreateFile() 来打开日志文件,使用FILE指针无法传递给动态库进行操作。 + m_file = CreateFileW(m_fullname.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == m_file) + { + m_file = NULL; + return false; + } + + SetFilePointer(m_file, 0, NULL, FILE_END); + m_filesize = GetFileSize(m_file, NULL); +#else + ex_astr _fullname; + ex_wstr2astr(m_fullname, _fullname); + m_file = fopen(_fullname.c_str(), "a"); + + if (NULL == m_file) + { + return false; + } + + fseek(m_file, 0, SEEK_END); + m_filesize = (ex_u32)ftell(m_file); +#endif + + return _rotate_file(); +} + +bool ExLogger::_rotate_file(void) +{ + if (m_filesize < m_max_filesize) + return true; + + if (m_file) + { +#ifdef EX_OS_WIN32 + CloseHandle(m_file); +#else + fclose(m_file); +#endif + m_file = NULL; + } + + // make a name for backup file. + wchar_t _tmpname[64] = { 0 }; +#ifdef EX_OS_WIN32 + SYSTEMTIME st; + GetLocalTime(&st); + swprintf_s(_tmpname, 64, L"%s.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); +#else + time_t timep; + time(&timep); + struct tm *p = localtime(&timep); + if (p == NULL) + return false; + + ex_wcsformat(_tmpname, 64, L"%ls.%04d%02d%02d%02d%02d%02d.bak", m_filename.c_str(), p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec); +#endif + + ex_wstr _new_fullname(m_path); + ex_path_join(_new_fullname, false, _tmpname, NULL); + +#ifdef EX_OS_WIN32 + if (!MoveFileW(m_fullname.c_str(), _new_fullname.c_str())) + { + EXLOGE_WIN("can not rename log file, remove old one and try again."); + DeleteFileW(_new_fullname.c_str()); + if (!MoveFileW(m_fullname.c_str(), _new_fullname.c_str())) + return false; + } +#else + ex_astr _a_fullname; + ex_astr _a_new_fullname; + ex_wstr2astr(m_fullname, _a_fullname); + ex_wstr2astr(_new_fullname, _a_new_fullname); + + if (rename(_a_fullname.c_str(), _a_new_fullname.c_str()) != 0) + { + remove(_a_new_fullname.c_str()); + if (0 != (rename(_a_fullname.c_str(), _a_new_fullname.c_str()))) + return false; + } +#endif + + return _open_file(); +} + +bool ExLogger::write_a(const char* buf) +{ + if (NULL == m_file) + return false; + + size_t len = strlen(buf); + + if (len > EX_LOG_CONTENT_MAX_LEN) + return false; + + char szTime[100] = { 0 }; +#ifdef EX_OS_WIN32 + SYSTEMTIME st; + GetLocalTime(&st); + sprintf_s(szTime, 100, "[%04d-%02d-%02d %02d:%02d:%02d] ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + + int lenTime = strlen(szTime); + DWORD dwWritten = 0; + WriteFile(m_file, szTime, lenTime, &dwWritten, NULL); + m_filesize += lenTime; + WriteFile(m_file, buf, len, &dwWritten, NULL); + m_filesize += len; + FlushFileBuffers(m_file); +#else + time_t timep; + struct tm *p; + time(&timep); + p = localtime(&timep); + if (p == NULL) + return false; + sprintf(szTime, "[%04d-%02d-%02d %02d:%02d:%02d] ", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec); + + size_t lenTime = strlen(szTime); + fwrite(szTime, lenTime, 1, m_file); + m_filesize += lenTime; + fwrite(buf, len, 1, m_file); + m_filesize += len; + fflush(m_file); +#endif + + + return _rotate_file(); +} + +bool ExLogger::write_w(const wchar_t* buf) +{ + ex_astr _buf; + ex_wstr2astr(buf, _buf, EX_CODEPAGE_UTF8); + return write_a(_buf.c_str()); +} diff --git a/common/libex/src/ex_path.cpp b/common/libex/src/ex_path.cpp index fd3e7f4..4ac23d5 100644 --- a/common/libex/src/ex_path.cpp +++ b/common/libex/src/ex_path.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/common/libex/src/ex_str.cpp b/common/libex/src/ex_str.cpp index a3fac76..1aa6046 100644 --- a/common/libex/src/ex_str.cpp +++ b/common/libex/src/ex_str.cpp @@ -1,855 +1,855 @@ -#include -#include -#include - -char* ex_strcpy(char* target, size_t size, const char* source) -{ - if (target == source) - return target; - -#ifdef EX_OS_WIN32 - if (SUCCEEDED(StringCchCopyA(target, size, source))) - return target; - else - return NULL; -#else - size_t len = strlen(source); - if (size > len) - { - return strcpy(target, source); - } - else - { - memmove(target, source, size - 1); - return NULL; - } -#endif -} - -wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source) -{ - if (target == source) - return target; - -#ifdef EX_OS_WIN32 - if (SUCCEEDED(StringCchCopyW(target, size, source))) - return target; - else - return NULL; -#else - size_t len = wcslen(source); - if (size > len) - { - return wcscpy(target, source); - } - else - { - memmove(target, source, (size - 1)*sizeof(wchar_t)); - return NULL; - } -#endif -} - -char* ex_strdup(const char* src) -{ - if (NULL == src) - return NULL; - size_t len = strlen(src) + 1; - char* ret = (char*)calloc(1, len); - memcpy(ret, src, len); - return ret; -} - -wchar_t* ex_wcsdup(const wchar_t* src) -{ - if (NULL == src) - return NULL; - size_t len = wcslen(src) + 1; - wchar_t* ret = (wchar_t*)calloc(sizeof(wchar_t), len); - memcpy(ret, src, sizeof(wchar_t)*len); - return ret; -} - -wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page) -{ - wchar_t* out_buffer = NULL; -#ifdef EX_OS_WIN32 - int wlen = 0; - UINT _cp = 0; - if (code_page == EX_CODEPAGE_ACP) - _cp = CP_ACP; - else if (code_page == EX_CODEPAGE_UTF8) - _cp = CP_UTF8; - - wlen = MultiByteToWideChar(_cp, 0, in_buffer, -1, NULL, 0); - if (0 == wlen) - return NULL; - - out_buffer = (wchar_t*)calloc(wlen + 1, sizeof(wchar_t)); - if (NULL == out_buffer) - return NULL; - - wlen = MultiByteToWideChar(_cp, 0, in_buffer, -1, out_buffer, wlen); - if (0 == wlen) - { - free(out_buffer); - return NULL; - } - -#else - size_t wlen = 0; - wlen = mbstowcs(NULL, in_buffer, 0); - if (wlen <= 0) - return NULL; - - out_buffer = (wchar_t*)calloc(wlen + 1, sizeof(wchar_t)); - if (NULL == out_buffer) - return NULL; - - wlen = mbstowcs(out_buffer, in_buffer, wlen); - if (wlen <= 0) - { - free(out_buffer); - return NULL; - } - -#endif - - return out_buffer; -} - - -char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page) -{ - char* out_buffer = NULL; - - if(NULL == in_buffer) - return NULL; - -#ifdef EX_OS_WIN32 - int len = 0; - UINT _cp = 0; - if (code_page == EX_CODEPAGE_ACP) - _cp = CP_ACP; - else if (code_page == EX_CODEPAGE_UTF8) - _cp = CP_UTF8; - - len = WideCharToMultiByte(_cp, 0, in_buffer, -1, NULL, 0, NULL, NULL); - if (0 == len) - return NULL; - - out_buffer = (char*)calloc(len + 1, sizeof(char)); - if (NULL == out_buffer) - return NULL; - - len = WideCharToMultiByte(_cp, 0, in_buffer, -1, out_buffer, len, NULL, NULL); - if (0 == len) - { - free(out_buffer); - return NULL; - } - -#else - size_t len = 0; - len = wcstombs(NULL, in_buffer, 0); - if (len <= 0) - return NULL; - - out_buffer = (char*)calloc(len + 1, sizeof(char)); - if (NULL == out_buffer) - return NULL; - - len = wcstombs(out_buffer, in_buffer, len); - if (len <= 0) - { - free(out_buffer); - return NULL; - } - -#endif - - return out_buffer; -} - -wchar_t** ex_make_wargv(int argc, char** argv) -{ - int i = 0; - wchar_t** ret = NULL; - - ret = (wchar_t**)calloc(argc + 1, sizeof(wchar_t*)); - if (!ret) - { - return NULL; - } - - for (i = 0; i < argc; ++i) - { - ret[i] = ex_str2wcs_alloc(argv[i], EX_CODEPAGE_DEFAULT); - if (NULL == ret[i]) - goto err; - } - - return ret; - -err: - ex_free_wargv(argc, ret); - return NULL; -} - -void ex_free_wargv(int argc, wchar_t** argv) -{ - int i = 0; - for (i = 0; i < argc; ++i) - free(argv[i]); - - free(argv); -} - -EX_BOOL ex_str_only_white_space(const wchar_t* src) -{ - if (ex_only_white_space(src)) - return EX_TRUE; - else - return EX_FALSE; -} - -EX_BOOL ex_wcs_only_white_space(const char* src) -{ - if (ex_only_white_space(src)) - return EX_TRUE; - else - return EX_FALSE; -} - -int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...) -{ - int ret = 0; - va_list valist; - va_start(valist, fmt); - //_ts_printf_a(level, EX_COLOR_BLACK, fmt, valist); -#ifdef EX_OS_WIN32 - ret = vsnprintf(out_buf, buf_size, fmt, valist); -#else - ret = vsprintf(out_buf, fmt, valist); -#endif - va_end(valist); - return ret; -} - -int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...) -{ - int ret = 0; - va_list valist; - va_start(valist, fmt); - //_ts_printf_a(level, EX_COLOR_BLACK, fmt, valist); -#ifdef EX_OS_WIN32 - //ret = vsnprintf(out_buf, buf_size, fmt, valist); - ret = _vsnwprintf_s(out_buf, buf_size, buf_size, fmt, valist); -#else - //ret = vsprintf(out_buf, fmt, valist); - ret = vswprintf(out_buf, buf_size, fmt, valist); -#endif - va_end(valist); - return ret; -} - - -#ifdef __cplusplus -bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) -{ - return ex_wstr2astr(in_str.c_str(), out_str, code_page); -} - -bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) -{ - char* astr = ex_wcs2str_alloc(in_str, code_page); - if (NULL == astr) - return false; - - out_str = astr; - ex_free(astr); - return true; -} - -bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) -{ - return ex_astr2wstr(in_str.c_str(), out_str, code_page); -} - -bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) -{ - wchar_t* wstr = ex_str2wcs_alloc(in_str, code_page); - if (NULL == wstr) - return false; - - out_str = wstr; - ex_free(wstr); - return true; -} - -bool ex_only_white_space(const ex_astr& str_check) -{ - ex_astr::size_type pos = 0; - ex_astr strFilter(" \t\r\n"); - pos = str_check.find_first_not_of(strFilter); - if (ex_astr::npos == pos) - return true; - else - return false; -} - -bool ex_only_white_space(const ex_wstr& str_check) -{ - ex_wstr::size_type pos = 0; - ex_wstr strFilter(L" \t\r\n"); - pos = str_check.find_first_not_of(strFilter); - if (ex_wstr::npos == pos) - return true; - else - return false; -} - -void ex_remove_white_space(ex_astr& str_fix, int ulFlag /*= EX_RSC_ALL*/) -{ - ex_astr::size_type pos = 0; - ex_astr strFilter(" \t\r\n"); - - if (ulFlag & EX_RSC_BEGIN) - { - pos = str_fix.find_first_not_of(strFilter); - if (ex_astr::npos != pos) - str_fix.erase(0, pos); - // FIXME - } - if (ulFlag & EX_RSC_END) - { - pos = str_fix.find_last_not_of(strFilter); - if (ex_astr::npos != pos) - str_fix.erase(pos + 1); - // FIXME - } -} - -void ex_remove_white_space(ex_wstr& str_fix, int ulFlag /*= EX_RSC_ALL*/) -{ - ex_wstr::size_type pos = 0; - ex_wstr strFilter(L" \t\r\n"); - - if (ulFlag & EX_RSC_BEGIN) - { - pos = str_fix.find_first_not_of(strFilter); - if (ex_wstr::npos != pos) - str_fix.erase(0, pos); - // FIXME - } - if (ulFlag & EX_RSC_END) - { - pos = str_fix.find_last_not_of(strFilter); - if (ex_wstr::npos != pos) - str_fix.erase(pos + 1); - // FIXME - } -} - -ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value) -{ - for (ex_astr::size_type pos(0); pos != ex_astr::npos; pos += new_value.length()) - { - if ((pos = str.find(old_value, pos)) != ex_astr::npos) - str.replace(pos, old_value.length(), new_value); - else - break; - } - - return str; -} - -ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value) -{ - for (ex_wstr::size_type pos(0); pos != ex_wstr::npos; pos += new_value.length()) - { - if ((pos = str.find(old_value, pos)) != ex_wstr::npos) - str.replace(pos, old_value.length(), new_value); - else - break; - } - - return str; -} - - - -#ifndef EX_OS_WIN32 - -#define BYTE ex_u8 -#define DWORD ex_u32 -#define WCHAR ex_i16 -#define LPWSTR WCHAR* -#define BOOL int -#define TRUE 1 -#define FALSE 0 -#define UINT unsigned int -#define LPCSTR const char* -#define CP_UTF8 1 - -typedef enum -{ - conversionOK, /* conversion successful */ - sourceExhausted, /* partial character in source, but hit end */ - targetExhausted, /* insuff. room in target for conversion */ - sourceIllegal /* source sequence is illegal/malformed */ -} ConversionResult; - -typedef enum -{ - strictConversion = 0, - lenientConversion -} ConversionFlags; - -static const char trailingBytesForUTF8[256] = -{ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; - -static const DWORD offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL -}; - -static const BYTE firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - -static const int halfShift = 10; /* used for shifting by 10 bits */ - -static const DWORD halfBase = 0x0010000UL; -static const DWORD halfMask = 0x3FFUL; - -#define UNI_SUR_HIGH_START (DWORD)0xD800 -#define UNI_SUR_HIGH_END (DWORD)0xDBFF -#define UNI_SUR_LOW_START (DWORD)0xDC00 -#define UNI_SUR_LOW_END (DWORD)0xDFFF - -#define UNI_REPLACEMENT_CHAR (DWORD)0x0000FFFD -#define UNI_MAX_BMP (DWORD)0x0000FFFF -#define UNI_MAX_UTF16 (DWORD)0x0010FFFF -#define UNI_MAX_UTF32 (DWORD)0x7FFFFFFF -#define UNI_MAX_LEGAL_UTF32 (DWORD)0x0010FFFF - - -static ConversionResult ConvertUTF16toUTF8(const WCHAR** sourceStart, const WCHAR* sourceEnd, BYTE** targetStart, BYTE* targetEnd, ConversionFlags flags) -{ - BYTE* target; - const WCHAR* source; - BOOL computeLength; - ConversionResult result; - computeLength = (!targetEnd) ? TRUE : FALSE; - source = *sourceStart; - target = *targetStart; - result = conversionOK; - - while (source < sourceEnd) - { - DWORD ch; - unsigned short bytesToWrite = 0; - const DWORD byteMask = 0xBF; - const DWORD byteMark = 0x80; - const WCHAR* oldSource = source; /* In case we have to back up because of target overflow. */ - ch = *source++; - - /* If we have a surrogate pair, convert to UTF32 first. */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) - { - /* If the 16 bits following the high surrogate are in the source buffer... */ - if (source < sourceEnd) - { - DWORD ch2 = *source; - - /* If it's a low surrogate, convert to UTF32. */ - if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) - { - ch = ((ch - UNI_SUR_HIGH_START) << halfShift) - + (ch2 - UNI_SUR_LOW_START) + halfBase; - ++source; - } - else if (flags == strictConversion) - { - /* it's an unpaired high surrogate */ - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - else - { - /* We don't have the 16 bits following the high surrogate. */ - --source; /* return to the high surrogate */ - result = sourceExhausted; - break; - } - } - else if (flags == strictConversion) - { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) - { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - - /* Figure out how many bytes the result will require */ - if (ch < (DWORD)0x80) - { - bytesToWrite = 1; - } - else if (ch < (DWORD)0x800) - { - bytesToWrite = 2; - } - else if (ch < (DWORD)0x10000) - { - bytesToWrite = 3; - } - else if (ch < (DWORD)0x110000) - { - bytesToWrite = 4; - } - else - { - bytesToWrite = 3; - ch = UNI_REPLACEMENT_CHAR; - } - - target += bytesToWrite; - - if ((target > targetEnd) && (!computeLength)) - { - source = oldSource; /* Back up source pointer! */ - target -= bytesToWrite; - result = targetExhausted; - break; - } - - if (!computeLength) - { - switch (bytesToWrite) - { - /* note: everything falls through. */ - case 4: - *--target = (BYTE)((ch | byteMark) & byteMask); - ch >>= 6; - - case 3: - *--target = (BYTE)((ch | byteMark) & byteMask); - ch >>= 6; - - case 2: - *--target = (BYTE)((ch | byteMark) & byteMask); - ch >>= 6; - - case 1: - *--target = (BYTE)(ch | firstByteMark[bytesToWrite]); - } - } - else - { - switch (bytesToWrite) - { - /* note: everything falls through. */ - case 4: - --target; - ch >>= 6; - - case 3: - --target; - ch >>= 6; - - case 2: - --target; - ch >>= 6; - - case 1: - --target; - } - } - - target += bytesToWrite; - } - - *sourceStart = source; - *targetStart = target; - return result; -} - - -static BOOL isLegalUTF8(const BYTE* source, int length) -{ - BYTE a; - const BYTE* srcptr = source + length; - - switch (length) - { - default: - return FALSE; - - /* Everything else falls through when "TRUE"... */ - case 4: - if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return FALSE; - - case 3: - if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return FALSE; - - case 2: - if ((a = (*--srcptr)) > 0xBF) return FALSE; - - switch (*source) - { - /* no fall-through in this inner switch */ - case 0xE0: - if (a < 0xA0) return FALSE; - - break; - - case 0xED: - if (a > 0x9F) return FALSE; - - break; - - case 0xF0: - if (a < 0x90) return FALSE; - - break; - - case 0xF4: - if (a > 0x8F) return FALSE; - - break; - - default: - if (a < 0x80) return FALSE; - } - - case 1: - if (*source >= 0x80 && *source < 0xC2) return FALSE; - } - - if (*source > 0xF4) - return FALSE; - - return TRUE; -} - -static ConversionResult _ConvertUTF8toUTF16(const BYTE** sourceStart, const BYTE* sourceEnd, WCHAR** targetStart, WCHAR* targetEnd, ConversionFlags flags) -{ - WCHAR* target; - const BYTE* source; - BOOL computeLength; - ConversionResult result; - computeLength = (!targetEnd) ? TRUE : FALSE; - result = conversionOK; - source = *sourceStart; - target = *targetStart; - - while (source < sourceEnd) - { - DWORD ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - - if ((source + extraBytesToRead) >= sourceEnd) - { - result = sourceExhausted; - break; - } - - /* Do this check whether lenient or strict */ - if (!isLegalUTF8(source, extraBytesToRead + 1)) - { - result = sourceIllegal; - break; - } - - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) - { - case 5: - ch += *source++; - ch <<= 6; /* remember, illegal UTF-8 */ - - case 4: - ch += *source++; - ch <<= 6; /* remember, illegal UTF-8 */ - - case 3: - ch += *source++; - ch <<= 6; - - case 2: - ch += *source++; - ch <<= 6; - - case 1: - ch += *source++; - ch <<= 6; - - case 0: - ch += *source++; - } - - ch -= offsetsFromUTF8[extraBytesToRead]; - - if ((target >= targetEnd) && (!computeLength)) - { - source -= (extraBytesToRead + 1); /* Back up source pointer! */ - result = targetExhausted; - break; - } - - if (ch <= UNI_MAX_BMP) - { - /* Target is a character <= 0xFFFF */ - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) - { - if (flags == strictConversion) - { - source -= (extraBytesToRead + 1); /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - else - { - if (!computeLength) - *target++ = UNI_REPLACEMENT_CHAR; - else - target++; - } - } - else - { - if (!computeLength) - *target++ = (WCHAR)ch; /* normal case */ - else - target++; - } - } - else if (ch > UNI_MAX_UTF16) - { - if (flags == strictConversion) - { - result = sourceIllegal; - source -= (extraBytesToRead + 1); /* return to the start */ - break; /* Bail out; shouldn't continue */ - } - else - { - if (!computeLength) - *target++ = UNI_REPLACEMENT_CHAR; - else - target++; - } - } - else - { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - if ((target + 1 >= targetEnd) && (!computeLength)) - { - source -= (extraBytesToRead + 1); /* Back up source pointer! */ - result = targetExhausted; - break; - } - - ch -= halfBase; - - if (!computeLength) - { - *target++ = (WCHAR)((ch >> halfShift) + UNI_SUR_HIGH_START); - *target++ = (WCHAR)((ch & halfMask) + UNI_SUR_LOW_START); - } - else - { - target++; - target++; - } - } - } - - *sourceStart = source; - *targetStart = target; - return result; -} - -static int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar) -{ - int length; - LPWSTR targetStart; - const BYTE* sourceStart; - ConversionResult result; - - /* If cbMultiByte is 0, the function fails */ - - if (cbMultiByte == 0) - return 0; - - /* If cbMultiByte is -1, the string is null-terminated */ - - if (cbMultiByte == -1) - cbMultiByte = (int)strlen((char*)lpMultiByteStr) + 1; - - /* - * if cchWideChar is 0, the function returns the required buffer size - * in characters for lpWideCharStr and makes no use of the output parameter itself. - */ - - if (cchWideChar == 0) - { - sourceStart = (const BYTE*)lpMultiByteStr; - targetStart = (WCHAR*)NULL; - - result = _ConvertUTF8toUTF16(&sourceStart, &sourceStart[cbMultiByte], - &targetStart, NULL, strictConversion); - - length = (int)(targetStart - ((WCHAR*)NULL)); - cchWideChar = length; - } - else - { - sourceStart = (const BYTE*)lpMultiByteStr; - targetStart = lpWideCharStr; - - result = _ConvertUTF8toUTF16(&sourceStart, &sourceStart[cbMultiByte], - &targetStart, &targetStart[cchWideChar], strictConversion); - - length = (int)(targetStart - ((WCHAR*)lpWideCharStr)); - cchWideChar = length; - } - - return cchWideChar; -} - -#endif - - - -bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to) -{ - int iSize = MultiByteToWideChar(CP_UTF8, 0, from.c_str(), -1, NULL, 0); - if (iSize <= 0) - return false; - - //++iSize; - to.resize(iSize); - memset(&to[0], 0, sizeof(ex_utf16)); - - MultiByteToWideChar(CP_UTF8, 0, from.c_str(), -1, &to[0], iSize); - - return true; -} - -#endif +#include +#include +#include + +char* ex_strcpy(char* target, size_t size, const char* source) +{ + if (target == source) + return target; + +#ifdef EX_OS_WIN32 + if (SUCCEEDED(StringCchCopyA(target, size, source))) + return target; + else + return NULL; +#else + size_t len = strlen(source); + if (size > len) + { + return strcpy(target, source); + } + else + { + memmove(target, source, size - 1); + return NULL; + } +#endif +} + +wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source) +{ + if (target == source) + return target; + +#ifdef EX_OS_WIN32 + if (SUCCEEDED(StringCchCopyW(target, size, source))) + return target; + else + return NULL; +#else + size_t len = wcslen(source); + if (size > len) + { + return wcscpy(target, source); + } + else + { + memmove(target, source, (size - 1)*sizeof(wchar_t)); + return NULL; + } +#endif +} + +char* ex_strdup(const char* src) +{ + if (NULL == src) + return NULL; + size_t len = strlen(src) + 1; + char* ret = (char*)calloc(1, len); + memcpy(ret, src, len); + return ret; +} + +wchar_t* ex_wcsdup(const wchar_t* src) +{ + if (NULL == src) + return NULL; + size_t len = wcslen(src) + 1; + wchar_t* ret = (wchar_t*)calloc(sizeof(wchar_t), len); + memcpy(ret, src, sizeof(wchar_t)*len); + return ret; +} + +wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page) +{ + wchar_t* out_buffer = NULL; +#ifdef EX_OS_WIN32 + int wlen = 0; + UINT _cp = 0; + if (code_page == EX_CODEPAGE_ACP) + _cp = CP_ACP; + else if (code_page == EX_CODEPAGE_UTF8) + _cp = CP_UTF8; + + wlen = MultiByteToWideChar(_cp, 0, in_buffer, -1, NULL, 0); + if (0 == wlen) + return NULL; + + out_buffer = (wchar_t*)calloc(wlen + 1, sizeof(wchar_t)); + if (NULL == out_buffer) + return NULL; + + wlen = MultiByteToWideChar(_cp, 0, in_buffer, -1, out_buffer, wlen); + if (0 == wlen) + { + free(out_buffer); + return NULL; + } + +#else + size_t wlen = 0; + wlen = mbstowcs(NULL, in_buffer, 0); + if (wlen <= 0) + return NULL; + + out_buffer = (wchar_t*)calloc(wlen + 1, sizeof(wchar_t)); + if (NULL == out_buffer) + return NULL; + + wlen = mbstowcs(out_buffer, in_buffer, wlen); + if (wlen <= 0) + { + free(out_buffer); + return NULL; + } + +#endif + + return out_buffer; +} + + +char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page) +{ + char* out_buffer = NULL; + + if(NULL == in_buffer) + return NULL; + +#ifdef EX_OS_WIN32 + int len = 0; + UINT _cp = 0; + if (code_page == EX_CODEPAGE_ACP) + _cp = CP_ACP; + else if (code_page == EX_CODEPAGE_UTF8) + _cp = CP_UTF8; + + len = WideCharToMultiByte(_cp, 0, in_buffer, -1, NULL, 0, NULL, NULL); + if (0 == len) + return NULL; + + out_buffer = (char*)calloc(len + 1, sizeof(char)); + if (NULL == out_buffer) + return NULL; + + len = WideCharToMultiByte(_cp, 0, in_buffer, -1, out_buffer, len, NULL, NULL); + if (0 == len) + { + free(out_buffer); + return NULL; + } + +#else + size_t len = 0; + len = wcstombs(NULL, in_buffer, 0); + if (len <= 0) + return NULL; + + out_buffer = (char*)calloc(len + 1, sizeof(char)); + if (NULL == out_buffer) + return NULL; + + len = wcstombs(out_buffer, in_buffer, len); + if (len <= 0) + { + free(out_buffer); + return NULL; + } + +#endif + + return out_buffer; +} + +wchar_t** ex_make_wargv(int argc, char** argv) +{ + int i = 0; + wchar_t** ret = NULL; + + ret = (wchar_t**)calloc(argc + 1, sizeof(wchar_t*)); + if (!ret) + { + return NULL; + } + + for (i = 0; i < argc; ++i) + { + ret[i] = ex_str2wcs_alloc(argv[i], EX_CODEPAGE_DEFAULT); + if (NULL == ret[i]) + goto err; + } + + return ret; + +err: + ex_free_wargv(argc, ret); + return NULL; +} + +void ex_free_wargv(int argc, wchar_t** argv) +{ + int i = 0; + for (i = 0; i < argc; ++i) + free(argv[i]); + + free(argv); +} + +EX_BOOL ex_str_only_white_space(const wchar_t* src) +{ + if (ex_only_white_space(src)) + return EX_TRUE; + else + return EX_FALSE; +} + +EX_BOOL ex_wcs_only_white_space(const char* src) +{ + if (ex_only_white_space(src)) + return EX_TRUE; + else + return EX_FALSE; +} + +int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...) +{ + int ret = 0; + va_list valist; + va_start(valist, fmt); + //_ts_printf_a(level, EX_COLOR_BLACK, fmt, valist); +#ifdef EX_OS_WIN32 + ret = vsnprintf(out_buf, buf_size, fmt, valist); +#else + ret = vsprintf(out_buf, fmt, valist); +#endif + va_end(valist); + return ret; +} + +int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...) +{ + int ret = 0; + va_list valist; + va_start(valist, fmt); + //_ts_printf_a(level, EX_COLOR_BLACK, fmt, valist); +#ifdef EX_OS_WIN32 + //ret = vsnprintf(out_buf, buf_size, fmt, valist); + ret = _vsnwprintf_s(out_buf, buf_size, buf_size, fmt, valist); +#else + //ret = vsprintf(out_buf, fmt, valist); + ret = vswprintf(out_buf, buf_size, fmt, valist); +#endif + va_end(valist); + return ret; +} + + +#ifdef __cplusplus +bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) +{ + return ex_wstr2astr(in_str.c_str(), out_str, code_page); +} + +bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) +{ + char* astr = ex_wcs2str_alloc(in_str, code_page); + if (NULL == astr) + return false; + + out_str = astr; + ex_free(astr); + return true; +} + +bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) +{ + return ex_astr2wstr(in_str.c_str(), out_str, code_page); +} + +bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page/* = EX_CODEPAGE_DEFAULT*/) +{ + wchar_t* wstr = ex_str2wcs_alloc(in_str, code_page); + if (NULL == wstr) + return false; + + out_str = wstr; + ex_free(wstr); + return true; +} + +bool ex_only_white_space(const ex_astr& str_check) +{ + ex_astr::size_type pos = 0; + ex_astr strFilter(" \t\r\n"); + pos = str_check.find_first_not_of(strFilter); + if (ex_astr::npos == pos) + return true; + else + return false; +} + +bool ex_only_white_space(const ex_wstr& str_check) +{ + ex_wstr::size_type pos = 0; + ex_wstr strFilter(L" \t\r\n"); + pos = str_check.find_first_not_of(strFilter); + if (ex_wstr::npos == pos) + return true; + else + return false; +} + +void ex_remove_white_space(ex_astr& str_fix, int ulFlag /*= EX_RSC_ALL*/) +{ + ex_astr::size_type pos = 0; + ex_astr strFilter(" \t\r\n"); + + if (ulFlag & EX_RSC_BEGIN) + { + pos = str_fix.find_first_not_of(strFilter); + if (ex_astr::npos != pos) + str_fix.erase(0, pos); + // FIXME + } + if (ulFlag & EX_RSC_END) + { + pos = str_fix.find_last_not_of(strFilter); + if (ex_astr::npos != pos) + str_fix.erase(pos + 1); + // FIXME + } +} + +void ex_remove_white_space(ex_wstr& str_fix, int ulFlag /*= EX_RSC_ALL*/) +{ + ex_wstr::size_type pos = 0; + ex_wstr strFilter(L" \t\r\n"); + + if (ulFlag & EX_RSC_BEGIN) + { + pos = str_fix.find_first_not_of(strFilter); + if (ex_wstr::npos != pos) + str_fix.erase(0, pos); + // FIXME + } + if (ulFlag & EX_RSC_END) + { + pos = str_fix.find_last_not_of(strFilter); + if (ex_wstr::npos != pos) + str_fix.erase(pos + 1); + // FIXME + } +} + +ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value) +{ + for (ex_astr::size_type pos(0); pos != ex_astr::npos; pos += new_value.length()) + { + if ((pos = str.find(old_value, pos)) != ex_astr::npos) + str.replace(pos, old_value.length(), new_value); + else + break; + } + + return str; +} + +ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value) +{ + for (ex_wstr::size_type pos(0); pos != ex_wstr::npos; pos += new_value.length()) + { + if ((pos = str.find(old_value, pos)) != ex_wstr::npos) + str.replace(pos, old_value.length(), new_value); + else + break; + } + + return str; +} + + + +#ifndef EX_OS_WIN32 + +#define BYTE ex_u8 +#define DWORD ex_u32 +#define WCHAR ex_i16 +#define LPWSTR WCHAR* +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#define UINT unsigned int +#define LPCSTR const char* +#define CP_UTF8 1 + +typedef enum +{ + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum +{ + strictConversion = 0, + lenientConversion +} ConversionFlags; + +static const char trailingBytesForUTF8[256] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +static const DWORD offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +static const BYTE firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const DWORD halfBase = 0x0010000UL; +static const DWORD halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (DWORD)0xD800 +#define UNI_SUR_HIGH_END (DWORD)0xDBFF +#define UNI_SUR_LOW_START (DWORD)0xDC00 +#define UNI_SUR_LOW_END (DWORD)0xDFFF + +#define UNI_REPLACEMENT_CHAR (DWORD)0x0000FFFD +#define UNI_MAX_BMP (DWORD)0x0000FFFF +#define UNI_MAX_UTF16 (DWORD)0x0010FFFF +#define UNI_MAX_UTF32 (DWORD)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (DWORD)0x0010FFFF + + +static ConversionResult ConvertUTF16toUTF8(const WCHAR** sourceStart, const WCHAR* sourceEnd, BYTE** targetStart, BYTE* targetEnd, ConversionFlags flags) +{ + BYTE* target; + const WCHAR* source; + BOOL computeLength; + ConversionResult result; + computeLength = (!targetEnd) ? TRUE : FALSE; + source = *sourceStart; + target = *targetStart; + result = conversionOK; + + while (source < sourceEnd) + { + DWORD ch; + unsigned short bytesToWrite = 0; + const DWORD byteMask = 0xBF; + const DWORD byteMark = 0x80; + const WCHAR* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) + { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) + { + DWORD ch2 = *source; + + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) + { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } + else if (flags == strictConversion) + { + /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + else + { + /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } + else if (flags == strictConversion) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) + { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + + /* Figure out how many bytes the result will require */ + if (ch < (DWORD)0x80) + { + bytesToWrite = 1; + } + else if (ch < (DWORD)0x800) + { + bytesToWrite = 2; + } + else if (ch < (DWORD)0x10000) + { + bytesToWrite = 3; + } + else if (ch < (DWORD)0x110000) + { + bytesToWrite = 4; + } + else + { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + + if ((target > targetEnd) && (!computeLength)) + { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + + if (!computeLength) + { + switch (bytesToWrite) + { + /* note: everything falls through. */ + case 4: + *--target = (BYTE)((ch | byteMark) & byteMask); + ch >>= 6; + + case 3: + *--target = (BYTE)((ch | byteMark) & byteMask); + ch >>= 6; + + case 2: + *--target = (BYTE)((ch | byteMark) & byteMask); + ch >>= 6; + + case 1: + *--target = (BYTE)(ch | firstByteMark[bytesToWrite]); + } + } + else + { + switch (bytesToWrite) + { + /* note: everything falls through. */ + case 4: + --target; + ch >>= 6; + + case 3: + --target; + ch >>= 6; + + case 2: + --target; + ch >>= 6; + + case 1: + --target; + } + } + + target += bytesToWrite; + } + + *sourceStart = source; + *targetStart = target; + return result; +} + + +static BOOL isLegalUTF8(const BYTE* source, int length) +{ + BYTE a; + const BYTE* srcptr = source + length; + + switch (length) + { + default: + return FALSE; + + /* Everything else falls through when "TRUE"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return FALSE; + + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return FALSE; + + case 2: + if ((a = (*--srcptr)) > 0xBF) return FALSE; + + switch (*source) + { + /* no fall-through in this inner switch */ + case 0xE0: + if (a < 0xA0) return FALSE; + + break; + + case 0xED: + if (a > 0x9F) return FALSE; + + break; + + case 0xF0: + if (a < 0x90) return FALSE; + + break; + + case 0xF4: + if (a > 0x8F) return FALSE; + + break; + + default: + if (a < 0x80) return FALSE; + } + + case 1: + if (*source >= 0x80 && *source < 0xC2) return FALSE; + } + + if (*source > 0xF4) + return FALSE; + + return TRUE; +} + +static ConversionResult _ConvertUTF8toUTF16(const BYTE** sourceStart, const BYTE* sourceEnd, WCHAR** targetStart, WCHAR* targetEnd, ConversionFlags flags) +{ + WCHAR* target; + const BYTE* source; + BOOL computeLength; + ConversionResult result; + computeLength = (!targetEnd) ? TRUE : FALSE; + result = conversionOK; + source = *sourceStart; + target = *targetStart; + + while (source < sourceEnd) + { + DWORD ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + + if ((source + extraBytesToRead) >= sourceEnd) + { + result = sourceExhausted; + break; + } + + /* Do this check whether lenient or strict */ + if (!isLegalUTF8(source, extraBytesToRead + 1)) + { + result = sourceIllegal; + break; + } + + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) + { + case 5: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + + case 4: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + + case 3: + ch += *source++; + ch <<= 6; + + case 2: + ch += *source++; + ch <<= 6; + + case 1: + ch += *source++; + ch <<= 6; + + case 0: + ch += *source++; + } + + ch -= offsetsFromUTF8[extraBytesToRead]; + + if ((target >= targetEnd) && (!computeLength)) + { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + + if (ch <= UNI_MAX_BMP) + { + /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + { + if (flags == strictConversion) + { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + else + { + if (!computeLength) + *target++ = UNI_REPLACEMENT_CHAR; + else + target++; + } + } + else + { + if (!computeLength) + *target++ = (WCHAR)ch; /* normal case */ + else + target++; + } + } + else if (ch > UNI_MAX_UTF16) + { + if (flags == strictConversion) + { + result = sourceIllegal; + source -= (extraBytesToRead + 1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } + else + { + if (!computeLength) + *target++ = UNI_REPLACEMENT_CHAR; + else + target++; + } + } + else + { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if ((target + 1 >= targetEnd) && (!computeLength)) + { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + + ch -= halfBase; + + if (!computeLength) + { + *target++ = (WCHAR)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (WCHAR)((ch & halfMask) + UNI_SUR_LOW_START); + } + else + { + target++; + target++; + } + } + } + + *sourceStart = source; + *targetStart = target; + return result; +} + +static int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar) +{ + int length; + LPWSTR targetStart; + const BYTE* sourceStart; + ConversionResult result; + + /* If cbMultiByte is 0, the function fails */ + + if (cbMultiByte == 0) + return 0; + + /* If cbMultiByte is -1, the string is null-terminated */ + + if (cbMultiByte == -1) + cbMultiByte = (int)strlen((char*)lpMultiByteStr) + 1; + + /* + * if cchWideChar is 0, the function returns the required buffer size + * in characters for lpWideCharStr and makes no use of the output parameter itself. + */ + + if (cchWideChar == 0) + { + sourceStart = (const BYTE*)lpMultiByteStr; + targetStart = (WCHAR*)NULL; + + result = _ConvertUTF8toUTF16(&sourceStart, &sourceStart[cbMultiByte], + &targetStart, NULL, strictConversion); + + length = (int)(targetStart - ((WCHAR*)NULL)); + cchWideChar = length; + } + else + { + sourceStart = (const BYTE*)lpMultiByteStr; + targetStart = lpWideCharStr; + + result = _ConvertUTF8toUTF16(&sourceStart, &sourceStart[cbMultiByte], + &targetStart, &targetStart[cchWideChar], strictConversion); + + length = (int)(targetStart - ((WCHAR*)lpWideCharStr)); + cchWideChar = length; + } + + return cchWideChar; +} + +#endif + + + +bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to) +{ + int iSize = MultiByteToWideChar(CP_UTF8, 0, from.c_str(), -1, NULL, 0); + if (iSize <= 0) + return false; + + //++iSize; + to.resize(iSize); + memset(&to[0], 0, sizeof(ex_utf16)); + + MultiByteToWideChar(CP_UTF8, 0, from.c_str(), -1, &to[0], iSize); + + return true; +} + +#endif diff --git a/common/libex/src/ex_thread.cpp b/common/libex/src/ex_thread.cpp index acdcdf8..9358a77 100644 --- a/common/libex/src/ex_thread.cpp +++ b/common/libex/src/ex_thread.cpp @@ -1,224 +1,224 @@ -#include -#include - -//========================================================= -// -//========================================================= - - -#ifdef EX_OS_WIN32 -unsigned int WINAPI ExThreadBase::_thread_func(LPVOID pParam) -#else - -void *ExThreadBase::_thread_func(void *pParam) -#endif -{ - ExThreadBase *_this = (ExThreadBase *) pParam; - - _this->m_is_running = true; - _this->_thread_loop(); - _this->m_is_running = false; - _this->m_handle = 0; - - EXLOGV(" # thread [%s] exit.\n", _this->m_thread_name.c_str()); - _this->_on_stopped(); - return 0; -} - -ExThreadBase::ExThreadBase(const char *thread_name) : - m_handle(0), - m_is_running(false), - m_need_stop(false) { - m_thread_name = thread_name; -} - -ExThreadBase::~ExThreadBase() { - if(m_is_running) { - EXLOGE(" # thread [%s] not stop before destroy.\n", m_thread_name.c_str()); - } -} - -bool ExThreadBase::start(void) { - m_need_stop = false; - EXLOGV(" . thread [%s] starting.\n", m_thread_name.c_str()); -#ifdef WIN32 - HANDLE h = (HANDLE)_beginthreadex(NULL, 0, _thread_func, (void*)this, 0, NULL); - - if (NULL == h) - { - return false; - } - m_handle = h; -#else - pthread_t ptid = 0; - int ret = pthread_create(&ptid, NULL, _thread_func, (void *) this); - if (ret != 0) { - return false; - } - m_handle = ptid; - -#endif - - return true; -} - -bool ExThreadBase::stop(void) { - if (m_handle == 0) { - EXLOGW("[thread] thread [%s] already stopped.\n", m_thread_name.c_str()); - return true; - } - - EXLOGV("[thread] try to stop thread [%s].\n", m_thread_name.c_str()); - m_need_stop = true; - _on_stop(); - - EXLOGV("[thread] wait thread [%s] exit.\n", m_thread_name.c_str()); - -#ifdef EX_OS_WIN32 - if (WaitForSingleObject(m_handle, INFINITE) != WAIT_OBJECT_0) - { - return false; - } -#else - if (pthread_join(m_handle, NULL) != 0) { - return false; - } -#endif - - return true; -} - -bool ExThreadBase::terminate(void) { -#ifdef EX_OS_WIN32 - return (TerminateThread(m_handle, 1) == TRUE); -#else - return (pthread_cancel(m_handle) == 0); -#endif -} - -//========================================================= -// -//========================================================= - -ExThreadManager::ExThreadManager() {} - -ExThreadManager::~ExThreadManager() { - if (!m_threads.empty()) { - EXLOGE("when destroy thread manager, there are %d thread not exit.\n", m_threads.size()); - stop_all(); - } -} - -void ExThreadManager::stop_all(void) { - ExThreadSmartLock locker(m_lock); - - ex_threads::iterator it = m_threads.begin(); - for (; it != m_threads.end(); ++it) { - (*it)->stop(); - } - m_threads.clear(); -} - -void ExThreadManager::add(ExThreadBase *tb) { - ExThreadSmartLock locker(m_lock); - - ex_threads::iterator it = m_threads.begin(); - for (; it != m_threads.end(); ++it) { - if ((*it) == tb) { - EXLOGE("when add thread to manager, it already exist.\n"); - return; - } - } - - m_threads.push_back(tb); -} - -void ExThreadManager::remove(ExThreadBase *tb) { - ExThreadSmartLock locker(m_lock); - - ex_threads::iterator it = m_threads.begin(); - for (; it != m_threads.end(); ++it) { - if ((*it) == tb) { - m_threads.erase(it); - return; - } - } - EXLOGE("thread not hold by thread-manager while remove it.\n"); -} - -//========================================================= -// -//========================================================= - -ExThreadLock::ExThreadLock() { -#ifdef EX_OS_WIN32 - InitializeCriticalSection(&m_locker); -#else - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m_locker, &attr); - pthread_mutexattr_destroy(&attr); -#endif -} - -ExThreadLock::~ExThreadLock() { -#ifdef EX_OS_WIN32 - DeleteCriticalSection(&m_locker); -#else - pthread_mutex_destroy(&m_locker); -#endif -} - -void ExThreadLock::lock(void) { -#ifdef EX_OS_WIN32 - EnterCriticalSection(&m_locker); -#else - pthread_mutex_lock(&m_locker); -#endif -} - -void ExThreadLock::unlock(void) { -#ifdef EX_OS_WIN32 - LeaveCriticalSection(&m_locker); -#else - pthread_mutex_unlock(&m_locker); -#endif -} - -//========================================================= -// -//========================================================= - -int ex_atomic_add(volatile int *pt, int t) { -#ifdef EX_OS_WIN32 - return (int)InterlockedExchangeAdd((long*)pt, (long)t); -#else - return __sync_add_and_fetch(pt, t); -#endif -} - -int ex_atomic_inc(volatile int *pt) { -#ifdef EX_OS_WIN32 - return (int)InterlockedIncrement((long*)pt); -#else - return __sync_add_and_fetch(pt, 1); -#endif -} - -int ex_atomic_dec(volatile int *pt) { -#ifdef EX_OS_WIN32 - return (int)InterlockedDecrement((long*)pt); -#else - return __sync_add_and_fetch(pt, -1); -#endif -} - - -ex_u64 ex_get_thread_id(void) { -#ifdef EX_OS_WIN32 - return GetCurrentThreadId(); -#else - return (ex_u64) pthread_self(); -#endif -} +#include +#include + +//========================================================= +// +//========================================================= + + +#ifdef EX_OS_WIN32 +unsigned int WINAPI ExThreadBase::_thread_func(LPVOID pParam) +#else + +void *ExThreadBase::_thread_func(void *pParam) +#endif +{ + ExThreadBase *_this = (ExThreadBase *) pParam; + + _this->m_is_running = true; + _this->_thread_loop(); + _this->m_is_running = false; + _this->m_handle = 0; + + EXLOGV(" # thread [%s] exit.\n", _this->m_thread_name.c_str()); + _this->_on_stopped(); + return 0; +} + +ExThreadBase::ExThreadBase(const char *thread_name) : + m_handle(0), + m_is_running(false), + m_need_stop(false) { + m_thread_name = thread_name; +} + +ExThreadBase::~ExThreadBase() { + if(m_is_running) { + EXLOGE(" # thread [%s] not stop before destroy.\n", m_thread_name.c_str()); + } +} + +bool ExThreadBase::start(void) { + m_need_stop = false; + EXLOGV(" . thread [%s] starting.\n", m_thread_name.c_str()); +#ifdef WIN32 + HANDLE h = (HANDLE)_beginthreadex(NULL, 0, _thread_func, (void*)this, 0, NULL); + + if (NULL == h) + { + return false; + } + m_handle = h; +#else + pthread_t ptid = 0; + int ret = pthread_create(&ptid, NULL, _thread_func, (void *) this); + if (ret != 0) { + return false; + } + m_handle = ptid; + +#endif + + return true; +} + +bool ExThreadBase::stop(void) { + if (m_handle == 0) { + EXLOGW("[thread] thread [%s] already stopped.\n", m_thread_name.c_str()); + return true; + } + + EXLOGV("[thread] try to stop thread [%s].\n", m_thread_name.c_str()); + m_need_stop = true; + _on_stop(); + + EXLOGV("[thread] wait thread [%s] exit.\n", m_thread_name.c_str()); + +#ifdef EX_OS_WIN32 + if (WaitForSingleObject(m_handle, INFINITE) != WAIT_OBJECT_0) + { + return false; + } +#else + if (pthread_join(m_handle, NULL) != 0) { + return false; + } +#endif + + return true; +} + +bool ExThreadBase::terminate(void) { +#ifdef EX_OS_WIN32 + return (TerminateThread(m_handle, 1) == TRUE); +#else + return (pthread_cancel(m_handle) == 0); +#endif +} + +//========================================================= +// +//========================================================= + +ExThreadManager::ExThreadManager() {} + +ExThreadManager::~ExThreadManager() { + if (!m_threads.empty()) { + EXLOGE("when destroy thread manager, there are %d thread not exit.\n", m_threads.size()); + stop_all(); + } +} + +void ExThreadManager::stop_all(void) { + ExThreadSmartLock locker(m_lock); + + ex_threads::iterator it = m_threads.begin(); + for (; it != m_threads.end(); ++it) { + (*it)->stop(); + } + m_threads.clear(); +} + +void ExThreadManager::add(ExThreadBase *tb) { + ExThreadSmartLock locker(m_lock); + + ex_threads::iterator it = m_threads.begin(); + for (; it != m_threads.end(); ++it) { + if ((*it) == tb) { + EXLOGE("when add thread to manager, it already exist.\n"); + return; + } + } + + m_threads.push_back(tb); +} + +void ExThreadManager::remove(ExThreadBase *tb) { + ExThreadSmartLock locker(m_lock); + + ex_threads::iterator it = m_threads.begin(); + for (; it != m_threads.end(); ++it) { + if ((*it) == tb) { + m_threads.erase(it); + return; + } + } + EXLOGE("thread not hold by thread-manager while remove it.\n"); +} + +//========================================================= +// +//========================================================= + +ExThreadLock::ExThreadLock() { +#ifdef EX_OS_WIN32 + InitializeCriticalSection(&m_locker); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_locker, &attr); + pthread_mutexattr_destroy(&attr); +#endif +} + +ExThreadLock::~ExThreadLock() { +#ifdef EX_OS_WIN32 + DeleteCriticalSection(&m_locker); +#else + pthread_mutex_destroy(&m_locker); +#endif +} + +void ExThreadLock::lock(void) { +#ifdef EX_OS_WIN32 + EnterCriticalSection(&m_locker); +#else + pthread_mutex_lock(&m_locker); +#endif +} + +void ExThreadLock::unlock(void) { +#ifdef EX_OS_WIN32 + LeaveCriticalSection(&m_locker); +#else + pthread_mutex_unlock(&m_locker); +#endif +} + +//========================================================= +// +//========================================================= + +int ex_atomic_add(volatile int *pt, int t) { +#ifdef EX_OS_WIN32 + return (int)InterlockedExchangeAdd((long*)pt, (long)t); +#else + return __sync_add_and_fetch(pt, t); +#endif +} + +int ex_atomic_inc(volatile int *pt) { +#ifdef EX_OS_WIN32 + return (int)InterlockedIncrement((long*)pt); +#else + return __sync_add_and_fetch(pt, 1); +#endif +} + +int ex_atomic_dec(volatile int *pt) { +#ifdef EX_OS_WIN32 + return (int)InterlockedDecrement((long*)pt); +#else + return __sync_add_and_fetch(pt, -1); +#endif +} + + +ex_u64 ex_get_thread_id(void) { +#ifdef EX_OS_WIN32 + return GetCurrentThreadId(); +#else + return (ex_u64) pthread_self(); +#endif +} diff --git a/common/libex/src/ex_util.cpp b/common/libex/src/ex_util.cpp index a84a001..c4daeba 100644 --- a/common/libex/src/ex_util.cpp +++ b/common/libex/src/ex_util.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/common/libex/src/ex_winsrv.cpp b/common/libex/src/ex_winsrv.cpp index 4c909f4..0b5087c 100644 --- a/common/libex/src/ex_winsrv.cpp +++ b/common/libex/src/ex_winsrv.cpp @@ -1,4 +1,4 @@ -#include +#include #ifdef EX_OS_WIN32 @@ -44,14 +44,14 @@ ex_rv ex_winsrv_install(const ex_wstr& srv_name, const ex_wstr& disp_name, const } SERVICE_FAILURE_ACTIONS failure_action; - failure_action.dwResetPeriod = 0; // reset failure count to zero ʱ䣬λΪ + failure_action.dwResetPeriod = 0; // reset failure count to zero 的时间,单位为秒 failure_action.lpRebootMsg = NULL; // Message to broadcast to server users before rebooting failure_action.lpCommand = NULL; // Command line of the process for the CreateProcess function to execute in response - failure_action.cActions = 3; // actionĸ + failure_action.cActions = 3; // action数组的个数 SC_ACTION actionarray[3]; - actionarray[0].Type = SC_ACTION_RESTART; // - actionarray[0].Delay = 60000; // λΪ + actionarray[0].Type = SC_ACTION_RESTART; // 重新启动服务 + actionarray[0].Delay = 60000; // 单位为毫秒 actionarray[1].Type = SC_ACTION_RESTART; actionarray[1].Delay = 60000; actionarray[2].Type = SC_ACTION_RESTART; diff --git a/common/teleport/teleport_const.h b/common/teleport/teleport_const.h index 8660e18..1f33d72 100644 --- a/common/teleport/teleport_const.h +++ b/common/teleport/teleport_const.h @@ -1,151 +1,151 @@ -#ifndef __TELEPORT_CONST_H__ -#define __TELEPORT_CONST_H__ - -// עͬͬԵconstļ - -// ļ趨teleportģ֮ͨѶʱĴֵJSONݣ -// - WEB -// - WEBWEB̨ -// - WEB̨COREķ - -//======================================================= -// Urlprotocol -//======================================================= -#define TP_URLPROTO_APP_NAME "teleport" - -//======================================================= -// Զ֤ʽ -//======================================================= -#define TP_AUTH_TYPE_NONE 0 -#define TP_AUTH_TYPE_PASSWORD 1 -#define TP_AUTH_TYPE_PRIVATE_KEY 2 - -//======================================================= -// ԶЭ -//======================================================= -#define TP_PROTOCOL_TYPE_RDP 1 -#define TP_PROTOCOL_TYPE_SSH 2 -#define TP_PROTOCOL_TYPE_TELNET 3 - -//======================================================= -// ԶЭ -//======================================================= -#define TP_PROTOCOL_TYPE_RDP_DESKTOP 100 -#define TP_PROTOCOL_TYPE_SSH_SHELL 200 -#define TP_PROTOCOL_TYPE_SSH_SFTP 201 -#define TP_PROTOCOL_TYPE_TELNET_SHELL 300 - - -//======================================================= -// Զϵͳ -//======================================================= -#define TP_OS_TYPE_WINDOWS 1 -#define TP_OS_TYPE_LINUX 2 - -//======================================================= -// ԶӻỰ״̬ -//======================================================= -#define TP_SESS_STAT_RUNNING 0 // Ựʼˣ -#define TP_SESS_STAT_END 9999 // Ựɹ -#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // ỰΪ֤ʧ -#define TP_SESS_STAT_ERR_CONNECT 2 // ỰΪ޷ӵԶ -#define TP_SESS_STAT_ERR_BAD_SSH_KEY 3 // ỰΪ޷ʶSSH˽Կ -#define TP_SESS_STAT_ERR_INTERNAL 4 // ỰΪڲ -#define TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL 5 // ỰΪЭ鲻֧(RDP) -#define TP_SESS_STAT_ERR_BAD_PKG 6 // ỰΪյı -#define TP_SESS_STAT_ERR_RESET 7 // ỰΪteleportķ -#define TP_SESS_STAT_ERR_IO 8 // ỰΪж -#define TP_SESS_STAT_ERR_SESSION 9 // ỰΪЧĻỰID -#define TP_SESS_STAT_ERR_AUTH_TYPE 10 // ỰΪ֤ʽ -#define TP_SESS_STAT_STARTED 100 // Ѿӳɹˣʼ¼¼ -#define TP_SESS_STAT_ERR_START_INTERNAL 104 // ỰΪڲ -#define TP_SESS_STAT_ERR_START_BAD_PKG 106 // ỰΪյı -#define TP_SESS_STAT_ERR_START_RESET 107 // ỰΪteleportķ -#define TP_SESS_STAT_ERR_START_IO 108 // ỰΪж - - -//======================================================= -// Ȩ -//======================================================= -#define TP_FLAG_ALL 0xFFFFFFFF -// Ự¼ -#define TP_FLAG_RECORD_REPLAY 0x00000001 // ¼ʷ¼طţ -#define TP_FLAG_RECORD_REAL_TIME 0x00000002 // ʵʱ -// RDP -#define TP_FLAG_RDP_DESKTOP 0x00000001 // Զ -#define TP_FLAG_RDP_CLIPBOARD 0x00000002 // -#define TP_FLAG_RDP_DISK 0x00000004 // ӳ -#define TP_FLAG_RDP_APP 0x00000008 // ԶAPPδʵ֣ -#define TP_FLAG_RDP_CONSOLE 0x00001000 //ӵԱỰRDPconsoleѡ -// SSH -#define TP_FLAG_SSH_SHELL 0x00000001 // SHELL -#define TP_FLAG_SSH_SFTP 0x00000002 // SFTP -#define TP_FLAG_SSH_X11 0x00000004 // X11תδʵ֣ -#define TP_FLAG_SSH_EXEC 0x00000008 // execִԶδʵ֣ -#define TP_FLAG_SSH_TUNNEL 0x00000010 // allow ssh tunnel. (not impl.) - - -//======================================================= -// ֵ -//======================================================= -#define TPE_OK 0 // ɹ -//------------------------------------------------------- -// ͨôֵ -//------------------------------------------------------- -#define TPE_NEED_MORE_DATA 1 // ҪݣһǴ -#define TPE_NEED_LOGIN 2 // Ҫ¼ -#define TPE_PRIVILEGE 3 // ûвȨ -#define TPE_NOT_IMPLEMENT 7 // δʵ -#define TPE_EXISTS 8 // ĿѾ -#define TPE_NOT_EXISTS 9 // Ŀ겻 - -// 100~299ͨôֵ - -#define TPE_FAILED 100 // ڲ -#define TPE_NETWORK 101 // -#define TPE_DATABASE 102 // ݿʧ - -// HTTPش -#define TPE_HTTP_METHOD 120 // Ч󷽷GET/POSTȣߴ󷽷ҪPOSTȴʹGETʽ -#define TPE_HTTP_URL_ENCODE 121 // URL޷룩 -//#define TPE_HTTP_URI 122 // ЧURI - -#define TPE_UNKNOWN_CMD 124 // δ֪ -#define TPE_JSON_FORMAT 125 // JSONʽҪJSONʽݣȴ޷JSONʽ룩 -#define TPE_PARAM 126 // -#define TPE_DATA 127 // ݴ - -// #define TPE_OPENFILE_ERROR 0x1007 // ޷ļ -// #define TPE_GETTEMPPATH_ERROR 0x1007 -#define TPE_OPENFILE 300 - - -//------------------------------------------------------- -// WEBרôֵ -//------------------------------------------------------- -#define TPE_CAPTCHA_EXPIRED 10000 // ֤ѹ -#define TPE_CAPTCHA_MISMATCH 10001 // ֤ -#define TPE_OATH_MISMATCH 10002 // ֤̬֤ -#define TPE_SYS_MAINTENANCE 10003 // ϵͳά - -#define TPE_USER_LOCKED 10100 // ûѾδ룩 -#define TPE_USER_DISABLED 10101 // ûѾ -#define TPE_USER_AUTH 10102 // ֤ʧ - -//------------------------------------------------------- -// ֳרôֵ -//------------------------------------------------------- -#define TPE_NO_ASSIST 100000 // δܼ⵽ֳ -#define TPE_OLD_ASSIST 100001 // ֳ汾̫ -#define TPE_START_CLIENT 100002 // ޷ͻ˳޷̣ - - - -//------------------------------------------------------- -// ķרôֵ -//------------------------------------------------------- -#define TPE_NO_CORE_SERVER 200000 // δܼ⵽ķ - - - -#endif // __TELEPORT_CONST_H__ +#ifndef __TELEPORT_CONST_H__ +#define __TELEPORT_CONST_H__ + +// 注意同步更新三个不同语言的const文件 + +// 本文件设定teleport各个模块之间通讯时的错误值(JSON数据),包括: +// - WEB界面与助手 +// - WEB界面与WEB后台 +// - WEB后台与CORE核心服务 + +//======================================================= +// Urlprotocol相关 +//======================================================= +#define TP_URLPROTO_APP_NAME "teleport" + +//======================================================= +// 远程连接认证方式 +//======================================================= +#define TP_AUTH_TYPE_NONE 0 +#define TP_AUTH_TYPE_PASSWORD 1 +#define TP_AUTH_TYPE_PRIVATE_KEY 2 + +//======================================================= +// 远程连接协议 +//======================================================= +#define TP_PROTOCOL_TYPE_RDP 1 +#define TP_PROTOCOL_TYPE_SSH 2 +#define TP_PROTOCOL_TYPE_TELNET 3 + +//======================================================= +// 远程连接子协议 +//======================================================= +#define TP_PROTOCOL_TYPE_RDP_DESKTOP 100 +#define TP_PROTOCOL_TYPE_SSH_SHELL 200 +#define TP_PROTOCOL_TYPE_SSH_SFTP 201 +#define TP_PROTOCOL_TYPE_TELNET_SHELL 300 + + +//======================================================= +// 远程主机操作系统 +//======================================================= +#define TP_OS_TYPE_WINDOWS 1 +#define TP_OS_TYPE_LINUX 2 + +//======================================================= +// 远程连接会话状态 +//======================================================= +#define TP_SESS_STAT_RUNNING 0 // 会话开始了,正在连接 +#define TP_SESS_STAT_END 9999 // 会话成功结束 +#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // 会话结束,因为认证失败 +#define TP_SESS_STAT_ERR_CONNECT 2 // 会话结束,因为无法连接到远程主机 +#define TP_SESS_STAT_ERR_BAD_SSH_KEY 3 // 会话结束,因为无法识别SSH私钥 +#define TP_SESS_STAT_ERR_INTERNAL 4 // 会话结束,因为内部错误 +#define TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL 5 // 会话结束,因为协议不支持(RDP) +#define TP_SESS_STAT_ERR_BAD_PKG 6 // 会话结束,因为收到错误的报文 +#define TP_SESS_STAT_ERR_RESET 7 // 会话结束,因为teleport核心服务重置了 +#define TP_SESS_STAT_ERR_IO 8 // 会话结束,因为网络中断 +#define TP_SESS_STAT_ERR_SESSION 9 // 会话结束,因为无效的会话ID +#define TP_SESS_STAT_ERR_AUTH_TYPE 10 // 会话结束,因为不被允许的认证方式 +#define TP_SESS_STAT_STARTED 100 // 已经连接成功了,开始记录录像了 +#define TP_SESS_STAT_ERR_START_INTERNAL 104 // 会话结束,因为内部错误 +#define TP_SESS_STAT_ERR_START_BAD_PKG 106 // 会话结束,因为收到错误的报文 +#define TP_SESS_STAT_ERR_START_RESET 107 // 会话结束,因为teleport核心服务重置了 +#define TP_SESS_STAT_ERR_START_IO 108 // 会话结束,因为网络中断 + + +//======================================================= +// 授权标记 +//======================================================= +#define TP_FLAG_ALL 0xFFFFFFFF +// 会话记录相关 +#define TP_FLAG_RECORD_REPLAY 0x00000001 // 允许记录历史(录像回放) +#define TP_FLAG_RECORD_REAL_TIME 0x00000002 // 允许实时监控 +// RDP相关 +#define TP_FLAG_RDP_DESKTOP 0x00000001 // 允许远程桌面 +#define TP_FLAG_RDP_CLIPBOARD 0x00000002 // 允许剪贴板 +#define TP_FLAG_RDP_DISK 0x00000004 // 允许磁盘映射 +#define TP_FLAG_RDP_APP 0x00000008 // 允许远程APP(尚未实现) +#define TP_FLAG_RDP_CONSOLE 0x00001000 //允许连接到管理员会话(RDP的console选项) +// SSH相关 +#define TP_FLAG_SSH_SHELL 0x00000001 // 允许SHELL +#define TP_FLAG_SSH_SFTP 0x00000002 // 允许SFTP +#define TP_FLAG_SSH_X11 0x00000004 // 允许X11转发(尚未实现) +#define TP_FLAG_SSH_EXEC 0x00000008 // 允许exec执行远程命令(尚未实现) +#define TP_FLAG_SSH_TUNNEL 0x00000010 // allow ssh tunnel. (not impl.) + + +//======================================================= +// 错误值 +//======================================================= +#define TPE_OK 0 // 成功 +//------------------------------------------------------- +// 通用错误值 +//------------------------------------------------------- +#define TPE_NEED_MORE_DATA 1 // 需要更多数据(不一定是错误) +#define TPE_NEED_LOGIN 2 // 需要登录 +#define TPE_PRIVILEGE 3 // 没有操作权限 +#define TPE_NOT_IMPLEMENT 7 // 功能尚未实现 +#define TPE_EXISTS 8 // 目标已经存在 +#define TPE_NOT_EXISTS 9 // 目标不存在 + +// 100~299是通用错误值 + +#define TPE_FAILED 100 // 内部错误 +#define TPE_NETWORK 101 // 网络错误 +#define TPE_DATABASE 102 // 数据库操作失败 + +// HTTP请求相关错误 +#define TPE_HTTP_METHOD 120 // 无效的请求方法(不是GET/POST等),或者错误的请求方法(例如需要POST,却使用GET方式请求) +#define TPE_HTTP_URL_ENCODE 121 // URL编码错误(无法解码) +//#define TPE_HTTP_URI 122 // 无效的URI + +#define TPE_UNKNOWN_CMD 124 // 未知的命令 +#define TPE_JSON_FORMAT 125 // 错误的JSON格式(需要JSON格式数据,但是却无法按JSON格式解码) +#define TPE_PARAM 126 // 参数错误 +#define TPE_DATA 127 // 数据错误 + +// #define TPE_OPENFILE_ERROR 0x1007 // 无法打开文件 +// #define TPE_GETTEMPPATH_ERROR 0x1007 +#define TPE_OPENFILE 300 + + +//------------------------------------------------------- +// WEB服务专用错误值 +//------------------------------------------------------- +#define TPE_CAPTCHA_EXPIRED 10000 // 验证码已过期 +#define TPE_CAPTCHA_MISMATCH 10001 // 验证码错误 +#define TPE_OATH_MISMATCH 10002 // 身份验证器动态验证码错误 +#define TPE_SYS_MAINTENANCE 10003 // 系统维护中 + +#define TPE_USER_LOCKED 10100 // 用户已经被锁定(连续多次错误密码) +#define TPE_USER_DISABLED 10101 // 用户已经被禁用 +#define TPE_USER_AUTH 10102 // 身份验证失败 + +//------------------------------------------------------- +// 助手程序专用错误值 +//------------------------------------------------------- +#define TPE_NO_ASSIST 100000 // 未能检测到助手程序 +#define TPE_OLD_ASSIST 100001 // 助手程序版本太低 +#define TPE_START_CLIENT 100002 // 无法启动客户端程序(无法创建进程) + + + +//------------------------------------------------------- +// 核心服务专用错误值 +//------------------------------------------------------- +#define TPE_NO_CORE_SERVER 200000 // 未能检测到核心服务 + + + +#endif // __TELEPORT_CONST_H__ diff --git a/config.ini.in b/config.ini.in index fbc4f5e..27cb9fa 100644 --- a/config.ini.in +++ b/config.ini.in @@ -22,6 +22,8 @@ wget = C:\Program Files (x86)\wget\wget.exe # if not set msbuild path, default to get it by register. #msbuild = C:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe +# need qt to build tp-player. +qt = C:\Qt\Qt5.12.0\5.12.0\msvc2017 # ============================================ # for Linux and macOS diff --git a/dist/client/windows/assist/installer.nsi b/dist/client/windows/assist/installer.nsi index 91f46d4..f6fc853 100644 Binary files a/dist/client/windows/assist/installer.nsi and b/dist/client/windows/assist/installer.nsi differ diff --git a/external/version.ini b/external/version.ini index 61456f5..481d0a0 100644 --- a/external/version.ini +++ b/external/version.ini @@ -1,8 +1,16 @@ [external_ver] -openssl = 1.0.2p,1000210f -libuv = 1.28.0 -mbedtls = 2.12.0 -libssh = 0.9.0 -jsoncpp = 0.10.6 -mongoose = 6.12 - +; https://github.com/openssl/openssl/releases +; http://slproweb.com/download/Win32OpenSSL-1_1_1d.exe +openssl = 1.1.1d,1010104f +; https://github.com/libuv/libuv/releases +libuv = 1.33.1 +; https://github.com/ARMmbed/mbedtls/releases +mbedtls = 2.16.3 +; https://github.com/open-source-parsers/jsoncpp/releases +jsoncpp = 1.9.2 +; https://github.com/cesanta/mongoose/releases +mongoose = 6.16 +; https://www.zlib.net/zlib1211.zip +zlib = 1.2.11,1211 +; https://git.libssh.org/projects/libssh.git/ +libssh = 0.9.2 diff --git a/resource/icon-tp-player.psd b/resource/icon-tp-player.psd new file mode 100644 index 0000000..3ae9008 Binary files /dev/null and b/resource/icon-tp-player.psd differ diff --git a/server/tp_core/common/base_env.cpp b/server/tp_core/common/base_env.cpp index 0975cfd..34f8c07 100644 --- a/server/tp_core/common/base_env.cpp +++ b/server/tp_core/common/base_env.cpp @@ -1,48 +1,48 @@ -#include "base_env.h" - -TppEnvBase::TppEnvBase() -{} - -TppEnvBase::~TppEnvBase() -{} - -bool TppEnvBase::init(TPP_INIT_ARGS* args) -{ - if (NULL == args) - { - EXLOGE("invalid init args(1).\n"); - return false; - } - - EXLOG_USE_LOGGER(args->logger); - - exec_path = args->exec_path; - etc_path = args->etc_path; - replay_path = args->replay_path; - - get_connect_info = args->func_get_connect_info; - free_connect_info = args->func_free_connect_info; - session_begin = args->func_session_begin; - session_update = args->func_session_update; - session_end = args->func_session_end; - - if (NULL == get_connect_info || NULL == free_connect_info || NULL == session_begin || NULL == session_update || NULL == session_end) - { - EXLOGE("invalid init args(2).\n"); - return false; - } - - if (NULL == args->cfg) - { - EXLOGE("invalid init args(3).\n"); - return false; - } - - if (!_on_init(args)) - { - EXLOGE("invalid init args(4).\n"); - return false; - } - - return true; -} +#include "base_env.h" + +TppEnvBase::TppEnvBase() +{} + +TppEnvBase::~TppEnvBase() +{} + +bool TppEnvBase::init(TPP_INIT_ARGS* args) +{ + if (NULL == args) + { + EXLOGE("invalid init args(1).\n"); + return false; + } + + EXLOG_USE_LOGGER(args->logger); + + exec_path = args->exec_path; + etc_path = args->etc_path; + replay_path = args->replay_path; + + get_connect_info = args->func_get_connect_info; + free_connect_info = args->func_free_connect_info; + session_begin = args->func_session_begin; + session_update = args->func_session_update; + session_end = args->func_session_end; + + if (NULL == get_connect_info || NULL == free_connect_info || NULL == session_begin || NULL == session_update || NULL == session_end) + { + EXLOGE("invalid init args(2).\n"); + return false; + } + + if (NULL == args->cfg) + { + EXLOGE("invalid init args(3).\n"); + return false; + } + + if (!_on_init(args)) + { + EXLOGE("invalid init args(4).\n"); + return false; + } + + return true; +} diff --git a/server/tp_core/common/base_env.h b/server/tp_core/common/base_env.h index c57fd41..316216d 100644 --- a/server/tp_core/common/base_env.h +++ b/server/tp_core/common/base_env.h @@ -1,29 +1,29 @@ -#ifndef __TS_BASE_ENV_H__ -#define __TS_BASE_ENV_H__ - -#include "protocol_interface.h" - -class TppEnvBase -{ -public: - TppEnvBase(); - virtual ~TppEnvBase(); - - bool init(TPP_INIT_ARGS* args); - -public: - ex_wstr exec_path; - ex_wstr etc_path; // ļSSH˽ԿļĴ· - ex_wstr replay_path; - - TPP_GET_CONNNECT_INFO_FUNC get_connect_info; - TPP_FREE_CONNECT_INFO_FUNC free_connect_info; - TPP_SESSION_BEGIN_FUNC session_begin; - TPP_SESSION_UPDATE_FUNC session_update; - TPP_SESSION_END_FUNC session_end; - -protected: - virtual bool _on_init(TPP_INIT_ARGS* args) = 0; -}; - -#endif // __TS_BASE_ENV_H__ +#ifndef __TS_BASE_ENV_H__ +#define __TS_BASE_ENV_H__ + +#include "protocol_interface.h" + +class TppEnvBase +{ +public: + TppEnvBase(); + virtual ~TppEnvBase(); + + bool init(TPP_INIT_ARGS* args); + +public: + ex_wstr exec_path; + ex_wstr etc_path; // 配置文件、SSH服务器的私钥文件的存放路径 + ex_wstr replay_path; + + TPP_GET_CONNNECT_INFO_FUNC get_connect_info; + TPP_FREE_CONNECT_INFO_FUNC free_connect_info; + TPP_SESSION_BEGIN_FUNC session_begin; + TPP_SESSION_UPDATE_FUNC session_update; + TPP_SESSION_END_FUNC session_end; + +protected: + virtual bool _on_init(TPP_INIT_ARGS* args) = 0; +}; + +#endif // __TS_BASE_ENV_H__ diff --git a/server/tp_core/common/base_record.cpp b/server/tp_core/common/base_record.cpp index 10849db..ae0d19f 100644 --- a/server/tp_core/common/base_record.cpp +++ b/server/tp_core/common/base_record.cpp @@ -1,4 +1,4 @@ -#include +#include #include "base_record.h" diff --git a/server/tp_core/common/base_record.h b/server/tp_core/common/base_record.h index cdcabb3..f4ac222 100644 --- a/server/tp_core/common/base_record.h +++ b/server/tp_core/common/base_record.h @@ -1,101 +1,109 @@ -#ifndef __TS_BASE_RECORD_H__ -#define __TS_BASE_RECORD_H__ - -#include "base_env.h" -#include "ts_membuf.h" -#include "protocol_interface.h" - -#include - -#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4 - -#pragma pack(push,1) - -/* - * ¼ - * - * һ¼ΪļһϢļһļ - * ڲ4M5룬ͽдļУͬʱϢļ - * - */ - - -// ¼ļͷ(¼д룬ıIJ) -typedef struct TS_RECORD_HEADER_INFO -{ - ex_u32 magic; // "TPPR" ־ TelePort Protocol Record - ex_u16 ver; // ¼ļ汾ĿǰΪ3 - ex_u32 packages; // ܰ - ex_u32 time_ms; // ܺʱ룩 - //ex_u32 file_size; // ļС -}TS_RECORD_HEADER_INFO; -#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO) - -// ¼ļͷ(̶䲿) -typedef struct TS_RECORD_HEADER_BASIC -{ - ex_u16 protocol_type; // Э飺1=RDP, 2=SSH, 3=Telnet - ex_u16 protocol_sub_type; // Э飺100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet - ex_u64 timestamp; // ¼ʼʱ䣨UTCʱ - ex_u16 width; // ʼĻߴ磺 - ex_u16 height; // ʼĻߴ磺 - char user_username[64]; // teleport˺ - char acc_username[64]; // Զû - - char host_ip[40]; // ԶIP - char conn_ip[40]; // ԶIP - ex_u16 conn_port; // Զ˿ - - char client_ip[40]; // ͻIP - - // RDPר - ex_u8 rdp_security; // 0 = RDP, 1 = TLS - - ex_u8 _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size]; -}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; - TS_RECORD_HEADER_BASIC basic; -}TS_RECORD_HEADER; - -// header֣header-info + header-basic = 512B -#define ts_record_header_size sizeof(TS_RECORD_HEADER) - - -// һݰͷ -typedef struct TS_RECORD_PKG -{ - ex_u8 type; // - ex_u32 size; // ܴСͷ - ex_u32 time_ms; // ʼʱʱ룬ζһӲܳ49죩 - ex_u8 _reserve[3]; // -}TS_RECORD_PKG; - -#pragma pack(pop) - -class TppRecBase -{ -public: - TppRecBase(); - virtual ~TppRecBase(); - - bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info); - bool end(); - -protected: - virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0; - virtual bool _on_end() = 0; - -protected: - ex_wstr m_base_path; // ¼ļ· /usr/local/teleport/data/replay/ssh/123ֱڲӵģΪλỰ¼ļĿ¼ - ex_wstr m_base_fname; // ¼ļļչ֣ڲԴΪϳļȫ¼ļ m_base_path ָĿ¼ - - ex_u64 m_start_time; - - MemBuffer m_cache; -}; - -#endif // __TS_BASE_RECORD_H__ +#ifndef __TS_BASE_RECORD_H__ +#define __TS_BASE_RECORD_H__ + +#include "base_env.h" +#include "ts_membuf.h" +#include "protocol_interface.h" + +#include + +#define MAX_CACHE_SIZE 1048576 // 1M = 1024*1024*1 +#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4 +// for test. +// #define MAX_CACHE_SIZE 524288 // 512KB = 512*1024 +// #define MAX_SIZE_PER_FILE 1048576 // 1M = 1024*1024*1 + +#pragma pack(push,1) + +/* + * 录像 + * + * 一个录像分为多个文件: + * *.tpr,录像信息文件,一个,固定大小(512字节) + * *.tpd,数据文件,n个,例如 tp-rdp-1.tpd,tp-rdp-2.tpd等等,每个数据文件约4MB + * *.tpk,关键帧信息文件,一个,仅RDP录像,记录各个关键帧数据所在的数据文件序号、偏移、时间点等信息。 + * *-cmd.txt,ssh命令记录文件,仅SSH。 + * 服务内部缓存最大4M,或者5秒,就将数据写入数据文件中,并同时更新信息文件。 + * + */ + +#define TS_TPPR_TYPE_UNKNOWN 0x0000 +#define TS_TPPR_TYPE_SSH 0x0001 +#define TS_TPPR_TYPE_RDP 0x0101 + + // 录像文件头(随着录像数据写入,会改变的部分) +typedef struct TS_RECORD_HEADER_INFO { + ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record + ex_u16 ver; // 录像文件版本,v3.5.0开始为4 + ex_u16 type; // 录像内容,SSH or RDP + // ex_u32 packages; // 总包数 + ex_u32 time_ms; // 总耗时(毫秒) + ex_u32 dat_file_count; // 数据文件数量 + ex_u8 _reserve[64-4-2-2-4-4]; +}TS_RECORD_HEADER_INFO; +#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO) + +// 录像文件头(固定不变部分) +typedef struct TS_RECORD_HEADER_BASIC { + ex_u16 protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet + ex_u16 protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet + ex_u64 timestamp; // 本次录像的起始时间(UTC时间戳) + ex_u16 width; // 初始屏幕尺寸:宽 + ex_u16 height; // 初始屏幕尺寸:高 + char user_username[64]; // teleport账号 + char acc_username[64]; // 远程主机用户名 + + char host_ip[40]; // 远程主机IP + char conn_ip[40]; // 远程主机IP + ex_u16 conn_port; // 远程主机端口 + + char client_ip[40]; // 客户端IP + +// // RDP专有 - v3.5.0废弃并移除 +// ex_u8 rdp_security; // 0 = RDP, 1 = TLS + + ex_u8 _reserve[512 - ts_record_header_info_size - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40]; +}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; + TS_RECORD_HEADER_BASIC basic; +}TS_RECORD_HEADER; + +// header部分(header-info + header-basic) = 512B +#define ts_record_header_size sizeof(TS_RECORD_HEADER) + +// 一个数据包的头 +typedef struct TS_RECORD_PKG { + ex_u8 type; // 包的数据类型 + ex_u8 _reserve[3]; // 保留 + ex_u32 size; // 这个包的总大小(不含包头) + ex_u32 time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天) + //ex_u32 index; // 这个包的序号(最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配) +}TS_RECORD_PKG; + +#pragma pack(pop) + +class TppRecBase { +public: + TppRecBase(); + virtual ~TppRecBase(); + + bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info); + bool end(); + +protected: + virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0; + virtual bool _on_end() = 0; + +protected: + ex_wstr m_base_path; // 录像文件基础路径,例如 /usr/local/teleport/data/replay/ssh/123,数字编号是内部附加的,作为本次会话录像文件的目录名称 + ex_wstr m_base_fname; // 录像文件的文件名,不含扩展名部分,内部会以此为基础合成文件全名,并将录像文件存放在 m_base_path 指向的目录中 + + ex_u64 m_start_time; + + MemBuffer m_cache; +}; + +#endif // __TS_BASE_RECORD_H__ diff --git a/server/tp_core/common/protocol_interface.h b/server/tp_core/common/protocol_interface.h index 50051c6..bc201ec 100644 --- a/server/tp_core/common/protocol_interface.h +++ b/server/tp_core/common/protocol_interface.h @@ -1,100 +1,100 @@ -#ifndef __TP_PROTOCOL_INTERFACE_H__ -#define __TP_PROTOCOL_INTERFACE_H__ - -#include "ts_const.h" -#include - -#ifdef EX_OS_WIN32 -# ifdef TPP_EXPORTS -# define TPP_API __declspec(dllexport) -# else -# define TPP_API __declspec(dllimport) -# endif -#else -# define TPP_API -#endif - -#define TPP_CMD_INIT 0x00000000 -#define TPP_CMD_SET_RUNTIME_CFG 0x00000005 -#define TPP_CMD_KILL_SESSIONS 0x00000006 - -typedef struct TPP_CONNECT_INFO -{ - char* sid; - - // ϢصҪصID - int user_id; - int host_id; - int acc_id; - - char* user_username; // 뱾ӵû - - char* host_ip; // ԶIPֱģʽremote_host_ipͬ - char* conn_ip; // ҪӵԶIPǶ˿ӳģʽΪ·IP - int conn_port; // ҪӵԶĶ˿ڣǶ˿ӳģʽΪ·Ķ˿ڣ - char* client_ip; - - char* acc_username; // Զ˺ - char* acc_secret; // Զ˺ŵ루˽Կ - char* username_prompt; // for telnet - char* password_prompt; // for telnet - - int protocol_type; - int protocol_sub_type; - int protocol_flag; - int record_flag; - int auth_type; -}TPP_CONNECT_INFO; - -typedef TPP_CONNECT_INFO* (*TPP_GET_CONNNECT_INFO_FUNC)(const char* sid); -typedef void(*TPP_FREE_CONNECT_INFO_FUNC)(TPP_CONNECT_INFO* info); -typedef bool(*TPP_SESSION_BEGIN_FUNC)(const TPP_CONNECT_INFO* info, int* db_id); -typedef bool(*TPP_SESSION_UPDATE_FUNC)(int db_id, int protocol_sub_type, int state); -typedef bool(*TPP_SESSION_END_FUNC)(const char* sid, int db_id, int ret); - - -typedef struct TPP_INIT_ARGS -{ - ExLogger* logger; - ex_wstr exec_path; - ex_wstr etc_path; - ex_wstr replay_path; - ExIniFile* cfg; - - TPP_GET_CONNNECT_INFO_FUNC func_get_connect_info; - TPP_FREE_CONNECT_INFO_FUNC func_free_connect_info; - TPP_SESSION_BEGIN_FUNC func_session_begin; - TPP_SESSION_UPDATE_FUNC func_session_update; - TPP_SESSION_END_FUNC func_session_end; -}TPP_INIT_ARGS; - -// typedef struct TPP_SET_CFG_ARGS { -// ex_u32 noop_timeout; // as second. -// }TPP_SET_CFG_ARGS; - -#ifdef __cplusplus -extern "C" -{ -#endif - - TPP_API ex_rv tpp_init(TPP_INIT_ARGS* init_args); - TPP_API ex_rv tpp_start(void); - TPP_API ex_rv tpp_stop(void); - TPP_API void tpp_timer(void); -// TPP_API void tpp_set_cfg(TPP_SET_CFG_ARGS* cfg_args); - - TPP_API ex_rv tpp_command(ex_u32 cmd, const char* param); - -#ifdef __cplusplus -} -#endif - -typedef ex_rv (*TPP_INIT_FUNC)(TPP_INIT_ARGS* init_args); -typedef ex_rv (*TPP_START_FUNC)(void); -typedef ex_rv(*TPP_STOP_FUNC)(void); -typedef void(*TPP_TIMER_FUNC)(void); -// typedef void(*TPP_SET_CFG_FUNC)(TPP_SET_CFG_ARGS* cfg_args); - -typedef ex_rv(*TPP_COMMAND_FUNC)(ex_u32 cmd, const char* param); // param is a JSON formatted string. - -#endif // __TP_PROTOCOL_INTERFACE_H__ +#ifndef __TP_PROTOCOL_INTERFACE_H__ +#define __TP_PROTOCOL_INTERFACE_H__ + +#include "ts_const.h" +#include + +#ifdef EX_OS_WIN32 +# ifdef TPP_EXPORTS +# define TPP_API __declspec(dllexport) +# else +# define TPP_API __declspec(dllimport) +# endif +#else +# define TPP_API +#endif + +#define TPP_CMD_INIT 0x00000000 +#define TPP_CMD_SET_RUNTIME_CFG 0x00000005 +#define TPP_CMD_KILL_SESSIONS 0x00000006 + +typedef struct TPP_CONNECT_INFO +{ + char* sid; + + // 与此连接信息相关的三个要素的ID + int user_id; + int host_id; + int acc_id; + + char* user_username; // 申请本次连接的用户名 + + char* host_ip; // 真正的远程主机IP(如果是直接连接模式,则与remote_host_ip相同) + char* conn_ip; // 要连接的远程主机的IP(如果是端口映射模式,则为路由主机的IP) + int conn_port; // 要连接的远程主机的端口(如果是端口映射模式,则为路由主机的端口) + char* client_ip; + + char* acc_username; // 远程主机的账号 + char* acc_secret; // 远程主机账号的密码(或者私钥) + char* username_prompt; // for telnet + char* password_prompt; // for telnet + + int protocol_type; + int protocol_sub_type; + int protocol_flag; + int record_flag; + int auth_type; +}TPP_CONNECT_INFO; + +typedef TPP_CONNECT_INFO* (*TPP_GET_CONNNECT_INFO_FUNC)(const char* sid); +typedef void(*TPP_FREE_CONNECT_INFO_FUNC)(TPP_CONNECT_INFO* info); +typedef bool(*TPP_SESSION_BEGIN_FUNC)(const TPP_CONNECT_INFO* info, int* db_id); +typedef bool(*TPP_SESSION_UPDATE_FUNC)(int db_id, int protocol_sub_type, int state); +typedef bool(*TPP_SESSION_END_FUNC)(const char* sid, int db_id, int ret); + + +typedef struct TPP_INIT_ARGS +{ + ExLogger* logger; + ex_wstr exec_path; + ex_wstr etc_path; + ex_wstr replay_path; + ExIniFile* cfg; + + TPP_GET_CONNNECT_INFO_FUNC func_get_connect_info; + TPP_FREE_CONNECT_INFO_FUNC func_free_connect_info; + TPP_SESSION_BEGIN_FUNC func_session_begin; + TPP_SESSION_UPDATE_FUNC func_session_update; + TPP_SESSION_END_FUNC func_session_end; +}TPP_INIT_ARGS; + +// typedef struct TPP_SET_CFG_ARGS { +// ex_u32 noop_timeout; // as second. +// }TPP_SET_CFG_ARGS; + +#ifdef __cplusplus +extern "C" +{ +#endif + + TPP_API ex_rv tpp_init(TPP_INIT_ARGS* init_args); + TPP_API ex_rv tpp_start(void); + TPP_API ex_rv tpp_stop(void); + TPP_API void tpp_timer(void); +// TPP_API void tpp_set_cfg(TPP_SET_CFG_ARGS* cfg_args); + + TPP_API ex_rv tpp_command(ex_u32 cmd, const char* param); + +#ifdef __cplusplus +} +#endif + +typedef ex_rv (*TPP_INIT_FUNC)(TPP_INIT_ARGS* init_args); +typedef ex_rv (*TPP_START_FUNC)(void); +typedef ex_rv(*TPP_STOP_FUNC)(void); +typedef void(*TPP_TIMER_FUNC)(void); +// typedef void(*TPP_SET_CFG_FUNC)(TPP_SET_CFG_ARGS* cfg_args); + +typedef ex_rv(*TPP_COMMAND_FUNC)(ex_u32 cmd, const char* param); // param is a JSON formatted string. + +#endif // __TP_PROTOCOL_INTERFACE_H__ diff --git a/server/tp_core/common/ts_const.h b/server/tp_core/common/ts_const.h index da1fc45..be55510 100644 --- a/server/tp_core/common/ts_const.h +++ b/server/tp_core/common/ts_const.h @@ -1,26 +1,26 @@ -#ifndef __TS_ERRNO_H__ -#define __TS_ERRNO_H__ - -//#include "ts_types.h" - -#define TS_RDP_PROXY_PORT 52089 -#define TS_RDP_PROXY_HOST "0.0.0.0" - -#define TS_SSH_PROXY_PORT 52189 -#define TS_SSH_PROXY_HOST "0.0.0.0" - -#define TS_TELNET_PROXY_PORT 52389 -#define TS_TELNET_PROXY_HOST "0.0.0.0" - -#define TS_HTTP_RPC_PORT 52080 -//#define TS_HTTP_RPC_HOST "127.0.0.1" -#define TS_HTTP_RPC_HOST "localhost" - - -#define TS_RDP_PROTOCOL_RDP 0 -#define TS_RDP_PROTOCOL_TLS 1 -#define TS_RDP_PROTOCOL_HYBRID 2 -#define TS_RDP_PROTOCOL_RDSTLS 4 -#define TS_RDP_PROTOCOL_HYBRID_EX 8 - -#endif // __TS_ERRNO_H__ +#ifndef __TS_ERRNO_H__ +#define __TS_ERRNO_H__ + +//#include "ts_types.h" + +#define TS_RDP_PROXY_PORT 52089 +#define TS_RDP_PROXY_HOST "0.0.0.0" + +#define TS_SSH_PROXY_PORT 52189 +#define TS_SSH_PROXY_HOST "0.0.0.0" + +#define TS_TELNET_PROXY_PORT 52389 +#define TS_TELNET_PROXY_HOST "0.0.0.0" + +#define TS_HTTP_RPC_PORT 52080 +//#define TS_HTTP_RPC_HOST "127.0.0.1" +#define TS_HTTP_RPC_HOST "localhost" + + +#define TS_RDP_PROTOCOL_RDP 0 +#define TS_RDP_PROTOCOL_TLS 1 +#define TS_RDP_PROTOCOL_HYBRID 2 +#define TS_RDP_PROTOCOL_RDSTLS 4 +#define TS_RDP_PROTOCOL_HYBRID_EX 8 + +#endif // __TS_ERRNO_H__ diff --git a/server/tp_core/common/ts_membuf.cpp b/server/tp_core/common/ts_membuf.cpp index c5f063f..f601f6a 100644 --- a/server/tp_core/common/ts_membuf.cpp +++ b/server/tp_core/common/ts_membuf.cpp @@ -1,4 +1,4 @@ -#include "ts_membuf.h" +#include "ts_membuf.h" #include MemBuffer::MemBuffer()// : m_buffer(NULL), m_data_size(0), m_buffer_size(0) @@ -45,7 +45,7 @@ void MemBuffer::reserve(size_t size) return; } - // µĻСȡ MEMBUF_BLOCK_SIZE + // 将新的缓冲区大小取整到 MEMBUF_BLOCK_SIZE 的整数倍 size_t new_size = (size + MEMBUF_BLOCK_SIZE - 1) & ~(MEMBUF_BLOCK_SIZE - 1); //EXLOGD("[mbuf:%p] reserve(2): m_buf: %p, m_buf_size: %d, size: %d, new size: %d.\n", this, m_buffer, m_buffer_size, size, new_size); diff --git a/server/tp_core/common/ts_membuf.h b/server/tp_core/common/ts_membuf.h index 0d7d283..95fa44c 100644 --- a/server/tp_core/common/ts_membuf.h +++ b/server/tp_core/common/ts_membuf.h @@ -1,4 +1,4 @@ -#ifndef __TS_MEMBUF_H__ +#ifndef __TS_MEMBUF_H__ #define __TS_MEMBUF_H__ #include @@ -11,15 +11,15 @@ public: MemBuffer(); virtual ~MemBuffer(); - // sizeֽڵݵĩβܻᵼ» + // 附加size字节的数据到缓冲区末尾(可能会导致缓冲区扩大) void append(const ex_u8* data, size_t size); - // Ϊָֽܻ󻺳С֤Чݲᱻı䣩 + // 缓冲区至少为指定字节数(可能会扩大缓冲区,但不会缩小缓冲区,保证有效数据不会被改变) void reserve(size_t size); - // mЧݸӵԼЧĩβܻ󻺳mݲ + // 将m的有效数据附加到自己的有效数据末尾,可能会扩大缓冲区,m内容不变 void concat(const MemBuffer& m); - // ӻͷƳsizeֽڣСܲʣµЧǰơ + // 从缓冲区头部移除size字节(缓冲区大小可能并不会收缩),剩下的有效数据前移。 void pop(size_t size); - // ջЧΪ0ֽڣ䣩 + // 清空缓冲区(有效数据为0字节,缓冲区不变) void empty(void) { m_data_size = 0; } bool is_empty(void) { return m_data_size == 0; } diff --git a/server/tp_core/common/ts_memstream.cpp b/server/tp_core/common/ts_memstream.cpp index e51c142..64388cb 100644 --- a/server/tp_core/common/ts_memstream.cpp +++ b/server/tp_core/common/ts_memstream.cpp @@ -1,222 +1,222 @@ -#include "ts_memstream.h" - -MemStream::MemStream(MemBuffer& mbuf) : m_mbuf(mbuf) -{ - m_offset = 0; -} - -MemStream::~MemStream() -{} - -void MemStream::reset(void) -{ - m_mbuf.empty(); - rewind(); -} - - -bool MemStream::seek(size_t offset) -{ - if (offset > m_mbuf.size()) - return false; - - m_offset = offset; - return true; -} - -bool MemStream::skip(size_t n) -{ - if (0 == n) - return true; - - if (m_offset + n > m_mbuf.size()) - return false; - m_offset += n; - return true; -} - -bool MemStream::rewind(size_t n) -{ - if (m_offset < n) - return false; - - if (0 == n) - m_offset = 0; - else - m_offset -= n; - return true; -} - -ex_u8 MemStream::get_u8(void) -{ - ASSERT(m_offset + 1 <= m_mbuf.size()); - - ex_u8 v = (m_mbuf.data() + m_offset)[0]; - m_offset++; - return v; -} - -ex_u16 MemStream::get_u16_le(void) -{ - ASSERT(m_offset + 2 <= m_mbuf.size()); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - ex_u16 v = (ex_u16)(p[0] | (p[1] << 8)); -#else - ex_u16 v = ((ex_u16*)p)[0]; -#endif - m_offset += 2; - return v; -} - -ex_u16 MemStream::get_u16_be(void) -{ - ASSERT(m_offset + 2 <= m_mbuf.size()); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - ex_u16 v = ((ex_u16*)p)[0]; -#else - ex_u16 v = (ex_u16)((p[0] << 8) | p[1]); -#endif - m_offset += 2; - return v; -} - - -ex_u32 MemStream::get_u32_le(void) -{ - ASSERT(m_offset + 4 <= m_mbuf.size()); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - ex_u32 v = (ex_u32)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); -#else - ex_u32 v = ((ex_u32*)p)[0]; -#endif - m_offset += 4; - return v; -} - -ex_u32 MemStream::get_u32_be(void) -{ - ASSERT(m_offset + 4 <= m_mbuf.size()); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - ex_u32 v = ((ex_u32*)p)[0]; -#else - ex_u32 v = (ex_u32)((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); -#endif - m_offset += 4; - return v; -} - -ex_u8* MemStream::get_bin(size_t n) -{ - ASSERT(m_offset + 4 <= m_mbuf.size()); - ex_u8* p = m_mbuf.data() + m_offset; - m_offset += n; - return p; -} - - -void MemStream::put_zero(size_t n) -{ - m_mbuf.reserve(m_mbuf.size() + n); - memset(m_mbuf.data() + m_offset, 0, n); - m_offset += n; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - -void MemStream::put_u8(ex_u8 v) -{ - m_mbuf.reserve(m_mbuf.size() + 1); - - (m_mbuf.data() + m_offset)[0] = v; - m_offset++; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - -void MemStream::put_u16_le(ex_u16 v) -{ - m_mbuf.reserve(m_mbuf.size() + 2); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - p[0] = (ex_u8)v; - p[1] = (ex_u8)(v >> 8); -#else - ((ex_u16*)p)[0] = v; -#endif - m_offset += 2; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - -void MemStream::put_u16_be(ex_u16 v) -{ - m_mbuf.reserve(m_mbuf.size() + 2); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - ((ex_u16*)p)[0] = v; -#else - ex_u8* _v = (ex_u8*)&v; - p[0] = _v[1]; - p[1] = _v[0]; -#endif - m_offset += 2; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - -void MemStream::put_u32_le(ex_u32 v) -{ - m_mbuf.reserve(m_mbuf.size() + 4); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - p[0] = (ex_u8)v; - p[1] = (ex_u8)(v >> 8); - p[2] = (ex_u8)(v >> 16); - p[3] = (ex_u8)(v >> 24); -#else - ((ex_u32*)p)[0] = v; -#endif - m_offset += 4; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - -void MemStream::put_u32_be(ex_u32 v) -{ - m_mbuf.reserve(m_mbuf.size() + 4); - - ex_u8* p = m_mbuf.data() + m_offset; -#if defined(B_ENDIAN) - ((ex_u32*)p)[0] = v; -#else - ex_u8* _v = (ex_u8*)&v; - p[0] = _v[3]; - p[1] = _v[2]; - p[2] = _v[1]; - p[3] = _v[0]; -#endif - m_offset += 4; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - -void MemStream::put_bin(const ex_u8* p, size_t n) -{ - m_mbuf.reserve(m_mbuf.size() + n); - memcpy(m_mbuf.data() + m_offset, p, n); - m_offset += n; - if (m_mbuf.size() < m_offset) - m_mbuf.size(m_offset); -} - +#include "ts_memstream.h" + +MemStream::MemStream(MemBuffer& mbuf) : m_mbuf(mbuf) +{ + m_offset = 0; +} + +MemStream::~MemStream() +{} + +void MemStream::reset(void) +{ + m_mbuf.empty(); + rewind(); +} + + +bool MemStream::seek(size_t offset) +{ + if (offset > m_mbuf.size()) + return false; + + m_offset = offset; + return true; +} + +bool MemStream::skip(size_t n) +{ + if (0 == n) + return true; + + if (m_offset + n > m_mbuf.size()) + return false; + m_offset += n; + return true; +} + +bool MemStream::rewind(size_t n) +{ + if (m_offset < n) + return false; + + if (0 == n) + m_offset = 0; + else + m_offset -= n; + return true; +} + +ex_u8 MemStream::get_u8(void) +{ + ASSERT(m_offset + 1 <= m_mbuf.size()); + + ex_u8 v = (m_mbuf.data() + m_offset)[0]; + m_offset++; + return v; +} + +ex_u16 MemStream::get_u16_le(void) +{ + ASSERT(m_offset + 2 <= m_mbuf.size()); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + ex_u16 v = (ex_u16)(p[0] | (p[1] << 8)); +#else + ex_u16 v = ((ex_u16*)p)[0]; +#endif + m_offset += 2; + return v; +} + +ex_u16 MemStream::get_u16_be(void) +{ + ASSERT(m_offset + 2 <= m_mbuf.size()); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + ex_u16 v = ((ex_u16*)p)[0]; +#else + ex_u16 v = (ex_u16)((p[0] << 8) | p[1]); +#endif + m_offset += 2; + return v; +} + + +ex_u32 MemStream::get_u32_le(void) +{ + ASSERT(m_offset + 4 <= m_mbuf.size()); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + ex_u32 v = (ex_u32)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); +#else + ex_u32 v = ((ex_u32*)p)[0]; +#endif + m_offset += 4; + return v; +} + +ex_u32 MemStream::get_u32_be(void) +{ + ASSERT(m_offset + 4 <= m_mbuf.size()); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + ex_u32 v = ((ex_u32*)p)[0]; +#else + ex_u32 v = (ex_u32)((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +#endif + m_offset += 4; + return v; +} + +ex_u8* MemStream::get_bin(size_t n) +{ + ASSERT(m_offset + 4 <= m_mbuf.size()); + ex_u8* p = m_mbuf.data() + m_offset; + m_offset += n; + return p; +} + + +void MemStream::put_zero(size_t n) +{ + m_mbuf.reserve(m_mbuf.size() + n); + memset(m_mbuf.data() + m_offset, 0, n); + m_offset += n; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + +void MemStream::put_u8(ex_u8 v) +{ + m_mbuf.reserve(m_mbuf.size() + 1); + + (m_mbuf.data() + m_offset)[0] = v; + m_offset++; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + +void MemStream::put_u16_le(ex_u16 v) +{ + m_mbuf.reserve(m_mbuf.size() + 2); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + p[0] = (ex_u8)v; + p[1] = (ex_u8)(v >> 8); +#else + ((ex_u16*)p)[0] = v; +#endif + m_offset += 2; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + +void MemStream::put_u16_be(ex_u16 v) +{ + m_mbuf.reserve(m_mbuf.size() + 2); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + ((ex_u16*)p)[0] = v; +#else + ex_u8* _v = (ex_u8*)&v; + p[0] = _v[1]; + p[1] = _v[0]; +#endif + m_offset += 2; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + +void MemStream::put_u32_le(ex_u32 v) +{ + m_mbuf.reserve(m_mbuf.size() + 4); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + p[0] = (ex_u8)v; + p[1] = (ex_u8)(v >> 8); + p[2] = (ex_u8)(v >> 16); + p[3] = (ex_u8)(v >> 24); +#else + ((ex_u32*)p)[0] = v; +#endif + m_offset += 4; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + +void MemStream::put_u32_be(ex_u32 v) +{ + m_mbuf.reserve(m_mbuf.size() + 4); + + ex_u8* p = m_mbuf.data() + m_offset; +#if defined(B_ENDIAN) + ((ex_u32*)p)[0] = v; +#else + ex_u8* _v = (ex_u8*)&v; + p[0] = _v[3]; + p[1] = _v[2]; + p[2] = _v[1]; + p[3] = _v[0]; +#endif + m_offset += 4; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + +void MemStream::put_bin(const ex_u8* p, size_t n) +{ + m_mbuf.reserve(m_mbuf.size() + n); + memcpy(m_mbuf.data() + m_offset, p, n); + m_offset += n; + if (m_mbuf.size() < m_offset) + m_mbuf.size(m_offset); +} + diff --git a/server/tp_core/common/ts_memstream.h b/server/tp_core/common/ts_memstream.h index 9ab425c..ddbb45f 100644 --- a/server/tp_core/common/ts_memstream.h +++ b/server/tp_core/common/ts_memstream.h @@ -1,45 +1,45 @@ -#ifndef __TS_MEMSTREAM_H__ -#define __TS_MEMSTREAM_H__ - -#include "ts_membuf.h" - -class MemStream -{ -public: - MemStream(MemBuffer& mbuf); - ~MemStream(); - - void reset(void); // ջݣͷڴ棩ָƶͷ - - bool seek(size_t offset); // ƶָ뵽ָƫƣԽ磬򷵻ش - bool rewind(size_t n = 0); // nֽڣԽ磬شnΪ0˵ʼ - bool skip(size_t n); // nֽڣԽ磬򷵻ش - - ex_u8* ptr(void) { return m_mbuf.data() + m_offset; } // صǰָ - size_t offset(void) { return m_offset; } // صǰָʼƫ - - size_t left(void) { return m_mbuf.size() - m_offset; } // ʣݵĴСӵǰָ뵽β - - ex_u8 get_u8(void); - ex_u16 get_u16_le(void); - ex_u16 get_u16_be(void); - ex_u32 get_u32_le(void); - ex_u32 get_u32_be(void); - ex_u8* get_bin(size_t n); // صǰָݵָ룬ڲƫƻƶnֽ - - void put_zero(size_t n); // nֽڵ0 - void put_u8(ex_u8 v); - void put_u16_le(ex_u16 v); - void put_u16_be(ex_u16 v); - void put_u32_le(ex_u32 v); - void put_u32_be(ex_u32 v); - void put_bin(const ex_u8* p, size_t n); // pָnֽ - - size_t size(void) { return m_mbuf.size(); } - -private: - MemBuffer& m_mbuf; - size_t m_offset; -}; - -#endif // __TS_MEMSTREAM_H__ +#ifndef __TS_MEMSTREAM_H__ +#define __TS_MEMSTREAM_H__ + +#include "ts_membuf.h" + +class MemStream +{ +public: + MemStream(MemBuffer& mbuf); + ~MemStream(); + + void reset(void); // 清空缓冲区数据(但不释放内存),指针移动到头部 + + bool seek(size_t offset); // 移动指针到指定偏移,如果越界,则返回错误 + bool rewind(size_t n = 0); // 回退n字节,如果越界,返回错误,如果n为0,则回退到最开始处 + bool skip(size_t n); // 跳过n字节,如果越界,则返回错误 + + ex_u8* ptr(void) { return m_mbuf.data() + m_offset; } // 返回当前数据指针 + size_t offset(void) { return m_offset; } // 返回当前指针相对数据起始的偏移 + + size_t left(void) { return m_mbuf.size() - m_offset; } // 返回剩余数据的大小(从当前数据指针到缓冲区结尾) + + ex_u8 get_u8(void); + ex_u16 get_u16_le(void); + ex_u16 get_u16_be(void); + ex_u32 get_u32_le(void); + ex_u32 get_u32_be(void); + ex_u8* get_bin(size_t n); // 返回当前指向的数据的指针,内部偏移会向后移动n字节 + + void put_zero(size_t n); // 填充n字节的0 + void put_u8(ex_u8 v); + void put_u16_le(ex_u16 v); + void put_u16_be(ex_u16 v); + void put_u32_le(ex_u32 v); + void put_u32_be(ex_u32 v); + void put_bin(const ex_u8* p, size_t n); // 填充p指向的n字节数据 + + size_t size(void) { return m_mbuf.size(); } + +private: + MemBuffer& m_mbuf; + size_t m_offset; +}; + +#endif // __TS_MEMSTREAM_H__ diff --git a/server/tp_core/core/tp_core.rc b/server/tp_core/core/tp_core.rc index 85a2b2f..0be0f40 100644 Binary files a/server/tp_core/core/tp_core.rc and b/server/tp_core/core/tp_core.rc differ diff --git a/server/tp_core/core/tp_core.vs2017.sln b/server/tp_core/core/tp_core.vs2017.sln index d059e1d..3c23a0b 100644 --- a/server/tp_core/core/tp_core.vs2017.sln +++ b/server/tp_core/core/tp_core.vs2017.sln @@ -19,4 +19,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {787A1953-2C25-4859-B81F-7F63A94B8EE3} + EndGlobalSection EndGlobal diff --git a/server/tp_core/core/tp_core.vs2017.vcxproj b/server/tp_core/core/tp_core.vs2017.vcxproj index 8f3236a..ee0e4d1 100644 --- a/server/tp_core/core/tp_core.vs2017.vcxproj +++ b/server/tp_core/core/tp_core.vs2017.vcxproj @@ -9,14 +9,6 @@ Release Win32 - - Debug - x64 - - - Release - x64 -
{6548CB1D-A7BA-4A68-9B3F-A5129F77868B} @@ -39,19 +31,6 @@ Unicode v141 - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - @@ -63,35 +42,19 @@ - - - - - - true ..\..\..\out\server\$(PlatformTarget)\$(Configuration)\ ..\..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ - C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) - C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) - - - true - ..\..\out\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ - ..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ + c:\apps\vld\include;$(IncludePath) + C:\apps\vld\lib\Win32;$(LibraryPath) false ..\..\..\out\server\$(PlatformTarget)\$(Configuration)\ ..\..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ - - false - ..\..\out\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ - ..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ - @@ -111,22 +74,6 @@ Debug - - - - - Level3 - Disabled - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - ../../external/windows/libuv/include;../../external/windows/openssl/include;../../external/windows/zlib/include;../../external/windows/mbedtls/include;../../external/windows/libssh/include;../../external/common/jsoncpp/include;../../external/common/sqlite;d:/apps/vld/include - - - Windows - true - ../../external/windows/openssl/lib;../../external/windows/zlib/lib;../../external/windows/libssh/lib - - Level3 @@ -148,26 +95,6 @@ - - - Level3 - - - MaxSpeed - true - true - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - ../../external/windows/libuv/include;../../external/windows/openssl/include;../../external/windows/zlib/include;../../external/windows/mbedtls/include;../../external/windows/libssh/include;../../external/common/jsoncpp/include;../../external/common/sqlite - - - Windows - true - true - true - ../../external/windows/openssl/lib;../../external/windows/zlib/lib;../../external/windows/libssh/lib - - diff --git a/server/tp_core/core/ts_http_rpc.cpp b/server/tp_core/core/ts_http_rpc.cpp index e8bd3f6..d9530ab 100644 --- a/server/tp_core/core/ts_http_rpc.cpp +++ b/server/tp_core/core/ts_http_rpc.cpp @@ -1,553 +1,570 @@ -#include "ts_http_rpc.h" -#include "ts_ver.h" -#include "ts_env.h" -#include "ts_session.h" -#include "ts_crypto.h" -#include "ts_web_rpc.h" -#include "tp_tpp_mgr.h" - -extern TppManager g_tpp_mgr; - -#include - - -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') -int ts_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) -{ - int i, j, a, b; - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) - { - if (src[i] == '%') - { - if (i < src_len - 2 && isxdigit(*(const unsigned char *)(src + i + 1)) && - isxdigit(*(const unsigned char *)(src + i + 2))) { - a = tolower(*(const unsigned char *)(src + i + 1)); - b = tolower(*(const unsigned char *)(src + i + 2)); - dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } - else - { - return -1; - } - } - else if (is_form_url_encoded && src[i] == '+') - { - dst[j] = ' '; - } - else - { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; /* Null-terminate the destination */ - - return i >= src_len ? j : -1; -} - -TsHttpRpc::TsHttpRpc() : - ExThreadBase("http-rpc-thread") -{ - mg_mgr_init(&m_mg_mgr, NULL); -} - -TsHttpRpc::~TsHttpRpc() -{ - mg_mgr_free(&m_mg_mgr); -} - -void TsHttpRpc::_thread_loop(void) -{ - EXLOGI("[core] TeleportServer-RPC ready on %s:%d\n", m_host_ip.c_str(), m_host_port); - - while (!m_need_stop) - { - mg_mgr_poll(&m_mg_mgr, 500); - } - - EXLOGV("[core] rpc main loop end.\n"); -} - - -bool TsHttpRpc::init(void) -{ - struct mg_connection* nc = NULL; - - m_host_ip = g_env.rpc_bind_ip; - m_host_port = g_env.rpc_bind_port; - - char addr[128] = { 0 }; - // if (0 == strcmp(m_host_ip.c_str(), "127.0.0.1") || 0 == strcmp(m_host_ip.c_str(), "localhost")) - // ex_strformat(addr, 128, ":%d", m_host_port); - // else - // ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); - if (0 == strcmp(m_host_ip.c_str(), "0.0.0.0")) - ex_strformat(addr, 128, ":%d", m_host_port); - else - ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); - - nc = mg_bind(&m_mg_mgr, addr, _mg_event_handler); - if (NULL == nc) - { - EXLOGE("[core] rpc listener failed to bind at %s.\n", addr); - return false; - } - - nc->user_data = this; - - mg_set_protocol_http_websocket(nc); - - // ڴй¶ĵطÿԼ1KBڴ棩 - // DO NOT USE MULTITHREADING OF MG. - // cpq (one of the authors of MG) commented on 3 Feb: Multithreading support has been removed. - // https://github.com/cesanta/mongoose/commit/707b9ed2d6f177b3ad8787cb16a1bff90ddad992 - //mg_enable_multithreading(nc); - - return true; -} - -void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_data) -{ - struct http_message *hm = (struct http_message*)ev_data; - - TsHttpRpc* _this = (TsHttpRpc*)nc->user_data; - if (NULL == _this) - { - EXLOGE("[core] rpc invalid http request.\n"); - return; - } - - switch (ev) - { - case MG_EV_HTTP_REQUEST: - { - ex_astr ret_buf; - - ex_astr uri; - uri.assign(hm->uri.p, hm->uri.len); - - //EXLOGD("[core] rpc got request: %s\n", uri.c_str()); - - if (uri == "/rpc") - { - ex_astr method; - Json::Value json_param; - - ex_rv rv = _this->_parse_request(hm, method, json_param); - if (TPE_OK != rv) - { - EXLOGE("[core] rpc got invalid request.\n"); - _this->_create_json_ret(ret_buf, rv); - } - else - { - EXLOGD("[core] rpc got request method `%s`\n", method.c_str()); - _this->_process_request(method, json_param, ret_buf); - } - } - else - { - EXLOGE("[core] rpc got invalid request: not `rpc` uri.\n"); - _this->_create_json_ret(ret_buf, TPE_PARAM, "not a `rpc` request."); - } - - mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: application/json\r\n\r\n%s", (int)ret_buf.size() - 1, &ret_buf[0]); - nc->flags |= MG_F_SEND_AND_CLOSE; - } - break; - default: - break; - } -} - -ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Json::Value& json_param) -{ - if (NULL == req) - return TPE_PARAM; - - bool is_get = true; - if (req->method.len == 3 && 0 == memcmp(req->method.p, "GET", req->method.len)) - is_get = true; - else if (req->method.len == 4 && 0 == memcmp(req->method.p, "POST", req->method.len)) - is_get = false; - else - return TPE_HTTP_METHOD; - - ex_astr json_str; - bool need_decode = false; - if (is_get) { - json_str.assign(req->query_string.p, req->query_string.len); - need_decode = true; - } - else { - json_str.assign(req->body.p, req->body.len); - if (json_str.length() > 0 && json_str[0] == '%') - need_decode = true; - } - - if (need_decode) { - // url-decode - int len = json_str.length() * 2; - ex_chars sztmp; - sztmp.resize(len); - memset(&sztmp[0], 0, len); - if (-1 == ts_url_decode(json_str.c_str(), json_str.length(), &sztmp[0], len, 0)) - return TPE_HTTP_URL_ENCODE; - - json_str = &sztmp[0]; - } - - if (0 == json_str.length()) - return TPE_PARAM; - - Json::Reader jreader; - - if (!jreader.parse(json_str.c_str(), json_param)) - return TPE_JSON_FORMAT; - - if (json_param.isArray()) - return TPE_PARAM; - - if (json_param["method"].isNull() || !json_param["method"].isString()) - return TPE_PARAM; - - func_cmd = json_param["method"].asCString(); - json_param = json_param["param"]; - - return TPE_OK; -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_data) -{ - // أ {"code":errcode, "data":{jr_data}} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - jr_root["data"] = jr_data; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) -{ - // أ {"code":errcode} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const char* message) -{ - // أ {"code":errcode, "message":message} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - jr_root["message"] = message; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf) -{ - if (func_cmd == "request_session") { - _rpc_func_request_session(json_param, buf); - } - else if (func_cmd == "kill_sessions") { - _rpc_func_kill_sessions(json_param, buf); - } - else if (func_cmd == "get_config") { - _rpc_func_get_config(json_param, buf); - } - else if (func_cmd == "set_config") { - _rpc_func_set_config(json_param, buf); - } - else if (func_cmd == "enc") { - _rpc_func_enc(json_param, buf); - } - else if (func_cmd == "exit") { - _rpc_func_exit(json_param, buf); - } - else { - EXLOGE("[core] rpc got unknown command: %s\n", func_cmd.c_str()); - _create_json_ret(buf, TPE_UNKNOWN_CMD); - } -} - -extern bool g_exit_flag; // ҪTS˳ı־ֹ̣ͣ߳ -void TsHttpRpc::_rpc_func_exit(const Json::Value& json_param, ex_astr& buf) -{ - // һȫ˳־ - g_exit_flag = true; - _create_json_ret(buf, TPE_OK); -} - -void TsHttpRpc::_rpc_func_get_config(const Json::Value& json_param, ex_astr& buf) -{ - Json::Value jr_data; - - ex_astr _replay_name; - ex_wstr2astr(g_env.m_replay_path, _replay_name); - jr_data["replay-path"] = _replay_name; - - jr_data["web-server-rpc"] = g_env.web_server_rpc; - - ex_astr _version; - ex_wstr2astr(TP_SERVER_VER, _version); - jr_data["version"] = _version; - - ExIniFile& ini = g_env.get_ini(); - ex_ini_sections& secs = ini.GetAllSections(); - ex_ini_sections::iterator it = secs.begin(); - for (; it != secs.end(); ++it) - { - if (it->first.length() > 9 && 0 == wcsncmp(it->first.c_str(), L"protocol-", 9)) - { - ex_wstr name; - name.assign(it->first, 9, it->first.length() - 9); - ex_astr _name; - ex_wstr2astr(name, _name); - - bool enabled = false; - it->second->GetBool(L"enabled", enabled, false); - - ex_wstr ip; - if (!it->second->GetStr(L"bind-ip", ip)) - continue; - ex_astr _ip; - ex_wstr2astr(ip, _ip); - - int port; - it->second->GetInt(L"bind-port", port, 52189); - - jr_data[_name.c_str()]["enable"] = enabled; - jr_data[_name.c_str()]["ip"] = _ip; - jr_data[_name.c_str()]["port"] = port; - } - } - - _create_json_ret(buf, TPE_OK, jr_data); -} - -void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#request_session - - int conn_id = 0; - ex_rv rv = TPE_OK; - - if (json_param["conn_id"].isNull()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - if (!json_param["conn_id"].isInt()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - conn_id = json_param["conn_id"].asInt(); - if (0 == conn_id) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - TS_CONNECT_INFO* info = new TS_CONNECT_INFO; - if ((rv = ts_web_rpc_get_conn_info(conn_id, *info)) != TPE_OK) - { - _create_json_ret(buf, rv); - return; - } - -// info->ref_count = 0; -// info->ticket_start = ex_get_tick_count(); -// - // һsession-idڲظ - ex_astr sid; - if (!g_session_mgr.request_session(sid, info)) { - _create_json_ret(buf, TPE_FAILED); - return; - } - - EXLOGD("[core] rpc new session-id: %s\n", sid.c_str()); - - Json::Value jr_data; - jr_data["sid"] = sid; - - _create_json_ret(buf, TPE_OK, jr_data); -} - -void TsHttpRpc::_rpc_func_kill_sessions(const Json::Value& json_param, ex_astr& buf) { - /* - { - "sessions": ["0123456", "ABCDEF", ...] - } - */ - - if (json_param.isArray()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - if (json_param["sessions"].isNull() || !json_param["sessions"].isArray()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - Json::Value s = json_param["sessions"]; - int cnt = s.size(); - for (int i = 0; i < cnt; ++i) { - if (!s[i].isString()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - } - - EXLOGV("[core] try to kill %d sessions.\n", cnt); - ex_astr sp = s.toStyledString(); - g_tpp_mgr.kill_sessions(sp); - - _create_json_ret(buf, TPE_OK); -} - -void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc - // һַ [ p=plain-text, c=cipher-text ] - // : {"p":"need be encrypt"} - // ʾ: {"p":"this-is-a-password"} - // p: ַܵ - // أ - // dataе"c"Ǽܺĵbase64 - // ʾ: {"code":0, "data":{"c":"Mxs340a9r3fs+3sdf=="}} - // 󷵻أ {"code":1234} - - if (json_param.isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_astr plain_text; - - if (json_param["p"].isNull() || !json_param["p"].isString()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - plain_text = json_param["p"].asCString(); - if (plain_text.length() == 0) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - ex_astr cipher_text; - - if (!ts_db_field_encrypt(plain_text, cipher_text)) - { - _create_json_ret(buf, TPE_FAILED); - return; - } - - Json::Value jr_data; - jr_data["c"] = cipher_text; - _create_json_ret(buf, TPE_OK, jr_data); -} - -void TsHttpRpc::_rpc_func_set_config(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#set_config - /* - { - "noop-timeout": 15 # Ӽ - } - */ - - if (json_param.isArray()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - if (json_param["noop_timeout"].isNull() || !json_param["noop_timeout"].isUInt()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - int noop_timeout = json_param["noop_timeout"].asUInt(); - EXLOGV("[core] set run-time config:\n"); - EXLOGV("[core] noop_timeout = %dm\n", noop_timeout); - - ex_astr sp = json_param.toStyledString(); - g_tpp_mgr.set_runtime_config(sp); - - _create_json_ret(buf, TPE_OK); -} - - -/* -void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc - // ַܶ [ p=plain-text, c=cipher-text ] - // : {"p":["need be encrypt", "plain to cipher"]} - // ʾ: {"p":["password-for-A"]} - // p: ַܵ - // أ - // dataе"c"Ǽܺĵbase64 - // ʾ: {"code":0, "data":{"c":["Mxs340a9r3fs+3sdf=="]}} - // 󷵻أ {"code":1234} - - if (json_param.isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_astr plain_text; - - if (json_param["p"].isNull() || !json_param["p"].isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - Json::Value c; - - Json::Value p = json_param["p"]; - int cnt = p.size(); - for (int i = 0; i < cnt; ++i) - { - if (!p[i].isString()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_astr p_txt = p[i].asCString(); - if (p_txt.length() == 0) { - c["c"].append(""); - } - - ex_astr c_txt; - if (!ts_db_field_encrypt(p_txt, c_txt)) - { - _create_json_ret(buf, TPE_FAILED); - return; - } - - c["c"].append(c_txt); - } - - Json::Value jr_data; - jr_data["c"] = c; - _create_json_ret(buf, TPE_OK, jr_data); -} -*/ +#include "ts_http_rpc.h" +#include "ts_ver.h" +#include "ts_env.h" +#include "ts_session.h" +#include "ts_crypto.h" +#include "ts_web_rpc.h" +#include "tp_tpp_mgr.h" + +extern TppManager g_tpp_mgr; + +#include + + +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') +int ts_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) +{ + int i, j, a, b; + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) + { + if (src[i] == '%') + { + if (i < src_len - 2 && isxdigit(*(const unsigned char *)(src + i + 1)) && + isxdigit(*(const unsigned char *)(src + i + 2))) { + a = tolower(*(const unsigned char *)(src + i + 1)); + b = tolower(*(const unsigned char *)(src + i + 2)); + dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } + else + { + return -1; + } + } + else if (is_form_url_encoded && src[i] == '+') + { + dst[j] = ' '; + } + else + { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +TsHttpRpc::TsHttpRpc() : + ExThreadBase("http-rpc-thread") +{ + mg_mgr_init(&m_mg_mgr, NULL); +} + +TsHttpRpc::~TsHttpRpc() +{ + mg_mgr_free(&m_mg_mgr); +} + +void TsHttpRpc::_thread_loop(void) +{ + EXLOGI("[core] TeleportServer-RPC ready on %s:%d\n", m_host_ip.c_str(), m_host_port); + + while (!m_need_stop) + { + mg_mgr_poll(&m_mg_mgr, 500); + } + + EXLOGV("[core] rpc main loop end.\n"); +} + + +bool TsHttpRpc::init(void) +{ + struct mg_connection* nc = NULL; + + m_host_ip = g_env.rpc_bind_ip; + m_host_port = g_env.rpc_bind_port; + + char addr[128] = { 0 }; + // if (0 == strcmp(m_host_ip.c_str(), "127.0.0.1") || 0 == strcmp(m_host_ip.c_str(), "localhost")) + // ex_strformat(addr, 128, ":%d", m_host_port); + // else + // ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); + if (0 == strcmp(m_host_ip.c_str(), "0.0.0.0")) + ex_strformat(addr, 128, ":%d", m_host_port); + else + ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); + + nc = mg_bind(&m_mg_mgr, addr, _mg_event_handler); + if (NULL == nc) + { + EXLOGE("[core] rpc listener failed to bind at %s.\n", addr); + return false; + } + + nc->user_data = this; + + mg_set_protocol_http_websocket(nc); + + // 导致内存泄露的地方(每次请求约消耗1KB内存) + // DO NOT USE MULTITHREADING OF MG. + // cpq (one of the authors of MG) commented on 3 Feb: Multithreading support has been removed. + // https://github.com/cesanta/mongoose/commit/707b9ed2d6f177b3ad8787cb16a1bff90ddad992 + //mg_enable_multithreading(nc); + + return true; +} + +void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_data) +{ + struct http_message *hm = (struct http_message*)ev_data; + + TsHttpRpc* _this = (TsHttpRpc*)nc->user_data; + if (NULL == _this) + { + EXLOGE("[core] rpc invalid http request.\n"); + return; + } + + switch (ev) + { + case MG_EV_HTTP_REQUEST: + { + ex_astr ret_buf; + + ex_astr uri; + uri.assign(hm->uri.p, hm->uri.len); + + //EXLOGD("[core] rpc got request: %s\n", uri.c_str()); + + if (uri == "/rpc") + { + ex_astr method; + Json::Value json_param; + + ex_rv rv = _this->_parse_request(hm, method, json_param); + if (TPE_OK != rv) + { + EXLOGE("[core] rpc got invalid request.\n"); + _this->_create_json_ret(ret_buf, rv); + } + else + { + EXLOGD("[core] rpc got request method `%s`\n", method.c_str()); + _this->_process_request(method, json_param, ret_buf); + } + } + else + { + EXLOGE("[core] rpc got invalid request: not `rpc` uri.\n"); + _this->_create_json_ret(ret_buf, TPE_PARAM, "not a `rpc` request."); + } + + mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: application/json\r\n\r\n%s", (int)ret_buf.length(), &ret_buf[0]); + nc->flags |= MG_F_SEND_AND_CLOSE; + } + break; + default: + break; + } +} + +ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Json::Value& json_param) +{ + if (NULL == req) + return TPE_PARAM; + + bool is_get = true; + if (req->method.len == 3 && 0 == memcmp(req->method.p, "GET", req->method.len)) + is_get = true; + else if (req->method.len == 4 && 0 == memcmp(req->method.p, "POST", req->method.len)) + is_get = false; + else + return TPE_HTTP_METHOD; + + ex_astr json_str; + bool need_decode = false; + if (is_get) { + json_str.assign(req->query_string.p, req->query_string.len); + need_decode = true; + } + else { + json_str.assign(req->body.p, req->body.len); + if (json_str.length() > 0 && json_str[0] == '%') + need_decode = true; + } + + if (need_decode) { + // 将参数进行 url-decode 解码 + int len = json_str.length() * 2; + ex_chars sztmp; + sztmp.resize(len); + memset(&sztmp[0], 0, len); + if (-1 == ts_url_decode(json_str.c_str(), json_str.length(), &sztmp[0], len, 0)) + return TPE_HTTP_URL_ENCODE; + + json_str = &sztmp[0]; + } + + if (0 == json_str.length()) + return TPE_PARAM; + + //Json::Reader jreader; + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = json_str.c_str(); + ex_astr err; + + //if (!jreader.parse(json_str.c_str(), json_param)) + if (!jreader->parse(str_json_begin, str_json_begin + json_str.length(), &json_param, &err)) + return TPE_JSON_FORMAT; + + if (json_param.isArray()) + return TPE_PARAM; + + if (json_param["method"].isNull() || !json_param["method"].isString()) + return TPE_PARAM; + + func_cmd = json_param["method"].asCString(); + json_param = json_param["param"]; + + return TPE_OK; +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_data) +{ + // 返回: {"code":errcode, "data":{jr_data}} + + //Json::FastWriter jr_writer; + Json::Value jr_root; + jr_root["code"] = errcode; + jr_root["data"] = jr_data; + //buf = jr_writer.write(jr_root); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jr_root, &os); + buf = os.str(); +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) +{ + // 返回: {"code":errcode} + + //Json::FastWriter jr_writer; + Json::Value jr_root; + jr_root["code"] = errcode; + //buf = jr_writer.write(jr_root); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jr_root, &os); + buf = os.str(); +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const char* message) +{ + // 返回: {"code":errcode, "message":message} + + //Json::FastWriter jr_writer; + Json::Value jr_root; + jr_root["code"] = errcode; + jr_root["message"] = message; + //buf = jr_writer.write(jr_root); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jr_root, &os); + buf = os.str(); +} + +void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf) +{ + if (func_cmd == "request_session") { + _rpc_func_request_session(json_param, buf); + } + else if (func_cmd == "kill_sessions") { + _rpc_func_kill_sessions(json_param, buf); + } + else if (func_cmd == "get_config") { + _rpc_func_get_config(json_param, buf); + } + else if (func_cmd == "set_config") { + _rpc_func_set_config(json_param, buf); + } + else if (func_cmd == "enc") { + _rpc_func_enc(json_param, buf); + } + else if (func_cmd == "exit") { + _rpc_func_exit(json_param, buf); + } + else { + EXLOGE("[core] rpc got unknown command: %s\n", func_cmd.c_str()); + _create_json_ret(buf, TPE_UNKNOWN_CMD); + } +} + +extern bool g_exit_flag; // 要求整个TS退出的标志(用于停止各个工作线程) +void TsHttpRpc::_rpc_func_exit(const Json::Value& json_param, ex_astr& buf) +{ + // 设置一个全局退出标志 + g_exit_flag = true; + _create_json_ret(buf, TPE_OK); +} + +void TsHttpRpc::_rpc_func_get_config(const Json::Value& json_param, ex_astr& buf) +{ + Json::Value jr_data; + + ex_astr _replay_name; + ex_wstr2astr(g_env.m_replay_path, _replay_name); + jr_data["replay-path"] = _replay_name; + + jr_data["web-server-rpc"] = g_env.web_server_rpc; + + ex_astr _version; + ex_wstr2astr(TP_SERVER_VER, _version); + jr_data["version"] = _version; + + ExIniFile& ini = g_env.get_ini(); + ex_ini_sections& secs = ini.GetAllSections(); + ex_ini_sections::iterator it = secs.begin(); + for (; it != secs.end(); ++it) + { + if (it->first.length() > 9 && 0 == wcsncmp(it->first.c_str(), L"protocol-", 9)) + { + ex_wstr name; + name.assign(it->first, 9, it->first.length() - 9); + ex_astr _name; + ex_wstr2astr(name, _name); + + bool enabled = false; + it->second->GetBool(L"enabled", enabled, false); + + ex_wstr ip; + if (!it->second->GetStr(L"bind-ip", ip)) + continue; + ex_astr _ip; + ex_wstr2astr(ip, _ip); + + int port; + it->second->GetInt(L"bind-port", port, 52189); + + jr_data[_name.c_str()]["enable"] = enabled; + jr_data[_name.c_str()]["ip"] = _ip; + jr_data[_name.c_str()]["port"] = port; + } + } + + _create_json_ret(buf, TPE_OK, jr_data); +} + +void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#request_session + + int conn_id = 0; + ex_rv rv = TPE_OK; + + if (json_param["conn_id"].isNull()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + if (!json_param["conn_id"].isInt()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + conn_id = json_param["conn_id"].asInt(); + if (0 == conn_id) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + TS_CONNECT_INFO* info = new TS_CONNECT_INFO; + if ((rv = ts_web_rpc_get_conn_info(conn_id, *info)) != TPE_OK) + { + _create_json_ret(buf, rv); + return; + } + +// info->ref_count = 0; +// info->ticket_start = ex_get_tick_count(); +// + // 生成一个session-id(内部会避免重复) + ex_astr sid; + if (!g_session_mgr.request_session(sid, info)) { + _create_json_ret(buf, TPE_FAILED); + return; + } + + EXLOGD("[core] rpc new session-id: %s\n", sid.c_str()); + + Json::Value jr_data; + jr_data["sid"] = sid; + + _create_json_ret(buf, TPE_OK, jr_data); +} + +void TsHttpRpc::_rpc_func_kill_sessions(const Json::Value& json_param, ex_astr& buf) { + /* + { + "sessions": ["0123456", "ABCDEF", ...] + } + */ + + if (json_param.isArray()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + if (json_param["sessions"].isNull() || !json_param["sessions"].isArray()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + Json::Value s = json_param["sessions"]; + int cnt = s.size(); + for (int i = 0; i < cnt; ++i) { + if (!s[i].isString()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + } + + EXLOGV("[core] try to kill %d sessions.\n", cnt); + ex_astr sp = s.toStyledString(); + g_tpp_mgr.kill_sessions(sp); + + _create_json_ret(buf, TPE_OK); +} + +void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc + // 加密一个字符串 [ p=plain-text, c=cipher-text ] + // 入参: {"p":"need be encrypt"} + // 示例: {"p":"this-is-a-password"} + // p: 被加密的字符串 + // 返回: + // data域中的"c"的内容是加密后密文的base64编码结果 + // 示例: {"code":0, "data":{"c":"Mxs340a9r3fs+3sdf=="}} + // 错误返回: {"code":1234} + + if (json_param.isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_astr plain_text; + + if (json_param["p"].isNull() || !json_param["p"].isString()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + plain_text = json_param["p"].asCString(); + if (plain_text.length() == 0) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + ex_astr cipher_text; + + if (!ts_db_field_encrypt(plain_text, cipher_text)) + { + _create_json_ret(buf, TPE_FAILED); + return; + } + + Json::Value jr_data; + jr_data["c"] = cipher_text; + _create_json_ret(buf, TPE_OK, jr_data); +} + +void TsHttpRpc::_rpc_func_set_config(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#set_config + /* + { + "noop-timeout": 15 # 按分钟计 + } + */ + + if (json_param.isArray()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + if (json_param["noop_timeout"].isNull() || !json_param["noop_timeout"].isUInt()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + int noop_timeout = json_param["noop_timeout"].asUInt(); + EXLOGV("[core] set run-time config:\n"); + EXLOGV("[core] noop_timeout = %dm\n", noop_timeout); + + ex_astr sp = json_param.toStyledString(); + g_tpp_mgr.set_runtime_config(sp); + + _create_json_ret(buf, TPE_OK); +} + + +/* +void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/tp4a/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc + // 加密多个个字符串 [ p=plain-text, c=cipher-text ] + // 入参: {"p":["need be encrypt", "plain to cipher"]} + // 示例: {"p":["password-for-A"]} + // p: 被加密的字符串 + // 返回: + // data域中的"c"的内容是加密后密文的base64编码结果 + // 示例: {"code":0, "data":{"c":["Mxs340a9r3fs+3sdf=="]}} + // 错误返回: {"code":1234} + + if (json_param.isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_astr plain_text; + + if (json_param["p"].isNull() || !json_param["p"].isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + Json::Value c; + + Json::Value p = json_param["p"]; + int cnt = p.size(); + for (int i = 0; i < cnt; ++i) + { + if (!p[i].isString()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_astr p_txt = p[i].asCString(); + if (p_txt.length() == 0) { + c["c"].append(""); + } + + ex_astr c_txt; + if (!ts_db_field_encrypt(p_txt, c_txt)) + { + _create_json_ret(buf, TPE_FAILED); + return; + } + + c["c"].append(c_txt); + } + + Json::Value jr_data; + jr_data["c"] = c; + _create_json_ret(buf, TPE_OK, jr_data); +} +*/ diff --git a/server/tp_core/core/ts_ver.h b/server/tp_core/core/ts_ver.h index 4da7b50..dda936f 100644 --- a/server/tp_core/core/ts_ver.h +++ b/server/tp_core/core/ts_ver.h @@ -1,6 +1,6 @@ #ifndef __TS_SERVER_VER_H__ #define __TS_SERVER_VER_H__ -#define TP_SERVER_VER L"3.3.0" +#define TP_SERVER_VER L"3.5.0" #endif // __TS_SERVER_VER_H__ diff --git a/server/tp_core/core/ts_web_rpc.cpp b/server/tp_core/core/ts_web_rpc.cpp index cf5b899..1ec813a 100644 --- a/server/tp_core/core/ts_web_rpc.cpp +++ b/server/tp_core/core/ts_web_rpc.cpp @@ -1,320 +1,359 @@ -#include "ts_web_rpc.h" -#include "ts_env.h" -#include "ts_crypto.h" -#include "ts_http_client.h" - -#include "../common/ts_const.h" - -#include -#include - -bool ts_web_rpc_register_core() -{ - Json::FastWriter json_writer; - Json::Value jreq; - jreq["method"] = "register_core"; - jreq["param"]["rpc"] = g_env.core_server_rpc; - - ex_astr json_param; - json_param = json_writer.write(jreq); - - ex_astr param; - ts_url_encode(json_param.c_str(), param); - - ex_astr url = g_env.web_server_rpc; - url += "?"; - url += param; - - ex_astr body; - return ts_http_get(url, body); -} - -int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info) -{ - Json::FastWriter json_writer; - Json::Value jreq; - jreq["method"] = "get_conn_info"; - jreq["param"]["conn_id"] = conn_id; - - ex_astr json_param; - json_param = json_writer.write(jreq); - - ex_astr param; - ts_url_encode(json_param.c_str(), param); - - ex_astr url = g_env.web_server_rpc; - url += "?"; - url += param; - - ex_astr body; - if (!ts_http_get(url, body)) - { - EXLOGE("[core] get conn info from web-server failed: can not connect to web-server.\n"); - return TPE_NETWORK; - } - if (body.length() == 0) { - EXLOGE("[core] get conn info from web-server failed: got nothing.\n"); - return TPE_NETWORK; - } - - Json::Reader jreader; - Json::Value jret; - - if (!jreader.parse(body.c_str(), jret)) - return TPE_PARAM; - if (!jret.isObject()) - return TPE_PARAM; - if (!jret["data"].isObject()) - return TPE_PARAM; - - Json::Value& _jret = jret["data"]; - - if(!_jret["user_id"].isInt()) - EXLOGE("connection info: need `user_id`.\n"); - if(!_jret["host_id"].isInt()) - EXLOGE("connection info: need `host_id`.\n"); - if(!_jret["acc_id"].isInt()) - EXLOGE("connection info: need `acc_id`.\n"); - if(!_jret["conn_port"].isInt()) - EXLOGE("connection info: need `conn_port`.\n"); - if(!_jret["protocol_type"].isInt()) - EXLOGE("connection info: need `protocol_type`.\n"); - if(!_jret["protocol_sub_type"].isInt()) - EXLOGE("connection info: need `protocol_sub_type`.\n"); - if(!_jret["auth_type"].isInt()) - EXLOGE("connection info: need `auth_type`.\n"); - if (!_jret["protocol_flag"].isUInt()) - EXLOGE("connection info: need `protocol_flag`.\n"); - if (!_jret["record_flag"].isUInt()) - EXLOGE("connection info: need `record_flag`.\n"); - if (!_jret["_enc"].isInt()) - EXLOGE("connection info: need `_enc`.\n"); - if(!_jret["user_username"].isString()) - EXLOGE("connection info: need `user_username`.\n"); - if(!_jret["host_ip"].isString()) - EXLOGE("connection info: need `host_ip`.\n"); - if(!_jret["conn_ip"].isString()) - EXLOGE("connection info: need `conn_ip`.\n"); - if(!_jret["client_ip"].isString()) - EXLOGE("connection info: need `client_ip`.\n"); - if(!_jret["acc_username"].isString()) - EXLOGE("connection info: need `acc_username`.\n"); - if(!_jret["acc_secret"].isString()) - EXLOGE("connection info: need `acc_secret`.\n"); - if(!_jret["username_prompt"].isString()) - EXLOGE("connection info: need `username_prompt`.\n"); - if(!_jret["password_prompt"].isString()) - EXLOGE("connection info: need `password_prompt`.\n"); - - if ( - !_jret["user_id"].isInt() - || !_jret["host_id"].isInt() - || !_jret["acc_id"].isInt() - || !_jret["conn_port"].isInt() - || !_jret["protocol_type"].isInt() - || !_jret["protocol_sub_type"].isInt() - || !_jret["auth_type"].isInt() - || !_jret["protocol_flag"].isUInt() - || !_jret["record_flag"].isUInt() - || !_jret["_enc"].isInt() - - || !_jret["user_username"].isString() - || !_jret["host_ip"].isString() - || !_jret["conn_ip"].isString() - || !_jret["client_ip"].isString() - || !_jret["acc_username"].isString() - || !_jret["acc_secret"].isString() - || !_jret["username_prompt"].isString() - || !_jret["password_prompt"].isString() - ) - { - EXLOGE("got connection info from web-server, but not all info valid.\n"); - return TPE_PARAM; - } - - int user_id; - int host_id; - int acc_id; - ex_astr user_username;// 뱾ӵû - ex_astr host_ip;// ԶIPֱģʽremote_host_ipͬ - ex_astr conn_ip;// ҪӵԶIPǶ˿ӳģʽΪ·IP - int conn_port;// ҪӵԶĶ˿ڣǶ˿ӳģʽΪ·Ķ˿ڣ - ex_astr client_ip; - ex_astr acc_username; // Զ˺ - ex_astr acc_secret;// Զ˺ŵ루˽Կ - ex_astr username_prompt; - ex_astr password_prompt; - int protocol_type = 0; - int protocol_sub_type = 0; - int auth_type = 0; - int protocol_flag = 0; - int record_flag = 0; - bool _enc; - - user_id = _jret["user_id"].asInt(); - host_id = _jret["host_id"].asInt(); - acc_id = _jret["acc_id"].asInt(); - user_username = _jret["user_username"].asString(); - host_ip = _jret["host_ip"].asString(); - conn_ip = _jret["conn_ip"].asString(); - conn_port = _jret["conn_port"].asInt(); - client_ip = _jret["client_ip"].asString(); - acc_username = _jret["acc_username"].asString(); - acc_secret = _jret["acc_secret"].asString(); - username_prompt = _jret["username_prompt"].asString(); - password_prompt = _jret["password_prompt"].asString(); - protocol_type = _jret["protocol_type"].asInt(); - protocol_sub_type = _jret["protocol_sub_type"].asInt(); - protocol_flag = _jret["protocol_flag"].asUInt(); - record_flag = _jret["record_flag"].asUInt(); - auth_type = _jret["auth_type"].asInt(); - _enc = _jret["_enc"].asBool(); - - - // һжϲǷϷ - // ע⣬account_idΪ-1ʾһβӡ - if (user_id <= 0 || host_id <= 0 - || user_username.length() == 0 - || host_ip.length() == 0 || conn_ip.length() == 0 || client_ip.length() == 0 - || conn_port <= 0 || conn_port >= 65535 - || acc_username.length() == 0 || acc_secret.length() == 0 - || !(protocol_type == TP_PROTOCOL_TYPE_RDP || protocol_type == TP_PROTOCOL_TYPE_SSH || protocol_type == TP_PROTOCOL_TYPE_TELNET) - || !(auth_type == TP_AUTH_TYPE_NONE || auth_type == TP_AUTH_TYPE_PASSWORD || auth_type == TP_AUTH_TYPE_PRIVATE_KEY) - ) - { - return TPE_PARAM; - } - - if (_enc) { - ex_astr _auth; - if (!ts_db_field_decrypt(acc_secret, _auth)) - return TPE_FAILED; - - acc_secret = _auth; - } - - info.user_id = user_id; - info.host_id = host_id; - info.acc_id = acc_id; - info.user_username = user_username; - info.host_ip = host_ip; - info.conn_ip = conn_ip; - info.conn_port = conn_port; - info.client_ip = client_ip; - info.acc_username = acc_username; - info.acc_secret = acc_secret; - info.username_prompt = username_prompt; - info.password_prompt = password_prompt; - info.protocol_type = protocol_type; - info.protocol_sub_type = protocol_sub_type; - info.auth_type = auth_type; - info.protocol_flag = protocol_flag; - info.record_flag = record_flag; - - return TPE_OK; -} - -bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id) -{ - Json::FastWriter json_writer; - Json::Value jreq; - - jreq["method"] = "session_begin"; - jreq["param"]["sid"] = info.sid.c_str(); - jreq["param"]["user_id"] = info.user_id; - jreq["param"]["host_id"] = info.host_id; - jreq["param"]["acc_id"] = info.acc_id; - jreq["param"]["user_username"] = info.user_username.c_str(); - jreq["param"]["acc_username"] = info.acc_username.c_str(); - jreq["param"]["host_ip"] = info.host_ip.c_str(); - jreq["param"]["conn_ip"] = info.conn_ip.c_str(); - jreq["param"]["client_ip"] = info.client_ip.c_str(); - //jreq["param"]["sys_type"] = info.sys_type; - jreq["param"]["conn_port"] = info.conn_port; - jreq["param"]["auth_type"] = info.auth_type; - jreq["param"]["protocol_type"] = info.protocol_type; - jreq["param"]["protocol_sub_type"] = info.protocol_sub_type; - - ex_astr json_param; - json_param = json_writer.write(jreq); - - ex_astr param; - ts_url_encode(json_param.c_str(), param); - - ex_astr url = g_env.web_server_rpc; - url += "?"; - url += param; - - ex_astr body; - if (!ts_http_get(url, body)) - { - // EXLOGV("request `rpc::session_begin` from web return: "); - // EXLOGV(body.c_str()); - // EXLOGV("\n"); - return false; - } - - Json::Reader jreader; - Json::Value jret; - - if (!jreader.parse(body.c_str(), jret)) - return false; - if (!jret.isObject()) - return false; - if (!jret["data"].isObject()) - return false; - if (!jret["data"]["rid"].isUInt()) - return false; - - record_id = jret["data"]["rid"].asUInt(); - - return true; -} - -bool ts_web_rpc_session_update(int record_id, int protocol_sub_type, int state) { - Json::FastWriter json_writer; - Json::Value jreq; - jreq["method"] = "session_update"; - jreq["param"]["rid"] = record_id; - jreq["param"]["protocol_sub_type"] = protocol_sub_type; - jreq["param"]["code"] = state; - - ex_astr json_param; - json_param = json_writer.write(jreq); - - ex_astr param; - ts_url_encode(json_param.c_str(), param); - - ex_astr url = g_env.web_server_rpc; - url += "?"; - url += param; - - ex_astr body; - return ts_http_get(url, body); -} - - -//session -bool ts_web_rpc_session_end(const char* sid, int record_id, int ret_code) -{ - // TODO: ָsidصĻỰüһ0ʱ٣ - - Json::FastWriter json_writer; - Json::Value jreq; - jreq["method"] = "session_end"; - jreq["param"]["rid"] = record_id; - jreq["param"]["code"] = ret_code; - - ex_astr json_param; - json_param = json_writer.write(jreq); - - ex_astr param; - ts_url_encode(json_param.c_str(), param); - - ex_astr url = g_env.web_server_rpc; - url += "?"; - url += param; - - ex_astr body; - return ts_http_get(url, body); -} +#include "ts_web_rpc.h" +#include "ts_env.h" +#include "ts_crypto.h" +#include "ts_http_client.h" + +#include "../common/ts_const.h" + +#include +#include + +bool ts_web_rpc_register_core() +{ + //Json::FastWriter json_writer; + Json::Value jreq; + jreq["method"] = "register_core"; + jreq["param"]["rpc"] = g_env.core_server_rpc; + + ex_astr json_param; + //json_param = json_writer.write(jreq); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jreq, &os); + json_param = os.str(); + + ex_astr param; + ts_url_encode(json_param.c_str(), param); + + ex_astr url = g_env.web_server_rpc; + url += "?"; + url += param; + + ex_astr body; + return ts_http_get(url, body); +} + +int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info) +{ + //Json::FastWriter json_writer; + Json::Value jreq; + jreq["method"] = "get_conn_info"; + jreq["param"]["conn_id"] = conn_id; + + ex_astr json_param; + //json_param = json_writer.write(jreq); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jreq, &os); + json_param = os.str(); + + ex_astr param; + ts_url_encode(json_param.c_str(), param); + + ex_astr url = g_env.web_server_rpc; + url += "?"; + url += param; + + ex_astr body; + if (!ts_http_get(url, body)) + { + EXLOGE("[core] get conn info from web-server failed: can not connect to web-server.\n"); + return TPE_NETWORK; + } + if (body.length() == 0) { + EXLOGE("[core] get conn info from web-server failed: got nothing.\n"); + return TPE_NETWORK; + } + + //Json::Reader jreader; + Json::Value jret; + + //if (!jreader.parse(body.c_str(), jret)) + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = body.c_str(); + ex_astr err; + + //if (!jreader.parse(func_args.c_str(), jsRoot)) { + if (!jreader->parse(str_json_begin, str_json_begin + body.length(), &jret, &err)) + return TPE_PARAM; + if (!jret.isObject()) + return TPE_PARAM; + if (!jret["data"].isObject()) + return TPE_PARAM; + + Json::Value& _jret = jret["data"]; + + if(!_jret["user_id"].isInt()) + EXLOGE("connection info: need `user_id`.\n"); + if(!_jret["host_id"].isInt()) + EXLOGE("connection info: need `host_id`.\n"); + if(!_jret["acc_id"].isInt()) + EXLOGE("connection info: need `acc_id`.\n"); + if(!_jret["conn_port"].isInt()) + EXLOGE("connection info: need `conn_port`.\n"); + if(!_jret["protocol_type"].isInt()) + EXLOGE("connection info: need `protocol_type`.\n"); + if(!_jret["protocol_sub_type"].isInt()) + EXLOGE("connection info: need `protocol_sub_type`.\n"); + if(!_jret["auth_type"].isInt()) + EXLOGE("connection info: need `auth_type`.\n"); + if (!_jret["protocol_flag"].isUInt()) + EXLOGE("connection info: need `protocol_flag`.\n"); + if (!_jret["record_flag"].isUInt()) + EXLOGE("connection info: need `record_flag`.\n"); + if (!_jret["_enc"].isInt()) + EXLOGE("connection info: need `_enc`.\n"); + if(!_jret["user_username"].isString()) + EXLOGE("connection info: need `user_username`.\n"); + if(!_jret["host_ip"].isString()) + EXLOGE("connection info: need `host_ip`.\n"); + if(!_jret["conn_ip"].isString()) + EXLOGE("connection info: need `conn_ip`.\n"); + if(!_jret["client_ip"].isString()) + EXLOGE("connection info: need `client_ip`.\n"); + if(!_jret["acc_username"].isString()) + EXLOGE("connection info: need `acc_username`.\n"); + if(!_jret["acc_secret"].isString()) + EXLOGE("connection info: need `acc_secret`.\n"); + if(!_jret["username_prompt"].isString()) + EXLOGE("connection info: need `username_prompt`.\n"); + if(!_jret["password_prompt"].isString()) + EXLOGE("connection info: need `password_prompt`.\n"); + + if ( + !_jret["user_id"].isInt() + || !_jret["host_id"].isInt() + || !_jret["acc_id"].isInt() + || !_jret["conn_port"].isInt() + || !_jret["protocol_type"].isInt() + || !_jret["protocol_sub_type"].isInt() + || !_jret["auth_type"].isInt() + || !_jret["protocol_flag"].isUInt() + || !_jret["record_flag"].isUInt() + || !_jret["_enc"].isInt() + + || !_jret["user_username"].isString() + || !_jret["host_ip"].isString() + || !_jret["conn_ip"].isString() + || !_jret["client_ip"].isString() + || !_jret["acc_username"].isString() + || !_jret["acc_secret"].isString() + || !_jret["username_prompt"].isString() + || !_jret["password_prompt"].isString() + ) + { + EXLOGE("got connection info from web-server, but not all info valid.\n"); + return TPE_PARAM; + } + + int user_id; + int host_id; + int acc_id; + ex_astr user_username;// 申请本次连接的用户名 + ex_astr host_ip;// 真正的远程主机IP(如果是直接连接模式,则与remote_host_ip相同) + ex_astr conn_ip;// 要连接的远程主机的IP(如果是端口映射模式,则为路由主机的IP) + int conn_port;// 要连接的远程主机的端口(如果是端口映射模式,则为路由主机的端口) + ex_astr client_ip; + ex_astr acc_username; // 远程主机的账号 + ex_astr acc_secret;// 远程主机账号的密码(或者私钥) + ex_astr username_prompt; + ex_astr password_prompt; + int protocol_type = 0; + int protocol_sub_type = 0; + int auth_type = 0; + int protocol_flag = 0; + int record_flag = 0; + bool _enc; + + user_id = _jret["user_id"].asInt(); + host_id = _jret["host_id"].asInt(); + acc_id = _jret["acc_id"].asInt(); + user_username = _jret["user_username"].asString(); + host_ip = _jret["host_ip"].asString(); + conn_ip = _jret["conn_ip"].asString(); + conn_port = _jret["conn_port"].asInt(); + client_ip = _jret["client_ip"].asString(); + acc_username = _jret["acc_username"].asString(); + acc_secret = _jret["acc_secret"].asString(); + username_prompt = _jret["username_prompt"].asString(); + password_prompt = _jret["password_prompt"].asString(); + protocol_type = _jret["protocol_type"].asInt(); + protocol_sub_type = _jret["protocol_sub_type"].asInt(); + protocol_flag = _jret["protocol_flag"].asUInt(); + record_flag = _jret["record_flag"].asUInt(); + auth_type = _jret["auth_type"].asInt(); + _enc = _jret["_enc"].asBool(); + + + // 进一步判断参数是否合法 + // 注意,account_id可以为-1,表示这是一次测试连接。 + if (user_id <= 0 || host_id <= 0 + || user_username.length() == 0 + || host_ip.length() == 0 || conn_ip.length() == 0 || client_ip.length() == 0 + || conn_port <= 0 || conn_port >= 65535 + || acc_username.length() == 0 || acc_secret.length() == 0 + || !(protocol_type == TP_PROTOCOL_TYPE_RDP || protocol_type == TP_PROTOCOL_TYPE_SSH || protocol_type == TP_PROTOCOL_TYPE_TELNET) + || !(auth_type == TP_AUTH_TYPE_NONE || auth_type == TP_AUTH_TYPE_PASSWORD || auth_type == TP_AUTH_TYPE_PRIVATE_KEY) + ) + { + return TPE_PARAM; + } + + if (_enc) { + ex_astr _auth; + if (!ts_db_field_decrypt(acc_secret, _auth)) + return TPE_FAILED; + + acc_secret = _auth; + } + + info.user_id = user_id; + info.host_id = host_id; + info.acc_id = acc_id; + info.user_username = user_username; + info.host_ip = host_ip; + info.conn_ip = conn_ip; + info.conn_port = conn_port; + info.client_ip = client_ip; + info.acc_username = acc_username; + info.acc_secret = acc_secret; + info.username_prompt = username_prompt; + info.password_prompt = password_prompt; + info.protocol_type = protocol_type; + info.protocol_sub_type = protocol_sub_type; + info.auth_type = auth_type; + info.protocol_flag = protocol_flag; + info.record_flag = record_flag; + + return TPE_OK; +} + +bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id) +{ + //Json::FastWriter json_writer; + Json::Value jreq; + + jreq["method"] = "session_begin"; + jreq["param"]["sid"] = info.sid.c_str(); + jreq["param"]["user_id"] = info.user_id; + jreq["param"]["host_id"] = info.host_id; + jreq["param"]["acc_id"] = info.acc_id; + jreq["param"]["user_username"] = info.user_username.c_str(); + jreq["param"]["acc_username"] = info.acc_username.c_str(); + jreq["param"]["host_ip"] = info.host_ip.c_str(); + jreq["param"]["conn_ip"] = info.conn_ip.c_str(); + jreq["param"]["client_ip"] = info.client_ip.c_str(); + //jreq["param"]["sys_type"] = info.sys_type; + jreq["param"]["conn_port"] = info.conn_port; + jreq["param"]["auth_type"] = info.auth_type; + jreq["param"]["protocol_type"] = info.protocol_type; + jreq["param"]["protocol_sub_type"] = info.protocol_sub_type; + + ex_astr json_param; + //json_param = json_writer.write(jreq); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jreq, &os); + json_param = os.str(); + + ex_astr param; + ts_url_encode(json_param.c_str(), param); + + ex_astr url = g_env.web_server_rpc; + url += "?"; + url += param; + + ex_astr body; + if (!ts_http_get(url, body)) + { + // EXLOGV("request `rpc::session_begin` from web return: "); + // EXLOGV(body.c_str()); + // EXLOGV("\n"); + return false; + } + + //Json::Reader jreader; + Json::Value jret; + + //if (!jreader.parse(body.c_str(), jret)) + Json::CharReaderBuilder jcrb; + std::unique_ptr const jreader(jcrb.newCharReader()); + const char *str_json_begin = body.c_str(); + ex_astr err; + + //if (!jreader.parse(func_args.c_str(), jsRoot)) { + if (!jreader->parse(str_json_begin, str_json_begin + body.length(), &jret, &err)) + return false; + if (!jret.isObject()) + return false; + if (!jret["data"].isObject()) + return false; + if (!jret["data"]["rid"].isUInt()) + return false; + + record_id = jret["data"]["rid"].asUInt(); + + return true; +} + +bool ts_web_rpc_session_update(int record_id, int protocol_sub_type, int state) { + //Json::FastWriter json_writer; + Json::Value jreq; + jreq["method"] = "session_update"; + jreq["param"]["rid"] = record_id; + jreq["param"]["protocol_sub_type"] = protocol_sub_type; + jreq["param"]["code"] = state; + + ex_astr json_param; + //json_param = json_writer.write(jreq); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jreq, &os); + json_param = os.str(); + + ex_astr param; + ts_url_encode(json_param.c_str(), param); + + ex_astr url = g_env.web_server_rpc; + url += "?"; + url += param; + + ex_astr body; + return ts_http_get(url, body); +} + + +//session 结束 +bool ts_web_rpc_session_end(const char* sid, int record_id, int ret_code) +{ + // TODO: 对指定的sid相关的会话的引用计数减一(但减到0时销毁) + + //Json::FastWriter json_writer; + Json::Value jreq; + jreq["method"] = "session_end"; + jreq["param"]["rid"] = record_id; + jreq["param"]["code"] = ret_code; + + ex_astr json_param; + //json_param = json_writer.write(jreq); + Json::StreamWriterBuilder jwb; + std::unique_ptr jwriter(jwb.newStreamWriter()); + ex_aoss os; + jwriter->write(jreq, &os); + json_param = os.str(); + + ex_astr param; + ts_url_encode(json_param.c_str(), param); + + ex_astr url = g_env.web_server_rpc; + url += "?"; + url += param; + + ex_astr body; + return ts_http_get(url, body); +} diff --git a/server/www/teleport/static/js/audit/record-list.js b/server/www/teleport/static/js/audit/record-list.js index 46eabca..e6612f0 100644 --- a/server/www/teleport/static/js/audit/record-list.js +++ b/server/www/teleport/static/js/audit/record-list.js @@ -157,7 +157,7 @@ $app.on_table_host_cell_created = function (tbl, row_id, col_key, cell_obj) { cell_obj.find('[data-action]').click(function () { var row_data = tbl.get_row(row_id); - console.log('---', row_data); + // console.log('---', row_data); var action = $(this).attr('data-action'); if (action === 'replay') { @@ -363,83 +363,26 @@ $app.on_table_host_render_created = function (render) { }; $app.do_replay_rdp = function (record_id, user_username, acc_username, host_ip, time_begin) { + if(!$app.options.core_running) { + $tp.notify_error(tp_error_msg(TPE_NO_CORE_SERVER), '无法播放。'); + return; + } + + if(!$assist.check()) + return; + $assist.do_rdp_replay( - { - rid: record_id - // , web: $tp.web_server // + '/audit/get_rdp_record/' + record_id // 'http://' + ip + ':' + port + '/log/replay/rdp/' + record_id; - // , sid: Cookies.get('_sid') - , user: user_username - , acc: acc_username - , host: host_ip - , start: time_begin//tp_format_datetime(tp_utc2local(time_begin), 'yyyyMMdd-HHmmss') - } + record_id , function () { // func_success } , function (code, message) { - if (code === TPE_NO_ASSIST) + if (code === TPE_NO_ASSIST) { + $assist.errcode = TPE_NO_ASSIST; $assist.alert_assist_not_found(); + } else $tp.notify_error('播放RDP操作录像失败:' + tp_error_msg(code, message)); } ); }; - - -// $app.on_table_host_header_created = function (header) { -// $('#' + header._table_ctrl.dom_id + ' a[data-reset-filter]').click(function () { -// CALLBACK_STACK.create() -// .add(header._table_ctrl.load_data) -// .add(header._table_ctrl.reset_filters) -// .exec(); -// }); -// -// // 表格内嵌过滤器的事件绑定在这时进行(也可以延期到整个表格创建完成时进行) -// header._table_ctrl.get_filter_ctrl('search').on_created(); -// }; - -// $app.get_selected_record = function (tbl) { -// var records = []; -// var _objs = $('#' + $app.table_record.dom_id + ' tbody tr td input[data-check-box]'); -// $.each(_objs, function (i, _obj) { -// if ($(_obj).is(':checked')) { -// var _row_data = tbl.get_row(_obj); -// records.push(_row_data.id); -// } -// }); -// return records; -// }; - -// $app.on_btn_remove_record_click = function () { -// var records = $app.get_selected_record($app.table_record); -// if (records.length === 0) { -// $tp.notify_error('请选择要删除的会话记录!'); -// return; -// } -// -// var _fn_sure = function (cb_stack, cb_args) { -// $tp.ajax_post_json('/user/remove-user', {users: users}, -// function (ret) { -// if (ret.code === TPE_OK) { -// cb_stack.add($app.check_host_all_selected); -// cb_stack.add($app.table_record.load_data); -// $tp.notify_success('删除用户账号操作成功!'); -// } else { -// $tp.notify_error('删除用户账号操作失败:' + tp_error_msg(ret.code, ret.message)); -// } -// -// cb_stack.exec(); -// }, -// function () { -// $tp.notify_error('网络故障,删除用户账号操作失败!'); -// cb_stack.exec(); -// } -// ); -// }; -// -// var cb_stack = CALLBACK_STACK.create(); -// $tp.dlg_confirm(cb_stack, { -// msg: '

注意:删除操作不可恢复!!

删除用户账号将同时将其从所在用户组中移除,并且删除所有分配给此用户的授权!

如果您希望禁止某个用户登录本系统,可对其进行“禁用”操作!

您确定要移除所有选定的 ' + user_list.length + '个 用户账号吗?

', -// fn_yes: _fn_sure -// }); -// }; diff --git a/server/www/teleport/static/js/tp-assist.js b/server/www/teleport/static/js/tp-assist.js index cc983db..98c0e74 100644 --- a/server/www/teleport/static/js/tp-assist.js +++ b/server/www/teleport/static/js/tp-assist.js @@ -66,16 +66,29 @@ $assist.init = function (cb_stack) { cb_stack.exec(); }; +$assist.check = function() { + if (!$assist.running) { + $assist.errcode = TPE_NO_ASSIST; + $assist.alert_assist_not_found(); + return false; + } else if (!$assist._version_compare()) { + $assist.errcode = TPE_OLD_ASSIST; + $assist.alert_assist_not_found(); + return false; + } + return true; +}; + + $assist.alert_assist_not_found = function () { - console.log($assist.errcode); if($assist.errcode === TPE_NO_ASSIST) { $assist.dom.msg_box_title.html('未检测到TELEPORT助手'); $assist.dom.msg_box_info.html('需要TELEPORT助手来辅助远程连接,请确认本机运行了TELEPORT助手!'); - $assist.dom.msg_box_desc.html('如果您尚未运行TELEPORT助手,请 下载最新版TELEPORT助手安装包 并安装。一旦运行了TELEPORT助手,即可重新进行远程连接。'); + $assist.dom.msg_box_desc.html('如果您尚未运行TELEPORT助手,请 下载最新版TELEPORT助手安装包 并安装。一旦运行了TELEPORT助手,即可刷新页面,重新进行远程连接。'); } else if($assist.errcode === TPE_OLD_ASSIST) { $assist.dom.msg_box_title.html('TELEPORT助手需要升级'); $assist.dom.msg_box_info.html('检测到TELEPORT助手版本 v'+ $assist.version +',但需要最低版本 v'+ $assist.ver_require+'。'); - $assist.dom.msg_box_desc.html('请 下载最新版TELEPORT助手安装包 并安装。一旦升级了TELEPORT助手,即可重新进行远程连接。'); + $assist.dom.msg_box_desc.html('请 下载最新版TELEPORT助手安装包 并安装。一旦升级了TELEPORT助手,即可刷新页面,重新进行远程连接。'); } $('#dialog-need-assist').modal(); @@ -134,13 +147,8 @@ $assist._make_message_box = function () { }; $assist.do_teleport = function (args, func_success, func_error) { - if(!$assist.running) { - $assist.errcode = TPE_NO_ASSIST; - func_error(TPE_NO_ASSIST, ''); - return; - } else if(!$assist._version_compare()) { - $assist.errcode = TPE_OLD_ASSIST; - func_error(TPE_NO_ASSIST, ''); + if(!$app.options.url_proto){ + if(!$assist.check()) return; } @@ -226,20 +234,13 @@ $assist.do_teleport = function (args, func_success, func_error) { }); }; -$assist.do_rdp_replay = function (args, func_success, func_error) { - // ================================================== - // args is dict with fields shown below: - // rid: (int) - record-id in database. - // user: (string) - who did the RDP connection. - // acc: (string) - account to login to remote RDP server. - // host: (string) - IP of the remote RDP server. - // start: (string) - when start the RDP connection, should be a UTC timestamp. - // ================================================== +$assist.do_rdp_replay = function (rid, func_success, func_error) { + // rid: (int) - record-id in database. - // now fix the args. + // now make the args. + var args = {rid: rid}; args.web = $tp.web_server; // (string) - teleport server base address, like "http://127.0.0.1:7190", without end-slash. args.sid = Cookies.get('_sid'); // (string) - current login user's session-id. - args.start = tp_format_datetime(tp_utc2local(args.start), 'yyyyMMdd-HHmmss'); // (string) - convert UTC timestamp to local human-readable string. console.log('do-rdp-replay:', args); @@ -264,34 +265,3 @@ $assist.do_rdp_replay = function (args, func_success, func_error) { } }); }; - -/* - -var version_compare = function () { - var cur_version = parseInt(g_current_version.split(".")[2]); - var req_version = parseInt(g_req_version.split(".")[2]); - return cur_version >= req_version; -}; - -var start_rdp_replay = function (args, func_success, func_error) { - var args_ = encodeURIComponent(JSON.stringify(args)); - $.ajax({ - type: 'GET', - timeout: 6000, - url: $assist.api_url + '/rdp_play/' + args_, - jsonp: 'callback', - dataType: 'json', - success: function (ret) { - if (ret.code === TPE_OK) { - error_process(ret, func_success, func_error); - } else { - func_error(ret.code, '查看录像失败!'); - } - console.log('ret', ret); - }, - error: function () { - func_error(TPE_NETWORK, '与助手的络通讯失败!'); - } - }); -}; -*/ diff --git a/server/www/teleport/static/js/user/user-list.js b/server/www/teleport/static/js/user/user-list.js index cd8d6ad..2208c9e 100755 --- a/server/www/teleport/static/js/user/user-list.js +++ b/server/www/teleport/static/js/user/user-list.js @@ -744,6 +744,8 @@ $app.create_dlg_edit_user = function () { dlg.field_mobile = ''; dlg.field_qq = ''; dlg.field_wechat = ''; + dlg.field_vaild_from = ''; + dlg.field_vaild_to = ''; dlg.field_desc = ''; dlg.dom = { @@ -756,6 +758,8 @@ $app.create_dlg_edit_user = function () { , edit_mobile: $('#edit-user-mobile') , edit_qq: $('#edit-user-qq') , edit_wechat: $('#edit-user-wechat') + , edit_valid_from: $('#edit-user-valid-from') + , edit_valid_to: $('#edit-user-valid-to') , edit_desc: $('#edit-user-desc') , msg: $('#edit-user-message') , btn_save: $('#btn-edit-user-save') @@ -778,6 +782,8 @@ $app.create_dlg_edit_user = function () { _ret.push('
  • ' + role.name + '
  • '); }); _ret.push(''); + dlg.dom.edit_valid_from.datetimepicker({format: "yyyy-mm-dd h:ii", autoclose: 1, todayHighlight: 1}); + dlg.dom.edit_valid_to.datetimepicker({format: "yyyy-mm-dd h:ii", autoclose: 1, todayHighlight: 1}); dlg.dom.select_role.after($(_ret.join(''))); dlg.dom.selected_role = $('#' + dlg.dom_id + ' span[data-selected-role]'); @@ -869,7 +875,7 @@ $app.create_dlg_edit_user = function () { var role_name = '选择角色'; dlg.field_role = -1; dlg.field_auth_type = 0; - + // dlg.dom.btn_auth_use_sys_config.removeClass('tp-selected'); // dlg.dom.btn_auth_username_password.removeClass('tp-selected'); // dlg.dom.btn_auth_username_password_captcha.removeClass('tp-selected'); @@ -887,6 +893,8 @@ $app.create_dlg_edit_user = function () { dlg.dom.edit_qq.val(''); dlg.dom.edit_wechat.val(''); dlg.dom.edit_desc.val(''); + dlg.dom.edit_valid_from.find('input').val(''); + dlg.dom.edit_valid_to.find('input').val(''); } else { dlg.field_id = user.id; dlg.field_auth_type = user.auth_type; @@ -905,6 +913,16 @@ $app.create_dlg_edit_user = function () { dlg.dom.edit_qq.val(user.qq); dlg.dom.edit_wechat.val(user.wechat); dlg.dom.edit_desc.val(user.desc); + if (user.valid_from == 0 ) { + dlg.dom.edit_valid_from.find('input').val(''); + }else{ + dlg.dom.edit_valid_from.find('input').val(tp_format_datetime(tp_utc2local(user.valid_from), 'yyyy-MM-dd HH:mm')); + } + if (user.valid_to == 0 ) { + dlg.dom.edit_valid_to.find('input').val(''); + }else{ + dlg.dom.edit_valid_to.find('input').val(tp_format_datetime(tp_utc2local(user.valid_to), 'yyyy-MM-dd HH:mm')); + } } dlg.dom.selected_role.text(role_name); @@ -943,6 +961,8 @@ $app.create_dlg_edit_user = function () { dlg.field_mobile = dlg.dom.edit_mobile.val(); dlg.field_qq = dlg.dom.edit_qq.val(); dlg.field_wechat = dlg.dom.edit_wechat.val(); + dlg.field_valid_from = dlg.dom.edit_valid_from.find('input').val(); + dlg.field_valid_to = dlg.dom.edit_valid_to.find('input').val(); dlg.field_desc = dlg.dom.edit_desc.val(); if (dlg.field_role === -1) { @@ -1002,6 +1022,8 @@ $app.create_dlg_edit_user = function () { , mobile: dlg.field_mobile , qq: dlg.field_qq , wechat: dlg.field_wechat + , valid_from: dlg.field_valid_from + , valid_to: dlg.field_valid_to , desc: dlg.field_desc }, function (ret) { diff --git a/server/www/teleport/view/user/user-list.mako b/server/www/teleport/view/user/user-list.mako index 8a5a02b..40ab30f 100644 --- a/server/www/teleport/view/user/user-list.mako +++ b/server/www/teleport/view/user/user-list.mako @@ -8,6 +8,7 @@ <%block name="extend_js_file"> + <%block name="embed_js"> @@ -230,6 +231,25 @@ + +
    + +
    +
    + + + + +
    +
    + + + + +
    +
    +
    +
    diff --git a/server/www/teleport/webroot/app/app_ver.py b/server/www/teleport/webroot/app/app_ver.py index 1c328c3..ada39de 100644 --- a/server/www/teleport/webroot/app/app_ver.py +++ b/server/www/teleport/webroot/app/app_ver.py @@ -1,3 +1,3 @@ # -*- coding: utf8 -*- -TP_SERVER_VER = "3.3.1" -TP_ASSIST_REQUIRE_VER = "3.3.1" +TP_SERVER_VER = "3.5.1" +TP_ASSIST_REQUIRE_VER = "3.5.1" diff --git a/server/www/teleport/webroot/app/base/utils.py b/server/www/teleport/webroot/app/base/utils.py index 426cebd..6c7855e 100644 --- a/server/www/teleport/webroot/app/base/utils.py +++ b/server/www/teleport/webroot/app/base/utils.py @@ -160,6 +160,12 @@ def tp_second2human(n): return ret +def tp_timestamp_from_str(t, fmt='%Y-%m-%d %H:%M:%S'): + _fmt = '%Y-%m-%d %H:%M:%S' if fmt is None else fmt + d = datetime.datetime.strptime(t, _fmt) + return int(d.timestamp()) + + def tp_timestamp_local_to_utc(t): return int(datetime.datetime.utcfromtimestamp(time.mktime(time.localtime(t))).timestamp()) diff --git a/server/www/teleport/webroot/app/controller/audit.py b/server/www/teleport/webroot/app/controller/audit.py index 9e2b3d7..529b03a 100644 --- a/server/www/teleport/webroot/app/controller/audit.py +++ b/server/www/teleport/webroot/app/controller/audit.py @@ -411,12 +411,15 @@ class RecordHandler(TPBaseHandler): return if not tp_cfg().core.detected: + core_running = False total_size = 0 free_size = 0 else: + core_running = True total_size, free_size = get_free_space_bytes(tp_cfg().core.replay_path) param = { + 'core_running': core_running, 'total_size': total_size, 'free_size': free_size, } @@ -659,18 +662,6 @@ class DoGetFileHandler(TPBaseHandler): require_privilege = TP_PRIVILEGE_OPS_AUZ | TP_PRIVILEGE_AUDIT_AUZ | TP_PRIVILEGE_AUDIT - # sid = self.get_argument('sid', None) - # if sid is None: - # self.set_status(403) - # return self.write('need login first.') - # - # self._s_id = sid - # _user = self.get_session('user') - # if _user is None: - # self.set_status(403) - # return self.write('need login first.') - # self._user = _user - if not self._user['_is_login']: self.set_status(401) # 401=未授权, 要求身份验证 return self.write('need login first.') @@ -710,8 +701,8 @@ class DoGetFileHandler(TPBaseHandler): self.set_status(416) # 416=请求范围不符合要求 return self.write('no more data.') - # we read most 4096 bytes one time. - BULK_SIZE = 4096 + # we read most 8192 bytes one time. + BULK_SIZE = 8192 total_need = file_size - offset if length != -1 and length < total_need: total_need = length @@ -721,6 +712,7 @@ class DoGetFileHandler(TPBaseHandler): read_this_time = BULK_SIZE if total_need > BULK_SIZE else total_need while read_this_time > 0: self.write(f.read(read_this_time)) + self.flush() total_read += read_this_time if total_read >= total_need: break diff --git a/server/www/teleport/webroot/app/controller/user.py b/server/www/teleport/webroot/app/controller/user.py index 6200873..d232529 100755 --- a/server/www/teleport/webroot/app/controller/user.py +++ b/server/www/teleport/webroot/app/controller/user.py @@ -11,7 +11,7 @@ from app.base.configs import tp_cfg from app.base.controller import TPBaseHandler, TPBaseJsonHandler from app.base.logger import * from app.base.session import tp_session -from app.base.utils import tp_check_strong_password, tp_gen_password +from app.base.utils import tp_check_strong_password, tp_gen_password, tp_timestamp_from_str from app.logic.auth.oath import tp_oath_verify_code from app.const import * from app.logic.auth.oath import tp_oath_generate_secret, tp_oath_generate_qrcode @@ -588,13 +588,22 @@ class DoUpdateUserHandler(TPBaseJsonHandler): args['mobile'] = args['mobile'].strip() args['qq'] = args['qq'].strip() args['wechat'] = args['wechat'].strip() + + if args['valid_from'] == '': + args['valid_from'] = 0 + else: + args['valid_from'] = tp_timestamp_from_str(args['valid_from'].strip(), '%Y-%m-%d %H:%M') + if args['valid_to'] == '': + args['valid_to'] = 0 + else: + args['valid_to'] = tp_timestamp_from_str(args['valid_to'].strip(), '%Y-%m-%d %H:%M') args['desc'] = args['desc'].strip() except: return self.write_json(TPE_PARAM) if len(args['username']) == 0: return self.write_json(TPE_PARAM) - + if args['id'] == -1: args['password'] = tp_gen_password(8) err, _ = user.create_user(self, args) diff --git a/server/www/teleport/webroot/app/model/account.py b/server/www/teleport/webroot/app/model/account.py index 3cb22b5..be11035 100644 --- a/server/www/teleport/webroot/app/model/account.py +++ b/server/www/teleport/webroot/app/model/account.py @@ -166,6 +166,7 @@ def get_accounts(sql_filter, sql_order, sql_limit, sql_restrict, sql_exclude): s = SQL(db) # s.select_from('acc', ['id', 'host_id', 'host_ip', 'router_ip', 'router_port', 'username', 'protocol_type', 'auth_type', 'state'], alt_name='a') s.select_from('acc', ['id', 'host_id', 'username', 'protocol_type', 'auth_type', 'state', 'username_prompt', 'password_prompt'], alt_name='a') + s.left_join('host', ['name', 'desc'], join_on='h.id=a.host_id', alt_name='h', out_map={'name': 'host_name'}) str_where = '' _where = list() @@ -189,7 +190,7 @@ def get_accounts(sql_filter, sql_order, sql_limit, sql_restrict, sql_exclude): if len(sql_filter) > 0: for k in sql_filter: if k == 'search': - _where.append('(a.username LIKE "%{filter}%" OR a.host_ip LIKE "%{filter}%" OR a.router_ip LIKE "%{filter}%")'.format(filter=sql_filter[k])) + _where.append('(a.username LIKE "%{filter}%" OR a.host_ip LIKE "%{filter}%" OR a.router_ip LIKE "%{filter}%" OR h.name LIKE "%{filter}%" OR h.desc LIKE "%{filter}%")'.format(filter=sql_filter[k])) # _where.append('(a.username LIKE "%{filter}%")'.format(filter=sql_filter[k])) if len(_where) > 0: diff --git a/server/www/teleport/webroot/app/model/user.py b/server/www/teleport/webroot/app/model/user.py index b3688ce..844c341 100755 --- a/server/www/teleport/webroot/app/model/user.py +++ b/server/www/teleport/webroot/app/model/user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +import time,datetime from app.base.configs import tp_cfg from app.base.db import get_db, SQL from app.base.logger import log @@ -38,7 +38,7 @@ def get_by_username(username): s.select_from('user', ['id', 'type', 'auth_type', 'username', 'surname', 'ldap_dn', 'password', 'oath_secret', 'role_id', 'state', 'fail_count', 'lock_time', 'email', 'create_time', 'last_login', 'last_ip', 'last_chpass', - 'mobile', 'qq', 'wechat', 'desc'], alt_name='u') + 'mobile', 'qq', 'wechat', 'valid_from', 'valid_to', 'desc'], alt_name='u') s.left_join('role', ['name', 'privilege'], join_on='r.id=u.role_id', alt_name='r', out_map={'name': 'role'}) s.where('u.username="{}"'.format(username)) err = s.query() @@ -57,7 +57,9 @@ def get_by_username(username): def login(handler, username, password=None, oath_code=None, check_bind_oath=False): sys_cfg = tp_cfg().sys msg = '' - + current_unix_time = int(time.mktime(datetime.datetime.now().timetuple())) +# log.e('current:',current_unix_time,'validfrom:', user_info['valid_from']) + err, user_info = get_by_username(username) if err != TPE_OK: return err, None, msg @@ -88,6 +90,10 @@ def login(handler, username, password=None, oath_code=None, check_bind_oath=Fals msg = '登录失败,用户状态异常' syslog.sys_log(user_info, handler.request.remote_ip, TPE_FAILED, msg) return TPE_FAILED, None, msg + elif current_unix_time < user_info['valid_from'] or (current_unix_time > user_info['valid_to'] and user_info['valid_to'] != 0): + msg = '登录失败,用户已过期' + syslog.sys_log(user_info, handler.request.remote_ip, TPE_FAILED, msg) + return TPE_FAILED, None, msg err_msg = '' if password is not None: @@ -172,7 +178,7 @@ def login(handler, username, password=None, oath_code=None, check_bind_oath=Fals def get_users(sql_filter, sql_order, sql_limit, sql_restrict, sql_exclude): dbtp = get_db().table_prefix s = SQL(get_db()) - s.select_from('user', ['id', 'type', 'auth_type', 'username', 'surname', 'role_id', 'state', 'email', 'last_login'], + s.select_from('user', ['id', 'type', 'auth_type', 'username', 'surname', 'role_id', 'state', 'email', 'last_login', 'valid_from', 'valid_to'], alt_name='u') s.left_join('role', ['name', 'privilege'], join_on='r.id=u.role_id', alt_name='r', out_map={'name': 'role'}) @@ -353,14 +359,15 @@ def create_user(handler, user): sql = 'INSERT INTO `{}user` (' \ '`role_id`, `username`, `surname`, `type`, `ldap_dn`, `auth_type`, `password`, `state`, ' \ - '`email`, `creator_id`, `create_time`, `last_login`, `last_chpass`, `desc`' \ + '`email`, `creator_id`, `create_time`, `last_login`, `last_chpass`, `valid_from`, `valid_to`, `desc`' \ ') VALUES (' \ '{role}, "{username}", "{surname}", {user_type}, "{ldap_dn}", {auth_type}, "{password}", {state}, ' \ - '"{email}", {creator_id}, {create_time}, {last_login}, {last_chpass}, "{desc}");' \ + '"{email}", {creator_id}, {create_time}, {last_login}, {last_chpass}, {valid_from}, '\ + '{valid_to}, "{desc}");' \ ''.format(db.table_prefix, role=user['role'], username=user['username'], surname=user['surname'], user_type=user['type'], ldap_dn=user['ldap_dn'], auth_type=user['auth_type'], password=_password, state=TP_STATE_NORMAL, email=user['email'], creator_id=operator['id'], create_time=_time_now, - last_login=0, last_chpass=_time_now, desc=user['desc']) + last_login=0, last_chpass=_time_now, valid_from=user['valid_from'], valid_to=user['valid_to'], desc=user['desc']) db_ret = db.exec(sql) if not db_ret: return TPE_DATABASE, 0 @@ -400,12 +407,12 @@ def update_user(handler, args): sql = 'UPDATE `{}user` SET ' \ '`username`="{username}", `surname`="{surname}", `auth_type`={auth_type}, ' \ '`role_id`={role}, `email`="{email}", `mobile`="{mobile}", `qq`="{qq}", ' \ - '`wechat`="{wechat}", `desc`="{desc}" WHERE `id`={user_id};' \ + '`wechat`="{wechat}", `valid_from`={valid_from}, `valid_to`={valid_to}, '\ + '`desc`="{desc}" WHERE `id`={user_id};' \ ''.format(db.table_prefix, username=args['username'], surname=args['surname'], auth_type=args['auth_type'], role=args['role'], - email=args['email'], - mobile=args['mobile'], qq=args['qq'], wechat=args['wechat'], desc=args['desc'], - user_id=args['id'] + email=args['email'], mobile=args['mobile'], qq=args['qq'], wechat=args['wechat'], + valid_from=args['valid_from'], valid_to=args['valid_to'], desc=args['desc'], user_id=args['id'] ) db_ret = db.exec(sql) if not db_ret: diff --git a/version.in b/version.in index 8405318..4de5209 100644 --- a/version.in +++ b/version.in @@ -10,8 +10,8 @@ Minor: 次版本号。如果两个程序集的名称和主版本号相同,而 Revision: 修订号。主版本号和次版本号都相同但修订号不同的程序集应是完全可互换的。 这适用于修复以前发布的程序集中的错误或安全漏洞。 -TP_SERVER 3.3.1 # 整个服务端打包的版本 -TP_TPCORE 3.3.0 # 核心服务 tp_core 的版本 +TP_SERVER 3.5.1 # 整个服务端打包的版本 +TP_TPCORE 3.5.0 # 核心服务 tp_core 的版本 TP_TPWEB 3.1.0 # web服务 tp_web 的版本(一般除非升级Python,否则不会变化) -TP_ASSIST 3.3.1 # 助手版本 -TP_ASSIST_REQUIRE 3.3.1 # 适配的助手最低版本 +TP_ASSIST 3.5.1 # 助手版本 +TP_ASSIST_REQUIRE 3.5.1 # 适配的助手最低版本