Compare commits

..

142 Commits

Author SHA1 Message Date
Bernhard Miklautz
33d9497253
Merge pull request #5992 from alexpilotti/fix_build
Fix stable-1.1 build on Windows
2020-03-27 00:41:29 +01:00
Alessandro Pilotti
3850ba376c Add missing wincred.h include on Windows 2020-03-25 05:09:29 +02:00
Alessandro Pilotti
7ff58e992a Fix GetSecurityStatusString on Windows 2020-03-25 05:08:02 +02:00
Bernhard Miklautz
6b66f199e6 fix: add missing file 2019-05-17 09:48:21 +02:00
akallabeth
590fa7dbf6
Merge pull request #5391 from bmiklautz/stable-1.1-backports
Backport multiple fixes from master
2019-05-09 12:57:10 +02:00
Bernhard Miklautz
a99ec8cb65 new: support for protocol version 6
Based on 8df96364f2

Backported by:
	Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
	Bernhard Miklautz <bernhard.miklautz@thincast.com>
	Martin Fleisz <martin.fleisz@thincast.com>
2019-05-09 12:03:06 +02:00
Bernhard Miklautz
6fa64006a1 fix: CVE-2018-8789
Backport of 2ee663f39d
2019-05-09 11:30:56 +02:00
Bernhard Miklautz
af9a8fbf31 fix: CVE-2018-8788
Backport of d1112c279b
2019-05-09 11:29:57 +02:00
Bernhard Miklautz
f777230e15 fix: CVE-2018-8787
Backport of 09b9d4f199
2019-05-09 11:28:29 +02:00
Bernhard Miklautz
4f2a7bd7d4
Merge pull request #5390 from abagarwams/portCVE2018-8786
Porting patch for CVE2018-8786 to 1.1 branch
2019-05-09 11:10:02 +02:00
abagarewa
9f96ce255e Porting patch for CVE2018-8786 to 1.1 branch 2019-05-08 16:33:33 -07:00
Bernhard Miklautz
851f0979d5
Merge pull request #4427 from ondrejholy/tls1+-stable-1.1
tls: enable tls 1+ for stable-1.1
2018-02-13 16:06:24 +01:00
Bernhard Miklautz
1c1d825b2f tls: enable tls 1+
Currently TLS version 1.0 is used implicitly by using the TLSv1_method.
To be able to also use TLS 1.1 and later use SSLv23_client_method
instead. To make sure SSLv2 or SSLv3 isn't used disable them.

Commit aa80f63b from master was backported by Ondrej Holy <oholy@redhat.com>.

https://github.com/FreeRDP/FreeRDP/issues/2128
2018-02-13 15:54:15 +01:00
Bernhard Miklautz
03ab683189 Fix multiple security issues
Fix the following issues identified by the CISCO TALOS project:

* TALOS-2017-0336 CVE-2017-2834
* TALOS-2017-0337 CVE-2017-2835
* TALOS-2017-0338 CVE-2017-2836
* TALOS-2017-0339 CVE-2017-2837
* TALOS-2017-0340 CVE-2017-2838
* TALOS-2017-0341 CVE-2017-2839

Backported based on commit 8292b4558f.
2017-07-27 14:04:21 +02:00
Bernhard Miklautz
4c69c3ea14 Merge pull request #2818 from vancepym/stable-1.1
Error: "Unable to detect time zone" - Add support for tz package
2016-02-23 16:22:16 +01:00
Scott Fan
605d0a8518 Add support for tz package
Fixes error message while freerdp connected the remote desktop:
Unable to detect time zone
2015-07-13 15:18:52 +08:00
Marc-André Moreau
770c67d340 Merge pull request #2453 from bmiklautz/stable-1.1_cmd_fixes
settings: handle collection_add in detection case
2015-03-12 07:31:38 -04:00
Bernhard Miklautz
85856224f6 settings: handle collection_add in detection case
Command line detection is run with dummy settings where not everything
is allocated. Collections (device, dynamic channel and static
channel) didn't handle this case properly.

(cherry picked from commit e9985c2093)

Conflicts:
	client/common/test/TestClientCmdLine.c
2015-03-10 14:30:51 +01:00
Bernhard Miklautz
a700ee75f9 Merge pull request #2440 from bmiklautz/stable1.1/issue_2439
core/glyph: copy data when adding glyph to cache
2015-03-05 16:42:52 +01:00
Bernhard Miklautz
daea54925b core/glyph: copy data when adding glyph to cache
fixes #2439

(cherry picked from commit c99d9ee72b)
2015-03-05 15:10:48 +01:00
Norbert Federa
b21ff842ef Merge pull request #2380 from bmiklautz/stable-1.1_cmdline
Build and command line fixes
2015-02-12 11:09:30 +01:00
Bernhard Miklautz
f42b03f7bb build: fix build on win32
Manually pick changes and improvements from master to build on win32
(without specifying platform toolset)
2015-02-11 17:51:30 +01:00
Bernhard Miklautz
3cd91cf599 build: updates to build with pull request builder 2015-02-11 11:41:16 +01:00
Bernhard Miklautz
ab59334c66 build: fix TestClientCmdLine MONOLITHIC_BUILD 2015-02-11 10:47:57 +01:00
Bernhard Miklautz
892431326f ci: add config-linux-all.txt 2015-02-11 02:05:32 +01:00
Bernhard Miklautz
a5f6ba04cd client: fixed line compatibility 2015-02-11 01:58:16 +01:00
Bernhard Miklautz
eb9112c84b client/common: add test for client command line 2015-02-11 01:57:31 +01:00
Armin Novak
ae025ae332 Fixed memory leak.
(cherry picked from commit 9863ccfe66)
2015-01-16 14:07:38 +01:00
Armin Novak
2228cd6e73 Fixed multiple parsing errors for compatibility command line.
(cherry picked from commit dfb6176df6)

Conflicts:
	client/common/compatibility.c
2015-01-16 14:07:31 +01:00
Armin Novak
1fdf05da3f Fixed windows command line
freerdp_detect_windows_style_command_line_syntax returns negative values
in error but also in help and version case... oh boy

(cherry picked from commit f86ed3ffef)
2015-01-16 13:56:35 +01:00
Armin Novak
41313e6d70 Fixed missing NULL pointer check.
(cherry picked from commit 241848038c)

Conflicts:
	client/common/compatibility.c
2015-01-16 13:56:23 +01:00
Armin Novak
dab7113f0f Fixed clang warnings.
Fixed check for compatibility command line.

(cherry picked from commit 20f7e4d301)

Conflicts:
	client/common/cmdline.c
2015-01-16 13:54:16 +01:00
Armin Novak
cd21ac3498 Fixed command line pre filter, now returning 2 to skip argument.
(cherry picked from commit 1d4403cbbe)

Conflicts:
	client/common/compatibility.c
2015-01-16 13:53:00 +01:00
Bernhard Miklautz
7974c5072c winpr build: cmake 3.1 compatibility
* since 3.1 file(GLOB FILEPATHS RELATIVE .. returns single
* / instead of // as previously - necessary adoptions for regex and
  matches done. Should work with all cmake versions.
2015-01-16 13:43:04 +01:00
Bernhard Miklautz
0027350ff0 build: improve X11 detection on OS X
/usr/X11R6 doesn't exist on OS X per default (anymore). Therefore add
PATHS to all X11 detection modules pointing to the Xquarz installation
directory in /opt/X11.
For FindX11 it was also necessary to ensure that the frameworks are
searched as last (after PATHS) otherwise it could happen that X11 headers
of a framework (e.g. Tk.framework) were used.

(cherry picked from commit b35dc849ee)
2015-01-16 13:27:31 +01:00
Bernhard Miklautz
598d30f8b8 Mac: set policy to silent cmake >= 3.0 warnings
Set the following policies to "OLD" if cmake version is greater than
2.8.12:

CMP0026 - Disallow use of the LOCATION target property
CMP0045 - Error on non-existent target in get_target_property

(cherry picked from commit 10ee2f72d9)
2015-01-16 13:27:22 +01:00
Bernhard Miklautz
b065492890 build: cmake 3.1 compatibility
* fix problem with REMOVE_DUPLICATES on undefined lists
* since 3.1 file(GLOB FILEPATHS RELATIVE .. returns single / instead of // as
  previously - necessary adoptions for regex and matches done. Should
	work with all cmake versions.

Tested with 3.1.0-rc3

(cherry picked from commit 1b663ceffe)

Conflicts:
	client/CMakeLists.txt
	server/CMakeLists.txt
2015-01-16 13:27:08 +01:00
Bernhard Miklautz
6c6fb62029 mfreerdp: fix build with latest cmake
Newer versions of cmake seem to use CMAKE_C_FLAGS for objective-c.
Now both flags are set for compatibility.

(cherry picked from commit 9ab95adf0d)
2015-01-16 13:24:52 +01:00
Marc-André Moreau
440916eae2 Merge pull request #2061 from bmiklautz/remove_winpr_configh
Get rid of winpr/config.h
2014-08-25 09:52:43 -04:00
Bernhard Miklautz
6d2d377ed1 Get rid of winpr/config.h
winpr/config.h just contains WITH_NATIVE_SSPI which is already
defined in top level config.h.
2014-08-24 14:31:13 +02:00
Marc-André Moreau
6fc97c7179 Merge pull request #2040 from dpoe/stable-1.1
handle user@corp.net username correctly
2014-08-18 13:07:30 -04:00
Daryl Poe
248c918508 handle user@corp.net username correctly 2014-08-15 15:45:06 -06:00
Bernhard Miklautz
b07a5c11e4 Merge pull request #2031 from bmiklautz/fix/stable-1.1/ffmpeg2
build: fix tsmf ffmpeg build
2014-08-09 21:06:40 +02:00
Bernhard Miklautz
dc7f6abbed build: fix tsmf ffmpeg build
Backported fixes to build against libavcodec >= 9.0
2014-08-08 16:05:45 +02:00
Bernhard Miklautz
7b5cd86a3e Merge pull request #2030 from bmiklautz/fix/stable-1.1/ffmpeg
build: fix tsmf ffmpeg
2014-08-08 15:22:12 +02:00
Ian Whyman
75b6e12c11 Backwards compat defines
Conflicts:
	channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
2014-08-08 15:13:25 +02:00
Ian Whyman
3b9b030b0e CODEC_ID_* -> AV_CODEC_ID_* 2014-08-08 15:08:15 +02:00
Bernhard Miklautz
28f65ff297 Merge pull request #1887 from dpoe/stable-1.1
correct Pause key sequence
2014-07-25 11:23:09 +02:00
Hardening
3122946757 Merge pull request #1891 from hardening/CVE-2014-0250_1dot1
Fix for CVE 2014-0250
2014-06-19 10:24:10 +02:00
Hardening
5ad707ddf3 Fix CVE-2014-0250
This patch fixes CVE-2014-0250 by checking width, height and bpp when
receiving a new pointer.
2014-06-06 23:24:16 +02:00
Daryl Poe
46a00b5c9e correct Pause key sequence 2014-06-04 15:41:17 -06:00
Marc-André Moreau
01865f0e28 Merge pull request #1770 from dpoe/stable-1.1
/kbd: option should be unsigned
2014-04-08 23:56:49 -04:00
Daryl Poe
fc6b72017f /kbd: option should be unsigned 2014-04-04 14:09:48 -06:00
Marc-André Moreau
2604ff20bd Merge pull request #1673 from dpoe/stable-1.1
Fixes for redirected-drive cut, mode, read/write
2014-02-27 13:55:30 -05:00
Daryl Poe
865b816b80 fixes for drive cut, mode, read/write 2014-01-27 17:21:34 -07:00
Marc-André Moreau
997d40639c Merge pull request #1658 from dbungert/compression
Address bitmap related crashes.
2014-01-14 11:16:01 -08:00
Dan Bungert
580e6b68c9 Switch back to 64K (RDP5) compression. 2014-01-10 08:05:23 -07:00
Marc-André Moreau
69af35a1b5 Merge pull request #1630 from bmiklautz/stable-1.1-bp
master stable sync
2013-12-08 15:52:51 -08:00
Bernhard Miklautz
7c4ebe2bc4 ios build: updated README.ios
(cherry picked from commit fa1bc1f2fe)
2013-12-08 17:35:57 +01:00
Bernhard Miklautz
37ac70aa1c ios ssl build: support for user specified sdks
To build against a specific (not auto detected) SDK version set
USER_OS_SDK and/or USER_SIM_SDK at the top of the script.
(cherry picked from commit 38883e5ecd)
2013-12-08 17:35:51 +01:00
Bernhard Miklautz
3754d07163 ios ssl build: do sdk detection before download
(cherry picked from commit 27d8844fab)
2013-12-08 17:35:43 +01:00
Bernhard Miklautz
2ef6c552c4 Cleaned up and improved iOS openssl build script
* Added support for optional build directory (first argument)
* Automatically detect iOS/iPhoneSimulator SDKs (oldest SDK found is used)
(cherry picked from commit e30d6109bc)
2013-12-08 17:35:36 +01:00
Bernhard Miklautz
323b081192 ios build: added option to set signing certificate
Code signing certificate can be set with with cmake option CODE_SIGN_IDENTITY.
Like -DCODE_SIGN_IDENTITY="signing identity"
(cherry picked from commit f30d3ac3a1)
2013-12-08 17:35:27 +01:00
Bernhard Miklautz
413c4dea65 ios simulator build: set CMAKE_OSX_SYSROOT
When building for iphone simulator it is required to set the CMAKE_OSX_SYSROOT
to "iphonesimulator" otherwise command line builds (with cmake --build) will fail.
(cherry picked from commit 6d2142182d)
2013-12-08 17:35:22 +01:00
Bernhard Miklautz
88963aae5c ios build: fixed toolchain for cmake 2.8.10
(cherry picked from commit 7b706ba840)
2013-12-08 17:35:15 +01:00
Bernhard Miklautz
30de5b226d windows build: use different .def file for xp
Use different def file for windows < 5.1.
(cherry picked from commit 57d295441f)
2013-12-08 17:27:58 +01:00
Bernhard Miklautz
5360f59940 windows build: ignore generated files (nmake)
(cherry picked from commit 23f33ca20e)
2013-12-08 17:27:50 +01:00
Bernhard Miklautz
abf84cf6e0 Cmake preload scripts for CI added
(cherry picked from commit f2b7876302)
2013-12-08 17:22:21 +01:00
Bernhard Miklautz
613b20d482 android: use version.h 2013-12-08 17:17:12 +01:00
Bernhard Miklautz
37602e6000 use version.h in ios client
(cherry picked from commit 0147b06d06)
2013-12-08 16:48:48 +01:00
Bernhard Miklautz
0380e652d0 install version.h
(cherry picked from commit c9f49162bd)
2013-12-08 16:47:53 +01:00
Bernhard Miklautz
23512af600 moved version information to freerdp/version.h
fixed #1465
(cherry picked from commit a0161a12ac)
2013-12-08 16:47:47 +01:00
Marc-André Moreau
d2a9df1ce0 Merge pull request #1494 from dpoe/stable-1.1
send RDP_NEG_REQ also in the case of a null server certificate
2013-09-19 13:03:46 -07:00
Daryl Poe
afec6957c4 send RDP_NEG_REQ also in the case of a null server certificate 2013-09-18 17:16:48 -06:00
Marc-André Moreau
d8d7a9c1c6 Merge pull request #1435 from dpoe/stable-1.1
Apply glyph fragment offset after fragment
2013-09-16 13:38:46 -07:00
Marc-André Moreau
fe40452d65 Merge pull request #1443 from bmiklautz/stable-1.1-sync
Backported commits from master
2013-09-03 15:40:57 -07:00
Marc-André Moreau
214fe2198a xfreerdp: fix monolithic build
(cherry picked from commit 3194967957)
2013-08-26 20:20:33 +02:00
Bernhard Miklautz
ec6b84fde4 Merge pull request #1429 from master 2013-08-26 20:19:55 +02:00
Armin Novak
a91592bd68 Using a monitor thread for tty input now to work around a WaitForMultipleObjects
limitation of winpr.
Using infinite timeout now in threads to reduce CPU usage to nearly zero.
2013-08-26 20:19:03 +02:00
Armin Novak
48ce36fd11 Reset file descriptor set only, if necessary now. 2013-08-26 20:19:03 +02:00
Armin Novak
313250d99b Fixed check for _GNU_SOURCE pthread extensions. 2013-08-26 20:19:03 +02:00
Armin Novak
ebff396e90 Fixed compilation error, now using WaitForMultipleObjects. 2013-08-26 20:16:06 +02:00
Armin Novak
c3923afa1f Fixed initialization of serial device, now aborting pending
read / write operations.
2013-08-26 20:16:06 +02:00
Bernhard Miklautz
d1e6221b25 Merge pull request #1395 from master 2013-08-26 20:14:17 +02:00
richterger
8d92adca1d Fix off by one problem in StreamPool allocation 2013-08-26 20:13:44 +02:00
richterger
ceae1b87a5 Fixed memory corruption problems within client redirect
- set freed pointers to NULL to avoid double free
- realloc mppc to cleanly restart compression
- avoid releaseing StreamPool from already freed transport after client redirect
2013-08-26 20:13:44 +02:00
Bernhard Miklautz
84f1001573 Merge pull request 1422 from master 2013-08-26 20:11:31 +02:00
Armin Novak
bd74f5c8b5 Removed EAGAIN handling, again passing on the error to the server. 2013-08-26 20:10:43 +02:00
Armin Novak
cf6b9d44ac Fixed invalid access to tty in thread, which was already removed by
serial_process_irp_close
Retry read now, if non blocking IO returns EAGAIN.
2013-08-26 20:10:43 +02:00
Armin Novak
4a5b19e816 Fixed high CPU usage. 2013-08-26 20:10:43 +02:00
Armin Novak
605f956486 Fixed resource leaks and missing thread sync. 2013-08-26 20:10:43 +02:00
Armin Novak
86c0c02975 Fixed resource leaks. 2013-08-26 20:10:42 +02:00
Armin Novak
ac63b9ae56 Using WaitForMultipleObjects now to reduce CPU load. 2013-08-26 20:10:42 +02:00
Bernhard Miklautz
0e758fef5b Merge pull request #1394 from master 2013-08-26 20:07:35 +02:00
Armin Novak
eab49cf89f Added generated documentation files to gitignore. 2013-08-26 20:05:09 +02:00
Armin Novak
6e5e62da60 Replaced xmlto with xsltproc to word around a bug preventing manpages
to be build, when there are spaces in the build path.
2013-08-26 20:05:09 +02:00
Armin Novak
8be65019e6 Added missing dependency for custom command generating manpage. 2013-08-26 20:05:09 +02:00
Armin Novak
9281014e4d Removed placeholder for channel documentation until it is available. 2013-08-26 20:05:09 +02:00
Armin Novak
25eda0dce7 Added better formatting of commands in manpage. 2013-08-26 20:05:09 +02:00
Armin Novak
387a75d1ef Minor adjustments in naming. 2013-08-26 20:05:08 +02:00
Armin Novak
a5d1b98e5b Added examples with short description to manpage.
Added placeholder for channel documentation.
2013-08-26 20:05:08 +02:00
Armin Novak
e92eef9579 Fixed generation of configure header, now replacing date fields
with current date.
Added CMake script to generate a variable containing the current
date.
Removed last argument (the terminating NULL element) from output.
2013-08-26 20:05:08 +02:00
Armin Novak
75e2b2a5ef Removed extended text field. 2013-08-26 20:05:08 +02:00
Armin Novak
cb5ffc497a Added simple converter generating docbook XML from command line
argument struct.
Modified xfreerdp.1.xml to include generated documentation.
Modified CMake to regenerate manpages correctly using the generator.
2013-08-26 20:05:08 +02:00
Bernhard Miklautz
5166dfec6e Merge pull request #1415 from master 2013-08-26 20:02:57 +02:00
Armin Novak
28ee436374 Added links to documentation of message sequences for clipboard data exchange. 2013-08-26 20:01:59 +02:00
Armin Novak
c3dc19cc5f Fixed invalid package size allocation and calculation in cliprdr_process_format_list_event
Enabled and fixed error handling in cliprdr_process_format_list_response
2013-08-26 20:01:50 +02:00
Armin Novak
7015f83d97 Fixed #1404, using WaitForMultipleObjects now.
(cherry picked from commit 44c80c29cc)
2013-08-26 20:00:20 +02:00
Bernhard Miklautz
4bec3a6547 Merged pull request #1416 from master 2013-08-26 19:58:36 +02:00
Armin Novak
20cd361fc5 winpr config.h now generated in binary tree. 2013-08-26 19:58:16 +02:00
Armin Novak
93cb8fc11a Moved generated tables.c to binary folder. 2013-08-26 19:58:09 +02:00
Armin Novak
387f780414 Generating config.h now in binray tree. 2013-08-26 19:57:39 +02:00
Bernhard Miklautz
4fe23c0788 Merge pull request #1421 from master 2013-08-26 19:01:41 +02:00
Armin Novak
84d4ec009c Added error messages for not implemented functions. 2013-08-26 18:59:29 +02:00
Armin Novak
83e9adf30d Thread timed join now only available with _GUN_SOURCE. 2013-08-26 18:59:29 +02:00
Armin Novak
2863a55f5b Implemented timeouts for WaitForSingleObject.
Added assertions for functions not implemented.
2013-08-26 18:59:22 +02:00
Norbert Federa
df7311b4f9 codec/rfx: fix multithreaded encoder
Some component of the encoder chain (I suspect the rlgr encoder) expects
the output buffer to be zeroed. The multithreaded RemoteFX encoder uses
wStreams from the StreamPool which are reused and not zeroed out of
course. For now, in order to prevent data corruption we clear the stream.
(cherry picked from commit ccc5d1b279)
2013-08-26 15:31:34 +02:00
Bernhard Miklautz
d817ef3516 android: updated toolchain file
* support for ndk version r8d+
* improved x86_64 host machine support
* support non-release NDK layouts
(cherry picked from commit 553f7c24f7)
2013-08-26 15:29:07 +02:00
Norbert Federa
75f23925cd codec/rfx: added multithreaded encoder
(cherry picked from commit 0d916527bc)
2013-08-26 15:28:53 +02:00
Norbert Federa
41ce9a0969 libwinpr-utils: Use criticalsection with spincount
Use InitializeCriticalSectionAndSpinCount instead of IntializeCriticalSection.
Using spin counts for critical sections of short duration enables the calling
thread to avoid the wait operation in most situations which can dramatically
improve the overall performance on multiprocessor systems.

On Linux this change has no effect because the new winpr critical section
implementation does not use the SpinCount field under Linux because the NPTL
synchronization primitives are implemented using the extremely performant
futex system calls which have this magic already built in.

However, on Mac OS X this change improved the overall performance of the
multithreaded RemoteFX decoder by 25 percent.

I've used a SpinCount of 4000 which avoided 99 percent of the wait calls.
This value is also used by Microsoft's heap manager for its per-heap
critical sections.

Note: This change requires pull request #1397 to be merged.
(cherry picked from commit 3a58934eb2)
2013-08-26 15:27:32 +02:00
Norbert Federa
434ac77b83 libwinpr-sync: New complete critical section code
- Complete implementation including recursion support
- Added an intensive ctest (TestSynchCritical)
- Struct members are used exactly as Windows does it internally:
  LockCount starts at -1, RecursionCount at 0
- Same performance optimizations as internally on Windows:
    - Fast lock acquisition path using CAS -> SpinCount -> wait
    - SpinCount automatically disabled on uniprocessor systems
- On Linux SpinCount is disabled because it provided no advantage over NPTL/futex in all tests

Support for CRITICAL_SECTION's DebugInfo is not yet included (but trivial to add).
(cherry picked from commit 2b25b4a520)
2013-08-26 15:27:15 +02:00
Norbert Federa
47b17b5386 codec/rfx: removed unnecessary WaitForSingleObject
The WaitForSingleObject call on TilePool's event is called with a zero time-out
interval and the event is a manual reset event ... thus no locking or waiting
is involved anyways and Queue_Dequeue may very well return NULL independently
of calling WaitForSingleObject which is already correctly handled.
(cherry picked from commit 938a0890a3)
2013-08-26 15:26:22 +02:00
Armin Novak
d67624684f Added proper find_feature check for JPEG library.
(cherry picked from commit 4d13b27a02)
2013-08-26 15:26:15 +02:00
Bernhard Miklautz
deb9d2bc8e Merge pull request #1389 2013-08-26 14:53:40 +02:00
Armin Novak
b6d06ec012 Added libusb detection CMake script. 2013-08-26 14:41:47 +02:00
Armin Novak
c22b539153 Fixed linking with libusb, now linking against correct library. 2013-08-26 14:41:47 +02:00
Armin Novak
fe221cdc56 Now using libraries detected by CMake for linking against libusb. 2013-08-26 14:41:47 +02:00
Armin Novak
67808b8bc3 Added proper CMake checks for libraries linked with urbdrc 2013-08-26 14:41:47 +02:00
Norbert Federa
f25f2e6055 winpr: improve and fix locking for data structures
- Improved/completed(almost) winpr's critical section implementation
- Replaced WaitForSingleObject locking with critical sections

Note:
WaitForSingleObject should _never_ be used for granular low-contention
locks as it _always_ enters the kernel.

Just replacing WaitForSingleObject locking in Bufferpool with
EnterCriticalSection boosts the multithreaded rfx decoder
performance by almost 400% on win32.
(pull #1388 - cherry picked from commit 81ef251fc8)
2013-08-26 12:53:43 +02:00
Bernhard Miklautz
e5bdfc5eed Fixed typo(cherry picked from commit 558d40b18c) 2013-08-26 12:45:23 +02:00
Vic Lee
0fc2d1d1d2 libfreerdp-core/fastpath: fix memory leak when sending large packet.
(cherry picked from commit 21796ad73d)
2013-08-26 12:44:59 +02:00
Daryl Poe
eb2f3a02b4 apply glyph fragment offset after fragment 2013-08-20 12:00:36 -06:00
Marc-André Moreau
14d4adb901 Merge pull request #1403 from dpoe/stable-1.1
fix per-device CAL licensing
2013-08-14 06:59:50 -07:00
Daryl Poe
d6d0d81d08 fix per-device CAL licensing 2013-08-07 16:28:31 -06:00
Bernhard Miklautz
fd8fc31ce6 Merge pull request #1378 from dpoe/stable-1.1
careful with passwd in compatibility.c
2013-07-30 05:11:03 -07:00
Daryl Poe
a4a5baf0da careful with passwd in compatibility.c 2013-07-29 16:50:38 -06:00
Norbert Federa
aca7d9366e codec/rfx: removed unused queue TileQueue
Declared, created, deleted but not used:

    git grep TileQueue
    libfreerdp/codec/rfx_types.h: wQueue* TileQueue;
    libfreerdp/codec/rfx.c:       context->priv->TileQueue = Queue_New(TRUE, -1, -1);
    libfreerdp/codec/rfx.c:       Queue_Free(context->priv->TileQueue);
(cherry picked from commit 1d384ce863)
2013-07-29 20:31:08 +02:00
Bernhard Miklautz
f3019b2b40 Merge pull request #1373 from dpoe/stable-1.1
fix segfault due to pulse input race condition
2013-07-29 09:18:02 -07:00
Daryl Poe
be51676541 fix segfault due to pulse input race condition 2013-07-26 13:54:20 -06:00
Bernhard Miklautz
359c5d258e Merge pull request #1371 from dpoe/stable-1.1
cover the case of servers asking for cached bitmaps they have never defi...
2013-07-26 02:45:45 -07:00
Daryl Poe
46a691db02 cover the case of servers asking for cached bitmaps they have never defined 2013-07-25 15:01:56 -06:00
Marc-André Moreau
cd68f79519 channels/disp: remove RDP8.1 Preview MS-RDPEDISP support from 1.1 stable branch (will be included in next release) 2013-07-16 15:21:17 -04:00
3257 changed files with 206004 additions and 572033 deletions

View File

@ -1,124 +0,0 @@
---
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: ForIndentation
...
Language: Cpp
Standard: Auto
NamespaceIndentation: All
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
...
Language: ObjC
PointerBindsToType: false
SortIncludes: false
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
...
Language: Java
BreakAfterJavaFieldAnnotations: false
...
Language: JavaScript
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
...
Language: Proto
...
Language: TableGen
...
Language: TextProto
...

View File

@ -1,128 +0,0 @@
---
Checks: >
-*,
abseil-*,
altera-*,
boost-*,
bugprone-*,
cert-*,
clang-analyzer*,
concurrency-*,
cppcoreguidelines*,
google-*,
hicpp-*,
llvm-*,
modernize-*,
objc-*,
openmp-*,
performance-*,
portability-*,
readability-*,
-altera-id-dependent-backward-branch,
-altera-struct-pack-align,
-altera-unroll-loops,
-cppcoreguidelines-interfaces-global-init,
-bugprone-easily-swappable-parameters,
-bugprone-assignment-in-if-condition,
-bugprone-branch-clone,
-bugprone-macro-parentheses,
-cert-dcl16-c,
-cert-env33-c,
-cert-dcl50-cpp,
-clang-analyzer-optin.performance.Padding,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
-clang-analyzer-valist.Uninitialized,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-no-malloc,
-google-readability-braces-around-statements,
-google-readability-todo,
-hicpp-avoid-c-arrays,
-hicpp-braces-around-statements,
-hicpp-no-array-decay,
-hicpp-no-assembler,
-hicpp-multiway-paths-covered,
-hicpp-signed-bitwise,
-hicpp-uppercase-literal-suffix,
-hicpp-vararg,
-hicpp-no-malloc,
-llvm-header-guard,
-llvm-include-order,
-llvm-qualified-auto,
-llvm-else-after-return,
-readability-else-after-return,
-readability-avoid-nested-conditional-operator,
-modernize-use-trailing-return-type,
-modernize-return-braced-init-list,
-modernize-macro-to-enum,
-modernize-pass-by-value,
-modernize-avoid-c-arrays,
-readability-braces-around-statements,
-readability-convert-member-functions-to-static,
-readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-implicit-bool-conversion,
-readability-magic-numbers,
-readability-math-missing-parentheses,
-readability-misleading-indentation,
-readability-qualified-auto,
-readability-suspicious-call-argument,
-readability-string-compare,
-readability-uppercase-literal-suffix,
-performance-no-int-to-ptr,
-performance-avoid-endl
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: file
User: nin
CheckOptions:
- key: readability-implicit-bool-conversion.AllowIntegerConditions
value: 'true'
- key: llvm-else-after-return.WarnOnConditionVariables
value: 'false'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons
value: 'false'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: cert-err33-c.CheckedFunctions
value: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
value: 'false'
- key: cert-dcl16-c.NewSuffixes
value: 'L;LL;LU;LLU'
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: 'true'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: llvm-qualified-auto.AddConstToQualified
value: 'false'
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: llvm-else-after-return.WarnOnUnfixable
value: 'false'
- key: google-readability-function-size.StatementThreshold
value: '800'
...

View File

@ -1,28 +0,0 @@
## Found a bug? - We would like to help you and smash the bug away.
1. __Please don't "report" questions as bugs.__
* We are reachable via
* Matrix room : #FreeRDP:matrix.org (main)
* XMPP channel: #FreeRDP#matrix.org@matrix.org (bridged)
* IRC channel : #freerdp @ irc.oftc.net (bridged)
* We are reachable via mailing list <freerdp-devel@lists.sourceforge.net>
* Try our mailing list for discussions/questions
1. Before reporting a bug have a look into our issue tracker to see if the bug was already reported and you can add some additional information.
1. If it's a __new__ bug - create a new issue.
1. For more details see https://github.com/FreeRDP/FreeRDP/wiki/BugReporting
## To save time and help us identify the issue a bug report should at least contain the following:
* a useful description of the bug - "It's not working" isn't good enough - you must try harder ;)
* the steps to reproduce the bug
* command line you have used
* to what system did you connect to? (win8, 2008, ..)
* what did you expect to happen?
* what actually happened?
* freerdp version (e.g. xfreerdp --version) or package version or git commit
* freerdp configuration (e.g. xfreerdp --buildconfig)
* operating System, architecture, distribution e.g. linux, amd64, debian
* if you built it yourself add some notes which branch you have used, also your cmake parameters can help
* extra information helping us to find the bug
## Please remove this text before submitting your issue!
_Thank you for reporting a bug!_

View File

@ -1,7 +0,0 @@
---
name: Backport
about: Create a issue to request/track a backport
---
Releated pull request for master:

View File

@ -1,55 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Found a bug? - We would like to help you and smash the bug away.**
1. __Please don't "report" questions as bugs. For these (questions/build instructions/...) please use one of the following means of contact:__
* We are reachable via:
* Matrix room : #FreeRDP:matrix.org (main)
* XMPP channel: #FreeRDP#matrix.org@matrix.org (bridged)
* IRC channel : #freerdp @ irc.oftc.net (bridged)
* We are reachable via mailing list <freerdp-devel@lists.sourceforge.net>
* Try our mailing list for discussions/questions
1. Before reporting a bug have a look into our issue tracker to see if the bug was already reported and you can add some additional information.
1. If it's a __new__ bug - create a new issue.
1. For more details see https://github.com/FreeRDP/FreeRDP/wiki/BugReporting
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Application details**
* FreeRDP version (`xfreerdp /version`)
* Command line used
* Output of `xfreerdp /buildconfig`
* OS version connecting to (server side)
* If available the log output from a run with `/log-level:trace 2>&1 | tee log.txt`
* If you built it yourself add some notes which tag/commit/branch you have used, also your cmake parameters and
compiler can help
**Environment (please complete the following information):**
- OS: [e.g. Linux/Windows/Android/..]
- Version/Distribution: [e.g. Debian 10, Windows 2008, Android 10]
- Architecture: [amd64, arm]:
**Additional context**
Add any other context about the problem here.
** Please remove this text before submitting your issue!
_Thank you for reporting a bug!_

View File

@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,28 +0,0 @@
## This is how are pull requests handled by FreeRDP
1. Every new pull request needs to build and pass the unit tests at https://ci.freerdp.com
1. At least 1 (better two) people need to review and test a pull request and agree to accept
## Preparations before creating a pull
* Rebase your branch to current master, no merges allowed!
* Try to clean up your commit history, group changes to commits
* Check your formatting! A _clang-format_ script can be found at ```.clang-format```
* The cmake target ```clangformat``` reformats the whole codebase
* Optional (but higly recommended)
* Run a clang scanbuild before and after your changes to avoid introducing new bugs
* Run your compiler at pedantic level to check for new warnings
## To ease accepting your contribution
* Give the pull request a proper name so people looking at it have an basic idea what it is for
* Add at least a brief description what it does (or should do :) and what it's good for
* Give instructions on how to test your changes
* Ideally add unit tests if adding new features
## What you should be prepared for
* fix issues found during the review phase
* Joining our chat to talk to other developers or help them test your pull might accelerate acceptance
* Matrix room : #FreeRDP:matrix.org (main)
* XMPP channel: #FreeRDP#matrix.org@matrix.org (bridged)
* IRC channel : #freerdp @ irc.oftc.net (bridged)
* Joining our mailing list <freerdp-devel@lists.sourceforge.net> may be helpful too.
## Please remove this text before submitting your pull!

View File

@ -1,102 +0,0 @@
name: abi-checker
on:
workflow_dispatch:
branches: [ master, stable* ]
inputs:
API_BASE_REF:
description: 'Base revision for ABI compatibility check'
required: true
default: '3.0.0'
pull_request:
branches: [ master, stable* ]
schedule:
- cron: '30 4 * * SUN'
jobs:
build:
runs-on: ubuntu-latest
name: "Run ABI checker on ubuntu-latest"
steps:
- name: "Check out pull request"
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'pull_request' }}
uses: suzuki-shunsuke/get-pr-action@v0.1.0
id: pr
- name: "Check out source"
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{steps.pr.outputs.merge_commit_sha}}
- name: "Prepare environment"
run: |
sudo apt-get update -q -y
sudo apt-get install -q -y \
libxrandr-dev \
libxinerama-dev \
libusb-1.0-0-dev \
xserver-xorg-dev \
libswscale-dev \
libswresample-dev \
libavformat-dev \
libavutil-dev \
libavcodec-dev \
libcups2-dev \
libpulse-dev \
libasound2-dev \
libpcsclite-dev \
libxcb-cursor-dev \
libxcursor-dev \
libcairo2-dev \
libfaad-dev \
libjpeg-dev \
libgsm1-dev \
ninja-build \
libxfixes-dev \
libxkbcommon-dev \
libpkcs11-helper1-dev \
libwayland-dev \
libpam0g-dev \
libxdamage-dev \
libxcb-damage0-dev \
libxtst-dev \
libfuse3-dev \
libsystemd-dev \
libcairo2-dev \
libsoxr-dev \
libsdl2-dev \
libkrb5-dev \
libcjson-dev \
libsdl2-ttf-dev \
libwebkit2gtk-4.0-dev \
libopus-dev \
libwebp-dev \
libpng-dev \
libv4l-dev \
libjpeg-dev \
liburiparser-dev \
cmake \
clang \
abigail-tools \
pylint \
curl
- name: "Prepare configuration"
run: |
mkdir -p abi-checker
cp ci/cmake-preloads/config-abi.txt abi-checker/
cp scripts/abi-suppr.txt abi-checker/
curl https://gist.githubusercontent.com/akallabeth/aa35caed0d39241fa17c3dc8a0539ea3/raw/ef12f8c720ac6be51aa1878710e2502b1b39cf4c/check-abi -o abi-checker/check-abi
chmod +x abi-checker/check-abi
echo "GITHUB_BASE_REF=$GITHUB_BASE_REF"
echo "GITHUB_HEAD_REF=$GITHUB_HEAD_REF"
echo "API_BASE_REF=${{ inputs.API_BASE_REF || '3.0.0' }}"
echo "HEAD=$(git rev-parse HEAD)"
echo "remotes=$(git remote -v)"
- name: "Run ABI check..."
env:
BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'workflow_dispatch' && inputs.API_BASE_REF || '3.0.0' }}
run: |
echo "BASE_REF=$BASE_REF"
./abi-checker/check-abi -s abi-checker/abi-suppr.txt --parameters="-Cabi-checker/config-abi.txt" $BASE_REF $(git rev-parse HEAD)

View File

@ -1,101 +0,0 @@
name: '[arm,ppc,ricsv] architecture builds'
on:
workflow_dispatch:
branches: [ master, stable* ]
schedule:
- cron: '30 5 * * SUN'
jobs:
build_job:
runs-on: ubuntu-latest
name: "Test on ${{ matrix.distro }}/${{ matrix.arch }}"
strategy:
fail-fast: false
matrix:
include:
- arch: armv6
distro: bullseye
- arch: armv7
distro: bullseye
- arch: aarch64
distro: bullseye
- arch: s390x
distro: bullseye
- arch: ppc64le
distro: bullseye
- arch: riscv64
distro: ubuntu22.04
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2.8.1
name: "Run tests"
id: build
with:
arch: ${{ matrix.arch }}
distro: ${{ matrix.distro }}
githubToken: ${{ github.token }}
env: |
CTEST_OUTPUT_ON_FAILURE: 1
WLOG_LEVEL: 'trace'
install: |
apt-get update -q -y
apt-get install -q -y \
libxrandr-dev \
libxinerama-dev \
libusb-1.0-0-dev \
xserver-xorg-dev \
libswscale-dev \
libswresample-dev \
libavutil-dev \
libavcodec-dev \
libcups2-dev \
libpulse-dev \
libasound2-dev \
libpcsclite-dev \
libxcb-cursor-dev \
libxcursor-dev \
libcairo2-dev \
libfaad-dev \
libgsm1-dev \
ninja-build \
libxfixes-dev \
libxkbcommon-dev \
libxkbfile-dev \
libwayland-dev \
libpam0g-dev \
libxdamage-dev \
libxcb-damage0-dev \
libxtst-dev \
libfuse3-dev \
libsystemd-dev \
libsoxr-dev \
libsdl2-dev \
libsdl2-ttf-dev \
libsdl2-image-dev \
libkrb5-dev \
libcjson-dev \
libpkcs11-helper1-dev \
libwebkit2gtk-4.0-dev \
libopus-dev \
libwebp-dev \
libpng-dev \
libjpeg-dev \
liburiparser-dev \
libssl-dev \
opensc-pkcs11 \
libv4l-dev \
cmake \
clang
run: |
cmake -GNinja \
-C ci/cmake-preloads/config-linux-all.txt \
-B ci-build \
-S . \
-DCMAKE_INSTALL_PREFIX=/tmp/ci-test \
-DCMAKE_C_COMPILER=/usr/bin/clang \
-DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
-DUSE_UNWIND=OFF \
-DUSE_EXECINFO=OFF \
-DWITH_SANITIZE_ADDRESS=OFF
cmake --build ci-build --parallel $(nproc) --target install
cmake --build ci-build --parallel $(nproc) --target test

View File

@ -1,24 +0,0 @@
name: Post clang-tidy review comments
on:
workflow_run:
workflows: ["clang-tidy-review"]
types:
- completed
permissions:
pull-requests: write
issues: write
checks: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: ZedThree/clang-tidy-review/post@v0.19.0
# lgtm_comment_body, max_comments, and annotations need to be set on the posting workflow in a split setup
with:
token: ${{ secrets.GITHUB_TOKEN }}
annotations: false
max_comments: 10

View File

@ -1,27 +0,0 @@
name: clang-tidy-review
on:
pull_request:
branches: [ master, stable* ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Run clang-tidy
- uses: ZedThree/clang-tidy-review@v0.19.0
id: review
with:
split_workflow: true
clang_tidy_checks: ''
# List of packages to install
apt_packages: libkrb5-dev,libxkbcommon-dev,libxkbfile-dev,libx11-dev,libwayland-dev,libxrandr-dev,libxi-dev,libxrender-dev,libxext-dev,libxinerama-dev,libxfixes-dev,libxcursor-dev,libxv-dev,libxdamage-dev,libxtst-dev,libcups2-dev,libcairo2-dev,libpcsclite-dev,libasound2-dev,libswscale-dev,libpulse-dev,libavformat-dev,libavcodec-dev,libavutil-dev,libfuse3-dev,libswresample-dev,libusb-1.0-0-dev,libudev-dev,libdbus-glib-1-dev,libpam0g-dev,uuid-dev,libcjson-dev,libsdl2-2.0-0,libsdl2-dev,libsdl2-ttf-dev,libsdl2-image-dev,libsystemd-dev,liburiparser-dev,libopus-dev,libwebp-dev,libjpeg-dev,libpng-dev,libgsm1-dev,libfaac-dev,libfaad-dev,libsoxr-dev,opencl-c-headers,opencl-headers,ocl-icd-opencl-dev,libssl-dev,libv4l-dev
# CMake command to run in order to generate compile_commands.json
build_dir: tidy
cmake_command: cmake -Btidy -S. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DWITH_DEBUG_ALL=ON -DWITH_INTERNAL_MD4=ON -DWITH_INTERNAL_MD5=ON -DWITH_INTERNAL_RC4=ON -DBUILD_TESTING=ON -DWINPR_UTILS_IMAGE_JPEG=ON -DWINPR_UTILS_IMAGE_PNG=ON -DWINPR_UTILS_IMAGE_WEBP=ON -DWITH_BINARY_VERSIONING=ON -DWITH_CAIRO=ON -DWITH_DSP_EXPERIMENTAL=ON -DWITH_FAAC=ON -DWITH_FAAD2=ON -DWITH_FREERDP_DEPRECATED=ON -DWITH_FREERDP_DEPRECATED_COMMANDLINE=ON -DWITH_GSM=ON -DWITH_OPUS=ON -DWITH_PROXY_EMULATE_SMARTCARD=ON -DWITH_PULSE=ON -DWITH_SMARTCARD_INSPECT=ON -DWITH_SOXR=ON -DWITH_UNICODE_BUILTIN=ON -DWITH_VAAPI=ON -DWITH_WINPR_DEPRECATED=ON -DWITH_SDL_IMAGE_DIALOGS=ON -DWITH_PROFILER=ON -DWITH_OPENCL=ON -DCHANNEL_TSMF=ON -DWITH_WEBVIEW=OFF
# Uploads an artefact containing clang_fixes.json
- uses: ZedThree/clang-tidy-review/upload@v0.19.0
id: upload-review

View File

@ -1,141 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
workflow_dispatch:
branches: [ master, stable* ]
push:
branches: [ "master", "stable*" ]
pull_request:
branches: [ "master", "stable*" ]
schedule:
- cron: '41 2 * * 2'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
# required for all workflows
security-events: write
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: c-cpp
build-mode: manual
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
run: |
sudo apt update
sudo apt install \
libxrandr-dev \
libxinerama-dev \
libusb-1.0-0-dev \
xserver-xorg-dev \
libswscale-dev \
libswresample-dev \
libavformat-dev \
libavutil-dev \
libavcodec-dev \
libcups2-dev \
libv4l-dev \
libpulse-dev \
libasound2-dev \
libpcsclite-dev \
libxcb-cursor-dev \
libxcursor-dev \
libcairo2-dev \
libfaac-dev \
libfaad-dev \
libjpeg-dev \
libgsm1-dev \
ninja-build \
libxfixes-dev \
libxkbcommon-dev \
libwayland-dev \
libpam0g-dev \
libxdamage-dev \
libxcb-damage0-dev \
ccache \
libxtst-dev \
libfuse3-dev \
libsystemd-dev \
libcairo2-dev \
libsoxr-dev \
libsdl2-dev \
libkrb5-dev \
libcjson-dev \
libsdl2-ttf-dev \
libsdl2-image-dev \
libwebkit2gtk-4.0-dev \
clang \
libopus-dev \
libwebp-dev \
libpng-dev \
libjpeg-dev \
liburiparser-dev
mkdir ci-build
cd ci-build
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
export CFLAGS="-Weverything"
export CXXFLAGS="-Weverything"
cmake -GNinja ../ci/cmake-preloads/config-linux-all.txt ..
cmake --build .
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -1,59 +0,0 @@
name: Coverity
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
branches: [ master, stable* ]
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'FreeRDP' }}
steps:
- uses: actions/checkout@v4
- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
devscripts \
ninja-build \
equivs \
ccache \
clang
sudo mk-build-deps --install packaging/deb/freerdp-nightly/control
- name: Download Coverity build tool
run: |
wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=FreeRDP" -O coverity_tool.tar.gz
mkdir coverity_tool
tar xzf coverity_tool.tar.gz --strip 1 -C coverity_tool
- name: Build with Coverity build tool
run: |
export PATH=`pwd`/coverity_tool/bin:$PATH
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
cov-configure --template --compiler clang --comptype clangcc
# in source build is used to help coverity to determine relative file path
cmake \
-GNinja \
-C ci/cmake-preloads/config-coverity.txt \
-DCOVERITY_BUILD=ON \
-Bcov-build \
-S.
cov-build --dir cov-int cmake --build cov-build
- name: Submit build result to Coverity Scan
run: |
tar czvf cov.tar.gz cov-int
curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \
--form email=team+coverity@freerdp.com \
--form file=@cov.tar.gz \
--form version="Commit $GITHUB_SHA" \
--form description="Build submitted via CI" \
https://scan.coverity.com/builds?project=FreeRDP

View File

@ -1,45 +0,0 @@
name: Fuzzing testing
on:
workflow_dispatch:
branches: [ master, stable* ]
pull_request:
branches: [ master, stable* ]
schedule:
- cron: "0 3 21 * *"
jobs:
fuzzing:
if: github.repository == 'FreeRDP/FreeRDP'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer: [address, undefined]
steps:
- uses: actions/checkout@v4
- name: Build fuzzers (${{ matrix.sanitizer }})
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'freerdp'
dry-run: false
sanitizer: ${{ matrix.sanitizer }}
- name: Run fuzzers (${{ matrix.sanitizer }})
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'freerdp'
fuzz-seconds: 600
dry-run: false
sanitizer: ${{ matrix.sanitizer }}
- name: Upload crash
uses: actions/upload-artifact@v4.3.6
if: failure() && steps.build.outcome == 'success'
with:
name: ${{ matrix.sanitizer }}-artifacts
retention-days: 21
path: ./out/artifacts

View File

@ -1,29 +0,0 @@
name: Close inactive issues
on:
workflow_dispatch:
schedule:
- cron: "33 3 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-stale: 30
days-before-close: 30
operations-per-run: 90
exempt-all-milestones: true
exempt-assignees: true
exempt-issue-labels: "wip,pinned,help-wanted,blocker,feature"
exempt-pr-labels: "wip,pinned,help-wanted,blocker,feature"
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,31 +0,0 @@
name: mingw-builder
on:
workflow_dispatch:
branches: [ master, stable* ]
schedule:
- cron: '30 5 * * SUN'
jobs:
build:
runs-on: ubuntu-latest
name: "Run mingw build on ubuntu-latest"
steps:
- name: "Check out source"
uses: actions/checkout@v4
- name: "Prepare environment"
run: |
sudo apt-get update -q -y
sudo apt-get install -q -y \
git \
nasm \
meson \
cmake \
ninja-build \
mingw-w64 \
mingw-w64-tools \
binutils-mingw-w64
- name: "Run mingw build..."
run: |
./scripts/mingw.sh

View File

@ -1,61 +0,0 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: timezone-update
on:
workflow_dispatch:
branches: [ master, stable* ]
schedule:
- cron: "0 5 11 * *"
jobs:
build:
runs-on: windows-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Configure CMake
run: cmake -G"Visual Studio 17 2022" -Bbuild -Swinpr\libwinpr\timezone\utils
- name: Restore dependencies
run: dotnet restore build\tzextract.sln
- name: Build & Install CMake
run: cmake --build build --config Release
- name: Update timezones
run: build\Release\tzextract.exe winpr\libwinpr\timezone
- name: Format code
run: |
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/WindowsZones.c
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/TimeZoneNameMap.c
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/TimeZoneNameMap_static.h
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/TimeZoneNameMap.json
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v6
with:
commit-message: Update timezone definitions
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com>
signoff: false
branch: timezone-patches
branch-suffix: timestamp
delete-branch: true
title: '[timezones] Update definitions'
body: |
Timezone update
- Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
labels: |
automated pr
assignees: akallabeth
reviewers: akallabeth
draft: false

114
.gitignore vendored Normal file → Executable file
View File

@ -1,5 +1,109 @@
**/CMakeCache.txt
**/CMakeFiles
build
checker
abi-checker
# CMake
CMakeFiles/
CMakeScripts/
CMakeCache.txt
config.h
install_manifest*.txt
CTestTestfile.cmake
freerdp.pc
Makefile
Testing
cmake_install.cmake
CPackConfig.cmake
CPackSourceConfig.cmake
DartConfiguration.tcl
_CPack_Packages
external/*
!external/README
include/freerdp/version.h
# Packages
*.zip
*.exe
*.sh
*.deb
*.rpm
*.tar.Z
*.tar.gz
# Eclipse
*.project
*.cproject
*.settings
# .rdp files
*.rdp
*.RDP
# Documentation
docs/api
client/X11/xfreerdp.1
client/X11/xfreerdp.1.xml
client/X11/xfreerdp-channels.1.xml
client/X11/xfreerdp-examples.1.xml
# Mac OS X
.DS_Store
*.xcodeproj/
DerivedData/
# iOS
FreeRDP.build
Debug-*
Release-*
# Windows
*.vcxproj
*.vcxproj.*
*.vcproj
*.vcproj.*
*.sdf
*.sln
*.suo
*.ncb
*.opensdf
Thumbs.db
ipch
Debug
RelWithDebInfo
*.lib
*.exp
*.pdb
*.dll
*.ilk
*.resource.txt
*.embed.manifest*
*.intermediate.manifest*
# Binaries
*.a
*.so
*.so.*
*.dylib
bin
libs
cunit/test_freerdp
client/X11/xfreerdp
client/Mac/xcode
client/Sample/sfreerdp
client/DirectFB/dfreerdp
server/Sample/sfreerdp-server
server/X11/xfreerdp-server
xcode
# Other
*~
*.dir
Release
Win32
build*/
*.orig
default.log
*Amplifier XE*
*Inspector XE*
*.cbp
*.txt.user
*.autosave

View File

@ -1,102 +0,0 @@
# Generate .txt license file for CPack (PackageMaker requires a file extension)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/LICENSE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt @ONLY)
# Workaround to remove c++ compiler macros and defines for Eclipse.
# If c++ macros/defines are set __cplusplus is also set which causes
# problems when compiling freerdp/jni. To prevent this problem we set the macros to "".
if (ANDROID AND CMAKE_EXTRA_GENERATOR STREQUAL "Eclipse CDT4")
set(CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS "")
message(STATUS "Disabled CXX system defines for eclipse (workaround).")
endif()
set(CPACK_SOURCE_IGNORE_FILES "/\\\\.git/;/\\\\.gitignore;/CMakeCache.txt")
if(NOT WIN32)
if(APPLE AND (NOT IOS))
if(WITH_SERVER)
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} "mfreerdp-server")
endif()
endif()
if(WITH_X11)
set(CPACK_PACKAGE_EXECUTABLES "xfreerdp")
if(WITH_SERVER)
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} "xfreerdp-server")
endif()
endif()
endif()
set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_TOPLEVEL_TAG "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_PROJECT_NAME_lower)
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME_lower}-${FREERDP_VERSION_FULL}-${CPACK_SYSTEM_NAME}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME_lower}-${FREERDP_VERSION_FULL}-${CPACK_SYSTEM_NAME}")
set(CPACK_PACKAGE_NAME "FreeRDP")
set(CPACK_PACKAGE_VENDOR "FreeRDP")
set(CPACK_PACKAGE_VERSION ${FREERDP_VERSION_FULL})
set(CPACK_PACKAGE_VERSION_MAJOR ${FREERDP_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${FREERDP_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${FREERDP_VERSION_REVISION})
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "FreeRDP: A Remote Desktop Protocol Implementation")
set(CPACK_PACKAGE_CONTACT "Marc-Andre Moreau")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "marcandre.moreau@gmail.com")
set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set(CPACK_PACKAGE_INSTALL_DIRECTORY "FreeRDP")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt")
set(CPACK_NSIS_MODIFY_PATH ON)
set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/resources\\\\FreeRDP_Install.bmp")
set(CPACK_NSIS_MUI_ICON "${PROJECT_SOURCE_DIR}/resources\\\\FreeRDP_Icon_96px.ico")
set(CPACK_NSIS_MUI_UNICON "${PROJECT_SOURCE_DIR}/resource\\\\FreeRDP_Icon_96px.ico")
set(CPACK_COMPONENTS_ALL client server libraries headers symbols tools)
if(MSVC)
string(FIND ${CMAKE_MSVC_RUNTIME_LIBRARY} "DLL" IS_SHARED)
if(NOT IS_SHARED STREQUAL "-1")
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT libraries)
endif()
endif()
set(CPACK_COMPONENT_CLIENT_DISPLAY_NAME "Client")
set(CPACK_COMPONENT_CLIENT_GROUP "Applications")
set(CPACK_COMPONENT_SERVER_DISPLAY_NAME "Server")
set(CPACK_COMPONENT_SERVER_GROUP "Applications")
set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries")
set(CPACK_COMPONENT_LIBRARIES_GROUP "Runtime")
set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "Headers")
set(CPACK_COMPONENT_HEADERS_GROUP "Development")
set(CPACK_COMPONENT_SYMBOLS_DISPLAY_NAME "Symbols")
set(CPACK_COMPONENT_SYMBOLS_GROUP "Development")
set(CPACK_COMPONENT_TOOLS_DISPLAY_NAME "Tools")
set(CPACK_COMPONENT_TOOLS_GROUP "Applications")
set(CPACK_COMPONENT_GROUP_RUNTIME_DESCRIPTION "Runtime")
set(CPACK_COMPONENT_GROUP_APPLICATIONS_DESCRIPTION "Applications")
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION "Development")
configure_file("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"
"${PROJECT_BINARY_DIR}/CMakeCPackOptions.cmake" @ONLY)
set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CMakeCPackOptions.cmake")
include(CPack)

View File

@ -1,10 +0,0 @@
# This file is configured at cmake time, and loaded at cpack time.
# To pass variables to cpack from cmake, they must be configured in this file.
if("${CPACK_GENERATOR}" STREQUAL "PackageMaker")
if(CMAKE_PACKAGE_QTGUI)
set(CPACK_PACKAGE_DEFAULT_LOCATION "/Applications")
else()
set(CPACK_PACKAGE_DEFAULT_LOCATION "/usr")
endif()
endif()

970
CMakeLists.txt Normal file → Executable file

File diff suppressed because it is too large Load Diff

619
ChangeLog
View File

@ -1,503 +1,118 @@
# 2024-10-21 Version 3.9.0
We're proud to present the newest release of FreeRDP.
This one brings some major code cleanup (we've addressed lots of clang-tidy
warnings) as well as some highly anticipated new features.
We also did update the API documentation quite a bit (still incomplete though,
help always welcome ;))
So, what is new:
* Support for RDPEAR (remote credential guard) /remoteGuard option for non windows clients
* Global configuration file support, allowing to configure certificate
accept/ignore/... default settings for all users
* Simplified manpage generation, eliminates docbook and xmlto dependencies
speeding up builds
* New API for client channels to run tasks on RDP thread
* New extended transport layer API
* RDPECAM MJPEG support
* the first updates of timezone definitions from our automated ci
Noteworthy changes:
* Fix bugs in SSE4.1/AVX2 image copy (#10720)
* Add warnings for invalid monitor settings during connect (#10672)
* Fix ALSA microphone support (#10664)
* Fix modal windows in RAILS mode (#10629)
* Update experimental SDL3 client (SDL3 API should now have been stabilized,
various pull requests)
* Fix keyboard layouts, the external JSON did miss a few (#10639)
For a complete and detailed change log since the last release run:
git log 3.9.0...3.8.0
# 2024-08-30 Version 3.8.0
This is a bugfix release. Due to additional exports required by a bugfix the minor version was incremented
Noteworthy changes:
* Reduce number of warnings on CI build (make dependency includes SYSTEM) (#10509)
* Fix possible crashes with P11 certificate parsing (#10462, #10463)
* Various clipboard related fixes (#10472, #10476, #10477, #10480, #10484)
* Fix a race condition on DesktopResize (xfreerdp) (#10488)
* Improve certificate warnings (#10489)
* Try all possible resolved IP addresses for a DNS name on connect (#10491)
* Fix an issue with GFX SolidFill alpha data (#10498)
* Various fixes for SDL clients (#10504, #10492, #10471)
* Fix serial and parallel redirection crashes (#10510)
* Fix android build issues with NDK 27 (#10529)
* Improve performance of some WinPR primitives (#10528)
* Fix an issue with autoreconnect (#10523)
* Support ssh-askpass like password reading (#10516)
* Lots of code cleanups to reduce clang-tidy warnings (#10531, #10525, #10521, #10520, #10519, #10518)
For a complete and detailed change log since the last release run:
git log 3.8.0...3.7.0
# 2024-08-08 Version 3.7.0
This release has accumulated quite a number of changes. Along bugfixes for 3.6.3 it also
contains a number of improvements for distributors:
* Support for FDK-AAC for sound and microphone redirection (activate with -DWITH_FDK_AAC=ON build option)
This allows enabling the AAC compression that do not ship faad2 and/or faac
* Support keyboard layouts as JSON resources (activate with -DWITH_KEYBOARD_LAYOUT_FROM_FILE=ON build option,
also requires JSON support)
This allows editing keyboard layouts for existing releases should the need arise
* Support timezones as JSON resources (activate with -DWITH_TIMEZONE_FROM_FILE=ON -DWITH_TIMEZONE_COMPILED=OFF build options,
also requires JSON support)
Allows reading the mapping between IANA and windows timezones from a JSON file, allowing easier updates without recompile
* Improve shadow server compatibility with windows 11 24H2 RDP client
Windows 7 RFX and bitmap updates with multiple rectangles have been deactivated, so adjust shadow to not send such.
Noteworthy changes:
* Fix ActionScript paramter (#10423)
* Support keyboard layouts as JSON resource (#10394)
* Support timezones as JSON resource and command line argument (#10428 #10393 #10391)
* Deactivate AsyncUpdate (#10402)
* Compatibility fixes for shadow with windows 11 24H2 (#10455 #10422 #10420 #10416)
* Fix SDL client autoreconnect (#10390)
* Fix xfreerdp clipboard locking (#10385)
* Improve logging (#10426 #10441)
* Improve warnings and code checks (#10381 #10401 #10403 #10405 #10406 #10410 #10421 #10454)
* Support FDK-AAC (#10372)
* Fix drive redirection state transitions (#10367 #10374)
* Support mth:// routing token (#10366)
* Ignore unsupported SetThreadPriority (#10363)
* Fix reported documentation and code typos (#10365 #10368 #10370 #10369 #10431 #10446)
For a complete and detailed change log since the last release run:
git log 3.7.0...3.6.3
# 2024-07-08 Version 3.6.3
Bugfix release for 3.6.2 issues reported
Noteworthy changes:
* fix a graphics regression (#10352)
* workaround for a protocol bug of older FreeRDP based servers (#10358)
* fix possible NULL dereference in command line parser (#10348)
* fix intrinsics detection (#10346, #10350)
For a complete and detailed change log since the last release run:
git log 3.6.3...3.6.2
# 2024-07-04 Version 3.6.2
Bugfix release for 3.6.1 issues detected during release tests
Noteworthy changes:
* Fix xfreerdp and sdl-freerdp manpage names (accidentally changed name)
* Fix crash of wfreerdp
For a complete and detailed change log since the last release run:
git log 3.6.2...3.6.1
# 2024-07-04 Version 3.6.1
Bugfix release for 3.6.0
Noteworthy changes:
* Fix missing dependency for ci abi-checker
* Fix build WITH_SSE2/WITH_NEON: only enable support if the compiler
also defines symbols that suggest support.
* Fix incomplete changelog for 3.6.0:
* Improved image copy (#10208)
* Experimental [MS-RDPECAM] support by @oleg0421 (#10258)
* Improved primitives (#10304)
* Connection timeout for HTTP gateway transport (#10288)
For a complete and detailed change log since the last release run:
git log 3.6.1...3.6.0
# 2024-07-03 Version 3.6.0
With this release we did improve decoder speed so you should notice a significant
speed improvement with progressive and other gfx codecs.
We've also eliminated a couple of issues along the way, so an update
is highly recommended.
Noteworthy changes:
* Improved command line failure logging (#10333)
* p11-kit support (#10081)
* json-c support (#10183)
* (experimental) SDL3 port SDL client (#10195)
* New option '/gfx:frame-ack:off' for connection delay testing (#10214)
* improved decoder speed (#10222, #10235)
* xfreerdp floatbar hide bug (#10237)
* winpr-makecert month bug (#10236)
* kerberos kdcUrl check fixes (#10238)
* timezone updates (#10120, #10144, #10170)
* fixed a capability protocol violation bug (#10132)
* fix SDL client dialog bug terminating on credential dialog (#10134)
* some more oss-fuzz issues (#10126, #10141, #10148, #10161, #10239)
* rails popup window fixes (#10160)
For a complete and detailed change log since the last release run:
git log 3.6.0...3.5.1
# 2024-04-22 Version 3.5.1
This release eliminates a bunch of issues detected during oss-fuzz runs.
The test coverage was increased and detected issues eliminates, so an update
is highly recommended.
Noteworthy changes:
* Lots of fixes for oss-fuzz reports
* Timezone detection fixes (#10106)
* SDL key remapping support (#10103)
* Improved help (#10099)
* FreeBSD epoll detection fix (#10097)
For a complete and detailed change log since the last release run:
git log 3.5.1...3.5.0
# 2024-04-16 Version 3.5.0
This release focus is on squashing bugs.
The improved test coverage and ci builds revealed a number of previously
unnoticed issues we have addressed and we also got a report from
Evgeny Legerov of Kaspersky Lab identifying a number of out of bound reads
in decoder components and one very nasty out of bound write.
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:
* location channel support #9981, #9984, #10065
* bugfixes for report from Evgeny Legerov of Kaspersky Lab #10077
* fuzzer tests from Evgeny Legerov of Kaspersky Lab #10078
* bugfixes for coverty scanner #10066, #10068, #10069, #10070, #10075
* clipboard and generic locking fixes #10076
* split autoreconnect support from enabling it #10063
* various nightly and workflow fixes #10064, #10058, #10062
* always set wm-class to app_id #10051
* refactored and simplified CMake #10046, #10047
* fix relative mouse event sending #10010
* improve and unify check for APIs used (POSIX, win32, mac, ...) #9995
* fix termination for gateway connections #9985
* fix drivestoredirect RDP file setting, ignore invalid #9989
* drop IPP support #10038
For a complete and detailed change log since the last release run:
git log 3.5.0...3.4.0
# 2024-03-14 Version 3.4.0
This release concentrates on improving test coverage and ci builds.
Some usability issues and inconvenient API functions were fixed on the way.
New features have been introduced (stub for location channel)
Noteworthy changes:
* fix a bug in RAIL mode not activating window focus (#9973)
* improve logging (#9969, #9943)
* OpenSSL <= 1.1.1 build fixes (#9897)
* improved help (#9899, #9905)
* improved MINGW support (#9914, #9915, #9919, #9964, #9965, #9920)
* fix right control ungrab for xfreerdp (#9960)
* fix RPATH option settings (#9963)
* fix SDL client screen updates (#9962, #9954)
* fix issues with childSession under windows (#9961, #9956, #9922)
* fix xfreerdp crash with +auth-only (#9947)
* fix windows printer channel (#9934)
* add support to enforce gateway policy (#9942)
* improve big endian support (#9927)
* ignore empty proxy environment variables (#9929)
* improve quoting support for command line (#9912)
For a complete and detailed change log since the last release run:
git log 3.4.0...3.3.0
# 2024-02-22 Version 3.3.0
This release concentrates on code cleanup and overall quality improvements.
Some usability issues and inconvenient API functions were fixed on the way.
New features have been introduced (better image clipboard) but that stays
deactivated by default as we´re in a stable series.
Check the new CMake options:
* PLUGIN_ABS_PATHS_DEFAULT disables loading of external channels from all
but a specified absolute plugin directory defined by FREERDP_PLUGIN_PATH
* WINPR_UTILS_IMAGE_PNG enables PNG support with libpng in winpr image/clipboard
* WITH_LODEPNG enables PNG support with lodepng library in winpr image/clipboard
* WINPR_UTILS_IMAGE_WEBP enables WEBP support in winpr image/clipboard
* WINPR_UTILS_IMAGE_JPEG enables JPEG support in winpr image/clipboard
* USE_EXECINFO enables or disables backtrace support with execinfo
* WITH_WEBVIEW now defaults to OFF on windows, apple and android (not implemented)
Noteworthy changes:
* Improved image clipboard (xfreerdp, wlfreerdp) (#9873, #9826)
* Improved SDL client (#9875, #9887, #9883, #9878, #9792)
* Allow plugin loader to only use absolute paths (#9809)
* Improved TLS channel binding (#9838)
* Add GCC/clang attribute malloc wrapper WINPR_ATTR_MALLOC (#9863)
* Major clang-tidy code cleanups and bugfixes (#9799, #9834)
* Provide some defaults for wObject functions (#9799)
* Fix a bug in shadow with GFX breaking mstsc (#9818)
* Improved manpages and help (#9813, #9804)
* Blocking mode via transport IO interface (#9793)
For a complete and detailed change log since the last release run:
git log 3.3.0...3.2.0
# 2024-01-19 Version 3.2.0
This release mostly addresses issues reported since the last release.
Fixing some usablity and build issues as well as adding API functions
that are needed from external projects
Noteworthy changes:
* Fix proxy module load check (#9777)
* Improve kerberos error logging (#9771)
* Improve mac client keyboard handling (#9767)
* Add option to run client dynamic channel synchronous (#9764)
* Move huge struct to heap (#9763)
* Improved failure logging of license module (#9759)
* Improve server side gfx logging (#9757)
* Print shadow server help with printf instead of WLog (#9756)
* Fix SDL client timer initialization (#9754)
* Fix server peer message parsing (#9751)
* Enable NEON instructions if __ARM_NEON is defined (#9748)
* Add new proxy config file option TlsSecLevel (#9741)
* Improve android and mac os build scripts (#9735)
* Do not disable wayland support on BSD (#9730)
* Fix issues with assistance file parsing (#9727, #9728)
* Keyboard handling fixes for wayland client (#9725)
* Fix relative pkg-config file paths (#9720)
* Add new transport IO callback GetPublicKey (#9719)
* Fix wayland client scaling (#9715)
For a complete and detailed change log since the last release run:
git log 3.2.0...3.1.0
# 2023-12-22 Version 3.1.0
A new 3.1.0 minor release for the new 3.0.0 series.
This contains bugfixes, adds (better) support for libressl and mbedtls and
brings a bunch of improvements for the SDL client.
This comes with a price though, we now (optionally) require SDL_image if you
want to build the sdl-client
Since there are multiple new features, some new files (man pages) and new
optional dependencies we´ve directly incremented the minor version.
New CMake options:
* SDL_USE_COMPILED_RESOURCES (default ON) builds fonts and images into SDL
client. Set to OFF to install these resources as files. (was already part of
3.0.0, but worth mentioning here)
* WITH_SDL_IMAGE_DIALOGS (default OFF) Show some nice icons for SDL client
connection dialogs. Requires SDL_image for build.
* WITH_BINARY_VERSIONING (default OFF) Similar as for libraries the binaries,
manpages and resource locations created by FreeRDP project are postfixed
with the API version. Recommended if packagers want to install the package
alongside FreeRDP 2 without conflicts.
* RDTK_FORCE_STATIC_BUILD (default OFF) Build and link RDTK statically into
shadow server. Recommended for packagers as this library is not really used
outside of FreeRDP-shadow.
* UWAC_FORCE_STATIC_BUILD (default OFF) Build and link UWAC statically into
wlfreerdp. Recommended for packagers as this library is not really used
outside of wlfreerdp.
Noteworthy changes:
* Fix a nasty bug with relative mouse movement (#9677)
* LibreSSL support enhancements (#9691, #9670)
* mbedTLS support enhancements (#9662)
* Improve building on mac OS (#9641)
* New and improved manpages (#9690, #9650)
* Unify CMake common options, add (optional) binary versioning and allow
building rdtk and uwac as static dependencies (#9695)
* SDL client improvements (#9693, #9657, #9659, #9683, #9680, #9657, #9664,
#9656)
For a complete and detailed change log since the last release run:
git log 3.1.0...3.0.0
# 2023-12-12 Version 3.0.0
Final 3.0.0 release just a little over two weeks after the last 3.0.0-rc0.
This contains bugfixes, drops some legacy code, implements a small feature
request and adds some improvements to the build system.
Most notably is the new PreventInSourceBuilds.cmake which does exactly what
the name implies, it aborts builds where source equals build directory.
If you can not use out of source tree builds for some reason, you can
circumvent this measure with the CMake setting -DALLOW_IN_SOURCE_BUILD=ON
Noteworthy changes:
* add support for AF_VSOCK #9561
* xfreerdp drop X11 GDI implementation #9492
* fixed connection freeze with childSession #9594
* fixed relative mouse input issues #9608
* fixed issues with drive redirection #9610
* simplified mac build #9601
* fixed TSMF to build again #9603
* fixed command line /gfx parsing bug #9598
* prevent in source tree build #9550
* fixed various issues with settings #9595, #9596
* add E2K cpu support in WinPR #9599
* fixed wfreerdp DPI settings when used as embedded window #9593
* android add mouse hover support #9495
For a complete and detailed change log since the last release run:
git log 3.0.0..3.0.0-rc0
# 2023-11-27 Version 3.0.0-rc0
Nearly 2 months of testing, bugfixing and API refinements later we´re
happy to announce the first release candidate for FreeRDP 3.0
The API should now be considered stable and only minor changes (if at all)
will happen from this point on, so every project using FreeRDP can check
compatibility with upcoming 3.0
Noteworthy changes:
* Updated rdpSettings API #9465:
* getter/setter now use enum types for keys (generates compiler warnings for mismatch)
* Refined functions (added missing, dropped problematic ones)
* prepared opaque settings (direct struct access now deprecated)
* Server side [MS-RDPEL] channel #9471
* Relative mouse movement support #9459
* relocatable pkg-config files (enable with -DPKG_CONFIG_RELOCATABLE=ON, #9453)
* cliprdr dropped support for fuse2 (#9453)
* added support for uriparser for clipboard file:// parsing (#9455)
* aFreeRDP translation for traditional chinese (zh-rTW) added (#9450)
* fixed sdl-freerdp crash on credential dialog (#9455)
* fixed sdl-freerdp alt+tab in fullscreen (#9442)
* added /connect-child-session option (WIN32 only, #9427)
* fix rfx-image codec setup (#9425)
* added missing cmake configuration for winpr-tools (#9453)
* cleaned up cmake configuration files, dropped no longer required ones (#9455)
* fixed x11 keyboard layout detection (#9433)
* add missing API calls for server implementation (tested against ogon, #9453)
* keep dynamic channels in a hash table instead of a list (#9448)
* keep TSCredentials in server peer instance (#9430)
* fix FFMPEG/AAC encoding (#9576)
* support remote credential guard (#9574)
* fix printing on mac os 14 (#9569)
* improve RPC gateway support (#9508)
* add opus audio support for gnome-remote-desktop (#9575)
* server side handling of mouse cursor channel [MS-RDPEMSC] (#9513)
For a complete and detailed change log since the last release run:
git log 3.0.0-rc0..3.0.0-beta4
# 2023-09-31 Version 3.0.0-beta4
Noteworthy changes:
* Improved and fixed AVD authentication, now allows retries for
machines just starting up
* Improve RDP file parser, prepare new fields used by AVD
* Fixed and improved pen support in multitouch implementation (xfreerdp)
* Lots of smaller code and leak cleanups
For a complete and detailed change log since the last release run:
git log 3.0.0-beta4..3.0.0-beta3
# 2023-08-31 Version 3.0.0-beta3
Noteworthy changes:
* fix xfreerdp keyboard on mac os
* Various crashes and input check fixes
* Improved logging of autodetect, redirection and fastpath failures
* Smartcard emulation now selectable at runtime
* Allow certificates without a subject to pass client checks
* Fix FindFirstFile issues on android
* Add FREERDP_ENTRY_POINT to silence -Wmissing-prototypes warnings for
library entry points
* Add WINPR_RESTRICT to enable restrict (C99) or __restring (MSVC)
keywords for compiler
* Fix support for older OpenSSL versions
For a complete and detailed change log since the last release run:
git log 3.0.0-beta3..3.0.0-beta2
# 2023-08-03 Version 3.0.0-beta2
Noteworthy changes:
* Update CMake defaults, now all features are enabled by default with a platform
independent option if multiple are available.
* SDL client: (basic) multimonitor support
* SDL client: fix dialog cleanup order (crash fix)
* clipboard: fix FUSE shutdown crash
* fixed drive redirection: FindNextFile did miss some files/directories
* improved AAD support: honor rdp file options
* improved (gateway) http failure logging
* improved shadow server error handling
* improved CMake configuration (using find_dependency)
* updated timezone definitions
* mbedTLS build fixed
* improved MINGW build support
For a complete and detailed change log since the last release run:
git log 3.0.0-beta2..3.0.0-beta1
# 2023-07-21 Version 3.0.0-beta1
We are pleased to announce the first beta release for the next stable 3.0
series of FreeRDP. It has been a huge endeavour to implement all the new
shiny bells and whistles as well as clean up the code base and we´re still
ironing out some smaller glitches.
This is the first API breaking change since the 2.0 series and there are
some adjustments to be made for existing applications.
See https://github.com/FreeRDP/FreeRDP/wiki/FreeRDP3-migration-notes for
help (still incomplete)
Noteworthy changes:
* Support for AAD/AVD authentication
* Support for websocket transport
* Support smartcard authentication (TLS and NLA)
* Full smartcard emulation support (login with certificate + key)
* Rewritten proxy, new module API
* New reference client based on SDL2 (work in progress)
* Rewritten logging, now parsing issues are all writing to the log so
that issues with protocol incompatibilities can be easier analyzed
by just turning on logging
* Full OpenSSL 3 support
* Internal implementations for RC4, MD4 and MD5 (required for non critical
parts in RDP but not part of more recend SSL libraries)
* Updated RDP protocol support
* Improved xfreerdp remote app support
* Reworked internal state machine for both client and server implementations
* Server implementations can now make use of connect-time network autodetection
* Improved clipboard handling, now also support server-to-client file transfer
(currently xfreerdp only)
* EnhancedRemoteApp support: Utilizing the more modern standard allows remote
apps with less glitches and window shadows
* Added client- and server-side handling for RDSTLS
* Support for the graphics redirection channel
For a complete and detailed change log since the last release run:
git log 3.0.0-beta1..2.10.0
2012-02-07 Version 1.0.1
FreeRDP 1.0.1 is a maintenance release to address a certain number of
issues found in 1.0.0. This release also brings corrective measures
to certificate validation which were required for inclusion in Ubuntu.
* Certificate Validation
* Improved validation logic and robustness
* Added validation of certificate name against hostname
* Token-based Server Redirection
* Fixed redirection logic
* HAProxy load-balancer support
* xfreerdp-server
* better event handling
* capture performance improvements
* wfreerdp
* Fix RemoteFX support
* Fix mingw64 compilation
* libfreerdp-core:
* Fix severe TCP sending bug
* Added server-side Standard RDP security
2012-01-16 Version 1.0.0
License:
FreeRDP 1.0 is the first release of FreeRDP under the Apache License 2.0.
The FreeRDP 1.x series is a rewrite, meaning there is no continuity with
the previous FreeRDP 0.x series which were released under GPLv2.
New Features:
* RemoteFX
* Both encoder and decoder
* SSE2 and NEON optimization
* NSCodec
* RemoteApp
* Working, minor glitches
* Multimedia Redirection
* ffmpeg support
* Network Level Authentication (NLA)
* NTLMv2
* Certificate validation
* FIPS-compliant RDP security
* new build system (cmake)
* added official logo and icon
New Architecture:
* libfreerdp-core
* core protocol
* highly portable
* both client and server
* libfreerdp-cache
* caching operations
* libfreerdp-codec
* bitmap decompression
* codec encoding/decoding
* libfreerdp-kbd
* keyboard mapping
* libfreerdp-channels
* virtual channel management
* client and server side support
* libfreerdp-gdi
* extensively unit tested
* portable software GDI implementation
* libfreerdp-rail
* RemoteApp library
* libfreerdp-utils
* shared utility library
FreeRDP Clients:
* client/X11 (xfreerdp)
* official client
* RemoteApp support
* X11 GDI implementation
* client/DirectFB (dfreerdp)
* DirectFB support
* software-based GDI (libfreerdp-gdi)
* client/Windows (wfreerdp)
* Native Win32 support
FreeRDP Servers (experimental):
* server/X11 (xfreerdp-server)
* RemoteFX-only
* no authentication
* highly experimental
* keyboard and mouse input supported
Virtual Channels:
* cliprdr (Clipboard Redirection)
* rail (RemoteApp)
* drdynvc (Dynamic Virtual Channels)
* audin (Audio Input Redirection)
* alsa support
* pulse support
* tsmf (Multimedia Redirection)
* alsa support
* pulse support
* ffmpeg support
* rdpdr (Device Redirection)
* disk (Disk Redirection)
* parallel (Parallel Port Redirection)
* serial (Serial Port Redirection)
* printer (Printer Redirection)
* CUPS support
* smartcard (Smartcard Redirection)
* rdpsnd (Sound Redirection)
* alsa support
* pulse support

36
README Normal file
View File

@ -0,0 +1,36 @@
FreeRDP: A Remote Desktop Protocol Implementation
=================================================
FreeRDP is a free implementation of the Remote Desktop Protocol (RDP), released under the Apache license.
Enjoy the freedom of using your software wherever you want, the way you want it, in a world where
interoperability can finally liberate your computing experience.
Resources
---------
Project website: http://www.freerdp.com/
Issue tracker: https://github.com/FreeRDP/FreeRDP/issues
Sources: https://github.com/FreeRDP/FreeRDP/
Wiki: https://github.com/FreeRDP/FreeRDP/wiki
Downloads and other resources: http://pub.freerdp.com
API doc: http://pub.freerdp.com/api/
IRC channel: #freerdp @ irc.freenode.net
Mailing list: https://lists.sourceforge.net/lists/listinfo/freerdp-devel
Microsoft Open Specifications
-----------------------------
Information regarding the Microsoft Open Specifications can be found at:
http://www.microsoft.com/openspecifications/
A list of reference documentation is maintained here:
https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation
Compilation
-----------
Instructions on how to get started compiling FreeRDP can be found on the wiki:
https://github.com/FreeRDP/FreeRDP/wiki/Compilation

View File

@ -1,43 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
FreeRDP is a free implementation of the Remote Desktop Protocol (RDP), released under the Apache license.
Enjoy the freedom of using your software wherever you want, the way you want it, in a world where
interoperability can finally liberate your computing experience.
## Code Quality Status
[![abi-checker](https://github.com/FreeRDP/FreeRDP/actions/workflows/abi-checker.yml/badge.svg)](https://github.com/FreeRDP/FreeRDP/actions/workflows/abi-checker.yml)
[![clang-tidy-review](https://github.com/FreeRDP/FreeRDP/actions/workflows/clang-tidy.yml/badge.svg?event=pull_request_target)](https://github.com/FreeRDP/FreeRDP/actions/workflows/clang-tidy.yml)
[![CodeQL](https://github.com/FreeRDP/FreeRDP/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/FreeRDP/FreeRDP/actions/workflows/codeql-analysis.yml)
[![mingw-builder](https://github.com/FreeRDP/FreeRDP/actions/workflows/mingw.yml/badge.svg)](https://github.com/FreeRDP/FreeRDP/actions/workflows/mingw.yml)
[![[arm,ppc,ricsv] architecture builds](https://github.com/FreeRDP/FreeRDP/actions/workflows/alt-architectures.yml/badge.svg)](https://github.com/FreeRDP/FreeRDP/actions/workflows/alt-architectures.yml)
[![coverity](https://scan.coverity.com/projects/616/badge.svg)](https://scan.coverity.com/projects/freerdp)
## Resources
Project website: https://www.freerdp.com/
Issue tracker: https://github.com/FreeRDP/FreeRDP/issues
Sources: https://github.com/FreeRDP/FreeRDP/
Downloads: https://pub.freerdp.com/releases/
Wiki: https://github.com/FreeRDP/FreeRDP/wiki
API documentation: https://pub.freerdp.com/api/
Security policy: https://github.com/FreeRDP/FreeRDP/security/policy
Matrix room : #FreeRDP:matrix.org (main)
XMPP channel: #FreeRDP#matrix.org@matrix.org (bridged)
IRC channel : #freerdp @ irc.oftc.net (bridged)
Mailing list: https://lists.sourceforge.net/lists/listinfo/freerdp-devel
## Microsoft Open Specifications
Information regarding the Microsoft Open Specifications can be found at:
https://www.microsoft.com/openspecifications/
A list of reference documentation is maintained here:
https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation
## Compilation
Instructions on how to get started compiling FreeRDP can be found on the wiki:
https://github.com/FreeRDP/FreeRDP/wiki/Compilation

View File

@ -1,114 +0,0 @@
# FreeRDP Security Policies and Procedures
This document describes the security policy and procedures for the [FreeRDP Project](https://github.com/FreeRDP/FreeRDP).
The following topics are covered:
* [Supported Versions](#supported-versions)
* [Reporting a Vulnerability](#reporting-a-vulnerability)
* [Disclosure Procedure](#disclosure-procedure)
## Supported versions
Security is very important for us therefore we try to provide security updates and support for
the latest stable version as well as for the development branch.
Since our development branch is, like the protocol itself, a moving target we won't request CVEs for issues that are *only* found on the development branch.
The following table shows the currently supported versions:
| Version | Branch | Supported |
| ------- |--------------| ------------------ |
| < 2.0.0 | stable-1.x | :x: |
| 2.x.x | stable-2.0 | :heavy_check_mark: |
| 3.x.x | stable-3.0 | :white_check_mark: |
| - | master | :white_check_mark: |
## Reporting a vulnerability
**IMPORTANT**: Please, do not file security vulnerabilities as public issues on GitHub
In advance: **Thank you** for reporting a security vulnerability and making FreeRDP more stable! We really appreciate your effort.
Please let us know who we should give the credit or attributions to.
If you have found a security vulnerability in FreeRDP you can either directly open an [Advisory on GitHub](https://github.com/FreeRDP/FreeRDP/security/advisories/new)[^1] or send us an email to mailto:security@freerdp.com
In case of an email you can use the [FreeRDP security team GPG key](#reporting-gpg-key) for encrypted communication.
Once we receive a report we will review it and respond as soon as possible.
###
## Disclosure procedure
When the FreeRDP team receives a report one of the team members will be assigned as primary contact.
The primary contact will do all further communications and coordinate the fix and release process.
How your report will be handled:
* When a report is received we will acknowledge the reception and review the reported issue(s) as soon as possible.
* Once confirmed we will determine the affected versions. If not reported via GitHub a [security advisory draft on GitHub](https://github.com/FreeRDP/FreeRDP/security/advisories) will be created for any issue. If it applies we will request a CVE.
* On a private branch we will fix the issue and check the code for any potential similar problem.
* After the fix is validated we will create and publish a new release for all supported versions and publish the advisories.
## Reporting GPG key
FreeRDP's security reporting public gpg key https://pub.freerdp.com/FreeRDP-security-team.pub.asc
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGBz+jsBEADaIM94hYfn/xDzncQwXl7/q6+06+ssqO3iUGqFr+0EPS+HxRjD
BeKjVRSkuo0+QLQoZgCwkoltEj1xRWNqCTDMA+oZkZH8L82eqCnUQqgCOyNWAVMH
6u6ValiZH3ruYxergBBHhyR4Ot2ia0xWN8MKTp+emLpzQ7goimGMo0mxR5FiDAdb
QKz1q5bgs3bb2pLpERNF+z13OS10Mzk1zdr++1pov5PWOTBRKmvBtPJKswmDpb0y
jQGeeqBFZwKzx0n6BTzDZtkqzTwvGhbm9Sb+qO0IO66IV8zQhPG/JUfDkByd6mX9
Ykke0gxoRx54XqoRwZGNydOxMN6g3Oj1+ioWisltYLs/SzW20f3AMCoTeYyfjKtf
01refrA3aRfhDctvW5/s2LP0OEG2P/yQYXiGhK6uVxShz3Oa5dhFwiS8G63omZRH
AEqSk46EhAbbT4xfZ/Np209rhis4KW40cMMpI0F+XpyfT05ZQD6ytHTPgWTxv/OF
G9zy2ysT0kq+t+Hb+1RWQUq/2Dz9Lf6xLZPgqtyzg8xiFxZ4i1kf/VDWa3M76zn3
qMcj3SPOxKY//wW70jCxf44yD38NvSa1M2Sz/K/RJKWkRWP/jhV1UHYusbzCmsvm
M9JkknNMJvGIjBDjHEVy6dlTaHQoHDY+Me9gsrEX0ZS9xXgAiB2IupabEwARAQAB
tEJGcmVlUkRQIFNlY3VyaXR5IFRlYW0gKGh0dHBzOi8vZnJlZXJkcC5jb20pIDxz
ZWN1cml0eUBmcmVlcmRwLmNvbT6JAk4EEwEKADgWIQRvuAE0sDt7JnxXu0o3Ibww
YbfjNAUCYHP6OwIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRA3IbwwYbfj
NPviD/43NLg7YfjAlvj5GipSmgelLwlIA+L/qbrf4NAB+NZ9oqp3bBdj4e5gZmiI
zd6bkANCqk21YiOE31medUfy+nfBQFVvj0oUg1X16C6RaIX5qA3Dwt5qBwKmDkT5
j7JlxUS6Eluiau67ePiDYu2Wbp0qYuAmNUNL+Y2NCO9UJiy0Oq6YVXS971D5lC+a
SX0x9pizmFV3zro+l6/3kHTVbPednfX99yz9SZge64aWXo3MXVN8JD0lR3+92l99
XsFDc+lGeR4azLFIqXC4Cr5Lbk34Hw/VwUC32xxFUaJ2ZmV3pA8bhCtBSxSmxnHS
H3hoBaD1WpuApbW8Psx6qsgoaSUdWsjluA4eQ5afJBf9O2NlT1mim5MAINY4PbWP
o4zq3p1ABVTzuB8tsGA9o6DeYVUUrj7lCv9STdGRhm0472BDkp/gvKMBoPgg3Qez
kvGKK7iVy8R/BOPjh9wP1art5JLVsralXGHA/5Ceid4ojKFzGIC9g3lnAPh+T/eM
duyY9XH4un1r73r6DRqUoczSfHYbxhKxWt0cRNdIadcXXusMPV/w4J4j55WcLrBE
5nopp/prJ5bYegUvRRrwVSFwLDxkE2dh68Zvlh5VWXIPFge0RPEAijYWR5qR2z+/
VHgPYmliOnWFJN1rzekmWjKFtg5A57FkZyk3cp5x0/2xAX+TIbkCDQRgc/o7ARAA
vw53CoVkMzBlisSEETNdEKQMaiQ8BtbC438v/b1mOOeoE0YCfSW7RyflA/TXHOah
db0s3v/Kk2xmbjeMS9IJXlWviKKnOVMrMZvtJdQ4EKfqc5EpxNx7OiEofA/7n7Xs
1YEt6KjYaM/vgANl9HA2UXzqSFiRhkWjj1WA7vhqCWUArpAMGeCDYab2BBfp6Z4f
W9178N2vHH+Hh/uBwGUDnShU38GH8Nstkdcyw5puiJqNQBfZ1Fz9luzutp6zAgHz
WzobeRPZCCXs7CfxcvpkFS0ctOteQtIRIfP+jbDnldMmClQ87UVcKv0pCCJkMLNk
YUCMAb2UC2boCIf0omeeque4+FOphcO4+R/8jc6cYlQpgwUg2/IwBEEnCqtvo3qu
k6uzONhfWZPtUdJd158MGKGTogXVXGzoGzxIrKkZ4W1VuuMiEmhIQZO8e7/4Iz4a
Zp4qQXI8rsmNJN3lB5a7MWgrZ8mjllYRdfiTEvfQ+PiQqnG6PEHZ82om9kp555gs
15UqhjHAqRRtfXzQvZko0ngAxxZNVFPwK8LnxkyEPClRBC5eV3ljI8cvCfnWD01q
rCzSlSafFHCEUEQOhOrf/bBbXPkYTJw2KlumH5w9R6xQWgqneiD/+Qmqdclzdn36
Pgbhyu6uSNZehbx5ptt/EM66JSAW7Q7W6Qnz5PNnHgEAEQEAAYkCNgQYAQoAIBYh
BG+4ATSwO3smfFe7SjchvDBht+M0BQJgc/o7AhsMAAoJEDchvDBht+M0JYUQALlV
dwmk6ZFq5dq0utWgutysL47b30BhYwNMVe0/6UW4h4TYaW6B3f58X7ik7EdYciyR
68eYfwKGhuv/y90QaGXJMU13XHpoInSaHQRhn5M/GkN16DBXdBok70Fh9Gx89Zhs
VKF3qwIVx5AO5CwrVA6F/iOiUEW31xiT7VFkbW1Cfl5H+M6nVXSR1bOdmxTObTz7
CEeJMOVrZs36hVLMWLqZF0igVebO2AsDOY63fy/9MLn8ynCHhnAMvsm9ULWuFzGj
OsJezChduaHqPkopgwihe7jthUn4qWjABbbzKkS6HLBpGAfCzUun+lMpvIEUf+EJ
bpk7gj9xDEP6y96tV/dCeWb4p8N8webR8nVgsRxoEnfIdCkoB80iZGOzKfYYnvdz
ngs8MIL6dC4Nc1/t9ECV4O/w4uwIH65nC1ay0YOK/O/j2SEfnVHQmAuOsgTz+pBn
u6DIA2HsBzFdOCljtf3m4AeAaTbL7MBSDceApqg0lcrhjclqHJo1aJh3M6aVm3gq
yUt7y26Hkh/vYEJwW4gqRho4gb7BvjTZh5LUbrjmRtexFQ1eWM82u23yYS2L+y2Y
ejSKIKmJhXHqsgCVGYw5woZEEMzgpkoIWYG/Eoy+oVuU02QITh/Uc5VRsA9DuwSV
Vw2F8gu/fHiadawxWIhUH+plFVQZc1KwgPcIMW3S
=O0kP
-----END PGP PUBLIC KEY BLOCK-----
```
[^1]: https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability

View File

@ -7,7 +7,7 @@
# 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
# 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,
@ -15,282 +15,221 @@
# See the License for the specific language governing permissions and
# limitations under the License.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
include(CMakeParseArguments)
include(CMakeDependentOption)
macro(define_channel_options)
set(PREFIX "CHANNEL")
set(PREFIX "CHANNEL")
cmake_parse_arguments(${PREFIX}
""
"NAME;TYPE;DESCRIPTION;SPECIFICATIONS;DEFAULT"
""
${ARGN})
cmake_parse_arguments(${PREFIX}
""
"NAME;TYPE;DESCRIPTION;SPECIFICATIONS;DEFAULT"
""
${ARGN})
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" CHANNEL_OPTION)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
string(TOUPPER "${CHANNEL_TYPE}" CHANNEL_TYPE)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" CHANNEL_OPTION)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
if(${${CHANNEL_CLIENT_OPTION}})
set(OPTION_CLIENT_DEFAULT ${${CHANNEL_CLIENT_OPTION}})
endif()
if(${${CHANNEL_CLIENT_OPTION}})
set(OPTION_CLIENT_DEFAULT ${${CHANNEL_CLIENT_OPTION}})
endif()
if(${${CHANNEL_SERVER_OPTION}})
set(OPTION_SERVER_DEFAULT ${${CHANNEL_SERVER_OPTION}})
endif()
if(${${CHANNEL_SERVER_OPTION}})
set(OPTION_SERVER_DEFAULT ${${CHANNEL_SERVER_OPTION}})
endif()
if(${${CHANNEL_OPTION}})
set(OPTION_DEFAULT ${${CHANNEL_OPTION}})
endif()
if(${${CHANNEL_OPTION}})
set(OPTION_DEFAULT ${${CHANNEL_OPTION}})
endif()
if(${OPTION_CLIENT_DEFAULT} OR ${OPTION_SERVER_DEFAULT})
set(OPTION_DEFAULT "ON")
endif()
if(${OPTION_CLIENT_DEFAULT} OR ${OPTION_SERVER_DEFAULT})
set(OPTION_DEFAULT "ON")
endif()
set(CHANNEL_DEFAULT ${OPTION_DEFAULT})
set(CHANNEL_DEFAULT ${OPTION_DEFAULT})
set(CHANNEL_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel")
if ("${CHANNEL_TYPE}" STREQUAL "DYNAMIC")
CMAKE_DEPENDENT_OPTION(${CHANNEL_OPTION} "${CHANNEL_OPTION_DOC}" ${CHANNEL_DEFAULT} "CHANNEL_DRDYNVC" OFF)
else()
option(${CHANNEL_OPTION} "${CHANNEL_OPTION_DOC}" ${CHANNEL_DEFAULT})
endif()
set(CHANNEL_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel")
option(${CHANNEL_OPTION} "${CHANNEL_OPTION_DOC}" ${CHANNEL_DEFAULT})
endmacro(define_channel_options)
macro(define_channel_client_options _channel_client_default)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" CHANNEL_OPTION)
set(CHANNEL_CLIENT_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel client")
CMAKE_DEPENDENT_OPTION(${CHANNEL_CLIENT_OPTION} "${CHANNEL_CLIENT_OPTION_DOC}"
${_channel_client_default} "${CHANNEL_OPTION}" OFF)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
set(CHANNEL_CLIENT_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel client")
option(${CHANNEL_CLIENT_OPTION} "${CHANNEL_CLIENT_OPTION_DOC}" ${_channel_client_default})
cmake_dependent_option(${CHANNEL_CLIENT_OPTION} "${CHANNEL_CLIENT_OPTION_DOC}"
${_channel_client_default} "${CHANNEL_OPTION}" OFF)
endmacro(define_channel_client_options)
macro(define_channel_server_options _channel_server_default)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" CHANNEL_OPTION)
set(CHANNEL_SERVER_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel server")
CMAKE_DEPENDENT_OPTION(${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}"
${_channel_server_default} "${CHANNEL_OPTION}" OFF)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
set(CHANNEL_SERVER_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel server")
option(${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}" ${_channel_server_default})
cmake_dependent_option(${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}"
${_channel_server_default} "${CHANNEL_OPTION}" OFF)
endmacro(define_channel_server_options)
macro(define_channel _channel_name)
set(CHANNEL_NAME ${_channel_name})
set(MODULE_NAME ${CHANNEL_NAME})
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" MODULE_PREFIX)
set(CHANNEL_NAME ${_channel_name})
set(MODULE_NAME ${CHANNEL_NAME})
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" MODULE_PREFIX)
endmacro(define_channel)
macro(define_channel_client _channel_name)
set(CHANNEL_NAME ${_channel_name})
set(MODULE_NAME "${CHANNEL_NAME}-client")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" MODULE_PREFIX)
set(CHANNEL_NAME ${_channel_name})
set(MODULE_NAME "${CHANNEL_NAME}-client")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" MODULE_PREFIX)
endmacro(define_channel_client)
macro(define_channel_server _channel_name)
set(CHANNEL_NAME ${_channel_name})
set(MODULE_NAME "${CHANNEL_NAME}-server")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" MODULE_PREFIX)
set(CHANNEL_NAME ${_channel_name})
set(MODULE_NAME "${CHANNEL_NAME}-server")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" MODULE_PREFIX)
endmacro(define_channel_server)
macro(define_channel_client_subsystem _channel_name _subsystem _type)
set(CHANNEL_NAME ${_channel_name})
set(CHANNEL_SUBSYSTEM ${_subsystem})
string(LENGTH "${_type}" _type_length)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_PREFIX)
if(_type_length GREATER 0)
set(SUBSYSTEM_TYPE ${_type})
set(MODULE_NAME "${CHANNEL_NAME}-client-${CHANNEL_SUBSYSTEM}-${SUBSYSTEM_TYPE}")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT_${CHANNEL_SUBSYSTEM}_${SUBSYSTEM_TYPE}" MODULE_PREFIX)
else()
set(MODULE_NAME "${CHANNEL_NAME}-client-${CHANNEL_SUBSYSTEM}")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT_${CHANNEL_SUBSYSTEM}" MODULE_PREFIX)
endif()
set(CHANNEL_NAME ${_channel_name})
set(CHANNEL_SUBSYSTEM ${_subsystem})
string(LENGTH "${_type}" _type_length)
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_PREFIX)
if(_type_length GREATER 0)
set(SUBSYSTEM_TYPE ${_type})
set(MODULE_NAME "${CHANNEL_NAME}-client-${CHANNEL_SUBSYSTEM}-${SUBSYSTEM_TYPE}")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT_${CHANNEL_SUBSYSTEM}_${SUBSYSTEM_TYPE}" MODULE_PREFIX)
else()
set(MODULE_NAME "${CHANNEL_NAME}-client-${CHANNEL_SUBSYSTEM}")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT_${CHANNEL_SUBSYSTEM}" MODULE_PREFIX)
endif()
endmacro(define_channel_client_subsystem)
macro(define_channel_server_subsystem _channel_name _subsystem _type)
set(CHANNEL_NAME ${_channel_name})
set(CHANNEL_SUBSYSTEM ${_subsystem})
set(MODULE_NAME "${CHANNEL_NAME}-server-${CHANNEL_SUBSYSTEM}")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_server_${CHANNEL_SUBSYSTEM}" MODULE_PREFIX)
set(CHANNEL_NAME ${_channel_name})
set(CHANNEL_SUBSYSTEM ${_subsystem})
set(MODULE_NAME "${CHANNEL_NAME}-server-${CHANNEL_SUBSYSTEM}")
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_server_${CHANNEL_SUBSYSTEM}" MODULE_PREFIX)
endmacro(define_channel_server_subsystem)
macro(add_channel_client _channel_prefix _channel_name)
if (${_channel_prefix}_CLIENT)
add_subdirectory(client)
if(${${_channel_prefix}_CLIENT_STATIC})
set(CHANNEL_STATIC_CLIENT_MODULES ${CHANNEL_STATIC_CLIENT_MODULES} ${_channel_prefix} PARENT_SCOPE)
set(${_channel_prefix}_CLIENT_NAME ${${_channel_prefix}_CLIENT_NAME} PARENT_SCOPE)
set(${_channel_prefix}_CLIENT_CHANNEL ${${_channel_prefix}_CLIENT_CHANNEL} PARENT_SCOPE)
set(${_channel_prefix}_CLIENT_ENTRY ${${_channel_prefix}_CLIENT_ENTRY} PARENT_SCOPE)
set(CHANNEL_STATIC_CLIENT_ENTRIES ${CHANNEL_STATIC_CLIENT_ENTRIES} ${${_channel_prefix}_CLIENT_ENTRY} PARENT_SCOPE)
endif()
add_subdirectory(client)
if(${${_channel_prefix}_CLIENT_STATIC})
set(CHANNEL_STATIC_CLIENT_MODULES ${CHANNEL_STATIC_CLIENT_MODULES} ${_channel_prefix} PARENT_SCOPE)
set(${_channel_prefix}_CLIENT_NAME ${${_channel_prefix}_CLIENT_NAME} PARENT_SCOPE)
set(${_channel_prefix}_CLIENT_CHANNEL ${${_channel_prefix}_CLIENT_CHANNEL} PARENT_SCOPE)
set(${_channel_prefix}_CLIENT_ENTRY ${${_channel_prefix}_CLIENT_ENTRY} PARENT_SCOPE)
set(CHANNEL_STATIC_CLIENT_ENTRIES ${CHANNEL_STATIC_CLIENT_ENTRIES} ${${_channel_prefix}_CLIENT_ENTRY} PARENT_SCOPE)
endif()
endmacro(add_channel_client)
macro(add_channel_server _channel_prefix _channel_name)
if (${_channel_prefix}_SERVER)
add_subdirectory(server)
if(${${_channel_prefix}_SERVER_STATIC})
set(CHANNEL_STATIC_SERVER_MODULES ${CHANNEL_STATIC_SERVER_MODULES} ${_channel_prefix} PARENT_SCOPE)
set(${_channel_prefix}_SERVER_NAME ${${_channel_prefix}_SERVER_NAME} PARENT_SCOPE)
set(${_channel_prefix}_SERVER_CHANNEL ${${_channel_prefix}_SERVER_CHANNEL} PARENT_SCOPE)
set(${_channel_prefix}_SERVER_ENTRY ${${_channel_prefix}_SERVER_ENTRY} PARENT_SCOPE)
set(CHANNEL_STATIC_SERVER_ENTRIES ${CHANNEL_STATIC_SERVER_ENTRIES} ${${_channel_prefix}_SERVER_ENTRY} PARENT_SCOPE)
endif()
add_subdirectory(server)
if(${${_channel_prefix}_SERVER_STATIC})
set(CHANNEL_STATIC_SERVER_MODULES ${CHANNEL_STATIC_SERVER_MODULES} ${_channel_prefix} PARENT_SCOPE)
set(${_channel_prefix}_SERVER_NAME ${${_channel_prefix}_SERVER_NAME} PARENT_SCOPE)
set(${_channel_prefix}_SERVER_CHANNEL ${${_channel_prefix}_SERVER_CHANNEL} PARENT_SCOPE)
set(${_channel_prefix}_SERVER_ENTRY ${${_channel_prefix}_SERVER_ENTRY} PARENT_SCOPE)
set(CHANNEL_STATIC_SERVER_ENTRIES ${CHANNEL_STATIC_SERVER_ENTRIES} ${${_channel_prefix}_SERVER_ENTRY} PARENT_SCOPE)
endif()
endmacro(add_channel_server)
macro(add_channel_client_subsystem _channel_prefix _channel_name _subsystem _type)
add_subdirectory(${_subsystem})
set(_channel_module_name "${_channel_name}-client")
string(LENGTH "${_type}" _type_length)
if(_type_length GREATER 0)
string(TOUPPER "CHANNEL_${_channel_name}_CLIENT_${_subsystem}_${_type}" _subsystem_prefix)
else()
string(TOUPPER "CHANNEL_${_channel_name}_CLIENT_${_subsystem}" _subsystem_prefix)
endif()
if(${${_subsystem_prefix}_STATIC})
get_target_property(CHANNEL_SUBSYSTEMS ${_channel_module_name} SUBSYSTEMS)
if(_type_length GREATER 0)
set(SUBSYSTEMS ${SUBSYSTEMS} "${_subsystem}-${_type}")
else()
set(SUBSYSTEMS ${SUBSYSTEMS} ${_subsystem})
endif()
set_target_properties(${_channel_module_name} PROPERTIES SUBSYSTEMS "${SUBSYSTEMS}")
endif()
add_subdirectory(${_subsystem})
set(_channel_module_name "${_channel_name}-client")
string(LENGTH "${_type}" _type_length)
if(_type_length GREATER 0)
string(TOUPPER "CHANNEL_${_channel_name}_CLIENT_${_subsystem}_${_type}" _subsystem_prefix)
else()
string(TOUPPER "CHANNEL_${_channel_name}_CLIENT_${_subsystem}" _subsystem_prefix)
endif()
if(${${_subsystem_prefix}_STATIC})
get_target_property(CHANNEL_SUBSYSTEMS ${_channel_module_name} SUBSYSTEMS)
if(_type_length GREATER 0)
set(SUBSYSTEMS ${SUBSYSTEMS} "${_subsystem}-${_type}")
else()
set(SUBSYSTEMS ${SUBSYSTEMS} ${_subsystem})
endif()
set_target_properties(${_channel_module_name} PROPERTIES SUBSYSTEMS "${SUBSYSTEMS}")
endif()
endmacro(add_channel_client_subsystem)
macro(channel_install _targets _destination _export_target)
if (NOT BUILD_SHARED_LIBS)
foreach(_target_name IN ITEMS ${_targets})
target_include_directories(${_target_name} INTERFACE $<INSTALL_INTERFACE:include>)
endforeach()
install(TARGETS ${_targets} DESTINATION ${_destination} EXPORT ${_export_target})
endif()
endmacro(channel_install)
macro(server_channel_install _targets _destination)
channel_install(${_targets} ${_destination} "FreeRDP-ServerTargets")
endmacro(server_channel_install)
macro(client_channel_install _targets _destination)
channel_install(${_targets} ${_destination} "FreeRDP-ClientTargets")
endmacro(client_channel_install)
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
if (NOT "${_lnk_dir}" STREQUAL "")
link_directories(${_lnk_dir})
endif()
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
add_library(${_module_name} OBJECT ${${_module_prefix}_SRCS})
set_property(TARGET ${_module_name} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
if (${_module_prefix}_LIBS)
target_link_libraries(${_module_name} PUBLIC ${${_module_prefix}_LIBS})
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
endif()
if(${_dynamic} AND (NOT STATIC_CHANNELS))
add_library(${_module_name} ${${_module_prefix}_SRCS})
else()
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
endif()
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
endmacro(add_channel_client_library)
macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_name _type _dynamic _entry)
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
if (NOT "${_lnk_dir}" STREQUAL "")
link_directories(${_lnk_dir})
endif()
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
set(${_module_prefix}_TYPE ${_type} PARENT_SCOPE)
add_library(${_module_name} OBJECT ${${_module_prefix}_SRCS})
set_property(TARGET ${_module_name} PROPERTY FOLDER "Channels/${_channel_name}/Client/Subsystem/${CHANNEL_SUBSYSTEM}")
if (${_module_prefix}_LIBS)
target_link_libraries(${_module_name} PUBLIC ${${_module_prefix}_LIBS})
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
endif()
if(${_dynamic} AND (NOT STATIC_CHANNELS))
add_library(${_module_name} ${${_module_prefix}_SRCS})
else()
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
set(${_module_prefix}_TYPE ${_type} PARENT_SCOPE)
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
endif()
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
endmacro(add_channel_client_subsystem_library)
macro(add_channel_server_library _module_prefix _module_name _channel_name _dynamic _entry)
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
if (NOT "${_lnk_dir}" STREQUAL "")
link_directories(${_lnk_dir})
endif()
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
add_library(${_module_name} OBJECT ${${_module_prefix}_SRCS})
set_property(TARGET ${_module_name} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
if (${_module_prefix}_LIBS)
target_link_libraries(${_module_name} PUBLIC ${${_module_prefix}_LIBS})
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
endif()
if(${_dynamic} AND (NOT STATIC_CHANNELS))
add_library(${_module_name} ${${_module_prefix}_SRCS})
else()
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
endif()
server_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
endmacro(add_channel_server_library)
set(FILENAME "ChannelOptions.cmake")
file(GLOB FILEPATHS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/${FILENAME}")
# We need special treatement for drdynvc:
# It needs to be the first entry so that every
# dynamic channel has the dependent options available.
set(DRDYNVC_MATCH "")
foreach(FILEPATH ${FILEPATHS})
if(${FILEPATH} MATCHES "^([^/]*)drdynvc/+${FILENAME}")
set(DRDYNVC_MATCH ${FILEPATH})
endif()
endforeach()
if (NOT "${DRDYNVC_MATCH}" STREQUAL "")
list(REMOVE_ITEM FILEPATHS ${DRDYNVC_MATCH})
list(APPEND FILEPATHS ${DRDYNVC_MATCH})
list(REVERSE FILEPATHS) # list PREPEND is not available on old CMake3
endif()
foreach(FILEPATH ${FILEPATHS})
if(${FILEPATH} MATCHES "^([^/]*)/+${FILENAME}")
string(REGEX REPLACE "^([^/]*)/+${FILENAME}" "\\1" DIR ${FILEPATH})
set(CHANNEL_OPTION)
include(${FILEPATH})
if(${CHANNEL_OPTION})
set(CHANNEL_MESSAGE "Adding ${CHANNEL_TYPE} channel")
if(${CHANNEL_CLIENT_OPTION})
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} client")
endif()
if(${CHANNEL_SERVER_OPTION})
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} server")
endif()
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} \"${CHANNEL_NAME}\"")
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE}: ${CHANNEL_DESCRIPTION}")
message(STATUS "${CHANNEL_MESSAGE}")
add_subdirectory(${DIR})
endif()
endif()
if(${FILEPATH} MATCHES "^([^/]*)/+${FILENAME}")
string(REGEX REPLACE "^([^/]*)/+${FILENAME}" "\\1" DIR ${FILEPATH})
set(CHANNEL_OPTION)
include(${FILEPATH})
if(${CHANNEL_OPTION})
set(CHANNEL_MESSAGE "Adding ${CHANNEL_TYPE} channel")
if(${CHANNEL_CLIENT_OPTION})
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} client")
endif()
if(${CHANNEL_SERVER_OPTION})
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} server")
endif()
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} \"${CHANNEL_NAME}\"")
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE}: ${CHANNEL_DESCRIPTION}")
message(STATUS "${CHANNEL_MESSAGE}")
add_subdirectory(${DIR})
endif()
endif()
endforeach(FILEPATH)
if (WITH_CHANNELS)
if(WITH_CLIENT_CHANNELS)
add_subdirectory(client)
set(FREERDP_CHANNELS_CLIENT_SRCS ${FREERDP_CHANNELS_CLIENT_SRCS} PARENT_SCOPE)
set(FREERDP_CHANNELS_CLIENT_LIBS ${FREERDP_CHANNELS_CLIENT_LIBS} PARENT_SCOPE)
endif()
if(WITH_SERVER_CHANNELS)
add_subdirectory(server)
set(FREERDP_CHANNELS_SERVER_SRCS ${FREERDP_CHANNELS_SERVER_SRCS} PARENT_SCOPE)
set(FREERDP_CHANNELS_SERVER_LIBS ${FREERDP_CHANNELS_SERVER_LIBS} PARENT_SCOPE)
endif()
if(WITH_CLIENT_CHANNELS)
add_subdirectory(client)
set(FREERDP_CHANNELS_CLIENT_SRCS ${FREERDP_CHANNELS_CLIENT_SRCS} PARENT_SCOPE)
set(FREERDP_CHANNELS_CLIENT_LIBS ${FREERDP_CHANNELS_CLIENT_LIBS} PARENT_SCOPE)
endif()
if(WITH_SERVER_CHANNELS)
add_subdirectory(server)
set(FREERDP_CHANNELS_SERVER_SRCS ${FREERDP_CHANNELS_SERVER_SRCS} PARENT_SCOPE)
set(FREERDP_CHANNELS_SERVER_LIBS ${FREERDP_CHANNELS_SERVER_LIBS} PARENT_SCOPE)
endif()

View File

@ -1,27 +0,0 @@
# 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("ainput")
if(WITH_CLIENT_CHANNELS)
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -1,13 +0,0 @@
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

@ -1,32 +0,0 @@
# 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
)
set(${MODULE_PREFIX}_LIBS
winpr
)
include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")

View File

@ -1,183 +0,0 @@
/**
* 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.
*/
#include <freerdp/config.h>
#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/channels.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_PLUGIN_ AINPUT_PLUGIN;
struct AINPUT_PLUGIN_
{
GENERIC_DYNVC_PLUGIN base;
AInputClientContext* context;
UINT32 MajorVersion;
UINT32 MinorVersion;
};
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
{
UINT16 type = 0;
AINPUT_PLUGIN* ainput = NULL;
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
WINPR_ASSERT(callback);
WINPR_ASSERT(data);
ainput = (AINPUT_PLUGIN*)callback->plugin;
WINPR_ASSERT(ainput);
if (!Stream_CheckAndLogRequiredLength(TAG, data, 2))
return ERROR_NO_DATA;
Stream_Read_UINT16(data, type);
switch (type)
{
case MSG_AINPUT_VERSION:
if (!Stream_CheckAndLogRequiredLength(TAG, 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 = NULL;
GENERIC_CHANNEL_CALLBACK* callback = NULL;
BYTE buffer[32] = { 0 };
UINT64 time = 0;
wStream sbuffer = { 0 };
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
WINPR_ASSERT(context);
time = GetTickCount64();
ainput = (AINPUT_PLUGIN*)context->handle;
WINPR_ASSERT(ainput);
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->base.listener_callback->channel_callback;
WINPR_ASSERT(callback);
{
char ebuffer[128] = { 0 };
WLog_VRB(TAG, "sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
ainput_flags_to_string(flags, ebuffer, sizeof(ebuffer)), 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)
{
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
free(callback);
return CHANNEL_RC_OK;
}
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
{
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
if (!context)
return CHANNEL_RC_NO_MEMORY;
context->handle = (void*)base;
context->AInputSendInputEvent = ainput_send_input_event;
ainput->context = context;
ainput->base.iface.pInterface = context;
return CHANNEL_RC_OK;
}
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
{
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
free(ainput->context);
}
static const IWTSVirtualChannelCallback ainput_functions = { ainput_on_data_received,
NULL, /* Open */
ainput_on_close, NULL };
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
{
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, AINPUT_DVC_CHANNEL_NAME,
sizeof(AINPUT_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
&ainput_functions, init_plugin_cb, terminate_plugin_cb);
}

View File

@ -1,40 +0,0 @@
/**
* 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
#include <freerdp/config.h>
#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

@ -1,59 +0,0 @@
/**
* 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

@ -1,28 +0,0 @@
# 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
)
set(${MODULE_PREFIX}_LIBS
freerdp
)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")

View File

@ -1,597 +0,0 @@
/**
* 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.
*/
#include <freerdp/config.h>
#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 <freerdp/freerdp.h>
#include <freerdp/channels/ainput.h>
#include <freerdp/server/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 = 0;
HANDLE hEvent = NULL;
DWORD StartTick = 0;
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 = 0;
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 = 0;
wStream* s = NULL;
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) <= UINT32_MAX);
if (!WTSVirtualChannelWrite(ainput->ainput_channel, Stream_BufferAs(s, char),
(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 = 0;
UINT64 time = 0;
INT32 x = 0;
INT32 y = 0;
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, "received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, 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)
{
void* 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 = 0;
HANDLE events[2] = { 0 };
ainput_server* ainput = (ainput_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status = 0;
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;
}
}
(void)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!");
(void)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)
{
(void)SetEvent(ainput->stopEvent);
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
(void)CloseHandle(ainput->thread);
(void)CloseHandle(ainput->stopEvent);
ainput->thread = NULL;
ainput->stopEvent = NULL;
}
if (ainput->externalThread)
{
if (ainput->state != AINPUT_INITIAL)
{
(void)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:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
ainput_server_context_free(&ainput->context);
WINPR_PRAGMA_DIAG_POP
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 = 0;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned = 0;
ULONG ActualBytesReturned = 0;
UINT16 MessageId = 0;
wStream* s = NULL;
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, Stream_BufferAs(s, char),
(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:
{
union
{
BYTE* pb;
void* pv;
} buffer;
DWORD BytesReturned = 0;
buffer.pv = NULL;
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer.pv,
&BytesReturned) != TRUE)
{
WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
}
else
{
if (*buffer.pb != 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.pv);
}
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

@ -4,6 +4,7 @@ set(OPTION_CLIENT_DEFAULT ON)
set(OPTION_SERVER_DEFAULT ON)
if(ANDROID)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
endif()

View File

@ -19,21 +19,27 @@ define_channel_client("audin")
set(${MODULE_PREFIX}_SRCS
audin_main.c
audin_main.h
)
set(${MODULE_PREFIX}_LIBS
freerdp winpr
)
audin_main.h)
include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
if(WITH_OSS)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "oss" "")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-common freerdp-utils)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
if(NOT STATIC_CHANNELS)
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_PLUGIN_PATH})
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
if(WITH_ALSA)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "alsa" "")
endif()
@ -41,23 +47,3 @@ endif()
if(WITH_PULSE)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "pulse" "")
endif()
if(WITH_OPENSLES)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "")
endif()
if(WITH_WINMM)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "")
endif()
if(WITH_MACAUDIO)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "mac" "")
endif()
if(WITH_SNDIO)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "sndio" "")
endif()
if(WITH_IOSAUDIO)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "ios" "")
endif()

View File

@ -17,19 +17,25 @@
define_channel_client_subsystem("audin" "alsa" "")
find_package(ALSA REQUIRED)
set(${MODULE_PREFIX}_SRCS
audin_alsa.c
)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${ALSA_LIBRARIES}
)
audin_alsa.c)
include_directories(..)
include_directories(SYSTEM ${ALSA_INCLUDE_DIRS})
include_directories(${ALSA_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${ALSA_LIBRARIES})
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
if(NOT STATIC_CHANNELS)
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH})
endif()

View File

@ -3,8 +3,6 @@
* Audio Input Redirection Virtual Channel - ALSA implementation
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +17,9 @@
* limitations under the License.
*/
#include <freerdp/config.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
@ -29,347 +29,353 @@
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <winpr/wlog.h>
#include <alsa/asoundlib.h>
#include <freerdp/freerdp.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
typedef struct
typedef struct _AudinALSADevice
{
IAudinDevice iface;
char* device_name;
UINT32 frames_per_packet;
AUDIO_FORMAT aformat;
UINT32 target_rate;
UINT32 actual_rate;
snd_pcm_format_t format;
UINT32 target_channels;
UINT32 actual_channels;
int bytes_per_channel;
int wformat;
int block_size;
FREERDP_DSP_CONTEXT* dsp_context;
HANDLE thread;
HANDLE stopEvent;
BYTE* buffer;
int buffer_frames;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
wLog* log;
size_t bytes_per_frame;
} AudinALSADevice;
static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChannel)
{
switch (wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (bitPerChannel)
{
case 16:
return SND_PCM_FORMAT_S16_LE;
case 8:
return SND_PCM_FORMAT_S8;
default:
return SND_PCM_FORMAT_UNKNOWN;
}
default:
return SND_PCM_FORMAT_UNKNOWN;
}
}
static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
{
int error = 0;
SSIZE_T s = 0;
UINT32 channels = alsa->aformat.nChannels;
snd_pcm_hw_params_t* hw_params = NULL;
snd_pcm_format_t format =
audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
int error;
snd_pcm_hw_params_t* hw_params;
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_hw_params_malloc (%s)", snd_strerror(error));
DEBUG_WARN("snd_pcm_hw_params_malloc (%s)",
snd_strerror(error));
return FALSE;
}
snd_pcm_hw_params_any(capture_handle, hw_params);
snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->aformat.nSamplesPerSec, NULL);
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &channels);
snd_pcm_hw_params_set_format(capture_handle, hw_params, alsa->format);
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->actual_rate, NULL);
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &alsa->actual_channels);
snd_pcm_hw_params(capture_handle, hw_params);
snd_pcm_hw_params_free(hw_params);
snd_pcm_prepare(capture_handle);
if (channels > UINT16_MAX)
return FALSE;
s = snd_pcm_format_size(format, 1);
if ((s < 0) || (s > UINT16_MAX))
return FALSE;
alsa->aformat.nChannels = (UINT16)channels;
alsa->bytes_per_frame = (size_t)s * channels;
if ((alsa->actual_rate != alsa->target_rate) ||
(alsa->actual_channels != alsa->target_channels))
{
DEBUG_DVC("actual rate %d / channel %d is "
"different from target rate %d / channel %d, resampling required.",
alsa->actual_rate, alsa->actual_channels,
alsa->target_rate, alsa->target_channels);
}
return TRUE;
}
static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size)
{
DWORD error = CHANNEL_RC_OK;
BYTE* buffer = NULL;
AudinALSADevice* alsa = (AudinALSADevice*)arg;
int frames;
int cframes;
int ret = 0;
int encoded_size;
BYTE* encoded_data;
int rbytes_per_frame;
int tbytes_per_frame;
WINPR_ASSERT(alsa);
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
WLog_Print(alsa->log, WLOG_DEBUG, "in");
if ((alsa->target_rate == alsa->actual_rate) &&
(alsa->target_channels == alsa->actual_channels))
{
frames = size / rbytes_per_frame;
}
else
{
alsa->dsp_context->resample(alsa->dsp_context, src, alsa->bytes_per_channel,
alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
alsa->target_channels, alsa->target_rate);
frames = alsa->dsp_context->resampled_frames;
DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
size = frames * tbytes_per_frame;
src = alsa->dsp_context->resampled_buffer;
}
while (frames > 0)
{
if (WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0)
break;
cframes = alsa->frames_per_packet - alsa->buffer_frames;
if (cframes > frames)
cframes = frames;
CopyMemory(alsa->buffer + alsa->buffer_frames * tbytes_per_frame, src, cframes * tbytes_per_frame);
alsa->buffer_frames += cframes;
if (alsa->buffer_frames >= alsa->frames_per_packet)
{
if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
{
alsa->dsp_context->encode_ima_adpcm(alsa->dsp_context,
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
alsa->target_channels, alsa->block_size);
encoded_data = alsa->dsp_context->adpcm_buffer;
encoded_size = alsa->dsp_context->adpcm_size;
DEBUG_DVC("encoded %d to %d",
alsa->buffer_frames * tbytes_per_frame, encoded_size);
}
else
{
encoded_data = alsa->buffer;
encoded_size = alsa->buffer_frames * tbytes_per_frame;
}
if (WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0)
{
ret = 0;
frames = 0;
}
else
{
ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
}
alsa->buffer_frames = 0;
if (!ret)
break;
}
src += cframes * tbytes_per_frame;
frames -= cframes;
}
return ret;
}
static void* audin_alsa_thread_func(void* arg)
{
int error;
BYTE* buffer;
int rbytes_per_frame;
int tbytes_per_frame;
snd_pcm_t* capture_handle = NULL;
const int rc = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0)
AudinALSADevice* alsa = (AudinALSADevice*) arg;
DEBUG_DVC("in");
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
alsa->buffer = (BYTE*) malloc(tbytes_per_frame * alsa->frames_per_packet);
ZeroMemory(alsa->buffer, tbytes_per_frame * alsa->frames_per_packet);
alsa->buffer_frames = 0;
buffer = (BYTE*) malloc(rbytes_per_frame * alsa->frames_per_packet);
ZeroMemory(buffer, rbytes_per_frame * alsa->frames_per_packet);
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
do
{
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(rc));
error = CHANNEL_RC_INITIALIZATION_ERROR;
goto out;
}
if (!audin_alsa_set_params(alsa, capture_handle))
{
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_set_params failed");
goto out;
}
buffer =
(BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
if (!buffer)
{
WLog_Print(alsa->log, WLOG_ERROR, "calloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
while (1)
{
size_t frames = alsa->frames_per_packet;
const DWORD status = WaitForSingleObject(alsa->stopEvent, 0);
if (status == WAIT_FAILED)
if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
{
error = GetLastError();
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
error);
DEBUG_WARN("snd_pcm_open (%s)", snd_strerror(error));
break;
}
if (status == WAIT_OBJECT_0)
if (!audin_alsa_set_params(alsa, capture_handle))
{
WLog_Print(alsa->log, WLOG_DEBUG, "alsa->stopEvent requests termination");
break;
}
snd_pcm_sframes_t framesRead = snd_pcm_readi(capture_handle, buffer, frames);
if (framesRead == 0)
continue;
if (framesRead == -EPIPE)
while (!(WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0))
{
const int res = snd_pcm_recover(capture_handle, (int)framesRead, 0);
if (res < 0)
WLog_Print(alsa->log, WLOG_WARN, "snd_pcm_recover (%s)", snd_strerror(res));
error = snd_pcm_readi(capture_handle, buffer, alsa->frames_per_packet);
continue;
}
else if (framesRead < 0)
{
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_readi (%s)", snd_strerror((int)framesRead));
error = ERROR_INTERNAL_ERROR;
break;
}
if (error == -EPIPE)
{
snd_pcm_recover(capture_handle, error, 0);
continue;
}
else if (error < 0)
{
DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error));
break;
}
error = alsa->receive(&alsa->aformat, buffer, (long)framesRead * alsa->bytes_per_frame,
alsa->user_data);
if (error)
{
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_thread_receive failed with error %ld",
error);
break;
if (!audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame))
break;
}
}
while (0);
free(buffer);
free(alsa->buffer);
alsa->buffer = NULL;
if (capture_handle)
{
const int res = snd_pcm_close(capture_handle);
if (res < 0)
WLog_Print(alsa->log, WLOG_WARN, "snd_pcm_close (%s)", snd_strerror(res));
}
snd_pcm_close(capture_handle);
out:
WLog_Print(alsa->log, WLOG_DEBUG, "out");
SetEvent(alsa->stopEvent);
if (error && alsa->rdpcontext)
setChannelError(alsa->rdpcontext, error, "audin_alsa_thread_func reported an error");
DEBUG_DVC("out");
ExitThread(error);
return error;
return NULL;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_free(IAudinDevice* device)
static void audin_alsa_free(IAudinDevice* device)
{
AudinALSADevice* alsa = (AudinALSADevice*)device;
AudinALSADevice* alsa = (AudinALSADevice*) device;
if (alsa)
free(alsa->device_name);
SetEvent(alsa->stopEvent);
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa->device_name);
free(alsa);
return CHANNEL_RC_OK;
}
static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* format)
{
if (!device || !format)
return FALSE;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels == 1 || format->nChannels == 2))
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
default:
return FALSE;
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
}
return FALSE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
{
AudinALSADevice* alsa = (AudinALSADevice*)device;
int bs;
AudinALSADevice* alsa = (AudinALSADevice*) device;
if (!alsa || !format)
return ERROR_INVALID_PARAMETER;
alsa->target_rate = format->nSamplesPerSec;
alsa->actual_rate = format->nSamplesPerSec;
alsa->target_channels = format->nChannels;
alsa->actual_channels = format->nChannels;
alsa->aformat = *format;
alsa->frames_per_packet = FramesPerPacket;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
alsa->format = SND_PCM_FORMAT_S8;
alsa->bytes_per_channel = 1;
break;
case 16:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
break;
}
break;
if (audin_alsa_format(format->wFormatTag, format->wBitsPerSample) == SND_PCM_FORMAT_UNKNOWN)
return ERROR_INTERNAL_ERROR;
case WAVE_FORMAT_DVI_ADPCM:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
alsa->frames_per_packet = (alsa->frames_per_packet * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
DEBUG_DVC("aligned FramesPerPacket=%d",
alsa->frames_per_packet);
break;
}
return CHANNEL_RC_OK;
alsa->wformat = format->wFormatTag;
alsa->block_size = format->nBlockAlign;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
static void audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinALSADevice* alsa = (AudinALSADevice*)device;
AudinALSADevice* alsa = (AudinALSADevice*) device;
if (!device || !receive || !user_data)
return ERROR_INVALID_PARAMETER;
DEBUG_DVC("");
alsa->receive = receive;
alsa->user_data = user_data;
if (!(alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_Print(alsa->log, WLOG_ERROR, "CreateEvent failed!");
goto error_out;
}
if (!(alsa->thread = CreateThread(NULL, 0, audin_alsa_thread_func, alsa, 0, NULL)))
{
WLog_Print(alsa->log, WLOG_ERROR, "CreateThread failed!");
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
(void)CloseHandle(alsa->stopEvent);
alsa->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
alsa->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) audin_alsa_thread_func, alsa, 0, NULL);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_close(IAudinDevice* device)
static void audin_alsa_close(IAudinDevice* device)
{
UINT error = CHANNEL_RC_OK;
AudinALSADevice* alsa = (AudinALSADevice*)device;
AudinALSADevice* alsa = (AudinALSADevice*) device;
if (!alsa)
return ERROR_INVALID_PARAMETER;
DEBUG_DVC("");
if (alsa->stopEvent)
{
(void)SetEvent(alsa->stopEvent);
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "",
error);
return error;
}
(void)CloseHandle(alsa->stopEvent);
alsa->stopEvent = NULL;
(void)CloseHandle(alsa->thread);
alsa->thread = NULL;
}
SetEvent(alsa->stopEvent);
alsa->receive = NULL;
alsa->user_data = NULL;
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, const ADDIN_ARGV* args)
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
{
int status = 0;
DWORD flags = 0;
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
AudinALSADevice* alsa = device;
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_alsa_args, flags, alsa, NULL,
NULL);
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
if (status < 0)
return ERROR_INVALID_PARAMETER;
static void audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinALSADevice* alsa = (AudinALSADevice*) device;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_alsa_args, flags, alsa, NULL, NULL);
arg = audin_alsa_args;
@ -378,85 +384,54 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, const ADDIN_ARG
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "audio-dev")
{
alsa->device_name = _strdup(arg->Value);
if (!alsa->device_name)
{
WLog_Print(alsa->log, WLOG_ERROR, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE alsa_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry alsa_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
const ADDIN_ARGV* args = NULL;
AudinALSADevice* alsa = NULL;
UINT error = 0;
alsa = (AudinALSADevice*)calloc(1, sizeof(AudinALSADevice));
ADDIN_ARGV* args;
AudinALSADevice* alsa;
if (!alsa)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
alsa = (AudinALSADevice*) malloc(sizeof(AudinALSADevice));
ZeroMemory(alsa, sizeof(AudinALSADevice));
alsa->log = WLog_Get(TAG);
alsa->iface.Open = audin_alsa_open;
alsa->iface.FormatSupported = audin_alsa_format_supported;
alsa->iface.SetFormat = audin_alsa_set_format;
alsa->iface.Close = audin_alsa_close;
alsa->iface.Free = audin_alsa_free;
alsa->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_alsa_parse_addin_args(alsa, args)))
{
WLog_Print(alsa->log, WLOG_ERROR,
"audin_alsa_parse_addin_args failed with errorcode %" PRIu32 "!", error);
goto error_out;
}
audin_alsa_parse_addin_args(alsa, args);
if (!alsa->device_name)
{
alsa->device_name = _strdup("default");
if (!alsa->device_name)
{
WLog_Print(alsa->log, WLOG_ERROR, "_strdup failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
alsa->frames_per_packet = 128;
alsa->aformat.nChannels = 2;
alsa->aformat.wBitsPerSample = 16;
alsa->aformat.wFormatTag = WAVE_FORMAT_PCM;
alsa->aformat.nSamplesPerSec = 44100;
alsa->target_rate = 22050;
alsa->actual_rate = 22050;
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->target_channels = 2;
alsa->actual_channels = 2;
alsa->bytes_per_channel = 2;
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)alsa)))
{
WLog_Print(alsa->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
error);
goto error_out;
}
alsa->dsp_context = freerdp_dsp_context_new();
return CHANNEL_RC_OK;
error_out:
free(alsa->device_name);
free(alsa);
return error;
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) alsa);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -17,17 +17,62 @@
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H
#define FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H
#ifndef __AUDIN_MAIN_H
#define __AUDIN_MAIN_H
#include <freerdp/config.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>
#include <freerdp/client/audin.h>
#include <freerdp/utils/debug.h>
#define TAG CHANNELS_TAG("audin.client")
#ifdef WITH_DEBUG_DVC
#define DEBUG_DVC(fmt, ...) DEBUG_CLASS(DVC, fmt, ## __VA_ARGS__)
#else
#define DEBUG_DVC(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
#endif
typedef BOOL (*AudinReceive) (BYTE* data, int size, void* user_data);
typedef struct audin_format audinFormat;
struct audin_format
{
UINT16 wFormatTag;
UINT16 nChannels;
UINT32 nSamplesPerSec;
UINT16 nBlockAlign;
UINT16 wBitsPerSample;
UINT16 cbSize;
BYTE* data;
};
typedef struct _IAudinDevice IAudinDevice;
struct _IAudinDevice
{
void (*Open) (IAudinDevice* devplugin, AudinReceive receive, void* user_data);
BOOL (*FormatSupported) (IAudinDevice* devplugin, audinFormat* format);
void (*SetFormat) (IAudinDevice* devplugin, audinFormat* format, UINT32 FramesPerPacket);
void (*Close) (IAudinDevice* devplugin);
void (*Free) (IAudinDevice* devplugin);
};
#define AUDIN_DEVICE_EXPORT_FUNC_NAME "freerdp_audin_client_subsystem_entry"
typedef void (*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
{
IWTSPlugin* plugin;
PREGISTERAUDINDEVICE pRegisterAudinDevice;
ADDIN_ARGV* args;
};
typedef struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS FREERDP_AUDIN_DEVICE_ENTRY_POINTS;
typedef FREERDP_AUDIN_DEVICE_ENTRY_POINTS* PFREERDP_AUDIN_DEVICE_ENTRY_POINTS;
typedef int (*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
#endif /* __AUDIN_MAIN_H */
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H */

View File

@ -1,39 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
# Copyright (c) 2015 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_subsystem("audin" "ios" "")
FIND_LIBRARY(CORE_AUDIO CoreAudio)
FIND_LIBRARY(AVFOUNDATION AVFoundation)
FIND_LIBRARY(AUDIO_TOOL AudioToolbox)
set(${MODULE_PREFIX}_SRCS
audin_ios.m
)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${AVFOUNDATION}
${CORE_AUDIO}
${AUDIO_TOOL}
)
include_directories(..)
include_directories(SYSTEM ${MAC_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")

View File

@ -1,335 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - iOS implementation
*
* Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
* Copyright 2015 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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/string.h>
#include <winpr/thread.h>
#include <winpr/debug.h>
#include <winpr/cmdline.h>
#import <AVFoundation/AVFoundation.h>
#define __COREFOUNDATION_CFPLUGINCOM__ 1
#define IUNKNOWN_C_GUTS \
void *_reserved; \
void *QueryInterface; \
void *AddRef; \
void *Release
#include <CoreAudio/CoreAudioTypes.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AudioToolbox/AudioQueue.h>
#include <freerdp/addin.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
#define IOS_AUDIO_QUEUE_NUM_BUFFERS 100
typedef struct
{
IAudinDevice iface;
AUDIO_FORMAT format;
UINT32 FramesPerPacket;
int dev_unit;
AudinReceive receive;
void *user_data;
rdpContext *rdpcontext;
bool isOpen;
AudioQueueRef audioQueue;
AudioStreamBasicDescription audioFormat;
AudioQueueBufferRef audioBuffers[IOS_AUDIO_QUEUE_NUM_BUFFERS];
} AudinIosDevice;
static AudioFormatID audin_ios_get_format(const AUDIO_FORMAT *format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
return kAudioFormatLinearPCM;
default:
return 0;
}
}
static AudioFormatFlags audin_ios_get_flags_for_format(const AUDIO_FORMAT *format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
return kAudioFormatFlagIsSignedInteger;
default:
return 0;
}
}
static BOOL audin_ios_format_supported(IAudinDevice *device, const AUDIO_FORMAT *format)
{
AudinIosDevice *ios = (AudinIosDevice *)device;
AudioFormatID req_fmt = 0;
if (device == NULL || format == NULL)
return FALSE;
req_fmt = audin_ios_get_format(format);
if (req_fmt == 0)
return FALSE;
return TRUE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_ios_set_format(IAudinDevice *device, const AUDIO_FORMAT *format,
UINT32 FramesPerPacket)
{
AudinIosDevice *ios = (AudinIosDevice *)device;
if (device == NULL || format == NULL)
return ERROR_INVALID_PARAMETER;
ios->FramesPerPacket = FramesPerPacket;
ios->format = *format;
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
audio_format_get_tag_string(format->wFormatTag), format->nChannels,
format->nSamplesPerSec, format->wBitsPerSample);
ios->audioFormat.mBitsPerChannel = format->wBitsPerSample;
if (format->wBitsPerSample == 0)
ios->audioFormat.mBitsPerChannel = 16;
ios->audioFormat.mChannelsPerFrame = ios->format.nChannels;
ios->audioFormat.mFramesPerPacket = 1;
ios->audioFormat.mBytesPerFrame =
ios->audioFormat.mChannelsPerFrame * (ios->audioFormat.mBitsPerChannel / 8);
ios->audioFormat.mBytesPerPacket =
ios->audioFormat.mBytesPerFrame * ios->audioFormat.mFramesPerPacket;
ios->audioFormat.mFormatFlags = audin_ios_get_flags_for_format(format);
ios->audioFormat.mFormatID = audin_ios_get_format(format);
ios->audioFormat.mReserved = 0;
ios->audioFormat.mSampleRate = ios->format.nSamplesPerSec;
return CHANNEL_RC_OK;
}
static void ios_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc)
{
AudinIosDevice *ios = (AudinIosDevice *)aqData;
UINT error = CHANNEL_RC_OK;
const BYTE *buffer = inBuffer->mAudioData;
int buffer_size = inBuffer->mAudioDataByteSize;
(void)inAQ;
(void)inStartTime;
(void)inNumPackets;
(void)inPacketDesc;
if (buffer_size > 0)
error = ios->receive(&ios->format, buffer, buffer_size, ios->user_data);
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
if (error)
{
WLog_ERR(TAG, "ios->receive failed with error %" PRIu32 "", error);
SetLastError(ERROR_INTERNAL_ERROR);
}
}
static UINT audin_ios_close(IAudinDevice *device)
{
UINT errCode = CHANNEL_RC_OK;
char errString[1024];
OSStatus devStat;
AudinIosDevice *ios = (AudinIosDevice *)device;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if (ios->isOpen)
{
devStat = AudioQueueStop(ios->audioQueue, true);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
ios->isOpen = false;
}
if (ios->audioQueue)
{
devStat = AudioQueueDispose(ios->audioQueue, true);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
ios->audioQueue = NULL;
}
ios->receive = NULL;
ios->user_data = NULL;
return errCode;
}
static UINT audin_ios_open(IAudinDevice *device, AudinReceive receive, void *user_data)
{
AudinIosDevice *ios = (AudinIosDevice *)device;
DWORD errCode;
char errString[1024];
OSStatus devStat;
ios->receive = receive;
ios->user_data = user_data;
devStat = AudioQueueNewInput(&(ios->audioFormat), ios_audio_queue_input_cb, ios, NULL,
kCFRunLoopCommonModes, 0, &(ios->audioQueue));
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
for (size_t index = 0; index < IOS_AUDIO_QUEUE_NUM_BUFFERS; index++)
{
devStat = AudioQueueAllocateBuffer(ios->audioQueue,
ios->FramesPerPacket * 2 * ios->format.nChannels,
&ios->audioBuffers[index]);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
devStat = AudioQueueEnqueueBuffer(ios->audioQueue, ios->audioBuffers[index], 0, NULL);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
}
devStat = AudioQueueStart(ios->audioQueue, NULL);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
ios->isOpen = true;
return CHANNEL_RC_OK;
err_out:
audin_ios_close(device);
return CHANNEL_RC_INITIALIZATION_ERROR;
}
static UINT audin_ios_free(IAudinDevice *device)
{
AudinIosDevice *ios = (AudinIosDevice *)device;
int error;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if ((error = audin_ios_close(device)))
{
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
}
free(ios);
return CHANNEL_RC_OK;
}
FREERDP_ENTRY_POINT(UINT VCAPITYPE ios_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
{
DWORD errCode;
char errString[1024];
const ADDIN_ARGV *args;
AudinIosDevice *ios;
UINT error;
ios = (AudinIosDevice *)calloc(1, sizeof(AudinIosDevice));
if (!ios)
{
errCode = GetLastError();
WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
return CHANNEL_RC_NO_MEMORY;
}
ios->iface.Open = audin_ios_open;
ios->iface.FormatSupported = audin_ios_format_supported;
ios->iface.SetFormat = audin_ios_set_format;
ios->iface.Close = audin_ios_close;
ios->iface.Free = audin_ios_free;
ios->rdpcontext = pEntryPoints->rdpcontext;
ios->dev_unit = -1;
args = pEntryPoints->args;
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)ios)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(ios);
return error;
}

View File

@ -1,41 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
# Copyright (c) 2015 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_subsystem("audin" "mac" "")
FIND_LIBRARY(CORE_AUDIO CoreAudio)
FIND_LIBRARY(AVFOUNDATION AVFoundation)
FIND_LIBRARY(AUDIO_TOOL AudioToolbox)
FIND_LIBRARY(APP_SERVICES ApplicationServices)
set(${MODULE_PREFIX}_SRCS
audin_mac.m
)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${AVFOUNDATION}
${CORE_AUDIO}
${AUDIO_TOOL}
${APP_SERVICES}
)
include_directories(..)
include_directories(SYSTEM ${MAC_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")

View File

@ -1,463 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - Mac OS X implementation
*
* Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
* Copyright 2015 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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/string.h>
#include <winpr/thread.h>
#include <winpr/debug.h>
#include <winpr/cmdline.h>
#import <AVFoundation/AVFoundation.h>
#define __COREFOUNDATION_CFPLUGINCOM__ 1
#define IUNKNOWN_C_GUTS \
void *_reserved; \
void *QueryInterface; \
void *AddRef; \
void *Release
#include <CoreAudio/CoreAudioTypes.h>
#include <CoreAudio/CoreAudio.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AudioToolbox/AudioQueue.h>
#include <freerdp/addin.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
/* Fix for #4462: Provide type alias if not declared (Mac OS < 10.10)
* https://developer.apple.com/documentation/coreaudio/audioformatid
*/
#ifndef AudioFormatID
typedef UInt32 AudioFormatID;
#endif
#ifndef AudioFormatFlags
typedef UInt32 AudioFormatFlags;
#endif
typedef struct
{
IAudinDevice iface;
AUDIO_FORMAT format;
UINT32 FramesPerPacket;
int dev_unit;
AudinReceive receive;
void *user_data;
rdpContext *rdpcontext;
bool isAuthorized;
bool isOpen;
AudioQueueRef audioQueue;
AudioStreamBasicDescription audioFormat;
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
} AudinMacDevice;
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT *format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
return kAudioFormatLinearPCM;
default:
return 0;
}
}
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT *format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
return kAudioFormatFlagIsSignedInteger;
default:
return 0;
}
}
static BOOL audin_mac_format_supported(IAudinDevice *device, const AUDIO_FORMAT *format)
{
AudinMacDevice *mac = (AudinMacDevice *)device;
AudioFormatID req_fmt = 0;
if (!mac->isAuthorized)
return FALSE;
if (device == NULL || format == NULL)
return FALSE;
if (format->nChannels != 2)
return FALSE;
req_fmt = audin_mac_get_format(format);
if (req_fmt == 0)
return FALSE;
return TRUE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_mac_set_format(IAudinDevice *device, const AUDIO_FORMAT *format,
UINT32 FramesPerPacket)
{
AudinMacDevice *mac = (AudinMacDevice *)device;
if (!mac->isAuthorized)
return ERROR_INTERNAL_ERROR;
if (device == NULL || format == NULL)
return ERROR_INVALID_PARAMETER;
mac->FramesPerPacket = FramesPerPacket;
mac->format = *format;
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
audio_format_get_tag_string(format->wFormatTag), format->nChannels,
format->nSamplesPerSec, format->wBitsPerSample);
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
if (format->wBitsPerSample == 0)
mac->audioFormat.mBitsPerChannel = 16;
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.mFormatID = audin_mac_get_format(format);
mac->audioFormat.mReserved = 0;
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
return CHANNEL_RC_OK;
}
static void mac_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc)
{
AudinMacDevice *mac = (AudinMacDevice *)aqData;
UINT error = CHANNEL_RC_OK;
const BYTE *buffer = inBuffer->mAudioData;
int buffer_size = inBuffer->mAudioDataByteSize;
(void)inAQ;
(void)inStartTime;
(void)inNumPackets;
(void)inPacketDesc;
if (buffer_size > 0)
error = mac->receive(&mac->format, buffer, buffer_size, mac->user_data);
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
if (error)
{
WLog_ERR(TAG, "mac->receive failed with error %" PRIu32 "", error);
SetLastError(ERROR_INTERNAL_ERROR);
}
}
static UINT audin_mac_close(IAudinDevice *device)
{
UINT errCode = CHANNEL_RC_OK;
char errString[1024];
OSStatus devStat;
AudinMacDevice *mac = (AudinMacDevice *)device;
if (!mac->isAuthorized)
return ERROR_INTERNAL_ERROR;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if (mac->isOpen)
{
devStat = AudioQueueStop(mac->audioQueue, true);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
mac->isOpen = false;
}
if (mac->audioQueue)
{
devStat = AudioQueueDispose(mac->audioQueue, true);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
mac->audioQueue = NULL;
}
mac->receive = NULL;
mac->user_data = NULL;
return errCode;
}
static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive, void *user_data)
{
AudinMacDevice *mac = (AudinMacDevice *)device;
DWORD errCode;
char errString[1024];
OSStatus devStat;
if (!mac->isAuthorized)
return ERROR_INTERNAL_ERROR;
mac->receive = receive;
mac->user_data = user_data;
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb, mac, NULL,
kCFRunLoopCommonModes, 0, &(mac->audioQueue));
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
for (size_t index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
{
devStat = AudioQueueAllocateBuffer(mac->audioQueue,
mac->FramesPerPacket * 2 * mac->format.nChannels,
&mac->audioBuffers[index]);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
devStat = AudioQueueEnqueueBuffer(mac->audioQueue, mac->audioBuffers[index], 0, NULL);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
}
devStat = AudioQueueStart(mac->audioQueue, NULL);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
mac->isOpen = true;
return CHANNEL_RC_OK;
err_out:
audin_mac_close(device);
return CHANNEL_RC_INITIALIZATION_ERROR;
}
static UINT audin_mac_free(IAudinDevice *device)
{
AudinMacDevice *mac = (AudinMacDevice *)device;
int error;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if ((error = audin_mac_close(device)))
{
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
}
free(mac);
return CHANNEL_RC_OK;
}
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, const ADDIN_ARGV *args)
{
DWORD errCode;
char errString[1024];
int status;
char *str_num, *eptr;
DWORD flags;
const COMMAND_LINE_ARGUMENT_A *arg;
COMMAND_LINE_ARGUMENT_A audin_mac_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
AudinMacDevice *mac = (AudinMacDevice *)device;
if (args->argc == 1)
return CHANNEL_RC_OK;
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status =
CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags, mac, NULL, NULL);
if (status < 0)
return ERROR_INVALID_PARAMETER;
arg = audin_mac_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
{
str_num = _strdup(arg->Value);
if (!str_num)
{
errCode = GetLastError();
WLog_ERR(TAG, "_strdup failed with %s [%d]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
return CHANNEL_RC_NO_MEMORY;
}
mac->dev_unit = strtol(str_num, &eptr, 10);
if (mac->dev_unit < 0 || *eptr != '\0')
mac->dev_unit = -1;
free(str_num);
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
FREERDP_ENTRY_POINT(UINT VCAPITYPE mac_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
{
DWORD errCode;
char errString[1024];
const ADDIN_ARGV *args;
AudinMacDevice *mac;
UINT error;
mac = (AudinMacDevice *)calloc(1, sizeof(AudinMacDevice));
if (!mac)
{
errCode = GetLastError();
WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
return CHANNEL_RC_NO_MEMORY;
}
mac->iface.Open = audin_mac_open;
mac->iface.FormatSupported = audin_mac_format_supported;
mac->iface.SetFormat = audin_mac_set_format;
mac->iface.Close = audin_mac_close;
mac->iface.Free = audin_mac_free;
mac->rdpcontext = pEntryPoints->rdpcontext;
mac->dev_unit = -1;
args = pEntryPoints->args;
if ((error = audin_mac_parse_addin_args(mac, args)))
{
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %" PRIu32 "!", error);
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)mac)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
goto error_out;
}
#if defined(MAC_OS_X_VERSION_10_14)
if (@available(macOS 10.14, *))
{
@autoreleasepool
{
AVAuthorizationStatus status =
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status)
{
case AVAuthorizationStatusAuthorized:
mac->isAuthorized = TRUE;
break;
case AVAuthorizationStatusNotDetermined:
[AVCaptureDevice
requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
if (granted == YES)
{
mac->isAuthorized = TRUE;
}
else
WLog_WARN(TAG, "Microphone access denied by user");
}];
break;
case AVAuthorizationStatusRestricted:
WLog_WARN(TAG, "Microphone access restricted by policy");
break;
case AVAuthorizationStatusDenied:
WLog_WARN(TAG, "Microphone access denied by policy");
break;
default:
break;
}
}
}
#endif
return CHANNEL_RC_OK;
error_out:
free(mac);
return error;
}

View File

@ -1,36 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2013 Armin Novak <armin.novak@gmail.com>
#
# 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_subsystem("audin" "opensles" "")
find_package(OpenSLES REQUIRED)
set(${MODULE_PREFIX}_SRCS
opensl_io.c
audin_opensl_es.c
)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${OpenSLES_LIBRARIES}
)
include_directories(..)
include_directories(SYSTEM ${OpenSLES_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")

View File

@ -1,336 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - OpenSL ES implementation
*
* Copyright 2013 Armin Novak <armin.novak@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <freerdp/freerdp.h>
#include <freerdp/addin.h>
#include <freerdp/channels/rdpsnd.h>
#include <SLES/OpenSLES.h>
#include <freerdp/client/audin.h>
#include "audin_main.h"
#include "opensl_io.h"
typedef struct
{
IAudinDevice iface;
char* device_name;
OPENSL_STREAM* stream;
AUDIO_FORMAT format;
UINT32 frames_per_packet;
UINT32 bytes_per_channel;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
wLog* log;
} AudinOpenSLESDevice;
static UINT audin_opensles_close(IAudinDevice* device);
static void audin_receive(void* context, const void* data, size_t size)
{
UINT error;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)context;
if (!opensles || !data)
{
WLog_ERR(TAG, "Invalid arguments context=%p, data=%p", opensles, data);
return;
}
error = opensles->receive(&opensles->format, data, size, opensles->user_data);
if (error && opensles->rdpcontext)
setChannelError(opensles->rdpcontext, error, "audin_receive reported an error");
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_free(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
if (!opensles)
return ERROR_INVALID_PARAMETER;
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
free(opensles->device_name);
free(opensles);
return CHANNEL_RC_OK;
}
static BOOL audin_opensles_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
if (!opensles || !format)
return FALSE;
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p", (void*)opensles, (void*)format);
WINPR_ASSERT(format);
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM: /* PCM */
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= 2))
{
return TRUE;
}
break;
default:
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04" PRIX16 "] not supported",
audio_format_get_tag_string(format->wFormatTag), format->wFormatTag);
break;
}
return FALSE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
if (!opensles || !format)
return ERROR_INVALID_PARAMETER;
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%" PRIu32 "",
(void*)device, (void*)format, FramesPerPacket);
WINPR_ASSERT(format);
opensles->format = *format;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
opensles->frames_per_packet = FramesPerPacket;
switch (format->wBitsPerSample)
{
case 4:
opensles->bytes_per_channel = 1;
break;
case 8:
opensles->bytes_per_channel = 1;
break;
case 16:
opensles->bytes_per_channel = 2;
break;
default:
return ERROR_UNSUPPORTED_TYPE;
}
break;
default:
WLog_Print(opensles->log, WLOG_ERROR,
"Encoding '%" PRIu16 "' [%04" PRIX16 "] not supported", format->wFormatTag,
format->wFormatTag);
return ERROR_UNSUPPORTED_TYPE;
}
WLog_Print(opensles->log, WLOG_DEBUG, "frames_per_packet=%" PRIu32,
opensles->frames_per_packet);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
if (!opensles || !receive || !user_data)
return ERROR_INVALID_PARAMETER;
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*)device,
(void*)receive, (void*)user_data);
if (opensles->stream)
goto error_out;
if (!(opensles->stream = android_OpenRecDevice(
opensles, audin_receive, opensles->format.nSamplesPerSec, opensles->format.nChannels,
opensles->frames_per_packet, opensles->format.wBitsPerSample)))
{
WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
goto error_out;
}
opensles->receive = receive;
opensles->user_data = user_data;
return CHANNEL_RC_OK;
error_out:
audin_opensles_close(device);
return ERROR_INTERNAL_ERROR;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT audin_opensles_close(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
if (!opensles)
return ERROR_INVALID_PARAMETER;
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
android_CloseRecDevice(opensles->stream);
opensles->receive = NULL;
opensles->user_data = NULL;
opensles->stream = NULL;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, const ADDIN_ARGV* args)
{
UINT status;
DWORD flags;
const COMMAND_LINE_ARGUMENT_A* arg;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL,
"audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, args=%p", (void*)device, (void*)args);
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_opensles_args, flags,
opensles, NULL, NULL);
if (status < 0)
return status;
arg = audin_opensles_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
{
opensles->device_name = _strdup(arg->Value);
if (!opensles->device_name)
{
WLog_Print(opensles->log, WLOG_ERROR, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
{
const ADDIN_ARGV* args;
AudinOpenSLESDevice* opensles;
UINT error;
opensles = (AudinOpenSLESDevice*)calloc(1, sizeof(AudinOpenSLESDevice));
if (!opensles)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
opensles->log = WLog_Get(TAG);
opensles->iface.Open = audin_opensles_open;
opensles->iface.FormatSupported = audin_opensles_format_supported;
opensles->iface.SetFormat = audin_opensles_set_format;
opensles->iface.Close = audin_opensles_close;
opensles->iface.Free = audin_opensles_free;
opensles->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_opensles_parse_addin_args(opensles, args)))
{
WLog_Print(opensles->log, WLOG_ERROR,
"audin_opensles_parse_addin_args failed with errorcode %" PRIu32 "!", error);
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)opensles)))
{
WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(opensles);
return error;
}

View File

@ -1,388 +0,0 @@
/*
opensl_io.c:
Android OpenSL input/output module
Copyright (c) 2012, Victor Lazzarini
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <winpr/assert.h>
#include "audin_main.h"
#include "opensl_io.h"
#define CONV16BIT 32768
#define CONVMYFLT (1. / 32768.)
typedef struct
{
size_t size;
void* data;
} queue_element;
struct opensl_stream
{
// engine interfaces
SLObjectItf engineObject;
SLEngineItf engineEngine;
// device interfaces
SLDeviceVolumeItf deviceVolume;
// recorder interfaces
SLObjectItf recorderObject;
SLRecordItf recorderRecord;
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
unsigned int inchannels;
unsigned int sr;
unsigned int buffersize;
unsigned int bits_per_sample;
queue_element* prep;
queue_element* next;
void* context;
opensl_receive_t receive;
};
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
// creates the OpenSL ES audio engine
static SLresult openSLCreateEngine(OPENSL_STREAM* p)
{
SLresult result;
// create engine
result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS)
goto engine_end;
// realize the engine
result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
goto engine_end;
// get the engine interface, which is needed in order to create other objects
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
if (result != SL_RESULT_SUCCESS)
goto engine_end;
// get the volume interface - important, this is optional!
result =
(*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME, &(p->deviceVolume));
if (result != SL_RESULT_SUCCESS)
{
p->deviceVolume = NULL;
result = SL_RESULT_SUCCESS;
}
engine_end:
WINPR_ASSERT(SL_RESULT_SUCCESS == result);
return result;
}
// Open the OpenSL ES device for input
static SLresult openSLRecOpen(OPENSL_STREAM* p)
{
SLresult result;
SLuint32 sr = p->sr;
SLuint32 channels = p->inchannels;
WINPR_ASSERT(!p->recorderObject);
if (channels)
{
switch (sr)
{
case 8000:
sr = SL_SAMPLINGRATE_8;
break;
case 11025:
sr = SL_SAMPLINGRATE_11_025;
break;
case 16000:
sr = SL_SAMPLINGRATE_16;
break;
case 22050:
sr = SL_SAMPLINGRATE_22_05;
break;
case 24000:
sr = SL_SAMPLINGRATE_24;
break;
case 32000:
sr = SL_SAMPLINGRATE_32;
break;
case 44100:
sr = SL_SAMPLINGRATE_44_1;
break;
case 48000:
sr = SL_SAMPLINGRATE_48;
break;
case 64000:
sr = SL_SAMPLINGRATE_64;
break;
case 88200:
sr = SL_SAMPLINGRATE_88_2;
break;
case 96000:
sr = SL_SAMPLINGRATE_96;
break;
case 192000:
sr = SL_SAMPLINGRATE_192;
break;
default:
return -1;
}
// configure audio source
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
SLDataSource audioSrc = { &loc_dev, NULL };
// configure audio sink
int speakers;
if (channels > 1)
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
else
speakers = SL_SPEAKER_FRONT_CENTER;
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
2 };
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = channels;
format_pcm.samplesPerSec = sr;
format_pcm.channelMask = speakers;
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
if (16 == p->bits_per_sample)
{
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
format_pcm.containerSize = 16;
}
else if (8 == p->bits_per_sample)
{
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
format_pcm.containerSize = 8;
}
else
WINPR_ASSERT(0);
SLDataSink audioSnk = { &loc_bq, &format_pcm };
// create audio recorder
// (requires the RECORD_AUDIO permission)
const SLInterfaceID id[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
const SLboolean req[] = { SL_BOOLEAN_TRUE };
result = (*p->engineEngine)
->CreateAudioRecorder(p->engineEngine, &(p->recorderObject), &audioSrc,
&audioSnk, 1, id, req);
WINPR_ASSERT(!result);
if (SL_RESULT_SUCCESS != result)
goto end_recopen;
// realize the audio recorder
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
WINPR_ASSERT(!result);
if (SL_RESULT_SUCCESS != result)
goto end_recopen;
// get the record interface
result = (*p->recorderObject)
->GetInterface(p->recorderObject, SL_IID_RECORD, &(p->recorderRecord));
WINPR_ASSERT(!result);
if (SL_RESULT_SUCCESS != result)
goto end_recopen;
// get the buffer queue interface
result = (*p->recorderObject)
->GetInterface(p->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&(p->recorderBufferQueue));
WINPR_ASSERT(!result);
if (SL_RESULT_SUCCESS != result)
goto end_recopen;
// register callback on the buffer queue
result = (*p->recorderBufferQueue)
->RegisterCallback(p->recorderBufferQueue, bqRecorderCallback, p);
WINPR_ASSERT(!result);
if (SL_RESULT_SUCCESS != result)
goto end_recopen;
end_recopen:
return result;
}
else
return SL_RESULT_SUCCESS;
}
// close the OpenSL IO and destroy the audio engine
static void openSLDestroyEngine(OPENSL_STREAM* p)
{
// destroy audio recorder object, and invalidate all associated interfaces
if (p->recorderObject != NULL)
{
(*p->recorderObject)->Destroy(p->recorderObject);
p->recorderObject = NULL;
p->recorderRecord = NULL;
p->recorderBufferQueue = NULL;
}
// destroy engine object, and invalidate all associated interfaces
if (p->engineObject != NULL)
{
(*p->engineObject)->Destroy(p->engineObject);
p->engineObject = NULL;
p->engineEngine = NULL;
}
}
static queue_element* opensles_queue_element_new(size_t size)
{
queue_element* q = calloc(1, sizeof(queue_element));
if (!q)
goto fail;
q->size = size;
q->data = malloc(size);
if (!q->data)
goto fail;
return q;
fail:
free(q);
return NULL;
}
static void opensles_queue_element_free(void* obj)
{
queue_element* e = (queue_element*)obj;
if (e)
free(e->data);
free(e);
}
// open the android audio device for input
OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive, int sr,
int inchannels, int bufferframes, int bits_per_sample)
{
OPENSL_STREAM* p;
if (!context || !receive)
return NULL;
p = (OPENSL_STREAM*)calloc(1, sizeof(OPENSL_STREAM));
if (!p)
return NULL;
p->context = context;
p->receive = receive;
p->inchannels = inchannels;
p->sr = sr;
p->buffersize = bufferframes;
p->bits_per_sample = bits_per_sample;
if ((p->bits_per_sample != 8) && (p->bits_per_sample != 16))
goto fail;
if (openSLCreateEngine(p) != SL_RESULT_SUCCESS)
goto fail;
if (openSLRecOpen(p) != SL_RESULT_SUCCESS)
goto fail;
/* Create receive buffers, prepare them and start recording */
p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
if (!p->prep || !p->next)
goto fail;
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, p->next->data, p->next->size);
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, p->prep->data, p->prep->size);
(*p->recorderRecord)->SetRecordState(p->recorderRecord, SL_RECORDSTATE_RECORDING);
return p;
fail:
android_CloseRecDevice(p);
return NULL;
}
// close the android audio device
void android_CloseRecDevice(OPENSL_STREAM* p)
{
if (p == NULL)
return;
opensles_queue_element_free(p->next);
opensles_queue_element_free(p->prep);
openSLDestroyEngine(p);
free(p);
}
// this callback handler is called every time a buffer finishes recording
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
OPENSL_STREAM* p = (OPENSL_STREAM*)context;
queue_element* e;
if (!p)
return;
e = p->next;
if (!e)
return;
if (!p->context || !p->receive)
WLog_WARN(TAG, "Missing receive callback=%p, context=%p", p->receive, p->context);
else
p->receive(p->context, e->data, e->size);
p->next = p->prep;
p->prep = e;
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, e->data, e->size);
}

View File

@ -1,65 +0,0 @@
/*
opensl_io.c:
Android OpenSL input/output module header
Copyright (c) 2012, Victor Lazzarini
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FREERDP_CHANNEL_AUDIN_CLIENT_OPENSL_IO_H
#define FREERDP_CHANNEL_AUDIN_CLIENT_OPENSL_IO_H
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <freerdp/api.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct opensl_stream OPENSL_STREAM;
typedef void (*opensl_receive_t)(void* context, const void* data, size_t size);
/*
Open the audio device with a given sampling rate (sr), input and output channels and IO buffer
size in frames. Returns a handle to the OpenSL stream
*/
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
int sr, int inchannels, int bufferframes,
int bits_per_sample);
/*
Close the audio device
*/
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
#ifdef __cplusplus
};
#endif
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_OPENSL_IO_H */

View File

@ -1,42 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
#
# 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_subsystem("audin" "oss" "")
find_package(OSS REQUIRED)
set(${MODULE_PREFIX}_SRCS
audin_oss.c
)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${OSS_LIBRARIES}
)
include_directories(..)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(SYSTEM ${OSS_INCLUDE_DIRS})
cleaning_configure_file(
${CMAKE_SOURCE_DIR}/cmake/oss-includes.h.in
${CMAKE_CURRENT_BINARY_DIR}/oss-includes.h
@ONLY
)
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")

View File

@ -1,486 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - OSS implementation
*
* Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/string.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <unistd.h>
#include <oss-includes.h>
#include <sys/ioctl.h>
#include <freerdp/freerdp.h>
#include <freerdp/addin.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
typedef struct
{
IAudinDevice iface;
HANDLE thread;
HANDLE stopEvent;
AUDIO_FORMAT format;
UINT32 FramesPerPacket;
int dev_unit;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
} AudinOSSDevice;
static void OSS_LOG_ERR(const char* _text, int _error)
{
if ((_error) != 0)
{
char buffer[256] = { 0 };
WLog_ERR(TAG, "%s: %i - %s\n", (_text), (_error),
winpr_strerror((_error), buffer, sizeof(buffer)));
}
}
static UINT32 audin_oss_get_format(const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
return AFMT_S8;
case 16:
return AFMT_S16_LE;
default:
break;
}
break;
default:
break;
}
return 0;
}
static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
if (device == NULL || format == NULL)
return FALSE;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
if (format->cbSize != 0 || format->nSamplesPerSec > 48000 ||
(format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
(format->nChannels != 1 && format->nChannels != 2))
return FALSE;
break;
default:
return FALSE;
}
return TRUE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_oss_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
AudinOSSDevice* oss = (AudinOSSDevice*)device;
if (device == NULL || format == NULL)
return ERROR_INVALID_PARAMETER;
oss->FramesPerPacket = FramesPerPacket;
oss->format = *format;
return CHANNEL_RC_OK;
}
static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
{
char dev_name[PATH_MAX] = "/dev/dsp";
char mixer_name[PATH_MAX] = "/dev/mixer";
int pcm_handle = -1;
int mixer_handle = 0;
BYTE* buffer = NULL;
unsigned long tmp = 0;
size_t buffer_size = 0;
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
UINT error = 0;
DWORD status = 0;
if (oss == NULL)
{
error = ERROR_INVALID_PARAMETER;
goto err_out;
}
if (oss->dev_unit != -1)
{
(void)sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
(void)sprintf_s(mixer_name, PATH_MAX - 1, "/dev/mixer%i", oss->dev_unit);
}
WLog_INFO(TAG, "open: %s", dev_name);
if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
{
OSS_LOG_ERR("sound dev open failed", errno);
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
/* Set rec volume to 100%. */
if ((mixer_handle = open(mixer_name, O_RDWR)) < 0)
{
OSS_LOG_ERR("mixer open failed, not critical", errno);
}
else
{
tmp = (100 | (100 << 8));
if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_MIC), &tmp) == -1)
OSS_LOG_ERR("WRITE_MIXER - SOUND_MIXER_MIC, not critical", errno);
tmp = (100 | (100 << 8));
if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_RECLEV), &tmp) == -1)
OSS_LOG_ERR("WRITE_MIXER - SOUND_MIXER_RECLEV, not critical", errno);
close(mixer_handle);
}
#if 0 /* FreeBSD OSS implementation at this moment (2015.03) does not set PCM_CAP_INPUT flag. */
tmp = 0;
if (ioctl(pcm_handle, SNDCTL_DSP_GETCAPS, &tmp) == -1)
{
OSS_LOG_ERR("SNDCTL_DSP_GETCAPS failed, try ignory", errno);
}
else if ((tmp & PCM_CAP_INPUT) == 0)
{
OSS_LOG_ERR("Device does not supports playback", EOPNOTSUPP);
goto err_out;
}
#endif
/* Set format. */
tmp = audin_oss_get_format(&oss->format);
if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
tmp = oss->format.nChannels;
if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
tmp = oss->format.nSamplesPerSec;
if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
tmp = oss->format.nBlockAlign;
if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
buffer_size =
(1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
if (NULL == buffer)
{
OSS_LOG_ERR("malloc() fail", errno);
error = ERROR_NOT_ENOUGH_MEMORY;
goto err_out;
}
while (1)
{
SSIZE_T stmp = -1;
status = WaitForSingleObject(oss->stopEvent, 0);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
goto err_out;
}
if (status == WAIT_OBJECT_0)
break;
stmp = read(pcm_handle, buffer, buffer_size);
/* Error happen. */
if (stmp < 0)
{
OSS_LOG_ERR("read() error", errno);
continue;
}
if ((size_t)stmp < buffer_size) /* Not enouth data. */
continue;
if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
{
WLog_ERR(TAG, "oss->receive failed with error %" PRIu32 "", error);
break;
}
}
err_out:
if (error && oss && oss->rdpcontext)
setChannelError(oss->rdpcontext, error, "audin_oss_thread_func reported an error");
if (pcm_handle != -1)
{
WLog_INFO(TAG, "close: %s", dev_name);
close(pcm_handle);
}
free(buffer);
ExitThread(error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinOSSDevice* oss = (AudinOSSDevice*)device;
oss->receive = receive;
oss->user_data = user_data;
if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
(void)CloseHandle(oss->stopEvent);
oss->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_oss_close(IAudinDevice* device)
{
UINT error = 0;
AudinOSSDevice* oss = (AudinOSSDevice*)device;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if (oss->stopEvent != NULL)
{
(void)SetEvent(oss->stopEvent);
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
(void)CloseHandle(oss->stopEvent);
oss->stopEvent = NULL;
(void)CloseHandle(oss->thread);
oss->thread = NULL;
}
oss->receive = NULL;
oss->user_data = NULL;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_oss_free(IAudinDevice* device)
{
AudinOSSDevice* oss = (AudinOSSDevice*)device;
UINT error = 0;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if ((error = audin_oss_close(device)))
{
WLog_ERR(TAG, "audin_oss_close failed with error code %" PRIu32 "!", error);
}
free(oss);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, const ADDIN_ARGV* args)
{
int status = 0;
char* str_num = NULL;
char* eptr = NULL;
DWORD flags = 0;
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
AudinOSSDevice* oss = device;
COMMAND_LINE_ARGUMENT_A audin_oss_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status =
CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
if (status < 0)
return ERROR_INVALID_PARAMETER;
arg = audin_oss_args;
errno = 0;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
{
str_num = _strdup(arg->Value);
if (!str_num)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
{
long val = strtol(str_num, &eptr, 10);
if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
{
free(str_num);
return CHANNEL_RC_NULL_DATA;
}
oss->dev_unit = (INT32)val;
}
if (oss->dev_unit < 0 || *eptr != '\0')
oss->dev_unit = -1;
free(str_num);
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
{
const ADDIN_ARGV* args = NULL;
AudinOSSDevice* oss = NULL;
UINT error = 0;
oss = (AudinOSSDevice*)calloc(1, sizeof(AudinOSSDevice));
if (!oss)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
oss->iface.Open = audin_oss_open;
oss->iface.FormatSupported = audin_oss_format_supported;
oss->iface.SetFormat = audin_oss_set_format;
oss->iface.Close = audin_oss_close;
oss->iface.Free = audin_oss_free;
oss->rdpcontext = pEntryPoints->rdpcontext;
oss->dev_unit = -1;
args = pEntryPoints->args;
if ((error = audin_oss_parse_addin_args(oss, args)))
{
WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %" PRIu32 "!", error);
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(oss);
return error;
}

View File

@ -17,19 +17,25 @@
define_channel_client_subsystem("audin" "pulse" "")
find_package(PulseAudio REQUIRED)
set(${MODULE_PREFIX}_SRCS
audin_pulse.c)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${PULSEAUDIO_LIBRARY}
${PULSEAUDIO_MAINLOOP_LIBRARY}
)
include_directories(..)
include_directories(SYSTEM ${PULSEAUDIO_INCLUDE_DIR})
include_directories(${PULSE_INCLUDE_DIR})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${PULSE_LIBRARY})
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
if(NOT STATIC_CHANNELS)
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH})
endif()

View File

@ -3,8 +3,6 @@
* Audio Input Redirection Virtual Channel - PulseAudio implementation
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +17,9 @@
* limitations under the License.
*/
#include <freerdp/config.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
@ -27,19 +27,16 @@
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <winpr/wlog.h>
#include <pulse/pulseaudio.h>
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/freerdp.h>
#include <freerdp/codec/audio.h>
#include <freerdp/client/audin.h>
#include <freerdp/codec/dsp.h>
#include "audin_main.h"
typedef struct
typedef struct _AudinPulseDevice
{
IAudinDevice iface;
@ -49,414 +46,423 @@ typedef struct
pa_context* context;
pa_sample_spec sample_spec;
pa_stream* stream;
AUDIO_FORMAT format;
int format;
int block_size;
size_t bytes_per_frame;
size_t buffer_frames;
FREERDP_DSP_CONTEXT* dsp_context;
int bytes_per_frame;
BYTE* buffer;
int buffer_frames;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
wLog* log;
} AudinPulseDevice;
static const char* pulse_context_state_string(pa_context_state_t state)
{
switch (state)
{
case PA_CONTEXT_UNCONNECTED:
return "PA_CONTEXT_UNCONNECTED";
case PA_CONTEXT_CONNECTING:
return "PA_CONTEXT_CONNECTING";
case PA_CONTEXT_AUTHORIZING:
return "PA_CONTEXT_AUTHORIZING";
case PA_CONTEXT_SETTING_NAME:
return "PA_CONTEXT_SETTING_NAME";
case PA_CONTEXT_READY:
return "PA_CONTEXT_READY";
case PA_CONTEXT_FAILED:
return "PA_CONTEXT_FAILED";
case PA_CONTEXT_TERMINATED:
return "PA_CONTEXT_TERMINATED";
default:
return "UNKNOWN";
}
}
static const char* pulse_stream_state_string(pa_stream_state_t state)
{
switch (state)
{
case PA_STREAM_UNCONNECTED:
return "PA_STREAM_UNCONNECTED";
case PA_STREAM_CREATING:
return "PA_STREAM_CREATING";
case PA_STREAM_READY:
return "PA_STREAM_READY";
case PA_STREAM_FAILED:
return "PA_STREAM_FAILED";
case PA_STREAM_TERMINATED:
return "PA_STREAM_TERMINATED";
default:
return "UNKNOWN";
}
}
static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
{
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
pa_context_state_t state = pa_context_get_state(context);
pa_context_state_t state;
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
WLog_Print(pulse->log, WLOG_DEBUG, "context state %s", pulse_context_state_string(state));
state = pa_context_get_state(context);
switch (state)
{
case PA_CONTEXT_READY:
DEBUG_DVC("PA_CONTEXT_READY");
pa_threaded_mainloop_signal (pulse->mainloop, 0);
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
pa_threaded_mainloop_signal(pulse->mainloop, 0);
DEBUG_DVC("state %d", (int)state);
pa_threaded_mainloop_signal (pulse->mainloop, 0);
break;
default:
DEBUG_DVC("state %d", (int)state);
break;
}
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_connect(IAudinDevice* device)
static BOOL audin_pulse_connect(IAudinDevice* device)
{
pa_context_state_t state = PA_CONTEXT_FAILED;
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
pa_context_state_t state;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return ERROR_INVALID_PARAMETER;
return FALSE;
if (pa_context_connect(pulse->context, NULL, 0, NULL))
{
WLog_Print(pulse->log, WLOG_ERROR, "pa_context_connect failed (%d)",
pa_context_errno(pulse->context));
return ERROR_INTERNAL_ERROR;
DEBUG_WARN("pa_context_connect failed (%d)",
pa_context_errno(pulse->context));
return FALSE;
}
pa_threaded_mainloop_lock(pulse->mainloop);
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_Print(pulse->log, WLOG_ERROR, "pa_threaded_mainloop_start failed (%d)",
pa_context_errno(pulse->context));
return ERROR_INTERNAL_ERROR;
DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
pa_context_errno(pulse->context));
return FALSE;
}
for (;;)
{
state = pa_context_get_state(pulse->context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state))
{
WLog_Print(pulse->log, WLOG_ERROR, "bad context state (%s: %d)",
pulse_context_state_string(state), pa_context_errno(pulse->context));
pa_context_disconnect(pulse->context);
return ERROR_INVALID_STATE;
DEBUG_WARN("bad context state (%d)",
pa_context_errno(pulse->context));
break;
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
return CHANNEL_RC_OK;
if (state == PA_CONTEXT_READY)
{
DEBUG_DVC("connected");
return TRUE;
}
else
{
pa_context_disconnect(pulse->context);
return FALSE;
}
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_free(IAudinDevice* device)
static void audin_pulse_free(IAudinDevice* device)
{
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
DEBUG_DVC("");
if (!pulse)
return ERROR_INVALID_PARAMETER;
return;
if (pulse->mainloop)
{
pa_threaded_mainloop_stop(pulse->mainloop);
}
if (pulse->context)
{
pa_context_disconnect(pulse->context);
pa_context_unref(pulse->context);
pulse->context = NULL;
}
if (pulse->mainloop)
{
pa_threaded_mainloop_free(pulse->mainloop);
pulse->mainloop = NULL;
}
freerdp_dsp_context_free(pulse->dsp_context);
free(pulse);
return CHANNEL_RC_OK;
}
static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
{
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
if (!pulse || !format)
return FALSE;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return 0;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
case 1: /* PCM */
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
{
return TRUE;
}
break;
default:
return FALSE;
}
case 6: /* A-LAW */
case 7: /* 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;
case 0x11: /* IMA ADPCM */
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
}
return FALSE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
{
int bs;
pa_sample_spec sample_spec = { 0 };
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
if (!pulse || !format)
return ERROR_INVALID_PARAMETER;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return ERROR_INVALID_PARAMETER;
return;
if (FramesPerPacket > 0)
{
pulse->frames_per_packet = FramesPerPacket;
}
sample_spec.rate = format->nSamplesPerSec;
sample_spec.channels = format->nChannels;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM: /* PCM */
case 1: /* PCM */
switch (format->wBitsPerSample)
{
case 8:
sample_spec.format = PA_SAMPLE_U8;
break;
case 16:
sample_spec.format = PA_SAMPLE_S16LE;
break;
default:
return ERROR_INTERNAL_ERROR;
}
break;
default:
return ERROR_INTERNAL_ERROR;
case 6: /* A-LAW */
sample_spec.format = PA_SAMPLE_ALAW;
break;
case 7: /* U-LAW */
sample_spec.format = PA_SAMPLE_ULAW;
break;
case 0x11: /* IMA ADPCM */
sample_spec.format = PA_SAMPLE_S16LE;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
pulse->frames_per_packet = (pulse->frames_per_packet * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
DEBUG_DVC("aligned FramesPerPacket=%d",
pulse->frames_per_packet);
break;
}
pulse->sample_spec = sample_spec;
pulse->format = *format;
return CHANNEL_RC_OK;
pulse->format = format->wFormatTag;
pulse->block_size = format->nBlockAlign;
}
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
{
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
WINPR_ASSERT(pulse);
pa_stream_state_t state;
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
pa_stream_state_t state = pa_stream_get_state(stream);
WLog_Print(pulse->log, WLOG_DEBUG, "stream state %s", pulse_stream_state_string(state));
state = pa_stream_get_state(stream);
switch (state)
{
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
DEBUG_DVC("PA_STREAM_READY");
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
DEBUG_DVC("state %d", (int)state);
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
default:
DEBUG_DVC("state %d", (int)state);
break;
}
}
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
{
const void* data = NULL;
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
UINT error = CHANNEL_RC_OK;
pa_stream_peek(stream, &data, &length);
error =
IFCALLRESULT(CHANNEL_RC_OK, pulse->receive, &pulse->format, data, length, pulse->user_data);
pa_stream_drop(stream);
int frames;
int cframes;
BOOL ret;
const void* data;
const BYTE* src;
int encoded_size;
BYTE* encoded_data;
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
if (error && pulse->rdpcontext)
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
/* There is a race condition here where we may receive this callback
* before the buffer has been set up in the main code. It's probably
* possible to fix with additional locking, but it's easier just to
* ignore input until the buffer is ready.
*/
if (pulse->buffer == NULL)
{
/* fprintf(stderr, "%s: ignoring input, pulse buffer not ready.\n", __func__); */
return;
}
pa_stream_peek(stream, &data, &length);
frames = length / pulse->bytes_per_frame;
DEBUG_DVC("length %d frames %d", (int) length, frames);
src = (const BYTE*) data;
while (frames > 0)
{
cframes = pulse->frames_per_packet - pulse->buffer_frames;
if (cframes > frames)
cframes = frames;
memcpy(pulse->buffer + pulse->buffer_frames * pulse->bytes_per_frame,
src, cframes * pulse->bytes_per_frame);
pulse->buffer_frames += cframes;
if (pulse->buffer_frames >= pulse->frames_per_packet)
{
if (pulse->format == 0x11)
{
pulse->dsp_context->encode_ima_adpcm(pulse->dsp_context,
pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
pulse->sample_spec.channels, pulse->block_size);
encoded_data = pulse->dsp_context->adpcm_buffer;
encoded_size = pulse->dsp_context->adpcm_size;
DEBUG_DVC("encoded %d to %d",
pulse->buffer_frames * pulse->bytes_per_frame, encoded_size);
}
else
{
encoded_data = pulse->buffer;
encoded_size = pulse->buffer_frames * pulse->bytes_per_frame;
}
ret = pulse->receive(encoded_data, encoded_size, pulse->user_data);
pulse->buffer_frames = 0;
if (!ret)
break;
}
src += cframes * pulse->bytes_per_frame;
frames -= cframes;
}
pa_stream_drop(stream);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_close(IAudinDevice* device)
static void audin_pulse_close(IAudinDevice* device)
{
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse)
return ERROR_INVALID_PARAMETER;
if (!pulse->context || !pulse->stream)
return;
if (pulse->stream)
{
pa_threaded_mainloop_lock(pulse->mainloop);
pa_stream_disconnect(pulse->stream);
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
pa_threaded_mainloop_unlock(pulse->mainloop);
}
DEBUG_DVC("");
pa_threaded_mainloop_lock(pulse->mainloop);
pa_stream_disconnect(pulse->stream);
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
pa_threaded_mainloop_unlock(pulse->mainloop);
pulse->receive = NULL;
pulse->user_data = NULL;
return CHANNEL_RC_OK;
if (pulse->buffer)
{
free(pulse->buffer);
pulse->buffer = NULL;
pulse->buffer_frames = 0;
}
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
pa_stream_state_t state = PA_STREAM_FAILED;
pa_stream_state_t state;
pa_buffer_attr buffer_attr = { 0 };
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
if (!pulse || !receive || !user_data)
return ERROR_INVALID_PARAMETER;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return ERROR_INVALID_PARAMETER;
return;
if (!pulse->sample_spec.rate || pulse->stream)
return ERROR_INVALID_PARAMETER;
return;
DEBUG_DVC("");
pulse->buffer = NULL;
pulse->receive = receive;
pulse->user_data = user_data;
pa_threaded_mainloop_lock(pulse->mainloop);
pulse->stream = pa_stream_new(pulse->context, "freerdp_audin", &pulse->sample_spec, NULL);
pa_threaded_mainloop_lock(pulse->mainloop);
pulse->stream = pa_stream_new(pulse->context, "freerdp_audin",
&pulse->sample_spec, NULL);
if (!pulse->stream)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_Print(pulse->log, WLOG_DEBUG, "pa_stream_new failed (%d)",
pa_context_errno(pulse->context));
return pa_context_errno(pulse->context);
DEBUG_DVC("pa_stream_new failed (%d)",
pa_context_errno(pulse->context));
return;
}
pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
pa_stream_set_state_callback(pulse->stream, audin_pulse_stream_state_callback, pulse);
pa_stream_set_read_callback(pulse->stream, audin_pulse_stream_request_callback, pulse);
buffer_attr.maxlength = (UINT32)-1;
buffer_attr.tlength = (UINT32)-1;
buffer_attr.prebuf = (UINT32)-1;
buffer_attr.minreq = (UINT32)-1;
pa_stream_set_state_callback(pulse->stream,
audin_pulse_stream_state_callback, pulse);
pa_stream_set_read_callback(pulse->stream,
audin_pulse_stream_request_callback, pulse);
buffer_attr.maxlength = (UINT32) -1;
buffer_attr.tlength = (UINT32) -1;
buffer_attr.prebuf = (UINT32) -1;
buffer_attr.minreq = (UINT32) -1;
/* 500ms latency */
const size_t frag = pulse->bytes_per_frame * pulse->frames_per_packet;
WINPR_ASSERT(frag <= UINT32_MAX);
buffer_attr.fragsize = (uint32_t)frag;
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
buffer_attr.fragsize +=
pulse->format.nBlockAlign - buffer_attr.fragsize % pulse->format.nBlockAlign;
if (pa_stream_connect_record(pulse->stream, pulse->device_name, &buffer_attr,
PA_STREAM_ADJUST_LATENCY) < 0)
buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec);
if (pa_stream_connect_record(pulse->stream,
pulse->device_name[0] ? pulse->device_name : NULL,
&buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_Print(pulse->log, WLOG_ERROR, "pa_stream_connect_playback failed (%d)",
pa_context_errno(pulse->context));
return pa_context_errno(pulse->context);
DEBUG_WARN("pa_stream_connect_playback failed (%d)",
pa_context_errno(pulse->context));
return;
}
while (pulse->stream)
for (;;)
{
state = pa_stream_get_state(pulse->stream);
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state))
{
audin_pulse_close(device);
WLog_Print(pulse->log, WLOG_ERROR, "bad stream state (%s: %d)",
pulse_stream_state_string(state), pa_context_errno(pulse->context));
pa_threaded_mainloop_unlock(pulse->mainloop);
return pa_context_errno(pulse->context);
DEBUG_WARN("bad stream state (%d)",
pa_context_errno(pulse->context));
break;
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_threaded_mainloop_unlock(pulse->mainloop);
pulse->buffer_frames = 0;
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
return CHANNEL_RC_OK;
if (state == PA_STREAM_READY)
{
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
pulse->buffer = malloc(pulse->bytes_per_frame * pulse->frames_per_packet);
ZeroMemory(pulse->buffer, pulse->bytes_per_frame * pulse->frames_per_packet);
pulse->buffer_frames = 0;
DEBUG_DVC("connected");
}
else
{
audin_pulse_close(device);
}
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, const ADDIN_ARGV* args)
COMMAND_LINE_ARGUMENT_A audin_pulse_args[] =
{
int status = 0;
DWORD flags = 0;
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
AudinPulseDevice* pulse = device;
COMMAND_LINE_ARGUMENT_A audin_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_pulse_args, flags, pulse,
NULL, NULL);
static void audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (status < 0)
return ERROR_INVALID_PARAMETER;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_pulse_args, flags, pulse, NULL, NULL);
arg = audin_pulse_args;
@ -465,92 +471,72 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, const ADDIN_A
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "audio-dev")
{
pulse->device_name = _strdup(arg->Value);
if (!pulse->device_name)
{
WLog_Print(pulse->log, WLOG_ERROR, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE pulse_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry pulse_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
const ADDIN_ARGV* args = NULL;
AudinPulseDevice* pulse = NULL;
UINT error = 0;
pulse = (AudinPulseDevice*)calloc(1, sizeof(AudinPulseDevice));
ADDIN_ARGV* args;
AudinPulseDevice* pulse;
if (!pulse)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
pulse = (AudinPulseDevice*) malloc(sizeof(AudinPulseDevice));
ZeroMemory(pulse, sizeof(AudinPulseDevice));
pulse->log = WLog_Get(TAG);
pulse->iface.Open = audin_pulse_open;
pulse->iface.FormatSupported = audin_pulse_format_supported;
pulse->iface.SetFormat = audin_pulse_set_format;
pulse->iface.Close = audin_pulse_close;
pulse->iface.Free = audin_pulse_free;
pulse->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_pulse_parse_addin_args(pulse, args)))
{
WLog_Print(pulse->log, WLOG_ERROR,
"audin_pulse_parse_addin_args failed with error %" PRIu32 "!", error);
goto error_out;
}
audin_pulse_parse_addin_args(pulse, args);
if (!pulse->device_name)
pulse->device_name = _strdup("default");
pulse->dsp_context = freerdp_dsp_context_new();
pulse->mainloop = pa_threaded_mainloop_new();
if (!pulse->mainloop)
{
WLog_Print(pulse->log, WLOG_ERROR, "pa_threaded_mainloop_new failed");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
DEBUG_WARN("pa_threaded_mainloop_new failed");
audin_pulse_free((IAudinDevice*) pulse);
return 1;
}
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
if (!pulse->context)
{
WLog_Print(pulse->log, WLOG_ERROR, "pa_context_new failed");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
DEBUG_WARN("pa_context_new failed");
audin_pulse_free((IAudinDevice*) pulse);
return 1;
}
pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
if ((error = audin_pulse_connect(&pulse->iface)))
if (!audin_pulse_connect((IAudinDevice*) pulse))
{
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_connect failed");
goto error_out;
audin_pulse_free((IAudinDevice*) pulse);
return 1;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &pulse->iface)))
{
WLog_Print(pulse->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
error);
goto error_out;
}
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse);
return CHANNEL_RC_OK;
error_out:
audin_pulse_free(&pulse->iface);
return error;
return 0;
}

View File

@ -1,36 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
# Copyright (c) 2020 Ingo Feinerer <feinerer@logic.at>
#
# 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_subsystem("audin" "sndio" "")
find_package(SNDIO REQUIRED)
set(${MODULE_PREFIX}_SRCS
audin_sndio.c)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
${SNDIO_LIBRARIES}
)
include_directories(..)
include_directories(SYSTEM ${SNDIO_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")

View File

@ -1,352 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - sndio implementation
*
* Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2020 Ingo Feinerer <feinerer@logic.at>
*
* 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.
*/
#include <freerdp/config.h>
#include <sndio.h>
#include <winpr/cmdline.h>
#include <freerdp/freerdp.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
typedef struct
{
IAudinDevice device;
HANDLE thread;
HANDLE stopEvent;
AUDIO_FORMAT format;
UINT32 FramesPerPacket;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
} AudinSndioDevice;
static BOOL audin_sndio_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
if (device == NULL || format == NULL)
return FALSE;
return (format->wFormatTag == WAVE_FORMAT_PCM);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_sndio_set_format(IAudinDevice* device, AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
if (device == NULL || format == NULL)
return ERROR_INVALID_PARAMETER;
if (format->wFormatTag != WAVE_FORMAT_PCM)
return ERROR_INTERNAL_ERROR;
sndio->format = *format;
sndio->FramesPerPacket = FramesPerPacket;
return CHANNEL_RC_OK;
}
static void* audin_sndio_thread_func(void* arg)
{
struct sio_hdl* hdl;
struct sio_par par;
BYTE* buffer = NULL;
size_t n, nbytes;
AudinSndioDevice* sndio = (AudinSndioDevice*)arg;
UINT error = 0;
DWORD status;
if (arg == NULL)
{
error = ERROR_INVALID_PARAMETER;
goto err_out;
}
hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
if (hdl == NULL)
{
WLog_ERR(TAG, "could not open audio device");
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
sio_initpar(&par);
par.bits = sndio->format.wBitsPerSample;
par.rchan = sndio->format.nChannels;
par.rate = sndio->format.nSamplesPerSec;
if (!sio_setpar(hdl, &par))
{
WLog_ERR(TAG, "could not set audio parameters");
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
if (!sio_getpar(hdl, &par))
{
WLog_ERR(TAG, "could not get audio parameters");
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
if (!sio_start(hdl))
{
WLog_ERR(TAG, "could not start audio device");
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
nbytes =
(sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));
buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));
if (buffer == NULL)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto err_out;
}
while (1)
{
status = WaitForSingleObject(sndio->stopEvent, 0);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
goto err_out;
}
if (status == WAIT_OBJECT_0)
break;
n = sio_read(hdl, buffer, nbytes);
if (n == 0)
{
WLog_ERR(TAG, "could not read");
continue;
}
if (n < nbytes)
continue;
if ((error = sndio->receive(&sndio->format, buffer, nbytes, sndio->user_data)))
{
WLog_ERR(TAG, "sndio->receive failed with error %" PRIu32 "", error);
break;
}
}
err_out:
if (error && sndio->rdpcontext)
setChannelError(sndio->rdpcontext, error, "audin_sndio_thread_func reported an error");
if (hdl != NULL)
{
WLog_INFO(TAG, "sio_close");
sio_stop(hdl);
sio_close(hdl);
}
free(buffer);
ExitThread(0);
return NULL;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_sndio_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
sndio->receive = receive;
sndio->user_data = user_data;
if (!(sndio->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed");
return ERROR_INTERNAL_ERROR;
}
if (!(sndio->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)audin_sndio_thread_func,
sndio, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed");
(void)CloseHandle(sndio->stopEvent);
sndio->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_sndio_close(IAudinDevice* device)
{
UINT error;
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if (sndio->stopEvent != NULL)
{
(void)SetEvent(sndio->stopEvent);
if (WaitForSingleObject(sndio->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
(void)CloseHandle(sndio->stopEvent);
sndio->stopEvent = NULL;
(void)CloseHandle(sndio->thread);
sndio->thread = NULL;
}
sndio->receive = NULL;
sndio->user_data = NULL;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_sndio_free(IAudinDevice* device)
{
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
int error;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if ((error = audin_sndio_close(device)))
{
WLog_ERR(TAG, "audin_sndio_close failed with error code %d", error);
}
free(sndio);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_sndio_parse_addin_args(AudinSndioDevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
COMMAND_LINE_ARGUMENT_A audin_sndio_args[] = { { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_sndio_args,
flags, sndio, NULL, NULL);
if (status < 0)
return ERROR_INVALID_PARAMETER;
arg = audin_sndio_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
{
ADDIN_ARGV* args;
AudinSndioDevice* sndio;
UINT ret = CHANNEL_RC_OK;
sndio = (AudinSndioDevice*)calloc(1, sizeof(AudinSndioDevice));
if (sndio == NULL)
return CHANNEL_RC_NO_MEMORY;
sndio->device.Open = audin_sndio_open;
sndio->device.FormatSupported = audin_sndio_format_supported;
sndio->device.SetFormat = audin_sndio_set_format;
sndio->device.Close = audin_sndio_close;
sndio->device.Free = audin_sndio_free;
sndio->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if (args->argc > 1)
{
ret = audin_sndio_parse_addin_args(sndio, args);
if (ret != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "error parsing arguments");
goto error;
}
}
if ((ret = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)sndio)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "", ret);
goto error;
}
return ret;
error:
audin_sndio_free(&sndio->device);
return ret;
}

View File

@ -1,31 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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_subsystem("audin" "winmm" "")
set(${MODULE_PREFIX}_SRCS
audin_winmm.c)
set(${MODULE_PREFIX}_LIBS
winpr
freerdp
winmm.lib
)
include_directories(..)
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")

View File

@ -1,566 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - WinMM implementation
*
* Copyright 2013 Zhang Zhaolong <zhangzl2013@126.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <mmsystem.h>
#include <winpr/crt.h>
#include <winpr/wtsapi.h>
#include <winpr/cmdline.h>
#include <freerdp/freerdp.h>
#include <freerdp/addin.h>
#include <freerdp/client/audin.h>
#include "audin_main.h"
/* fix missing definitions in mingw */
#ifndef WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE
#define WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE 0x0010
#endif
typedef struct
{
IAudinDevice iface;
char* device_name;
AudinReceive receive;
void* user_data;
HANDLE thread;
HANDLE stopEvent;
HWAVEIN hWaveIn;
PWAVEFORMATEX* ppwfx;
PWAVEFORMATEX pwfx_cur;
UINT32 ppwfx_size;
UINT32 cFormats;
UINT32 frames_per_packet;
rdpContext* rdpcontext;
wLog* log;
} AudinWinmmDevice;
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)dwInstance;
PWAVEHDR pWaveHdr;
UINT error = CHANNEL_RC_OK;
MMRESULT mmResult;
switch (uMsg)
{
case WIM_CLOSE:
break;
case WIM_DATA:
pWaveHdr = (WAVEHDR*)dwParam1;
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
{
if (pWaveHdr->dwBytesRecorded &&
!(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
{
AUDIO_FORMAT format;
format.cbSize = winmm->pwfx_cur->cbSize;
format.nBlockAlign = winmm->pwfx_cur->nBlockAlign;
format.nAvgBytesPerSec = winmm->pwfx_cur->nAvgBytesPerSec;
format.nChannels = winmm->pwfx_cur->nChannels;
format.nSamplesPerSec = winmm->pwfx_cur->nSamplesPerSec;
format.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
if ((error = winmm->receive(&format, pWaveHdr->lpData,
pWaveHdr->dwBytesRecorded, winmm->user_data)))
break;
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
if (mmResult != MMSYSERR_NOERROR)
error = ERROR_INTERNAL_ERROR;
}
}
break;
case WIM_OPEN:
break;
default:
break;
}
if (error && winmm->rdpcontext)
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)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
char* buffer = NULL;
int size = 0;
WAVEHDR waveHdr[4] = { 0 };
DWORD status = 0;
MMRESULT rc = 0;
if (!winmm->hWaveIn)
{
rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
(DWORD_PTR)winmm,
CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
if (!log_mmresult(winmm, "waveInOpen", rc))
return ERROR_INTERNAL_ERROR;
}
size =
(winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
7) /
8;
for (int i = 0; i < 4; i++)
{
buffer = (char*)malloc(size);
if (!buffer)
return CHANNEL_RC_NO_MEMORY;
waveHdr[i].dwBufferLength = size;
waveHdr[i].dwFlags = 0;
waveHdr[i].lpData = buffer;
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
{
}
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (!log_mmresult(winmm, "waveInAddBuffer", rc))
{
}
}
rc = waveInStart(winmm->hWaveIn);
if (!log_mmresult(winmm, "waveInStart", rc))
{
}
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
if (status == WAIT_FAILED)
{
WLog_Print(winmm->log, WLOG_DEBUG, "WaitForSingleObject failed.");
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
rc = waveInReset(winmm->hWaveIn);
if (!log_mmresult(winmm, "waveInReset", rc))
{
}
for (int i = 0; i < 4; i++)
{
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
{
}
free(waveHdr[i].lpData);
}
rc = waveInClose(winmm->hWaveIn);
if (!log_mmresult(winmm, "waveInClose", rc))
{
}
winmm->hWaveIn = NULL;
return 0;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_free(IAudinDevice* device)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
if (!winmm)
return ERROR_INVALID_PARAMETER;
for (UINT32 i = 0; i < winmm->cFormats; i++)
{
free(winmm->ppwfx[i]);
}
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_close(IAudinDevice* device)
{
DWORD status;
UINT error = CHANNEL_RC_OK;
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
if (!winmm)
return ERROR_INVALID_PARAMETER;
(void)SetEvent(winmm->stopEvent);
status = WaitForSingleObject(winmm->thread, INFINITE);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_Print(winmm->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
error);
return error;
}
(void)CloseHandle(winmm->thread);
(void)CloseHandle(winmm->stopEvent);
winmm->thread = NULL;
winmm->stopEvent = NULL;
winmm->receive = NULL;
winmm->user_data = NULL;
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
if (!winmm || !format)
return ERROR_INVALID_PARAMETER;
winmm->frames_per_packet = FramesPerPacket;
for (UINT32 i = 0; i < winmm->cFormats; i++)
{
const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
(ppwfx->wBitsPerSample == format->wBitsPerSample) &&
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
{
/* BUG: Many devices report to support stereo recording but fail here.
* 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 ERROR_INVALID_PARAMETER;
}
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
PWAVEFORMATEX pwfx;
BYTE* data;
if (!winmm || !format)
return FALSE;
if (format->wFormatTag != WAVE_FORMAT_PCM)
return FALSE;
if (format->nChannels != 1)
return FALSE;
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
if (!pwfx)
return FALSE;
pwfx->cbSize = format->cbSize;
pwfx->wFormatTag = format->wFormatTag;
pwfx->nChannels = format->nChannels;
pwfx->nSamplesPerSec = format->nSamplesPerSec;
pwfx->nBlockAlign = format->nBlockAlign;
pwfx->wBitsPerSample = format->wBitsPerSample;
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
memcpy(data, format->data, format->cbSize);
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
if (!test_format_supported(pwfx))
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)
goto fail;
winmm->ppwfx_size *= 2;
winmm->ppwfx = tmp_ppwfx;
}
winmm->ppwfx[winmm->cFormats++] = pwfx;
return TRUE;
fail:
free(pwfx);
return FALSE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
if (!winmm || !receive || !user_data)
return ERROR_INVALID_PARAMETER;
winmm->receive = receive;
winmm->user_data = user_data;
if (!(winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_Print(winmm->log, WLOG_ERROR, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
{
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
(void)CloseHandle(winmm->stopEvent);
winmm->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, const ADDIN_ARGV* args)
{
int status;
DWORD flags;
const COMMAND_LINE_ARGUMENT_A* arg;
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
COMMAND_LINE_ARGUMENT_A audin_winmm_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
flags =
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_winmm_args, flags, winmm,
NULL, NULL);
arg = audin_winmm_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
{
winmm->device_name = _strdup(arg->Value);
if (!winmm->device_name)
{
WLog_Print(winmm->log, WLOG_ERROR, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
{
const ADDIN_ARGV* args;
AudinWinmmDevice* winmm;
UINT error;
if (waveInGetNumDevs() == 0)
{
WLog_Print(WLog_Get(TAG), WLOG_ERROR, "No microphone available!");
return ERROR_DEVICE_NOT_AVAILABLE;
}
winmm = (AudinWinmmDevice*)calloc(1, sizeof(AudinWinmmDevice));
if (!winmm)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
winmm->log = WLog_Get(TAG);
winmm->iface.Open = audin_winmm_open;
winmm->iface.FormatSupported = audin_winmm_format_supported;
winmm->iface.SetFormat = audin_winmm_set_format;
winmm->iface.Close = audin_winmm_close;
winmm->iface.Free = audin_winmm_free;
winmm->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_winmm_parse_addin_args(winmm, args)))
{
WLog_Print(winmm->log, WLOG_ERROR,
"audin_winmm_parse_addin_args failed with error %" PRIu32 "!", error);
goto error_out;
}
if (!winmm->device_name)
{
winmm->device_name = _strdup("default");
if (!winmm->device_name)
{
WLog_Print(winmm->log, WLOG_ERROR, "_strdup failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
winmm->ppwfx_size = 10;
winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
if (!winmm->ppwfx)
{
WLog_Print(winmm->log, WLOG_ERROR, "malloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
{
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return error;
}

View File

@ -18,10 +18,15 @@
define_channel_server("audin")
set(${MODULE_PREFIX}_SRCS
audin.c
)
audin.c)
set(${MODULE_PREFIX}_LIBS
freerdp
)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-codec freerdp-utils)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

File diff suppressed because it is too large Load Diff

2
channels/client/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
tables.c

View File

@ -2,8 +2,6 @@
# FreeRDP cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2024 Armin Novak <anovak@thincast.com>
# Copyright 2024 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.
@ -25,79 +23,50 @@ set(${MODULE_PREFIX}_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/tables.h
${CMAKE_CURRENT_SOURCE_DIR}/addin.c
${CMAKE_CURRENT_SOURCE_DIR}/addin.h
${CMAKE_CURRENT_SOURCE_DIR}/generic_dynvc.c)
${CMAKE_CURRENT_SOURCE_DIR}/init.c
${CMAKE_CURRENT_SOURCE_DIR}/init.h
${CMAKE_CURRENT_SOURCE_DIR}/open.c
${CMAKE_CURRENT_SOURCE_DIR}/open.h
${CMAKE_CURRENT_SOURCE_DIR}/channels.c
${CMAKE_CURRENT_SOURCE_DIR}/channels.h)
if(CHANNEL_STATIC_CLIENT_ENTRIES)
list(REMOVE_DUPLICATES CHANNEL_STATIC_CLIENT_ENTRIES)
list(REMOVE_DUPLICATES CHANNEL_STATIC_CLIENT_ENTRIES)
endif()
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
if(${ENTRY} STREQUAL ${STATIC_ENTRY})
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
list(APPEND ${MODULE_PREFIX}_LIBS ${STATIC_MODULE_NAME})
if(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL ${STATIC_ENTRY})
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${STATIC_MODULE_NAME})
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${ENTRY}")
if(${ENTRY} STREQUAL "VirtualChannelEntry")
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS,PVOID);")
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(IDRDYNVC_ENTRY_POINTS* pEntryPoints);")
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints);")
else()
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(void);")
endif()
string(APPEND ${STATIC_ENTRY}_IMPORTS "\n${ENTRY_POINT_IMPORT}")
string(APPEND ${STATIC_ENTRY}_TABLE "\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME} },")
endif()
endforeach()
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${${STATIC_MODULE}_CLIENT_ENTRY}")
set(ENTRY_POINT_IMPORT "extern void ${ENTRY_POINT_NAME}();")
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME} },")
endif()
endforeach()
endforeach()
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\nextern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];\n")
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[] =\n{")
set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\nconst STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[] =\n{")
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
set(CLIENT_STATIC_ENTRY_IMPORTS "${CLIENT_STATIC_ENTRY_IMPORTS}\n${${STATIC_ENTRY}_IMPORTS}")
if(${STATIC_ENTRY} STREQUAL "VirtualChannelEntry")
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_VC")
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csevc")
elseif(${STATIC_ENTRY} STREQUAL "VirtualChannelEntryEx")
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_VCEX")
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csevcex")
elseif(${STATIC_ENTRY} MATCHES "DVCPluginEntry$")
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_DVC")
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csedvc")
elseif(${STATIC_ENTRY} MATCHES "DeviceServiceEntry$")
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_DSE")
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csedse")
else()
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY")
set(CLIENT_STATIC_ENTRY_INITIALIZER ".cse")
endif()
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\nextern const ${CLIENT_STATIC_ENTRY_TYPE} CLIENT_${STATIC_ENTRY}_TABLE[];\n")
string(APPEND CLIENT_STATIC_ENTRY_TABLES "const ${CLIENT_STATIC_ENTRY_TYPE} CLIENT_${STATIC_ENTRY}_TABLE[] =\n{")
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\n${${STATIC_ENTRY}_TABLE}")
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\n\t{ NULL, NULL }\n};")
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\n\t{ \"${STATIC_ENTRY}\", { ${CLIENT_STATIC_ENTRY_INITIALIZER} = CLIENT_${STATIC_ENTRY}_TABLE } },")
set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\nconst STATIC_ENTRY CLIENT_${STATIC_ENTRY}_TABLE[] =\n{")
set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\n${${STATIC_ENTRY}_TABLE}")
set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\n\t{ NULL, NULL }\n};")
set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\n\t{ \"${STATIC_ENTRY}\", CLIENT_${STATIC_ENTRY}_TABLE },")
endforeach()
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\n\t{ NULL, { .cse = NULL } }\n};")
set(CLIENT_STATIC_ADDIN_TABLE "extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];\n")
string(APPEND CLIENT_STATIC_ADDIN_TABLE "const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[] =\n{")
set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\n\t{ NULL, NULL }\n};")
set(CLIENT_STATIC_ADDIN_TABLE "const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[] =\n{")
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
string(TOUPPER "CLIENT_${STATIC_MODULE_CHANNEL}_SUBSYSTEM_TABLE" SUBSYSTEM_TABLE_NAME)
set(SUBSYSTEM_TABLE "extern const STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[];\nconst STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[] =\n{")
set(SUBSYSTEM_TABLE "const STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[] =\n{")
get_target_property(CHANNEL_SUBSYSTEMS ${STATIC_MODULE_NAME} SUBSYSTEMS)
if(CHANNEL_SUBSYSTEMS MATCHES "NOTFOUND")
set(CHANNEL_SUBSYSTEMS "")
@ -112,40 +81,34 @@ foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
endif()
string(LENGTH "${STATIC_SUBSYSTEM_TYPE}" _type_length)
set(SUBSYSTEM_MODULE_NAME "${STATIC_MODULE_NAME}-${STATIC_SUBSYSTEM}")
list(APPEND ${MODULE_PREFIX}_LIBS ${SUBSYSTEM_MODULE_NAME})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${SUBSYSTEM_MODULE_NAME})
if(_type_length GREATER 0)
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_${STATIC_SUBSYSTEM_TYPE}_subsystem_entry")
else()
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_subsystem_entry")
endif()
string(APPEND SUBSYSTEM_TABLE "\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
set(SUBSYSTEM_IMPORT "extern UINT VCAPITYPE ${STATIC_SUBSYSTEM_ENTRY}(void*);")
string(APPEND CLIENT_STATIC_SUBSYSTEM_IMPORTS "\n${SUBSYSTEM_IMPORT}")
endforeach()
string(APPEND SUBSYSTEM_TABLE "\n\t{ NULL, NULL, NULL }\n};")
string(APPEND CLIENT_STATIC_SUBSYSTEM_TABLES "\n${SUBSYSTEM_TABLE}")
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
set (ENTRY_POINT_NAME ${STATIC_MODULE_CHANNEL}_${ENTRY})
if(${ENTRY} STREQUAL "VirtualChannelEntry")
set(ENTRY_INITIALIZER ".csevc")
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
set(ENTRY_INITIALIZER ".csevcex")
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
set(ENTRY_INITIALIZER ".csedvc")
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
set(ENTRY_INITIALIZER ".csedse")
else()
set(ENTRY_INITIALIZER ".cse")
endif()
string(APPEND CLIENT_STATIC_ADDIN_TABLE "\n\t{ \"${STATIC_MODULE_CHANNEL}\", \"${ENTRY}\", { ${ENTRY_INITIALIZER} = ${ENTRY_POINT_NAME} }, ${SUBSYSTEM_TABLE_NAME} },")
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
set(SUBSYSTEM_IMPORT "extern void ${STATIC_SUBSYSTEM_ENTRY}();")
set(CLIENT_STATIC_SUBSYSTEM_IMPORTS "${CLIENT_STATIC_SUBSYSTEM_IMPORTS}\n${SUBSYSTEM_IMPORT}")
endforeach()
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ NULL, NULL, NULL }\n};")
set(CLIENT_STATIC_SUBSYSTEM_TABLES "${CLIENT_STATIC_SUBSYSTEM_TABLES}\n${SUBSYSTEM_TABLE}")
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${${STATIC_MODULE}_CLIENT_ENTRY}")
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME}, ${SUBSYSTEM_TABLE_NAME} },")
endforeach()
string(APPEND CLIENT_STATIC_ADDIN_TABLE "\n\t{ NULL, NULL, { .cse = NULL }, NULL }\n};")
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ NULL, NULL, NULL }\n};")
cleaning_configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp winpr)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-crt winpr-path winpr-file winpr-synch winpr-library winpr-interlocked)
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} PARENT_SCOPE)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)

View File

@ -3,8 +3,6 @@
* Channel Addins
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,44 +17,41 @@
* limitations under the License.
*/
#include <freerdp/config.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/path.h>
#include <winpr/string.h>
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/library.h>
#include <winpr/collections.h>
#include <freerdp/freerdp.h>
#include <freerdp/addin.h>
#include <freerdp/build-config.h>
#include <freerdp/client/channels.h>
#include "tables.h"
#include "addin.h"
#include <freerdp/channels/log.h>
#define TAG CHANNELS_TAG("addin")
extern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];
static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
const char* identifier)
void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table, const char* identifier)
{
size_t index = 0;
const STATIC_ENTRY* pEntry = &table->table.cse[index++];
int index = 0;
STATIC_ENTRY* pEntry;
pEntry = (STATIC_ENTRY*) &table->table[index++];
while (pEntry->entry != NULL)
{
static_entry_fn_t fkt = pEntry->entry;
if (strcmp(pEntry->name, identifier) == 0)
return WINPR_FUNC_PTR_CAST(fkt, void*);
{
return (void*) pEntry->entry;
}
pEntry = &table->table.cse[index++];
pEntry = (STATIC_ENTRY*) &table->table[index++];
}
return NULL;
@ -64,17 +59,19 @@ static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABL
void* freerdp_channels_client_find_static_entry(const char* name, const char* identifier)
{
size_t index = 0;
const STATIC_ENTRY_TABLE* pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
int index = 0;
STATIC_ENTRY_TABLE* pEntry;
while (pEntry->table.cse != NULL)
pEntry = (STATIC_ENTRY_TABLE*) &CLIENT_STATIC_ENTRY_TABLES[index++];
while (pEntry->table != NULL)
{
if (strcmp(pEntry->name, name) == 0)
{
return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
}
pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
pEntry = (STATIC_ENTRY_TABLE*) &CLIENT_STATIC_ENTRY_TABLES[index++];
}
return NULL;
@ -82,337 +79,203 @@ void* freerdp_channels_client_find_static_entry(const char* name, const char* id
extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName,
LPCSTR pszSubsystem,
LPCSTR pszType, DWORD dwFlags)
FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType, DWORD dwFlags)
{
DWORD nAddins = 0;
int i, j;
DWORD nAddins;
FREERDP_ADDIN* pAddin;
FREERDP_ADDIN** ppAddins = NULL;
const STATIC_SUBSYSTEM_ENTRY* subsystems = NULL;
STATIC_SUBSYSTEM_ENTRY* subsystems;
nAddins = 0;
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
if (!ppAddins)
{
WLog_ERR(TAG, "calloc failed!");
return NULL;
}
ppAddins = (FREERDP_ADDIN**) malloc(sizeof(FREERDP_ADDIN*) * 128);
ppAddins[nAddins] = NULL;
for (size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
for (i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
{
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
const STATIC_ADDIN_TABLE* table = &CLIENT_STATIC_ADDIN_TABLE[i];
if (!pAddin)
{
WLog_ERR(TAG, "calloc failed!");
goto error_out;
}
pAddin = (FREERDP_ADDIN*) malloc(sizeof(FREERDP_ADDIN));
ZeroMemory(pAddin, sizeof(FREERDP_ADDIN));
strcpy(pAddin->cName, CLIENT_STATIC_ADDIN_TABLE[i].name);
(void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
ppAddins[nAddins++] = pAddin;
subsystems = table->table;
for (size_t j = 0; subsystems[j].name != NULL; j++)
subsystems = (STATIC_SUBSYSTEM_ENTRY*) CLIENT_STATIC_ADDIN_TABLE[i].table;
for (j = 0; subsystems[j].name != NULL; j++)
{
pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
pAddin = (FREERDP_ADDIN*) malloc(sizeof(FREERDP_ADDIN));
ZeroMemory(pAddin, sizeof(FREERDP_ADDIN));
if (!pAddin)
{
WLog_ERR(TAG, "calloc failed!");
goto error_out;
}
strcpy(pAddin->cName, CLIENT_STATIC_ADDIN_TABLE[i].name);
strcpy(pAddin->cSubsystem, subsystems[j].name);
(void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
(void)sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s",
subsystems[j].name);
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
ppAddins[nAddins++] = pAddin;
}
}
ppAddins[nAddins] = NULL;
return ppAddins;
error_out:
freerdp_channels_addin_list_free(ppAddins);
return NULL;
}
static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, WIN32_FIND_DATAW* FindData)
FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType, DWORD dwFlags)
{
HANDLE hdl = INVALID_HANDLE_VALUE;
if (!pszSearchPath)
return hdl;
WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, NULL);
if (!wpath)
return hdl;
hdl = FindFirstFileW(wpath, FindData);
free(wpath);
return hdl;
}
static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
LPCSTR pszType, DWORD dwFlags)
{
int nDashes = 0;
HANDLE hFind = NULL;
DWORD nAddins = 0;
LPSTR pszPattern = NULL;
size_t cchPattern = 0;
int index;
int nDashes;
HANDLE hFind;
DWORD nAddins;
LPSTR pszPattern;
size_t cchPattern;
LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
LPCSTR pszExtension = NULL;
LPSTR pszSearchPath = NULL;
size_t cchSearchPath = 0;
size_t cchAddinPath = 0;
size_t cchInstallPrefix = 0;
FREERDP_ADDIN** ppAddins = NULL;
WIN32_FIND_DATAW FindData = { 0 };
cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
pszExtension = PathGetSharedLibraryExtensionA(0);
cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
pszPattern = (LPSTR)malloc(cchPattern + 1);
LPCSTR pszExtension;
LPSTR pszSearchPath;
size_t cchSearchPath;
size_t cchAddinPath;
size_t cchInstallPrefix;
FREERDP_ADDIN** ppAddins;
WIN32_FIND_DATAA FindData;
if (!pszPattern)
{
WLog_ERR(TAG, "malloc failed!");
return NULL;
}
cchAddinPath = strlen(pszAddinPath);
cchInstallPrefix = strlen(pszInstallPrefix);
pszExtension = PathGetSharedLibraryExtensionA(0);
cchPattern = 128 + strlen(pszExtension) + 2;
pszPattern = (LPSTR) malloc(cchPattern + 1);
if (pszName && pszSubsystem && pszType)
{
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
pszName, pszSubsystem, pszType, pszExtension);
sprintf_s(pszPattern, cchPattern, "%s-client-%s-%s.%s", pszName, pszSubsystem, pszType, pszExtension);
}
else if (pszName && pszType)
{
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
pszName, pszType, pszExtension);
sprintf_s(pszPattern, cchPattern, "%s-client-?-%s.%s", pszName, pszType, pszExtension);
}
else if (pszName)
{
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s",
pszName, pszExtension);
sprintf_s(pszPattern, cchPattern, "%s-client*.%s", pszName, pszExtension);
}
else
{
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
pszExtension);
sprintf_s(pszPattern, cchPattern, "?-client*.%s", pszExtension);
}
cchPattern = strnlen(pszPattern, cchPattern);
cchPattern = strlen(pszPattern);
cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
pszSearchPath = (LPSTR)calloc(cchSearchPath + 1, sizeof(char));
if (!pszSearchPath)
{
WLog_ERR(TAG, "malloc failed!");
free(pszPattern);
return NULL;
}
pszSearchPath = (LPSTR) malloc(cchSearchPath + 1);
CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
pszSearchPath[cchInstallPrefix] = '\0';
const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
free(pszPattern);
if (FAILED(hr1) || FAILED(hr2))
{
free(pszSearchPath);
return NULL;
}
NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
cchSearchPath = strlen(pszSearchPath);
hFind = FindFirstFileA(pszSearchPath, &FindData);
free(pszSearchPath);
nAddins = 0;
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
if (!ppAddins)
{
FindClose(hFind);
WLog_ERR(TAG, "calloc failed!");
return NULL;
}
ppAddins = (FREERDP_ADDIN**) malloc(sizeof(FREERDP_ADDIN*) * 128);
ppAddins[nAddins] = NULL;
if (hFind == INVALID_HANDLE_VALUE)
return ppAddins;
do
{
char* cFileName = NULL;
BOOL used = FALSE;
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
if (!pAddin)
{
WLog_ERR(TAG, "calloc failed!");
goto error_out;
}
cFileName =
ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), NULL);
if (!cFileName)
goto skip;
char* p[5];
FREERDP_ADDIN* pAddin;
nDashes = 0;
for (size_t index = 0; cFileName[index]; index++)
nDashes += (cFileName[index] == '-') ? 1 : 0;
pAddin = (FREERDP_ADDIN*) malloc(sizeof(FREERDP_ADDIN));
ZeroMemory(pAddin, sizeof(FREERDP_ADDIN));
for (index = 0; FindData.cFileName[index]; index++)
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
if (nDashes == 1)
{
size_t len = 0;
char* p[2] = { 0 };
/* <name>-client.<extension> */
p[0] = cFileName;
p[1] = strchr(p[0], '-');
if (!p[1])
goto skip;
p[1] += 1;
len = (size_t)(p[1] - p[0]);
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
goto skip;
}
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
p[0] = FindData.cFileName;
p[1] = strchr(p[0], '-') + 1;
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
ppAddins[nAddins++] = pAddin;
used = TRUE;
ppAddins[nAddins++] = pAddin;
}
else if (nDashes == 2)
{
size_t len = 0;
char* p[4] = { 0 };
/* <name>-client-<subsystem>.<extension> */
p[0] = cFileName;
p[1] = strchr(p[0], '-');
if (!p[1])
goto skip;
p[1] += 1;
p[2] = strchr(p[1], '-');
if (!p[2])
goto skip;
p[2] += 1;
p[3] = strchr(p[2], '.');
if (!p[3])
goto skip;
p[3] += 1;
len = (size_t)(p[1] - p[0]);
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
goto skip;
}
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
p[0] = FindData.cFileName;
p[1] = strchr(p[0], '-') + 1;
p[2] = strchr(p[1], '-') + 1;
p[3] = strchr(p[2], '.') + 1;
len = (size_t)(p[3] - p[2]);
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
goto skip;
}
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
ppAddins[nAddins++] = pAddin;
used = TRUE;
ppAddins[nAddins++] = pAddin;
}
else if (nDashes == 3)
{
size_t len = 0;
char* p[5] = { 0 };
/* <name>-client-<subsystem>-<type>.<extension> */
p[0] = cFileName;
p[1] = strchr(p[0], '-');
if (!p[1])
goto skip;
p[1] += 1;
p[2] = strchr(p[1], '-');
if (!p[2])
goto skip;
p[2] += 1;
p[3] = strchr(p[2], '-');
if (!p[3])
goto skip;
p[3] += 1;
p[4] = strchr(p[3], '.');
if (!p[4])
goto skip;
p[4] += 1;
len = (size_t)(p[1] - p[0]);
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
goto skip;
}
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
p[0] = FindData.cFileName;
p[1] = strchr(p[0], '-') + 1;
p[2] = strchr(p[1], '-') + 1;
p[3] = strchr(p[2], '-') + 1;
p[4] = strchr(p[3], '.') + 1;
len = (size_t)(p[3] - p[2]);
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
goto skip;
}
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
len = (size_t)(p[4] - p[3]);
if (len < 1)
{
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
goto skip;
}
strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
strncpy(pAddin->cType, p[3], (p[4] - p[3]) - 1);
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
ppAddins[nAddins++] = pAddin;
used = TRUE;
}
skip:
free(cFileName);
if (!used)
else
{
free(pAddin);
} while (FindNextFileW(hFind, &FindData));
}
}
while (FindNextFileA(hFind, &FindData));
FindClose(hFind);
ppAddins[nAddins] = NULL;
return ppAddins;
error_out:
FindClose(hFind);
freerdp_channels_addin_list_free(ppAddins);
return NULL;
}
FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
DWORD dwFlags)
FREERDP_ADDIN** freerdp_channels_list_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType, DWORD dwFlags)
{
if (dwFlags & FREERDP_ADDIN_STATIC)
return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
@ -424,338 +287,49 @@ FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem
void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
{
if (!ppAddins)
return;
int index;
for (size_t index = 0; ppAddins[index] != NULL; index++)
for (index = 0; ppAddins[index] != NULL; index++)
free(ppAddins[index]);
free(ppAddins);
}
extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
void* freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPSTR pszSubsystem, LPSTR pszType, DWORD dwFlags)
{
for (size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
int i, j;
STATIC_SUBSYSTEM_ENTRY* subsystems;
for (i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
{
const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
if (!strncmp(entry->name, pszName, MAX_PATH))
return TRUE;
}
return FALSE;
}
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
LPCSTR pszType, DWORD dwFlags)
{
const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
const char* type = NULL;
if (!pszName)
return NULL;
if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
type = "DVCPluginEntry";
else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
type = "DeviceServiceEntry";
else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
{
if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
type = "VirtualChannelEntryEx";
else
type = "VirtualChannelEntry";
}
for (; table->name != NULL; table++)
{
if (strncmp(table->name, pszName, MAX_PATH) == 0)
if (strcmp(CLIENT_STATIC_ADDIN_TABLE[i].name, pszName) == 0)
{
if (type && (strncmp(table->type, type, MAX_PATH) != 0))
continue;
if (pszSubsystem != NULL)
{
const STATIC_SUBSYSTEM_ENTRY* subsystems = table->table;
subsystems = (STATIC_SUBSYSTEM_ENTRY*) CLIENT_STATIC_ADDIN_TABLE[i].table;
for (; subsystems->name != NULL; subsystems++)
for (j = 0; subsystems[j].name != NULL; j++)
{
/* If the pszSubsystem is an empty string use the default backend. */
if ((strnlen(pszSubsystem, 1) ==
0) || /* we only want to know if strnlen is > 0 */
(strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
if (strcmp(subsystems[j].name, pszSubsystem) == 0)
{
static_subsystem_entry_fn_t fkt = subsystems->entry;
if (pszType)
{
if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
if (strcmp(subsystems[j].type, pszType) == 0)
return (void*) subsystems[j].entry;
}
else
return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
{
return (void*) subsystems[j].entry;
}
}
}
}
else
{
if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
{
if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
return NULL;
}
return table->entry.csevc;
return (void*) CLIENT_STATIC_ADDIN_TABLE[i].entry;
}
}
}
return NULL;
}
typedef struct
{
wMessageQueue* queue;
wStream* data_in;
HANDLE thread;
char* channel_name;
rdpContext* ctx;
LPVOID userdata;
MsgHandler msg_handler;
} msg_proc_internals;
static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
{
UINT error = CHANNEL_RC_OK;
wStream* data = NULL;
wMessage message = { 0 };
msg_proc_internals* internals = userdata;
WINPR_ASSERT(internals);
while (1)
{
if (!MessageQueue_Wait(internals->queue))
{
WLog_ERR(TAG, "MessageQueue_Wait failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
if (!MessageQueue_Peek(internals->queue, &message, TRUE))
{
WLog_ERR(TAG, "MessageQueue_Peek failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
if (message.id == WMQ_QUIT)
break;
if (message.id == 0)
{
data = (wStream*)message.wParam;
if ((error = internals->msg_handler(internals->userdata, data)))
{
WLog_ERR(TAG, "msg_handler failed with error %" PRIu32 "!", error);
break;
}
}
}
if (error && internals->ctx)
{
char msg[128];
(void)_snprintf(msg, 127,
"%s_virtual_channel_client_thread reported an"
" error",
internals->channel_name);
setChannelError(internals->ctx, error, msg);
}
ExitThread(error);
return error;
}
static void free_msg(void* obj)
{
wMessage* msg = (wMessage*)obj;
if (msg && (msg->id == 0))
{
wStream* s = (wStream*)msg->wParam;
Stream_Free(s, TRUE);
}
}
static void channel_client_handler_free(msg_proc_internals* internals)
{
if (!internals)
return;
if (internals->thread)
(void)CloseHandle(internals->thread);
MessageQueue_Free(internals->queue);
Stream_Free(internals->data_in, TRUE);
free(internals->channel_name);
free(internals);
}
/* Create message queue and thread or not, depending on settings */
void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
const char* channel_name)
{
msg_proc_internals* internals = calloc(1, sizeof(msg_proc_internals));
if (!internals)
{
WLog_ERR(TAG, "calloc failed!");
return NULL;
}
internals->msg_handler = msg_handler;
internals->userdata = userdata;
if (channel_name)
{
internals->channel_name = _strdup(channel_name);
if (!internals->channel_name)
goto fail;
}
WINPR_ASSERT(ctx);
WINPR_ASSERT(ctx->settings);
internals->ctx = ctx;
if ((freerdp_settings_get_uint32(ctx->settings, FreeRDP_ThreadingFlags) &
THREADING_FLAGS_DISABLE_THREADS) == 0)
{
wObject obj = { 0 };
obj.fnObjectFree = free_msg;
internals->queue = MessageQueue_New(&obj);
if (!internals->queue)
{
WLog_ERR(TAG, "MessageQueue_New failed!");
goto fail;
}
if (!(internals->thread =
CreateThread(NULL, 0, channel_client_thread_proc, (void*)internals, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
goto fail;
}
}
return internals;
fail:
channel_client_handler_free(internals);
return NULL;
}
/* post a message in the queue or directly call the processing handler */
UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
UINT32 totalLength, UINT32 dataFlags)
{
msg_proc_internals* internals = MsgsHandle;
wStream* data_in = NULL;
if (!internals)
{
/* TODO: return some error here */
return CHANNEL_RC_OK;
}
if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
{
return CHANNEL_RC_OK;
}
if (dataFlags & CHANNEL_FLAG_FIRST)
{
if (internals->data_in)
{
if (!Stream_EnsureCapacity(internals->data_in, totalLength))
return CHANNEL_RC_NO_MEMORY;
}
else
internals->data_in = Stream_New(NULL, totalLength);
}
if (!(data_in = internals->data_in))
{
WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_NO_MEMORY;
}
if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
{
Stream_Free(internals->data_in, TRUE);
internals->data_in = NULL;
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write(data_in, pData, dataLength);
if (dataFlags & CHANNEL_FLAG_LAST)
{
if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
{
char msg[128];
(void)_snprintf(msg, 127, "%s_plugin_process_received: read error",
internals->channel_name);
WLog_ERR(TAG, msg);
return ERROR_INTERNAL_ERROR;
}
internals->data_in = NULL;
Stream_SealLength(data_in);
Stream_SetPosition(data_in, 0);
if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
THREADING_FLAGS_DISABLE_THREADS) != 0)
{
UINT error = CHANNEL_RC_OK;
if ((error = internals->msg_handler(internals->userdata, data_in)))
{
WLog_ERR(TAG,
"msg_handler failed with error"
" %" PRIu32 "!",
error);
return ERROR_INTERNAL_ERROR;
}
}
else if (!MessageQueue_Post(internals->queue, NULL, 0, (void*)data_in, NULL))
{
WLog_ERR(TAG, "MessageQueue_Post failed!");
return ERROR_INTERNAL_ERROR;
}
}
return CHANNEL_RC_OK;
}
/* Tear down queue and thread */
UINT channel_client_quit_handler(void* MsgsHandle)
{
msg_proc_internals* internals = MsgsHandle;
UINT rc = 0;
if (!internals)
{
/* TODO: return some error here */
return CHANNEL_RC_OK;
}
WINPR_ASSERT(internals->ctx);
WINPR_ASSERT(internals->ctx->settings);
if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
THREADING_FLAGS_DISABLE_THREADS) == 0)
{
if (internals->queue && internals->thread)
{
if (MessageQueue_PostQuit(internals->queue, 0) &&
(WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
{
rc = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
return rc;
}
}
}
channel_client_handler_free(internals);
return CHANNEL_RC_OK;
}

View File

@ -1,4 +1,4 @@
/*
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Channel Addins
*
@ -17,12 +17,4 @@
* limitations under the License.
*/
typedef UINT (*MsgHandler)(LPVOID userdata, wStream* data);
FREERDP_API void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata,
MsgHandler handler, const char* channel_name);
UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
UINT32 totalLength, UINT32 dataFlags);
UINT channel_client_quit_handler(void* MsgsHandle);

785
channels/client/channels.c Normal file
View File

@ -0,0 +1,785 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Client Channels
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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 <freerdp/freerdp.h>
#include <freerdp/constants.h>
#include <freerdp/client/channels.h>
#include <freerdp/channels/channels.h>
#include <freerdp/svc.h>
#include <freerdp/addin.h>
#include <freerdp/utils/event.h>
#include <freerdp/utils/debug.h>
#include <winpr/crt.h>
#include <winpr/path.h>
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/library.h>
#include <winpr/collections.h>
#include "addin.h"
#include "init.h"
#include "open.h"
#include "channels.h"
/**
* MS compatible plugin interface
* reference:
* http://msdn.microsoft.com/en-us/library/aa383580.aspx
*
* Notes on threads:
* Many virtual channel plugins are built using threads.
* Non main threads may call MyVirtualChannelOpen,
* MyVirtualChannelClose, or MyVirtualChannelWrite.
* Since the plugin's VirtualChannelEntry function is called
* from the main thread, MyVirtualChannelInit has to be called
* from the main thread.
*/
/**
* The current channel manager reference passes from VirtualChannelEntry to
* VirtualChannelInit for the pInitHandle.
*/
void* g_pInterface;
CHANNEL_INIT_DATA g_ChannelInitData;
static wArrayList* g_ChannelsList = NULL;
/* To generate unique sequence for all open handles */
int g_open_handle_sequence;
/* For locking the global resources */
static HANDLE g_mutex_init;
rdpChannels* freerdp_channels_find_by_open_handle(int open_handle, int* pindex)
{
int i, j;
BOOL found = FALSE;
rdpChannels* channels = NULL;
ArrayList_Lock(g_ChannelsList);
i = j = 0;
channels = (rdpChannels*) ArrayList_GetItem(g_ChannelsList, i++);
while (channels)
{
for (j = 0; j < channels->openDataCount; j++)
{
if (channels->openDataList[j].OpenHandle == open_handle)
{
*pindex = j;
found = TRUE;
break;
}
}
if (found)
break;
channels = (rdpChannels*) ArrayList_GetItem(g_ChannelsList, i++);
}
ArrayList_Unlock(g_ChannelsList);
return (found) ? channels : NULL;
}
rdpChannels* freerdp_channels_find_by_instance(freerdp* instance)
{
int index;
BOOL found = FALSE;
rdpChannels* channels = NULL;
ArrayList_Lock(g_ChannelsList);
index = 0;
channels = (rdpChannels*) ArrayList_GetItem(g_ChannelsList, index++);
while (channels)
{
if (channels->instance == instance)
{
found = TRUE;
break;
}
channels = (rdpChannels*) ArrayList_GetItem(g_ChannelsList, index++);
}
ArrayList_Unlock(g_ChannelsList);
return (found) ? channels : NULL;
}
CHANNEL_OPEN_DATA* freerdp_channels_find_channel_open_data_by_name(rdpChannels* channels, const char* channel_name)
{
int index;
CHANNEL_OPEN_DATA* pChannelOpenData;
for (index = 0; index < channels->openDataCount; index++)
{
pChannelOpenData = &channels->openDataList[index];
if (strcmp(channel_name, pChannelOpenData->name) == 0)
return pChannelOpenData;
}
return NULL;
}
/* returns rdpChannel for the channel id passed in */
rdpChannel* freerdp_channels_find_channel_by_id(rdpChannels* channels, rdpSettings* settings, int channel_id, int* pindex)
{
int index;
int count;
rdpChannel* channel;
count = settings->ChannelCount;
for (index = 0; index < count; index++)
{
channel = &settings->ChannelDefArray[index];
if (channel->ChannelId == channel_id)
{
if (pindex != 0)
*pindex = index;
return channel;
}
}
return NULL;
}
/* returns rdpChannel for the channel name passed in */
rdpChannel* freerdp_channels_find_channel_by_name(rdpChannels* channels,
rdpSettings* settings, const char* channel_name, int* pindex)
{
int index;
int count;
rdpChannel* channel;
count = settings->ChannelCount;
for (index = 0; index < count; index++)
{
channel = &settings->ChannelDefArray[index];
if (strcmp(channel_name, channel->Name) == 0)
{
if (pindex != 0)
*pindex = index;
return channel;
}
}
return NULL;
}
UINT32 FreeRDP_VirtualChannelWrite(UINT32 openHandle, void* pData, UINT32 dataLength, void* pUserData)
{
int index;
rdpChannels* channels;
CHANNEL_OPEN_EVENT* item;
CHANNEL_OPEN_DATA* pChannelOpenData;
channels = freerdp_channels_find_by_open_handle(openHandle, &index);
if ((!channels) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
{
DEBUG_CHANNELS("error bad channel handle");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
}
if (!channels->is_connected)
{
DEBUG_CHANNELS("error not connected");
return CHANNEL_RC_NOT_CONNECTED;
}
if (!pData)
{
DEBUG_CHANNELS("error bad pData");
return CHANNEL_RC_NULL_DATA;
}
if (!dataLength)
{
DEBUG_CHANNELS("error bad dataLength");
return CHANNEL_RC_ZERO_LENGTH;
}
pChannelOpenData = &channels->openDataList[index];
if (pChannelOpenData->flags != 2)
{
DEBUG_CHANNELS("error not open");
return CHANNEL_RC_NOT_OPEN;
}
if (!channels->is_connected)
{
DEBUG_CHANNELS("error not connected");
return CHANNEL_RC_NOT_CONNECTED;
}
item = (CHANNEL_OPEN_EVENT*) malloc(sizeof(CHANNEL_OPEN_EVENT));
item->Data = pData;
item->DataLength = dataLength;
item->UserData = pUserData;
item->Index = index;
MessageQueue_Post(channels->MsgPipe->Out, (void*) channels, 0, (void*) item, NULL);
return CHANNEL_RC_OK;
}
UINT32 FreeRDP_VirtualChannelEventPush(UINT32 openHandle, wMessage* event)
{
int index;
rdpChannels* channels;
CHANNEL_OPEN_DATA* pChannelOpenData;
channels = freerdp_channels_find_by_open_handle(openHandle, &index);
if ((!channels) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
{
DEBUG_CHANNELS("error bad channels handle");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
}
if (!channels->is_connected)
{
DEBUG_CHANNELS("error not connected");
return CHANNEL_RC_NOT_CONNECTED;
}
if (!event)
{
DEBUG_CHANNELS("error bad event");
return CHANNEL_RC_NULL_DATA;
}
pChannelOpenData = &channels->openDataList[index];
if (pChannelOpenData->flags != 2)
{
DEBUG_CHANNELS("error not open");
return CHANNEL_RC_NOT_OPEN;
}
if (!channels->is_connected)
{
DEBUG_CHANNELS("error not connected");
return CHANNEL_RC_NOT_CONNECTED;
}
/**
* We really intend to use the In queue for events, but we're pushing on both
* to wake up threads waiting on the out queue. Doing this cleanly would require
* breaking freerdp_pop_event() a bit too early in this refactoring.
*/
MessageQueue_Post(channels->MsgPipe->In, (void*) channels, 1, (void*) event, NULL);
MessageQueue_Post(channels->MsgPipe->Out, (void*) channels, 1, (void*) event, NULL);
return CHANNEL_RC_OK;
}
/**
* this is called shortly after the application starts and
* before any other function in the file
* called only from main thread
*/
int freerdp_channels_global_init(void)
{
g_open_handle_sequence = 1;
g_mutex_init = CreateMutex(NULL, FALSE, NULL);
if (!g_ChannelsList)
g_ChannelsList = ArrayList_New(TRUE);
return 0;
}
int freerdp_channels_global_uninit(void)
{
/* TODO: free channels list */
CloseHandle(g_mutex_init);
return 0;
}
rdpChannels* freerdp_channels_new(void)
{
rdpChannels* channels;
channels = (rdpChannels*) malloc(sizeof(rdpChannels));
ZeroMemory(channels, sizeof(rdpChannels));
channels->MsgPipe = MessagePipe_New();
ArrayList_Add(g_ChannelsList, (void*) channels);
return channels;
}
void freerdp_channels_free(rdpChannels* channels)
{
MessagePipe_Free(channels->MsgPipe);
/* TODO: remove from channels list */
free(channels);
}
int freerdp_channels_client_load(rdpChannels* channels, rdpSettings* settings, void* entry, void* data)
{
int status;
CHANNEL_ENTRY_POINTS_EX ep;
CHANNEL_CLIENT_DATA* pChannelClientData;
if (channels->clientDataCount + 1 >= CHANNEL_MAX_COUNT)
{
fprintf(stderr, "error: too many channels\n");
return 1;
}
pChannelClientData = &channels->clientDataList[channels->clientDataCount];
pChannelClientData->entry = (PVIRTUALCHANNELENTRY) entry;
ep.cbSize = sizeof(ep);
ep.protocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000;
ep.pVirtualChannelInit = FreeRDP_VirtualChannelInit;
ep.pVirtualChannelOpen = FreeRDP_VirtualChannelOpen;
ep.pVirtualChannelClose = FreeRDP_VirtualChannelClose;
ep.pVirtualChannelWrite = FreeRDP_VirtualChannelWrite;
g_pInterface = NULL;
ep.MagicNumber = FREERDP_CHANNEL_MAGIC_NUMBER;
ep.ppInterface = &g_pInterface;
ep.pExtendedData = data;
ep.pVirtualChannelEventPush = FreeRDP_VirtualChannelEventPush;
/* enable VirtualChannelInit */
channels->can_call_init = TRUE;
channels->settings = settings;
WaitForSingleObject(g_mutex_init, INFINITE);
g_ChannelInitData.channels = channels;
status = pChannelClientData->entry((PCHANNEL_ENTRY_POINTS) &ep);
ReleaseMutex(g_mutex_init);
/* disable MyVirtualChannelInit */
channels->settings = NULL;
channels->can_call_init = FALSE;
if (!status)
{
fprintf(stderr, "error: channel export function call failed\n");
return 1;
}
return 0;
}
/**
* this is called when processing the command line parameters
* called only from main thread
*/
int freerdp_channels_load_plugin(rdpChannels* channels, rdpSettings* settings, const char* name, void* data)
{
void* entry;
DEBUG_CHANNELS("%s", name);
entry = (PVIRTUALCHANNELENTRY) freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
if (!entry)
{
DEBUG_CHANNELS("failed to find export function");
return 1;
}
return freerdp_channels_client_load(channels, settings, entry, data);
}
int freerdp_drdynvc_on_channel_connected(DrdynvcClientContext* context, const char* name, void* pInterface)
{
int status = 0;
ChannelConnectedEventArgs e;
rdpChannels* channels = (rdpChannels*) context->custom;
freerdp* instance = channels->instance;
EventArgsInit(&e, "freerdp");
e.name = name;
e.pInterface = pInterface;
PubSub_OnChannelConnected(instance->context->pubSub, instance->context, &e);
return status;
}
int freerdp_drdynvc_on_channel_disconnected(DrdynvcClientContext* context, const char* name, void* pInterface)
{
int status = 0;
ChannelDisconnectedEventArgs e;
rdpChannels* channels = (rdpChannels*) context->custom;
freerdp* instance = channels->instance;
EventArgsInit(&e, "freerdp");
e.name = name;
e.pInterface = pInterface;
PubSub_OnChannelDisconnected(instance->context->pubSub, instance->context, &e);
return status;
}
/**
* go through and inform all the libraries that we are initialized
* called only from main thread
*/
int freerdp_channels_pre_connect(rdpChannels* channels, freerdp* instance)
{
int index;
CHANNEL_CLIENT_DATA* pChannelClientData;
DEBUG_CHANNELS("enter");
channels->instance = instance;
for (index = 0; index < channels->clientDataCount; index++)
{
pChannelClientData = &channels->clientDataList[index];
if (pChannelClientData->pChannelInitEventProc)
pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle, CHANNEL_EVENT_INITIALIZED, 0, 0);
}
return 0;
}
/**
* go through and inform all the libraries that we are connected
* this will tell the libraries that its ok to call MyVirtualChannelOpen
* called only from main thread
*/
int freerdp_channels_post_connect(rdpChannels* channels, freerdp* instance)
{
int index;
char* hostname;
int hostnameLength;
CHANNEL_CLIENT_DATA* pChannelClientData;
channels->is_connected = 1;
hostname = instance->settings->ServerHostname;
hostnameLength = strlen(hostname);
DEBUG_CHANNELS("hostname [%s] channels->num_libs [%d]", hostname, channels->clientDataCount);
for (index = 0; index < channels->clientDataCount; index++)
{
pChannelClientData = &channels->clientDataList[index];
if (pChannelClientData->pChannelInitEventProc)
pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle, CHANNEL_EVENT_CONNECTED, hostname, hostnameLength);
}
channels->drdynvc = (DrdynvcClientContext*) freerdp_channels_get_static_channel_interface(channels, "drdynvc");
if (channels->drdynvc)
{
channels->drdynvc->custom = (void*) channels;
channels->drdynvc->OnChannelConnected = freerdp_drdynvc_on_channel_connected;
channels->drdynvc->OnChannelDisconnected = freerdp_drdynvc_on_channel_disconnected;
}
return 0;
}
/**
* data coming from the server to the client
* called only from main thread
*/
int freerdp_channels_data(freerdp* instance, int channel_id, void* data, int data_size, int flags, int total_size)
{
int index;
rdpChannel* channel;
rdpChannels* channels;
CHANNEL_OPEN_DATA* pChannelOpenData;
channels = freerdp_channels_find_by_instance(instance);
if (!channels)
{
DEBUG_CHANNELS("could not find channel manager");
return 1;
}
channel = freerdp_channels_find_channel_by_id(channels, instance->settings, channel_id, &index);
if (!channel)
{
DEBUG_CHANNELS("could not find channel id");
return 1;
}
pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, channel->Name);
if (!pChannelOpenData)
{
DEBUG_CHANNELS("could not find channel name");
return 1;
}
if (pChannelOpenData->pChannelOpenEventProc)
{
pChannelOpenData->pChannelOpenEventProc(pChannelOpenData->OpenHandle,
CHANNEL_EVENT_DATA_RECEIVED, data, data_size, total_size, flags);
}
return 0;
}
/**
* Send a plugin-defined event to the plugin.
* called only from main thread
* @param channels the channel manager instance
* @param event an event object created by freerdp_event_new()
*/
FREERDP_API int freerdp_channels_send_event(rdpChannels* channels, wMessage* event)
{
const char* name = NULL;
CHANNEL_OPEN_DATA* pChannelOpenData;
switch (GetMessageClass(event->id))
{
case DebugChannel_Class:
name = "rdpdbg";
break;
case CliprdrChannel_Class:
name = "cliprdr";
break;
case TsmfChannel_Class:
name = "tsmf";
break;
case RailChannel_Class:
name = "rail";
break;
}
if (!name)
{
DEBUG_CHANNELS("unknown event_class %d", GetMessageClass(event->id));
freerdp_event_free(event);
return 1;
}
pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, name);
if (!pChannelOpenData)
{
DEBUG_CHANNELS("could not find channel name %s", name);
freerdp_event_free(event);
return 1;
}
if (pChannelOpenData->pChannelOpenEventProc)
{
pChannelOpenData->pChannelOpenEventProc(pChannelOpenData->OpenHandle, CHANNEL_EVENT_USER,
event, sizeof(wMessage), sizeof(wMessage), 0);
}
return 0;
}
/**
* called only from main thread
*/
static void freerdp_channels_process_sync(rdpChannels* channels, freerdp* instance)
{
wMessage message;
wMessage* event;
rdpChannel* channel;
CHANNEL_OPEN_EVENT* item;
CHANNEL_OPEN_DATA* pChannelOpenData;
while (MessageQueue_Peek(channels->MsgPipe->Out, &message, TRUE))
{
if (message.id == WMQ_QUIT)
break;
if (message.id == 0)
{
item = (CHANNEL_OPEN_EVENT*) message.wParam;
if (!item)
break;
pChannelOpenData = &channels->openDataList[item->Index];
channel = freerdp_channels_find_channel_by_name(channels, instance->settings,
pChannelOpenData->name, &item->Index);
if (channel)
instance->SendChannelData(instance, channel->ChannelId, item->Data, item->DataLength);
if (pChannelOpenData->pChannelOpenEventProc)
{
pChannelOpenData->pChannelOpenEventProc(pChannelOpenData->OpenHandle,
CHANNEL_EVENT_WRITE_COMPLETE, item->UserData, item->DataLength, item->DataLength, 0);
}
free(item);
}
else if (message.id == 1)
{
event = (wMessage*) message.wParam;
/**
* Ignore for now, the same event is being pushed on the In queue,
* and we're pushing it on the Out queue just to wake other threads
*/
}
}
}
/**
* called only from main thread
*/
BOOL freerdp_channels_get_fds(rdpChannels* channels, freerdp* instance, void** read_fds,
int* read_count, void** write_fds, int* write_count)
{
void* pfd;
pfd = GetEventWaitObject(MessageQueue_Event(channels->MsgPipe->Out));
if (pfd)
{
read_fds[*read_count] = pfd;
(*read_count)++;
}
return TRUE;
}
void* freerdp_channels_get_static_channel_interface(rdpChannels* channels, const char* name)
{
void* pInterface = NULL;
CHANNEL_OPEN_DATA* pChannelOpenData;
pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, name);
if (pChannelOpenData)
pInterface = pChannelOpenData->pInterface;
return pInterface;
}
HANDLE freerdp_channels_get_event_handle(freerdp* instance)
{
HANDLE event = NULL;
rdpChannels* channels;
channels = instance->context->channels;
event = MessageQueue_Event(channels->MsgPipe->Out);
return event;
}
int freerdp_channels_process_pending_messages(freerdp* instance)
{
rdpChannels* channels;
channels = instance->context->channels;
if (WaitForSingleObject(MessageQueue_Event(channels->MsgPipe->Out), 0) == WAIT_OBJECT_0)
{
freerdp_channels_process_sync(channels, instance);
}
return TRUE;
}
/**
* called only from main thread
*/
BOOL freerdp_channels_check_fds(rdpChannels* channels, freerdp* instance)
{
if (WaitForSingleObject(MessageQueue_Event(channels->MsgPipe->Out), 0) == WAIT_OBJECT_0)
{
freerdp_channels_process_sync(channels, instance);
}
return TRUE;
}
wMessage* freerdp_channels_pop_event(rdpChannels* channels)
{
wMessage message;
wMessage* event = NULL;
if (MessageQueue_Peek(channels->MsgPipe->In, &message, TRUE))
{
if (message.id == 1)
{
event = (wMessage*) message.wParam;
}
}
return event;
}
void freerdp_channels_close(rdpChannels* channels, freerdp* instance)
{
int index;
CHANNEL_CLIENT_DATA* pChannelClientData;
DEBUG_CHANNELS("closing");
channels->is_connected = 0;
freerdp_channels_check_fds(channels, instance);
/* tell all libraries we are shutting down */
for (index = 0; index < channels->clientDataCount; index++)
{
pChannelClientData = &channels->clientDataList[index];
if (pChannelClientData->pChannelInitEventProc)
pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle, CHANNEL_EVENT_TERMINATED, 0, 0);
}
}

122
channels/client/channels.h Normal file
View File

@ -0,0 +1,122 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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_PRIVATE_CLIENT_CHANNELS
#define FREERDP_PRIVATE_CLIENT_CHANNELS
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/collections.h>
#include <freerdp/freerdp.h>
#include <freerdp/constants.h>
#include <freerdp/svc.h>
#include <freerdp/addin.h>
#include <freerdp/utils/event.h>
#include <freerdp/utils/debug.h>
#include <freerdp/client/channels.h>
#include <freerdp/client/drdynvc.h>
#include <freerdp/channels/channels.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define CHANNEL_MAX_COUNT 30
struct rdp_channel_client_data
{
PVIRTUALCHANNELENTRY entry;
PCHANNEL_INIT_EVENT_FN pChannelInitEventProc;
void* pInitHandle;
};
typedef struct rdp_channel_client_data CHANNEL_CLIENT_DATA;
struct rdp_channel_open_data
{
char name[8];
int OpenHandle;
int options;
int flags;
void* pInterface;
PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc;
};
typedef struct rdp_channel_open_data CHANNEL_OPEN_DATA;
struct _CHANNEL_OPEN_EVENT
{
void* Data;
UINT32 DataLength;
void* UserData;
int Index;
};
typedef struct _CHANNEL_OPEN_EVENT CHANNEL_OPEN_EVENT;
/**
* pInitHandle: handle that identifies the client connection
* Obtained by the client with VirtualChannelInit
* Used by the client with VirtualChannelOpen
*/
struct rdp_channel_init_data
{
rdpChannels* channels;
void* pInterface;
};
typedef struct rdp_channel_init_data CHANNEL_INIT_DATA;
struct rdp_channels
{
/* internal */
int clientDataCount;
CHANNEL_CLIENT_DATA clientDataList[CHANNEL_MAX_COUNT];
int openDataCount;
CHANNEL_OPEN_DATA openDataList[CHANNEL_MAX_COUNT];
int initDataCount;
CHANNEL_INIT_DATA initDataList[CHANNEL_MAX_COUNT];
/* control for entry into MyVirtualChannelInit */
int can_call_init;
rdpSettings* settings;
/* true once freerdp_channels_post_connect is called */
int is_connected;
/* used for locating the channels for a given instance */
freerdp* instance;
wMessagePipe* MsgPipe;
DrdynvcClientContext* drdynvc;
};
#ifdef WITH_DEBUG_CHANNELS
#define DEBUG_CHANNELS(fmt, ...) DEBUG_CLASS(CHANNELS, fmt, ## __VA_ARGS__)
#else
#define DEBUG_CHANNELS(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
#endif
rdpChannels* freerdp_channels_find_by_open_handle(int open_handle, int* pindex);
CHANNEL_OPEN_DATA* freerdp_channels_find_channel_open_data_by_name(rdpChannels* channels, const char* channel_name);
#endif /* FREERDP_PRIVATE_CLIENT_CHANNELS */

View File

@ -1,214 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Dynamic channel
*
* Copyright 2022 David Fort <contact@hardening-consulting.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <freerdp/log.h>
#include <freerdp/client/channels.h>
#define TAG FREERDP_TAG("genericdynvc")
static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel, BYTE* Data,
BOOL* pbAccept,
IWTSVirtualChannelCallback** ppCallback)
{
GENERIC_CHANNEL_CALLBACK* callback = NULL;
GENERIC_DYNVC_PLUGIN* plugin = NULL;
GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
if (!listener_callback || !listener_callback->plugin)
return ERROR_INTERNAL_ERROR;
plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin;
WLog_Print(plugin->log, WLOG_TRACE, "...");
callback = (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize);
if (!callback)
{
WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
/* implant configured channel callbacks */
callback->iface = *plugin->channel_callbacks;
callback->plugin = listener_callback->plugin;
callback->channel_mgr = listener_callback->channel_mgr;
callback->channel = pChannel;
listener_callback->channel_callback = callback;
listener_callback->channel = pChannel;
*ppCallback = (IWTSVirtualChannelCallback*)callback;
return CHANNEL_RC_OK;
}
static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin,
IWTSVirtualChannelManager* pChannelMgr)
{
UINT rc = 0;
GENERIC_LISTENER_CALLBACK* listener_callback = NULL;
GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
if (!plugin)
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
if (!pChannelMgr)
return ERROR_INVALID_PARAMETER;
if (plugin->initialized)
{
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name);
return ERROR_INVALID_DATA;
}
WLog_Print(plugin->log, WLOG_TRACE, "...");
listener_callback = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
if (!listener_callback)
{
WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
plugin->listener_callback = listener_callback;
listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection;
listener_callback->plugin = pPlugin;
listener_callback->channel_mgr = pChannelMgr;
rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface,
&plugin->listener);
plugin->listener->pInterface = plugin->iface.pInterface;
plugin->initialized = (rc == CHANNEL_RC_OK);
return rc;
}
static UINT generic_plugin_terminated(IWTSPlugin* pPlugin)
{
GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
UINT error = CHANNEL_RC_OK;
if (!plugin)
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
WLog_Print(plugin->log, WLOG_TRACE, "...");
/* some channels (namely rdpei), look at initialized to see if they should continue to run */
plugin->initialized = FALSE;
if (plugin->terminatePluginFn)
plugin->terminatePluginFn(plugin);
if (plugin->listener_callback)
{
IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr;
if (mgr)
IFCALL(mgr->DestroyListener, mgr, plugin->listener);
}
free(plugin->listener_callback);
free(plugin->dynvc_name);
free(plugin);
return error;
}
static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin)
{
GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin;
UINT error = CHANNEL_RC_OK;
if (!pluginn)
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
pluginn->attached = TRUE;
return error;
}
static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin)
{
GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
UINT error = CHANNEL_RC_OK;
if (!plugin)
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
plugin->attached = FALSE;
return error;
}
UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag,
const char* name, size_t pluginSize, size_t channelCallbackSize,
const IWTSVirtualChannelCallback* channel_callbacks,
DYNVC_PLUGIN_INIT_FN initPluginFn,
DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn)
{
GENERIC_DYNVC_PLUGIN* plugin = NULL;
UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
WINPR_ASSERT(pEntryPoints);
WINPR_ASSERT(pEntryPoints->GetPlugin);
WINPR_ASSERT(logTag);
WINPR_ASSERT(name);
WINPR_ASSERT(pluginSize >= sizeof(*plugin));
WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK));
plugin = (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name);
if (plugin != NULL)
return CHANNEL_RC_ALREADY_INITIALIZED;
plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, pluginSize);
if (!plugin)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
plugin->log = WLog_Get(logTag);
plugin->attached = TRUE;
plugin->channel_callbacks = channel_callbacks;
plugin->channelCallbackSize = channelCallbackSize;
plugin->iface.Initialize = generic_dynvc_plugin_initialize;
plugin->iface.Connected = NULL;
plugin->iface.Disconnected = NULL;
plugin->iface.Terminated = generic_plugin_terminated;
plugin->iface.Attached = generic_dynvc_plugin_attached;
plugin->iface.Detached = generic_dynvc_plugin_detached;
plugin->terminatePluginFn = terminatePluginFn;
if (initPluginFn)
{
rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints);
error = initPluginFn(plugin, context, settings);
if (error != CHANNEL_RC_OK)
goto error;
}
plugin->dynvc_name = _strdup(name);
if (!plugin->dynvc_name)
goto error;
error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface);
if (error == CHANNEL_RC_OK)
return error;
error:
generic_plugin_terminated(&plugin->iface);
return error;
}

135
channels/client/init.c Normal file
View File

@ -0,0 +1,135 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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 "channels.h"
#include "init.h"
extern int g_open_handle_sequence;
extern void* g_pInterface;
extern CHANNEL_INIT_DATA g_ChannelInitData;
UINT32 FreeRDP_VirtualChannelInit(void** ppInitHandle, PCHANNEL_DEF pChannel,
int channelCount, UINT32 versionRequested, PCHANNEL_INIT_EVENT_FN pChannelInitEventProc)
{
int index;
void* pInterface;
rdpChannel* channel;
rdpChannels* channels;
PCHANNEL_DEF pChannelDef;
CHANNEL_INIT_DATA* pChannelInitData;
CHANNEL_OPEN_DATA* pChannelOpenData;
CHANNEL_CLIENT_DATA* pChannelClientData;
if (!ppInitHandle)
{
DEBUG_CHANNELS("error bad init handle");
return CHANNEL_RC_BAD_INIT_HANDLE;
}
channels = g_ChannelInitData.channels;
pInterface = g_pInterface;
pChannelInitData = &(channels->initDataList[channels->initDataCount]);
*ppInitHandle = pChannelInitData;
channels->initDataCount++;
pChannelInitData->channels = channels;
pChannelInitData->pInterface = pInterface;
DEBUG_CHANNELS("enter");
if (!channels->can_call_init)
{
DEBUG_CHANNELS("error not in entry");
return CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY;
}
if (channels->openDataCount + channelCount >= CHANNEL_MAX_COUNT)
{
DEBUG_CHANNELS("error too many channels");
return CHANNEL_RC_TOO_MANY_CHANNELS;
}
if (!pChannel)
{
DEBUG_CHANNELS("error bad channel");
return CHANNEL_RC_BAD_CHANNEL;
}
if (channels->is_connected)
{
DEBUG_CHANNELS("error already connected");
return CHANNEL_RC_ALREADY_CONNECTED;
}
if (versionRequested != VIRTUAL_CHANNEL_VERSION_WIN2000)
{
DEBUG_CHANNELS("warning version");
}
for (index = 0; index < channelCount; index++)
{
pChannelDef = &pChannel[index];
if (freerdp_channels_find_channel_open_data_by_name(channels, pChannelDef->name) != 0)
{
DEBUG_CHANNELS("error channel already used");
return CHANNEL_RC_BAD_CHANNEL;
}
}
pChannelClientData = &channels->clientDataList[channels->clientDataCount];
pChannelClientData->pChannelInitEventProc = pChannelInitEventProc;
pChannelClientData->pInitHandle = *ppInitHandle;
channels->clientDataCount++;
for (index = 0; index < channelCount; index++)
{
pChannelDef = &pChannel[index];
pChannelOpenData = &channels->openDataList[channels->openDataCount];
pChannelOpenData->OpenHandle = g_open_handle_sequence++;
pChannelOpenData->flags = 1; /* init */
strncpy(pChannelOpenData->name, pChannelDef->name, CHANNEL_NAME_LEN);
pChannelOpenData->options = pChannelDef->options;
if (channels->settings->ChannelCount < CHANNEL_MAX_COUNT)
{
channel = channels->settings->ChannelDefArray + channels->settings->ChannelCount;
strncpy(channel->Name, pChannelDef->name, 7);
channel->options = pChannelDef->options;
channels->settings->ChannelCount++;
}
else
{
DEBUG_CHANNELS("warning more than %d channels", CHANNEL_MAX_COUNT);
}
channels->openDataCount++;
}
return CHANNEL_RC_OK;
}

28
channels/client/init.h Normal file
View File

@ -0,0 +1,28 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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_PRIVATE_CLIENT_CHANNELS_INIT
#define FREERDP_PRIVATE_CLIENT_CHANNELS_INIT
#include "channels.h"
UINT32 FreeRDP_VirtualChannelInit(void** ppInitHandle, PCHANNEL_DEF pChannel,
int channelCount, UINT32 versionRequested, PCHANNEL_INIT_EVENT_FN pChannelInitEventProc);
#endif /* FREERDP_PRIVATE_CLIENT_CHANNELS_INIT */

103
channels/client/open.c Normal file
View File

@ -0,0 +1,103 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include "open.h"
UINT32 FreeRDP_VirtualChannelOpen(void* pInitHandle, UINT32* pOpenHandle,
char* pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc)
{
void* pInterface;
rdpChannels* channels;
CHANNEL_INIT_DATA* pChannelInitData;
CHANNEL_OPEN_DATA* pChannelOpenData;
DEBUG_CHANNELS("enter");
pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
channels = pChannelInitData->channels;
pInterface = pChannelInitData->pInterface;
if (!pOpenHandle)
{
DEBUG_CHANNELS("error bad channel handle");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
}
if (!pChannelOpenEventProc)
{
DEBUG_CHANNELS("error bad proc");
return CHANNEL_RC_BAD_PROC;
}
if (!channels->is_connected)
{
DEBUG_CHANNELS("error not connected");
return CHANNEL_RC_NOT_CONNECTED;
}
pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, pChannelName);
if (!pChannelOpenData)
{
DEBUG_CHANNELS("error channel name");
return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
}
if (pChannelOpenData->flags == 2)
{
DEBUG_CHANNELS("error channel already open");
return CHANNEL_RC_ALREADY_OPEN;
}
pChannelOpenData->flags = 2; /* open */
pChannelOpenData->pInterface = pInterface;
pChannelOpenData->pChannelOpenEventProc = pChannelOpenEventProc;
*pOpenHandle = pChannelOpenData->OpenHandle;
return CHANNEL_RC_OK;
}
UINT32 FreeRDP_VirtualChannelClose(UINT32 openHandle)
{
int index;
rdpChannels* channels;
CHANNEL_OPEN_DATA* pChannelOpenData;
DEBUG_CHANNELS("enter");
channels = freerdp_channels_find_by_open_handle(openHandle, &index);
if ((channels == NULL) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
{
DEBUG_CHANNELS("error bad channels");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
}
pChannelOpenData = &channels->openDataList[index];
if (pChannelOpenData->flags != 2)
{
DEBUG_CHANNELS("error not open");
return CHANNEL_RC_NOT_OPEN;
}
pChannelOpenData->flags = 0;
return CHANNEL_RC_OK;
}

30
channels/client/open.h Normal file
View File

@ -0,0 +1,30 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Client Channels
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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_PRIVATE_CLIENT_CHANNELS_OPEN
#define FREERDP_PRIVATE_CLIENT_CHANNELS_OPEN
#include "channels.h"
UINT32 FreeRDP_VirtualChannelOpen(void* pInitHandle, UINT32* pOpenHandle,
char* pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc);
UINT32 FreeRDP_VirtualChannelClose(UINT32 openHandle);
#endif /* FREERDP_PRIVATE_CLIENT_CHANNELS_OPEN */

View File

@ -3,8 +3,6 @@
* Static Entry Point Tables
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,15 +17,17 @@
* limitations under the License.
*/
#include <freerdp/dvc.h>
#include <freerdp/channels/rdpdr.h>
#include "tables.h"
${CLIENT_STATIC_TYPEDEFS}
${CLIENT_STATIC_ENTRY_IMPORTS}
${CLIENT_STATIC_SUBSYSTEM_IMPORTS}
${CLIENT_STATIC_ENTRY_TABLES}
${CLIENT_STATIC_ENTRY_TABLES_LIST}
${CLIENT_STATIC_SUBSYSTEM_IMPORTS}
${CLIENT_STATIC_SUBSYSTEM_TABLES}
${CLIENT_STATIC_ADDIN_TABLE}

View File

@ -17,89 +17,34 @@
* limitations under the License.
*/
#include <winpr/platform.h>
#include <winpr/wtsapi.h>
#include <freerdp/svc.h>
#include <freerdp/dvc.h>
#include <freerdp/channels/rdpdr.h>
/* The 'entry' function pointers have variable arguments. */
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES
typedef UINT(VCAPITYPE* static_entry_fn_t)();
typedef struct
struct _STATIC_ENTRY
{
const char* name;
static_entry_fn_t entry;
} STATIC_ENTRY;
const void* entry;
};
typedef struct _STATIC_ENTRY STATIC_ENTRY;
typedef BOOL(VCAPITYPE* static_entry_vc_fn_t)(PCHANNEL_ENTRY_POINTS);
typedef struct
struct _STATIC_ENTRY_TABLE
{
const char* name;
static_entry_vc_fn_t entry;
} STATIC_ENTRY_VC;
const STATIC_ENTRY* table;
};
typedef struct _STATIC_ENTRY_TABLE STATIC_ENTRY_TABLE;
typedef BOOL(VCAPITYPE* static_entry_vcex_fn_t)(PCHANNEL_ENTRY_POINTS, PVOID);
typedef struct
{
const char* name;
static_entry_vcex_fn_t entry;
} STATIC_ENTRY_VCEX;
typedef UINT(VCAPITYPE* static_entry_dvc_fn_t)(IDRDYNVC_ENTRY_POINTS*);
typedef struct
{
const char* name;
static_entry_dvc_fn_t entry;
} STATIC_ENTRY_DVC;
typedef UINT(VCAPITYPE* static_entry_dse_fn_t)(PDEVICE_SERVICE_ENTRY_POINTS);
typedef struct
{
const char* name;
static_entry_dse_fn_t entry;
} STATIC_ENTRY_DSE;
typedef union
{
const STATIC_ENTRY* cse;
const STATIC_ENTRY_VC* csevc;
const STATIC_ENTRY_VCEX* csevcex;
const STATIC_ENTRY_DVC* csedvc;
const STATIC_ENTRY_DSE* csedse;
} static_entry_u;
typedef union
{
static_entry_fn_t cse;
static_entry_vc_fn_t csevc;
static_entry_vcex_fn_t csevcex;
static_entry_dvc_fn_t csedvc;
static_entry_dse_fn_t csedse;
} static_entry_fn_u;
typedef struct
{
const char* name;
static_entry_u table;
} STATIC_ENTRY_TABLE;
typedef UINT(VCAPITYPE* static_subsystem_entry_fn_t)(void*);
typedef struct
struct _STATIC_SUBSYSTEM_ENTRY
{
const char* name;
const char* type;
static_subsystem_entry_fn_t entry;
} STATIC_SUBSYSTEM_ENTRY;
const void* entry;
};
typedef struct _STATIC_SUBSYSTEM_ENTRY STATIC_SUBSYSTEM_ENTRY;
typedef struct
struct _STATIC_ADDIN_TABLE
{
const char* name;
const char* type;
static_entry_fn_u entry;
const void* entry;
const STATIC_SUBSYSTEM_ENTRY* table;
} STATIC_ADDIN_TABLE;
WINPR_PRAGMA_DIAG_POP
};
typedef struct _STATIC_ADDIN_TABLE STATIC_ADDIN_TABLE;

View File

@ -21,6 +21,3 @@ if(WITH_CLIENT_CHANNELS)
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -1,7 +1,7 @@
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT ON)
set(OPTION_SERVER_DEFAULT ON)
set(OPTION_SERVER_DEFAULT OFF)
define_channel_options(NAME "cliprdr" TYPE "static"
DESCRIPTION "Clipboard Virtual Channel Extension"

View File

@ -18,15 +18,28 @@
define_channel_client("cliprdr")
set(${MODULE_PREFIX}_SRCS
cliprdr_constants.h
cliprdr_format.c
cliprdr_format.h
cliprdr_main.c
cliprdr_main.h
../cliprdr_common.h
../cliprdr_common.c
)
cliprdr_main.h)
set(${MODULE_PREFIX}_LIBS
winpr freerdp
)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-crt)
if(NOT STATIC_CHANNELS)
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_PLUGIN_PATH})
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")

View File

@ -0,0 +1,58 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Clipboard Virtual Channel
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
*
* 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 __CLIPRDR_CONSTANTS
#define __CLIPRDR_CONSTANTS
/* CLIPRDR_HEADER.msgType */
#define CB_MONITOR_READY 0x0001
#define CB_FORMAT_LIST 0x0002
#define CB_FORMAT_LIST_RESPONSE 0x0003
#define CB_FORMAT_DATA_REQUEST 0x0004
#define CB_FORMAT_DATA_RESPONSE 0x0005
#define CB_TEMP_DIRECTORY 0x0006
#define CB_CLIP_CAPS 0x0007
#define CB_FILECONTENTS_REQUEST 0x0008
#define CB_FILECONTENTS_RESPONSE 0x0009
#define CB_LOCK_CLIPDATA 0x000A
#define CB_UNLOCK_CLIPDATA 0x000B
/* CLIPRDR_HEADER.msgFlags */
#define CB_RESPONSE_OK 0x0001
#define CB_RESPONSE_FAIL 0x0002
#define CB_ASCII_NAMES 0x0004
/* CLIPRDR_CAPS_SET.capabilitySetType */
#define CB_CAPSTYPE_GENERAL 0x0001
/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */
#define CB_CAPSTYPE_GENERAL_LEN 12
/* CLIPRDR_GENERAL_CAPABILITY.version */
#define CB_CAPS_VERSION_1 0x00000001
#define CB_CAPS_VERSION_2 0x00000002
/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */
#define CB_USE_LONG_FORMAT_NAMES 0x00000002
#define CB_STREAM_FILECLIP_ENABLED 0x00000004
#define CB_FILECLIP_NO_FILE_PATHS 0x00000008
#define CB_CAN_LOCK_CLIPDATA 0x00000010
#endif /* __CLIPRDR_CONSTANTS */

View File

@ -4,8 +4,6 @@
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,225 +18,345 @@
* limitations under the License.
*/
#include <freerdp/config.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/print.h>
#include <freerdp/types.h>
#include <freerdp/freerdp.h>
#include <freerdp/settings.h>
#include <freerdp/constants.h>
#include <freerdp/utils/svc_plugin.h>
#include <freerdp/client/cliprdr.h>
#include "cliprdr_constants.h"
#include "cliprdr_main.h"
#include "cliprdr_format.h"
#include "../cliprdr_common.h"
CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list, const UINT32 mask,
const UINT32 checkMask)
#define CFSTR_HTML "H\0T\0M\0L\0 \0F\0o\0r\0m\0a\0t\0\0"
#define CFSTR_PNG "P\0N\0G\0\0"
#define CFSTR_JPEG "J\0F\0I\0F\0\0"
#define CFSTR_GIF "G\0I\0F\0\0"
void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, RDP_CB_FORMAT_LIST_EVENT* cb_event)
{
const UINT32 maskData =
checkMask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_REMOTE_TO_LOCAL);
const UINT32 maskFiles =
checkMask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
WINPR_ASSERT(list);
int i;
wStream* s;
CLIPRDR_FORMAT_LIST filtered = { 0 };
filtered.common.msgType = CB_FORMAT_LIST;
filtered.numFormats = list->numFormats;
filtered.formats = calloc(filtered.numFormats, sizeof(CLIPRDR_FORMAT));
DEBUG_CLIPRDR("Sending Clipboard Format List");
size_t wpos = 0;
if ((mask & checkMask) == checkMask)
if (cb_event->raw_format_data)
{
for (size_t x = 0; x < list->numFormats; x++)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[x];
cur->formatId = format->formatId;
if (format->formatName)
cur->formatName = _strdup(format->formatName);
wpos++;
}
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, cb_event->raw_format_data_size);
Stream_Write(s, cb_event->raw_format_data, cb_event->raw_format_data_size);
}
else if ((mask & maskFiles) != 0)
else
{
for (size_t x = 0; x < list->numFormats; x++)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
wStream* body = Stream_New(NULL, 64);
if (!format->formatName)
continue;
if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0 ||
strcmp(format->formatName, type_FileContents) == 0)
for (i = 0; i < cb_event->num_formats; i++)
{
const char* name;
int name_length;
switch (cb_event->formats[i])
{
cur->formatId = format->formatId;
cur->formatName = _strdup(format->formatName);
wpos++;
case CB_FORMAT_HTML:
name = CFSTR_HTML; name_length = sizeof(CFSTR_HTML);
break;
case CB_FORMAT_PNG:
name = CFSTR_PNG; name_length = sizeof(CFSTR_PNG);
break;
case CB_FORMAT_JPEG:
name = CFSTR_JPEG; name_length = sizeof(CFSTR_JPEG);
break;
case CB_FORMAT_GIF:
name = CFSTR_GIF; name_length = sizeof(CFSTR_GIF);
break;
default:
name = "\0\0";
name_length = 2;
break;
}
if (!cliprdr->use_long_format_names)
name_length = 32;
Stream_EnsureRemainingCapacity(body, 4 + name_length);
Stream_Write_UINT32(body, cb_event->formats[i]);
Stream_Write(body, name, name_length);
}
Stream_SealLength(body);
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, Stream_Length(body));
Stream_Write(s, Stream_Buffer(body), Stream_Length(body));
Stream_Free(body, TRUE);
}
else if ((mask & maskData) != 0)
cliprdr_packet_send(cliprdr, s);
}
static void cliprdr_send_format_list_response(cliprdrPlugin* cliprdr)
{
wStream* s;
DEBUG_CLIPRDR("Sending Clipboard Format List Response");
s = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, CB_RESPONSE_OK, 0);
cliprdr_packet_send(cliprdr, s);
}
void cliprdr_process_short_format_names(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
{
int i;
BOOL ascii;
int num_formats;
CLIPRDR_FORMAT_NAME* format_name;
num_formats = length / 36;
if (num_formats <= 0)
{
for (size_t x = 0; x < list->numFormats; x++)
cliprdr->format_names = NULL;
cliprdr->num_format_names = 0;
return;
}
if (num_formats * 36 != length)
DEBUG_WARN("dataLen %d not divided by 36!", length);
ascii = (flags & CB_ASCII_NAMES) ? TRUE : FALSE;
cliprdr->format_names = (CLIPRDR_FORMAT_NAME*) malloc(sizeof(CLIPRDR_FORMAT_NAME) * num_formats);
cliprdr->num_format_names = num_formats;
for (i = 0; i < num_formats; i++)
{
format_name = &cliprdr->format_names[i];
Stream_Read_UINT32(s, format_name->id);
if (ascii)
{
const CLIPRDR_FORMAT* format = &list->formats[x];
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
if (!format->formatName ||
(strcmp(format->formatName, type_FileGroupDescriptorW) != 0 &&
strcmp(format->formatName, type_FileContents) != 0))
{
cur->formatId = format->formatId;
if (format->formatName)
cur->formatName = _strdup(format->formatName);
wpos++;
}
format_name->name = _strdup((char*) s->pointer);
format_name->length = strlen(format_name->name);
}
else
{
format_name->name = NULL;
format_name->length = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) s->pointer, 32 / 2, &format_name->name, 0, NULL, NULL);
}
Stream_Seek(s, 32);
}
WINPR_ASSERT(wpos <= UINT32_MAX);
filtered.numFormats = (UINT32)wpos;
return filtered;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags)
void cliprdr_process_long_format_names(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
{
CLIPRDR_FORMAT_LIST formatList = { 0 };
CLIPRDR_FORMAT_LIST filteredFormatList = { 0 };
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
int allocated_formats = 8;
BYTE* end_mark;
CLIPRDR_FORMAT_NAME* format_name;
formatList.common.msgType = CB_FORMAT_LIST;
formatList.common.msgFlags = msgFlags;
formatList.common.dataLen = dataLen;
Stream_GetPointer(s, end_mark);
end_mark += length;
if ((error = cliprdr_read_format_list(s, &formatList, cliprdr->useLongFormatNames)))
goto error_out;
cliprdr->format_names = (CLIPRDR_FORMAT_NAME*) malloc(sizeof(CLIPRDR_FORMAT_NAME) * allocated_formats);
cliprdr->num_format_names = 0;
const UINT32 mask =
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
filteredFormatList = cliprdr_filter_format_list(
&formatList, mask, CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
if (filteredFormatList.numFormats == 0)
goto error_out;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "",
filteredFormatList.numFormats);
if (context->ServerFormatList)
while (Stream_GetRemainingLength(s) >= 6)
{
if ((error = context->ServerFormatList(context, &filteredFormatList)))
WLog_ERR(TAG, "ServerFormatList failed with error %" PRIu32 "", error);
BYTE* p;
int name_len;
if (cliprdr->num_format_names >= allocated_formats)
{
allocated_formats *= 2;
cliprdr->format_names = (CLIPRDR_FORMAT_NAME*) realloc(cliprdr->format_names,
sizeof(CLIPRDR_FORMAT_NAME) * allocated_formats);
}
format_name = &cliprdr->format_names[cliprdr->num_format_names++];
Stream_Read_UINT32(s, format_name->id);
format_name->name = NULL;
format_name->length = 0;
for (p = Stream_Pointer(s), name_len = 0; p + 1 < end_mark; p += 2, name_len += 2)
{
if (*((unsigned short*) p) == 0)
break;
}
format_name->length = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), name_len / 2, &format_name->name, 0, NULL, NULL);
Stream_Seek(s, name_len + 2);
}
error_out:
cliprdr_free_format_list(&filteredFormatList);
cliprdr_free_format_list(&formatList);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags)
void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = { 0 };
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
int i;
UINT32 format;
BOOL supported;
CLIPRDR_FORMAT_NAME* format_name;
RDP_CB_FORMAT_LIST_EVENT* cb_event;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
CliprdrChannel_FormatList, NULL, NULL);
formatListResponse.common.msgType = CB_FORMAT_LIST_RESPONSE;
formatListResponse.common.msgFlags = msgFlags;
formatListResponse.common.dataLen = dataLen;
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
if (error)
WLog_ERR(TAG, "ServerFormatListResponse failed with error %" PRIu32 "!", error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags)
{
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = { 0 };
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest");
formatDataRequest.common.msgType = CB_FORMAT_DATA_REQUEST;
formatDataRequest.common.msgFlags = msgFlags;
formatDataRequest.common.dataLen = dataLen;
if ((error = cliprdr_read_format_data_request(s, &formatDataRequest)))
return error;
const UINT32 mask =
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
if ((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) == 0)
if (dataLen > 0)
{
return cliprdr_send_error_response(cliprdr, CB_FORMAT_DATA_RESPONSE);
cb_event->raw_format_data = (BYTE*) malloc(dataLen);
memcpy(cb_event->raw_format_data, Stream_Pointer(s), dataLen);
cb_event->raw_format_data_size = dataLen;
}
context->lastRequestedFormatId = formatDataRequest.requestedFormatId;
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
if (error)
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %" PRIu32 "!", error);
if (cliprdr->use_long_format_names)
cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags);
else
cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags);
return error;
}
if (cliprdr->num_format_names > 0)
cb_event->formats = (UINT32*) malloc(sizeof(UINT32) * cliprdr->num_format_names);
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags)
{
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse = { 0 };
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
UINT error = CHANNEL_RC_OK;
cb_event->num_formats = 0;
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse");
formatDataResponse.common.msgType = CB_FORMAT_DATA_RESPONSE;
formatDataResponse.common.msgFlags = msgFlags;
formatDataResponse.common.dataLen = dataLen;
if ((error = cliprdr_read_format_data_response(s, &formatDataResponse)))
return error;
const UINT32 mask =
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
if ((mask & (CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES)) == 0)
for (i = 0; i < cliprdr->num_format_names; i++)
{
WLog_WARN(TAG,
"Received ServerFormatDataResponse but remote -> local clipboard is disabled");
return CHANNEL_RC_OK;
supported = TRUE;
format_name = &cliprdr->format_names[i];
format = format_name->id;
switch (format)
{
case CB_FORMAT_TEXT:
case CB_FORMAT_DIB:
case CB_FORMAT_UNICODETEXT:
break;
default:
if (format_name->length > 0)
{
DEBUG_CLIPRDR("format: %s", format_name->name);
if (strcmp(format_name->name, "HTML Format") == 0)
{
format = CB_FORMAT_HTML;
break;
}
if (strcmp(format_name->name, "PNG") == 0)
{
format = CB_FORMAT_PNG;
break;
}
if (strcmp(format_name->name, "JFIF") == 0)
{
format = CB_FORMAT_JPEG;
break;
}
if (strcmp(format_name->name, "GIF") == 0)
{
format = CB_FORMAT_GIF;
break;
}
}
else
{
supported = FALSE;
}
break;
}
if (supported)
cb_event->formats[cb_event->num_formats++] = format;
if (format_name->length > 0)
free(format_name->name);
}
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
if (error)
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %" PRIu32 "!", error);
free(cliprdr->format_names);
cliprdr->format_names = NULL;
return error;
cliprdr->num_format_names = 0;
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
cliprdr_send_format_list_response(cliprdr);
}
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
/* http://msdn.microsoft.com/en-us/library/hh872154.aspx */
wMessage* event;
if ((msgFlags & CB_RESPONSE_FAIL) != 0)
{
/* In case of an error the clipboard will not be synchronized with the server.
* Post this event to restart format negociation and data transfer. */
event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
}
}
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
RDP_CB_DATA_REQUEST_EVENT* cb_event;
cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
CliprdrChannel_DataRequest, NULL, NULL);
Stream_Read_UINT32(s, cb_event->format);
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
}
void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event)
{
wStream* s;
DEBUG_CLIPRDR("Sending Format Data Response");
if (cb_event->size > 0)
{
s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, cb_event->size);
Stream_Write(s, cb_event->data, cb_event->size);
}
else
{
s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_FAIL, 0);
}
cliprdr_packet_send(cliprdr, s);
}
void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_REQUEST_EVENT* cb_event)
{
wStream* s;
DEBUG_CLIPRDR("Sending Format Data Request");
s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
Stream_Write_UINT32(s, cb_event->format);
cliprdr_packet_send(cliprdr, s);
}
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
{
RDP_CB_DATA_RESPONSE_EVENT* cb_event;
cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
CliprdrChannel_DataResponse, NULL, NULL);
if (dataLen > 0)
{
cb_event->size = dataLen;
cb_event->data = (BYTE*) malloc(dataLen);
CopyMemory(cb_event->data, Stream_Pointer(s), dataLen);
}
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
}

View File

@ -4,8 +4,6 @@
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,18 +18,17 @@
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H
#define FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H
#ifndef __CLIPRDR_FORMAT_H
#define __CLIPRDR_FORMAT_H
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags);
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags);
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags);
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
UINT16 msgFlags);
CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list, const UINT32 mask,
const UINT32 checkMask);
void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, RDP_CB_FORMAT_LIST_EVENT* cb_event);
void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H */
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event);
void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_REQUEST_EVENT* cb_event);
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
#endif /* __CLIPRDR_FORMAT_H */

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,43 +18,41 @@
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H
#define FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H
#ifndef __CLIPRDR_MAIN_H
#define __CLIPRDR_MAIN_H
#include <winpr/stream.h>
#include <freerdp/svc.h>
#include <freerdp/addin.h>
#include <freerdp/channels/log.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/utils/debug.h>
#define TAG CHANNELS_TAG("cliprdr.client")
typedef struct
struct _CLIPRDR_FORMAT_NAME
{
CHANNEL_DEF channelDef;
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
UINT32 id;
char* name;
int length;
};
typedef struct _CLIPRDR_FORMAT_NAME CLIPRDR_FORMAT_NAME;
CliprdrClientContext* context;
struct cliprdr_plugin
{
rdpSvcPlugin plugin;
BOOL received_caps;
BOOL use_long_format_names;
BOOL stream_fileclip_enabled;
BOOL fileclip_no_file_paths;
BOOL can_lock_clipdata;
CLIPRDR_FORMAT_NAME* format_names;
int num_format_names;
};
typedef struct cliprdr_plugin cliprdrPlugin;
wLog* log;
void* InitHandle;
DWORD OpenHandle;
void* MsgsHandle;
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen);
void cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* data_out);
BOOL capabilitiesReceived;
BOOL useLongFormatNames;
BOOL streamFileClipEnabled;
BOOL fileClipNoFilePaths;
BOOL canLockClipData;
BOOL hasHugeFileSupport;
BOOL initialFormatListSent;
} cliprdrPlugin;
#ifdef WITH_DEBUG_CLIPRDR
#define DEBUG_CLIPRDR(fmt, ...) DEBUG_CLASS(CLIPRDR, fmt, ## __VA_ARGS__)
#else
#define DEBUG_CLIPRDR(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
#endif
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type);
extern const char type_FileGroupDescriptorW[];
extern const char type_FileContents[];
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */
#endif /* __CLIPRDR_MAIN_H */

View File

@ -0,0 +1,3 @@
LIBRARY "cliprdr"
EXPORTS
VirtualChannelEntry @1

View File

@ -1,558 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Cliprdr common
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
*
* 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.
*/
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/log.h>
#define TAG CHANNELS_TAG("cliprdr.common")
#include "cliprdr_common.h"
static const char* CB_MSG_TYPE_STR(UINT32 type)
{
switch (type)
{
case CB_TYPE_NONE:
return "CB_TYPE_NONE";
case CB_MONITOR_READY:
return "CB_MONITOR_READY";
case CB_FORMAT_LIST:
return "CB_FORMAT_LIST";
case CB_FORMAT_LIST_RESPONSE:
return "CB_FORMAT_LIST_RESPONSE";
case CB_FORMAT_DATA_REQUEST:
return "CB_FORMAT_DATA_REQUEST";
case CB_FORMAT_DATA_RESPONSE:
return "CB_FORMAT_DATA_RESPONSE";
case CB_TEMP_DIRECTORY:
return "CB_TEMP_DIRECTORY";
case CB_CLIP_CAPS:
return "CB_CLIP_CAPS";
case CB_FILECONTENTS_REQUEST:
return "CB_FILECONTENTS_REQUEST";
case CB_FILECONTENTS_RESPONSE:
return "CB_FILECONTENTS_RESPONSE";
case CB_LOCK_CLIPDATA:
return "CB_LOCK_CLIPDATA";
case CB_UNLOCK_CLIPDATA:
return "CB_UNLOCK_CLIPDATA";
default:
return "UNKNOWN";
}
}
const char* CB_MSG_TYPE_STRING(UINT16 type, char* buffer, size_t size)
{
(void)_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", CB_MSG_TYPE_STR(type), type);
return buffer;
}
const char* CB_MSG_FLAGS_STRING(UINT16 msgFlags, char* buffer, size_t size)
{
if ((msgFlags & CB_RESPONSE_OK) != 0)
winpr_str_append("CB_RESPONSE_OK", buffer, size, "|");
if ((msgFlags & CB_RESPONSE_FAIL) != 0)
winpr_str_append("CB_RESPONSE_FAIL", buffer, size, "|");
if ((msgFlags & CB_ASCII_NAMES) != 0)
winpr_str_append("CB_ASCII_NAMES", buffer, size, "|");
const size_t len = strnlen(buffer, size);
if (!len)
winpr_str_append("NONE", buffer, size, "");
char val[32] = { 0 };
(void)_snprintf(val, sizeof(val), "[0x%04" PRIx16 "]", msgFlags);
winpr_str_append(val, buffer, size, "|");
return buffer;
}
static BOOL cliprdr_validate_file_contents_request(const CLIPRDR_FILE_CONTENTS_REQUEST* request)
{
/*
* [MS-RDPECLIP] 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST).
*
* A request for the size of the file identified by the lindex field. The size MUST be
* returned as a 64-bit, unsigned integer. The cbRequested field MUST be set to
* 0x00000008 and both the nPositionLow and nPositionHigh fields MUST be
* set to 0x00000000.
*/
if (request->dwFlags & FILECONTENTS_SIZE)
{
if (request->cbRequested != sizeof(UINT64))
{
WLog_ERR(TAG, "cbRequested must be %" PRIu32 ", got %" PRIu32 "", sizeof(UINT64),
request->cbRequested);
return FALSE;
}
if (request->nPositionHigh != 0 || request->nPositionLow != 0)
{
WLog_ERR(TAG, "nPositionHigh and nPositionLow must be set to 0");
return FALSE;
}
}
return TRUE;
}
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen)
{
wStream* s = NULL;
s = Stream_New(NULL, dataLen + 8);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return NULL;
}
Stream_Write_UINT16(s, msgType);
Stream_Write_UINT16(s, msgFlags);
/* Write actual length after the entire packet has been constructed. */
Stream_Write_UINT32(s, 0);
return s;
}
static void cliprdr_write_file_contents_request(wStream* s,
const CLIPRDR_FILE_CONTENTS_REQUEST* request)
{
Stream_Write_UINT32(s, request->streamId); /* streamId (4 bytes) */
Stream_Write_UINT32(s, request->listIndex); /* listIndex (4 bytes) */
Stream_Write_UINT32(s, request->dwFlags); /* dwFlags (4 bytes) */
Stream_Write_UINT32(s, request->nPositionLow); /* nPositionLow (4 bytes) */
Stream_Write_UINT32(s, request->nPositionHigh); /* nPositionHigh (4 bytes) */
Stream_Write_UINT32(s, request->cbRequested); /* cbRequested (4 bytes) */
if (request->haveClipDataId)
Stream_Write_UINT32(s, request->clipDataId); /* clipDataId (4 bytes) */
}
static INLINE void cliprdr_write_lock_unlock_clipdata(wStream* s, UINT32 clipDataId)
{
Stream_Write_UINT32(s, clipDataId);
}
static void cliprdr_write_lock_clipdata(wStream* s,
const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
{
cliprdr_write_lock_unlock_clipdata(s, lockClipboardData->clipDataId);
}
static void cliprdr_write_unlock_clipdata(wStream* s,
const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
{
cliprdr_write_lock_unlock_clipdata(s, unlockClipboardData->clipDataId);
}
static void cliprdr_write_file_contents_response(wStream* s,
const CLIPRDR_FILE_CONTENTS_RESPONSE* response)
{
Stream_Write_UINT32(s, response->streamId); /* streamId (4 bytes) */
Stream_Write(s, response->requestedData, response->cbRequested);
}
wStream* cliprdr_packet_lock_clipdata_new(const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
{
wStream* s = NULL;
if (!lockClipboardData)
return NULL;
s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
if (!s)
return NULL;
cliprdr_write_lock_clipdata(s, lockClipboardData);
return s;
}
wStream*
cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
{
wStream* s = NULL;
if (!unlockClipboardData)
return NULL;
s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
if (!s)
return NULL;
cliprdr_write_unlock_clipdata(s, unlockClipboardData);
return s;
}
wStream* cliprdr_packet_file_contents_request_new(const CLIPRDR_FILE_CONTENTS_REQUEST* request)
{
wStream* s = NULL;
if (!request)
return NULL;
s = cliprdr_packet_new(CB_FILECONTENTS_REQUEST, 0, 28);
if (!s)
return NULL;
cliprdr_write_file_contents_request(s, request);
return s;
}
wStream* cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_RESPONSE* response)
{
wStream* s = NULL;
if (!response)
return NULL;
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, response->common.msgFlags,
4 + response->cbRequested);
if (!s)
return NULL;
cliprdr_write_file_contents_response(s, response);
return s;
}
wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
BOOL useLongFormatNames, BOOL useAsciiNames)
{
WINPR_ASSERT(formatList);
if (formatList->common.msgType != CB_FORMAT_LIST)
WLog_WARN(TAG, "called with invalid type %08" PRIx32, formatList->common.msgType);
if (useLongFormatNames && useAsciiNames)
WLog_WARN(TAG, "called with invalid arguments useLongFormatNames=true && "
"useAsciiNames=true. useAsciiNames requires "
"useLongFormatNames=false, ignoring argument.");
const UINT32 length = formatList->numFormats * 36;
const size_t formatNameCharSize =
(useLongFormatNames || !useAsciiNames) ? sizeof(WCHAR) : sizeof(CHAR);
wStream* s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
if (!s)
{
WLog_ERR(TAG, "cliprdr_packet_new failed!");
return NULL;
}
for (UINT32 index = 0; index < formatList->numFormats; index++)
{
const CLIPRDR_FORMAT* format = &(formatList->formats[index]);
const char* szFormatName = format->formatName;
size_t formatNameLength = 0;
if (szFormatName)
formatNameLength = strlen(szFormatName);
size_t formatNameMaxLength = formatNameLength + 1; /* Ensure '\0' termination in output */
if (!Stream_EnsureRemainingCapacity(s,
4 + MAX(32, formatNameMaxLength * formatNameCharSize)))
goto fail;
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
if (!useLongFormatNames)
{
formatNameMaxLength = useAsciiNames ? 32 : 16;
formatNameLength = MIN(formatNameMaxLength - 1, formatNameLength);
}
if (szFormatName && (formatNameLength > 0))
{
if (useAsciiNames)
{
Stream_Write(s, szFormatName, formatNameLength);
Stream_Zero(s, formatNameMaxLength - formatNameLength);
}
else
{
if (Stream_Write_UTF16_String_From_UTF8(s, formatNameMaxLength, szFormatName,
formatNameLength, TRUE) < 0)
goto fail;
}
}
else
Stream_Zero(s, formatNameMaxLength * formatNameCharSize);
}
return s;
fail:
Stream_Free(s, TRUE);
return NULL;
}
UINT cliprdr_read_unlock_clipdata(wStream* s, CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, unlockClipboardData->clipDataId); /* clipDataId (4 bytes) */
return CHANNEL_RC_OK;
}
UINT cliprdr_read_format_data_request(wStream* s, CLIPRDR_FORMAT_DATA_REQUEST* request)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, request->requestedFormatId); /* requestedFormatId (4 bytes) */
return CHANNEL_RC_OK;
}
UINT cliprdr_read_format_data_response(wStream* s, CLIPRDR_FORMAT_DATA_RESPONSE* response)
{
response->requestedFormatData = NULL;
if (!Stream_CheckAndLogRequiredLength(TAG, s, response->common.dataLen))
return ERROR_INVALID_DATA;
if (response->common.dataLen)
response->requestedFormatData = Stream_ConstPointer(s);
return CHANNEL_RC_OK;
}
UINT cliprdr_read_file_contents_request(wStream* s, CLIPRDR_FILE_CONTENTS_REQUEST* request)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
return ERROR_INVALID_DATA;
request->haveClipDataId = FALSE;
Stream_Read_UINT32(s, request->streamId); /* streamId (4 bytes) */
Stream_Read_UINT32(s, request->listIndex); /* listIndex (4 bytes) */
Stream_Read_UINT32(s, request->dwFlags); /* dwFlags (4 bytes) */
Stream_Read_UINT32(s, request->nPositionLow); /* nPositionLow (4 bytes) */
Stream_Read_UINT32(s, request->nPositionHigh); /* nPositionHigh (4 bytes) */
Stream_Read_UINT32(s, request->cbRequested); /* cbRequested (4 bytes) */
if (Stream_GetRemainingLength(s) >= 4)
{
Stream_Read_UINT32(s, request->clipDataId); /* clipDataId (4 bytes) */
request->haveClipDataId = TRUE;
}
if (!cliprdr_validate_file_contents_request(request))
return ERROR_BAD_ARGUMENTS;
return CHANNEL_RC_OK;
}
UINT cliprdr_read_file_contents_response(wStream* s, CLIPRDR_FILE_CONTENTS_RESPONSE* response)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, response->streamId); /* streamId (4 bytes) */
response->requestedData = Stream_ConstPointer(s); /* requestedFileContentsData */
WINPR_ASSERT(response->common.dataLen >= 4);
response->cbRequested = response->common.dataLen - 4;
return CHANNEL_RC_OK;
}
UINT cliprdr_read_format_list(wStream* s, CLIPRDR_FORMAT_LIST* formatList, BOOL useLongFormatNames)
{
UINT32 index = 0;
size_t formatNameLength = 0;
const char* szFormatName = NULL;
const WCHAR* wszFormatName = NULL;
wStream sub1buffer = { 0 };
CLIPRDR_FORMAT* formats = NULL;
UINT error = ERROR_INTERNAL_ERROR;
const BOOL asciiNames = (formatList->common.msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
index = 0;
/* empty format list */
formatList->formats = NULL;
formatList->numFormats = 0;
wStream* sub1 =
Stream_StaticConstInit(&sub1buffer, Stream_ConstPointer(s), formatList->common.dataLen);
if (!Stream_SafeSeek(s, formatList->common.dataLen))
return ERROR_INVALID_DATA;
if (!formatList->common.dataLen)
{
}
else if (!useLongFormatNames)
{
const size_t cap = Stream_Capacity(sub1) / 36ULL;
if (cap > UINT32_MAX)
{
WLog_ERR(TAG, "Invalid short format list length: %" PRIuz "", cap);
return ERROR_INTERNAL_ERROR;
}
formatList->numFormats = (UINT32)cap;
if (formatList->numFormats)
formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
if (!formats)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
formatList->formats = formats;
while (Stream_GetRemainingLength(sub1) >= 4)
{
CLIPRDR_FORMAT* format = &formats[index];
Stream_Read_UINT32(sub1, format->formatId); /* formatId (4 bytes) */
/* According to MS-RDPECLIP 2.2.3.1.1.1 formatName is "a 32-byte block containing
* the *null-terminated* name assigned to the Clipboard Format: (32 ASCII 8 characters
* or 16 Unicode characters)"
* However, both Windows RDSH and mstsc violate this specs as seen in the following
* example of a transferred short format name string: [R.i.c.h. .T.e.x.t. .F.o.r.m.a.t.]
* These are 16 unicode charaters - *without* terminating null !
*/
szFormatName = Stream_ConstPointer(sub1);
wszFormatName = Stream_ConstPointer(sub1);
if (!Stream_SafeSeek(sub1, 32))
goto error_out;
free(format->formatName);
format->formatName = NULL;
if (asciiNames)
{
if (szFormatName[0])
{
/* ensure null termination */
format->formatName = strndup(szFormatName, 31);
if (!format->formatName)
{
WLog_ERR(TAG, "malloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
}
else
{
if (wszFormatName[0])
{
format->formatName = ConvertWCharNToUtf8Alloc(wszFormatName, 16, NULL);
if (!format->formatName)
goto error_out;
}
}
index++;
}
}
else
{
wStream sub2buffer = sub1buffer;
wStream* sub2 = &sub2buffer;
while (Stream_GetRemainingLength(sub1) > 0)
{
size_t rest = 0;
if (!Stream_SafeSeek(sub1, 4)) /* formatId (4 bytes) */
goto error_out;
wszFormatName = Stream_ConstPointer(sub1);
rest = Stream_GetRemainingLength(sub1);
formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
if (!Stream_SafeSeek(sub1, (formatNameLength + 1) * sizeof(WCHAR)))
goto error_out;
formatList->numFormats++;
}
if (formatList->numFormats)
formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
if (!formats)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
formatList->formats = formats;
while (Stream_GetRemainingLength(sub2) >= 4)
{
size_t rest = 0;
CLIPRDR_FORMAT* format = &formats[index];
Stream_Read_UINT32(sub2, format->formatId); /* formatId (4 bytes) */
free(format->formatName);
format->formatName = NULL;
wszFormatName = Stream_ConstPointer(sub2);
rest = Stream_GetRemainingLength(sub2);
formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
if (!Stream_SafeSeek(sub2, (formatNameLength + 1) * sizeof(WCHAR)))
goto error_out;
if (formatNameLength)
{
format->formatName =
ConvertWCharNToUtf8Alloc(wszFormatName, formatNameLength, NULL);
if (!format->formatName)
goto error_out;
}
index++;
}
}
return CHANNEL_RC_OK;
error_out:
cliprdr_free_format_list(formatList);
return error;
}
void cliprdr_free_format_list(CLIPRDR_FORMAT_LIST* formatList)
{
if (formatList == NULL)
return;
if (formatList->formats)
{
for (UINT32 index = 0; index < formatList->numFormats; index++)
{
free(formatList->formats[index].formatName);
}
free(formatList->formats);
formatList->formats = NULL;
formatList->numFormats = 0;
}
}

View File

@ -1,64 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Cliprdr common
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
*
* 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_RDPECLIP_COMMON_H
#define FREERDP_CHANNEL_RDPECLIP_COMMON_H
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/cliprdr.h>
#include <freerdp/api.h>
FREERDP_LOCAL const char* CB_MSG_TYPE_STRING(UINT16 type, char* buffer, size_t size);
FREERDP_LOCAL const char* CB_MSG_FLAGS_STRING(UINT16 msgFlags, char* buffer, size_t size);
FREERDP_LOCAL wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen);
FREERDP_LOCAL wStream*
cliprdr_packet_lock_clipdata_new(const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData);
FREERDP_LOCAL wStream*
cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData);
FREERDP_LOCAL wStream*
cliprdr_packet_file_contents_request_new(const CLIPRDR_FILE_CONTENTS_REQUEST* request);
FREERDP_LOCAL wStream*
cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_RESPONSE* response);
FREERDP_LOCAL wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
BOOL useLongFormatNames, BOOL useAsciiNames);
FREERDP_LOCAL UINT cliprdr_read_lock_clipdata(wStream* s,
CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData);
FREERDP_LOCAL UINT cliprdr_read_unlock_clipdata(wStream* s,
CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData);
FREERDP_LOCAL UINT cliprdr_read_format_data_request(wStream* s,
CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest);
FREERDP_LOCAL UINT cliprdr_read_format_data_response(wStream* s,
CLIPRDR_FORMAT_DATA_RESPONSE* response);
FREERDP_LOCAL UINT
cliprdr_read_file_contents_request(wStream* s, CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest);
FREERDP_LOCAL UINT cliprdr_read_file_contents_response(wStream* s,
CLIPRDR_FILE_CONTENTS_RESPONSE* response);
FREERDP_LOCAL UINT cliprdr_read_format_list(wStream* s, CLIPRDR_FORMAT_LIST* formatList,
BOOL useLongFormatNames);
FREERDP_LOCAL void cliprdr_free_format_list(CLIPRDR_FORMAT_LIST* formatList);
#endif /* FREERDP_CHANNEL_RDPECLIP_COMMON_H */

View File

@ -1,30 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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("cliprdr")
set(${MODULE_PREFIX}_SRCS
cliprdr_main.c
cliprdr_main.h
../cliprdr_common.h
../cliprdr_common.c
)
set(${MODULE_PREFIX}_LIBS
freerdp
)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Clipboard Virtual Channel Extension
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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_CLIPRDR_SERVER_MAIN_H
#define FREERDP_CHANNEL_CLIPRDR_SERVER_MAIN_H
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/stream.h>
#include <winpr/thread.h>
#include <freerdp/server/cliprdr.h>
#include <freerdp/channels/log.h>
#define TAG CHANNELS_TAG("cliprdr.server")
#define CLIPRDR_HEADER_LENGTH 8
typedef struct
{
HANDLE vcm;
HANDLE Thread;
HANDLE StopEvent;
void* ChannelHandle;
HANDLE ChannelEvent;
wStream* s;
char temporaryDirectory[260];
} CliprdrServerPrivate;
#endif /* FREERDP_CHANNEL_CLIPRDR_SERVER_MAIN_H */

View File

@ -1,26 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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("disp")
if(WITH_CLIENT_CHANNELS)
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

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

View File

@ -1,32 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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("disp")
set(${MODULE_PREFIX}_SRCS
disp_main.c
disp_main.h
../disp_common.c
../disp_common.h
)
set(${MODULE_PREFIX}_LIBS
winpr
)
include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")

View File

@ -1,323 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Display Update Virtual Channel Extension
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/synch.h>
#include <winpr/print.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include <winpr/cmdline.h>
#include <winpr/collections.h>
#include <freerdp/addin.h>
#include <freerdp/client/channels.h>
#include "disp_main.h"
#include "../disp_common.h"
typedef struct
{
GENERIC_DYNVC_PLUGIN base;
DispClientContext* context;
UINT32 MaxNumMonitors;
UINT32 MaxMonitorAreaFactorA;
UINT32 MaxMonitorAreaFactorB;
} DISP_PLUGIN;
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT
disp_send_display_control_monitor_layout_pdu(GENERIC_CHANNEL_CALLBACK* callback, UINT32 NumMonitors,
const DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
{
UINT status = 0;
wStream* s = NULL;
DISP_PLUGIN* disp = NULL;
UINT32 MonitorLayoutSize = 0;
DISPLAY_CONTROL_HEADER header = { 0 };
WINPR_ASSERT(callback);
WINPR_ASSERT(Monitors || (NumMonitors == 0));
disp = (DISP_PLUGIN*)callback->plugin;
WINPR_ASSERT(disp);
MonitorLayoutSize = DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE;
header.length = 8 + 8 + (NumMonitors * MonitorLayoutSize);
header.type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
s = Stream_New(NULL, header.length);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_NO_MEMORY;
}
if ((status = disp_write_header(s, &header)))
{
WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", status);
goto out;
}
if (NumMonitors > disp->MaxNumMonitors)
NumMonitors = disp->MaxNumMonitors;
Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
WLog_DBG(TAG, "NumMonitors=%" PRIu32 "", NumMonitors);
for (UINT32 index = 0; index < NumMonitors; index++)
{
DISPLAY_CONTROL_MONITOR_LAYOUT current = Monitors[index];
current.Width -= (current.Width % 2);
if (current.Width < 200)
current.Width = 200;
if (current.Width > 8192)
current.Width = 8192;
if (current.Width % 2)
current.Width++;
if (current.Height < 200)
current.Height = 200;
if (current.Height > 8192)
current.Height = 8192;
Stream_Write_UINT32(s, current.Flags); /* Flags (4 bytes) */
Stream_Write_INT32(s, current.Left); /* Left (4 bytes) */
Stream_Write_INT32(s, current.Top); /* Top (4 bytes) */
Stream_Write_UINT32(s, current.Width); /* Width (4 bytes) */
Stream_Write_UINT32(s, current.Height); /* Height (4 bytes) */
Stream_Write_UINT32(s, current.PhysicalWidth); /* PhysicalWidth (4 bytes) */
Stream_Write_UINT32(s, current.PhysicalHeight); /* PhysicalHeight (4 bytes) */
Stream_Write_UINT32(s, current.Orientation); /* Orientation (4 bytes) */
Stream_Write_UINT32(s, current.DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
Stream_Write_UINT32(s, current.DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
WLog_DBG(TAG,
"\t%" PRIu32 " : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32
") W/H=%" PRIu32 "x%" PRIu32 ")",
index, current.Flags, current.Left, current.Top, current.Width, current.Height);
WLog_DBG(TAG,
"\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32 " Orientation: %" PRIu32
"",
current.PhysicalWidth, current.PhysicalHeight, current.Orientation);
}
out:
Stream_SealLength(s);
status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
NULL);
Stream_Free(s, TRUE);
return status;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_recv_display_control_caps_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
{
DISP_PLUGIN* disp = NULL;
DispClientContext* context = NULL;
UINT ret = CHANNEL_RC_OK;
WINPR_ASSERT(callback);
WINPR_ASSERT(s);
disp = (DISP_PLUGIN*)callback->plugin;
WINPR_ASSERT(disp);
context = disp->context;
WINPR_ASSERT(context);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, disp->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
if (context->DisplayControlCaps)
ret = context->DisplayControlCaps(context, disp->MaxNumMonitors,
disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB);
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
{
UINT32 error = 0;
DISPLAY_CONTROL_HEADER header = { 0 };
WINPR_ASSERT(callback);
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
return ERROR_INVALID_DATA;
if ((error = disp_read_header(s, &header)))
{
WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
return error;
}
if (!Stream_EnsureRemainingCapacity(s, header.length))
{
WLog_ERR(TAG, "not enough remaining data");
return ERROR_INVALID_DATA;
}
switch (header.type)
{
case DISPLAY_CONTROL_PDU_TYPE_CAPS:
return disp_recv_display_control_caps_pdu(callback, s);
default:
WLog_ERR(TAG, "Type %" PRIu32 " not recognized!", header.type);
return ERROR_INTERNAL_ERROR;
}
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
{
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
return disp_recv_pdu(callback, data);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
free(pChannelCallback);
return CHANNEL_RC_OK;
}
/**
* Channel Client Interface
*/
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors,
DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
{
DISP_PLUGIN* disp = NULL;
GENERIC_CHANNEL_CALLBACK* callback = NULL;
WINPR_ASSERT(context);
disp = (DISP_PLUGIN*)context->handle;
WINPR_ASSERT(disp);
callback = disp->base.listener_callback->channel_callback;
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_plugin_initialize(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext,
rdpSettings* settings)
{
DispClientContext* context = NULL;
DISP_PLUGIN* disp = (DISP_PLUGIN*)base;
WINPR_ASSERT(disp);
disp->MaxNumMonitors = 16;
disp->MaxMonitorAreaFactorA = 8192;
disp->MaxMonitorAreaFactorB = 8192;
context = (DispClientContext*)calloc(1, sizeof(DispClientContext));
if (!context)
{
WLog_Print(base->log, WLOG_ERROR, "unable to allocate DispClientContext");
return CHANNEL_RC_NO_MEMORY;
}
context->handle = (void*)disp;
context->SendMonitorLayout = disp_send_monitor_layout;
disp->base.iface.pInterface = disp->context = context;
return CHANNEL_RC_OK;
}
static void disp_plugin_terminated(GENERIC_DYNVC_PLUGIN* base)
{
DISP_PLUGIN* disp = (DISP_PLUGIN*)base;
WINPR_ASSERT(disp);
free(disp->context);
}
static const IWTSVirtualChannelCallback disp_callbacks = { disp_on_data_received, NULL, /* Open */
disp_on_close, NULL };
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
FREERDP_ENTRY_POINT(UINT VCAPITYPE disp_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
{
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, DISP_DVC_CHANNEL_NAME,
sizeof(DISP_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
&disp_callbacks, disp_plugin_initialize,
disp_plugin_terminated);
}

View File

@ -1,36 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Display Update Virtual Channel Extension
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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_DISP_CLIENT_MAIN_H
#define FREERDP_CHANNEL_DISP_CLIENT_MAIN_H
#include <freerdp/config.h>
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/channels/log.h>
#include <freerdp/client/disp.h>
#define TAG CHANNELS_TAG("disp.client")
#endif /* FREERDP_CHANNEL_DISP_CLIENT_MAIN_H */

View File

@ -1,55 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/log.h>
#define TAG CHANNELS_TAG("disp.common")
#include "disp_common.h"
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT disp_read_header(wStream* s, DISPLAY_CONTROL_HEADER* header)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, header->type);
Stream_Read_UINT32(s, header->length);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT disp_write_header(wStream* s, const DISPLAY_CONTROL_HEADER* header)
{
Stream_Write_UINT32(s, header->type);
Stream_Write_UINT32(s, header->length);
return CHANNEL_RC_OK;
}

View File

@ -1,32 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
*
* 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_DISP_COMMON_H
#define FREERDP_CHANNEL_DISP_COMMON_H
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/disp.h>
#include <freerdp/api.h>
FREERDP_LOCAL UINT disp_read_header(wStream* s, DISPLAY_CONTROL_HEADER* header);
FREERDP_LOCAL UINT disp_write_header(wStream* s, const DISPLAY_CONTROL_HEADER* header);
#endif /* FREERDP_CHANNEL_DISP_COMMON_H */

View File

@ -1,32 +0,0 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
#
# 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("disp")
set(${MODULE_PREFIX}_SRCS
disp_main.c
disp_main.h
../disp_common.c
../disp_common.h
)
set(${MODULE_PREFIX}_LIBS
freerdp
)
include_directories(..)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")

View File

@ -1,639 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
*
* 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.
*/
#include <freerdp/config.h>
#include "disp_main.h"
#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 <freerdp/channels/wtsvc.h>
#include <freerdp/channels/log.h>
#include <freerdp/server/disp.h>
#include "../disp_common.h"
#define TAG CHANNELS_TAG("rdpedisp.server")
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
{
UINT error = 0;
DISPLAY_CONTROL_HEADER header;
wStream* s = Stream_New(NULL, DISPLAY_CONTROL_HEADER_LENGTH + length);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
goto error;
}
header.type = type;
header.length = DISPLAY_CONTROL_HEADER_LENGTH + length;
if ((error = disp_write_header(s, &header)))
{
WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
goto error;
}
return s;
error:
Stream_Free(s, TRUE);
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(const DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
{
WINPR_ASSERT(monitor);
if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
monitor->Width > DISPLAY_CONTROL_MAX_MONITOR_WIDTH)
{
WLog_WARN(TAG, "Received invalid value for monitor->Width: %" PRIu32 "", monitor->Width);
return FALSE;
}
if (monitor->Height < DISPLAY_CONTROL_MIN_MONITOR_HEIGHT ||
monitor->Height > DISPLAY_CONTROL_MAX_MONITOR_HEIGHT)
{
WLog_WARN(TAG, "Received invalid value for monitor->Height: %" PRIu32 "", monitor->Height);
return FALSE;
}
switch (monitor->Orientation)
{
case ORIENTATION_LANDSCAPE:
case ORIENTATION_PORTRAIT:
case ORIENTATION_LANDSCAPE_FLIPPED:
case ORIENTATION_PORTRAIT_FLIPPED:
break;
default:
WLog_WARN(TAG, "Received incorrect value for monitor->Orientation: %" PRIu32 "",
monitor->Orientation);
return FALSE;
}
return TRUE;
}
static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerContext* context)
{
UINT32 error = CHANNEL_RC_OK;
DISPLAY_CONTROL_MONITOR_LAYOUT_PDU pdu = { 0 };
WINPR_ASSERT(s);
WINPR_ASSERT(context);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, pdu.MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
if (pdu.MonitorLayoutSize != DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE)
{
WLog_ERR(TAG, "MonitorLayoutSize is set to %" PRIu32 ". expected %" PRIu32 "",
pdu.MonitorLayoutSize, DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE);
return ERROR_INVALID_DATA;
}
Stream_Read_UINT32(s, pdu.NumMonitors); /* NumMonitors (4 bytes) */
if (pdu.NumMonitors > context->MaxNumMonitors)
{
WLog_ERR(TAG, "NumMonitors (%" PRIu32 ")> server MaxNumMonitors (%" PRIu32 ")",
pdu.NumMonitors, context->MaxNumMonitors);
return ERROR_INVALID_DATA;
}
if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.NumMonitors,
DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE))
return ERROR_INVALID_DATA;
pdu.Monitors = (DISPLAY_CONTROL_MONITOR_LAYOUT*)calloc(pdu.NumMonitors,
sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
if (!pdu.Monitors)
{
WLog_ERR(TAG, "disp_recv_display_control_monitor_layout_pdu(): calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
WLog_DBG(TAG, "disp_recv_display_control_monitor_layout_pdu: NumMonitors=%" PRIu32 "",
pdu.NumMonitors);
for (UINT32 index = 0; index < pdu.NumMonitors; index++)
{
DISPLAY_CONTROL_MONITOR_LAYOUT* monitor = &(pdu.Monitors[index]);
Stream_Read_UINT32(s, monitor->Flags); /* Flags (4 bytes) */
Stream_Read_UINT32(s, monitor->Left); /* Left (4 bytes) */
Stream_Read_UINT32(s, monitor->Top); /* Top (4 bytes) */
Stream_Read_UINT32(s, monitor->Width); /* Width (4 bytes) */
Stream_Read_UINT32(s, monitor->Height); /* Height (4 bytes) */
Stream_Read_UINT32(s, monitor->PhysicalWidth); /* PhysicalWidth (4 bytes) */
Stream_Read_UINT32(s, monitor->PhysicalHeight); /* PhysicalHeight (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->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
disp_server_sanitize_monitor_layout(monitor);
WLog_DBG(TAG,
"\t%" PRIu32 " : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32
") W/H=%" PRIu32 "x%" PRIu32 ")",
index, monitor->Flags, monitor->Left, monitor->Top, monitor->Width,
monitor->Height);
WLog_DBG(TAG,
"\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32 " Orientation: %" PRIu32
"",
monitor->PhysicalWidth, monitor->PhysicalHeight, monitor->Orientation);
if (!disp_server_is_monitor_layout_valid(monitor))
{
error = ERROR_INVALID_DATA;
goto out;
}
}
if (context)
IFCALLRET(context->DispMonitorLayout, error, context, &pdu);
out:
free(pdu.Monitors);
return error;
}
static UINT disp_server_receive_pdu(DispServerContext* context, wStream* s)
{
UINT error = CHANNEL_RC_OK;
size_t beg = 0;
size_t end = 0;
DISPLAY_CONTROL_HEADER header = { 0 };
WINPR_ASSERT(s);
WINPR_ASSERT(context);
beg = Stream_GetPosition(s);
if ((error = disp_read_header(s, &header)))
{
WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
return error;
}
switch (header.type)
{
case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT:
if ((error = disp_recv_display_control_monitor_layout_pdu(s, context)))
WLog_ERR(TAG,
"disp_recv_display_control_monitor_layout_pdu "
"failed with error %" PRIu32 "!",
error);
break;
default:
error = CHANNEL_RC_BAD_PROC;
WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.type);
break;
}
end = Stream_GetPosition(s);
if (end != (beg + header.length))
{
WLog_ERR(TAG, "Unexpected DISP pdu end: Actual: %" PRIuz ", Expected: %" PRIuz "", end,
(beg + header.length));
Stream_SetPosition(s, (beg + header.length));
}
return error;
}
static UINT disp_server_handle_messages(DispServerContext* context)
{
DWORD BytesReturned = 0;
void* buffer = NULL;
UINT ret = CHANNEL_RC_OK;
DispServerPrivate* priv = NULL;
wStream* s = NULL;
WINPR_ASSERT(context);
priv = context->priv;
WINPR_ASSERT(priv);
s = priv->input_stream;
WINPR_ASSERT(s);
/* Check whether the dynamic channel is ready */
if (!priv->isReady)
{
if (WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualChannelReady, &buffer,
&BytesReturned) == FALSE)
{
if (GetLastError() == ERROR_NO_DATA)
return ERROR_NO_DATA;
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
return ERROR_INTERNAL_ERROR;
}
priv->isReady = *((BOOL*)buffer);
WTSFreeMemory(buffer);
}
/* Consume channel event only after the disp dynamic channel is ready */
Stream_SetPosition(s, 0);
if (!WTSVirtualChannelRead(priv->disp_channel, 0, NULL, 0, &BytesReturned))
{
if (GetLastError() == ERROR_NO_DATA)
return ERROR_NO_DATA;
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
return ERROR_INTERNAL_ERROR;
}
if (BytesReturned < 1)
return CHANNEL_RC_OK;
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
return CHANNEL_RC_NO_MEMORY;
}
const size_t cap = Stream_Capacity(s);
if (cap > UINT32_MAX)
return CHANNEL_RC_NO_BUFFER;
if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s, char), (ULONG)cap,
&BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
return ERROR_INTERNAL_ERROR;
}
Stream_SetLength(s, BytesReturned);
Stream_SetPosition(s, 0);
while (Stream_GetPosition(s) < Stream_Length(s))
{
if ((ret = disp_server_receive_pdu(context, s)))
{
WLog_ERR(TAG,
"disp_server_receive_pdu "
"failed with error %" PRIu32 "!",
ret);
return ret;
}
}
return ret;
}
static DWORD WINAPI disp_server_thread_func(LPVOID arg)
{
DispServerContext* context = (DispServerContext*)arg;
DispServerPrivate* priv = NULL;
DWORD status = 0;
DWORD nCount = 0;
HANDLE events[8] = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
priv = context->priv;
WINPR_ASSERT(priv);
events[nCount++] = priv->stopEvent;
events[nCount++] = priv->channelEvent;
/* Main virtual channel loop. RDPEDISP do not need version negotiation */
while (TRUE)
{
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
break;
}
/* Stop Event */
if (status == WAIT_OBJECT_0)
break;
if ((error = disp_server_handle_messages(context)))
{
WLog_ERR(TAG, "disp_server_handle_messages failed with error %" PRIu32 "", error);
break;
}
}
ExitThread(error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_server_open(DispServerContext* context)
{
UINT rc = ERROR_INTERNAL_ERROR;
DispServerPrivate* priv = NULL;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
void* buffer = NULL;
UINT32 channelId = 0;
BOOL status = TRUE;
WINPR_ASSERT(context);
priv = context->priv;
WINPR_ASSERT(priv);
priv->SessionId = WTS_CURRENT_SESSION;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
priv->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
priv->disp_channel =
WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!priv->disp_channel)
{
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
rc = GetLastError();
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 */
if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) ||
(BytesReturned != sizeof(HANDLE)))
{
WLog_ERR(TAG,
"WTSVirtualChannelQuery failed "
"or invalid returned size(%" PRIu32 ")",
BytesReturned);
if (buffer)
WTSFreeMemory(buffer);
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
if (priv->thread == NULL)
{
if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
if (!(priv->thread =
CreateThread(NULL, 0, disp_server_thread_func, (void*)context, 0, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
(void)CloseHandle(priv->stopEvent);
priv->stopEvent = NULL;
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
}
return CHANNEL_RC_OK;
out_close:
(void)WTSVirtualChannelClose(priv->disp_channel);
priv->disp_channel = NULL;
priv->channelEvent = NULL;
return rc;
}
static UINT disp_server_packet_send(DispServerContext* context, wStream* s)
{
UINT ret = 0;
ULONG written = 0;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
const size_t pos = Stream_GetPosition(s);
WINPR_ASSERT(pos <= UINT32_MAX);
if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s, char), (UINT32)pos,
&written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
ret = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
ret = CHANNEL_RC_OK;
out:
Stream_Free(s, TRUE);
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_server_send_caps_pdu(DispServerContext* context)
{
wStream* s = NULL;
WINPR_ASSERT(context);
s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
if (!s)
{
WLog_ERR(TAG, "disp_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT32(s, context->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
return disp_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT disp_server_close(DispServerContext* context)
{
UINT error = CHANNEL_RC_OK;
DispServerPrivate* priv = NULL;
WINPR_ASSERT(context);
priv = context->priv;
WINPR_ASSERT(priv);
if (priv->thread)
{
(void)SetEvent(priv->stopEvent);
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
(void)CloseHandle(priv->thread);
(void)CloseHandle(priv->stopEvent);
priv->thread = NULL;
priv->stopEvent = NULL;
}
if (priv->disp_channel)
{
(void)WTSVirtualChannelClose(priv->disp_channel);
priv->disp_channel = NULL;
}
return error;
}
DispServerContext* disp_server_context_new(HANDLE vcm)
{
DispServerContext* context = NULL;
DispServerPrivate* priv = NULL;
context = (DispServerContext*)calloc(1, sizeof(DispServerContext));
if (!context)
{
WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!");
goto fail;
}
priv = context->priv = (DispServerPrivate*)calloc(1, sizeof(DispServerPrivate));
if (!context->priv)
{
WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!");
goto fail;
}
priv->input_stream = Stream_New(NULL, 4);
if (!priv->input_stream)
{
WLog_ERR(TAG, "Stream_New failed!");
goto fail;
}
context->vcm = vcm;
context->Open = disp_server_open;
context->Close = disp_server_close;
context->DisplayControlCaps = disp_server_send_caps_pdu;
priv->isReady = FALSE;
return context;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
disp_server_context_free(context);
WINPR_PRAGMA_DIAG_POP
return NULL;
}
void disp_server_context_free(DispServerContext* context)
{
if (!context)
return;
if (context->priv)
{
disp_server_close(context);
Stream_Free(context->priv->input_stream, TRUE);
free(context->priv);
}
free(context);
}

View File

@ -1,37 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
*
* 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_DISP_SERVER_MAIN_H
#define FREERDP_CHANNEL_DISP_SERVER_MAIN_H
#include <freerdp/server/disp.h>
struct s_disp_server_private
{
BOOL isReady;
wStream* input_stream;
HANDLE channelEvent;
HANDLE thread;
HANDLE stopEvent;
DWORD SessionId;
void* disp_channel;
};
#endif /* FREERDP_CHANNEL_DISP_SERVER_MAIN_H */

View File

@ -21,6 +21,3 @@ if(WITH_CLIENT_CHANNELS)
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -1,7 +1,7 @@
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT ON)
set(OPTION_SERVER_DEFAULT ON)
set(OPTION_SERVER_DEFAULT OFF)
define_channel_options(NAME "drdynvc" TYPE "static"
DESCRIPTION "Dynamic Virtual Channel Extension"

View File

@ -20,6 +20,23 @@ define_channel_client("drdynvc")
set(${MODULE_PREFIX}_SRCS
drdynvc_main.c
drdynvc_main.h
)
drdynvc_types.h
dvcman.c
dvcman.h)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-synch)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,6 @@
* Dynamic Virtual Channel
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,115 +17,40 @@
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_DRDYNVC_CLIENT_MAIN_H
#define FREERDP_CHANNEL_DRDYNVC_CLIENT_MAIN_H
#include <winpr/wlog.h>
#include <winpr/synch.h>
#include <freerdp/settings.h>
#include <winpr/collections.h>
#ifndef __DRDYNVC_MAIN_H
#define __DRDYNVC_MAIN_H
#include <freerdp/api.h>
#include <freerdp/svc.h>
#include <freerdp/dvc.h>
#include <freerdp/addin.h>
#include <freerdp/channels/log.h>
#include <freerdp/client/drdynvc.h>
#include <freerdp/freerdp.h>
#include <freerdp/utils/svc_plugin.h>
#define CREATE_REQUEST_PDU 0x01
#define DATA_FIRST_PDU 0x02
#define DATA_PDU 0x03
#define CLOSE_REQUEST_PDU 0x04
#define CAPABILITY_REQUEST_PDU 0x05
typedef struct drdynvc_plugin drdynvcPlugin;
typedef struct
{
IWTSVirtualChannelManager iface;
drdynvcPlugin* drdynvc;
wArrayList* plugin_names;
wArrayList* plugins;
wHashTable* listeners;
wHashTable* channelsById;
wStreamPool* pool;
} DVCMAN;
typedef struct
{
IWTSListener iface;
DVCMAN* dvcman;
char* channel_name;
UINT32 flags;
IWTSListenerCallback* listener_callback;
} DVCMAN_LISTENER;
typedef struct
{
IDRDYNVC_ENTRY_POINTS iface;
DVCMAN* dvcman;
const ADDIN_ARGV* args;
rdpContext* context;
} DVCMAN_ENTRY_POINTS;
typedef enum
{
DVC_CHANNEL_INIT,
DVC_CHANNEL_RUNNING,
DVC_CHANNEL_CLOSED
} DVC_CHANNEL_STATE;
typedef struct
{
IWTSVirtualChannel iface;
volatile LONG refCounter;
DVC_CHANNEL_STATE state;
DVCMAN* dvcman;
void* pInterface;
UINT32 channel_id;
char* channel_name;
IWTSVirtualChannelCallback* channel_callback;
wStream* dvc_data;
UINT32 dvc_data_length;
CRITICAL_SECTION lock;
} DVCMAN_CHANNEL;
typedef enum
{
DRDYNVC_STATE_INITIAL,
DRDYNVC_STATE_CAPABILITIES,
DRDYNVC_STATE_READY,
DRDYNVC_STATE_OPENING_CHANNEL,
DRDYNVC_STATE_SEND_RECEIVE,
DRDYNVC_STATE_FINAL
} DRDYNVC_STATE;
struct drdynvc_plugin
{
CHANNEL_DEF channelDef;
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
rdpSvcPlugin plugin;
wLog* log;
HANDLE thread;
BOOL async;
wStream* data_in;
void* InitHandle;
DWORD OpenHandle;
wMessageQueue* queue;
DRDYNVC_STATE state;
DrdynvcClientContext* context;
UINT16 version;
int version;
int PriorityCharge0;
int PriorityCharge1;
int PriorityCharge2;
int PriorityCharge3;
rdpContext* rdpcontext;
int channel_error;
IWTSVirtualChannelManager* channel_mgr;
};
#endif /* FREERDP_CHANNEL_DRDYNVC_CLIENT_MAIN_H */
int drdynvc_write_data(drdynvcPlugin* plugin, UINT32 ChannelId, BYTE* data, UINT32 data_size);
int drdynvc_push_event(drdynvcPlugin* plugin, wMessage* event);
#endif

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