Compare commits

...

116 Commits

Author SHA1 Message Date
Jeremy Bícha
1cc8082c0a releasing package freerdp2 version 2.11.7+dfsg1-6 2024-12-18 10:11:19 -05:00
Adrien Nader
b607683919 * d/tests/connect: use /cert-tofu to avoid errors with proxies 2024-12-18 10:09:53 -05:00
Jeremy Bícha
dc059a2d39 releasing package freerdp2 version 2.11.7+dfsg1-5 2024-12-16 22:57:31 -05:00
Jeremy Bícha
cc9c0a66c8 autopkgtest: add Depends: ca-certificates 2024-12-16 22:15:07 -05:00
Jeremy Bícha
4d650bf630 releasing package freerdp2 version 2.11.7+dfsg1-4 2024-10-04 16:22:31 -04:00
Jeremy Bícha
48be888513 Replace autopkgtests with the tests used by freerdp3 2024-10-04 16:21:27 -04:00
Jeremy Bícha
6187297b6e releasing package freerdp2 version 2.11.7+dfsg1-3 2024-10-03 11:12:15 -04:00
Jeremy Bícha
47c0f4de25 Apply CVE-2024-32661 patch from Ubuntu 2024-10-03 11:10:17 -04:00
Bernhard Übelacker
5685a9eca1 Apply multiple fixes to autopkgtests
Closes: #1079025
2024-10-03 09:54:40 -04:00
Jeremy Bícha
40ac300cbc Remove obsolete time transition lintian overrides 2024-10-03 09:41:37 -04:00
Sébastien Noel
4930b843f5 Add patch to fix build with ffmpeg 7
Closes: #1072413
2024-10-03 09:40:14 -04:00
Jeremy Bícha
66e976dd46 Cherry-pick several patches to fix build with gcc-14
Closes: #1074969
2024-10-03 09:39:57 -04:00
Mike Gabriel
7a0cfc871a upload to unstable (debian/2.11.7+dfsg1-2) 2024-07-20 15:37:51 +02:00
Mike Gabriel
d8fe268285 debian/tests/control: Add xauth. Fix tests on Debian, where xvfb does not pull-in xauth as dependency (other than in Ubuntu). 2024-07-20 15:36:23 +02:00
Mike Gabriel
22e2e0315b upload to unstable (debian/2.11.7+dfsg1-1) 2024-07-15 16:51:34 +02:00
Mike Gabriel
f78f13ecf5 Update upstream source from tag 'upstream/2.11.7+dfsg1'
Update to upstream version '2.11.7+dfsg1'
with Debian dir 64ec704343
2024-07-15 16:44:16 +02:00
Mike Gabriel
ce01c78202 New upstream version 2.11.7+dfsg1 2024-07-15 16:44:11 +02:00
Mike Gabriel
3d3a1415b7 prepare new upstream release (v2.11.7) 2024-07-15 16:43:36 +02:00
Nathan Pratta Teodosio
df721c20b4 Add autopkgtest to test whether a client can connect
to an XRDP server via freerdp2 and that the login screen shows up

Closes: #1073156
LP: #2060976

Gbp-Dch: Full
2024-06-13 13:26:42 -04:00
Mike Gabriel
eeaac3f057 upload to unstable (debian/2.11.5+dfsg1-1) 2024-03-25 16:33:39 +01:00
Mike Gabriel
2d6a9f3b9b debian/control: Switch from pkg-config to pkgconf. Thanks, lintian. 2024-03-25 16:26:12 +01:00
Mike Gabriel
6fe4ba798f Update upstream source from tag 'upstream/2.11.5+dfsg1'
Update to upstream version '2.11.5+dfsg1'
with Debian dir 3a144157ec
2024-03-25 16:13:55 +01:00
Mike Gabriel
995fbfcdd7 New upstream version 2.11.5+dfsg1 2024-03-25 16:13:49 +01:00
Mike Gabriel
a0544fd1f6 debian/watch: Adjust so we only see 2.x release. 2024-03-25 16:13:02 +01:00
Mike Gabriel
1c5b0b32c3 prepare new upstream release (v2.11.5) 2024-03-25 16:08:59 +01:00
Lukas Märdian
382aa6b66a NMU to experimental (debian/2.11.2+dfsg1-1.1~exp2) 2024-03-25 16:04:28 +01:00
Lukas Märdian
bdad2ddbfa NMU to experimental (debian/2.11.2+dfsg1-1.1~exp1) 2024-03-25 16:04:07 +01:00
Mike Gabriel
da7dfbb2d6 upload to unstable (debian/2.11.2+dfsg1-1) 2023-10-01 23:52:38 +02:00
Mike Gabriel
682454acf9 debian/control: Add B-D: libkrb5-dev. 2023-10-01 23:36:38 +02:00
Mike Gabriel
577316d7c0 debian/rules: Add -DWITH_KERBEROS=ON configure option. (Closes: #1036095). 2023-10-01 23:34:51 +02:00
Mike Gabriel
928c8abca7 debian/watch: Rework file. Find all released versions of freerdp2. (Closes: #1053317). 2023-10-01 23:33:48 +02:00
Mike Gabriel
5bbfde9671 debian/patches: Drop 0001_fix_ftbfs_1041377.patch. Applied upstream. 2023-10-01 23:21:11 +02:00
Mike Gabriel
057b4985ff Update upstream source from tag 'upstream/2.11.2+dfsg1'
Update to upstream version '2.11.2+dfsg1'
with Debian dir d56d512921
2023-10-01 23:20:10 +02:00
Mike Gabriel
4bb5bc077f New upstream version 2.11.2+dfsg1 2023-10-01 23:19:42 +02:00
Mike Gabriel
3d5ae03c6a prepare new upstream release (v2.11.2) 2023-10-01 23:19:33 +02:00
Héctor Orón Martínez
d260e3c398 include upstream fix for FTBFS with FFmpeg 6.0
Fixes DebianBug #1041377

Signed-off-by: Héctor Orón Martínez <zumbi@debian.org>
2023-08-04 11:30:55 +02:00
Mike Gabriel
3a365cab32 upload to unstable (debian/2.10.0+dfsg1-1) 2023-02-26 22:47:29 +01:00
Mike Gabriel
a98f5e3e7c debian/libfreerdp2-2.symbols: Update symbols. 2023-02-26 22:46:13 +01:00
Mike Gabriel
67a51fdf54 debian/copyright: Update copyright attributions. 2023-02-26 22:30:32 +01:00
Mike Gabriel
05945da048 debian/copyright: Update auto-generated copyright.in file. 2023-02-26 22:27:57 +01:00
Mike Gabriel
4781bcfcda Update upstream source from tag 'upstream/2.10.0+dfsg1'
Update to upstream version '2.10.0+dfsg1'
with Debian dir b038285fe6
2023-02-26 21:53:37 +01:00
Mike Gabriel
2d5b821574 New upstream version 2.10.0+dfsg1 2023-02-26 21:53:19 +01:00
Mike Gabriel
ab16b2e2e4 debian/control: Bump Standards-Version: to 4.6.2. No changes needed. 2023-02-26 21:51:36 +01:00
Mike Gabriel
1a6267125c prepare new upstream release (v2.10.0) 2023-02-26 21:51:08 +01:00
Mike Gabriel
ac3e8bd4ac upload to unstable (debian/2.9.0+dfsg1-1) 2022-11-28 10:14:33 +01:00
Mike Gabriel
9f4041a26f debian/*.symbols: Update .symbols files. 2022-11-28 10:03:09 +01:00
Mike Gabriel
c0ca3d2d26 debian/copyright: Update auto-generated copyright.in file. 2022-11-28 09:51:53 +01:00
Mike Gabriel
ca0db20d36 debian/copyright: Update copyright attributions. 2022-11-28 09:51:36 +01:00
Mike Gabriel
8e69b04d95 Update upstream source from tag 'upstream/2.9.0+dfsg1'
Update to upstream version '2.9.0+dfsg1'
with Debian dir c712c5d91d
2022-11-28 09:30:26 +01:00
Mike Gabriel
97496cbb22 New upstream version 2.9.0+dfsg1 2022-11-28 09:30:00 +01:00
Mike Gabriel
307d2915d6 prepare new upstream release (v2.9.0) 2022-11-28 09:29:38 +01:00
Mike Gabriel
d556b6eca0 upload to unstable (debian/2.8.1+dfsg1-1) 2022-10-12 23:34:24 +02:00
Mike Gabriel
f37e46dc33 Update upstream source from tag 'upstream/2.8.1+dfsg1'
Update to upstream version '2.8.1+dfsg1'
with Debian dir 84624410bb
2022-10-12 23:28:40 +02:00
Mike Gabriel
03a18ec27e New upstream version 2.8.1+dfsg1 2022-10-12 23:28:31 +02:00
Mike Gabriel
9cec8baad9 debian/patches: Drop 1001_amend-DumpThreadHandles-inclusion.patch. Resolved upstream. 2022-10-12 23:26:41 +02:00
Mike Gabriel
cf6d35cc6a prepare new upstream release (v2.8.1) 2022-10-12 23:26:41 +02:00
Mike Gabriel
0bae4013ec debian/changelog: post-upload update with missing item. 2022-09-21 22:30:49 +02:00
Mike Gabriel
c232eabd62 upload to unstable (debian/2.8.0+dfsg1-1) 2022-09-21 22:24:45 +02:00
Mike Gabriel
833412e07b debian/*.symbols: Update .symbols files. 2022-09-21 22:24:31 +02:00
Mike Gabriel
d332a69cd6 debian/control: Bump Standards-Version: to 4.6.1. No changes needed. 2022-09-21 22:20:48 +02:00
Mike Gabriel
ce847dee67 debian/patches/: Drop 1001_keep-symbol-DumpThreadHandles-if-debugging-is-disabled.patch. Applied upstream, but only partially. Add 1001_amend-DumpThreadHandles-inclusion.patch. Amend missing adjustment in thread.h. 2022-09-21 22:03:35 +02:00
Mike Gabriel
cfbb8ebcbd New upstream version 2.8.0+dfsg1 2022-09-21 21:49:18 +02:00
Mike Gabriel
e8d0f1c676 New upstream version 2.8.0+dfsg1 2022-08-16 23:18:01 +02:00
Mike Gabriel
f26a873894 debian/copyright: Update copyright attributions. 2022-08-16 23:04:46 +02:00
Mike Gabriel
1742e02337 debian/copyright: Update auto-generated copyright.in file. 2022-08-16 23:04:16 +02:00
Mike Gabriel
04e3d1c061 upload to unstable (debian/2.7.0+dfsg1-1) 2022-04-27 17:17:20 +02:00
Mike Gabriel
069a421a52 debian/*.symbols: Update .symbols for 2.7.0. 2022-04-27 16:55:15 +02:00
Mike Gabriel
f9cf50afd8 debian/copyright: Update copyright attributions. 2022-04-27 16:49:31 +02:00
Mike Gabriel
0caf470244 debian/copyright: Update auto-generated copyright.in file. 2022-04-27 16:49:20 +02:00
Mike Gabriel
af4000f101 Update upstream source from tag 'upstream/2.7.0+dfsg1'
Update to upstream version '2.7.0+dfsg1'
with Debian dir 85455a6037
2022-04-27 16:47:19 +02:00
Mike Gabriel
b937a3a632 New upstream version 2.7.0+dfsg1 2022-04-27 16:46:57 +02:00
Mike Gabriel
2f23bb8b95 prepare new upstream release (v2.7.0) 2022-04-27 16:46:01 +02:00
Mike Gabriel
f8793d5c6e upload to unstable (debian/2.6.1+dfsg1-3) 2022-03-08 08:43:43 +01:00
Mike Gabriel
30905452ca Revert "debian/libwinpr2-2.symbols: Update symbols."
This reverts commit 8d7afd7dd0.
2022-03-08 08:42:29 +01:00
Mike Gabriel
f726052dd4 debian/patches: Add 1001_keep-symbol-DumpThreadHandles-if-debugging-is-disabled.patch. Keep DumpThreadHandles as a symbol even if WITH_DEBUG_THREADS is OFF. 2022-03-08 08:42:29 +01:00
Mike Gabriel
c13d353c77 upload to unstable (debian/2.6.1+dfsg1-2) 2022-03-08 08:12:07 +01:00
Bernhard Miklautz
8d7afd7dd0 debian/libwinpr2-2.symbols: Update symbols. 2022-03-08 08:06:37 +01:00
Bernhard Miklautz
a90b67e6c0 debian/rules: Disable additional debug logging. (Closes: #1006683). 2022-03-08 08:01:58 +01:00
Bernhard Miklautz
0b71f55532 debian/rules: Use ffmpeg for audio decoding if available to support additional audio formats. 2022-03-08 08:01:19 +01:00
Bernhard Miklautz
11fa5f7311 debian/control: Drop unused gstreamer dependencies libgstreamer1.0-dev and
libgstreamer-plugins-base1.0-dev.
2022-03-08 07:55:34 +01:00
Mike Gabriel
53c06fa209 upload to unstable (debian/2.6.1+dfsg1-1) 2022-03-08 07:49:51 +01:00
Mike Gabriel
ce43920f22 debian/copyright: Update copyright attributions. 2022-03-08 07:41:52 +01:00
Mike Gabriel
9292e895f0 debian/copyright.in: Update auto-generated copyright.in file. 2022-03-08 07:41:33 +01:00
Mike Gabriel
834bae447c debian/patches: Drop 2001-fake-git-revision.patch. Not required anymore. 2022-03-08 07:37:38 +01:00
Mike Gabriel
8fc6ef2a66 Update upstream source from tag 'upstream/2.6.1+dfsg1'
Update to upstream version '2.6.1+dfsg1'
with Debian dir 788e1fd09a
2022-03-08 07:35:08 +01:00
Mike Gabriel
f2ecc9e1e9 New upstream version 2.6.1+dfsg1 2022-03-08 07:34:48 +01:00
Mike Gabriel
167ff6dfdc prepare new upstream release (v2.6.1) 2022-03-08 07:34:42 +01:00
Mike Gabriel
85e7b6936c upload to unstable (debian/2.6.0+dfsg1-1) 2022-02-26 22:33:45 +01:00
Mike Gabriel
85472f7e5f debian/libfreerdp-server2-2.symbols: Update symbols. 2022-02-26 22:27:38 +01:00
Mike Gabriel
f7a9ffba9f rebase copyright 2022-02-26 22:20:57 +01:00
Mike Gabriel
63d3311554 debian/copyright: Update copyright attributions. 2022-02-26 21:55:22 +01:00
Mike Gabriel
c7cf752140 debian/copyright.in: Update auto-generated copyright.in file. 2022-02-26 21:55:08 +01:00
Mike Gabriel
4ab6e978fe Update upstream source from tag 'upstream/2.6.0+dfsg1'
Update to upstream version '2.6.0+dfsg1'
with Debian dir e82f2caa6e
2022-02-26 21:44:29 +01:00
Mike Gabriel
45f744e4b0 New upstream version 2.6.0+dfsg1 2022-02-26 21:44:23 +01:00
Mike Gabriel
cf66ce7ac6 debian/copyright: Update list of files in Files-Excluded: field. 2022-02-26 21:41:23 +01:00
Mike Gabriel
0f88669b15 prepare new upstream release (v2.6.0) 2022-02-26 21:14:11 +01:00
Mike Gabriel
0547b4933a upload to unstable (debian/2.5.0+dfsg1-1) 2022-02-14 08:31:43 +01:00
Mike Gabriel
ae4ce05b6b debian/copyright: Update copyright attributions. 2022-02-14 08:25:32 +01:00
Mike Gabriel
4f40975b3a debian/copyright: Update auto-generated copyright.in file. 2022-02-14 08:15:25 +01:00
Mike Gabriel
bbef5038b1 debian/patches/2001-fake-git-revision.patch: Mark patch as non-forwardable. 2022-02-14 08:15:25 +01:00
Mike Gabriel
b049925296 New upstream version 2.5.0+dfsg1 2022-02-14 08:15:25 +01:00
Mike Gabriel
1aaad2c68f New upstream version 2.5.0+dfsg1 2022-02-14 08:03:24 +01:00
Mike Gabriel
58e356d6f6 prepare new upstreak release (v2.5.0) 2022-02-14 08:02:55 +01:00
Mike Gabriel
a73b15bc0d upload to unstable (debian/2.4.1+dfsg1-1) 2021-12-09 23:22:33 +01:00
Mike Gabriel
a375cb6a81 debian/copyright: Update copyright attributions. 2021-12-09 23:16:29 +01:00
Mike Gabriel
2558cdec15 debian/copyright: Update auto-generated copyright.in reference file. 2021-12-09 23:16:17 +01:00
Mike Gabriel
12d8c44ea9 debian/libwinpr2-2.symbols: Update symbols. 2021-12-09 23:05:23 +01:00
Mike Gabriel
b6a6ec6f80 Update upstream source from tag 'upstream/2.4.1+dfsg1'
Update to upstream version '2.4.1+dfsg1'
with Debian dir 0b5f7144f0
2021-12-09 22:57:24 +01:00
Mike Gabriel
4e5cdbea0a New upstream version 2.4.1+dfsg1 2021-12-09 22:57:15 +01:00
Mike Gabriel
65144c4ee4 prepare new upstream release (v2.4.1) 2021-12-09 22:56:48 +01:00
Mike Gabriel
90d92d21f0 debian/patches/: Drop all patches pulled in from upstream recently. All part of 2.4.0. 2021-09-13 22:33:47 +02:00
Mike Gabriel
6378eb1d42 debian/copyright: Update auto-generated copyright.in template/reference file. 2021-09-13 22:33:47 +02:00
Mike Gabriel
55feba579f debian/control: Bump Standards-Version: to 4.6.0. No changes needed. 2021-09-13 22:33:38 +02:00
Mike Gabriel
2ef323aa69 New upstream version 2.4.0+dfsg1 2021-09-13 22:33:38 +02:00
Mike Gabriel
68e1ca324f New upstream version 2.4.0+dfsg1 2021-09-13 22:31:48 +02:00
Mike Gabriel
a2fc9432ec prepare new upstream release (2.4.0) 2021-09-13 22:25:21 +02:00
476 changed files with 25776 additions and 11807 deletions

View File

@ -104,7 +104,6 @@ ForEachMacros:
... ...
Language: ObjC Language: ObjC
PointerBindsToType: false PointerBindsToType: false
ObjCSpaceAfterProperty: true
SortIncludes: false SortIncludes: false
ObjCBlockIndentWidth: 4 ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false ObjCSpaceAfterProperty: false

View File

@ -41,7 +41,6 @@ addons:
- libgsm1-dev - libgsm1-dev
- libavcodec-dev - libavcodec-dev
- libavutil-dev - libavutil-dev
- libx264-dev
- libxext-dev - libxext-dev
- ninja-build - ninja-build
- libsystemd-dev - libsystemd-dev

View File

@ -34,9 +34,9 @@ if(NOT DEFINED FREERDP_VENDOR)
set(FREERDP_VENDOR 1) set(FREERDP_VENDOR 1)
endif() endif()
set(CMAKE_COLOR_MAKEFILE ON) option(CMAKE_COLOR_MAKEFILE "colorful CMake makefile" ON)
option(CMAKE_VERBOSE_MAKEFILE "verbose CMake makefile" ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) option(CMAKE_POSITION_INDEPENDENT_CODE "build with position independent code (-fPIC or -fPIE)" ON)
# Include our extra modules # Include our extra modules
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
@ -74,6 +74,10 @@ include(InstallFreeRDPMan)
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
include(SetFreeRDPCMakeInstallDir) include(SetFreeRDPCMakeInstallDir)
if (DEFINE_NO_DEPRECATED)
add_definitions(-DDEFINE_NO_DEPRECATED)
endif()
# Soname versioning # Soname versioning
set(BUILD_NUMBER 0) set(BUILD_NUMBER 0)
if ($ENV{BUILD_NUMBER}) if ($ENV{BUILD_NUMBER})
@ -81,7 +85,7 @@ if ($ENV{BUILD_NUMBER})
endif() endif()
set(WITH_LIBRARY_VERSIONING "ON") set(WITH_LIBRARY_VERSIONING "ON")
set(RAW_VERSION_STRING "2.3.0") set(RAW_VERSION_STRING "2.11.7")
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
elseif(USE_VERSION_FROM_GIT_TAG) elseif(USE_VERSION_FROM_GIT_TAG)
@ -107,6 +111,24 @@ else()
endif() endif()
message("FREERDP_VERSION=${FREERDP_VERSION_FULL}") message("FREERDP_VERSION=${FREERDP_VERSION_FULL}")
if(EXISTS "${PROJECT_SOURCE_DIR}/.source_version" )
file(READ ${PROJECT_SOURCE_DIR}/.source_version GIT_REVISION)
string(STRIP ${GIT_REVISION} GIT_REVISION)
elseif(USE_VERSION_FROM_GIT_TAG)
git_get_exact_tag(GIT_REVISION --tags --always)
if (${GIT_REVISION} STREQUAL "n/a")
git_rev_parse (GIT_REVISION --short)
endif()
endif()
if (NOT GIT_REVISION)
set(GIT_REVISION ${FREERDP_VERSION})
endif()
message(STATUS "Git Revision ${GIT_REVISION}")
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/") set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
# Compatibility options # Compatibility options
@ -143,25 +165,11 @@ if(CCACHE AND WITH_CCACHE)
endif() endif()
endif(CCACHE AND WITH_CCACHE) endif(CCACHE AND WITH_CCACHE)
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_version" )
file(READ ${CMAKE_SOURCE_DIR}/.source_version GIT_REVISION)
string(STRIP ${GIT_REVISION} GIT_REVISION)
else()
git_get_exact_tag(GIT_REVISION --tags --always)
if (${GIT_REVISION} STREQUAL "n/a")
git_rev_parse (GIT_REVISION --short)
endif()
endif()
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
endif(CMAKE_CROSSCOMPILING) endif(CMAKE_CROSSCOMPILING)
# /Allow to search the host machine for git/ccache # /Allow to search the host machine for git/ccache
message(STATUS "Git Revision ${GIT_REVISION}")
# Turn on solution folders (2.8.4+) # Turn on solution folders (2.8.4+)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -217,7 +225,7 @@ endif()
if(MSVC) if(MSVC)
include(MSVCRuntime) include(MSVCRuntime)
if(NOT DEFINED MSVC_RUNTIME) if(NOT DEFINED MSVC_RUNTIME)
set(MSVC_RUNTIME "dynamic") set(MSVC_RUNTIME "dynamic" CACHE STRING "MSVC runtime type [dynamic|static]")
endif() endif()
if(MSVC_RUNTIME STREQUAL "static") if(MSVC_RUNTIME STREQUAL "static")
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
@ -347,7 +355,7 @@ if(NOT IOS)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
endif() endif()
if(NOT WIN32) if(NOT WIN32 AND NOT IOS)
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
@ -524,7 +532,6 @@ add_definitions(-DWINPR_EXPORTS -DFREERDP_EXPORTS)
if(NOT IOS) if(NOT IOS)
check_include_files(fcntl.h HAVE_FCNTL_H) check_include_files(fcntl.h HAVE_FCNTL_H)
check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(unistd.h HAVE_UNISTD_H)
check_include_files(execinfo.h HAVE_EXECINFO_H)
check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(inttypes.h HAVE_INTTYPES_H)
check_include_files(sys/modem.h HAVE_SYS_MODEM_H) check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
check_include_files(sys/filio.h HAVE_SYS_FILIO_H) check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
@ -532,6 +539,18 @@ if(NOT IOS)
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H) check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
check_include_files(sys/select.h HAVE_SYS_SELECT_H) check_include_files(sys/select.h HAVE_SYS_SELECT_H)
check_include_files(syslog.h HAVE_SYSLOG_H) check_include_files(syslog.h HAVE_SYSLOG_H)
check_include_files(execinfo.h HAVE_EXECINFO_HEADER)
if (HAVE_EXECINFO_HEADER)
check_symbol_exists(backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
check_symbol_exists(backtrace_symbols execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS)
check_symbol_exists(backtrace_symbols_fd execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
# Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define
# the backtrace functions. Disable detection for these cases
if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS AND HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
set(HAVE_EXECINFO_H ON)
endif()
endif()
else() else()
set(HAVE_FCNTL_H 1) set(HAVE_FCNTL_H 1)
set(HAVE_UNISTD_H 1) set(HAVE_UNISTD_H 1)
@ -590,6 +609,11 @@ if(ANDROID)
set (WITH_NEON OFF) set (WITH_NEON OFF)
endif() endif()
if(ANDROID_ABI STREQUAL arm64-v8a)
# https://github.com/android/ndk/issues/910
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
endif()
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_definitions(-DNDK_DEBUG=1) add_definitions(-DNDK_DEBUG=1)
@ -648,6 +672,7 @@ if(UNIX OR CYGWIN)
include(CheckFunctionExists) include(CheckFunctionExists)
check_function_exists(getlogin_r HAVE_GETLOGIN_R) check_function_exists(getlogin_r HAVE_GETLOGIN_R)
check_function_exists(getpwuid_r HAVE_GETPWUID_R)
else() else()
set(X11_FEATURE_TYPE "DISABLED") set(X11_FEATURE_TYPE "DISABLED")
set(WAYLAND_FEATURE_TYPE "DISABLED") set(WAYLAND_FEATURE_TYPE "DISABLED")
@ -705,7 +730,7 @@ set(FFMPEG_FEATURE_DESCRIPTION "multimedia redirection, audio and video playback
set(VAAPI_FEATURE_TYPE "OPTIONAL") set(VAAPI_FEATURE_TYPE "OPTIONAL")
set(VAAPI_FEATURE_PURPOSE "multimedia") set(VAAPI_FEATURE_PURPOSE "multimedia")
set(VAAPI_FEATURE_DESCRIPTION "VA-API hardware acceleration for video playback") set(VAAPI_FEATURE_DESCRIPTION "[experimental] VA-API hardware acceleration for video playback")
set(IPP_FEATURE_TYPE "OPTIONAL") set(IPP_FEATURE_TYPE "OPTIONAL")
set(IPP_FEATURE_PURPOSE "performance") set(IPP_FEATURE_PURPOSE "performance")
@ -715,17 +740,13 @@ set(JPEG_FEATURE_TYPE "OPTIONAL")
set(JPEG_FEATURE_PURPOSE "codec") set(JPEG_FEATURE_PURPOSE "codec")
set(JPEG_FEATURE_DESCRIPTION "use JPEG library") set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
set(X264_FEATURE_TYPE "OPTIONAL")
set(X264_FEATURE_PURPOSE "codec")
set(X264_FEATURE_DESCRIPTION "use x264 library")
set(OPENH264_FEATURE_TYPE "OPTIONAL") set(OPENH264_FEATURE_TYPE "OPTIONAL")
set(OPENH264_FEATURE_PURPOSE "codec") set(OPENH264_FEATURE_PURPOSE "codec")
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library") set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
set(OPENCL_FEATURE_TYPE "OPTIONAL") set(OPENCL_FEATURE_TYPE "OPTIONAL")
set(OPENCL_FEATURE_PURPOSE "codec") set(OPENCL_FEATURE_PURPOSE "codec")
set(OPENCL_FEATURE_DESCRIPTION "use OpenCL library") set(OPENCL_FEATURE_DESCRIPTION "[experimental] use OpenCL library")
set(GSM_FEATURE_TYPE "OPTIONAL") set(GSM_FEATURE_TYPE "OPTIONAL")
set(GSM_FEATURE_PURPOSE "codec") set(GSM_FEATURE_PURPOSE "codec")
@ -741,7 +762,7 @@ set(FAAD2_FEATURE_DESCRIPTION "FAAD2 AAC audio codec library")
set(FAAC_FEATURE_TYPE "OPTIONAL") set(FAAC_FEATURE_TYPE "OPTIONAL")
set(FAAC_FEATURE_PURPOSE "codec") set(FAAC_FEATURE_PURPOSE "codec")
set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library") set(FAAC_FEATURE_DESCRIPTION "[experimental] FAAC AAC audio codec library")
set(SOXR_FEATURE_TYPE "OPTIONAL") set(SOXR_FEATURE_TYPE "OPTIONAL")
set(SOXR_FEATURE_PURPOSE "codec") set(SOXR_FEATURE_PURPOSE "codec")
@ -749,7 +770,7 @@ set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library")
set(GSSAPI_FEATURE_TYPE "OPTIONAL") set(GSSAPI_FEATURE_TYPE "OPTIONAL")
set(GSSAPI_FEATURE_PURPOSE "auth") set(GSSAPI_FEATURE_PURPOSE "auth")
set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support") set(GSSAPI_FEATURE_DESCRIPTION "[experimental] add kerberos support")
if(WIN32) if(WIN32)
set(X11_FEATURE_TYPE "DISABLED") set(X11_FEATURE_TYPE "DISABLED")
@ -804,7 +825,6 @@ if(ANDROID)
set(PULSE_FEATURE_TYPE "DISABLED") set(PULSE_FEATURE_TYPE "DISABLED")
set(CUPS_FEATURE_TYPE "DISABLED") set(CUPS_FEATURE_TYPE "DISABLED")
set(PCSC_FEATURE_TYPE "DISABLED") set(PCSC_FEATURE_TYPE "DISABLED")
set(FFMPEG_FEATURE_TYPE "DISABLED")
set(VAAPI_FEATURE_TYPE "DISABLED") set(VAAPI_FEATURE_TYPE "DISABLED")
set(OPENSLES_FEATURE_TYPE "REQUIRED") set(OPENSLES_FEATURE_TYPE "REQUIRED")
endif() endif()
@ -827,7 +847,6 @@ find_feature(PCSC ${PCSC_FEATURE_TYPE} ${PCSC_FEATURE_PURPOSE} ${PCSC_FEATURE_DE
find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION}) find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION})
find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION}) find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION})
find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION})
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION}) find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION}) find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION})
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION}) find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
@ -853,18 +872,23 @@ if (WITH_DSP_FFMPEG)
# Deactivate FFmpeg backend for sound, if the version is too old. # Deactivate FFmpeg backend for sound, if the version is too old.
# See libfreerdp/codec/dsp_ffmpeg.h # See libfreerdp/codec/dsp_ffmpeg.h
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+") file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
if (EXISTS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h")
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h" AV_VERSION_FILE2 REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
list(APPEND AV_VERSION_FILE ${AV_VERSION_FILE2})
endif()
FOREACH(item ${AV_VERSION_FILE}) FOREACH(item ${AV_VERSION_FILE})
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item}) STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
IF(litem) IF(litem)
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem}) string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN) list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2") if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
message(ERROR "invalid entry in libavcodec version header ${item}") message(ERROR "invalid entry in libavcodec version header ${item}")
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2") endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
list(GET VSPLIT_LINE 0 VNAME) list(GET VSPLIT_LINE 0 VNAME)
list(GET VSPLIT_LINE 1 VVALUE) list(GET VSPLIT_LINE 1 VVALUE)
set(${VNAME} ${VVALUE}) set(${VNAME} ${VVALUE})
ENDIF(litem) ENDIF(litem)
ENDFOREACH(item ${AV_VERSION_FILE}) ENDFOREACH(item ${AV_VERSION_FILE})
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}") set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
@ -913,7 +937,7 @@ if(MBEDTLS_FOUND)
add_definitions("-DWITH_MBEDTLS") add_definitions("-DWITH_MBEDTLS")
endif() endif()
if (WITH_X264 OR WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG) if (WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG OR WITH_MEDIACODEC)
set(WITH_GFX_H264 ON) set(WITH_GFX_H264 ON)
else() else()
set(WITH_GFX_H264 OFF) set(WITH_GFX_H264 OFF)
@ -966,7 +990,16 @@ if (APPLE)
else (APPLE) else (APPLE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
if (NOT FREEBSD) if (NOT FREEBSD)
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..") if (NOT BUILTIN_CHANNELS)
if (NOT DEFINED WITH_PLUGIN_RPATH_ONLY)
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..:\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
else()
# we need to supply this run path, even if not using RPATH in general
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
endif()
else()
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
endif()
endif() endif()
endif(APPLE) endif(APPLE)
@ -1029,15 +1062,6 @@ add_subdirectory(include)
add_subdirectory(libfreerdp) add_subdirectory(libfreerdp)
if (IOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0")
if (IOS_PLATFORM MATCHES "SIMULATOR")
set(CMAKE_OSX_SYSROOT "iphonesimulator")
else()
set(CMAKE_OSX_SYSROOT "iphoneos")
endif()
endif()
# RdTk # RdTk
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include") include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
include_directories("${CMAKE_BINARY_DIR}/rdtk/include") include_directories("${CMAKE_BINARY_DIR}/rdtk/include")

344
ChangeLog
View File

@ -1,3 +1,346 @@
# 2024-04-22 Version 2.11.7
Noteworthy changes:
* Backported oss-fuzz fixes
For a complete and detailed change log since the last release run:
git log 2.11.7...2.11.6
# 2024-04-17 Version 2.11.6
CVE:
CVE-2024-32041 [Low[ OutOfBound Read in zgfx_decompress_segment
CVE-2024-32039 [Moderate] Integer overflow & OutOfBound Write in clear_decompress_residual_data
CVE-2024-32040 [Low] integer underflow in nsc_rle_decode
CVE-2024-32458 [Low] OutOfBound Read in planar_skip_plane_rle
CVE-2024-32459 [Low] OutOfBound Read in ncrush_decompress
CVE-2024-32460 [Low] OutOfBound Read in interleaved_decompress
Noteworthy changes:
* Backported #10077
For a complete and detailed change log since the last release run:
git log 2.11.6...2.11.5
# 2024-01-19 Version 2.11.5
Noteworthy changes:
* Fix integer overflow in progressive decoder
* Update OpenSSL API usage for compatiblility with newer versions (#9747)
* Prevent NULL dereference for single thread decoder (#9712)
For a complete and detailed change log since the last release run:
git log 2.11.5...2.11.4
# 2023-12-14 Version 2.11.4
Notworthy changes:
* fix a typo in unicode commit (#9652)
For a complete and detailed change log since the last release run:
git log 2.11.4...2.11.3
# 2023-12-14 Version 2.11.3
Notworthy changes:
* Disabled windows MEDIA FOUNDATION h264 decoder due to reported issues (#9469)
* Fix issues with drive redirection (#9530,9554, #9586, #9617)
* Use endian safe ICU string converter (#9631)
* Improve AAC support (#9577)
* Fix swiss german keyboard layout (#9560)
* Enable rfx-mode:image (#9428)
For a complete and detailed change log since the last release run:
git log 2.11.3...2.11.2
# 2023-09-20 Version 2.11.2
Notworthy changes:
* Backported #9378: backported wArrayList (optional) copy on insert
* Backported #9360: backported certificate algorithm detection
For a complete and detailed change log since the last release run:
git log 2.11.2...2.11.1
# 2023-09-04 Version 2.11.1
Notworthy changes:
* Backported #9356: Fix issues with order updates
For a complete and detailed change log since the last release run:
git log 2.11.1..2.11.0
# 2023-08-28 Version 2.11.0
Noteworthy changes:
* Various input validation fixes
* Added various CMake options #9317
* LibreSSL build fixes #8709
Fixed issues:
* Backported #9233: Big endian support
* Backported #9099: Mouse grabbing support
* Backported #6851: wayland scrolling fix
* Backported #8690: Update h264 to use new FFMPEG API
* Backported #7306: early bail from update_read_window_state_order breaks protocol
* Backported #8903: rdpecam/server: Remove wrong assertion
* Backported #8994: bounds checks for gdi/gfx rectangles
* Backported #9023: enforce rdpdr client side state checks
* Backported #6331: deactivate mouse grabbing by default
* Cherry-pick out of #9172: channels/cliprdr: Fix writing incorrect PDU type for unlock PDUs
# 2023-02-16 Version 2.10.0
Noteworthy changes:
* Fix android build scripts, use CMake from SDK
* Fix connection negotiation with mstsc/msrdc #8426
* [ntlm]: use rfc5929 binding hash algorithm #8430
* [channels,printer] Fixed reference counting #8433
* Fix uwac pixman #8439
* Fix Rdp security #8457
* [client,x11] Detect key autorepeat #8522
* [build] add channel path to RPATH #8551
* Fix build with BUILTIN_CHANNELS=OFF #8560
* revert changes so that the osmajortype/osminortype is not overwritten #8571
* [uwac] do not use iso C functions #8604
* [winpr,sam] fix inalid NULL arguments #8605
* Fix incompatible function pointer types #8625
Fixed issues:
* Backported #8581: Ignore data PDUs for DVCs that were not opened successfully
* Backported #8498: [channel,urbdrc] fix type of usb hotplug callback
* Backported #8537: Extended info enforce limits
* Backported #8611: [core] add missing redirection fields
# 2022-11-16 Version 2.9.0
Noteworthy changes:
* Backported #8252: Support sending server redirection PDU
* Backported #8406: Ensure X11 client cursor is never smaller 1x1
* Backported #8403: Fixed multiple client side input validation issues
(CVE-2022-39316, CVE-2022-39317, CVE-2022-39318, CVE-2022-39319,
CVE-2022-39320, CVE-2022-41877, CVE-2022-39347)
* Backported #7282: Proxy server now discards input events sent before
activation was received
* Backported #8324: Internal replacements for md4, md5 and hmac-md5
For the time being the RDP protocol requires these outdated hash
algorithms. So any distribution that wants to ship a working
FreeRDP should check the options WITH_INTERNAL_MD4 (and depending
on OpenSSL deprecation status WITH_INTERNAL_MD5)
Fixed issues:
* Backported #8341: Null checks in winpr_Digest_Free
* Backported #8335: Missing NULL return in winpr_Digest_New
* Backported #8192: Support for audin version 2 microphone channel
* Backported #7282: Discard input events before activation (Fixes #8374)
For a complete and detailed change log since the last release run:
git log 2.8.1..2.9.0
# 2022-10-12 Version 2.8.1
Noteworthy changes:
* Fixed CVE-2022-39282
* Fixed CVE-2022-39283
* Added missing commit for backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable)
* Added hash checks for android build script dependencies
Fixed issues:
* Backported #8190: Fix build break with newer FFMPEG versions
* Backported #8234: Updated flatpak with build script
* Backported #8210: Better execinfo support check for android
* Backported #7708: Header now defines DumpThreadHandles
* Backported #8176: Check fullscreen state and not setting
* Backported #8236: Send resize on window state change
* Backported #7611: Audin macOS monterey fix
* Backported #8291: Android build script update
# 2022-07-28 Version 2.8.0
Noteworthy changes:
* Backported API to get peer accepted channel option flags
* Backported API to get peer accepted channel names
* Backported Stream_CheckAndLogRequiredLength
* Backported #7954: Add server side handling for [MS-RDPET]
* Backported #8010: Add server side handling for [MS-RDPECAM]
* Backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable)
* Backported #8051: Relieve CLIPRDR filename restriction when connecting to non-MS Windows servers
* Backported #8048: TLS version control
* Backported #7987: Add a new command line arg to enforce tls1.2
Fixed issues:
* Fixed #7837: Prevent out of bound reads for FFMPEG
* Backported #7859 and #7861: Unwind support for backtrace generation
* Backported #7440: wlfreerdp appid
* Backported #7832: RAIL window restore
* Backported #7833: Refactored WinPR thread locking
* Backported #7893: Mac rdpsnd memory leak fixes
* Backported #7895: Mac audin memory leak fixes
* Backported #7898: Automatic android versioning
* Backported #7916: GFX 10.7 capability support
* Backported #7949: Server RDPSND API improvements
* Backported #7957: Server DVC API improvements
* Backported #7760: Fixed osMinorType values
* Backported #8013: Add missing osMajorType values
* Backported #8076: Fix wrong usage of subband diffing flag (tile artifact fix)
For a complete and detailed change log since the last release run:
git log 2.7.0..2.8.0
# 2022-04-25 Version 2.7.0
Noteworthy changes:
* Backported OpenSSL3 gateway support (#7822)
* Backported various NTLM fixes
* Backported WINPR_ASSERT to ease future backports
Fixed issues:
* Backported #6786: Use /network:auto by default
* Backported #7714: Workaround for broken surface frame marker
* Backported #7733: Support 10bit X11 color (BGRX32 only)
* Backported #7745: GFX progressive double free
* Backported #7808: Disable websockets with /gt:rpc
* Backported #7815: RAIL expect LOGON_MSG_SESSION_CONTINUE
Important notes:
For a complete and detailed change log since the last release run:
git log 2.6.1..2.7.0
# 2022-03-07 Version 2.6.1
Noteworthy changes:
Fixed issues:
* Backported freerdp_abort_connect during freerdp_connect fix (#7700)
* Backported improved version dection see docs/version_detection.md for details
* Backported various rdpsnd fixes (#7695)
Important notes:
For a complete and detailed change log since the last release run:
git log 2.0.0..2.6.1
# 2022-02-22 Version 2.6.0
Noteworthy changes:
* Backported android FFMPEG build scripts
* Updated android build dependencies
Fixed issues:
* Backported #7303: Fix PDU length for RDPINPUT_PROTOCOL_V300
* Backported #7658: Sanitize optional physical monitor size values
* Backported #7426: Wayland memory corruption
* Backported #7293: Remove unused codec x264
* Backported #7541: Allow resolutions larger 2048x2048
* Backported #7574: FFMPEG 5.0 support
* Backported #7578: FFMPEG 5.0 support
* Backported #7580: Fixed device hotplugging
* Backported #7583: GetUserNameExA: Prefer getpwuid_r over getlogin_r over getlogin
* Backported #7585: Android Mediacodec support
Important notes:
For a complete and detailed change log since the last release run:
git log 2.5.0..2.6.0
# 2022-01-12 Version 2.5.0
Noteworthy changes:
* Fixed smartcard login in case a redirection occurs the pin was lost
* Backported windows client drawing fixes
* Backported improved macOS keyboard layout detection
* Backported TcpConnectTimeout
* Backported LibreSSL compatibility patches
* Backported signal handler backtrace
* Backported OpenSSL 3.0 support
Fixed issues:
* Backport #7539: Wayland client clipboard issues
* Backport #7509: Various fixes regarding registry emulation, addin loader
and updated locale detection
* Backport #7466: Android android_register_pointer missing initialization
Important notes:
For a complete and detailed change log since the last release run:
git log 2.4.1..2.5.0
# 2021-10-20 Version 2.4.1
Noteworthy changes:
* Refactored RPC gateway parsing code
* OpenSSL 3.0 compatibility fixes
* USB redirection: fixed transfer lengths
Fixed issues:
* #7363: Length checks in ConvertUTF8toUTF16
* #7349: Added checks for bitmap width and heigth values
Important notes:
* CVE-2021-41159: Improper client input validation for gateway connections allows to overwrite memory
* CVE-2021-41160: Improper region checks in all clients allow out of bound write to memory
For a complete and detailed change log since the last release run:
git log 2.4.0..2.4.1
# 2021-07-27 Version 2.4.0
Noteworthy changes:
* Backported multithreadded progressive decoder (#7036)
* Backported clipboard fixes (#6924)
* Fixed remote file read (#7185)
Fixed issues:
* #6938: RAILS clipboard remote -> local
* #6985: Support newer FFMPEG builds
* #6989: Use OpenSSL default certificate store settings
* #7073: Planar alignment fixes
# 2021-03-15 Version 2.3.2
For a complete and detailed change log since the last release run:
git log 2.3.2..2.4.0
Noteworthy changes:
* Fixed autoreconnect printer backend loading
* Fixed compilation on older mac os versions < 10.14
* Fixed mouse pointer move with smart-sizing
* Added command line option to disable websocket gateway support
* Fixed drive hotplugging issues with windows
* Fixed smartcard issues on mac
Fixed issues:
* #6900: Transparency issues with aFreeRDP
* #6848: Invalid format string in smartcard trace
* #6846: Fixed static builds
* #6888: Crash due to missing bounds checks
* #6882: Use default sound devoce on mac
For a complete and detailed change log since the last release run:
git log 2.3.1..2.3.2
# 2021-03-01 Version 2.3.1
Noteworthy changes:
* This is a compatibility bugfix release readding some (deprecated)
symbols/defines
* Also add some more EXPERIMENTAL warnings to CMake flags as some were not
clear enough.
* Fixed a memory leak in xfreerdp (mouse pointer updates)
* No longer activating some compile time debug options with -DWITH_DEBUG_ALL=ON
which might leak sensitive information.
* Added -DDEFINE_NO_DEPRECATED for developers to detect use of deprecated
symbols
For a complete and detailed change log since the last release run:
git log 2.3.0..2.3.1
# 2021-02-24 Version 2.3.0 # 2021-02-24 Version 2.3.0
Important notes: Important notes:
@ -433,4 +776,3 @@ Virtual Channels:
* rdpsnd (Sound Redirection) * rdpsnd (Sound Redirection)
* alsa support * alsa support
* pulse support * pulse support

View File

@ -1,7 +1,8 @@
# FreeRDP cmake android options # FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
# #
# Copyright 2013 Thincast Technologies GmbH # Copyright 2022 Armin Novak <anovak@thincast.com>
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com> # Copyright 2022 Thincast Technologies GmbH
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,8 +16,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
option(WITH_OPENSLES "Enable sound and microphone redirection using OpenSLES" ON) define_channel("ainput")
set(ANDROID_APP_TARGET_SDK 21 CACHE STRING "Application target android SDK") if(WITH_CLIENT_CHANNELS)
set(ANDROID_APP_MIN_SDK 14 CACHE STRING "Application minimum android SDK requirement") add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,13 @@
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT ON)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "ainput" TYPE "dynamic"
DESCRIPTION "Advanced Input Virtual Channel Extension"
SPECIFICATIONS "[XXXXX]"
DEFAULT ${OPTION_DEFAULT})
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,34 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Armin Novak <anovak@thincast.com>
# Copyright 2022 Thincast Technologies GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_client("ainput")
set(${MODULE_PREFIX}_SRCS
ainput_main.c
ainput_main.h)
include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
endif()
target_link_libraries(${MODULE_NAME} winpr)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")

View File

@ -0,0 +1,315 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Advanced Input Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include "ainput_main.h"
#include <freerdp/channels/log.h>
#include <freerdp/client/ainput.h>
#include <freerdp/channels/ainput.h>
#include "../common/ainput_common.h"
#define TAG CHANNELS_TAG("ainput.client")
typedef struct AINPUT_CHANNEL_CALLBACK_ AINPUT_CHANNEL_CALLBACK;
struct AINPUT_CHANNEL_CALLBACK_
{
IWTSVirtualChannelCallback iface;
IWTSPlugin* plugin;
IWTSVirtualChannelManager* channel_mgr;
IWTSVirtualChannel* channel;
};
typedef struct AINPUT_LISTENER_CALLBACK_ AINPUT_LISTENER_CALLBACK;
struct AINPUT_LISTENER_CALLBACK_
{
IWTSListenerCallback iface;
IWTSPlugin* plugin;
IWTSVirtualChannelManager* channel_mgr;
AINPUT_CHANNEL_CALLBACK* channel_callback;
};
typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN;
struct AINPUT_PLUGIN_
{
IWTSPlugin iface;
AINPUT_LISTENER_CALLBACK* listener_callback;
IWTSListener* listener;
UINT32 MajorVersion;
UINT32 MinorVersion;
BOOL initialized;
};
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
{
UINT16 type;
AINPUT_PLUGIN* ainput;
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
WINPR_ASSERT(callback);
WINPR_ASSERT(data);
ainput = (AINPUT_PLUGIN*)callback->plugin;
WINPR_ASSERT(ainput);
if (Stream_GetRemainingLength(data) < 2)
return ERROR_NO_DATA;
Stream_Read_UINT16(data, type);
switch (type)
{
case MSG_AINPUT_VERSION:
if (Stream_GetRemainingLength(data) < 8)
return ERROR_NO_DATA;
Stream_Read_UINT32(data, ainput->MajorVersion);
Stream_Read_UINT32(data, ainput->MinorVersion);
break;
default:
WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type);
break;
}
return CHANNEL_RC_OK;
}
static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
{
AINPUT_PLUGIN* ainput;
AINPUT_CHANNEL_CALLBACK* callback;
BYTE buffer[32] = { 0 };
UINT64 time;
wStream sbuffer = { 0 };
wStream* s = &sbuffer;
Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
WINPR_ASSERT(context);
time = GetTickCount64();
ainput = (AINPUT_PLUGIN*)context->handle;
WINPR_ASSERT(ainput);
WINPR_ASSERT(ainput->listener_callback);
if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
{
WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
ainput->MajorVersion, ainput->MinorVersion);
return CHANNEL_RC_UNSUPPORTED_VERSION;
}
callback = ainput->listener_callback->channel_callback;
WINPR_ASSERT(callback);
{
char buffer[128] = { 0 };
WLog_VRB(TAG, "[%s] sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32,
__FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
}
/* Message type */
Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
/* Event data */
Stream_Write_UINT64(s, time);
Stream_Write_UINT64(s, flags);
Stream_Write_INT32(s, x);
Stream_Write_INT32(s, y);
Stream_SealLength(s);
/* ainput back what we have received. AINPUT does not have any message IDs. */
WINPR_ASSERT(callback->channel);
WINPR_ASSERT(callback->channel->Write);
return callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s),
NULL);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
free(callback);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel, BYTE* Data,
BOOL* pbAccept,
IWTSVirtualChannelCallback** ppCallback)
{
AINPUT_CHANNEL_CALLBACK* callback;
AINPUT_LISTENER_CALLBACK* listener_callback = (AINPUT_LISTENER_CALLBACK*)pListenerCallback;
WINPR_ASSERT(listener_callback);
WINPR_UNUSED(Data);
WINPR_UNUSED(pbAccept);
callback = (AINPUT_CHANNEL_CALLBACK*)calloc(1, sizeof(AINPUT_CHANNEL_CALLBACK));
if (!callback)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
callback->iface.OnDataReceived = ainput_on_data_received;
callback->iface.OnClose = ainput_on_close;
callback->plugin = listener_callback->plugin;
callback->channel_mgr = listener_callback->channel_mgr;
callback->channel = pChannel;
listener_callback->channel_callback = callback;
*ppCallback = &callback->iface;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
{
UINT status;
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
WINPR_ASSERT(ainput);
if (ainput->initialized)
{
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AINPUT_DVC_CHANNEL_NAME);
return ERROR_INVALID_DATA;
}
ainput->listener_callback =
(AINPUT_LISTENER_CALLBACK*)calloc(1, sizeof(AINPUT_LISTENER_CALLBACK));
if (!ainput->listener_callback)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
ainput->listener_callback->iface.OnNewChannelConnection = ainput_on_new_channel_connection;
ainput->listener_callback->plugin = pPlugin;
ainput->listener_callback->channel_mgr = pChannelMgr;
status = pChannelMgr->CreateListener(pChannelMgr, AINPUT_DVC_CHANNEL_NAME, 0,
&ainput->listener_callback->iface, &ainput->listener);
ainput->listener->pInterface = ainput->iface.pInterface;
ainput->initialized = status == CHANNEL_RC_OK;
return status;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin)
{
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
if (ainput && ainput->listener_callback)
{
IWTSVirtualChannelManager* mgr = ainput->listener_callback->channel_mgr;
if (mgr)
IFCALL(mgr->DestroyListener, mgr, ainput->listener);
}
if (ainput)
{
free(ainput->listener_callback);
free(ainput->iface.pInterface);
}
free(ainput);
return CHANNEL_RC_OK;
}
#ifdef BUILTIN_CHANNELS
#define DVCPluginEntry ainput_DVCPluginEntry
#else
#define DVCPluginEntry FREERDP_API DVCPluginEntry
#endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
{
UINT status = CHANNEL_RC_OK;
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "ainput");
if (!ainput)
{
AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
ainput = (AINPUT_PLUGIN*)calloc(1, sizeof(AINPUT_PLUGIN));
if (!ainput || !context)
{
free(context);
free(ainput);
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
ainput->iface.Initialize = ainput_plugin_initialize;
ainput->iface.Terminated = ainput_plugin_terminated;
context->handle = (void*)ainput;
context->AInputSendInputEvent = ainput_send_input_event;
ainput->iface.pInterface = (void*)context;
status = pEntryPoints->RegisterPlugin(pEntryPoints, AINPUT_CHANNEL_NAME, &ainput->iface);
}
return status;
}

