Merge branch 'feature/mstsc' of github.com:tp4a/teleport into feature/mstsc
commit
f99d39e2fa
|
|
@ -79,8 +79,7 @@ __pycache__
|
|||
/client/tp_rdp
|
||||
/server/tp_core/protocol/rdp
|
||||
/client/tools/tprdp
|
||||
/client/tp_assist_win_it_doctor
|
||||
/dist/client/windows/assist-it-doctor
|
||||
/client/build-tp-player-*
|
||||
|
||||
# for MacOS.
|
||||
.DS_Store
|
||||
|
|
@ -104,4 +103,5 @@ profile
|
|||
|
||||
/server/tp_core/testssh/Debug
|
||||
/server/tp_core/testssh/Release
|
||||
/client/build-tp-player-*
|
||||
/external/zlib
|
||||
/client/tools/qt-redist
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with NO BOM">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/common/libex/include/ex/ex_log.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/common/libex/include/ex/ex_path.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/common/libex/include/ex/ex_thread.h" charset="GBK" />
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
<file url="file://$PROJECT_DIR$/server/tp_core/common/ts_membuf.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/common/ts_memstream.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/main.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/tp_tpp_mgr.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_env.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_http_rpc.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_http_rpc.h" charset="GBK" />
|
||||
|
|
@ -29,17 +30,12 @@
|
|||
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_session.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_web_rpc.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/core/ts_web_rpc.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/librdp/core/config.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/librdp/rdp/x224.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/membuf.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/membuf.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_conn.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_conn.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_package.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_proxy.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_session.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_session.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/transport.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/ssh/ssh_proxy.cpp" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/ssh/ssh_proxy.h" charset="GBK" />
|
||||
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/ssh/ssh_recorder.cpp" charset="GBK" />
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
|||
set(OS_POSIX 1)
|
||||
MESSAGE(STATUS "build on Linux...")
|
||||
# add_subdirectory(server/tp_web/src)
|
||||
# set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(TP_EXTERNAL_RELEASE_DIR "${PROJECT_SOURCE_DIR}/external/linux/release")
|
||||
elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
|
||||
# MESSAGE(FATAL_ERROR "unsupported platform: Windows")
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
################################################################
|
||||
# Basic settings.
|
||||
################################################################
|
||||
VER_PYTHON="3.7.4"
|
||||
VER_PYTHON="3.7.5"
|
||||
VER_PYTHON_SHORT="3.7"
|
||||
VER_OPENSSL="1.0.2s"
|
||||
VER_SQLITE="3290000"
|
||||
VER_OPENSSL="1.1.1d"
|
||||
VER_SQLITE="3300100"
|
||||
VER_ZLIB="1.2.11"
|
||||
VER_PYTHON_LIB="${VER_PYTHON_SHORT}m"
|
||||
|
||||
|
|
@ -38,15 +38,6 @@ function on_error()
|
|||
exit 1
|
||||
}
|
||||
|
||||
function setp_build_git()
|
||||
{
|
||||
# su -s
|
||||
# yum install zlib-devel expat-devel libcurl-devel
|
||||
# make prefix=/usr/local
|
||||
# make prefix=/usr/local install
|
||||
echo 'skip build git now.'
|
||||
}
|
||||
|
||||
function dlfile()
|
||||
{
|
||||
echo -n "Downloading $1 ..."
|
||||
|
|
@ -121,6 +112,16 @@ function step_prepare_source()
|
|||
on_error "Can not prepare source code for build sqlite3 module for Python."
|
||||
fi
|
||||
|
||||
if [ ! -f "${PATH_FIX}/Python-${VER_PYTHON}/Modules/Setup.dist" ]; then
|
||||
on_error "Can not fix source for build Python."
|
||||
fi
|
||||
if [ ! -f "${PATH_FIX}/Python-${VER_PYTHON}/Modules/_sqlite/cache.h" ]; then
|
||||
on_error "Can not fix source for build sqlite3 module for Python."
|
||||
fi
|
||||
if [ ! -f "${PATH_FIX}/Python-${VER_PYTHON}/Modules/_sqlite/prepare_protocol.h" ]; then
|
||||
on_error "Can not fix source for build sqlite3 module for Python."
|
||||
fi
|
||||
|
||||
cp "${PATH_FIX}/Python-${VER_PYTHON}/Modules/Setup.dist" "${PY_PATH_SRC}/Modules/Setup.dist"
|
||||
cp "${PATH_FIX}/Python-${VER_PYTHON}/Modules/Setup.dist" "${PY_PATH_SRC}/Modules/Setup"
|
||||
cp "${PATH_FIX}/Python-${VER_PYTHON}/Modules/_sqlite/cache.h" "${PY_PATH_SRC}/Modules/_sqlite/cache.h"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ def main():
|
|||
elif x == 'a':
|
||||
clean_everything()
|
||||
continue
|
||||
elif x == 'e':
|
||||
clean_external()
|
||||
continue
|
||||
|
||||
try:
|
||||
x = int(x)
|
||||
|
|
@ -117,6 +120,27 @@ def clean_everything():
|
|||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libuv.a'))
|
||||
|
||||
|
||||
def clean_external():
|
||||
#utils.remove(os.path.join(env.root_path, 'out'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'jsoncpp'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'libuv'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'mbedtls'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'mongoose'))
|
||||
#utils.remove(os.path.join(env.root_path, 'external', 'openssl'))
|
||||
#utils.remove(os.path.join(env.root_path, 'external', 'python'))
|
||||
#utils.remove(os.path.join(env.root_path, 'external', 'libssh-win-static', 'lib'))
|
||||
#utils.remove(os.path.join(env.root_path, 'external', 'libssh-win-static', 'src'))
|
||||
#utils.remove(os.path.join(env.root_path, 'external', 'linux', 'tmp'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedcrypto.a'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedtls.a'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedx509.a'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libsqlite3.a'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libssh.a'))
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libssh_threads.a'))
|
||||
|
||||
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libuv.a'))
|
||||
|
||||
|
||||
def do_opt(opt):
|
||||
arg = ''
|
||||
|
||||
|
|
@ -249,6 +273,7 @@ def show_menu():
|
|||
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, '%2d' % options[o]['id']), (cc.CR_NORMAL, '] ', options[o]['disp']))
|
||||
|
||||
cc.v(' -------------------------------------------------------')
|
||||
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, ' E'), (cc.CR_NORMAL, '] clean external temp. files.'))
|
||||
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, ' C'), (cc.CR_NORMAL, '] clean build and dist.'))
|
||||
cc.o((cc.CR_NORMAL, ' ['), (cc.CR_INFO, ' A'), (cc.CR_NORMAL, '] clean everything.'))
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -27,19 +27,21 @@ 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
|
||||
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
|
||||
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 +49,56 @@ 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
|
||||
self._build_libuv(file_name)
|
||||
|
||||
def _download_libuv(self, file_name):
|
||||
return utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_libuv(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_mbedtls(self):
|
||||
file_name = 'mbedtls-mbedtls-{}.zip'.format(env.ver_mbedtls)
|
||||
if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
|
||||
return
|
||||
self._build_mbedtls(file_name)
|
||||
|
||||
def _download_mbedtls(self, file_name):
|
||||
return utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_mbedtls(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_zlib(self):
|
||||
file_name = 'zlilb{}.zip'.format(env.ver_zlib_number)
|
||||
self._build_zlib(file_name)
|
||||
|
||||
def _download_zlib(self, file_name):
|
||||
return utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_zlib(self, file_name):
|
||||
cc.e("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
|
||||
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 +123,10 @@ class BuilderWin(BuilderBase):
|
|||
self.MBEDTLS_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mbedtls')
|
||||
self.LIBUV_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libuv')
|
||||
self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh')
|
||||
self.ZLIB_PATH_SRC = os.path.join(PATH_EXTERNAL, 'zlib')
|
||||
|
||||
def _prepare_python(self):
|
||||
cc.n('prepare python header files ...', end='')
|
||||
cc.n('prepare python header files ... ', end='')
|
||||
|
||||
if os.path.exists(os.path.join(PATH_EXTERNAL, 'python', 'include', 'Python.h')):
|
||||
cc.w('already exists, skip.')
|
||||
|
|
@ -125,68 +146,97 @@ class BuilderWin(BuilderBase):
|
|||
utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include'))
|
||||
|
||||
def _build_openssl(self, file_name):
|
||||
cc.n('build openssl static library from source code... ')
|
||||
|
||||
if not super()._build_openssl(file_name):
|
||||
return
|
||||
|
||||
_chk_output = [
|
||||
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
|
||||
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
|
||||
os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
|
||||
]
|
||||
|
||||
need_build = False
|
||||
for f in _chk_output:
|
||||
if not os.path.exists(f):
|
||||
need_build = True
|
||||
break
|
||||
|
||||
if not need_build:
|
||||
cc.n('build openssl static library from source code... ', end='')
|
||||
cc.n('prepare OpenSSL pre-built package ... ', end='')
|
||||
if os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
cc.w('already exists, skip.')
|
||||
return
|
||||
cc.v('')
|
||||
|
||||
cc.n('prepare openssl source code...')
|
||||
_alt_ver = '_'.join(env.ver_ossl.split('.'))
|
||||
if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
|
||||
if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
raise RuntimeError('can not prepare openssl source code.')
|
||||
else:
|
||||
cc.w('already exists, skip.')
|
||||
|
||||
os.chdir(self.OPENSSL_PATH_SRC)
|
||||
os.system('""{}" Configure VC-WIN32"'.format(env.perl))
|
||||
os.system(r'ms\do_nasm')
|
||||
# for vs2015
|
||||
# utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
# for vs2017 community
|
||||
utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
file_name = 'Win32OpenSSL-{}.msi'.format(_alt_ver)
|
||||
installer = os.path.join(PATH_DOWNLOAD, file_name)
|
||||
|
||||
for f in _chk_output:
|
||||
if not os.path.exists(f):
|
||||
raise RuntimeError('build openssl static library from source code failed.')
|
||||
if not os.path.exists(installer):
|
||||
if not utils.download_file('openssl installer', 'http://slproweb.com/download/{}'.format(filename), PATH_DOWNLOAD, file_name):
|
||||
cc.e('can not download pre-built installer of OpenSSL.')
|
||||
return
|
||||
|
||||
utils.ensure_file_exists(installer)
|
||||
|
||||
cc.w('On Windows, we use pre-built package of OpenSSL.')
|
||||
cc.w('The installer have been downloaded at "{}".'.format(installer))
|
||||
cc.w('please install OpenSSL into "{}".'.format(self.OPENSSL_PATH_SRC))
|
||||
cc.w('\nOnce the OpenSSL installed, press Enter to continue or Q to quit...', end='')
|
||||
try:
|
||||
x = env.input()
|
||||
except EOFError:
|
||||
x = 'q'
|
||||
if x == 'q':
|
||||
return
|
||||
|
||||
|
||||
# cc.n('build openssl static library from source code... ')
|
||||
|
||||
# if not super()._build_openssl(file_name):
|
||||
# return
|
||||
|
||||
# _chk_output = [
|
||||
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
|
||||
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
|
||||
# os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
|
||||
# ]
|
||||
|
||||
# need_build = False
|
||||
# for f in _chk_output:
|
||||
# if not os.path.exists(f):
|
||||
# need_build = True
|
||||
# break
|
||||
|
||||
# if not need_build:
|
||||
# cc.n('build openssl static library from source code... ', end='')
|
||||
# cc.w('already exists, skip.')
|
||||
# return
|
||||
# cc.v('')
|
||||
|
||||
# cc.n('prepare openssl source code...')
|
||||
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
|
||||
# if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
# utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
# os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
|
||||
# if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
# raise RuntimeError('can not prepare openssl source code.')
|
||||
# else:
|
||||
# cc.w('already exists, skip.')
|
||||
|
||||
# os.chdir(self.OPENSSL_PATH_SRC)
|
||||
# os.system('""{}" Configure VC-WIN32"'.format(env.perl))
|
||||
# os.system(r'ms\do_nasm')
|
||||
# # for vs2015
|
||||
# # utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
# # for vs2017 community
|
||||
# utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
|
||||
# for f in _chk_output:
|
||||
# if not os.path.exists(f):
|
||||
# raise RuntimeError('build openssl static library from source code failed.')
|
||||
|
||||
def _build_libssh(self, file_name):
|
||||
cc.n('build libssh static library from source code... ', end='')
|
||||
if not self._download_libssh(file_name):
|
||||
return
|
||||
cc.n('build libssh library from source code... ', end='')
|
||||
|
||||
if not os.path.exists(self.LIBSSH_PATH_SRC):
|
||||
cc.v('')
|
||||
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
os.rename(os.path.join(PATH_EXTERNAL, 'libssh-{}'.format(env.ver_libssh)), self.LIBSSH_PATH_SRC)
|
||||
|
||||
# cc.n('fix libssh source code... ', end='')
|
||||
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', 'src', 'sftp.c'))
|
||||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'sftp.c')
|
||||
cc.n('fix libssh source code... ', end='')
|
||||
s_name = 'libssh-{}'.format(env.ver_libssh)
|
||||
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'session.c'))
|
||||
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'session.c'))
|
||||
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto.c'))
|
||||
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto-compat.c'))
|
||||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'session.c')
|
||||
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'session.c')
|
||||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto.c')
|
||||
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto-compat.c')
|
||||
|
||||
|
|
@ -210,7 +260,7 @@ class BuilderWin(BuilderBase):
|
|||
|
||||
cc.i('build libssh...')
|
||||
sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'build', 'libssh.sln')
|
||||
utils.msvc_build(sln_file, 'ssh_shared', ctx.target_path, 'win32', False)
|
||||
utils.msvc_build(sln_file, 'ssh', ctx.target_path, 'win32', False)
|
||||
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.lib'))
|
||||
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.dll'))
|
||||
utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.lib')
|
||||
|
|
@ -218,7 +268,53 @@ class BuilderWin(BuilderBase):
|
|||
utils.ensure_file_exists(out_file_lib)
|
||||
utils.ensure_file_exists(out_file_dll)
|
||||
|
||||
def _build_zlib(self, file_name):
|
||||
if not self._download_zlib(file_name):
|
||||
return
|
||||
cc.n('build zlib library from source code... ', end='')
|
||||
|
||||
if not os.path.exists(self.ZLIB_PATH_SRC):
|
||||
cc.v('')
|
||||
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
os.rename(os.path.join(PATH_EXTERNAL, 'zlib-{}'.format(env.ver_zlib)), self.ZLIB_PATH_SRC)
|
||||
|
||||
if ctx.target_path == 'debug':
|
||||
olib = 'zlibd.lib'
|
||||
odll = 'zlibd.dll'
|
||||
else:
|
||||
olib = 'zlib.lib'
|
||||
odll = 'zlib.dll'
|
||||
out_file_lib = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, olib)
|
||||
out_file_dll = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, odll)
|
||||
|
||||
if os.path.exists(out_file_lib) and os.path.exists(out_file_dll):
|
||||
cc.w('already exists, skip.')
|
||||
return
|
||||
cc.v('')
|
||||
|
||||
cc.w('On Windows, when build zlib, need you use cmake-gui.exe to generate solution file')
|
||||
cc.w('for Visual Studio 2017. Visit https://docs.tp4a.com for more details.')
|
||||
cc.w('\nOnce the zlib.sln generated, press Enter to continue or Q to quit...', end='')
|
||||
try:
|
||||
x = env.input()
|
||||
except EOFError:
|
||||
x = 'q'
|
||||
if x == 'q':
|
||||
return
|
||||
|
||||
cc.i('build zlib...')
|
||||
sln_file = os.path.join(self.ZLIB_PATH_SRC, 'build', 'zlib.sln')
|
||||
utils.msvc_build(sln_file, 'zlib', ctx.target_path, 'win32', False)
|
||||
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.lib'))
|
||||
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.dll'))
|
||||
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.lib')
|
||||
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.dll')
|
||||
utils.ensure_file_exists(out_file_lib)
|
||||
utils.ensure_file_exists(out_file_dll)
|
||||
|
||||
def _build_jsoncpp(self, file_name):
|
||||
if not self._download_jsoncpp(file_name):
|
||||
return
|
||||
cc.n('prepare jsoncpp source code... ', end='')
|
||||
if not os.path.exists(self.JSONCPP_PATH_SRC):
|
||||
cc.v('')
|
||||
|
|
@ -228,6 +324,8 @@ class BuilderWin(BuilderBase):
|
|||
cc.w('already exists, skip.')
|
||||
|
||||
def _build_mongoose(self, file_name):
|
||||
if not self._download_mongoose(file_name):
|
||||
return
|
||||
cc.n('prepare mongoose source code... ', end='')
|
||||
if not os.path.exists(self.MONGOOSE_PATH_SRC):
|
||||
cc.v('')
|
||||
|
|
@ -237,6 +335,8 @@ class BuilderWin(BuilderBase):
|
|||
cc.w('already exists, skip.')
|
||||
|
||||
def _build_mbedtls(self, file_name):
|
||||
if not self._download_mbedtls(file_name):
|
||||
return
|
||||
cc.n('prepare mbedtls source code... ', end='')
|
||||
if not os.path.exists(self.MBEDTLS_PATH_SRC):
|
||||
cc.v('')
|
||||
|
|
@ -254,6 +354,8 @@ class BuilderWin(BuilderBase):
|
|||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
|
||||
|
||||
def _build_libuv(self, file_name):
|
||||
if not self._download_libuv(file_name):
|
||||
return
|
||||
cc.n('prepare libuv source code... ', end='')
|
||||
if not os.path.exists(self.LIBUV_PATH_SRC):
|
||||
cc.v('')
|
||||
|
|
@ -277,6 +379,7 @@ class BuilderLinux(BuilderBase):
|
|||
self.LIBUV_PATH_SRC = os.path.join(self.PATH_TMP, 'libuv-{}'.format(env.ver_libuv))
|
||||
self.MBEDTLS_PATH_SRC = os.path.join(self.PATH_TMP, 'mbedtls-mbedtls-{}'.format(env.ver_mbedtls))
|
||||
self.LIBSSH_PATH_SRC = os.path.join(self.PATH_TMP, 'libssh-{}'.format(env.ver_libssh))
|
||||
self.ZLIB_PATH_SRC = os.path.join(self.PATH_TMP, 'zlib-{}'.format(env.ver_zlib))
|
||||
|
||||
self.JSONCPP_PATH_SRC = os.path.join(PATH_EXTERNAL, 'jsoncpp')
|
||||
self.MONGOOSE_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mongoose')
|
||||
|
|
@ -288,7 +391,7 @@ class BuilderLinux(BuilderBase):
|
|||
cc.n('prepare python header and lib files ...')
|
||||
|
||||
if os.path.exists(os.path.join(self.PATH_RELEASE, 'include', 'python', 'Python.h')):
|
||||
cc.w(' - header file already exists, skip.')
|
||||
cc.w('python header file already exists, skip.')
|
||||
else:
|
||||
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'include', 'python{}m'.format(ctx.py_dot_ver), 'Python.h'))
|
||||
utils.sys_exec('ln -s "{}" "{}"'.format(
|
||||
|
|
@ -300,6 +403,8 @@ class BuilderLinux(BuilderBase):
|
|||
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', lib_file))
|
||||
|
||||
def _build_jsoncpp(self, file_name):
|
||||
if not self._download_jsoncpp(file_name):
|
||||
return
|
||||
cc.n('prepare jsoncpp source code...', end='')
|
||||
if not os.path.exists(self.JSONCPP_PATH_SRC):
|
||||
cc.v('')
|
||||
|
|
@ -309,6 +414,8 @@ class BuilderLinux(BuilderBase):
|
|||
cc.w('already exists, skip.')
|
||||
|
||||
def _build_mongoose(self, file_name):
|
||||
if not self._download_mongoose(file_name):
|
||||
return
|
||||
cc.n('prepare mongoose source code...', end='')
|
||||
if not os.path.exists(self.MONGOOSE_PATH_SRC):
|
||||
cc.v('')
|
||||
|
|
@ -319,9 +426,11 @@ class BuilderLinux(BuilderBase):
|
|||
|
||||
def _build_openssl(self, file_name):
|
||||
# we do not need build openssl anymore, because first time run build.sh we built Python with openssl included.
|
||||
pass
|
||||
cc.w('skip build openssl again.')
|
||||
|
||||
def _build_libuv(self, file_name):
|
||||
if not self._download_libuv(file_name):
|
||||
return
|
||||
if not os.path.exists(self.LIBUV_PATH_SRC):
|
||||
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
|
||||
|
||||
|
|
@ -351,6 +460,8 @@ class BuilderLinux(BuilderBase):
|
|||
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', 'libuv.a'))
|
||||
|
||||
def _build_mbedtls(self, file_name):
|
||||
if not self._download_mbedtls(file_name):
|
||||
return
|
||||
if not os.path.exists(self.MBEDTLS_PATH_SRC):
|
||||
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
|
||||
|
||||
|
|
@ -393,21 +504,24 @@ class BuilderLinux(BuilderBase):
|
|||
os.chdir(old_p)
|
||||
|
||||
def _build_libssh(self, file_name):
|
||||
if not self._download_libssh(file_name):
|
||||
return
|
||||
if not os.path.exists(self.LIBSSH_PATH_SRC):
|
||||
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
|
||||
|
||||
cc.n('build libssh...', end='')
|
||||
if os.path.exists(os.path.join(self.PATH_RELEASE, 'lib', 'libssh.a')):
|
||||
out_file = os.path.join(self.PATH_RELEASE, 'lib64', 'libssh.a')
|
||||
if os.path.exists(out_file):
|
||||
cc.w('already exists, skip.')
|
||||
return
|
||||
cc.v('')
|
||||
|
||||
cc.n('fix libssh source code... ', end='')
|
||||
s_name = 'libssh-{}'.format(env.ver_libssh)
|
||||
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'session.c'))
|
||||
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'session.c'))
|
||||
# utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto.c'))
|
||||
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src', 'libcrypto-compat.c'))
|
||||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'session.c')
|
||||
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'session.c')
|
||||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto.c')
|
||||
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', s_name, 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'libcrypto-compat.c')
|
||||
|
||||
|
|
@ -418,16 +532,54 @@ class BuilderLinux(BuilderBase):
|
|||
' -DOPENSSL_LIBRARIES={path_release}/lib' \
|
||||
' -DWITH_SFTP=ON' \
|
||||
' -DWITH_SERVER=ON' \
|
||||
' -DWITH_STATIC_LIB=ON' \
|
||||
' -DWITH_GSSAPI=OFF' \
|
||||
' -DWITH_ZLIB=OFF' \
|
||||
' -DWITH_ZLIB=ON' \
|
||||
' -DWITH_PCAP=OFF' \
|
||||
' -DBUILD_SHARED_LIBS=OFF' \
|
||||
' -DUNIT_TESTING=OFF' \
|
||||
' -DWITH_EXAMPLES=OFF' \
|
||||
' -DWITH_BENCHMARKS=OFF' \
|
||||
' -DWITH_NACL=OFF' \
|
||||
' ..'.format(path_release=self.PATH_RELEASE)
|
||||
|
||||
# ' -DWITH_STATIC_LIB=ON'
|
||||
|
||||
|
||||
old_p = os.getcwd()
|
||||
try:
|
||||
utils.cmake(build_path, 'Release', False, cmake_define=cmake_define, cmake_pre_define='CFLAGS="-fPIC"')
|
||||
os.chdir(build_path)
|
||||
utils.sys_exec('make install')
|
||||
except:
|
||||
pass
|
||||
os.chdir(old_p)
|
||||
|
||||
utils.ensure_file_exists(out_file)
|
||||
# files = os.listdir(os.path.join(self.PATH_RELEASE, 'lib'))
|
||||
# for i in files:
|
||||
# if i.startswith('libssh.so'):
|
||||
# # use os.unlink() because some file should be a link.
|
||||
# os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
|
||||
|
||||
def _build_zlib(self, file_name):
|
||||
# cc.w('skip build zlib again.')
|
||||
if not self._download_zlib(file_name):
|
||||
return
|
||||
if not os.path.exists(self.ZLIB_PATH_SRC):
|
||||
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
|
||||
|
||||
cc.n('build zlib...', end='')
|
||||
out_file = os.path.join(self.PATH_RELEASE, 'lib', 'libz.a')
|
||||
if os.path.exists(out_file):
|
||||
cc.w('already exists, skip.')
|
||||
return
|
||||
cc.v('')
|
||||
|
||||
build_path = os.path.join(self.ZLIB_PATH_SRC, 'build')
|
||||
|
||||
cmake_define = ' -DCMAKE_INSTALL_PREFIX={path_release}' \
|
||||
' ..'.format(path_release=self.PATH_RELEASE)
|
||||
|
||||
old_p = os.getcwd()
|
||||
try:
|
||||
utils.cmake(build_path, 'Release', False, cmake_define=cmake_define, cmake_pre_define='CFLAGS="-fPIC"')
|
||||
|
|
@ -437,10 +589,10 @@ class BuilderLinux(BuilderBase):
|
|||
pass
|
||||
os.chdir(old_p)
|
||||
|
||||
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', 'libssh.a'))
|
||||
utils.ensure_file_exists(out_file)
|
||||
files = os.listdir(os.path.join(self.PATH_RELEASE, 'lib'))
|
||||
for i in files:
|
||||
if i.startswith('libssh.so'):
|
||||
if i.startswith('libz.so'):
|
||||
# use os.unlink() because some file should be a link.
|
||||
os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
|
||||
|
||||
|
|
@ -674,6 +826,7 @@ def main():
|
|||
builder.build_openssl()
|
||||
builder.build_libuv()
|
||||
builder.build_mbedtls()
|
||||
builder.build_zlib()
|
||||
builder.build_libssh()
|
||||
|
||||
builder.fix_output()
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ ctx = BuildContext()
|
|||
|
||||
MODULES_WIN = ['_asyncio', '_bz2', '_ctypes', '_hashlib', '_lzma', '_overlapped', '_socket', '_sqlite3', '_ssl', 'select', 'sqlite3',
|
||||
'libcrypto-1_1', 'libssl-1_1', 'unicodedata']
|
||||
PY_LIB_REMOVE_WIN = ['ctypes/test', 'curses', 'dbm', 'distutils', 'email/test', 'ensurepip', 'idlelib', 'lib2to3',
|
||||
PY_LIB_REMOVE_WIN = ['ctypes/test', 'curses', 'dbm', 'distutils/test', 'email/tests', 'ensurepip', 'idlelib', 'lib2to3',
|
||||
'lib-dynload', 'pydoc_data', 'site-packages', 'sqlite3/test', 'test', 'tkinter', 'turtledemo',
|
||||
'unittest', 'venv', 'wsgiref', 'doctest.py', 'pdb.py', 'py_compile.py', 'pydoc.py',
|
||||
'this.py', 'wave.py', 'webbrowser.py', 'zipapp.py']
|
||||
PY_LIB_REMOVE_LINUX = ['ctypes/test', 'curses', 'dbm', 'distutils', 'ensurepip', 'idlelib', 'lib2to3',
|
||||
PY_LIB_REMOVE_LINUX = ['ctypes/test', 'curses', 'dbm', 'distutils/tests', 'ensurepip', 'idlelib', 'lib2to3',
|
||||
'lib-dynload', 'pydoc_data', 'site-packages', 'sqlite3/test', 'test', 'tkinter', 'turtledemo', 'unittest', 'venv',
|
||||
'wsgiref', 'doctest.py', 'pdb.py', 'py_compile.py', 'pydoc.py', 'this.py', 'wave.py', 'webbrowser.py', 'zipapp.py']
|
||||
PY_MODULE_REMOVE_LINUX = ['_ctypes_test', '_testbuffer', '_testcapi', '_testimportmultiple', '_testmultiphase', '_xxtestfuzz']
|
||||
|
|
@ -46,7 +46,7 @@ class PYSBase:
|
|||
utils.sys_exec('{} -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip --upgrade'.format(env.py_exec))
|
||||
|
||||
pip = self._get_pip()
|
||||
pypi_modules = ['ldap3', 'mako', 'Pillow', 'psutil', 'pymysql', 'qrcode', 'tornado', 'wheezy.captcha']
|
||||
pypi_modules = ['cffi', 'cryptography', 'ldap3', 'mako', 'Pillow', 'psutil', 'pyasn1', 'pymysql', 'qrcode', 'tornado', 'wheezy.captcha']
|
||||
for p in pypi_modules:
|
||||
cc.n('install {} ...'.format(p))
|
||||
utils.sys_exec('{} install -i https://pypi.tuna.tsinghua.edu.cn/simple {}'.format(pip, p), direct_output=True)
|
||||
|
|
@ -111,7 +111,7 @@ class PYSBase:
|
|||
utils.ensure_file_exists(out_file)
|
||||
|
||||
cc.v('remove temp folder...')
|
||||
utils.remove(_tmp_)
|
||||
# utils.remove(_tmp_)
|
||||
|
||||
def _make_py_ver_file(self):
|
||||
pass
|
||||
|
|
@ -250,7 +250,7 @@ class PYSLinux(PYSBase):
|
|||
|
||||
def _get_pip(self):
|
||||
_exec_path = os.path.dirname(env.py_exec)
|
||||
return os.path.join(_exec_path, 'pip')
|
||||
return os.path.join(_exec_path, 'pip3.7')
|
||||
|
||||
def _make_py_ver_file(self):
|
||||
# do nothing.
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@ void Bar::onMousePress(int x, int y, Qt::MouseButton button) {
|
|||
}
|
||||
}
|
||||
|
||||
void Bar::onMouseRelease(int x, int y, Qt::MouseButton button) {
|
||||
void Bar::onMouseRelease(int, int, Qt::MouseButton button) {
|
||||
// 我们只关心左键释放
|
||||
if(button != Qt::LeftButton)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -14,14 +14,10 @@
|
|||
// tp-player.exe path/contains/tp-rdp.tpr 包含 .tpr 文件的路径
|
||||
//
|
||||
// ## 从TP服务器上下载
|
||||
// (废弃) tp-player.exe "http://127.0.0.1:7190" 1234 "tp_1491560510_ca67fceb75a78c9d" "000000256-admin-administrator-218.244.140.14-20171209-020047"
|
||||
// (废弃) TP服务器地址 记录编号 session-id(仅授权用户可下载) 合成的名称,用于本地生成路径来存放下载的文件
|
||||
//
|
||||
// ## 从TP服务器上下载
|
||||
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意,并不直接访问此URI,实际上其并不存在)
|
||||
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
|
||||
// TP服务器地址(可能包含子路径,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
|
||||
// 按 “/” 进行分割后,去掉最后两个项,剩下部分是TP服务器的WEB地址,用于合成后续的文件下载URL。
|
||||
// 根据下载的.tpr文件内容,本地合成类似于 "000000256-admin-administrator-218.244.140.14-20171209-020047" 的路径来存放下载的文件
|
||||
// 根据下载的.tpr文件内容,本地合成类似于 "000000256-admin-administrator-123.45.77.88-20191109-020047" 的路径来存放下载的文件
|
||||
// 特别注意,如果账号是 domain\user 这种形式,需要将 "\" 替换为下划线,否则此符号作为路径分隔符,会导致路径不存在而无法保存下载的文件。
|
||||
// - 获取文件大小: http://127.0.0.1:7190/audit/get-file?act=size&type=rdp&rid=yyyyy&f=file-name
|
||||
// - 'act'为`size`表示获取文件大小(返回一个数字字符串,就是指定的文件大小)
|
||||
|
|
@ -41,9 +37,9 @@ void show_usage(QCommandLineParser& parser) {
|
|||
+ parser.helpText()
|
||||
+ "\n\n"
|
||||
+ "RESOURCE could be:\n"
|
||||
+ " teleport record file (.tpr).\n"
|
||||
+ " a directory contains .tpr file.\n"
|
||||
+ " an URL to download teleport record file."
|
||||
+ " - teleport record file (.tpr).\n"
|
||||
+ " - a directory contains .tpr file.\n"
|
||||
+ " - an URL to download teleport record file."
|
||||
+ "</pre></body></html>");
|
||||
}
|
||||
|
||||
|
|
@ -82,15 +78,6 @@ int main(int argc, char *argv[])
|
|||
|
||||
if(parser.isSet(opt_help)) {
|
||||
show_usage(parser);
|
||||
// QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
|
||||
// "<html><head/><body><pre>"
|
||||
// + parser.helpText()
|
||||
// + "\n\n"
|
||||
// + "RESOURCE could be:\n"
|
||||
// + " teleport record file (.tpr).\n"
|
||||
// + " a directory contains .tpr file.\n"
|
||||
// + " an URL for download teleport record file."
|
||||
// + "</pre></body></html>");
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
|
@ -104,10 +91,6 @@ int main(int argc, char *argv[])
|
|||
qDebug() << resource;
|
||||
|
||||
|
||||
// QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));
|
||||
// QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
|
||||
// QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
|
||||
|
||||
MainWindow w;
|
||||
w.set_resource(resource);
|
||||
w.show();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
m_play_state = PLAY_STATE_UNKNOWN;
|
||||
m_thr_data = nullptr;
|
||||
|
||||
m_disable_draw = false;
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->centralWidget->setMouseTracking(true);
|
||||
|
|
@ -99,7 +101,7 @@ void MainWindow::set_resource(const QString &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();
|
||||
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*)));
|
||||
|
|
@ -134,20 +136,6 @@ void MainWindow::paintEvent(QPaintEvent *e)
|
|||
painter.drawPixmap(m_pt.x-m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2, m_pt_normal);
|
||||
}
|
||||
|
||||
// {
|
||||
// QRect rc_draw = e->rect();
|
||||
// QRect rc(m_rc_message);
|
||||
// //rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
|
||||
// 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);
|
||||
// }
|
||||
|
||||
// 绘制浮动控制窗
|
||||
if(m_bar_fading) {
|
||||
painter.setOpacity(m_bar_opacity);
|
||||
|
|
@ -161,15 +149,16 @@ void MainWindow::paintEvent(QPaintEvent *e)
|
|||
if(m_show_message) {
|
||||
QRect rc_draw = e->rect();
|
||||
QRect rc(m_rc_message);
|
||||
//rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +176,6 @@ void MainWindow::resume(bool relocate, uint32_t ms) {
|
|||
m_thr_play->resume(relocate, ms);
|
||||
}
|
||||
else if(m_play_state == PLAY_STATE_STOP) {
|
||||
// _start_play_thread();
|
||||
m_thr_data->restart(ms);
|
||||
m_thr_play->resume(true, ms);
|
||||
}
|
||||
|
|
@ -214,11 +202,11 @@ void MainWindow::_do_update_data(UpdateData* dat) {
|
|||
return;
|
||||
}
|
||||
else if(dat->data_type() == TYPE_IMAGE) {
|
||||
UpdateImages uimgs;
|
||||
if(!dat->get_images(uimgs))
|
||||
const UpdateImages uimgs = dat->get_images();
|
||||
if(uimgs.size() == 0)
|
||||
return;
|
||||
|
||||
if(uimgs.size() > 1) {
|
||||
if(uimgs.size() > 1 && !m_disable_draw) {
|
||||
// 禁止界面更新
|
||||
setUpdatesEnabled(false);
|
||||
}
|
||||
|
|
@ -227,11 +215,13 @@ void MainWindow::_do_update_data(UpdateData* dat) {
|
|||
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);
|
||||
update(uimgs[i].x, uimgs[i].y, uimgs[i].w, uimgs[i].h);
|
||||
|
||||
if(!m_disable_draw)
|
||||
update(uimgs[i].x, uimgs[i].y, uimgs[i].w, uimgs[i].h);
|
||||
}
|
||||
|
||||
|
||||
if(uimgs.size() > 1) {
|
||||
if(uimgs.size() > 1 && !m_disable_draw) {
|
||||
// 允许界面更新
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
|
|
@ -246,13 +236,16 @@ void MainWindow::_do_update_data(UpdateData* dat) {
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -265,10 +258,6 @@ void MainWindow::_do_update_data(UpdateData* dat) {
|
|||
m_show_message = true;
|
||||
|
||||
qDebug("1message, w=%d, h=%d", m_canvas.width(), m_canvas.height());
|
||||
// if(0 == m_canvas.width()) {
|
||||
// QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message());
|
||||
// return;
|
||||
// }
|
||||
|
||||
QPainter pp(&m_canvas);
|
||||
QRect rcWin(0, 0, m_canvas.width(), m_canvas.height());
|
||||
|
|
@ -318,22 +307,13 @@ void MainWindow::_do_update_data(UpdateData* dat) {
|
|||
|
||||
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")";
|
||||
|
||||
//if(m_canvas.width() != m_rec_hdr.basic.width && m_canvas.height() != m_rec_hdr.basic.height) {
|
||||
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
|
||||
//m_win_board_w = frameGeometry().width() - geometry().width();
|
||||
//m_win_board_h = frameGeometry().height() - geometry().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);
|
||||
|
||||
QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以
|
||||
qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height());
|
||||
//move((desktop->width() - this->width())/2, (desktop->height() - this->height())/2);
|
||||
move(10, (desktop->height() - m_rec_hdr.basic.height)/2);
|
||||
|
||||
//setFixedSize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
|
||||
//resize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
|
||||
//resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
//}
|
||||
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
|
||||
m_canvas.fill(QColor(38, 73, 111));
|
||||
|
||||
|
|
@ -352,13 +332,11 @@ void MainWindow::_do_update_data(UpdateData* dat) {
|
|||
|
||||
QString title;
|
||||
if (m_rec_hdr.basic.conn_port == 3389) {
|
||||
// title = QString(LOCAL8BIT("[%1] %2@%3 [Teleport-RDP录像回放]").arg(m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip));
|
||||
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 [Teleport-RDP录像回放]").arg(m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, _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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ private:
|
|||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
|
||||
// void _start_play_thread();
|
||||
|
||||
private slots:
|
||||
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)
|
||||
void _do_update_data(UpdateData*);
|
||||
|
|
@ -86,6 +84,7 @@ private:
|
|||
bool m_show_message;
|
||||
QPixmap m_img_message;
|
||||
QRect m_rc_message;
|
||||
bool m_disable_draw;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#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)
|
||||
|
||||
|
|
@ -27,7 +28,6 @@ typedef struct TS_RECORD_HEADER_INFO {
|
|||
// 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)
|
||||
|
||||
|
|
@ -49,15 +49,14 @@ typedef struct TS_RECORD_HEADER_BASIC {
|
|||
|
||||
// // 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_INFO info;
|
||||
uint8_t _reserve1[64 - ts_record_header_info_size];
|
||||
TS_RECORD_HEADER_BASIC basic;
|
||||
uint8_t _reserve2[512 - 64 - ts_record_header_basic_size];
|
||||
}TS_RECORD_HEADER;
|
||||
|
||||
// header部分(header-info + header-basic) = 512B
|
||||
|
|
@ -66,10 +65,9 @@ typedef struct 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数量匹配)
|
||||
uint8_t _reserve[3]; // 保留
|
||||
}TS_RECORD_PKG;
|
||||
|
||||
|
||||
|
|
@ -92,6 +90,7 @@ typedef struct TS_RECORD_RDP_IMAGE_INFO {
|
|||
uint8_t format;
|
||||
uint8_t _reserved;
|
||||
uint32_t dat_len;
|
||||
uint32_t zip_len;
|
||||
}TS_RECORD_RDP_IMAGE_INFO;
|
||||
|
||||
// 关键帧索引
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <QStandardPaths>
|
||||
#include <qcoreapplication.h>
|
||||
#include <inttypes.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "thr_play.h"
|
||||
#include "thr_data.h"
|
||||
|
|
@ -15,6 +16,80 @@
|
|||
|
||||
#include "rle.h"
|
||||
|
||||
|
||||
static QImage* _rdpimg2QImage(int w, int h, int bitsPerPixel, bool isCompressed, const uint8_t* dat, uint32_t len) {
|
||||
QImage* out;
|
||||
switch(bitsPerPixel) {
|
||||
case 15:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
|
||||
if(!bitmap_decompress1(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
return nullptr;
|
||||
}
|
||||
out = new QImage(_dat, w, h, QImage::Format_RGB555);
|
||||
free(_dat);
|
||||
}
|
||||
else {
|
||||
out = new QImage(QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
|
||||
}
|
||||
return out;
|
||||
|
||||
case 16:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
|
||||
if(!bitmap_decompress2(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
qDebug() << "22------------------DECOMPRESS2 failed.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: 这里需要进一步优化,直接操作QImage的buffer。
|
||||
out = new QImage(w, h, QImage::Format_RGB16);
|
||||
for(int y = 0; y < h; y++) {
|
||||
for(int x = 0; x < w; x++) {
|
||||
uint16 a = ((uint16*)_dat)[y * w + x];
|
||||
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
|
||||
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
|
||||
uint8 b = (a & 0x001f) * 255 / 31;
|
||||
out->setPixelColor(x, y, QColor(r,g,b));
|
||||
}
|
||||
}
|
||||
free(_dat);
|
||||
return out;
|
||||
}
|
||||
else {
|
||||
out = new QImage(QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
|
||||
}
|
||||
return out;
|
||||
|
||||
case 24:
|
||||
case 32:
|
||||
default:
|
||||
qDebug() << "--------NOT support 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
|
||||
//=================================================================
|
||||
|
|
@ -186,9 +261,9 @@ void ThrData::_run() {
|
|||
pkg_count_in_queue = m_data.size();
|
||||
m_locker.unlock();
|
||||
|
||||
// 少于500个的话,补足到1000个
|
||||
if(m_data.size() < 500)
|
||||
pkg_need_add = 1000 - pkg_count_in_queue;
|
||||
// 少于1000个的话,补足到2000个
|
||||
if(m_data.size() < 1000)
|
||||
pkg_need_add = 2000 - pkg_count_in_queue;
|
||||
|
||||
if(pkg_need_add == 0) {
|
||||
msleep(100);
|
||||
|
|
@ -236,7 +311,6 @@ void ThrData::_run() {
|
|||
file_processed = 0;
|
||||
qDebug("Open file tp-rdp-%d.tpd, processed: %" PRId64 ", size: %" PRId64, m_file_idx+1, file_processed, file_size);
|
||||
}
|
||||
// qDebug("B processed: %" PRId64 ", size: %" PRId64, file_processed, file_size);
|
||||
|
||||
// 如果指定了起始偏移,则跳过这部分数据
|
||||
if(m_offset > 0) {
|
||||
|
|
@ -256,8 +330,6 @@ void ThrData::_run() {
|
|||
|
||||
TS_RECORD_PKG pkg;
|
||||
read_len = fdata->read(reinterpret_cast<char*>(&pkg), sizeof(TS_RECORD_PKG));
|
||||
// if(read_len == 0)
|
||||
// break;
|
||||
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));
|
||||
|
|
@ -283,13 +355,22 @@ void ThrData::_run() {
|
|||
}
|
||||
file_processed += pkg.size;
|
||||
|
||||
UpdateData* dat = new UpdateData(m_hdr.basic.width, m_hdr.basic.height);
|
||||
if(!dat->parse(pkg, pkg_data)) {
|
||||
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);
|
||||
|
|
@ -312,7 +393,7 @@ void ThrData::_run() {
|
|||
}
|
||||
|
||||
// 让线程调度器让播放线程有机会执行
|
||||
msleep(1);
|
||||
// msleep(1);
|
||||
|
||||
// 如果此文件已经处理完毕,则关闭文件,这样下次处理一个新的文件
|
||||
if(file_processed >= file_size) {
|
||||
|
|
@ -333,6 +414,147 @@ void ThrData::_run() {
|
|||
}
|
||||
}
|
||||
|
||||
UpdateData* ThrData::_parse(const TS_RECORD_PKG& pkg, const QByteArray& data) {
|
||||
if(pkg.type == TS_RECORD_TYPE_RDP_POINTER) {
|
||||
if(data.size() != sizeof(TS_RECORD_RDP_POINTER))
|
||||
return nullptr;
|
||||
|
||||
UpdateData* ud = new UpdateData();
|
||||
ud->set_pointer(pkg.time_ms, reinterpret_cast<const TS_RECORD_RDP_POINTER*>(data.data()));
|
||||
return ud;
|
||||
}
|
||||
else if(pkg.type == TS_RECORD_TYPE_RDP_IMAGE) {
|
||||
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
|
||||
|
||||
if(data.size() < static_cast<int>(sizeof(uint16_t) + sizeof(TS_RECORD_RDP_IMAGE_INFO))) {
|
||||
delete ud;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* dat_ptr = reinterpret_cast<const uint8_t*>(data.data());
|
||||
|
||||
uint16_t count = (reinterpret_cast<const uint16_t*>(dat_ptr))[0];
|
||||
uint32_t offset = sizeof(uint16_t);
|
||||
|
||||
UpdateImages& imgs = ud->get_images();
|
||||
|
||||
for(uint16_t i = 0; i < count; ++i) {
|
||||
|
||||
const TS_RECORD_RDP_IMAGE_INFO* info = reinterpret_cast<const TS_RECORD_RDP_IMAGE_INFO*>(dat_ptr+offset);
|
||||
offset += sizeof(TS_RECORD_RDP_IMAGE_INFO);
|
||||
|
||||
if(info->format != TS_RDP_IMG_ALT) {
|
||||
const uint8_t* img_dat = dat_ptr + offset;
|
||||
|
||||
const uint8_t* real_img_dat = nullptr;
|
||||
QByteArray unzip_data;
|
||||
if(info->zip_len > 0) {
|
||||
// 数据被压缩了,需要解压缩
|
||||
unzip_data.resize(static_cast<int>(info->dat_len));
|
||||
|
||||
uLong u_len = info->dat_len;
|
||||
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, img_dat, info->zip_len);
|
||||
if(err != Z_OK || u_len != info->dat_len) {
|
||||
qDebug("image uncompress failed. err=%d.", err);
|
||||
}
|
||||
else {
|
||||
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
|
||||
}
|
||||
|
||||
offset += info->zip_len;
|
||||
}
|
||||
else {
|
||||
real_img_dat = img_dat;
|
||||
offset += info->dat_len;
|
||||
}
|
||||
|
||||
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = info->destLeft;
|
||||
uimg.y = info->destTop;
|
||||
uimg.w = info->destRight - info->destLeft + 1;
|
||||
uimg.h = info->destBottom - info->destTop + 1;
|
||||
if(real_img_dat)
|
||||
uimg.img = _rdpimg2QImage(info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, real_img_dat, info->dat_len);
|
||||
else
|
||||
uimg.img = nullptr;
|
||||
imgs.push_back(uimg);
|
||||
|
||||
QImage* cache_img = nullptr;
|
||||
if(uimg.img != nullptr)
|
||||
cache_img = new QImage(*uimg.img);
|
||||
|
||||
m_cache_imgs.push_back(cache_img);
|
||||
}
|
||||
else {
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = info->destLeft;
|
||||
uimg.y = info->destTop;
|
||||
uimg.w = info->destRight - info->destLeft + 1;
|
||||
uimg.h = info->destBottom - info->destTop + 1;
|
||||
|
||||
size_t cache_idx = info->dat_len;
|
||||
|
||||
if(cache_idx >= m_cache_imgs.size() || m_cache_imgs[cache_idx] == nullptr) {
|
||||
uimg.img = nullptr;
|
||||
}
|
||||
else {
|
||||
uimg.img = new QImage(*m_cache_imgs[cache_idx]);
|
||||
}
|
||||
imgs.push_back(uimg);
|
||||
}
|
||||
}
|
||||
|
||||
return ud;
|
||||
}
|
||||
else if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
|
||||
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
|
||||
const TS_RECORD_RDP_KEYFRAME_INFO* info = reinterpret_cast<const TS_RECORD_RDP_KEYFRAME_INFO*>(data.data());
|
||||
const uint8_t* data_buf = reinterpret_cast<const uint8_t*>(data.data() + sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
uint32_t data_len = data.size() - sizeof(TS_RECORD_RDP_KEYFRAME_INFO);
|
||||
|
||||
UpdateImages& imgs = ud->get_images();
|
||||
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = 0;
|
||||
uimg.y = 0;
|
||||
uimg.w = m_hdr.basic.width;
|
||||
uimg.h = m_hdr.basic.height;
|
||||
|
||||
const uint8_t* real_img_dat = nullptr;
|
||||
uint32_t real_img_len = m_hdr.basic.width * m_hdr.basic.height * 2;
|
||||
|
||||
QByteArray unzip_data;
|
||||
if(data_len != real_img_len) {
|
||||
// 数据被压缩了,需要解压缩
|
||||
unzip_data.resize(static_cast<int>(real_img_len));
|
||||
|
||||
uLong u_len = real_img_len;
|
||||
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, data_buf, data_len);
|
||||
if(err != Z_OK || u_len != real_img_len) {
|
||||
qDebug("keyframe uncompress failed. err=%d.", err);
|
||||
}
|
||||
else {
|
||||
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
real_img_dat = data_buf;
|
||||
}
|
||||
|
||||
if(real_img_dat != nullptr)
|
||||
uimg.img = _raw2QImage(m_hdr.basic.width, m_hdr.basic.height, real_img_dat, real_img_len);
|
||||
else
|
||||
uimg.img = nullptr;
|
||||
imgs.push_back(uimg);
|
||||
|
||||
return ud;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void ThrData::restart(uint32_t start_ms) {
|
||||
qDebug("restart at %ld ms", start_ms);
|
||||
// 让处理线程处理完当前循环,然后等待
|
||||
|
|
@ -441,19 +663,19 @@ bool ThrData::_load_keyframe() {
|
|||
}
|
||||
|
||||
qint64 fsize = f_kf.size();
|
||||
if(!fsize || fsize % sizeof(KEYFRAME_INFO) != 0) {
|
||||
if(!fsize || fsize % sizeof(TS_RECORD_RDP_KEYFRAME_INFO) != 0) {
|
||||
qDebug() << "Can not open " << tpk_fname << " for read.";
|
||||
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 read_len = 0;
|
||||
int kf_count = static_cast<int>(fsize / sizeof(KEYFRAME_INFO));
|
||||
int kf_count = static_cast<int>(fsize / sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
for(int i = 0; i < kf_count; ++i) {
|
||||
KEYFRAME_INFO kf;
|
||||
memset(&kf, 0, sizeof(KEYFRAME_INFO));
|
||||
read_len = f_kf.read(reinterpret_cast<char*>(&kf), sizeof(KEYFRAME_INFO));
|
||||
if(read_len != sizeof(KEYFRAME_INFO)) {
|
||||
TS_RECORD_RDP_KEYFRAME_INFO kf;
|
||||
memset(&kf, 0, sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
read_len = f_kf.read(reinterpret_cast<char*>(&kf), sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
if(read_len != sizeof(TS_RECORD_RDP_KEYFRAME_INFO)) {
|
||||
qDebug() << "invaid .tpk file.";
|
||||
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
|
||||
return false;
|
||||
|
|
@ -465,20 +687,11 @@ bool ThrData::_load_keyframe() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ThrData::_prepare() {
|
||||
UpdateData* d = new UpdateData(TYPE_HEADER_INFO);
|
||||
|
||||
m_locker.lock();
|
||||
m_data.enqueue(d);
|
||||
m_locker.unlock();
|
||||
}
|
||||
|
||||
UpdateData* ThrData::get_data() {
|
||||
UpdateData* d = nullptr;
|
||||
|
||||
m_locker.lock();
|
||||
if(m_data.size() > 0) {
|
||||
// qDebug("get_data(), left: %d", m_data.size());
|
||||
d = m_data.dequeue();
|
||||
}
|
||||
m_locker.unlock();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <QNetworkReply>
|
||||
#include <QFile>
|
||||
#include <QEventLoop>
|
||||
#include <QImage>
|
||||
#include "update_data.h"
|
||||
#include "record_format.h"
|
||||
#include "thr_download.h"
|
||||
|
|
@ -33,13 +34,9 @@
|
|||
这样,下次需要下载指定文件时,如果发现对应的临时文件存在,可以根据已下载字节数,继续下载。
|
||||
*/
|
||||
|
||||
typedef struct KEYFRAME_INFO {
|
||||
uint32_t time_ms; // 此关键帧的时间点
|
||||
uint32_t file_index; // 此关键帧图像数据位于哪一个数据文件中
|
||||
uint32_t offset; // 此关键帧图像数据在数据文件中的偏移
|
||||
}KEYFRAME_INFO;
|
||||
typedef std::vector<TS_RECORD_RDP_KEYFRAME_INFO> KeyFrames;
|
||||
|
||||
typedef std::vector<KEYFRAME_INFO> KeyFrames;
|
||||
typedef std::vector<QImage*> CachedImages;
|
||||
|
||||
class MainWindow;
|
||||
|
||||
|
|
@ -67,7 +64,9 @@ private:
|
|||
bool _load_keyframe();
|
||||
|
||||
void _clear_data();
|
||||
void _prepare();
|
||||
// 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);
|
||||
|
|
@ -102,8 +101,7 @@ private:
|
|||
uint32_t m_file_idx;
|
||||
uint32_t m_offset;
|
||||
|
||||
// bool m_xxx;
|
||||
// int m_restart_kf_idx;
|
||||
CachedImages m_cache_imgs;
|
||||
};
|
||||
|
||||
#endif // THR_DATA_H
|
||||
|
|
|
|||
|
|
@ -36,19 +36,9 @@ void ThrPlay::stop() {
|
|||
if(!isRunning())
|
||||
return;
|
||||
|
||||
// warning: never call stop() inside thread::run() loop.
|
||||
|
||||
m_need_stop = true;
|
||||
wait();
|
||||
qDebug() << "play-thread end.";
|
||||
|
||||
// if(m_thr_data) {
|
||||
// m_thr_data->stop();
|
||||
// qDebug("delete thrData.");
|
||||
// //m_thr_download->wait();
|
||||
// delete m_thr_data;
|
||||
// m_thr_data = nullptr;
|
||||
// }
|
||||
}
|
||||
|
||||
void ThrPlay::_notify_message(const QString& msg) {
|
||||
|
|
@ -144,6 +134,7 @@ void ThrPlay::run() {
|
|||
delete dat;
|
||||
dat = nullptr;
|
||||
UpdateData* _disable = new UpdateData(TYPE_DISABLE_DRAW);
|
||||
msleep(500);
|
||||
emit signal_update_data(_disable);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ TARGET = tp-player
|
|||
|
||||
QT += core gui widgets network
|
||||
|
||||
#DEFINES += QT_NO_DEBUG_OUTPUT
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
bar.h \
|
||||
|
|
@ -37,3 +35,24 @@ RC_FILE += \
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,81 +1,9 @@
|
|||
#include "update_data.h"
|
||||
#include "rle.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
static QImage* _rdpimg2QImage(int w, int h, int bitsPerPixel, bool isCompressed, const uint8_t* dat, uint32_t len) {
|
||||
QImage* out;
|
||||
switch(bitsPerPixel) {
|
||||
case 15:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
|
||||
if(!bitmap_decompress1(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
return nullptr;
|
||||
}
|
||||
out = new QImage(_dat, w, h, QImage::Format_RGB555);
|
||||
free(_dat);
|
||||
}
|
||||
else {
|
||||
out = new QImage(QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
|
||||
}
|
||||
return out;
|
||||
|
||||
case 16:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
|
||||
if(!bitmap_decompress2(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
qDebug() << "22------------------DECOMPRESS2 failed.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: 这里需要进一步优化,直接操作QImage的buffer。
|
||||
out = new QImage(w, h, QImage::Format_RGB16);
|
||||
for(int y = 0; y < h; y++) {
|
||||
for(int x = 0; x < w; x++) {
|
||||
uint16 a = ((uint16*)_dat)[y * w + x];
|
||||
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
|
||||
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
|
||||
uint8 b = (a & 0x001f) * 255 / 31;
|
||||
out->setPixelColor(x, y, QColor(r,g,b));
|
||||
}
|
||||
}
|
||||
free(_dat);
|
||||
return out;
|
||||
}
|
||||
else {
|
||||
out = new QImage(QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
|
||||
}
|
||||
return out;
|
||||
|
||||
case 24:
|
||||
case 32:
|
||||
default:
|
||||
qDebug() << "--------NOT support 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;
|
||||
}
|
||||
|
||||
UpdateData::UpdateData() : QObject(nullptr)
|
||||
{
|
||||
_init();
|
||||
|
|
@ -87,6 +15,13 @@ UpdateData::UpdateData(int data_type) : QObject(nullptr)
|
|||
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();
|
||||
|
|
@ -95,25 +30,14 @@ UpdateData::UpdateData(const TS_RECORD_HEADER& hdr) : QObject(nullptr)
|
|||
memcpy(m_hdr, &hdr, sizeof(TS_RECORD_HEADER));
|
||||
}
|
||||
|
||||
UpdateData::UpdateData(uint16_t screen_w, uint16_t screen_h) {
|
||||
_init();
|
||||
m_screen_w = screen_w;
|
||||
m_screen_h = screen_h;
|
||||
}
|
||||
|
||||
void UpdateData::_init() {
|
||||
m_data_type = TYPE_UNKNOWN;
|
||||
m_hdr = nullptr;
|
||||
m_pointer = nullptr;
|
||||
// m_img = nullptr;
|
||||
// m_img_info = nullptr;
|
||||
|
||||
m_data_buf = nullptr;
|
||||
m_data_len = 0;
|
||||
m_time_ms = 0;
|
||||
|
||||
m_screen_w = 0;
|
||||
m_screen_h = 0;
|
||||
}
|
||||
|
||||
UpdateData::~UpdateData() {
|
||||
|
|
@ -121,10 +45,6 @@ UpdateData::~UpdateData() {
|
|||
delete m_hdr;
|
||||
if(m_pointer)
|
||||
delete m_pointer;
|
||||
// if(m_img)
|
||||
// delete m_img;
|
||||
// if(m_img_info)
|
||||
// delete m_img_info;
|
||||
for(int i = 0; i < m_images.size(); ++i) {
|
||||
delete m_images[i].img;
|
||||
}
|
||||
|
|
@ -134,87 +54,13 @@ UpdateData::~UpdateData() {
|
|||
delete m_data_buf;
|
||||
}
|
||||
|
||||
bool UpdateData::parse(const TS_RECORD_PKG& pkg, const QByteArray& data) {
|
||||
m_time_ms = pkg.time_ms;
|
||||
|
||||
if(pkg.type == TS_RECORD_TYPE_RDP_POINTER) {
|
||||
m_data_type = TYPE_POINTER;
|
||||
if(data.size() != sizeof(TS_RECORD_RDP_POINTER))
|
||||
return false;
|
||||
m_pointer = new TS_RECORD_RDP_POINTER;
|
||||
memcpy(m_pointer, data.data(), sizeof(TS_RECORD_RDP_POINTER));
|
||||
return true;
|
||||
}
|
||||
else if(pkg.type == TS_RECORD_TYPE_RDP_IMAGE) {
|
||||
m_data_type = TYPE_IMAGE;
|
||||
if(data.size() <= static_cast<int>(sizeof(uint16_t) + sizeof(TS_RECORD_RDP_IMAGE_INFO)))
|
||||
return false;
|
||||
|
||||
const uint8_t* dat_ptr = reinterpret_cast<const uint8_t*>(data.data());
|
||||
|
||||
uint16_t count = (reinterpret_cast<const uint16_t*>(dat_ptr))[0];
|
||||
uint32_t offset = sizeof(uint16_t);
|
||||
|
||||
for(uint16_t i = 0; i < count; ++i) {
|
||||
|
||||
const TS_RECORD_RDP_IMAGE_INFO* info = reinterpret_cast<const TS_RECORD_RDP_IMAGE_INFO*>(dat_ptr+offset);
|
||||
offset += sizeof(TS_RECORD_RDP_IMAGE_INFO);
|
||||
//const uint8_t* img_dat = reinterpret_cast<const uint8_t*>(data.data() + sizeof(TS_RECORD_RDP_IMAGE_INFO));
|
||||
//uint32_t img_len = data.size() - sizeof(TS_RECORD_RDP_IMAGE_INFO);
|
||||
const uint8_t* img_dat = dat_ptr + offset;
|
||||
offset += info->dat_len;
|
||||
|
||||
|
||||
QImage* img = _rdpimg2QImage(info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, img_dat, info->dat_len);
|
||||
if(img == nullptr)
|
||||
return false;
|
||||
|
||||
// m_img = img;
|
||||
// m_img_x = info->destLeft;
|
||||
// m_img_y = info->destTop;
|
||||
// m_img_w = info->destRight - info->destLeft + 1;
|
||||
// m_img_h = info->destBottom - info->destTop + 1;
|
||||
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;
|
||||
uimg.img = img;
|
||||
m_images.push_back(uimg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
|
||||
m_data_type = TYPE_IMAGE;
|
||||
// const TS_RECORD_RDP_KEYFRAME_INFO* info = reinterpret_cast<const TS_RECORD_RDP_KEYFRAME_INFO*>(data.data());
|
||||
const uint8_t* img_dat = reinterpret_cast<const uint8_t*>(data.data() + sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
uint32_t img_len = data.size() - sizeof(TS_RECORD_RDP_KEYFRAME_INFO);
|
||||
|
||||
QImage* img = _raw2QImage((int)m_screen_w, (int)m_screen_h, img_dat, img_len);
|
||||
if(img == nullptr)
|
||||
return false;
|
||||
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = 0;
|
||||
uimg.y = 0;
|
||||
uimg.w = m_screen_w;
|
||||
uimg.h = m_screen_h;
|
||||
uimg.img = img;
|
||||
m_images.push_back(uimg);
|
||||
|
||||
// m_img = img;
|
||||
// m_img_x = 0;
|
||||
// m_img_y = 0;
|
||||
// m_img_w = m_screen_w;
|
||||
// m_img_h = m_screen_h;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -37,29 +37,16 @@ class UpdateData : public QObject
|
|||
public:
|
||||
explicit UpdateData();
|
||||
explicit UpdateData(int data_type);
|
||||
explicit UpdateData(int data_type, uint32_t time_ms);
|
||||
explicit UpdateData(const TS_RECORD_HEADER& hdr);
|
||||
explicit UpdateData(uint16_t screen_w, uint16_t screen_h);
|
||||
virtual ~UpdateData();
|
||||
|
||||
bool parse(const TS_RECORD_PKG& pkg, const QByteArray& data);
|
||||
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;}
|
||||
// bool get_image(QImage** img, int& x, int& y, int& w, int& h) {
|
||||
// if(m_img == nullptr)
|
||||
// return false;
|
||||
// *img = m_img;
|
||||
// x = m_img_x;
|
||||
// y = m_img_y;
|
||||
// w = m_img_w;
|
||||
// h = m_img_h;
|
||||
// return true;
|
||||
// }
|
||||
bool get_images(UpdateImages& uimgs) const {
|
||||
if(m_images.size() == 0)
|
||||
return false;
|
||||
uimgs = m_images;
|
||||
return true;
|
||||
}
|
||||
UpdateImages& get_images() {return m_images;}
|
||||
const UpdateImages& get_images() const {return m_images;}
|
||||
|
||||
uint32_t get_time() {return m_time_ms;}
|
||||
|
||||
|
|
@ -98,17 +85,7 @@ private:
|
|||
// for POINTER
|
||||
TS_RECORD_RDP_POINTER* m_pointer;
|
||||
// for IMAGE
|
||||
// QImage* m_img;
|
||||
// int m_img_x;
|
||||
// int m_img_y;
|
||||
// int m_img_w;
|
||||
// int m_img_h;
|
||||
UpdateImages m_images;
|
||||
|
||||
// TS_RECORD_RDP_IMAGE_INFO* m_img_info;
|
||||
|
||||
uint16_t m_screen_w;
|
||||
uint16_t m_screen_h;
|
||||
};
|
||||
|
||||
class UpdateDataHelper {
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.3.1</string>
|
||||
<string>3.5.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.3.1</string>
|
||||
<string>3.5.1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -1 +1,14 @@
|
|||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <ex.h>
|
||||
|
||||
// #ifdef EX_DEBUG
|
||||
// // # pragma comment(lib, "libssl32MTd.lib")
|
||||
// // # pragma comment(lib, "libcrypto32MTd.lib")
|
||||
// #else
|
||||
// # pragma comment(lib, "libssl32MT.lib")
|
||||
// # pragma comment(lib, "libcrypto32MT.lib")
|
||||
// #endif
|
||||
|
||||
# pragma comment(lib, "libssl.lib")
|
||||
# pragma comment(lib, "libcrypto.lib")
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -61,13 +61,14 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;_DEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
|
|
@ -79,7 +80,7 @@
|
|||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;NDEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
|
@ -87,7 +88,8 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "ts_cfg.h"
|
||||
#include "ts_env.h"
|
||||
|
||||
|
|
@ -29,8 +29,12 @@ bool TsCfg::save(const ex_astr& new_value)
|
|||
if (!_load(new_value))
|
||||
return false;
|
||||
|
||||
Json::StyledWriter jwriter;
|
||||
ex_astr val = jwriter.write(m_root);
|
||||
//Json::StyledWriter jwriter;
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(m_root, &os);
|
||||
ex_astr val = os.str();
|
||||
|
||||
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
|
||||
EXLOGE("can not save config file.\n");
|
||||
|
|
@ -41,10 +45,14 @@ bool TsCfg::save(const ex_astr& new_value)
|
|||
}
|
||||
|
||||
bool TsCfg::_load(const ex_astr& str_json) {
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = str_json.c_str();
|
||||
|
||||
if (!jreader.parse(str_json.c_str(), m_root)) {
|
||||
EXLOGE("can not parse new config data, not in json format? %s\n", jreader.getFormattedErrorMessages().c_str());
|
||||
ex_astr err;
|
||||
if (!jreader->parse(str_json_begin, str_json_begin + str_json.length(), &m_root, &err)) {
|
||||
EXLOGE("can not parse new config data, not in json format? %s\n", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "ts_env.h"
|
||||
|
||||
#include <time.h>
|
||||
|
|
@ -40,8 +40,8 @@ bool TsEnv::init(void)
|
|||
m_site_path = m_exec_path;
|
||||
ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL);
|
||||
|
||||
m_tools_path = m_exec_path;
|
||||
ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
|
||||
// m_tools_path = m_exec_path;
|
||||
// ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
|
||||
|
||||
cfg_default = m_exec_path;
|
||||
ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL);
|
||||
|
|
@ -50,13 +50,16 @@ bool TsEnv::init(void)
|
|||
m_site_path = m_exec_path;
|
||||
ex_path_join(m_site_path, false, L"site", NULL);
|
||||
|
||||
m_tools_path = m_exec_path;
|
||||
ex_path_join(m_tools_path, false, L"tools", NULL);
|
||||
// m_tools_path = m_exec_path;
|
||||
// ex_path_join(m_tools_path, false, L"tools", NULL);
|
||||
|
||||
cfg_default = m_exec_path;
|
||||
ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL);
|
||||
#endif
|
||||
|
||||
m_tools_path = m_exec_path;
|
||||
ex_path_join(m_tools_path, false, L"tools", NULL);
|
||||
|
||||
if (!ex_is_file_exists(m_cfg_file.c_str())) {
|
||||
ex_wstr cfg_path = m_exec_path;
|
||||
ex_path_join(cfg_path, false, L"cfg", NULL);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
#pragma warning(disable:4091)
|
||||
|
||||
|
|
@ -17,22 +17,22 @@
|
|||
|
||||
/*
|
||||
1.
|
||||
SecureCRT支持设置标签页的标题,命令行参数 /N "tab name"就可以
|
||||
SecureCRT支持设置标签页的标题,命令行参数 /N "tab name"就可以
|
||||
Example:
|
||||
To launch a new Telnet session, displaying the name "Houston, TX" on the tab, use the following:
|
||||
/T /N "Houston, TX" /TELNET 192.168.0.6
|
||||
|
||||
2.
|
||||
多次启动的SecureCRT放到一个窗口的不同标签页中,使用参数: /T
|
||||
多次启动的SecureCRT放到一个窗口的不同标签页中,使用参数: /T
|
||||
SecureCRT.exe /T /N "TP#ssh://192.168.1.3" /SSH2 /L root /PASSWORD 1234 120.26.109.25
|
||||
|
||||
3.
|
||||
telnet客户端的启动:
|
||||
telnet客户端的启动:
|
||||
putty.exe telnet://administrator@127.0.0.1:52389
|
||||
如果是SecureCRT,则需要
|
||||
如果是SecureCRT,则需要
|
||||
SecureCRT.exe /T /N "TP#telnet://192.168.1.3" /SCRIPT X:\path\to\startup.vbs /TELNET 127.0.0.1 52389
|
||||
其中,startup.vbs的内容为:
|
||||
---------文件开始---------
|
||||
其中,startup.vbs的内容为:
|
||||
---------文件开始---------
|
||||
#$language = "VBScript"
|
||||
#$interface = "1.0"
|
||||
Sub main
|
||||
|
|
@ -41,11 +41,11 @@ Sub main
|
|||
crt.Screen.Send "SESSION-ID" & VbCr
|
||||
crt.Screen.Synchronous = False
|
||||
End Sub
|
||||
---------文件结束---------
|
||||
---------文件结束---------
|
||||
|
||||
4. 为了让putty的窗口标签显示正常的IP,可以尝试在连接成功后,主动向服务端发送下列命令:
|
||||
4. 为了让putty的窗口标签显示正常的IP,可以尝试在连接成功后,主动向服务端发送下列命令:
|
||||
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@192.168.1.2: \w\a\]$PS1"
|
||||
手工测试了,ubuntu服务器可以,不知道是否能够支持所有的Linux。SecureCRT对此表示忽略。
|
||||
手工测试了,ubuntu服务器可以,不知道是否能够支持所有的Linux。SecureCRT对此表示忽略。
|
||||
*/
|
||||
|
||||
//#define RDP_CLIENT_SYSTEM_BUILTIN
|
||||
|
|
@ -67,14 +67,16 @@ desktopwidth:i:%d\n\
|
|||
desktopheight:i:%d\n\
|
||||
session bpp:i:16\n\
|
||||
winposstr:s:0,1,%d,%d,%d,%d\n\
|
||||
compression:i:1\n\
|
||||
bitmapcachepersistenable:i:1\n\
|
||||
bitmapcachesize:i:32000\n\
|
||||
compression:i:1\n\
|
||||
keyboardhook:i:2\n\
|
||||
audiocapturemode:i:0\n\
|
||||
videoplaybackmode:i:1\n\
|
||||
connection type:i:7\n\
|
||||
networkautodetect:i:1\n\
|
||||
bandwidthautodetect:i:1\n\
|
||||
disableclipboardredirection:i:0\n\
|
||||
displayconnectionbar:i:1\n\
|
||||
enableworkspacereconnect:i:0\n\
|
||||
disable wallpaper:i:1\n\
|
||||
|
|
@ -112,6 +114,17 @@ username:s:%s\n\
|
|||
password 51:b:%s\n\
|
||||
";
|
||||
|
||||
// https://www.donkz.nl/overview-rdp-file-settings/
|
||||
//
|
||||
// authentication level:i:2\n
|
||||
//
|
||||
//
|
||||
// negotiate security layer:i:1\n
|
||||
// 0 = negotiation is not enabled and the session is started by using Secure Sockets Layer (SSL).
|
||||
// 1 = negotiation is enabled and the session is started by using x.224 encryption.
|
||||
|
||||
|
||||
|
||||
//redirectdirectx:i:0\n\
|
||||
//prompt for credentials on client:i:0\n\
|
||||
|
||||
|
|
@ -210,7 +223,7 @@ bool calc_psw51b(const char* password, std::string& ret) {
|
|||
|
||||
bool isDegital(std::string str) {
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
if (str.at(i) == '-' && str.size() > 1) // 有可能出现负数
|
||||
if (str.at(i) == '-' && str.size() > 1) // 有可能出现负数
|
||||
continue;
|
||||
if (str.at(i) > '9' || str.at(i) < '0')
|
||||
return false;
|
||||
|
|
@ -377,7 +390,7 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
|
|||
bool b_is_html = false;
|
||||
|
||||
// if (uri == "/") {
|
||||
// ex_wstr page = L"<html lang=\"zh_CN\"><head><meta charset=\"utf-8\"/><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>Teleport助手</title>\n<style type=\"text/css\">\n.box{padding:20px;margin:40px;border:1px solid #78b17c;background-color:#e4ffe5;}\n</style>\n</head><body><div class=\"box\">Teleport助手工作正常!</div></body></html>";
|
||||
// ex_wstr page = L"<html lang=\"zh_CN\"><head><meta charset=\"utf-8\"/><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>Teleport助手</title>\n<style type=\"text/css\">\n.box{padding:20px;margin:40px;border:1px solid #78b17c;background-color:#e4ffe5;}\n</style>\n</head><body><div class=\"box\">Teleport助手工作正常!</div></body></html>";
|
||||
// ex_wstr2astr(page, ret_buf, EX_CODEPAGE_UTF8);
|
||||
//
|
||||
// mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n%s", ret_buf.size() - 1, &ret_buf[0]);
|
||||
|
|
@ -410,7 +423,7 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
|
|||
_this->_process_js_request(method, json_param, ret_buf);
|
||||
}
|
||||
|
||||
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: application/json\r\n\r\n%s", ret_buf.size() - 1, &ret_buf[0]);
|
||||
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %ld\r\nContent-Type: application/json\r\n\r\n%s", ret_buf.length(), &ret_buf[0]);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -478,7 +491,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
|
|||
|
||||
ex_astrs strs;
|
||||
|
||||
size_t pos_start = 1; // 跳过第一个字节,一定是 '/'
|
||||
size_t pos_start = 1; // 跳过第一个字节,一定是 '/'
|
||||
|
||||
size_t i = 0;
|
||||
for (i = pos_start; i < req->uri.len; ++i) {
|
||||
|
|
@ -488,7 +501,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
|
|||
tmp_uri.assign(req->uri.p + pos_start, i - pos_start);
|
||||
strs.push_back(tmp_uri);
|
||||
}
|
||||
pos_start = i + 1; // 跳过当前找到的分隔符
|
||||
pos_start = i + 1; // 跳过当前找到的分隔符
|
||||
}
|
||||
}
|
||||
if (pos_start < req->uri.len) {
|
||||
|
|
@ -522,7 +535,7 @@ int TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_as
|
|||
}
|
||||
|
||||
if (func_args.length() > 0) {
|
||||
// 将参数进行 url-decode 解码
|
||||
// 将参数进行 url-decode 解码
|
||||
int len = func_args.length() * 2;
|
||||
ex_chars sztmp;
|
||||
sztmp.resize(len);
|
||||
|
|
@ -558,24 +571,33 @@ void TsHttpRpc::_process_js_request(const ex_astr& func_cmd, const ex_astr& func
|
|||
}
|
||||
|
||||
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) {
|
||||
// 返回: {"code":123}
|
||||
// 返回: {"code":123}
|
||||
|
||||
Json::FastWriter jr_writer;
|
||||
Json::Value jr_root;
|
||||
|
||||
jr_root["code"] = errcode;
|
||||
buf = jr_writer.write(jr_root);
|
||||
|
||||
// buf = jr_writer.write(jr_root);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(jr_root, &os);
|
||||
buf = os.str();
|
||||
}
|
||||
|
||||
void TsHttpRpc::_create_json_ret(ex_astr& buf, Json::Value& jr_root) {
|
||||
Json::FastWriter jr_writer;
|
||||
buf = jr_writer.write(jr_root);
|
||||
// Json::FastWriter jr_writer;
|
||||
// buf = jr_writer.write(jr_root);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(jr_root, &os);
|
||||
buf = os.str();
|
||||
}
|
||||
|
||||
void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf)
|
||||
{
|
||||
//处理urlprotocol调用访式
|
||||
// 将参数进行 url-decode 解码
|
||||
//处理urlprotocol调用访式
|
||||
// 将参数进行 url-decode 解码
|
||||
std::string func_args = args;
|
||||
if (func_args.length() > 0)
|
||||
{
|
||||
|
|
@ -589,17 +611,17 @@ void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf)
|
|||
func_args = &sztmp[0];
|
||||
}
|
||||
EXLOGD(("%s\n"), func_args.c_str());
|
||||
//处理传参过来的teleport://{}/,只保留参数部份
|
||||
//处理传参过来的teleport://{}/,只保留参数部份
|
||||
std::string urlproto_appname = TP_URLPROTO_APP_NAME;
|
||||
urlproto_appname += "://{";
|
||||
func_args.erase(0, urlproto_appname.length());//去除第一个URLPROTO_APP_NAME以及://字符
|
||||
func_args.erase(0, urlproto_appname.length());//去除第一个URLPROTO_APP_NAME以及://字符
|
||||
int pos = func_args.length() - 1;
|
||||
if (func_args.substr(pos, 1) == "/")
|
||||
func_args.erase(pos - 1, 2);//去除最后一个}/字符
|
||||
func_args.erase(pos - 1, 2);//去除最后一个}/字符
|
||||
else
|
||||
func_args.erase(pos, 1);
|
||||
|
||||
//由于命令行、ie浏览器参数传递时会把原来json结构中的"号去掉,需要重新格式化参数为json格式
|
||||
//由于命令行、ie浏览器参数传递时会把原来json结构中的"号去掉,需要重新格式化参数为json格式
|
||||
if (func_args.find("\"", 0) == std::string::npos) {
|
||||
std::vector<std::string> strv;
|
||||
SplitString(func_args, strv, ",");
|
||||
|
|
@ -620,21 +642,27 @@ void TsHttpRpc::_rpc_func_url_protocol(const ex_astr& args, ex_astr& buf)
|
|||
}
|
||||
func_args = "{" + func_args + "}";
|
||||
EXLOGD(("%s\n"), func_args.c_str());
|
||||
//调用TsHttpRpc类里的_rpc_func_run_client启动客户端
|
||||
//调用TsHttpRpc类里的_rpc_func_run_client启动客户端
|
||||
_rpc_func_run_client(func_args, buf);
|
||||
}
|
||||
|
||||
void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
||||
// 入参:{"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2}
|
||||
// 入参:{"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2}
|
||||
// authmode: 1=password, 2=private-key
|
||||
// protocol: 1=rdp, 2=ssh
|
||||
// SSH返回: {"code":0, "data":{"sid":"0123abcde"}}
|
||||
// RDP返回: {"code":0, "data":{"sid":"0123abcde0A"}}
|
||||
// SSH返回: {"code":0, "data":{"sid":"0123abcde"}}
|
||||
// RDP返回: {"code":0, "data":{"sid":"0123abcde0A"}}
|
||||
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::Value jsRoot;
|
||||
|
||||
if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = func_args.c_str();
|
||||
ex_astr err;
|
||||
|
||||
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
|
||||
_create_json_ret(buf, TPE_JSON_FORMAT);
|
||||
return;
|
||||
}
|
||||
|
|
@ -643,7 +671,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
|||
return;
|
||||
}
|
||||
|
||||
// 判断参数是否正确
|
||||
// 判断参数是否正确
|
||||
if (!jsRoot["teleport_ip"].isString()
|
||||
|| !jsRoot["teleport_port"].isNumeric() || !jsRoot["remote_host_ip"].isString()
|
||||
|| !jsRoot["session_id"].isString() || !jsRoot["protocol_type"].isNumeric() || !jsRoot["protocol_sub_type"].isNumeric()
|
||||
|
|
@ -756,7 +784,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
|||
int iHeight = GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
if (rdp_w == 0 || rdp_h == 0) {
|
||||
//全屏
|
||||
//全屏
|
||||
width = iWidth;
|
||||
higth = iHeight;
|
||||
display = 2;
|
||||
|
|
@ -825,7 +853,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
|||
fclose(f);
|
||||
ex_astr2wstr(sz_file_name, tmp_rdp_file);
|
||||
|
||||
// 变量替换
|
||||
// 变量替换
|
||||
ex_replace_all(w_exe_path, _T("{tmp_rdp_file}"), tmp_rdp_file);
|
||||
} else if (g_cfg.rdp_name == L"freerdp") {
|
||||
w_exe_path += L"{size} {console} {clipboard} {drives} ";
|
||||
|
|
@ -834,7 +862,7 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
|||
ex_wstr w_screen;
|
||||
|
||||
if (rdp_w == 0 || rdp_h == 0) {
|
||||
//全屏
|
||||
//全屏
|
||||
w_screen = _T("/f");
|
||||
} else {
|
||||
char sz_size[64] = { 0 };
|
||||
|
|
@ -860,10 +888,10 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
|||
|
||||
w_sid = L"02" + w_sid;
|
||||
|
||||
w_exe_path += L" /gdi:sw"; // 使用软件渲染,gdi:hw使用硬件加速,但是会出现很多黑块(录像回放时又是正常的!)
|
||||
w_exe_path += L" -grab-keyboard"; // [new style] 防止启动FreeRDP后,失去本地键盘响应,必须得先最小化一下FreeRDP窗口(不过貌似不起作用)
|
||||
w_exe_path += L" /gdi:sw"; // 使用软件渲染,gdi:hw使用硬件加速,但是会出现很多黑块(录像回放时又是正常的!)
|
||||
w_exe_path += L" -grab-keyboard"; // [new style] 防止启动FreeRDP后,失去本地键盘响应,必须得先最小化一下FreeRDP窗口(不过貌似不起作用)
|
||||
|
||||
// 变量替换
|
||||
// 变量替换
|
||||
ex_replace_all(w_exe_path, _T("{size}"), w_screen);
|
||||
|
||||
if (flag_console && rdp_console)
|
||||
|
|
@ -941,22 +969,24 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf) {
|
|||
}
|
||||
|
||||
void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::Value jsRoot;
|
||||
|
||||
if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = func_args.c_str();
|
||||
ex_astr err;
|
||||
|
||||
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
|
||||
_create_json_ret(buf, TPE_JSON_FORMAT);
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断参数是否正确
|
||||
// 判断参数是否正确
|
||||
if (!jsRoot["rid"].isInt()
|
||||
|| !jsRoot["web"].isString()
|
||||
|| !jsRoot["sid"].isString()
|
||||
|| !jsRoot["user"].isString()
|
||||
|| !jsRoot["acc"].isString()
|
||||
|| !jsRoot["host"].isString()
|
||||
|| !jsRoot["start"].isString()
|
||||
) {
|
||||
_create_json_ret(buf, TPE_PARAM);
|
||||
return;
|
||||
|
|
@ -965,67 +995,9 @@ void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
|
|||
int rid = jsRoot["rid"].asInt();
|
||||
ex_astr a_url_base = jsRoot["web"].asCString();
|
||||
ex_astr a_sid = jsRoot["sid"].asCString();
|
||||
ex_astr a_user = jsRoot["user"].asCString();
|
||||
ex_astr a_acc = jsRoot["acc"].asCString();
|
||||
ex_astr a_host = jsRoot["host"].asCString();
|
||||
ex_astr a_start = jsRoot["start"].asCString();
|
||||
|
||||
char cmd_args[1024] = { 0 };
|
||||
ex_strformat(cmd_args, 1023, "%d \"%s\" \"%09d-%s-%s-%s-%s\"", rid, a_sid.c_str(), rid, a_user.c_str(), a_acc.c_str(), a_host.c_str(), a_start.c_str());
|
||||
|
||||
// TODO: 理论上不应该由助手来提前做域名转为IP这样的操作,而是应该将域名发送给播放器,由播放器自己去处理
|
||||
// 但是在改造FreeRDP制作的播放器时,为了从服务器上下载文件,使用了Mongoose库,如果传入的是域名,会出现问题(貌似是异步查询DNS的问题)
|
||||
// 所以暂时先由助手进行域名IP转换。
|
||||
{
|
||||
unsigned int port_i = 0;
|
||||
struct mg_str scheme, query, fragment, user_info, host, path;
|
||||
|
||||
if (mg_parse_uri(mg_mk_str(a_url_base.c_str()), &scheme, &user_info, &host, &port_i, &path, &query, &fragment) != 0) {
|
||||
EXLOGE(_T("parse url failed.\n"));
|
||||
Json::Value root_ret;
|
||||
root_ret["code"] = TPE_PARAM;
|
||||
_create_json_ret(buf, root_ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ex_astr _scheme;
|
||||
_scheme.assign(scheme.p, scheme.len);
|
||||
|
||||
// 将host从域名转换为IP
|
||||
ex_astr str_tp_host;
|
||||
str_tp_host.assign(host.p, host.len);
|
||||
struct hostent *tp_host = gethostbyname(str_tp_host.c_str());
|
||||
if (NULL == tp_host) {
|
||||
EXLOGE(_T("resolve host name failed.\n"));
|
||||
Json::Value root_ret;
|
||||
root_ret["code"] = TPE_PARAM;
|
||||
_create_json_ret(buf, root_ret);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
char* _ip = NULL;
|
||||
if (tp_host->h_addrtype == AF_INET) {
|
||||
struct in_addr addr;
|
||||
while (tp_host->h_addr_list[i] != 0) {
|
||||
addr.s_addr = *(u_long *)tp_host->h_addr_list[i++];
|
||||
_ip = inet_ntoa(addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == _ip) {
|
||||
EXLOGE(_T("resolve host name failed.\n"));
|
||||
Json::Value root_ret;
|
||||
root_ret["code"] = TPE_PARAM;
|
||||
_create_json_ret(buf, root_ret);
|
||||
return;
|
||||
}
|
||||
|
||||
char _url_base[256];
|
||||
ex_strformat(_url_base, 255, "%s://%s:%d", _scheme.c_str(), _ip, port_i);
|
||||
a_url_base = _url_base;
|
||||
}
|
||||
ex_strformat(cmd_args, 1023, "%s/%d", a_sid.c_str(), rid);
|
||||
|
||||
ex_wstr w_url_base;
|
||||
ex_astr2wstr(a_url_base, w_url_base);
|
||||
|
|
@ -1034,10 +1006,10 @@ void TsHttpRpc::_rpc_func_rdp_play(const ex_astr& func_args, ex_astr& buf) {
|
|||
|
||||
ex_wstr w_exe_path;
|
||||
w_exe_path = _T("\"");
|
||||
w_exe_path += g_env.m_tools_path + _T("\\tprdp\\tprdp-replay.exe\"");
|
||||
w_exe_path += g_env.m_exec_path + _T("\\tp-player.exe\"");
|
||||
w_exe_path += _T(" \"");
|
||||
w_exe_path += w_url_base;
|
||||
w_exe_path += _T("\" ");
|
||||
w_exe_path += _T("/");
|
||||
w_exe_path += w_cmd_args;
|
||||
|
||||
Json::Value root_ret;
|
||||
|
|
@ -1073,9 +1045,15 @@ void TsHttpRpc::_rpc_func_get_config(const ex_astr& func_args, ex_astr& buf) {
|
|||
}
|
||||
|
||||
void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf) {
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::Value jsRoot;
|
||||
if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = func_args.c_str();
|
||||
ex_astr err;
|
||||
|
||||
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
|
||||
_create_json_ret(buf, TPE_JSON_FORMAT);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1088,14 +1066,20 @@ void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf) {
|
|||
|
||||
void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
|
||||
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::Value jsRoot;
|
||||
|
||||
if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = func_args.c_str();
|
||||
ex_astr err;
|
||||
|
||||
//if (!jreader.parse(func_args.c_str(), jsRoot)) {
|
||||
if (!jreader->parse(str_json_begin, str_json_begin + func_args.length(), &jsRoot, &err)) {
|
||||
_create_json_ret(buf, TPE_JSON_FORMAT);
|
||||
return;
|
||||
}
|
||||
// 判断参数是否正确
|
||||
// 判断参数是否正确
|
||||
if (!jsRoot["action"].isNumeric()) {
|
||||
_create_json_ret(buf, TPE_PARAM);
|
||||
return;
|
||||
|
|
@ -1118,9 +1102,9 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
|
|||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.lpstrTitle = _T("选择文件");
|
||||
ofn.lpstrTitle = _T("选择文件");
|
||||
ofn.hwndOwner = hParent;
|
||||
ofn.lpstrFilter = _T("可执行程序 (*.exe)\0*.exe\0");
|
||||
ofn.lpstrFilter = _T("可执行程序 (*.exe)\0*.exe\0");
|
||||
ofn.lpstrFile = wszReturnPath;
|
||||
ofn.nMaxFile = MAX_PATH;
|
||||
ofn.lpstrInitialDir = wsDefaultPath.c_str();
|
||||
|
|
@ -1138,12 +1122,12 @@ void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
|
|||
ZeroMemory(&bi, sizeof(BROWSEINFO));
|
||||
bi.hwndOwner = NULL;
|
||||
bi.pidlRoot = NULL;
|
||||
bi.pszDisplayName = wszReturnPath; //此参数如为NULL则不能显示对话框
|
||||
bi.lpszTitle = _T("选择目录");
|
||||
bi.pszDisplayName = wszReturnPath; //此参数如为NULL则不能显示对话框
|
||||
bi.lpszTitle = _T("选择目录");
|
||||
bi.ulFlags = BIF_RETURNONLYFSDIRS;
|
||||
bi.lpfn = NULL;
|
||||
bi.iImage = 0; //初始化入口参数bi结束
|
||||
LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//调用显示选择对话框
|
||||
bi.iImage = 0; //初始化入口参数bi结束
|
||||
LPITEMIDLIST pIDList = SHBrowseForFolder(&bi);//调用显示选择对话框
|
||||
if (pIDList) {
|
||||
ret = true;
|
||||
SHGetPathFromIDList(pIDList, wszReturnPath);
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
# include <fcntl.h> // O_RDONLY, etc.
|
||||
# include <errno.h>
|
||||
# include <wchar.h>
|
||||
# include <memory>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
|
|
|
|||
|
|
@ -55,9 +55,13 @@ int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...);
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
//#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
typedef std::string ex_astr;
|
||||
typedef std::wstring ex_wstr;
|
||||
typedef std::ostringstream ex_aoss;
|
||||
typedef std::wostringstream ex_woss;
|
||||
|
||||
typedef std::vector<ex_astr> ex_astrs;
|
||||
typedef std::vector<ex_wstr> ex_wstrs;
|
||||
|
|
|
|||
|
|
@ -80,8 +80,10 @@ bool ExThreadBase::stop(void) {
|
|||
return false;
|
||||
}
|
||||
#else
|
||||
if (pthread_join(m_handle, NULL) != 0) {
|
||||
return false;
|
||||
if(m_handle != 0) {
|
||||
if (pthread_join(m_handle, NULL) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ wget = C:\Program Files (x86)\wget\wget.exe
|
|||
# if not set msbuild path, default to get it by register.
|
||||
#msbuild = C:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe
|
||||
|
||||
# need qt to build tp-player.
|
||||
qt = C:\Qt\Qt5.12.0\5.12.0\msvc2017
|
||||
|
||||
# ============================================
|
||||
# for Linux and macOS
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,400 @@
|
|||
# -*- makefile -*-
|
||||
# The file Setup is used by the makesetup script to construct the files
|
||||
# Makefile and config.c, from Makefile.pre and config.c.in,
|
||||
# respectively. The file Setup itself is initially copied from
|
||||
# Setup.dist; once it exists it will not be overwritten, so you can edit
|
||||
# Setup to your heart's content. Note that Makefile.pre is created
|
||||
# from Makefile.pre.in by the toplevel configure script.
|
||||
|
||||
# (VPATH notes: Setup and Makefile.pre are in the build directory, as
|
||||
# are Makefile and config.c; the *.in and *.dist files are in the source
|
||||
# directory.)
|
||||
|
||||
# Each line in this file describes one or more optional modules.
|
||||
# Modules configured here will not be compiled by the setup.py script,
|
||||
# so the file can be used to override setup.py's behavior.
|
||||
# Tag lines containing just the word "*static*", "*shared*" or "*disabled*"
|
||||
# (without the quotes but with the stars) are used to tag the following module
|
||||
# descriptions. Tag lines may alternate throughout this file. Modules are
|
||||
# built statically when they are preceded by a "*static*" tag line or when
|
||||
# there is no tag line between the start of the file and the module
|
||||
# description. Modules are built as a shared library when they are preceded by
|
||||
# a "*shared*" tag line. Modules are not built at all, not by the Makefile,
|
||||
# nor by the setup.py script, when they are preceded by a "*disabled*" tag
|
||||
# line.
|
||||
|
||||
# Lines have the following structure:
|
||||
#
|
||||
# <module> ... [<sourcefile> ...] [<cpparg> ...] [<library> ...]
|
||||
#
|
||||
# <sourcefile> is anything ending in .c (.C, .cc, .c++ are C++ files)
|
||||
# <cpparg> is anything starting with -I, -D, -U or -C
|
||||
# <library> is anything ending in .a or beginning with -l or -L
|
||||
# <module> is anything else but should be a valid Python
|
||||
# identifier (letters, digits, underscores, beginning with non-digit)
|
||||
#
|
||||
# (As the makesetup script changes, it may recognize some other
|
||||
# arguments as well, e.g. *.so and *.sl as libraries. See the big
|
||||
# case statement in the makesetup script.)
|
||||
#
|
||||
# Lines can also have the form
|
||||
#
|
||||
# <name> = <value>
|
||||
#
|
||||
# which defines a Make variable definition inserted into Makefile.in
|
||||
#
|
||||
# The build process works like this:
|
||||
#
|
||||
# 1. Build all modules that are declared as static in Modules/Setup,
|
||||
# combine them into libpythonxy.a, combine that into python.
|
||||
# 2. Build all modules that are listed as shared in Modules/Setup.
|
||||
# 3. Invoke setup.py. That builds all modules that
|
||||
# a) are not builtin, and
|
||||
# b) are not listed in Modules/Setup, and
|
||||
# c) can be build on the target
|
||||
#
|
||||
# Therefore, modules declared to be shared will not be
|
||||
# included in the config.c file, nor in the list of objects to be
|
||||
# added to the library archive, and their linker options won't be
|
||||
# added to the linker options. Rules to create their .o files and
|
||||
# their shared libraries will still be added to the Makefile, and
|
||||
# their names will be collected in the Make variable SHAREDMODS. This
|
||||
# is used to build modules as shared libraries. (They can be
|
||||
# installed using "make sharedinstall", which is implied by the
|
||||
# toplevel "make install" target.) (For compatibility,
|
||||
# *noconfig* has the same effect as *shared*.)
|
||||
#
|
||||
# NOTE: As a standard policy, as many modules as can be supported by a
|
||||
# platform should be present. The distribution comes with all modules
|
||||
# enabled that are supported by most platforms and don't require you
|
||||
# to ftp sources from elsewhere.
|
||||
|
||||
|
||||
# Some special rules to define PYTHONPATH.
|
||||
# Edit the definitions below to indicate which options you are using.
|
||||
# Don't add any whitespace or comments!
|
||||
|
||||
# Directories where library files get installed.
|
||||
# DESTLIB is for Python modules; MACHDESTLIB for shared libraries.
|
||||
DESTLIB=$(LIBDEST)
|
||||
MACHDESTLIB=$(BINLIBDEST)
|
||||
|
||||
# NOTE: all the paths are now relative to the prefix that is computed
|
||||
# at run time!
|
||||
|
||||
# Standard path -- don't edit.
|
||||
# No leading colon since this is the first entry.
|
||||
# Empty since this is now just the runtime prefix.
|
||||
DESTPATH=
|
||||
|
||||
# Site specific path components -- should begin with : if non-empty
|
||||
SITEPATH=
|
||||
|
||||
# Standard path components for test modules
|
||||
TESTPATH=
|
||||
|
||||
COREPYTHONPATH=$(DESTPATH)$(SITEPATH)$(TESTPATH)
|
||||
PYTHONPATH=$(COREPYTHONPATH)
|
||||
|
||||
|
||||
# The modules listed here can't be built as shared libraries for
|
||||
# various reasons; therefore they are listed here instead of in the
|
||||
# normal order.
|
||||
|
||||
# This only contains the minimal set of modules required to run the
|
||||
# setup.py script in the root of the Python source tree.
|
||||
|
||||
posix -DPy_BUILD_CORE posixmodule.c # posix (UNIX) system calls
|
||||
errno errnomodule.c # posix (UNIX) errno values
|
||||
pwd pwdmodule.c # this is needed to find out the user's home dir
|
||||
# if $HOME is not set
|
||||
_sre _sre.c # Fredrik Lundh's new regular expressions
|
||||
_codecs _codecsmodule.c # access to the builtin codecs and codec registry
|
||||
_weakref _weakref.c # weak references
|
||||
_functools -DPy_BUILD_CORE _functoolsmodule.c # Tools for working with functions and callable objects
|
||||
_operator _operator.c # operator.add() and similar goodies
|
||||
_collections _collectionsmodule.c # Container types
|
||||
_abc _abc.c # Abstract base classes
|
||||
itertools itertoolsmodule.c # Functions creating iterators for efficient looping
|
||||
atexit atexitmodule.c # Register functions to be run at interpreter-shutdown
|
||||
_signal -DPy_BUILD_CORE signalmodule.c
|
||||
_stat _stat.c # stat.h interface
|
||||
time -DPy_BUILD_CORE timemodule.c # -lm # time operations and variables
|
||||
_thread -DPy_BUILD_CORE _threadmodule.c # low-level threading interface
|
||||
|
||||
# access to ISO C locale support
|
||||
_locale _localemodule.c # -lintl
|
||||
|
||||
# Standard I/O baseline
|
||||
_io -DPy_BUILD_CORE -I$(srcdir)/Modules/_io _io/_iomodule.c _io/iobase.c _io/fileio.c _io/bytesio.c _io/bufferedio.c _io/textio.c _io/stringio.c
|
||||
|
||||
# The zipimport module is always imported at startup. Having it as a
|
||||
# builtin module avoids some bootstrapping problems and reduces overhead.
|
||||
zipimport -DPy_BUILD_CORE zipimport.c
|
||||
|
||||
# faulthandler module
|
||||
faulthandler faulthandler.c
|
||||
|
||||
# debug tool to trace memory blocks allocated by Python
|
||||
_tracemalloc _tracemalloc.c hashtable.c
|
||||
|
||||
# The rest of the modules listed in this file are all commented out by
|
||||
# default. Usually they can be detected and built as dynamically
|
||||
# loaded modules by the new setup.py script added in Python 2.1. If
|
||||
# you're on a platform that doesn't support dynamic loading, want to
|
||||
# compile modules statically into the Python binary, or need to
|
||||
# specify some odd set of compiler switches, you can uncomment the
|
||||
# appropriate lines below.
|
||||
|
||||
# ======================================================================
|
||||
|
||||
# The Python symtable module depends on .h files that setup.py doesn't track
|
||||
_symtable symtablemodule.c
|
||||
|
||||
# Uncommenting the following line tells makesetup that all following
|
||||
# modules are to be built as shared libraries (see above for more
|
||||
# detail; also note that *static* or *disabled* cancels this effect):
|
||||
|
||||
#*shared*
|
||||
*static*
|
||||
|
||||
# GNU readline. Unlike previous Python incarnations, GNU readline is
|
||||
# now incorporated in an optional module, configured in the Setup file
|
||||
# instead of by a configure script switch. You may have to insert a
|
||||
# -L option pointing to the directory where libreadline.* lives,
|
||||
# and you may have to change -ltermcap to -ltermlib or perhaps remove
|
||||
# it, depending on your system -- see the GNU readline instructions.
|
||||
# It's okay for this to be a shared library, too.
|
||||
|
||||
#readline readline.c -lreadline -ltermcap
|
||||
|
||||
|
||||
# Modules that should always be present (non UNIX dependent):
|
||||
|
||||
array arraymodule.c # array objects
|
||||
cmath cmathmodule.c _math.c # -lm # complex math library functions
|
||||
math mathmodule.c _math.c # -lm # math library functions, e.g. sin()
|
||||
_contextvars _contextvarsmodule.c # Context Variables
|
||||
_struct _struct.c # binary structure packing/unpacking
|
||||
_weakref _weakref.c # basic weak reference support
|
||||
#_testcapi _testcapimodule.c # Python C API test module
|
||||
_random _randommodule.c # Random number generator
|
||||
_elementtree -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI _elementtree.c # elementtree accelerator
|
||||
_pickle _pickle.c # pickle accelerator
|
||||
_datetime _datetimemodule.c # datetime accelerator
|
||||
_bisect _bisectmodule.c # Bisection algorithms
|
||||
_heapq _heapqmodule.c # Heap queue algorithm
|
||||
_asyncio _asynciomodule.c # Fast asyncio Future
|
||||
|
||||
unicodedata unicodedata.c # static Unicode character database
|
||||
|
||||
|
||||
# Modules with some UNIX dependencies -- on by default:
|
||||
# (If you have a really backward UNIX, select and socket may not be
|
||||
# supported...)
|
||||
|
||||
fcntl fcntlmodule.c # fcntl(2) and ioctl(2)
|
||||
spwd spwdmodule.c # spwd(3)
|
||||
grp grpmodule.c # grp(3)
|
||||
select selectmodule.c # select(2); not on ancient System V
|
||||
|
||||
# Memory-mapped files (also works on Win32).
|
||||
mmap mmapmodule.c
|
||||
|
||||
# CSV file helper
|
||||
_csv _csv.c
|
||||
|
||||
# Socket module helper for socket(2)
|
||||
_socket socketmodule.c
|
||||
|
||||
# Socket module helper for SSL support; you must comment out the other
|
||||
# socket line above, and possibly edit the SSL variable:
|
||||
SSL=$(srcdir)/../../release
|
||||
_ssl _ssl.c \
|
||||
-DUSE_SSL -I$(SSL)/include \
|
||||
$(SSL)/lib/libssl.a $(SSL)/lib/libcrypto.a
|
||||
# -lssl -lcrypto
|
||||
|
||||
# The crypt module is now disabled by default because it breaks builds
|
||||
# on many systems (where -lcrypt is needed), e.g. Linux (I believe).
|
||||
|
||||
#_crypt _cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems
|
||||
|
||||
|
||||
# Some more UNIX dependent modules -- off by default, since these
|
||||
# are not supported by all UNIX systems:
|
||||
|
||||
#nis nismodule.c -lnsl # Sun yellow pages -- not everywhere
|
||||
termios termios.c # Steen Lumholt's termios module
|
||||
resource resource.c # Jeremy Hylton's rlimit interface
|
||||
|
||||
_posixsubprocess _posixsubprocess.c # POSIX subprocess module helper
|
||||
|
||||
# Multimedia modules -- off by default.
|
||||
# These don't work for 64-bit platforms!!!
|
||||
# #993173 says audioop works on 64-bit platforms, though.
|
||||
# These represent audio samples or images as strings:
|
||||
|
||||
#audioop audioop.c # Operations on audio samples
|
||||
|
||||
|
||||
# Note that the _md5 and _sha modules are normally only built if the
|
||||
# system does not have the OpenSSL libs containing an optimized version.
|
||||
|
||||
# The _md5 module implements the RSA Data Security, Inc. MD5
|
||||
# Message-Digest Algorithm, described in RFC 1321.
|
||||
|
||||
_md5 md5module.c
|
||||
|
||||
|
||||
# The _sha module implements the SHA checksum algorithms.
|
||||
# (NIST's Secure Hash Algorithms.)
|
||||
_sha1 sha1module.c
|
||||
_sha256 sha256module.c
|
||||
_sha512 sha512module.c
|
||||
_sha3 _sha3/sha3module.c
|
||||
|
||||
# _blake module
|
||||
_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c
|
||||
|
||||
# The _tkinter module.
|
||||
#
|
||||
# The command for _tkinter is long and site specific. Please
|
||||
# uncomment and/or edit those parts as indicated. If you don't have a
|
||||
# specific extension (e.g. Tix or BLT), leave the corresponding line
|
||||
# commented out. (Leave the trailing backslashes in! If you
|
||||
# experience strange errors, you may want to join all uncommented
|
||||
# lines and remove the backslashes -- the backslash interpretation is
|
||||
# done by the shell's "read" command and it may not be implemented on
|
||||
# every system.
|
||||
|
||||
# *** Always uncomment this (leave the leading underscore in!):
|
||||
# _tkinter _tkinter.c tkappinit.c -DWITH_APPINIT \
|
||||
# *** Uncomment and edit to reflect where your Tcl/Tk libraries are:
|
||||
# -L/usr/local/lib \
|
||||
# *** Uncomment and edit to reflect where your Tcl/Tk headers are:
|
||||
# -I/usr/local/include \
|
||||
# *** Uncomment and edit to reflect where your X11 header files are:
|
||||
# -I/usr/X11R6/include \
|
||||
# *** Or uncomment this for Solaris:
|
||||
# -I/usr/openwin/include \
|
||||
# *** Uncomment and edit for Tix extension only:
|
||||
# -DWITH_TIX -ltix8.1.8.2 \
|
||||
# *** Uncomment and edit for BLT extension only:
|
||||
# -DWITH_BLT -I/usr/local/blt/blt8.0-unoff/include -lBLT8.0 \
|
||||
# *** Uncomment and edit for PIL (TkImaging) extension only:
|
||||
# (See http://www.pythonware.com/products/pil/ for more info)
|
||||
# -DWITH_PIL -I../Extensions/Imaging/libImaging tkImaging.c \
|
||||
# *** Uncomment and edit for TOGL extension only:
|
||||
# -DWITH_TOGL togl.c \
|
||||
# *** Uncomment and edit to reflect your Tcl/Tk versions:
|
||||
# -ltk8.2 -ltcl8.2 \
|
||||
# *** Uncomment and edit to reflect where your X11 libraries are:
|
||||
# -L/usr/X11R6/lib \
|
||||
# *** Or uncomment this for Solaris:
|
||||
# -L/usr/openwin/lib \
|
||||
# *** Uncomment these for TOGL extension only:
|
||||
# -lGL -lGLU -lXext -lXmu \
|
||||
# *** Uncomment for AIX:
|
||||
# -lld \
|
||||
# *** Always uncomment this; X11 libraries to link with:
|
||||
# -lX11
|
||||
|
||||
# Lance Ellinghaus's syslog module
|
||||
syslog syslogmodule.c # syslog daemon interface
|
||||
|
||||
|
||||
# Curses support, requiring the System V version of curses, often
|
||||
# provided by the ncurses library. e.g. on Linux, link with -lncurses
|
||||
# instead of -lcurses).
|
||||
|
||||
#_curses _cursesmodule.c -lcurses -ltermcap
|
||||
# Wrapper for the panel library that's part of ncurses and SYSV curses.
|
||||
#_curses_panel _curses_panel.c -lpanel -lncurses
|
||||
|
||||
|
||||
# Modules that provide persistent dictionary-like semantics. You will
|
||||
# probably want to arrange for at least one of them to be available on
|
||||
# your machine, though none are defined by default because of library
|
||||
# dependencies. The Python module dbm/__init__.py provides an
|
||||
# implementation independent wrapper for these; dbm/dumb.py provides
|
||||
# similar functionality (but slower of course) implemented in Python.
|
||||
|
||||
#_dbm _dbmmodule.c # dbm(3) may require -lndbm or similar
|
||||
|
||||
# Anthony Baxter's gdbm module. GNU dbm(3) will require -lgdbm:
|
||||
|
||||
#_gdbm _gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm
|
||||
|
||||
|
||||
# Helper module for various ascii-encoders
|
||||
binascii binascii.c
|
||||
|
||||
# Fred Drake's interface to the Python parser
|
||||
#parser parsermodule.c
|
||||
|
||||
|
||||
# Andrew Kuchling's zlib module.
|
||||
# This require zlib 1.1.3 (or later).
|
||||
# See http://www.gzip.org/zlib/
|
||||
#zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz
|
||||
|
||||
# Interface to the Expat XML parser
|
||||
#
|
||||
# Expat was written by James Clark and is now maintained by a group of
|
||||
# developers on SourceForge; see www.libexpat.org for more
|
||||
# information. The pyexpat module was written by Paul Prescod after a
|
||||
# prototype by Jack Jansen. Source of Expat 1.95.2 is included in
|
||||
# Modules/expat/. Usage of a system shared libexpat.so/expat.dll is
|
||||
# not advised.
|
||||
#
|
||||
# More information on Expat can be found at www.libexpat.org.
|
||||
#
|
||||
#pyexpat expat/xmlparse.c expat/xmlrole.c expat/xmltok.c pyexpat.c -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI
|
||||
|
||||
# Hye-Shik Chang's CJKCodecs
|
||||
|
||||
# multibytecodec is required for all the other CJK codec modules
|
||||
_multibytecodec cjkcodecs/multibytecodec.c
|
||||
|
||||
_codecs_cn cjkcodecs/_codecs_cn.c
|
||||
_codecs_hk cjkcodecs/_codecs_hk.c
|
||||
_codecs_iso2022 cjkcodecs/_codecs_iso2022.c
|
||||
_codecs_jp cjkcodecs/_codecs_jp.c
|
||||
_codecs_kr cjkcodecs/_codecs_kr.c
|
||||
_codecs_tw cjkcodecs/_codecs_tw.c
|
||||
|
||||
# Example -- included for reference only:
|
||||
# xx xxmodule.c
|
||||
|
||||
# Another example -- the 'xxsubtype' module shows C-level subtyping in action
|
||||
#xxsubtype xxsubtype.c
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
# add extra-builtin-module by Apex Liu.
|
||||
########################################################################
|
||||
|
||||
zlib zlibmodule.c -I$(srcdir)/Modules/zlib \
|
||||
zlib/adler32.c zlib/crc32.c zlib/deflate.c zlib/infback.c zlib/inffast.c zlib/inflate.c zlib/inftrees.c zlib/trees.c zlib/zutil.c \
|
||||
zlib/compress.c zlib/uncompr.c zlib/gzclose.c zlib/gzlib.c zlib/gzread.c zlib/gzwrite.c
|
||||
|
||||
_json _json.c
|
||||
|
||||
_sqlite3 -I$(srcdir)/Modules/_sqlite/sqlite3 -I$(srcdir)/Modules/_sqlite \
|
||||
$(srcdir)/Modules/_sqlite/sqlite3/sqlite3.c \
|
||||
_sqlite/cache.c _sqlite/connection.c _sqlite/cursor.c _sqlite/microprotocols.c _sqlite/module.c \
|
||||
_sqlite/prepare_protocol.c _sqlite/row.c _sqlite/statement.c _sqlite/util.c
|
||||
|
||||
# Uncommenting the following line tells makesetup that all following modules
|
||||
# are not built (see above for more detail).
|
||||
#
|
||||
#*disabled*
|
||||
#
|
||||
#_sqlite3 _tkinter _curses pyexpat
|
||||
#_codecs_jp _codecs_kr _codecs_tw unicodedata
|
||||
|
||||
*disabled*
|
||||
_tkinter _curses
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/* cache.h - definitions for the LRU cache
|
||||
*
|
||||
* Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de>
|
||||
*
|
||||
* This file is part of pysqlite.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef PYSQLITE_CACHE_H
|
||||
#define PYSQLITE_CACHE_H
|
||||
#include "Python.h"
|
||||
|
||||
#define MODULE_NAME "sqlite3"
|
||||
|
||||
/* The LRU cache is implemented as a combination of a doubly-linked with a
|
||||
* dictionary. The list items are of type 'Node' and the dictionary has the
|
||||
* nodes as values. */
|
||||
|
||||
typedef struct _pysqlite_Node
|
||||
{
|
||||
PyObject_HEAD
|
||||
PyObject* key;
|
||||
PyObject* data;
|
||||
long count;
|
||||
struct _pysqlite_Node* prev;
|
||||
struct _pysqlite_Node* next;
|
||||
} pysqlite_Node;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
int size;
|
||||
|
||||
/* a dictionary mapping keys to Node entries */
|
||||
PyObject* mapping;
|
||||
|
||||
/* the factory callable */
|
||||
PyObject* factory;
|
||||
|
||||
pysqlite_Node* first;
|
||||
pysqlite_Node* last;
|
||||
|
||||
/* if set, decrement the factory function when the Cache is deallocated.
|
||||
* this is almost always desirable, but not in the pysqlite context */
|
||||
int decref_factory;
|
||||
} pysqlite_Cache;
|
||||
|
||||
extern PyTypeObject pysqlite_NodeType;
|
||||
extern PyTypeObject pysqlite_CacheType;
|
||||
|
||||
int pysqlite_node_init(pysqlite_Node* self, PyObject* args, PyObject* kwargs);
|
||||
void pysqlite_node_dealloc(pysqlite_Node* self);
|
||||
|
||||
int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs);
|
||||
void pysqlite_cache_dealloc(pysqlite_Cache* self);
|
||||
PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args);
|
||||
|
||||
int pysqlite_cache_setup_types(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/* prepare_protocol.h - the protocol for preparing values for SQLite
|
||||
*
|
||||
* Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de>
|
||||
*
|
||||
* This file is part of pysqlite.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef PYSQLITE_PREPARE_PROTOCOL_H
|
||||
#define PYSQLITE_PREPARE_PROTOCOL_H
|
||||
#include "Python.h"
|
||||
|
||||
#define MODULE_NAME "sqlite3"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
} pysqlite_PrepareProtocol;
|
||||
|
||||
extern PyTypeObject pysqlite_PrepareProtocolType;
|
||||
|
||||
int pysqlite_prepare_protocol_init(pysqlite_PrepareProtocol* self, PyObject* args, PyObject* kwargs);
|
||||
void pysqlite_prepare_protocol_dealloc(pysqlite_PrepareProtocol* self);
|
||||
|
||||
int pysqlite_prepare_protocol_setup_types(void);
|
||||
|
||||
#define UNKNOWN (-1)
|
||||
#endif
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the OpenSSL license (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "libcrypto-compat.h"
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
|
||||
static void *OPENSSL_zalloc(size_t num)
|
||||
{
|
||||
void *ret = OPENSSL_malloc(num);
|
||||
|
||||
if (ret != NULL)
|
||||
memset(ret, 0, num);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
{
|
||||
/* If the fields n and e in r are NULL, the corresponding input
|
||||
* parameters MUST be non-NULL for n and e. d may be
|
||||
* left NULL (in case only the public key is used).
|
||||
*/
|
||||
if ((r->n == NULL && n == NULL)
|
||||
|| (r->e == NULL && e == NULL))
|
||||
return 0;
|
||||
|
||||
if (n != NULL) {
|
||||
BN_free(r->n);
|
||||
r->n = n;
|
||||
}
|
||||
if (e != NULL) {
|
||||
BN_free(r->e);
|
||||
r->e = e;
|
||||
}
|
||||
if (d != NULL) {
|
||||
BN_free(r->d);
|
||||
r->d = d;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
|
||||
{
|
||||
/* If the fields p and q in r are NULL, the corresponding input
|
||||
* parameters MUST be non-NULL.
|
||||
*/
|
||||
if ((r->p == NULL && p == NULL)
|
||||
|| (r->q == NULL && q == NULL))
|
||||
return 0;
|
||||
|
||||
if (p != NULL) {
|
||||
BN_free(r->p);
|
||||
r->p = p;
|
||||
}
|
||||
if (q != NULL) {
|
||||
BN_free(r->q);
|
||||
r->q = q;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
|
||||
{
|
||||
/* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
|
||||
* parameters MUST be non-NULL.
|
||||
*/
|
||||
if ((r->dmp1 == NULL && dmp1 == NULL)
|
||||
|| (r->dmq1 == NULL && dmq1 == NULL)
|
||||
|| (r->iqmp == NULL && iqmp == NULL))
|
||||
return 0;
|
||||
|
||||
if (dmp1 != NULL) {
|
||||
BN_free(r->dmp1);
|
||||
r->dmp1 = dmp1;
|
||||
}
|
||||
if (dmq1 != NULL) {
|
||||
BN_free(r->dmq1);
|
||||
r->dmq1 = dmq1;
|
||||
}
|
||||
if (iqmp != NULL) {
|
||||
BN_free(r->iqmp);
|
||||
r->iqmp = iqmp;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RSA_get0_key(const RSA *r,
|
||||
const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
||||
{
|
||||
if (n != NULL)
|
||||
*n = r->n;
|
||||
if (e != NULL)
|
||||
*e = r->e;
|
||||
if (d != NULL)
|
||||
*d = r->d;
|
||||
}
|
||||
|
||||
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
|
||||
{
|
||||
if (p != NULL)
|
||||
*p = r->p;
|
||||
if (q != NULL)
|
||||
*q = r->q;
|
||||
}
|
||||
|
||||
void RSA_get0_crt_params(const RSA *r,
|
||||
const BIGNUM **dmp1, const BIGNUM **dmq1,
|
||||
const BIGNUM **iqmp)
|
||||
{
|
||||
if (dmp1 != NULL)
|
||||
*dmp1 = r->dmp1;
|
||||
if (dmq1 != NULL)
|
||||
*dmq1 = r->dmq1;
|
||||
if (iqmp != NULL)
|
||||
*iqmp = r->iqmp;
|
||||
}
|
||||
|
||||
void DSA_get0_pqg(const DSA *d,
|
||||
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
||||
{
|
||||
if (p != NULL)
|
||||
*p = d->p;
|
||||
if (q != NULL)
|
||||
*q = d->q;
|
||||
if (g != NULL)
|
||||
*g = d->g;
|
||||
}
|
||||
|
||||
int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{
|
||||
/* If the fields p, q and g in d are NULL, the corresponding input
|
||||
* parameters MUST be non-NULL.
|
||||
*/
|
||||
if ((d->p == NULL && p == NULL)
|
||||
|| (d->q == NULL && q == NULL)
|
||||
|| (d->g == NULL && g == NULL))
|
||||
return 0;
|
||||
|
||||
if (p != NULL) {
|
||||
BN_free(d->p);
|
||||
d->p = p;
|
||||
}
|
||||
if (q != NULL) {
|
||||
BN_free(d->q);
|
||||
d->q = q;
|
||||
}
|
||||
if (g != NULL) {
|
||||
BN_free(d->g);
|
||||
d->g = g;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DSA_get0_key(const DSA *d,
|
||||
const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{
|
||||
if (pub_key != NULL)
|
||||
*pub_key = d->pub_key;
|
||||
if (priv_key != NULL)
|
||||
*priv_key = d->priv_key;
|
||||
}
|
||||
|
||||
int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{
|
||||
/* If the field pub_key in d is NULL, the corresponding input
|
||||
* parameters MUST be non-NULL. The priv_key field may
|
||||
* be left NULL.
|
||||
*/
|
||||
if (d->pub_key == NULL && pub_key == NULL)
|
||||
return 0;
|
||||
|
||||
if (pub_key != NULL) {
|
||||
BN_free(d->pub_key);
|
||||
d->pub_key = pub_key;
|
||||
}
|
||||
if (priv_key != NULL) {
|
||||
BN_free(d->priv_key);
|
||||
d->priv_key = priv_key;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||
{
|
||||
if (pr != NULL)
|
||||
*pr = sig->r;
|
||||
if (ps != NULL)
|
||||
*ps = sig->s;
|
||||
}
|
||||
|
||||
int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
||||
{
|
||||
if (r == NULL || s == NULL)
|
||||
return 0;
|
||||
BN_clear_free(sig->r);
|
||||
BN_clear_free(sig->s);
|
||||
sig->r = r;
|
||||
sig->s = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||
{
|
||||
if (pr != NULL)
|
||||
*pr = sig->r;
|
||||
if (ps != NULL)
|
||||
*ps = sig->s;
|
||||
}
|
||||
|
||||
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
||||
{
|
||||
if (r == NULL || s == NULL)
|
||||
return 0;
|
||||
BN_clear_free(sig->r);
|
||||
BN_clear_free(sig->s);
|
||||
sig->r = r;
|
||||
sig->s = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVP_MD_CTX *EVP_MD_CTX_new(void)
|
||||
{
|
||||
return OPENSSL_zalloc(sizeof(EVP_MD_CTX));
|
||||
}
|
||||
|
||||
static void OPENSSL_clear_free(void *str, size_t num)
|
||||
{
|
||||
if (str == NULL)
|
||||
return;
|
||||
if (num)
|
||||
OPENSSL_cleanse(str, num);
|
||||
OPENSSL_free(str);
|
||||
}
|
||||
|
||||
/* This call frees resources associated with the context */
|
||||
int EVP_MD_CTX_reset(EVP_MD_CTX *ctx)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because
|
||||
* sometimes only copies of the context are ever finalised.
|
||||
*/
|
||||
if (ctx->digest && ctx->digest->cleanup
|
||||
&& !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED))
|
||||
ctx->digest->cleanup(ctx);
|
||||
if (ctx->digest && ctx->digest->ctx_size && ctx->md_data
|
||||
&& !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) {
|
||||
OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size);
|
||||
}
|
||||
EVP_PKEY_CTX_free(ctx->pctx);
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
ENGINE_finish(ctx->engine);
|
||||
#endif
|
||||
OPENSSL_cleanse(ctx, sizeof(*ctx));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
|
||||
{
|
||||
EVP_MD_CTX_reset(ctx);
|
||||
OPENSSL_free(ctx);
|
||||
}
|
||||
|
||||
HMAC_CTX *HMAC_CTX_new(void)
|
||||
{
|
||||
HMAC_CTX *ctx = OPENSSL_zalloc(sizeof(HMAC_CTX));
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (!HMAC_CTX_reset(ctx)) {
|
||||
HMAC_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void hmac_ctx_cleanup(HMAC_CTX *ctx)
|
||||
{
|
||||
EVP_MD_CTX_reset(&ctx->i_ctx);
|
||||
EVP_MD_CTX_reset(&ctx->o_ctx);
|
||||
EVP_MD_CTX_reset(&ctx->md_ctx);
|
||||
ctx->md = NULL;
|
||||
ctx->key_length = 0;
|
||||
OPENSSL_cleanse(ctx->key, sizeof(ctx->key));
|
||||
}
|
||||
|
||||
void HMAC_CTX_free(HMAC_CTX *ctx)
|
||||
{
|
||||
if (ctx != NULL) {
|
||||
hmac_ctx_cleanup(ctx);
|
||||
#if OPENSSL_VERSION_NUMBER > 0x10100000L
|
||||
EVP_MD_CTX_free(&ctx->i_ctx);
|
||||
EVP_MD_CTX_free(&ctx->o_ctx);
|
||||
EVP_MD_CTX_free(&ctx->md_ctx);
|
||||
#endif
|
||||
OPENSSL_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int HMAC_CTX_reset(HMAC_CTX *ctx)
|
||||
{
|
||||
HMAC_CTX_init(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0 // by apex.liu
|
||||
#ifndef HAVE_OPENSSL_EVP_CIPHER_CTX_NEW
|
||||
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
|
||||
{
|
||||
return OPENSSL_zalloc(sizeof(EVP_CIPHER_CTX));
|
||||
}
|
||||
|
||||
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
|
||||
{
|
||||
/* EVP_CIPHER_CTX_reset(ctx); alias */
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
OPENSSL_free(ctx);
|
||||
}
|
||||
#endif
|
||||
#endif // endif by apex.liu
|
||||
|
||||
void DH_get0_pqg(const DH *dh,
|
||||
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
||||
{
|
||||
if (p) {
|
||||
*p = dh->p;
|
||||
}
|
||||
if (q) {
|
||||
*q = NULL;
|
||||
}
|
||||
if (g) {
|
||||
*g = dh->g;
|
||||
}
|
||||
}
|
||||
|
||||
int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{
|
||||
if (p) {
|
||||
if (dh->p) {
|
||||
BN_free(dh->p);
|
||||
}
|
||||
dh->p = p;
|
||||
}
|
||||
if (g) {
|
||||
if (dh->g) {
|
||||
BN_free(dh->g);
|
||||
}
|
||||
dh->g = g;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DH_get0_key(const DH *dh,
|
||||
const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{
|
||||
if (pub_key) {
|
||||
*pub_key = dh->pub_key;
|
||||
}
|
||||
if (priv_key) {
|
||||
*priv_key = dh->priv_key;
|
||||
}
|
||||
}
|
||||
|
||||
int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{
|
||||
if (pub_key) {
|
||||
if (dh->pub_key) {
|
||||
BN_free(dh->pub_key);
|
||||
}
|
||||
dh->pub_key = pub_key;
|
||||
}
|
||||
if (priv_key) {
|
||||
if (dh->priv_key) {
|
||||
BN_free(dh->priv_key);
|
||||
}
|
||||
dh->priv_key = priv_key;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,8 +1,16 @@
|
|||
[external_ver]
|
||||
openssl = 1.0.2s,1000210f
|
||||
libuv = 1.28.0
|
||||
mbedtls = 2.12.0
|
||||
libssh = 0.9.0
|
||||
jsoncpp = 0.10.6
|
||||
mongoose = 6.12
|
||||
|
||||
; https://github.com/openssl/openssl/releases
|
||||
; http://slproweb.com/download/Win32OpenSSL-1_1_1d.exe
|
||||
openssl = 1.1.1d,1010104f
|
||||
; https://github.com/libuv/libuv/releases
|
||||
libuv = 1.33.1
|
||||
; https://github.com/ARMmbed/mbedtls/releases
|
||||
mbedtls = 2.16.3
|
||||
; https://github.com/open-source-parsers/jsoncpp/releases
|
||||
jsoncpp = 1.9.2
|
||||
; https://github.com/cesanta/mongoose/releases
|
||||
mongoose = 6.16
|
||||
; https://www.zlib.net/zlib1211.zip
|
||||
zlib = 1.2.11,1211
|
||||
; https://git.libssh.org/projects/libssh.git/
|
||||
libssh = 0.9.2
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ typedef struct TS_RECORD_HEADER_INFO {
|
|||
// 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)
|
||||
|
||||
|
|
@ -62,13 +61,14 @@ typedef struct TS_RECORD_HEADER_BASIC {
|
|||
// // 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;
|
||||
ex_u8 _reserve1[64 - ts_record_header_info_size];
|
||||
TS_RECORD_HEADER_BASIC basic;
|
||||
ex_u8 _reserve2[512 - 64 - ts_record_header_basic_size];
|
||||
}TS_RECORD_HEADER;
|
||||
|
||||
// header部分(header-info + header-basic) = 512B
|
||||
|
|
@ -77,10 +77,9 @@ typedef struct 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数量匹配)
|
||||
ex_u8 _reserve[3]; // 保留
|
||||
}TS_RECORD_PKG;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,4 +1,4 @@
|
|||
#include "ts_http_rpc.h"
|
||||
#include "ts_http_rpc.h"
|
||||
#include "ts_ver.h"
|
||||
#include "ts_env.h"
|
||||
#include "ts_session.h"
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
extern TppManager g_tpp_mgr;
|
||||
|
||||
#include <teleport_const.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
||||
|
|
@ -99,7 +100,7 @@ bool TsHttpRpc::init(void)
|
|||
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
|
||||
// 导致内存泄露的地方(每次请求约消耗1KB内存)
|
||||
// 导致内存泄露的地方(每次请求约消耗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
|
||||
|
|
@ -153,7 +154,7 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
|
|||
_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]);
|
||||
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;
|
||||
|
|
@ -188,7 +189,7 @@ ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Jso
|
|||
}
|
||||
|
||||
if (need_decode) {
|
||||
// 将参数进行 url-decode 解码
|
||||
// 将参数进行 url-decode 解码
|
||||
int len = json_str.length() * 2;
|
||||
ex_chars sztmp;
|
||||
sztmp.resize(len);
|
||||
|
|
@ -202,9 +203,14 @@ ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Jso
|
|||
if (0 == json_str.length())
|
||||
return TPE_PARAM;
|
||||
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> 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(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())
|
||||
|
|
@ -221,37 +227,49 @@ ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Jso
|
|||
|
||||
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_data)
|
||||
{
|
||||
// 返回: {"code":errcode, "data":{jr_data}}
|
||||
// 返回: {"code":errcode, "data":{jr_data}}
|
||||
|
||||
Json::FastWriter jr_writer;
|
||||
//Json::FastWriter jr_writer;
|
||||
Json::Value jr_root;
|
||||
|
||||
jr_root["code"] = errcode;
|
||||
jr_root["data"] = jr_data;
|
||||
buf = jr_writer.write(jr_root);
|
||||
//buf = jr_writer.write(jr_root);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(jr_root, &os);
|
||||
buf = os.str();
|
||||
}
|
||||
|
||||
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode)
|
||||
{
|
||||
// 返回: {"code":errcode}
|
||||
// 返回: {"code":errcode}
|
||||
|
||||
Json::FastWriter jr_writer;
|
||||
//Json::FastWriter jr_writer;
|
||||
Json::Value jr_root;
|
||||
|
||||
jr_root["code"] = errcode;
|
||||
buf = jr_writer.write(jr_root);
|
||||
//buf = jr_writer.write(jr_root);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(jr_root, &os);
|
||||
buf = os.str();
|
||||
}
|
||||
|
||||
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const char* message)
|
||||
{
|
||||
// 返回: {"code":errcode, "message":message}
|
||||
// 返回: {"code":errcode, "message":message}
|
||||
|
||||
Json::FastWriter jr_writer;
|
||||
//Json::FastWriter jr_writer;
|
||||
Json::Value jr_root;
|
||||
|
||||
jr_root["code"] = errcode;
|
||||
jr_root["message"] = message;
|
||||
buf = jr_writer.write(jr_root);
|
||||
//buf = jr_writer.write(jr_root);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(jr_root, &os);
|
||||
buf = os.str();
|
||||
}
|
||||
|
||||
void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf)
|
||||
|
|
@ -280,10 +298,10 @@ void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& jso
|
|||
}
|
||||
}
|
||||
|
||||
extern bool g_exit_flag; // 要求整个TS退出的标志(用于停止各个工作线程)
|
||||
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);
|
||||
}
|
||||
|
|
@ -370,7 +388,7 @@ void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr
|
|||
// info->ref_count = 0;
|
||||
// info->ticket_start = ex_get_tick_count();
|
||||
//
|
||||
// 生成一个session-id(内部会避免重复)
|
||||
// 生成一个session-id(内部会避免重复)
|
||||
ex_astr sid;
|
||||
if (!g_session_mgr.request_session(sid, info)) {
|
||||
_create_json_ret(buf, TPE_FAILED);
|
||||
|
|
@ -421,14 +439,14 @@ void TsHttpRpc::_rpc_func_kill_sessions(const Json::Value& json_param, ex_astr&
|
|||
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}
|
||||
// 加密一个字符串 [ 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())
|
||||
{
|
||||
|
|
@ -468,7 +486,7 @@ 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 # 按分钟计
|
||||
"noop-timeout": 15 # 按分钟计
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
@ -497,14 +515,14 @@ void TsHttpRpc::_rpc_func_set_config(const Json::Value& json_param, ex_astr& buf
|
|||
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}
|
||||
// 加密多个个字符串 [ 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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "ts_web_rpc.h"
|
||||
#include "ts_web_rpc.h"
|
||||
#include "ts_env.h"
|
||||
#include "ts_crypto.h"
|
||||
#include "ts_http_client.h"
|
||||
|
|
@ -10,13 +10,18 @@
|
|||
|
||||
bool ts_web_rpc_register_core()
|
||||
{
|
||||
Json::FastWriter json_writer;
|
||||
//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_param = json_writer.write(jreq);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> 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);
|
||||
|
|
@ -31,13 +36,18 @@ bool ts_web_rpc_register_core()
|
|||
|
||||
int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info)
|
||||
{
|
||||
Json::FastWriter json_writer;
|
||||
//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_param = json_writer.write(jreq);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> 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);
|
||||
|
|
@ -57,10 +67,17 @@ int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info)
|
|||
return TPE_NETWORK;
|
||||
}
|
||||
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::Value jret;
|
||||
|
||||
if (!jreader.parse(body.c_str(), jret))
|
||||
//if (!jreader.parse(body.c_str(), jret))
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> 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;
|
||||
|
|
@ -135,13 +152,13 @@ int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info)
|
|||
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 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 acc_username; // 远程主机的账号
|
||||
ex_astr acc_secret;// 远程主机账号的密码(或者私钥)
|
||||
ex_astr username_prompt;
|
||||
ex_astr password_prompt;
|
||||
int protocol_type = 0;
|
||||
|
|
@ -171,8 +188,8 @@ int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info)
|
|||
_enc = _jret["_enc"].asBool();
|
||||
|
||||
|
||||
// 进一步判断参数是否合法
|
||||
// 注意,account_id可以为-1,表示这是一次测试连接。
|
||||
// 进一步判断参数是否合法
|
||||
// 注意,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
|
||||
|
|
@ -216,7 +233,7 @@ int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info)
|
|||
|
||||
bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id)
|
||||
{
|
||||
Json::FastWriter json_writer;
|
||||
//Json::FastWriter json_writer;
|
||||
Json::Value jreq;
|
||||
|
||||
jreq["method"] = "session_begin";
|
||||
|
|
@ -236,7 +253,12 @@ bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id)
|
|||
jreq["param"]["protocol_sub_type"] = info.protocol_sub_type;
|
||||
|
||||
ex_astr json_param;
|
||||
json_param = json_writer.write(jreq);
|
||||
//json_param = json_writer.write(jreq);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> 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);
|
||||
|
|
@ -254,11 +276,18 @@ bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id)
|
|||
return false;
|
||||
}
|
||||
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::Value jret;
|
||||
|
||||
if (!jreader.parse(body.c_str(), jret))
|
||||
return false;
|
||||
//if (!jreader.parse(body.c_str(), jret))
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> 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())
|
||||
|
|
@ -272,7 +301,7 @@ bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id)
|
|||
}
|
||||
|
||||
bool ts_web_rpc_session_update(int record_id, int protocol_sub_type, int state) {
|
||||
Json::FastWriter json_writer;
|
||||
//Json::FastWriter json_writer;
|
||||
Json::Value jreq;
|
||||
jreq["method"] = "session_update";
|
||||
jreq["param"]["rid"] = record_id;
|
||||
|
|
@ -280,7 +309,12 @@ bool ts_web_rpc_session_update(int record_id, int protocol_sub_type, int state)
|
|||
jreq["param"]["code"] = state;
|
||||
|
||||
ex_astr json_param;
|
||||
json_param = json_writer.write(jreq);
|
||||
//json_param = json_writer.write(jreq);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> 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);
|
||||
|
|
@ -294,19 +328,24 @@ bool ts_web_rpc_session_update(int record_id, int protocol_sub_type, int state)
|
|||
}
|
||||
|
||||
|
||||
//session 结束
|
||||
//session 结束
|
||||
bool ts_web_rpc_session_end(const char* sid, int record_id, int ret_code)
|
||||
{
|
||||
// TODO: 对指定的sid相关的会话的引用计数减一(但减到0时销毁)
|
||||
// TODO: 对指定的sid相关的会话的引用计数减一(但减到0时销毁)
|
||||
|
||||
Json::FastWriter json_writer;
|
||||
//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_param = json_writer.write(jreq);
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> 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);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ MESSAGE(STATUS "=======================================================")
|
|||
include(../../../../CMakeCfg.txt)
|
||||
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-fPIC")
|
||||
set(CMAKE_C_FLAGS "-fPIC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
|
||||
|
||||
aux_source_directory(. DIR_SSH_SRCS)
|
||||
aux_source_directory(../../common DIR_SSH_SRCS)
|
||||
|
|
@ -27,15 +28,18 @@ include_directories(
|
|||
)
|
||||
|
||||
include_directories(
|
||||
${TP_EXTERNAL_RELEASE_DIR}/include
|
||||
)
|
||||
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
|
||||
${TP_EXTERNAL_RELEASE_DIR}/include
|
||||
)
|
||||
link_directories(
|
||||
${TP_EXTERNAL_RELEASE_DIR}/lib
|
||||
${TP_EXTERNAL_RELEASE_DIR}/lib64
|
||||
)
|
||||
|
||||
|
||||
add_library(tpssh SHARED ${DIR_SSH_SRCS})
|
||||
|
||||
if (OS_LINUX)
|
||||
target_link_libraries(tpssh ssh ssl crypto mbedx509 mbedtls mbedcrypto dl pthread rt util)
|
||||
target_link_libraries(tpssh ssh ssl crypto mbedx509 mbedtls mbedcrypto z dl pthread rt util)
|
||||
elseif (OS_MACOS)
|
||||
target_link_libraries(tpssh ssh ssl crypto mbedx509 mbedtls mbedcrypto dl pthread util)
|
||||
target_link_libraries(tpssh ssh ssl crypto mbedx509 mbedtls mbedcrypto z dl pthread util)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ void SshProxy::kill_sessions(const ex_astrs &sessions) {
|
|||
for (size_t i = 0; i < sessions.size(); ++i) {
|
||||
if (it->first->sid() == sessions[i]) {
|
||||
EXLOGW("[ssh] try to kill %s\n", sessions[i].c_str());
|
||||
it->first->check_noop_timeout(0, 0); // 立即结束
|
||||
it->first->check_noop_timeout(0, 0); // 立即结束
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,13 +96,15 @@ void SshProxy::_thread_loop() {
|
|||
EXLOGI("[ssh] TeleportServer-SSH ready on %s:%d\n", m_host_ip.c_str(), m_host_port);
|
||||
|
||||
for (;;) {
|
||||
// 注意,ssh_new()出来的指针,如果遇到停止标志,本函数内部就释放了,否则这个指针交给了SshSession类实例管理,其析构时会释放。
|
||||
// 注意,ssh_new()出来的指针,如果遇到停止标志,本函数内部就释放了,否则这个指针交给了SshSession类实例管理,其析构时会释放。
|
||||
ssh_session sess_to_client = ssh_new();
|
||||
|
||||
// int flag = SSH_LOG_FUNCTIONS;
|
||||
// ssh_options_set(sess_to_client, SSH_OPTIONS_LOG_VERBOSITY, &flag);
|
||||
// #ifdef EX_DEBUG
|
||||
// int flag = SSH_LOG_FUNCTIONS;
|
||||
// ssh_options_set(sess_to_client, SSH_OPTIONS_LOG_VERBOSITY, &flag);
|
||||
// #endif
|
||||
|
||||
ssh_set_blocking(sess_to_client, 1);
|
||||
//ssh_set_blocking(sess_to_client, 1);
|
||||
|
||||
struct sockaddr_storage sock_client;
|
||||
char ip[32] = {0};
|
||||
|
|
@ -145,14 +147,14 @@ void SshProxy::_thread_loop() {
|
|||
sess->start();
|
||||
}
|
||||
|
||||
// 等待所有工作线程退出
|
||||
// 等待所有工作线程退出
|
||||
//m_thread_mgr.stop_all();
|
||||
|
||||
{
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
ts_ssh_sessions::iterator it = m_sessions.begin();
|
||||
for (; it != m_sessions.end(); ++it) {
|
||||
it->first->check_noop_timeout(0, 0); // 立即结束
|
||||
it->first->check_noop_timeout(0, 0); // 立即结束
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +175,7 @@ void SshProxy::_on_stop() {
|
|||
ExThreadBase::_on_stop();
|
||||
|
||||
if (m_is_running) {
|
||||
// 用一个变通的方式来结束阻塞中的监听,就是连接一下它。
|
||||
// 用一个变通的方式来结束阻塞中的监听,就是连接一下它。
|
||||
ex_astr host_ip = m_host_ip;
|
||||
if (host_ip == "0.0.0.0")
|
||||
host_ip = "127.0.0.1";
|
||||
|
|
@ -185,14 +187,17 @@ void SshProxy::_on_stop() {
|
|||
int _timeout_us = 10;
|
||||
ssh_options_set(_session, SSH_OPTIONS_TIMEOUT, &_timeout_us);
|
||||
ssh_connect(_session);
|
||||
ssh_disconnect(_session);
|
||||
ssh_free(_session);
|
||||
|
||||
ex_sleep_ms(100);
|
||||
}
|
||||
|
||||
// m_thread_mgr.stop_all();
|
||||
}
|
||||
|
||||
void SshProxy::session_finished(SshSession *sess) {
|
||||
// TODO: 向核心模块汇报此会话终止,以减少对应连接信息的引用计数
|
||||
// TODO: 向核心模块汇报此会话终止,以减少对应连接信息的引用计数
|
||||
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
ts_ssh_sessions::iterator it = m_sessions.find(sess);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "ssh_recorder.h"
|
||||
#include "ssh_recorder.h"
|
||||
//#include <teleport_const.h>
|
||||
|
||||
static ex_u8 TPP_RECORD_MAGIC[4] = {'T', 'P', 'P', 'R'};
|
||||
|
|
@ -8,7 +8,8 @@ TppSshRec::TppSshRec() {
|
|||
|
||||
memset(&m_head, 0, sizeof(TS_RECORD_HEADER));
|
||||
memcpy((ex_u8 *) (&m_head.info.magic), TPP_RECORD_MAGIC, sizeof(ex_u32));
|
||||
m_head.info.ver = 0x03;
|
||||
m_head.info.ver = 0x04;
|
||||
m_head.info.type = TS_TPPR_TYPE_SSH;
|
||||
m_header_changed = false;
|
||||
m_save_full_header = false;
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ bool TppSshRec::_on_begin(const TPP_CONNECT_INFO *info) {
|
|||
}
|
||||
|
||||
bool TppSshRec::_on_end() {
|
||||
// 如果还有剩下未写入的数据,写入文件中。
|
||||
// 如果还有剩下未写入的数据,写入文件中。
|
||||
save_record();
|
||||
|
||||
if (m_file_info != NULL)
|
||||
|
|
@ -73,13 +74,14 @@ void TppSshRec::record(ex_u8 type, const ex_u8 *data, size_t size) {
|
|||
if (m_start_time > 0) {
|
||||
pkg.time_ms = (ex_u32) (ex_get_tick_count() - m_start_time);
|
||||
m_head.info.time_ms = pkg.time_ms;
|
||||
m_header_changed = true;
|
||||
}
|
||||
|
||||
m_cache.append((ex_u8 *) &pkg, sizeof(TS_RECORD_PKG));
|
||||
m_cache.append(data, size);
|
||||
|
||||
m_head.info.packages++;
|
||||
m_header_changed = true;
|
||||
//m_head.info.packages++;
|
||||
//m_header_changed = true;
|
||||
}
|
||||
|
||||
void TppSshRec::record_win_size_startup(int width, int height) {
|
||||
|
|
@ -95,7 +97,7 @@ void TppSshRec::record_win_size_change(int width, int height) {
|
|||
record(TS_RECORD_TYPE_SSH_TERM_SIZE, (ex_u8 *) &pkg, sizeof(TS_RECORD_WIN_SIZE));
|
||||
}
|
||||
|
||||
// 为了录像回放和命令历史能够对应(比如点击命令直接跳到录像的对应时点),仿照录像数据包的方式记录相对时间偏移,而不是绝对时间。
|
||||
// 为了录像回放和命令历史能够对应(比如点击命令直接跳到录像的对应时点),仿照录像数据包的方式记录相对时间偏移,而不是绝对时间。
|
||||
void TppSshRec::record_command(int flag, const ex_astr &cmd) {
|
||||
char szTime[100] = {0};
|
||||
#ifdef EX_OS_WIN32
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ void SshSession::_record_end(TP_SSH_CHANNEL_PAIR *cp) {
|
|||
if (cp->db_id > 0) {
|
||||
//EXLOGD("[ssh] [channel:%d] channel end with code: %d\n", cp->channel_id, cp->state);
|
||||
|
||||
// 如果会话过程中没有发生错误,则将其状态改为结束,否则记录下错误值
|
||||
// 如果会话过程中没有发生错误,则将其状态改为结束,否则记录下错误值
|
||||
if (cp->state == TP_SESS_STAT_RUNNING || cp->state == TP_SESS_STAT_STARTED)
|
||||
cp->state = TP_SESS_STAT_END;
|
||||
|
||||
|
|
@ -257,7 +257,7 @@ void SshSession::_run(void) {
|
|||
|
||||
int err = SSH_OK;
|
||||
|
||||
// 安全连接(密钥交换)
|
||||
// 安全连接(密钥交换)
|
||||
err = ssh_handle_key_exchange(m_cli_session);
|
||||
if (err != SSH_OK) {
|
||||
EXLOGE("[ssh] key exchange with client failed: %s\n", ssh_get_error(m_cli_session));
|
||||
|
|
@ -275,7 +275,7 @@ void SshSession::_run(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
// 认证,并打开一个通道
|
||||
// 认证,并打开一个通道
|
||||
while (!(m_is_logon && !m_channels.empty())) {
|
||||
if (m_have_error)
|
||||
break;
|
||||
|
|
@ -296,7 +296,7 @@ void SshSession::_run(void) {
|
|||
|
||||
EXLOGW("[ssh] authenticated and got a channel.\n");
|
||||
|
||||
// 现在双方的连接已经建立好了,开始转发
|
||||
// 现在双方的连接已经建立好了,开始转发
|
||||
ssh_event_add_session(event_loop, m_srv_session);
|
||||
do {
|
||||
//err = ssh_event_dopoll(event_loop, 5000);
|
||||
|
|
@ -333,11 +333,11 @@ void SshSession::_run(void) {
|
|||
ssh_event_free(event_loop);
|
||||
|
||||
|
||||
// 如果一边是走SSHv1,另一边是SSHv2,放在同一个event_loop时,SSHv1会收不到数据,放到循环中时,SSHv2得不到数据
|
||||
// 所以,当SSHv1的远程主机连接后,到建立好shell环境之后,就进入另一种读取数据的循环,不再使用ssh_event_dopoll()了。
|
||||
// 如果一边是走SSHv1,另一边是SSHv2,放在同一个event_loop时,SSHv1会收不到数据,放到循环中时,SSHv2得不到数据
|
||||
// 所以,当SSHv1的远程主机连接后,到建立好shell环境之后,就进入另一种读取数据的循环,不再使用ssh_event_dopoll()了。
|
||||
|
||||
if (m_ssh_ver == 1) {
|
||||
tp_channels::iterator it = m_channels.begin(); // SSHv1只能打开一个channel
|
||||
tp_channels::iterator it = m_channels.begin(); // SSHv1只能打开一个channel
|
||||
ssh_channel cli = (*it)->cli_channel;
|
||||
ssh_channel srv = (*it)->srv_channel;
|
||||
|
||||
|
|
@ -447,7 +447,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
|
|||
EXLOGV("[ssh] try to connect to real SSH server %s:%d\n", _this->m_conn_ip.c_str(), _this->m_conn_port);
|
||||
_this->m_srv_session = ssh_new();
|
||||
|
||||
ssh_set_blocking(_this->m_srv_session, 1);
|
||||
// ssh_set_blocking(_this->m_srv_session, 1);
|
||||
|
||||
ssh_options_set(_this->m_srv_session, SSH_OPTIONS_HOST, _this->m_conn_ip.c_str());
|
||||
int port = (int) _this->m_conn_port;
|
||||
|
|
@ -460,12 +460,16 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
|
|||
// ssh_options_set(_this->m_srv_session, SSH_OPTIONS_LOG_VERBOSITY, &flag);
|
||||
//#endif
|
||||
|
||||
int _timeout_cli = 120; // 120 sec.
|
||||
ssh_options_set(_this->m_cli_session, SSH_OPTIONS_TIMEOUT, &_timeout_cli);
|
||||
|
||||
|
||||
if (_this->m_auth_type != TP_AUTH_TYPE_NONE)
|
||||
ssh_options_set(_this->m_srv_session, SSH_OPTIONS_USER, _this->m_acc_name.c_str());
|
||||
|
||||
// default timeout is 10 seconds, it is too short for connect progress, so set it to 60 sec.
|
||||
int _timeout = 60; // 60 sec.
|
||||
// default timeout is 10 seconds, it is too short for connect progress, so set it to 120 sec.
|
||||
// usually when sshd config to UseDNS.
|
||||
int _timeout = 120; // 120 sec.
|
||||
ssh_options_set(_this->m_srv_session, SSH_OPTIONS_TIMEOUT, &_timeout);
|
||||
|
||||
int rc = 0;
|
||||
|
|
@ -477,14 +481,19 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
|
|||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
if(ssh_is_blocking(_this->m_cli_session))
|
||||
EXLOGD("[ssh] client session is blocking.\n");
|
||||
if(ssh_is_blocking(_this->m_srv_session))
|
||||
EXLOGD("[ssh] server session is blocking.\n");
|
||||
|
||||
// once the server are connected, change the timeout back to default.
|
||||
_timeout = 30; // in seconds.
|
||||
_timeout = 120; // in seconds.
|
||||
ssh_options_set(_this->m_srv_session, SSH_OPTIONS_TIMEOUT, &_timeout);
|
||||
|
||||
// get ssh version of host, v1 or v2
|
||||
// TODO: libssh-0.8.5 does not support sshv1 anymore.
|
||||
_this->m_ssh_ver = ssh_get_version(_this->m_srv_session);
|
||||
EXLOGW("[ssh] real host is SSHv%d\n", _this->m_ssh_ver);
|
||||
//EXLOGW("[ssh] real host is SSHv%d\n", _this->m_ssh_ver);
|
||||
|
||||
#if 0
|
||||
// check supported auth type by host
|
||||
|
|
@ -634,13 +643,13 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
|
|||
}
|
||||
|
||||
ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userdata) {
|
||||
// 客户端尝试打开一个通道(然后才能通过这个通道发控制命令或者收发数据)
|
||||
// 客户端尝试打开一个通道(然后才能通过这个通道发控制命令或者收发数据)
|
||||
EXLOGV("[ssh] client open channel\n");
|
||||
|
||||
SshSession *_this = (SshSession *) userdata;
|
||||
|
||||
// TODO: 客户端与TP连接使用的总是SSHv2协议,因为最开始连接时还不知道真正的远程主机是不是SSHv1。
|
||||
// 因此此处行为与客户端直连远程主机有些不一样。直连时,SecureCRT的克隆会话功能会因为以为连接的是SSHv1而自动重新连接,而不是打开新通道。
|
||||
// TODO: 客户端与TP连接使用的总是SSHv2协议,因为最开始连接时还不知道真正的远程主机是不是SSHv1。
|
||||
// 因此此处行为与客户端直连远程主机有些不一样。直连时,SecureCRT的克隆会话功能会因为以为连接的是SSHv1而自动重新连接,而不是打开新通道。
|
||||
if (_this->m_ssh_ver == 1 && _this->m_channels.size() != 0) {
|
||||
EXLOGE("[ssh] SSH1 supports only one execution channel. One has already been opened.\n");
|
||||
return NULL;
|
||||
|
|
@ -653,7 +662,7 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd
|
|||
}
|
||||
ssh_set_channel_callbacks(cli_channel, &_this->m_cli_channel_cb);
|
||||
|
||||
// 我们也要向真正的服务器申请打开一个通道,来进行转发
|
||||
// 我们也要向真正的服务器申请打开一个通道,来进行转发
|
||||
ssh_channel srv_channel = ssh_channel_new(_this->m_srv_session);
|
||||
if (srv_channel == NULL) {
|
||||
EXLOGE("[ssh] can not create channel for server.\n");
|
||||
|
|
@ -682,7 +691,7 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// 将客户端和服务端的通道关联起来
|
||||
// 将客户端和服务端的通道关联起来
|
||||
{
|
||||
ExThreadSmartLock locker(_this->m_lock);
|
||||
_this->m_channels.push_back(cp);
|
||||
|
|
@ -794,7 +803,7 @@ int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel
|
|||
|
||||
SshSession *_this = (SshSession *) userdata;
|
||||
|
||||
// 当前线程正在接收服务端返回的数据,因此我们直接返回,这样紧跟着会重新再发送此数据的
|
||||
// 当前线程正在接收服务端返回的数据,因此我们直接返回,这样紧跟着会重新再发送此数据的
|
||||
if (_this->m_recving_from_srv) {
|
||||
// EXLOGD("recving from srv...try again later...\n");
|
||||
return 0;
|
||||
|
|
@ -815,14 +824,14 @@ int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel
|
|||
|
||||
int _len = len;
|
||||
if (cp->type == TS_SSH_CHANNEL_TYPE_SHELL) {
|
||||
// 在收取服务端数据直到显示命令行提示符之前,不允许发送客户端数据到服务端,避免日志记录混乱。
|
||||
// 在收取服务端数据直到显示命令行提示符之前,不允许发送客户端数据到服务端,避免日志记录混乱。
|
||||
if (!cp->server_ready) {
|
||||
_this->m_recving_from_cli = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 不可以拆分!!否则执行 rz 命令会出错!
|
||||
// xxxx 如果用户复制粘贴多行文本,我们将其拆分为每一行发送一次数据包
|
||||
// 不可以拆分!!否则执行 rz 命令会出错!
|
||||
// xxxx 如果用户复制粘贴多行文本,我们将其拆分为每一行发送一次数据包
|
||||
// for (unsigned int i = 0; i < len; ++i) {
|
||||
// if (((ex_u8 *) data)[i] == 0x0d) {
|
||||
// _len = i + 1;
|
||||
|
|
@ -889,7 +898,7 @@ int SshSession::_on_client_channel_subsystem_request(ssh_session session, ssh_ch
|
|||
cp->last_access_timestamp = (ex_u32) time(NULL);
|
||||
|
||||
|
||||
// 目前只支持SFTP子系统
|
||||
// 目前只支持SFTP子系统
|
||||
if (strcmp(subsystem, "sftp") != 0) {
|
||||
EXLOGE("[ssh] support `sftp` subsystem only, but got `%s`.\n", subsystem);
|
||||
cp->state = TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL;
|
||||
|
|
@ -961,7 +970,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
|||
|
||||
int ret = 0;
|
||||
|
||||
// 收到第一包服务端返回的数据时,在输出数据之前显示一些自定义的信息
|
||||
// 收到第一包服务端返回的数据时,在输出数据之前显示一些自定义的信息
|
||||
#if 1
|
||||
if (!is_stderr && cp->is_first_server_data) {
|
||||
cp->is_first_server_data = false;
|
||||
|
|
@ -988,13 +997,15 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
|||
"\r\n"\
|
||||
"%s\r\n"\
|
||||
"Teleport SSH Bastion Server...\r\n"\
|
||||
" - teleport to %s:%d\r\n"\
|
||||
" - teleport to %s:%d [%d]\r\n"\
|
||||
" - authroized by %s\r\n"\
|
||||
"%s\r\n"\
|
||||
"\r\n\r\n",
|
||||
line.c_str(),
|
||||
_this->m_conn_ip.c_str(),
|
||||
_this->m_conn_port, auth_mode,
|
||||
_this->m_conn_port,
|
||||
cp->db_id,
|
||||
auth_mode,
|
||||
line.c_str()
|
||||
);
|
||||
|
||||
|
|
@ -1013,15 +1024,45 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
|||
#endif
|
||||
|
||||
#if 1
|
||||
// 直接转发数据到客户端
|
||||
if (is_stderr)
|
||||
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
|
||||
else
|
||||
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||
//static int idx = 0;
|
||||
|
||||
ssh_set_blocking(_this->m_cli_session, 0);
|
||||
|
||||
int xx = 0;
|
||||
for(xx = 0; xx < 10; ++xx) {
|
||||
|
||||
// idx++;
|
||||
// EXLOGD(">>>>> %d . %d\n", cp->db_id, idx);
|
||||
|
||||
// 直接转发数据到客户端
|
||||
if (is_stderr)
|
||||
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
|
||||
else
|
||||
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||
|
||||
// EXLOGD("<<<<< %d . %d\n", cp->db_id, idx);
|
||||
|
||||
if(ret == SSH_OK) {
|
||||
// EXLOGD("ssh_channel_write() ok.\n");
|
||||
break;
|
||||
}
|
||||
else if(ret == SSH_AGAIN) {
|
||||
// EXLOGD("ssh_channel_write() need again, %d.\n", xx);
|
||||
ex_sleep_ms(500);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// EXLOGD("ssh_channel_write() failed.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ssh_set_blocking(_this->m_cli_session, 1);
|
||||
|
||||
#else
|
||||
// 分析收到的服务端数据包,如果包含类似 \033]0;AABB\007 这样的数据,客户端会根据此改变窗口标题
|
||||
// 我们需要替换这部分数据,使之显示类似 \033]0;TP#ssh://remote-ip\007 这样的标题。
|
||||
// 但是这样会降低一些性能,因此目前不启用,保留此部分代码备用。
|
||||
// 分析收到的服务端数据包,如果包含类似 \033]0;AABB\007 这样的数据,客户端会根据此改变窗口标题
|
||||
// 我们需要替换这部分数据,使之显示类似 \033]0;TP#ssh://remote-ip\007 这样的标题。
|
||||
// 但是这样会降低一些性能,因此目前不启用,保留此部分代码备用。
|
||||
if (is_stderr) {
|
||||
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
|
||||
}
|
||||
|
|
@ -1038,7 +1079,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
|||
{
|
||||
_end++;
|
||||
|
||||
// 这个包中含有改变标题的数据,将标题换为我们想要的
|
||||
// 这个包中含有改变标题的数据,将标题换为我们想要的
|
||||
EXLOGD("-- found title\n");
|
||||
size_t len_end = len - (_end - (const ex_u8*)data);
|
||||
MemBuffer mbuf;
|
||||
|
|
@ -1060,7 +1101,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
|||
if (ret == SSH_ERROR)
|
||||
break;
|
||||
if (ret == mbuf.size()) {
|
||||
ret = len; // 表示我们已经处理了所有的数据了。
|
||||
ret = len; // 表示我们已经处理了所有的数据了。
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1139,7 +1180,7 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
if (TP_SSH_CLIENT_SIDE == from) {
|
||||
if (len >= 2) {
|
||||
if (((ex_u8 *) data)[len - 1] == 0x0d) {
|
||||
// 疑似复制粘贴多行命令一次性执行,将其记录到日志文件中
|
||||
// 疑似复制粘贴多行命令一次性执行,将其记录到日志文件中
|
||||
ex_astr str((const char *) data, len - 1);
|
||||
cp->rec.record_command(1, str);
|
||||
|
||||
|
|
@ -1148,13 +1189,13 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
}
|
||||
}
|
||||
|
||||
// 客户端输入回车时,可能时执行了一条命令,需要根据服务端返回的数据进行进一步判断
|
||||
// 客户端输入回车时,可能时执行了一条命令,需要根据服务端返回的数据进行进一步判断
|
||||
cp->maybe_cmd = (data[len - 1] == 0x0d);
|
||||
// if (cp->maybe_cmd)
|
||||
// EXLOGD("[ssh] maybe cmd.\n");
|
||||
|
||||
// 有时在执行类似top命令的情况下,输入一个字母'q'就退出程序,没有输入回车,可能会导致后续记录命令时将返回的命令行提示符作为命令
|
||||
// 记录下来了,要避免这种情况,排除的方式是:客户端单个字母,后续服务端如果收到的是控制序列 1b 5b xx xx,就不计做命令。
|
||||
// 有时在执行类似top命令的情况下,输入一个字母'q'就退出程序,没有输入回车,可能会导致后续记录命令时将返回的命令行提示符作为命令
|
||||
// 记录下来了,要避免这种情况,排除的方式是:客户端单个字母,后续服务端如果收到的是控制序列 1b 5b xx xx,就不计做命令。
|
||||
cp->client_single_char = (len == 1 && isprint(data[0]));
|
||||
|
||||
cp->process_srv = true;
|
||||
|
|
@ -1193,15 +1234,15 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
|
||||
case 0x4b: { // 'K'
|
||||
if (0 == esc_arg) {
|
||||
// 删除光标到行尾的字符串
|
||||
// 删除光标到行尾的字符串
|
||||
cp->cmd_char_list.erase(cp->cmd_char_pos, cp->cmd_char_list.end());
|
||||
cp->cmd_char_pos = cp->cmd_char_list.end();
|
||||
} else if (1 == esc_arg) {
|
||||
// 删除从开始到光标处的字符串
|
||||
// 删除从开始到光标处的字符串
|
||||
cp->cmd_char_list.erase(cp->cmd_char_list.begin(), cp->cmd_char_pos);
|
||||
cp->cmd_char_pos = cp->cmd_char_list.end();
|
||||
} else if (2 == esc_arg) {
|
||||
// 删除整行
|
||||
// 删除整行
|
||||
cp->cmd_char_list.clear();
|
||||
cp->cmd_char_pos = cp->cmd_char_list.begin();
|
||||
}
|
||||
|
|
@ -1210,7 +1251,7 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
break;
|
||||
}
|
||||
case 0x43: {// ^[C
|
||||
// 光标右移
|
||||
// 光标右移
|
||||
if (esc_arg == 0)
|
||||
esc_arg = 1;
|
||||
for (int j = 0; j < esc_arg; ++j) {
|
||||
|
|
@ -1221,7 +1262,7 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
break;
|
||||
}
|
||||
case 0x44: { // ^[D
|
||||
// 光标左移
|
||||
// 光标左移
|
||||
if (esc_arg == 0)
|
||||
esc_arg = 1;
|
||||
for (int j = 0; j < esc_arg; ++j) {
|
||||
|
|
@ -1233,7 +1274,7 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
break;
|
||||
}
|
||||
|
||||
case 0x50: {// 'P' 删除指定数量的字符
|
||||
case 0x50: {// 'P' 删除指定数量的字符
|
||||
|
||||
if (esc_arg == 0)
|
||||
esc_arg = 1;
|
||||
|
|
@ -1245,7 +1286,7 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
break;
|
||||
}
|
||||
|
||||
case 0x40: { // '@' 插入指定数量的空白字符
|
||||
case 0x40: { // '@' 插入指定数量的空白字符
|
||||
if (esc_arg == 0)
|
||||
esc_arg = 1;
|
||||
for (int j = 0; j < esc_arg; ++j)
|
||||
|
|
@ -1267,10 +1308,10 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR *cp, int from, const e
|
|||
|
||||
switch (ch) {
|
||||
case 0x07:
|
||||
// 响铃
|
||||
// 响铃
|
||||
break;
|
||||
case 0x08: {
|
||||
// 光标左移
|
||||
// 光标左移
|
||||
if (cp->cmd_char_pos != cp->cmd_char_list.begin())
|
||||
cp->cmd_char_pos--;
|
||||
break;
|
||||
|
|
@ -1343,10 +1384,10 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR *cp, const ex_u8 *dat
|
|||
// SFTP protocol: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
|
||||
//EXLOG_BIN(data, len, "[sftp] client channel data");
|
||||
|
||||
// TODO: 根据客户端的请求和服务端的返回,可以进一步判断用户是如何操作文件的,比如读、写等等,以及操作的结果是成功还是失败。
|
||||
// 记录格式: time-offset,flag,action,result,file-path,[file-path]
|
||||
// 其中,flag目前总是为0,可以忽略(为保证与ssh-cmd格式一致),time-offset/action/result 都是数字
|
||||
// file-path是被操作的对象,规格为 长度:实际内容,例如, 13:/root/abc.txt
|
||||
// TODO: 根据客户端的请求和服务端的返回,可以进一步判断用户是如何操作文件的,比如读、写等等,以及操作的结果是成功还是失败。
|
||||
// 记录格式: time-offset,flag,action,result,file-path,[file-path]
|
||||
// 其中,flag目前总是为0,可以忽略(为保证与ssh-cmd格式一致),time-offset/action/result 都是数字
|
||||
// file-path是被操作的对象,规格为 长度:实际内容,例如, 13:/root/abc.txt
|
||||
|
||||
|
||||
if (len < 9)
|
||||
|
|
@ -1364,7 +1405,7 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR *cp, const ex_u8 *dat
|
|||
return;
|
||||
}
|
||||
|
||||
// 需要的数据至少14字节
|
||||
// 需要的数据至少14字节
|
||||
// uint32 + byte + uint32 + (uint32 + char + ...)
|
||||
// pkg_len + cmd + req_id + string( length + content...)
|
||||
if (len < 14)
|
||||
|
|
@ -1397,13 +1438,13 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR *cp, const ex_u8 *dat
|
|||
break;
|
||||
case 0x12:
|
||||
// 0x12 = 18 = SSH_FXP_RENAME
|
||||
// rename操作数据中包含两个字符串
|
||||
// rename操作数据中包含两个字符串
|
||||
str2_ptr = str1_ptr + str1_len + 4;
|
||||
str2_len = (int) ((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]);
|
||||
break;
|
||||
case 0x15:
|
||||
// 0x15 = 21 = SSH_FXP_LINK
|
||||
// link操作数据中包含两个字符串,前者是新的链接文件名,后者是现有被链接的文件名
|
||||
// link操作数据中包含两个字符串,前者是新的链接文件名,后者是现有被链接的文件名
|
||||
str2_ptr = str1_ptr + str1_len + 4;
|
||||
str2_len = (int) ((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// tpssh.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
|
|
@ -11,11 +11,17 @@
|
|||
|
||||
#ifdef EX_OS_WIN32
|
||||
# ifdef EX_DEBUG
|
||||
# pragma comment(lib, "debug/ssh.lib")
|
||||
# pragma comment(lib, "debug/ssh.lib")
|
||||
# pragma comment(lib, "libcrypto32MTd.lib")
|
||||
# pragma comment(lib, "libssl32MTd.lib")
|
||||
# else
|
||||
# pragma comment(lib, "release/ssh.lib")
|
||||
# pragma comment(lib, "release/ssh.lib")
|
||||
# pragma comment(lib, "libcrypto32MT.lib")
|
||||
# pragma comment(lib, "libssl32MT.lib")
|
||||
# endif
|
||||
# pragma comment(lib, "libeay32.lib")
|
||||
// # pragma comment(lib, "libcrypto.lib")
|
||||
// # pragma comment(lib, "libeay32.lib")
|
||||
# pragma comment(lib, "ws2_32.lib")
|
||||
# pragma comment(lib, "crypt32.lib")
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "ssh_proxy.h"
|
||||
#include "ssh_proxy.h"
|
||||
#include "tpp_env.h"
|
||||
|
||||
#include <teleport_const.h>
|
||||
|
|
@ -46,10 +46,15 @@ TPP_API void tpp_timer(void) {
|
|||
|
||||
static ex_rv _set_runtime_config(const char* param) {
|
||||
Json::Value jp;
|
||||
Json::Reader jreader;
|
||||
//Json::Reader jreader;
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = param;
|
||||
ex_astr err;
|
||||
|
||||
if (!jreader.parse(param, jp))
|
||||
return TPE_JSON_FORMAT;
|
||||
//if (!jreader.parse(param, jp))
|
||||
if (!jreader->parse(str_json_begin, param + strlen(param), &jp, &err))
|
||||
return TPE_JSON_FORMAT;
|
||||
|
||||
if (!jp.isObject())
|
||||
return TPE_PARAM;
|
||||
|
|
@ -68,10 +73,16 @@ static ex_rv _set_runtime_config(const char* param) {
|
|||
|
||||
static ex_rv _kill_sessions(const char* param) {
|
||||
Json::Value jp;
|
||||
Json::Reader jreader;
|
||||
// Json::Reader jreader;
|
||||
// if (!jreader.parse(param, jp))
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = param;
|
||||
ex_astr err;
|
||||
|
||||
if (!jreader.parse(param, jp))
|
||||
return TPE_JSON_FORMAT;
|
||||
//if (!jreader.parse(param, jp))
|
||||
if (!jreader->parse(str_json_begin, param + strlen(param), &jp, &err))
|
||||
return TPE_JSON_FORMAT;
|
||||
|
||||
if (!jp.isArray())
|
||||
return TPE_PARAM;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>Debug</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\..\..\..\external\libssh\build\src\static;..\..\..\..\external\openssl\out32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>..\..\..\..\external\libssh\build\src;..\..\..\..\external\openssl\lib\VC\static;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>..\..\..\..\external\libssh\build\src\static;..\..\..\..\external\openssl\out32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>..\..\..\..\external\libssh\build\src;..\..\..\..\external\openssl\lib\VC\static;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ TppTelnetRec::TppTelnetRec()
|
|||
|
||||
memset(&m_head, 0, sizeof(TS_RECORD_HEADER));
|
||||
memcpy((ex_u8*)(&m_head.info.magic), TPP_RECORD_MAGIC, sizeof(ex_u32));
|
||||
m_head.info.ver = 0x03;
|
||||
m_head.info.ver = 0x04;
|
||||
m_header_changed = false;
|
||||
m_save_full_header = false;
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ bool TppTelnetRec::_on_begin(const TPP_CONNECT_INFO* info)
|
|||
|
||||
bool TppTelnetRec::_on_end()
|
||||
{
|
||||
// 如果还有剩下未写入的数据,写入文件中。
|
||||
// 如果还有剩下未写入的数据,写入文件中。
|
||||
save_record();
|
||||
|
||||
if (m_file_info != NULL)
|
||||
|
|
@ -76,13 +76,14 @@ void TppTelnetRec::record(ex_u8 type, const ex_u8* data, size_t size)
|
|||
{
|
||||
pkg.time_ms = (ex_u32)(ex_get_tick_count() - m_start_time);
|
||||
m_head.info.time_ms = pkg.time_ms;
|
||||
m_header_changed = true;
|
||||
}
|
||||
|
||||
m_cache.append((ex_u8*)&pkg, sizeof(TS_RECORD_PKG));
|
||||
m_cache.append(data, size);
|
||||
|
||||
m_head.info.packages++;
|
||||
m_header_changed = true;
|
||||
// m_head.info.packages++;
|
||||
// m_header_changed = true;
|
||||
}
|
||||
|
||||
// void TppTelnetRec::record_win_size(int width, int height)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "telnet_proxy.h"
|
||||
#include "telnet_proxy.h"
|
||||
#include "tpp_env.h"
|
||||
|
||||
#include <teleport_const.h>
|
||||
|
|
@ -40,11 +40,17 @@ TPP_API void tpp_timer(void) {
|
|||
|
||||
|
||||
static ex_rv _set_runtime_config(const char* param) {
|
||||
Json::Value jp;
|
||||
Json::Reader jreader;
|
||||
// Json::Value jp;
|
||||
// Json::Reader jreader;
|
||||
//
|
||||
// if (!jreader.parse(param, jp))
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
|
||||
if (!jreader.parse(param, jp))
|
||||
return TPE_JSON_FORMAT;
|
||||
ex_astr err;
|
||||
Json::Value jp;
|
||||
if (!jreader->parse(param, param + strlen(param), &jp, &err))
|
||||
return TPE_JSON_FORMAT;
|
||||
|
||||
if (!jp.isObject())
|
||||
return TPE_PARAM;
|
||||
|
|
@ -62,11 +68,17 @@ static ex_rv _set_runtime_config(const char* param) {
|
|||
}
|
||||
|
||||
static ex_rv _kill_sessions(const char* param) {
|
||||
Json::Value jp;
|
||||
Json::Reader jreader;
|
||||
// Json::Value jp;
|
||||
// Json::Reader jreader;
|
||||
//
|
||||
// if (!jreader.parse(param, jp))
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
|
||||
if (!jreader.parse(param, jp))
|
||||
return TPE_JSON_FORMAT;
|
||||
ex_astr err;
|
||||
Json::Value jp;
|
||||
if (!jreader->parse(param, param + strlen(param), &jp, &err))
|
||||
return TPE_JSON_FORMAT;
|
||||
|
||||
if (!jp.isArray())
|
||||
return TPE_PARAM;
|
||||
|
|
|
|||
|
|
@ -17,14 +17,17 @@ include_directories(
|
|||
include_directories(
|
||||
${TP_EXTERNAL_RELEASE_DIR}/include
|
||||
)
|
||||
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
|
||||
link_directories(
|
||||
${TP_EXTERNAL_RELEASE_DIR}/lib
|
||||
${TP_EXTERNAL_RELEASE_DIR}/lib64
|
||||
)
|
||||
|
||||
|
||||
add_executable(testssh ${DIR_SRCS})
|
||||
|
||||
if (OS_LINUX)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-export-dynamic")
|
||||
target_link_libraries(testssh ssh ssl crypto dl pthread rt util)
|
||||
target_link_libraries(testssh ssh ssl z crypto dl pthread rt util)
|
||||
elseif (OS_MACOS)
|
||||
target_link_libraries(testssh ssh ssl crypto dl pthread util)
|
||||
target_link_libraries(testssh ssh ssl z crypto dl pthread util)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// testssh.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <vld.h>
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
|
||||
#ifdef _DEBUG
|
||||
# pragma comment(lib, "debug/ssh.lib")
|
||||
# pragma comment(lib, "libcrypto32MTd.lib")
|
||||
# pragma comment(lib, "libssl32MTd.lib")
|
||||
#else
|
||||
# pragma comment(lib, "release/ssh.lib")
|
||||
# pragma comment(lib, "libcrypto32MT.lib")
|
||||
# pragma comment(lib, "libssl32MT.lib")
|
||||
#endif
|
||||
#pragma comment(lib, "libeay32.lib")
|
||||
// #pragma comment(lib, "libeay32.lib")
|
||||
// #pragma comment(lib, "libcrypto.lib")
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// testssh.cpp : Defines the entry point for the console application.
|
||||
// testssh.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
//#include "stdafx.h"
|
||||
#ifdef _WIN32
|
||||
# include "stdafx.h"
|
||||
#endif
|
||||
|
||||
#include <libssh/libssh.h>
|
||||
#include <ex.h>
|
||||
|
|
@ -40,7 +42,7 @@ int main(int argc, char** argv)
|
|||
|
||||
ssh_options_set(sess, SSH_OPTIONS_USER, username);
|
||||
|
||||
int _timeout = 60; // 60 sec.
|
||||
int _timeout = 120; // 60 sec.
|
||||
ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &_timeout);
|
||||
|
||||
// connect to real SSH host.
|
||||
|
|
@ -52,7 +54,7 @@ int main(int argc, char** argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
_timeout = 10; // 60 sec.
|
||||
_timeout = 120; // 60 sec.
|
||||
ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &_timeout);
|
||||
|
||||
// get version of SSH server.
|
||||
|
|
@ -70,6 +72,12 @@ int main(int argc, char** argv)
|
|||
|
||||
int auth_methods = ssh_userauth_list(sess, username);
|
||||
printf("[INFO] supported auth-type: 0x%08x\n", auth_methods);
|
||||
if(auth_methods == SSH_AUTH_METHOD_UNKNOWN) {
|
||||
// auth_methods = SSH_AUTH_METHOD_PASSWORD|SSH_AUTH_METHOD_INTERACTIVE;
|
||||
// printf("[WRN] unknown auth-type, try PASSWORD and INTERACTIVE\n");
|
||||
auth_methods = SSH_AUTH_METHOD_PASSWORD;
|
||||
printf("[WRN] unknown auth-type, try PASSWORD mode.\n");
|
||||
}
|
||||
|
||||
// get banner.
|
||||
const char* banner = ssh_get_issue_banner(sess);
|
||||
|
|
@ -155,9 +163,14 @@ int main(int argc, char** argv)
|
|||
if (!ok) {
|
||||
printf("[ERROR] can not use password mode or interactive mode to login to SSH server.\n");
|
||||
}
|
||||
else {
|
||||
printf("[INFO] login success.\n");
|
||||
}
|
||||
|
||||
ssh_disconnect(sess);
|
||||
ssh_free(sess);
|
||||
ssh_finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;LIBSSH_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\..\external\libssh\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\..\external\libssh\include;..\..\..\common\libex\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\..\..\external\libssh\build\src\static;..\..\..\external\openssl\out32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>..\..\..\external\libssh\build\src;..\..\..\external\openssl\lib\VC\static;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;LIBSSH_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\..\external\libssh\include;C:\Program Files (x86)\Visual Leak Detector\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\..\external\libssh\include;..\..\..\common\libex\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
|
@ -92,6 +92,26 @@
|
|||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_log.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_path.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_str.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_thread.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_util.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@
|
|||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="libex">
|
||||
<UniqueIdentifier>{d9087583-83c7-4d47-bba3-fd86b0cb3901}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
|
|
@ -32,5 +35,20 @@
|
|||
<ClCompile Include="testssh.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_util.cpp">
|
||||
<Filter>libex</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_str.cpp">
|
||||
<Filter>libex</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_log.cpp">
|
||||
<Filter>libex</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_thread.cpp">
|
||||
<Filter>libex</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\libex\src\ex_path.cpp">
|
||||
<Filter>libex</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,15 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
MESSAGE(STATUS "=======================================================")
|
||||
MESSAGE(STATUS " libtptelnet")
|
||||
MESSAGE(STATUS " tp_web")
|
||||
MESSAGE(STATUS "=======================================================")
|
||||
#MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
|
||||
#MESSAGE(STATUS "current source directory is ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
include(../../../CMakeCfg.txt)
|
||||
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-export-dynamic")
|
||||
|
||||
aux_source_directory(. DIR_SRCS)
|
||||
|
|
@ -23,7 +21,7 @@ include_directories(
|
|||
|
||||
include_directories(
|
||||
${TP_EXTERNAL_RELEASE_DIR}/include
|
||||
${TP_EXTERNAL_RELEASE_DIR}/include/python
|
||||
${TP_EXTERNAL_RELEASE_DIR}/include/python3.7m
|
||||
)
|
||||
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -19,8 +19,7 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
from . import Image, FontFile
|
||||
|
||||
from . import FontFile, Image
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# parse X Bitmap Distribution Format (BDF)
|
||||
|
|
@ -32,14 +31,10 @@ bdf_slant = {
|
|||
"O": "Oblique",
|
||||
"RI": "Reverse Italic",
|
||||
"RO": "Reverse Oblique",
|
||||
"OT": "Other"
|
||||
"OT": "Other",
|
||||
}
|
||||
|
||||
bdf_spacing = {
|
||||
"P": "Proportional",
|
||||
"M": "Monospaced",
|
||||
"C": "Cell"
|
||||
}
|
||||
bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"}
|
||||
|
||||
|
||||
def bdf_char(f):
|
||||
|
|
@ -50,7 +45,7 @@ def bdf_char(f):
|
|||
return None
|
||||
if s[:9] == b"STARTCHAR":
|
||||
break
|
||||
id = s[9:].strip().decode('ascii')
|
||||
id = s[9:].strip().decode("ascii")
|
||||
|
||||
# load symbol properties
|
||||
props = {}
|
||||
|
|
@ -59,7 +54,7 @@ def bdf_char(f):
|
|||
if not s or s[:6] == b"BITMAP":
|
||||
break
|
||||
i = s.find(b" ")
|
||||
props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
|
||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
||||
|
||||
# load bitmap
|
||||
bitmap = []
|
||||
|
|
@ -73,7 +68,7 @@ def bdf_char(f):
|
|||
[x, y, l, d] = [int(p) for p in props["BBX"].split()]
|
||||
[dx, dy] = [int(p) for p in props["DWIDTH"].split()]
|
||||
|
||||
bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y)
|
||||
bbox = (dx, dy), (l, -d - y, x + l, -d), (0, 0, x, y)
|
||||
|
||||
try:
|
||||
im = Image.frombytes("1", (x, y), bitmap, "hex", "1")
|
||||
|
|
@ -87,8 +82,8 @@ def bdf_char(f):
|
|||
##
|
||||
# Font file plugin for the X11 BDF format.
|
||||
|
||||
class BdfFontFile(FontFile.FontFile):
|
||||
|
||||
class BdfFontFile(FontFile.FontFile):
|
||||
def __init__(self, fp):
|
||||
|
||||
FontFile.FontFile.__init__(self)
|
||||
|
|
@ -105,10 +100,10 @@ class BdfFontFile(FontFile.FontFile):
|
|||
if not s or s[:13] == b"ENDPROPERTIES":
|
||||
break
|
||||
i = s.find(b" ")
|
||||
props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
|
||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
||||
if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
|
||||
if s.find(b"LogicalFontDescription") < 0:
|
||||
comments.append(s[i+1:-1].decode('ascii'))
|
||||
comments.append(s[i + 1 : -1].decode("ascii"))
|
||||
|
||||
while True:
|
||||
c = bdf_char(fp)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ from io import BytesIO
|
|||
|
||||
from . import Image, ImageFile
|
||||
|
||||
|
||||
BLP_FORMAT_JPEG = 0
|
||||
|
||||
BLP_ENCODING_UNCOMPRESSED = 1
|
||||
|
|
@ -47,11 +46,7 @@ BLP_ALPHA_ENCODING_DXT5 = 7
|
|||
|
||||
|
||||
def unpack_565(i):
|
||||
return (
|
||||
((i >> 11) & 0x1f) << 3,
|
||||
((i >> 5) & 0x3f) << 2,
|
||||
(i & 0x1f) << 3
|
||||
)
|
||||
return (((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3)
|
||||
|
||||
|
||||
def decode_dxt1(data, alpha=False):
|
||||
|
|
@ -119,7 +114,7 @@ def decode_dxt3(data):
|
|||
|
||||
for block in range(blocks):
|
||||
idx = block * 16
|
||||
block = data[idx:idx + 16]
|
||||
block = data[idx : idx + 16]
|
||||
# Decode next 16-byte block.
|
||||
bits = struct.unpack_from("<8B", block)
|
||||
color0, color1 = struct.unpack_from("<HH", block, 8)
|
||||
|
|
@ -139,7 +134,7 @@ def decode_dxt3(data):
|
|||
a >>= 4
|
||||
else:
|
||||
high = True
|
||||
a &= 0xf
|
||||
a &= 0xF
|
||||
a *= 17 # We get a value between 0 and 15
|
||||
|
||||
color_code = (code >> 2 * (4 * j + i)) & 0x03
|
||||
|
|
@ -172,14 +167,12 @@ def decode_dxt5(data):
|
|||
|
||||
for block in range(blocks):
|
||||
idx = block * 16
|
||||
block = data[idx:idx + 16]
|
||||
block = data[idx : idx + 16]
|
||||
# Decode next 16-byte block.
|
||||
a0, a1 = struct.unpack_from("<BB", block)
|
||||
|
||||
bits = struct.unpack_from("<6B", block, 2)
|
||||
alphacode1 = (
|
||||
bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
|
||||
)
|
||||
alphacode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
|
||||
alphacode2 = bits[0] | (bits[1] << 8)
|
||||
|
||||
color0, color1 = struct.unpack_from("<HH", block, 8)
|
||||
|
|
@ -242,6 +235,7 @@ class BlpImageFile(ImageFile.ImageFile):
|
|||
"""
|
||||
Blizzard Mipmap Format
|
||||
"""
|
||||
|
||||
format = "BLP"
|
||||
format_description = "Blizzard Mipmap Format"
|
||||
|
||||
|
|
@ -258,9 +252,7 @@ class BlpImageFile(ImageFile.ImageFile):
|
|||
else:
|
||||
raise BLPFormatError("Bad BLP magic %r" % (self.magic))
|
||||
|
||||
self.tile = [
|
||||
(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))
|
||||
]
|
||||
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
|
||||
|
||||
def _read_blp_header(self):
|
||||
self._blp_compression, = struct.unpack("<i", self.fp.read(4))
|
||||
|
|
@ -324,7 +316,6 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
|||
|
||||
|
||||
class BLP1Decoder(_BLPBaseDecoder):
|
||||
|
||||
def _load(self):
|
||||
if self._blp_compression == BLP_FORMAT_JPEG:
|
||||
self._decode_jpeg_stream()
|
||||
|
|
@ -368,7 +359,6 @@ class BLP1Decoder(_BLPBaseDecoder):
|
|||
|
||||
|
||||
class BLP2Decoder(_BLPBaseDecoder):
|
||||
|
||||
def _load(self):
|
||||
palette = self._read_palette()
|
||||
|
||||
|
|
@ -393,8 +383,7 @@ class BLP2Decoder(_BLPBaseDecoder):
|
|||
linesize = (self.size[0] + 3) // 4 * 8
|
||||
for yb in range((self.size[1] + 3) // 4):
|
||||
for d in decode_dxt1(
|
||||
self.fd.read(linesize),
|
||||
alpha=bool(self._blp_alpha_depth)
|
||||
self.fd.read(linesize), alpha=bool(self._blp_alpha_depth)
|
||||
):
|
||||
data += d
|
||||
|
||||
|
|
@ -410,18 +399,14 @@ class BLP2Decoder(_BLPBaseDecoder):
|
|||
for d in decode_dxt5(self.fd.read(linesize)):
|
||||
data += d
|
||||
else:
|
||||
raise BLPFormatError("Unsupported alpha encoding %r" % (
|
||||
self._blp_alpha_encoding
|
||||
))
|
||||
raise BLPFormatError(
|
||||
"Unsupported alpha encoding %r" % (self._blp_alpha_encoding)
|
||||
)
|
||||
else:
|
||||
raise BLPFormatError(
|
||||
"Unknown BLP encoding %r" % (self._blp_encoding)
|
||||
)
|
||||
raise BLPFormatError("Unknown BLP encoding %r" % (self._blp_encoding))
|
||||
|
||||
else:
|
||||
raise BLPFormatError(
|
||||
"Unknown BLP compression %r" % (self._blp_compression)
|
||||
)
|
||||
raise BLPFormatError("Unknown BLP compression %r" % (self._blp_compression))
|
||||
|
||||
self.set_as_raw(bytes(data))
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@
|
|||
|
||||
|
||||
from . import Image, ImageFile, ImagePalette
|
||||
from ._binary import i8, i16le as i16, i32le as i32, \
|
||||
o8, o16le as o16, o32le as o32
|
||||
import math
|
||||
from ._binary import i8, i16le as i16, i32le as i32, o8, o16le as o16, o32le as o32
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.7"
|
||||
|
||||
#
|
||||
|
|
@ -50,25 +50,24 @@ def _accept(prefix):
|
|||
return prefix[:2] == b"BM"
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def _dib_accept(prefix):
|
||||
return i32(prefix[:4]) in [12, 40, 64, 108, 124]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Image plugin for the Windows BMP format.
|
||||
# ==============================================================================
|
||||
# =============================================================================
|
||||
class BmpImageFile(ImageFile.ImageFile):
|
||||
""" Image plugin for the Windows Bitmap format (BMP) """
|
||||
|
||||
# -------------------------------------------------------------- Description
|
||||
# ------------------------------------------------------------- Description
|
||||
format_description = "Windows Bitmap"
|
||||
format = "BMP"
|
||||
# --------------------------------------------------- BMP Compression values
|
||||
COMPRESSIONS = {
|
||||
'RAW': 0,
|
||||
'RLE8': 1,
|
||||
'RLE4': 2,
|
||||
'BITFIELDS': 3,
|
||||
'JPEG': 4,
|
||||
'PNG': 5
|
||||
}
|
||||
RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5
|
||||
|
||||
# -------------------------------------------------- BMP Compression values
|
||||
COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
|
||||
for k, v in COMPRESSIONS.items():
|
||||
vars()[k] = v
|
||||
|
||||
def _bitmap(self, header=0, offset=0):
|
||||
""" Read relevant info about the BMP """
|
||||
|
|
@ -77,157 +76,190 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
seek(header)
|
||||
file_info = {}
|
||||
# read bmp header size @offset 14 (this is part of the header size)
|
||||
file_info['header_size'] = i32(read(4))
|
||||
file_info['direction'] = -1
|
||||
# --------------------- If requested, read header at a specific position
|
||||
file_info["header_size"] = i32(read(4))
|
||||
file_info["direction"] = -1
|
||||
|
||||
# -------------------- If requested, read header at a specific position
|
||||
# read the rest of the bmp header, without its size
|
||||
header_data = ImageFile._safe_read(self.fp,
|
||||
file_info['header_size'] - 4)
|
||||
# --------------------------------------------------- IBM OS/2 Bitmap v1
|
||||
# ------ This format has different offsets because of width/height types
|
||||
if file_info['header_size'] == 12:
|
||||
file_info['width'] = i16(header_data[0:2])
|
||||
file_info['height'] = i16(header_data[2:4])
|
||||
file_info['planes'] = i16(header_data[4:6])
|
||||
file_info['bits'] = i16(header_data[6:8])
|
||||
file_info['compression'] = self.RAW
|
||||
file_info['palette_padding'] = 3
|
||||
# ---------------------------------------------- Windows Bitmap v2 to v5
|
||||
elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5
|
||||
if file_info['header_size'] >= 40: # v3 and OS/2
|
||||
file_info['y_flip'] = i8(header_data[7]) == 0xff
|
||||
file_info['direction'] = 1 if file_info['y_flip'] else -1
|
||||
file_info['width'] = i32(header_data[0:4])
|
||||
file_info['height'] = (i32(header_data[4:8])
|
||||
if not file_info['y_flip']
|
||||
else 2**32 - i32(header_data[4:8]))
|
||||
file_info['planes'] = i16(header_data[8:10])
|
||||
file_info['bits'] = i16(header_data[10:12])
|
||||
file_info['compression'] = i32(header_data[12:16])
|
||||
# byte size of pixel data
|
||||
file_info['data_size'] = i32(header_data[16:20])
|
||||
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
|
||||
i32(header_data[24:28]))
|
||||
file_info['colors'] = i32(header_data[28:32])
|
||||
file_info['palette_padding'] = 4
|
||||
self.info["dpi"] = tuple(
|
||||
map(lambda x: int(math.ceil(x / 39.3701)),
|
||||
file_info['pixels_per_meter']))
|
||||
if file_info['compression'] == self.BITFIELDS:
|
||||
if len(header_data) >= 52:
|
||||
for idx, mask in enumerate(['r_mask',
|
||||
'g_mask',
|
||||
'b_mask',
|
||||
'a_mask']):
|
||||
file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
|
||||
else:
|
||||
# 40 byte headers only have the three components in the
|
||||
# bitfields masks,
|
||||
# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
||||
# See also https://github.com/python-pillow/Pillow/issues/1293
|
||||
# There is a 4th component in the RGBQuad, in the alpha
|
||||
# location, but it is listed as a reserved component,
|
||||
# and it is not generally an alpha channel
|
||||
file_info['a_mask'] = 0x0
|
||||
for mask in ['r_mask', 'g_mask', 'b_mask']:
|
||||
file_info[mask] = i32(read(4))
|
||||
file_info['rgb_mask'] = (file_info['r_mask'],
|
||||
file_info['g_mask'],
|
||||
file_info['b_mask'])
|
||||
file_info['rgba_mask'] = (file_info['r_mask'],
|
||||
file_info['g_mask'],
|
||||
file_info['b_mask'],
|
||||
file_info['a_mask'])
|
||||
header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)
|
||||
|
||||
# -------------------------------------------------- IBM OS/2 Bitmap v1
|
||||
# ----- This format has different offsets because of width/height types
|
||||
if file_info["header_size"] == 12:
|
||||
file_info["width"] = i16(header_data[0:2])
|
||||
file_info["height"] = i16(header_data[2:4])
|
||||
file_info["planes"] = i16(header_data[4:6])
|
||||
file_info["bits"] = i16(header_data[6:8])
|
||||
file_info["compression"] = self.RAW
|
||||
file_info["palette_padding"] = 3
|
||||
|
||||
# --------------------------------------------- Windows Bitmap v2 to v5
|
||||
# v3, OS/2 v2, v4, v5
|
||||
elif file_info["header_size"] in (40, 64, 108, 124):
|
||||
file_info["y_flip"] = i8(header_data[7]) == 0xFF
|
||||
file_info["direction"] = 1 if file_info["y_flip"] else -1
|
||||
file_info["width"] = i32(header_data[0:4])
|
||||
file_info["height"] = (
|
||||
i32(header_data[4:8])
|
||||
if not file_info["y_flip"]
|
||||
else 2 ** 32 - i32(header_data[4:8])
|
||||
)
|
||||
file_info["planes"] = i16(header_data[8:10])
|
||||
file_info["bits"] = i16(header_data[10:12])
|
||||
file_info["compression"] = i32(header_data[12:16])
|
||||
# byte size of pixel data
|
||||
file_info["data_size"] = i32(header_data[16:20])
|
||||
file_info["pixels_per_meter"] = (
|
||||
i32(header_data[20:24]),
|
||||
i32(header_data[24:28]),
|
||||
)
|
||||
file_info["colors"] = i32(header_data[28:32])
|
||||
file_info["palette_padding"] = 4
|
||||
self.info["dpi"] = tuple(
|
||||
int(x / 39.3701 + 0.5) for x in file_info["pixels_per_meter"]
|
||||
)
|
||||
if file_info["compression"] == self.BITFIELDS:
|
||||
if len(header_data) >= 52:
|
||||
for idx, mask in enumerate(
|
||||
["r_mask", "g_mask", "b_mask", "a_mask"]
|
||||
):
|
||||
file_info[mask] = i32(header_data[36 + idx * 4 : 40 + idx * 4])
|
||||
else:
|
||||
# 40 byte headers only have the three components in the
|
||||
# bitfields masks, ref:
|
||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
||||
# See also
|
||||
# https://github.com/python-pillow/Pillow/issues/1293
|
||||
# There is a 4th component in the RGBQuad, in the alpha
|
||||
# location, but it is listed as a reserved component,
|
||||
# and it is not generally an alpha channel
|
||||
file_info["a_mask"] = 0x0
|
||||
for mask in ["r_mask", "g_mask", "b_mask"]:
|
||||
file_info[mask] = i32(read(4))
|
||||
file_info["rgb_mask"] = (
|
||||
file_info["r_mask"],
|
||||
file_info["g_mask"],
|
||||
file_info["b_mask"],
|
||||
)
|
||||
file_info["rgba_mask"] = (
|
||||
file_info["r_mask"],
|
||||
file_info["g_mask"],
|
||||
file_info["b_mask"],
|
||||
file_info["a_mask"],
|
||||
)
|
||||
else:
|
||||
raise IOError("Unsupported BMP header type (%d)" %
|
||||
file_info['header_size'])
|
||||
raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"])
|
||||
|
||||
# ------------------ Special case : header is reported 40, which
|
||||
# ---------------------- is shorter than real size for bpp >= 16
|
||||
self._size = file_info['width'], file_info['height']
|
||||
# -------- If color count was not found in the header, compute from bits
|
||||
file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits'])
|
||||
# -------------------------------- Check abnormal values for DOS attacks
|
||||
if file_info['width'] * file_info['height'] > 2**31:
|
||||
self._size = file_info["width"], file_info["height"]
|
||||
|
||||
# ------- If color count was not found in the header, compute from bits
|
||||
file_info["colors"] = (
|
||||
file_info["colors"]
|
||||
if file_info.get("colors", 0)
|
||||
else (1 << file_info["bits"])
|
||||
)
|
||||
|
||||
# ------------------------------- Check abnormal values for DOS attacks
|
||||
if file_info["width"] * file_info["height"] > 2 ** 31:
|
||||
raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
|
||||
# ----------------------- Check bit depth for unusual unsupported values
|
||||
self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
|
||||
|
||||
# ---------------------- Check bit depth for unusual unsupported values
|
||||
self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
|
||||
if self.mode is None:
|
||||
raise IOError("Unsupported BMP pixel depth (%d)"
|
||||
% file_info['bits'])
|
||||
# ----------------- Process BMP with Bitfields compression (not palette)
|
||||
if file_info['compression'] == self.BITFIELDS:
|
||||
raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
|
||||
|
||||
# ---------------- Process BMP with Bitfields compression (not palette)
|
||||
if file_info["compression"] == self.BITFIELDS:
|
||||
SUPPORTED = {
|
||||
32: [(0xff0000, 0xff00, 0xff, 0x0),
|
||||
(0xff0000, 0xff00, 0xff, 0xff000000),
|
||||
(0x0, 0x0, 0x0, 0x0),
|
||||
(0xff000000, 0xff0000, 0xff00, 0x0)],
|
||||
24: [(0xff0000, 0xff00, 0xff)],
|
||||
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
|
||||
32: [
|
||||
(0xFF0000, 0xFF00, 0xFF, 0x0),
|
||||
(0xFF0000, 0xFF00, 0xFF, 0xFF000000),
|
||||
(0xFF, 0xFF00, 0xFF0000, 0xFF000000),
|
||||
(0x0, 0x0, 0x0, 0x0),
|
||||
(0xFF000000, 0xFF0000, 0xFF00, 0x0),
|
||||
],
|
||||
24: [(0xFF0000, 0xFF00, 0xFF)],
|
||||
16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
|
||||
}
|
||||
MASK_MODES = {
|
||||
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
|
||||
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
|
||||
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
|
||||
(32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
|
||||
(32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
|
||||
(32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
|
||||
(32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
|
||||
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
||||
(24, (0xff0000, 0xff00, 0xff)): "BGR",
|
||||
(16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
|
||||
(16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
|
||||
(24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
|
||||
(16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
|
||||
(16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
|
||||
}
|
||||
if file_info['bits'] in SUPPORTED:
|
||||
if file_info['bits'] == 32 and \
|
||||
file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
|
||||
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
|
||||
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
|
||||
elif (file_info['bits'] in (24, 16) and
|
||||
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
|
||||
raw_mode = MASK_MODES[
|
||||
(file_info['bits'], file_info['rgb_mask'])
|
||||
]
|
||||
if file_info["bits"] in SUPPORTED:
|
||||
if (
|
||||
file_info["bits"] == 32
|
||||
and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
|
||||
):
|
||||
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
|
||||
self.mode = "RGBA" if "A" in raw_mode else self.mode
|
||||
elif (
|
||||
file_info["bits"] in (24, 16)
|
||||
and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
|
||||
):
|
||||
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
|
||||
else:
|
||||
raise IOError("Unsupported BMP bitfields layout")
|
||||
else:
|
||||
raise IOError("Unsupported BMP bitfields layout")
|
||||
elif file_info['compression'] == self.RAW:
|
||||
if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset
|
||||
elif file_info["compression"] == self.RAW:
|
||||
if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset
|
||||
raw_mode, self.mode = "BGRA", "RGBA"
|
||||
else:
|
||||
raise IOError("Unsupported BMP compression (%d)" %
|
||||
file_info['compression'])
|
||||
# ---------------- Once the header is processed, process the palette/LUT
|
||||
raise IOError("Unsupported BMP compression (%d)" % file_info["compression"])
|
||||
|
||||
# --------------- Once the header is processed, process the palette/LUT
|
||||
if self.mode == "P": # Paletted for 1, 4 and 8 bit images
|
||||
# ----------------------------------------------------- 1-bit images
|
||||
if not (0 < file_info['colors'] <= 65536):
|
||||
raise IOError("Unsupported BMP Palette size (%d)" %
|
||||
file_info['colors'])
|
||||
|
||||
# ---------------------------------------------------- 1-bit images
|
||||
if not (0 < file_info["colors"] <= 65536):
|
||||
raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"])
|
||||
else:
|
||||
padding = file_info['palette_padding']
|
||||
palette = read(padding * file_info['colors'])
|
||||
padding = file_info["palette_padding"]
|
||||
palette = read(padding * file_info["colors"])
|
||||
greyscale = True
|
||||
indices = (0, 255) if file_info['colors'] == 2 else \
|
||||
list(range(file_info['colors']))
|
||||
# ------------------ Check if greyscale and ignore palette if so
|
||||
indices = (
|
||||
(0, 255)
|
||||
if file_info["colors"] == 2
|
||||
else list(range(file_info["colors"]))
|
||||
)
|
||||
|
||||
# ----------------- Check if greyscale and ignore palette if so
|
||||
for ind, val in enumerate(indices):
|
||||
rgb = palette[ind*padding:ind*padding + 3]
|
||||
rgb = palette[ind * padding : ind * padding + 3]
|
||||
if rgb != o8(val) * 3:
|
||||
greyscale = False
|
||||
# -------- If all colors are grey, white or black, ditch palette
|
||||
|
||||
# ------- If all colors are grey, white or black, ditch palette
|
||||
if greyscale:
|
||||
self.mode = "1" if file_info['colors'] == 2 else "L"
|
||||
self.mode = "1" if file_info["colors"] == 2 else "L"
|
||||
raw_mode = self.mode
|
||||
else:
|
||||
self.mode = "P"
|
||||
self.palette = ImagePalette.raw(
|
||||
"BGRX" if padding == 4 else "BGR", palette)
|
||||
"BGRX" if padding == 4 else "BGR", palette
|
||||
)
|
||||
|
||||
# ----------------------------- Finally set the tile data for the plugin
|
||||
self.info['compression'] = file_info['compression']
|
||||
# ---------------------------- Finally set the tile data for the plugin
|
||||
self.info["compression"] = file_info["compression"]
|
||||
self.tile = [
|
||||
('raw',
|
||||
(0, 0, file_info['width'], file_info['height']),
|
||||
offset or self.fp.tell(),
|
||||
(raw_mode,
|
||||
((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
|
||||
file_info['direction']))
|
||||
(
|
||||
"raw",
|
||||
(0, 0, file_info["width"], file_info["height"]),
|
||||
offset or self.fp.tell(),
|
||||
(
|
||||
raw_mode,
|
||||
((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3),
|
||||
file_info["direction"],
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
def _open(self):
|
||||
|
|
@ -243,9 +275,9 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
self._bitmap(offset=offset)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# =============================================================================
|
||||
# Image plugin for the DIB format (BMP alias)
|
||||
# ==============================================================================
|
||||
# =============================================================================
|
||||
class DibImageFile(BmpImageFile):
|
||||
|
||||
format = "DIB"
|
||||
|
|
@ -254,6 +286,7 @@ class DibImageFile(BmpImageFile):
|
|||
def _open(self):
|
||||
self._bitmap()
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
# Write BMP file
|
||||
|
|
@ -268,7 +301,11 @@ SAVE = {
|
|||
}
|
||||
|
||||
|
||||
def _save(im, fp, filename):
|
||||
def _dib_save(im, fp, filename):
|
||||
_save(im, fp, filename, False)
|
||||
|
||||
|
||||
def _save(im, fp, filename, bitmap_header=True):
|
||||
try:
|
||||
rawmode, bits, colors = SAVE[im.mode]
|
||||
except KeyError:
|
||||
|
|
@ -279,32 +316,38 @@ def _save(im, fp, filename):
|
|||
dpi = info.get("dpi", (96, 96))
|
||||
|
||||
# 1 meter == 39.3701 inches
|
||||
ppm = tuple(map(lambda x: int(x * 39.3701), dpi))
|
||||
ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))
|
||||
|
||||
stride = ((im.size[0]*bits+7)//8+3) & (~3)
|
||||
stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
|
||||
header = 40 # or 64 for OS/2 version 2
|
||||
offset = 14 + header + colors * 4
|
||||
image = stride * im.size[1]
|
||||
|
||||
# bitmap header
|
||||
fp.write(b"BM" + # file type (magic)
|
||||
o32(offset+image) + # file size
|
||||
o32(0) + # reserved
|
||||
o32(offset)) # image data offset
|
||||
if bitmap_header:
|
||||
offset = 14 + header + colors * 4
|
||||
fp.write(
|
||||
b"BM"
|
||||
+ o32(offset + image) # file type (magic)
|
||||
+ o32(0) # file size
|
||||
+ o32(offset) # reserved
|
||||
) # image data offset
|
||||
|
||||
# bitmap info header
|
||||
fp.write(o32(header) + # info header size
|
||||
o32(im.size[0]) + # width
|
||||
o32(im.size[1]) + # height
|
||||
o16(1) + # planes
|
||||
o16(bits) + # depth
|
||||
o32(0) + # compression (0=uncompressed)
|
||||
o32(image) + # size of bitmap
|
||||
o32(ppm[0]) + o32(ppm[1]) + # resolution
|
||||
o32(colors) + # colors used
|
||||
o32(colors)) # colors important
|
||||
fp.write(
|
||||
o32(header) # info header size
|
||||
+ o32(im.size[0]) # width
|
||||
+ o32(im.size[1]) # height
|
||||
+ o16(1) # planes
|
||||
+ o16(bits) # depth
|
||||
+ o32(0) # compression (0=uncompressed)
|
||||
+ o32(image) # size of bitmap
|
||||
+ o32(ppm[0]) # resolution
|
||||
+ o32(ppm[1]) # resolution
|
||||
+ o32(colors) # colors used
|
||||
+ o32(colors) # colors important
|
||||
)
|
||||
|
||||
fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
|
||||
fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
|
||||
|
||||
if im.mode == "1":
|
||||
for i in (0, 255):
|
||||
|
|
@ -315,8 +358,8 @@ def _save(im, fp, filename):
|
|||
elif im.mode == "P":
|
||||
fp.write(im.im.getpalette("RGB", "BGRX"))
|
||||
|
||||
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0,
|
||||
(rawmode, stride, -1))])
|
||||
ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
|
@ -329,3 +372,10 @@ Image.register_save(BmpImageFile.format, _save)
|
|||
Image.register_extension(BmpImageFile.format, ".bmp")
|
||||
|
||||
Image.register_mime(BmpImageFile.format, "image/bmp")
|
||||
|
||||
Image.register_open(DibImageFile.format, DibImageFile, _dib_accept)
|
||||
Image.register_save(DibImageFile.format, _dib_save)
|
||||
|
||||
Image.register_extension(DibImageFile.format, ".dib")
|
||||
|
||||
Image.register_mime(DibImageFile.format, "image/bmp")
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ def register_handler(handler):
|
|||
# --------------------------------------------------------------------
|
||||
# Image adapter
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@
|
|||
# A file object that provides read access to a part of an existing
|
||||
# file (for example a TAR file).
|
||||
|
||||
import io
|
||||
|
||||
|
||||
class ContainerIO(object):
|
||||
|
||||
def __init__(self, file, offset, length):
|
||||
"""
|
||||
Create file object.
|
||||
|
|
@ -39,9 +40,9 @@ class ContainerIO(object):
|
|||
# Always false.
|
||||
|
||||
def isatty(self):
|
||||
return 0
|
||||
return False
|
||||
|
||||
def seek(self, offset, mode=0):
|
||||
def seek(self, offset, mode=io.SEEK_SET):
|
||||
"""
|
||||
Move file pointer.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
from . import Image, BmpImagePlugin
|
||||
from . import BmpImagePlugin, Image
|
||||
from ._binary import i8, i16le as i16, i32le as i32
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.1"
|
||||
|
||||
#
|
||||
|
|
@ -34,6 +36,7 @@ def _accept(prefix):
|
|||
##
|
||||
# Image plugin for Windows Cursor files.
|
||||
|
||||
|
||||
class CurImageFile(BmpImagePlugin.BmpImageFile):
|
||||
|
||||
format = "CUR"
|
||||
|
|
@ -63,9 +66,9 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
|||
self._bitmap(i32(m[12:]) + offset)
|
||||
|
||||
# patch up the bitmap height
|
||||
self._size = self.size[0], self.size[1]//2
|
||||
self._size = self.size[0], self.size[1] // 2
|
||||
d, e, o, a = self.tile[0]
|
||||
self.tile[0] = d, (0, 0)+self.size, o, a
|
||||
self.tile[0] = d, (0, 0) + self.size, o, a
|
||||
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ from . import Image
|
|||
from ._binary import i32le as i32
|
||||
from .PcxImagePlugin import PcxImageFile
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.2"
|
||||
|
||||
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
|
||||
|
|
@ -37,6 +39,7 @@ def _accept(prefix):
|
|||
##
|
||||
# Image plugin for the Intel DCX format.
|
||||
|
||||
|
||||
class DcxImageFile(PcxImageFile):
|
||||
|
||||
format = "DCX"
|
||||
|
|
@ -81,6 +84,15 @@ class DcxImageFile(PcxImageFile):
|
|||
def tell(self):
|
||||
return self.frame
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
self.__fp = None
|
||||
|
||||
|
||||
Image.register_open(DcxImageFile.format, DcxImageFile, _accept)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ Full text of the CC0 license:
|
|||
|
||||
import struct
|
||||
from io import BytesIO
|
||||
from . import Image, ImageFile
|
||||
|
||||
from . import Image, ImageFile
|
||||
|
||||
# Magic ("DDS ")
|
||||
DDS_MAGIC = 0x20534444
|
||||
|
|
@ -61,8 +61,7 @@ DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS
|
|||
DDS_ALPHA = DDPF_ALPHA
|
||||
DDS_PAL8 = DDPF_PALETTEINDEXED8
|
||||
|
||||
DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |
|
||||
DDSD_PIXELFORMAT)
|
||||
DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
|
||||
DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT
|
||||
DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH
|
||||
DDS_HEADER_FLAGS_PITCH = DDSD_PITCH
|
||||
|
|
@ -118,48 +117,54 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
self.mode = "RGBA"
|
||||
|
||||
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
|
||||
reserved = struct.unpack("<11I", header.read(44))
|
||||
struct.unpack("<11I", header.read(44)) # reserved
|
||||
|
||||
# pixel format
|
||||
pfsize, pfflags = struct.unpack("<2I", header.read(8))
|
||||
fourcc = header.read(4)
|
||||
bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I",
|
||||
header.read(20))
|
||||
bitcount, = struct.unpack("<I", header.read(4))
|
||||
masks = struct.unpack("<4I", header.read(16))
|
||||
if pfflags & 0x40:
|
||||
# DDPF_RGB - Texture contains uncompressed RGB data
|
||||
masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
|
||||
rawmode = ""
|
||||
if bitcount == 32:
|
||||
rawmode += masks[0xFF000000]
|
||||
rawmode += masks[0xFF0000] + masks[0xFF00] + masks[0xFF]
|
||||
|
||||
data_start = header_size + 4
|
||||
n = 0
|
||||
if fourcc == b"DXT1":
|
||||
self.pixel_format = "DXT1"
|
||||
n = 1
|
||||
elif fourcc == b"DXT3":
|
||||
self.pixel_format = "DXT3"
|
||||
n = 2
|
||||
elif fourcc == b"DXT5":
|
||||
self.pixel_format = "DXT5"
|
||||
n = 3
|
||||
elif fourcc == b"DX10":
|
||||
data_start += 20
|
||||
# ignoring flags which pertain to volume textures and cubemaps
|
||||
dxt10 = BytesIO(self.fp.read(20))
|
||||
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
|
||||
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
|
||||
DXGI_FORMAT_BC7_UNORM):
|
||||
self.pixel_format = "BC7"
|
||||
n = 7
|
||||
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||
self.pixel_format = "BC7"
|
||||
self.im_info["gamma"] = 1/2.2
|
||||
n = 7
|
||||
else:
|
||||
raise NotImplementedError("Unimplemented DXGI format %d" %
|
||||
(dxgi_format))
|
||||
self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
|
||||
else:
|
||||
raise NotImplementedError("Unimplemented pixel format %r" %
|
||||
(fourcc))
|
||||
data_start = header_size + 4
|
||||
n = 0
|
||||
if fourcc == b"DXT1":
|
||||
self.pixel_format = "DXT1"
|
||||
n = 1
|
||||
elif fourcc == b"DXT3":
|
||||
self.pixel_format = "DXT3"
|
||||
n = 2
|
||||
elif fourcc == b"DXT5":
|
||||
self.pixel_format = "DXT5"
|
||||
n = 3
|
||||
elif fourcc == b"DX10":
|
||||
data_start += 20
|
||||
# ignoring flags which pertain to volume textures and cubemaps
|
||||
dxt10 = BytesIO(self.fp.read(20))
|
||||
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
|
||||
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
|
||||
self.pixel_format = "BC7"
|
||||
n = 7
|
||||
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||
self.pixel_format = "BC7"
|
||||
self.im_info["gamma"] = 1 / 2.2
|
||||
n = 7
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Unimplemented DXGI format %d" % (dxgi_format)
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError("Unimplemented pixel format %r" % (fourcc))
|
||||
|
||||
self.tile = [
|
||||
("bcn", (0, 0) + self.size, data_start, (n))
|
||||
]
|
||||
self.tile = [("bcn", (0, 0) + self.size, data_start, (n))]
|
||||
|
||||
def load_seek(self, pos):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,13 +20,16 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
import re
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from . import Image, ImageFile
|
||||
from ._binary import i32le as i32
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.5"
|
||||
|
||||
#
|
||||
|
|
@ -36,15 +39,17 @@ split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
|||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
||||
|
||||
gs_windows_binary = None
|
||||
if sys.platform.startswith('win'):
|
||||
if sys.platform.startswith("win"):
|
||||
import shutil
|
||||
if hasattr(shutil, 'which'):
|
||||
|
||||
if hasattr(shutil, "which"):
|
||||
which = shutil.which
|
||||
else:
|
||||
# Python 2
|
||||
import distutils.spawn
|
||||
|
||||
which = distutils.spawn.find_executable
|
||||
for binary in ('gswin32c', 'gswin64c', 'gs'):
|
||||
for binary in ("gswin32c", "gswin64c", "gs"):
|
||||
if which(binary) is not None:
|
||||
gs_windows_binary = binary
|
||||
break
|
||||
|
|
@ -55,11 +60,12 @@ if sys.platform.startswith('win'):
|
|||
def has_ghostscript():
|
||||
if gs_windows_binary:
|
||||
return True
|
||||
if not sys.platform.startswith('win'):
|
||||
if not sys.platform.startswith("win"):
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
subprocess.check_call(['gs', '--version'], stdout=devnull)
|
||||
with open(os.devnull, "wb") as devnull:
|
||||
subprocess.check_call(["gs", "--version"], stdout=devnull)
|
||||
return True
|
||||
except OSError:
|
||||
# No Ghostscript
|
||||
|
|
@ -80,8 +86,10 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
# orig_bbox = bbox
|
||||
size = (size[0] * scale, size[1] * scale)
|
||||
# resolution is dependent on bbox and size
|
||||
res = (float((72.0 * size[0]) / (bbox[2]-bbox[0])),
|
||||
float((72.0 * size[1]) / (bbox[3]-bbox[1])))
|
||||
res = (
|
||||
float((72.0 * size[0]) / (bbox[2] - bbox[0])),
|
||||
float((72.0 * size[1]) / (bbox[3] - bbox[1])),
|
||||
)
|
||||
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
|
@ -90,7 +98,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
os.close(out_fd)
|
||||
|
||||
infile_temp = None
|
||||
if hasattr(fp, 'name') and os.path.exists(fp.name):
|
||||
if hasattr(fp, "name") and os.path.exists(fp.name):
|
||||
infile = fp.name
|
||||
else:
|
||||
in_fd, infile_temp = tempfile.mkstemp()
|
||||
|
|
@ -100,51 +108,54 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
# Ignore length and offset!
|
||||
# Ghostscript can read it
|
||||
# Copy whole file to read in Ghostscript
|
||||
with open(infile_temp, 'wb') as f:
|
||||
with open(infile_temp, "wb") as f:
|
||||
# fetch length of fp
|
||||
fp.seek(0, 2)
|
||||
fp.seek(0, io.SEEK_END)
|
||||
fsize = fp.tell()
|
||||
# ensure start position
|
||||
# go back
|
||||
fp.seek(0)
|
||||
lengthfile = fsize
|
||||
while lengthfile > 0:
|
||||
s = fp.read(min(lengthfile, 100*1024))
|
||||
s = fp.read(min(lengthfile, 100 * 1024))
|
||||
if not s:
|
||||
break
|
||||
lengthfile -= len(s)
|
||||
f.write(s)
|
||||
|
||||
# Build Ghostscript command
|
||||
command = ["gs",
|
||||
"-q", # quiet mode
|
||||
"-g%dx%d" % size, # set output geometry (pixels)
|
||||
"-r%fx%f" % res, # set input DPI (dots per inch)
|
||||
"-dBATCH", # exit after processing
|
||||
"-dNOPAUSE", # don't pause between pages
|
||||
"-dSAFER", # safe mode
|
||||
"-sDEVICE=ppmraw", # ppm driver
|
||||
"-sOutputFile=%s" % outfile, # output file
|
||||
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
|
||||
# adjust for image origin
|
||||
"-f", infile, # input file
|
||||
"-c", "showpage", # showpage (see: https://bugs.ghostscript.com/show_bug.cgi?id=698272)
|
||||
]
|
||||
command = [
|
||||
"gs",
|
||||
"-q", # quiet mode
|
||||
"-g%dx%d" % size, # set output geometry (pixels)
|
||||
"-r%fx%f" % res, # set input DPI (dots per inch)
|
||||
"-dBATCH", # exit after processing
|
||||
"-dNOPAUSE", # don't pause between pages
|
||||
"-dSAFER", # safe mode
|
||||
"-sDEVICE=ppmraw", # ppm driver
|
||||
"-sOutputFile=%s" % outfile, # output file
|
||||
# adjust for image origin
|
||||
"-c",
|
||||
"%d %d translate" % (-bbox[0], -bbox[1]),
|
||||
"-f",
|
||||
infile, # input file
|
||||
# showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
|
||||
"-c",
|
||||
"showpage",
|
||||
]
|
||||
|
||||
if gs_windows_binary is not None:
|
||||
if not gs_windows_binary:
|
||||
raise WindowsError('Unable to locate Ghostscript on paths')
|
||||
raise WindowsError("Unable to locate Ghostscript on paths")
|
||||
command[0] = gs_windows_binary
|
||||
|
||||
# push data through Ghostscript
|
||||
try:
|
||||
with open(os.devnull, 'w+b') as devnull:
|
||||
startupinfo = None
|
||||
if sys.platform.startswith('win'):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
subprocess.check_call(command, stdin=devnull, stdout=devnull,
|
||||
startupinfo=startupinfo)
|
||||
startupinfo = None
|
||||
if sys.platform.startswith("win"):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
subprocess.check_call(command, startupinfo=startupinfo)
|
||||
im = Image.open(outfile)
|
||||
im.load()
|
||||
finally:
|
||||
|
|
@ -162,11 +173,12 @@ class PSFile(object):
|
|||
"""
|
||||
Wrapper for bytesio object that treats either CR or LF as end of line.
|
||||
"""
|
||||
|
||||
def __init__(self, fp):
|
||||
self.fp = fp
|
||||
self.char = None
|
||||
|
||||
def seek(self, offset, whence=0):
|
||||
def seek(self, offset, whence=io.SEEK_SET):
|
||||
self.char = None
|
||||
self.fp.seek(offset, whence)
|
||||
|
||||
|
|
@ -184,12 +196,12 @@ class PSFile(object):
|
|||
if self.char in b"\r\n":
|
||||
self.char = None
|
||||
|
||||
return s.decode('latin-1')
|
||||
return s.decode("latin-1")
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:4] == b"%!PS" or \
|
||||
(len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
|
||||
return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for Encapsulated Postscript. This plugin supports only
|
||||
|
|
@ -223,7 +235,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
# Load EPS header
|
||||
|
||||
s_raw = fp.readline()
|
||||
s = s_raw.strip('\r\n')
|
||||
s = s_raw.strip("\r\n")
|
||||
|
||||
while s_raw:
|
||||
if s:
|
||||
|
|
@ -245,8 +257,9 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
# put floating point values there anyway.
|
||||
box = [int(float(i)) for i in v.split()]
|
||||
self._size = box[2] - box[0], box[3] - box[1]
|
||||
self.tile = [("eps", (0, 0) + self.size, offset,
|
||||
(length, box))]
|
||||
self.tile = [
|
||||
("eps", (0, 0) + self.size, offset, (length, box))
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
@ -261,7 +274,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
self.info[k[:8]] = k[9:]
|
||||
else:
|
||||
self.info[k] = ""
|
||||
elif s[0] == '%':
|
||||
elif s[0] == "%":
|
||||
# handle non-DSC Postscript comments that some
|
||||
# tools mistakenly put in the Comments section
|
||||
pass
|
||||
|
|
@ -269,7 +282,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
raise IOError("bad EPS header")
|
||||
|
||||
s_raw = fp.readline()
|
||||
s = s_raw.strip('\r\n')
|
||||
s = s_raw.strip("\r\n")
|
||||
|
||||
if s and s[:1] != "%":
|
||||
break
|
||||
|
|
@ -296,7 +309,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
self._size = int(x), int(y)
|
||||
return
|
||||
|
||||
s = fp.readline().strip('\r\n')
|
||||
s = fp.readline().strip("\r\n")
|
||||
if not s:
|
||||
break
|
||||
|
||||
|
|
@ -309,7 +322,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
|
||||
if s[:4] == b"%!PS":
|
||||
# for HEAD without binary preview
|
||||
fp.seek(0, 2)
|
||||
fp.seek(0, io.SEEK_END)
|
||||
length = fp.tell()
|
||||
offset = 0
|
||||
elif i32(s[0:4]) == 0xC6D3D0C5:
|
||||
|
|
@ -343,6 +356,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
def _save(im, fp, filename, eps=1):
|
||||
"""EPS Writer for the Python Imaging Library."""
|
||||
|
||||
|
|
@ -365,7 +379,7 @@ def _save(im, fp, filename, eps=1):
|
|||
wrapped_fp = False
|
||||
if fp != sys.stdout:
|
||||
if sys.version_info.major > 2:
|
||||
fp = io.TextIOWrapper(fp, encoding='latin-1')
|
||||
fp = io.TextIOWrapper(fp, encoding="latin-1")
|
||||
wrapped_fp = True
|
||||
|
||||
try:
|
||||
|
|
@ -380,7 +394,7 @@ def _save(im, fp, filename, eps=1):
|
|||
fp.write("%%EndComments\n")
|
||||
fp.write("%%Page: 1 1\n")
|
||||
fp.write("%%ImageData: %d %d " % im.size)
|
||||
fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
|
||||
fp.write('%d %d 0 1 1 "%s"\n' % operator)
|
||||
|
||||
#
|
||||
# image header
|
||||
|
|
@ -395,7 +409,7 @@ def _save(im, fp, filename, eps=1):
|
|||
if hasattr(fp, "flush"):
|
||||
fp.flush()
|
||||
|
||||
ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
|
||||
ImageFile._save(im, base_fp, [("eps", (0, 0) + im.size, 0, None)])
|
||||
|
||||
fp.write("\n%%%%EndBinary\n")
|
||||
fp.write("grestore end\n")
|
||||
|
|
@ -405,6 +419,7 @@ def _save(im, fp, filename, eps=1):
|
|||
if wrapped_fp:
|
||||
fp.detach()
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@
|
|||
# Maps EXIF tags to tag names.
|
||||
|
||||
TAGS = {
|
||||
|
||||
# possibly incomplete
|
||||
0x000b: "ProcessingSoftware",
|
||||
0x00fe: "NewSubfileType",
|
||||
0x00ff: "SubfileType",
|
||||
0x000B: "ProcessingSoftware",
|
||||
0x00FE: "NewSubfileType",
|
||||
0x00FF: "SubfileType",
|
||||
0x0100: "ImageWidth",
|
||||
0x0101: "ImageLength",
|
||||
0x0102: "BitsPerSample",
|
||||
|
|
@ -31,10 +30,10 @@ TAGS = {
|
|||
0x0107: "Thresholding",
|
||||
0x0108: "CellWidth",
|
||||
0x0109: "CellLength",
|
||||
0x010a: "FillOrder",
|
||||
0x010d: "DocumentName",
|
||||
0x010e: "ImageDescription",
|
||||
0x010f: "Make",
|
||||
0x010A: "FillOrder",
|
||||
0x010D: "DocumentName",
|
||||
0x010E: "ImageDescription",
|
||||
0x010F: "Make",
|
||||
0x0110: "Model",
|
||||
0x0111: "StripOffsets",
|
||||
0x0112: "Orientation",
|
||||
|
|
@ -43,10 +42,10 @@ TAGS = {
|
|||
0x0117: "StripByteCounts",
|
||||
0x0118: "MinSampleValue",
|
||||
0x0119: "MaxSampleValue",
|
||||
0x011a: "XResolution",
|
||||
0x011b: "YResolution",
|
||||
0x011c: "PlanarConfiguration",
|
||||
0x011d: "PageName",
|
||||
0x011A: "XResolution",
|
||||
0x011B: "YResolution",
|
||||
0x011C: "PlanarConfiguration",
|
||||
0x011D: "PageName",
|
||||
0x0120: "FreeOffsets",
|
||||
0x0121: "FreeByteCounts",
|
||||
0x0122: "GrayResponseUnit",
|
||||
|
|
@ -55,24 +54,24 @@ TAGS = {
|
|||
0x0125: "T6Options",
|
||||
0x0128: "ResolutionUnit",
|
||||
0x0129: "PageNumber",
|
||||
0x012d: "TransferFunction",
|
||||
0x012D: "TransferFunction",
|
||||
0x0131: "Software",
|
||||
0x0132: "DateTime",
|
||||
0x013b: "Artist",
|
||||
0x013c: "HostComputer",
|
||||
0x013d: "Predictor",
|
||||
0x013e: "WhitePoint",
|
||||
0x013f: "PrimaryChromaticities",
|
||||
0x013B: "Artist",
|
||||
0x013C: "HostComputer",
|
||||
0x013D: "Predictor",
|
||||
0x013E: "WhitePoint",
|
||||
0x013F: "PrimaryChromaticities",
|
||||
0x0140: "ColorMap",
|
||||
0x0141: "HalftoneHints",
|
||||
0x0142: "TileWidth",
|
||||
0x0143: "TileLength",
|
||||
0x0144: "TileOffsets",
|
||||
0x0145: "TileByteCounts",
|
||||
0x014a: "SubIFDs",
|
||||
0x014c: "InkSet",
|
||||
0x014d: "InkNames",
|
||||
0x014e: "NumberOfInks",
|
||||
0x014A: "SubIFDs",
|
||||
0x014C: "InkSet",
|
||||
0x014D: "InkNames",
|
||||
0x014E: "NumberOfInks",
|
||||
0x0150: "DotRange",
|
||||
0x0151: "TargetPrinter",
|
||||
0x0152: "ExtraSamples",
|
||||
|
|
@ -83,9 +82,9 @@ TAGS = {
|
|||
0x0157: "ClipPath",
|
||||
0x0158: "XClipPathUnits",
|
||||
0x0159: "YClipPathUnits",
|
||||
0x015a: "Indexed",
|
||||
0x015b: "JPEGTables",
|
||||
0x015f: "OPIProxy",
|
||||
0x015A: "Indexed",
|
||||
0x015B: "JPEGTables",
|
||||
0x015F: "OPIProxy",
|
||||
0x0200: "JPEGProc",
|
||||
0x0201: "JpegIFOffset",
|
||||
0x0202: "JpegIFByteCount",
|
||||
|
|
@ -99,20 +98,20 @@ TAGS = {
|
|||
0x0212: "YCbCrSubSampling",
|
||||
0x0213: "YCbCrPositioning",
|
||||
0x0214: "ReferenceBlackWhite",
|
||||
0x02bc: "XMLPacket",
|
||||
0x02BC: "XMLPacket",
|
||||
0x1000: "RelatedImageFileFormat",
|
||||
0x1001: "RelatedImageWidth",
|
||||
0x1002: "RelatedImageLength",
|
||||
0x4746: "Rating",
|
||||
0x4749: "RatingPercent",
|
||||
0x800d: "ImageID",
|
||||
0x828d: "CFARepeatPatternDim",
|
||||
0x828e: "CFAPattern",
|
||||
0x828f: "BatteryLevel",
|
||||
0x800D: "ImageID",
|
||||
0x828D: "CFARepeatPatternDim",
|
||||
0x828E: "CFAPattern",
|
||||
0x828F: "BatteryLevel",
|
||||
0x8298: "Copyright",
|
||||
0x829a: "ExposureTime",
|
||||
0x829d: "FNumber",
|
||||
0x83bb: "IPTCNAA",
|
||||
0x829A: "ExposureTime",
|
||||
0x829D: "FNumber",
|
||||
0x83BB: "IPTCNAA",
|
||||
0x8649: "ImageResources",
|
||||
0x8769: "ExifOffset",
|
||||
0x8773: "InterColorProfile",
|
||||
|
|
@ -122,8 +121,8 @@ TAGS = {
|
|||
0x8827: "ISOSpeedRatings",
|
||||
0x8828: "OECF",
|
||||
0x8829: "Interlace",
|
||||
0x882a: "TimeZoneOffset",
|
||||
0x882b: "SelfTimerMode",
|
||||
0x882A: "TimeZoneOffset",
|
||||
0x882B: "SelfTimerMode",
|
||||
0x9000: "ExifVersion",
|
||||
0x9003: "DateTimeOriginal",
|
||||
0x9004: "DateTimeDigitized",
|
||||
|
|
@ -138,142 +137,142 @@ TAGS = {
|
|||
0x9207: "MeteringMode",
|
||||
0x9208: "LightSource",
|
||||
0x9209: "Flash",
|
||||
0x920a: "FocalLength",
|
||||
0x920b: "FlashEnergy",
|
||||
0x920c: "SpatialFrequencyResponse",
|
||||
0x920d: "Noise",
|
||||
0x920A: "FocalLength",
|
||||
0x920B: "FlashEnergy",
|
||||
0x920C: "SpatialFrequencyResponse",
|
||||
0x920D: "Noise",
|
||||
0x9211: "ImageNumber",
|
||||
0x9212: "SecurityClassification",
|
||||
0x9213: "ImageHistory",
|
||||
0x9214: "SubjectLocation",
|
||||
0x9215: "ExposureIndex",
|
||||
0x9216: "TIFF/EPStandardID",
|
||||
0x927c: "MakerNote",
|
||||
0x927C: "MakerNote",
|
||||
0x9286: "UserComment",
|
||||
0x9290: "SubsecTime",
|
||||
0x9291: "SubsecTimeOriginal",
|
||||
0x9292: "SubsecTimeDigitized",
|
||||
0x9c9b: "XPTitle",
|
||||
0x9c9c: "XPComment",
|
||||
0x9c9d: "XPAuthor",
|
||||
0x9c9e: "XPKeywords",
|
||||
0x9c9f: "XPSubject",
|
||||
0xa000: "FlashPixVersion",
|
||||
0xa001: "ColorSpace",
|
||||
0xa002: "ExifImageWidth",
|
||||
0xa003: "ExifImageHeight",
|
||||
0xa004: "RelatedSoundFile",
|
||||
0xa005: "ExifInteroperabilityOffset",
|
||||
0xa20b: "FlashEnergy",
|
||||
0xa20c: "SpatialFrequencyResponse",
|
||||
0xa20e: "FocalPlaneXResolution",
|
||||
0xa20f: "FocalPlaneYResolution",
|
||||
0xa210: "FocalPlaneResolutionUnit",
|
||||
0xa214: "SubjectLocation",
|
||||
0xa215: "ExposureIndex",
|
||||
0xa217: "SensingMethod",
|
||||
0xa300: "FileSource",
|
||||
0xa301: "SceneType",
|
||||
0xa302: "CFAPattern",
|
||||
0xa401: "CustomRendered",
|
||||
0xa402: "ExposureMode",
|
||||
0xa403: "WhiteBalance",
|
||||
0xa404: "DigitalZoomRatio",
|
||||
0xa405: "FocalLengthIn35mmFilm",
|
||||
0xa406: "SceneCaptureType",
|
||||
0xa407: "GainControl",
|
||||
0xa408: "Contrast",
|
||||
0xa409: "Saturation",
|
||||
0xa40a: "Sharpness",
|
||||
0xa40b: "DeviceSettingDescription",
|
||||
0xa40c: "SubjectDistanceRange",
|
||||
0xa420: "ImageUniqueID",
|
||||
0xa430: "CameraOwnerName",
|
||||
0xa431: "BodySerialNumber",
|
||||
0xa432: "LensSpecification",
|
||||
0xa433: "LensMake",
|
||||
0xa434: "LensModel",
|
||||
0xa435: "LensSerialNumber",
|
||||
0xa500: "Gamma",
|
||||
0xc4a5: "PrintImageMatching",
|
||||
0xc612: "DNGVersion",
|
||||
0xc613: "DNGBackwardVersion",
|
||||
0xc614: "UniqueCameraModel",
|
||||
0xc615: "LocalizedCameraModel",
|
||||
0xc616: "CFAPlaneColor",
|
||||
0xc617: "CFALayout",
|
||||
0xc618: "LinearizationTable",
|
||||
0xc619: "BlackLevelRepeatDim",
|
||||
0xc61a: "BlackLevel",
|
||||
0xc61b: "BlackLevelDeltaH",
|
||||
0xc61c: "BlackLevelDeltaV",
|
||||
0xc61d: "WhiteLevel",
|
||||
0xc61e: "DefaultScale",
|
||||
0xc61f: "DefaultCropOrigin",
|
||||
0xc620: "DefaultCropSize",
|
||||
0xc621: "ColorMatrix1",
|
||||
0xc622: "ColorMatrix2",
|
||||
0xc623: "CameraCalibration1",
|
||||
0xc624: "CameraCalibration2",
|
||||
0xc625: "ReductionMatrix1",
|
||||
0xc626: "ReductionMatrix2",
|
||||
0xc627: "AnalogBalance",
|
||||
0xc628: "AsShotNeutral",
|
||||
0xc629: "AsShotWhiteXY",
|
||||
0xc62a: "BaselineExposure",
|
||||
0xc62b: "BaselineNoise",
|
||||
0xc62c: "BaselineSharpness",
|
||||
0xc62d: "BayerGreenSplit",
|
||||
0xc62e: "LinearResponseLimit",
|
||||
0xc62f: "CameraSerialNumber",
|
||||
0xc630: "LensInfo",
|
||||
0xc631: "ChromaBlurRadius",
|
||||
0xc632: "AntiAliasStrength",
|
||||
0xc633: "ShadowScale",
|
||||
0xc634: "DNGPrivateData",
|
||||
0xc635: "MakerNoteSafety",
|
||||
0xc65a: "CalibrationIlluminant1",
|
||||
0xc65b: "CalibrationIlluminant2",
|
||||
0xc65c: "BestQualityScale",
|
||||
0xc65d: "RawDataUniqueID",
|
||||
0xc68b: "OriginalRawFileName",
|
||||
0xc68c: "OriginalRawFileData",
|
||||
0xc68d: "ActiveArea",
|
||||
0xc68e: "MaskedAreas",
|
||||
0xc68f: "AsShotICCProfile",
|
||||
0xc690: "AsShotPreProfileMatrix",
|
||||
0xc691: "CurrentICCProfile",
|
||||
0xc692: "CurrentPreProfileMatrix",
|
||||
0xc6bf: "ColorimetricReference",
|
||||
0xc6f3: "CameraCalibrationSignature",
|
||||
0xc6f4: "ProfileCalibrationSignature",
|
||||
0xc6f6: "AsShotProfileName",
|
||||
0xc6f7: "NoiseReductionApplied",
|
||||
0xc6f8: "ProfileName",
|
||||
0xc6f9: "ProfileHueSatMapDims",
|
||||
0xc6fa: "ProfileHueSatMapData1",
|
||||
0xc6fb: "ProfileHueSatMapData2",
|
||||
0xc6fc: "ProfileToneCurve",
|
||||
0xc6fd: "ProfileEmbedPolicy",
|
||||
0xc6fe: "ProfileCopyright",
|
||||
0xc714: "ForwardMatrix1",
|
||||
0xc715: "ForwardMatrix2",
|
||||
0xc716: "PreviewApplicationName",
|
||||
0xc717: "PreviewApplicationVersion",
|
||||
0xc718: "PreviewSettingsName",
|
||||
0xc719: "PreviewSettingsDigest",
|
||||
0xc71a: "PreviewColorSpace",
|
||||
0xc71b: "PreviewDateTime",
|
||||
0xc71c: "RawImageDigest",
|
||||
0xc71d: "OriginalRawFileDigest",
|
||||
0xc71e: "SubTileBlockSize",
|
||||
0xc71f: "RowInterleaveFactor",
|
||||
0xc725: "ProfileLookTableDims",
|
||||
0xc726: "ProfileLookTableData",
|
||||
0xc740: "OpcodeList1",
|
||||
0xc741: "OpcodeList2",
|
||||
0xc74e: "OpcodeList3",
|
||||
0xc761: "NoiseProfile"
|
||||
0x9C9B: "XPTitle",
|
||||
0x9C9C: "XPComment",
|
||||
0x9C9D: "XPAuthor",
|
||||
0x9C9E: "XPKeywords",
|
||||
0x9C9F: "XPSubject",
|
||||
0xA000: "FlashPixVersion",
|
||||
0xA001: "ColorSpace",
|
||||
0xA002: "ExifImageWidth",
|
||||
0xA003: "ExifImageHeight",
|
||||
0xA004: "RelatedSoundFile",
|
||||
0xA005: "ExifInteroperabilityOffset",
|
||||
0xA20B: "FlashEnergy",
|
||||
0xA20C: "SpatialFrequencyResponse",
|
||||
0xA20E: "FocalPlaneXResolution",
|
||||
0xA20F: "FocalPlaneYResolution",
|
||||
0xA210: "FocalPlaneResolutionUnit",
|
||||
0xA214: "SubjectLocation",
|
||||
0xA215: "ExposureIndex",
|
||||
0xA217: "SensingMethod",
|
||||
0xA300: "FileSource",
|
||||
0xA301: "SceneType",
|
||||
0xA302: "CFAPattern",
|
||||
0xA401: "CustomRendered",
|
||||
0xA402: "ExposureMode",
|
||||
0xA403: "WhiteBalance",
|
||||
0xA404: "DigitalZoomRatio",
|
||||
0xA405: "FocalLengthIn35mmFilm",
|
||||
0xA406: "SceneCaptureType",
|
||||
0xA407: "GainControl",
|
||||
0xA408: "Contrast",
|
||||
0xA409: "Saturation",
|
||||
0xA40A: "Sharpness",
|
||||
0xA40B: "DeviceSettingDescription",
|
||||
0xA40C: "SubjectDistanceRange",
|
||||
0xA420: "ImageUniqueID",
|
||||
0xA430: "CameraOwnerName",
|
||||
0xA431: "BodySerialNumber",
|
||||
0xA432: "LensSpecification",
|
||||
0xA433: "LensMake",
|
||||
0xA434: "LensModel",
|
||||
0xA435: "LensSerialNumber",
|
||||
0xA500: "Gamma",
|
||||
0xC4A5: "PrintImageMatching",
|
||||
0xC612: "DNGVersion",
|
||||
0xC613: "DNGBackwardVersion",
|
||||
0xC614: "UniqueCameraModel",
|
||||
0xC615: "LocalizedCameraModel",
|
||||
0xC616: "CFAPlaneColor",
|
||||
0xC617: "CFALayout",
|
||||
0xC618: "LinearizationTable",
|
||||
0xC619: "BlackLevelRepeatDim",
|
||||
0xC61A: "BlackLevel",
|
||||
0xC61B: "BlackLevelDeltaH",
|
||||
0xC61C: "BlackLevelDeltaV",
|
||||
0xC61D: "WhiteLevel",
|
||||
0xC61E: "DefaultScale",
|
||||
0xC61F: "DefaultCropOrigin",
|
||||
0xC620: "DefaultCropSize",
|
||||
0xC621: "ColorMatrix1",
|
||||
0xC622: "ColorMatrix2",
|
||||
0xC623: "CameraCalibration1",
|
||||
0xC624: "CameraCalibration2",
|
||||
0xC625: "ReductionMatrix1",
|
||||
0xC626: "ReductionMatrix2",
|
||||
0xC627: "AnalogBalance",
|
||||
0xC628: "AsShotNeutral",
|
||||
0xC629: "AsShotWhiteXY",
|
||||
0xC62A: "BaselineExposure",
|
||||
0xC62B: "BaselineNoise",
|
||||
0xC62C: "BaselineSharpness",
|
||||
0xC62D: "BayerGreenSplit",
|
||||
0xC62E: "LinearResponseLimit",
|
||||
0xC62F: "CameraSerialNumber",
|
||||
0xC630: "LensInfo",
|
||||
0xC631: "ChromaBlurRadius",
|
||||
0xC632: "AntiAliasStrength",
|
||||
0xC633: "ShadowScale",
|
||||
0xC634: "DNGPrivateData",
|
||||
0xC635: "MakerNoteSafety",
|
||||
0xC65A: "CalibrationIlluminant1",
|
||||
0xC65B: "CalibrationIlluminant2",
|
||||
0xC65C: "BestQualityScale",
|
||||
0xC65D: "RawDataUniqueID",
|
||||
0xC68B: "OriginalRawFileName",
|
||||
0xC68C: "OriginalRawFileData",
|
||||
0xC68D: "ActiveArea",
|
||||
0xC68E: "MaskedAreas",
|
||||
0xC68F: "AsShotICCProfile",
|
||||
0xC690: "AsShotPreProfileMatrix",
|
||||
0xC691: "CurrentICCProfile",
|
||||
0xC692: "CurrentPreProfileMatrix",
|
||||
0xC6BF: "ColorimetricReference",
|
||||
0xC6F3: "CameraCalibrationSignature",
|
||||
0xC6F4: "ProfileCalibrationSignature",
|
||||
0xC6F6: "AsShotProfileName",
|
||||
0xC6F7: "NoiseReductionApplied",
|
||||
0xC6F8: "ProfileName",
|
||||
0xC6F9: "ProfileHueSatMapDims",
|
||||
0xC6FA: "ProfileHueSatMapData1",
|
||||
0xC6FB: "ProfileHueSatMapData2",
|
||||
0xC6FC: "ProfileToneCurve",
|
||||
0xC6FD: "ProfileEmbedPolicy",
|
||||
0xC6FE: "ProfileCopyright",
|
||||
0xC714: "ForwardMatrix1",
|
||||
0xC715: "ForwardMatrix2",
|
||||
0xC716: "PreviewApplicationName",
|
||||
0xC717: "PreviewApplicationVersion",
|
||||
0xC718: "PreviewSettingsName",
|
||||
0xC719: "PreviewSettingsDigest",
|
||||
0xC71A: "PreviewColorSpace",
|
||||
0xC71B: "PreviewDateTime",
|
||||
0xC71C: "RawImageDigest",
|
||||
0xC71D: "OriginalRawFileDigest",
|
||||
0xC71E: "SubTileBlockSize",
|
||||
0xC71F: "RowInterleaveFactor",
|
||||
0xC725: "ProfileLookTableDims",
|
||||
0xC726: "ProfileLookTableData",
|
||||
0xC740: "OpcodeList1",
|
||||
0xC741: "OpcodeList2",
|
||||
0xC74E: "OpcodeList3",
|
||||
0xC761: "NoiseProfile",
|
||||
}
|
||||
|
||||
##
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ def register_handler(handler):
|
|||
global _handler
|
||||
_handler = handler
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Image adapter
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,15 @@
|
|||
from . import Image, ImageFile, ImagePalette
|
||||
from ._binary import i8, i16le as i16, i32le as i32, o8
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.2"
|
||||
|
||||
|
||||
#
|
||||
# decoder
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return len(prefix) >= 6 and i16(prefix[4:6]) in [0xAF11, 0xAF12]
|
||||
|
||||
|
|
@ -33,6 +36,7 @@ def _accept(prefix):
|
|||
# Image plugin for the FLI/FLC animation format. Use the <b>seek</b>
|
||||
# method to load individual frames.
|
||||
|
||||
|
||||
class FliImageFile(ImageFile.ImageFile):
|
||||
|
||||
format = "FLI"
|
||||
|
|
@ -44,9 +48,11 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
# HEAD
|
||||
s = self.fp.read(128)
|
||||
magic = i16(s[4:6])
|
||||
if not (magic in [0xAF11, 0xAF12] and
|
||||
i16(s[14:16]) in [0, 3] and # flags
|
||||
s[20:22] == b"\x00\x00"): # reserved
|
||||
if not (
|
||||
magic in [0xAF11, 0xAF12]
|
||||
and i16(s[14:16]) in [0, 3] # flags
|
||||
and s[20:22] == b"\x00\x00" # reserved
|
||||
):
|
||||
raise SyntaxError("not an FLI/FLC file")
|
||||
|
||||
# frames
|
||||
|
|
@ -82,7 +88,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
elif i16(s[4:6]) == 4:
|
||||
self._palette(palette, 0)
|
||||
|
||||
palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette]
|
||||
palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
|
||||
self.palette = ImagePalette.raw("RGB", b"".join(palette))
|
||||
|
||||
# set things up to decode first frame
|
||||
|
|
@ -104,8 +110,8 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
s = self.fp.read(n * 3)
|
||||
for n in range(0, len(s), 3):
|
||||
r = i8(s[n]) << shift
|
||||
g = i8(s[n+1]) << shift
|
||||
b = i8(s[n+2]) << shift
|
||||
g = i8(s[n + 1]) << shift
|
||||
b = i8(s[n + 2]) << shift
|
||||
palette[i] = (r, g, b)
|
||||
i += 1
|
||||
|
||||
|
|
@ -131,6 +137,9 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
self.__frame = -1
|
||||
self.__fp.seek(self.__rewind)
|
||||
self.__offset = 128
|
||||
else:
|
||||
# ensure that the previous frame was loaded
|
||||
self.load()
|
||||
|
||||
if frame != self.__frame + 1:
|
||||
raise ValueError("cannot seek to frame %d" % frame)
|
||||
|
|
@ -147,13 +156,22 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
framesize = i32(s)
|
||||
|
||||
self.decodermaxblock = framesize
|
||||
self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
|
||||
self.tile = [("fli", (0, 0) + self.size, self.__offset, None)]
|
||||
|
||||
self.__offset += framesize
|
||||
|
||||
def tell(self):
|
||||
return self.__frame
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
self.__fp = None
|
||||
|
||||
|
||||
#
|
||||
# registry
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
|
||||
from . import Image, _binary
|
||||
|
||||
WIDTH = 800
|
||||
|
|
@ -33,6 +34,7 @@ def puti16(fp, values):
|
|||
##
|
||||
# Base class for raster font file handlers.
|
||||
|
||||
|
||||
class FontFile(object):
|
||||
|
||||
bitmap = None
|
||||
|
|
@ -46,7 +48,7 @@ class FontFile(object):
|
|||
return self.glyph[ix]
|
||||
|
||||
def compile(self):
|
||||
"Create metrics and bitmap"
|
||||
"""Create metrics and bitmap"""
|
||||
|
||||
if self.bitmap:
|
||||
return
|
||||
|
|
@ -61,7 +63,7 @@ class FontFile(object):
|
|||
w = w + (src[2] - src[0])
|
||||
if w > WIDTH:
|
||||
lines += 1
|
||||
w = (src[2] - src[0])
|
||||
w = src[2] - src[0]
|
||||
maxwidth = max(maxwidth, w)
|
||||
|
||||
xsize = maxwidth
|
||||
|
|
@ -93,7 +95,7 @@ class FontFile(object):
|
|||
self.metrics[i] = d, dst, s
|
||||
|
||||
def save(self, filename):
|
||||
"Save font"
|
||||
"""Save font"""
|
||||
|
||||
self.compile()
|
||||
|
||||
|
|
@ -103,7 +105,7 @@ class FontFile(object):
|
|||
# font metrics
|
||||
with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
|
||||
fp.write(b"PILfont\n")
|
||||
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
|
||||
fp.write((";;;;;;%d;\n" % self.ysize).encode("ascii")) # HACK!!!
|
||||
fp.write(b"DATA\n")
|
||||
for id in range(256):
|
||||
m = self.metrics[id]
|
||||
|
|
|
|||
|
|
@ -17,32 +17,35 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
from . import Image, ImageFile
|
||||
from ._binary import i32le as i32, i8
|
||||
|
||||
import olefile
|
||||
|
||||
from . import Image, ImageFile
|
||||
from ._binary import i8, i32le as i32
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.1"
|
||||
|
||||
# we map from colour field tuples to (mode, rawmode) descriptors
|
||||
MODES = {
|
||||
# opacity
|
||||
(0x00007ffe): ("A", "L"),
|
||||
(0x00007FFE): ("A", "L"),
|
||||
# monochrome
|
||||
(0x00010000,): ("L", "L"),
|
||||
(0x00018000, 0x00017ffe): ("RGBA", "LA"),
|
||||
(0x00018000, 0x00017FFE): ("RGBA", "LA"),
|
||||
# photo YCC
|
||||
(0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"),
|
||||
(0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"),
|
||||
(0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"),
|
||||
# standard RGB (NIFRGB)
|
||||
(0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"),
|
||||
(0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"),
|
||||
(0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"),
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:8] == olefile.MAGIC
|
||||
|
||||
|
|
@ -50,6 +53,7 @@ def _accept(prefix):
|
|||
##
|
||||
# Image plugin for the FlashPix images.
|
||||
|
||||
|
||||
class FpxImageFile(ImageFile.ImageFile):
|
||||
|
||||
format = "FPX"
|
||||
|
|
@ -74,10 +78,9 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
#
|
||||
# get the Image Contents Property Set
|
||||
|
||||
prop = self.ole.getproperties([
|
||||
"Data Object Store %06d" % index,
|
||||
"\005Image Contents"
|
||||
])
|
||||
prop = self.ole.getproperties(
|
||||
["Data Object Store %06d" % index, "\005Image Contents"]
|
||||
)
|
||||
|
||||
# size (highest resolution)
|
||||
|
||||
|
|
@ -103,7 +106,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
colors = []
|
||||
for i in range(i32(s, 4)):
|
||||
# note: for now, we ignore the "uncalibrated" flag
|
||||
colors.append(i32(s, 8+i*4) & 0x7fffffff)
|
||||
colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF)
|
||||
|
||||
self.mode, self.rawmode = MODES[tuple(colors)]
|
||||
|
||||
|
|
@ -123,7 +126,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
stream = [
|
||||
"Data Object Store %06d" % index,
|
||||
"Resolution %04d" % subimage,
|
||||
"Subimage 0000 Header"
|
||||
"Subimage 0000 Header",
|
||||
]
|
||||
|
||||
fp = self.ole.openstream(stream)
|
||||
|
|
@ -155,17 +158,29 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
|
||||
for i in range(0, len(s), length):
|
||||
|
||||
compression = i32(s, i+8)
|
||||
compression = i32(s, i + 8)
|
||||
|
||||
if compression == 0:
|
||||
self.tile.append(("raw", (x, y, x+xtile, y+ytile),
|
||||
i32(s, i) + 28, (self.rawmode)))
|
||||
self.tile.append(
|
||||
(
|
||||
"raw",
|
||||
(x, y, x + xtile, y + ytile),
|
||||
i32(s, i) + 28,
|
||||
(self.rawmode),
|
||||
)
|
||||
)
|
||||
|
||||
elif compression == 1:
|
||||
|
||||
# FIXME: the fill decoder is not implemented
|
||||
self.tile.append(("fill", (x, y, x+xtile, y+ytile),
|
||||
i32(s, i) + 28, (self.rawmode, s[12:16])))
|
||||
self.tile.append(
|
||||
(
|
||||
"fill",
|
||||
(x, y, x + xtile, y + ytile),
|
||||
i32(s, i) + 28,
|
||||
(self.rawmode, s[12:16]),
|
||||
)
|
||||
)
|
||||
|
||||
elif compression == 2:
|
||||
|
||||
|
|
@ -187,8 +202,14 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
# The image is stored as defined by rawmode
|
||||
jpegmode = rawmode
|
||||
|
||||
self.tile.append(("jpeg", (x, y, x+xtile, y+ytile),
|
||||
i32(s, i) + 28, (rawmode, jpegmode)))
|
||||
self.tile.append(
|
||||
(
|
||||
"jpeg",
|
||||
(x, y, x + xtile, y + ytile),
|
||||
i32(s, i) + 28,
|
||||
(rawmode, jpegmode),
|
||||
)
|
||||
)
|
||||
|
||||
# FIXME: jpeg tables are tile dependent; the prefix
|
||||
# data must be placed in the tile descriptor itself!
|
||||
|
|
@ -211,11 +232,11 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
def load(self):
|
||||
|
||||
if not self.fp:
|
||||
self.fp = self.ole.openstream(self.stream[:2] +
|
||||
["Subimage 0000 Data"])
|
||||
self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"])
|
||||
|
||||
return ImageFile.ImageFile.load(self)
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,14 @@ has the following structure:
|
|||
{format_directory}
|
||||
{data}
|
||||
Where:
|
||||
{header} = { u32:magic, u32:version, u32:width, u32:height, u32:mipmap_count, u32:format_count }
|
||||
{header} = {
|
||||
u32:magic,
|
||||
u32:version,
|
||||
u32:width,
|
||||
u32:height,
|
||||
u32:mipmap_count,
|
||||
u32:format_count
|
||||
}
|
||||
|
||||
* The "magic" number is "FTEX".
|
||||
* "width" and "height" are the dimensions of the texture.
|
||||
|
|
@ -46,8 +53,8 @@ Note: All data is stored in little-Endian (Intel) byte order.
|
|||
|
||||
import struct
|
||||
from io import BytesIO
|
||||
from . import Image, ImageFile
|
||||
|
||||
from . import Image, ImageFile
|
||||
|
||||
MAGIC = b"FTEX"
|
||||
FORMAT_DXT1 = 0
|
||||
|
|
@ -59,8 +66,8 @@ class FtexImageFile(ImageFile.ImageFile):
|
|||
format_description = "Texture File Format (IW2:EOC)"
|
||||
|
||||
def _open(self):
|
||||
magic = struct.unpack("<I", self.fp.read(4))
|
||||
version = struct.unpack("<i", self.fp.read(4))
|
||||
struct.unpack("<I", self.fp.read(4)) # magic
|
||||
struct.unpack("<i", self.fp.read(4)) # version
|
||||
self._size = struct.unpack("<2i", self.fp.read(8))
|
||||
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
|
||||
|
||||
|
|
@ -80,10 +87,9 @@ class FtexImageFile(ImageFile.ImageFile):
|
|||
self.mode = "RGBA"
|
||||
self.tile = [("bcn", (0, 0) + self.size, 0, (1))]
|
||||
elif format == FORMAT_UNCOMPRESSED:
|
||||
self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
|
||||
self.tile = [("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))]
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid texture compression format: %r" % (format))
|
||||
raise ValueError("Invalid texture compression format: %r" % (format))
|
||||
|
||||
self.fp.close()
|
||||
self.fp = BytesIO(data)
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ from ._binary import i32be as i32
|
|||
|
||||
|
||||
def _accept(prefix):
|
||||
return len(prefix) >= 8 and \
|
||||
i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
|
||||
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for the GIMP brush format.
|
||||
|
||||
|
||||
class GbrImageFile(ImageFile.ImageFile):
|
||||
|
||||
format = "GBR"
|
||||
|
|
@ -55,24 +55,23 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
if width <= 0 or height <= 0:
|
||||
raise SyntaxError("not a GIMP brush")
|
||||
if color_depth not in (1, 4):
|
||||
raise SyntaxError(
|
||||
"Unsupported GIMP brush color depth: %s" % color_depth)
|
||||
raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth)
|
||||
|
||||
if version == 1:
|
||||
comment_length = header_size-20
|
||||
comment_length = header_size - 20
|
||||
else:
|
||||
comment_length = header_size-28
|
||||
comment_length = header_size - 28
|
||||
magic_number = self.fp.read(4)
|
||||
if magic_number != b'GIMP':
|
||||
if magic_number != b"GIMP":
|
||||
raise SyntaxError("not a GIMP brush, bad magic number")
|
||||
self.info['spacing'] = i32(self.fp.read(4))
|
||||
self.info["spacing"] = i32(self.fp.read(4))
|
||||
|
||||
comment = self.fp.read(comment_length)[:-1]
|
||||
|
||||
if color_depth == 1:
|
||||
self.mode = "L"
|
||||
else:
|
||||
self.mode = 'RGBA'
|
||||
self.mode = "RGBA"
|
||||
|
||||
self._size = width, height
|
||||
|
||||
|
|
@ -88,6 +87,7 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
self.im = Image.core.new(self.mode, self.size)
|
||||
self.frombytes(self.fp.read(self._data_size))
|
||||
|
||||
|
||||
#
|
||||
# registry
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
from . import ImageFile, ImagePalette
|
||||
from ._binary import i8, i16be as i16, i32be as i32
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.1"
|
||||
|
||||
|
||||
|
|
@ -35,6 +37,7 @@ __version__ = "0.1"
|
|||
# this plugin, you have to import the <b>GdImageFile</b> module and
|
||||
# use the <b>GdImageFile.open</b> function.
|
||||
|
||||
|
||||
class GdImageFile(ImageFile.ImageFile):
|
||||
|
||||
format = "GD"
|
||||
|
|
@ -55,14 +58,17 @@ class GdImageFile(ImageFile.ImageFile):
|
|||
trueColorOffset = 2 if trueColor else 0
|
||||
|
||||
# transparency index
|
||||
tindex = i32(s[7+trueColorOffset:7+trueColorOffset+4])
|
||||
tindex = i32(s[7 + trueColorOffset : 7 + trueColorOffset + 4])
|
||||
if tindex < 256:
|
||||
self.info["transparency"] = tindex
|
||||
|
||||
self.palette = ImagePalette.raw("XBGR", s[7+trueColorOffset+4:7+trueColorOffset+4+256*4])
|
||||
self.palette = ImagePalette.raw(
|
||||
"XBGR", s[7 + trueColorOffset + 4 : 7 + trueColorOffset + 4 + 256 * 4]
|
||||
)
|
||||
|
||||
self.tile = [("raw", (0, 0)+self.size, 7+trueColorOffset+4+256*4,
|
||||
("L", 0, 1))]
|
||||
self.tile = [
|
||||
("raw", (0, 0) + self.size, 7 + trueColorOffset + 4 + 256 * 4, ("L", 0, 1))
|
||||
]
|
||||
|
||||
|
||||
def open(fp, mode="r"):
|
||||
|
|
|
|||
|
|
@ -24,17 +24,20 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
from . import Image, ImageFile, ImagePalette, ImageChops, ImageSequence
|
||||
from ._binary import i8, i16le as i16, o8, o16le as o16
|
||||
|
||||
import itertools
|
||||
|
||||
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
|
||||
from ._binary import i8, i16le as i16, o8, o16le as o16
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
__version__ = "0.9"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Identify/read GIF files
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:6] in [b"GIF87a", b"GIF89a"]
|
||||
|
||||
|
|
@ -43,6 +46,7 @@ def _accept(prefix):
|
|||
# Image plugin for GIF images. This plugin supports both GIF87 and
|
||||
# GIF89 images.
|
||||
|
||||
|
||||
class GifImageFile(ImageFile.ImageFile):
|
||||
|
||||
format = "GIF"
|
||||
|
|
@ -76,7 +80,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
# check if palette contains colour indices
|
||||
p = self.fp.read(3 << bits)
|
||||
for i in range(0, len(p), 3):
|
||||
if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])):
|
||||
if not (i // 3 == i8(p[i]) == i8(p[i + 1]) == i8(p[i + 2])):
|
||||
p = ImagePalette.raw("RGB", p)
|
||||
self.global_palette = self.palette = p
|
||||
break
|
||||
|
|
@ -120,6 +124,8 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
if not self._seek_check(frame):
|
||||
return
|
||||
if frame < self.__frame:
|
||||
if frame != 0:
|
||||
self.im = None
|
||||
self._seek(0)
|
||||
|
||||
last_frame = self.__frame
|
||||
|
|
@ -164,6 +170,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
self.im.paste(self.dispose, self.dispose_extent)
|
||||
|
||||
from copy import copy
|
||||
|
||||
self.palette = copy(self.global_palette)
|
||||
|
||||
info = {}
|
||||
|
|
@ -201,7 +208,13 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
#
|
||||
# comment extension
|
||||
#
|
||||
info["comment"] = block
|
||||
while block:
|
||||
if "comment" in info:
|
||||
info["comment"] += block
|
||||
else:
|
||||
info["comment"] = block
|
||||
block = self.data()
|
||||
continue
|
||||
elif i8(s) == 255:
|
||||
#
|
||||
# application extension
|
||||
|
|
@ -223,6 +236,8 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
# extent
|
||||
x0, y0 = i16(s[0:]), i16(s[2:])
|
||||
x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
|
||||
if x1 > self.size[0] or y1 > self.size[1]:
|
||||
self._size = max(x1, self.size[0]), max(y1, self.size[1])
|
||||
self.dispose_extent = x0, y0, x1, y1
|
||||
flags = i8(s[8])
|
||||
|
||||
|
|
@ -230,16 +245,14 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
|
||||
if flags & 128:
|
||||
bits = (flags & 7) + 1
|
||||
self.palette =\
|
||||
ImagePalette.raw("RGB", self.fp.read(3 << bits))
|
||||
self.palette = ImagePalette.raw("RGB", self.fp.read(3 << bits))
|
||||
|
||||
# image data
|
||||
bits = i8(self.fp.read(1))
|
||||
self.__offset = self.fp.tell()
|
||||
self.tile = [("gif",
|
||||
(x0, y0, x1, y1),
|
||||
self.__offset,
|
||||
(bits, interlace))]
|
||||
self.tile = [
|
||||
("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace))
|
||||
]
|
||||
break
|
||||
|
||||
else:
|
||||
|
|
@ -252,8 +265,8 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
self.dispose = None
|
||||
elif self.disposal_method == 2:
|
||||
# replace with background colour
|
||||
self.dispose = Image.core.fill("P", self.size,
|
||||
self.info["background"])
|
||||
Image._decompression_bomb_check(self.size)
|
||||
self.dispose = Image.core.fill("P", self.size, self.info["background"])
|
||||
else:
|
||||
# replace with previous contents
|
||||
if self.im:
|
||||
|
|
@ -291,20 +304,25 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
# we do this by pasting the updated area onto the previous
|
||||
# frame which we then use as the current image content
|
||||
updated = self._crop(self.im, self.dispose_extent)
|
||||
self._prev_im.paste(updated, self.dispose_extent,
|
||||
updated.convert('RGBA'))
|
||||
self._prev_im.paste(updated, self.dispose_extent, updated.convert("RGBA"))
|
||||
self.im = self._prev_im
|
||||
self._prev_im = self.im.copy()
|
||||
|
||||
def _close__fp(self):
|
||||
try:
|
||||
if self.__fp != self.fp:
|
||||
self.__fp.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
self.__fp = None
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Write GIF files
|
||||
|
||||
|
||||
RAWMODE = {
|
||||
"1": "L",
|
||||
"L": "L",
|
||||
"P": "P"
|
||||
}
|
||||
RAWMODE = {"1": "L", "L": "L", "P": "P"}
|
||||
|
||||
|
||||
def _normalize_mode(im, initial_call=False):
|
||||
|
|
@ -355,19 +373,23 @@ def _normalize_palette(im, palette, info):
|
|||
if isinstance(palette, (bytes, bytearray, list)):
|
||||
source_palette = bytearray(palette[:768])
|
||||
if isinstance(palette, ImagePalette.ImagePalette):
|
||||
source_palette = bytearray(itertools.chain.from_iterable(
|
||||
zip(palette.palette[:256],
|
||||
palette.palette[256:512],
|
||||
palette.palette[512:768])))
|
||||
source_palette = bytearray(
|
||||
itertools.chain.from_iterable(
|
||||
zip(
|
||||
palette.palette[:256],
|
||||
palette.palette[256:512],
|
||||
palette.palette[512:768],
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if im.mode == "P":
|
||||
if not source_palette:
|
||||
source_palette = im.im.getpalette("RGB")[:768]
|
||||
else: # L-mode
|
||||
if not source_palette:
|
||||
source_palette = bytearray(i//3 for i in range(768))
|
||||
im.palette = ImagePalette.ImagePalette("RGB",
|
||||
palette=source_palette)
|
||||
source_palette = bytearray(i // 3 for i in range(768))
|
||||
im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette)
|
||||
|
||||
used_palette_colors = _get_optimize(im, info)
|
||||
if used_palette_colors is not None:
|
||||
|
|
@ -379,6 +401,8 @@ def _normalize_palette(im, palette, info):
|
|||
|
||||
def _write_single_frame(im, fp, palette):
|
||||
im_out = _normalize_mode(im, True)
|
||||
for k, v in im_out.info.items():
|
||||
im.encoderinfo.setdefault(k, v)
|
||||
im_out = _normalize_palette(im_out, palette, im.encoderinfo)
|
||||
|
||||
for s in _get_global_header(im_out, im.encoderinfo):
|
||||
|
|
@ -391,29 +415,31 @@ def _write_single_frame(im, fp, palette):
|
|||
_write_local_header(fp, im, (0, 0), flags)
|
||||
|
||||
im_out.encoderconfig = (8, get_interlace(im))
|
||||
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
|
||||
RAWMODE[im_out.mode])])
|
||||
ImageFile._save(im_out, fp, [("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])])
|
||||
|
||||
fp.write(b"\0") # end of image data
|
||||
|
||||
|
||||
def _write_multiple_frames(im, fp, palette):
|
||||
|
||||
duration = im.encoderinfo.get("duration", None)
|
||||
disposal = im.encoderinfo.get('disposal', None)
|
||||
duration = im.encoderinfo.get("duration", im.info.get("duration"))
|
||||
disposal = im.encoderinfo.get("disposal", im.info.get("disposal"))
|
||||
|
||||
im_frames = []
|
||||
frame_count = 0
|
||||
for imSequence in itertools.chain([im],
|
||||
im.encoderinfo.get("append_images", [])):
|
||||
background_im = None
|
||||
for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])):
|
||||
for im_frame in ImageSequence.Iterator(imSequence):
|
||||
# a copy is required here since seek can still mutate the image
|
||||
im_frame = _normalize_mode(im_frame.copy())
|
||||
if frame_count == 0:
|
||||
for k, v in im_frame.info.items():
|
||||
im.encoderinfo.setdefault(k, v)
|
||||
im_frame = _normalize_palette(im_frame, palette, im.encoderinfo)
|
||||
|
||||
encoderinfo = im.encoderinfo.copy()
|
||||
if isinstance(duration, (list, tuple)):
|
||||
encoderinfo['duration'] = duration[frame_count]
|
||||
encoderinfo["duration"] = duration[frame_count]
|
||||
if isinstance(disposal, (list, tuple)):
|
||||
encoderinfo["disposal"] = disposal[frame_count]
|
||||
frame_count += 1
|
||||
|
|
@ -421,45 +447,54 @@ def _write_multiple_frames(im, fp, palette):
|
|||
if im_frames:
|
||||
# delta frame
|
||||
previous = im_frames[-1]
|
||||
if _get_palette_bytes(im_frame) == \
|
||||
_get_palette_bytes(previous['im']):
|
||||
delta = ImageChops.subtract_modulo(im_frame,
|
||||
previous['im'])
|
||||
if encoderinfo.get("disposal") == 2:
|
||||
if background_im is None:
|
||||
background = _get_background(
|
||||
im,
|
||||
im.encoderinfo.get("background", im.info.get("background")),
|
||||
)
|
||||
background_im = Image.new("P", im_frame.size, background)
|
||||
background_im.putpalette(im_frames[0]["im"].palette)
|
||||
base_im = background_im
|
||||
else:
|
||||
base_im = previous["im"]
|
||||
if _get_palette_bytes(im_frame) == _get_palette_bytes(base_im):
|
||||
delta = ImageChops.subtract_modulo(im_frame, base_im)
|
||||
else:
|
||||
delta = ImageChops.subtract_modulo(
|
||||
im_frame.convert('RGB'), previous['im'].convert('RGB'))
|
||||
im_frame.convert("RGB"), base_im.convert("RGB")
|
||||
)
|
||||
bbox = delta.getbbox()
|
||||
if not bbox:
|
||||
# This frame is identical to the previous frame
|
||||
if duration:
|
||||
previous['encoderinfo']['duration'] += \
|
||||
encoderinfo['duration']
|
||||
previous["encoderinfo"]["duration"] += encoderinfo["duration"]
|
||||
continue
|
||||
else:
|
||||
bbox = None
|
||||
im_frames.append({
|
||||
'im': im_frame,
|
||||
'bbox': bbox,
|
||||
'encoderinfo': encoderinfo
|
||||
})
|
||||
im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
|
||||
|
||||
if len(im_frames) > 1:
|
||||
for frame_data in im_frames:
|
||||
im_frame = frame_data['im']
|
||||
if not frame_data['bbox']:
|
||||
im_frame = frame_data["im"]
|
||||
if not frame_data["bbox"]:
|
||||
# global header
|
||||
for s in _get_global_header(im_frame,
|
||||
frame_data['encoderinfo']):
|
||||
for s in _get_global_header(im_frame, frame_data["encoderinfo"]):
|
||||
fp.write(s)
|
||||
offset = (0, 0)
|
||||
else:
|
||||
# compress difference
|
||||
frame_data['encoderinfo']['include_color_table'] = True
|
||||
frame_data["encoderinfo"]["include_color_table"] = True
|
||||
|
||||
im_frame = im_frame.crop(frame_data['bbox'])
|
||||
offset = frame_data['bbox'][:2]
|
||||
_write_frame_data(fp, im_frame, offset, frame_data['encoderinfo'])
|
||||
im_frame = im_frame.crop(frame_data["bbox"])
|
||||
offset = frame_data["bbox"][:2]
|
||||
_write_frame_data(fp, im_frame, offset, frame_data["encoderinfo"])
|
||||
return True
|
||||
elif "duration" in im.encoderinfo and isinstance(
|
||||
im.encoderinfo["duration"], (list, tuple)
|
||||
):
|
||||
# Since multiple frames will not be written, add together the frame durations
|
||||
im.encoderinfo["duration"] = sum(im.encoderinfo["duration"])
|
||||
|
||||
|
||||
def _save_all(im, fp, filename):
|
||||
|
|
@ -467,12 +502,10 @@ def _save_all(im, fp, filename):
|
|||
|
||||
|
||||
def _save(im, fp, filename, save_all=False):
|
||||
for k, v in im.info.items():
|
||||
im.encoderinfo.setdefault(k, v)
|
||||
# header
|
||||
try:
|
||||
palette = im.encoderinfo["palette"]
|
||||
except KeyError:
|
||||
if "palette" in im.encoderinfo or "palette" in im.info:
|
||||
palette = im.encoderinfo.get("palette", im.info.get("palette"))
|
||||
else:
|
||||
palette = None
|
||||
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
|
||||
|
||||
|
|
@ -519,7 +552,7 @@ def _write_local_header(fp, im, offset, flags):
|
|||
else:
|
||||
duration = 0
|
||||
|
||||
disposal = int(im.encoderinfo.get('disposal', 0))
|
||||
disposal = int(im.encoderinfo.get("disposal", 0))
|
||||
|
||||
if transparent_color_exists or duration != 0 or disposal:
|
||||
packed_flag = 1 if transparent_color_exists else 0
|
||||
|
|
@ -527,48 +560,53 @@ def _write_local_header(fp, im, offset, flags):
|
|||
if not transparent_color_exists:
|
||||
transparency = 0
|
||||
|
||||
fp.write(b"!" +
|
||||
o8(249) + # extension intro
|
||||
o8(4) + # length
|
||||
o8(packed_flag) + # packed fields
|
||||
o16(duration) + # duration
|
||||
o8(transparency) + # transparency index
|
||||
o8(0))
|
||||
fp.write(
|
||||
b"!"
|
||||
+ o8(249) # extension intro
|
||||
+ o8(4) # length
|
||||
+ o8(packed_flag) # packed fields
|
||||
+ o16(duration) # duration
|
||||
+ o8(transparency) # transparency index
|
||||
+ o8(0)
|
||||
)
|
||||
|
||||
if "comment" in im.encoderinfo and \
|
||||
1 <= len(im.encoderinfo["comment"]) <= 255:
|
||||
fp.write(b"!" +
|
||||
o8(254) + # extension intro
|
||||
o8(len(im.encoderinfo["comment"])) +
|
||||
im.encoderinfo["comment"] +
|
||||
o8(0))
|
||||
if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]):
|
||||
fp.write(b"!" + o8(254)) # extension intro
|
||||
for i in range(0, len(im.encoderinfo["comment"]), 255):
|
||||
subblock = im.encoderinfo["comment"][i : i + 255]
|
||||
fp.write(o8(len(subblock)) + subblock)
|
||||
fp.write(o8(0))
|
||||
if "loop" in im.encoderinfo:
|
||||
number_of_loops = im.encoderinfo["loop"]
|
||||
fp.write(b"!" +
|
||||
o8(255) + # extension intro
|
||||
o8(11) +
|
||||
b"NETSCAPE2.0" +
|
||||
o8(3) +
|
||||
o8(1) +
|
||||
o16(number_of_loops) + # number of loops
|
||||
o8(0))
|
||||
include_color_table = im.encoderinfo.get('include_color_table')
|
||||
fp.write(
|
||||
b"!"
|
||||
+ o8(255) # extension intro
|
||||
+ o8(11)
|
||||
+ b"NETSCAPE2.0"
|
||||
+ o8(3)
|
||||
+ o8(1)
|
||||
+ o16(number_of_loops) # number of loops
|
||||
+ o8(0)
|
||||
)
|
||||
include_color_table = im.encoderinfo.get("include_color_table")
|
||||
if include_color_table:
|
||||
palette_bytes = _get_palette_bytes(im)
|
||||
color_table_size = _get_color_table_size(palette_bytes)
|
||||
if color_table_size:
|
||||
flags = flags | 128 # local color table flag
|
||||
flags = flags | 128 # local color table flag
|
||||
flags = flags | color_table_size
|
||||
|
||||
fp.write(b"," +
|
||||
o16(offset[0]) + # offset
|
||||
o16(offset[1]) +
|
||||
o16(im.size[0]) + # size
|
||||
o16(im.size[1]) +
|
||||
o8(flags)) # flags
|
||||
fp.write(
|
||||
b","
|
||||
+ o16(offset[0]) # offset
|
||||
+ o16(offset[1])
|
||||
+ o16(im.size[0]) # size
|
||||
+ o16(im.size[1])
|
||||
+ o8(flags) # flags
|
||||
)
|
||||
if include_color_table and color_table_size:
|
||||
fp.write(_get_header_palette(palette_bytes))
|
||||
fp.write(o8(8)) # bits
|
||||
fp.write(o8(8)) # bits
|
||||
|
||||
|
||||
def _save_netpbm(im, fp, filename):
|
||||
|
|
@ -582,21 +620,23 @@ def _save_netpbm(im, fp, filename):
|
|||
|
||||
import os
|
||||
from subprocess import Popen, check_call, PIPE, CalledProcessError
|
||||
file = im._dump()
|
||||
|
||||
with open(filename, 'wb') as f:
|
||||
tempfile = im._dump()
|
||||
|
||||
with open(filename, "wb") as f:
|
||||
if im.mode != "RGB":
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
check_call(["ppmtogif", file], stdout=f, stderr=devnull)
|
||||
with open(os.devnull, "wb") as devnull:
|
||||
check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull)
|
||||
else:
|
||||
# Pipe ppmquant output into ppmtogif
|
||||
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
|
||||
quant_cmd = ["ppmquant", "256", file]
|
||||
# "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename)
|
||||
quant_cmd = ["ppmquant", "256", tempfile]
|
||||
togif_cmd = ["ppmtogif"]
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
with open(os.devnull, "wb") as devnull:
|
||||
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull)
|
||||
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
|
||||
stdout=f, stderr=devnull)
|
||||
togif_proc = Popen(
|
||||
togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=devnull
|
||||
)
|
||||
|
||||
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
|
||||
quant_proc.stdout.close()
|
||||
|
|
@ -610,7 +650,7 @@ def _save_netpbm(im, fp, filename):
|
|||
raise CalledProcessError(retcode, togif_cmd)
|
||||
|
||||
try:
|
||||
os.unlink(file)
|
||||
os.unlink(tempfile)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
|
@ -642,7 +682,7 @@ def _get_optimize(im, info):
|
|||
# * If we have a 'large' image, the palette is in the noise.
|
||||
|
||||
# create the new palette if not every color is used
|
||||
optimise = _FORCE_OPTIMIZE or im.mode == 'L'
|
||||
optimise = _FORCE_OPTIMIZE or im.mode == "L"
|
||||
if optimise or im.width * im.height < 512 * 512:
|
||||
# check which colors are used
|
||||
used_palette_colors = []
|
||||
|
|
@ -650,18 +690,23 @@ def _get_optimize(im, info):
|
|||
if count:
|
||||
used_palette_colors.append(i)
|
||||
|
||||
if optimise or (len(used_palette_colors) <= 128 and
|
||||
max(used_palette_colors) > len(used_palette_colors)):
|
||||
if optimise or (
|
||||
len(used_palette_colors) <= 128
|
||||
and max(used_palette_colors) > len(used_palette_colors)
|
||||
):
|
||||
return used_palette_colors
|
||||
|
||||
|
||||
def _get_color_table_size(palette_bytes):
|
||||
# calculate the palette size for the header
|
||||
import math
|
||||
color_table_size = int(math.ceil(math.log(len(palette_bytes)//3, 2)))-1
|
||||
if color_table_size < 0:
|
||||
color_table_size = 0
|
||||
return color_table_size
|
||||
|
||||
if not palette_bytes:
|
||||
return 0
|
||||
elif len(palette_bytes) < 9:
|
||||
return 1
|
||||
else:
|
||||
return int(math.ceil(math.log(len(palette_bytes) // 3, 2))) - 1
|
||||
|
||||
|
||||
def _get_header_palette(palette_bytes):
|
||||
|
|
@ -676,7 +721,7 @@ def _get_header_palette(palette_bytes):
|
|||
|
||||
# add the missing amount of bytes
|
||||
# the palette has to be 2<<n in size
|
||||
actual_target_size_diff = (2 << color_table_size) - len(palette_bytes)//3
|
||||
actual_target_size_diff = (2 << color_table_size) - len(palette_bytes) // 3
|
||||
if actual_target_size_diff > 0:
|
||||
palette_bytes += o8(0) * 3 * actual_target_size_diff
|
||||
return palette_bytes
|
||||
|
|
@ -692,6 +737,18 @@ def _get_palette_bytes(im):
|
|||
return im.palette.palette
|
||||
|
||||
|
||||
def _get_background(im, infoBackground):
|
||||
background = 0
|
||||
if infoBackground:
|
||||
background = infoBackground
|
||||
if isinstance(background, tuple):
|
||||
# WebPImagePlugin stores an RGBA value in info["background"]
|
||||
# So it must be converted to the same format as GifImagePlugin's
|
||||
# info["background"] - a global color table index
|
||||
background = im.palette.getcolor(background)
|
||||
return background
|
||||
|
||||
|
||||
def _get_global_header(im, info):
|
||||
"""Return a list of strings representing a GIF header"""
|
||||
|
||||
|
|
@ -701,9 +758,9 @@ def _get_global_header(im, info):
|
|||
version = b"87a"
|
||||
for extensionKey in ["transparency", "duration", "loop", "comment"]:
|
||||
if info and extensionKey in info:
|
||||
if ((extensionKey == "duration" and info[extensionKey] == 0) or
|
||||
(extensionKey == "comment" and
|
||||
not (1 <= len(info[extensionKey]) <= 255))):
|
||||
if (extensionKey == "duration" and info[extensionKey] == 0) or (
|
||||
extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255)
|
||||
):
|
||||
continue
|
||||
version = b"89a"
|
||||
break
|
||||
|
|
@ -711,24 +768,23 @@ def _get_global_header(im, info):
|
|||
if im.info.get("version") == b"89a":
|
||||
version = b"89a"
|
||||
|
||||
background = _get_background(im, info.get("background"))
|
||||
|
||||
palette_bytes = _get_palette_bytes(im)
|
||||
color_table_size = _get_color_table_size(palette_bytes)
|
||||
|
||||
background = info["background"] if "background" in info else 0
|
||||
|
||||
return [
|
||||
b"GIF"+version + # signature + version
|
||||
o16(im.size[0]) + # canvas width
|
||||
o16(im.size[1]), # canvas height
|
||||
|
||||
b"GIF" # signature
|
||||
+ version # version
|
||||
+ o16(im.size[0]) # canvas width
|
||||
+ o16(im.size[1]), # canvas height
|
||||
# Logical Screen Descriptor
|
||||
# size of global color table + global color table flag
|
||||
o8(color_table_size + 128), # packed fields
|
||||
o8(color_table_size + 128), # packed fields
|
||||
# background + reserved/aspect
|
||||
o8(background) + o8(0),
|
||||
|
||||
# Global Color Table
|
||||
_get_header_palette(palette_bytes)
|
||||
_get_header_palette(palette_bytes),
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -739,13 +795,15 @@ def _write_frame_data(fp, im_frame, offset, params):
|
|||
# local image header
|
||||
_write_local_header(fp, im_frame, offset, 0)
|
||||
|
||||
ImageFile._save(im_frame, fp, [("gif", (0, 0)+im_frame.size, 0,
|
||||
RAWMODE[im_frame.mode])])
|
||||
ImageFile._save(
|
||||
im_frame, fp, [("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])]
|
||||
)
|
||||
|
||||
fp.write(b"\0") # end of image data
|
||||
finally:
|
||||
del im_frame.encoderinfo
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Legacy GIF utilities
|
||||
|
||||
|
|
@ -794,6 +852,7 @@ def getdata(im, offset=(0, 0), **params):
|
|||
:returns: List of Bytes containing gif encoded frame data
|
||||
|
||||
"""
|
||||
|
||||
class Collector(object):
|
||||
data = []
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
from math import pi, log, sin, sqrt
|
||||
from math import log, pi, sin, sqrt
|
||||
|
||||
from ._binary import o8
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
|
@ -72,7 +73,7 @@ class GradientFile(object):
|
|||
|
||||
for i in range(entries):
|
||||
|
||||
x = i / float(entries-1)
|
||||
x = i / float(entries - 1)
|
||||
|
||||
while x1 < x:
|
||||
ix += 1
|
||||
|
|
@ -100,8 +101,8 @@ class GradientFile(object):
|
|||
##
|
||||
# File handler for GIMP's gradient format.
|
||||
|
||||
class GimpGradientFile(GradientFile):
|
||||
|
||||
class GimpGradientFile(GradientFile):
|
||||
def __init__(self, fp):
|
||||
|
||||
if fp.readline()[:13] != b"GIMP Gradient":
|
||||
|
|
|
|||
|
|
@ -15,31 +15,30 @@
|
|||
#
|
||||
|
||||
import re
|
||||
from ._binary import o8
|
||||
|
||||
from ._binary import o8
|
||||
|
||||
##
|
||||
# File handler for GIMP's palette format.
|
||||
|
||||
|
||||
class GimpPaletteFile(object):
|
||||
|
||||
rawmode = "RGB"
|
||||
|
||||
def __init__(self, fp):
|
||||
|
||||
self.palette = [o8(i)*3 for i in range(256)]
|
||||
self.palette = [o8(i) * 3 for i in range(256)]
|
||||
|
||||
if fp.readline()[:12] != b"GIMP Palette":
|
||||
raise SyntaxError("not a GIMP palette file")
|
||||
|
||||
i = 0
|
||||
|
||||
while i <= 255:
|
||||
for i in range(256):
|
||||
|
||||
s = fp.readline()
|
||||
|
||||
if not s:
|
||||
break
|
||||
|
||||
# skip fields and comment lines
|
||||
if re.match(br"\w+:|#", s):
|
||||
continue
|
||||
|
|
@ -50,10 +49,7 @@ class GimpPaletteFile(object):
|
|||
if len(v) != 3:
|
||||
raise ValueError("bad palette entry")
|
||||
|
||||
if 0 <= i <= 255:
|
||||
self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])
|
||||
|
||||
i += 1
|
||||
self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])
|
||||
|
||||
self.palette = b"".join(self.palette)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ def register_handler(handler):
|
|||
# --------------------------------------------------------------------
|
||||
# Image adapter
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[0:4] == b"GRIB" and i8(prefix[7]) == 1
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue