Merge branch 'feature/mstsc' of github.com:tp4a/teleport into feature/mstsc

pull/236/head
Apex Liu 2019-11-25 20:25:59 +08:00
commit f99d39e2fa
462 changed files with 40711 additions and 23271 deletions

6
.gitignore vendored
View File

@ -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

View File

@ -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" />

View File

@ -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")

View File

@ -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"

View File

@ -64,6 +64,9 @@ def main():
elif x == 'a':
clean_everything()
continue
elif x == 'e':
clean_external()
continue
try:
x = int(x)
@ -117,6 +120,27 @@ def clean_everything():
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libuv.a'))
def clean_external():
#utils.remove(os.path.join(env.root_path, 'out'))
utils.remove(os.path.join(env.root_path, 'external', 'jsoncpp'))
utils.remove(os.path.join(env.root_path, 'external', 'libuv'))
utils.remove(os.path.join(env.root_path, 'external', 'mbedtls'))
utils.remove(os.path.join(env.root_path, 'external', 'mongoose'))
#utils.remove(os.path.join(env.root_path, 'external', 'openssl'))
#utils.remove(os.path.join(env.root_path, 'external', 'python'))
#utils.remove(os.path.join(env.root_path, 'external', 'libssh-win-static', 'lib'))
#utils.remove(os.path.join(env.root_path, 'external', 'libssh-win-static', 'src'))
#utils.remove(os.path.join(env.root_path, 'external', 'linux', 'tmp'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedcrypto.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedtls.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libmbedx509.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libsqlite3.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libssh.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libssh_threads.a'))
utils.remove(os.path.join(env.root_path, 'external', 'linux', 'release', 'lib', 'libuv.a'))
def do_opt(opt):
arg = ''
@ -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.'))

View File

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

View File

@ -27,19 +27,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,6 +123,7 @@ 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='')
@ -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()

View File

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

View File

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

View File

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

View File

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

View File

@ -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;

View File

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

View File

@ -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,8 +149,8 @@ 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());
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;
@ -172,6 +160,7 @@ void MainWindow::paintEvent(QPaintEvent *e)
painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h);
}
}
}
void MainWindow::pause() {
if(m_play_state != PLAY_STATE_RUNNING)
@ -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);
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_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((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);
//}
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));
}

View File

@ -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

View File

@ -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;
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;
// 关键帧索引

View File

@ -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();

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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,86 +54,12 @@ 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) {
void UpdateData::set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p) {
m_data_type = TYPE_POINTER;
if(data.size() != sizeof(TS_RECORD_RDP_POINTER))
return false;
m_time_ms = ts;
m_pointer = new TS_RECORD_RDP_POINTER;
memcpy(m_pointer, data.data(), sizeof(TS_RECORD_RDP_POINTER));
return true;
memcpy(m_pointer, p, sizeof(TS_RECORD_RDP_POINTER));
}
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::alloc_data(uint32_t len) {
if(m_data_buf)

View File

@ -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 {

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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>

View File

@ -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;

View File

@ -80,9 +80,11 @@ bool ExThreadBase::stop(void) {
return false;
}
#else
if(m_handle != 0) {
if (pthread_join(m_handle, NULL) != 0) {
return false;
}
}
#endif
return true;

View File

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

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

22
external/version.ini vendored
View File

@ -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

View File

@ -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.

View File

@ -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())
{

View File

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

View File

@ -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,10 +276,17 @@ 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))
//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;
@ -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);

View File

@ -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)
@ -29,13 +30,16 @@ 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_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()

View File

@ -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();
// #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);

View File

@ -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

View File

@ -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
// 直接转发数据到客户端
//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;

View File

@ -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
@ -12,10 +12,16 @@
#ifdef EX_OS_WIN32
# ifdef EX_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, "libcrypto.lib")
// # pragma comment(lib, "libeay32.lib")
# pragma comment(lib, "ws2_32.lib")
# pragma comment(lib, "crypt32.lib")
#endif

View File

@ -1,4 +1,4 @@
#include "ssh_proxy.h"
#include "ssh_proxy.h"
#include "tpp_env.h"
#include <teleport_const.h>
@ -46,9 +46,14 @@ 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))
//if (!jreader.parse(param, jp))
if (!jreader->parse(str_json_begin, param + strlen(param), &jp, &err))
return TPE_JSON_FORMAT;
if (!jp.isObject())
@ -68,9 +73,15 @@ 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))
//if (!jreader.parse(param, jp))
if (!jreader->parse(str_json_begin, param + strlen(param), &jp, &err))
return TPE_JSON_FORMAT;
if (!jp.isArray())

View File

@ -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>

View File

@ -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)