View File

@ -0,0 +1,43 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Advanced Input Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
#define FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/channels/log.h>
#define DVC_TAG CHANNELS_TAG("ainput.client")
#ifdef WITH_DEBUG_DVC
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
#else
#define DEBUG_DVC(...) \
do \
{ \
} while (0)
#endif
#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H */

View File

@ -0,0 +1,59 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_INT_AINPUT_COMMON_H
#define FREERDP_INT_AINPUT_COMMON_H
#include <winpr/string.h>
#include <freerdp/channels/ainput.h>
static INLINE const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size)
{
char number[32] = { 0 };
if (flags & AINPUT_FLAGS_HAVE_REL)
winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|");
if (flags & AINPUT_FLAGS_WHEEL)
winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|");
if (flags & AINPUT_FLAGS_MOVE)
winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|");
if (flags & AINPUT_FLAGS_DOWN)
winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|");
if (flags & AINPUT_FLAGS_REL)
winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|");
if (flags & AINPUT_FLAGS_BUTTON1)
winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|");
if (flags & AINPUT_FLAGS_BUTTON2)
winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|");
if (flags & AINPUT_FLAGS_BUTTON3)
winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|");
if (flags & AINPUT_XFLAGS_BUTTON1)
winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|");
if (flags & AINPUT_XFLAGS_BUTTON2)
winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|");
_snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags);
winpr_str_append(number, buffer, size, " ");
return buffer;
}
#endif /* FREERDP_INT_AINPUT_COMMON_H */

View File

@ -0,0 +1,27 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Armin Novak <anovak@thincast.com>
# Copyright 2022 Thincast Technologies GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("ainput")
set(${MODULE_PREFIX}_SRCS
ainput_main.c)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,587 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Advanced Input Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include <winpr/stream.h>
#include <freerdp/server/ainput.h>
#include <freerdp/channels/ainput.h>
#include <freerdp/channels/log.h>
#include "../common/ainput_common.h"
#define TAG CHANNELS_TAG("ainput.server")
typedef enum
{
AINPUT_INITIAL,
AINPUT_OPENED,
AINPUT_VERSION_SENT,
} eAInputChannelState;
typedef struct
{
ainput_server_context context;
BOOL opened;
HANDLE stopEvent;
HANDLE thread;
void* ainput_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eAInputChannelState state;
wStream* buffer;
} ainput_server;
static UINT ainput_server_context_poll(ainput_server_context* context);
static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
static UINT ainput_server_context_poll_int(ainput_server_context* context);
static BOOL ainput_server_is_open(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
return ainput->isOpened;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_server_open_channel(ainput_server* ainput)
{
DWORD Error;
HANDLE hEvent;
DWORD StartTick;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
WINPR_ASSERT(ainput);
if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
ainput->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm);
StartTick = GetTickCount();
while (ainput->ainput_channel == NULL)
{
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
WTS_CHANNEL_OPTION_DYNAMIC);
Error = GetLastError();
if (Error == ERROR_NOT_FOUND)
{
WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
break;
}
if (ainput->ainput_channel)
{
UINT32 channelId;
BOOL status = TRUE;
channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
break;
}
if (GetTickCount() - StartTick > 5000)
{
WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
break;
}
}
return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static UINT ainput_server_send_version(ainput_server* ainput)
{
ULONG written;
wStream* s;
WINPR_ASSERT(ainput);
s = ainput->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
if (!Stream_EnsureCapacity(s, 10))
{
WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
return ERROR_OUTOFMEMORY;
}
Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX);
if (!WTSVirtualChannelWrite(ainput->ainput_channel, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
{
UINT error = CHANNEL_RC_OK;
UINT64 flags, time;
INT32 x, y;
char buffer[128] = { 0 };
WINPR_ASSERT(ainput);
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
return ERROR_NO_DATA;
Stream_Read_UINT64(s, time);
Stream_Read_UINT64(s, flags);
Stream_Read_INT32(s, x);
Stream_Read_INT32(s, y);
WLog_VRB(TAG, "[%s] received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32,
__FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
return error;
}
static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
{
BYTE* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(ainput);
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
ainput_server* ainput = (ainput_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(ainput);
nCount = 0;
events[nCount++] = ainput->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (ainput->state)
{
case AINPUT_OPENED:
events[1] = ainput_server_get_channel_handle(ainput);
nCount = 2;
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
switch (status)
{
case WAIT_TIMEOUT:
case WAIT_OBJECT_0 + 1:
case WAIT_OBJECT_0:
error = ainput_server_context_poll_int(&ainput->context);
break;
case WAIT_FAILED:
default:
WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
error = ERROR_INTERNAL_ERROR;
break;
}
break;
case AINPUT_VERSION_SENT:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_TIMEOUT:
case WAIT_OBJECT_0 + 1:
case WAIT_OBJECT_0:
error = ainput_server_context_poll_int(&ainput->context);
break;
case WAIT_FAILED:
default:
WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
error = ERROR_INTERNAL_ERROR;
break;
}
break;
default:
error = ainput_server_context_poll_int(&ainput->context);
break;
}
}
WTSVirtualChannelClose(ainput->ainput_channel);
ainput->ainput_channel = NULL;
if (error && ainput->context.rdpcontext)
setChannelError(ainput->context.rdpcontext, error,
"ainput_server_thread_func reported an error");
ExitThread(error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_server_open(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
if (!ainput->externalThread && (ainput->thread == NULL))
{
ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ainput->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL);
if (!ainput->thread)
{
WLog_ERR(TAG, "CreateEvent failed!");
CloseHandle(ainput->stopEvent);
ainput->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
ainput->isOpened = TRUE;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_server_close(ainput_server_context* context)
{
UINT error = CHANNEL_RC_OK;
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
if (!ainput->externalThread && ainput->thread)
{
SetEvent(ainput->stopEvent);
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(ainput->thread);
CloseHandle(ainput->stopEvent);
ainput->thread = NULL;
ainput->stopEvent = NULL;
}
if (ainput->externalThread)
{
if (ainput->state != AINPUT_INITIAL)
{
WTSVirtualChannelClose(ainput->ainput_channel);
ainput->ainput_channel = NULL;
ainput->state = AINPUT_INITIAL;
}
}
ainput->isOpened = FALSE;
return error;
}
static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
if (ainput->isOpened)
{
WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
"state is not possible!");
return ERROR_INVALID_STATE;
}
ainput->externalThread = externalThread;
return error;
}
ainput_server_context* ainput_server_context_new(HANDLE vcm)
{
ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
if (!ainput)
return NULL;
ainput->context.vcm = vcm;
ainput->context.Open = ainput_server_open;
ainput->context.IsOpen = ainput_server_is_open;
ainput->context.Close = ainput_server_close;
ainput->context.Initialize = ainput_server_initialize;
ainput->context.Poll = ainput_server_context_poll;
ainput->context.ChannelHandle = ainput_server_context_handle;
ainput->buffer = Stream_New(NULL, 4096);
if (!ainput->buffer)
goto fail;
return &ainput->context;
fail:
ainput_server_context_free(ainput);
return NULL;
}
void ainput_server_context_free(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
if (ainput)
{
ainput_server_close(context);
Stream_Free(ainput->buffer, TRUE);
}
free(ainput);
}
static UINT ainput_process_message(ainput_server* ainput)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned, ActualBytesReturned;
UINT16 MessageId;
wStream* s;
WINPR_ASSERT(ainput);
WINPR_ASSERT(ainput->ainput_channel);
s = ainput->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 2)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
if (BytesReturned != ActualBytesReturned)
{
WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32,
ActualBytesReturned, BytesReturned);
goto out;
}
Stream_SetLength(s, ActualBytesReturned);
Stream_Read_UINT16(s, MessageId);
switch (MessageId)
{
case MSG_AINPUT_MOUSE:
error = ainput_server_recv_mouse_event(ainput, s);
break;
default:
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
{
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
WINPR_ASSERT(handle);
if (!ainput->externalThread)
{
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
return FALSE;
}
if (ainput->state == AINPUT_INITIAL)
{
WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
return FALSE;
}
*handle = ainput_server_get_channel_handle(ainput);
return TRUE;
}
UINT ainput_server_context_poll_int(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(ainput);
switch (ainput->state)
{
case AINPUT_INITIAL:
error = ainput_server_open_channel(ainput);
if (error)
WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
else
ainput->state = AINPUT_OPENED;
break;
case AINPUT_OPENED:
{
BYTE* buffer = NULL;
DWORD BytesReturned = 0;
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer,
&BytesReturned) != TRUE)
{
WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
}
else
{
if (*buffer != 0)
{
error = ainput_server_send_version(ainput);
if (error)
WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
error);
else
ainput->state = AINPUT_VERSION_SENT;
}
else
error = CHANNEL_RC_OK;
}
WTSFreeMemory(buffer);
}
break;
case AINPUT_VERSION_SENT:
error = ainput_process_message(ainput);
break;
default:
WLog_ERR(TAG, "AINPUT chanel is in invalid state %d", ainput->state);
break;
}
return error;
}
UINT ainput_server_context_poll(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
if (!ainput->externalThread)
{
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
return ERROR_INTERNAL_ERROR;
}
return ainput_server_context_poll_int(context);
}

View File

@ -242,10 +242,6 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT
break; break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
return TRUE;
default: default:
return FALSE; return FALSE;
} }

View File

@ -43,13 +43,18 @@
#include "audin_main.h" #include "audin_main.h"
#define MSG_SNDIN_VERSION 0x01 #define SNDIN_VERSION 0x02
#define MSG_SNDIN_FORMATS 0x02
#define MSG_SNDIN_OPEN 0x03 enum
#define MSG_SNDIN_OPEN_REPLY 0x04 {
#define MSG_SNDIN_DATA_INCOMING 0x05 MSG_SNDIN_VERSION = 0x01,
#define MSG_SNDIN_DATA 0x06 MSG_SNDIN_FORMATS = 0x02,
#define MSG_SNDIN_FORMATCHANGE 0x07 MSG_SNDIN_OPEN = 0x03,
MSG_SNDIN_OPEN_REPLY = 0x04,
MSG_SNDIN_DATA_INCOMING = 0x05,
MSG_SNDIN_DATA = 0x06,
MSG_SNDIN_FORMATCHANGE = 0x07
} MSG_SNDIN_CMD;
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK; typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
struct _AUDIN_LISTENER_CALLBACK struct _AUDIN_LISTENER_CALLBACK
@ -105,6 +110,7 @@ struct _AUDIN_PLUGIN
IWTSListener* listener; IWTSListener* listener;
BOOL initialized; BOOL initialized;
UINT32 version;
}; };
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args); static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
@ -138,7 +144,7 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s) static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
{ {
wStream* out; wStream* out;
const UINT32 ClientVersion = 0x01; const UINT32 ClientVersion = SNDIN_VERSION;
UINT32 ServerVersion; UINT32 ServerVersion;
if (Stream_GetRemainingLength(s) < 4) if (Stream_GetRemainingLength(s) < 4)
@ -149,7 +155,7 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
ServerVersion, ClientVersion); ServerVersion, ClientVersion);
/* Do not answer server packet, we do not support the channel version. */ /* Do not answer server packet, we do not support the channel version. */
if (ServerVersion != ClientVersion) if (ServerVersion > ClientVersion)
{ {
WLog_Print(audin->log, WLOG_WARN, WLog_Print(audin->log, WLOG_WARN,
"Incompatible channel version server=%" PRIu32 "Incompatible channel version server=%" PRIu32
@ -157,6 +163,7 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
ServerVersion, ClientVersion); ServerVersion, ClientVersion);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
audin->version = ServerVersion;
out = Stream_New(NULL, 5); out = Stream_New(NULL, 5);
@ -444,11 +451,8 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb
return FALSE; return FALSE;
} }
if (!supported) if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
{ return FALSE;
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
return FALSE;
}
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback); IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);

View File

@ -154,12 +154,16 @@ static UINT audin_mac_set_format(IAudinDevice *device, const AUDIO_FORMAT *forma
if (format->wBitsPerSample == 0) if (format->wBitsPerSample == 0)
mac->audioFormat.mBitsPerChannel = 16; mac->audioFormat.mBitsPerChannel = 16;
mac->audioFormat.mBytesPerFrame = 0;
mac->audioFormat.mBytesPerPacket = 0;
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels; mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
mac->audioFormat.mFramesPerPacket = 1;
mac->audioFormat.mBytesPerFrame =
mac->audioFormat.mChannelsPerFrame * (mac->audioFormat.mBitsPerChannel / 8);
mac->audioFormat.mBytesPerPacket =
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format); mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
mac->audioFormat.mFormatID = audin_mac_get_format(format); mac->audioFormat.mFormatID = audin_mac_get_format(format);
mac->audioFormat.mFramesPerPacket = 1;
mac->audioFormat.mReserved = 0; mac->audioFormat.mReserved = 0;
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec; mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
@ -420,33 +424,40 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
goto error_out; goto error_out;
} }
AVAuthorizationStatus status = #if defined(MAC_OS_X_VERSION_10_14)
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; if (@available(macOS 10.14, *))
switch (status)
{ {
case AVAuthorizationStatusAuthorized: @autoreleasepool {
mac->isAuthorized = TRUE; AVAuthorizationStatus status =
break; [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
case AVAuthorizationStatusNotDetermined: switch (status)
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio {
completionHandler:^(BOOL granted) { case AVAuthorizationStatusAuthorized:
if (granted == YES) mac->isAuthorized = TRUE;
{ break;
mac->isAuthorized = TRUE; case AVAuthorizationStatusNotDetermined:
} [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
else completionHandler:^(BOOL granted) {
WLog_WARN(TAG, "Microphone access denied by user"); if (granted == YES)
}]; {
break; mac->isAuthorized = TRUE;
case AVAuthorizationStatusRestricted: }
WLog_WARN(TAG, "Microphone access restricted by policy"); else
break; WLog_WARN(TAG, "Microphone access denied by user");
case AVAuthorizationStatusDenied: }];
WLog_WARN(TAG, "Microphone access denied by policy"); break;
break; case AVAuthorizationStatusRestricted:
default: WLog_WARN(TAG, "Microphone access restricted by policy");
break; break;
case AVAuthorizationStatusDenied:
WLog_WARN(TAG, "Microphone access denied by policy");
break;
default:
break;
}
}
} }
#endif
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
error_out: error_out:

View File

@ -215,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void
opensles->user_data = user_data; opensles->user_data = user_data;
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
error_out: error_out:
audin_opensles_close(opensles); audin_opensles_close(device);
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
} }
@ -248,7 +248,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, ADDIN_A
{ {
UINT status; UINT status;
DWORD flags; DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg; const COMMAND_LINE_ARGUMENT_A* arg;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device; AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = { COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL,

View File

@ -113,10 +113,6 @@ static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT*
break; break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
return TRUE;
default: default:
return FALSE; return FALSE;
} }

View File

@ -236,17 +236,6 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA
break; break;
case WAVE_FORMAT_ALAW: /* A-LAW */
case WAVE_FORMAT_MULAW: /* U-LAW */
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
{
return TRUE;
}
break;
default: default:
return FALSE; return FALSE;
} }

View File

@ -110,27 +110,74 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error"); setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
} }
static BOOL log_mmresult(AudinWinmmDevice* winmm, const char* what, MMRESULT result)
{
if (result != MMSYSERR_NOERROR)
{
CHAR buffer[8192] = { 0 };
CHAR msg[8192] = { 0 };
CHAR cmsg[8192] = { 0 };
waveInGetErrorTextA(result, buffer, sizeof(buffer));
_snprintf(msg, sizeof(msg) - 1, "%s failed. %" PRIu32 " [%s]", what, result, buffer);
_snprintf(cmsg, sizeof(cmsg) - 1, "audin_winmm_thread_func reported an error '%s'", msg);
WLog_Print(winmm->log, WLOG_DEBUG, "%s", msg);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, cmsg);
return FALSE;
}
return TRUE;
}
static BOOL test_format_supported(const PWAVEFORMATEX pwfx)
{
MMRESULT rc;
WAVEINCAPSA caps = { 0 };
rc = waveInGetDevCapsA(WAVE_MAPPER, &caps, sizeof(caps));
if (rc != MMSYSERR_NOERROR)
return FALSE;
switch (pwfx->nChannels)
{
case 1:
if ((caps.dwFormats &
(WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08 |
WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_96M16)) == 0)
return FALSE;
break;
case 2:
if ((caps.dwFormats &
(WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08 |
WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_96S16)) == 0)
return FALSE;
break;
default:
return FALSE;
}
rc = waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0,
WAVE_FORMAT_QUERY | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
return (rc == MMSYSERR_NOERROR);
}
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg) static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
{ {
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg; AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
char* buffer; char* buffer;
int size, i; int size, i;
WAVEHDR waveHdr[4]; WAVEHDR waveHdr[4] = { 0 };
DWORD status; DWORD status;
MMRESULT rc; MMRESULT rc;
if (!winmm->hWaveIn) if (!winmm->hWaveIn)
{ {
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, MMRESULT rc;
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
CALLBACK_FUNCTION)) (DWORD_PTR)winmm,
{ CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
if (winmm->rdpcontext) if (!log_mmresult(winmm, "waveInOpen", rc))
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
}
} }
size = size =
@ -150,36 +197,22 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
waveHdr[i].lpData = buffer; waveHdr[i].lpData = buffer;
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])); rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc) if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
{ {
WLog_Print(winmm->log, WLOG_DEBUG, "waveInPrepareHeader failed. %" PRIu32 "", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
} }
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])); rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc) if (!log_mmresult(winmm, "waveInAddBuffer", rc))
{ {
WLog_Print(winmm->log, WLOG_DEBUG, "waveInAddBuffer failed. %" PRIu32 "", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
} }
} }
rc = waveInStart(winmm->hWaveIn); rc = waveInStart(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc) if (!log_mmresult(winmm, "waveInStart", rc))
{ {
WLog_Print(winmm->log, WLOG_DEBUG, "waveInStart failed. %" PRIu32 "", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
} }
status = WaitForSingleObject(winmm->stopEvent, INFINITE); status = WaitForSingleObject(winmm->stopEvent, INFINITE);
@ -195,26 +228,17 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
rc = waveInReset(winmm->hWaveIn); rc = waveInReset(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc) if (!log_mmresult(winmm, "waveInReset", rc))
{ {
WLog_Print(winmm->log, WLOG_DEBUG, "waveInReset failed. %" PRIu32 "", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
} }
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])); rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc) if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
{ {
WLog_Print(winmm->log, WLOG_DEBUG, "waveInUnprepareHeader failed. %" PRIu32 "", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
} }
free(waveHdr[i].lpData); free(waveHdr[i].lpData);
@ -222,13 +246,8 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
rc = waveInClose(winmm->hWaveIn); rc = waveInClose(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc) if (!log_mmresult(winmm, "waveInClose", rc))
{ {
WLog_Print(winmm->log, WLOG_DEBUG, "waveInClose failed. %" PRIu32 "", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
} }
winmm->hWaveIn = NULL; winmm->hWaveIn = NULL;
@ -311,16 +330,32 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
for (i = 0; i < winmm->cFormats; i++) for (i = 0; i < winmm->cFormats; i++)
{ {
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag && const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
winmm->ppwfx[i]->nChannels == format->nChannels && if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample) (ppwfx->wBitsPerSample == format->wBitsPerSample) &&
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
{ {
winmm->pwfx_cur = winmm->ppwfx[i]; /* BUG: Many devices report to support stereo recording but fail here.
break; * Ensure we always use mono. */
if (ppwfx->nChannels > 1)
{
ppwfx->nChannels = 1;
}
if (ppwfx->nBlockAlign != 2)
{
ppwfx->nBlockAlign = 2;
ppwfx->nAvgBytesPerSec = ppwfx->nSamplesPerSec * ppwfx->nBlockAlign;
}
if (!test_format_supported(ppwfx))
return ERROR_INVALID_PARAMETER;
winmm->pwfx_cur = ppwfx;
return CHANNEL_RC_OK;
} }
} }
return CHANNEL_RC_OK; return ERROR_INVALID_PARAMETER;
} }
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format) static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
@ -332,6 +367,9 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
if (!winmm || !format) if (!winmm || !format)
return FALSE; return FALSE;
if (format->wFormatTag != WAVE_FORMAT_PCM)
return FALSE;
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize); pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
if (!pwfx) if (!pwfx)
@ -346,29 +384,27 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX); data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
memcpy(data, format->data, format->cbSize); memcpy(data, format->data, format->cbSize);
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
if (!test_format_supported(pwfx))
goto fail;
if (winmm->cFormats >= winmm->ppwfx_size)
{ {
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign; PWAVEFORMATEX* tmp_ppwfx;
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY)) if (!tmp_ppwfx)
{ goto fail;
if (winmm->cFormats >= winmm->ppwfx_size)
{
PWAVEFORMATEX* tmp_ppwfx;
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
if (!tmp_ppwfx) winmm->ppwfx_size *= 2;
return FALSE; winmm->ppwfx = tmp_ppwfx;
winmm->ppwfx_size *= 2;
winmm->ppwfx = tmp_ppwfx;
}
winmm->ppwfx[winmm->cFormats++] = pwfx;
return TRUE;
}
} }
winmm->ppwfx[winmm->cFormats++] = pwfx;
return TRUE;
fail:
free(pwfx); free(pwfx);
return FALSE; return FALSE;
} }
@ -507,7 +543,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
} }
winmm->ppwfx_size = 10; winmm->ppwfx_size = 10;
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size); winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
if (!winmm->ppwfx) if (!winmm->ppwfx)
{ {
@ -516,7 +552,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
goto error_out; goto error_out;
} }
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)winmm))) if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
{ {
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!", WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
error); error);

View File

@ -162,9 +162,6 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
for (i = 0; i < audin->context.num_server_formats; i++) for (i = 0; i < audin->context.num_server_formats; i++)
{ {
AUDIO_FORMAT format = audin->context.server_formats[i]; AUDIO_FORMAT format = audin->context.server_formats[i];
// TODO: Eliminate this
format.nAvgBytesPerSec =
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
if (!audio_format_write(s, &format)) if (!audio_format_write(s, &format))
{ {
@ -560,6 +557,8 @@ static BOOL audin_server_open(audin_server_context* context)
PULONG pSessionId = NULL; PULONG pSessionId = NULL;
DWORD BytesReturned = 0; DWORD BytesReturned = 0;
audin->SessionId = WTS_CURRENT_SESSION; audin->SessionId = WTS_CURRENT_SESSION;
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned)) (LPSTR*)&pSessionId, &BytesReturned))
@ -577,6 +576,15 @@ static BOOL audin_server_open(audin_server_context* context)
return FALSE; return FALSE;
} }
channelId = WTSChannelGetIdByHandle(audin->audin_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{ {
WLog_ERR(TAG, "CreateEvent failed!"); WLog_ERR(TAG, "CreateEvent failed!");

View File

@ -231,10 +231,8 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS
do do
{ {
char* p[5]; BOOL used = FALSE;
FREERDP_ADDIN* pAddin; FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
nDashes = 0;
pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
if (!pAddin) if (!pAddin)
{ {
@ -242,57 +240,116 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS
goto error_out; goto error_out;
} }
nDashes = 0;
for (index = 0; FindData.cFileName[index]; index++) for (index = 0; FindData.cFileName[index]; index++)
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0; nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
if (nDashes == 1) if (nDashes == 1)
{ {
size_t len;
char* p[2] = { 0 };
/* <name>-client.<extension> */ /* <name>-client.<extension> */
p[0] = FindData.cFileName; p[0] = FindData.cFileName;
p[1] = strchr(p[0], '-') + 1; p[1] = strchr(p[0], '-') + 1;
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
len = p[1] - p[0];
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
goto skip;
}
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_NAME;
ppAddins[nAddins++] = pAddin; ppAddins[nAddins++] = pAddin;
used = TRUE;
} }
else if (nDashes == 2) else if (nDashes == 2)
{ {
size_t len;
char* p[4] = { 0 };
/* <name>-client-<subsystem>.<extension> */ /* <name>-client-<subsystem>.<extension> */
p[0] = FindData.cFileName; p[0] = FindData.cFileName;
p[1] = strchr(p[0], '-') + 1; p[1] = strchr(p[0], '-') + 1;
p[2] = strchr(p[1], '-') + 1; p[2] = strchr(p[1], '-') + 1;
p[3] = strchr(p[2], '.') + 1; p[3] = strchr(p[2], '.') + 1;
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1); len = p[1] - p[0];
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
goto skip;
}
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
len = p[3] - p[2];
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
goto skip;
}
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_NAME;
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM; pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
ppAddins[nAddins++] = pAddin; ppAddins[nAddins++] = pAddin;
used = TRUE;
} }
else if (nDashes == 3) else if (nDashes == 3)
{ {
size_t len;
char* p[5] = { 0 };
/* <name>-client-<subsystem>-<type>.<extension> */ /* <name>-client-<subsystem>-<type>.<extension> */
p[0] = FindData.cFileName; p[0] = FindData.cFileName;
p[1] = strchr(p[0], '-') + 1; p[1] = strchr(p[0], '-') + 1;
p[2] = strchr(p[1], '-') + 1; p[2] = strchr(p[1], '-') + 1;
p[3] = strchr(p[2], '-') + 1; p[3] = strchr(p[2], '-') + 1;
p[4] = strchr(p[3], '.') + 1; p[4] = strchr(p[3], '.') + 1;
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1); len = p[1] - p[0];
strncpy(pAddin->cType, p[3], (p[4] - p[3]) - 1); if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
goto skip;
}
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
len = p[3] - p[2];
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
goto skip;
}
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
len = p[4] - p[3];
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
goto skip;
}
strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_NAME;
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM; pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
pAddin->dwFlags |= FREERDP_ADDIN_TYPE; pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
ppAddins[nAddins++] = pAddin; ppAddins[nAddins++] = pAddin;
used = TRUE;
} }
else
{ skip:
if (!used)
free(pAddin); free(pAddin);
}
} while (FindNextFileA(hFind, &FindData)); } while (FindNextFileA(hFind, &FindData));
FindClose(hFind); FindClose(hFind);