View File

@ -1,4 +1,4 @@
#include "telnet_proxy.h"
#include "telnet_proxy.h"
#include "tpp_env.h"
#include <teleport_const.h>
@ -40,10 +40,16 @@ 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))
ex_astr err;
Json::Value jp;
if (!jreader->parse(param, param + strlen(param), &jp, &err))
return TPE_JSON_FORMAT;
if (!jp.isObject())
@ -62,10 +68,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::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))
ex_astr err;
Json::Value jp;
if (!jreader->parse(param, param + strlen(param), &jp, &err))
return TPE_JSON_FORMAT;
if (!jp.isArray())

View File

@ -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 ()

View File

@ -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")

View File

@ -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;
}

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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 = []
@ -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)

View File

@ -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):
@ -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
@ -177,9 +172,7 @@ def decode_dxt5(data):
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
@ -409,19 +398,15 @@ class BLP2Decoder(_BLPBaseDecoder):
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt5(self.fd.read(linesize)):
data += d
else:
raise BLPFormatError("Unsupported alpha encoding %r" % (
self._blp_alpha_encoding
))
else:
raise BLPFormatError(
"Unknown BLP encoding %r" % (self._blp_encoding)
"Unsupported alpha encoding %r" % (self._blp_alpha_encoding)
)
else:
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))

View File

@ -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])
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
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:
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']):
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
# 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["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'])
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),
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)]
(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]
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']),
(
"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_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,30 +316,36 @@ 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)
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)
@ -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")

View File

@ -27,6 +27,7 @@ def register_handler(handler):
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"

View File

@ -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.

View File

@ -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"

View File

@ -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)

View File

@ -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,14 +117,23 @@ 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]
self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
else:
data_start = header_size + 4
n = 0
if fourcc == b"DXT1":
@ -142,8 +150,7 @@ class DdsImageFile(ImageFile.ImageFile):
# 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):
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:
@ -151,15 +158,13 @@ class DdsImageFile(ImageFile.ImageFile):
self.im_info["gamma"] = 1 / 2.2
n = 7
else:
raise NotImplementedError("Unimplemented DXGI format %d" %
(dxgi_format))
raise NotImplementedError(
"Unimplemented DXGI format %d" % (dxgi_format)
)
else:
raise NotImplementedError("Unimplemented pixel format %r" %
(fourcc))
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

View File

@ -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,9 +108,9 @@ 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
@ -116,7 +124,8 @@ def Ghostscript(tile, size, fp, scale=1):
f.write(s)
# Build Ghostscript command
command = ["gs",
command = [
"gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
@ -125,26 +134,28 @@ def Ghostscript(tile, size, fp, scale=1):
"-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)
"-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'):
if sys.platform.startswith("win"):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.check_call(command, stdin=devnull, stdout=devnull,
startupinfo=startupinfo)
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
@ -405,6 +419,7 @@ def _save(im, fp, filename, eps=1):
if wrapped_fp:
fp.detach()
#
# --------------------------------------------------------------------

View File

@ -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",
}
##

View File

@ -23,6 +23,7 @@ def register_handler(handler):
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter

View File

@ -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
@ -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)
@ -154,6 +163,15 @@ class FliImageFile(ImageFile.ImageFile):
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

View File

@ -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]

View File

@ -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)
@ -158,14 +161,26 @@ class FpxImageFile(ImageFile.ImageFile):
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)
#
# --------------------------------------------------------------------

View File

@ -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)

View File

@ -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
else:
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

View File

@ -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"
@ -59,10 +62,13 @@ class GdImageFile(ImageFile.ImageFile):
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"):

View File

@ -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"
@ -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
#
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,10 +373,15 @@ 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],
source_palette = bytearray(
itertools.chain.from_iterable(
zip(
palette.palette[:256],
palette.palette[256:512],
palette.palette[512:768])))
palette.palette[512:768],
)
)
)
if im.mode == "P":
if not source_palette:
@ -366,8 +389,7 @@ def _normalize_palette(im, palette, info):
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)
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,32 +560,35 @@ 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)
@ -560,12 +596,14 @@ def _write_local_header(fp, im, offset, flags):
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
@ -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):
@ -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
# 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 = []

View File

@ -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
# --------------------------------------------------------------------
@ -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":

View File

@ -15,12 +15,13 @@
#
import re
from ._binary import o8
from ._binary import o8
##
# File handler for GIMP's palette format.
class GimpPaletteFile(object):
rawmode = "RGB"
@ -32,14 +33,12 @@ class GimpPaletteFile(object):
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,11 +49,8 @@ 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 = b"".join(self.palette)
def getpalette(self):

View File

@ -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