View File

@ -173,198 +173,3 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
return error; return error;
} }
static UINT64 filetime_to_uint64(FILETIME value)
{
UINT64 converted = 0;
converted |= (UINT32)value.dwHighDateTime;
converted <<= 32;
converted |= (UINT32)value.dwLowDateTime;
return converted;
}
static FILETIME uint64_to_filetime(UINT64 value)
{
FILETIME converted;
converted.dwLowDateTime = (UINT32)(value >> 0);
converted.dwHighDateTime = (UINT32)(value >> 32);
return converted;
}
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
/**
* Parse a packed file list.
*
* The resulting array must be freed with the `free()` function.
*
* @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
* @param [in] format_data_length length of `format_data` in bytes.
* @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
* @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
*
* @returns 0 on success, otherwise a Win32 error code.
*/
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
{
UINT result = NO_ERROR;
UINT32 i;
UINT32 count = 0;
wStream* s = NULL;
if (!format_data || !file_descriptor_array || !file_descriptor_count)
return ERROR_BAD_ARGUMENTS;
s = Stream_New((BYTE*)format_data, format_data_length);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
if (Stream_GetRemainingLength(s) < 4)
{
WLog_ERR(TAG, "invalid packed file list");
result = ERROR_INCORRECT_SIZE;
goto out;
}
Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
{
WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
result = ERROR_INCORRECT_SIZE;
goto out;
}
*file_descriptor_count = count;
*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
if (!*file_descriptor_array)
{
result = ERROR_NOT_ENOUGH_MEMORY;
goto out;
}
for (i = 0; i < count; i++)
{
int c;
UINT64 lastWriteTime;
FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Stream_Seek(s, 32); /* reserved1 (32 bytes) */
Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Seek(s, 16); /* reserved2 (16 bytes) */
Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Stream_Read_UINT16(s, file->cFileName[c]);
}
if (Stream_GetRemainingLength(s) > 0)
WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
Stream_GetRemainingLength(s));
out:
Stream_Free(s, FALSE);
return result;
}
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
/**
* Serialize a packed file list.
*
* The resulting format data must be freed with the `free()` function.
*
* @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
* @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
* @param [out] format_data serialized CLIPRDR_FILELIST.
* @param [out] format_data_length length of `format_data` in bytes.
*
* @returns 0 on success, otherwise a Win32 error code.
*/
UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length)
{
return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
file_descriptor_count, format_data, format_data_length);
}
UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length)
{
UINT result = NO_ERROR;
UINT32 i;
wStream* s = NULL;
if (!file_descriptor_array || !format_data || !format_data_length)
return ERROR_BAD_ARGUMENTS;
if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
{
WLog_WARN(TAG, "No file clipboard support annouonced!");
return ERROR_BAD_ARGUMENTS;
}
s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
for (i = 0; i < file_descriptor_count; i++)
{
int c;
UINT64 lastWriteTime;
const FILEDESCRIPTORW* file = &file_descriptor_array[i];
/*
* There is a known issue with Windows server getting stuck in
* an infinite loop when downloading files that are larger than
* 2 gigabytes. Do not allow clients to send such file lists.
*
* https://support.microsoft.com/en-us/help/2258090
*/
if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
{
if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
{
WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
result = ERROR_FILE_TOO_LARGE;
goto error;
}
}
Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Stream_Zero(s, 32); /* reserved1 (32 bytes) */
Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Zero(s, 16); /* reserved2 (16 bytes) */
lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Stream_Write_UINT16(s, file->cFileName[c]);
}
Stream_SealLength(s);
Stream_GetBuffer(s, *format_data);
Stream_GetLength(s, *format_data_length);
Stream_Free(s, FALSE);
return result;
error:
Stream_Free(s, TRUE);
return result;
}

View File

@ -138,7 +138,7 @@ cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockCl
if (!unlockClipboardData) if (!unlockClipboardData)
return NULL; return NULL;
s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4); s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
if (!s) if (!s)
return NULL; return NULL;

View File

@ -58,7 +58,7 @@ static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
} }
header.type = type; header.type = type;
header.length = length; header.length = DISPLAY_CONTROL_HEADER_LENGTH + length;
if ((error = disp_write_header(s, &header))) if ((error = disp_write_header(s, &header)))
{ {
@ -72,6 +72,24 @@ error:
return NULL; return NULL;
} }
static void disp_server_sanitize_monitor_layout(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
{
if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH ||
monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH ||
monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT ||
monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT)
{
if (monitor->PhysicalWidth != 0 || monitor->PhysicalHeight != 0)
WLog_DBG(
TAG,
"Sanitizing invalid physical monitor size. Old physical monitor size: [%" PRIu32
", %" PRIu32 "]",
monitor->PhysicalWidth, monitor->PhysicalHeight);
monitor->PhysicalWidth = monitor->PhysicalHeight = 0;
}
}
static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor) static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
{ {
if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH || if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
@ -88,22 +106,6 @@ static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT*
return FALSE; return FALSE;
} }
if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH ||
monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH)
{
WLog_WARN(TAG, "Received invalid value for monitor->PhysicalWidth: %" PRIu32 "",
monitor->PhysicalWidth);
return FALSE;
}
if (monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT ||
monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT)
{
WLog_WARN(TAG, "Received invalid value for monitor->Height: %" PRIu32 "",
monitor->PhysicalHeight);
return FALSE;
}
switch (monitor->Orientation) switch (monitor->Orientation)
{ {
case ORIENTATION_LANDSCAPE: case ORIENTATION_LANDSCAPE:
@ -183,6 +185,8 @@ static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerC
Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */ Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */
Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */ Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */ Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
disp_server_sanitize_monitor_layout(monitor);
WLog_DBG(TAG, WLog_DBG(TAG,
"\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32 "\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32
"x%" PRIu32 ")", "x%" PRIu32 ")",
@ -374,6 +378,8 @@ static UINT disp_server_open(DispServerContext* context)
void* buffer; void* buffer;
buffer = NULL; buffer = NULL;
priv->SessionId = WTS_CURRENT_SESSION; priv->SessionId = WTS_CURRENT_SESSION;
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE) (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
@ -395,6 +401,16 @@ static UINT disp_server_open(DispServerContext* context)
goto out_close; goto out_close;
} }
channelId = WTSChannelGetIdByHandle(priv->disp_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
/* Query for channel event handle */ /* Query for channel event handle */
if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer, if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) || &BytesReturned) ||

View File

@ -61,10 +61,14 @@
} while (0) } while (0)
#endif #endif
static void drive_file_fix_path(WCHAR* path) static BOOL drive_file_fix_path(WCHAR* path, size_t length)
{ {
size_t i; size_t i;
size_t length = _wcslen(path);
if ((length == 0) || (length > UINT32_MAX))
return FALSE;
WINPR_ASSERT(path);
for (i = 0; i < length; i++) for (i = 0; i < length; i++)
{ {
@ -75,58 +79,82 @@ static void drive_file_fix_path(WCHAR* path)
#ifdef WIN32 #ifdef WIN32
if ((length == 3) && (path[1] == L':') && (path[2] == L'/')) if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
return; return FALSE;
#else #else
if ((length == 1) && (path[0] == L'/')) if ((length == 1) && (path[0] == L'/'))
return; return FALSE;
#endif #endif
if ((length > 0) && (path[length - 1] == L'/')) if ((length > 0) && (path[length - 1] == L'/'))
path[length - 1] = L'\0'; path[length - 1] = L'\0';
return TRUE;
} }
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path, static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
size_t PathLength) size_t PathWCharLength)
{ {
WCHAR* fullpath; BOOL ok = FALSE;
size_t base_path_length; WCHAR* fullpath = NULL;
size_t length;
if (!base_path || (!path && (PathLength > 0))) if (!base_path || (!path && (PathWCharLength > 0)))
return NULL; goto fail;
base_path_length = _wcslen(base_path) * 2; const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR)); length = base_path_length + PathWCharLength + 1;
fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
if (!fullpath) if (!fullpath)
goto fail;
CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
if (path)
CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
if (!drive_file_fix_path(fullpath, length))
goto fail;
/* Ensure the path does not contain sequences like '..' */
const WCHAR dotdot[] = { '.', '.', '\0' };
if (_wcsstr(&fullpath[base_path_length], dotdot))
{ {
WLog_ERR(TAG, "malloc failed!"); char abuffer[MAX_PATH] = { 0 };
return NULL; ConvertFromUnicode(CP_UTF8, 0, &fullpath[base_path_length], -1, (char**)&abuffer,
ARRAYSIZE(abuffer) - 1, NULL, NULL);
WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
&abuffer[base_path_length]);
goto fail;
} }
CopyMemory(fullpath, base_path, base_path_length); ok = TRUE;
if (path) fail:
CopyMemory((char*)fullpath + base_path_length, path, PathLength); if (!ok)
drive_file_fix_path(fullpath); {
free(fullpath);
fullpath = NULL;
}
return fullpath; return fullpath;
} }
static BOOL drive_file_remove_dir(const WCHAR* path) static BOOL drive_file_remove_dir(const WCHAR* path)
{ {
WIN32_FIND_DATAW findFileData; WIN32_FIND_DATAW findFileData = { 0 };
BOOL ret = TRUE; BOOL ret = TRUE;
HANDLE dir; HANDLE dir = INVALID_HANDLE_VALUE;
WCHAR* fullpath; WCHAR* fullpath = NULL;
WCHAR* path_slash; WCHAR* path_slash = NULL;
size_t base_path_length; size_t base_path_length = 0;
if (!path) if (!path)
return FALSE; return FALSE;
base_path_length = _wcslen(path) * 2; base_path_length = _wcslen(path);
path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3); path_slash = (WCHAR*)calloc(base_path_length + 3, sizeof(WCHAR));
if (!path_slash) if (!path_slash)
{ {
@ -134,12 +162,11 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
return FALSE; return FALSE;
} }
CopyMemory(path_slash, path, base_path_length); CopyMemory(path_slash, path, base_path_length * sizeof(WCHAR));
path_slash[base_path_length / 2] = L'/'; path_slash[base_path_length] = L'/';
path_slash[base_path_length / 2 + 1] = L'*'; path_slash[base_path_length + 1] = L'*';
DEBUG_WSTR("Search in %s", path_slash); DEBUG_WSTR("Search in %s", path_slash);
dir = FindFirstFileW(path_slash, &findFileData); dir = FindFirstFileW(path_slash, &findFileData);
path_slash[base_path_length / 2 + 1] = 0;
if (dir == INVALID_HANDLE_VALUE) if (dir == INVALID_HANDLE_VALUE)
{ {
@ -149,7 +176,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
do do
{ {
size_t len = _wcslen(findFileData.cFileName); const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
if ((len == 1 && findFileData.cFileName[0] == L'.') || if ((len == 1 && findFileData.cFileName[0] == L'.') ||
(len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.')) (len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
@ -157,7 +184,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
continue; continue;
} }
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2); fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len);
DEBUG_WSTR("Delete %s", fullpath); DEBUG_WSTR("Delete %s", fullpath);
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@ -333,13 +360,13 @@ static BOOL drive_file_init(DRIVE_FILE* file)
return file->file_handle != INVALID_HANDLE_VALUE; return file->file_handle != INVALID_HANDLE_VALUE;
} }
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id, DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions, UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
UINT32 FileAttributes, UINT32 SharedAccess) UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
{ {
DRIVE_FILE* file; DRIVE_FILE* file;
if (!base_path || (!path && (PathLength > 0))) if (!base_path || (!path && (PathWCharLength > 0)))
return NULL; return NULL;
file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE)); file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
@ -359,7 +386,7 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat
file->CreateDisposition = CreateDisposition; file->CreateDisposition = CreateDisposition;
file->CreateOptions = CreateOptions; file->CreateOptions = CreateOptions;
file->SharedAccess = SharedAccess; file->SharedAccess = SharedAccess;
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength)); drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength));
if (!drive_file_init(file)) if (!drive_file_init(file))
{ {
@ -714,13 +741,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
return FALSE; return FALSE;
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input), fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
FileNameLength); FileNameLength / sizeof(WCHAR));
if (!fullpath) if (!fullpath)
{
WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
return FALSE; return FALSE;
}
#ifdef _WIN32 #ifdef _WIN32
@ -759,7 +783,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
} }
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
const WCHAR* path, UINT32 PathLength, wStream* output) const WCHAR* path, UINT32 PathWCharLength, wStream* output)
{ {
size_t length; size_t length;
WCHAR* ent_path; WCHAR* ent_path;
@ -773,7 +797,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
if (file->find_handle != INVALID_HANDLE_VALUE) if (file->find_handle != INVALID_HANDLE_VALUE)
FindClose(file->find_handle); FindClose(file->find_handle);
ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength); ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength);
/* open new search handle and retrieve the first entry */ /* open new search handle and retrieve the first entry */
file->find_handle = FindFirstFileW(ent_path, &file->find_data); file->find_handle = FindFirstFileW(ent_path, &file->find_data);
free(ent_path); free(ent_path);

View File

@ -51,9 +51,9 @@ struct _DRIVE_FILE
UINT32 CreateOptions; UINT32 CreateOptions;
}; };
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id, DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions, UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
UINT32 FileAttributes, UINT32 SharedAccess); UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
BOOL drive_file_free(DRIVE_FILE* file); BOOL drive_file_free(DRIVE_FILE* file);
BOOL drive_file_open(DRIVE_FILE* file); BOOL drive_file_open(DRIVE_FILE* file);
@ -64,6 +64,6 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
wStream* input); wStream* input);
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
const WCHAR* path, UINT32 PathLength, wStream* output); const WCHAR* path, UINT32 PathWCharLength, wStream* output);
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */ #endif /* FREERDP_CHANNEL_DRIVE_FILE_H */

View File

@ -184,8 +184,8 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
path = (const WCHAR*)Stream_Pointer(irp->input); path = (const WCHAR*)Stream_Pointer(irp->input);
FileId = irp->devman->id_sequence++; FileId = irp->devman->id_sequence++;
file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition, file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
CreateOptions, FileAttributes, SharedAccess); CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
if (!file) if (!file)
{ {
@ -629,6 +629,9 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
Stream_Read_UINT32(irp->input, PathLength); Stream_Read_UINT32(irp->input, PathLength);
Stream_Seek(irp->input, 23); /* Padding */ Stream_Seek(irp->input, 23); /* Padding */
path = (WCHAR*)Stream_Pointer(irp->input); path = (WCHAR*)Stream_Pointer(irp->input);
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
return ERROR_INVALID_DATA;
file = drive_get_file_by_id(drive, irp->FileId); file = drive_get_file_by_id(drive, irp->FileId);
if (file == NULL) if (file == NULL)
@ -636,8 +639,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
irp->IoStatus = STATUS_UNSUCCESSFUL; irp->IoStatus = STATUS_UNSUCCESSFUL;
Stream_Write_UINT32(irp->output, 0); /* Length */ Stream_Write_UINT32(irp->output, 0); /* Length */
} }
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength, else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
irp->output)) PathLength / sizeof(WCHAR), irp->output))
{ {
irp->IoStatus = drive_map_windows_err(GetLastError()); irp->IoStatus = drive_map_windows_err(GetLastError());
} }

View File

@ -91,7 +91,21 @@ static UINT echo_server_open_channel(echo_server* echo)
WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC); WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
if (echo->echo_channel) if (echo->echo_channel)
{
UINT32 channelId;
BOOL status = TRUE;
channelId = WTSChannelGetIdByHandle(echo->echo_channel);
IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
break; break;
}
Error = GetLastError(); Error = GetLastError();

View File

@ -25,9 +25,10 @@ include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
if (NOT BUILTIN_CHANNELS OR NOT BUILD_SHARED_LIBS)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
endif()
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})

View File

@ -159,7 +159,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT32(irp->input, Length); Stream_Read_UINT32(irp->input, Length);
Stream_Read_UINT64(irp->input, Offset); Stream_Read_UINT64(irp->input, Offset);
buffer = (BYTE*)malloc(Length); buffer = (BYTE*)calloc(Length, sizeof(BYTE));
if (!buffer) if (!buffer)
{ {
@ -178,6 +178,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
} }
else else
{ {
Length = status;
} }
Stream_Write_UINT32(irp->output, Length); Stream_Write_UINT32(irp->output, Length);

View File

@ -33,6 +33,7 @@
#include <cups/cups.h> #include <cups/cups.h>
#include <winpr/crt.h> #include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/string.h> #include <winpr/string.h>
#include <freerdp/channels/rdpdr.h> #include <freerdp/channels/rdpdr.h>
@ -92,7 +93,7 @@ static UINT printer_cups_write_printjob(rdpPrintJob* printjob, const BYTE* data,
{ {
FILE* fp; FILE* fp;
fp = fopen((const char*)cups_printjob->printjob_object, "a+b"); fp = winpr_fopen((const char*)cups_printjob->printjob_object, "a+b");
if (!fp) if (!fp)
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
@ -402,8 +403,8 @@ FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver; uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver;
uniq_cups_driver->id_sequence = 1; uniq_cups_driver->id_sequence = 1;
uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver);
} }
uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver);
return &uniq_cups_driver->driver; return &uniq_cups_driver->driver;
} }

View File

@ -82,9 +82,9 @@ static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* n
char* bname = crypto_base64_encode((const BYTE*)name, (int)length); char* bname = crypto_base64_encode((const BYTE*)name, (int)length);
char* config = GetCombinedPath(dir, bname); char* config = GetCombinedPath(dir, bname);
if (config && !PathFileExistsA(config)) if (config && !winpr_PathFileExists(config))
{ {
if (!PathMakePathA(config, NULL)) if (!winpr_PathMakePath(config, NULL))
{ {
free(config); free(config);
config = NULL; config = NULL;
@ -145,7 +145,7 @@ static BOOL printer_config_valid(const char* path)
if (!path) if (!path)
return FALSE; return FALSE;
if (!PathFileExistsA(path)) if (!winpr_PathFileExists(path))
return FALSE; return FALSE;
return TRUE; return TRUE;
@ -261,7 +261,7 @@ static BOOL printer_remove_config(const rdpSettings* settings, const WCHAR* name
if (!printer_config_valid(path)) if (!printer_config_valid(path))
goto fail; goto fail;
rc = RemoveDirectoryA(path); rc = winpr_RemoveDirectory(path);
fail: fail:
free(path); free(path);
return rc; return rc;
@ -275,7 +275,7 @@ static BOOL printer_move_config(const rdpSettings* settings, const WCHAR* oldNam
char* newPath = get_printer_config_path(settings, newName, newLength); char* newPath = get_printer_config_path(settings, newName, newLength);
if (printer_config_valid(oldPath)) if (printer_config_valid(oldPath))
rc = MoveFileA(oldPath, newPath); rc = winpr_MoveFile(oldPath, newPath);
free(oldPath); free(oldPath);
free(newPath); free(newPath);
@ -979,7 +979,7 @@ printer_DeviceServiceEntry
device = (RDPDR_PRINTER*)pEntryPoints->device; device = (RDPDR_PRINTER*)pEntryPoints->device;
name = device->Name; name = device->Name;
driver_name = device->DriverName; driver_name = _strdup(device->DriverName);
/* Secondary argument is one of the following: /* Secondary argument is one of the following:
* *
@ -1016,7 +1016,8 @@ printer_DeviceServiceEntry
if (!driver) if (!driver)
{ {
WLog_ERR(TAG, "Could not get a printer driver!"); WLog_ERR(TAG, "Could not get a printer driver!");
return CHANNEL_RC_INITIALIZATION_ERROR; error = CHANNEL_RC_INITIALIZATION_ERROR;
goto fail;
} }
if (name && name[0]) if (name && name[0])
@ -1064,7 +1065,9 @@ printer_DeviceServiceEntry
} }
fail: fail:
driver->ReleaseRef(driver); free(driver_name);
if (driver)
driver->ReleaseRef(driver);
return error; return error;
} }

View File

@ -452,8 +452,8 @@ FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
win_driver->driver.ReleaseRef = printer_win_release_ref_driver; win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
win_driver->id_sequence = 1; win_driver->id_sequence = 1;
win_driver->driver.AddRef(&win_driver->driver);
} }
win_driver->driver.AddRef(&win_driver->driver);
return &win_driver->driver; return &win_driver->driver;
} }

View File

@ -32,6 +32,8 @@
#include "rail_orders.h" #include "rail_orders.h"
static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask);
/** /**
* Function description * Function description
* *
@ -39,6 +41,7 @@
*/ */
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType) UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
{ {
char buffer[128] = { 0 };
UINT16 orderLength; UINT16 orderLength;
if (!rail || !s) if (!rail || !s)
@ -49,7 +52,7 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
rail_write_pdu_header(s, orderType, orderLength); rail_write_pdu_header(s, orderType, orderLength);
Stream_SetPosition(s, orderLength); Stream_SetPosition(s, orderLength);
WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "", WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "",
rail_get_order_type_string(orderType), orderLength); rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
return rail_send_channel_data(rail, s); return rail_send_channel_data(rail, s);
} }
@ -388,7 +391,46 @@ static UINT rail_recv_handshake_order(railPlugin* rail, wStream* s)
return error; return error;
} }
static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask) static UINT rail_read_compartment_info_order(wStream* s,
RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */
Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */
Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */
Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */
return CHANNEL_RC_OK;
}
static UINT rail_recv_compartmentinfo_order(railPlugin* rail, wStream* s)
{
RailClientContext* context = rail_get_client_interface(rail);
RAIL_COMPARTMENT_INFO_ORDER pdu = { 0 };
UINT error;
if (!context || !s)
return ERROR_INVALID_PARAMETER;
if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
return ERROR_BAD_CONFIGURATION;
if ((error = rail_read_compartment_info_order(s, &pdu)))
return error;
if (context->custom)
{
IFCALLRET(context->ClientCompartmentInfo, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error);
}
return error;
}
BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
{ {
UINT32 supported, masked; UINT32 supported, masked;
@ -400,7 +442,15 @@ static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureM
masked = (supported & featureMask); masked = (supported & featureMask);
if (masked != featureMask) if (masked != featureMask)
{
char mask[256] = { 0 };
char actual[256] = { 0 };
WLog_WARN(TAG, "[%s] have %s, require %s", __func__,
freerdp_rail_support_flags_to_string(supported, actual, sizeof(actual)),
freerdp_rail_support_flags_to_string(featureMask, mask, sizeof(mask)));
return FALSE; return FALSE;
}
return TRUE; return TRUE;
} }
@ -904,6 +954,73 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai
return error; return error;
} }
static UINT rail_read_textscaleinfo_order(wStream* s, UINT32* pTextScaleFactor)
{
WINPR_ASSERT(pTextScaleFactor);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, *pTextScaleFactor);
return CHANNEL_RC_OK;
}
static UINT rail_recv_textscaleinfo_order(railPlugin* rail, wStream* s)
{
RailClientContext* context = rail_get_client_interface(rail);
UINT32 TextScaleFactor = 0;
UINT error;
if (!context)
return ERROR_INVALID_PARAMETER;
if ((error = rail_read_textscaleinfo_order(s, &TextScaleFactor)))
return error;
if (context->custom)
{
IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor);
if (error)
WLog_ERR(TAG, "context.ClientTextScale failed with error %" PRIu32 "", error);
}
return error;
}
static UINT rail_read_caretblinkinfo_order(wStream* s, UINT32* pCaretBlinkRate)
{
WINPR_ASSERT(pCaretBlinkRate);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, *pCaretBlinkRate);
return CHANNEL_RC_OK;
}
static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s)
{
RailClientContext* context = rail_get_client_interface(rail);
UINT32 CaretBlinkRate = 0;
UINT error;
if (!context)
return ERROR_INVALID_PARAMETER;
if ((error = rail_read_caretblinkinfo_order(s, &CaretBlinkRate)))
return error;
if (context->custom)
{
IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate);
if (error)
WLog_ERR(TAG, "context.ClientCaretBlinkRate failed with error %" PRIu32 "", error);
}
return error;
}
/** /**
* Function description * Function description
* *
@ -911,9 +1028,10 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai
*/ */
UINT rail_order_recv(railPlugin* rail, wStream* s) UINT rail_order_recv(railPlugin* rail, wStream* s)
{ {
char buffer[128] = { 0 };
UINT16 orderType; UINT16 orderType;
UINT16 orderLength; UINT16 orderLength;
UINT error; UINT error = CHANNEL_RC_OK;
if (!rail || !s) if (!rail || !s)
return ERROR_INVALID_PARAMETER; return ERROR_INVALID_PARAMETER;
@ -925,55 +1043,88 @@ UINT rail_order_recv(railPlugin* rail, wStream* s)
} }
WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "", WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "",
rail_get_order_type_string(orderType), orderLength); rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
switch (orderType) switch (orderType)
{ {
case TS_RAIL_ORDER_HANDSHAKE: case TS_RAIL_ORDER_HANDSHAKE:
return rail_recv_handshake_order(rail, s); error = rail_recv_handshake_order(rail, s);
break;
case TS_RAIL_ORDER_COMPARTMENTINFO:
error = rail_recv_compartmentinfo_order(rail, s);
break;
case TS_RAIL_ORDER_HANDSHAKE_EX: case TS_RAIL_ORDER_HANDSHAKE_EX:
return rail_recv_handshake_ex_order(rail, s); error = rail_recv_handshake_ex_order(rail, s);
break;
case TS_RAIL_ORDER_EXEC_RESULT: case TS_RAIL_ORDER_EXEC_RESULT:
return rail_recv_exec_result_order(rail, s); error = rail_recv_exec_result_order(rail, s);
break;
case TS_RAIL_ORDER_SYSPARAM: case TS_RAIL_ORDER_SYSPARAM:
return rail_recv_server_sysparam_order(rail, s); error = rail_recv_server_sysparam_order(rail, s);
break;
case TS_RAIL_ORDER_MINMAXINFO: case TS_RAIL_ORDER_MINMAXINFO:
return rail_recv_server_minmaxinfo_order(rail, s); error = rail_recv_server_minmaxinfo_order(rail, s);
break;
case TS_RAIL_ORDER_LOCALMOVESIZE: case TS_RAIL_ORDER_LOCALMOVESIZE:
return rail_recv_server_localmovesize_order(rail, s); error = rail_recv_server_localmovesize_order(rail, s);
break;
case TS_RAIL_ORDER_GET_APPID_RESP: case TS_RAIL_ORDER_GET_APPID_RESP:
return rail_recv_server_get_appid_resp_order(rail, s); error = rail_recv_server_get_appid_resp_order(rail, s);
break;
case TS_RAIL_ORDER_LANGBARINFO: case TS_RAIL_ORDER_LANGBARINFO:
return rail_recv_langbar_info_order(rail, s); error = rail_recv_langbar_info_order(rail, s);
break;
case TS_RAIL_ORDER_TASKBARINFO: case TS_RAIL_ORDER_TASKBARINFO:
return rail_recv_taskbar_info_order(rail, s); error = rail_recv_taskbar_info_order(rail, s);
break;
case TS_RAIL_ORDER_ZORDER_SYNC: case TS_RAIL_ORDER_ZORDER_SYNC:
return rail_recv_zorder_sync_order(rail, s); error = rail_recv_zorder_sync_order(rail, s);
break;
case TS_RAIL_ORDER_CLOAK: case TS_RAIL_ORDER_CLOAK:
return rail_recv_cloak_order(rail, s); error = rail_recv_cloak_order(rail, s);
break;
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST: case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
return rail_recv_power_display_request_order(rail, s); error = rail_recv_power_display_request_order(rail, s);
break;
case TS_RAIL_ORDER_GET_APPID_RESP_EX: case TS_RAIL_ORDER_GET_APPID_RESP_EX:
return rail_recv_get_application_id_extended_response_order(rail, s); error = rail_recv_get_application_id_extended_response_order(rail, s);
break;
case TS_RAIL_ORDER_TEXTSCALEINFO:
error = rail_recv_textscaleinfo_order(rail, s);
break;
case TS_RAIL_ORDER_CARETBLINKINFO:
error = rail_recv_caretblinkinfo_order(rail, s);
break;
default: default:
WLog_ERR(TAG, "Unknown RAIL PDU order reveived."); WLog_ERR(TAG, "Unknown RAIL PDU %s received.",
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)));
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
} }
return CHANNEL_RC_OK; if (error != CHANNEL_RC_OK)
{
char buffer[128] = { 0 };
WLog_Print(rail->log, WLOG_ERROR, "Failed to process rail %s PDU, length:%" PRIu16 "",
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
}
return error;
} }
/** /**
@ -1358,6 +1509,9 @@ UINT rail_send_client_compartment_info_order(railPlugin* rail,
if (!rail || !compartmentInfo) if (!rail || !compartmentInfo)
return ERROR_INVALID_PARAMETER; return ERROR_INVALID_PARAMETER;
if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
return ERROR_BAD_CONFIGURATION;
s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH); s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH);
if (!s) if (!s)

View File

@ -27,37 +27,72 @@
#define TAG CHANNELS_TAG("rail.common") #define TAG CHANNELS_TAG("rail.common")
static const char* const RAIL_ORDER_TYPE_STRINGS[] = { "",
"Execute",
"Activate",
"System Parameters Update",
"System Command",
"Handshake",
"Notify Event",
"",
"Window Move",
"Local Move/Size",
"Min Max Info",
"Client Status",
"System Menu",
"Language Bar Info",
"Get Application ID Request",
"Get Application ID Response",
"Execute Result",
"",
"",
"",
"",
"",
"" };
const char* rail_get_order_type_string(UINT16 orderType) const char* rail_get_order_type_string(UINT16 orderType)
{ {
UINT32 index = ((orderType & 0xF0) >> 3) + (orderType & 0x0F); switch (orderType)
if (index >= ARRAYSIZE(RAIL_ORDER_TYPE_STRINGS)) {
return "UNKNOWN"; case TS_RAIL_ORDER_EXEC:
return "TS_RAIL_ORDER_EXEC";
case TS_RAIL_ORDER_ACTIVATE:
return "TS_RAIL_ORDER_ACTIVATE";
case TS_RAIL_ORDER_SYSPARAM:
return "TS_RAIL_ORDER_SYSPARAM";
case TS_RAIL_ORDER_SYSCOMMAND:
return "TS_RAIL_ORDER_SYSCOMMAND";
case TS_RAIL_ORDER_HANDSHAKE:
return "TS_RAIL_ORDER_HANDSHAKE";
case TS_RAIL_ORDER_NOTIFY_EVENT:
return "TS_RAIL_ORDER_NOTIFY_EVENT";
case TS_RAIL_ORDER_WINDOWMOVE:
return "TS_RAIL_ORDER_WINDOWMOVE";
case TS_RAIL_ORDER_LOCALMOVESIZE:
return "TS_RAIL_ORDER_LOCALMOVESIZE";
case TS_RAIL_ORDER_MINMAXINFO:
return "TS_RAIL_ORDER_MINMAXINFO";
case TS_RAIL_ORDER_CLIENTSTATUS:
return "TS_RAIL_ORDER_CLIENTSTATUS";
case TS_RAIL_ORDER_SYSMENU:
return "TS_RAIL_ORDER_SYSMENU";
case TS_RAIL_ORDER_LANGBARINFO:
return "TS_RAIL_ORDER_LANGBARINFO";
case TS_RAIL_ORDER_GET_APPID_REQ:
return "TS_RAIL_ORDER_GET_APPID_REQ";
case TS_RAIL_ORDER_GET_APPID_RESP:
return "TS_RAIL_ORDER_GET_APPID_RESP";
case TS_RAIL_ORDER_TASKBARINFO:
return "TS_RAIL_ORDER_TASKBARINFO";
case TS_RAIL_ORDER_LANGUAGEIMEINFO:
return "TS_RAIL_ORDER_LANGUAGEIMEINFO";
case TS_RAIL_ORDER_COMPARTMENTINFO:
return "TS_RAIL_ORDER_COMPARTMENTINFO";
case TS_RAIL_ORDER_HANDSHAKE_EX:
return "TS_RAIL_ORDER_HANDSHAKE_EX";
case TS_RAIL_ORDER_ZORDER_SYNC:
return "TS_RAIL_ORDER_ZORDER_SYNC";
case TS_RAIL_ORDER_CLOAK:
return "TS_RAIL_ORDER_CLOAK";
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST";
case TS_RAIL_ORDER_SNAP_ARRANGE:
return "TS_RAIL_ORDER_SNAP_ARRANGE";
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
case TS_RAIL_ORDER_EXEC_RESULT:
return "TS_RAIL_ORDER_EXEC_RESULT";
case TS_RAIL_ORDER_TEXTSCALEINFO:
return "TS_RAIL_ORDER_TEXTSCALEINFO";
case TS_RAIL_ORDER_CARETBLINKINFO:
return "TS_RAIL_ORDER_CARETBLINKINFO";
default:
return "TS_RAIL_ORDER_UNKNOWN";
}
}
return RAIL_ORDER_TYPE_STRINGS[index]; const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
{
_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
orderType);
return buffer;
} }
/** /**

View File

@ -71,5 +71,6 @@ UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
BOOL extendedSpiSupported); BOOL extendedSpiSupported);
BOOL rail_is_extended_spi_supported(UINT32 channelsFlags); BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
const char* rail_get_order_type_string(UINT16 orderType); const char* rail_get_order_type_string(UINT16 orderType);
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length);
#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */ #endif /* FREERDP_CHANNEL_RAIL_COMMON_H */

View File

@ -62,6 +62,7 @@ static UINT rail_send(RailServerContext* context, wStream* s, ULONG length)
*/ */
static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType) static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType)
{ {
char buffer[128] = { 0 };
UINT16 orderLength; UINT16 orderLength;
if (!context || !s) if (!context || !s)
@ -71,8 +72,8 @@ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16
Stream_SetPosition(s, 0); Stream_SetPosition(s, 0);
rail_write_pdu_header(s, orderType, orderLength); rail_write_pdu_header(s, orderType, orderLength);
Stream_SetPosition(s, orderLength); Stream_SetPosition(s, orderLength);
WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", rail_get_order_type_string(orderType), WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "",
orderLength); rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
return rail_send(context, s, orderLength); return rail_send(context, s, orderLength);
} }
@ -1535,6 +1536,7 @@ void rail_server_set_handshake_ex_flags(RailServerContext* context, DWORD flags)
UINT rail_server_handle_messages(RailServerContext* context) UINT rail_server_handle_messages(RailServerContext* context)
{ {
char buffer[128] = { 0 };
UINT status = CHANNEL_RC_OK; UINT status = CHANNEL_RC_OK;
DWORD bytesReturned; DWORD bytesReturned;
UINT16 orderType; UINT16 orderType;
@ -1584,8 +1586,8 @@ UINT rail_server_handle_messages(RailServerContext* context)
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
} }
WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", rail_get_order_type_string(orderType), WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "",
orderLength); rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
switch (orderType) switch (orderType)
{ {

View File

@ -22,6 +22,6 @@ set(${MODULE_PREFIX}_SRCS
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx") add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
target_link_libraries(${MODULE_NAME} freerdp) target_link_libraries(${MODULE_NAME} winpr freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client") set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")

View File

@ -41,6 +41,8 @@
#include "devman.h" #include "devman.h"
#define TAG CHANNELS_TAG("rdpdr.client")
static void devman_device_free(void* obj) static void devman_device_free(void* obj)
{ {
DEVICE* device = (DEVICE*)obj; DEVICE* device = (DEVICE*)obj;
@ -62,7 +64,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
if (!devman) if (!devman)
{ {
WLog_INFO(TAG, "calloc failed!"); WLog_Print(rdpdr->log, WLOG_INFO, "calloc failed!");
return NULL; return NULL;
} }
@ -72,7 +74,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
if (!devman->devices) if (!devman->devices)
{ {
WLog_INFO(TAG, "ListDictionary_New failed!"); WLog_Print(rdpdr->log, WLOG_INFO, "ListDictionary_New failed!");
free(devman); free(devman);
return NULL; return NULL;
} }

View File

@ -76,13 +76,13 @@ static UINT irp_complete(IRP* irp)
return irp_free(irp); return irp_free(irp);
} }
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error) IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
{ {
IRP* irp; IRP* irp;
DEVICE* device; DEVICE* device;
UINT32 DeviceId; UINT32 DeviceId;
if (Stream_GetRemainingLength(s) < 20) if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20))
{ {
if (error) if (error)
*error = ERROR_INVALID_DATA; *error = ERROR_INVALID_DATA;
@ -94,7 +94,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
if (!device) if (!device)
{ {
WLog_WARN(TAG, "devman_get_device_by_id failed!"); WLog_Print(log, WLOG_WARN, "devman_get_device_by_id failed!");
if (error) if (error)
*error = CHANNEL_RC_OK; *error = CHANNEL_RC_OK;
@ -105,7 +105,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
if (!irp) if (!irp)
{ {
WLog_ERR(TAG, "_aligned_malloc failed!"); WLog_Print(log, WLOG_ERROR, "_aligned_malloc failed!");
if (error) if (error)
*error = CHANNEL_RC_NO_MEMORY; *error = CHANNEL_RC_NO_MEMORY;
return NULL; return NULL;
@ -125,7 +125,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
irp->output = Stream_New(NULL, 256); irp->output = Stream_New(NULL, 256);
if (!irp->output) if (!irp->output)
{ {
WLog_ERR(TAG, "Stream_New failed!"); WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
_aligned_free(irp); _aligned_free(irp);
if (error) if (error)
*error = CHANNEL_RC_NO_MEMORY; *error = CHANNEL_RC_NO_MEMORY;

View File

@ -21,8 +21,9 @@
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H #ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
#define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H #define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
#include <winpr/wlog.h>
#include "rdpdr_main.h" #include "rdpdr_main.h"
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error); IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error);
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */ #endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */

View File

@ -35,6 +35,8 @@
#include "rdpdr_main.h" #include "rdpdr_main.h"
#include "rdpdr_capabilities.h" #include "rdpdr_capabilities.h"
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
/* Output device redirection capability set header */ /* Output device redirection capability set header */
static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength, static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength,
UINT32 version) UINT32 version)
@ -48,39 +50,59 @@ static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16
static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s) static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
{ {
WINPR_UNUSED(rdpdr); WINPR_UNUSED(rdpdr);
rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, 44, GENERAL_CAPABILITY_VERSION_02);
Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */ const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1;
Stream_Write_UINT32(s, 0); /* osVersion, unused and must be set to zero */ const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2;
Stream_Write_UINT16(s, 1); /* protocolMajorVersion, must be set to 1 */
Stream_Write_UINT16(s, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinorVersion */ rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36,
Stream_Write_UINT32(s, 0x0000FFFF); /* ioCode1 */ GENERAL_CAPABILITY_VERSION_02);
Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */ Stream_Write_UINT32(s, rdpdr->clientOsType); /* osType, ignored on receipt */
Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */
RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */ Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */
Stream_Write_UINT32(s, ENABLE_ASYNCIO); /* extraFlags1 */ Stream_Write_UINT16(s, rdpdr->clientVersionMinor); /* protocolMinorVersion */
Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */ Stream_Write_UINT32(s, ioCode1); /* ioCode1 */
Stream_Write_UINT32(s, ioCode2); /* ioCode2, must be set to zero, reserved for future use */
Stream_Write_UINT32(s, rdpdr->clientExtendedPDU); /* extendedPDU */
Stream_Write_UINT32(s, rdpdr->clientExtraFlags1); /* extraFlags1 */
Stream_Write_UINT32( Stream_Write_UINT32(
s, 0); /* SpecialTypeDeviceCap, number of special devices to be redirected before logon */ s,
rdpdr->clientExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
Stream_Write_UINT32(
s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
be redirected before logon */
} }
/* Process device direction general capability set */ /* Process device direction general capability set */
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s) static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
{ {
UINT16 capabilityLength;
WINPR_UNUSED(rdpdr); WINPR_UNUSED(rdpdr);
if (Stream_GetRemainingLength(s) < 2) if (capabilityLength != 36)
{
WLog_Print(rdpdr->log, WLOG_ERROR,
"CAP_GENERAL_TYPE::CapabilityLength expected 36, got %" PRIu32,
capabilityLength);
return ERROR_INVALID_DATA;
}
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, capabilityLength); Stream_Read_UINT32(s, rdpdr->serverOsType); /* osType, ignored on receipt */
Stream_Read_UINT32(s, rdpdr->serverOsVersion); /* osVersion, unused and must be set to zero */
if (capabilityLength < 4) Stream_Read_UINT16(s, rdpdr->serverVersionMajor); /* protocolMajorVersion, must be set to 1 */
return ERROR_INVALID_DATA; Stream_Read_UINT16(s, rdpdr->serverVersionMinor); /* protocolMinorVersion */
Stream_Read_UINT32(s, rdpdr->serverIOCode1); /* ioCode1 */
if (Stream_GetRemainingLength(s) < capabilityLength - 4U) Stream_Read_UINT32(
return ERROR_INVALID_DATA; s, rdpdr->serverIOCode2); /* ioCode2, must be set to zero, reserved for future use */
Stream_Read_UINT32(s, rdpdr->serverExtendedPDU); /* extendedPDU */
Stream_Seek(s, capabilityLength - 4U); Stream_Read_UINT32(s, rdpdr->serverExtraFlags1); /* extraFlags1 */
Stream_Read_UINT32(
s,
rdpdr->serverExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
Stream_Read_UINT32(
s, rdpdr->serverSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
be redirected before logon */
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -92,23 +114,14 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
} }
/* Process printer direction capability set */ /* Process printer direction capability set */
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s) static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
{ {
UINT16 capabilityLength;
WINPR_UNUSED(rdpdr); WINPR_UNUSED(rdpdr);
if (Stream_GetRemainingLength(s) < 2) if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, capabilityLength); Stream_Seek(s, capabilityLength);
if (capabilityLength < 4)
return ERROR_INVALID_DATA;
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
return ERROR_INVALID_DATA;
Stream_Seek(s, capabilityLength - 4U);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -120,23 +133,14 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
} }
/* Process port redirection capability set */ /* Process port redirection capability set */
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s) static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
{ {
UINT16 capabilityLength;
WINPR_UNUSED(rdpdr); WINPR_UNUSED(rdpdr);
if (Stream_GetRemainingLength(s) < 2) if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, capabilityLength); Stream_Seek(s, capabilityLength);
if (capabilityLength < 4U)
return ERROR_INVALID_DATA;
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
return ERROR_INVALID_DATA;
Stream_Seek(s, capabilityLength - 4U);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -148,23 +152,14 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
} }
/* Process drive redirection capability set */ /* Process drive redirection capability set */
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s) static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
{ {
UINT16 capabilityLength;
WINPR_UNUSED(rdpdr); WINPR_UNUSED(rdpdr);
if (Stream_GetRemainingLength(s) < 2) if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, capabilityLength); Stream_Seek(s, capabilityLength);
if (capabilityLength < 4)
return ERROR_INVALID_DATA;
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
return ERROR_INVALID_DATA;
Stream_Seek(s, capabilityLength - 4U);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -176,23 +171,14 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
} }
/* Process smartcard redirection capability set */ /* Process smartcard redirection capability set */
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s) static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
{ {
UINT16 capabilityLength;
WINPR_UNUSED(rdpdr); WINPR_UNUSED(rdpdr);
if (Stream_GetRemainingLength(s) < 2) if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, capabilityLength); Stream_Seek(s, capabilityLength);
if (capabilityLength < 4)
return ERROR_INVALID_DATA;
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
return ERROR_INVALID_DATA;
Stream_Seek(s, capabilityLength - 4U);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -201,12 +187,14 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
UINT status = CHANNEL_RC_OK; UINT status = CHANNEL_RC_OK;
UINT16 i; UINT16 i;
UINT16 numCapabilities; UINT16 numCapabilities;
UINT16 capabilityType;
if (!rdpdr || !s) if (!rdpdr || !s)
return CHANNEL_RC_NULL_DATA; return CHANNEL_RC_NULL_DATA;
if (Stream_GetRemainingLength(s) < 4) WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_SERVER_CAPS);
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_CLIENT_CAPS);
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, numCapabilities); Stream_Read_UINT16(s, numCapabilities);
@ -214,31 +202,44 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
for (i = 0; i < numCapabilities; i++) for (i = 0; i < numCapabilities; i++)
{ {
if (Stream_GetRemainingLength(s) < sizeof(UINT16)) UINT16 capabilityType;
UINT16 capabilityLength;
UINT32 version = 0;
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s,
2 * sizeof(UINT16) + sizeof(UINT32)))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, capabilityType); Stream_Read_UINT16(s, capabilityType);
Stream_Read_UINT16(s, capabilityLength);
Stream_Read_UINT32(s, version);
if (capabilityLength < 8)
{
return ERROR_INVALID_DATA;
}
capabilityLength -= 8;
switch (capabilityType) switch (capabilityType)
{ {
case CAP_GENERAL_TYPE: case CAP_GENERAL_TYPE:
status = rdpdr_process_general_capset(rdpdr, s); status = rdpdr_process_general_capset(rdpdr, s, capabilityLength);
break; break;
case CAP_PRINTER_TYPE: case CAP_PRINTER_TYPE:
status = rdpdr_process_printer_capset(rdpdr, s); status = rdpdr_process_printer_capset(rdpdr, s, capabilityLength);
break; break;
case CAP_PORT_TYPE: case CAP_PORT_TYPE:
status = rdpdr_process_port_capset(rdpdr, s); status = rdpdr_process_port_capset(rdpdr, s, capabilityLength);
break; break;
case CAP_DRIVE_TYPE: case CAP_DRIVE_TYPE:
status = rdpdr_process_drive_capset(rdpdr, s); status = rdpdr_process_drive_capset(rdpdr, s, capabilityLength);
break; break;
case CAP_SMARTCARD_TYPE: case CAP_SMARTCARD_TYPE:
status = rdpdr_process_smartcard_capset(rdpdr, s); status = rdpdr_process_smartcard_capset(rdpdr, s, capabilityLength);
break; break;
default: default:
@ -264,7 +265,7 @@ UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
if (!s) if (!s)
{ {
WLog_ERR(TAG, "Stream_New failed!"); WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
return CHANNEL_RC_NO_MEMORY; return CHANNEL_RC_NO_MEMORY;
} }

File diff suppressed because it is too large Load Diff

View File

@ -42,15 +42,33 @@
#include <CoreServices/CoreServices.h> #include <CoreServices/CoreServices.h>
#endif #endif
#define TAG CHANNELS_TAG("rdpdr.client") #if !defined(Stream_CheckAndLogRequiredLengthWLog)
#define Stream_CheckAndLogRequiredLengthWLog(log, s, len) \
Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \
__FILE__, __LINE__)
#endif
typedef struct rdpdr_plugin rdpdrPlugin; typedef struct rdpdr_plugin rdpdrPlugin;
enum RDPDR_CHANNEL_STATE
{
RDPDR_CHANNEL_STATE_INITIAL = 0,
RDPDR_CHANNEL_STATE_ANNOUNCE,
RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY,
RDPDR_CHANNEL_STATE_NAME_REQUEST,
RDPDR_CHANNEL_STATE_SERVER_CAPS,
RDPDR_CHANNEL_STATE_CLIENT_CAPS,
RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM,
RDPDR_CHANNEL_STATE_READY,
RDPDR_CHANNEL_STATE_USER_LOGGEDON
};
struct rdpdr_plugin struct rdpdr_plugin
{ {
CHANNEL_DEF channelDef; CHANNEL_DEF channelDef;
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints; CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
enum RDPDR_CHANNEL_STATE state;
HANDLE thread; HANDLE thread;
wStream* data_in; wStream* data_in;
void* InitHandle; void* InitHandle;
@ -59,12 +77,33 @@ struct rdpdr_plugin
DEVMAN* devman; DEVMAN* devman;
UINT16 versionMajor; UINT32 serverOsType;
UINT16 versionMinor; UINT32 serverOsVersion;
UINT16 clientID; UINT16 serverVersionMajor;
UINT16 serverVersionMinor;
UINT32 serverExtendedPDU;
UINT32 serverIOCode1;
UINT32 serverIOCode2;
UINT32 serverExtraFlags1;
UINT32 serverExtraFlags2;
UINT32 serverSpecialTypeDeviceCap;
UINT32 clientOsType;
UINT32 clientOsVersion;
UINT16 clientVersionMajor;
UINT16 clientVersionMinor;
UINT32 clientExtendedPDU;
UINT32 clientIOCode1;
UINT32 clientIOCode2;
UINT32 clientExtraFlags1;
UINT32 clientExtraFlags2;
UINT32 clientSpecialTypeDeviceCap;
UINT32 clientID;
char computerName[256]; char computerName[256];
UINT32 sequenceId; UINT32 sequenceId;
BOOL userLoggedOn;
/* hotplug support */ /* hotplug support */
HANDLE hotplugThread; HANDLE hotplugThread;
@ -78,8 +117,10 @@ struct rdpdr_plugin
HANDLE stopEvent; HANDLE stopEvent;
#endif #endif
rdpContext* rdpcontext; rdpContext* rdpcontext;
wLog* log;
}; };
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next);
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s); UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */ #endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */

View File

@ -1952,7 +1952,7 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con
irp->Callback = rdpdr_server_drive_query_directory_callback2; irp->Callback = rdpdr_server_drive_query_directory_callback2;
irp->DeviceId = deviceId; irp->DeviceId = deviceId;
irp->FileId = fileId; irp->FileId = fileId;
strcat(irp->PathName, "\\*.*"); winpr_str_append("\\*.*", irp->PathName, ARRAYSIZE(irp->PathName), NULL);
if (!rdpdr_server_enqueue_irp(context, irp)) if (!rdpdr_server_enqueue_irp(context, irp))
{ {

View File

@ -57,13 +57,6 @@ struct _RDPDR_HEADER
}; };
typedef struct _RDPDR_HEADER RDPDR_HEADER; typedef struct _RDPDR_HEADER RDPDR_HEADER;
#define RDPDR_VERSION_MAJOR 0x0001
#define RDPDR_VERSION_MINOR_RDP50 0x0002
#define RDPDR_VERSION_MINOR_RDP51 0x0005
#define RDPDR_VERSION_MINOR_RDP52 0x000A
#define RDPDR_VERSION_MINOR_RDP6X 0x000C
#define RDPDR_CAPABILITY_HEADER_LENGTH 8 #define RDPDR_CAPABILITY_HEADER_LENGTH 8
struct _RDPDR_CAPABILITY_HEADER struct _RDPDR_CAPABILITY_HEADER

View File

@ -0,0 +1,22 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel("rdpecam")
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,12 @@
set(OPTION_DEFAULT ON)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "rdpecam" TYPE "dynamic"
DESCRIPTION "Video Capture Virtual Channel Extension"
SPECIFICATIONS "[MS-RDPECAM]"
DEFAULT ${OPTION_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,27 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("rdpecam")
set(${MODULE_PREFIX}_SRCS
camera_device_enumerator_main.c
camera_device_main.c)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,612 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/channels/log.h>
#include <freerdp/server/rdpecam-enumerator.h>
#define TAG CHANNELS_TAG("rdpecam-enumerator.server")
typedef enum
{
ENUMERATOR_INITIAL,
ENUMERATOR_OPENED,
} eEnumeratorChannelState;
typedef struct
{
CamDevEnumServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* enumerator_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eEnumeratorChannelState state;
wStream* buffer;
} enumerator_server;
static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (enumerator->isOpened)
{
WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
enumerator->externalThread = externalThread;
return error;
}
static UINT enumerator_server_open_channel(enumerator_server* enumerator)
{
CamDevEnumServerContext* context = &enumerator->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(enumerator);
if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
enumerator->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!enumerator->enumerator_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SELECT_VERSION_REQUEST pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error);
return error;
}
static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_DEVICE_ADDED_NOTIFICATION pdu;
UINT error = CHANNEL_RC_OK;
size_t remaining_length;
WCHAR* channel_name_start;
char* tmp;
size_t i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
/*
* RequiredLength 4:
*
* Nullterminator DeviceName (2),
* VirtualChannelName (>= 1),
* Nullterminator VirtualChannelName (1)
*/
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_NO_DATA;
pdu.DeviceName = (WCHAR*)Stream_Pointer(s);
remaining_length = Stream_GetRemainingLength(s);
channel_name_start = (WCHAR*)Stream_Pointer(s);
/* Search for null terminator of DeviceName */
for (i = 0; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start)
{
if (*channel_name_start == L'\0')
break;
}
if (*channel_name_start != L'\0')
{
WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!");
return ERROR_INVALID_DATA;
}
pdu.VirtualChannelName = (char*)++channel_name_start;
++i;
if (i >= remaining_length || *pdu.VirtualChannelName == '\0')
{
WLog_ERR(TAG,
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
return ERROR_INVALID_DATA;
}
tmp = pdu.VirtualChannelName;
for (; i < remaining_length; ++i, ++tmp)
{
if (*tmp == '\0')
break;
}
if (*tmp != '\0')
{
WLog_ERR(TAG,
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
return ERROR_INVALID_DATA;
}
IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error);
return error;
}
static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_DEVICE_REMOVED_NOTIFICATION pdu;
UINT error = CHANNEL_RC_OK;
size_t remaining_length;
char* tmp;
size_t i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return ERROR_NO_DATA;
pdu.VirtualChannelName = (char*)Stream_Pointer(s);
remaining_length = Stream_GetRemainingLength(s);
tmp = (char*)(Stream_Pointer(s) + 1);
for (i = 1; i < remaining_length; ++i, ++tmp)
{
if (*tmp == '\0')
break;
}
if (*tmp != '\0')
{
WLog_ERR(TAG,
"enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
return ERROR_INVALID_DATA;
}
IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error);
return error;
}
static UINT enumerator_process_message(enumerator_server* enumerator)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
CAM_SHARED_MSG_HEADER header = { 0 };
wStream* s;
WINPR_ASSERT(enumerator);
WINPR_ASSERT(enumerator->enumerator_channel);
s = enumerator->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, header.Version);
Stream_Read_UINT8(s, header.MessageId);
switch (header.MessageId)
{
case CAM_MSG_ID_SelectVersionRequest:
error =
enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
break;
case CAM_MSG_ID_DeviceAddedNotification:
error =
enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
break;
case CAM_MSG_ID_DeviceRemovedNotification:
error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
&header);
break;
default:
WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "",
header.MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(enumerator);
switch (enumerator->state)
{
case ENUMERATOR_INITIAL:
error = enumerator_server_open_channel(enumerator);
if (error)
WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!",
error);
else
enumerator->state = ENUMERATOR_OPENED;
break;
case ENUMERATOR_OPENED:
error = enumerator_process_message(enumerator);
break;
}
return error;
}
static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(enumerator);
if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
enumerator_server* enumerator = (enumerator_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(enumerator);
nCount = 0;
events[nCount++] = enumerator->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (enumerator->state)
{
case ENUMERATOR_INITIAL:
error = enumerator_server_context_poll_int(&enumerator->context);
if (error == CHANNEL_RC_OK)
{
events[1] = enumerator_server_get_channel_handle(enumerator);
nCount = 2;
}
break;
case ENUMERATOR_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = enumerator_server_context_poll_int(&enumerator->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(enumerator->enumerator_channel);
enumerator->enumerator_channel = NULL;
if (error && enumerator->context.rdpcontext)
setChannelError(enumerator->context.rdpcontext, error,
"enumerator_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT enumerator_server_open(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (!enumerator->externalThread && (enumerator->thread == NULL))
{
enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!enumerator->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
enumerator->thread =
CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
if (!enumerator->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(enumerator->stopEvent);
enumerator->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
enumerator->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT enumerator_server_close(CamDevEnumServerContext* context)
{
UINT error = CHANNEL_RC_OK;
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (!enumerator->externalThread && enumerator->thread)
{
SetEvent(enumerator->stopEvent);
if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(enumerator->thread);
CloseHandle(enumerator->stopEvent);
enumerator->thread = NULL;
enumerator->stopEvent = NULL;
}
if (enumerator->externalThread)
{
if (enumerator->state != ENUMERATOR_INITIAL)
{
WTSVirtualChannelClose(enumerator->enumerator_channel);
enumerator->enumerator_channel = NULL;
enumerator->state = ENUMERATOR_INITIAL;
}
}
enumerator->isOpened = FALSE;
return error;
}
static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (!enumerator->externalThread)
return ERROR_INTERNAL_ERROR;
return enumerator_server_context_poll_int(context);
}
static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
{
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
WINPR_ASSERT(handle);
if (!enumerator->externalThread)
return FALSE;
if (enumerator->state == ENUMERATOR_INITIAL)
return FALSE;
*handle = enumerator_server_get_channel_handle(enumerator);
return TRUE;
}
static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s)
{
enumerator_server* enumerator = (enumerator_server*)context;
UINT error = CHANNEL_RC_OK;
ULONG written;
if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
error = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
out:
Stream_Free(s, TRUE);
return error;
}
static UINT enumerator_send_select_version_response_pdu(
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse)
{
wStream* s;
s = Stream_New(NULL, CAM_HEADER_SIZE);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
Stream_Write_UINT8(s, selectVersionResponse->Header.MessageId);
return enumerator_server_packet_send(context, s);
}
CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
{
enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server));
if (!enumerator)
return NULL;
enumerator->context.vcm = vcm;
enumerator->context.Initialize = enumerator_server_initialize;
enumerator->context.Open = enumerator_server_open;
enumerator->context.Close = enumerator_server_close;
enumerator->context.Poll = enumerator_server_context_poll;
enumerator->context.ChannelHandle = enumerator_server_context_handle;
enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
enumerator->buffer = Stream_New(NULL, 4096);
if (!enumerator->buffer)
goto fail;
return &enumerator->context;
fail:
cam_dev_enum_server_context_free(&enumerator->context);
return NULL;
}
void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
if (enumerator)
{
enumerator_server_close(context);
Stream_Free(enumerator->buffer, TRUE);
}
free(enumerator);
}

View File

@ -0,0 +1,969 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/channels/log.h>
#include <freerdp/server/rdpecam.h>
#define TAG CHANNELS_TAG("rdpecam.server")
typedef enum
{
CAMERA_DEVICE_INITIAL,
CAMERA_DEVICE_OPENED,
} eCameraDeviceChannelState;
typedef struct
{
CameraDeviceServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* device_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eCameraDeviceChannelState state;
wStream* buffer;
} device_server;
static UINT device_server_initialize(CameraDeviceServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (device->isOpened)
{
WLog_WARN(TAG, "Application error: Camera channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
device->externalThread = externalThread;
return error;
}
static UINT device_server_open_channel(device_server* device)
{
CameraDeviceServerContext* context = &device->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(device);
if (WTSQuerySessionInformationA(device->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
device->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(device->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
device->device_channel = WTSVirtualChannelOpenEx(device->SessionId, context->virtualChannelName,
WTS_CHANNEL_OPTION_DYNAMIC);
if (!device->device_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(device->device_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT device_server_handle_success_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SUCCESS_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
IFCALLRET(context->SuccessResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SuccessResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_error_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_ERROR_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_NO_DATA;
Stream_Read_UINT32(s, pdu.ErrorCode);
IFCALLRET(context->ErrorResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->ErrorResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_stream_list_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_STREAM_LIST_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
BYTE i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
return ERROR_NO_DATA;
pdu.N_Descriptions = MIN(Stream_GetRemainingLength(s) / 5, 255);
for (i = 0; i < pdu.N_Descriptions; ++i)
{
CAM_STREAM_DESCRIPTION* StreamDescription = &pdu.StreamDescriptions[i];
Stream_Read_UINT16(s, StreamDescription->FrameSourceTypes);
Stream_Read_UINT8(s, StreamDescription->StreamCategory);
Stream_Read_UINT8(s, StreamDescription->Selected);
Stream_Read_UINT8(s, StreamDescription->CanBeShared);
}
IFCALLRET(context->StreamListResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->StreamListResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_media_type_list_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_MEDIA_TYPE_LIST_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
BYTE i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
return ERROR_NO_DATA;
pdu.N_Descriptions = Stream_GetRemainingLength(s) / 26;
pdu.MediaTypeDescriptions = calloc(pdu.N_Descriptions, sizeof(CAM_MEDIA_TYPE_DESCRIPTION));
if (!pdu.MediaTypeDescriptions)
{
WLog_ERR(TAG, "Failed to allocate %zu CAM_MEDIA_TYPE_DESCRIPTION structs",
pdu.N_Descriptions);
return ERROR_NOT_ENOUGH_MEMORY;
}
for (i = 0; i < pdu.N_Descriptions; ++i)
{
CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions = &pdu.MediaTypeDescriptions[i];
Stream_Read_UINT8(s, MediaTypeDescriptions->Format);
Stream_Read_UINT32(s, MediaTypeDescriptions->Width);
Stream_Read_UINT32(s, MediaTypeDescriptions->Height);
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateNumerator);
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateDenominator);
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioNumerator);
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioDenominator);
Stream_Read_UINT8(s, MediaTypeDescriptions->Flags);
}
IFCALLRET(context->MediaTypeListResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->MediaTypeListResponse failed with error %" PRIu32 "", error);
free(pdu.MediaTypeDescriptions);
return error;
}
static UINT device_server_recv_current_media_type_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_CURRENT_MEDIA_TYPE_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Format);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Width);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Height);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateNumerator);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateDenominator);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioNumerator);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioDenominator);
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Flags);
IFCALLRET(context->CurrentMediaTypeResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->CurrentMediaTypeResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_sample_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SAMPLE_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.StreamIndex);
pdu.SampleSize = Stream_GetRemainingLength(s);
pdu.Sample = Stream_Pointer(s);
IFCALLRET(context->SampleResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SampleResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_sample_error_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SAMPLE_ERROR_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.StreamIndex);
Stream_Read_UINT32(s, pdu.ErrorCode);
IFCALLRET(context->SampleErrorResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SampleErrorResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_property_list_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_PROPERTY_LIST_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
pdu.N_Properties = Stream_GetRemainingLength(s) / 19;
if (pdu.N_Properties > 0)
{
size_t i;
pdu.Properties = calloc(pdu.N_Properties, sizeof(CAM_PROPERTY_DESCRIPTION));
if (!pdu.Properties)
{
WLog_ERR(TAG, "Failed to allocate %zu CAM_PROPERTY_DESCRIPTION structs",
pdu.N_Properties);
return ERROR_NOT_ENOUGH_MEMORY;
}
for (i = 0; i < pdu.N_Properties; ++i)
{
Stream_Read_UINT8(s, pdu.Properties[i].PropertySet);
Stream_Read_UINT8(s, pdu.Properties[i].PropertyId);
Stream_Read_UINT8(s, pdu.Properties[i].Capabilities);
Stream_Read_INT32(s, pdu.Properties[i].MinValue);
Stream_Read_INT32(s, pdu.Properties[i].MaxValue);
Stream_Read_INT32(s, pdu.Properties[i].Step);
Stream_Read_INT32(s, pdu.Properties[i].DefaultValue);
}
}
IFCALLRET(context->PropertyListResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->PropertyListResponse failed with error %" PRIu32 "", error);
free(pdu.Properties);
return error;
}
static UINT device_server_recv_property_value_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_PROPERTY_VALUE_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.PropertyValue.Mode);
Stream_Read_INT32(s, pdu.PropertyValue.Value);
IFCALLRET(context->PropertyValueResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->PropertyValueResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_process_message(device_server* device)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
CAM_SHARED_MSG_HEADER header = { 0 };
wStream* s;
WINPR_ASSERT(device);
WINPR_ASSERT(device->device_channel);
s = device->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(device->device_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, header.Version);
Stream_Read_UINT8(s, header.MessageId);
switch (header.MessageId)
{
case CAM_MSG_ID_SuccessResponse:
error = device_server_handle_success_response(&device->context, s, &header);
break;
case CAM_MSG_ID_ErrorResponse:
error = device_server_recv_error_response(&device->context, s, &header);
break;
case CAM_MSG_ID_StreamListResponse:
error = device_server_recv_stream_list_response(&device->context, s, &header);
break;
case CAM_MSG_ID_MediaTypeListResponse:
error = device_server_recv_media_type_list_response(&device->context, s, &header);
break;
case CAM_MSG_ID_CurrentMediaTypeResponse:
error = device_server_recv_current_media_type_response(&device->context, s, &header);
break;
case CAM_MSG_ID_SampleResponse:
error = device_server_recv_sample_response(&device->context, s, &header);
break;
case CAM_MSG_ID_SampleErrorResponse:
error = device_server_recv_sample_error_response(&device->context, s, &header);
break;
case CAM_MSG_ID_PropertyListResponse:
error = device_server_recv_property_list_response(&device->context, s, &header);
break;
case CAM_MSG_ID_PropertyValueResponse:
error = device_server_recv_property_value_response(&device->context, s, &header);
break;
default:
WLog_ERR(TAG, "device_process_message: unknown or invalid MessageId %" PRIu8 "",
header.MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT device_server_context_poll_int(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(device);
switch (device->state)
{
case CAMERA_DEVICE_INITIAL:
error = device_server_open_channel(device);
if (error)
WLog_ERR(TAG, "device_server_open_channel failed with error %" PRIu32 "!", error);
else
device->state = CAMERA_DEVICE_OPENED;
break;
case CAMERA_DEVICE_OPENED:
error = device_process_message(device);
break;
}
return error;
}
static HANDLE device_server_get_channel_handle(device_server* device)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(device);
if (WTSVirtualChannelQuery(device->device_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI device_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
device_server* device = (device_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(device);
nCount = 0;
events[nCount++] = device->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (device->state)
{
case CAMERA_DEVICE_INITIAL:
error = device_server_context_poll_int(&device->context);
if (error == CHANNEL_RC_OK)
{
events[1] = device_server_get_channel_handle(device);
nCount = 2;
}
break;
case CAMERA_DEVICE_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = device_server_context_poll_int(&device->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(device->device_channel);
device->device_channel = NULL;
if (error && device->context.rdpcontext)
setChannelError(device->context.rdpcontext, error,
"device_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT device_server_open(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (!device->externalThread && (device->thread == NULL))
{
device->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!device->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
device->thread = CreateThread(NULL, 0, device_server_thread_func, device, 0, NULL);
if (!device->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(device->stopEvent);
device->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
device->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT device_server_close(CameraDeviceServerContext* context)
{
UINT error = CHANNEL_RC_OK;
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (!device->externalThread && device->thread)
{
SetEvent(device->stopEvent);
if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(device->thread);
CloseHandle(device->stopEvent);
device->thread = NULL;
device->stopEvent = NULL;
}
if (device->externalThread)
{
if (device->state != CAMERA_DEVICE_INITIAL)
{
WTSVirtualChannelClose(device->device_channel);
device->device_channel = NULL;
device->state = CAMERA_DEVICE_INITIAL;
}
}
device->isOpened = FALSE;
return error;
}
static UINT device_server_context_poll(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (!device->externalThread)
return ERROR_INTERNAL_ERROR;
return device_server_context_poll_int(context);
}
static BOOL device_server_context_handle(CameraDeviceServerContext* context, HANDLE* handle)
{
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
WINPR_ASSERT(handle);
if (!device->externalThread)
return FALSE;
if (device->state == CAMERA_DEVICE_INITIAL)
return FALSE;
*handle = device_server_get_channel_handle(device);
return TRUE;
}
static wStream* device_server_packet_new(size_t size, BYTE version, BYTE messageId)
{
wStream* s;
/* Allocate what we need plus header bytes */
s = Stream_New(NULL, size + CAM_HEADER_SIZE);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return NULL;
}
Stream_Write_UINT8(s, version);
Stream_Write_UINT8(s, messageId);
return s;
}
static UINT device_server_packet_send(CameraDeviceServerContext* context, wStream* s)
{
device_server* device = (device_server*)context;
UINT error = CHANNEL_RC_OK;
ULONG written;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
error = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
out:
Stream_Free(s, TRUE);
return error;
}
static UINT device_server_write_and_send_header(CameraDeviceServerContext* context, BYTE messageId)
{
wStream* s;
WINPR_ASSERT(context);
s = device_server_packet_new(0, context->protocolVersion, messageId);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
return device_server_packet_send(context, s);
}
static UINT
device_send_activate_device_request_pdu(CameraDeviceServerContext* context,
const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_ActivateDeviceRequest);
}
static UINT device_send_deactivate_device_request_pdu(
CameraDeviceServerContext* context,
const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_DeactivateDeviceRequest);
}
static UINT device_send_stream_list_request_pdu(CameraDeviceServerContext* context,
const CAM_STREAM_LIST_REQUEST* streamListRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_StreamListRequest);
}
static UINT
device_send_media_type_list_request_pdu(CameraDeviceServerContext* context,
const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(mediaTypeListRequest);
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_MediaTypeListRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, mediaTypeListRequest->StreamIndex);
return device_server_packet_send(context, s);
}
static UINT device_send_current_media_type_request_pdu(
CameraDeviceServerContext* context,
const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(currentMediaTypeRequest);
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_CurrentMediaTypeRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, currentMediaTypeRequest->StreamIndex);
return device_server_packet_send(context, s);
}
static UINT
device_send_start_streams_request_pdu(CameraDeviceServerContext* context,
const CAM_START_STREAMS_REQUEST* startStreamsRequest)
{
wStream* s;
size_t i;
WINPR_ASSERT(context);
WINPR_ASSERT(startStreamsRequest);
s = device_server_packet_new(startStreamsRequest->N_Infos * 27ul, context->protocolVersion,
CAM_MSG_ID_StartStreamsRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
for (i = 0; i < startStreamsRequest->N_Infos; ++i)
{
const CAM_START_STREAM_INFO* info = &startStreamsRequest->StartStreamsInfo[i];
const CAM_MEDIA_TYPE_DESCRIPTION* description = &info->MediaTypeDescription;
Stream_Write_UINT8(s, info->StreamIndex);
Stream_Write_UINT8(s, description->Format);
Stream_Write_UINT32(s, description->Width);
Stream_Write_UINT32(s, description->Height);
Stream_Write_UINT32(s, description->FrameRateNumerator);
Stream_Write_UINT32(s, description->FrameRateDenominator);
Stream_Write_UINT32(s, description->PixelAspectRatioNumerator);
Stream_Write_UINT32(s, description->PixelAspectRatioDenominator);
Stream_Write_UINT8(s, description->Flags);
}
return device_server_packet_send(context, s);
}
static UINT device_send_stop_streams_request_pdu(CameraDeviceServerContext* context,
const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_StopStreamsRequest);
}
static UINT device_send_sample_request_pdu(CameraDeviceServerContext* context,
const CAM_SAMPLE_REQUEST* sampleRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(sampleRequest);
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_SampleRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, sampleRequest->StreamIndex);
return device_server_packet_send(context, s);
}
static UINT
device_send_property_list_request_pdu(CameraDeviceServerContext* context,
const CAM_PROPERTY_LIST_REQUEST* propertyListRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_PropertyListRequest);
}
static UINT
device_send_property_value_request_pdu(CameraDeviceServerContext* context,
const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(propertyValueRequest);
s = device_server_packet_new(2, context->protocolVersion, CAM_MSG_ID_PropertyValueRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, propertyValueRequest->PropertySet);
Stream_Write_UINT8(s, propertyValueRequest->PropertyId);
return device_server_packet_send(context, s);
}
static UINT device_send_set_property_value_request_pdu(
CameraDeviceServerContext* context,
const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(setPropertyValueRequest);
s = device_server_packet_new(2 + 5, context->protocolVersion,
CAM_MSG_ID_SetPropertyValueRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, setPropertyValueRequest->PropertySet);
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyId);
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyValue.Mode);
Stream_Write_INT32(s, setPropertyValueRequest->PropertyValue.Value);
return device_server_packet_send(context, s);
}
CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm)
{
device_server* device = (device_server*)calloc(1, sizeof(device_server));
if (!device)
return NULL;
device->context.vcm = vcm;
device->context.Initialize = device_server_initialize;
device->context.Open = device_server_open;
device->context.Close = device_server_close;
device->context.Poll = device_server_context_poll;
device->context.ChannelHandle = device_server_context_handle;
device->context.ActivateDeviceRequest = device_send_activate_device_request_pdu;
device->context.DeactivateDeviceRequest = device_send_deactivate_device_request_pdu;
device->context.StreamListRequest = device_send_stream_list_request_pdu;
device->context.MediaTypeListRequest = device_send_media_type_list_request_pdu;
device->context.CurrentMediaTypeRequest = device_send_current_media_type_request_pdu;
device->context.StartStreamsRequest = device_send_start_streams_request_pdu;
device->context.StopStreamsRequest = device_send_stop_streams_request_pdu;
device->context.SampleRequest = device_send_sample_request_pdu;
device->context.PropertyListRequest = device_send_property_list_request_pdu;
device->context.PropertyValueRequest = device_send_property_value_request_pdu;
device->context.SetPropertyValueRequest = device_send_set_property_value_request_pdu;
device->buffer = Stream_New(NULL, 4096);
if (!device->buffer)
goto fail;
return &device->context;
fail:
camera_device_server_context_free(&device->context);
return NULL;
}
void camera_device_server_context_free(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
if (device)
{
device_server_close(context);
Stream_Free(device->buffer, TRUE);
}
free(context->virtualChannelName);
free(device);
}

View File

@ -1386,7 +1386,6 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
error_out: error_out:
free(context); free(context);
free(rdpei->contactPoints);
free(rdpei); free(rdpei);
return error; return error;
} }

View File

@ -99,6 +99,8 @@ UINT rdpei_server_init(RdpeiServerContext* context)
void* buffer = NULL; void* buffer = NULL;
DWORD bytesReturned; DWORD bytesReturned;
RdpeiServerPrivate* priv = context->priv; RdpeiServerPrivate* priv = context->priv;
UINT32 channelId;
BOOL status = TRUE;
priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME, priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME,
WTS_CHANNEL_OPTION_DYNAMIC); WTS_CHANNEL_OPTION_DYNAMIC);
@ -108,6 +110,15 @@ UINT rdpei_server_init(RdpeiServerContext* context)
return CHANNEL_RC_INITIALIZATION_ERROR; return CHANNEL_RC_INITIALIZATION_ERROR;
} }
channelId = WTSChannelGetIdByHandle(priv->channelHandle);
IFCALLRET(context->onChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->onChannelIdAssigned failed!");
goto out_close;
}
if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer, if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer,
&bytesReturned) || &bytesReturned) ||
(bytesReturned != sizeof(HANDLE))) (bytesReturned != sizeof(HANDLE)))
@ -612,6 +623,7 @@ UINT rdpei_server_send_sc_ready_ex(RdpeiServerContext* context, UINT32 version,
{ {
ULONG written; ULONG written;
RdpeiServerPrivate* priv = context->priv; RdpeiServerPrivate* priv = context->priv;
UINT32 pduLen = 4;
if (priv->automataState != STATE_INITIAL) if (priv->automataState != STATE_INITIAL)
{ {
@ -621,14 +633,17 @@ UINT rdpei_server_send_sc_ready_ex(RdpeiServerContext* context, UINT32 version,
Stream_SetPosition(priv->outputStream, 0); Stream_SetPosition(priv->outputStream, 0);
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4)) if (version >= RDPINPUT_PROTOCOL_V300)
pduLen += 4;
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen))
{ {
WLog_ERR(TAG, "Stream_EnsureCapacity failed!"); WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
return CHANNEL_RC_NO_MEMORY; return CHANNEL_RC_NO_MEMORY;
} }
Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY); Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY);
Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4); Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen);
Stream_Write_UINT32(priv->outputStream, version); Stream_Write_UINT32(priv->outputStream, version);
if (version >= RDPINPUT_PROTOCOL_V300) if (version >= RDPINPUT_PROTOCOL_V300)
Stream_Write_UINT32(priv->outputStream, features); Stream_Write_UINT32(priv->outputStream, features);

View File

@ -163,11 +163,12 @@ fail:
static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps) static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps)
{ {
const UINT32 filter = gfx->capsFilter; const UINT32 filter = gfx->capsFilter;
const UINT32 capList[] = { const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81,
RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101,
RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106 RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105,
}; RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR,
RDPGFX_CAPVERSION_107 };
UINT32 x; UINT32 x;
for (x = 0; x < ARRAYSIZE(capList); x++) for (x = 0; x < ARRAYSIZE(capList); x++)
@ -190,7 +191,7 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback)
RdpgfxClientContext* context; RdpgfxClientContext* context;
RDPGFX_CAPSET* capsSet; RDPGFX_CAPSET* capsSet;
RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 }; RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 };
RDPGFX_CAPS_ADVERTISE_PDU pdu; RDPGFX_CAPS_ADVERTISE_PDU pdu = { 0 };
if (!callback) if (!callback)
return ERROR_BAD_ARGUMENTS; return ERROR_BAD_ARGUMENTS;
@ -323,6 +324,25 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback)
capsSet->length = 0x4; capsSet->length = 0x4;
capsSet->flags = caps10Flags; capsSet->flags = caps10Flags;
} }
if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR))
{
capsSet = &capsSets[pdu.capsSetCount++];
capsSet->version = RDPGFX_CAPVERSION_106_ERR;
capsSet->length = 0x4;
capsSet->flags = caps10Flags;
}
if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107))
{
capsSet = &capsSets[pdu.capsSetCount++];
capsSet->version = RDPGFX_CAPVERSION_107;
capsSet->length = 0x4;
capsSet->flags = caps10Flags;
#if !defined(CAIRO_FOUND) && !defined(SWSCALE_FOUND)
capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
#endif
}
} }
return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu); return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu);
@ -895,6 +915,8 @@ static UINT rdpgfx_recv_end_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream
case RDPGFX_CAPVERSION_104: case RDPGFX_CAPVERSION_104:
case RDPGFX_CAPVERSION_105: case RDPGFX_CAPVERSION_105:
case RDPGFX_CAPVERSION_106: case RDPGFX_CAPVERSION_106:
case RDPGFX_CAPVERSION_106_ERR:
case RDPGFX_CAPVERSION_107:
if (gfx->SendQoeAck) if (gfx->SendQoeAck)
{ {
RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe; RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe;
@ -999,6 +1021,19 @@ static UINT rdpgfx_recv_wire_to_surface_1_pdu(RDPGFX_CHANNEL_CALLBACK* callback,
cmd.data = pdu.bitmapData; cmd.data = pdu.bitmapData;
cmd.extra = NULL; cmd.extra = NULL;
if (cmd.right < cmd.left)
{
WLog_Print(gfx->log, WLOG_ERROR, "RecvWireToSurface1Pdu right=%" PRIu32 " < left=%" PRIu32,
cmd.right, cmd.left);
return ERROR_INVALID_DATA;
}
if (cmd.bottom < cmd.top)
{
WLog_Print(gfx->log, WLOG_ERROR, "RecvWireToSurface1Pdu bottom=%" PRIu32 " < top=%" PRIu32,
cmd.bottom, cmd.top);
return ERROR_INVALID_DATA;
}
if ((error = rdpgfx_decode(gfx, &cmd))) if ((error = rdpgfx_decode(gfx, &cmd)))
WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_decode failed with error %" PRIu32 "!", error); WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_decode failed with error %" PRIu32 "!", error);

View File

@ -1,4 +1,4 @@
/** /**
* FreeRDP: A Remote Desktop Protocol Implementation * FreeRDP: A Remote Desktop Protocol Implementation
* Graphics Pipeline Extension * Graphics Pipeline Extension
* *
@ -24,6 +24,7 @@
#endif #endif
#include <winpr/crt.h> #include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/stream.h> #include <winpr/stream.h>
#include <freerdp/channels/log.h> #include <freerdp/channels/log.h>
@ -110,6 +111,9 @@ const char* rdpgfx_get_codec_id_string(UINT16 codecId)
*/ */
UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header) UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header)
{ {
WINPR_ASSERT(s);
WINPR_ASSERT(header);
if (Stream_GetRemainingLength(s) < 8) if (Stream_GetRemainingLength(s) < 8)
{ {
WLog_ERR(TAG, "calloc failed!"); WLog_ERR(TAG, "calloc failed!");
@ -119,6 +123,13 @@ UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header)
Stream_Read_UINT16(s, header->cmdId); /* cmdId (2 bytes) */ Stream_Read_UINT16(s, header->cmdId); /* cmdId (2 bytes) */
Stream_Read_UINT16(s, header->flags); /* flags (2 bytes) */ Stream_Read_UINT16(s, header->flags); /* flags (2 bytes) */
Stream_Read_UINT32(s, header->pduLength); /* pduLength (4 bytes) */ Stream_Read_UINT32(s, header->pduLength); /* pduLength (4 bytes) */
if ((header->pduLength < 8) || (Stream_GetRemainingLength(s) < (header->pduLength - 8)))
{
WLog_ERR(TAG, "header->pduLength %u less than 8!", header->pduLength);
return ERROR_INVALID_DATA;
}
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }

View File

@ -1428,6 +1428,8 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
PULONG pSessionId = NULL; PULONG pSessionId = NULL;
DWORD BytesReturned = 0; DWORD BytesReturned = 0;
priv->SessionId = WTS_CURRENT_SESSION; priv->SessionId = WTS_CURRENT_SESSION;
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE) (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
@ -1447,6 +1449,15 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
return FALSE; return FALSE;
} }
channelId = WTSChannelGetIdByHandle(priv->rdpgfx_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
goto out_close;
}
/* Query for channel event handle */ /* Query for channel event handle */
if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer, if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) || &BytesReturned) ||

View File

@ -213,10 +213,6 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMA
break; break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
break;
default: default:
return FALSE; return FALSE;
} }

View File

@ -139,86 +139,99 @@ static void rdpsnd_mac_release(rdpsndMacPlugin *mac)
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency) static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency)
{ {
AudioDeviceID outputDeviceID; @autoreleasepool
UInt32 propertySize; {
OSStatus err; AudioDeviceID outputDeviceID;
NSError *error; UInt32 propertySize;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; OSStatus err;
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultSystemOutputDevice, NSError *error;
kAudioObjectPropertyScopeGlobal, rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
kAudioObjectPropertyElementMaster }; AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
#if defined(MAC_OS_VERSION_12_0)
kAudioObjectPropertyElementMain
#else
kAudioObjectPropertyElementMaster
#endif
};
if (mac->isOpen) if (mac->isOpen)
return TRUE;
if (!rdpsnd_mac_set_format(device, format, latency))
return FALSE;
propertySize = sizeof(outputDeviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
&propertySize, &outputDeviceID);
if (err)
{
WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err));
return FALSE;
}
mac->engine = [[AVAudioEngine alloc] init];
if (!mac->engine)
return FALSE;
err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
0, &outputDeviceID, sizeof(outputDeviceID));
if (err)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err));
return FALSE;
}
mac->player = [[AVAudioPlayerNode alloc] init];
if (!mac->player)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AVAudioPlayerNode::init() failed");
return FALSE;
}
[mac->engine attachNode:mac->player];
[mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
[mac->engine prepare];
if (![mac->engine startAndReturnError:&error])
{
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s",
[error.localizedDescription UTF8String]);
return FALSE;
}
mac->isOpen = TRUE;
return TRUE; return TRUE;
if (!rdpsnd_mac_set_format(device, format, latency))
return FALSE;
propertySize = sizeof(outputDeviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
&propertySize, &outputDeviceID);
if (err)
{
WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err));
return FALSE;
} }
mac->engine = [[AVAudioEngine alloc] init];
if (!mac->engine)
return FALSE;
err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,
&outputDeviceID, sizeof(outputDeviceID));
if (err)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err));
return FALSE;
}
mac->player = [[AVAudioPlayerNode alloc] init];
if (!mac->player)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AVAudioPlayerNode::init() failed");
return FALSE;
}
[mac->engine attachNode:mac->player];
[mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
[mac->engine prepare];
if (![mac->engine startAndReturnError:&error])
{
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s", [error.localizedDescription UTF8String]);
return FALSE;
}
mac->isOpen = TRUE;
return TRUE;
} }
static void rdpsnd_mac_close(rdpsndDevicePlugin *device) static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
{ {
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; @autoreleasepool
if (mac->isPlaying)
{ {
[mac->player stop]; rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
mac->isPlaying = FALSE;
}
if (mac->isOpen) if (mac->isPlaying)
{ {
[mac->engine stop]; [mac->player stop];
mac->isOpen = FALSE; mac->isPlaying = FALSE;
} }
rdpsnd_mac_release(mac); if (mac->isOpen)
{
[mac->engine stop];
mac->isOpen = FALSE;
}
rdpsnd_mac_release(mac);
}
} }
static void rdpsnd_mac_free(rdpsndDevicePlugin *device) static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
@ -246,108 +259,118 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device, const AUDIO_
static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value) static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value)
{ {
Float32 fVolume; @autoreleasepool
UINT16 volumeLeft; {
UINT16 volumeRight; Float32 fVolume;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; UINT16 volumeLeft;
UINT16 volumeRight;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (!mac->player) if (!mac->player)
return FALSE; return FALSE;
volumeLeft = (value & 0xFFFF); volumeLeft = (value & 0xFFFF);
volumeRight = ((value >> 16) & 0xFFFF); volumeRight = ((value >> 16) & 0xFFFF);
fVolume = ((float)volumeLeft) / 65535.0f; fVolume = ((float)volumeLeft) / 65535.0f;
mac->player.volume = fVolume; mac->player.volume = fVolume;
return TRUE; return TRUE;
}
} }
static void rdpsnd_mac_start(rdpsndDevicePlugin *device) static void rdpsnd_mac_start(rdpsndDevicePlugin *device)
{ {
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; @autoreleasepool
if (!mac->isPlaying)
{ {
if (!mac->engine.isRunning) rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (!mac->isPlaying)
{ {
NSError *error; if (!mac->engine.isRunning)
if (![mac->engine startAndReturnError:&error])
{ {
device->Close(device); NSError *error;
WLog_ERR(TAG, "Failed to start audio player %s",
[error.localizedDescription UTF8String]); if (![mac->engine startAndReturnError:&error])
return; {
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s",
[error.localizedDescription UTF8String]);
return;
}
} }
[mac->player play];
mac->isPlaying = TRUE;
mac->diff = 100; /* Initial latency, corrected after first sample is played. */
} }
[mac->player play];
mac->isPlaying = TRUE;
mac->diff = 100; /* Initial latency, corrected after first sample is played. */
} }
} }
static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size) static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size)
{ {
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; @autoreleasepool
AVAudioPCMBuffer *buffer;
AVAudioFormat *format;
float *const *db;
size_t pos, step, x;
AVAudioFrameCount count;
UINT64 start = GetTickCount64();
if (!mac->isOpen)
return 0;
step = 2 * mac->format.nChannels;
count = size / step;
format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:mac->format.nSamplesPerSec
channels:mac->format.nChannels
interleaved:NO];
if (!format)
{ {
WLog_WARN(TAG, "AVAudioFormat::init() failed"); rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
return 0; AVAudioPCMBuffer *buffer;
} AVAudioFormat *format;
buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count]; float *const *db;
if (!buffer) size_t pos, step, x;
{ AVAudioFrameCount count;
[format release]; UINT64 start = GetTickCount64();
WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed");
return 0;
}
buffer.frameLength = buffer.frameCapacity; if (!mac->isOpen)
db = buffer.floatChannelData; return 0;
for (pos = 0; pos < count; pos++) step = 2 * mac->format.nChannels;
{
const BYTE *d = &data[pos * step]; count = size / step;
for (x = 0; x < mac->format.nChannels; x++) format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:mac->format.nSamplesPerSec
channels:mac->format.nChannels
interleaved:NO];
if (!format)
{ {
const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f; WLog_WARN(TAG, "AVAudioFormat::init() failed");
db[x][pos] = val; return 0;
d += sizeof(int16_t);
} }
buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
[format release];
if (!buffer)
{
WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed");
return 0;
}
buffer.frameLength = buffer.frameCapacity;
db = buffer.floatChannelData;
for (pos = 0; pos < count; pos++)
{
const BYTE *d = &data[pos * step];
for (x = 0; x < mac->format.nChannels; x++)
{
const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
db[x][pos] = val;
d += sizeof(int16_t);
}
}
rdpsnd_mac_start(device);
[mac->player scheduleBuffer:buffer
completionHandler:^{
UINT64 stop = GetTickCount64();
if (start > stop)
mac->diff = 0;
else
mac->diff = stop - start;
}];
[buffer release];
return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
} }
rdpsnd_mac_start(device);
[mac->player scheduleBuffer:buffer
completionHandler:^{
UINT64 stop = GetTickCount64();
if (start > stop)
mac->diff = 0;
else
mac->diff = stop - start;
}];
return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
} }
#ifdef BUILTIN_CHANNELS #ifdef BUILTIN_CHANNELS

View File

@ -117,10 +117,6 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_
break; break;
case WAVE_FORMAT_MULAW:
case WAVE_FORMAT_ALAW:
break;
default: default:
return FALSE; return FALSE;
} }

View File

@ -25,6 +25,9 @@ include_directories(..)
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "") add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
if (NOT BUILTIN_CHANNELS)
list(APPEND ${MODULE_PREFIX}_LIBS freerdp-client)
endif()
list(APPEND ${MODULE_PREFIX}_LIBS freerdp) list(APPEND ${MODULE_PREFIX}_LIBS freerdp)
list(APPEND ${MODULE_PREFIX}_LIBS winpr) list(APPEND ${MODULE_PREFIX}_LIBS winpr)

View File

@ -23,11 +23,15 @@
#include "config.h" #include "config.h"
#endif #endif
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <winpr/crt.h> #include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/stream.h> #include <winpr/stream.h>
#include <winpr/cmdline.h> #include <winpr/cmdline.h>
@ -51,8 +55,39 @@ struct rdpsnd_pulse_plugin
pa_stream* stream; pa_stream* stream;
UINT32 latency; UINT32 latency;
UINT32 volume; UINT32 volume;
time_t reconnect_delay_seconds;
time_t reconnect_time;
}; };
static BOOL rdpsnd_check_pulse(rdpsndPulsePlugin* pulse, BOOL haveStream)
{
BOOL rc = TRUE;
WINPR_ASSERT(pulse);
if (!pulse->context)
{
WLog_WARN(TAG, "pulse->context=%p", pulse->context);
rc = FALSE;
}
if (haveStream)
{
if (!pulse->stream)
{
WLog_WARN(TAG, "pulse->stream=%p", pulse->stream);
rc = FALSE;
}
}
if (!pulse->mainloop)
{
WLog_WARN(TAG, "pulse->mainloop=%p", pulse->mainloop);
rc = FALSE;
}
return rc;
}
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format); static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol, static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol,
@ -65,7 +100,8 @@ static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int
; ;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
if (!pulse || !c || !i) WINPR_ASSERT(c);
if (!rdpsnd_check_pulse(pulse, FALSE) || !i)
return; return;
for (x = 0; x < i->volume.channels; x++) for (x = 0; x < i->volume.channels; x++)
@ -97,6 +133,10 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
{ {
pa_context_state_t state; pa_context_state_t state;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
WINPR_ASSERT(context);
WINPR_ASSERT(pulse);
state = pa_context_get_state(context); state = pa_context_get_state(context);
switch (state) switch (state)
@ -106,6 +146,14 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
break; break;
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
// Destroy context now, create new one for next connection attempt
pa_context_unref(pulse->context);
pulse->context = NULL;
if (pulse->reconnect_delay_seconds >= 0)
pulse->reconnect_time = time(NULL) + pulse->reconnect_delay_seconds;
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
case PA_CONTEXT_TERMINATED: case PA_CONTEXT_TERMINATED:
pa_threaded_mainloop_signal(pulse->mainloop, 0); pa_threaded_mainloop_signal(pulse->mainloop, 0);
break; break;
@ -117,21 +165,17 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device) static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
{ {
BOOL rc;
pa_operation* o; pa_operation* o;
pa_context_state_t state; pa_context_state_t state;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse->context) if (!rdpsnd_check_pulse(pulse, FALSE))
return FALSE; return FALSE;
if (pa_context_connect(pulse->context, NULL, 0, NULL))
{
return FALSE;
}
pa_threaded_mainloop_lock(pulse->mainloop); pa_threaded_mainloop_lock(pulse->mainloop);
if (pa_threaded_mainloop_start(pulse->mainloop) < 0) if (pa_context_connect(pulse->context, NULL, 0, NULL) < 0)
{ {
pa_threaded_mainloop_unlock(pulse->mainloop); pa_threaded_mainloop_unlock(pulse->mainloop);
return FALSE; return FALSE;
@ -157,27 +201,35 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
if (o) if (o)
pa_operation_unref(o); pa_operation_unref(o);
pa_threaded_mainloop_unlock(pulse->mainloop);
if (state == PA_CONTEXT_READY) if (state == PA_CONTEXT_READY)
{ {
return TRUE; rc = TRUE;
} }
else else
{ {
pa_context_disconnect(pulse->context); pa_context_disconnect(pulse->context);
return FALSE; rc = FALSE;
} }
pa_threaded_mainloop_unlock(pulse->mainloop);
return rc;
} }
static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata) static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
{ {
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
if (!rdpsnd_check_pulse(pulse, TRUE))
return;
pa_threaded_mainloop_signal(pulse->mainloop, 0); pa_threaded_mainloop_signal(pulse->mainloop, 0);
} }
static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation) static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
{ {
if (!rdpsnd_check_pulse(pulse, TRUE))
return;
if (!operation) if (!operation)
return; return;
@ -193,6 +245,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
{ {
pa_stream_state_t state; pa_stream_state_t state;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
WINPR_ASSERT(stream);
if (!rdpsnd_check_pulse(pulse, TRUE))
return;
state = pa_stream_get_state(stream); state = pa_stream_get_state(stream);
switch (state) switch (state)
@ -203,6 +260,8 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED: case PA_STREAM_TERMINATED:
// Stream object is about to be destroyed, clean up our pointer
pulse->stream = NULL;
pa_threaded_mainloop_signal(pulse->mainloop, 0); pa_threaded_mainloop_signal(pulse->mainloop, 0);
break; break;
@ -214,6 +273,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata) static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
{ {
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
WINPR_ASSERT(stream);
if (!rdpsnd_check_pulse(pulse, TRUE))
return;
pa_threaded_mainloop_signal(pulse->mainloop, 0); pa_threaded_mainloop_signal(pulse->mainloop, 0);
} }
@ -221,15 +285,20 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
{ {
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse->context || !pulse->stream) WINPR_ASSERT(pulse);
if (!rdpsnd_check_pulse(pulse, FALSE))
return; return;
pa_threaded_mainloop_lock(pulse->mainloop); pa_threaded_mainloop_lock(pulse->mainloop);
rdpsnd_pulse_wait_for_operation( if (pulse->stream)
pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse)); {
pa_stream_disconnect(pulse->stream); rdpsnd_pulse_wait_for_operation(
pa_stream_unref(pulse->stream); pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
pulse->stream = NULL; pa_stream_disconnect(pulse->stream);
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
}
pa_threaded_mainloop_unlock(pulse->mainloop); pa_threaded_mainloop_unlock(pulse->mainloop);
} }
@ -237,7 +306,9 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
{ {
pa_sample_spec sample_spec = { 0 }; pa_sample_spec sample_spec = { 0 };
if (!pulse->context) WINPR_ASSERT(format);
if (!rdpsnd_check_pulse(pulse, FALSE))
return FALSE; return FALSE;
if (!rdpsnd_pulse_format_supported(&pulse->device, format)) if (!rdpsnd_pulse_format_supported(&pulse->device, format))
@ -281,30 +352,52 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
return TRUE; return TRUE;
} }
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, static BOOL rdpsnd_pulse_context_connect(rdpsndDevicePlugin* device)
UINT32 latency) {
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
if (!pulse->context)
return FALSE;
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
return FALSE;
return TRUE;
}
static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device)
{ {
pa_stream_state_t state; pa_stream_state_t state;
pa_stream_flags_t flags; pa_stream_flags_t flags;
pa_buffer_attr buffer_attr = { 0 }; pa_buffer_attr buffer_attr = { 0 };
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX]; char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = { 0 };
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse->context || pulse->stream)
return TRUE;
if (!rdpsnd_pulse_set_format_spec(pulse, format))
return FALSE;
pulse->latency = latency;
if (pa_sample_spec_valid(&pulse->sample_spec) == 0) if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
{ {
pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec); pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec);
return TRUE; return FALSE;
} }
pa_threaded_mainloop_lock(pulse->mainloop); pa_threaded_mainloop_lock(pulse->mainloop);
if (!pulse->context)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
if (pulse->reconnect_delay_seconds >= 0 && time(NULL) - pulse->reconnect_time >= 0)
rdpsnd_pulse_context_connect(device);
pa_threaded_mainloop_lock(pulse->mainloop);
}
if (!rdpsnd_check_pulse(pulse, FALSE))
{
pa_threaded_mainloop_unlock(pulse->mainloop);
return FALSE;
}
pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL); pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
if (!pulse->stream) if (!pulse->stream)
@ -320,19 +413,22 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
if (pulse->latency > 0) if (pulse->latency > 0)
{ {
buffer_attr.maxlength = pa_usec_to_bytes(pulse->latency * 2 * 1000, &pulse->sample_spec); buffer_attr.maxlength = UINT32_MAX;
buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec); buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec);
buffer_attr.prebuf = (UINT32)-1; buffer_attr.prebuf = UINT32_MAX;
buffer_attr.minreq = (UINT32)-1; buffer_attr.minreq = UINT32_MAX;
buffer_attr.fragsize = (UINT32)-1; buffer_attr.fragsize = UINT32_MAX;
flags |= PA_STREAM_ADJUST_LATENCY; flags |= PA_STREAM_ADJUST_LATENCY;
} }
if (pa_stream_connect_playback(pulse->stream, pulse->device_name, if (pa_stream_connect_playback(pulse->stream, pulse->device_name,
pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0) pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
{ {
WLog_ERR(TAG, "error connecting playback stream");
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
pa_threaded_mainloop_unlock(pulse->mainloop); pa_threaded_mainloop_unlock(pulse->mainloop);
return TRUE; return FALSE;
} }
for (;;) for (;;)
@ -359,6 +455,24 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
return FALSE; return FALSE;
} }
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
WINPR_ASSERT(format);
if (!rdpsnd_check_pulse(pulse, FALSE))
return TRUE;
if (!rdpsnd_pulse_set_format_spec(pulse, format))
return FALSE;
pulse->latency = latency;
return rdpsnd_pulse_open_stream(device);
}
static void rdpsnd_pulse_free(rdpsndDevicePlugin* device) static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
{ {
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
@ -369,9 +483,7 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
rdpsnd_pulse_close(device); rdpsnd_pulse_close(device);
if (pulse->mainloop) if (pulse->mainloop)
{
pa_threaded_mainloop_stop(pulse->mainloop); pa_threaded_mainloop_stop(pulse->mainloop);
}
if (pulse->context) if (pulse->context)
{ {
@ -394,6 +506,7 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
AUDIO_FORMAT* defaultFormat) AUDIO_FORMAT* defaultFormat)
{ {
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse || !defaultFormat) if (!pulse || !defaultFormat)
return FALSE; return FALSE;
@ -415,6 +528,9 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format) BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{ {
WINPR_ASSERT(device);
WINPR_ASSERT(format);
switch (format->wFormatTag) switch (format->wFormatTag)
{ {
case WAVE_FORMAT_PCM: case WAVE_FORMAT_PCM:
@ -427,15 +543,7 @@ BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMA
break; break;
case WAVE_FORMAT_ALAW: default:
case WAVE_FORMAT_MULAW:
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
{
return TRUE;
}
break; break;
} }
@ -447,39 +555,51 @@ static UINT32 rdpsnd_pulse_get_volume(rdpsndDevicePlugin* device)
pa_operation* o; pa_operation* o;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse) if (!rdpsnd_check_pulse(pulse, FALSE))
return 0;
if (!pulse->context || !pulse->mainloop)
return 0; return 0;
pa_threaded_mainloop_lock(pulse->mainloop); pa_threaded_mainloop_lock(pulse->mainloop);
o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse); o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
pa_operation_unref(o); if (o)
pa_operation_unref(o);
pa_threaded_mainloop_unlock(pulse->mainloop); pa_threaded_mainloop_unlock(pulse->mainloop);
return pulse->volume; return pulse->volume;
} }
static void rdpsnd_set_volume_success_cb(pa_context* c, int success, void* userdata)
{
rdpsndPulsePlugin* pulse = userdata;
if (!rdpsnd_check_pulse(pulse, TRUE))
return;
WINPR_ASSERT(c);
WLog_INFO(TAG, "%s: %d", __FUNCTION__, success);
}
static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value) static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
{ {
pa_cvolume cv; pa_cvolume cv = { 0 };
pa_volume_t left; pa_volume_t left;
pa_volume_t right; pa_volume_t right;
pa_operation* operation; pa_operation* operation;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse->context || !pulse->stream) if (!rdpsnd_check_pulse(pulse, TRUE))
{
WLog_WARN(TAG, "%s called before pulse backend was initialized");
return FALSE; return FALSE;
}
left = (pa_volume_t)(value & 0xFFFF); left = (pa_volume_t)(value & 0xFFFF);
right = (pa_volume_t)((value >> 16) & 0xFFFF); right = (pa_volume_t)((value >> 16) & 0xFFFF);
pa_cvolume_init(&cv); pa_cvolume_init(&cv);
cv.channels = 2; cv.channels = 2;
cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF; cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF; cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
pa_threaded_mainloop_lock(pulse->mainloop); pa_threaded_mainloop_lock(pulse->mainloop);
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream), operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
&cv, NULL, NULL); &cv, rdpsnd_set_volume_success_cb, pulse);
if (operation) if (operation)
pa_operation_unref(operation); pa_operation_unref(operation);
@ -491,28 +611,38 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size) static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{ {
size_t length; size_t length;
void* pa_data;
int status; int status;
pa_usec_t latency; pa_usec_t latency;
int negative; int negative;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse->stream || !data) if (!data)
return 0; return 0;
pa_threaded_mainloop_lock(pulse->mainloop); pa_threaded_mainloop_lock(pulse->mainloop);
if (!rdpsnd_check_pulse(pulse, TRUE))
{
pa_threaded_mainloop_unlock(pulse->mainloop);
// Discard this playback request and just attempt to reconnect the stream
WLog_DBG(TAG, "reconnecting playback stream");
rdpsnd_pulse_open_stream(device);
return 0;
}
while (size > 0) while (size > 0)
{ {
while ((length = pa_stream_writable_size(pulse->stream)) == 0) length = size;
pa_threaded_mainloop_wait(pulse->mainloop);
if (length == (size_t)-1) status = pa_stream_begin_write(pulse->stream, &pa_data, &length);
if (status < 0)
break; break;
if (length > size) memcpy(pa_data, data, length);
length = size;
status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE); status = pa_stream_write(pulse->stream, pa_data, length, NULL, 0LL, PA_SEEK_RELATIVE);
if (status < 0) if (status < 0)
{ {
@ -530,22 +660,24 @@ static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size
return latency / 1000; return latency / 1000;
} }
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args) static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
{ {
int status; int status;
DWORD flags; DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg; COMMAND_LINE_ARGUMENT_A* arg;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = {
"<device>", NULL, NULL, -1, NULL, "device" }, { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } }; { "reconnect_delay_seconds", COMMAND_LINE_VALUE_REQUIRED, "<reconnect_delay_seconds>", NULL,
NULL, -1, NULL, "reconnect_delay_seconds" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
flags = flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD; COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
WINPR_ASSERT(pulse);
WINPR_ASSERT(args);
status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse, status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse,
NULL, NULL); NULL, NULL);
@ -566,6 +698,15 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
if (!pulse->device_name) if (!pulse->device_name)
return ERROR_OUTOFMEMORY; return ERROR_OUTOFMEMORY;
} }
CommandLineSwitchCase(arg, "reconnect_delay_seconds")
{
unsigned long val = strtoul(arg->Value, NULL, 0);
if ((errno != 0) || (val > INT32_MAX))
return ERROR_INVALID_DATA;
pulse->reconnect_delay_seconds = val;
}
CommandLineSwitchEnd(arg) CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
@ -578,16 +719,14 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
#define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry #define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry
#endif #endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints) UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
{ {
ADDIN_ARGV* args; ADDIN_ARGV* args;
rdpsndPulsePlugin* pulse; rdpsndPulsePlugin* pulse;
UINT ret; UINT ret;
WINPR_ASSERT(pEntryPoints);
pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin)); pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin));
if (!pulse) if (!pulse)
@ -613,6 +752,8 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
goto error; goto error;
} }
} }
pulse->reconnect_delay_seconds = 5;
pulse->reconnect_time = time(NULL);
ret = CHANNEL_RC_NO_MEMORY; ret = CHANNEL_RC_NO_MEMORY;
pulse->mainloop = pa_threaded_mainloop_new(); pulse->mainloop = pa_threaded_mainloop_new();
@ -620,15 +761,17 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
if (!pulse->mainloop) if (!pulse->mainloop)
goto error; goto error;
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp"); pa_threaded_mainloop_lock(pulse->mainloop);
if (!pulse->context) if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
goto error; {
pa_threaded_mainloop_unlock(pulse->mainloop);
return FALSE;
}
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse); pa_threaded_mainloop_unlock(pulse->mainloop);
ret = ERROR_INVALID_OPERATION;
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse)) if (!rdpsnd_pulse_context_connect((rdpsndDevicePlugin*)pulse))
goto error; goto error;
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse); pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse);

View File

@ -126,6 +126,10 @@ struct rdpsnd_plugin
HANDLE thread; HANDLE thread;
wMessageQueue* queue; wMessageQueue* queue;
BOOL initialized; BOOL initialized;
UINT16 wVersion;
UINT32 volume;
BOOL applyVolume;
}; };
static const char* rdpsnd_is_dyn_str(BOOL dynamic) static const char* rdpsnd_is_dyn_str(BOOL dynamic)
@ -182,7 +186,7 @@ static void rdpsnd_select_supported_audio_formats(rdpsndPlugin* rdpsnd)
rdpsnd->ClientFormats = audio_formats_new(rdpsnd->NumberOfServerFormats); rdpsnd->ClientFormats = audio_formats_new(rdpsnd->NumberOfServerFormats);
if (!rdpsnd->ClientFormats) if (!rdpsnd->ClientFormats || !rdpsnd->device)
return; return;
for (index = 0; index < rdpsnd->NumberOfServerFormats; index++) for (index = 0; index < rdpsnd->NumberOfServerFormats; index++)
@ -213,6 +217,10 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
UINT16 length; UINT16 length;
UINT32 dwVolume; UINT32 dwVolume;
UINT16 wNumberOfFormats; UINT16 wNumberOfFormats;
if (!rdpsnd->device || (!rdpsnd->dynamic && (rdpsnd->OpenHandle == 0)))
return CHANNEL_RC_INITIALIZATION_ERROR;
dwVolume = IFCALLRESULT(0, rdpsnd->device->GetVolume, rdpsnd->device); dwVolume = IFCALLRESULT(0, rdpsnd->device->GetVolume, rdpsnd->device);
wNumberOfFormats = rdpsnd->NumberOfClientFormats; wNumberOfFormats = rdpsnd->NumberOfClientFormats;
length = 4 + 20; length = 4 + 20;
@ -264,9 +272,9 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s) static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
{ {
UINT16 index; UINT16 index;
UINT16 wVersion;
UINT16 wNumberOfFormats; UINT16 wNumberOfFormats;
UINT ret = ERROR_BAD_LENGTH; UINT ret = ERROR_BAD_LENGTH;
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats); audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
rdpsnd->NumberOfServerFormats = 0; rdpsnd->NumberOfServerFormats = 0;
rdpsnd->ServerFormats = NULL; rdpsnd->ServerFormats = NULL;
@ -281,7 +289,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
Stream_Seek_UINT16(s); /* wDGramPort */ Stream_Seek_UINT16(s); /* wDGramPort */
Stream_Read_UINT16(s, wNumberOfFormats); Stream_Read_UINT16(s, wNumberOfFormats);
Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */ Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
Stream_Read_UINT16(s, wVersion); /* wVersion */ Stream_Read_UINT16(s, rdpsnd->wVersion); /* wVersion */
Stream_Seek_UINT8(s); /* bPad */ Stream_Seek_UINT8(s); /* bPad */
rdpsnd->NumberOfServerFormats = wNumberOfFormats; rdpsnd->NumberOfServerFormats = wNumberOfFormats;
@ -308,7 +316,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
if (ret == CHANNEL_RC_OK) if (ret == CHANNEL_RC_OK)
{ {
if (wVersion >= CHANNEL_VERSION_WIN_7) if (rdpsnd->wVersion >= CHANNEL_VERSION_WIN_7)
ret = rdpsnd_send_quality_mode_pdu(rdpsnd); ret = rdpsnd_send_quality_mode_pdu(rdpsnd);
} }
@ -369,6 +377,20 @@ static UINT rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s)
return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize); return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
} }
static BOOL rdpsnd_apply_volume(rdpsndPlugin* rdpsnd)
{
WINPR_ASSERT(rdpsnd);
if (rdpsnd->isOpen && rdpsnd->applyVolume && rdpsnd->device)
{
BOOL rc = IFCALLRESULT(TRUE, rdpsnd->device->SetVolume, rdpsnd->device, rdpsnd->volume);
if (!rc)
return FALSE;
rdpsnd->applyVolume = FALSE;
}
return TRUE;
}
static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo, static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
const AUDIO_FORMAT* format) const AUDIO_FORMAT* format)
{ {
@ -417,7 +439,7 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
rdpsnd->totalPlaySize = 0; rdpsnd->totalPlaySize = 0;
} }
return TRUE; return rdpsnd_apply_volume(rdpsnd);
} }
/** /**
@ -578,6 +600,7 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
UINT64 end; UINT64 end;
UINT64 diffMS, ts; UINT64 diffMS, ts;
UINT latency = 0; UINT latency = 0;
UINT error;
if (Stream_GetRemainingLength(s) < size) if (Stream_GetRemainingLength(s) < size)
return ERROR_BAD_LENGTH; return ERROR_BAD_LENGTH;
@ -585,6 +608,15 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats) if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats)
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
/*
* Send the first WaveConfirm PDU. The server side uses this to determine the
* network latency.
* See also [MS-RDPEA] 2.2.3.8 Wave Confirm PDU
*/
error = rdpsnd_send_wave_confirm_pdu(rdpsnd, rdpsnd->wTimeStamp, rdpsnd->cBlockNo);
if (error)
return error;
data = Stream_Pointer(s); data = Stream_Pointer(s);
format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo]; format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
WLog_Print(rdpsnd->log, WLOG_DEBUG, WLog_Print(rdpsnd->log, WLOG_DEBUG,
@ -617,10 +649,11 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
diffMS = end - rdpsnd->wArrivalTime + latency; diffMS = end - rdpsnd->wArrivalTime + latency;
ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX; ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX;
/* Don't send wave confirm PDU if on dynamic channel */ /*
if (rdpsnd->dynamic) * Send the second WaveConfirm PDU. With the first WaveConfirm PDU,
return CHANNEL_RC_OK; * the server side uses this second WaveConfirm PDU to determine the actual
* render latency.
*/
return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo); return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo);
} }
@ -695,7 +728,7 @@ static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
*/ */
static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s) static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
{ {
BOOL rc; BOOL rc = TRUE;
UINT32 dwVolume; UINT32 dwVolume;
if (Stream_GetRemainingLength(s) < 4) if (Stream_GetRemainingLength(s) < 4)
@ -704,7 +737,10 @@ static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
Stream_Read_UINT32(s, dwVolume); Stream_Read_UINT32(s, dwVolume);
WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "", WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "",
rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume); rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume);
rc = IFCALLRESULT(FALSE, rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
rdpsnd->volume = dwVolume;
rdpsnd->applyVolume = TRUE;
rc = rdpsnd_apply_volume(rdpsnd);
if (!rc) if (!rc)
{ {
@ -1182,38 +1218,51 @@ static UINT rdpsnd_virtual_channel_event_connected(rdpsndPlugin* rdpsnd, LPVOID
UINT32 dataLength) UINT32 dataLength)
{ {
UINT32 status; UINT32 status;
DWORD opened = 0;
WINPR_UNUSED(pData); WINPR_UNUSED(pData);
WINPR_UNUSED(dataLength); WINPR_UNUSED(dataLength);
status = rdpsnd->channelEntryPoints.pVirtualChannelOpenEx( status = rdpsnd->channelEntryPoints.pVirtualChannelOpenEx(
rdpsnd->InitHandle, &rdpsnd->OpenHandle, rdpsnd->channelDef.name, rdpsnd->InitHandle, &opened, rdpsnd->channelDef.name, rdpsnd_virtual_channel_open_event_ex);
rdpsnd_virtual_channel_open_event_ex);
if (status != CHANNEL_RC_OK) if (status != CHANNEL_RC_OK)
{ {
WLog_ERR(TAG, "%s pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]", WLog_ERR(TAG, "%s pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]",
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(status), status); rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(status), status);
return status; goto fail;
} }
rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE); if (rdpsnd_process_connect(rdpsnd) != CHANNEL_RC_OK)
if (!rdpsnd->dsp_context)
goto fail; goto fail;
rdpsnd->pool = StreamPool_New(TRUE, 4096); rdpsnd->OpenHandle = opened;
return CHANNEL_RC_OK;
if (!rdpsnd->pool)
goto fail;
return rdpsnd_process_connect(rdpsnd);
fail: fail:
freerdp_dsp_context_free(rdpsnd->dsp_context); if (opened != 0)
StreamPool_Free(rdpsnd->pool); rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened);
return CHANNEL_RC_NO_MEMORY; return CHANNEL_RC_NO_MEMORY;
} }
static void cleanup_internals(rdpsndPlugin* rdpsnd)
{
if (!rdpsnd)
return;
if (rdpsnd->pool)
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
rdpsnd->NumberOfClientFormats = 0;
rdpsnd->ClientFormats = NULL;
rdpsnd->NumberOfServerFormats = 0;
rdpsnd->ServerFormats = NULL;
rdpsnd->data_in = NULL;
}
/** /**
* Function description * Function description
* *
@ -1223,33 +1272,25 @@ static UINT rdpsnd_virtual_channel_event_disconnected(rdpsndPlugin* rdpsnd)
{ {
UINT error; UINT error;
if (rdpsnd->OpenHandle == 0) if (rdpsnd->OpenHandle != 0)
return CHANNEL_RC_OK;
if (rdpsnd->device)
IFCALL(rdpsnd->device->Close, rdpsnd->device);
error =
rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, rdpsnd->OpenHandle);
if (CHANNEL_RC_OK != error)
{ {
WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", DWORD opened = rdpsnd->OpenHandle;
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error); rdpsnd->OpenHandle = 0;
return error;
if (rdpsnd->device)
IFCALL(rdpsnd->device->Close, rdpsnd->device);
error = rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened);
if (CHANNEL_RC_OK != error)
{
WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error);
return error;
}
} }
rdpsnd->OpenHandle = 0; cleanup_internals(rdpsnd);
freerdp_dsp_context_free(rdpsnd->dsp_context);
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
StreamPool_Free(rdpsnd->pool);
audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
rdpsnd->NumberOfClientFormats = 0;
rdpsnd->ClientFormats = NULL;
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
rdpsnd->NumberOfServerFormats = 0;
rdpsnd->ServerFormats = NULL;
if (rdpsnd->device) if (rdpsnd->device)
{ {
@ -1266,6 +1307,36 @@ static void _queue_free(void* obj)
Stream_Release(s); Stream_Release(s);
} }
static void free_internals(rdpsndPlugin* rdpsnd)
{
if (!rdpsnd)
return;
freerdp_dsp_context_free(rdpsnd->dsp_context);
StreamPool_Free(rdpsnd->pool);
rdpsnd->pool = NULL;
rdpsnd->dsp_context = NULL;
}
static BOOL allocate_internals(rdpsndPlugin* rdpsnd)
{
if (!rdpsnd->pool)
{
rdpsnd->pool = StreamPool_New(TRUE, 4096);
if (!rdpsnd->pool)
return FALSE;
}
if (!rdpsnd->dsp_context)
{
rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE);
if (!rdpsnd->dsp_context)
return FALSE;
}
return TRUE;
}
static DWORD WINAPI play_thread(LPVOID arg) static DWORD WINAPI play_thread(LPVOID arg)
{ {
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
@ -1311,17 +1382,22 @@ static UINT rdpsnd_virtual_channel_event_initialized(rdpsndPlugin* rdpsnd)
if (!rdpsnd->queue) if (!rdpsnd->queue)
return CHANNEL_RC_NO_MEMORY; return CHANNEL_RC_NO_MEMORY;
if (!allocate_internals(rdpsnd))
return CHANNEL_RC_NO_MEMORY;
rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL); rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
if (!rdpsnd->thread) if (!rdpsnd->thread)
return CHANNEL_RC_INITIALIZATION_ERROR; return CHANNEL_RC_INITIALIZATION_ERROR;
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd) void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
{ {
if (rdpsnd) if (rdpsnd)
{ {
MessageQueue_PostQuit(rdpsnd->queue, 0); if (rdpsnd->queue)
MessageQueue_PostQuit(rdpsnd->queue, 0);
if (rdpsnd->thread) if (rdpsnd->thread)
{ {
WaitForSingleObject(rdpsnd->thread, INFINITE); WaitForSingleObject(rdpsnd->thread, INFINITE);
@ -1329,6 +1405,7 @@ static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
} }
MessageQueue_Free(rdpsnd->queue); MessageQueue_Free(rdpsnd->queue);
free_internals(rdpsnd);
audio_formats_free(rdpsnd->fixed_format, 1); audio_formats_free(rdpsnd->fixed_format, 1);
free(rdpsnd->subsystem); free(rdpsnd->subsystem);
free(rdpsnd->device_name); free(rdpsnd->device_name);
@ -1370,6 +1447,7 @@ static VOID VCAPITYPE rdpsnd_virtual_channel_init_event_ex(LPVOID lpUserParam, L
case CHANNEL_EVENT_TERMINATED: case CHANNEL_EVENT_TERMINATED:
rdpsnd_virtual_channel_event_terminated(plugin); rdpsnd_virtual_channel_event_terminated(plugin);
plugin = NULL;
break; break;
case CHANNEL_EVENT_ATTACHED: case CHANNEL_EVENT_ATTACHED:
@ -1401,6 +1479,27 @@ rdpContext* freerdp_rdpsnd_get_context(rdpsndPlugin* plugin)
return plugin->rdpcontext; return plugin->rdpcontext;
} }
static rdpsndPlugin* allocatePlugin(void)
{
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin));
if (!rdpsnd)
goto fail;
rdpsnd->fixed_format = audio_format_new();
if (!rdpsnd->fixed_format)
goto fail;
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
if (!rdpsnd->log)
goto fail;
rdpsnd->attached = TRUE;
return rdpsnd;
fail:
if (rdpsnd)
audio_format_free(rdpsnd->fixed_format);
return NULL;
}
/* rdpsnd is always built-in */ /* rdpsnd is always built-in */
BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle) BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
{ {
@ -1411,12 +1510,11 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
if (!pEntryPoints) if (!pEntryPoints)
return FALSE; return FALSE;
rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin)); rdpsnd = allocatePlugin();
if (!rdpsnd) if (!rdpsnd)
return FALSE; return FALSE;
rdpsnd->attached = TRUE;
rdpsnd->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP; rdpsnd->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP;
sprintf_s(rdpsnd->channelDef.name, ARRAYSIZE(rdpsnd->channelDef.name), "rdpsnd"); sprintf_s(rdpsnd->channelDef.name, ARRAYSIZE(rdpsnd->channelDef.name), "rdpsnd");
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints; pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
@ -1427,15 +1525,6 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
rdpsnd->rdpcontext = pEntryPointsEx->context; rdpsnd->rdpcontext = pEntryPointsEx->context;
} }
rdpsnd->fixed_format = audio_format_new();
if (!rdpsnd->fixed_format)
{
free(rdpsnd);
return FALSE;
}
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints, CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints,
sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)); sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
rdpsnd->InitHandle = pInitHandle; rdpsnd->InitHandle = pInitHandle;
@ -1447,7 +1536,7 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
{ {
WLog_ERR(TAG, "%s pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", WLog_ERR(TAG, "%s pVirtualChannelInitEx failed with %s [%08" PRIX32 "]",
rdpsnd_is_dyn_str(FALSE), WTSErrorToString(rc), rc); rdpsnd_is_dyn_str(FALSE), WTSErrorToString(rc), rc);
free(rdpsnd); rdpsnd_virtual_channel_event_terminated(rdpsnd);
return FALSE; return FALSE;
} }
@ -1459,19 +1548,10 @@ static UINT rdpsnd_on_open(IWTSVirtualChannelCallback* pChannelCallback)
RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback; RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback;
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)callback->plugin; rdpsndPlugin* rdpsnd = (rdpsndPlugin*)callback->plugin;
rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE); if (!allocate_internals(rdpsnd))
if (!rdpsnd->dsp_context) return ERROR_OUTOFMEMORY;
goto fail;
rdpsnd->pool = StreamPool_New(TRUE, 4096);
if (!rdpsnd->pool)
goto fail;
return rdpsnd_process_connect(rdpsnd); return rdpsnd_process_connect(rdpsnd);
fail:
freerdp_dsp_context_free(rdpsnd->dsp_context);
StreamPool_Free(rdpsnd->pool);
return CHANNEL_RC_NO_MEMORY;
} }
static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
@ -1479,7 +1559,9 @@ static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback
RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback; RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback;
rdpsndPlugin* plugin; rdpsndPlugin* plugin;
wStream* copy; wStream* copy;
size_t len = Stream_GetRemainingLength(data); size_t len;
len = Stream_GetRemainingLength(data);
if (!callback || !callback->plugin) if (!callback || !callback->plugin)
return ERROR_INVALID_PARAMETER; return ERROR_INVALID_PARAMETER;
@ -1508,22 +1590,16 @@ static UINT rdpsnd_on_close(IWTSVirtualChannelCallback* pChannelCallback)
if (rdpsnd->device) if (rdpsnd->device)
IFCALL(rdpsnd->device->Close, rdpsnd->device); IFCALL(rdpsnd->device->Close, rdpsnd->device);
freerdp_dsp_context_free(rdpsnd->dsp_context);
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
StreamPool_Free(rdpsnd->pool);
audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats); cleanup_internals(rdpsnd);
rdpsnd->NumberOfClientFormats = 0;
rdpsnd->ClientFormats = NULL;
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
rdpsnd->NumberOfServerFormats = 0;
rdpsnd->ServerFormats = NULL;
if (rdpsnd->device) if (rdpsnd->device)
{ {
IFCALL(rdpsnd->device->Free, rdpsnd->device); IFCALL(rdpsnd->device->Free, rdpsnd->device);
rdpsnd->device = NULL; rdpsnd->device = NULL;
} }
free_internals(rdpsnd);
free(pChannelCallback); free(pChannelCallback);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -1622,7 +1698,7 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
if (!rdpsnd) if (!rdpsnd)
{ {
rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin)); rdpsnd = allocatePlugin();
if (!rdpsnd) if (!rdpsnd)
{ {
WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE)); WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE));
@ -1633,14 +1709,10 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
rdpsnd->iface.Connected = NULL; rdpsnd->iface.Connected = NULL;
rdpsnd->iface.Disconnected = NULL; rdpsnd->iface.Disconnected = NULL;
rdpsnd->iface.Terminated = rdpsnd_plugin_terminated; rdpsnd->iface.Terminated = rdpsnd_plugin_terminated;
rdpsnd->attached = TRUE;
rdpsnd->dynamic = TRUE; rdpsnd->dynamic = TRUE;
rdpsnd->fixed_format = audio_format_new();
if (!rdpsnd->fixed_format)
goto fail;
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client"); /* user data pointer is not const, cast to avoid warning. */
rdpsnd->channelEntryPoints.pExtendedData = pEntryPoints->GetPluginData(pEntryPoints); rdpsnd->channelEntryPoints.pExtendedData = (void*)pEntryPoints->GetPluginData(pEntryPoints);
error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpsnd", &rdpsnd->iface); error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpsnd", &rdpsnd->iface);
} }
@ -1650,8 +1722,5 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
return CHANNEL_RC_BAD_CHANNEL; return CHANNEL_RC_BAD_CHANNEL;
} }
return error;
fail:
rdpsnd_plugin_terminated(&rdpsnd->iface);
return error; return error;
} }

View File

@ -28,6 +28,7 @@
#include <string.h> #include <string.h>
#include <winpr/crt.h> #include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/print.h> #include <winpr/print.h>
#include <winpr/stream.h> #include <winpr/stream.h>
@ -36,17 +37,33 @@
#include "rdpsnd_common.h" #include "rdpsnd_common.h"
#include "rdpsnd_main.h" #include "rdpsnd_main.h"
static wStream* rdpsnd_server_get_buffer(RdpsndServerContext* context)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
s = context->priv->rdpsnd_pdu;
Stream_SetPosition(s, 0);
return s;
}
/** /**
* Function description * Send Server Audio Formats and Version PDU (2.2.2.1)
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) static UINT rdpsnd_server_send_formats(RdpsndServerContext* context)
{ {
wStream* s = rdpsnd_server_get_buffer(context);
size_t pos; size_t pos;
UINT16 i; UINT16 i;
BOOL status = FALSE; BOOL status = FALSE;
ULONG written; ULONG written;
if (!Stream_EnsureRemainingCapacity(s, 24))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT8(s, SNDC_FORMATS); Stream_Write_UINT8(s, SNDC_FORMATS);
Stream_Write_UINT8(s, 0); Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s); Stream_Seek_UINT16(s);
@ -61,12 +78,9 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
for (i = 0; i < context->num_server_formats; i++) for (i = 0; i < context->num_server_formats; i++)
{ {
AUDIO_FORMAT format = context->server_formats[i]; const AUDIO_FORMAT* format = &context->server_formats[i];
// TODO: Eliminate this!!!
format.nAvgBytesPerSec =
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
if (!audio_format_write(s, &format)) if (!audio_format_write(s, format))
goto fail; goto fail;
} }
@ -74,6 +88,8 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
Stream_SetPosition(s, 2); Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, pos - 4); Stream_Write_UINT16(s, pos - 4);
Stream_SetPosition(s, pos); Stream_SetPosition(s, pos);
WINPR_ASSERT(context->priv);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written); Stream_GetPosition(s), &written);
Stream_SetPosition(s, 0); Stream_SetPosition(s, 0);
@ -82,7 +98,7 @@ fail:
} }
/** /**
* Function description * Read Wave Confirm PDU (2.2.3.8) and handle callback
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
@ -92,11 +108,10 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
BYTE confirmBlockNum; BYTE confirmBlockNum;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
if (Stream_GetRemainingLength(s) < 4) WINPR_ASSERT(context);
{
WLog_ERR(TAG, "not enough data in stream!"); if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
}
Stream_Read_UINT16(s, timestamp); Stream_Read_UINT16(s, timestamp);
Stream_Read_UINT8(s, confirmBlockNum); Stream_Read_UINT8(s, confirmBlockNum);
@ -110,13 +125,39 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
} }
/** /**
* Function description * Read Training Confirm PDU (2.2.3.2) and handle callback
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_recv_trainingconfirm(RdpsndServerContext* context, wStream* s)
{
UINT16 timestamp;
UINT16 packsize;
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, timestamp);
Stream_Read_UINT16(s, packsize);
IFCALLRET(context->TrainingConfirm, error, context, timestamp, packsize);
if (error)
WLog_ERR(TAG, "context->TrainingConfirm failed with error %" PRIu32 "", error);
return error;
}
/**
* Read Quality Mode PDU (2.2.2.3)
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s) static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s)
{ {
UINT16 quality; WINPR_ASSERT(context);
if (Stream_GetRemainingLength(s) < 4) if (Stream_GetRemainingLength(s) < 4)
{ {
@ -124,34 +165,34 @@ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStrea
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
} }
Stream_Read_UINT16(s, quality); Stream_Read_UINT16(s, context->qualityMode); /* wQualityMode */
Stream_Seek_UINT16(s); // reserved Stream_Seek_UINT16(s); /* Reserved */
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", quality);
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", context->qualityMode);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
/** /**
* Function description * Read Client Audio Formats and Version PDU (2.2.2.2)
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
{ {
UINT16 i, num_known_format = 0; UINT16 i, num_known_format = 0;
UINT32 flags, vol, pitch;
UINT16 udpPort; UINT16 udpPort;
BYTE lastblock; BYTE lastblock;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
if (Stream_GetRemainingLength(s) < 20) WINPR_ASSERT(context);
{
WLog_ERR(TAG, "not enough data in stream!");
return ERROR_INVALID_DATA;
}
Stream_Read_UINT32(s, flags); /* dwFlags */ if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
Stream_Read_UINT32(s, vol); /* dwVolume */ return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, pitch); /* dwPitch */
Stream_Read_UINT32(s, context->capsFlags); /* dwFlags */
Stream_Read_UINT32(s, context->initialVolume); /* dwVolume */
Stream_Read_UINT32(s, context->initialPitch); /* dwPitch */
Stream_Read_UINT16(s, udpPort); /* wDGramPort */ Stream_Read_UINT16(s, udpPort); /* wDGramPort */
Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */ Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */
Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */
@ -159,11 +200,8 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
Stream_Seek_UINT8(s); /* bPad */ Stream_Seek_UINT8(s); /* bPad */
/* this check is only a guess as cbSize can influence the size of a format record */ /* this check is only a guess as cbSize can influence the size of a format record */
if (Stream_GetRemainingLength(s) < context->num_client_formats * 18) if (!Stream_CheckAndLogRequiredLength(TAG, s, 18ull * context->num_client_formats))
{
WLog_ERR(TAG, "not enough data in stream!");
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
}
if (!context->num_client_formats) if (!context->num_client_formats)
{ {
@ -181,24 +219,26 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
for (i = 0; i < context->num_client_formats; i++) for (i = 0; i < context->num_client_formats; i++)
{ {
if (Stream_GetRemainingLength(s) < 18) AUDIO_FORMAT* format = &context->client_formats[i];
if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
{ {
WLog_ERR(TAG, "not enough data in stream!"); WLog_ERR(TAG, "not enough data in stream!");
error = ERROR_INVALID_DATA; error = ERROR_INVALID_DATA;
goto out_free; goto out_free;
} }
Stream_Read_UINT16(s, context->client_formats[i].wFormatTag); Stream_Read_UINT16(s, format->wFormatTag);
Stream_Read_UINT16(s, context->client_formats[i].nChannels); Stream_Read_UINT16(s, format->nChannels);
Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec); Stream_Read_UINT32(s, format->nSamplesPerSec);
Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec); Stream_Read_UINT32(s, format->nAvgBytesPerSec);
Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign); Stream_Read_UINT16(s, format->nBlockAlign);
Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample); Stream_Read_UINT16(s, format->wBitsPerSample);
Stream_Read_UINT16(s, context->client_formats[i].cbSize); Stream_Read_UINT16(s, format->cbSize);
if (context->client_formats[i].cbSize > 0) if (format->cbSize > 0)
{ {
if (!Stream_SafeSeek(s, context->client_formats[i].cbSize)) if (!Stream_SafeSeek(s, format->cbSize))
{ {
WLog_ERR(TAG, "Stream_SafeSeek failed!"); WLog_ERR(TAG, "Stream_SafeSeek failed!");
error = ERROR_INTERNAL_ERROR; error = ERROR_INTERNAL_ERROR;
@ -206,7 +246,7 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
} }
} }
if (context->client_formats[i].wFormatTag != 0) if (format->wFormatTag != 0)
{ {
// lets call this a known format // lets call this a known format
// TODO: actually look through our own list of known formats // TODO: actually look through our own list of known formats
@ -228,15 +268,19 @@ out_free:
static DWORD WINAPI rdpsnd_server_thread(LPVOID arg) static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
{ {
DWORD nCount, status; DWORD nCount = 0, status;
HANDLE events[8]; HANDLE events[2] = { 0 };
RdpsndServerContext* context; RdpsndServerContext* context = (RdpsndServerContext*)arg;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
context = (RdpsndServerContext*)arg;
nCount = 0; WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
events[nCount++] = context->priv->channelEvent; events[nCount++] = context->priv->channelEvent;
events[nCount++] = context->priv->StopEvent; events[nCount++] = context->priv->StopEvent;
WINPR_ASSERT(nCount <= ARRAYSIZE(events));
while (TRUE) while (TRUE)
{ {
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
@ -281,6 +325,9 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
*/ */
static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread) static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread)
{ {
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
context->priv->ownThread = ownThread; context->priv->ownThread = ownThread;
return context->Start(context); return context->Start(context);
} }
@ -297,6 +344,9 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, UINT16 cli
AUDIO_FORMAT* format; AUDIO_FORMAT* format;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if ((client_format_index >= context->num_client_formats) || (!context->src_format)) if ((client_format_index >= context->num_client_formats) || (!context->src_format))
{ {
WLog_ERR(TAG, "index %d is not correct.", client_format_index); WLog_ERR(TAG, "index %d is not correct.", client_format_index);
@ -371,6 +421,51 @@ out:
return error; return error;
} }
/**
* Send Training PDU (2.2.3.1)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_training(RdpsndServerContext* context, UINT16 timestamp, UINT16 packsize,
BYTE* data)
{
size_t end = 0;
ULONG written;
BOOL status;
wStream* s = rdpsnd_server_get_buffer(context);
if (!Stream_EnsureRemainingCapacity(s, 8))
return ERROR_INTERNAL_ERROR;
Stream_Write_UINT8(s, SNDC_TRAINING);
Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s);
Stream_Write_UINT16(s, timestamp);
Stream_Write_UINT16(s, packsize);
if (packsize > 0)
{
if (!Stream_EnsureRemainingCapacity(s, packsize))
{
Stream_SetPosition(s, 0);
return ERROR_INTERNAL_ERROR;
}
Stream_Write(s, data, packsize);
}
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - 4);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
&written);
Stream_SetPosition(s, 0);
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment) static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment)
{ {
size_t size; size_t size;
@ -404,15 +499,21 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
const BYTE* src; const BYTE* src;
AUDIO_FORMAT* format; AUDIO_FORMAT* format;
ULONG written; ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
wStream* s = rdpsnd_server_get_buffer(context);
if (context->selected_client_format >= context->num_client_formats) if (context->selected_client_format > context->num_client_formats)
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
WINPR_ASSERT(context->client_formats);
format = &context->client_formats[context->selected_client_format]; format = &context->client_formats[context->selected_client_format];
/* WaveInfo PDU */ /* WaveInfo PDU */
Stream_SetPosition(s, 0); Stream_SetPosition(s, 0);
if (!Stream_EnsureRemainingCapacity(s, 16))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */ Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */ Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, 0); /* BodySize */ Stream_Write_UINT16(s, 0); /* BodySize */
@ -422,7 +523,7 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
Stream_Seek(s, 3); /* bPad */ Stream_Seek(s, 3); /* bPad */
start = Stream_GetPosition(s); start = Stream_GetPosition(s);
src = context->priv->out_buffer; src = context->priv->out_buffer;
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; length = 1ull * context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s)) if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
@ -436,7 +537,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
Stream_SetPosition(s, 2); Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - start + 8); Stream_Write_UINT16(s, end - start + 8);
Stream_SetPosition(s, end); Stream_SetPosition(s, end);
context->block_no = (context->block_no + 1) % 256;
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
start + 4, &written)) start + 4, &written))
@ -464,6 +564,8 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
error = ERROR_INTERNAL_ERROR; error = ERROR_INTERNAL_ERROR;
} }
context->block_no = (context->block_no + 1) % 256;
out: out:
Stream_SetPosition(s, 0); Stream_SetPosition(s, 0);
context->priv->out_pending_frames = 0; context->priv->out_pending_frames = 0;
@ -476,59 +578,79 @@ out:
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wTimestamp) static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 formatNo,
const BYTE* data, size_t size, BOOL encoded,
UINT16 timestamp, UINT32 audioTimeStamp)
{ {
size_t length;
size_t end = 0; size_t end = 0;
const BYTE* src;
AUDIO_FORMAT* format;
ULONG written; ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
BOOL status;
wStream* s = rdpsnd_server_get_buffer(context);
if (context->selected_client_format >= context->num_client_formats) if (!Stream_EnsureRemainingCapacity(s, 16))
return ERROR_INTERNAL_ERROR; {
format = &context->client_formats[context->selected_client_format];
/* WaveInfo PDU */
Stream_SetPosition(s, 0);
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, 0); /* BodySize */
Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */
Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
Stream_Seek(s, 3); /* bPad */
Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */
src = context->priv->out_buffer;
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
error = ERROR_INTERNAL_ERROR; error = ERROR_INTERNAL_ERROR;
goto out;
}
/* Wave2 PDU */
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, 0); /* BodySize */
Stream_Write_UINT16(s, timestamp); /* wTimeStamp */
Stream_Write_UINT16(s, formatNo); /* wFormatNo */
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT32(s, audioTimeStamp); /* dwAudioTimeStamp */
if (encoded)
{
if (!Stream_EnsureRemainingCapacity(s, size))
{
error = ERROR_INTERNAL_ERROR;
goto out;
}
Stream_Write(s, data, size);
}
else else
{ {
BOOL rc; AUDIO_FORMAT* format;
/* Set stream size */ if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, data, size, s))
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
return ERROR_INTERNAL_ERROR;
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - 4);
context->block_no = (context->block_no + 1) % 256;
rc = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
&written);
if (!rc || (end != written))
{ {
WLog_ERR(TAG,
"WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
end, written);
error = ERROR_INTERNAL_ERROR; error = ERROR_INTERNAL_ERROR;
goto out;
}
format = &context->client_formats[formatNo];
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
{
error = ERROR_INTERNAL_ERROR;
goto out;
} }
} }
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - 4);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
&written);
if (!status || (end != written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
end, written);
error = ERROR_INTERNAL_ERROR;
}
context->block_no = (context->block_no + 1) % 256;
out:
Stream_SetPosition(s, 0); Stream_SetPosition(s, 0);
context->priv->out_pending_frames = 0; context->priv->out_pending_frames = 0;
return error; return error;
@ -537,8 +659,21 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wT
/* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */ /* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp) static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
{ {
const BYTE* src;
size_t length;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if (context->selected_client_format >= context->num_client_formats)
return ERROR_INTERNAL_ERROR;
src = context->priv->out_buffer;
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
if (context->clientVersion >= CHANNEL_VERSION_WIN_8) if (context->clientVersion >= CHANNEL_VERSION_WIN_8)
return rdpsnd_server_send_wave2_pdu(context, wTimestamp); return rdpsnd_server_send_wave2_pdu(context, context->selected_client_format, src, length,
FALSE, wTimestamp, wTimestamp);
else else
return rdpsnd_server_send_wave_pdu(context, wTimestamp); return rdpsnd_server_send_wave_pdu(context, wTimestamp);
} }
@ -551,9 +686,11 @@ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wT
static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes, static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes,
UINT16 wTimestamp) UINT16 wTimestamp)
{ {
int cframes;
int cframesize;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
EnterCriticalSection(&context->priv->lock); EnterCriticalSection(&context->priv->lock);
if (context->selected_client_format >= context->num_client_formats) if (context->selected_client_format >= context->num_client_formats)
@ -566,12 +703,13 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void*
while (nframes > 0) while (nframes > 0)
{ {
cframes = MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames); const size_t cframes =
cframesize = cframes * context->priv->src_bytes_per_frame; MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames);
size_t cframesize = cframes * context->priv->src_bytes_per_frame;
CopyMemory(context->priv->out_buffer + CopyMemory(context->priv->out_buffer +
(context->priv->out_pending_frames * context->priv->src_bytes_per_frame), (context->priv->out_pending_frames * context->priv->src_bytes_per_frame),
buf, cframesize); buf, cframesize);
buf = (BYTE*)buf + cframesize; buf = (const BYTE*)buf + cframesize;
nframes -= cframes; nframes -= cframes;
context->priv->out_pending_frames += cframes; context->priv->out_pending_frames += cframes;
@ -590,6 +728,33 @@ out:
return error; return error;
} }
/**
* Send encoded audio samples using a Wave2 PDU.
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 formatNo,
const void* buf, size_t size, UINT16 timestamp,
UINT32 audioTimeStamp)
{
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if (context->clientVersion < CHANNEL_VERSION_WIN_8)
return ERROR_INTERNAL_ERROR;
EnterCriticalSection(&context->priv->lock);
error =
rdpsnd_server_send_wave2_pdu(context, formatNo, buf, size, TRUE, timestamp, audioTimeStamp);
LeaveCriticalSection(&context->priv->lock);
return error;
}
/** /**
* Function description * Function description
* *
@ -597,21 +762,23 @@ out:
*/ */
static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right) static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right)
{ {
size_t pos; size_t len;
BOOL status; BOOL status;
ULONG written; ULONG written;
wStream* s = context->priv->rdpsnd_pdu; wStream* s = rdpsnd_server_get_buffer(context);
if (!Stream_EnsureRemainingCapacity(s, 8))
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, SNDC_SETVOLUME); Stream_Write_UINT8(s, SNDC_SETVOLUME);
Stream_Write_UINT8(s, 0); Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s); Stream_Write_UINT16(s, 4); /* Payload length */
Stream_Write_UINT16(s, left); Stream_Write_UINT16(s, left);
Stream_Write_UINT16(s, right); Stream_Write_UINT16(s, right);
pos = Stream_GetPosition(s); len = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, pos - 4);
Stream_SetPosition(s, pos);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written); (ULONG)len, &written);
Stream_SetPosition(s, 0); Stream_SetPosition(s, 0);
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
} }
@ -626,8 +793,9 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
size_t pos; size_t pos;
BOOL status; BOOL status;
ULONG written; ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
wStream* s = rdpsnd_server_get_buffer(context);
EnterCriticalSection(&context->priv->lock); EnterCriticalSection(&context->priv->lock);
if (context->priv->out_pending_frames > 0) if (context->priv->out_pending_frames > 0)
@ -649,6 +817,10 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
return error; return error;
context->selected_client_format = 0xFFFF; context->selected_client_format = 0xFFFF;
if (!Stream_EnsureRemainingCapacity(s, 4))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT8(s, SNDC_CLOSE); Stream_Write_UINT8(s, SNDC_CLOSE);
Stream_Write_UINT8(s, 0); Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s); Stream_Seek_UINT16(s);
@ -671,14 +843,58 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
{ {
void* buffer = NULL; void* buffer = NULL;
DWORD bytesReturned; DWORD bytesReturned;
RdpsndServerPrivate* priv = context->priv; RdpsndServerPrivate* priv;
UINT error = ERROR_INTERNAL_ERROR; UINT error = ERROR_INTERNAL_ERROR;
priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd"); PULONG pSessionId = NULL;
if (!priv->ChannelHandle) WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
priv = context->priv;
priv->SessionId = WTS_CURRENT_SESSION;
if (context->use_dynamic_virtual_channel)
{ {
WLog_ERR(TAG, "WTSVirtualChannelOpen failed!"); UINT32 channelId;
return ERROR_INTERNAL_ERROR; BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &bytesReturned))
{
priv->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
priv->ChannelHandle = (HANDLE)WTSVirtualChannelOpenEx(
priv->SessionId, "AUDIO_PLAYBACK_DVC", WTS_CHANNEL_OPTION_DYNAMIC);
if (!priv->ChannelHandle)
{
WLog_ERR(TAG, "Open audio dynamic virtual channel (AUDIO_PLAYBACK_DVC) failed!");
return ERROR_INTERNAL_ERROR;
}
channelId = WTSChannelGetIdByHandle(priv->ChannelHandle);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
goto out_close;
}
}
else
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
}
else
{
priv->ChannelHandle =
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd");
if (!priv->ChannelHandle)
{
WLog_ERR(TAG, "Open audio static virtual channel (rdpsnd) failed!");
return ERROR_INTERNAL_ERROR;
}
} }
if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer, if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
@ -713,7 +929,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
goto out_pdu; goto out_pdu;
} }
if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu))) if ((error = rdpsnd_server_send_formats(context)))
{ {
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error); WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error);
goto out_lock; goto out_lock;
@ -763,6 +979,12 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context)
{ {
UINT error = CHANNEL_RC_OK; UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if (!context->priv->StopEvent)
return error;
if (context->priv->ownThread) if (context->priv->ownThread)
{ {
if (context->priv->StopEvent) if (context->priv->StopEvent)
@ -778,36 +1000,46 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context)
CloseHandle(context->priv->Thread); CloseHandle(context->priv->Thread);
CloseHandle(context->priv->StopEvent); CloseHandle(context->priv->StopEvent);
context->priv->Thread = NULL;
context->priv->StopEvent = NULL;
} }
} }
DeleteCriticalSection(&context->priv->lock); DeleteCriticalSection(&context->priv->lock);
if (context->priv->rdpsnd_pdu) if (context->priv->rdpsnd_pdu)
{
Stream_Free(context->priv->rdpsnd_pdu, TRUE); Stream_Free(context->priv->rdpsnd_pdu, TRUE);
context->priv->rdpsnd_pdu = NULL;
}
if (context->priv->ChannelHandle)
{
WTSVirtualChannelClose(context->priv->ChannelHandle);
context->priv->ChannelHandle = NULL;
}
return error; return error;
} }
RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
{ {
RdpsndServerContext* context;
RdpsndServerPrivate* priv; RdpsndServerPrivate* priv;
context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext)); RdpsndServerContext* context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext));
if (!context) if (!context)
{ goto fail;
WLog_ERR(TAG, "calloc failed!");
return NULL;
}
context->vcm = vcm; context->vcm = vcm;
context->Start = rdpsnd_server_start; context->Start = rdpsnd_server_start;
context->Stop = rdpsnd_server_stop; context->Stop = rdpsnd_server_stop;
context->selected_client_format = 0xFFFF; context->selected_client_format = 0xFFFF;
context->Initialize = rdpsnd_server_initialize; context->Initialize = rdpsnd_server_initialize;
context->SendFormats = rdpsnd_server_send_formats;
context->SelectFormat = rdpsnd_server_select_format; context->SelectFormat = rdpsnd_server_select_format;
context->Training = rdpsnd_server_training;
context->SendSamples = rdpsnd_server_send_samples; context->SendSamples = rdpsnd_server_send_samples;
context->SendSamples2 = rdpsnd_server_send_samples2;
context->SetVolume = rdpsnd_server_set_volume; context->SetVolume = rdpsnd_server_set_volume;
context->Close = rdpsnd_server_close; context->Close = rdpsnd_server_close;
context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate)); context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate));
@ -815,7 +1047,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
if (!priv) if (!priv)
{ {
WLog_ERR(TAG, "calloc failed!"); WLog_ERR(TAG, "calloc failed!");
goto out_free; goto fail;
} }
priv->dsp_context = freerdp_dsp_context_new(TRUE); priv->dsp_context = freerdp_dsp_context_new(TRUE);
@ -823,7 +1055,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
if (!priv->dsp_context) if (!priv->dsp_context)
{ {
WLog_ERR(TAG, "freerdp_dsp_context_new failed!"); WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
goto out_free_priv; goto fail;
} }
priv->input_stream = Stream_New(NULL, 4); priv->input_stream = Stream_New(NULL, 4);
@ -831,24 +1063,23 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
if (!priv->input_stream) if (!priv->input_stream)
{ {
WLog_ERR(TAG, "Stream_New failed!"); WLog_ERR(TAG, "Stream_New failed!");
goto out_free_dsp; goto fail;
} }
priv->expectedBytes = 4; priv->expectedBytes = 4;
priv->waitingHeader = TRUE; priv->waitingHeader = TRUE;
priv->ownThread = TRUE; priv->ownThread = TRUE;
return context; return context;
out_free_dsp: fail:
freerdp_dsp_context_free(priv->dsp_context); rdpsnd_server_context_free(context);
out_free_priv:
free(context->priv);
out_free:
free(context);
return NULL; return NULL;
} }
void rdpsnd_server_context_reset(RdpsndServerContext* context) void rdpsnd_server_context_reset(RdpsndServerContext* context)
{ {
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
context->priv->expectedBytes = 4; context->priv->expectedBytes = 4;
context->priv->waitingHeader = TRUE; context->priv->waitingHeader = TRUE;
Stream_SetPosition(context->priv->input_stream, 0); Stream_SetPosition(context->priv->input_stream, 0);
@ -856,16 +1087,21 @@ void rdpsnd_server_context_reset(RdpsndServerContext* context)
void rdpsnd_server_context_free(RdpsndServerContext* context) void rdpsnd_server_context_free(RdpsndServerContext* context)
{ {
if (context->priv->ChannelHandle) if (!context)
WTSVirtualChannelClose(context->priv->ChannelHandle); return;
free(context->priv->out_buffer); if (context->priv)
{
rdpsnd_server_stop(context);
if (context->priv->dsp_context) free(context->priv->out_buffer);
freerdp_dsp_context_free(context->priv->dsp_context);
if (context->priv->input_stream) if (context->priv->dsp_context)
Stream_Free(context->priv->input_stream, TRUE); freerdp_dsp_context_free(context->priv->dsp_context);
if (context->priv->input_stream)
Stream_Free(context->priv->input_stream, TRUE);
}
free(context->server_formats); free(context->server_formats);
free(context->client_formats); free(context->client_formats);
@ -875,6 +1111,9 @@ void rdpsnd_server_context_free(RdpsndServerContext* context)
HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context) HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context)
{ {
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
return context->priv->channelEvent; return context->priv->channelEvent;
} }
@ -896,8 +1135,14 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
{ {
DWORD bytesReturned; DWORD bytesReturned;
UINT ret = CHANNEL_RC_OK; UINT ret = CHANNEL_RC_OK;
RdpsndServerPrivate* priv = context->priv; RdpsndServerPrivate* priv;
wStream* s = priv->input_stream; wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
priv = context->priv;
s = priv->input_stream;
if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s), if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s),
priv->expectedBytes, &bytesReturned)) priv->expectedBytes, &bytesReturned))
@ -952,6 +1197,10 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
ret = rdpsnd_server_recv_waveconfirm(context, s); ret = rdpsnd_server_recv_waveconfirm(context, s);
break; break;
case SNDC_TRAINING:
ret = rdpsnd_server_recv_trainingconfirm(context, s);
break;
case SNDC_FORMATS: case SNDC_FORMATS:
ret = rdpsnd_server_recv_formats(context, s); ret = rdpsnd_server_recv_formats(context, s);
@ -962,8 +1211,6 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
case SNDC_QUALITYMODE: case SNDC_QUALITYMODE:
ret = rdpsnd_server_recv_quality_mode(context, s); ret = rdpsnd_server_recv_quality_mode(context, s);
Stream_SetPosition(s,
0); /* in case the Activated callback tries to treat some messages */
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7)) if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7))
IFCALL(context->Activated, context); IFCALL(context->Activated, context);

View File

@ -39,6 +39,7 @@ struct _rdpsnd_server_private
HANDLE StopEvent; HANDLE StopEvent;
HANDLE channelEvent; HANDLE channelEvent;
void* ChannelHandle; void* ChannelHandle;
DWORD SessionId;
BOOL waitingHeader; BOOL waitingHeader;
DWORD expectedBytes; DWORD expectedBytes;

View File

@ -50,9 +50,21 @@
#include <freerdp/server/remdesk.h> #include <freerdp/server/remdesk.h>
#include <freerdp/server/encomsp.h> #include <freerdp/server/encomsp.h>
#include <freerdp/server/rail.h> #include <freerdp/server/rail.h>
#include <freerdp/server/telemetry.h>
#include <freerdp/server/rdpgfx.h> #include <freerdp/server/rdpgfx.h>
#include <freerdp/server/disp.h> #include <freerdp/server/disp.h>
#if defined(CHANNEL_RDPECAM_SERVER)
#include <freerdp/server/rdpecam-enumerator.h>
#include <freerdp/server/rdpecam.h>
#endif
#if defined(CHANNEL_AINPUT_SERVER)
#include <freerdp/server/ainput.h>
#endif
extern void freerdp_channels_dummy(void);
void freerdp_channels_dummy(void) void freerdp_channels_dummy(void)
{ {
audin_server_context* audin; audin_server_context* audin;
@ -65,8 +77,13 @@ void freerdp_channels_dummy(void)
RemdeskServerContext* remdesk; RemdeskServerContext* remdesk;
EncomspServerContext* encomsp; EncomspServerContext* encomsp;
RailServerContext* rail; RailServerContext* rail;
TelemetryServerContext* telemetry;
RdpgfxServerContext* rdpgfx; RdpgfxServerContext* rdpgfx;
DispServerContext* disp; DispServerContext* disp;
#if defined (CHANNEL_RDPECAM_SERVER)
CamDevEnumServerContext* camera_enumerator;
CameraDeviceServerContext* camera_device;
#endif
audin = audin_server_context_new(NULL); audin = audin_server_context_new(NULL);
audin_server_context_free(audin); audin_server_context_free(audin);
rdpsnd = rdpsnd_server_context_new(NULL); rdpsnd = rdpsnd_server_context_new(NULL);
@ -87,10 +104,26 @@ void freerdp_channels_dummy(void)
encomsp_server_context_free(encomsp); encomsp_server_context_free(encomsp);
rail = rail_server_context_new(NULL); rail = rail_server_context_new(NULL);
rail_server_context_free(rail); rail_server_context_free(rail);
telemetry = telemetry_server_context_new(NULL);
telemetry_server_context_free(telemetry);
rdpgfx = rdpgfx_server_context_new(NULL); rdpgfx = rdpgfx_server_context_new(NULL);
rdpgfx_server_context_free(rdpgfx); rdpgfx_server_context_free(rdpgfx);
disp = disp_server_context_new(NULL); disp = disp_server_context_new(NULL);
disp_server_context_free(disp); disp_server_context_free(disp);
#if defined (CHANNEL_RDPECAM_SERVER)
camera_enumerator = cam_dev_enum_server_context_new(NULL);
cam_dev_enum_server_context_free(camera_enumerator);
camera_device = camera_device_server_context_new(NULL);
camera_device_server_context_free(camera_device);
#endif
#if defined(CHANNEL_AINPUT_SERVER)
{
ainput_server_context* ainput = ainput_server_context_new(NULL);
ainput_server_context_free(ainput);
}
#endif
} }
/** /**

View File

@ -180,6 +180,7 @@ void smartcard_context_free(void* pCtx)
/* cancel blocking calls like SCardGetStatusChange */ /* cancel blocking calls like SCardGetStatusChange */
SCardCancel(pContext->hContext); SCardCancel(pContext->hContext);
SCardReleaseContext(pContext->hContext);
if (MessageQueue_PostQuit(pContext->IrpQueue, 0) && if (MessageQueue_PostQuit(pContext->IrpQueue, 0) &&
(WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED)) (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED))
@ -237,7 +238,7 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
/* Put thread to sleep so that PC/SC can process the cancel requests. This fixes a race /* Put thread to sleep so that PC/SC can process the cancel requests. This fixes a race
* condition that sometimes caused the pc/sc daemon to crash on MacOS (_xpc_api_misuse) */ * condition that sometimes caused the pc/sc daemon to crash on MacOS (_xpc_api_misuse) */
Sleep(100); SleepEx(100, FALSE);
/** /**
* Call SCardReleaseContext on remaining contexts and remove them from rgSCardContextList. * Call SCardReleaseContext on remaining contexts and remove them from rgSCardContextList.
@ -251,27 +252,7 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
for (index = 0; index < keyCount; index++) for (index = 0; index < keyCount; index++)
{ {
pContext = (SMARTCARD_CONTEXT*)ListDictionary_Remove(smartcard->rgSCardContextList, ListDictionary_SetItemValue(smartcard->rgSCardContextList, (void*)pKeys[index], NULL);
(void*)pKeys[index]);
if (!pContext)
continue;
hContext = pContext->hContext;
if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS)
{
SCardReleaseContext(hContext);
if (MessageQueue_PostQuit(pContext->IrpQueue, 0) &&
(WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED))
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!",
GetLastError());
CloseHandle(pContext->thread);
MessageQueue_Free(pContext->IrpQueue);
free(pContext);
}
} }
free(pKeys); free(pKeys);

View File

@ -349,10 +349,16 @@ static char* smartcard_convert_string_list(const void* in, size_t bytes, BOOL un
if (bytes < 1) if (bytes < 1)
return NULL; return NULL;
if (in == NULL)
return NULL;
if (unicode) if (unicode)
{ {
length = (bytes / 2); length = (bytes / sizeof(WCHAR)) - 1;
if (ConvertFromUnicode(CP_UTF8, 0, string.wz, (int)length, &mszA, 0, NULL, NULL) != mszA = (char*)calloc(length + 1, sizeof(WCHAR));
if (!mszA)
return NULL;
if (ConvertFromUnicode(CP_UTF8, 0, string.wz, (int)length, &mszA, length + 1, NULL, NULL) !=
(int)length) (int)length)
{ {
free(mszA); free(mszA);
@ -362,10 +368,11 @@ static char* smartcard_convert_string_list(const void* in, size_t bytes, BOOL un
else else
{ {
length = bytes; length = bytes;
mszA = (char*)malloc(length); mszA = (char*)calloc(length, sizeof(char));
if (!mszA) if (!mszA)
return NULL; return NULL;
CopyMemory(mszA, string.sz, length); CopyMemory(mszA, string.sz, length - 1);
mszA[length - 1] = '\0';
} }
for (index = 0; index < length - 1; index++) for (index = 0; index < length - 1; index++)
@ -1307,21 +1314,24 @@ static void smartcard_trace_status_return(SMARTCARD_DEVICE* smartcard, const Sta
static void smartcard_trace_state_return(SMARTCARD_DEVICE* smartcard, const State_Return* ret) static void smartcard_trace_state_return(SMARTCARD_DEVICE* smartcard, const State_Return* ret)
{ {
char buffer[1024]; char buffer[1024];
char* state;
WINPR_UNUSED(smartcard); WINPR_UNUSED(smartcard);
if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
return; return;
state = SCardGetReaderStateString(ret->dwState);
WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {"); WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {");
WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ")", ret->dwState); WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ")", state, ret->dwState);
WLog_LVL(TAG, g_LogLevel, " dwProtocol: %s (0x%08" PRIX32 ")", ret->dwProtocol); WLog_LVL(TAG, g_LogLevel, " dwProtocol: %s (0x%08" PRIX32 ")",
WLog_LVL(TAG, g_LogLevel, " cbAtrLen: %s (0x%08" PRIX32 ")", ret->cbAtrLen); SCardGetProtocolString(ret->dwProtocol), ret->dwProtocol);
WLog_LVL(TAG, g_LogLevel, " cbAtrLen: (0x%08" PRIX32 ")", ret->cbAtrLen);
WLog_LVL(TAG, g_LogLevel, " rgAtr: %s", WLog_LVL(TAG, g_LogLevel, " rgAtr: %s",
smartcard_array_dump(ret->rgAtr, sizeof(ret->rgAtr), buffer, sizeof(buffer))); smartcard_array_dump(ret->rgAtr, sizeof(ret->rgAtr), buffer, sizeof(buffer)));
WLog_LVL(TAG, g_LogLevel, "}"); WLog_LVL(TAG, g_LogLevel, "}");
free(state);
} }
static void smartcard_trace_reconnect_return(SMARTCARD_DEVICE* smartcard, static void smartcard_trace_reconnect_return(SMARTCARD_DEVICE* smartcard,

View File

@ -0,0 +1,22 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel("telemetry")
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,12 @@
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "telemetry" TYPE "dynamic"
DESCRIPTION "Telemetry Virtual Channel Extension"
SPECIFICATIONS "[MS-RDPET]"
DEFAULT ${OPTION_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,26 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("telemetry")
set(${MODULE_PREFIX}_SRCS
telemetry_main.c)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,443 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Telemetry Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/channels/log.h>
#include <freerdp/server/telemetry.h>
#define TAG CHANNELS_TAG("telemetry.server")
typedef enum
{
TELEMETRY_INITIAL,
TELEMETRY_OPENED,
} eTelemetryChannelState;
typedef struct
{
TelemetryServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* telemetry_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eTelemetryChannelState state;
wStream* buffer;
} telemetry_server;
static UINT telemetry_server_initialize(TelemetryServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (telemetry->isOpened)
{
WLog_WARN(TAG, "Application error: TELEMETRY channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
telemetry->externalThread = externalThread;
return error;
}
static UINT telemetry_server_open_channel(telemetry_server* telemetry)
{
TelemetryServerContext* context = &telemetry->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(telemetry);
if (WTSQuerySessionInformationA(telemetry->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
telemetry->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(telemetry->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
telemetry->telemetry_channel = WTSVirtualChannelOpenEx(
telemetry->SessionId, TELEMETRY_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!telemetry->telemetry_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(telemetry->telemetry_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT telemetry_server_recv_rdp_telemetry_pdu(TelemetryServerContext* context, wStream* s)
{
TELEMETRY_RDP_TELEMETRY_PDU pdu;
UINT error = CHANNEL_RC_OK;
if (Stream_GetRemainingLength(s) < 16)
{
WLog_ERR(TAG, "telemetry_server_recv_rdp_telemetry_pdu: Not enough data!");
return ERROR_NO_DATA;
}
Stream_Read_UINT32(s, pdu.PromptForCredentialsMillis);
Stream_Read_UINT32(s, pdu.PromptForCredentialsDoneMillis);
Stream_Read_UINT32(s, pdu.GraphicsChannelOpenedMillis);
Stream_Read_UINT32(s, pdu.FirstGraphicsReceivedMillis);
IFCALLRET(context->RdpTelemetry, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->RdpTelemetry failed with error %" PRIu32 "", error);
return error;
}
static UINT telemetry_process_message(telemetry_server* telemetry)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
BYTE MessageId;
BYTE Length;
wStream* s;
WINPR_ASSERT(telemetry);
WINPR_ASSERT(telemetry->telemetry_channel);
s = telemetry->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(telemetry->telemetry_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(telemetry->telemetry_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, MessageId);
Stream_Read_UINT8(s, Length);
switch (MessageId)
{
case 0x01:
error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s);
break;
default:
WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT telemetry_server_context_poll_int(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(telemetry);
switch (telemetry->state)
{
case TELEMETRY_INITIAL:
error = telemetry_server_open_channel(telemetry);
if (error)
WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!",
error);
else
telemetry->state = TELEMETRY_OPENED;
break;
case TELEMETRY_OPENED:
error = telemetry_process_message(telemetry);
break;
}
return error;
}
static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(telemetry);
if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI telemetry_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
telemetry_server* telemetry = (telemetry_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(telemetry);
nCount = 0;
events[nCount++] = telemetry->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (telemetry->state)
{
case TELEMETRY_INITIAL:
error = telemetry_server_context_poll_int(&telemetry->context);
if (error == CHANNEL_RC_OK)
{
events[1] = telemetry_server_get_channel_handle(telemetry);
nCount = 2;
}
break;
case TELEMETRY_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = telemetry_server_context_poll_int(&telemetry->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(telemetry->telemetry_channel);
telemetry->telemetry_channel = NULL;
if (error && telemetry->context.rdpcontext)
setChannelError(telemetry->context.rdpcontext, error,
"telemetry_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT telemetry_server_open(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (!telemetry->externalThread && (telemetry->thread == NULL))
{
telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!telemetry->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL);
if (!telemetry->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(telemetry->stopEvent);
telemetry->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
telemetry->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT telemetry_server_close(TelemetryServerContext* context)
{
UINT error = CHANNEL_RC_OK;
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (!telemetry->externalThread && telemetry->thread)
{
SetEvent(telemetry->stopEvent);
if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(telemetry->thread);
CloseHandle(telemetry->stopEvent);
telemetry->thread = NULL;
telemetry->stopEvent = NULL;
}
if (telemetry->externalThread)
{
if (telemetry->state != TELEMETRY_INITIAL)
{
WTSVirtualChannelClose(telemetry->telemetry_channel);
telemetry->telemetry_channel = NULL;
telemetry->state = TELEMETRY_INITIAL;
}
}
telemetry->isOpened = FALSE;
return error;
}
static UINT telemetry_server_context_poll(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (!telemetry->externalThread)
return ERROR_INTERNAL_ERROR;
return telemetry_server_context_poll_int(context);
}
static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle)
{
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
WINPR_ASSERT(handle);
if (!telemetry->externalThread)
return FALSE;
if (telemetry->state == TELEMETRY_INITIAL)
return FALSE;
*handle = telemetry_server_get_channel_handle(telemetry);
return TRUE;
}
TelemetryServerContext* telemetry_server_context_new(HANDLE vcm)
{
telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server));
if (!telemetry)
return NULL;
telemetry->context.vcm = vcm;
telemetry->context.Initialize = telemetry_server_initialize;
telemetry->context.Open = telemetry_server_open;
telemetry->context.Close = telemetry_server_close;
telemetry->context.Poll = telemetry_server_context_poll;
telemetry->context.ChannelHandle = telemetry_server_context_handle;
telemetry->buffer = Stream_New(NULL, 4096);
if (!telemetry->buffer)
goto fail;
return &telemetry->context;
fail:
telemetry_server_context_free(&telemetry->context);
return NULL;
}
void telemetry_server_context_free(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
if (telemetry)
{
telemetry_server_close(context);
Stream_Free(telemetry->buffer, TRUE);
}
free(telemetry);
}

View File

@ -195,9 +195,12 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP
if (media_type->SubType == TSMF_SUB_TYPE_AVC1 && if (media_type->SubType == TSMF_SUB_TYPE_AVC1 &&
media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO) media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO)
{ {
size_t required = 6;
/* The extradata format that FFmpeg uses is following CodecPrivate in Matroska. /* The extradata format that FFmpeg uses is following CodecPrivate in Matroska.
See http://haali.su/mkv/codecs.pdf */ See http://haali.su/mkv/codecs.pdf */
p = mdecoder->codec_context->extradata; p = mdecoder->codec_context->extradata;
if (mdecoder->codec_context->extradata_size < required)
return FALSE;
*p++ = 1; /* Reserved? */ *p++ = 1; /* Reserved? */
*p++ = media_type->ExtraData[8]; /* Profile */ *p++ = media_type->ExtraData[8]; /* Profile */
*p++ = 0; /* Profile */ *p++ = 0; /* Profile */
@ -206,23 +209,36 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP
*p++ = 0xe0 | 0x01; /* Reserved | #sps */ *p++ = 0xe0 | 0x01; /* Reserved | #sps */
s = media_type->ExtraData + 20; s = media_type->ExtraData + 20;
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1))); size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
required += size + 2;
if (mdecoder->codec_context->extradata_size < required)
return FALSE;
memcpy(p, s, size + 2); memcpy(p, s, size + 2);
s += size + 2; s += size + 2;
p += size + 2; p += size + 2;
required++;
if (mdecoder->codec_context->extradata_size < required)
return FALSE;
*p++ = 1; /* #pps */ *p++ = 1; /* #pps */
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1))); size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
required += size + 2;
if (mdecoder->codec_context->extradata_size < required)
return FALSE;
memcpy(p, s, size + 2); memcpy(p, s, size + 2);
} }
else else
{ {
memcpy(mdecoder->codec_context->extradata, media_type->ExtraData, memcpy(mdecoder->codec_context->extradata, media_type->ExtraData,
media_type->ExtraDataSize); media_type->ExtraDataSize);
if (mdecoder->codec_context->extradata_size < media_type->ExtraDataSize + 8)
return FALSE;
memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8); memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8);
} }
} }
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100)
if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED) if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED)
mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED; mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED;
#endif
return TRUE; return TRUE;
} }
@ -245,6 +261,9 @@ static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* medi
{ {
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder; TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
WINPR_ASSERT(mdecoder);
WINPR_ASSERT(media_type);
switch (media_type->MajorType) switch (media_type->MajorType)
{ {
case TSMF_MAJOR_TYPE_VIDEO: case TSMF_MAJOR_TYPE_VIDEO:
@ -297,6 +316,9 @@ static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* medi
http://msdn.microsoft.com/en-us/library/dd757806.aspx */ http://msdn.microsoft.com/en-us/library/dd757806.aspx */
if (media_type->ExtraData) if (media_type->ExtraData)
{ {
if (media_type->ExtraDataSize < 12)
return FALSE;
media_type->ExtraData += 12; media_type->ExtraData += 12;
media_type->ExtraDataSize -= 12; media_type->ExtraDataSize -= 12;
} }
@ -615,7 +637,9 @@ static void tsmf_ffmpeg_free(ITSMFDecoder* decoder)
static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT; static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT;
static BOOL CALLBACK InitializeAvCodecs(PINIT_ONCE once, PVOID param, PVOID* context) static BOOL CALLBACK InitializeAvCodecs(PINIT_ONCE once, PVOID param, PVOID* context)
{ {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
avcodec_register_all(); avcodec_register_all();
#endif
return TRUE; return TRUE;
} }

View File

@ -420,6 +420,8 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
http://msdn.microsoft.com/en-us/library/dd757806.aspx */ http://msdn.microsoft.com/en-us/library/dd757806.aspx */
if (media_type->ExtraData) if (media_type->ExtraData)
{ {
if (media_type->ExtraDataSize < 12)
return FALSE;
media_type->ExtraData += 12; media_type->ExtraData += 12;
media_type->ExtraDataSize -= 12; media_type->ExtraDataSize -= 12;
} }

View File

@ -386,7 +386,12 @@ static BOOL tsmf_read_format_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s, UINT3
if (cbFormat > 176) if (cbFormat > 176)
{ {
mediatype->ExtraDataSize = cbFormat - 176; const size_t nsize = cbFormat - 176;
if (mediatype->ExtraDataSize < nsize)
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, nsize))
return FALSE;
mediatype->ExtraDataSize = nsize;
mediatype->ExtraData = Stream_Pointer(s); mediatype->ExtraData = Stream_Pointer(s);
} }
break; break;

View File

@ -69,16 +69,20 @@ UINT tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman)
*/ */
UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman) UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman)
{ {
UINT32 i; UINT32 i = 0;
UINT32 v; UINT32 v = 0;
UINT32 pos; UINT32 pos = 0;
UINT32 CapabilityType; UINT32 CapabilityType = 0;
UINT32 cbCapabilityLength; UINT32 cbCapabilityLength = 0;
UINT32 numHostCapabilities; UINT32 numHostCapabilities = 0;
WINPR_ASSERT(ifman);
if (!Stream_EnsureRemainingCapacity(ifman->output, ifman->input_size + 4)) if (!Stream_EnsureRemainingCapacity(ifman->output, ifman->input_size + 4))
return ERROR_OUTOFMEMORY; return ERROR_OUTOFMEMORY;
if (Stream_GetRemainingLength(ifman->input) < ifman->input_size)
return ERROR_INVALID_DATA;
pos = Stream_GetPosition(ifman->output); pos = Stream_GetPosition(ifman->output);
Stream_Copy(ifman->input, ifman->output, ifman->input_size); Stream_Copy(ifman->input, ifman->output, ifman->input_size);
Stream_SetPosition(ifman->output, pos); Stream_SetPosition(ifman->output, pos);

View File

@ -97,7 +97,13 @@ static wStream* urb_create_iocompletion(UINT32 InterfaceField, UINT32 MessageId,
UINT32 OutputBufferSize) UINT32 OutputBufferSize)
{ {
const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF); const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF);
wStream* out = Stream_New(NULL, OutputBufferSize + 28);
#if UINT32_MAX >= SIZE_MAX
if (OutputBufferSize > UINT32_MAX - 28ull)
return NULL;
#endif
wStream* out = Stream_New(NULL, OutputBufferSize + 28ull);
if (!out) if (!out)
return NULL; return NULL;
@ -241,6 +247,10 @@ static UINT urbdrc_process_io_control(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* c
Stream_Read_UINT32(s, OutputBufferSize); Stream_Read_UINT32(s, OutputBufferSize);
Stream_Read_UINT32(s, RequestId); Stream_Read_UINT32(s, RequestId);
if (OutputBufferSize > UINT32_MAX - 4)
return ERROR_INVALID_DATA;
InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));
out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4); out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4);
@ -673,7 +683,11 @@ static UINT urb_control_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba
buffer = Stream_Pointer(out); buffer = Stream_Pointer(out);
if (transferDir == USBD_TRANSFER_DIRECTION_OUT) if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
return ERROR_INVALID_DATA;
Stream_Copy(s, out, OutputBufferSize); Stream_Copy(s, out, OutputBufferSize);
}
/** process TS_URB_CONTROL_TRANSFER */ /** process TS_URB_CONTROL_TRANSFER */
if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType, if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType,
@ -720,10 +734,20 @@ static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA
Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */ Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */
Stream_Read_UINT32(s, OutputBufferSize); Stream_Read_UINT32(s, OutputBufferSize);
EndpointAddress = (PipeHandle & 0x000000ff); EndpointAddress = (PipeHandle & 0x000000ff);
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
{
return ERROR_INVALID_DATA;
}
}
/** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */ /** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */
return pdev->bulk_or_interrupt_transfer(pdev, callback, MessageId, RequestId, EndpointAddress, return pdev->bulk_or_interrupt_transfer(
TransferFlags, noAck, OutputBufferSize, pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, noAck,
urb_bulk_transfer_cb, 10000); OutputBufferSize, (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL,
urb_bulk_transfer_cb, 10000);
} }
static void urb_isoch_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out, static void urb_isoch_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out,
@ -803,6 +827,13 @@ static UINT urb_isoch_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback
packetDescriptorData = Stream_Pointer(s); packetDescriptorData = Stream_Pointer(s);
Stream_Seek(s, NumberOfPackets * 12); Stream_Seek(s, NumberOfPackets * 12);
Stream_Read_UINT32(s, OutputBufferSize); Stream_Read_UINT32(s, OutputBufferSize);
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
return ERROR_INVALID_DATA;
}
return pdev->isoch_transfer( return pdev->isoch_transfer(
pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame, pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame,
ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize, ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize,
@ -1748,6 +1779,13 @@ static UINT urbdrc_process_transfer_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLB
break; break;
} }
if (error)
{
WLog_Print(urbdrc->log, WLOG_WARN,
"USB transfer request URB Function %08" PRIx32 " failed with %08" PRIx32,
URB_Function, error);
}
return error; return error;
} }

View File

@ -193,8 +193,8 @@ const char* usb_interface_class_to_string(uint8_t class)
static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UINT32 MessageId, static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UINT32 MessageId,
size_t offset, size_t BufferSize, size_t offset, size_t BufferSize,
size_t packetSize, BOOL NoAck, const BYTE* data, size_t packetSize,
t_isoch_transfer_cb cb, BOOL NoAck, t_isoch_transfer_cb cb,
URBDRC_CHANNEL_CALLBACK* callback) URBDRC_CHANNEL_CALLBACK* callback)
{ {
ASYNC_TRANSFER_USER_DATA* user_data = calloc(1, sizeof(ASYNC_TRANSFER_USER_DATA)); ASYNC_TRANSFER_USER_DATA* user_data = calloc(1, sizeof(ASYNC_TRANSFER_USER_DATA));
@ -210,14 +210,19 @@ static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UI
free(user_data); free(user_data);
return NULL; return NULL;
} }
Stream_Seek(user_data->data, offset); /* Skip header offset */ Stream_Seek(user_data->data, offset); /* Skip header offset */
if (data)
memcpy(Stream_Pointer(user_data->data), data, BufferSize);
else
user_data->OutputBufferSize = BufferSize;
user_data->noack = NoAck; user_data->noack = NoAck;
user_data->cb = cb; user_data->cb = cb;
user_data->callback = callback; user_data->callback = callback;
user_data->idev = idev; user_data->idev = idev;
user_data->MessageId = MessageId; user_data->MessageId = MessageId;
user_data->OutputBufferSize = BufferSize;
user_data->queue = pdev->request_queue; user_data->queue = pdev->request_queue;
return user_data; return user_data;
@ -232,7 +237,7 @@ static void async_transfer_user_data_free(ASYNC_TRANSFER_USER_DATA* user_data)
} }
} }
static void func_iso_callback(struct libusb_transfer* transfer) static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer)
{ {
ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data;
const UINT32 streamID = stream_id_from_buffer(transfer); const UINT32 streamID = stream_id_from_buffer(transfer);
@ -331,7 +336,7 @@ static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPT
return NULL; return NULL;
} }
static void func_bulk_transfer_cb(struct libusb_transfer* transfer) static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer)
{ {
ASYNC_TRANSFER_USER_DATA* user_data; ASYNC_TRANSFER_USER_DATA* user_data;
uint32_t streamID; uint32_t streamID;
@ -485,17 +490,19 @@ static LIBUSB_DEVICE* udev_get_libusb_dev(libusb_context* context, uint8_t bus_n
for (i = 0; i < total_device; i++) for (i = 0; i < total_device; i++)
{ {
uint8_t cbus = libusb_get_bus_number(libusb_list[i]); LIBUSB_DEVICE* dev = libusb_list[i];
uint8_t caddr = libusb_get_device_address(libusb_list[i]); if ((bus_number == libusb_get_bus_number(dev)) &&
(dev_number == libusb_get_device_address(dev)))
if ((bus_number == cbus) && (dev_number == caddr))
{ {
device = libusb_list[i]; device = dev;
break; }
else
{
libusb_unref_device(dev);
} }
} }
libusb_free_device_list(libusb_list, 1); libusb_free_device_list(libusb_list, 0);
return device; return device;
} }
@ -517,7 +524,6 @@ static LIBUSB_DEVICE_DESCRIPTOR* udev_new_descript(URBDRC_PLUGIN* urbdrc, LIBUSB
return descriptor; return descriptor;
} }
static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting) static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting)
{ {
int error = 0, diff = 0; int error = 0, diff = 0;
@ -1007,6 +1013,9 @@ static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev)
if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc) if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc)
return FALSE; return FALSE;
#ifdef _WIN32
return TRUE;
#else
urbdrc = pdev->urbdrc; urbdrc = pdev->urbdrc;
if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0) if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0)
@ -1027,6 +1036,7 @@ static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev)
} }
return TRUE; return TRUE;
#endif
} }
static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev) static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev)
@ -1043,12 +1053,14 @@ static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev)
log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_release_interface", err); log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_release_interface", err);
#ifndef _WIN32
if (err != LIBUSB_ERROR_NO_DEVICE) if (err != LIBUSB_ERROR_NO_DEVICE)
{ {
err = libusb_attach_kernel_driver(pdev->libusb_handle, i); err = libusb_attach_kernel_driver(pdev->libusb_handle, i);
log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_attach_kernel_driver if=%d", log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_attach_kernel_driver if=%d",
err, i); err, i);
} }
#endif
} }
return TRUE; return TRUE;
@ -1197,8 +1209,8 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c
return -1; return -1;
urbdrc = pdev->urbdrc; urbdrc = pdev->urbdrc;
user_data = async_transfer_user_data_new(idev, MessageId, 48, BufferSize, outSize + 1024, NoAck, user_data = async_transfer_user_data_new(idev, MessageId, 48, BufferSize, Buffer,
cb, callback); outSize + 1024, NoAck, cb, callback);
if (!user_data) if (!user_data)
return -1; return -1;
@ -1206,20 +1218,21 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c
user_data->ErrorCount = ErrorCount; user_data->ErrorCount = ErrorCount;
user_data->StartFrame = StartFrame; user_data->StartFrame = StartFrame;
if (Buffer) /* We read data, prepare a bufffer */ if (!Buffer)
{
user_data->OutputBufferSize = 0;
memmove(Stream_Pointer(user_data->data), Buffer, BufferSize);
}
else
Stream_Seek(user_data->data, (NumberOfPackets * 12)); Stream_Seek(user_data->data, (NumberOfPackets * 12));
iso_packet_size = BufferSize / NumberOfPackets; if (NumberOfPackets > 0)
iso_transfer = libusb_alloc_transfer(NumberOfPackets); {
iso_packet_size = BufferSize / NumberOfPackets;
iso_transfer = libusb_alloc_transfer((int)NumberOfPackets);
}
if (iso_transfer == NULL) if (iso_transfer == NULL)
{ {
WLog_Print(urbdrc->log, WLOG_ERROR, "Error: libusb_alloc_transfer."); WLog_Print(urbdrc->log, WLOG_ERROR,
"Error: libusb_alloc_transfer [NumberOfPackets=%" PRIu32 ", BufferSize=%" PRIu32
" ]",
NumberOfPackets, BufferSize);
async_transfer_user_data_free(user_data); async_transfer_user_data_free(user_data);
return -1; return -1;
} }
@ -1270,7 +1283,7 @@ static BOOL libusb_udev_control_transfer(IUDEVICE* idev, UINT32 RequestId, UINT3
static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback, static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback,
UINT32 MessageId, UINT32 RequestId, UINT32 MessageId, UINT32 RequestId,
UINT32 EndpointAddress, UINT32 TransferFlags, UINT32 EndpointAddress, UINT32 TransferFlags,
BOOL NoAck, UINT32 BufferSize, BOOL NoAck, UINT32 BufferSize, const BYTE* data,
t_isoch_transfer_cb cb, UINT32 Timeout) t_isoch_transfer_cb cb, UINT32 Timeout)
{ {
UINT32 transfer_type; UINT32 transfer_type;
@ -1286,7 +1299,7 @@ static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL
urbdrc = pdev->urbdrc; urbdrc = pdev->urbdrc;
user_data = user_data =
async_transfer_user_data_new(idev, MessageId, 36, BufferSize, 0, NoAck, cb, callback); async_transfer_user_data_new(idev, MessageId, 36, BufferSize, data, 0, NoAck, cb, callback);
if (!user_data) if (!user_data)
return -1; return -1;
@ -1474,6 +1487,7 @@ static void udev_free(IUDEVICE* idev)
ArrayList_Free(udev->request_queue); ArrayList_Free(udev->request_queue);
/* free the config descriptor that send from windows */ /* free the config descriptor that send from windows */
msusb_msconfig_free(udev->MsConfig); msusb_msconfig_free(udev->MsConfig);
libusb_unref_device(udev->libusb_dev);
libusb_close(udev->libusb_handle); libusb_close(udev->libusb_handle);
libusb_close(udev->hub_handle); libusb_close(udev->hub_handle);
free(udev->devDescriptor); free(udev->devDescriptor);
@ -1522,8 +1536,8 @@ static void udev_load_interface(UDEVICE* pdev)
pdev->iface.free = udev_free; pdev->iface.free = udev_free;
} }
static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev, static int udev_get_device_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev,
UINT16 bus_number, UINT16 dev_number) UINT16 bus_number, UINT16 dev_number)
{ {
int error; int error;
ssize_t i, total_device; ssize_t i, total_device;
@ -1535,21 +1549,19 @@ static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVI
for (i = 0; i < total_device; i++) for (i = 0; i < total_device; i++)
{ {
LIBUSB_DEVICE_HANDLE* handle; LIBUSB_DEVICE* dev = libusb_list[i];
uint8_t cbus = libusb_get_bus_number(libusb_list[i]);
uint8_t caddr = libusb_get_device_address(libusb_list[i]);
if ((bus_number != cbus) || (dev_number != caddr)) if ((bus_number != libusb_get_bus_number(dev)) ||
(dev_number != libusb_get_device_address(dev)))
continue; continue;
error = libusb_open(libusb_list[i], &handle); error = libusb_open(dev, &pdev->libusb_handle);
if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error))
break; break;
/* get port number */ /* get port number */
error = libusb_get_port_numbers(libusb_list[i], port_numbers, sizeof(port_numbers)); error = libusb_get_port_numbers(dev, port_numbers, sizeof(port_numbers));
libusb_close(handle);
if (error < 1) if (error < 1)
{ {
@ -1567,29 +1579,40 @@ static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVI
WLog_Print(urbdrc->log, WLOG_DEBUG, " DevPath: %s", pdev->path); WLog_Print(urbdrc->log, WLOG_DEBUG, " DevPath: %s", pdev->path);
break; break;
} }
libusb_free_device_list(libusb_list, 1);
if (error < 0)
return -1;
return 0;
}
static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev,
UINT16 bus_number, UINT16 dev_number)
{
int error;
ssize_t i, total_device;
LIBUSB_DEVICE** libusb_list;
LIBUSB_DEVICE_HANDLE* handle;
total_device = libusb_get_device_list(ctx, &libusb_list);
/* Look for device hub. */ /* Look for device hub. */
if (error == 0) error = -1;
for (i = 0; i < total_device; i++)
{ {
error = -1; LIBUSB_DEVICE* dev = libusb_list[i];
for (i = 0; i < total_device; i++) if ((bus_number != libusb_get_bus_number(dev)) ||
{ (1 != libusb_get_device_address(dev))) /* Root hub allways first on bus. */
LIBUSB_DEVICE_HANDLE* handle; continue;
uint8_t cbus = libusb_get_bus_number(libusb_list[i]);
uint8_t caddr = libusb_get_device_address(libusb_list[i]);
if ((bus_number != cbus) || (1 != caddr)) /* Root hub allways first on bus. */ WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number);
continue; error = libusb_open(dev, &handle);
WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number); if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error))
error = libusb_open(libusb_list[i], &handle); pdev->hub_handle = handle;
if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) break;
pdev->hub_handle = handle;
break;
}
} }
libusb_free_device_list(libusb_list, 1); libusb_free_device_list(libusb_list, 1);
@ -1640,30 +1663,26 @@ static IUDEVICE* udev_init(URBDRC_PLUGIN* urbdrc, libusb_context* context, LIBUS
if (urbdrc->listener_callback) if (urbdrc->listener_callback)
udev_set_channelManager(&pdev->iface, urbdrc->listener_callback->channel_mgr); udev_set_channelManager(&pdev->iface, urbdrc->listener_callback->channel_mgr);
/* Get DEVICE handle */
status = udev_get_device_handle(urbdrc, context, pdev, bus_number, dev_number);
if (status != LIBUSB_SUCCESS)
{
struct libusb_device_descriptor desc;
const uint8_t port = libusb_get_port_number(pdev->libusb_dev);
libusb_get_device_descriptor(pdev->libusb_dev, &desc);
log_libusb_result(urbdrc->log, WLOG_ERROR,
"libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]", status,
bus_number, port, dev_number, desc.idVendor, desc.idProduct);
goto fail;
}
/* Get HUB handle */ /* Get HUB handle */
status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number); status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number);
if (status < 0) if (status < 0)
pdev->hub_handle = NULL; pdev->hub_handle = NULL;
{
struct libusb_device_descriptor desc;
const uint8_t bus = libusb_get_bus_number(pdev->libusb_dev);
const uint8_t port = libusb_get_port_number(pdev->libusb_dev);
const uint8_t addr = libusb_get_device_address(pdev->libusb_dev);
libusb_get_device_descriptor(pdev->libusb_dev, &desc);
status = libusb_open(pdev->libusb_dev, &pdev->libusb_handle);
if (status != LIBUSB_SUCCESS)
{
log_libusb_result(urbdrc->log, WLOG_ERROR,
"libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]",
status, bus, port, addr, desc.idVendor, desc.idProduct);
goto fail;
}
}
pdev->devDescriptor = udev_new_descript(urbdrc, pdev->libusb_dev); pdev->devDescriptor = udev_new_descript(urbdrc, pdev->libusb_dev);
if (!pdev->devDescriptor) if (!pdev->devDescriptor)
@ -1734,8 +1753,6 @@ size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendo
{ {
LIBUSB_DEVICE** libusb_list; LIBUSB_DEVICE** libusb_list;
UDEVICE** array; UDEVICE** array;
UINT16 bus_number;
UINT16 dev_number;
ssize_t i, total_device; ssize_t i, total_device;
size_t num = 0; size_t num = 0;
@ -1752,23 +1769,27 @@ size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendo
for (i = 0; i < total_device; i++) for (i = 0; i < total_device; i++)
{ {
LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, libusb_list[i]); LIBUSB_DEVICE* dev = libusb_list[i];
LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, dev);
if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct)) if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct))
{ {
bus_number = libusb_get_bus_number(libusb_list[i]); array[num] = (PUDEVICE)udev_init(urbdrc, ctx, dev, libusb_get_bus_number(dev),
dev_number = libusb_get_device_address(libusb_list[i]); libusb_get_device_address(dev));
array[num] = (PUDEVICE)udev_init(urbdrc, ctx, libusb_list[i], bus_number, dev_number);
if (array[num] != NULL) if (array[num] != NULL)
num++; num++;
} }
else
{
libusb_unref_device(dev);
}
free(descriptor); free(descriptor);
} }
fail: fail:
libusb_free_device_list(libusb_list, 1); libusb_free_device_list(libusb_list, 0);
*devArray = (IUDEVICE**)array; *devArray = (IUDEVICE**)array;
return num; return num;
} }

View File

@ -195,6 +195,12 @@ static size_t udevman_register_udevice(IUDEVMAN* idevman, BYTE bus_number, BYTE
/* register all device that match pid vid */ /* register all device that match pid vid */
num = udev_new_by_id(urbdrc, udevman->context, idVendor, idProduct, &devArray); num = udev_new_by_id(urbdrc, udevman->context, idVendor, idProduct, &devArray);
if (num == 0)
{
WLog_Print(urbdrc->log, WLOG_WARN,
"Could not find or redirect any usb devices by id %04x:%04x", idVendor, idProduct);
}
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
{ {
UINT32 id; UINT32 id;
@ -505,12 +511,7 @@ static BOOL filter_by_class(uint8_t bDeviceClass, uint8_t bDeviceSubClass)
static BOOL append(char* dst, size_t length, const char* src) static BOOL append(char* dst, size_t length, const char* src)
{ {
size_t slen = strlen(src); return winpr_str_append(src, dst, length, NULL);
size_t dlen = strnlen(dst, length);
if (dlen + slen >= length)
return FALSE;
strcat(dst, src);
return TRUE;
} }
static BOOL device_is_filtered(struct libusb_device* dev, static BOOL device_is_filtered(struct libusb_device* dev,
@ -536,7 +537,7 @@ static BOOL device_is_filtered(struct libusb_device* dev,
for (x = 0; x < config->bNumInterfaces; x++) for (x = 0; x < config->bNumInterfaces; x++)
{ {
uint8_t y; int y;
const struct libusb_interface* ifc = &config->interface[x]; const struct libusb_interface* ifc = &config->interface[x];
for (y = 0; y < ifc->num_altsetting; y++) for (y = 0; y < ifc->num_altsetting; y++)
{ {
@ -580,8 +581,8 @@ static BOOL device_is_filtered(struct libusb_device* dev,
return filtered; return filtered;
} }
static int hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev, static int LIBUSB_CALL hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev,
libusb_hotplug_event event, void* user_data) libusb_hotplug_event event, void* user_data)
{ {
VID_PID_PAIR pair; VID_PID_PAIR pair;
struct libusb_device_descriptor desc; struct libusb_device_descriptor desc;
@ -834,7 +835,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman)
{ {
int rc = LIBUSB_SUCCESS; int rc = LIBUSB_SUCCESS;
struct timeval tv = { 0, 500 }; struct timeval tv = { 0, 500 };
if (libusb_try_lock_events(udevman->context)) if (libusb_try_lock_events(udevman->context) == 0)
{ {
if (libusb_event_handling_ok(udevman->context)) if (libusb_event_handling_ok(udevman->context))
{ {
@ -859,7 +860,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman)
return rc > 0; return rc > 0;
} }
static DWORD poll_thread(LPVOID lpThreadParameter) static DWORD WINAPI poll_thread(LPVOID lpThreadParameter)
{ {
libusb_hotplug_callback_handle handle; libusb_hotplug_callback_handle handle;
UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter; UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter;
@ -921,7 +922,7 @@ UINT freerdp_urbdrc_client_subsystem_entry(PFREERDP_URBDRC_SERVICE_ENTRY_POINTS
udevman->next_device_id = BASE_USBDEVICE_NUM; udevman->next_device_id = BASE_USBDEVICE_NUM;
udevman->iface.plugin = pEntryPoints->plugin; udevman->iface.plugin = pEntryPoints->plugin;
rc = libusb_init(&udevman->context); rc = libusb_init(&udevman->context);
if (rc != LIBUSB_SUCCESS) if (rc != LIBUSB_SUCCESS)
goto fail; goto fail;

View File

@ -134,7 +134,7 @@ struct _IUDEVICE
int (*bulk_or_interrupt_transfer)(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback, int (*bulk_or_interrupt_transfer)(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback,
UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress, UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress,
UINT32 TransferFlags, BOOL NoAck, UINT32 BufferSize, UINT32 TransferFlags, BOOL NoAck, UINT32 BufferSize,
t_isoch_transfer_cb cb, UINT32 Timeout); const BYTE* data, t_isoch_transfer_cb cb, UINT32 Timeout);
int (*select_configuration)(IUDEVICE* idev, UINT32 bConfigurationValue); int (*select_configuration)(IUDEVICE* idev, UINT32 bConfigurationValue);

View File

@ -25,8 +25,10 @@ include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
if (NOT BUILTIN_CHANNELS)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} rdpgfx-client)
endif()
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})

View File

@ -666,7 +666,7 @@ static void video_timer(VideoClientContext* video, UINT64 now)
presentation = frame->presentation; presentation = frame->presentation;
priv->publishedFrames++; priv->publishedFrames++;
memcpy(presentation->surfaceData, frame->surfaceData, frame->w * frame->h * 4); memcpy(presentation->surfaceData, frame->surfaceData, frame->w * frame->h * 4ULL);
video->showSurface(video, presentation->surface); video->showSurface(video, presentation->surface);
@ -848,7 +848,7 @@ static UINT video_VideoData(VideoClientContext* context, TSMM_VIDEO_DATA* data)
frame->w = presentation->SourceWidth; frame->w = presentation->SourceWidth;
frame->h = presentation->SourceHeight; frame->h = presentation->SourceHeight;
frame->surfaceData = BufferPool_Take(priv->surfacePool, frame->w * frame->h * 4); frame->surfaceData = BufferPool_Take(priv->surfacePool, frame->w * frame->h * 4ULL);
if (!frame->surfaceData) if (!frame->surfaceData)
{ {
WLog_ERR(TAG, "unable to allocate frame data"); WLog_ERR(TAG, "unable to allocate frame data");
@ -930,6 +930,8 @@ static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCall
Stream_Read_UINT16(s, data.PacketsInSample); Stream_Read_UINT16(s, data.PacketsInSample);
Stream_Read_UINT32(s, data.SampleNumber); Stream_Read_UINT32(s, data.SampleNumber);
Stream_Read_UINT32(s, data.cbSample); Stream_Read_UINT32(s, data.cbSample);
if (!Stream_CheckAndLogRequiredLength(TAG, s, data.cbSample))
return ERROR_INVALID_DATA;
data.pSample = Stream_Pointer(s); data.pSample = Stream_Pointer(s);
/* /*

View File

@ -1,7 +0,0 @@
message("PRELOADING android cache")
set(CMAKE_TOOLCHAIN_FILE "$ANDROID_NDK/build/cmake/android.toolchain.cmake" CACHE PATH "ToolChain file")
set(WITH_SANITIZE_ADDRESS ON)
set(FREERDP_EXTERNAL_SSL_PATH $ENV{ANDROID_SSL_PATH} CACHE PATH "android ssl")
# ANDROID_NDK and ANDROID_SDK must be set as environment variable
#set(ANDROID_NDK $ENV{ANDROID_SDK} CACHE PATH "Android NDK")
#set(ANDROID_SDK "${ANDROID_NDK}" CACHE PATH "android SDK")

View File

@ -1,11 +0,0 @@
message("PRELOADING cache")
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
set (WITH_GSSAPI ON CACHE BOOL "Kerberos support")
set (WITH_ALSA OFF CACHE BOOL "alsa audio")
set (WITH_FFMPEG OFF CACHE BOOL "ffmepg support")
set (WITH_XV OFF CACHE BOOL "xvideo support")
set (BUILD_TESTING ON CACHE BOOL "build testing")
set (WITH_XSHM OFF CACHE BOOL "build with xshm support")
set (WITH_SANITIZE_ADDRESS ON)

View File

@ -1,7 +0,0 @@
message("PRELOADING iOS cache")
set (CMAKE_TOOLCHAIN_FILE "cmake/iOSToolchain.cmake" CACHE PATH "ToolChain file")
set (FREERDP_IOS_EXTERNAL_SSL_PATH $ENV{FREERDP_IOS_EXTERNAL_SSL_PATH} CACHE PATH "android ssl")
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
set (IOS_PLATFORM "SIMULATOR" CACHE STRING "iso platfrorm to build")
set (WITH_SANITIZE_ADDRESS ON CACHE BOOL "build with address sanitizer")
set (WITH_CLIENT OFF CACHE BOOL "disable iOS client")

View File

@ -1,53 +0,0 @@
message("PRELOADING cache")
set (BUILD_TESTING ON CACHE BOOL "testing")
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
set (BUILD_TESTING ON CACHE BOOL "build testing")
set (WITH_PULSE ON CACHE BOOL "pulse")
set (WITH_CHANNELS ON CACHE BOOL "channels")
set (BUILTIN_CHANNELS ON CACHE BOOL "static channels")
set (WITH_CUPS ON CACHE BOOL "cups")
set (WITH_WAYLAND ON CACHE BOOL "wayland")
set (WITH_GSSAPI ON CACHE BOOL "Kerberos support")
set (WITH_PCSC ON CACHE BOOL "PCSC")
set (WITH_JPEG ON CACHE BOOL "jpeg")
set (WITH_GSTREAMER_0_10 ON CACHE BOOL "gstreamer")
set (WITH_GSM ON CACHE BOOL "gsm")
set (CHANNEL_URBDRC ON CACHE BOOL "urbdrc")
set (CHANNEL_URBDRC_CLIENT ON CACHE BOOL "urbdrc client")
set (WITH_SERVER ON CACHE BOOL "server side")
set (WITH_DEBUG_ALL OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_CAPABILITIES OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_CERTIFICATE OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_CHANNELS OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_CLIPRDR OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_RDPGFX OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_DVC OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_KBD OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_LICENSE OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_NEGO OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_NLA OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_NTLM OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_RAIL OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_RDP OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_RDPEI OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_REDIR OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_RDPDR OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_RFX OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_SCARD OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_SND OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_SVC OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_THREADS OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_TIMEZONE OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_TRANSPORT OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_TSG OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_TSMF OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_WND OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_X11 OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_X11_CLIPRDR OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_X11_LOCAL_MOVESIZE OFF CACHE BOOL "enable debug")
set (WITH_DEBUG_XV OFF CACHE BOOL "enable debug")
set (WITH_SAMPLE ON CACHE BOOL "samples")
set (WITH_NO_UNDEFINED ON CACHE BOOL "don't link with undefined symbols")
set (WITH_SANITIZE_ADDRESS ON)
set (WITH_PROXY_MODULES OFF CACHE BOOL "compile proxy modules")

View File

@ -1,8 +0,0 @@
message("PRELOADING mac cache")
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
set (WITH_CUPS ON CACHE BOOL "CUPS printing")
set (CHANNEL_URBDRC OFF CACHE BOOL "USB redirection")
set (WITH_X11 ON CACHE BOOL "Enable X11")
set (BUILD_TESTING ON CACHE BOOL "build testing")
set (WITH_SANITIZE_ADDRESS ON)

View File

@ -1,11 +0,0 @@
message("PRELOADING cache")
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
set (WITH_GSSAPI ON CACHE BOOL "Kerberos support")
set (WITH_ALSA OFF CACHE BOOL "alsa audio")
set (WITH_FFMPEG OFF CACHE BOOL "ffmepg support")
set (WITH_XV OFF CACHE BOOL "xvideo support")
set (BUILD_TESTING ON CACHE BOOL "build testing")
set (WITH_XSHM OFF CACHE BOOL "build with xshm support")
set (WITH_SANITIZE_ADDRESS ON)

View File

@ -1,6 +0,0 @@
message("PRELOADING windows cache")
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
set (WITH_SERVER "ON" CACHE BOOL "Build server binaries")
set (CHANNEL_URBDRC OFF CACHE BOOL "USB redirection")
set (BUILD_TESTING ON CACHE BOOL "build testing")
set (WITH_SANITIZE_ADDRESS ON)

View File

@ -43,7 +43,10 @@ if(FREERDP_VENDOR AND WITH_CLIENT)
add_subdirectory(iOS) add_subdirectory(iOS)
endif() endif()
else() else()
add_subdirectory(Mac) option(WITH_CLIENT_MAC "Build native mac client" ON)
if (WITH_CLIENT_MAC)
add_subdirectory(Mac)
endif()
endif() endif()
endif() endif()

View File

@ -80,6 +80,7 @@ struct wlf_clipboard
FILE* responseFile; FILE* responseFile;
UINT32 responseFormat; UINT32 responseFormat;
const char* responseMime; const char* responseMime;
CRITICAL_SECTION lock;
}; };
static BOOL wlf_mime_is_text(const char* mime) static BOOL wlf_mime_is_text(const char* mime)
@ -275,7 +276,7 @@ BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent*
return TRUE; return TRUE;
case UWAC_EVENT_CLIPBOARD_OFFER: case UWAC_EVENT_CLIPBOARD_OFFER:
WLog_Print(clipboard->log, WLOG_INFO, "client announces mime %s", event->mime); WLog_Print(clipboard->log, WLOG_DEBUG, "client announces mime %s", event->mime);
wlf_cliprdr_add_client_format(clipboard, event->mime); wlf_cliprdr_add_client_format(clipboard, event->mime);
return TRUE; return TRUE;
@ -387,6 +388,8 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char*
wfClipboard* clipboard = (wfClipboard*)context; wfClipboard* clipboard = (wfClipboard*)context;
size_t x; size_t x;
WINPR_UNUSED(seat); WINPR_UNUSED(seat);
EnterCriticalSection(&clipboard->lock);
clipboard->responseMime = NULL; clipboard->responseMime = NULL;
for (x = 0; x < ARRAYSIZE(mime_html); x++) for (x = 0; x < ARRAYSIZE(mime_html); x++)
@ -427,6 +430,8 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char*
if (clipboard->responseMime != NULL) if (clipboard->responseMime != NULL)
{ {
if (clipboard->responseFile != NULL)
fclose(clipboard->responseFile);
clipboard->responseFile = fdopen(fd, "w"); clipboard->responseFile = fdopen(fd, "w");
if (clipboard->responseFile) if (clipboard->responseFile)
@ -436,6 +441,7 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char*
"failed to open clipboard file descriptor for MIME %s", "failed to open clipboard file descriptor for MIME %s",
clipboard->responseMime); clipboard->responseMime);
} }
LeaveCriticalSection(&clipboard->lock);
} }
static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context) static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context)
@ -673,6 +679,8 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context,
const WCHAR* wdata = (const WCHAR*)formatDataResponse->requestedFormatData; const WCHAR* wdata = (const WCHAR*)formatDataResponse->requestedFormatData;
wfClipboard* clipboard = (wfClipboard*)context->custom; wfClipboard* clipboard = (wfClipboard*)context->custom;
EnterCriticalSection(&clipboard->lock);
if (size > INT_MAX * sizeof(WCHAR)) if (size > INT_MAX * sizeof(WCHAR))
return ERROR_INTERNAL_ERROR; return ERROR_INTERNAL_ERROR;
@ -694,10 +702,16 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context,
break; break;
} }
fwrite(data, 1, size, clipboard->responseFile); if (clipboard->responseFile)
fclose(clipboard->responseFile); {
fwrite(data, 1, size, clipboard->responseFile);
fclose(clipboard->responseFile);
clipboard->responseFile = NULL;
}
rc = CHANNEL_RC_OK; rc = CHANNEL_RC_OK;
free(cdata); free(cdata);
LeaveCriticalSection(&clipboard->lock);
return rc; return rc;
} }
@ -829,17 +843,24 @@ static UINT wlf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegat
wfClipboard* wlf_clipboard_new(wlfContext* wfc) wfClipboard* wlf_clipboard_new(wlfContext* wfc)
{ {
rdpChannels* channels; rdpChannels* channels;
wfClipboard* clipboard; wfClipboard* clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
if (!(clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard)))) if (!clipboard)
return NULL; goto fail;
InitializeCriticalSection(&clipboard->lock);
clipboard->wfc = wfc; clipboard->wfc = wfc;
channels = wfc->context.channels; channels = wfc->context.channels;
clipboard->log = WLog_Get(TAG); clipboard->log = WLog_Get(TAG);
clipboard->channels = channels; clipboard->channels = channels;
clipboard->system = ClipboardCreate(); clipboard->system = ClipboardCreate();
if (!clipboard->system)
goto fail;
clipboard->delegate = ClipboardGetDelegate(clipboard->system); clipboard->delegate = ClipboardGetDelegate(clipboard->system);
if (!clipboard->delegate)
goto fail;
clipboard->delegate->custom = clipboard; clipboard->delegate->custom = clipboard;
/* TODO: set up a filesystem base path for local URI */ /* TODO: set up a filesystem base path for local URI */
/* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */ /* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */
@ -848,6 +869,10 @@ wfClipboard* wlf_clipboard_new(wlfContext* wfc)
clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success; clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success;
clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure; clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure;
return clipboard; return clipboard;
fail:
wlf_clipboard_free(clipboard);
return NULL;
} }
void wlf_clipboard_free(wfClipboard* clipboard) void wlf_clipboard_free(wfClipboard* clipboard)
@ -858,6 +883,12 @@ void wlf_clipboard_free(wfClipboard* clipboard)
wlf_cliprdr_free_server_formats(clipboard); wlf_cliprdr_free_server_formats(clipboard);
wlf_cliprdr_free_client_formats(clipboard); wlf_cliprdr_free_client_formats(clipboard);
ClipboardDestroy(clipboard->system); ClipboardDestroy(clipboard->system);
EnterCriticalSection(&clipboard->lock);
if (clipboard->responseFile)
fclose(clipboard->responseFile);
LeaveCriticalSection(&clipboard->lock);
DeleteCriticalSection(&clipboard->lock);
free(clipboard); free(clipboard);
} }

View File

@ -134,26 +134,44 @@ BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent*
} }
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev) BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
{
wlfContext* context;
if (!instance || !instance->context || !ev)
return FALSE;
context = (wlfContext*)instance->context;
ArrayList_Add(context->events, ev);
return TRUE;
}
BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev)
{
wlfContext* context;
if (!instance || !instance->context || !ev)
return FALSE;
context = (wlfContext*)instance->context;
ArrayList_Add(context->events, ev);
return TRUE;
}
static BOOL wlf_handle_wheel(freerdp* instance, uint32_t x, uint32_t y, uint32_t axis,
int32_t value)
{ {
rdpInput* input; rdpInput* input;
UINT16 flags = 0; UINT16 flags = 0;
int32_t direction; int32_t direction;
uint32_t x, y; uint32_t avalue = abs(value);
uint32_t i;
if (!instance || !ev || !instance->input) input = instance->input;
return FALSE;
x = ev->x;
y = ev->y;
if (!wlf_scale_coordinates(instance->context, &x, &y, TRUE)) if (!wlf_scale_coordinates(instance->context, &x, &y, TRUE))
return FALSE; return FALSE;
input = instance->input; input = instance->input;
direction = ev->value; direction = value;
switch (ev->axis) switch (axis)
{ {
case WL_POINTER_AXIS_VERTICAL_SCROLL: case WL_POINTER_AXIS_VERTICAL_SCROLL:
flags |= PTR_FLAGS_WHEEL; flags |= PTR_FLAGS_WHEEL;
@ -176,16 +194,102 @@ BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
* positive: 0 ... 0xFF -> slow ... fast * positive: 0 ... 0xFF -> slow ... fast
* negative: 0 ... 0xFF -> fast ... slow * negative: 0 ... 0xFF -> fast ... slow
*/ */
for (i = 0; i < abs(direction); i++)
while (avalue > 0)
{ {
uint32_t cflags = flags | 0x78; const uint32_t cval = avalue > 0xFF ? 0xFF : avalue;
uint32_t cflags = flags | cval;
/* Convert negative values to 9bit twos complement */ /* Convert negative values to 9bit twos complement */
if (flags & PTR_FLAGS_WHEEL_NEGATIVE) if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
cflags = (flags & 0xFF00) | (0x100 - (cflags & 0xFF)); cflags = (flags & 0xFF00) | (0x100 - cval);
if (!freerdp_input_send_mouse_event(input, cflags, (UINT16)x, (UINT16)y)) if (!freerdp_input_send_mouse_event(input, cflags, (UINT16)x, (UINT16)y))
return FALSE; return FALSE;
avalue -= cval;
}
return TRUE;
}
BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev)
{
BOOL success = TRUE;
BOOL handle = FALSE;
size_t x;
wlfContext* context;
enum wl_pointer_axis_source source;
if (!instance || !ev || !instance->input || !instance->context)
return FALSE;
context = (wlfContext*)instance->context;
for (x = 0; x < ArrayList_Count(context->events); x++)
{
UwacEvent* ev = ArrayList_GetItem(context->events, x);
if (!ev)
continue;
if (ev->type == UWAC_EVENT_POINTER_SOURCE)
{
handle = TRUE;
source = ev->mouse_source.axis_source;
}
} }
/* We need source events to determine how to interpret the data */
if (handle)
{
for (x = 0; x < ArrayList_Count(context->events); x++)
{
UwacEvent* ev = ArrayList_GetItem(context->events, x);
if (!ev)
continue;
switch (source)
{
/* If we have a mouse wheel, just use discrete data */
case WL_POINTER_AXIS_SOURCE_WHEEL:
#if defined(WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION)
case WL_POINTER_AXIS_SOURCE_WHEEL_TILT:
#endif
if (ev->type == UWAC_EVENT_POINTER_AXIS_DISCRETE)
{
/* Get the number of steps, multiply by default step width of 120 */
int32_t val = ev->mouse_axis.value * 0x78;
/* No wheel event received, success! */
if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y,
ev->mouse_axis.axis, val))
success = FALSE;
}
break;
/* If we have a touch pad we get actual data, scale */
case WL_POINTER_AXIS_SOURCE_FINGER:
case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
if (ev->type == UWAC_EVENT_POINTER_AXIS)
{
double dval = wl_fixed_to_double(ev->mouse_axis.value);
int32_t val = dval * 0x78 / 10.0;
if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y,
ev->mouse_axis.axis, val))
success = FALSE;
}
break;
default:
break;
}
}
}
ArrayList_Clear(context->events);
return success;
}
BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev)
{
wlfContext* context;
if (!instance || !instance->context || !ev)
return FALSE;
context = (wlfContext*)instance->context;
ArrayList_Add(context->events, ev);
return TRUE; return TRUE;
} }

View File

@ -30,6 +30,9 @@ BOOL wlf_handle_pointer_enter(freerdp* instance, const UwacPointerEnterLeaveEven
BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev); BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev);
BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev); BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev);
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev); BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev);
BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev);
BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev);
BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev);
BOOL wlf_handle_touch_up(freerdp* instance, const UwacTouchUp* ev); BOOL wlf_handle_touch_up(freerdp* instance, const UwacTouchUp* ev);
BOOL wlf_handle_touch_down(freerdp* instance, const UwacTouchDown* ev); BOOL wlf_handle_touch_down(freerdp* instance, const UwacTouchDown* ev);
BOOL wlf_handle_touch_motion(freerdp* instance, const UwacTouchMotion* ev); BOOL wlf_handle_touch_motion(freerdp* instance, const UwacTouchMotion* ev);

View File

@ -42,7 +42,7 @@ static BOOL wlf_Pointer_New(rdpContext* context, rdpPointer* pointer)
if (!ptr) if (!ptr)
return FALSE; return FALSE;
ptr->size = pointer->width * pointer->height * 4; ptr->size = pointer->width * pointer->height * 4ULL;
ptr->data = _aligned_malloc(ptr->size, 16); ptr->data = _aligned_malloc(ptr->size, 16);
if (!ptr->data) if (!ptr->data)
@ -92,7 +92,7 @@ static BOOL wlf_Pointer_Set(rdpContext* context, const rdpPointer* pointer)
!wlf_scale_coordinates(context, &w, &h, FALSE)) !wlf_scale_coordinates(context, &w, &h, FALSE))
return FALSE; return FALSE;
size = w * h * 4; size = w * h * 4ULL;
data = malloc(size); data = malloc(size);
if (!data) if (!data)

View File

@ -225,6 +225,7 @@ static BOOL wl_post_connect(freerdp* instance)
wlfContext* context; wlfContext* context;
rdpSettings* settings; rdpSettings* settings;
char* title = "FreeRDP"; char* title = "FreeRDP";
char* app_id = "wlfreerdp";
UINT32 w, h; UINT32 w, h;
if (!instance || !instance->context) if (!instance || !instance->context)
@ -266,6 +267,7 @@ static BOOL wl_post_connect(freerdp* instance)
UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen); UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen);
UwacWindowSetTitle(window, title); UwacWindowSetTitle(window, title);
UwacWindowSetAppId(window, app_id);
UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h); UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h);
instance->update->BeginPaint = wl_begin_paint; instance->update->BeginPaint = wl_begin_paint;
instance->update->EndPaint = wl_end_paint; instance->update->EndPaint = wl_end_paint;
@ -331,12 +333,15 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
break; break;
case UWAC_EVENT_FRAME_DONE: case UWAC_EVENT_FRAME_DONE:
{
UwacReturnCode r;
EnterCriticalSection(&context->critical); EnterCriticalSection(&context->critical);
rc = UwacWindowSubmitBuffer(context->window, false); r = UwacWindowSubmitBuffer(context->window, false);
LeaveCriticalSection(&context->critical); LeaveCriticalSection(&context->critical);
if (rc != UWAC_SUCCESS) if (r != UWAC_SUCCESS)
return FALSE; return FALSE;
break; }
break;
case UWAC_EVENT_POINTER_ENTER: case UWAC_EVENT_POINTER_ENTER:
if (!wlf_handle_pointer_enter(instance, &event.mouse_enter_leave)) if (!wlf_handle_pointer_enter(instance, &event.mouse_enter_leave))
@ -357,12 +362,22 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
break; break;
case UWAC_EVENT_POINTER_AXIS: case UWAC_EVENT_POINTER_AXIS:
if (!wlf_handle_pointer_axis(instance, &event.mouse_axis))
return FALSE;
break; break;
case UWAC_EVENT_POINTER_AXIS_DISCRETE: case UWAC_EVENT_POINTER_AXIS_DISCRETE:
if (!wlf_handle_pointer_axis(instance, &event.mouse_axis)) if (!wlf_handle_pointer_axis_discrete(instance, &event.mouse_axis))
return FALSE; return FALSE;
break;
case UWAC_EVENT_POINTER_FRAME:
if (!wlf_handle_pointer_frame(instance, &event.mouse_frame))
return FALSE;
break;
case UWAC_EVENT_POINTER_SOURCE:
if (!wlf_handle_pointer_source(instance, &event.mouse_source))
return FALSE;
break; break;
case UWAC_EVENT_KEY: case UWAC_EVENT_KEY:
@ -556,8 +571,37 @@ static int wlf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
return 1; return 1;
} }
static void wlf_client_free(freerdp* instance, rdpContext* context)
{
wlfContext* wlf = (wlfContext*)instance->context;
if (!context)
return;
if (wlf->display)
UwacCloseDisplay(&wlf->display);
if (wlf->displayHandle)
CloseHandle(wlf->displayHandle);
ArrayList_Free(wlf->events);
DeleteCriticalSection(&wlf->critical);
}
static void* uwac_event_clone(const void* val)
{
UwacEvent* copy;
UwacEvent* ev = (UwacEvent*)val;
copy = calloc(1, sizeof(UwacEvent));
if (!copy)
return NULL;
*copy = *ev;
return copy;
}
static BOOL wlf_client_new(freerdp* instance, rdpContext* context) static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
{ {
wObject* obj;
UwacReturnCode status; UwacReturnCode status;
wlfContext* wfl = (wlfContext*)context; wlfContext* wfl = (wlfContext*)context;
@ -585,26 +629,19 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
if (!wfl->displayHandle) if (!wfl->displayHandle)
return FALSE; return FALSE;
wfl->events = ArrayList_New(FALSE);
if (!wfl->events)
return FALSE;
obj = ArrayList_Object(wfl->events);
obj->fnObjectNew = uwac_event_clone;
obj->fnObjectFree = free;
InitializeCriticalSection(&wfl->critical); InitializeCriticalSection(&wfl->critical);
return TRUE; return TRUE;
} }
static void wlf_client_free(freerdp* instance, rdpContext* context)
{
wlfContext* wlf = (wlfContext*)instance->context;
if (!context)
return;
if (wlf->display)
UwacCloseDisplay(&wlf->display);
if (wlf->displayHandle)
CloseHandle(wlf->displayHandle);
DeleteCriticalSection(&wlf->critical);
}
static int wfl_client_start(rdpContext* context) static int wfl_client_start(rdpContext* context)
{ {
WINPR_UNUSED(context); WINPR_UNUSED(context);

View File

@ -53,6 +53,7 @@ struct wlf_context
wlfDispContext* disp; wlfDispContext* disp;
wLog* log; wLog* log;
CRITICAL_SECTION critical; CRITICAL_SECTION critical;
wArrayList* events;
}; };
BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fromLocalToRDP); BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fromLocalToRDP);

View File

@ -59,11 +59,9 @@ int main(int argc, char* argv[])
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE); status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
if (status) if (status)
{ {
BOOL list = settings->ListMonitors;
rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv); rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
if (list) if (settings->ListMonitors)
xf_list_monitors(xfc); xf_list_monitors(xfc);
goto out; goto out;

View File

@ -27,6 +27,7 @@
#endif #endif
#include <assert.h> #include <assert.h>
#include <winpr/sspicli.h>
#include <float.h> #include <float.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
@ -814,8 +815,6 @@ void xf_lock_x11_(xfContext* xfc, const char* fkt)
else else
XLockDisplay(xfc->display); XLockDisplay(xfc->display);
if (xfc->locked)
WLog_WARN(TAG, "%s:\t[%" PRIu32 "] recursive lock from %s", __FUNCTION__, xfc->locked, fkt);
xfc->locked++; xfc->locked++;
WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt); WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt);
} }
@ -1183,20 +1182,12 @@ static BOOL xf_pre_connect(freerdp* instance)
if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon) if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon)
{ {
int rc;
char login_name[MAX_PATH] = { 0 }; char login_name[MAX_PATH] = { 0 };
ULONG size = sizeof(login_name) - 1;
#ifdef HAVE_GETLOGIN_R if (GetUserNameExA(NameSamCompatible, login_name, &size))
rc = getlogin_r(login_name, sizeof(login_name));
#else
strncpy(login_name, getlogin(), sizeof(login_name));
rc = 0;
#endif
if (rc == 0)
{ {
settings->Username = _strdup(login_name); if (!freerdp_settings_set_string(settings, FreeRDP_Username, login_name))
if (!settings->Username)
return FALSE; return FALSE;
WLog_INFO(TAG, "No user name set. - Using login name: %s", settings->Username); WLog_INFO(TAG, "No user name set. - Using login name: %s", settings->Username);
@ -1263,6 +1254,7 @@ static BOOL xf_post_connect(freerdp* instance)
context = instance->context; context = instance->context;
settings = instance->settings; settings = instance->settings;
update = context->update; update = context->update;
BOOL serverIsWindowsPlatform;
if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE))) if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE)))
return FALSE; return FALSE;
@ -1332,7 +1324,8 @@ static BOOL xf_post_connect(freerdp* instance)
update->SetKeyboardIndicators = xf_keyboard_set_indicators; update->SetKeyboardIndicators = xf_keyboard_set_indicators;
update->SetKeyboardImeStatus = xf_keyboard_set_ime_status; update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
if (!(xfc->clipboard = xf_clipboard_new(xfc))) serverIsWindowsPlatform = (settings->OsMajorType == OSMAJORTYPE_WINDOWS);
if (!(xfc->clipboard = xf_clipboard_new(xfc, !serverIsWindowsPlatform)))
return FALSE; return FALSE;
if (!(xfc->xfDisp = xf_disp_new(xfc))) if (!(xfc->xfDisp = xf_disp_new(xfc)))
@ -1391,7 +1384,10 @@ static int xf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
const char* str_data = freerdp_get_logon_error_info_data(data); const char* str_data = freerdp_get_logon_error_info_data(data);
const char* str_type = freerdp_get_logon_error_info_type(type); const char* str_type = freerdp_get_logon_error_info_type(type);
WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type); WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
xf_rail_disable_remoteapp_mode(xfc); if(type != LOGON_MSG_SESSION_CONTINUE)
{
xf_rail_disable_remoteapp_mode(xfc);
}
return 1; return 1;
} }
@ -1401,11 +1397,8 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
DWORD status; DWORD status;
DWORD nCount; DWORD nCount;
HANDLE events[3]; HANDLE events[3];
XEvent xevent;
wMessage msg; wMessage msg;
wMessageQueue* queue; wMessageQueue* queue;
int pending_status = 1;
int process_status = 1;
freerdp* instance = (freerdp*)arg; freerdp* instance = (freerdp*)arg;
xfContext* xfc = (xfContext*)instance->context; xfContext* xfc = (xfContext*)instance->context;
queue = freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE); queue = freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE);
@ -1434,26 +1427,7 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
if (WaitForSingleObject(events[1], 0) == WAIT_OBJECT_0) if (WaitForSingleObject(events[1], 0) == WAIT_OBJECT_0)
{ {
do if (!xf_process_x_events(xfc->context.instance))
{
xf_lock_x11(xfc);
pending_status = XPending(xfc->display);
xf_unlock_x11(xfc);
if (pending_status)
{
xf_lock_x11(xfc);
ZeroMemory(&xevent, sizeof(xevent));
XNextEvent(xfc->display, &xevent);
process_status = xf_event_process(instance, &xevent);
xf_unlock_x11(xfc);
if (!process_status)
break;
}
} while (pending_status);
if (!process_status)
{ {
running = FALSE; running = FALSE;
break; break;
@ -1472,6 +1446,7 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
} }
MessageQueue_PostQuit(queue, 0); MessageQueue_PostQuit(queue, 0);
freerdp_abort_connect(xfc->context.instance);
ExitThread(0); ExitThread(0);
return 0; return 0;
} }
@ -1489,7 +1464,7 @@ static BOOL handle_window_events(freerdp* instance)
{ {
if (!xf_process_x_events(instance)) if (!xf_process_x_events(instance))
{ {
WLog_INFO(TAG, "Closed from X11"); WLog_DBG(TAG, "Closed from X11");
return FALSE; return FALSE;
} }
} }
@ -1533,6 +1508,84 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
else if (freerdp_get_last_error(instance->context) == else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED) FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
exit_code = XF_EXIT_NEGO_FAILURE; exit_code = XF_EXIT_NEGO_FAILURE;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_LOGON_FAILURE)
exit_code = XF_EXIT_LOGON_FAILURE;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT)
exit_code = XF_EXIT_ACCOUNT_LOCKED_OUT;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_PRE_CONNECT_FAILED)
exit_code = XF_EXIT_PRE_CONNECT_FAILED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_UNDEFINED)
exit_code = XF_EXIT_CONNECT_UNDEFINED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_POST_CONNECT_FAILED)
exit_code = XF_EXIT_POST_CONNECT_FAILED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_DNS_ERROR)
exit_code = XF_EXIT_DNS_ERROR;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_DNS_NAME_NOT_FOUND)
exit_code = XF_EXIT_DNS_NAME_NOT_FOUND;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_FAILED)
exit_code = XF_EXIT_CONNECT_FAILED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR)
exit_code = XF_EXIT_MCS_CONNECT_INITIAL_ERROR;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_TLS_CONNECT_FAILED)
exit_code = XF_EXIT_TLS_CONNECT_FAILED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_INSUFFICIENT_PRIVILEGES)
exit_code = XF_EXIT_INSUFFICIENT_PRIVILEGES;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_CANCELLED)
exit_code = XF_EXIT_CONNECT_CANCELLED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
exit_code = XF_EXIT_SECURITY_NEGO_CONNECT_FAILED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_TRANSPORT_FAILED)
exit_code = XF_EXIT_CONNECT_TRANSPORT_FAILED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED)
exit_code = XF_EXIT_CONNECT_PASSWORD_EXPIRED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE)
exit_code = XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_KDC_UNREACHABLE)
exit_code = XF_EXIT_CONNECT_KDC_UNREACHABLE;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED)
exit_code = XF_EXIT_CONNECT_ACCOUNT_DISABLED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED)
exit_code = XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_CLIENT_REVOKED)
exit_code = XF_EXIT_CONNECT_CLIENT_REVOKED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_WRONG_PASSWORD)
exit_code = XF_EXIT_CONNECT_WRONG_PASSWORD;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_ACCESS_DENIED)
exit_code = XF_EXIT_CONNECT_ACCESS_DENIED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION)
exit_code = XF_EXIT_CONNECT_ACCOUNT_RESTRICTION;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED)
exit_code = XF_EXIT_CONNECT_ACCOUNT_EXPIRED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED)
exit_code = XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED;
else if (freerdp_get_last_error(instance->context) ==
FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS)
exit_code = XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS;
else else
exit_code = XF_EXIT_CONN_FAILED; exit_code = XF_EXIT_CONN_FAILED;
} }
@ -1697,7 +1750,7 @@ end:
DWORD xf_exit_code_from_disconnect_reason(DWORD reason) DWORD xf_exit_code_from_disconnect_reason(DWORD reason)
{ {
if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_NEGO_FAILURE)) if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS))
return reason; return reason;
/* License error set */ /* License error set */
else if (reason >= 0x100 && reason <= 0x10A) else if (reason >= 0x100 && reason <= 0x10A)

View File

@ -1034,7 +1034,8 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
if (!delayRespond) if (!delayRespond)
{ {
union { union
{
XEvent* ev; XEvent* ev;
XSelectionEvent* sev; XSelectionEvent* sev;
} conv; } conv;
@ -1423,7 +1424,10 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context,
} }
ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE); ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE);
xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard); if (xfc->remote_app)
xf_cliprdr_set_selection_owner(xfc, clipboard, CurrentTime);
else
xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard);
return ret; return ret;
} }
@ -1566,7 +1570,7 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
{ {
if (SrcSize == 0) if (SrcSize == 0)
{ {
WLog_INFO(TAG, "skipping, empty data detected!!!"); WLog_DBG(TAG, "skipping, empty data detected!");
free(clipboard->respond); free(clipboard->respond);
clipboard->respond = NULL; clipboard->respond = NULL;
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
@ -1611,7 +1615,8 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize); xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize);
{ {
union { union
{
XEvent* ev; XEvent* ev;
XSelectionEvent* sev; XSelectionEvent* sev;
} conv; } conv;
@ -1751,7 +1756,27 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response); return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
} }
xfClipboard* xf_clipboard_new(xfContext* xfc) static BOOL xf_cliprdr_clipboard_is_valid_unix_filename(LPCWSTR filename)
{
LPCWSTR c;
if (!filename)
return FALSE;
if (filename[0] == L'\0')
return FALSE;
/* Reserved characters */
for (c = filename; *c; ++c)
{
if (*c == L'/')
return FALSE;
}
return TRUE;
}
xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction)
{ {
int i, n = 0; int i, n = 0;
rdpChannels* channels; rdpChannels* channels;
@ -1880,6 +1905,13 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure; clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success; clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure; clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure;
if (relieveFilenameRestriction)
{
WLog_DBG(TAG, "Relieving CLIPRDR filename restriction");
clipboard->delegate->IsFileNameComponentValid = xf_cliprdr_clipboard_is_valid_unix_filename;
}
return clipboard; return clipboard;
error: error:

View File

@ -25,7 +25,7 @@
#include <freerdp/client/cliprdr.h> #include <freerdp/client/cliprdr.h>
xfClipboard* xf_clipboard_new(xfContext* xfc); xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction);
void xf_clipboard_free(xfClipboard* clipboard); void xf_clipboard_free(xfClipboard* clipboard);
void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr); void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr);

View File

@ -93,7 +93,7 @@ static BOOL xf_update_last_sent(xfDispContext* xfDisp)
static BOOL xf_disp_sendResize(xfDispContext* xfDisp) static BOOL xf_disp_sendResize(xfDispContext* xfDisp)
{ {
DISPLAY_CONTROL_MONITOR_LAYOUT layout; DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 };
xfContext* xfc; xfContext* xfc;
rdpSettings* settings; rdpSettings* settings;
@ -242,7 +242,24 @@ static void xf_disp_OnTimer(void* context, TimerEventArgs* e)
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings)) if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
return; return;
if (!xfDisp->activated || settings->Fullscreen) if (!xfDisp->activated || xfc->fullscreen)
return;
xf_disp_sendResize(xfDisp);
}
static void xf_disp_OnWindowStateChange(void* context, WindowStateChangeEventArgs* e)
{
xfContext* xfc;
xfDispContext* xfDisp;
rdpSettings* settings;
WINPR_UNUSED(e);
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
return;
if (!xfDisp->activated || !xfc->fullscreen)
return; return;
xf_disp_sendResize(xfDisp); xf_disp_sendResize(xfDisp);
@ -274,6 +291,7 @@ xfDispContext* xf_disp_new(xfContext* xfc)
PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated); PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated);
PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset); PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset);
PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer); PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer);
PubSub_SubscribeWindowStateChange(xfc->context.pubSub, xf_disp_OnWindowStateChange);
return ret; return ret;
} }
@ -287,6 +305,7 @@ void xf_disp_free(xfDispContext* disp)
PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated); PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated);
PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset); PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset);
PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer); PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer);
PubSub_UnsubscribeWindowStateChange(disp->xfc->context.pubSub, xf_disp_OnWindowStateChange);
} }
free(disp); free(disp);

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