Compare commits
189 Commits
stretch-ba
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cc8082c0a | ||
|
|
b607683919 | ||
|
|
dc059a2d39 | ||
|
|
cc9c0a66c8 | ||
|
|
4d650bf630 | ||
|
|
48be888513 | ||
|
|
6187297b6e | ||
|
|
47c0f4de25 | ||
|
|
5685a9eca1 | ||
|
|
40ac300cbc | ||
|
|
4930b843f5 | ||
|
|
66e976dd46 | ||
|
|
7a0cfc871a | ||
|
|
d8fe268285 | ||
|
|
22e2e0315b | ||
|
|
f78f13ecf5 | ||
|
|
ce01c78202 | ||
|
|
3d3a1415b7 | ||
|
|
df721c20b4 | ||
|
|
eeaac3f057 | ||
|
|
2d6a9f3b9b | ||
|
|
6fe4ba798f | ||
|
|
995fbfcdd7 | ||
|
|
a0544fd1f6 | ||
|
|
1c5b0b32c3 | ||
|
|
382aa6b66a | ||
|
|
bdad2ddbfa | ||
|
|
da7dfbb2d6 | ||
|
|
682454acf9 | ||
|
|
577316d7c0 | ||
|
|
928c8abca7 | ||
|
|
5bbfde9671 | ||
|
|
057b4985ff | ||
|
|
4bb5bc077f | ||
|
|
3d5ae03c6a | ||
|
|
d260e3c398 | ||
|
|
3a365cab32 | ||
|
|
a98f5e3e7c | ||
|
|
67a51fdf54 | ||
|
|
05945da048 | ||
|
|
4781bcfcda | ||
|
|
2d5b821574 | ||
|
|
ab16b2e2e4 | ||
|
|
1a6267125c | ||
|
|
ac3e8bd4ac | ||
|
|
9f4041a26f | ||
|
|
c0ca3d2d26 | ||
|
|
ca0db20d36 | ||
|
|
8e69b04d95 | ||
|
|
97496cbb22 | ||
|
|
307d2915d6 | ||
|
|
d556b6eca0 | ||
|
|
f37e46dc33 | ||
|
|
03a18ec27e | ||
|
|
9cec8baad9 | ||
|
|
cf6d35cc6a | ||
|
|
0bae4013ec | ||
|
|
c232eabd62 | ||
|
|
833412e07b | ||
|
|
d332a69cd6 | ||
|
|
ce847dee67 | ||
|
|
cfbb8ebcbd | ||
|
|
e8d0f1c676 | ||
|
|
f26a873894 | ||
|
|
1742e02337 | ||
|
|
04e3d1c061 | ||
|
|
069a421a52 | ||
|
|
f9cf50afd8 | ||
|
|
0caf470244 | ||
|
|
af4000f101 | ||
|
|
b937a3a632 | ||
|
|
2f23bb8b95 | ||
|
|
f8793d5c6e | ||
|
|
30905452ca | ||
|
|
f726052dd4 | ||
|
|
c13d353c77 | ||
|
|
8d7afd7dd0 | ||
|
|
a90b67e6c0 | ||
|
|
0b71f55532 | ||
|
|
11fa5f7311 | ||
|
|
53c06fa209 | ||
|
|
ce43920f22 | ||
|
|
9292e895f0 | ||
|
|
834bae447c | ||
|
|
8fc6ef2a66 | ||
|
|
f2ecc9e1e9 | ||
|
|
167ff6dfdc | ||
|
|
85e7b6936c | ||
|
|
85472f7e5f | ||
|
|
f7a9ffba9f | ||
|
|
63d3311554 | ||
|
|
c7cf752140 | ||
|
|
4ab6e978fe | ||
|
|
45f744e4b0 | ||
|
|
cf66ce7ac6 | ||
|
|
0f88669b15 | ||
|
|
0547b4933a | ||
|
|
ae4ce05b6b | ||
|
|
4f40975b3a | ||
|
|
bbef5038b1 | ||
|
|
b049925296 | ||
|
|
1aaad2c68f | ||
|
|
58e356d6f6 | ||
|
|
a73b15bc0d | ||
|
|
a375cb6a81 | ||
|
|
2558cdec15 | ||
|
|
12d8c44ea9 | ||
|
|
b6a6ec6f80 | ||
|
|
4e5cdbea0a | ||
|
|
65144c4ee4 | ||
|
|
90d92d21f0 | ||
|
|
6378eb1d42 | ||
|
|
55feba579f | ||
|
|
2ef323aa69 | ||
|
|
68e1ca324f | ||
|
|
a2fc9432ec | ||
|
|
21d2367ceb | ||
|
|
1a69e83215 | ||
|
|
91e29c5e59 | ||
|
|
ff70cab82f | ||
|
|
2d7707f3f8 | ||
|
|
fc8bd9add6 | ||
|
|
a58a05cff9 | ||
|
|
702fe3f9aa | ||
|
|
bf9eaf6dde | ||
|
|
4a2c5cc4fa | ||
|
|
1fc6dbb280 | ||
|
|
69806bec69 | ||
|
|
5243d7ecba | ||
|
|
6e36b45ff9 | ||
|
|
98586d2380 | ||
|
|
0dc851016e | ||
|
|
3685742385 | ||
|
|
ed9b274020 | ||
|
|
6d198059a2 | ||
|
|
7966d9219f | ||
|
|
b87910fc96 | ||
|
|
c118a0148b | ||
|
|
cd9ec0da61 | ||
|
|
fe6378ecd3 | ||
|
|
5c9c6b6890 | ||
|
|
34e4ef129f | ||
|
|
11e004d268 | ||
|
|
f5d19a26c6 | ||
|
|
e6ef80167b | ||
|
|
8baebc39b4 | ||
|
|
e07d692f0f | ||
|
|
71cb74df5f | ||
|
|
e7fbbcca00 | ||
|
|
5f504f040f | ||
|
|
1b42d1864a | ||
|
|
e672a1bc07 | ||
|
|
2bba749e9d | ||
|
|
724b0ca3fd | ||
|
|
3c49534da9 | ||
|
|
0136a1e751 | ||
|
|
7acf8c267f | ||
|
|
e8c8aa4315 | ||
|
|
8e3d922b3a | ||
|
|
912dda7d3e | ||
|
|
7b93d179a4 | ||
|
|
087a78eaf3 | ||
|
|
71289074c9 | ||
|
|
c23d6e5522 | ||
|
|
7097255046 | ||
|
|
520a960178 | ||
|
|
d79d3435f1 | ||
|
|
febc688034 | ||
|
|
682b29838a | ||
|
|
2ccf2b990b | ||
|
|
810f50e374 | ||
|
|
9810fe2325 | ||
|
|
c533b1b395 | ||
|
|
a249cc0471 | ||
|
|
2feae26618 | ||
|
|
55aaa4bce0 | ||
|
|
2bad0fc101 | ||
|
|
cd773f0284 | ||
|
|
9f7715c3c1 | ||
|
|
4ade698d02 | ||
|
|
faaa641fab | ||
|
|
0a5c52c556 | ||
|
|
4ab566bb23 | ||
|
|
97e4118d5b | ||
|
|
a67bf7a39d | ||
|
|
94e4a9adb8 | ||
|
|
b7f902f748 | ||
|
|
aa06bd72da | ||
|
|
29326a1671 |
124
.clang-format
Normal file
124
.clang-format
Normal file
@ -0,0 +1,124 @@
|
||||
---
|
||||
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
|
||||
...
|
||||
25
.github/ISSUE_TEMPLATE.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
## 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 IRC _#freerdp on freenode_
|
||||
* 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!_
|
||||
56
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
56
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
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 IRC _#freerdp on freenode_
|
||||
* 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**
|
||||
* Version of FreeRDP
|
||||
* Command line used
|
||||
* output of `/buildconfig`
|
||||
* OS version connecting to
|
||||
* If available the log output from a run with `/log-level:trace`
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
** Please remove this text before submitting your issue!
|
||||
|
||||
_Thank you for reporting a bug!_
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
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.
|
||||
25
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
25
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
## 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 IRC _#freerdp_ to talk to other developers or help them test your pull might accelerate acceptance
|
||||
* Joining our mailing list <freerdp-devel@lists.sourceforge.net> may be helpful too.
|
||||
|
||||
## Please remove this text before submitting your pull!
|
||||
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# 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:
|
||||
push:
|
||||
branches: [ master, stable* ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master, stable* ]
|
||||
schedule:
|
||||
- cron: '30 8 * * 6'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# 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.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
- run: |
|
||||
sudo apt update
|
||||
sudo apt install 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 xsltproc 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 libfuse-dev libsystemd-dev libcairo2-dev libsoxr-dev
|
||||
mkdir ci-build
|
||||
cd ci-build
|
||||
cmake -GNinja ../ci/cmake-preloads/config-linux-all.txt ..
|
||||
cmake --build .
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
154
.gitignore
vendored
Normal file
154
.gitignore
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
#ninja
|
||||
.ninja_deps
|
||||
.ninja_log
|
||||
build.ninja
|
||||
rules.ninja
|
||||
|
||||
# CMake
|
||||
CMakeFiles/
|
||||
CMakeScripts/
|
||||
CMakeCache.txt
|
||||
config.h
|
||||
install_manifest*.txt
|
||||
CTestTestfile.cmake
|
||||
*.pc
|
||||
Makefile
|
||||
Testing
|
||||
cmake_install.cmake
|
||||
CPackConfig.cmake
|
||||
CPackSourceConfig.cmake
|
||||
DartConfiguration.tcl
|
||||
CMakeCPackOptions.cmake
|
||||
_CPack_Packages
|
||||
LICENSE.txt
|
||||
/external/*
|
||||
!external/README
|
||||
*Config.cmake
|
||||
*ConfigVersion.cmake
|
||||
include/freerdp/version.h
|
||||
include/freerdp/build-config.h
|
||||
buildflags.h
|
||||
|
||||
*.a.objlist.cmake
|
||||
*.a.objlist
|
||||
*.a.objdir
|
||||
*_dummy.c
|
||||
*_dummy.c.base
|
||||
|
||||
# Eclipse
|
||||
*.project
|
||||
*.cproject
|
||||
*.settings
|
||||
|
||||
nbproject/
|
||||
compile_commands.json
|
||||
|
||||
# .rdp files
|
||||
*.rdp
|
||||
*.RDP
|
||||
|
||||
# Documentation
|
||||
docs/api
|
||||
client/X11/xfreerdp.1
|
||||
client/X11/xfreerdp.1.xml
|
||||
|
||||
# Mac OS X
|
||||
.DS_Store
|
||||
*.xcodeproj/
|
||||
DerivedData/
|
||||
|
||||
# iOS
|
||||
FreeRDP.build
|
||||
Debug-*
|
||||
Release-*
|
||||
|
||||
# Windows
|
||||
*.vcxproj
|
||||
*.vcxproj.*
|
||||
*.vcproj
|
||||
*.vcproj.*
|
||||
*.aps
|
||||
*.sdf
|
||||
*.sln
|
||||
*.suo
|
||||
*.ncb
|
||||
*.opensdf
|
||||
Thumbs.db
|
||||
ipch
|
||||
Debug
|
||||
RelWithDebInfo
|
||||
*.lib
|
||||
*.exp
|
||||
*.pdb
|
||||
*.dll
|
||||
*.ilk
|
||||
*.resource.txt
|
||||
*.embed.manifest*
|
||||
*.intermediate.manifest*
|
||||
version.rc
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
|
||||
# Binaries
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
bin
|
||||
libs
|
||||
cunit/test_freerdp
|
||||
client/X11/xfreerdp
|
||||
client/Mac/xcode
|
||||
client/Sample/sfreerdp
|
||||
client/Wayland/wlfreerdp
|
||||
server/Sample/sfreerdp-server
|
||||
server/X11/xfreerdp-server
|
||||
server/proxy/freerdp-proxy
|
||||
xcode
|
||||
libfreerdp/codec/test/TestOpenH264ASM
|
||||
|
||||
# Other
|
||||
*~
|
||||
*.dir
|
||||
Release
|
||||
Win32
|
||||
build*/
|
||||
*.orig
|
||||
*.msrcIncident
|
||||
|
||||
default.log
|
||||
*Amplifier XE*
|
||||
*Inspector XE*
|
||||
|
||||
*.cbp
|
||||
*.txt.user
|
||||
|
||||
*.autosave
|
||||
|
||||
# etags
|
||||
TAGS
|
||||
|
||||
# generated packages
|
||||
*.zip
|
||||
*.exe
|
||||
#*.sh
|
||||
*.deb
|
||||
*.rpm
|
||||
*.dmg
|
||||
*.tar.Z
|
||||
*.tar.gz
|
||||
|
||||
# packaging related files
|
||||
!packaging/**.sh
|
||||
packaging/deb/freerdp-nightly/freerdp-nightly
|
||||
packaging/deb/freerdp-nightly/freerdp-nightly-dev
|
||||
packaging/deb/freerdp-nightly/freerdp-nightly-dbg
|
||||
.source_version
|
||||
|
||||
#
|
||||
.idea
|
||||
|
||||
# VisualStudio Code
|
||||
.vscode
|
||||
cache/
|
||||
@ -1 +0,0 @@
|
||||
2693389a+debian
|
||||
@ -41,14 +41,16 @@ addons:
|
||||
- libgsm1-dev
|
||||
- libavcodec-dev
|
||||
- libavutil-dev
|
||||
- libx264-dev
|
||||
- libxext-dev
|
||||
- ninja-build
|
||||
- libsystemd-dev
|
||||
- libwayland-dev
|
||||
|
||||
before_script:
|
||||
- ulimit -c unlimited -S
|
||||
|
||||
script:
|
||||
- sudo hostname travis-ci.local
|
||||
- cmake -G "Unix Makefiles" -C ci/cmake-preloads/config-linux-all.txt -D CMAKE_BUILD_TYPE=Debug -DWITH_LIBSYSTEMD=OFF -DWITH_WAYLAND=OFF .
|
||||
- cmake -G Ninja -C ci/cmake-preloads/config-linux-all.txt -D CMAKE_BUILD_TYPE=Debug .
|
||||
- make
|
||||
- make test
|
||||
|
||||
369
CMakeLists.txt
369
CMakeLists.txt
@ -34,9 +34,9 @@ if(NOT DEFINED FREERDP_VENDOR)
|
||||
set(FREERDP_VENDOR 1)
|
||||
endif()
|
||||
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
option(CMAKE_COLOR_MAKEFILE "colorful CMake makefile" ON)
|
||||
option(CMAKE_VERBOSE_MAKEFILE "verbose CMake makefile" ON)
|
||||
option(CMAKE_POSITION_INDEPENDENT_CODE "build with position independent code (-fPIC or -fPIE)" ON)
|
||||
|
||||
# Include our extra modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
||||
@ -51,6 +51,10 @@ endif()
|
||||
include(CheckCmakeCompat)
|
||||
|
||||
# Include cmake modules
|
||||
if(WITH_CLANG_FORMAT)
|
||||
include(ClangFormat)
|
||||
endif()
|
||||
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckSymbolExists)
|
||||
@ -70,6 +74,10 @@ include(InstallFreeRDPMan)
|
||||
include(GetGitRevisionDescription)
|
||||
include(SetFreeRDPCMakeInstallDir)
|
||||
|
||||
if (DEFINE_NO_DEPRECATED)
|
||||
add_definitions(-DDEFINE_NO_DEPRECATED)
|
||||
endif()
|
||||
|
||||
# Soname versioning
|
||||
set(BUILD_NUMBER 0)
|
||||
if ($ENV{BUILD_NUMBER})
|
||||
@ -77,22 +85,22 @@ if ($ENV{BUILD_NUMBER})
|
||||
endif()
|
||||
set(WITH_LIBRARY_VERSIONING "ON")
|
||||
|
||||
set(RAW_VERSTION_STRING "2.0.0-dev5")
|
||||
set(RAW_VERSION_STRING "2.11.7")
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSTION_STRING)
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
|
||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||
git_get_exact_tag(_GIT_TAG --tags --always)
|
||||
if (NOT ${_GIT_TAG} STREQUAL "n/a")
|
||||
set(RAW_VERSTION_STRING ${_GIT_TAG})
|
||||
set(RAW_VERSION_STRING ${_GIT_TAG})
|
||||
endif()
|
||||
endif()
|
||||
string(STRIP ${RAW_VERSTION_STRING} RAW_VERSTION_STRING)
|
||||
string(STRIP ${RAW_VERSION_STRING} RAW_VERSION_STRING)
|
||||
|
||||
set(VERSION_REGEX "^.?([0-9]+)\\.([0-9]+)\\.([0-9]+)-?(.*)")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\1" FREERDP_VERSION_MAJOR "${RAW_VERSTION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\2" FREERDP_VERSION_MINOR "${RAW_VERSTION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\3" FREERDP_VERSION_REVISION "${RAW_VERSTION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\4" FREERDP_VERSION_SUFFIX "${RAW_VERSTION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\1" FREERDP_VERSION_MAJOR "${RAW_VERSION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\2" FREERDP_VERSION_MINOR "${RAW_VERSION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\3" FREERDP_VERSION_REVISION "${RAW_VERSION_STRING}")
|
||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\4" FREERDP_VERSION_SUFFIX "${RAW_VERSION_STRING}")
|
||||
|
||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}")
|
||||
set(FREERDP_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}.${FREERDP_VERSION_REVISION}")
|
||||
@ -103,6 +111,24 @@ else()
|
||||
endif()
|
||||
message("FREERDP_VERSION=${FREERDP_VERSION_FULL}")
|
||||
|
||||
if(EXISTS "${PROJECT_SOURCE_DIR}/.source_version" )
|
||||
file(READ ${PROJECT_SOURCE_DIR}/.source_version GIT_REVISION)
|
||||
|
||||
string(STRIP ${GIT_REVISION} GIT_REVISION)
|
||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||
git_get_exact_tag(GIT_REVISION --tags --always)
|
||||
|
||||
if (${GIT_REVISION} STREQUAL "n/a")
|
||||
git_rev_parse (GIT_REVISION --short)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT GIT_REVISION)
|
||||
set(GIT_REVISION ${FREERDP_VERSION})
|
||||
endif()
|
||||
|
||||
message(STATUS "Git Revision ${GIT_REVISION}")
|
||||
|
||||
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
||||
|
||||
# Compatibility options
|
||||
@ -139,25 +165,11 @@ if(CCACHE AND WITH_CCACHE)
|
||||
endif()
|
||||
endif(CCACHE AND WITH_CCACHE)
|
||||
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_version" )
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.source_version GIT_REVISION)
|
||||
|
||||
string(STRIP ${GIT_REVISION} GIT_REVISION)
|
||||
else()
|
||||
git_get_exact_tag(GIT_REVISION --tags --always)
|
||||
|
||||
if (${GIT_REVISION} STREQUAL "n/a")
|
||||
git_rev_parse (GIT_REVISION --short)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
endif(CMAKE_CROSSCOMPILING)
|
||||
# /Allow to search the host machine for git/ccache
|
||||
|
||||
message(STATUS "Git Revision ${GIT_REVISION}")
|
||||
|
||||
# Turn on solution folders (2.8.4+)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
@ -213,7 +225,7 @@ endif()
|
||||
if(MSVC)
|
||||
include(MSVCRuntime)
|
||||
if(NOT DEFINED MSVC_RUNTIME)
|
||||
set(MSVC_RUNTIME "dynamic")
|
||||
set(MSVC_RUNTIME "dynamic" CACHE STRING "MSVC runtime type [dynamic|static]")
|
||||
endif()
|
||||
if(MSVC_RUNTIME STREQUAL "static")
|
||||
if(BUILD_SHARED_LIBS)
|
||||
@ -337,106 +349,99 @@ if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(THREAD_PREFER_PTHREAD_FLAG TRUE)
|
||||
|
||||
if(NOT IOS)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT IOS)
|
||||
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
endif (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
CHECK_LIBRARY_EXISTS(pthreads pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
endif (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
|
||||
if (HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
set(HAVE_PTHREAD_MUTEX_TIMEDLOCK ON)
|
||||
endif (HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
endif()
|
||||
|
||||
# Enable address sanitizer, where supported and when required
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_REQUIRED_FLAGS_SAVED ${CMAKE_REQUIRED_FLAGS})
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
|
||||
if (fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_LINK_OPTIONS_SAVED ${CMAKE_REQUIRED_LINK_OPTIONS})
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/foo.txt "")
|
||||
if(WITH_SANITIZE_ADDRESS)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
|
||||
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=address")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=address" fsanitize-address)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-address-use-after-scope" fsanitize-address-use-after-scope)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(fsanitize-address)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-address-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-address-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
|
||||
if(fsanitize-address-use-after-scope)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-address-use-after-scope")
|
||||
endif(fsanitize-address-use-after-scope)
|
||||
|
||||
else(fsanitize-address)
|
||||
message(WARNING "Missing support for address sanitizer!")
|
||||
endif(fsanitize-address)
|
||||
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
if(fsanitize-address-use-after-scope)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-address-use-after-scope")
|
||||
endif(fsanitize-address-use-after-scope)
|
||||
elseif(WITH_SANITIZE_MEMORY)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=memory")
|
||||
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=memory")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=memory" fsanitize-memory)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-memory-use-after-dtor" fsanitize-memory-use-after-dtor)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-memory-track-origins" fsanitize-memory-track-origins)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(fsanitize-memory)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory")
|
||||
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-memory-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-memory-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
|
||||
if (fsanitize-memory-use-after-dtor)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-use-after-dtor")
|
||||
endif(fsanitize-memory-use-after-dtor)
|
||||
if (fsanitize-memory-use-after-dtor)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-use-after-dtor")
|
||||
endif(fsanitize-memory-use-after-dtor)
|
||||
|
||||
if (fsanitize-memory-track-origins)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins")
|
||||
endif(fsanitize-memory-track-origins)
|
||||
|
||||
else(fsanitize-memory)
|
||||
message(WARNING "Missing support for memory sanitizer!")
|
||||
endif(fsanitize-memory)
|
||||
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
if (fsanitize-memory-track-origins)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins")
|
||||
endif(fsanitize-memory-track-origins)
|
||||
elseif(WITH_SANITIZE_THREAD)
|
||||
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=thread")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=thread" fsanitize-thread)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if(fsanitize-thread)
|
||||
set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=thread")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-thread-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=thread")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread")
|
||||
else(fsanitize-thread)
|
||||
message(WARNING "Missing support for thread sanitizer!")
|
||||
endif(fsanitize-thread)
|
||||
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-thread-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=thread")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread")
|
||||
endif()
|
||||
|
||||
file(REMOVE ${CMAKE_BINARY_DIR}/foo.txt)
|
||||
set(CMAKE_REQUIRED_LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS_SAVED})
|
||||
|
||||
if (WITH_NO_UNDEFINED)
|
||||
set(CMAKE_REQUIRED_FLAGS "-Wl,--no-undefined")
|
||||
CHECK_C_COMPILER_FLAG (-Wl,--no-undefined no-undefined)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(no-undefined)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined" )
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVED})
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
@ -493,7 +498,7 @@ if(WIN32)
|
||||
string(TIMESTAMP RC_VERSION_YEAR "%Y")
|
||||
|
||||
if(NOT DEFINED CMAKE_WINDOWS_VERSION)
|
||||
set(CMAKE_WINDOWS_VERSION "WINXP")
|
||||
set(CMAKE_WINDOWS_VERSION "WIN7")
|
||||
endif()
|
||||
|
||||
if(CMAKE_WINDOWS_VERSION STREQUAL "WINXP")
|
||||
@ -510,7 +515,7 @@ if(WIN32)
|
||||
set(RC_VERSION_VENDOR ${VENDOR})
|
||||
set(RC_VERSION_PRODUCT ${PRODUCT})
|
||||
set(RC_VERSION_PATCH ${BUILD_NUMBER})
|
||||
set(RC_VERSION_DESCRIPTION "${FREERDP_VERSION_FULL} ${GIT_REVISION} ${CMAKE_WINDOWS_VERSION} ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
set(RC_VERSION_DESCRIPTION "${FREERDP_VERSION_FULL} ${GIT_REVISION} ${CMAKE_WINDOWS_VERSION} ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
|
||||
if (FREERDP_EXTERNAL_SSL_PATH)
|
||||
set(OPENSSL_ROOT_DIR ${FREERDP_EXTERNAL_SSL_PATH})
|
||||
@ -527,7 +532,6 @@ add_definitions(-DWINPR_EXPORTS -DFREERDP_EXPORTS)
|
||||
if(NOT IOS)
|
||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(execinfo.h HAVE_EXECINFO_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
||||
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
||||
@ -535,6 +539,18 @@ if(NOT IOS)
|
||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||
check_include_files(execinfo.h HAVE_EXECINFO_HEADER)
|
||||
if (HAVE_EXECINFO_HEADER)
|
||||
check_symbol_exists(backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
|
||||
check_symbol_exists(backtrace_symbols execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS)
|
||||
check_symbol_exists(backtrace_symbols_fd execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
|
||||
|
||||
# Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define
|
||||
# the backtrace functions. Disable detection for these cases
|
||||
if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS AND HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
|
||||
set(HAVE_EXECINFO_H ON)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(HAVE_FCNTL_H 1)
|
||||
set(HAVE_UNISTD_H 1)
|
||||
@ -593,6 +609,11 @@ if(ANDROID)
|
||||
set (WITH_NEON OFF)
|
||||
endif()
|
||||
|
||||
if(ANDROID_ABI STREQUAL arm64-v8a)
|
||||
# https://github.com/android/ndk/issues/910
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
|
||||
endif()
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
add_definitions(-DNDK_DEBUG=1)
|
||||
|
||||
@ -611,7 +632,7 @@ if(ANDROID)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list (APPEND CMAKE_INCLUDE_PATH ${FREERDP_EXTERNAL_PATH}/include)
|
||||
list (APPEND CMAKE_INCLUDE_PATH ${FREERDP_EXTERNAL_PATH}/${ANDROID_ABI}/include)
|
||||
list (APPEND CMAKE_LIBRARY_PATH ${FREERDP_EXTERNAL_PATH}/${ANDROID_ABI}/ )
|
||||
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH )
|
||||
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH )
|
||||
@ -622,26 +643,6 @@ if(ANDROID)
|
||||
endif(WITH_GPROF)
|
||||
endif()
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
|
||||
if(NOT IOS)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
endif (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
CHECK_LIBRARY_EXISTS(pthreads pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
endif (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
|
||||
if (HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
set(HAVE_PTHREAD_MUTEX_TIMEDLOCK ON)
|
||||
endif (HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
endif()
|
||||
|
||||
if(WITH_VALGRIND_MEMCHECK)
|
||||
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
else()
|
||||
@ -667,6 +668,11 @@ if(UNIX OR CYGWIN)
|
||||
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES m)
|
||||
set(X11_FEATURE_TYPE "RECOMMENDED")
|
||||
set(WAYLAND_FEATURE_TYPE "RECOMMENDED")
|
||||
|
||||
include(CheckFunctionExists)
|
||||
|
||||
check_function_exists(getlogin_r HAVE_GETLOGIN_R)
|
||||
check_function_exists(getpwuid_r HAVE_GETPWUID_R)
|
||||
else()
|
||||
set(X11_FEATURE_TYPE "DISABLED")
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
@ -706,15 +712,15 @@ set(ALSA_FEATURE_TYPE "RECOMMENDED")
|
||||
set(ALSA_FEATURE_PURPOSE "sound")
|
||||
set(ALSA_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection")
|
||||
|
||||
set(PULSE_FEATURE_TYPE "OPTIONAL")
|
||||
set(PULSE_FEATURE_TYPE "RECOMMENDED")
|
||||
set(PULSE_FEATURE_PURPOSE "sound")
|
||||
set(PULSE_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection")
|
||||
|
||||
set(CUPS_FEATURE_TYPE "OPTIONAL")
|
||||
set(CUPS_FEATURE_TYPE "RECOMMENDED")
|
||||
set(CUPS_FEATURE_PURPOSE "printing")
|
||||
set(CUPS_FEATURE_DESCRIPTION "printer device redirection")
|
||||
|
||||
set(PCSC_FEATURE_TYPE "OPTIONAL")
|
||||
set(PCSC_FEATURE_TYPE "RECOMMENDED")
|
||||
set(PCSC_FEATURE_PURPOSE "smart card")
|
||||
set(PCSC_FEATURE_DESCRIPTION "smart card device redirection")
|
||||
|
||||
@ -724,15 +730,7 @@ set(FFMPEG_FEATURE_DESCRIPTION "multimedia redirection, audio and video playback
|
||||
|
||||
set(VAAPI_FEATURE_TYPE "OPTIONAL")
|
||||
set(VAAPI_FEATURE_PURPOSE "multimedia")
|
||||
set(VAAPI_FEATURE_DESCRIPTION "VA-API hardware acceleration for video playback")
|
||||
|
||||
set(GSTREAMER_0_10_FEATURE_TYPE "OPTIONAL")
|
||||
set(GSTREAMER_0_10_FEATURE_PURPOSE "multimedia")
|
||||
set(GSTREAMER_0_10_FEATURE_DESCRIPTION "multimedia redirection, audio and video playback, gstreamer 0.10 version")
|
||||
|
||||
set(GSTREAMER_1_0_FEATURE_TYPE "RECOMMENDED")
|
||||
set(GSTREAMER_1_0_FEATURE_PURPOSE "multimedia")
|
||||
set(GSTREAMER_1_0_FEATURE_DESCRIPTION "multimedia redirection, audio and video playback")
|
||||
set(VAAPI_FEATURE_DESCRIPTION "[experimental] VA-API hardware acceleration for video playback")
|
||||
|
||||
set(IPP_FEATURE_TYPE "OPTIONAL")
|
||||
set(IPP_FEATURE_PURPOSE "performance")
|
||||
@ -742,14 +740,14 @@ set(JPEG_FEATURE_TYPE "OPTIONAL")
|
||||
set(JPEG_FEATURE_PURPOSE "codec")
|
||||
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
||||
|
||||
set(X264_FEATURE_TYPE "OPTIONAL")
|
||||
set(X264_FEATURE_PURPOSE "codec")
|
||||
set(X264_FEATURE_DESCRIPTION "use x264 library")
|
||||
|
||||
set(OPENH264_FEATURE_TYPE "OPTIONAL")
|
||||
set(OPENH264_FEATURE_PURPOSE "codec")
|
||||
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
||||
|
||||
set(OPENCL_FEATURE_TYPE "OPTIONAL")
|
||||
set(OPENCL_FEATURE_PURPOSE "codec")
|
||||
set(OPENCL_FEATURE_DESCRIPTION "[experimental] use OpenCL library")
|
||||
|
||||
set(GSM_FEATURE_TYPE "OPTIONAL")
|
||||
set(GSM_FEATURE_PURPOSE "codec")
|
||||
set(GSM_FEATURE_DESCRIPTION "GSM audio codec library")
|
||||
@ -764,7 +762,7 @@ set(FAAD2_FEATURE_DESCRIPTION "FAAD2 AAC audio codec library")
|
||||
|
||||
set(FAAC_FEATURE_TYPE "OPTIONAL")
|
||||
set(FAAC_FEATURE_PURPOSE "codec")
|
||||
set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library")
|
||||
set(FAAC_FEATURE_DESCRIPTION "[experimental] FAAC AAC audio codec library")
|
||||
|
||||
set(SOXR_FEATURE_TYPE "OPTIONAL")
|
||||
set(SOXR_FEATURE_PURPOSE "codec")
|
||||
@ -772,7 +770,7 @@ set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library")
|
||||
|
||||
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
|
||||
set(GSSAPI_FEATURE_PURPOSE "auth")
|
||||
set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support")
|
||||
set(GSSAPI_FEATURE_DESCRIPTION "[experimental] add kerberos support")
|
||||
|
||||
if(WIN32)
|
||||
set(X11_FEATURE_TYPE "DISABLED")
|
||||
@ -785,15 +783,12 @@ if(WIN32)
|
||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||
set(FFMPEG_FEATURE_TYPE "DISABLED")
|
||||
set(VAAPI_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_1_0_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_0_10_FEATURE_TYPE "OPTIONAL")
|
||||
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(FFMPEG_FEATURE_TYPE "OPTIONAL")
|
||||
set(VAAPI_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_1_0_FEATURE_TYPE "OPTIONAL")
|
||||
set(X11_FEATURE_TYPE "OPTIONAL")
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
set(OSS_FEATURE_TYPE "DISABLED")
|
||||
@ -803,8 +798,6 @@ if(APPLE)
|
||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_1_0_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_0_10_FEATURE_TYPE "DISABLED")
|
||||
endif()
|
||||
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
||||
endif()
|
||||
@ -832,10 +825,7 @@ if(ANDROID)
|
||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||
set(FFMPEG_FEATURE_TYPE "DISABLED")
|
||||
set(VAAPI_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_1_0_FEATURE_TYPE "DISABLED")
|
||||
set(GSTREAMER_0_10_FEATURE_TYPE "DISABLED")
|
||||
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
||||
endif()
|
||||
|
||||
@ -856,12 +846,9 @@ find_feature(PCSC ${PCSC_FEATURE_TYPE} ${PCSC_FEATURE_PURPOSE} ${PCSC_FEATURE_DE
|
||||
|
||||
find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(GStreamer_0_10 ${GSTREAMER_0_10_FEATURE_TYPE} ${GSTREAMER_0_10_FEATURE_PURPOSE} ${GSTREAMER_0_10_FEATURE_DESCRIPTION})
|
||||
find_feature(GStreamer_1_0 ${GSTREAMER_1_0_FEATURE_TYPE} ${GSTREAMER_1_0_FEATURE_PURPOSE} ${GSTREAMER_1_0_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION})
|
||||
find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION})
|
||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
||||
find_feature(LAME ${LAME_FEATURE_TYPE} ${LAME_FEATURE_PURPOSE} ${LAME_FEATURE_DESCRIPTION})
|
||||
find_feature(FAAD2 ${FAAD2_FEATURE_TYPE} ${FAAD2_FEATURE_PURPOSE} ${FAAD2_FEATURE_DESCRIPTION})
|
||||
@ -871,7 +858,7 @@ find_feature(soxr ${SOXR_FEATURE_TYPE} ${SOXR_FEATURE_PURPOSE} ${SOXR_FEATURE_DE
|
||||
find_feature(GSSAPI ${GSSAPI_FEATURE_TYPE} ${GSSAPI_FEATURE_PURPOSE} ${GSSAPI_FEATURE_DESCRIPTION})
|
||||
|
||||
if (WITH_OPENH264 AND NOT WITH_OPENH264_LOADING)
|
||||
set(WITH_OPENH264_LOADING OFF)
|
||||
option(WITH_OPENH264_LOADING "Use LoadLibrary to load openh264 at runtime" OFF)
|
||||
endif (WITH_OPENH264 AND NOT WITH_OPENH264_LOADING)
|
||||
|
||||
if ((WITH_FFMPEG OR WITH_DSP_FFMPEG) AND NOT FFMPEG_FOUND)
|
||||
@ -885,18 +872,23 @@ if (WITH_DSP_FFMPEG)
|
||||
# Deactivate FFmpeg backend for sound, if the version is too old.
|
||||
# See libfreerdp/codec/dsp_ffmpeg.h
|
||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||
if (EXISTS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h")
|
||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h" AV_VERSION_FILE2 REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||
list(APPEND AV_VERSION_FILE ${AV_VERSION_FILE2})
|
||||
endif()
|
||||
|
||||
FOREACH(item ${AV_VERSION_FILE})
|
||||
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
||||
IF(litem)
|
||||
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
||||
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
||||
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
message(ERROR "invalid entry in libavcodec version header ${item}")
|
||||
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
list(GET VSPLIT_LINE 0 VNAME)
|
||||
list(GET VSPLIT_LINE 1 VVALUE)
|
||||
set(${VNAME} ${VVALUE})
|
||||
ENDIF(litem)
|
||||
IF(litem)
|
||||
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
||||
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
||||
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
message(ERROR "invalid entry in libavcodec version header ${item}")
|
||||
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
list(GET VSPLIT_LINE 0 VNAME)
|
||||
list(GET VSPLIT_LINE 1 VVALUE)
|
||||
set(${VNAME} ${VVALUE})
|
||||
ENDIF(litem)
|
||||
ENDFOREACH(item ${AV_VERSION_FILE})
|
||||
|
||||
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
||||
@ -945,11 +937,7 @@ if(MBEDTLS_FOUND)
|
||||
add_definitions("-DWITH_MBEDTLS")
|
||||
endif()
|
||||
|
||||
if (TARGET_ARCH MATCHES "sparc")
|
||||
set(HAVE_ALIGNED_REQUIRED 1)
|
||||
endif()
|
||||
|
||||
if (WITH_X264 OR WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG)
|
||||
if (WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG OR WITH_MEDIACODEC)
|
||||
set(WITH_GFX_H264 ON)
|
||||
else()
|
||||
set(WITH_GFX_H264 OFF)
|
||||
@ -957,32 +945,42 @@ endif()
|
||||
|
||||
# Android expects all libraries to be loadable
|
||||
# without paths.
|
||||
if (ANDROID)
|
||||
set(FREERDP_DATA_PATH "share")
|
||||
set(FREERDP_INSTALL_PREFIX ".")
|
||||
set(FREERDP_LIBRARY_PATH ".")
|
||||
set(FREERDP_PLUGIN_PATH ".")
|
||||
set(FREERDP_ADDIN_PATH ".")
|
||||
else (ANDROID)
|
||||
if (ANDROID OR WIN32 OR MAC_BUNDLE)
|
||||
set(FREERDP_DATA_PATH "share")
|
||||
if (NOT FREERDP_INSTALL_PREFIX)
|
||||
set(FREERDP_INSTALL_PREFIX ".")
|
||||
endif()
|
||||
set(FREERDP_LIBRARY_PATH ".")
|
||||
set(FREERDP_PLUGIN_PATH ".")
|
||||
else()
|
||||
set(FREERDP_DATA_PATH "${CMAKE_INSTALL_PREFIX}/share/freerdp${FREERDP_VERSION_MAJOR}")
|
||||
set(FREERDP_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
if (NOT FREERDP_INSTALL_PREFIX)
|
||||
set(FREERDP_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
endif()
|
||||
set(FREERDP_LIBRARY_PATH "${CMAKE_INSTALL_LIBDIR}")
|
||||
set(FREERDP_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}")
|
||||
set(FREERDP_ADDIN_PATH "${FREERDP_PLUGIN_PATH}")
|
||||
endif(ANDROID)
|
||||
endif()
|
||||
set(FREERDP_ADDIN_PATH "${FREERDP_PLUGIN_PATH}")
|
||||
|
||||
# Path to put extensions
|
||||
set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}/extensions")
|
||||
|
||||
# Proxy plugins path
|
||||
if(NOT DEFINED PROXY_PLUGINDIR)
|
||||
message("using default plugins location")
|
||||
set(FREERDP_PROXY_PLUGINDIR "${CMAKE_BINARY_DIR}/server/proxy/plugins")
|
||||
else()
|
||||
set(FREERDP_PROXY_PLUGINDIR "${PROXY_PLUGINDIR}")
|
||||
endif()
|
||||
|
||||
# Declare we have config.h, generated later on.
|
||||
add_definitions("-DHAVE_CONFIG_H")
|
||||
|
||||
# Include directories
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Configure files
|
||||
add_definitions("-DHAVE_CONFIG_H")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
# RPATH configuration
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
@ -992,7 +990,16 @@ if (APPLE)
|
||||
else (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
if (NOT FREEBSD)
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
||||
if (NOT BUILTIN_CHANNELS)
|
||||
if (NOT DEFINED WITH_PLUGIN_RPATH_ONLY)
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..:\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
|
||||
else()
|
||||
# we need to supply this run path, even if not using RPATH in general
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
||||
endif()
|
||||
endif()
|
||||
endif(APPLE)
|
||||
|
||||
@ -1055,15 +1062,6 @@ add_subdirectory(include)
|
||||
|
||||
add_subdirectory(libfreerdp)
|
||||
|
||||
if (IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0")
|
||||
if (IOS_PLATFORM MATCHES "SIMULATOR")
|
||||
set(CMAKE_OSX_SYSROOT "iphonesimulator")
|
||||
else()
|
||||
set(CMAKE_OSX_SYSROOT "iphoneos")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# RdTk
|
||||
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
|
||||
include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
|
||||
@ -1098,6 +1096,9 @@ if(WITH_SERVER)
|
||||
add_subdirectory(server)
|
||||
endif()
|
||||
|
||||
# Configure files - Add last so all symbols are defined
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
# Packaging
|
||||
|
||||
set(CMAKE_CPACK_INCLUDE_FILE "CMakeCPack.cmake")
|
||||
|
||||
@ -1,25 +1,22 @@
|
||||
FreeRDP: A Remote Desktop Protocol Implementation
|
||||
=================================================
|
||||
# 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
|
||||
---------
|
||||
## 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/
|
||||
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/
|
||||
|
||||
IRC channel: #freerdp @ irc.freenode.net
|
||||
IRC channel: #freerdp @ irc.freenode.net
|
||||
Mailing list: https://lists.sourceforge.net/lists/listinfo/freerdp-devel
|
||||
|
||||
Microsoft Open Specifications
|
||||
-----------------------------
|
||||
## Microsoft Open Specifications
|
||||
|
||||
Information regarding the Microsoft Open Specifications can be found at:
|
||||
http://www.microsoft.com/openspecifications/
|
||||
@ -27,8 +24,7 @@ http://www.microsoft.com/openspecifications/
|
||||
A list of reference documentation is maintained here:
|
||||
https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
## Compilation
|
||||
|
||||
Instructions on how to get started compiling FreeRDP can be found on the wiki:
|
||||
https://github.com/FreeRDP/FreeRDP/wiki/Compilation
|
||||
@ -32,7 +32,8 @@ macro(define_channel_options)
|
||||
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)
|
||||
|
||||
if(${${CHANNEL_CLIENT_OPTION}})
|
||||
set(OPTION_CLIENT_DEFAULT ${${CHANNEL_CLIENT_OPTION}})
|
||||
endif()
|
||||
@ -52,23 +53,30 @@ macro(define_channel_options)
|
||||
set(CHANNEL_DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
set(CHANNEL_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel")
|
||||
option(${CHANNEL_OPTION} "${CHANNEL_OPTION_DOC}" ${CHANNEL_DEFAULT})
|
||||
|
||||
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()
|
||||
|
||||
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")
|
||||
option(${CHANNEL_CLIENT_OPTION} "${CHANNEL_CLIENT_OPTION_DOC}" ${_channel_client_default})
|
||||
cmake_dependent_option(${CHANNEL_CLIENT_OPTION} "${CHANNEL_CLIENT_OPTION_DOC}"
|
||||
|
||||
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")
|
||||
option(${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}" ${_channel_server_default})
|
||||
cmake_dependent_option(${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}"
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}"
|
||||
${_channel_server_default} "${CHANNEL_OPTION}" OFF)
|
||||
endmacro(define_channel_server_options)
|
||||
|
||||
@ -167,7 +175,12 @@ macro(client_channel_install _targets _destination)
|
||||
endmacro(client_channel_install)
|
||||
|
||||
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
|
||||
if (NOT "${_lnk_dir}" STREQUAL "")
|
||||
link_directories(${_lnk_dir})
|
||||
endif()
|
||||
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
if (WIN32)
|
||||
@ -185,22 +198,30 @@ macro(add_channel_client_library _module_prefix _module_name _channel_name _dyna
|
||||
set ( ${_module_prefix}_SRCS ${${_module_prefix}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
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})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
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})
|
||||
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client_library)
|
||||
|
||||
macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_name _type _dynamic _entry)
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
|
||||
if (NOT "${_lnk_dir}" STREQUAL "")
|
||||
link_directories(${_lnk_dir})
|
||||
endif()
|
||||
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
if (WIN32)
|
||||
@ -218,21 +239,29 @@ macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_
|
||||
set ( ${_module_prefix}_SRCS ${${_module_prefix}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
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})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
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})
|
||||
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client_subsystem_library)
|
||||
|
||||
macro(add_channel_server_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
|
||||
if (NOT "${_lnk_dir}" STREQUAL "")
|
||||
link_directories(${_lnk_dir})
|
||||
endif()
|
||||
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
if (WIN32)
|
||||
@ -266,6 +295,23 @@ 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})
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# FreeRDP cmake android options
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2013 Thincast Technologies GmbH
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
# 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.
|
||||
@ -15,8 +16,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
option(WITH_OPENSLES "Enable sound and microphone redirection using OpenSLES" ON)
|
||||
define_channel("ainput")
|
||||
|
||||
set(ANDROID_APP_TARGET_SDK 21 CACHE STRING "Application target android SDK")
|
||||
set(ANDROID_APP_MIN_SDK 14 CACHE STRING "Application minimum android SDK requirement")
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
13
channels/ainput/ChannelOptions.cmake
Normal file
13
channels/ainput/ChannelOptions.cmake
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(NAME "ainput" TYPE "dynamic"
|
||||
DESCRIPTION "Advanced Input Virtual Channel Extension"
|
||||
SPECIFICATIONS "[XXXXX]"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
||||
|
||||
34
channels/ainput/client/CMakeLists.txt
Normal file
34
channels/ainput/client/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2022 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2022 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client("ainput")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
ainput_main.c
|
||||
ainput_main.h)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
315
channels/ainput/client/ainput_main.c
Normal file
315
channels/ainput/client/ainput_main.c
Normal file
@ -0,0 +1,315 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Advanced Input Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2022 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include "ainput_main.h"
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/ainput.h>
|
||||
#include <freerdp/channels/ainput.h>
|
||||
|
||||
#include "../common/ainput_common.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("ainput.client")
|
||||
|
||||
typedef struct AINPUT_CHANNEL_CALLBACK_ AINPUT_CHANNEL_CALLBACK;
|
||||
struct AINPUT_CHANNEL_CALLBACK_
|
||||
{
|
||||
IWTSVirtualChannelCallback iface;
|
||||
|
||||
IWTSPlugin* plugin;
|
||||
IWTSVirtualChannelManager* channel_mgr;
|
||||
IWTSVirtualChannel* channel;
|
||||
};
|
||||
|
||||
typedef struct AINPUT_LISTENER_CALLBACK_ AINPUT_LISTENER_CALLBACK;
|
||||
struct AINPUT_LISTENER_CALLBACK_
|
||||
{
|
||||
IWTSListenerCallback iface;
|
||||
|
||||
IWTSPlugin* plugin;
|
||||
IWTSVirtualChannelManager* channel_mgr;
|
||||
AINPUT_CHANNEL_CALLBACK* channel_callback;
|
||||
};
|
||||
|
||||
typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN;
|
||||
struct AINPUT_PLUGIN_
|
||||
{
|
||||
IWTSPlugin iface;
|
||||
|
||||
AINPUT_LISTENER_CALLBACK* listener_callback;
|
||||
IWTSListener* listener;
|
||||
UINT32 MajorVersion;
|
||||
UINT32 MinorVersion;
|
||||
BOOL initialized;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
UINT16 type;
|
||||
AINPUT_PLUGIN* ainput;
|
||||
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
ainput = (AINPUT_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (Stream_GetRemainingLength(data) < 2)
|
||||
return ERROR_NO_DATA;
|
||||
Stream_Read_UINT16(data, type);
|
||||
switch (type)
|
||||
{
|
||||
case MSG_AINPUT_VERSION:
|
||||
if (Stream_GetRemainingLength(data) < 8)
|
||||
return ERROR_NO_DATA;
|
||||
Stream_Read_UINT32(data, ainput->MajorVersion);
|
||||
Stream_Read_UINT32(data, ainput->MinorVersion);
|
||||
break;
|
||||
default:
|
||||
WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type);
|
||||
break;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
|
||||
{
|
||||
AINPUT_PLUGIN* ainput;
|
||||
AINPUT_CHANNEL_CALLBACK* callback;
|
||||
BYTE buffer[32] = { 0 };
|
||||
UINT64 time;
|
||||
wStream sbuffer = { 0 };
|
||||
wStream* s = &sbuffer;
|
||||
|
||||
Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
|
||||
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
time = GetTickCount64();
|
||||
ainput = (AINPUT_PLUGIN*)context->handle;
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(ainput->listener_callback);
|
||||
|
||||
if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
|
||||
{
|
||||
WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
|
||||
ainput->MajorVersion, ainput->MinorVersion);
|
||||
return CHANNEL_RC_UNSUPPORTED_VERSION;
|
||||
}
|
||||
callback = ainput->listener_callback->channel_callback;
|
||||
WINPR_ASSERT(callback);
|
||||
|
||||
{
|
||||
char buffer[128] = { 0 };
|
||||
WLog_VRB(TAG, "[%s] sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32,
|
||||
__FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
|
||||
}
|
||||
|
||||
/* Message type */
|
||||
Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
|
||||
|
||||
/* Event data */
|
||||
Stream_Write_UINT64(s, time);
|
||||
Stream_Write_UINT64(s, flags);
|
||||
Stream_Write_INT32(s, x);
|
||||
Stream_Write_INT32(s, y);
|
||||
Stream_SealLength(s);
|
||||
|
||||
/* ainput back what we have received. AINPUT does not have any message IDs. */
|
||||
WINPR_ASSERT(callback->channel);
|
||||
WINPR_ASSERT(callback->channel->Write);
|
||||
return callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s),
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
free(callback);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data,
|
||||
BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
AINPUT_CHANNEL_CALLBACK* callback;
|
||||
AINPUT_LISTENER_CALLBACK* listener_callback = (AINPUT_LISTENER_CALLBACK*)pListenerCallback;
|
||||
|
||||
WINPR_ASSERT(listener_callback);
|
||||
WINPR_UNUSED(Data);
|
||||
WINPR_UNUSED(pbAccept);
|
||||
|
||||
callback = (AINPUT_CHANNEL_CALLBACK*)calloc(1, sizeof(AINPUT_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
callback->iface.OnDataReceived = ainput_on_data_received;
|
||||
callback->iface.OnClose = ainput_on_close;
|
||||
callback->plugin = listener_callback->plugin;
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
listener_callback->channel_callback = callback;
|
||||
|
||||
*ppCallback = &callback->iface;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
UINT status;
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (ainput->initialized)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AINPUT_DVC_CHANNEL_NAME);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
ainput->listener_callback =
|
||||
(AINPUT_LISTENER_CALLBACK*)calloc(1, sizeof(AINPUT_LISTENER_CALLBACK));
|
||||
|
||||
if (!ainput->listener_callback)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
ainput->listener_callback->iface.OnNewChannelConnection = ainput_on_new_channel_connection;
|
||||
ainput->listener_callback->plugin = pPlugin;
|
||||
ainput->listener_callback->channel_mgr = pChannelMgr;
|
||||
|
||||
status = pChannelMgr->CreateListener(pChannelMgr, AINPUT_DVC_CHANNEL_NAME, 0,
|
||||
&ainput->listener_callback->iface, &ainput->listener);
|
||||
|
||||
ainput->listener->pInterface = ainput->iface.pInterface;
|
||||
ainput->initialized = status == CHANNEL_RC_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
|
||||
if (ainput && ainput->listener_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = ainput->listener_callback->channel_mgr;
|
||||
if (mgr)
|
||||
IFCALL(mgr->DestroyListener, mgr, ainput->listener);
|
||||
}
|
||||
if (ainput)
|
||||
{
|
||||
free(ainput->listener_callback);
|
||||
free(ainput->iface.pInterface);
|
||||
}
|
||||
free(ainput);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry ainput_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "ainput");
|
||||
|
||||
if (!ainput)
|
||||
{
|
||||
AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
|
||||
ainput = (AINPUT_PLUGIN*)calloc(1, sizeof(AINPUT_PLUGIN));
|
||||
|
||||
if (!ainput || !context)
|
||||
{
|
||||
free(context);
|
||||
free(ainput);
|
||||
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
ainput->iface.Initialize = ainput_plugin_initialize;
|
||||
ainput->iface.Terminated = ainput_plugin_terminated;
|
||||
|
||||
context->handle = (void*)ainput;
|
||||
context->AInputSendInputEvent = ainput_send_input_event;
|
||||
ainput->iface.pInterface = (void*)context;
|
||||
|
||||
status = pEntryPoints->RegisterPlugin(pEntryPoints, AINPUT_CHANNEL_NAME, &ainput->iface);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
43
channels/ainput/client/ainput_main.h
Normal file
43
channels/ainput/client/ainput_main.h
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Advanced Input Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2022 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define DVC_TAG CHANNELS_TAG("ainput.client")
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H */
|
||||
59
channels/ainput/common/ainput_common.h
Normal file
59
channels/ainput/common/ainput_common.h
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel
|
||||
*
|
||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2022 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_INT_AINPUT_COMMON_H
|
||||
#define FREERDP_INT_AINPUT_COMMON_H
|
||||
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <freerdp/channels/ainput.h>
|
||||
|
||||
static INLINE const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size)
|
||||
{
|
||||
char number[32] = { 0 };
|
||||
|
||||
if (flags & AINPUT_FLAGS_HAVE_REL)
|
||||
winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_WHEEL)
|
||||
winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_MOVE)
|
||||
winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_DOWN)
|
||||
winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_REL)
|
||||
winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_BUTTON1)
|
||||
winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_BUTTON2)
|
||||
winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_BUTTON3)
|
||||
winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|");
|
||||
if (flags & AINPUT_XFLAGS_BUTTON1)
|
||||
winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|");
|
||||
if (flags & AINPUT_XFLAGS_BUTTON2)
|
||||
winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|");
|
||||
|
||||
_snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags);
|
||||
winpr_str_append(number, buffer, size, " ");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endif /* FREERDP_INT_AINPUT_COMMON_H */
|
||||
27
channels/ainput/server/CMakeLists.txt
Normal file
27
channels/ainput/server/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2022 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2022 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_server("ainput")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
ainput_main.c)
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
587
channels/ainput/server/ainput_main.c
Normal file
587
channels/ainput/server/ainput_main.c
Normal file
@ -0,0 +1,587 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Advanced Input Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2022 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/server/ainput.h>
|
||||
#include <freerdp/channels/ainput.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "../common/ainput_common.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("ainput.server")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AINPUT_INITIAL,
|
||||
AINPUT_OPENED,
|
||||
AINPUT_VERSION_SENT,
|
||||
} eAInputChannelState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ainput_server_context context;
|
||||
|
||||
BOOL opened;
|
||||
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
void* ainput_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
BOOL isOpened;
|
||||
BOOL externalThread;
|
||||
|
||||
/* Channel state */
|
||||
eAInputChannelState state;
|
||||
|
||||
wStream* buffer;
|
||||
} ainput_server;
|
||||
|
||||
static UINT ainput_server_context_poll(ainput_server_context* context);
|
||||
static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
|
||||
static UINT ainput_server_context_poll_int(ainput_server_context* context);
|
||||
|
||||
static BOOL ainput_server_is_open(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
return ainput->isOpened;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_server_open_channel(ainput_server* ainput)
|
||||
{
|
||||
DWORD Error;
|
||||
HANDLE hEvent;
|
||||
DWORD StartTick;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = NULL;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ainput->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm);
|
||||
StartTick = GetTickCount();
|
||||
|
||||
while (ainput->ainput_channel == NULL)
|
||||
{
|
||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
|
||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_NOT_FOUND)
|
||||
{
|
||||
WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ainput->ainput_channel)
|
||||
{
|
||||
UINT32 channelId;
|
||||
BOOL status = TRUE;
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
|
||||
|
||||
IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetTickCount() - StartTick > 5000)
|
||||
{
|
||||
WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static UINT ainput_server_send_version(ainput_server* ainput)
|
||||
{
|
||||
ULONG written;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
s = ainput->buffer;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
if (!Stream_EnsureCapacity(s, 10))
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
|
||||
Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
|
||||
Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
|
||||
|
||||
WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX);
|
||||
if (!WTSVirtualChannelWrite(ainput->ainput_channel, (PCHAR)Stream_Buffer(s),
|
||||
(ULONG)Stream_GetPosition(s), &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
UINT64 flags, time;
|
||||
INT32 x, y;
|
||||
char buffer[128] = { 0 };
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT64(s, time);
|
||||
Stream_Read_UINT64(s, flags);
|
||||
Stream_Read_INT32(s, x);
|
||||
Stream_Read_INT32(s, y);
|
||||
|
||||
WLog_VRB(TAG, "[%s] received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32,
|
||||
__FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
|
||||
IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
|
||||
{
|
||||
BYTE* buffer = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
HANDLE ChannelEvent = NULL;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
return ChannelEvent;
|
||||
}
|
||||
|
||||
static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
|
||||
{
|
||||
DWORD nCount;
|
||||
HANDLE events[2] = { 0 };
|
||||
ainput_server* ainput = (ainput_server*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = ainput->stopEvent;
|
||||
|
||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
||||
{
|
||||
switch (ainput->state)
|
||||
{
|
||||
case AINPUT_OPENED:
|
||||
events[1] = ainput_server_get_channel_handle(ainput);
|
||||
nCount = 2;
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_OBJECT_0:
|
||||
error = ainput_server_context_poll_int(&ainput->context);
|
||||
break;
|
||||
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AINPUT_VERSION_SENT:
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_OBJECT_0:
|
||||
error = ainput_server_context_poll_int(&ainput->context);
|
||||
break;
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = ainput_server_context_poll_int(&ainput->context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
ainput->ainput_channel = NULL;
|
||||
|
||||
if (error && ainput->context.rdpcontext)
|
||||
setChannelError(ainput->context.rdpcontext, error,
|
||||
"ainput_server_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_server_open(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (!ainput->externalThread && (ainput->thread == NULL))
|
||||
{
|
||||
ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!ainput->stopEvent)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL);
|
||||
if (!ainput->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(ainput->stopEvent);
|
||||
ainput->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
ainput->isOpened = TRUE;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_server_close(ainput_server_context* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (!ainput->externalThread && ainput->thread)
|
||||
{
|
||||
SetEvent(ainput->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(ainput->thread);
|
||||
CloseHandle(ainput->stopEvent);
|
||||
ainput->thread = NULL;
|
||||
ainput->stopEvent = NULL;
|
||||
}
|
||||
if (ainput->externalThread)
|
||||
{
|
||||
if (ainput->state != AINPUT_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
ainput->ainput_channel = NULL;
|
||||
ainput->state = AINPUT_INITIAL;
|
||||
}
|
||||
}
|
||||
ainput->isOpened = FALSE;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (ainput->isOpened)
|
||||
{
|
||||
WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
|
||||
"state is not possible!");
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
ainput->externalThread = externalThread;
|
||||
return error;
|
||||
}
|
||||
|
||||
ainput_server_context* ainput_server_context_new(HANDLE vcm)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
|
||||
|
||||
if (!ainput)
|
||||
return NULL;
|
||||
|
||||
ainput->context.vcm = vcm;
|
||||
ainput->context.Open = ainput_server_open;
|
||||
ainput->context.IsOpen = ainput_server_is_open;
|
||||
ainput->context.Close = ainput_server_close;
|
||||
ainput->context.Initialize = ainput_server_initialize;
|
||||
ainput->context.Poll = ainput_server_context_poll;
|
||||
ainput->context.ChannelHandle = ainput_server_context_handle;
|
||||
|
||||
ainput->buffer = Stream_New(NULL, 4096);
|
||||
if (!ainput->buffer)
|
||||
goto fail;
|
||||
return &ainput->context;
|
||||
fail:
|
||||
ainput_server_context_free(ainput);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ainput_server_context_free(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
if (ainput)
|
||||
{
|
||||
ainput_server_close(context);
|
||||
Stream_Free(ainput->buffer, TRUE);
|
||||
}
|
||||
free(ainput);
|
||||
}
|
||||
|
||||
static UINT ainput_process_message(ainput_server* ainput)
|
||||
{
|
||||
BOOL rc;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
ULONG BytesReturned, ActualBytesReturned;
|
||||
UINT16 MessageId;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(ainput->ainput_channel);
|
||||
|
||||
s = ainput->buffer;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned);
|
||||
if (!rc)
|
||||
goto out;
|
||||
|
||||
if (BytesReturned < 2)
|
||||
{
|
||||
error = CHANNEL_RC_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
(ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (BytesReturned != ActualBytesReturned)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32,
|
||||
ActualBytesReturned, BytesReturned);
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_SetLength(s, ActualBytesReturned);
|
||||
Stream_Read_UINT16(s, MessageId);
|
||||
|
||||
switch (MessageId)
|
||||
{
|
||||
case MSG_AINPUT_MOUSE:
|
||||
error = ainput_server_recv_mouse_event(ainput, s);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (error)
|
||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(handle);
|
||||
|
||||
if (!ainput->externalThread)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
|
||||
return FALSE;
|
||||
}
|
||||
if (ainput->state == AINPUT_INITIAL)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
|
||||
return FALSE;
|
||||
}
|
||||
*handle = ainput_server_get_channel_handle(ainput);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT ainput_server_context_poll_int(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
switch (ainput->state)
|
||||
{
|
||||
case AINPUT_INITIAL:
|
||||
error = ainput_server_open_channel(ainput);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
|
||||
else
|
||||
ainput->state = AINPUT_OPENED;
|
||||
break;
|
||||
case AINPUT_OPENED:
|
||||
{
|
||||
BYTE* buffer = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
|
||||
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer,
|
||||
&BytesReturned) != TRUE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*buffer != 0)
|
||||
{
|
||||
error = ainput_server_send_version(ainput);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
|
||||
error);
|
||||
else
|
||||
ainput->state = AINPUT_VERSION_SENT;
|
||||
}
|
||||
else
|
||||
error = CHANNEL_RC_OK;
|
||||
}
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
break;
|
||||
case AINPUT_VERSION_SENT:
|
||||
error = ainput_process_message(ainput);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "AINPUT chanel is in invalid state %d", ainput->state);
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
UINT ainput_server_context_poll(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
if (!ainput->externalThread)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
return ainput_server_context_poll_int(context);
|
||||
}
|
||||
@ -87,34 +87,36 @@ static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChanne
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_set_params(AudinALSADevice* alsa,
|
||||
snd_pcm_t* capture_handle)
|
||||
static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
|
||||
{
|
||||
int error;
|
||||
SSIZE_T s;
|
||||
UINT32 channels = alsa->aformat.nChannels;
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
snd_pcm_format_t format = audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
|
||||
snd_pcm_format_t format =
|
||||
audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
|
||||
|
||||
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));
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "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_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_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(capture_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_prepare(capture_handle);
|
||||
alsa->aformat.nChannels = channels;
|
||||
alsa->bytes_per_frame = snd_pcm_format_size(format, 1) * channels;
|
||||
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;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -123,12 +125,11 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
long error;
|
||||
BYTE* buffer;
|
||||
snd_pcm_t* capture_handle = NULL;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) arg;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)arg;
|
||||
DWORD status;
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "in");
|
||||
|
||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name,
|
||||
SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(error));
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
@ -141,7 +142,8 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = (BYTE*) calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
|
||||
buffer =
|
||||
(BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
@ -181,12 +183,13 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
error = alsa->receive(&alsa->aformat,
|
||||
buffer, error * alsa->bytes_per_frame, alsa->user_data);
|
||||
error =
|
||||
alsa->receive(&alsa->aformat, buffer, error * alsa->bytes_per_frame, alsa->user_data);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_thread_receive failed with error %ld", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_thread_receive failed with error %ld",
|
||||
error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -200,8 +203,7 @@ out:
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "out");
|
||||
|
||||
if (error && alsa->rdpcontext)
|
||||
setChannelError(alsa->rdpcontext, error,
|
||||
"audin_alsa_thread_func reported an error");
|
||||
setChannelError(alsa->rdpcontext, error, "audin_alsa_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
@ -214,7 +216,7 @@ out:
|
||||
*/
|
||||
static UINT audin_alsa_free(IAudinDevice* device)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (alsa)
|
||||
free(alsa->device_name);
|
||||
@ -223,8 +225,7 @@ static UINT audin_alsa_free(IAudinDevice* device)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
const AUDIO_FORMAT* format)
|
||||
static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
if (!device || !format)
|
||||
return FALSE;
|
||||
@ -232,8 +233,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
@ -242,10 +242,6 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
@ -261,7 +257,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (!alsa || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -280,10 +276,9 @@ static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* form
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
|
||||
void* user_data)
|
||||
static UINT 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;
|
||||
@ -297,8 +292,7 @@ static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(alsa->thread = CreateThread(NULL, 0,
|
||||
audin_alsa_thread_func, alsa, 0, NULL)))
|
||||
if (!(alsa->thread = CreateThread(NULL, 0, audin_alsa_thread_func, alsa, 0, NULL)))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "CreateThread failed!");
|
||||
goto error_out;
|
||||
@ -319,7 +313,7 @@ error_out:
|
||||
static UINT audin_alsa_close(IAudinDevice* device)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (!alsa)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -331,7 +325,8 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -346,28 +341,24 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
return error;
|
||||
}
|
||||
|
||||
static 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 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
ADDIN_ARGV* args)
|
||||
static UINT 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 |
|
||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
audin_alsa_args, flags, alsa, NULL, NULL);
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)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);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -379,8 +370,7 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
alsa->device_name = _strdup(arg->Value);
|
||||
|
||||
@ -391,16 +381,15 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry alsa_freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry alsa_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -408,13 +397,12 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
pEntryPoints)
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinALSADevice* alsa;
|
||||
UINT error;
|
||||
alsa = (AudinALSADevice*) calloc(1, sizeof(AudinALSADevice));
|
||||
alsa = (AudinALSADevice*)calloc(1, sizeof(AudinALSADevice));
|
||||
|
||||
if (!alsa)
|
||||
{
|
||||
@ -433,8 +421,8 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
|
||||
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);
|
||||
WLog_Print(alsa->log, WLOG_ERROR,
|
||||
"audin_alsa_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -456,10 +444,10 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
alsa->aformat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
alsa->aformat.nSamplesPerSec = 44100;
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) alsa)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)alsa)))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -39,16 +39,22 @@
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MSG_SNDIN_VERSION 0x01
|
||||
#define MSG_SNDIN_FORMATS 0x02
|
||||
#define MSG_SNDIN_OPEN 0x03
|
||||
#define MSG_SNDIN_OPEN_REPLY 0x04
|
||||
#define MSG_SNDIN_DATA_INCOMING 0x05
|
||||
#define MSG_SNDIN_DATA 0x06
|
||||
#define MSG_SNDIN_FORMATCHANGE 0x07
|
||||
#define SNDIN_VERSION 0x02
|
||||
|
||||
enum
|
||||
{
|
||||
MSG_SNDIN_VERSION = 0x01,
|
||||
MSG_SNDIN_FORMATS = 0x02,
|
||||
MSG_SNDIN_OPEN = 0x03,
|
||||
MSG_SNDIN_OPEN_REPLY = 0x04,
|
||||
MSG_SNDIN_DATA_INCOMING = 0x05,
|
||||
MSG_SNDIN_DATA = 0x06,
|
||||
MSG_SNDIN_FORMATCHANGE = 0x07
|
||||
} MSG_SNDIN_CMD;
|
||||
|
||||
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
|
||||
struct _AUDIN_LISTENER_CALLBACK
|
||||
@ -100,12 +106,17 @@ struct _AUDIN_PLUGIN
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
wLog* log;
|
||||
|
||||
IWTSListener* listener;
|
||||
|
||||
BOOL initialized;
|
||||
UINT32 version;
|
||||
};
|
||||
|
||||
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
||||
|
||||
static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
|
||||
BOOL freeStream)
|
||||
BOOL freeStream)
|
||||
{
|
||||
UINT error;
|
||||
|
||||
@ -116,9 +127,8 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
Stream_SealLength(out);
|
||||
error = callback->channel->Write(callback->channel,
|
||||
Stream_Length(out),
|
||||
Stream_Buffer(out), NULL);
|
||||
error =
|
||||
callback->channel->Write(callback->channel, Stream_Length(out), Stream_Buffer(out), NULL);
|
||||
|
||||
if (freeStream)
|
||||
Stream_Free(out, TRUE);
|
||||
@ -126,7 +136,6 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -135,24 +144,26 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
||||
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
wStream* out;
|
||||
const UINT32 ClientVersion = 0x01;
|
||||
const UINT32 ClientVersion = SNDIN_VERSION;
|
||||
UINT32 ServerVersion;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, ServerVersion);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%"PRIu32", ClientVersion=%"PRIu32, ServerVersion,
|
||||
ClientVersion);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%" PRIu32 ", ClientVersion=%" PRIu32,
|
||||
ServerVersion, ClientVersion);
|
||||
|
||||
/* Do not answer server packet, we do not support the channel version. */
|
||||
if (ServerVersion != ClientVersion)
|
||||
if (ServerVersion > ClientVersion)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_WARN,
|
||||
"Incompatible channel version server=%"PRIu32", client supports version=%"PRIu32, ServerVersion,
|
||||
ClientVersion);
|
||||
"Incompatible channel version server=%" PRIu32
|
||||
", client supports version=%" PRIu32,
|
||||
ServerVersion, ClientVersion);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
audin->version = ServerVersion;
|
||||
|
||||
out = Stream_New(NULL, 5);
|
||||
|
||||
@ -199,11 +210,11 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, NumFormats);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "NumFormats %"PRIu32"", NumFormats);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "NumFormats %" PRIu32 "", NumFormats);
|
||||
|
||||
if ((NumFormats < 1) || (NumFormats > 1000))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "bad NumFormats %"PRIu32"", NumFormats);
|
||||
WLog_Print(audin->log, WLOG_ERROR, "bad NumFormats %" PRIu32 "", NumFormats);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -271,11 +282,11 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
||||
goto out;
|
||||
}
|
||||
|
||||
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
|
||||
cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
|
||||
Stream_SetPosition(out, 0);
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
|
||||
Stream_Write_UINT32(out, callback->formats_count); /* NumFormats (4 bytes) */
|
||||
Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
|
||||
Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
|
||||
Stream_SetPosition(out, cbSizeFormatsPacket);
|
||||
error = audin_channel_write_and_free(callback, out, FALSE);
|
||||
out:
|
||||
@ -296,7 +307,7 @@ out:
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
|
||||
UINT32 NewFormat)
|
||||
UINT32 NewFormat)
|
||||
{
|
||||
wStream* out = Stream_New(NULL, 5);
|
||||
|
||||
@ -337,13 +348,13 @@ static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBAC
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format,
|
||||
const BYTE* data, size_t size, void* user_data)
|
||||
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, const BYTE* data, size_t size,
|
||||
void* user_data)
|
||||
{
|
||||
UINT error;
|
||||
BOOL compatible;
|
||||
AUDIN_PLUGIN* audin;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
|
||||
|
||||
if (!callback)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
@ -382,7 +393,7 @@ static UINT audin_receive_wave_data(const AUDIO_FORMAT* format,
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
audio_format_print(audin->log, WLOG_TRACE, audin->format);
|
||||
WLog_Print(audin->log, WLOG_TRACE, "[%"PRIdz"/%"PRIdz"]", size,
|
||||
WLog_Print(audin->log, WLOG_TRACE, "[%" PRIdz "/%" PRIdz "]", size,
|
||||
Stream_GetPosition(audin->data) - 1);
|
||||
|
||||
if ((error = audin_send_incoming_data_pdu(callback)))
|
||||
@ -411,12 +422,7 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb
|
||||
if (!supported)
|
||||
{
|
||||
/* Default sample rates supported by most backends. */
|
||||
const UINT32 samplerates[] = {
|
||||
96000,
|
||||
48000,
|
||||
44100,
|
||||
22050
|
||||
};
|
||||
const UINT32 samplerates[] = { 96000, 48000, 44100, 22050 };
|
||||
BOOL test = FALSE;
|
||||
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
@ -425,7 +431,7 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb
|
||||
if (!test)
|
||||
{
|
||||
size_t x;
|
||||
for (x=0; x<ARRAYSIZE(samplerates); x++)
|
||||
for (x = 0; x < ARRAYSIZE(samplerates); x++)
|
||||
{
|
||||
format.nSamplesPerSec = samplerates[x];
|
||||
test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
|
||||
@ -437,28 +443,22 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->device->SetFormat, error,
|
||||
audin->device, &format,
|
||||
audin->FramesPerPacket);
|
||||
IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "SetFormat failed with errorcode %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "SetFormat failed with errorcode %" PRIu32 "", error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
|
||||
return FALSE;
|
||||
}
|
||||
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
|
||||
return FALSE;
|
||||
|
||||
IFCALLRET(audin->device->Open, error, audin->device,
|
||||
audin_receive_wave_data, callback);
|
||||
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Open failed with errorcode %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "Open failed with errorcode %" PRIu32 "", error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -480,13 +480,13 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* call
|
||||
|
||||
Stream_Read_UINT32(s, FramesPerPacket);
|
||||
Stream_Read_UINT32(s, initialFormat);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%"PRIu32" initialFormat=%"PRIu32"",
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
|
||||
FramesPerPacket, initialFormat);
|
||||
audin->FramesPerPacket = FramesPerPacket;
|
||||
|
||||
if (initialFormat >= callback->formats_count)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %"PRIu32" (total %d)",
|
||||
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %d)",
|
||||
initialFormat, callback->formats_count);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
@ -523,12 +523,12 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, NewFormat);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "NewFormat=%"PRIu32"", NewFormat);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "NewFormat=%" PRIu32 "", NewFormat);
|
||||
|
||||
if (NewFormat >= callback->formats_count)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %"PRIu32" (total %d)",
|
||||
NewFormat, callback->formats_count);
|
||||
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %d)", NewFormat,
|
||||
callback->formats_count);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -540,7 +540,7 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Close failed with errorcode %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "Close failed with errorcode %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -564,12 +564,12 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
UINT error;
|
||||
BYTE MessageId;
|
||||
AUDIN_PLUGIN* audin;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
if (!callback || !data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
audin = (AUDIN_PLUGIN*)callback->plugin;
|
||||
|
||||
if (!audin)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -578,7 +578,7 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT8(data, MessageId);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "MessageId=0x%02"PRIx8"", MessageId);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "MessageId=0x%02" PRIx8 "", MessageId);
|
||||
|
||||
switch (MessageId)
|
||||
{
|
||||
@ -599,7 +599,7 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(audin->log, WLOG_ERROR, "unknown MessageId=0x%02"PRIx8"", MessageId);
|
||||
WLog_Print(audin->log, WLOG_ERROR, "unknown MessageId=0x%02" PRIx8 "", MessageId);
|
||||
error = ERROR_INVALID_DATA;
|
||||
break;
|
||||
}
|
||||
@ -614,8 +614,8 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
*/
|
||||
static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
|
||||
@ -624,7 +624,7 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
IFCALLRET(audin->device->Close, error, audin->device);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %"PRIu32"", error);
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %" PRIu32 "", error);
|
||||
}
|
||||
|
||||
audin->format = NULL;
|
||||
@ -639,19 +639,19 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data,
|
||||
BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback;
|
||||
AUDIN_PLUGIN* audin;
|
||||
AUDIN_LISTENER_CALLBACK* listener_callback = (AUDIN_LISTENER_CALLBACK*) pListenerCallback;
|
||||
AUDIN_LISTENER_CALLBACK* listener_callback = (AUDIN_LISTENER_CALLBACK*)pListenerCallback;
|
||||
|
||||
if (!listener_callback || !listener_callback->plugin)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
audin = (AUDIN_PLUGIN*) listener_callback->plugin;
|
||||
audin = (AUDIN_PLUGIN*)listener_callback->plugin;
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
callback = (AUDIN_CHANNEL_CALLBACK*) calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
|
||||
callback = (AUDIN_CHANNEL_CALLBACK*)calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
@ -664,7 +664,7 @@ static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
|
||||
callback->plugin = listener_callback->plugin;
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -675,7 +675,8 @@ static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
|
||||
*/
|
||||
static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
UINT rc;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||
|
||||
if (!audin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
@ -683,8 +684,14 @@ static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManag
|
||||
if (!pChannelMgr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (audin->initialized)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*) calloc(1, sizeof(AUDIN_LISTENER_CALLBACK));
|
||||
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*)calloc(1, sizeof(AUDIN_LISTENER_CALLBACK));
|
||||
|
||||
if (!audin->listener_callback)
|
||||
{
|
||||
@ -695,8 +702,11 @@ static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManag
|
||||
audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
|
||||
audin->listener_callback->plugin = pPlugin;
|
||||
audin->listener_callback->channel_mgr = pChannelMgr;
|
||||
return pChannelMgr->CreateListener(pChannelMgr, "AUDIO_INPUT", 0,
|
||||
(IWTSListenerCallback*) audin->listener_callback, NULL);
|
||||
rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
|
||||
&audin->listener_callback->iface, &audin->listener);
|
||||
|
||||
audin->initialized = rc == CHANNEL_RC_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -706,14 +716,21 @@ static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManag
|
||||
*/
|
||||
static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!audin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
audio_format_free(audin->fixed_format);
|
||||
|
||||
if (audin->listener_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
|
||||
if (mgr)
|
||||
IFCALL(mgr->DestroyListener, mgr, audin->listener);
|
||||
}
|
||||
audio_formats_free(audin->fixed_format, 1);
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
@ -721,7 +738,7 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %"PRIu32"", error);
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %" PRIu32 "", error);
|
||||
// dont stop on error
|
||||
}
|
||||
|
||||
@ -739,7 +756,7 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
|
||||
static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!audin)
|
||||
@ -751,7 +768,7 @@ static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
|
||||
|
||||
static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!audin)
|
||||
@ -768,7 +785,7 @@ static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
|
||||
*/
|
||||
static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
@ -791,8 +808,8 @@ static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, char* name, ADDIN_ARGV
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY entry;
|
||||
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
|
||||
UINT error;
|
||||
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", (LPSTR) name, NULL,
|
||||
0);
|
||||
entry = (PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_channel_addin_entry("audin", (LPSTR)name,
|
||||
NULL, 0);
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
@ -802,14 +819,14 @@ static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, char* name, ADDIN_ARGV
|
||||
return ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
|
||||
entryPoints.plugin = (IWTSPlugin*) audin;
|
||||
entryPoints.plugin = (IWTSPlugin*)audin;
|
||||
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
|
||||
entryPoints.args = args;
|
||||
entryPoints.rdpcontext = audin->rdpcontext;
|
||||
|
||||
if ((error = entry(&entryPoints)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %"PRIu32".", name, error);
|
||||
WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %" PRIu32 ".", name, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -855,29 +872,28 @@ static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_args[] =
|
||||
{
|
||||
{ "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
{ "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", NULL, NULL, -1, NULL, "format" },
|
||||
{ "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", NULL, NULL, -1, NULL, "rate" },
|
||||
{ "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", NULL, NULL, -1, NULL, "channel" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
UINT error;
|
||||
COMMAND_LINE_ARGUMENT_A audin_args[] = {
|
||||
{ "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
{ "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", NULL, NULL, -1, NULL, "format" },
|
||||
{ "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", NULL, NULL, -1, NULL, "rate" },
|
||||
{ "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", NULL, NULL, -1, NULL, "channel" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
if (!args || args->argc == 1)
|
||||
return TRUE;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
audin_args, flags, audin, NULL, NULL);
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status =
|
||||
CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin, NULL, NULL);
|
||||
|
||||
if (status != 0)
|
||||
return FALSE;
|
||||
@ -890,12 +906,12 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "sys")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, arg->Value)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_set_subsystem failed with error %"PRIu32"!", error);
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"audin_set_subsystem failed with error %" PRIu32 "!", error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@ -903,7 +919,8 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
{
|
||||
if ((error = audin_set_device_name(audin, arg->Value)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_set_device_name failed with error %"PRIu32"!", error);
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"audin_set_device_name failed with error %" PRIu32 "!", error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@ -936,16 +953,15 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
{
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry audin_DVCPluginEntry
|
||||
#define DVCPluginEntry audin_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -966,34 +982,34 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
struct SubsystemEntry entries[] =
|
||||
{
|
||||
#if defined(WITH_PULSE)
|
||||
{"pulse", ""},
|
||||
{ "pulse", "" },
|
||||
#endif
|
||||
#if defined(WITH_OSS)
|
||||
{"oss", "default"},
|
||||
{ "oss", "default" },
|
||||
#endif
|
||||
#if defined(WITH_ALSA)
|
||||
{"alsa", "default"},
|
||||
{ "alsa", "default" },
|
||||
#endif
|
||||
#if defined(WITH_OPENSLES)
|
||||
{"opensles", "default"},
|
||||
{ "opensles", "default" },
|
||||
#endif
|
||||
#if defined(WITH_WINMM)
|
||||
{"winmm", "default"},
|
||||
{ "winmm", "default" },
|
||||
#endif
|
||||
#if defined(WITH_MACAUDIO)
|
||||
{"mac", "default"},
|
||||
{ "mac", "default" },
|
||||
#endif
|
||||
{NULL, NULL}
|
||||
{ NULL, NULL }
|
||||
};
|
||||
struct SubsystemEntry* entry = &entries[0];
|
||||
assert(pEntryPoints);
|
||||
assert(pEntryPoints->GetPlugin);
|
||||
audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
|
||||
audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "audin");
|
||||
|
||||
if (audin != NULL)
|
||||
return CHANNEL_RC_ALREADY_INITIALIZED;
|
||||
|
||||
audin = (AUDIN_PLUGIN*) calloc(1, sizeof(AUDIN_PLUGIN));
|
||||
audin = (AUDIN_PLUGIN*)calloc(1, sizeof(AUDIN_PLUGIN));
|
||||
|
||||
if (!audin)
|
||||
{
|
||||
@ -1024,8 +1040,8 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
audin->iface.Attached = audin_plugin_attached;
|
||||
audin->iface.Detached = audin_plugin_detached;
|
||||
args = pEntryPoints->GetPluginData(pEntryPoints);
|
||||
audin->rdpcontext = ((freerdp*)((rdpSettings*) pEntryPoints->GetRdpSettings(
|
||||
pEntryPoints))->instance)->context;
|
||||
audin->rdpcontext =
|
||||
((freerdp*)((rdpSettings*)pEntryPoints->GetRdpSettings(pEntryPoints))->instance)->context;
|
||||
|
||||
if (args)
|
||||
{
|
||||
@ -1037,8 +1053,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_load_device_plugin %s failed with error %"PRIu32"!",
|
||||
audin->subsystem, error);
|
||||
WLog_Print(
|
||||
audin->log, WLOG_ERROR,
|
||||
"Unable to load microphone redirection subsystem %s because of error %" PRIu32 "",
|
||||
audin->subsystem, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -1048,17 +1066,20 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, entry->subsystem)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_set_subsystem for %s failed with error %"PRIu32"!",
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"audin_set_subsystem for %s failed with error %" PRIu32 "!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
else if ((error = audin_set_device_name(audin, entry->device)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_set_device_name for %s failed with error %"PRIu32"!",
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"audin_set_device_name for %s failed with error %" PRIu32 "!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_load_device_plugin %s failed with error %"PRIu32"!",
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"audin_load_device_plugin %s failed with error %" PRIu32 "!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
|
||||
@ -1067,13 +1088,19 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
}
|
||||
|
||||
if (audin->device == NULL)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "no sound device.");
|
||||
{
|
||||
/* If we have no audin device do not register plugin but still return OK or the client will
|
||||
* just disconnect due to a missing microphone. */
|
||||
WLog_Print(audin->log, WLOG_ERROR, "No microphone device could be found.");
|
||||
error = CHANNEL_RC_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*)audin);
|
||||
if (error == CHANNEL_RC_OK)
|
||||
return error;
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*) audin);
|
||||
out:
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
audin_plugin_terminated((IWTSPlugin*)audin);
|
||||
|
||||
audin_plugin_terminated((IWTSPlugin*)audin);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -33,4 +33,3 @@
|
||||
#define TAG CHANNELS_TAG("audin.client")
|
||||
|
||||
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H */
|
||||
|
||||
|
||||
@ -18,17 +18,18 @@
|
||||
|
||||
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.c)
|
||||
audin_mac.m)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${CORE_AUDIO} ${AUDIO_TOOL} ${APP_SERVICES} winpr)
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${AVFOUNDATION} ${CORE_AUDIO} ${AUDIO_TOOL} ${APP_SERVICES} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
@ -33,8 +33,14 @@
|
||||
#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
|
||||
#define IUNKNOWN_C_GUTS \
|
||||
void *_reserved; \
|
||||
void *QueryInterface; \
|
||||
void *AddRef; \
|
||||
void *Release
|
||||
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
@ -46,7 +52,7 @@
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
|
||||
#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
|
||||
@ -68,17 +74,18 @@ typedef struct _AudinMacDevice
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
void *user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
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)
|
||||
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT *format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
@ -90,7 +97,7 @@ static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT* format)
|
||||
}
|
||||
}
|
||||
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT* format)
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT *format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
@ -102,10 +109,14 @@ static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT* forma
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_mac_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
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;
|
||||
|
||||
@ -122,10 +133,13 @@ static BOOL audin_mac_format_supported(IAudinDevice* device, const AUDIO_FORMAT*
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_mac_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
static UINT audin_mac_set_format(IAudinDevice *device, const AUDIO_FORMAT *format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
|
||||
if (!mac->isAuthorized)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -133,34 +147,35 @@ static UINT audin_mac_set_format(IAudinDevice* device, const AUDIO_FORMAT* forma
|
||||
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);
|
||||
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.mBytesPerFrame = 0;
|
||||
mac->audioFormat.mBytesPerPacket = 0;
|
||||
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.mFramesPerPacket = 1;
|
||||
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)
|
||||
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;
|
||||
AudinMacDevice *mac = (AudinMacDevice *)aqData;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
const BYTE* buffer = inBuffer->mAudioData;
|
||||
const BYTE *buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
@ -174,17 +189,20 @@ static void mac_audio_queue_input_cb(void* aqData,
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "mac->receive failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "mac->receive failed with error %" PRIu32 "", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static UINT audin_mac_close(IAudinDevice* device)
|
||||
static UINT audin_mac_close(IAudinDevice *device)
|
||||
{
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
|
||||
if (!mac->isAuthorized)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -196,7 +214,7 @@ static UINT audin_mac_close(IAudinDevice* device)
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%"PRIu32"]",
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
@ -210,7 +228,7 @@ static UINT audin_mac_close(IAudinDevice* device)
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%"PRIu32"]",
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
@ -222,22 +240,26 @@ static UINT audin_mac_close(IAudinDevice* device)
|
||||
return errCode;
|
||||
}
|
||||
|
||||
static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive, void *user_data)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
size_t index;
|
||||
|
||||
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));
|
||||
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"]",
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
@ -251,20 +273,17 @@ static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* use
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%"PRIu32"]",
|
||||
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);
|
||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue, mac->audioBuffers[index], 0, NULL);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%"PRIu32"]",
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
@ -275,7 +294,7 @@ static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* use
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%"PRIu32"]",
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
@ -287,9 +306,9 @@ err_out:
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
static UINT audin_mac_free(IAudinDevice* device)
|
||||
static UINT audin_mac_free(IAudinDevice *device)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
int error;
|
||||
|
||||
if (device == NULL)
|
||||
@ -304,28 +323,27 @@ static UINT audin_mac_free(IAudinDevice* device)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static 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 }
|
||||
};
|
||||
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice* device, ADDIN_ARGV* args)
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
char *str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
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);
|
||||
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;
|
||||
@ -337,8 +355,7 @@ static UINT audin_mac_parse_addin_args(AudinMacDevice* device, ADDIN_ARGV* args)
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
|
||||
@ -358,31 +375,30 @@ static UINT audin_mac_parse_addin_args(AudinMacDevice* device, ADDIN_ARGV* args)
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry mac_freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry mac_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
ADDIN_ARGV* args;
|
||||
AudinMacDevice* mac;
|
||||
ADDIN_ARGV *args;
|
||||
AudinMacDevice *mac;
|
||||
UINT error;
|
||||
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
|
||||
mac = (AudinMacDevice *)calloc(1, sizeof(AudinMacDevice));
|
||||
|
||||
if (!mac)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%"PRIu32"]",
|
||||
WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
@ -398,16 +414,51 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
if ((error = audin_mac_parse_addin_args(mac, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)mac)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
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);
|
||||
@ -65,7 +65,7 @@ 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;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)context;
|
||||
|
||||
if (!opensles || !data)
|
||||
{
|
||||
@ -86,34 +86,32 @@ static void audin_receive(void* context, const void* data, size_t size)
|
||||
*/
|
||||
static UINT audin_opensles_free(IAudinDevice* device)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*) device);
|
||||
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)
|
||||
static BOOL audin_opensles_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles || !format)
|
||||
return FALSE;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p", (void*) opensles, (void*) format);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p", (void*)opensles, (void*)format);
|
||||
assert(format);
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM: /* PCM */
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= 2))
|
||||
{
|
||||
@ -123,9 +121,8 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device,
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04X"PRIX16"] not supported",
|
||||
audio_format_get_tag_string(format->wFormatTag),
|
||||
format->wFormatTag);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04X" PRIX16 "] not supported",
|
||||
audio_format_get_tag_string(format->wFormatTag), format->wFormatTag);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -137,16 +134,16 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
const AUDIO_FORMAT* format, UINT32 FramesPerPacket)
|
||||
static UINT audin_opensles_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
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);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%" PRIu32 "",
|
||||
(void*)device, (void*)format, FramesPerPacket);
|
||||
assert(format);
|
||||
|
||||
opensles->format = *format;
|
||||
@ -177,13 +174,13 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "Encoding '%"PRIu16"' [%04"PRIX16"] not supported",
|
||||
format->wFormatTag,
|
||||
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,
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "frames_per_packet=%" PRIu32,
|
||||
opensles->frames_per_packet);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -193,27 +190,22 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
||||
void* user_data)
|
||||
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
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);
|
||||
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)))
|
||||
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;
|
||||
@ -223,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
||||
opensles->user_data = user_data;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
audin_opensles_close(opensles);
|
||||
audin_opensles_close(device);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -234,12 +226,12 @@ error_out:
|
||||
*/
|
||||
UINT audin_opensles_close(IAudinDevice* device)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*) device);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
|
||||
android_CloseRecDevice(opensles->stream);
|
||||
opensles->receive = NULL;
|
||||
opensles->user_data = NULL;
|
||||
@ -247,31 +239,28 @@ UINT audin_opensles_close(IAudinDevice* device)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static 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 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
ADDIN_ARGV* args)
|
||||
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
UINT status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
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);
|
||||
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;
|
||||
@ -283,8 +272,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
opensles->device_name = _strdup(arg->Value);
|
||||
|
||||
@ -295,18 +283,15 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry \
|
||||
opensles_freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry opensles_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry \
|
||||
FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -314,13 +299,12 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinOpenSLESDevice* opensles;
|
||||
UINT error;
|
||||
opensles = (AudinOpenSLESDevice*) calloc(1, sizeof(AudinOpenSLESDevice));
|
||||
opensles = (AudinOpenSLESDevice*)calloc(1, sizeof(AudinOpenSLESDevice));
|
||||
|
||||
if (!opensles)
|
||||
{
|
||||
@ -340,13 +324,14 @@ UINT freerdp_audin_client_subsystem_entry(
|
||||
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);
|
||||
"audin_opensles_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) opensles)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)opensles)))
|
||||
{
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -6,14 +6,14 @@ 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.
|
||||
* 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
|
||||
@ -32,7 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "audin_main.h"
|
||||
#include "opensl_io.h"
|
||||
#define CONV16BIT 32768
|
||||
#define CONVMYFLT (1./32768.)
|
||||
#define CONVMYFLT (1. / 32768.)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -66,7 +66,6 @@ struct opensl_stream
|
||||
opensl_receive_t receive;
|
||||
};
|
||||
|
||||
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
|
||||
// creates the OpenSL ES audio engine
|
||||
@ -76,22 +75,24 @@ static SLresult openSLCreateEngine(OPENSL_STREAM* p)
|
||||
// create engine
|
||||
result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS) goto engine_end;
|
||||
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;
|
||||
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));
|
||||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
|
||||
|
||||
if (result != SL_RESULT_SUCCESS) goto engine_end;
|
||||
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));
|
||||
result =
|
||||
(*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME, &(p->deviceVolume));
|
||||
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
@ -169,10 +170,9 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
}
|
||||
|
||||
// configure audio source
|
||||
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL
|
||||
};
|
||||
SLDataSource audioSrc = {&loc_dev, NULL};
|
||||
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
|
||||
SLDataSource audioSrc = { &loc_dev, NULL };
|
||||
// configure audio sink
|
||||
int speakers;
|
||||
|
||||
@ -181,7 +181,8 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
else
|
||||
speakers = SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
2 };
|
||||
SLDataFormat_PCM format_pcm;
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = channels;
|
||||
@ -202,41 +203,46 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
else
|
||||
assert(0);
|
||||
|
||||
SLDataSink audioSnk = {&loc_bq, &format_pcm};
|
||||
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);
|
||||
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);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// realize the audio recorder
|
||||
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// get the record interface
|
||||
result = (*p->recorderObject)->GetInterface(p->recorderObject,
|
||||
SL_IID_RECORD, &(p->recorderRecord));
|
||||
result = (*p->recorderObject)
|
||||
->GetInterface(p->recorderObject, SL_IID_RECORD, &(p->recorderRecord));
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// get the buffer queue interface
|
||||
result = (*p->recorderObject)->GetInterface(p->recorderObject,
|
||||
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&(p->recorderBufferQueue));
|
||||
result = (*p->recorderObject)
|
||||
->GetInterface(p->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&(p->recorderBufferQueue));
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// register callback on the buffer queue
|
||||
result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue,
|
||||
bqRecorderCallback, p);
|
||||
result = (*p->recorderBufferQueue)
|
||||
->RegisterCallback(p->recorderBufferQueue, bqRecorderCallback, p);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
@ -245,7 +251,8 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
end_recopen:
|
||||
return result;
|
||||
}
|
||||
else return SL_RESULT_SUCCESS;
|
||||
else
|
||||
return SL_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// close the OpenSL IO and destroy the audio engine
|
||||
@ -299,17 +306,15 @@ static void opensles_queue_element_free(void* obj)
|
||||
}
|
||||
|
||||
// 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* 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));
|
||||
p = (OPENSL_STREAM*)calloc(1, sizeof(OPENSL_STREAM));
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
@ -337,12 +342,9 @@ OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
|
||||
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);
|
||||
(*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);
|
||||
@ -364,7 +366,7 @@ void android_CloseRecDevice(OPENSL_STREAM* 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;
|
||||
OPENSL_STREAM* p = (OPENSL_STREAM*)context;
|
||||
queue_element* e;
|
||||
|
||||
if (!p)
|
||||
@ -382,7 +384,5 @@ static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
||||
|
||||
p->next = p->prep;
|
||||
p->prep = e;
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
||||
e->data, e->size);
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, e->data, e->size);
|
||||
}
|
||||
|
||||
|
||||
@ -6,14 +6,14 @@ 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.
|
||||
* 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
|
||||
@ -38,25 +38,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct opensl_stream OPENSL_STREAM;
|
||||
typedef struct opensl_stream OPENSL_STREAM;
|
||||
|
||||
typedef void (*opensl_receive_t)(void* context, const void* data, size_t size);
|
||||
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);
|
||||
/*
|
||||
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
|
||||
};
|
||||
|
||||
@ -69,11 +69,10 @@ typedef struct _AudinOSSDevice
|
||||
} AudinOSSDevice;
|
||||
|
||||
#define OSS_LOG_ERR(_text, _error) \
|
||||
if (_error != 0) \
|
||||
if (_error != 0) \
|
||||
WLog_ERR(TAG, "%s: %i - %s\n", _text, _error, strerror(_error));
|
||||
|
||||
|
||||
static int audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||
static UINT32 audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
@ -99,8 +98,7 @@ static int audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL audin_oss_format_supported(IAudinDevice* device,
|
||||
const AUDIO_FORMAT* format)
|
||||
static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
if (device == NULL || format == NULL)
|
||||
return FALSE;
|
||||
@ -108,18 +106,13 @@ static BOOL audin_oss_format_supported(IAudinDevice* device,
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize != 0 ||
|
||||
format->nSamplesPerSec > 48000 ||
|
||||
if (format->cbSize != 0 || format->nSamplesPerSec > 48000 ||
|
||||
(format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
|
||||
(format->nChannels != 1 && format->nChannels != 2))
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
@ -151,7 +144,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
char mixer_name[PATH_MAX] = "/dev/mixer";
|
||||
int pcm_handle = -1, mixer_handle;
|
||||
BYTE* buffer = NULL;
|
||||
int tmp;
|
||||
unsigned long tmp;
|
||||
size_t buffer_size;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
|
||||
UINT error = 0;
|
||||
@ -233,8 +226,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
|
||||
|
||||
buffer_size = (oss->FramesPerPacket * oss->format.nChannels *
|
||||
(oss->format.wBitsPerSample / 8));
|
||||
buffer_size = (oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8));
|
||||
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
|
||||
|
||||
if (NULL == buffer)
|
||||
@ -246,33 +238,34 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
|
||||
while (1)
|
||||
{
|
||||
SSIZE_T stmp;
|
||||
status = WaitForSingleObject(oss->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
tmp = read(pcm_handle, buffer, buffer_size);
|
||||
stmp = read(pcm_handle, buffer, buffer_size);
|
||||
|
||||
/* Error happen. */
|
||||
if (tmp < 0)
|
||||
if (stmp < 0)
|
||||
{
|
||||
OSS_LOG_ERR("read() error", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmp < buffer_size) /* Not enouth data. */
|
||||
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);
|
||||
WLog_ERR(TAG, "oss->receive failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -280,8 +273,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
err_out:
|
||||
|
||||
if (error && oss && oss->rdpcontext)
|
||||
setChannelError(oss->rdpcontext, error,
|
||||
"audin_oss_thread_func reported an error");
|
||||
setChannelError(oss->rdpcontext, error, "audin_oss_thread_func reported an error");
|
||||
|
||||
if (pcm_handle != -1)
|
||||
{
|
||||
@ -299,8 +291,7 @@ err_out:
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive,
|
||||
void* user_data)
|
||||
static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
oss->receive = receive;
|
||||
@ -343,7 +334,7 @@ static UINT audin_oss_close(IAudinDevice* device)
|
||||
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -366,7 +357,7 @@ static UINT audin_oss_close(IAudinDevice* device)
|
||||
static UINT audin_oss_free(IAudinDevice* device)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
int error;
|
||||
UINT error;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -380,12 +371,6 @@ static UINT audin_oss_free(IAudinDevice* device)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static 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 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -394,14 +379,18 @@ static COMMAND_LINE_ARGUMENT_A audin_oss_args[] =
|
||||
static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
char *str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
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);
|
||||
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;
|
||||
@ -414,8 +403,7 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
|
||||
@ -434,7 +422,7 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
}
|
||||
|
||||
oss->dev_unit = val;
|
||||
oss->dev_unit = (INT32)val;
|
||||
}
|
||||
|
||||
if (oss->dev_unit < 0 || *eptr != '\0')
|
||||
@ -443,16 +431,15 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry oss_freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry oss_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -460,8 +447,7 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
pEntryPoints)
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinOSSDevice* oss;
|
||||
@ -485,14 +471,13 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
|
||||
if ((error = audin_oss_parse_addin_args(oss, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) oss)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -62,27 +62,67 @@ typedef struct _AudinPulseDevice
|
||||
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)
|
||||
{
|
||||
pa_context_state_t state;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||
state = pa_context_get_state(context);
|
||||
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "context state %s", pulse_context_state_string(state));
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "PA_CONTEXT_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -95,7 +135,7 @@ static void audin_pulse_context_state_callback(pa_context* context, void* userda
|
||||
static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
{
|
||||
pa_context_state_t state;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse->context)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -126,8 +166,8 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "bad context state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
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;
|
||||
}
|
||||
@ -136,7 +176,7 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -147,7 +187,7 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
*/
|
||||
static UINT audin_pulse_free(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -176,7 +216,7 @@ static UINT audin_pulse_free(IAudinDevice* device)
|
||||
|
||||
static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse || !format)
|
||||
return FALSE;
|
||||
@ -187,8 +227,7 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||
{
|
||||
@ -197,18 +236,6 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW: /* A-LAW */
|
||||
case WAVE_FORMAT_MULAW: /* U-LAW */
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
@ -225,7 +252,7 @@ static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
pa_sample_spec sample_spec = { 0 };
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -278,24 +305,22 @@ static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||
state = pa_stream_get_state(stream);
|
||||
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "stream state %s", pulse_stream_state_string(state));
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "PA_STREAM_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -303,17 +328,17 @@ static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||
{
|
||||
const void* data;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||
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);
|
||||
error =
|
||||
IFCALLRESULT(CHANNEL_RC_OK, pulse->receive, &pulse->format, data, length, pulse->user_data);
|
||||
pa_stream_drop(stream);
|
||||
|
||||
if (error && pulse->rdpcontext)
|
||||
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -321,7 +346,7 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length
|
||||
*/
|
||||
static UINT audin_pulse_close(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -349,7 +374,7 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
pa_buffer_attr buffer_attr = { 0 };
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -363,8 +388,7 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
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);
|
||||
pulse->stream = pa_stream_new(pulse->context, "freerdp_audin", &pulse->sample_spec, NULL);
|
||||
|
||||
if (!pulse->stream)
|
||||
{
|
||||
@ -375,24 +399,21 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
}
|
||||
|
||||
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 */
|
||||
buffer_attr.fragsize = pulse->bytes_per_frame * pulse->frames_per_packet;
|
||||
|
||||
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
|
||||
buffer_attr.fragsize += pulse->format.nBlockAlign - 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)
|
||||
if (pa_stream_connect_record(pulse->stream, pulse->device_name, &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)",
|
||||
@ -410,8 +431,8 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
audin_pulse_close(device);
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "bad stream state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
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);
|
||||
}
|
||||
@ -425,12 +446,6 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static 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 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -441,10 +456,15 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
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);
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)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 } };
|
||||
|
||||
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);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -456,8 +476,7 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
pulse->device_name = _strdup(arg->Value);
|
||||
|
||||
@ -468,16 +487,15 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry pulse_freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry pulse_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -490,7 +508,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
ADDIN_ARGV* args;
|
||||
AudinPulseDevice* pulse;
|
||||
UINT error;
|
||||
pulse = (AudinPulseDevice*) calloc(1, sizeof(AudinPulseDevice));
|
||||
pulse = (AudinPulseDevice*)calloc(1, sizeof(AudinPulseDevice));
|
||||
|
||||
if (!pulse)
|
||||
{
|
||||
@ -509,8 +527,8 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
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);
|
||||
WLog_Print(pulse->log, WLOG_ERROR,
|
||||
"audin_pulse_parse_addin_args failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -534,21 +552,21 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
|
||||
|
||||
if ((error = audin_pulse_connect((IAudinDevice*) pulse)))
|
||||
if ((error = audin_pulse_connect(&pulse->iface)))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_connect failed");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &pulse->iface)))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
audin_pulse_free((IAudinDevice*)pulse);
|
||||
audin_pulse_free(&pulse->iface);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ typedef struct _AudinWinmmDevice
|
||||
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)dwInstance;
|
||||
PWAVEHDR pWaveHdr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
MMRESULT mmResult;
|
||||
@ -74,8 +74,8 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
||||
|
||||
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
|
||||
{
|
||||
if (pWaveHdr->dwBytesRecorded
|
||||
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
if (pWaveHdr->dwBytesRecorded &&
|
||||
!(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
{
|
||||
AUDIO_FORMAT format;
|
||||
format.cbSize = winmm->pwfx_cur->cbSize;
|
||||
@ -86,8 +86,8 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
||||
format.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
|
||||
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
|
||||
|
||||
if ((error = winmm->receive(&format, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded,
|
||||
winmm->user_data)))
|
||||
if ((error = winmm->receive(&format, pWaveHdr->lpData,
|
||||
pWaveHdr->dwBytesRecorded, winmm->user_data)))
|
||||
break;
|
||||
|
||||
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
||||
@ -110,34 +110,84 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
||||
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;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
|
||||
char* buffer;
|
||||
int size, i;
|
||||
WAVEHDR waveHdr[4];
|
||||
WAVEHDR waveHdr[4] = { 0 };
|
||||
DWORD status;
|
||||
MMRESULT rc;
|
||||
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
|
||||
{
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
|
||||
MMRESULT rc;
|
||||
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;
|
||||
size =
|
||||
(winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
|
||||
7) /
|
||||
8;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
buffer = (char*) malloc(size);
|
||||
buffer = (char*)malloc(size);
|
||||
|
||||
if (!buffer)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
@ -147,36 +197,22 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
waveHdr[i].lpData = buffer;
|
||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInPrepareHeader failed. %"PRIu32"", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInAddBuffer", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInAddBuffer failed. %"PRIu32"", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
}
|
||||
|
||||
rc = waveInStart(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInStart", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInStart failed. %"PRIu32"", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
@ -192,26 +228,17 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
|
||||
rc = waveInReset(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInReset", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInReset failed. %"PRIu32"", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInUnprepareHeader failed. %"PRIu32"", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
free(waveHdr[i].lpData);
|
||||
@ -219,13 +246,8 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
|
||||
rc = waveInClose(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInClose", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInClose failed. %"PRIu32"", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
winmm->hWaveIn = NULL;
|
||||
@ -240,7 +262,7 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
static UINT audin_winmm_free(IAudinDevice* device)
|
||||
{
|
||||
UINT32 i;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -265,7 +287,7 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
||||
{
|
||||
DWORD status;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -276,7 +298,8 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -298,7 +321,7 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
UINT32 i;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -307,27 +330,46 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
|
||||
for (i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag
|
||||
&& winmm->ppwfx[i]->nChannels == format->nChannels
|
||||
&& winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
|
||||
const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
|
||||
if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
|
||||
(ppwfx->wBitsPerSample == format->wBitsPerSample) &&
|
||||
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
|
||||
{
|
||||
winmm->pwfx_cur = winmm->ppwfx[i];
|
||||
break;
|
||||
/* 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 CHANNEL_RC_OK;
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
PWAVEFORMATEX pwfx;
|
||||
BYTE* data;
|
||||
|
||||
if (!winmm || !format)
|
||||
return FALSE;
|
||||
|
||||
if (format->wFormatTag != WAVE_FORMAT_PCM)
|
||||
return FALSE;
|
||||
|
||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||
|
||||
if (!pwfx)
|
||||
@ -342,29 +384,27 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
||||
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
||||
memcpy(data, format->data, format->cbSize);
|
||||
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
|
||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||
|
||||
if (!test_format_supported(pwfx))
|
||||
goto fail;
|
||||
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||
PWAVEFORMATEX* tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
|
||||
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
|
||||
{
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
PWAVEFORMATEX* tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
if (!tmp_ppwfx)
|
||||
goto fail;
|
||||
|
||||
if (!tmp_ppwfx)
|
||||
return FALSE;
|
||||
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
return TRUE;
|
||||
}
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
free(pwfx);
|
||||
return FALSE;
|
||||
}
|
||||
@ -376,7 +416,7 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
||||
*/
|
||||
static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -390,8 +430,7 @@ static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(winmm->thread = CreateThread(NULL, 0,
|
||||
audin_winmm_thread_func, winmm, 0, NULL)))
|
||||
if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
|
||||
CloseHandle(winmm->stopEvent);
|
||||
@ -402,12 +441,6 @@ static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static 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 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -418,10 +451,15 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
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);
|
||||
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
|
||||
@ -429,8 +467,7 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
winmm->device_name = _strdup(arg->Value);
|
||||
|
||||
@ -441,16 +478,15 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -463,7 +499,14 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
ADDIN_ARGV* args;
|
||||
AudinWinmmDevice* winmm;
|
||||
UINT error;
|
||||
winmm = (AudinWinmmDevice*) calloc(1, sizeof(AudinWinmmDevice));
|
||||
|
||||
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)
|
||||
{
|
||||
@ -482,8 +525,8 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
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);
|
||||
WLog_Print(winmm->log, WLOG_ERROR,
|
||||
"audin_winmm_parse_addin_args failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -500,7 +543,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
}
|
||||
|
||||
winmm->ppwfx_size = 10;
|
||||
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
|
||||
|
||||
if (!winmm->ppwfx)
|
||||
{
|
||||
@ -509,9 +552,10 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -35,17 +35,18 @@
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/codec/audio.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#include <freerdp/channels/audin.h>
|
||||
#include <freerdp/server/audin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("audin.server")
|
||||
#define MSG_SNDIN_VERSION 0x01
|
||||
#define MSG_SNDIN_FORMATS 0x02
|
||||
#define MSG_SNDIN_OPEN 0x03
|
||||
#define MSG_SNDIN_OPEN_REPLY 0x04
|
||||
#define MSG_SNDIN_DATA_INCOMING 0x05
|
||||
#define MSG_SNDIN_DATA 0x06
|
||||
#define MSG_SNDIN_FORMATCHANGE 0x07
|
||||
#define MSG_SNDIN_VERSION 0x01
|
||||
#define MSG_SNDIN_FORMATS 0x02
|
||||
#define MSG_SNDIN_OPEN 0x03
|
||||
#define MSG_SNDIN_OPEN_REPLY 0x04
|
||||
#define MSG_SNDIN_DATA_INCOMING 0x05
|
||||
#define MSG_SNDIN_DATA 0x06
|
||||
#define MSG_SNDIN_FORMATCHANGE 0x07
|
||||
|
||||
typedef struct _audin_server
|
||||
{
|
||||
@ -69,15 +70,13 @@ typedef struct _audin_server
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_select_format(audin_server_context* context,
|
||||
size_t client_format_index)
|
||||
static UINT audin_server_select_format(audin_server_context* context, size_t client_format_index)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
if (client_format_index >= context->num_client_formats)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"error in protocol: client_format_index >= context->num_client_formats!");
|
||||
WLog_ERR(TAG, "error in protocol: client_format_index >= context->num_client_formats!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -109,7 +108,7 @@ static UINT audin_server_send_version(audin_server* audin, wStream* s)
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_VERSION);
|
||||
Stream_Write_UINT32(s, 1); /* Version (4 bytes) */
|
||||
|
||||
if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
@ -124,14 +123,13 @@ static UINT audin_server_send_version(audin_server* audin, wStream* s)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_version(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
static UINT audin_server_recv_version(audin_server* audin, wStream* s, UINT32 length)
|
||||
{
|
||||
UINT32 Version;
|
||||
|
||||
if (length < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %"PRIu32"",
|
||||
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %" PRIu32 "",
|
||||
length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
@ -140,7 +138,7 @@ static UINT audin_server_recv_version(audin_server* audin, wStream* s,
|
||||
|
||||
if (Version < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "expected Version > 0 but got %"PRIu32"", Version);
|
||||
WLog_ERR(TAG, "expected Version > 0 but got %" PRIu32 "", Version);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -158,16 +156,12 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
ULONG written;
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_FORMATS);
|
||||
Stream_Write_UINT32(s,
|
||||
audin->context.num_server_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Write_UINT32(s,
|
||||
0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */
|
||||
Stream_Write_UINT32(s, audin->context.num_server_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Write_UINT32(s, 0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */
|
||||
|
||||
for (i = 0; i < audin->context.num_server_formats; i++)
|
||||
{
|
||||
AUDIO_FORMAT format = audin->context.server_formats[i];
|
||||
// TODO: Eliminate this
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
||||
|
||||
if (!audio_format_write(s, &format))
|
||||
{
|
||||
@ -176,8 +170,10 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
}
|
||||
}
|
||||
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written)
|
||||
? CHANNEL_RC_OK
|
||||
: ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,22 +181,20 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
static UINT audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 length)
|
||||
{
|
||||
size_t i;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
|
||||
if (length < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing rec formats: expected at least 8 bytes, got %"PRIu32"",
|
||||
WLog_ERR(TAG, "error parsing rec formats: expected at least 8 bytes, got %" PRIu32 "",
|
||||
length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s,
|
||||
audin->context.num_client_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */
|
||||
Stream_Read_UINT32(s, audin->context.num_client_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */
|
||||
length -= 8;
|
||||
|
||||
if (audin->context.num_client_formats <= 0)
|
||||
@ -223,7 +217,7 @@ static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
||||
{
|
||||
audio_formats_free(audin->context.client_formats, i);
|
||||
audin->context.client_formats = NULL;
|
||||
WLog_ERR(TAG, "expected length at least 18, but got %"PRIu32"", length);
|
||||
WLog_ERR(TAG, "expected length at least 18, but got %" PRIu32 "", length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -233,7 +227,7 @@ static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
||||
IFCALLRET(audin->context.Opening, success, &audin->context);
|
||||
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.Opening failed with error %"PRIu32"", success);
|
||||
WLog_ERR(TAG, "context.Opening failed with error %" PRIu32 "", success);
|
||||
|
||||
return success;
|
||||
}
|
||||
@ -257,24 +251,24 @@ static UINT audin_server_send_open(audin_server* audin, wStream* s)
|
||||
audin->opened = TRUE;
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_OPEN);
|
||||
Stream_Write_UINT32(s,
|
||||
audin->context.frames_per_packet); /* FramesPerPacket (4 bytes) */
|
||||
Stream_Write_UINT32(s,
|
||||
audin->context.selected_client_format); /* initialFormat (4 bytes) */
|
||||
Stream_Write_UINT32(s, audin->context.frames_per_packet); /* FramesPerPacket (4 bytes) */
|
||||
Stream_Write_UINT32(s, audin->context.selected_client_format); /* initialFormat (4 bytes) */
|
||||
/*
|
||||
* [MS-RDPEAI] 3.2.5.1.6
|
||||
* The second format specify the format that SHOULD be used to capture data from
|
||||
* the actual audio input device.
|
||||
*/
|
||||
Stream_Write_UINT16(s, 1); /* wFormatTag = PCM */
|
||||
Stream_Write_UINT16(s, 2); /* nChannels */
|
||||
Stream_Write_UINT32(s, 44100); /* nSamplesPerSec */
|
||||
Stream_Write_UINT16(s, 1); /* wFormatTag = PCM */
|
||||
Stream_Write_UINT16(s, 2); /* nChannels */
|
||||
Stream_Write_UINT32(s, 44100); /* nSamplesPerSec */
|
||||
Stream_Write_UINT32(s, 44100 * 2 * 2); /* nAvgBytesPerSec */
|
||||
Stream_Write_UINT16(s, 4); /* nBlockAlign */
|
||||
Stream_Write_UINT16(s, 16); /* wBitsPerSample */
|
||||
Stream_Write_UINT16(s, 0); /* cbSize */
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
Stream_Write_UINT16(s, 4); /* nBlockAlign */
|
||||
Stream_Write_UINT16(s, 16); /* wBitsPerSample */
|
||||
Stream_Write_UINT16(s, 0); /* cbSize */
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written)
|
||||
? CHANNEL_RC_OK
|
||||
: ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -282,15 +276,14 @@ static UINT audin_server_send_open(audin_server* audin, wStream* s)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s, UINT32 length)
|
||||
{
|
||||
UINT32 Result;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
|
||||
if (length < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %"PRIu32"",
|
||||
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %" PRIu32 "",
|
||||
length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
@ -299,7 +292,7 @@ static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s,
|
||||
IFCALLRET(audin->context.OpenResult, success, &audin->context, Result);
|
||||
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.OpenResult failed with error %"PRIu32"", success);
|
||||
WLog_ERR(TAG, "context.OpenResult failed with error %" PRIu32 "", success);
|
||||
|
||||
return success;
|
||||
}
|
||||
@ -309,8 +302,7 @@ static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_data(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
static UINT audin_server_recv_data(audin_server* audin, wStream* s, UINT32 length)
|
||||
{
|
||||
AUDIO_FORMAT* format;
|
||||
int sbytes_per_sample;
|
||||
@ -346,7 +338,7 @@ static UINT audin_server_recv_data(audin_server* audin, wStream* s,
|
||||
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, &dformat, out, frames);
|
||||
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", success);
|
||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %" PRIu32 "", success);
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "freerdp_dsp_decode failed!");
|
||||
@ -365,7 +357,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
BOOL ready = FALSE;
|
||||
HANDLE ChannelEvent;
|
||||
DWORD BytesReturned = 0;
|
||||
audin_server* audin = (audin_server*) arg;
|
||||
audin_server* audin = (audin_server*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
buffer = NULL;
|
||||
@ -395,26 +387,25 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE,
|
||||
100)) == WAIT_OBJECT_0)
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE, 100)) == WAIT_OBJECT_0)
|
||||
goto out;
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady,
|
||||
&buffer, &BytesReturned) == FALSE)
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer,
|
||||
&BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ready = *((BOOL*) buffer);
|
||||
ready = *((BOOL*)buffer);
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (ready)
|
||||
@ -434,21 +425,20 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
{
|
||||
if ((error = audin_server_send_version(audin, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_send_version failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
while (ready)
|
||||
{
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE,
|
||||
INFINITE)) == WAIT_OBJECT_0)
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -467,7 +457,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
break;
|
||||
|
||||
if (WTSVirtualChannelRead(audin->audin_channel, 0, (PCHAR) Stream_Buffer(s),
|
||||
if (WTSVirtualChannelRead(audin->audin_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
@ -483,13 +473,15 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
case MSG_SNDIN_VERSION:
|
||||
if ((error = audin_server_recv_version(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_version failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_recv_version failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
if ((error = audin_server_send_formats(audin, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_send_formats failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_send_formats failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
@ -498,13 +490,14 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
case MSG_SNDIN_FORMATS:
|
||||
if ((error = audin_server_recv_formats(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_formats failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_recv_formats failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
if ((error = audin_server_send_open(audin, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_send_open failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_send_open failed with error %" PRIu32 "!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
@ -513,7 +506,8 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
case MSG_SNDIN_OPEN_REPLY:
|
||||
if ((error = audin_server_recv_open_reply(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_open_reply failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_recv_open_reply failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
@ -525,7 +519,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
case MSG_SNDIN_DATA:
|
||||
if ((error = audin_server_recv_data(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_data failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "audin_server_recv_data failed with error %" PRIu32 "!", error);
|
||||
goto out_capacity;
|
||||
};
|
||||
|
||||
@ -535,7 +529,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %"PRIu8"", MessageId);
|
||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -556,23 +550,25 @@ out:
|
||||
|
||||
static BOOL audin_server_open(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
if (!audin->thread)
|
||||
{
|
||||
PULONG pSessionId = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
audin->SessionId = WTS_CURRENT_SESSION;
|
||||
UINT32 channelId;
|
||||
BOOL status = TRUE;
|
||||
|
||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION,
|
||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned))
|
||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned))
|
||||
{
|
||||
audin->SessionId = (DWORD) * pSessionId;
|
||||
audin->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
}
|
||||
|
||||
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId,
|
||||
"AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId, AUDIN_DVC_CHANNEL_NAME,
|
||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (!audin->audin_channel)
|
||||
{
|
||||
@ -580,13 +576,23 @@ static BOOL audin_server_open(audin_server_context* context)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(audin->audin_channel);
|
||||
|
||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(audin->thread = CreateThread(NULL, 0, audin_server_thread_func, (void*) audin, 0, NULL)))
|
||||
if (!(audin->thread =
|
||||
CreateThread(NULL, 0, audin_server_thread_func, (void*)audin, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(audin->stopEvent);
|
||||
@ -603,7 +609,7 @@ static BOOL audin_server_open(audin_server_context* context)
|
||||
|
||||
static BOOL audin_server_is_open(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
if (!audin)
|
||||
return FALSE;
|
||||
@ -613,7 +619,7 @@ static BOOL audin_server_is_open(audin_server_context* context)
|
||||
|
||||
static BOOL audin_server_close(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
if (audin->thread)
|
||||
{
|
||||
@ -621,7 +627,7 @@ static BOOL audin_server_close(audin_server_context* context)
|
||||
|
||||
if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", GetLastError());
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -668,12 +674,12 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (audin_server_context*) audin;
|
||||
return (audin_server_context*)audin;
|
||||
}
|
||||
|
||||
void audin_server_context_free(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
if (!audin)
|
||||
return;
|
||||
|
||||
@ -33,22 +33,28 @@ set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}typedef UINT (*static_addin
|
||||
|
||||
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
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})
|
||||
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})
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${STATIC_MODULE_NAME})
|
||||
|
||||
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${${STATIC_MODULE}_CLIENT_ENTRY}")
|
||||
if(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
|
||||
elseif(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS,PVOID);")
|
||||
else()
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(void);")
|
||||
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 ${ENTRY_POINT_NAME}(IDRDYNVC_ENTRY_POINTS* pEntryPoints);")
|
||||
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints);")
|
||||
else()
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(void);")
|
||||
endif()
|
||||
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
|
||||
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", (static_entry_fkt)${ENTRY_POINT_NAME} },")
|
||||
endif()
|
||||
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
|
||||
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", (static_entry_fkt)${ENTRY_POINT_NAME} },")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
@ -91,15 +97,17 @@ foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_subsystem_entry")
|
||||
endif()
|
||||
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
|
||||
set(SUBSYSTEM_IMPORT "extern void ${STATIC_SUBSYSTEM_ENTRY}(void);")
|
||||
set(SUBSYSTEM_IMPORT "extern UINT ${STATIC_SUBSYSTEM_ENTRY}(void*);")
|
||||
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}\", (static_addin_fkt)${ENTRY_POINT_NAME}, ${SUBSYSTEM_TABLE_NAME} },")
|
||||
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
|
||||
set (ENTRY_POINT_NAME ${STATIC_MODULE_CHANNEL}_${ENTRY})
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", \"${ENTRY}\", (static_addin_fkt)${ENTRY_POINT_NAME}, ${SUBSYSTEM_TABLE_NAME} },")
|
||||
endforeach()
|
||||
endforeach()
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ NULL, NULL, NULL }\n};")
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ NULL, NULL, NULL, NULL }\n};")
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
|
||||
|
||||
|
||||
@ -43,21 +43,21 @@
|
||||
|
||||
extern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];
|
||||
|
||||
void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
|
||||
const char* identifier)
|
||||
static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
|
||||
const char* identifier)
|
||||
{
|
||||
int index = 0;
|
||||
size_t index = 0;
|
||||
STATIC_ENTRY* pEntry;
|
||||
pEntry = (STATIC_ENTRY*) &table->table[index++];
|
||||
pEntry = (STATIC_ENTRY*)&table->table[index++];
|
||||
|
||||
while (pEntry->entry != NULL)
|
||||
{
|
||||
if (strcmp(pEntry->name, identifier) == 0)
|
||||
{
|
||||
return (void*) pEntry->entry;
|
||||
return (void*)pEntry->entry;
|
||||
}
|
||||
|
||||
pEntry = (STATIC_ENTRY*) &table->table[index++];
|
||||
pEntry = (STATIC_ENTRY*)&table->table[index++];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -65,9 +65,9 @@ void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* tabl
|
||||
|
||||
void* freerdp_channels_client_find_static_entry(const char* name, const char* identifier)
|
||||
{
|
||||
int index = 0;
|
||||
size_t index = 0;
|
||||
STATIC_ENTRY_TABLE* pEntry;
|
||||
pEntry = (STATIC_ENTRY_TABLE*) &CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
pEntry = (STATIC_ENTRY_TABLE*)&CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
|
||||
while (pEntry->table != NULL)
|
||||
{
|
||||
@ -76,7 +76,7 @@ void* freerdp_channels_client_find_static_entry(const char* name, const char* id
|
||||
return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
|
||||
}
|
||||
|
||||
pEntry = (STATIC_ENTRY_TABLE*) &CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
pEntry = (STATIC_ENTRY_TABLE*)&CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -84,15 +84,16 @@ void* freerdp_channels_client_find_static_entry(const char* name, const char* id
|
||||
|
||||
extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
|
||||
|
||||
FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR pszSubsystem,
|
||||
LPSTR pszType, DWORD dwFlags)
|
||||
static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName,
|
||||
LPCSTR pszSubsystem,
|
||||
LPCSTR pszType, DWORD dwFlags)
|
||||
{
|
||||
size_t i, j;
|
||||
DWORD nAddins;
|
||||
FREERDP_ADDIN** ppAddins = NULL;
|
||||
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
||||
nAddins = 0;
|
||||
ppAddins = (FREERDP_ADDIN**) calloc(128, sizeof(FREERDP_ADDIN*));
|
||||
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
|
||||
|
||||
if (!ppAddins)
|
||||
{
|
||||
@ -104,7 +105,7 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
||||
|
||||
for (i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
|
||||
{
|
||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||
|
||||
if (!pAddin)
|
||||
{
|
||||
@ -117,11 +118,11 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
subsystems = (STATIC_SUBSYSTEM_ENTRY*) CLIENT_STATIC_ADDIN_TABLE[i].table;
|
||||
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*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||
|
||||
if (!pAddin)
|
||||
{
|
||||
@ -129,7 +130,8 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s",
|
||||
CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s", subsystems[j].name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
@ -145,8 +147,8 @@ error_out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSubsystem,
|
||||
LPSTR pszType, DWORD dwFlags)
|
||||
static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
|
||||
LPCSTR pszType, DWORD dwFlags)
|
||||
{
|
||||
int index;
|
||||
int nDashes;
|
||||
@ -163,11 +165,11 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
size_t cchInstallPrefix;
|
||||
FREERDP_ADDIN** ppAddins;
|
||||
WIN32_FIND_DATAA FindData;
|
||||
cchAddinPath = strlen(pszAddinPath);
|
||||
cchInstallPrefix = strlen(pszInstallPrefix);
|
||||
cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
|
||||
cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
|
||||
pszExtension = PathGetSharedLibraryExtensionA(0);
|
||||
cchPattern = 128 + strlen(pszExtension) + 2;
|
||||
pszPattern = (LPSTR) malloc(cchPattern + 1);
|
||||
cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
|
||||
pszPattern = (LPSTR)malloc(cchPattern + 1);
|
||||
|
||||
if (!pszPattern)
|
||||
{
|
||||
@ -177,28 +179,28 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
|
||||
if (pszName && pszSubsystem && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client-%s-%s.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
|
||||
pszName, pszSubsystem, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client-?-%s.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
|
||||
pszName, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client*.%s",
|
||||
pszName, pszExtension);
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s", pszName,
|
||||
pszExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"?-client*.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
|
||||
pszExtension);
|
||||
}
|
||||
|
||||
cchPattern = strlen(pszPattern);
|
||||
cchPattern = strnlen(pszPattern, cchPattern);
|
||||
cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
|
||||
pszSearchPath = (LPSTR) malloc(cchSearchPath + 1);
|
||||
pszSearchPath = (LPSTR)malloc(cchSearchPath + 1);
|
||||
|
||||
if (!pszSearchPath)
|
||||
{
|
||||
@ -215,7 +217,7 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
hFind = FindFirstFileA(pszSearchPath, &FindData);
|
||||
free(pszSearchPath);
|
||||
nAddins = 0;
|
||||
ppAddins = (FREERDP_ADDIN**) calloc(128, sizeof(FREERDP_ADDIN*));
|
||||
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
|
||||
|
||||
if (!ppAddins)
|
||||
{
|
||||
@ -229,10 +231,8 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
|
||||
do
|
||||
{
|
||||
char* p[5];
|
||||
FREERDP_ADDIN* pAddin;
|
||||
nDashes = 0;
|
||||
pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
BOOL used = FALSE;
|
||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||
|
||||
if (!pAddin)
|
||||
{
|
||||
@ -240,59 +240,117 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
nDashes = 0;
|
||||
for (index = 0; FindData.cFileName[index]; index++)
|
||||
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
|
||||
|
||||
if (nDashes == 1)
|
||||
{
|
||||
size_t len;
|
||||
char* p[2] = { 0 };
|
||||
/* <name>-client.<extension> */
|
||||
p[0] = FindData.cFileName;
|
||||
p[1] = strchr(p[0], '-') + 1;
|
||||
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
||||
|
||||
len = p[1] - p[0];
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
|
||||
used = TRUE;
|
||||
}
|
||||
else if (nDashes == 2)
|
||||
{
|
||||
size_t len;
|
||||
char* p[4] = { 0 };
|
||||
/* <name>-client-<subsystem>.<extension> */
|
||||
p[0] = FindData.cFileName;
|
||||
p[1] = strchr(p[0], '-') + 1;
|
||||
p[2] = strchr(p[1], '-') + 1;
|
||||
p[3] = strchr(p[2], '.') + 1;
|
||||
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
||||
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
|
||||
|
||||
len = p[1] - p[0];
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||
|
||||
len = p[3] - p[2];
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
||||
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
|
||||
used = TRUE;
|
||||
}
|
||||
else if (nDashes == 3)
|
||||
{
|
||||
size_t len;
|
||||
char* p[5] = { 0 };
|
||||
/* <name>-client-<subsystem>-<type>.<extension> */
|
||||
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;
|
||||
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);
|
||||
|
||||
len = p[1] - p[0];
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||
|
||||
len = p[3] - p[2];
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
||||
|
||||
len = p[4] - p[3];
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
|
||||
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
|
||||
used = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
skip:
|
||||
if (!used)
|
||||
free(pAddin);
|
||||
}
|
||||
}
|
||||
while (FindNextFileA(hFind, &FindData));
|
||||
|
||||
} while (FindNextFileA(hFind, &FindData));
|
||||
|
||||
FindClose(hFind);
|
||||
ppAddins[nAddins] = NULL;
|
||||
@ -303,8 +361,8 @@ error_out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FREERDP_ADDIN** freerdp_channels_list_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType,
|
||||
DWORD dwFlags)
|
||||
FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
if (dwFlags & FREERDP_ADDIN_STATIC)
|
||||
return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
|
||||
@ -316,7 +374,7 @@ FREERDP_ADDIN** freerdp_channels_list_addins(LPSTR pszName, LPSTR pszSubsystem,
|
||||
|
||||
void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
|
||||
{
|
||||
int index;
|
||||
size_t index;
|
||||
|
||||
if (!ppAddins)
|
||||
return;
|
||||
@ -329,48 +387,68 @@ void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
|
||||
|
||||
extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
|
||||
|
||||
BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
|
||||
static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
|
||||
{
|
||||
int i;
|
||||
STATIC_ENTRY* entry;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
|
||||
{
|
||||
entry = (STATIC_ENTRY*) &CLIENT_VirtualChannelEntryEx_TABLE[i];
|
||||
const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
|
||||
|
||||
if (!strcmp(entry->name, pszName))
|
||||
if (!strncmp(entry->name, pszName, MAX_PATH))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPSTR pszSubsystem,
|
||||
LPSTR pszType, DWORD dwFlags)
|
||||
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
|
||||
LPCSTR pszType, DWORD dwFlags)
|
||||
{
|
||||
int i, j;
|
||||
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
||||
const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
|
||||
const char* type = NULL;
|
||||
|
||||
for (i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
|
||||
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 (strcmp(CLIENT_STATIC_ADDIN_TABLE[i].name, pszName) == 0)
|
||||
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 (type && strncmp(table->type, type, MAX_PATH))
|
||||
continue;
|
||||
|
||||
if (pszSubsystem != NULL)
|
||||
{
|
||||
subsystems = (STATIC_SUBSYSTEM_ENTRY*) CLIENT_STATIC_ADDIN_TABLE[i].table;
|
||||
const STATIC_SUBSYSTEM_ENTRY* subsystems = table->table;
|
||||
|
||||
for (j = 0; subsystems[j].name != NULL; j++)
|
||||
for (; subsystems->name != NULL; subsystems++)
|
||||
{
|
||||
if (strcmp(subsystems[j].name, pszSubsystem) == 0)
|
||||
/* 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 (pszType)
|
||||
{
|
||||
if (strcmp(subsystems[j].type, pszType) == 0)
|
||||
return (PVIRTUALCHANNELENTRY) subsystems[j].entry;
|
||||
if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
|
||||
return (PVIRTUALCHANNELENTRY)subsystems->entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PVIRTUALCHANNELENTRY) subsystems[j].entry;
|
||||
return (PVIRTUALCHANNELENTRY)subsystems->entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -383,7 +461,7 @@ PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LP
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (PVIRTUALCHANNELENTRY) CLIENT_STATIC_ADDIN_TABLE[i].entry;
|
||||
return (PVIRTUALCHANNELENTRY)table->entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,5 +16,3 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include "tables.h"
|
||||
|
||||
${CLIENT_STATIC_TYPEDEFS}
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
struct _STATIC_ENTRY
|
||||
{
|
||||
const char* name;
|
||||
UINT(*entry)();
|
||||
UINT (*entry)();
|
||||
};
|
||||
typedef struct _STATIC_ENTRY STATIC_ENTRY;
|
||||
|
||||
@ -37,14 +37,15 @@ struct _STATIC_SUBSYSTEM_ENTRY
|
||||
{
|
||||
const char* name;
|
||||
const char* type;
|
||||
void (*entry)(void);
|
||||
UINT (*entry)();
|
||||
};
|
||||
typedef struct _STATIC_SUBSYSTEM_ENTRY STATIC_SUBSYSTEM_ENTRY;
|
||||
|
||||
struct _STATIC_ADDIN_TABLE
|
||||
{
|
||||
const char* name;
|
||||
UINT(*entry)();
|
||||
const char* type;
|
||||
UINT (*entry)();
|
||||
const STATIC_SUBSYSTEM_ENTRY* table;
|
||||
};
|
||||
typedef struct _STATIC_ADDIN_TABLE STATIC_ADDIN_TABLE;
|
||||
|
||||
@ -21,7 +21,10 @@ set(${MODULE_PREFIX}_SRCS
|
||||
cliprdr_format.c
|
||||
cliprdr_format.h
|
||||
cliprdr_main.c
|
||||
cliprdr_main.h)
|
||||
cliprdr_main.h
|
||||
../cliprdr_common.h
|
||||
../cliprdr_common.c
|
||||
)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||
|
||||
|
||||
@ -33,22 +33,17 @@
|
||||
|
||||
#include "cliprdr_main.h"
|
||||
#include "cliprdr_format.h"
|
||||
#include "../cliprdr_common.h"
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
UINT32 index;
|
||||
size_t position;
|
||||
BOOL asciiNames;
|
||||
int formatNameLength;
|
||||
char* szFormatName;
|
||||
WCHAR* wszFormatName;
|
||||
CLIPRDR_FORMAT* formats = NULL;
|
||||
CLIPRDR_FORMAT_LIST formatList;
|
||||
CLIPRDR_FORMAT_LIST formatList = { 0 };
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
@ -58,185 +53,24 @@ UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
asciiNames = (msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
|
||||
|
||||
formatList.msgType = CB_FORMAT_LIST;
|
||||
formatList.msgFlags = msgFlags;
|
||||
formatList.dataLen = dataLen;
|
||||
|
||||
index = 0;
|
||||
formatList.numFormats = 0;
|
||||
position = Stream_GetPosition(s);
|
||||
if ((error = cliprdr_read_format_list(s, &formatList, cliprdr->useLongFormatNames)))
|
||||
goto error_out;
|
||||
|
||||
if (!formatList.dataLen)
|
||||
{
|
||||
/* empty format list */
|
||||
formatList.formats = NULL;
|
||||
formatList.numFormats = 0;
|
||||
}
|
||||
else if (!cliprdr->useLongFormatNames)
|
||||
{
|
||||
formatList.numFormats = (dataLen / 36);
|
||||
|
||||
if ((formatList.numFormats * 36) != dataLen)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid short format list length: %"PRIu32"", dataLen);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
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 (dataLen)
|
||||
{
|
||||
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
|
||||
dataLen -= 4;
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
|
||||
/* 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 !
|
||||
*/
|
||||
|
||||
if (asciiNames)
|
||||
{
|
||||
szFormatName = (char*) Stream_Pointer(s);
|
||||
|
||||
if (szFormatName[0])
|
||||
{
|
||||
/* ensure null termination */
|
||||
formats[index].formatName = (char*) malloc(32 + 1);
|
||||
if (!formats[index].formatName)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
CopyMemory(formats[index].formatName, szFormatName, 32);
|
||||
formats[index].formatName[32] = '\0';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
||||
|
||||
if (wszFormatName[0])
|
||||
{
|
||||
/* ConvertFromUnicode always returns a null-terminated
|
||||
* string on success, even if the source string isn't.
|
||||
*/
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, 16,
|
||||
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to convert short clipboard format name");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Seek(s, 32);
|
||||
dataLen -= 32;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (dataLen)
|
||||
{
|
||||
Stream_Seek(s, 4); /* formatId (4 bytes) */
|
||||
dataLen -= 4;
|
||||
|
||||
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
||||
|
||||
if (!wszFormatName[0])
|
||||
formatNameLength = 0;
|
||||
else
|
||||
formatNameLength = _wcslen(wszFormatName);
|
||||
|
||||
Stream_Seek(s, (formatNameLength + 1) * 2);
|
||||
dataLen -= ((formatNameLength + 1) * 2);
|
||||
|
||||
formatList.numFormats++;
|
||||
}
|
||||
|
||||
dataLen = formatList.dataLen;
|
||||
Stream_SetPosition(s, position);
|
||||
|
||||
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 (dataLen)
|
||||
{
|
||||
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
|
||||
dataLen -= 4;
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
|
||||
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
||||
|
||||
if (!wszFormatName[0])
|
||||
formatNameLength = 0;
|
||||
else
|
||||
formatNameLength = _wcslen(wszFormatName);
|
||||
|
||||
if (formatNameLength)
|
||||
{
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, -1,
|
||||
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to convert long clipboard format name");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Seek(s, (formatNameLength + 1) * 2);
|
||||
dataLen -= ((formatNameLength + 1) * 2);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %"PRIu32"",
|
||||
formatList.numFormats);
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "",
|
||||
formatList.numFormats);
|
||||
|
||||
if (context->ServerFormatList)
|
||||
{
|
||||
if ((error = context->ServerFormatList(context, &formatList)))
|
||||
WLog_ERR(TAG, "ServerFormatList failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "ServerFormatList failed with error %" PRIu32 "", error);
|
||||
}
|
||||
|
||||
error_out:
|
||||
if (formats)
|
||||
{
|
||||
for (index = 0; index < formatList.numFormats; index++)
|
||||
{
|
||||
free(formats[index].formatName);
|
||||
}
|
||||
|
||||
free(formats);
|
||||
}
|
||||
cliprdr_free_format_list(&formatList);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -245,9 +79,10 @@ error_out:
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
|
||||
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = { 0 };
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
@ -265,7 +100,7 @@ UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
||||
|
||||
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFormatListResponse failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "ServerFormatListResponse failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -275,7 +110,8 @@ UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
@ -293,12 +129,13 @@ UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN
|
||||
formatDataRequest.msgFlags = msgFlags;
|
||||
formatDataRequest.dataLen = dataLen;
|
||||
|
||||
Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */
|
||||
|
||||
if ((error = cliprdr_read_format_data_request(s, &formatDataRequest)))
|
||||
return error;
|
||||
|
||||
context->lastRequestedFormatId = formatDataRequest.requestedFormatId;
|
||||
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -308,7 +145,8 @@ UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
@ -325,192 +163,13 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
||||
formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
|
||||
formatDataResponse.msgFlags = msgFlags;
|
||||
formatDataResponse.dataLen = dataLen;
|
||||
formatDataResponse.requestedFormatData = NULL;
|
||||
|
||||
if (dataLen)
|
||||
formatDataResponse.requestedFormatData = (BYTE*) Stream_Pointer(s);
|
||||
if ((error = cliprdr_read_format_data_response(s, &formatDataResponse)))
|
||||
return error;
|
||||
|
||||
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT64 filetime_to_uint64(FILETIME value)
|
||||
{
|
||||
UINT64 converted = 0;
|
||||
converted |= (UINT32) value.dwHighDateTime;
|
||||
converted <<= 32;
|
||||
converted |= (UINT32) value.dwLowDateTime;
|
||||
return converted;
|
||||
}
|
||||
|
||||
static FILETIME uint64_to_filetime(UINT64 value)
|
||||
{
|
||||
FILETIME converted;
|
||||
converted.dwLowDateTime = (UINT32) (value >> 0);
|
||||
converted.dwHighDateTime = (UINT32) (value >> 32);
|
||||
return converted;
|
||||
}
|
||||
|
||||
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
|
||||
|
||||
/**
|
||||
* Parse a packed file list.
|
||||
*
|
||||
* The resulting array must be freed with the `free()` function.
|
||||
*
|
||||
* @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
|
||||
* @param [in] format_data_length length of `format_data` in bytes.
|
||||
* @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
|
||||
* @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
|
||||
*
|
||||
* @returns 0 on success, otherwise a Win32 error code.
|
||||
*/
|
||||
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
|
||||
FILEDESCRIPTOR** file_descriptor_array, UINT32* file_descriptor_count)
|
||||
{
|
||||
UINT result = NO_ERROR;
|
||||
UINT32 i;
|
||||
UINT32 count = 0;
|
||||
wStream* s = NULL;
|
||||
|
||||
if (!format_data || !file_descriptor_array || !file_descriptor_count)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
s = Stream_New((BYTE*) format_data, format_data_length);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "invalid packed file list");
|
||||
|
||||
result = ERROR_INCORRECT_SIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
|
||||
|
||||
if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
|
||||
{
|
||||
WLog_ERR(TAG, "packed file list is too short: expected %"PRIuz", have %"PRIuz,
|
||||
((size_t) count) * CLIPRDR_FILEDESCRIPTOR_SIZE,
|
||||
Stream_GetRemainingLength(s));
|
||||
|
||||
result = ERROR_INCORRECT_SIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*file_descriptor_count = count;
|
||||
*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTOR));
|
||||
if (!*file_descriptor_array)
|
||||
{
|
||||
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
int c;
|
||||
UINT64 lastWriteTime;
|
||||
FILEDESCRIPTOR* file = &((*file_descriptor_array)[i]);
|
||||
|
||||
Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
|
||||
Stream_Seek(s, 32); /* reserved1 (32 bytes) */
|
||||
Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
|
||||
Stream_Seek(s, 16); /* reserved2 (16 bytes) */
|
||||
Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
|
||||
file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
|
||||
Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
|
||||
Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
|
||||
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
|
||||
Stream_Read_UINT16(s, file->cFileName[c]);
|
||||
}
|
||||
|
||||
if (Stream_GetRemainingLength(s) > 0)
|
||||
WLog_WARN(TAG, "packed file list has %"PRIuz" excess bytes",
|
||||
Stream_GetRemainingLength(s));
|
||||
out:
|
||||
Stream_Free(s, FALSE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
|
||||
|
||||
/**
|
||||
* Serialize a packed file list.
|
||||
*
|
||||
* The resulting format data must be freed with the `free()` function.
|
||||
*
|
||||
* @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
|
||||
* @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
|
||||
* @param [out] format_data serialized CLIPRDR_FILELIST.
|
||||
* @param [out] format_data_length length of `format_data` in bytes.
|
||||
*
|
||||
* @returns 0 on success, otherwise a Win32 error code.
|
||||
*/
|
||||
UINT cliprdr_serialize_file_list(const FILEDESCRIPTOR* file_descriptor_array,
|
||||
UINT32 file_descriptor_count, BYTE** format_data, UINT32* format_data_length)
|
||||
{
|
||||
UINT result = NO_ERROR;
|
||||
UINT32 i;
|
||||
wStream* s = NULL;
|
||||
|
||||
if (!file_descriptor_array || !format_data || !format_data_length)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
|
||||
|
||||
for (i = 0; i < file_descriptor_count; i++)
|
||||
{
|
||||
int c;
|
||||
UINT64 lastWriteTime;
|
||||
const FILEDESCRIPTOR* file = &file_descriptor_array[i];
|
||||
|
||||
/*
|
||||
* There is a known issue with Windows server getting stuck in
|
||||
* an infinite loop when downloading files that are larger than
|
||||
* 2 gigabytes. Do not allow clients to send such file lists.
|
||||
*
|
||||
* https://support.microsoft.com/en-us/help/2258090
|
||||
*/
|
||||
if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
|
||||
result = ERROR_FILE_TOO_LARGE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
|
||||
Stream_Zero(s, 32); /* reserved1 (32 bytes) */
|
||||
Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
|
||||
Stream_Zero(s, 16); /* reserved2 (16 bytes) */
|
||||
lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
|
||||
Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
|
||||
Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
|
||||
Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
|
||||
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
|
||||
Stream_Write_UINT16(s, file->cFileName[c]);
|
||||
}
|
||||
|
||||
Stream_SealLength(s);
|
||||
|
||||
Stream_GetBuffer(s, *format_data);
|
||||
Stream_GetLength(s, *format_data_length);
|
||||
|
||||
Stream_Free(s, FALSE);
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
Stream_Free(s, TRUE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -23,9 +23,13 @@
|
||||
#ifndef FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H
|
||||
#define FREERDP_CHANNEL_CLIPRDR_CLIENT_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);
|
||||
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);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -49,6 +49,7 @@ struct cliprdr_plugin
|
||||
BOOL streamFileClipEnabled;
|
||||
BOOL fileClipNoFilePaths;
|
||||
BOOL canLockClipData;
|
||||
BOOL hasHugeFileSupport;
|
||||
};
|
||||
typedef struct cliprdr_plugin cliprdrPlugin;
|
||||
|
||||
@ -57,7 +58,10 @@ CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
#define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_CLIPRDR(...) do { } while (0)
|
||||
#define DEBUG_CLIPRDR(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */
|
||||
|
||||
588
channels/cliprdr/cliprdr_common.c
Normal file
588
channels/cliprdr/cliprdr_common.c
Normal file
@ -0,0 +1,588 @@
|
||||
/**
|
||||
* 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 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, "[%s]: cbRequested must be %" PRIu32 ", got %" PRIu32 "", __FUNCTION__,
|
||||
sizeof(UINT64), request->cbRequested);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (request->nPositionHigh != 0 || request->nPositionLow != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s]: nPositionHigh and nPositionLow must be set to 0", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen)
|
||||
{
|
||||
wStream* s;
|
||||
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_Seek(s, 4);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (!response)
|
||||
return NULL;
|
||||
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, response->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)
|
||||
{
|
||||
wStream* s;
|
||||
UINT32 index;
|
||||
int cchWideChar;
|
||||
LPWSTR lpWideCharStr;
|
||||
int formatNameSize;
|
||||
char* szFormatName;
|
||||
WCHAR* wszFormatName;
|
||||
BOOL asciiNames = FALSE;
|
||||
CLIPRDR_FORMAT* format;
|
||||
UINT32 length;
|
||||
|
||||
if (formatList->msgType != CB_FORMAT_LIST)
|
||||
WLog_WARN(TAG, "[%s] called with invalid type %08" PRIx32, __FUNCTION__,
|
||||
formatList->msgType);
|
||||
|
||||
if (!useLongFormatNames)
|
||||
{
|
||||
length = formatList->numFormats * 36;
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
size_t formatNameLength = 0;
|
||||
format = (CLIPRDR_FORMAT*)&(formatList->formats[index]);
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
formatNameSize = 0;
|
||||
|
||||
szFormatName = format->formatName;
|
||||
|
||||
if (asciiNames)
|
||||
{
|
||||
if (szFormatName)
|
||||
formatNameLength = strnlen(szFormatName, 32);
|
||||
|
||||
if (formatNameLength > 31)
|
||||
formatNameLength = 31;
|
||||
|
||||
Stream_Write(s, szFormatName, formatNameLength);
|
||||
Stream_Zero(s, 32 - formatNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
wszFormatName = NULL;
|
||||
|
||||
if (szFormatName)
|
||||
formatNameSize =
|
||||
ConvertToUnicode(CP_UTF8, 0, szFormatName, -1, &wszFormatName, 0);
|
||||
|
||||
if (formatNameSize < 0)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (formatNameSize > 15)
|
||||
formatNameSize = 15;
|
||||
|
||||
/* size in bytes instead of wchar */
|
||||
formatNameSize *= 2;
|
||||
|
||||
if (wszFormatName)
|
||||
Stream_Write(s, wszFormatName, (size_t)formatNameSize);
|
||||
|
||||
Stream_Zero(s, (size_t)(32 - formatNameSize));
|
||||
free(wszFormatName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0;
|
||||
for (index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
format = (CLIPRDR_FORMAT*)&(formatList->formats[index]);
|
||||
length += 4;
|
||||
formatNameSize = 2;
|
||||
|
||||
if (format->formatName)
|
||||
formatNameSize =
|
||||
MultiByteToWideChar(CP_UTF8, 0, format->formatName, -1, NULL, 0) * 2;
|
||||
|
||||
if (formatNameSize < 0)
|
||||
return NULL;
|
||||
|
||||
length += (UINT32)formatNameSize;
|
||||
}
|
||||
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
format = (CLIPRDR_FORMAT*)&(formatList->formats[index]);
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
if (format->formatName)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(s);
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
const size_t rem = cap - pos;
|
||||
if ((cap < pos) || ((rem / 2) > INT_MAX))
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lpWideCharStr = (LPWSTR)Stream_Pointer(s);
|
||||
cchWideChar = (int)(rem / 2);
|
||||
formatNameSize = MultiByteToWideChar(CP_UTF8, 0, format->formatName, -1,
|
||||
lpWideCharStr, cchWideChar) *
|
||||
2;
|
||||
if (formatNameSize < 0)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
Stream_Seek(s, (size_t)formatNameSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_Write_UINT16(s, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
UINT cliprdr_read_unlock_clipdata(wStream* s, CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough remaining data");
|
||||
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_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data in stream!");
|
||||
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_GetRemainingLength(s) < response->dataLen)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data in stream!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (response->dataLen)
|
||||
response->requestedFormatData = Stream_Pointer(s);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_file_contents_request(wStream* s, CLIPRDR_FILE_CONTENTS_REQUEST* request)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 24)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough remaining data");
|
||||
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_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough remaining data");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, response->streamId); /* streamId (4 bytes) */
|
||||
response->requestedData = Stream_Pointer(s); /* requestedFileContentsData */
|
||||
response->cbRequested = response->dataLen - 4;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_format_list(wStream* s, CLIPRDR_FORMAT_LIST* formatList, BOOL useLongFormatNames)
|
||||
{
|
||||
UINT32 index;
|
||||
BOOL asciiNames;
|
||||
int formatNameLength;
|
||||
char* szFormatName;
|
||||
WCHAR* wszFormatName;
|
||||
wStream sub1, sub2;
|
||||
CLIPRDR_FORMAT* formats = NULL;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
asciiNames = (formatList->msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
|
||||
|
||||
index = 0;
|
||||
/* empty format list */
|
||||
formatList->formats = NULL;
|
||||
formatList->numFormats = 0;
|
||||
|
||||
Stream_StaticInit(&sub1, Stream_Pointer(s), formatList->dataLen);
|
||||
if (!Stream_SafeSeek(s, formatList->dataLen))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!formatList->dataLen)
|
||||
{
|
||||
}
|
||||
else if (!useLongFormatNames)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(&sub1);
|
||||
formatList->numFormats = (cap / 36);
|
||||
|
||||
if ((formatList->numFormats * 36) != cap)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid short format list length: %" PRIuz "", cap);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Stream_Read_UINT32(&sub1, formats[index].formatId); /* formatId (4 bytes) */
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
|
||||
/* 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 = (char*)Stream_Pointer(&sub1);
|
||||
wszFormatName = (WCHAR*)Stream_Pointer(&sub1);
|
||||
if (!Stream_SafeSeek(&sub1, 32))
|
||||
goto error_out;
|
||||
if (asciiNames)
|
||||
{
|
||||
if (szFormatName[0])
|
||||
{
|
||||
/* ensure null termination */
|
||||
formats[index].formatName = (char*)malloc(32 + 1);
|
||||
if (!formats[index].formatName)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
CopyMemory(formats[index].formatName, szFormatName, 32);
|
||||
formats[index].formatName[32] = '\0';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wszFormatName[0])
|
||||
{
|
||||
/* ConvertFromUnicode always returns a null-terminated
|
||||
* string on success, even if the source string isn't.
|
||||
*/
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, 16,
|
||||
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to convert short clipboard format name");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sub2 = sub1;
|
||||
while (Stream_GetRemainingLength(&sub1) > 0)
|
||||
{
|
||||
size_t rest;
|
||||
if (!Stream_SafeSeek(&sub1, 4)) /* formatId (4 bytes) */
|
||||
goto error_out;
|
||||
|
||||
wszFormatName = (WCHAR*)Stream_Pointer(&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;
|
||||
Stream_Read_UINT32(&sub2, formats[index].formatId); /* formatId (4 bytes) */
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
|
||||
wszFormatName = (WCHAR*)Stream_Pointer(&sub2);
|
||||
rest = Stream_GetRemainingLength(&sub2);
|
||||
formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
|
||||
if (!Stream_SafeSeek(&sub2, (formatNameLength + 1) * sizeof(WCHAR)))
|
||||
goto error_out;
|
||||
|
||||
if (formatNameLength)
|
||||
{
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, formatNameLength,
|
||||
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to convert long clipboard format name");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
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)
|
||||
{
|
||||
UINT index = 0;
|
||||
|
||||
if (formatList == NULL)
|
||||
return;
|
||||
|
||||
if (formatList->formats)
|
||||
{
|
||||
for (index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
free(formatList->formats[index].formatName);
|
||||
}
|
||||
|
||||
free(formatList->formats);
|
||||
formatList->formats = NULL;
|
||||
formatList->numFormats = 0;
|
||||
}
|
||||
}
|
||||
61
channels/cliprdr/cliprdr_common.h
Normal file
61
channels/cliprdr/cliprdr_common.h
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
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 */
|
||||
@ -19,7 +19,10 @@ define_channel_server("cliprdr")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
cliprdr_main.c
|
||||
cliprdr_main.h)
|
||||
cliprdr_main.h
|
||||
../cliprdr_common.h
|
||||
../cliprdr_common.c
|
||||
)
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@
|
||||
|
||||
#define TAG CHANNELS_TAG("cliprdr.server")
|
||||
|
||||
#define CLIPRDR_HEADER_LENGTH 8
|
||||
#define CLIPRDR_HEADER_LENGTH 8
|
||||
|
||||
struct _cliprdr_server_private
|
||||
{
|
||||
|
||||
@ -20,3 +20,7 @@ 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()
|
||||
|
||||
@ -19,7 +19,9 @@ define_channel_client("disp")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
disp_main.c
|
||||
disp_main.h)
|
||||
disp_main.h
|
||||
../disp_common.c
|
||||
../disp_common.h)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
|
||||
182
channels/disp/client/disp_main.c
Executable file → Normal file
182
channels/disp/client/disp_main.c
Executable file → Normal file
@ -40,6 +40,7 @@
|
||||
#include <freerdp/addin.h>
|
||||
|
||||
#include "disp_main.h"
|
||||
#include "../disp_common.h"
|
||||
|
||||
struct _DISP_CHANNEL_CALLBACK
|
||||
{
|
||||
@ -71,6 +72,7 @@ struct _DISP_PLUGIN
|
||||
UINT32 MaxNumMonitors;
|
||||
UINT32 MaxMonitorAreaFactorA;
|
||||
UINT32 MaxMonitorAreaFactorB;
|
||||
BOOL initialized;
|
||||
};
|
||||
typedef struct _DISP_PLUGIN DISP_PLUGIN;
|
||||
|
||||
@ -79,41 +81,42 @@ typedef struct _DISP_PLUGIN DISP_PLUGIN;
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
static UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback,
|
||||
UINT32 NumMonitors,
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
UINT status;
|
||||
wStream* s;
|
||||
UINT32 type;
|
||||
UINT32 index;
|
||||
UINT32 length;
|
||||
DISP_PLUGIN* disp;
|
||||
UINT32 MonitorLayoutSize;
|
||||
DISPLAY_CONTROL_HEADER header;
|
||||
disp = (DISP_PLUGIN*)callback->plugin;
|
||||
MonitorLayoutSize = DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE;
|
||||
header.length = 8 + 8 + (NumMonitors * MonitorLayoutSize);
|
||||
header.type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
|
||||
|
||||
disp = (DISP_PLUGIN*) callback->plugin;
|
||||
s = Stream_New(NULL, header.length);
|
||||
|
||||
MonitorLayoutSize = 40;
|
||||
|
||||
length = 8 + 8 + (NumMonitors * MonitorLayoutSize);
|
||||
|
||||
type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
|
||||
|
||||
s = Stream_New(NULL, length);
|
||||
if(!s)
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, type); /* Type (4 bytes) */
|
||||
Stream_Write_UINT32(s, length); /* Length (4 bytes) */
|
||||
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, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", NumMonitors);
|
||||
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
|
||||
WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%" PRIu32 "",
|
||||
NumMonitors);
|
||||
|
||||
for (index = 0; index < NumMonitors; index++)
|
||||
{
|
||||
@ -134,30 +137,34 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac
|
||||
if (Monitors[index].Height > 8192)
|
||||
Monitors[index].Height = 8192;
|
||||
|
||||
Stream_Write_UINT32(s, Monitors[index].Flags); /* Flags (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Left); /* Left (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Top); /* Top (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Width); /* Width (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Height); /* Height (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].PhysicalWidth); /* PhysicalWidth (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Flags); /* Flags (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Left); /* Left (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Top); /* Top (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Width); /* Width (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Height); /* Height (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].PhysicalWidth); /* PhysicalWidth (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].PhysicalHeight); /* PhysicalHeight (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Orientation); /* Orientation (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].Orientation); /* Orientation (4 bytes) */
|
||||
Stream_Write_UINT32(s,
|
||||
Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
||||
Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
||||
|
||||
WLog_DBG(TAG, "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index,
|
||||
Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top, Monitors[index].Width,
|
||||
Monitors[index].Height);
|
||||
WLog_DBG(TAG, "\t PhysicalWidth: %"PRIu32" PhysicalHeight: %"PRIu32" Orientation: %"PRIu32"",
|
||||
Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight, Monitors[index].Orientation);
|
||||
WLog_DBG(TAG,
|
||||
"\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32
|
||||
"x%" PRIu32 ")",
|
||||
index, Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top,
|
||||
Monitors[index].Width, Monitors[index].Height);
|
||||
WLog_DBG(TAG,
|
||||
"\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32 " Orientation: %" PRIu32
|
||||
"",
|
||||
Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight,
|
||||
Monitors[index].Orientation);
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_SealLength(s);
|
||||
|
||||
status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), NULL);
|
||||
|
||||
status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
|
||||
NULL);
|
||||
Stream_Free(s, TRUE);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -166,14 +173,13 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
static UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
DISP_PLUGIN* disp;
|
||||
DispClientContext *context;
|
||||
DispClientContext* context;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
|
||||
disp = (DISP_PLUGIN*) callback->plugin;
|
||||
context = (DispClientContext *)disp->iface.pInterface;
|
||||
disp = (DISP_PLUGIN*)callback->plugin;
|
||||
context = (DispClientContext*)disp->iface.pInterface;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 12)
|
||||
{
|
||||
@ -181,12 +187,13 @@ UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, disp->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
|
||||
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);
|
||||
ret = context->DisplayControlCaps(context, disp->MaxNumMonitors,
|
||||
disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -196,10 +203,10 @@ UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
static UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT32 type;
|
||||
UINT32 length;
|
||||
UINT32 error;
|
||||
DISPLAY_CONTROL_HEADER header;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
{
|
||||
@ -207,18 +214,25 @@ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, type); /* Type (4 bytes) */
|
||||
Stream_Read_UINT32(s, length); /* Length (4 bytes) */
|
||||
if ((error = disp_read_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
//WLog_ERR(TAG, "Type: %"PRIu32" Length: %"PRIu32"", type, length);
|
||||
if (!Stream_EnsureRemainingCapacity(s, header.length))
|
||||
{
|
||||
WLog_ERR(TAG, "not enough remaining data");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
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!", type);
|
||||
WLog_ERR(TAG, "Type %" PRIu32 " not recognized!", header.type);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
@ -228,10 +242,9 @@ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
DISP_CHANNEL_CALLBACK* callback = (DISP_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
DISP_CHANNEL_CALLBACK* callback = (DISP_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
return disp_recv_pdu(callback, data);
|
||||
}
|
||||
|
||||
@ -252,13 +265,12 @@ static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
DISP_CHANNEL_CALLBACK* callback;
|
||||
DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*) pListenerCallback;
|
||||
|
||||
callback = (DISP_CHANNEL_CALLBACK*) calloc(1, sizeof(DISP_CHANNEL_CALLBACK));
|
||||
DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*)pListenerCallback;
|
||||
callback = (DISP_CHANNEL_CALLBACK*)calloc(1, sizeof(DISP_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
@ -272,9 +284,7 @@ static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
listener_callback->channel_callback = callback;
|
||||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -286,9 +296,13 @@ static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
||||
static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
UINT status;
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin;
|
||||
|
||||
disp->listener_callback = (DISP_LISTENER_CALLBACK*) calloc(1, sizeof(DISP_LISTENER_CALLBACK));
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*)pPlugin;
|
||||
if (disp->initialized)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", DISP_DVC_CHANNEL_NAME);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
disp->listener_callback = (DISP_LISTENER_CALLBACK*)calloc(1, sizeof(DISP_LISTENER_CALLBACK));
|
||||
|
||||
if (!disp->listener_callback)
|
||||
{
|
||||
@ -299,12 +313,11 @@ static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
disp->listener_callback->iface.OnNewChannelConnection = disp_on_new_channel_connection;
|
||||
disp->listener_callback->plugin = pPlugin;
|
||||
disp->listener_callback->channel_mgr = pChannelMgr;
|
||||
|
||||
status = pChannelMgr->CreateListener(pChannelMgr, DISP_DVC_CHANNEL_NAME, 0,
|
||||
(IWTSListenerCallback*) disp->listener_callback, &(disp->listener));
|
||||
|
||||
&disp->listener_callback->iface, &(disp->listener));
|
||||
disp->listener->pInterface = disp->iface.pInterface;
|
||||
|
||||
disp->initialized = status == CHANNEL_RC_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -315,7 +328,15 @@ static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
*/
|
||||
static UINT disp_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin;
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*)pPlugin;
|
||||
|
||||
if (disp && disp->listener_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = disp->listener_callback->channel_mgr;
|
||||
if (mgr)
|
||||
IFCALL(mgr->DestroyListener, mgr, disp->listener);
|
||||
}
|
||||
|
||||
free(disp->listener_callback);
|
||||
free(disp->iface.pInterface);
|
||||
free(pPlugin);
|
||||
@ -331,18 +352,18 @@ static UINT disp_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
static UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors,
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) context->handle;
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*)context->handle;
|
||||
DISP_CHANNEL_CALLBACK* callback = disp->listener_callback->channel_callback;
|
||||
|
||||
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry disp_DVCPluginEntry
|
||||
#define DVCPluginEntry disp_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -355,11 +376,12 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DISP_PLUGIN* disp;
|
||||
DispClientContext* context;
|
||||
disp = (DISP_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "disp");
|
||||
|
||||
disp = (DISP_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "disp");
|
||||
if (!disp)
|
||||
{
|
||||
disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN));
|
||||
disp = (DISP_PLUGIN*)calloc(1, sizeof(DISP_PLUGIN));
|
||||
|
||||
if (!disp)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
@ -373,8 +395,8 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
disp->MaxNumMonitors = 16;
|
||||
disp->MaxMonitorAreaFactorA = 8192;
|
||||
disp->MaxMonitorAreaFactorB = 8192;
|
||||
context = (DispClientContext*)calloc(1, sizeof(DispClientContext));
|
||||
|
||||
context = (DispClientContext*) calloc(1, sizeof(DispClientContext));
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
@ -382,12 +404,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
context->handle = (void*) disp;
|
||||
context->handle = (void*)disp;
|
||||
context->SendMonitorLayout = disp_send_monitor_layout;
|
||||
|
||||
disp->iface.pInterface = (void*) context;
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp);
|
||||
disp->iface.pInterface = (void*)context;
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*)disp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -33,10 +33,6 @@
|
||||
|
||||
#include <freerdp/client/disp.h>
|
||||
|
||||
#define DISPLAY_CONTROL_PDU_TYPE_CAPS 0x00000005
|
||||
#define DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT 0x00000002
|
||||
|
||||
#define TAG CHANNELS_TAG("disp.client")
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DISP_CLIENT_MAIN_H */
|
||||
|
||||
|
||||
60
channels/disp/disp_common.c
Normal file
60
channels/disp/disp_common.c
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#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_GetRemainingLength(s) < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "header parsing failed: not enough data!");
|
||||
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;
|
||||
}
|
||||
32
channels/disp/disp_common.h
Normal file
32
channels/disp/disp_common.h
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 */
|
||||
32
channels/disp/server/CMakeLists.txt
Normal file
32
channels/disp/server/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
# 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
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
597
channels/disp/server/disp_main.c
Normal file
597
channels/disp/server/disp_main.c
Normal file
@ -0,0 +1,597 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "disp_main.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.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;
|
||||
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(DISPLAY_CONTROL_MONITOR_LAYOUT* 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->Width);
|
||||
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;
|
||||
UINT32 index;
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT_PDU pdu;
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT* monitor;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data!");
|
||||
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_GetRemainingLength(s) < DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE * pdu.NumMonitors)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data!");
|
||||
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 (index = 0; index < pdu.NumMonitors; index++)
|
||||
{
|
||||
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%d : 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, end;
|
||||
DISPLAY_CONTROL_HEADER header;
|
||||
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: %d, Expected: %" PRIu32 "", end,
|
||||
(beg + header.length));
|
||||
Stream_SetPosition(s, (beg + header.length));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT disp_server_handle_messages(DispServerContext* context)
|
||||
{
|
||||
DWORD BytesReturned;
|
||||
void* buffer;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
DispServerPrivate* priv = context->priv;
|
||||
wStream* s = priv->input_stream;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(priv->disp_channel, 0, (PCHAR)Stream_Buffer(s), Stream_Capacity(s),
|
||||
&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 = context->priv;
|
||||
DWORD status;
|
||||
DWORD nCount;
|
||||
HANDLE events[8];
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
nCount = 0;
|
||||
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 = context->priv;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = NULL;
|
||||
void* buffer;
|
||||
buffer = NULL;
|
||||
priv->SessionId = WTS_CURRENT_SESSION;
|
||||
UINT32 channelId;
|
||||
BOOL status = TRUE;
|
||||
|
||||
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 = (HANDLE)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;
|
||||
}
|
||||
|
||||
if (!(priv->thread =
|
||||
CreateThread(NULL, 0, disp_server_thread_func, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(priv->stopEvent);
|
||||
priv->stopEvent = NULL;
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
out_close:
|
||||
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;
|
||||
ULONG written;
|
||||
|
||||
if (!WTSVirtualChannelWrite(context->priv->disp_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &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 = 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 = context->priv;
|
||||
|
||||
if (priv->thread)
|
||||
{
|
||||
SetEvent(priv->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(priv->thread);
|
||||
CloseHandle(priv->stopEvent);
|
||||
priv->thread = NULL;
|
||||
priv->stopEvent = NULL;
|
||||
}
|
||||
|
||||
if (priv->disp_channel)
|
||||
{
|
||||
WTSVirtualChannelClose(priv->disp_channel);
|
||||
priv->disp_channel = NULL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
DispServerContext* disp_server_context_new(HANDLE vcm)
|
||||
{
|
||||
DispServerContext* context;
|
||||
DispServerPrivate* priv;
|
||||
context = (DispServerContext*)calloc(1, sizeof(DispServerContext));
|
||||
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
priv = context->priv = (DispServerPrivate*)calloc(1, sizeof(DispServerPrivate));
|
||||
|
||||
if (!context->priv)
|
||||
{
|
||||
WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
priv->input_stream = Stream_New(NULL, 4);
|
||||
|
||||
if (!priv->input_stream)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
goto out_free_priv;
|
||||
}
|
||||
|
||||
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;
|
||||
out_free_priv:
|
||||
free(context->priv);
|
||||
out_free:
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void disp_server_context_free(DispServerContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
disp_server_close(context);
|
||||
|
||||
if (context->priv)
|
||||
{
|
||||
Stream_Free(context->priv->input_stream, TRUE);
|
||||
free(context->priv);
|
||||
}
|
||||
|
||||
free(context);
|
||||
}
|
||||
37
channels/disp/server/disp_main.h
Normal file
37
channels/disp/server/disp_main.h
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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 _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 */
|
||||
File diff suppressed because it is too large
Load Diff
@ -37,21 +37,16 @@
|
||||
|
||||
typedef struct drdynvc_plugin drdynvcPlugin;
|
||||
|
||||
#define MAX_PLUGINS 32
|
||||
|
||||
struct _DVCMAN
|
||||
{
|
||||
IWTSVirtualChannelManager iface;
|
||||
|
||||
drdynvcPlugin* drdynvc;
|
||||
|
||||
int num_plugins;
|
||||
const char* plugin_names[MAX_PLUGINS];
|
||||
IWTSPlugin* plugins[MAX_PLUGINS];
|
||||
|
||||
int num_listeners;
|
||||
IWTSListener* listeners[MAX_PLUGINS];
|
||||
wArrayList* plugin_names;
|
||||
wArrayList* plugins;
|
||||
|
||||
wArrayList* listeners;
|
||||
wArrayList* channels;
|
||||
wStreamPool* pool;
|
||||
};
|
||||
@ -106,11 +101,11 @@ enum _DRDYNVC_STATE
|
||||
};
|
||||
typedef enum _DRDYNVC_STATE DRDYNVC_STATE;
|
||||
|
||||
#define CREATE_REQUEST_PDU 0x01
|
||||
#define DATA_FIRST_PDU 0x02
|
||||
#define DATA_PDU 0x03
|
||||
#define CLOSE_REQUEST_PDU 0x04
|
||||
#define CAPABILITY_REQUEST_PDU 0x05
|
||||
#define CREATE_REQUEST_PDU 0x01
|
||||
#define DATA_FIRST_PDU 0x02
|
||||
#define DATA_PDU 0x03
|
||||
#define CLOSE_REQUEST_PDU 0x04
|
||||
#define CAPABILITY_REQUEST_PDU 0x05
|
||||
|
||||
struct drdynvc_plugin
|
||||
{
|
||||
@ -127,7 +122,7 @@ struct drdynvc_plugin
|
||||
DRDYNVC_STATE state;
|
||||
DrdynvcClientContext* context;
|
||||
|
||||
int version;
|
||||
UINT16 version;
|
||||
int PriorityCharge0;
|
||||
int PriorityCharge1;
|
||||
int PriorityCharge2;
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
|
||||
#define TAG CHANNELS_TAG("drdynvc.server")
|
||||
|
||||
|
||||
static DWORD WINAPI drdynvc_server_thread(LPVOID arg)
|
||||
{
|
||||
#if 0
|
||||
@ -121,8 +120,8 @@ static DWORD WINAPI drdynvc_server_thread(LPVOID arg)
|
||||
*/
|
||||
static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm,
|
||||
WTS_CURRENT_SESSION, "drdynvc");
|
||||
context->priv->ChannelHandle =
|
||||
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "drdynvc");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
{
|
||||
@ -136,7 +135,8 @@ static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(context->priv->Thread = CreateThread(NULL, 0, drdynvc_server_thread, (void*) context, 0, NULL)))
|
||||
if (!(context->priv->Thread =
|
||||
CreateThread(NULL, 0, drdynvc_server_thread, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
@ -160,7 +160,7 @@ static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -171,14 +171,14 @@ static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
DrdynvcServerContext* drdynvc_server_context_new(HANDLE vcm)
|
||||
{
|
||||
DrdynvcServerContext* context;
|
||||
context = (DrdynvcServerContext*) calloc(1, sizeof(DrdynvcServerContext));
|
||||
context = (DrdynvcServerContext*)calloc(1, sizeof(DrdynvcServerContext));
|
||||
|
||||
if (context)
|
||||
{
|
||||
context->vcm = vcm;
|
||||
context->Start = drdynvc_server_start;
|
||||
context->Stop = drdynvc_server_stop;
|
||||
context->priv = (DrdynvcServerPrivate*) calloc(1, sizeof(DrdynvcServerPrivate));
|
||||
context->priv = (DrdynvcServerPrivate*)calloc(1, sizeof(DrdynvcServerPrivate));
|
||||
|
||||
if (!context->priv)
|
||||
{
|
||||
|
||||
@ -46,15 +46,29 @@
|
||||
#include "drive_file.h"
|
||||
|
||||
#ifdef WITH_DEBUG_RDPDR
|
||||
#define DEBUG_WSTR(msg, wstr) do { LPSTR lpstr; ConvertFromUnicode(CP_UTF8, 0, wstr, -1, &lpstr, 0, NULL, NULL); WLog_DBG(TAG, msg, lpstr); free(lpstr); } while (0)
|
||||
#define DEBUG_WSTR(msg, wstr) \
|
||||
do \
|
||||
{ \
|
||||
LPSTR lpstr; \
|
||||
ConvertFromUnicode(CP_UTF8, 0, wstr, -1, &lpstr, 0, NULL, NULL); \
|
||||
WLog_DBG(TAG, msg, lpstr); \
|
||||
free(lpstr); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DEBUG_WSTR(msg, wstr) do { } while (0)
|
||||
#define DEBUG_WSTR(msg, wstr) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static void drive_file_fix_path(WCHAR* path)
|
||||
static BOOL drive_file_fix_path(WCHAR* path, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
size_t length = _wcslen(path);
|
||||
|
||||
if ((length == 0) || (length > UINT32_MAX))
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(path);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
@ -65,57 +79,82 @@ static void drive_file_fix_path(WCHAR* path)
|
||||
#ifdef WIN32
|
||||
|
||||
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
#else
|
||||
|
||||
if ((length == 1) && (path[0] == L'/'))
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
#endif
|
||||
|
||||
if ((length > 0) && (path[length - 1] == L'/'))
|
||||
path[length - 1] = L'\0';
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
|
||||
size_t PathLength)
|
||||
size_t PathWCharLength)
|
||||
{
|
||||
WCHAR* fullpath;
|
||||
size_t base_path_length;
|
||||
BOOL ok = FALSE;
|
||||
WCHAR* fullpath = NULL;
|
||||
size_t length;
|
||||
|
||||
if (!base_path || !path)
|
||||
return NULL;
|
||||
if (!base_path || (!path && (PathWCharLength > 0)))
|
||||
goto fail;
|
||||
|
||||
base_path_length = _wcslen(base_path) * 2;
|
||||
fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR));
|
||||
const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
|
||||
length = base_path_length + PathWCharLength + 1;
|
||||
fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
|
||||
|
||||
if (!fullpath)
|
||||
goto fail;
|
||||
|
||||
CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
|
||||
if (path)
|
||||
CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
|
||||
|
||||
if (!drive_file_fix_path(fullpath, length))
|
||||
goto fail;
|
||||
|
||||
/* Ensure the path does not contain sequences like '..' */
|
||||
const WCHAR dotdot[] = { '.', '.', '\0' };
|
||||
if (_wcsstr(&fullpath[base_path_length], dotdot))
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return NULL;
|
||||
char abuffer[MAX_PATH] = { 0 };
|
||||
ConvertFromUnicode(CP_UTF8, 0, &fullpath[base_path_length], -1, (char**)&abuffer,
|
||||
ARRAYSIZE(abuffer) - 1, NULL, NULL);
|
||||
|
||||
WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
|
||||
&abuffer[base_path_length]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
CopyMemory(fullpath, base_path, base_path_length);
|
||||
CopyMemory((char*)fullpath + base_path_length, path, PathLength);
|
||||
drive_file_fix_path(fullpath);
|
||||
ok = TRUE;
|
||||
fail:
|
||||
if (!ok)
|
||||
{
|
||||
free(fullpath);
|
||||
fullpath = NULL;
|
||||
}
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
static BOOL drive_file_remove_dir(const WCHAR* path)
|
||||
{
|
||||
WIN32_FIND_DATAW findFileData;
|
||||
WIN32_FIND_DATAW findFileData = { 0 };
|
||||
BOOL ret = TRUE;
|
||||
HANDLE dir;
|
||||
WCHAR* fullpath;
|
||||
WCHAR* path_slash;
|
||||
size_t base_path_length;
|
||||
HANDLE dir = INVALID_HANDLE_VALUE;
|
||||
WCHAR* fullpath = NULL;
|
||||
WCHAR* path_slash = NULL;
|
||||
size_t base_path_length = 0;
|
||||
|
||||
if (!path)
|
||||
return FALSE;
|
||||
|
||||
base_path_length = _wcslen(path) * 2;
|
||||
path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3);
|
||||
base_path_length = _wcslen(path);
|
||||
path_slash = (WCHAR*)calloc(base_path_length + 3, sizeof(WCHAR));
|
||||
|
||||
if (!path_slash)
|
||||
{
|
||||
@ -123,12 +162,11 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CopyMemory(path_slash, path, base_path_length);
|
||||
path_slash[base_path_length / 2] = L'/';
|
||||
path_slash[base_path_length / 2 + 1] = L'*';
|
||||
CopyMemory(path_slash, path, base_path_length * sizeof(WCHAR));
|
||||
path_slash[base_path_length] = L'/';
|
||||
path_slash[base_path_length + 1] = L'*';
|
||||
DEBUG_WSTR("Search in %s", path_slash);
|
||||
dir = FindFirstFileW(path_slash, &findFileData);
|
||||
path_slash[base_path_length / 2 + 1] = 0;
|
||||
|
||||
if (dir == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -138,15 +176,15 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
||||
|
||||
do
|
||||
{
|
||||
size_t len = _wcslen(findFileData.cFileName);
|
||||
const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
|
||||
|
||||
if ((len == 1 && findFileData.cFileName[0] == L'.') || (len == 2 &&
|
||||
findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
|
||||
if ((len == 1 && findFileData.cFileName[0] == L'.') ||
|
||||
(len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2);
|
||||
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len);
|
||||
DEBUG_WSTR("Delete %s", fullpath);
|
||||
|
||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
@ -162,8 +200,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
||||
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
while (ret && FindNextFileW(dir, &findFileData) != 0);
|
||||
} while (ret && FindNextFileW(dir, &findFileData) != 0);
|
||||
|
||||
FindClose(dir);
|
||||
|
||||
@ -238,7 +275,8 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
||||
if (file->is_dir)
|
||||
{
|
||||
/* Should only create the directory if the disposition allows for it */
|
||||
if ((file->CreateDisposition == FILE_OPEN_IF) || (file->CreateDisposition == FILE_CREATE))
|
||||
if ((file->CreateDisposition == FILE_OPEN_IF) ||
|
||||
(file->CreateDisposition == FILE_CREATE))
|
||||
{
|
||||
if (CreateDirectoryW(file->fullpath, NULL) != 0)
|
||||
{
|
||||
@ -255,27 +293,33 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
||||
{
|
||||
switch (file->CreateDisposition)
|
||||
{
|
||||
case FILE_SUPERSEDE: /* If the file already exists, replace it with the given file. If it does not, create the given file. */
|
||||
case FILE_SUPERSEDE: /* If the file already exists, replace it with the given file. If
|
||||
it does not, create the given file. */
|
||||
CreateDisposition = CREATE_ALWAYS;
|
||||
break;
|
||||
|
||||
case FILE_OPEN: /* If the file already exists, open it instead of creating a new file. If it does not, fail the request and do not create a new file. */
|
||||
case FILE_OPEN: /* If the file already exists, open it instead of creating a new file.
|
||||
If it does not, fail the request and do not create a new file. */
|
||||
CreateDisposition = OPEN_EXISTING;
|
||||
break;
|
||||
|
||||
case FILE_CREATE: /* If the file already exists, fail the request and do not create or open the given file. If it does not, create the given file. */
|
||||
case FILE_CREATE: /* If the file already exists, fail the request and do not create or
|
||||
open the given file. If it does not, create the given file. */
|
||||
CreateDisposition = CREATE_NEW;
|
||||
break;
|
||||
|
||||
case FILE_OPEN_IF: /* If the file already exists, open it. If it does not, create the given file. */
|
||||
case FILE_OPEN_IF: /* If the file already exists, open it. If it does not, create the
|
||||
given file. */
|
||||
CreateDisposition = OPEN_ALWAYS;
|
||||
break;
|
||||
|
||||
case FILE_OVERWRITE: /* If the file already exists, open it and overwrite it. If it does not, fail the request. */
|
||||
case FILE_OVERWRITE: /* If the file already exists, open it and overwrite it. If it does
|
||||
not, fail the request. */
|
||||
CreateDisposition = TRUNCATE_EXISTING;
|
||||
break;
|
||||
|
||||
case FILE_OVERWRITE_IF: /* If the file already exists, open it and overwrite it. If it does not, create the given file. */
|
||||
case FILE_OVERWRITE_IF: /* If the file already exists, open it and overwrite it. If it
|
||||
does not, create the given file. */
|
||||
CreateDisposition = CREATE_ALWAYS;
|
||||
break;
|
||||
|
||||
@ -286,11 +330,11 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
||||
#ifndef WIN32
|
||||
file->SharedAccess = 0;
|
||||
#endif
|
||||
file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess,
|
||||
file->SharedAccess, NULL, CreateDisposition,
|
||||
file->FileAttributes, NULL);
|
||||
file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess, file->SharedAccess,
|
||||
NULL, CreateDisposition, file->FileAttributes, NULL);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (file->file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
/* Get the error message, if any. */
|
||||
@ -298,31 +342,34 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
||||
|
||||
if (errorMessageID != 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
LPSTR messageBuffer = NULL;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
||||
size_t size =
|
||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)&messageBuffer, 0, NULL);
|
||||
WLog_ERR(TAG, "Error in drive_file_init: %s %s", messageBuffer, file->fullpath);
|
||||
/* Free the buffer. */
|
||||
LocalFree(messageBuffer);
|
||||
#endif
|
||||
/* restore original error code */
|
||||
SetLastError(errorMessageID);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return file->file_handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
|
||||
UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
||||
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
|
||||
{
|
||||
DRIVE_FILE* file;
|
||||
|
||||
if (!base_path || !path)
|
||||
if (!base_path || (!path && (PathWCharLength > 0)))
|
||||
return NULL;
|
||||
|
||||
file = (DRIVE_FILE*) calloc(1, sizeof(DRIVE_FILE));
|
||||
file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@ -339,11 +386,13 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat
|
||||
file->CreateDisposition = CreateDisposition;
|
||||
file->CreateOptions = CreateOptions;
|
||||
file->SharedAccess = SharedAccess;
|
||||
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength));
|
||||
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength));
|
||||
|
||||
if (!drive_file_init(file))
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
drive_file_free(file);
|
||||
SetLastError(lastError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -461,15 +510,23 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, 36); /* Length */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(
|
||||
output, fileAttributes.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output,
|
||||
fileAttributes.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.dwFileAttributes); /* FileAttributes */
|
||||
/* Reserved(4), MUST NOT be added! */
|
||||
break;
|
||||
|
||||
@ -479,15 +536,16 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 22))
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, 22); /* Length */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, 22); /* Length */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, 0); /* NumberOfLinks */
|
||||
Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
|
||||
Stream_Write_UINT8(output, fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? TRUE :
|
||||
FALSE); /* Directory */
|
||||
Stream_Write_UINT32(output, 0); /* NumberOfLinks */
|
||||
Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
|
||||
Stream_Write_UINT8(output, fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
|
||||
? TRUE
|
||||
: FALSE); /* Directory */
|
||||
/* Reserved(2), MUST NOT be added! */
|
||||
break;
|
||||
|
||||
@ -497,9 +555,9 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 8))
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, 8); /* Length */
|
||||
Stream_Write_UINT32(output, 8); /* Length */
|
||||
Stream_Write_UINT32(output, fileAttributes.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, 0); /* ReparseTag */
|
||||
Stream_Write_UINT32(output, 0); /* ReparseTag */
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -556,47 +614,49 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
|
||||
if (file->file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to set file time %s (%"PRId32")", file->fullpath, GetLastError());
|
||||
WLog_ERR(TAG, "Unable to set file time %s (%" PRId32 ")", file->fullpath,
|
||||
GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (liCreationTime.QuadPart != 0)
|
||||
{
|
||||
ftCreationTime.dwHighDateTime = liCreationTime.HighPart;
|
||||
ftCreationTime.dwLowDateTime = liCreationTime.LowPart;
|
||||
ftCreationTime.dwHighDateTime = liCreationTime.u.HighPart;
|
||||
ftCreationTime.dwLowDateTime = liCreationTime.u.LowPart;
|
||||
pftCreationTime = &ftCreationTime;
|
||||
}
|
||||
|
||||
if (liLastAccessTime.QuadPart != 0)
|
||||
{
|
||||
ftLastAccessTime.dwHighDateTime = liLastAccessTime.HighPart;
|
||||
ftLastAccessTime.dwLowDateTime = liLastAccessTime.LowPart;
|
||||
ftLastAccessTime.dwHighDateTime = liLastAccessTime.u.HighPart;
|
||||
ftLastAccessTime.dwLowDateTime = liLastAccessTime.u.LowPart;
|
||||
pftLastAccessTime = &ftLastAccessTime;
|
||||
}
|
||||
|
||||
if (liLastWriteTime.QuadPart != 0)
|
||||
{
|
||||
ftLastWriteTime.dwHighDateTime = liLastWriteTime.HighPart;
|
||||
ftLastWriteTime.dwLowDateTime = liLastWriteTime.LowPart;
|
||||
ftLastWriteTime.dwHighDateTime = liLastWriteTime.u.HighPart;
|
||||
ftLastWriteTime.dwLowDateTime = liLastWriteTime.u.LowPart;
|
||||
pftLastWriteTime = &ftLastWriteTime;
|
||||
}
|
||||
|
||||
if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart)
|
||||
{
|
||||
ftLastWriteTime.dwHighDateTime = liChangeTime.HighPart;
|
||||
ftLastWriteTime.dwLowDateTime = liChangeTime.LowPart;
|
||||
ftLastWriteTime.dwHighDateTime = liChangeTime.u.HighPart;
|
||||
ftLastWriteTime.dwLowDateTime = liChangeTime.u.LowPart;
|
||||
pftLastWriteTime = &ftLastWriteTime;
|
||||
}
|
||||
|
||||
DEBUG_WSTR("SetFileTime %s", file->fullpath);
|
||||
|
||||
if (!SetFileTime(file->file_handle, pftCreationTime, pftLastAccessTime, pftLastWriteTime))
|
||||
SetFileAttributesW(file->fullpath, FileAttributes);
|
||||
if (!SetFileTime(file->file_handle, pftCreationTime, pftLastAccessTime,
|
||||
pftLastWriteTime))
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to set file time to %s", file->fullpath);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SetFileAttributesW(file->fullpath, FileAttributes);
|
||||
break;
|
||||
|
||||
case FileEndOfFileInformation:
|
||||
@ -611,8 +671,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
|
||||
if (file->file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %"PRId64" (%"PRId32")", file->fullpath, size,
|
||||
GetLastError());
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", file->fullpath,
|
||||
size, GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -620,7 +680,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
|
||||
if (!SetFilePointerEx(file->file_handle, liSize, NULL, FILE_BEGIN))
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d (%"PRId32")", file->fullpath, size, GetLastError());
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d (%" PRId32 ")", file->fullpath, size,
|
||||
GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -628,7 +689,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
|
||||
if (SetEndOfFile(file->file_handle) == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d (%"PRId32")", file->fullpath, size, GetLastError());
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d (%" PRId32 ")", file->fullpath, size,
|
||||
GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -679,13 +741,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
return FALSE;
|
||||
|
||||
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
|
||||
FileNameLength);
|
||||
FileNameLength / sizeof(WCHAR));
|
||||
|
||||
if (!fullpath)
|
||||
{
|
||||
WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@ -699,7 +758,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
DEBUG_WSTR("MoveFileExW %s", file->fullpath);
|
||||
|
||||
if (MoveFileExW(file->fullpath, fullpath,
|
||||
MOVEFILE_COPY_ALLOWED | (ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0)))
|
||||
MOVEFILE_COPY_ALLOWED |
|
||||
(ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0)))
|
||||
{
|
||||
if (!drive_file_set_fullpath(file, fullpath))
|
||||
return FALSE;
|
||||
@ -723,7 +783,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
}
|
||||
|
||||
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||
const WCHAR* path, UINT32 PathLength, wStream* output)
|
||||
const WCHAR* path, UINT32 PathWCharLength, wStream* output)
|
||||
{
|
||||
size_t length;
|
||||
WCHAR* ent_path;
|
||||
@ -737,7 +797,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
||||
if (file->find_handle != INVALID_HANDLE_VALUE)
|
||||
FindClose(file->find_handle);
|
||||
|
||||
ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength);
|
||||
ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength);
|
||||
/* open new search handle and retrieve the first entry */
|
||||
file->find_handle = FindFirstFileW(ent_path, &file->find_data);
|
||||
free(ent_path);
|
||||
@ -762,22 +822,30 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, (UINT32)(64 + length)); /* Length */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(
|
||||
output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(
|
||||
output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write(output, file->find_data.cFileName, length);
|
||||
break;
|
||||
|
||||
@ -791,23 +859,31 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, (UINT32)(68 + length)); /* Length */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(
|
||||
output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(
|
||||
output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write_UINT32(output, 0); /* EaSize */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write_UINT32(output, 0); /* EaSize */
|
||||
Stream_Write(output, file->find_data.cFileName, length);
|
||||
break;
|
||||
|
||||
@ -821,24 +897,32 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, (UINT32)(93 + length)); /* Length */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(
|
||||
output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(
|
||||
output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output,
|
||||
file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write_UINT32(output, 0); /* EaSize */
|
||||
Stream_Write_UINT8(output, 0); /* ShortNameLength */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write_UINT32(output, 0); /* EaSize */
|
||||
Stream_Write_UINT8(output, 0); /* ShortNameLength */
|
||||
/* Reserved(1), MUST NOT be added! */
|
||||
Stream_Zero(output, 24); /* ShortName */
|
||||
Stream_Write(output, file->find_data.cFileName, length);
|
||||
@ -854,14 +938,14 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, (UINT32)(12 + length)); /* Length */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||
Stream_Write(output, file->find_data.cFileName, length);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "unhandled FsInformationClass %"PRIu32, FsInformationClass);
|
||||
WLog_ERR(TAG, "unhandled FsInformationClass %" PRIu32, FsInformationClass);
|
||||
/* Unhandled FsInformationClass */
|
||||
goto out_fail;
|
||||
}
|
||||
@ -869,6 +953,6 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
||||
return TRUE;
|
||||
out_fail:
|
||||
Stream_Write_UINT32(output, 0); /* Length */
|
||||
Stream_Write_UINT8(output, 0); /* Padding */
|
||||
Stream_Write_UINT8(output, 0); /* Padding */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -51,8 +51,8 @@ struct _DRIVE_FILE
|
||||
UINT32 CreateOptions;
|
||||
};
|
||||
|
||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
|
||||
UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
||||
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
|
||||
BOOL drive_file_free(DRIVE_FILE* file);
|
||||
|
||||
@ -64,6 +64,6 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
|
||||
wStream* input);
|
||||
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||
const WCHAR* path, UINT32 PathLength, wStream* output);
|
||||
const WCHAR* path, UINT32 PathWCharLength, wStream* output);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
|
||||
|
||||
@ -102,7 +102,7 @@ static DWORD drive_map_windows_err(DWORD fs_errno)
|
||||
|
||||
case ERROR_FILE_EXISTS:
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
rc = STATUS_OBJECT_NAME_COLLISION;
|
||||
rc = STATUS_OBJECT_NAME_COLLISION;
|
||||
break;
|
||||
|
||||
case ERROR_INVALID_NAME:
|
||||
@ -127,7 +127,7 @@ static DWORD drive_map_windows_err(DWORD fs_errno)
|
||||
|
||||
default:
|
||||
rc = STATUS_UNSUCCESSFUL;
|
||||
WLog_ERR(TAG, "Error code not found: %"PRIu32"", fs_errno);
|
||||
WLog_ERR(TAG, "Error code not found: %" PRIu32 "", fs_errno);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -137,12 +137,12 @@ static DWORD drive_map_windows_err(DWORD fs_errno)
|
||||
static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
|
||||
{
|
||||
DRIVE_FILE* file = NULL;
|
||||
void* key = (void*)(size_t) id;
|
||||
void* key = (void*)(size_t)id;
|
||||
|
||||
if (!drive)
|
||||
return NULL;
|
||||
|
||||
file = (DRIVE_FILE*) ListDictionary_GetItemValue(drive->files, key);
|
||||
file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
|
||||
return file;
|
||||
}
|
||||
|
||||
@ -182,10 +182,10 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
||||
if (Stream_GetRemainingLength(irp->input) < PathLength)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
path = (WCHAR*) Stream_Pointer(irp->input);
|
||||
path = (const WCHAR*)Stream_Pointer(irp->input);
|
||||
FileId = irp->devman->id_sequence++;
|
||||
file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition,
|
||||
CreateOptions, FileAttributes, SharedAccess);
|
||||
file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
|
||||
CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@ -195,7 +195,7 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
||||
}
|
||||
else
|
||||
{
|
||||
void* key = (void*)(size_t) file->id;
|
||||
void* key = (void*)(size_t)file->id;
|
||||
|
||||
if (!ListDictionary_Add(drive->files, key, file))
|
||||
{
|
||||
@ -245,7 +245,7 @@ static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
key = (void*)(size_t) irp->FileId;
|
||||
key = (void*)(size_t)irp->FileId;
|
||||
|
||||
if (!file)
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
@ -331,6 +331,7 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
|
||||
DRIVE_FILE* file;
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
void* ptr;
|
||||
|
||||
if (!drive || !irp || !irp->input || !irp->output || !irp->Complete)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -341,6 +342,9 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
ptr = Stream_Pointer(irp->input);
|
||||
if (!Stream_SafeSeek(irp->input, Length))
|
||||
return ERROR_INVALID_DATA;
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (!file)
|
||||
@ -353,7 +357,7 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
|
||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||
Length = 0;
|
||||
}
|
||||
else if (!drive_file_write(file, Stream_Pointer(irp->input), Length))
|
||||
else if (!drive_file_write(file, ptr, Length))
|
||||
{
|
||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||
Length = 0;
|
||||
@ -421,8 +425,7 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if (!drive_file_set_information(file, FsInformationClass, Length,
|
||||
irp->input))
|
||||
else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input))
|
||||
{
|
||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||
}
|
||||
@ -434,19 +437,17 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
IRP* irp)
|
||||
static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
|
||||
{
|
||||
UINT32 FsInformationClass;
|
||||
wStream* output = NULL;
|
||||
char* volumeLabel = {"FREERDP"};
|
||||
char* diskType = {"FAT32"};
|
||||
char* volumeLabel = { "FREERDP" };
|
||||
char* diskType = { "FAT32" };
|
||||
WCHAR* outStr = NULL;
|
||||
int length;
|
||||
DWORD lpSectorsPerCluster;
|
||||
@ -489,10 +490,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
|
||||
GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
|
||||
Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
|
||||
Stream_Write_UINT32(output, wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
|
||||
Stream_Write_UINT32(output,
|
||||
wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
|
||||
Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
|
||||
Stream_Write_UINT32(output, length); /* VolumeLabelLength */
|
||||
Stream_Write_UINT8(output, 0); /* SupportsObjects */
|
||||
Stream_Write_UINT32(output, length); /* VolumeLabelLength */
|
||||
Stream_Write_UINT8(output, 0); /* SupportsObjects */
|
||||
/* Reserved(1), MUST NOT be added! */
|
||||
Stream_Write(output, outStr, length); /* VolumeLabel (Unicode) */
|
||||
free(outStr);
|
||||
@ -509,9 +511,9 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
}
|
||||
|
||||
Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
|
||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
||||
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
||||
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
||||
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
||||
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
||||
break;
|
||||
|
||||
case FileFsAttributeInformation:
|
||||
@ -532,13 +534,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(output,
|
||||
FILE_CASE_SENSITIVE_SEARCH |
|
||||
FILE_CASE_PRESERVED_NAMES |
|
||||
FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
|
||||
Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
|
||||
Stream_Write_UINT32(output, length); /* FileSystemNameLength */
|
||||
Stream_Write(output, outStr, length); /* FileSystemName (Unicode) */
|
||||
Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
|
||||
FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
|
||||
Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
|
||||
Stream_Write_UINT32(output, length); /* FileSystemNameLength */
|
||||
Stream_Write(output, outStr, length); /* FileSystemName (Unicode) */
|
||||
free(outStr);
|
||||
break;
|
||||
|
||||
@ -553,10 +553,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
}
|
||||
|
||||
Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
|
||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
|
||||
Stream_Write_UINT64(output,
|
||||
lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
|
||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
||||
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
||||
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
||||
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
||||
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
||||
break;
|
||||
|
||||
case FileFsDeviceInformation:
|
||||
@ -570,7 +571,7 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(output, FILE_DEVICE_DISK); /* DeviceType */
|
||||
Stream_Write_UINT32(output, 0); /* Characteristics */
|
||||
Stream_Write_UINT32(output, 0); /* Characteristics */
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -627,7 +628,10 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT8(irp->input, InitialQuery);
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
Stream_Seek(irp->input, 23); /* Padding */
|
||||
path = (WCHAR*) Stream_Pointer(irp->input);
|
||||
path = (WCHAR*)Stream_Pointer(irp->input);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (file == NULL)
|
||||
@ -635,8 +639,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
Stream_Write_UINT32(irp->output, 0); /* Length */
|
||||
}
|
||||
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength,
|
||||
irp->output))
|
||||
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
|
||||
PathLength / sizeof(WCHAR), irp->output))
|
||||
{
|
||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||
}
|
||||
@ -754,7 +758,7 @@ static DWORD WINAPI drive_thread_func(LPVOID arg)
|
||||
{
|
||||
IRP* irp;
|
||||
wMessage message;
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) arg;
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!drive)
|
||||
@ -782,13 +786,13 @@ static DWORD WINAPI drive_thread_func(LPVOID arg)
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
irp = (IRP*) message.wParam;
|
||||
irp = (IRP*)message.wParam;
|
||||
|
||||
if (irp)
|
||||
{
|
||||
if ((error = drive_process_irp(drive, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "drive_process_irp failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "drive_process_irp failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -810,12 +814,12 @@ fail:
|
||||
*/
|
||||
static UINT drive_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) device;
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
|
||||
|
||||
if (!drive)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*) irp, NULL))
|
||||
if (!MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*)irp, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -847,17 +851,17 @@ static UINT drive_free_int(DRIVE_DEVICE* drive)
|
||||
*/
|
||||
static UINT drive_free(DEVICE* device)
|
||||
{
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) device;
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!drive)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (MessageQueue_PostQuit(drive->IrpQueue, 0)
|
||||
&& (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
|
||||
if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
|
||||
(WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -869,7 +873,7 @@ static UINT drive_free(DEVICE* device)
|
||||
*/
|
||||
static void drive_file_objfree(void* obj)
|
||||
{
|
||||
drive_file_free((DRIVE_FILE*) obj);
|
||||
drive_file_free((DRIVE_FILE*)obj);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -877,17 +881,24 @@ static void drive_file_objfree(void* obj)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
const char* name, const char* path, BOOL automount)
|
||||
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
|
||||
const char* path, BOOL automount)
|
||||
{
|
||||
size_t i, length;
|
||||
DRIVE_DEVICE* drive;
|
||||
UINT error;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!pEntryPoints || !name || !path)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] Invalid parameters: pEntryPoints=%p, name=%p, path=%p", pEntryPoints,
|
||||
name, path);
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (name[0] && path[0])
|
||||
{
|
||||
size_t pathLength = strnlen(path, MAX_PATH);
|
||||
drive = (DRIVE_DEVICE*) calloc(1, sizeof(DRIVE_DEVICE));
|
||||
drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
|
||||
|
||||
if (!drive)
|
||||
{
|
||||
@ -896,7 +907,6 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
}
|
||||
|
||||
drive->device.type = RDPDR_DTYP_FILESYSTEM;
|
||||
drive->device.name = name;
|
||||
drive->device.IRPRequest = drive_irp_request;
|
||||
drive->device.Free = drive_free;
|
||||
drive->rdpcontext = pEntryPoints->rdpcontext;
|
||||
@ -911,11 +921,34 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
for (i = 0; i <= length; i++)
|
||||
Stream_Write_UINT8(drive->device.data, name[i] < 0 ? '_' : name[i]);
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
/* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */
|
||||
switch (name[i])
|
||||
{
|
||||
case ':':
|
||||
case '<':
|
||||
case '>':
|
||||
case '\"':
|
||||
case '/':
|
||||
case '\\':
|
||||
case '|':
|
||||
case ' ':
|
||||
Stream_Write_UINT8(drive->device.data, '_');
|
||||
break;
|
||||
default:
|
||||
Stream_Write_UINT8(drive->device.data, (BYTE)name[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Stream_Write_UINT8(drive->device.data, '\0');
|
||||
|
||||
drive->device.name = (const char*)Stream_Buffer(drive->device.data);
|
||||
if (!drive->device.name)
|
||||
goto out_error;
|
||||
|
||||
if ((pathLength > 1) && (path[pathLength - 1] == '/'))
|
||||
pathLength --;
|
||||
pathLength--;
|
||||
|
||||
if (ConvertToUnicode(sys_code_page, 0, path, pathLength, &drive->path, 0) <= 0)
|
||||
{
|
||||
@ -943,15 +976,14 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
||||
(DEVICE*) drive)))
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)drive)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (!(drive->thread = CreateThread(NULL, 0, drive_thread_func, drive,
|
||||
CREATE_SUSPENDED, NULL)))
|
||||
if (!(drive->thread =
|
||||
CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
goto out_error;
|
||||
@ -967,9 +999,9 @@ out_error:
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DeviceServiceEntry drive_DeviceServiceEntry
|
||||
#define DeviceServiceEntry drive_DeviceServiceEntry
|
||||
#else
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -988,7 +1020,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
char* bufdup;
|
||||
char* devdup;
|
||||
#endif
|
||||
drive = (RDPDR_DRIVE*) pEntryPoints->device;
|
||||
drive = (RDPDR_DRIVE*)pEntryPoints->device;
|
||||
#ifndef WIN32
|
||||
sys_code_page = CP_UTF8;
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
|
||||
#include "echo_main.h"
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/channels/echo.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("echo.client")
|
||||
|
||||
@ -59,6 +60,8 @@ struct _ECHO_PLUGIN
|
||||
IWTSPlugin iface;
|
||||
|
||||
ECHO_LISTENER_CALLBACK* listener_callback;
|
||||
IWTSListener* listener;
|
||||
BOOL initialized;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -66,9 +69,9 @@ struct _ECHO_PLUGIN
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
static UINT echo_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
ECHO_CHANNEL_CALLBACK* callback = (ECHO_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
ECHO_CHANNEL_CALLBACK* callback = (ECHO_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
BYTE* pBuffer = Stream_Pointer(data);
|
||||
UINT32 cbSize = Stream_GetRemainingLength(data);
|
||||
|
||||
@ -83,7 +86,7 @@ static UINT echo_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
*/
|
||||
static UINT echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
ECHO_CHANNEL_CALLBACK* callback = (ECHO_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
ECHO_CHANNEL_CALLBACK* callback = (ECHO_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
free(callback);
|
||||
|
||||
@ -96,13 +99,13 @@ static UINT echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
ECHO_CHANNEL_CALLBACK* callback;
|
||||
ECHO_LISTENER_CALLBACK* listener_callback = (ECHO_LISTENER_CALLBACK*) pListenerCallback;
|
||||
ECHO_LISTENER_CALLBACK* listener_callback = (ECHO_LISTENER_CALLBACK*)pListenerCallback;
|
||||
|
||||
callback = (ECHO_CHANNEL_CALLBACK*) calloc(1, sizeof(ECHO_CHANNEL_CALLBACK));
|
||||
callback = (ECHO_CHANNEL_CALLBACK*)calloc(1, sizeof(ECHO_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
@ -116,7 +119,7 @@ static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -128,9 +131,14 @@ static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
||||
*/
|
||||
static UINT echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*) pPlugin;
|
||||
|
||||
echo->listener_callback = (ECHO_LISTENER_CALLBACK*) calloc(1, sizeof(ECHO_LISTENER_CALLBACK));
|
||||
UINT status;
|
||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*)pPlugin;
|
||||
if (echo->initialized)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", ECHO_DVC_CHANNEL_NAME);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
echo->listener_callback = (ECHO_LISTENER_CALLBACK*)calloc(1, sizeof(ECHO_LISTENER_CALLBACK));
|
||||
|
||||
if (!echo->listener_callback)
|
||||
{
|
||||
@ -142,8 +150,11 @@ static UINT echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
echo->listener_callback->plugin = pPlugin;
|
||||
echo->listener_callback->channel_mgr = pChannelMgr;
|
||||
|
||||
return pChannelMgr->CreateListener(pChannelMgr, "ECHO", 0,
|
||||
(IWTSListenerCallback*) echo->listener_callback, NULL);
|
||||
status = pChannelMgr->CreateListener(pChannelMgr, ECHO_DVC_CHANNEL_NAME, 0,
|
||||
&echo->listener_callback->iface, &echo->listener);
|
||||
|
||||
echo->initialized = status == CHANNEL_RC_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,17 +164,22 @@ static UINT echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
*/
|
||||
static UINT echo_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*) pPlugin;
|
||||
|
||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*)pPlugin;
|
||||
if (echo && echo->listener_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = echo->listener_callback->channel_mgr;
|
||||
if (mgr)
|
||||
IFCALL(mgr->DestroyListener, mgr, echo->listener);
|
||||
}
|
||||
free(echo);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry echo_DVCPluginEntry
|
||||
#define DVCPluginEntry echo_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -176,11 +192,11 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
ECHO_PLUGIN* echo;
|
||||
|
||||
echo = (ECHO_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "echo");
|
||||
echo = (ECHO_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "echo");
|
||||
|
||||
if (!echo)
|
||||
{
|
||||
echo = (ECHO_PLUGIN*) calloc(1, sizeof(ECHO_PLUGIN));
|
||||
echo = (ECHO_PLUGIN*)calloc(1, sizeof(ECHO_PLUGIN));
|
||||
|
||||
if (!echo)
|
||||
{
|
||||
@ -193,7 +209,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
echo->iface.Disconnected = NULL;
|
||||
echo->iface.Terminated = echo_plugin_terminated;
|
||||
|
||||
status = pEntryPoints->RegisterPlugin(pEntryPoints, "echo", (IWTSPlugin*) echo);
|
||||
status = pEntryPoints->RegisterPlugin(pEntryPoints, "echo", (IWTSPlugin*)echo);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
@ -33,8 +33,10 @@
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(...) do { } while (0)
|
||||
#define DEBUG_DVC(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_ECHO_CLIENT_MAIN_H */
|
||||
|
||||
|
||||
@ -66,14 +66,14 @@ static UINT echo_server_open_channel(echo_server* echo)
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = NULL;
|
||||
|
||||
if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION,
|
||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned) == FALSE)
|
||||
if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
echo->SessionId = (DWORD) * pSessionId;
|
||||
echo->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
|
||||
StartTick = GetTickCount();
|
||||
@ -83,15 +83,29 @@ static UINT echo_server_open_channel(echo_server* echo)
|
||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", Error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId,
|
||||
"ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
echo->echo_channel =
|
||||
WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (echo->echo_channel)
|
||||
{
|
||||
UINT32 channelId;
|
||||
BOOL status = TRUE;
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(echo->echo_channel);
|
||||
|
||||
IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Error = GetLastError();
|
||||
|
||||
@ -114,19 +128,19 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
BOOL ready = FALSE;
|
||||
HANDLE ChannelEvent;
|
||||
DWORD BytesReturned = 0;
|
||||
echo_server* echo = (echo_server*) arg;
|
||||
echo_server* echo = (echo_server*)arg;
|
||||
UINT error;
|
||||
DWORD status;
|
||||
|
||||
if ((error = echo_server_open_channel(echo)))
|
||||
{
|
||||
UINT error2 = 0;
|
||||
WLog_ERR(TAG, "echo_server_open_channel failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "echo_server_open_channel failed with error %" PRIu32 "!", error);
|
||||
IFCALLRET(echo->context.OpenResult, error2, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED);
|
||||
|
||||
if (error2)
|
||||
WLog_ERR(TAG, "echo server's OpenResult callback failed with error %"PRIu32"",
|
||||
WLog_ERR(TAG, "echo server's OpenResult callback failed with error %" PRIu32 "",
|
||||
error2);
|
||||
|
||||
goto out;
|
||||
@ -158,7 +172,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -168,7 +182,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
ECHO_SERVER_OPEN_RESULT_CLOSED);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -180,21 +194,20 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
ECHO_SERVER_OPEN_RESULT_ERROR);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ready = *((BOOL*) buffer);
|
||||
ready = *((BOOL*)buffer);
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (ready)
|
||||
{
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_OK);
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context, ECHO_SERVER_OPEN_RESULT_OK);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -217,7 +230,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -237,20 +250,20 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR) Stream_Buffer(s),
|
||||
(ULONG) Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
IFCALLRET(echo->context.Response, error, &echo->context,
|
||||
(BYTE*) Stream_Buffer(s), BytesReturned);
|
||||
IFCALLRET(echo->context.Response, error, &echo->context, (BYTE*)Stream_Buffer(s),
|
||||
BytesReturned);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "Response failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -275,7 +288,7 @@ out:
|
||||
*/
|
||||
static UINT echo_server_open(echo_server_context* context)
|
||||
{
|
||||
echo_server* echo = (echo_server*) context;
|
||||
echo_server* echo = (echo_server*)context;
|
||||
|
||||
if (echo->thread == NULL)
|
||||
{
|
||||
@ -285,7 +298,7 @@ static UINT echo_server_open(echo_server_context* context)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(echo->thread = CreateThread(NULL, 0, echo_server_thread_func, (void*) echo, 0, NULL)))
|
||||
if (!(echo->thread = CreateThread(NULL, 0, echo_server_thread_func, (void*)echo, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(echo->stopEvent);
|
||||
@ -305,7 +318,7 @@ static UINT echo_server_open(echo_server_context* context)
|
||||
static UINT echo_server_close(echo_server_context* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
echo_server* echo = (echo_server*) context;
|
||||
echo_server* echo = (echo_server*)context;
|
||||
|
||||
if (echo->thread)
|
||||
{
|
||||
@ -314,7 +327,7 @@ static UINT echo_server_close(echo_server_context* context)
|
||||
if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -327,17 +340,16 @@ static UINT echo_server_close(echo_server_context* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
static BOOL echo_server_request(echo_server_context* context,
|
||||
const BYTE* buffer, UINT32 length)
|
||||
static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length)
|
||||
{
|
||||
echo_server* echo = (echo_server*) context;
|
||||
return WTSVirtualChannelWrite(echo->echo_channel, (PCHAR) buffer, length, NULL);
|
||||
echo_server* echo = (echo_server*)context;
|
||||
return WTSVirtualChannelWrite(echo->echo_channel, (PCHAR)buffer, length, NULL);
|
||||
}
|
||||
|
||||
echo_server_context* echo_server_context_new(HANDLE vcm)
|
||||
{
|
||||
echo_server* echo;
|
||||
echo = (echo_server*) calloc(1, sizeof(echo_server));
|
||||
echo = (echo_server*)calloc(1, sizeof(echo_server));
|
||||
|
||||
if (echo)
|
||||
{
|
||||
@ -349,12 +361,12 @@ echo_server_context* echo_server_context_new(HANDLE vcm)
|
||||
else
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
|
||||
return (echo_server_context*) echo;
|
||||
return (echo_server_context*)echo;
|
||||
}
|
||||
|
||||
void echo_server_context_free(echo_server_context* context)
|
||||
{
|
||||
echo_server* echo = (echo_server*) context;
|
||||
echo_server* echo = (echo_server*)context;
|
||||
echo_server_close(context);
|
||||
free(echo);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -37,20 +37,6 @@
|
||||
|
||||
#define TAG CHANNELS_TAG("encomsp.client")
|
||||
|
||||
struct encomsp_plugin
|
||||
{
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
|
||||
EncomspClientContext* context;
|
||||
|
||||
HANDLE thread;
|
||||
wStream* data_in;
|
||||
void* InitHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessageQueue* queue;
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
typedef struct encomsp_plugin encomspPlugin;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_ENCOMSP_CLIENT_MAIN_H */
|
||||
|
||||
@ -43,7 +43,7 @@ static UINT encomsp_read_header(wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
if (Stream_GetRemainingLength(s) < ENCOMSP_ORDER_HEADER_SIZE)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, header->Type); /* Type (2 bytes) */
|
||||
Stream_Read_UINT16(s, header->Type); /* Type (2 bytes) */
|
||||
Stream_Read_UINT16(s, header->Length); /* Length (2 bytes) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -83,13 +83,14 @@ static int encomsp_read_unicode_string(wStream* s, ENCOMSP_UNICODE_STRING* str)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_recv_change_participant_control_level_pdu(
|
||||
EncomspServerContext* context, wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
static UINT encomsp_recv_change_participant_control_level_pdu(EncomspServerContext* context,
|
||||
wStream* s,
|
||||
ENCOMSP_ORDER_HEADER* header)
|
||||
{
|
||||
int beg, end;
|
||||
ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
beg = ((int) Stream_GetPosition(s)) - ENCOMSP_ORDER_HEADER_SIZE;
|
||||
beg = ((int)Stream_GetPosition(s)) - ENCOMSP_ORDER_HEADER_SIZE;
|
||||
CopyMemory(&pdu, header, sizeof(ENCOMSP_ORDER_HEADER));
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 6)
|
||||
@ -98,9 +99,9 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, pdu.Flags); /* Flags (2 bytes) */
|
||||
Stream_Read_UINT16(s, pdu.Flags); /* Flags (2 bytes) */
|
||||
Stream_Read_UINT32(s, pdu.ParticipantId); /* ParticipantId (4 bytes) */
|
||||
end = (int) Stream_GetPosition(s);
|
||||
end = (int)Stream_GetPosition(s);
|
||||
|
||||
if ((beg + header->Length) < end)
|
||||
{
|
||||
@ -110,7 +111,7 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
||||
|
||||
if ((beg + header->Length) > end)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < ((beg + header->Length) - end))
|
||||
if (Stream_GetRemainingLength(s) < (size_t)((beg + header->Length) - end))
|
||||
{
|
||||
WLog_ERR(TAG, "Not enough data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
@ -122,7 +123,7 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
||||
IFCALLRET(context->ChangeParticipantControlLevel, error, context, &pdu);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->ChangeParticipantControlLevel failed with error %"PRIu32"",
|
||||
WLog_ERR(TAG, "context->ChangeParticipantControlLevel failed with error %" PRIu32 "",
|
||||
error);
|
||||
|
||||
return error;
|
||||
@ -133,8 +134,7 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
||||
wStream* s)
|
||||
static UINT encomsp_server_receive_pdu(EncomspServerContext* context, wStream* s)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ENCOMSP_ORDER_HEADER header;
|
||||
@ -143,21 +143,22 @@ static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
||||
{
|
||||
if ((error = encomsp_read_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "encomsp_read_header failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "encomsp_read_header failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "EncomspReceive: Type: %"PRIu16" Length: %"PRIu16"", header.Type,
|
||||
WLog_INFO(TAG, "EncomspReceive: Type: %" PRIu16 " Length: %" PRIu16 "", header.Type,
|
||||
header.Length);
|
||||
|
||||
switch (header.Type)
|
||||
{
|
||||
case ODTYPE_PARTICIPANT_CTRL_CHANGED:
|
||||
if ((error = encomsp_recv_change_participant_control_level_pdu(context, s,
|
||||
&header)))
|
||||
if ((error =
|
||||
encomsp_recv_change_participant_control_level_pdu(context, s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"encomsp_recv_change_participant_control_level_pdu failed with error %"PRIu32"!",
|
||||
"encomsp_recv_change_participant_control_level_pdu failed with error "
|
||||
"%" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
@ -165,7 +166,7 @@ static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "header.Type unknown %"PRIu16"!", header.Type);
|
||||
WLog_ERR(TAG, "header.Type unknown %" PRIu16 "!", header.Type);
|
||||
return ERROR_INVALID_DATA;
|
||||
break;
|
||||
}
|
||||
@ -186,7 +187,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
EncomspServerContext* context;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
context = (EncomspServerContext*) arg;
|
||||
context = (EncomspServerContext*)arg;
|
||||
|
||||
buffer = NULL;
|
||||
BytesReturned = 0;
|
||||
@ -200,8 +201,8 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle,
|
||||
&buffer, &BytesReturned) == TRUE)
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
@ -220,7 +221,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -229,7 +230,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -250,8 +251,8 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, (PCHAR)Stream_Buffer(s),
|
||||
Stream_Capacity(s), &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
@ -260,7 +261,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
|
||||
if (Stream_GetPosition(s) >= ENCOMSP_ORDER_HEADER_SIZE)
|
||||
{
|
||||
header = (ENCOMSP_ORDER_HEADER*) Stream_Buffer(s);
|
||||
header = (ENCOMSP_ORDER_HEADER*)Stream_Buffer(s);
|
||||
|
||||
if (header->Length >= Stream_GetPosition(s))
|
||||
{
|
||||
@ -269,7 +270,8 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
|
||||
if ((error = encomsp_server_receive_pdu(context, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "encomsp_server_receive_pdu failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "encomsp_server_receive_pdu failed with error %" PRIu32 "!",
|
||||
error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -282,8 +284,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
out:
|
||||
|
||||
if (error && context->rdpcontext)
|
||||
setChannelError(context->rdpcontext, error,
|
||||
"encomsp_server_thread reported an error");
|
||||
setChannelError(context->rdpcontext, error, "encomsp_server_thread reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
@ -296,8 +297,8 @@ out:
|
||||
*/
|
||||
static UINT encomsp_server_start(EncomspServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm,
|
||||
WTS_CURRENT_SESSION, "encomsp");
|
||||
context->priv->ChannelHandle =
|
||||
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "encomsp");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return CHANNEL_RC_BAD_CHANNEL;
|
||||
@ -308,8 +309,8 @@ static UINT encomsp_server_start(EncomspServerContext* context)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(context->priv->Thread = CreateThread(NULL, 0,
|
||||
encomsp_server_thread, (void*) context, 0, NULL)))
|
||||
if (!(context->priv->Thread =
|
||||
CreateThread(NULL, 0, encomsp_server_thread, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
@ -333,7 +334,7 @@ static UINT encomsp_server_stop(EncomspServerContext* context)
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -345,14 +346,14 @@ static UINT encomsp_server_stop(EncomspServerContext* context)
|
||||
EncomspServerContext* encomsp_server_context_new(HANDLE vcm)
|
||||
{
|
||||
EncomspServerContext* context;
|
||||
context = (EncomspServerContext*) calloc(1, sizeof(EncomspServerContext));
|
||||
context = (EncomspServerContext*)calloc(1, sizeof(EncomspServerContext));
|
||||
|
||||
if (context)
|
||||
{
|
||||
context->vcm = vcm;
|
||||
context->Start = encomsp_server_start;
|
||||
context->Stop = encomsp_server_stop;
|
||||
context->priv = (EncomspServerPrivate*) calloc(1, sizeof(EncomspServerPrivate));
|
||||
context->priv = (EncomspServerPrivate*)calloc(1, sizeof(EncomspServerPrivate));
|
||||
|
||||
if (!context->priv)
|
||||
{
|
||||
@ -371,7 +372,7 @@ void encomsp_server_context_free(EncomspServerContext* context)
|
||||
{
|
||||
if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
|
||||
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
|
||||
|
||||
free(context->priv);
|
||||
free(context);
|
||||
}
|
||||
|
||||
@ -25,9 +25,10 @@ include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
if (NOT BUILTIN_CHANNELS OR NOT BUILD_SHARED_LIBS)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/interlocked.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
@ -69,47 +68,30 @@ struct _GEOMETRY_PLUGIN
|
||||
GEOMETRY_LISTENER_CALLBACK* listener_callback;
|
||||
|
||||
GeometryClientContext* context;
|
||||
BOOL initialized;
|
||||
};
|
||||
typedef struct _GEOMETRY_PLUGIN GEOMETRY_PLUGIN;
|
||||
|
||||
|
||||
static UINT32 mappedGeometryHash(UINT64 *g)
|
||||
static UINT32 mappedGeometryHash(UINT64* g)
|
||||
{
|
||||
return (UINT32)((*g >> 32) + (*g & 0xffffffff));
|
||||
}
|
||||
|
||||
static BOOL mappedGeometryKeyCompare(UINT64 *g1, UINT64 *g2)
|
||||
static BOOL mappedGeometryKeyCompare(UINT64* g1, UINT64* g2)
|
||||
{
|
||||
return *g1 == *g2;
|
||||
}
|
||||
|
||||
void mappedGeometryRef(MAPPED_GEOMETRY *g)
|
||||
{
|
||||
InterlockedIncrement(&g->refCounter);
|
||||
}
|
||||
|
||||
void mappedGeometryUnref(MAPPED_GEOMETRY *g)
|
||||
{
|
||||
if (InterlockedDecrement(&g->refCounter))
|
||||
return;
|
||||
|
||||
g->MappedGeometryUpdate = NULL;
|
||||
g->MappedGeometryClear = NULL;
|
||||
g->custom = NULL;
|
||||
free(g->geometry.rects);
|
||||
free(g);
|
||||
}
|
||||
|
||||
|
||||
void freerdp_rgndata_reset(FREERDP_RGNDATA *data)
|
||||
static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
|
||||
{
|
||||
data->nRectCount = 0;
|
||||
}
|
||||
|
||||
static UINT32 geometry_read_RGNDATA(wStream *s, UINT32 len, FREERDP_RGNDATA *rgndata)
|
||||
static UINT32 geometry_read_RGNDATA(wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata)
|
||||
{
|
||||
UINT32 dwSize, iType;
|
||||
INT32 right, bottom;
|
||||
INT32 x, y, w, h;
|
||||
|
||||
if (len < 32)
|
||||
{
|
||||
@ -129,18 +111,26 @@ static UINT32 geometry_read_RGNDATA(wStream *s, UINT32 len, FREERDP_RGNDATA *rgn
|
||||
|
||||
if (iType != RDH_RECTANGLE)
|
||||
{
|
||||
WLog_ERR(TAG, "iType %"PRIu32" for RGNDATA is not supported", iType);
|
||||
WLog_ERR(TAG, "iType %" PRIu32 " for RGNDATA is not supported", iType);
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, rgndata->nRectCount);
|
||||
Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
|
||||
Stream_Read_INT32(s, rgndata->boundingRect.x);
|
||||
Stream_Read_INT32(s, rgndata->boundingRect.y);
|
||||
Stream_Read_INT32(s, x);
|
||||
Stream_Read_INT32(s, y);
|
||||
Stream_Read_INT32(s, right);
|
||||
Stream_Read_INT32(s, bottom);
|
||||
rgndata->boundingRect.width = right - rgndata->boundingRect.x;
|
||||
rgndata->boundingRect.height = bottom - rgndata->boundingRect.y;
|
||||
if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
w = right - x;
|
||||
h = bottom - y;
|
||||
if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
rgndata->boundingRect.x = (INT16)x;
|
||||
rgndata->boundingRect.y = (INT16)y;
|
||||
rgndata->boundingRect.width = (INT16)w;
|
||||
rgndata->boundingRect.height = (INT16)h;
|
||||
len -= 32;
|
||||
|
||||
if (len / (4 * 4) < rgndata->nRectCount)
|
||||
@ -150,24 +140,32 @@ static UINT32 geometry_read_RGNDATA(wStream *s, UINT32 len, FREERDP_RGNDATA *rgn
|
||||
|
||||
if (rgndata->nRectCount)
|
||||
{
|
||||
int i;
|
||||
RDP_RECT *tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
|
||||
UINT32 i;
|
||||
RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
|
||||
|
||||
if (!tmp)
|
||||
{
|
||||
WLog_ERR(TAG, "unable to allocate memory for %"PRIu32" RECTs", rgndata->nRectCount);
|
||||
WLog_ERR(TAG, "unable to allocate memory for %" PRIu32 " RECTs", rgndata->nRectCount);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
rgndata->rects = tmp;
|
||||
|
||||
for (i = 0; i < rgndata->nRectCount; i++)
|
||||
{
|
||||
Stream_Read_INT32(s, rgndata->rects[i].x);
|
||||
Stream_Read_INT32(s, rgndata->rects[i].y);
|
||||
Stream_Read_INT32(s, x);
|
||||
Stream_Read_INT32(s, y);
|
||||
Stream_Read_INT32(s, right);
|
||||
Stream_Read_INT32(s, bottom);
|
||||
rgndata->rects[i].width = right - rgndata->rects[i].x;
|
||||
rgndata->rects[i].height = bottom - rgndata->rects[i].y;
|
||||
if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
w = right - x;
|
||||
h = bottom - y;
|
||||
if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
rgndata->rects[i].x = (INT16)x;
|
||||
rgndata->rects[i].y = (INT16)y;
|
||||
rgndata->rects[i].width = (INT16)w;
|
||||
rgndata->rects[i].height = (INT16)h;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,14 +180,14 @@ static UINT32 geometry_read_RGNDATA(wStream *s, UINT32 len, FREERDP_RGNDATA *rgn
|
||||
static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT32 length, cbGeometryBuffer;
|
||||
MAPPED_GEOMETRY *mappedGeometry;
|
||||
MAPPED_GEOMETRY* mappedGeometry;
|
||||
GEOMETRY_PLUGIN* geometry;
|
||||
GeometryClientContext *context;
|
||||
GeometryClientContext* context;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
UINT32 version, updateType, geometryType;
|
||||
UINT64 id;
|
||||
|
||||
geometry = (GEOMETRY_PLUGIN*) callback->plugin;
|
||||
geometry = (GEOMETRY_PLUGIN*)callback->plugin;
|
||||
context = (GeometryClientContext*)geometry->iface.pInterface;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
@ -213,17 +211,18 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
|
||||
mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
|
||||
|
||||
if (updateType == GEOMETRY_CLEAR )
|
||||
if (updateType == GEOMETRY_CLEAR)
|
||||
{
|
||||
if (!mappedGeometry)
|
||||
{
|
||||
WLog_ERR(TAG, "geometry 0x%"PRIx64" not found here, ignoring clear command", id);
|
||||
WLog_ERR(TAG, "geometry 0x%" PRIx64 " not found here, ignoring clear command", id);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "clearing geometry 0x%"PRIx64"", id);
|
||||
WLog_DBG(TAG, "clearing geometry 0x%" PRIx64 "", id);
|
||||
|
||||
if (mappedGeometry->MappedGeometryClear && !mappedGeometry->MappedGeometryClear(mappedGeometry))
|
||||
if (mappedGeometry->MappedGeometryClear &&
|
||||
!mappedGeometry->MappedGeometryClear(mappedGeometry))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!HashTable_Remove(context->geometries, &id))
|
||||
@ -236,7 +235,7 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
if (!mappedGeometry)
|
||||
{
|
||||
newOne = TRUE;
|
||||
WLog_DBG(TAG, "creating geometry 0x%"PRIx64"", id);
|
||||
WLog_DBG(TAG, "creating geometry 0x%" PRIx64 "", id);
|
||||
mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
|
||||
if (!mappedGeometry)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
@ -244,16 +243,17 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
mappedGeometry->refCounter = 1;
|
||||
mappedGeometry->mappingId = id;
|
||||
|
||||
if (HashTable_Add(context->geometries, &(mappedGeometry->mappingId), mappedGeometry) < 0)
|
||||
if (HashTable_Add(context->geometries, &(mappedGeometry->mappingId), mappedGeometry) <
|
||||
0)
|
||||
{
|
||||
WLog_ERR(TAG, "unable to register geometry 0x%"PRIx64" in the table", id);
|
||||
WLog_ERR(TAG, "unable to register geometry 0x%" PRIx64 " in the table", id);
|
||||
free(mappedGeometry);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_DBG(TAG, "updating geometry 0x%"PRIx64"", id);
|
||||
WLog_DBG(TAG, "updating geometry 0x%" PRIx64 "", id);
|
||||
}
|
||||
|
||||
Stream_Read_UINT64(s, mappedGeometry->topLevelId);
|
||||
@ -290,7 +290,8 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
|
||||
if (newOne)
|
||||
{
|
||||
if (context->MappedGeometryAdded && !context->MappedGeometryAdded(context, mappedGeometry))
|
||||
if (context->MappedGeometryAdded &&
|
||||
!context->MappedGeometryAdded(context, mappedGeometry))
|
||||
{
|
||||
WLog_ERR(TAG, "geometry added callback failed");
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
@ -298,7 +299,8 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mappedGeometry->MappedGeometryUpdate && !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
|
||||
if (mappedGeometry->MappedGeometryUpdate &&
|
||||
!mappedGeometry->MappedGeometryUpdate(mappedGeometry))
|
||||
{
|
||||
WLog_ERR(TAG, "geometry update callback failed");
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
@ -307,11 +309,10 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "unknown updateType=%"PRIu32"", updateType);
|
||||
WLog_ERR(TAG, "unknown updateType=%" PRIu32 "", updateType);
|
||||
ret = CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -322,7 +323,7 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
*/
|
||||
static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
GEOMETRY_CHANNEL_CALLBACK* callback = (GEOMETRY_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
GEOMETRY_CHANNEL_CALLBACK* callback = (GEOMETRY_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
return geometry_recv_pdu(callback, data);
|
||||
}
|
||||
|
||||
@ -343,12 +344,17 @@ static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data,
|
||||
BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
GEOMETRY_CHANNEL_CALLBACK* callback;
|
||||
GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*) pListenerCallback;
|
||||
callback = (GEOMETRY_CHANNEL_CALLBACK*) calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK));
|
||||
GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*)pListenerCallback;
|
||||
|
||||
WINPR_UNUSED(Data);
|
||||
WINPR_UNUSED(pbAccept);
|
||||
|
||||
callback = (GEOMETRY_CHANNEL_CALLBACK*)calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
@ -362,7 +368,7 @@ static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCa
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
listener_callback->channel_callback = callback;
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -374,9 +380,14 @@ static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCa
|
||||
static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
UINT status;
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*) pPlugin;
|
||||
geometry->listener_callback = (GEOMETRY_LISTENER_CALLBACK*) calloc(1,
|
||||
sizeof(GEOMETRY_LISTENER_CALLBACK));
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
|
||||
if (geometry->initialized)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", GEOMETRY_DVC_CHANNEL_NAME);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
geometry->listener_callback =
|
||||
(GEOMETRY_LISTENER_CALLBACK*)calloc(1, sizeof(GEOMETRY_LISTENER_CALLBACK));
|
||||
|
||||
if (!geometry->listener_callback)
|
||||
{
|
||||
@ -387,9 +398,12 @@ static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelMa
|
||||
geometry->listener_callback->iface.OnNewChannelConnection = geometry_on_new_channel_connection;
|
||||
geometry->listener_callback->plugin = pPlugin;
|
||||
geometry->listener_callback->channel_mgr = pChannelMgr;
|
||||
status = pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0,
|
||||
(IWTSListenerCallback*) geometry->listener_callback, &(geometry->listener));
|
||||
status =
|
||||
pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0,
|
||||
&geometry->listener_callback->iface, &(geometry->listener));
|
||||
geometry->listener->pInterface = geometry->iface.pInterface;
|
||||
|
||||
geometry->initialized = status == CHANNEL_RC_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -400,8 +414,15 @@ static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelMa
|
||||
*/
|
||||
static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*) pPlugin;
|
||||
GeometryClientContext* context = (GeometryClientContext *)geometry->iface.pInterface;
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
|
||||
GeometryClientContext* context = (GeometryClientContext*)geometry->iface.pInterface;
|
||||
|
||||
if (geometry && geometry->listener_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = geometry->listener_callback->channel_mgr;
|
||||
if (mgr)
|
||||
IFCALL(mgr->DestroyListener, mgr, geometry->listener);
|
||||
}
|
||||
|
||||
if (context)
|
||||
HashTable_Free(context->geometries);
|
||||
@ -417,9 +438,9 @@ static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
*/
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry geometry_DVCPluginEntry
|
||||
#define DVCPluginEntry geometry_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -432,11 +453,11 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
GEOMETRY_PLUGIN* geometry;
|
||||
GeometryClientContext* context;
|
||||
geometry = (GEOMETRY_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "geometry");
|
||||
geometry = (GEOMETRY_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "geometry");
|
||||
|
||||
if (!geometry)
|
||||
{
|
||||
geometry = (GEOMETRY_PLUGIN*) calloc(1, sizeof(GEOMETRY_PLUGIN));
|
||||
geometry = (GEOMETRY_PLUGIN*)calloc(1, sizeof(GEOMETRY_PLUGIN));
|
||||
|
||||
if (!geometry)
|
||||
{
|
||||
@ -448,7 +469,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
geometry->iface.Connected = NULL;
|
||||
geometry->iface.Disconnected = NULL;
|
||||
geometry->iface.Terminated = geometry_plugin_terminated;
|
||||
context = (GeometryClientContext*) calloc(1, sizeof(GeometryClientContext));
|
||||
context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
|
||||
|
||||
if (!context)
|
||||
{
|
||||
@ -461,10 +482,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
context->geometries->keyCompare = (HASH_TABLE_KEY_COMPARE_FN)mappedGeometryKeyCompare;
|
||||
context->geometries->valueFree = (HASH_TABLE_VALUE_FREE_FN)mappedGeometryUnref;
|
||||
|
||||
context->handle = (void*) geometry;
|
||||
geometry->iface.pInterface = (void*) context;
|
||||
context->handle = (void*)geometry;
|
||||
geometry->iface.pInterface = (void*)context;
|
||||
geometry->context = context;
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*) geometry);
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*)geometry);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -477,5 +498,4 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
error_context:
|
||||
free(geometry);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
}
|
||||
|
||||
@ -29,6 +29,4 @@
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/geometry.h>
|
||||
|
||||
|
||||
#endif /* FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H */
|
||||
|
||||
|
||||
@ -83,16 +83,22 @@ static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
char* path = NULL;
|
||||
int status;
|
||||
WCHAR* ptr;
|
||||
UINT32 PathLength;
|
||||
Stream_Seek(irp->input, 28);
|
||||
if (!Stream_SafeSeek(irp->input, 28))
|
||||
return ERROR_INVALID_DATA;
|
||||
/* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
|
||||
/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
|
||||
if (Stream_GetRemainingLength(irp->input) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input),
|
||||
PathLength / 2, &path, 0, NULL, NULL);
|
||||
ptr = (WCHAR*)Stream_Pointer(irp->input);
|
||||
if (!Stream_SafeSeek(irp->input, PathLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, ptr, PathLength / 2, &path, 0, NULL, NULL);
|
||||
|
||||
if (status < 1)
|
||||
if (!(path = (char*) calloc(1, 1)))
|
||||
if (!(path = (char*)calloc(1, 1)))
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
@ -149,9 +155,11 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
UINT64 Offset;
|
||||
ssize_t status;
|
||||
BYTE* buffer = NULL;
|
||||
if (Stream_GetRemainingLength(irp->input) < 12)
|
||||
return ERROR_INVALID_DATA;
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
buffer = (BYTE*) malloc(Length);
|
||||
buffer = (BYTE*)calloc(Length, sizeof(BYTE));
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
@ -170,6 +178,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
}
|
||||
else
|
||||
{
|
||||
Length = status;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
@ -201,14 +210,22 @@ static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
ssize_t status;
|
||||
void* ptr;
|
||||
if (Stream_GetRemainingLength(irp->input) > 12)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
if (!Stream_SafeSeek(irp->input, 20)) /* Padding */
|
||||
return ERROR_INVALID_DATA;
|
||||
ptr = Stream_Pointer(irp->input);
|
||||
if (!Stream_SafeSeek(irp->input, Length))
|
||||
return ERROR_INVALID_DATA;
|
||||
len = Length;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
status = write(parallel->file, Stream_Pointer(irp->input), len);
|
||||
status = write(parallel->file, ptr, len);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
@ -231,8 +248,7 @@ static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel,
|
||||
IRP* irp)
|
||||
static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
||||
return irp->Complete(irp);
|
||||
@ -252,7 +268,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
case IRP_MJ_CREATE:
|
||||
if ((error = parallel_process_irp_create(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_create failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "parallel_process_irp_create failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -261,7 +277,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
case IRP_MJ_CLOSE:
|
||||
if ((error = parallel_process_irp_close(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_close failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "parallel_process_irp_close failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -270,7 +286,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
case IRP_MJ_READ:
|
||||
if ((error = parallel_process_irp_read(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_read failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "parallel_process_irp_read failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -279,7 +295,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
case IRP_MJ_WRITE:
|
||||
if ((error = parallel_process_irp_write(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_write failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "parallel_process_irp_write failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -288,7 +304,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
if ((error = parallel_process_irp_device_control(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %"PRIu32"!",
|
||||
WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
@ -308,7 +324,7 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
{
|
||||
IRP* irp;
|
||||
wMessage message;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) arg;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
while (1)
|
||||
@ -330,18 +346,17 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
irp = (IRP*) message.wParam;
|
||||
irp = (IRP*)message.wParam;
|
||||
|
||||
if ((error = parallel_process_irp(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "parallel_process_irp failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error && parallel->rdpcontext)
|
||||
setChannelError(parallel->rdpcontext, error,
|
||||
"parallel_thread_func reported an error");
|
||||
setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
@ -354,9 +369,9 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
*/
|
||||
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) device;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
|
||||
|
||||
if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*) irp, NULL))
|
||||
if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -373,13 +388,13 @@ static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
||||
static UINT parallel_free(DEVICE* device)
|
||||
{
|
||||
UINT error;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) device;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
|
||||
|
||||
if (!MessageQueue_PostQuit(parallel->queue, 0)
|
||||
|| (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
||||
if (!MessageQueue_PostQuit(parallel->queue, 0) ||
|
||||
(WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -391,9 +406,9 @@ static UINT parallel_free(DEVICE* device)
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DeviceServiceEntry parallel_DeviceServiceEntry
|
||||
#define DeviceServiceEntry parallel_DeviceServiceEntry
|
||||
#else
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -410,7 +425,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
RDPDR_PARALLEL* device;
|
||||
PARALLEL_DEVICE* parallel;
|
||||
UINT error;
|
||||
device = (RDPDR_PARALLEL*) pEntryPoints->device;
|
||||
device = (RDPDR_PARALLEL*)pEntryPoints->device;
|
||||
name = device->Name;
|
||||
path = device->Path;
|
||||
|
||||
@ -422,7 +437,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
|
||||
if (name[0] && path[0])
|
||||
{
|
||||
parallel = (PARALLEL_DEVICE*) calloc(1, sizeof(PARALLEL_DEVICE));
|
||||
parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
|
||||
|
||||
if (!parallel)
|
||||
{
|
||||
@ -458,15 +473,14 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
||||
(DEVICE*) parallel)))
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)parallel)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(parallel->thread = CreateThread(NULL, 0,
|
||||
parallel_thread_func, (void*) parallel, 0, NULL)))
|
||||
if (!(parallel->thread =
|
||||
CreateThread(NULL, 0, parallel_thread_func, (void*)parallel, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
@ -18,39 +18,23 @@
|
||||
define_channel_client("printer")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
printer_main.c
|
||||
printer_main.h)
|
||||
|
||||
if(WITH_CUPS)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
printer_cups.c
|
||||
printer_cups.h)
|
||||
|
||||
include_directories(${CUPS_INCLUDE_DIR})
|
||||
add_definitions(-DWITH_CUPS)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT UWP)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
printer_win.c
|
||||
printer_win.h)
|
||||
endif()
|
||||
printer_main.c)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
if(WITH_CUPS)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${CUPS_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
if(WITH_CUPS)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "cups" "")
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT UWP)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "win" "")
|
||||
endif()
|
||||
|
||||
31
channels/printer/client/cups/CMakeLists.txt
Normal file
31
channels/printer/client/cups/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2019 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("printer" "cups" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
printer_cups.c)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${CUPS_INCLUDE_DIR})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${CUPS_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
410
channels/printer/client/cups/printer_cups.c
Normal file
410
channels/printer/client/cups/printer_cups.c
Normal file
@ -0,0 +1,410 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Print Virtual Channel - CUPS driver
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <cups/cups.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
#include <freerdp/client/printer.h>
|
||||
|
||||
typedef struct rdp_cups_printer_driver rdpCupsPrinterDriver;
|
||||
typedef struct rdp_cups_printer rdpCupsPrinter;
|
||||
typedef struct rdp_cups_print_job rdpCupsPrintJob;
|
||||
|
||||
struct rdp_cups_printer_driver
|
||||
{
|
||||
rdpPrinterDriver driver;
|
||||
|
||||
int id_sequence;
|
||||
size_t references;
|
||||
};
|
||||
|
||||
struct rdp_cups_printer
|
||||
{
|
||||
rdpPrinter printer;
|
||||
|
||||
rdpCupsPrintJob* printjob;
|
||||
};
|
||||
|
||||
struct rdp_cups_print_job
|
||||
{
|
||||
rdpPrintJob printjob;
|
||||
|
||||
void* printjob_object;
|
||||
int printjob_id;
|
||||
};
|
||||
|
||||
static void printer_cups_get_printjob_name(char* buf, size_t size, size_t id)
|
||||
{
|
||||
time_t tt;
|
||||
struct tm tres;
|
||||
struct tm* t;
|
||||
|
||||
tt = time(NULL);
|
||||
t = localtime_r(&tt, &tres);
|
||||
sprintf_s(buf, size - 1, "FreeRDP Print %04d-%02d-%02d %02d-%02d-%02d - Job %" PRIdz,
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_cups_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob;
|
||||
|
||||
#ifndef _CUPS_API_1_4
|
||||
|
||||
{
|
||||
FILE* fp;
|
||||
|
||||
fp = winpr_fopen((const char*)cups_printjob->printjob_object, "a+b");
|
||||
|
||||
if (!fp)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (fwrite(data, 1, size, fp) < size)
|
||||
{
|
||||
fclose(fp);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
// FIXME once this function doesn't return void anymore!
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
cupsWriteRequestData((http_t*)cups_printjob->printjob_object, (const char*)data, size);
|
||||
|
||||
#endif
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_cups_close_printjob(rdpPrintJob* printjob)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob;
|
||||
|
||||
#ifndef _CUPS_API_1_4
|
||||
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
printer_cups_get_printjob_name(buf, sizeof(buf), printjob->id);
|
||||
|
||||
if (cupsPrintFile(printjob->printer->name, (const char*)cups_printjob->printjob_object, buf,
|
||||
0, NULL) == 0)
|
||||
{
|
||||
}
|
||||
|
||||
unlink(cups_printjob->printjob_object);
|
||||
free(cups_printjob->printjob_object);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
cupsFinishDocument((http_t*)cups_printjob->printjob_object, printjob->printer->name);
|
||||
cups_printjob->printjob_id = 0;
|
||||
httpClose((http_t*)cups_printjob->printjob_object);
|
||||
|
||||
#endif
|
||||
|
||||
((rdpCupsPrinter*)printjob->printer)->printjob = NULL;
|
||||
free(cups_printjob);
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
|
||||
rdpCupsPrintJob* cups_printjob;
|
||||
|
||||
if (cups_printer->printjob != NULL)
|
||||
return NULL;
|
||||
|
||||
cups_printjob = (rdpCupsPrintJob*)calloc(1, sizeof(rdpCupsPrintJob));
|
||||
if (!cups_printjob)
|
||||
return NULL;
|
||||
|
||||
cups_printjob->printjob.id = id;
|
||||
cups_printjob->printjob.printer = printer;
|
||||
|
||||
cups_printjob->printjob.Write = printer_cups_write_printjob;
|
||||
cups_printjob->printjob.Close = printer_cups_close_printjob;
|
||||
|
||||
#ifndef _CUPS_API_1_4
|
||||
|
||||
cups_printjob->printjob_object = _strdup(tmpnam(NULL));
|
||||
if (!cups_printjob->printjob_object)
|
||||
{
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
#if !defined(_CUPS_API_1_7)
|
||||
cups_printjob->printjob_object =
|
||||
httpConnectEncrypt(cupsServer(), ippPort(), HTTP_ENCRYPT_IF_REQUESTED);
|
||||
#else
|
||||
cups_printjob->printjob_object = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
|
||||
HTTP_ENCRYPT_IF_REQUESTED, 1, 10000, NULL);
|
||||
#endif
|
||||
if (!cups_printjob->printjob_object)
|
||||
{
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printer_cups_get_printjob_name(buf, sizeof(buf), cups_printjob->printjob.id);
|
||||
|
||||
cups_printjob->printjob_id =
|
||||
cupsCreateJob((http_t*)cups_printjob->printjob_object, printer->name, buf, 0, NULL);
|
||||
|
||||
if (!cups_printjob->printjob_id)
|
||||
{
|
||||
httpClose((http_t*)cups_printjob->printjob_object);
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cupsStartDocument((http_t*)cups_printjob->printjob_object, printer->name,
|
||||
cups_printjob->printjob_id, buf, CUPS_FORMAT_AUTO, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
cups_printer->printjob = cups_printjob;
|
||||
|
||||
return (rdpPrintJob*)cups_printjob;
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_cups_find_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
|
||||
|
||||
if (cups_printer->printjob == NULL)
|
||||
return NULL;
|
||||
if (cups_printer->printjob->printjob.id != id)
|
||||
return NULL;
|
||||
|
||||
return (rdpPrintJob*)cups_printer->printjob;
|
||||
}
|
||||
|
||||
static void printer_cups_free_printer(rdpPrinter* printer)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
|
||||
|
||||
if (cups_printer->printjob)
|
||||
cups_printer->printjob->printjob.Close((rdpPrintJob*)cups_printer->printjob);
|
||||
|
||||
if (printer->backend)
|
||||
printer->backend->ReleaseRef(printer->backend);
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static void printer_cups_add_ref_printer(rdpPrinter* printer)
|
||||
{
|
||||
if (printer)
|
||||
printer->references++;
|
||||
}
|
||||
|
||||
static void printer_cups_release_ref_printer(rdpPrinter* printer)
|
||||
{
|
||||
if (!printer)
|
||||
return;
|
||||
if (printer->references <= 1)
|
||||
printer_cups_free_printer(printer);
|
||||
else
|
||||
printer->references--;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_cups_new_printer(rdpCupsPrinterDriver* cups_driver, const char* name,
|
||||
const char* driverName, BOOL is_default)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer;
|
||||
|
||||
cups_printer = (rdpCupsPrinter*)calloc(1, sizeof(rdpCupsPrinter));
|
||||
if (!cups_printer)
|
||||
return NULL;
|
||||
|
||||
cups_printer->printer.backend = &cups_driver->driver;
|
||||
|
||||
cups_printer->printer.id = cups_driver->id_sequence++;
|
||||
cups_printer->printer.name = _strdup(name);
|
||||
if (!cups_printer->printer.name)
|
||||
{
|
||||
free(cups_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (driverName)
|
||||
cups_printer->printer.driver = _strdup(driverName);
|
||||
else
|
||||
cups_printer->printer.driver = _strdup("MS Publisher Imagesetter");
|
||||
if (!cups_printer->printer.driver)
|
||||
{
|
||||
free(cups_printer->printer.name);
|
||||
free(cups_printer);
|
||||
return NULL;
|
||||
}
|
||||
cups_printer->printer.is_default = is_default;
|
||||
|
||||
cups_printer->printer.CreatePrintJob = printer_cups_create_printjob;
|
||||
cups_printer->printer.FindPrintJob = printer_cups_find_printjob;
|
||||
cups_printer->printer.AddRef = printer_cups_add_ref_printer;
|
||||
cups_printer->printer.ReleaseRef = printer_cups_release_ref_printer;
|
||||
|
||||
cups_printer->printer.AddRef(&cups_printer->printer);
|
||||
cups_printer->printer.backend->AddRef(cups_printer->printer.backend);
|
||||
return &cups_printer->printer;
|
||||
}
|
||||
|
||||
static void printer_cups_release_enum_printers(rdpPrinter** printers)
|
||||
{
|
||||
rdpPrinter** cur = printers;
|
||||
|
||||
while ((cur != NULL) && ((*cur) != NULL))
|
||||
{
|
||||
if ((*cur)->ReleaseRef)
|
||||
(*cur)->ReleaseRef(*cur);
|
||||
cur++;
|
||||
}
|
||||
free(printers);
|
||||
}
|
||||
|
||||
static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpPrinter** printers;
|
||||
int num_printers;
|
||||
cups_dest_t* dests;
|
||||
cups_dest_t* dest;
|
||||
int num_dests;
|
||||
int i;
|
||||
|
||||
num_dests = cupsGetDests(&dests);
|
||||
printers = (rdpPrinter**)calloc(num_dests + 1, sizeof(rdpPrinter*));
|
||||
if (!printers)
|
||||
return NULL;
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
for (i = 0, dest = dests; i < num_dests; i++, dest++)
|
||||
{
|
||||
if (dest->instance == NULL)
|
||||
{
|
||||
rdpPrinter* current = printer_cups_new_printer((rdpCupsPrinterDriver*)driver,
|
||||
dest->name, NULL, dest->is_default);
|
||||
if (!current)
|
||||
{
|
||||
printer_cups_release_enum_printers(printers);
|
||||
printers = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
printers[num_printers++] = current;
|
||||
}
|
||||
}
|
||||
cupsFreeDests(num_dests, dests);
|
||||
|
||||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_cups_get_printer(rdpPrinterDriver* driver, const char* name,
|
||||
const char* driverName)
|
||||
{
|
||||
rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver;
|
||||
|
||||
return printer_cups_new_printer(cups_driver, name, driverName,
|
||||
cups_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static void printer_cups_add_ref_driver(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver;
|
||||
if (cups_driver)
|
||||
cups_driver->references++;
|
||||
}
|
||||
|
||||
/* Singleton */
|
||||
static rdpCupsPrinterDriver* uniq_cups_driver = NULL;
|
||||
|
||||
static void printer_cups_release_ref_driver(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver;
|
||||
if (cups_driver->references <= 1)
|
||||
{
|
||||
if (uniq_cups_driver == cups_driver)
|
||||
uniq_cups_driver = NULL;
|
||||
free(cups_driver);
|
||||
cups_driver = NULL;
|
||||
}
|
||||
else
|
||||
cups_driver->references--;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
rdpPrinterDriver* cups_freerdp_printer_client_subsystem_entry(void)
|
||||
#else
|
||||
FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
|
||||
#endif
|
||||
{
|
||||
if (!uniq_cups_driver)
|
||||
{
|
||||
uniq_cups_driver = (rdpCupsPrinterDriver*)calloc(1, sizeof(rdpCupsPrinterDriver));
|
||||
|
||||
if (!uniq_cups_driver)
|
||||
return NULL;
|
||||
|
||||
uniq_cups_driver->driver.EnumPrinters = printer_cups_enum_printers;
|
||||
uniq_cups_driver->driver.ReleaseEnumPrinters = printer_cups_release_enum_printers;
|
||||
uniq_cups_driver->driver.GetPrinter = printer_cups_get_printer;
|
||||
|
||||
uniq_cups_driver->driver.AddRef = printer_cups_add_ref_driver;
|
||||
uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver;
|
||||
|
||||
uniq_cups_driver->id_sequence = 1;
|
||||
}
|
||||
uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver);
|
||||
|
||||
return &uniq_cups_driver->driver;
|
||||
}
|
||||
@ -1,331 +0,0 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Print Virtual Channel - CUPS driver
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <cups/cups.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
#include "printer_main.h"
|
||||
|
||||
#include "printer_cups.h"
|
||||
|
||||
typedef struct rdp_cups_printer_driver rdpCupsPrinterDriver;
|
||||
typedef struct rdp_cups_printer rdpCupsPrinter;
|
||||
typedef struct rdp_cups_print_job rdpCupsPrintJob;
|
||||
|
||||
struct rdp_cups_printer_driver
|
||||
{
|
||||
rdpPrinterDriver driver;
|
||||
|
||||
int id_sequence;
|
||||
};
|
||||
|
||||
struct rdp_cups_printer
|
||||
{
|
||||
rdpPrinter printer;
|
||||
|
||||
rdpCupsPrintJob* printjob;
|
||||
};
|
||||
|
||||
struct rdp_cups_print_job
|
||||
{
|
||||
rdpPrintJob printjob;
|
||||
|
||||
void* printjob_object;
|
||||
int printjob_id;
|
||||
};
|
||||
|
||||
static void printer_cups_get_printjob_name(char* buf, int size)
|
||||
{
|
||||
time_t tt;
|
||||
struct tm* t;
|
||||
|
||||
tt = time(NULL);
|
||||
t = localtime(&tt);
|
||||
sprintf_s(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_cups_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*) printjob;
|
||||
|
||||
#ifndef _CUPS_API_1_4
|
||||
|
||||
{
|
||||
FILE* fp;
|
||||
|
||||
fp = fopen((const char*) cups_printjob->printjob_object, "a+b");
|
||||
|
||||
if (!fp)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (fwrite(data, 1, size, fp) < size)
|
||||
{
|
||||
fclose(fp);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
// FIXME once this function doesn't return void anymore!
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
cupsWriteRequestData((http_t*) cups_printjob->printjob_object, (const char*) data, size);
|
||||
|
||||
#endif
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_cups_close_printjob(rdpPrintJob* printjob)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*) printjob;
|
||||
|
||||
#ifndef _CUPS_API_1_4
|
||||
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
printer_cups_get_printjob_name(buf, sizeof(buf));
|
||||
|
||||
if (cupsPrintFile(printjob->printer->name, (const char*) cups_printjob->printjob_object, buf, 0, NULL) == 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unlink(cups_printjob->printjob_object);
|
||||
free(cups_printjob->printjob_object);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
cupsFinishDocument((http_t*) cups_printjob->printjob_object, printjob->printer->name);
|
||||
cups_printjob->printjob_id = 0;
|
||||
httpClose((http_t*) cups_printjob->printjob_object);
|
||||
|
||||
#endif
|
||||
|
||||
((rdpCupsPrinter*) printjob->printer)->printjob = NULL;
|
||||
free(cups_printjob) ;
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*) printer;
|
||||
rdpCupsPrintJob* cups_printjob;
|
||||
|
||||
if (cups_printer->printjob != NULL)
|
||||
return NULL;
|
||||
|
||||
cups_printjob = (rdpCupsPrintJob*) calloc(1, sizeof(rdpCupsPrintJob));
|
||||
if (!cups_printjob)
|
||||
return NULL;
|
||||
|
||||
cups_printjob->printjob.id = id;
|
||||
cups_printjob->printjob.printer = printer;
|
||||
|
||||
cups_printjob->printjob.Write = printer_cups_write_printjob;
|
||||
cups_printjob->printjob.Close = printer_cups_close_printjob;
|
||||
|
||||
#ifndef _CUPS_API_1_4
|
||||
|
||||
cups_printjob->printjob_object = _strdup(tmpnam(NULL));
|
||||
if (!cups_printjob->printjob_object)
|
||||
{
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
cups_printjob->printjob_object = httpConnectEncrypt(cupsServer(), ippPort(), HTTP_ENCRYPT_IF_REQUESTED);
|
||||
|
||||
if (!cups_printjob->printjob_object)
|
||||
{
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printer_cups_get_printjob_name(buf, sizeof(buf));
|
||||
|
||||
cups_printjob->printjob_id = cupsCreateJob((http_t*) cups_printjob->printjob_object,
|
||||
printer->name, buf, 0, NULL);
|
||||
|
||||
if (!cups_printjob->printjob_id)
|
||||
{
|
||||
httpClose((http_t*) cups_printjob->printjob_object);
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cupsStartDocument((http_t*) cups_printjob->printjob_object,
|
||||
printer->name, cups_printjob->printjob_id, buf, CUPS_FORMAT_AUTO, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
cups_printer->printjob = cups_printjob;
|
||||
|
||||
return (rdpPrintJob*)cups_printjob;
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_cups_find_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
|
||||
|
||||
if (cups_printer->printjob == NULL)
|
||||
return NULL;
|
||||
if (cups_printer->printjob->printjob.id != id)
|
||||
return NULL;
|
||||
|
||||
return (rdpPrintJob*)cups_printer->printjob;
|
||||
}
|
||||
|
||||
static void printer_cups_free_printer(rdpPrinter* printer)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*) printer;
|
||||
|
||||
if (cups_printer->printjob)
|
||||
cups_printer->printjob->printjob.Close((rdpPrintJob*) cups_printer->printjob);
|
||||
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_cups_new_printer(rdpCupsPrinterDriver* cups_driver,
|
||||
const char* name, const char* driverName, BOOL is_default)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer;
|
||||
|
||||
cups_printer = (rdpCupsPrinter*) calloc(1, sizeof(rdpCupsPrinter));
|
||||
if (!cups_printer)
|
||||
return NULL;
|
||||
|
||||
cups_printer->printer.id = cups_driver->id_sequence++;
|
||||
cups_printer->printer.name = _strdup(name);
|
||||
if (!cups_printer->printer.name)
|
||||
{
|
||||
free(cups_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (driverName)
|
||||
cups_printer->printer.driver = _strdup(driverName);
|
||||
else
|
||||
cups_printer->printer.driver = _strdup("MS Publisher Imagesetter");
|
||||
if (!cups_printer->printer.driver)
|
||||
{
|
||||
free(cups_printer->printer.name);
|
||||
free(cups_printer);
|
||||
return NULL;
|
||||
}
|
||||
cups_printer->printer.is_default = is_default;
|
||||
|
||||
cups_printer->printer.CreatePrintJob = printer_cups_create_printjob;
|
||||
cups_printer->printer.FindPrintJob = printer_cups_find_printjob;
|
||||
cups_printer->printer.Free = printer_cups_free_printer;
|
||||
|
||||
return (rdpPrinter*) cups_printer;
|
||||
}
|
||||
|
||||
static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpPrinter** printers;
|
||||
int num_printers;
|
||||
cups_dest_t *dests;
|
||||
cups_dest_t *dest;
|
||||
int num_dests;
|
||||
int i;
|
||||
|
||||
num_dests = cupsGetDests(&dests);
|
||||
printers = (rdpPrinter**) calloc(num_dests + 1, sizeof(rdpPrinter*));
|
||||
if (!printers)
|
||||
return NULL;
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
for (i = 0, dest = dests; i < num_dests; i++, dest++)
|
||||
{
|
||||
if (dest->instance == NULL)
|
||||
{
|
||||
printers[num_printers++] = printer_cups_new_printer((rdpCupsPrinterDriver*) driver,
|
||||
dest->name, NULL, dest->is_default);
|
||||
}
|
||||
}
|
||||
cupsFreeDests(num_dests, dests);
|
||||
|
||||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_cups_get_printer(rdpPrinterDriver* driver,
|
||||
const char* name, const char* driverName)
|
||||
{
|
||||
rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*) driver;
|
||||
|
||||
return printer_cups_new_printer(cups_driver, name, driverName,
|
||||
cups_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static rdpCupsPrinterDriver* cups_driver = NULL;
|
||||
|
||||
rdpPrinterDriver* printer_cups_get_driver(void)
|
||||
{
|
||||
if (cups_driver == NULL)
|
||||
{
|
||||
cups_driver = (rdpCupsPrinterDriver*) calloc(1, sizeof(rdpCupsPrinterDriver));
|
||||
|
||||
if (!cups_driver)
|
||||
return NULL;
|
||||
|
||||
cups_driver->driver.EnumPrinters = printer_cups_enum_printers;
|
||||
cups_driver->driver.GetPrinter = printer_cups_get_printer;
|
||||
|
||||
cups_driver->id_sequence = 1;
|
||||
}
|
||||
|
||||
return (rdpPrinterDriver*) cups_driver;
|
||||
}
|
||||
|
||||
@ -42,15 +42,7 @@
|
||||
|
||||
#include "../printer.h"
|
||||
|
||||
#ifdef WITH_CUPS
|
||||
#include "printer_cups.h"
|
||||
#endif
|
||||
|
||||
#include "printer_main.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(_UWP)
|
||||
#include "printer_win.h"
|
||||
#endif
|
||||
#include <freerdp/client/printer.h>
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
@ -79,26 +71,20 @@ typedef enum
|
||||
PRN_CONF_PNP = 1,
|
||||
PRN_CONF_DRIVER = 2,
|
||||
PRN_CONF_DATA = 3
|
||||
}
|
||||
prn_conf_t;
|
||||
} prn_conf_t;
|
||||
|
||||
static const char* filemap[] =
|
||||
{
|
||||
"PortDosName",
|
||||
"PnPName",
|
||||
"DriverName",
|
||||
"CachedPrinterConfigData"
|
||||
};
|
||||
static const char* filemap[] = { "PortDosName", "PnPName", "DriverName",
|
||||
"CachedPrinterConfigData" };
|
||||
|
||||
static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* name, size_t length)
|
||||
{
|
||||
char* dir = GetCombinedPath(settings->ConfigPath, "printers");
|
||||
char* bname = crypto_base64_encode((const BYTE*) name, (int)length);
|
||||
char* bname = crypto_base64_encode((const BYTE*)name, (int)length);
|
||||
char* config = GetCombinedPath(dir, bname);
|
||||
|
||||
if (config && !PathFileExistsA(config))
|
||||
if (config && !winpr_PathFileExists(config))
|
||||
{
|
||||
if (!PathMakePathA(config, NULL))
|
||||
if (!winpr_PathMakePath(config, NULL))
|
||||
{
|
||||
free(config);
|
||||
config = NULL;
|
||||
@ -121,11 +107,10 @@ static BOOL printer_write_setting(const char* path, prn_conf_t type, const void*
|
||||
const char* name = filemap[type];
|
||||
char* abs = GetCombinedPath(path, name);
|
||||
|
||||
if (!abs)
|
||||
if (!abs || (length > INT32_MAX))
|
||||
return FALSE;
|
||||
|
||||
file = CreateFileA(abs, GENERIC_WRITE, 0,
|
||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
file = CreateFileA(abs, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
free(abs);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
@ -138,7 +123,9 @@ static BOOL printer_write_setting(const char* path, prn_conf_t type, const void*
|
||||
if (!base64)
|
||||
goto fail;
|
||||
|
||||
b64len = strlen(base64);
|
||||
/* base64 char represents 6bit -> 4*(n/3) is the length which is
|
||||
* always smaller than 2*n */
|
||||
b64len = strnlen(base64, 2 * length);
|
||||
rc = WriteFile(file, base64, b64len, &written, NULL);
|
||||
|
||||
if (b64len != written)
|
||||
@ -158,27 +145,26 @@ static BOOL printer_config_valid(const char* path)
|
||||
if (!path)
|
||||
return FALSE;
|
||||
|
||||
if (!PathFileExistsA(path))
|
||||
if (!winpr_PathFileExists(path))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL printer_read_setting(const char* path, prn_conf_t type, void** data, size_t* length)
|
||||
static BOOL printer_read_setting(const char* path, prn_conf_t type, void** data, UINT32* length)
|
||||
{
|
||||
DWORD lowSize, highSize;
|
||||
DWORD read = 0;
|
||||
BOOL rc = FALSE;
|
||||
HANDLE file;
|
||||
BYTE* fdata = NULL;
|
||||
char* fdata = NULL;
|
||||
const char* name = filemap[type];
|
||||
char* abs = GetCombinedPath(path, name);
|
||||
|
||||
if (!abs)
|
||||
return FALSE;
|
||||
|
||||
file = CreateFileA(abs, GENERIC_READ, 0,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
file = CreateFileA(abs, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
free(abs);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
@ -205,13 +191,13 @@ static BOOL printer_read_setting(const char* path, prn_conf_t type, void** data,
|
||||
fail:
|
||||
CloseHandle(file);
|
||||
|
||||
if (rc)
|
||||
if (rc && (lowSize <= INT_MAX))
|
||||
{
|
||||
int blen = 0;
|
||||
crypto_base64_decode(fdata, lowSize, data, &blen);
|
||||
crypto_base64_decode(fdata, (int)lowSize, (BYTE**)data, &blen);
|
||||
|
||||
if (*data)
|
||||
*length = blen;
|
||||
if (*data && (blen > 0))
|
||||
*length = (UINT32)blen;
|
||||
else
|
||||
{
|
||||
rc = FALSE;
|
||||
@ -228,9 +214,8 @@ fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL printer_save_to_config(const rdpSettings* settings,
|
||||
const char* PortDosName, size_t PortDosNameLen,
|
||||
const WCHAR* PnPName, size_t PnPNameLen,
|
||||
static BOOL printer_save_to_config(const rdpSettings* settings, const char* PortDosName,
|
||||
size_t PortDosNameLen, const WCHAR* PnPName, size_t PnPNameLen,
|
||||
const WCHAR* DriverName, size_t DriverNameLen,
|
||||
const WCHAR* PrinterName, size_t PrintNameLen,
|
||||
const BYTE* CachedPrinterConfigData, size_t CacheFieldsLen)
|
||||
@ -276,7 +261,7 @@ static BOOL printer_remove_config(const rdpSettings* settings, const WCHAR* name
|
||||
if (!printer_config_valid(path))
|
||||
goto fail;
|
||||
|
||||
rc = RemoveDirectoryA(path);
|
||||
rc = winpr_RemoveDirectory(path);
|
||||
fail:
|
||||
free(path);
|
||||
return rc;
|
||||
@ -290,7 +275,7 @@ static BOOL printer_move_config(const rdpSettings* settings, const WCHAR* oldNam
|
||||
char* newPath = get_printer_config_path(settings, newName, newLength);
|
||||
|
||||
if (printer_config_valid(oldPath))
|
||||
rc = MoveFileA(oldPath, newPath);
|
||||
rc = winpr_MoveFile(oldPath, newPath);
|
||||
|
||||
free(oldPath);
|
||||
free(newPath);
|
||||
@ -306,13 +291,13 @@ static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* pr
|
||||
char* path = NULL;
|
||||
int rc;
|
||||
UINT32 flags = 0;
|
||||
WCHAR* DriverName = NULL;
|
||||
size_t DriverNameLen = 0;
|
||||
WCHAR* PnPName = NULL;
|
||||
size_t PnPNameLen = 0;
|
||||
BYTE* CachedPrinterConfigData = NULL;
|
||||
size_t CachedFieldsLen = 0;
|
||||
size_t PrinterNameLen = 0;
|
||||
void* DriverName = NULL;
|
||||
UINT32 DriverNameLen = 0;
|
||||
void* PnPName = NULL;
|
||||
UINT32 PnPNameLen = 0;
|
||||
void* CachedPrinterConfigData = NULL;
|
||||
UINT32 CachedFieldsLen = 0;
|
||||
UINT32 PrinterNameLen = 0;
|
||||
|
||||
if (!settings || !printer)
|
||||
return FALSE;
|
||||
@ -338,8 +323,8 @@ static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* pr
|
||||
|
||||
if (!printer_read_setting(path, PRN_CONF_DRIVER, &DriverName, &DriverNameLen))
|
||||
{
|
||||
DriverNameLen = ConvertToUnicode(CP_UTF8, 0, printer->driver, -1, &DriverName,
|
||||
0) * 2 + 1;
|
||||
DriverNameLen =
|
||||
ConvertToUnicode(CP_UTF8, 0, printer->driver, -1, (LPWSTR*)&DriverName, 0) * 2 + 1;
|
||||
}
|
||||
|
||||
if (!printer_read_setting(path, PRN_CONF_DATA, &CachedPrinterConfigData, &CachedFieldsLen))
|
||||
@ -352,7 +337,7 @@ static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* pr
|
||||
goto fail;
|
||||
|
||||
Stream_Write_UINT32(printer_dev->device.data, flags);
|
||||
Stream_Write_UINT32(printer_dev->device.data, 0); /* CodePage, reserved */
|
||||
Stream_Write_UINT32(printer_dev->device.data, 0); /* CodePage, reserved */
|
||||
Stream_Write_UINT32(printer_dev->device.data, PnPNameLen); /* PnPNameLen */
|
||||
Stream_Write_UINT32(printer_dev->device.data, DriverNameLen);
|
||||
Stream_Write_UINT32(printer_dev->device.data, PrinterNameLen);
|
||||
@ -441,8 +426,8 @@ static UINT printer_process_irp_create(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
rdpPrintJob* printjob = NULL;
|
||||
|
||||
if (printer_dev->printer)
|
||||
printjob = printer_dev->printer->CreatePrintJob(printer_dev->printer,
|
||||
irp->devman->id_sequence++);
|
||||
printjob =
|
||||
printer_dev->printer->CreatePrintJob(printer_dev->printer, irp->devman->id_sequence++);
|
||||
|
||||
if (printjob)
|
||||
{
|
||||
@ -467,8 +452,7 @@ static UINT printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
rdpPrintJob* printjob = NULL;
|
||||
|
||||
if (printer_dev->printer)
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer,
|
||||
irp->FileId);
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
|
||||
|
||||
if (!printjob)
|
||||
{
|
||||
@ -494,13 +478,18 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
UINT64 Offset;
|
||||
rdpPrintJob* printjob = NULL;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
void* ptr;
|
||||
|
||||
if (Stream_GetRemainingLength(irp->input) < 32)
|
||||
return ERROR_INVALID_DATA;
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
|
||||
ptr = Stream_Pointer(irp->input);
|
||||
if (!Stream_SafeSeek(irp->input, Length))
|
||||
return ERROR_INVALID_DATA;
|
||||
if (printer_dev->printer)
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer,
|
||||
irp->FileId);
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
|
||||
|
||||
if (!printjob)
|
||||
{
|
||||
@ -509,12 +498,12 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
}
|
||||
else
|
||||
{
|
||||
error = printjob->Write(printjob, Stream_Pointer(irp->input), Length);
|
||||
error = printjob->Write(printjob, ptr, Length);
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "printjob->Write failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "printjob->Write failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -528,8 +517,7 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_process_irp_device_control(PRINTER_DEVICE* printer_dev,
|
||||
IRP* irp)
|
||||
static UINT printer_process_irp_device_control(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
{
|
||||
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
||||
return irp->Complete(irp);
|
||||
@ -549,7 +537,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
case IRP_MJ_CREATE:
|
||||
if ((error = printer_process_irp_create(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_create failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "printer_process_irp_create failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -558,7 +546,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
case IRP_MJ_CLOSE:
|
||||
if ((error = printer_process_irp_close(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_close failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "printer_process_irp_close failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -567,7 +555,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
case IRP_MJ_WRITE:
|
||||
if ((error = printer_process_irp_write(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_write failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "printer_process_irp_write failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -576,7 +564,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
if ((error = printer_process_irp_device_control(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_device_control failed with error %"PRIu32"!",
|
||||
WLog_ERR(TAG, "printer_process_irp_device_control failed with error %" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
@ -595,8 +583,8 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||
{
|
||||
IRP* irp;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) arg;
|
||||
HANDLE obj[] = {printer_dev->event, printer_dev->stopEvent};
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)arg;
|
||||
HANDLE obj[] = { printer_dev->event, printer_dev->stopEvent };
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
while (1)
|
||||
@ -606,7 +594,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||
if (rc == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -616,7 +604,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||
continue;
|
||||
|
||||
ResetEvent(printer_dev->event);
|
||||
irp = (IRP*) InterlockedPopEntrySList(printer_dev->pIrpList);
|
||||
irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList);
|
||||
|
||||
if (irp == NULL)
|
||||
{
|
||||
@ -627,14 +615,13 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||
|
||||
if ((error = printer_process_irp(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "printer_process_irp failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error && printer_dev->rdpcontext)
|
||||
setChannelError(printer_dev->rdpcontext, error,
|
||||
"printer_thread_func reported an error");
|
||||
setChannelError(printer_dev->rdpcontext, error, "printer_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
@ -647,7 +634,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||
*/
|
||||
static UINT printer_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
|
||||
InterlockedPushEntrySList(printer_dev->pIrpList, &(irp->ItemEntry));
|
||||
SetEvent(printer_dev->event);
|
||||
return CHANNEL_RC_OK;
|
||||
@ -656,7 +643,7 @@ static UINT printer_irp_request(DEVICE* device, IRP* irp)
|
||||
static UINT printer_custom_component(DEVICE* device, UINT16 component, UINT16 packetId, wStream* s)
|
||||
{
|
||||
UINT32 eventID;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
|
||||
const rdpSettings* settings = printer_dev->rdpcontext->settings;
|
||||
|
||||
if (component != RDPDR_CTYP_PRN)
|
||||
@ -673,156 +660,156 @@ static UINT printer_custom_component(DEVICE* device, UINT16 component, UINT16 pa
|
||||
switch (eventID)
|
||||
{
|
||||
case RDPDR_ADD_PRINTER_EVENT:
|
||||
{
|
||||
char PortDosName[8];
|
||||
UINT32 PnPNameLen, DriverNameLen, PrintNameLen, CacheFieldsLen;
|
||||
const WCHAR* PnPName, *DriverName, *PrinterName;
|
||||
const BYTE* CachedPrinterConfigData;
|
||||
{
|
||||
char PortDosName[8];
|
||||
UINT32 PnPNameLen, DriverNameLen, PrintNameLen, CacheFieldsLen;
|
||||
const WCHAR *PnPName, *DriverName, *PrinterName;
|
||||
const BYTE* CachedPrinterConfigData;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 24)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < 24)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read(s, PortDosName, sizeof(PortDosName));
|
||||
Stream_Read_UINT32(s, PnPNameLen);
|
||||
Stream_Read_UINT32(s, DriverNameLen);
|
||||
Stream_Read_UINT32(s, PrintNameLen);
|
||||
Stream_Read_UINT32(s, CacheFieldsLen);
|
||||
Stream_Read(s, PortDosName, sizeof(PortDosName));
|
||||
Stream_Read_UINT32(s, PnPNameLen);
|
||||
Stream_Read_UINT32(s, DriverNameLen);
|
||||
Stream_Read_UINT32(s, PrintNameLen);
|
||||
Stream_Read_UINT32(s, CacheFieldsLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < PnPNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < PnPNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
PnPName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PnPNameLen);
|
||||
PnPName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PnPNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < DriverNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < DriverNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
DriverName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, DriverNameLen);
|
||||
DriverName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, DriverNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < PrintNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < PrintNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PrintNameLen);
|
||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PrintNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < CacheFieldsLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < CacheFieldsLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
CachedPrinterConfigData = Stream_Pointer(s);
|
||||
Stream_Seek(s, CacheFieldsLen);
|
||||
CachedPrinterConfigData = Stream_Pointer(s);
|
||||
Stream_Seek(s, CacheFieldsLen);
|
||||
|
||||
if (!printer_save_to_config(settings,
|
||||
PortDosName, sizeof(PortDosName),
|
||||
PnPName, PnPNameLen,
|
||||
DriverName, DriverNameLen,
|
||||
PrinterName, PrintNameLen,
|
||||
CachedPrinterConfigData, CacheFieldsLen))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
if (!printer_save_to_config(settings, PortDosName, sizeof(PortDosName), PnPName,
|
||||
PnPNameLen, DriverName, DriverNameLen, PrinterName,
|
||||
PrintNameLen, CachedPrinterConfigData,
|
||||
CacheFieldsLen))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case RDPDR_UPDATE_PRINTER_EVENT:
|
||||
{
|
||||
UINT32 PrinterNameLen, ConfigDataLen;
|
||||
const WCHAR* PrinterName;
|
||||
const BYTE* ConfigData;
|
||||
{
|
||||
UINT32 PrinterNameLen, ConfigDataLen;
|
||||
const WCHAR* PrinterName;
|
||||
const BYTE* ConfigData;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, PrinterNameLen);
|
||||
Stream_Read_UINT32(s, ConfigDataLen);
|
||||
Stream_Read_UINT32(s, PrinterNameLen);
|
||||
Stream_Read_UINT32(s, ConfigDataLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PrinterNameLen);
|
||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PrinterNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < ConfigDataLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < ConfigDataLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
ConfigData = Stream_Pointer(s);
|
||||
Stream_Seek(s, ConfigDataLen);
|
||||
ConfigData = Stream_Pointer(s);
|
||||
Stream_Seek(s, ConfigDataLen);
|
||||
|
||||
if (!printer_update_to_config(settings, PrinterName, PrinterNameLen, ConfigData, ConfigDataLen))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
if (!printer_update_to_config(settings, PrinterName, PrinterNameLen, ConfigData,
|
||||
ConfigDataLen))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case RDPDR_DELETE_PRINTER_EVENT:
|
||||
{
|
||||
UINT32 PrinterNameLen;
|
||||
const WCHAR* PrinterName;
|
||||
{
|
||||
UINT32 PrinterNameLen;
|
||||
const WCHAR* PrinterName;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, PrinterNameLen);
|
||||
Stream_Read_UINT32(s, PrinterNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PrinterNameLen);
|
||||
printer_remove_config(settings, PrinterName, PrinterNameLen);
|
||||
}
|
||||
break;
|
||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, PrinterNameLen);
|
||||
printer_remove_config(settings, PrinterName, PrinterNameLen);
|
||||
}
|
||||
break;
|
||||
|
||||
case RDPDR_RENAME_PRINTER_EVENT:
|
||||
{
|
||||
UINT32 OldPrinterNameLen, NewPrinterNameLen;
|
||||
const WCHAR* OldPrinterName;
|
||||
const WCHAR* NewPrinterName;
|
||||
{
|
||||
UINT32 OldPrinterNameLen, NewPrinterNameLen;
|
||||
const WCHAR* OldPrinterName;
|
||||
const WCHAR* NewPrinterName;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, OldPrinterNameLen);
|
||||
Stream_Read_UINT32(s, NewPrinterNameLen);
|
||||
Stream_Read_UINT32(s, OldPrinterNameLen);
|
||||
Stream_Read_UINT32(s, NewPrinterNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < OldPrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < OldPrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
OldPrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, OldPrinterNameLen);
|
||||
OldPrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, OldPrinterNameLen);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < NewPrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < NewPrinterNameLen)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
NewPrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, NewPrinterNameLen);
|
||||
NewPrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||
Stream_Seek(s, NewPrinterNameLen);
|
||||
|
||||
if (!printer_move_config(settings, OldPrinterName, OldPrinterNameLen, NewPrinterName,
|
||||
NewPrinterNameLen))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
if (!printer_move_config(settings, OldPrinterName, OldPrinterNameLen,
|
||||
NewPrinterName, NewPrinterNameLen))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "Unknown cache data eventID: 0x%08"PRIX32"", eventID);
|
||||
WLog_ERR(TAG, "Unknown cache data eventID: 0x%08" PRIX32 "", eventID);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PAKID_PRN_USING_XPS:
|
||||
{
|
||||
UINT32 flags;
|
||||
{
|
||||
UINT32 flags;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, flags);
|
||||
WLog_ERR(TAG,
|
||||
"Ignoring unhandled message PAKID_PRN_USING_XPS [printerID=%08"PRIx32", flags=%08"PRIx32"]",
|
||||
eventID, flags);
|
||||
}
|
||||
break;
|
||||
Stream_Read_UINT32(s, flags);
|
||||
WLog_ERR(TAG,
|
||||
"Ignoring unhandled message PAKID_PRN_USING_XPS [printerID=%08" PRIx32
|
||||
", flags=%08" PRIx32 "]",
|
||||
eventID, flags);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "Unknown printing component packetID: 0x%04"PRIX16"", packetId);
|
||||
WLog_ERR(TAG, "Unknown printing component packetID: 0x%04" PRIX16 "", packetId);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -837,14 +824,14 @@ static UINT printer_custom_component(DEVICE* device, UINT16 component, UINT16 pa
|
||||
static UINT printer_free(DEVICE* device)
|
||||
{
|
||||
IRP* irp;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
|
||||
UINT error;
|
||||
SetEvent(printer_dev->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(printer_dev->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
|
||||
/* The analyzer is confused by this premature return value.
|
||||
* Since this case can not be handled gracefully silence the
|
||||
@ -854,7 +841,7 @@ static UINT printer_free(DEVICE* device)
|
||||
#endif
|
||||
}
|
||||
|
||||
while ((irp = (IRP*) InterlockedPopEntrySList(printer_dev->pIrpList)) != NULL)
|
||||
while ((irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList)) != NULL)
|
||||
irp->Discard(irp);
|
||||
|
||||
CloseHandle(printer_dev->thread);
|
||||
@ -863,7 +850,7 @@ static UINT printer_free(DEVICE* device)
|
||||
_aligned_free(printer_dev->pIrpList);
|
||||
|
||||
if (printer_dev->printer)
|
||||
printer_dev->printer->Free(printer_dev->printer);
|
||||
printer_dev->printer->ReleaseRef(printer_dev->printer);
|
||||
|
||||
Stream_Free(printer_dev->device.data, TRUE);
|
||||
free(printer_dev);
|
||||
@ -875,12 +862,11 @@ static UINT printer_free(DEVICE* device)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
rdpPrinter* printer)
|
||||
static UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* printer)
|
||||
{
|
||||
PRINTER_DEVICE* printer_dev;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
printer_dev = (PRINTER_DEVICE*) calloc(1, sizeof(PRINTER_DEVICE));
|
||||
printer_dev = (PRINTER_DEVICE*)calloc(1, sizeof(PRINTER_DEVICE));
|
||||
|
||||
if (!printer_dev)
|
||||
{
|
||||
@ -893,7 +879,7 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
if (!printer_dev->device.data)
|
||||
goto error_out;
|
||||
|
||||
sprintf_s(printer_dev->port, sizeof(printer_dev->port), "PRN%d", printer->id);
|
||||
sprintf_s(printer_dev->port, sizeof(printer_dev->port), "PRN%" PRIdz, printer->id);
|
||||
printer_dev->device.type = RDPDR_DTYP_PRINT;
|
||||
printer_dev->device.name = printer_dev->port;
|
||||
printer_dev->device.IRPRequest = printer_irp_request;
|
||||
@ -901,8 +887,8 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
printer_dev->device.Free = printer_free;
|
||||
printer_dev->rdpcontext = pEntryPoints->rdpcontext;
|
||||
printer_dev->printer = printer;
|
||||
printer_dev->pIrpList = (WINPR_PSLIST_HEADER) _aligned_malloc(sizeof(
|
||||
WINPR_SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
printer_dev->pIrpList = (WINPR_PSLIST_HEADER)_aligned_malloc(sizeof(WINPR_SLIST_HEADER),
|
||||
MEMORY_ALLOCATION_ALIGNMENT);
|
||||
|
||||
if (!printer_dev->pIrpList)
|
||||
{
|
||||
@ -930,102 +916,158 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
||||
(DEVICE*) printer_dev)))
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)printer_dev)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(printer_dev->thread = CreateThread(NULL, 0, printer_thread_func, (void*) printer_dev, 0,
|
||||
NULL)))
|
||||
if (!(printer_dev->thread =
|
||||
CreateThread(NULL, 0, printer_thread_func, (void*)printer_dev, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
printer->AddRef(printer);
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
printer_free(&printer_dev->device);
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DeviceServiceEntry printer_DeviceServiceEntry
|
||||
#else
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#endif
|
||||
static rdpPrinterDriver* printer_load_backend(const char* backend)
|
||||
{
|
||||
typedef rdpPrinterDriver* (*backend_load_t)(void);
|
||||
union {
|
||||
PVIRTUALCHANNELENTRY entry;
|
||||
backend_load_t backend;
|
||||
} fktconv;
|
||||
|
||||
fktconv.entry = freerdp_load_channel_addin_entry("printer", backend, NULL, 0);
|
||||
if (!fktconv.entry)
|
||||
return NULL;
|
||||
|
||||
return fktconv.backend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
UINT
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
printer_DeviceServiceEntry
|
||||
#else
|
||||
FREERDP_API
|
||||
DeviceServiceEntry
|
||||
#endif
|
||||
(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
int i;
|
||||
char* name;
|
||||
char* driver_name;
|
||||
rdpPrinter* printer;
|
||||
rdpPrinter** printers;
|
||||
RDPDR_PRINTER* device;
|
||||
BOOL default_backend = TRUE;
|
||||
RDPDR_PRINTER* device = NULL;
|
||||
rdpPrinterDriver* driver = NULL;
|
||||
UINT error;
|
||||
#ifdef WITH_CUPS
|
||||
driver = printer_cups_get_driver();
|
||||
#endif
|
||||
#if defined(_WIN32) && !defined(_UWP)
|
||||
driver = printer_win_get_driver();
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!pEntryPoints || !pEntryPoints->device)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
device = (RDPDR_PRINTER*)pEntryPoints->device;
|
||||
name = device->Name;
|
||||
driver_name = _strdup(device->DriverName);
|
||||
|
||||
/* Secondary argument is one of the following:
|
||||
*
|
||||
* <driver_name> ... name of a printer driver
|
||||
* <driver_name>:<backend_name> ... name of a printer driver and local printer backend to use
|
||||
*/
|
||||
if (driver_name)
|
||||
{
|
||||
char* sep = strstr(driver_name, ":");
|
||||
if (sep)
|
||||
{
|
||||
const char* backend = sep + 1;
|
||||
*sep = '\0';
|
||||
driver = printer_load_backend(backend);
|
||||
default_backend = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!driver && default_backend)
|
||||
{
|
||||
const char* backend =
|
||||
#if defined(WITH_CUPS)
|
||||
"cups"
|
||||
#elif defined(_WIN32)
|
||||
"win"
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
;
|
||||
|
||||
driver = printer_load_backend(backend);
|
||||
}
|
||||
|
||||
if (!driver)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not get a printer driver!");
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
device = (RDPDR_PRINTER*) pEntryPoints->device;
|
||||
name = device->Name;
|
||||
driver_name = device->DriverName;
|
||||
|
||||
if (name && name[0])
|
||||
{
|
||||
printer = driver->GetPrinter(driver, name, driver_name);
|
||||
rdpPrinter* printer = driver->GetPrinter(driver, name, driver_name);
|
||||
|
||||
if (!printer)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not get printer %s!", name);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!printer_save_default_config(pEntryPoints->rdpcontext->settings, printer))
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
{
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
printer->ReleaseRef(printer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((error = printer_register(pEntryPoints, printer)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_register failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
WLog_ERR(TAG, "printer_register failed with error %" PRIu32 "!", error);
|
||||
printer->ReleaseRef(printer);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printers = driver->EnumPrinters(driver);
|
||||
rdpPrinter** printers = driver->EnumPrinters(driver);
|
||||
rdpPrinter** current = printers;
|
||||
|
||||
for (i = 0; printers[i]; i++)
|
||||
for (i = 0; current[i]; i++)
|
||||
{
|
||||
printer = printers[i];
|
||||
rdpPrinter* printer = current[i];
|
||||
|
||||
if ((error = printer_register(pEntryPoints, printer)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_register failed with error %"PRIu32"!", error);
|
||||
free(printers);
|
||||
return error;
|
||||
WLog_ERR(TAG, "printer_register failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(printers);
|
||||
driver->ReleaseEnumPrinters(printers);
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
fail:
|
||||
free(driver_name);
|
||||
if (driver)
|
||||
driver->ReleaseRef(driver);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1,341 +0,0 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Print Virtual Channel - WIN driver
|
||||
*
|
||||
* Copyright 2012 Gerald Richter
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <winspool.h>
|
||||
|
||||
#include "printer_main.h"
|
||||
|
||||
#include "printer_win.h"
|
||||
|
||||
typedef struct rdp_win_printer_driver rdpWinPrinterDriver;
|
||||
typedef struct rdp_win_printer rdpWinPrinter;
|
||||
typedef struct rdp_win_print_job rdpWinPrintJob;
|
||||
|
||||
struct rdp_win_printer_driver
|
||||
{
|
||||
rdpPrinterDriver driver;
|
||||
|
||||
int id_sequence;
|
||||
};
|
||||
|
||||
struct rdp_win_printer
|
||||
{
|
||||
rdpPrinter printer;
|
||||
HANDLE hPrinter;
|
||||
rdpWinPrintJob* printjob;
|
||||
};
|
||||
|
||||
struct rdp_win_print_job
|
||||
{
|
||||
rdpPrintJob printjob;
|
||||
DOC_INFO_1 di;
|
||||
DWORD handle;
|
||||
|
||||
void* printjob_object;
|
||||
int printjob_id;
|
||||
};
|
||||
|
||||
static void printer_win_get_printjob_name(char* buf, int size)
|
||||
{
|
||||
time_t tt;
|
||||
struct tm* t;
|
||||
|
||||
tt = time(NULL);
|
||||
t = localtime(&tt);
|
||||
sprintf_s(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_win_write_printjob(rdpPrintJob* printjob, BYTE* data, int size)
|
||||
{
|
||||
rdpWinPrintJob* win_printjob = (rdpWinPrintJob*) printjob;
|
||||
|
||||
LPVOID pBuf = data;
|
||||
DWORD cbBuf = size;
|
||||
DWORD pcWritten;
|
||||
|
||||
if(!WritePrinter(((rdpWinPrinter*)printjob->printer)->hPrinter, pBuf, cbBuf, &pcWritten))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_win_close_printjob(rdpPrintJob* printjob)
|
||||
{
|
||||
rdpWinPrintJob* win_printjob = (rdpWinPrintJob*) printjob;
|
||||
|
||||
if (!EndPagePrinter(((rdpWinPrinter*) printjob->printer)->hPrinter))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (!ClosePrinter(((rdpWinPrinter*) printjob->printer)->hPrinter))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
((rdpWinPrinter*) printjob->printer)->printjob = NULL;
|
||||
|
||||
free(win_printjob);
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
rdpWinPrintJob* win_printjob;
|
||||
|
||||
if (win_printer->printjob != NULL)
|
||||
return NULL;
|
||||
|
||||
win_printjob = (rdpWinPrintJob*) calloc(1, sizeof(rdpWinPrintJob));
|
||||
if (!win_printjob)
|
||||
return NULL;
|
||||
|
||||
win_printjob->printjob.id = id;
|
||||
win_printjob->printjob.printer = printer;
|
||||
win_printjob->di.pDocName = L"FREERDPjob";
|
||||
win_printjob->di.pDatatype= NULL;
|
||||
win_printjob->di.pOutputFile = NULL;
|
||||
|
||||
win_printjob->handle = StartDocPrinter(win_printer->hPrinter, 1, (LPBYTE) &(win_printjob->di));
|
||||
|
||||
if (!win_printjob->handle)
|
||||
{
|
||||
free(win_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!StartPagePrinter(win_printer->hPrinter))
|
||||
{
|
||||
free(win_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win_printjob->printjob.Write = printer_win_write_printjob;
|
||||
win_printjob->printjob.Close = printer_win_close_printjob;
|
||||
|
||||
win_printer->printjob = win_printjob;
|
||||
|
||||
return (rdpPrintJob*) win_printjob;
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_win_find_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*) printer;
|
||||
|
||||
if (!win_printer->printjob)
|
||||
return NULL;
|
||||
|
||||
if (win_printer->printjob->printjob.id != id)
|
||||
return NULL;
|
||||
|
||||
return (rdpPrintJob*) win_printer->printjob;
|
||||
}
|
||||
|
||||
static void printer_win_free_printer(rdpPrinter* printer)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*) printer;
|
||||
|
||||
if (win_printer->printjob)
|
||||
win_printer->printjob->printjob.Close((rdpPrintJob*) win_printer->printjob);
|
||||
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver,
|
||||
const WCHAR* name, const WCHAR* drivername, BOOL is_default)
|
||||
{
|
||||
rdpWinPrinter* win_printer;
|
||||
DWORD needed = 0;
|
||||
int status;
|
||||
PRINTER_INFO_2 *prninfo=NULL;
|
||||
|
||||
win_printer = (rdpWinPrinter*) calloc(1, sizeof(rdpWinPrinter));
|
||||
if (!win_printer)
|
||||
return NULL;
|
||||
|
||||
win_printer->printer.id = win_driver->id_sequence++;
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, name, -1, &win_printer->printer.name, 0, NULL, NULL) < 1)
|
||||
{
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!win_printer->printer.name)
|
||||
{
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
win_printer->printer.is_default = is_default;
|
||||
|
||||
win_printer->printer.CreatePrintJob = printer_win_create_printjob;
|
||||
win_printer->printer.FindPrintJob = printer_win_find_printjob;
|
||||
win_printer->printer.Free = printer_win_free_printer;
|
||||
|
||||
if (!OpenPrinter(name, &(win_printer->hPrinter), NULL))
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* How many memory should be allocated for printer data */
|
||||
GetPrinter(win_printer->hPrinter, 2, (LPBYTE) prninfo, 0, &needed);
|
||||
if (needed == 0)
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prninfo = (PRINTER_INFO_2*) GlobalAlloc(GPTR,needed);
|
||||
if (!prninfo)
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!GetPrinter(win_printer->hPrinter, 2, (LPBYTE) prninfo, needed, &needed))
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (drivername)
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, drivername, -1, &win_printer->printer.driver, 0, NULL, NULL);
|
||||
else
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, prninfo->pDriverName, -1, &win_printer->printer.driver, 0, NULL, NULL);
|
||||
if (!win_printer->printer.driver || (status <= 0))
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (rdpPrinter*)win_printer;
|
||||
}
|
||||
|
||||
static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpPrinter** printers;
|
||||
int num_printers;
|
||||
int i;
|
||||
PRINTER_INFO_2* prninfo = NULL;
|
||||
DWORD needed, returned;
|
||||
|
||||
/* find required size for the buffer */
|
||||
EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed, &returned);
|
||||
|
||||
|
||||
/* allocate array of PRINTER_INFO structures */
|
||||
prninfo = (PRINTER_INFO_2*) GlobalAlloc(GPTR,needed);
|
||||
if (!prninfo)
|
||||
return NULL;
|
||||
|
||||
/* call again */
|
||||
if (!EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, (LPBYTE) prninfo, needed, &needed, &returned))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
printers = (rdpPrinter**) calloc((returned + 1), sizeof(rdpPrinter*));
|
||||
if (!printers)
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
for (i = 0; i < (int) returned; i++)
|
||||
{
|
||||
printers[num_printers++] = printer_win_new_printer((rdpWinPrinterDriver*)driver,
|
||||
prninfo[i].pPrinterName, prninfo[i].pDriverName, 0);
|
||||
}
|
||||
|
||||
GlobalFree(prninfo);
|
||||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver,
|
||||
const char* name, const char* driverName)
|
||||
{
|
||||
WCHAR* driverNameW = NULL;
|
||||
rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
|
||||
rdpPrinter *myPrinter = NULL;
|
||||
|
||||
if (driverName)
|
||||
{
|
||||
ConvertToUnicode(CP_UTF8, 0, driverName, -1, &driverNameW, 0);
|
||||
if (!driverNameW)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
myPrinter = printer_win_new_printer(win_driver, name, driverNameW,
|
||||
win_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
free(driverNameW);
|
||||
|
||||
return myPrinter;
|
||||
}
|
||||
|
||||
static rdpWinPrinterDriver* win_driver = NULL;
|
||||
|
||||
rdpPrinterDriver* printer_win_get_driver(void)
|
||||
{
|
||||
if (!win_driver)
|
||||
{
|
||||
win_driver = (rdpWinPrinterDriver*) calloc(1, sizeof(rdpWinPrinterDriver));
|
||||
if (!win_driver)
|
||||
return NULL;
|
||||
|
||||
win_driver->driver.EnumPrinters = printer_win_enum_printers;
|
||||
win_driver->driver.GetPrinter = printer_win_get_printer;
|
||||
|
||||
win_driver->id_sequence = 1;
|
||||
}
|
||||
|
||||
return (rdpPrinterDriver*) win_driver;
|
||||
}
|
||||
|
||||
29
channels/printer/client/win/CMakeLists.txt
Normal file
29
channels/printer/client/win/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2019 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("printer" "win" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
printer_win.c)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
459
channels/printer/client/win/printer_win.c
Normal file
459
channels/printer/client/win/printer_win.c
Normal file
@ -0,0 +1,459 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Print Virtual Channel - WIN driver
|
||||
*
|
||||
* Copyright 2012 Gerald Richter
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <winspool.h>
|
||||
|
||||
#include <freerdp/client/printer.h>
|
||||
|
||||
#define PRINTER_TAG CHANNELS_TAG("printer.client")
|
||||
#ifdef WITH_DEBUG_WINPR
|
||||
#define DEBUG_WINPR(...) WLog_DBG(PRINTER_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_WINPR(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef struct rdp_win_printer_driver rdpWinPrinterDriver;
|
||||
typedef struct rdp_win_printer rdpWinPrinter;
|
||||
typedef struct rdp_win_print_job rdpWinPrintJob;
|
||||
|
||||
struct rdp_win_printer_driver
|
||||
{
|
||||
rdpPrinterDriver driver;
|
||||
|
||||
size_t id_sequence;
|
||||
size_t references;
|
||||
};
|
||||
|
||||
struct rdp_win_printer
|
||||
{
|
||||
rdpPrinter printer;
|
||||
HANDLE hPrinter;
|
||||
rdpWinPrintJob* printjob;
|
||||
};
|
||||
|
||||
struct rdp_win_print_job
|
||||
{
|
||||
rdpPrintJob printjob;
|
||||
DOC_INFO_1 di;
|
||||
DWORD handle;
|
||||
|
||||
void* printjob_object;
|
||||
int printjob_id;
|
||||
};
|
||||
|
||||
static WCHAR* printer_win_get_printjob_name(size_t id)
|
||||
{
|
||||
time_t tt;
|
||||
struct tm tres;
|
||||
struct tm* t;
|
||||
WCHAR* str;
|
||||
size_t len = 1024;
|
||||
int rc;
|
||||
|
||||
tt = time(NULL);
|
||||
t = localtime_s(&tt, &tres);
|
||||
|
||||
str = calloc(len, sizeof(WCHAR));
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
rc = swprintf_s(str, len, L"FreeRDP Print %04d-%02d-%02d% 02d-%02d-%02d - Job %lu\0",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
|
||||
id);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_win_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
|
||||
{
|
||||
rdpWinPrinter* printer;
|
||||
LPCVOID pBuf = data;
|
||||
DWORD cbBuf = size;
|
||||
DWORD pcWritten;
|
||||
|
||||
if (!printjob || !data)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
printer = (rdpWinPrinter*)printjob->printer;
|
||||
if (!printer)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
if (!WritePrinter(printer->hPrinter, pBuf, cbBuf, &pcWritten))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_win_close_printjob(rdpPrintJob* printjob)
|
||||
{
|
||||
rdpWinPrintJob* win_printjob = (rdpWinPrintJob*)printjob;
|
||||
rdpWinPrinter* win_printer;
|
||||
|
||||
if (!printjob)
|
||||
return;
|
||||
|
||||
win_printer = (rdpWinPrinter*)printjob->printer;
|
||||
if (!win_printer)
|
||||
return;
|
||||
|
||||
if (!EndPagePrinter(win_printer->hPrinter))
|
||||
{
|
||||
}
|
||||
|
||||
if (!ClosePrinter(win_printer->hPrinter))
|
||||
{
|
||||
}
|
||||
|
||||
win_printer->printjob = NULL;
|
||||
|
||||
free(win_printjob->di.pDocName);
|
||||
free(win_printjob);
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
rdpWinPrintJob* win_printjob;
|
||||
|
||||
if (win_printer->printjob != NULL)
|
||||
return NULL;
|
||||
|
||||
win_printjob = (rdpWinPrintJob*)calloc(1, sizeof(rdpWinPrintJob));
|
||||
if (!win_printjob)
|
||||
return NULL;
|
||||
|
||||
win_printjob->printjob.id = id;
|
||||
win_printjob->printjob.printer = printer;
|
||||
win_printjob->di.pDocName = printer_win_get_printjob_name(id);
|
||||
win_printjob->di.pDatatype = NULL;
|
||||
win_printjob->di.pOutputFile = NULL;
|
||||
|
||||
win_printjob->handle = StartDocPrinter(win_printer->hPrinter, 1, (LPBYTE) & (win_printjob->di));
|
||||
|
||||
if (!win_printjob->handle)
|
||||
{
|
||||
free(win_printjob->di.pDocName);
|
||||
free(win_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!StartPagePrinter(win_printer->hPrinter))
|
||||
{
|
||||
free(win_printjob->di.pDocName);
|
||||
free(win_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win_printjob->printjob.Write = printer_win_write_printjob;
|
||||
win_printjob->printjob.Close = printer_win_close_printjob;
|
||||
|
||||
win_printer->printjob = win_printjob;
|
||||
|
||||
return &win_printjob->printjob;
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_win_find_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
|
||||
if (!win_printer->printjob)
|
||||
return NULL;
|
||||
|
||||
if (win_printer->printjob->printjob.id != id)
|
||||
return NULL;
|
||||
|
||||
return (rdpPrintJob*)win_printer->printjob;
|
||||
}
|
||||
|
||||
static void printer_win_free_printer(rdpPrinter* printer)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
|
||||
if (win_printer->printjob)
|
||||
win_printer->printjob->printjob.Close((rdpPrintJob*)win_printer->printjob);
|
||||
|
||||
if (printer->backend)
|
||||
printer->backend->ReleaseRef(printer->backend);
|
||||
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static void printer_win_add_ref_printer(rdpPrinter* printer)
|
||||
{
|
||||
if (printer)
|
||||
printer->references++;
|
||||
}
|
||||
|
||||
static void printer_win_release_ref_printer(rdpPrinter* printer)
|
||||
{
|
||||
if (!printer)
|
||||
return;
|
||||
if (printer->references <= 1)
|
||||
printer_win_free_printer(printer);
|
||||
else
|
||||
printer->references--;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, const WCHAR* name,
|
||||
const WCHAR* drivername, BOOL is_default)
|
||||
{
|
||||
rdpWinPrinter* win_printer;
|
||||
DWORD needed = 0;
|
||||
int status;
|
||||
PRINTER_INFO_2* prninfo = NULL;
|
||||
|
||||
win_printer = (rdpWinPrinter*)calloc(1, sizeof(rdpWinPrinter));
|
||||
if (!win_printer)
|
||||
return NULL;
|
||||
|
||||
win_printer->printer.backend = &win_driver->driver;
|
||||
win_printer->printer.id = win_driver->id_sequence++;
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, name, -1, &win_printer->printer.name, 0, NULL, NULL) < 1)
|
||||
{
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!win_printer->printer.name)
|
||||
{
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
win_printer->printer.is_default = is_default;
|
||||
|
||||
win_printer->printer.CreatePrintJob = printer_win_create_printjob;
|
||||
win_printer->printer.FindPrintJob = printer_win_find_printjob;
|
||||
win_printer->printer.AddRef = printer_win_add_ref_printer;
|
||||
win_printer->printer.ReleaseRef = printer_win_release_ref_printer;
|
||||
|
||||
if (!OpenPrinter(name, &(win_printer->hPrinter), NULL))
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* How many memory should be allocated for printer data */
|
||||
GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, 0, &needed);
|
||||
if (needed == 0)
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
|
||||
if (!prninfo)
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, needed, &needed))
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (drivername)
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, drivername, -1, &win_printer->printer.driver, 0,
|
||||
NULL, NULL);
|
||||
else
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, prninfo->pDriverName, -1,
|
||||
&win_printer->printer.driver, 0, NULL, NULL);
|
||||
if (!win_printer->printer.driver || (status <= 0))
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win_printer->printer.AddRef(&win_printer->printer);
|
||||
win_printer->printer.backend->AddRef(win_printer->printer.backend);
|
||||
return &win_printer->printer;
|
||||
}
|
||||
|
||||
static void printer_win_release_enum_printers(rdpPrinter** printers)
|
||||
{
|
||||
rdpPrinter** cur = printers;
|
||||
|
||||
while ((cur != NULL) && ((*cur) != NULL))
|
||||
{
|
||||
if ((*cur)->ReleaseRef)
|
||||
(*cur)->ReleaseRef(*cur);
|
||||
cur++;
|
||||
}
|
||||
free(printers);
|
||||
}
|
||||
|
||||
static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpPrinter** printers;
|
||||
int num_printers;
|
||||
int i;
|
||||
PRINTER_INFO_2* prninfo = NULL;
|
||||
DWORD needed, returned;
|
||||
|
||||
/* find required size for the buffer */
|
||||
EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed,
|
||||
&returned);
|
||||
|
||||
/* allocate array of PRINTER_INFO structures */
|
||||
prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
|
||||
if (!prninfo)
|
||||
return NULL;
|
||||
|
||||
/* call again */
|
||||
if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, (LPBYTE)prninfo,
|
||||
needed, &needed, &returned))
|
||||
{
|
||||
}
|
||||
|
||||
printers = (rdpPrinter**)calloc((returned + 1), sizeof(rdpPrinter*));
|
||||
if (!printers)
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
for (i = 0; i < (int)returned; i++)
|
||||
{
|
||||
rdpPrinter* current = printers[num_printers];
|
||||
current = printer_win_new_printer((rdpWinPrinterDriver*)driver, prninfo[i].pPrinterName,
|
||||
prninfo[i].pDriverName, 0);
|
||||
if (!current)
|
||||
{
|
||||
printer_win_release_enum_printers(printers);
|
||||
printers = NULL;
|
||||
break;
|
||||
}
|
||||
printers[num_printers++] = current;
|
||||
}
|
||||
|
||||
GlobalFree(prninfo);
|
||||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver, const char* name,
|
||||
const char* driverName)
|
||||
{
|
||||
WCHAR* driverNameW = NULL;
|
||||
WCHAR* nameW = NULL;
|
||||
rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
|
||||
rdpPrinter* myPrinter = NULL;
|
||||
|
||||
if (name)
|
||||
{
|
||||
ConvertToUnicode(CP_UTF8, 0, name, -1, &nameW, 0);
|
||||
if (!driverNameW)
|
||||
return NULL;
|
||||
}
|
||||
if (driverName)
|
||||
{
|
||||
ConvertToUnicode(CP_UTF8, 0, driverName, -1, &driverNameW, 0);
|
||||
if (!driverNameW)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
myPrinter = printer_win_new_printer(win_driver, nameW, driverNameW,
|
||||
win_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
free(driverNameW);
|
||||
free(nameW);
|
||||
|
||||
return myPrinter;
|
||||
}
|
||||
|
||||
static void printer_win_add_ref_driver(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
|
||||
if (win)
|
||||
win->references++;
|
||||
}
|
||||
|
||||
/* Singleton */
|
||||
static rdpWinPrinterDriver* win_driver = NULL;
|
||||
|
||||
static void printer_win_release_ref_driver(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
|
||||
if (win->references <= 1)
|
||||
{
|
||||
free(win);
|
||||
win_driver = NULL;
|
||||
}
|
||||
else
|
||||
win->references--;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
rdpPrinterDriver* win_freerdp_printer_client_subsystem_entry(void)
|
||||
#else
|
||||
FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
|
||||
#endif
|
||||
{
|
||||
if (!win_driver)
|
||||
{
|
||||
win_driver = (rdpWinPrinterDriver*)calloc(1, sizeof(rdpWinPrinterDriver));
|
||||
|
||||
if (!win_driver)
|
||||
return NULL;
|
||||
|
||||
win_driver->driver.EnumPrinters = printer_win_enum_printers;
|
||||
win_driver->driver.ReleaseEnumPrinters = printer_win_release_enum_printers;
|
||||
win_driver->driver.GetPrinter = printer_win_get_printer;
|
||||
|
||||
win_driver->driver.AddRef = printer_win_add_ref_driver;
|
||||
win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
|
||||
|
||||
win_driver->id_sequence = 1;
|
||||
}
|
||||
win_driver->driver.AddRef(&win_driver->driver);
|
||||
|
||||
return &win_driver->driver;
|
||||
}
|
||||
@ -21,17 +21,16 @@
|
||||
#define FREERDP_CHANNEL_PRINTER_PRINTER_H
|
||||
|
||||
/* SERVER_PRINTER_CACHE_EVENT.cachedata */
|
||||
#define RDPDR_ADD_PRINTER_EVENT 0x00000001
|
||||
#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
|
||||
#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
|
||||
#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
|
||||
#define RDPDR_ADD_PRINTER_EVENT 0x00000001
|
||||
#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
|
||||
#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
|
||||
#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
|
||||
|
||||
/* DR_PRN_DEVICE_ANNOUNCE.Flags */
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
|
||||
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
|
||||
|
||||
#endif /* FREERDP_CHANNEL_PRINTER_PRINTER_H */
|
||||
|
||||
@ -20,3 +20,7 @@ define_channel("rail")
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
@ -42,7 +42,7 @@ RailClientContext* rail_get_client_interface(railPlugin* rail)
|
||||
if (!rail)
|
||||
return NULL;
|
||||
|
||||
pInterface = (RailClientContext*) rail->channelEntryPoints.pInterface;
|
||||
pInterface = (RailClientContext*)rail->channelEntryPoints.pInterface;
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
@ -56,15 +56,18 @@ static UINT rail_send(railPlugin* rail, wStream* s)
|
||||
UINT status;
|
||||
|
||||
if (!rail)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
}
|
||||
|
||||
status = rail->channelEntryPoints.pVirtualChannelWriteEx(rail->InitHandle, rail->OpenHandle,
|
||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
||||
status = rail->channelEntryPoints.pVirtualChannelWriteEx(
|
||||
rail->InitHandle, rail->OpenHandle, Stream_Buffer(s), (UINT32)Stream_GetPosition(s), s);
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08"PRIX32"]",
|
||||
WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
|
||||
WTSErrorToString(status), status);
|
||||
}
|
||||
|
||||
@ -76,13 +79,15 @@ static UINT rail_send(railPlugin* rail, wStream* s)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length)
|
||||
UINT rail_send_channel_data(railPlugin* rail, wStream* src)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
wStream* s;
|
||||
size_t length;
|
||||
|
||||
if (!rail || !data)
|
||||
if (!rail || !src)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
length = Stream_GetPosition(src);
|
||||
s = Stream_New(NULL, length);
|
||||
|
||||
if (!s)
|
||||
@ -91,7 +96,7 @@ UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(s, data, length);
|
||||
Stream_Write(s, Stream_Buffer(src), length);
|
||||
return rail_send(rail, s);
|
||||
}
|
||||
|
||||
@ -104,8 +109,7 @@ UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_execute(RailClientContext* context,
|
||||
const RAIL_EXEC_ORDER* exec)
|
||||
static UINT rail_client_execute(RailClientContext* context, const RAIL_EXEC_ORDER* exec)
|
||||
{
|
||||
char* exeOrFile;
|
||||
UINT error;
|
||||
@ -118,25 +122,19 @@ static UINT rail_client_execute(RailClientContext* context,
|
||||
if (!context || !exec)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
exeOrFile = exec->RemoteApplicationProgram;
|
||||
flags = exec->flags;
|
||||
|
||||
if (!exeOrFile)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (strnlen(exeOrFile, MAX_PATH) >= 2)
|
||||
{
|
||||
if (strncmp(exeOrFile, "||", 2) != 0)
|
||||
flags |= RAIL_EXEC_FLAG_FILE;
|
||||
}
|
||||
|
||||
if (!rail_string_to_unicode_string(exec->RemoteApplicationProgram,
|
||||
&ruExeOrFile) || /* RemoteApplicationProgram */
|
||||
!rail_string_to_unicode_string(exec->RemoteApplicationWorkingDir,
|
||||
&ruWorkingDir) || /* ShellWorkingDirectory */
|
||||
!rail_string_to_unicode_string(exec->RemoteApplicationArguments,
|
||||
&ruArguments)) /* RemoteApplicationCmdLine */
|
||||
if (!utf8_string_to_rail_string(exec->RemoteApplicationProgram,
|
||||
&ruExeOrFile) || /* RemoteApplicationProgram */
|
||||
!utf8_string_to_rail_string(exec->RemoteApplicationWorkingDir,
|
||||
&ruWorkingDir) || /* ShellWorkingDirectory */
|
||||
!utf8_string_to_rail_string(exec->RemoteApplicationArguments,
|
||||
&ruArguments)) /* RemoteApplicationCmdLine */
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
else
|
||||
error = rail_send_client_exec_order(rail, flags, &ruExeOrFile, &ruWorkingDir, &ruArguments);
|
||||
@ -152,15 +150,14 @@ static UINT rail_client_execute(RailClientContext* context,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_activate(RailClientContext* context,
|
||||
const RAIL_ACTIVATE_ORDER* activate)
|
||||
static UINT rail_client_activate(RailClientContext* context, const RAIL_ACTIVATE_ORDER* activate)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !activate)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_activate_order(rail, activate);
|
||||
}
|
||||
|
||||
@ -169,18 +166,18 @@ static UINT rail_client_activate(RailClientContext* context,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_send_client_sysparam(RailClientContext* context,
|
||||
RAIL_SYSPARAM_ORDER* sysparam)
|
||||
static UINT rail_send_client_sysparam(RailClientContext* context, RAIL_SYSPARAM_ORDER* sysparam)
|
||||
{
|
||||
wStream* s;
|
||||
size_t length = RAIL_SYSPARAM_ORDER_LENGTH;
|
||||
railPlugin* rail;
|
||||
UINT error;
|
||||
BOOL extendedSpiSupported;
|
||||
|
||||
if (!context || !sysparam)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
|
||||
switch (sysparam->param)
|
||||
{
|
||||
@ -201,9 +198,18 @@ static UINT rail_send_client_sysparam(RailClientContext* context,
|
||||
length += sysparam->highContrast.colorSchemeLength + 10;
|
||||
break;
|
||||
|
||||
default:
|
||||
length += 8;
|
||||
case SPI_SETFILTERKEYS:
|
||||
length += 20;
|
||||
break;
|
||||
|
||||
case SPI_SETSTICKYKEYS:
|
||||
case SPI_SETCARETWIDTH:
|
||||
case SPI_SETTOGGLEKEYS:
|
||||
length += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
s = rail_pdu_init(length);
|
||||
@ -214,16 +220,17 @@ static UINT rail_send_client_sysparam(RailClientContext* context,
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((error = rail_write_client_sysparam_order(s, sysparam)))
|
||||
extendedSpiSupported = rail_is_extended_spi_supported(rail->channelFlags);
|
||||
if ((error = rail_write_sysparam_order(s, sysparam, extendedSpiSupported)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_write_client_sysparam_order failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_write_client_sysparam_order failed with error %" PRIu32 "!", error);
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = rail_send_pdu(rail, s, RDP_RAIL_ORDER_SYSPARAM)))
|
||||
if ((error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSPARAM)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_pdu failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_pdu failed with error %" PRIu32 "!", error);
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
@ -252,7 +259,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -263,7 +270,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -274,7 +281,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -285,7 +292,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -296,7 +303,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -307,7 +314,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -318,7 +325,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -326,20 +333,6 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_system_param(RailClientContext* context,
|
||||
const RAIL_SYSPARAM_ORDER* sysparam)
|
||||
{
|
||||
if (!context || !sysparam)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -353,7 +346,7 @@ static UINT rail_client_system_command(RailClientContext* context,
|
||||
if (!context || !syscommand)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_syscommand_order(rail, syscommand);
|
||||
}
|
||||
|
||||
@ -362,63 +355,17 @@ static UINT rail_client_system_command(RailClientContext* context,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_handshake(RailClientContext* context,
|
||||
const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
static UINT rail_client_handshake(RailClientContext* context, const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !handshake)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_handshake_order(rail, handshake);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_handshake(RailClientContext* context,
|
||||
const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
if (!context || !handshake)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_handshake_ex(RailClientContext* context,
|
||||
const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !handshakeEx)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
return rail_send_handshake_ex_order(rail, handshakeEx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_handshake_ex(RailClientContext* context,
|
||||
const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
if (!context || !handshakeEx)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -432,7 +379,7 @@ static UINT rail_client_notify_event(RailClientContext* context,
|
||||
if (!context || !notifyEvent)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_notify_event_order(rail, notifyEvent);
|
||||
}
|
||||
|
||||
@ -449,38 +396,10 @@ static UINT rail_client_window_move(RailClientContext* context,
|
||||
if (!context || !windowMove)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_window_move_order(rail, windowMove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_local_move_size(RailClientContext* context,
|
||||
const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
|
||||
{
|
||||
if (!context || !localMoveSize)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_min_max_info(RailClientContext* context,
|
||||
const RAIL_MINMAXINFO_ORDER* minMaxInfo)
|
||||
{
|
||||
if (!context || !minMaxInfo)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -494,7 +413,7 @@ static UINT rail_client_information(RailClientContext* context,
|
||||
if (!context || !clientStatus)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_status_order(rail, clientStatus);
|
||||
}
|
||||
|
||||
@ -503,15 +422,14 @@ static UINT rail_client_information(RailClientContext* context,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_system_menu(RailClientContext* context,
|
||||
const RAIL_SYSMENU_ORDER* sysmenu)
|
||||
static UINT rail_client_system_menu(RailClientContext* context, const RAIL_SYSMENU_ORDER* sysmenu)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !sysmenu)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_sysmenu_order(rail, sysmenu);
|
||||
}
|
||||
|
||||
@ -521,43 +439,27 @@ static UINT rail_client_system_menu(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_language_bar_info(RailClientContext* context,
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !langBarInfo)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_langbar_info_order(rail, langBarInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_language_bar_info(RailClientContext* context,
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
static UINT rail_client_language_ime_info(RailClientContext* context,
|
||||
const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
|
||||
{
|
||||
if (!context || !langBarInfo)
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !langImeInfo)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_execute_result(RailClientContext* context,
|
||||
const RAIL_EXEC_RESULT_ORDER* execResult)
|
||||
{
|
||||
if (!context || !execResult)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_languageime_info_order(rail, langImeInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -566,29 +468,49 @@ static UINT rail_server_execute_result(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_get_appid_request(RailClientContext* context,
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !getAppIdReq || !context->handle)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_get_appid_req_order(rail, getAppIdReq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_get_appid_response(RailClientContext* context,
|
||||
const RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
|
||||
static UINT rail_client_compartment_info(RailClientContext* context,
|
||||
const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
|
||||
{
|
||||
if (!context || !getAppIdResp)
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !compartmentInfo || !context->handle)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK; /* stub - should be registered by client */
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_compartment_info_order(rail, compartmentInfo);
|
||||
}
|
||||
|
||||
static UINT rail_client_cloak(RailClientContext* context, const RAIL_CLOAK* cloak)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !cloak || !context->handle)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_cloak_order(rail, cloak);
|
||||
}
|
||||
|
||||
static UINT rail_client_snap_arrange(RailClientContext* context, const RAIL_SNAP_ARRANGE* snap)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
if (!context || !snap || !context->handle)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_snap_arrange_order(rail, snap);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -596,8 +518,9 @@ static UINT rail_server_get_appid_response(RailClientContext* context,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
static UINT rail_virtual_channel_event_data_received(railPlugin* rail, void* pData,
|
||||
UINT32 dataLength, UINT32 totalLength,
|
||||
UINT32 dataFlags)
|
||||
{
|
||||
wStream* data_in;
|
||||
|
||||
@ -622,7 +545,7 @@ static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
||||
|
||||
data_in = rail->data_in;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(data_in, (int) dataLength))
|
||||
if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
@ -634,7 +557,7 @@ static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
||||
{
|
||||
if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_plugin_process_received: read error");
|
||||
WLog_ERR(TAG, "rail_plugin_process_received: read error");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -642,7 +565,7 @@ static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
||||
Stream_SealLength(data_in);
|
||||
Stream_SetPosition(data_in, 0);
|
||||
|
||||
if (!MessageQueue_Post(rail->queue, NULL, 0, (void*) data_in, NULL))
|
||||
if (!MessageQueue_Post(rail->queue, NULL, 0, (void*)data_in, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -653,36 +576,42 @@ static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
|
||||
UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
UINT event, LPVOID pData,
|
||||
UINT32 dataLength, UINT32 totalLength,
|
||||
UINT32 dataFlags)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
railPlugin* rail = (railPlugin*) lpUserParam;
|
||||
|
||||
if (!rail || (rail->OpenHandle != openHandle))
|
||||
{
|
||||
WLog_ERR(TAG, "error no match");
|
||||
return;
|
||||
}
|
||||
railPlugin* rail = (railPlugin*)lpUserParam;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||
if (!rail || (rail->OpenHandle != openHandle))
|
||||
{
|
||||
WLog_ERR(TAG, "error no match");
|
||||
return;
|
||||
}
|
||||
if ((error = rail_virtual_channel_event_data_received(rail, pData, dataLength,
|
||||
totalLength, dataFlags)))
|
||||
WLog_ERR(TAG, "rail_virtual_channel_event_data_received failed with error %"PRIu32"!",
|
||||
totalLength, dataFlags)))
|
||||
WLog_ERR(TAG,
|
||||
"rail_virtual_channel_event_data_received failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_CANCELLED:
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
break;
|
||||
{
|
||||
wStream* s = (wStream*)pData;
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && rail->rdpcontext)
|
||||
if (error && rail && rail->rdpcontext)
|
||||
setChannelError(rail->rdpcontext, error,
|
||||
"rail_virtual_channel_open_event reported an error");
|
||||
|
||||
@ -693,7 +622,7 @@ static DWORD WINAPI rail_virtual_channel_client_thread(LPVOID arg)
|
||||
{
|
||||
wStream* data;
|
||||
wMessage message;
|
||||
railPlugin* rail = (railPlugin*) arg;
|
||||
railPlugin* rail = (railPlugin*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
while (1)
|
||||
@ -717,13 +646,13 @@ static DWORD WINAPI rail_virtual_channel_client_thread(LPVOID arg)
|
||||
|
||||
if (message.id == 0)
|
||||
{
|
||||
data = (wStream*) message.wParam;
|
||||
data = (wStream*)message.wParam;
|
||||
error = rail_order_recv(rail, data);
|
||||
Stream_Free(data, TRUE);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "rail_order_recv failed with error %"PRIu32"!", error);
|
||||
WLog_ERR(TAG, "rail_order_recv failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -742,20 +671,30 @@ static DWORD WINAPI rail_virtual_channel_client_thread(LPVOID arg)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData,
|
||||
UINT32 dataLength)
|
||||
static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData, UINT32 dataLength)
|
||||
{
|
||||
RailClientContext* context = rail_get_client_interface(rail);
|
||||
UINT status;
|
||||
status = rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle,
|
||||
&rail->OpenHandle, rail->channelDef.name, rail_virtual_channel_open_event_ex);
|
||||
status = rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle, &rail->OpenHandle,
|
||||
rail->channelDef.name,
|
||||
rail_virtual_channel_open_event_ex);
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "pVirtualChannelOpen failed with %s [%08"PRIX32"]",
|
||||
WLog_ERR(TAG, "pVirtualChannelOpen failed with %s [%08" PRIX32 "]",
|
||||
WTSErrorToString(status), status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (context)
|
||||
{
|
||||
IFCALLRET(context->OnOpen, status, context, &rail->sendHandshake);
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
WLog_ERR(TAG, "context->OnOpen failed with %s [%08" PRIX32 "]",
|
||||
WTSErrorToString(status), status);
|
||||
}
|
||||
|
||||
rail->queue = MessageQueue_New(NULL);
|
||||
|
||||
if (!rail->queue)
|
||||
@ -764,9 +703,8 @@ static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData,
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!(rail->thread = CreateThread(NULL, 0,
|
||||
rail_virtual_channel_client_thread, (void*) rail, 0,
|
||||
NULL)))
|
||||
if (!(rail->thread =
|
||||
CreateThread(NULL, 0, rail_virtual_channel_client_thread, (void*)rail, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
MessageQueue_Free(rail->queue);
|
||||
@ -789,11 +727,11 @@ static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
|
||||
if (rail->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (MessageQueue_PostQuit(rail->queue, 0)
|
||||
&& (WaitForSingleObject(rail->thread, INFINITE) == WAIT_FAILED))
|
||||
if (MessageQueue_PostQuit(rail->queue, 0) &&
|
||||
(WaitForSingleObject(rail->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
rc = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", rc);
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -805,8 +743,8 @@ static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
|
||||
|
||||
if (CHANNEL_RC_OK != rc)
|
||||
{
|
||||
WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08"PRIX32"]",
|
||||
WTSErrorToString(rc), rc);
|
||||
WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -829,14 +767,14 @@ static void rail_virtual_channel_event_terminated(railPlugin* rail)
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
|
||||
UINT event, LPVOID pData, UINT dataLength)
|
||||
UINT event, LPVOID pData, UINT dataLength)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
railPlugin* rail = (railPlugin*) lpUserParam;
|
||||
railPlugin* rail = (railPlugin*)lpUserParam;
|
||||
|
||||
if (!rail || (rail->InitHandle != pInitHandle))
|
||||
{
|
||||
WLog_ERR(TAG, "error no match");
|
||||
WLog_ERR(TAG, "error no match");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -844,14 +782,15 @@ static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPV
|
||||
{
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
if ((error = rail_virtual_channel_event_connected(rail, pData, dataLength)))
|
||||
WLog_ERR(TAG, "rail_virtual_channel_event_connected failed with error %"PRIu32"!",
|
||||
WLog_ERR(TAG, "rail_virtual_channel_event_connected failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
if ((error = rail_virtual_channel_event_disconnected(rail)))
|
||||
WLog_ERR(TAG, "rail_virtual_channel_event_disconnected failed with error %"PRIu32"!",
|
||||
WLog_ERR(TAG,
|
||||
"rail_virtual_channel_event_disconnected failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
break;
|
||||
@ -867,11 +806,12 @@ static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPV
|
||||
}
|
||||
|
||||
if (error && rail->rdpcontext)
|
||||
setChannelError(rail->rdpcontext, error, "rail_virtual_channel_init_event_ex reported an error");
|
||||
setChannelError(rail->rdpcontext, error,
|
||||
"rail_virtual_channel_init_event_ex reported an error");
|
||||
}
|
||||
|
||||
/* rail is always built-in */
|
||||
#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
|
||||
#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
|
||||
|
||||
BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
|
||||
{
|
||||
@ -880,7 +820,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
RailClientContext* context = NULL;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
|
||||
BOOL isFreerdp = FALSE;
|
||||
rail = (railPlugin*) calloc(1, sizeof(railPlugin));
|
||||
rail = (railPlugin*)calloc(1, sizeof(railPlugin));
|
||||
|
||||
if (!rail)
|
||||
{
|
||||
@ -888,18 +828,17 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rail->channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), "rail");
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
/* Default to automatically replying to server handshakes */
|
||||
rail->sendHandshake = TRUE;
|
||||
rail->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
||||
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
||||
{
|
||||
context = (RailClientContext*) calloc(1, sizeof(RailClientContext));
|
||||
context = (RailClientContext*)calloc(1, sizeof(RailClientContext));
|
||||
|
||||
if (!context)
|
||||
{
|
||||
@ -908,28 +847,23 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
context->handle = (void*) rail;
|
||||
context->handle = (void*)rail;
|
||||
context->custom = NULL;
|
||||
context->ClientExecute = rail_client_execute;
|
||||
context->ClientActivate = rail_client_activate;
|
||||
context->ClientSystemParam = rail_client_system_param;
|
||||
context->ServerSystemParam = rail_server_system_param;
|
||||
context->ClientSystemCommand = rail_client_system_command;
|
||||
context->ClientHandshake = rail_client_handshake;
|
||||
context->ServerHandshake = rail_server_handshake;
|
||||
context->ClientHandshakeEx = rail_client_handshake_ex;
|
||||
context->ServerHandshakeEx = rail_server_handshake_ex;
|
||||
context->ClientNotifyEvent = rail_client_notify_event;
|
||||
context->ClientWindowMove = rail_client_window_move;
|
||||
context->ServerLocalMoveSize = rail_server_local_move_size;
|
||||
context->ServerMinMaxInfo = rail_server_min_max_info;
|
||||
context->ClientInformation = rail_client_information;
|
||||
context->ClientSystemMenu = rail_client_system_menu;
|
||||
context->ClientLanguageBarInfo = rail_client_language_bar_info;
|
||||
context->ServerLanguageBarInfo = rail_server_language_bar_info;
|
||||
context->ServerExecuteResult = rail_server_execute_result;
|
||||
context->ClientLanguageIMEInfo = rail_client_language_ime_info;
|
||||
context->ClientGetAppIdRequest = rail_client_get_appid_request;
|
||||
context->ServerGetAppIdResponse = rail_server_get_appid_response;
|
||||
context->ClientSnapArrange = rail_client_snap_arrange;
|
||||
context->ClientCloak = rail_client_cloak;
|
||||
context->ClientCompartmentInfo = rail_client_compartment_info;
|
||||
rail->rdpcontext = pEntryPointsEx->context;
|
||||
rail->context = context;
|
||||
isFreerdp = TRUE;
|
||||
@ -937,17 +871,15 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
|
||||
rail->log = WLog_Get("com.freerdp.channels.rail.client");
|
||||
WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntryEx");
|
||||
CopyMemory(&(rail->channelEntryPoints), pEntryPoints,
|
||||
sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
|
||||
CopyMemory(&(rail->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
|
||||
rail->InitHandle = pInitHandle;
|
||||
rc = rail->channelEntryPoints.pVirtualChannelInitEx(rail, context, pInitHandle,
|
||||
&rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
|
||||
rail_virtual_channel_init_event_ex);
|
||||
rc = rail->channelEntryPoints.pVirtualChannelInitEx(
|
||||
rail, context, pInitHandle, &rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
|
||||
rail_virtual_channel_init_event_ex);
|
||||
|
||||
if (CHANNEL_RC_OK != rc)
|
||||
{
|
||||
WLog_ERR(TAG, "failed with %s [%08"PRIX32"]",
|
||||
WTSErrorToString(rc), rc);
|
||||
WLog_ERR(TAG, "failed with %s [%08" PRIX32 "]", WTSErrorToString(rc), rc);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -50,10 +50,14 @@ struct rail_plugin
|
||||
DWORD OpenHandle;
|
||||
wMessageQueue* queue;
|
||||
rdpContext* rdpcontext;
|
||||
DWORD channelBuildNumber;
|
||||
DWORD channelFlags;
|
||||
RAIL_CLIENT_STATUS_ORDER clientStatus;
|
||||
BOOL sendHandshake;
|
||||
};
|
||||
typedef struct rail_plugin railPlugin;
|
||||
|
||||
RailClientContext* rail_get_client_interface(railPlugin* rail);
|
||||
UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length);
|
||||
UINT rail_send_channel_data(railPlugin* rail, wStream* s);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_CLIENT_MAIN_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -29,8 +29,6 @@
|
||||
|
||||
#define TAG CHANNELS_TAG("rail.client")
|
||||
|
||||
UINT rail_write_client_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam);
|
||||
|
||||
UINT rail_order_recv(railPlugin* rail, wStream* s);
|
||||
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType);
|
||||
|
||||
@ -38,18 +36,25 @@ UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* han
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
UINT rail_send_client_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_ORDER* clientStatus);
|
||||
UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
|
||||
const RAIL_UNICODE_STRING* exeOrFile, const RAIL_UNICODE_STRING* workingDir,
|
||||
const RAIL_UNICODE_STRING* exeOrFile,
|
||||
const RAIL_UNICODE_STRING* workingDir,
|
||||
const RAIL_UNICODE_STRING* arguments);
|
||||
UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER* activate);
|
||||
UINT rail_send_client_sysmenu_order(railPlugin* rail, const RAIL_SYSMENU_ORDER* sysmenu);
|
||||
UINT rail_send_client_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||
|
||||
UINT rail_send_client_notify_event_order(railPlugin* rail,
|
||||
const RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
|
||||
const RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
|
||||
UINT rail_send_client_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE_ORDER* windowMove);
|
||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail,
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
|
||||
UINT rail_send_client_langbar_info_order(railPlugin* rail,
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo);
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo);
|
||||
UINT rail_send_client_languageime_info_order(railPlugin* rail,
|
||||
const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo);
|
||||
UINT rail_send_client_cloak_order(railPlugin* rail, const RAIL_CLOAK* cloak);
|
||||
UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRANGE* snap);
|
||||
UINT rail_send_client_compartment_info_order(railPlugin* rail,
|
||||
const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H */
|
||||
|
||||
@ -23,56 +23,76 @@
|
||||
#include "rail_common.h"
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
const char* const RAIL_ORDER_TYPE_STRINGS[] =
|
||||
#define TAG CHANNELS_TAG("rail.common")
|
||||
|
||||
const char* rail_get_order_type_string(UINT16 orderType)
|
||||
{
|
||||
"",
|
||||
"Execute",
|
||||
"Activate",
|
||||
"System Parameters Update",
|
||||
"System Command",
|
||||
"Handshake",
|
||||
"Notify Event",
|
||||
"",
|
||||
"Window Move",
|
||||
"Local Move/Size",
|
||||
"Min Max Info",
|
||||
"Client Status",
|
||||
"System Menu",
|
||||
"Language Bar Info",
|
||||
"Get Application ID Request",
|
||||
"Get Application ID Response",
|
||||
"Execute Result",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
BOOL rail_string_to_unicode_string(const char* string, RAIL_UNICODE_STRING* unicode_string)
|
||||
{
|
||||
WCHAR* buffer = NULL;
|
||||
int length = 0;
|
||||
free(unicode_string->string);
|
||||
unicode_string->string = NULL;
|
||||
unicode_string->length = 0;
|
||||
|
||||
if (!string || strlen(string) < 1)
|
||||
return TRUE;
|
||||
|
||||
length = ConvertToUnicode(CP_UTF8, 0, string, -1, &buffer, 0);
|
||||
|
||||
if ((length < 0) || ((size_t)length * sizeof(WCHAR) > UINT16_MAX))
|
||||
switch (orderType)
|
||||
{
|
||||
free(buffer);
|
||||
return FALSE;
|
||||
case TS_RAIL_ORDER_EXEC:
|
||||
return "TS_RAIL_ORDER_EXEC";
|
||||
case TS_RAIL_ORDER_ACTIVATE:
|
||||
return "TS_RAIL_ORDER_ACTIVATE";
|
||||
case TS_RAIL_ORDER_SYSPARAM:
|
||||
return "TS_RAIL_ORDER_SYSPARAM";
|
||||
case TS_RAIL_ORDER_SYSCOMMAND:
|
||||
return "TS_RAIL_ORDER_SYSCOMMAND";
|
||||
case TS_RAIL_ORDER_HANDSHAKE:
|
||||
return "TS_RAIL_ORDER_HANDSHAKE";
|
||||
case TS_RAIL_ORDER_NOTIFY_EVENT:
|
||||
return "TS_RAIL_ORDER_NOTIFY_EVENT";
|
||||
case TS_RAIL_ORDER_WINDOWMOVE:
|
||||
return "TS_RAIL_ORDER_WINDOWMOVE";
|
||||
case TS_RAIL_ORDER_LOCALMOVESIZE:
|
||||
return "TS_RAIL_ORDER_LOCALMOVESIZE";
|
||||
case TS_RAIL_ORDER_MINMAXINFO:
|
||||
return "TS_RAIL_ORDER_MINMAXINFO";
|
||||
case TS_RAIL_ORDER_CLIENTSTATUS:
|
||||
return "TS_RAIL_ORDER_CLIENTSTATUS";
|
||||
case TS_RAIL_ORDER_SYSMENU:
|
||||
return "TS_RAIL_ORDER_SYSMENU";
|
||||
case TS_RAIL_ORDER_LANGBARINFO:
|
||||
return "TS_RAIL_ORDER_LANGBARINFO";
|
||||
case TS_RAIL_ORDER_GET_APPID_REQ:
|
||||
return "TS_RAIL_ORDER_GET_APPID_REQ";
|
||||
case TS_RAIL_ORDER_GET_APPID_RESP:
|
||||
return "TS_RAIL_ORDER_GET_APPID_RESP";
|
||||
case TS_RAIL_ORDER_TASKBARINFO:
|
||||
return "TS_RAIL_ORDER_TASKBARINFO";
|
||||
case TS_RAIL_ORDER_LANGUAGEIMEINFO:
|
||||
return "TS_RAIL_ORDER_LANGUAGEIMEINFO";
|
||||
case TS_RAIL_ORDER_COMPARTMENTINFO:
|
||||
return "TS_RAIL_ORDER_COMPARTMENTINFO";
|
||||
case TS_RAIL_ORDER_HANDSHAKE_EX:
|
||||
return "TS_RAIL_ORDER_HANDSHAKE_EX";
|
||||
case TS_RAIL_ORDER_ZORDER_SYNC:
|
||||
return "TS_RAIL_ORDER_ZORDER_SYNC";
|
||||
case TS_RAIL_ORDER_CLOAK:
|
||||
return "TS_RAIL_ORDER_CLOAK";
|
||||
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
|
||||
return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST";
|
||||
case TS_RAIL_ORDER_SNAP_ARRANGE:
|
||||
return "TS_RAIL_ORDER_SNAP_ARRANGE";
|
||||
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
|
||||
return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
|
||||
case TS_RAIL_ORDER_EXEC_RESULT:
|
||||
return "TS_RAIL_ORDER_EXEC_RESULT";
|
||||
case TS_RAIL_ORDER_TEXTSCALEINFO:
|
||||
return "TS_RAIL_ORDER_TEXTSCALEINFO";
|
||||
case TS_RAIL_ORDER_CARETBLINKINFO:
|
||||
return "TS_RAIL_ORDER_CARETBLINKINFO";
|
||||
default:
|
||||
return "TS_RAIL_ORDER_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
unicode_string->string = (BYTE*) buffer;
|
||||
unicode_string->length = (UINT16) length * sizeof(WCHAR);
|
||||
return TRUE;
|
||||
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
|
||||
{
|
||||
_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
|
||||
orderType);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,14 +108,14 @@ UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength)
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, *orderType); /* orderType (2 bytes) */
|
||||
Stream_Read_UINT16(s, *orderType); /* orderType (2 bytes) */
|
||||
Stream_Read_UINT16(s, *orderLength); /* orderLength (2 bytes) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
|
||||
{
|
||||
Stream_Write_UINT16(s, orderType); /* orderType (2 bytes) */
|
||||
Stream_Write_UINT16(s, orderType); /* orderType (2 bytes) */
|
||||
Stream_Write_UINT16(s, orderLength); /* orderLength (2 bytes) */
|
||||
}
|
||||
|
||||
@ -140,13 +160,459 @@ UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshake
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
|
||||
Stream_Read_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
|
||||
Stream_Read_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
Stream_Write_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
|
||||
Stream_Write_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
|
||||
Stream_Write_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_write_unicode_string(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
|
||||
{
|
||||
if (!s || !unicode_string)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 2 + unicode_string->length))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, unicode_string->length); /* cbString (2 bytes) */
|
||||
Stream_Write(s, unicode_string->string, unicode_string->length); /* string */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (!s || !unicode_string)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
length = unicode_string->length;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, length))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(s, unicode_string->string, length); /* string */
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_read_high_contrast(wStream* s, RAIL_HIGH_CONTRAST* highContrast)
|
||||
{
|
||||
if (!s || !highContrast)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, highContrast->flags); /* flags (4 bytes) */
|
||||
Stream_Read_UINT32(s, highContrast->colorSchemeLength); /* colorSchemeLength (4 bytes) */
|
||||
|
||||
if (!rail_read_unicode_string(s, &highContrast->colorScheme)) /* colorScheme */
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_write_high_contrast(wStream* s, const RAIL_HIGH_CONTRAST* highContrast)
|
||||
{
|
||||
UINT32 colorSchemeLength;
|
||||
|
||||
if (!s || !highContrast)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
colorSchemeLength = highContrast->colorScheme.length + 2;
|
||||
Stream_Write_UINT32(s, highContrast->flags); /* flags (4 bytes) */
|
||||
Stream_Write_UINT32(s, colorSchemeLength); /* colorSchemeLength (4 bytes) */
|
||||
return rail_write_unicode_string(s, &highContrast->colorScheme); /* colorScheme */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_read_filterkeys(wStream* s, TS_FILTERKEYS* filterKeys)
|
||||
{
|
||||
if (!s || !filterKeys)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 20)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, filterKeys->Flags);
|
||||
Stream_Read_UINT32(s, filterKeys->WaitTime);
|
||||
Stream_Read_UINT32(s, filterKeys->DelayTime);
|
||||
Stream_Read_UINT32(s, filterKeys->RepeatTime);
|
||||
Stream_Read_UINT32(s, filterKeys->BounceTime);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_write_filterkeys(wStream* s, const TS_FILTERKEYS* filterKeys)
|
||||
{
|
||||
if (!s || !filterKeys)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 20))
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, filterKeys->Flags);
|
||||
Stream_Write_UINT32(s, filterKeys->WaitTime);
|
||||
Stream_Write_UINT32(s, filterKeys->DelayTime);
|
||||
Stream_Write_UINT32(s, filterKeys->RepeatTime);
|
||||
Stream_Write_UINT32(s, filterKeys->BounceTime);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_read_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported)
|
||||
{
|
||||
BYTE body;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!s || !sysparam)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 5)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
|
||||
|
||||
sysparam->params = 0; /* bitflags of received params */
|
||||
|
||||
switch (sysparam->param)
|
||||
{
|
||||
/* Client sysparams */
|
||||
case SPI_SET_DRAG_FULL_WINDOWS:
|
||||
sysparam->params |= SPI_MASK_SET_DRAG_FULL_WINDOWS;
|
||||
Stream_Read_UINT8(s, body); /* body (1 byte) */
|
||||
sysparam->dragFullWindows = body != 0;
|
||||
break;
|
||||
|
||||
case SPI_SET_KEYBOARD_CUES:
|
||||
sysparam->params |= SPI_MASK_SET_KEYBOARD_CUES;
|
||||
Stream_Read_UINT8(s, body); /* body (1 byte) */
|
||||
sysparam->keyboardCues = body != 0;
|
||||
break;
|
||||
|
||||
case SPI_SET_KEYBOARD_PREF:
|
||||
sysparam->params |= SPI_MASK_SET_KEYBOARD_PREF;
|
||||
Stream_Read_UINT8(s, body); /* body (1 byte) */
|
||||
sysparam->keyboardPref = body != 0;
|
||||
break;
|
||||
|
||||
case SPI_SET_MOUSE_BUTTON_SWAP:
|
||||
sysparam->params |= SPI_MASK_SET_MOUSE_BUTTON_SWAP;
|
||||
Stream_Read_UINT8(s, body); /* body (1 byte) */
|
||||
sysparam->mouseButtonSwap = body != 0;
|
||||
break;
|
||||
|
||||
case SPI_SET_WORK_AREA:
|
||||
sysparam->params |= SPI_MASK_SET_WORK_AREA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, sysparam->workArea.left); /* left (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->workArea.top); /* top (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->workArea.right); /* right (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
|
||||
break;
|
||||
|
||||
case SPI_DISPLAY_CHANGE:
|
||||
sysparam->params |= SPI_MASK_DISPLAY_CHANGE;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, sysparam->displayChange.left); /* left (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->displayChange.top); /* top (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->displayChange.right); /* right (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
|
||||
break;
|
||||
|
||||
case SPI_TASKBAR_POS:
|
||||
sysparam->params |= SPI_MASK_TASKBAR_POS;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, sysparam->taskbarPos.left); /* left (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->taskbarPos.top); /* top (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->taskbarPos.right); /* right (2 bytes) */
|
||||
Stream_Read_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
|
||||
break;
|
||||
|
||||
case SPI_SET_HIGH_CONTRAST:
|
||||
sysparam->params |= SPI_MASK_SET_HIGH_CONTRAST;
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
error = rail_read_high_contrast(s, &sysparam->highContrast);
|
||||
break;
|
||||
|
||||
case SPI_SETCARETWIDTH:
|
||||
sysparam->params |= SPI_MASK_SET_CARET_WIDTH;
|
||||
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, sysparam->caretWidth);
|
||||
|
||||
if (sysparam->caretWidth < 0x0001)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
break;
|
||||
|
||||
case SPI_SETSTICKYKEYS:
|
||||
sysparam->params |= SPI_MASK_SET_STICKY_KEYS;
|
||||
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, sysparam->stickyKeys);
|
||||
break;
|
||||
|
||||
case SPI_SETTOGGLEKEYS:
|
||||
sysparam->params |= SPI_MASK_SET_TOGGLE_KEYS;
|
||||
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, sysparam->toggleKeys);
|
||||
break;
|
||||
|
||||
case SPI_SETFILTERKEYS:
|
||||
sysparam->params |= SPI_MASK_SET_FILTER_KEYS;
|
||||
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 20)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_GetRemainingLength failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
error = rail_read_filterkeys(s, &sysparam->filterKeys);
|
||||
break;
|
||||
|
||||
/* Server sysparams */
|
||||
case SPI_SETSCREENSAVEACTIVE:
|
||||
sysparam->params |= SPI_MASK_SET_SCREEN_SAVE_ACTIVE;
|
||||
|
||||
Stream_Read_UINT8(s, body); /* body (1 byte) */
|
||||
sysparam->setScreenSaveActive = body != 0;
|
||||
break;
|
||||
|
||||
case SPI_SETSCREENSAVESECURE:
|
||||
sysparam->params |= SPI_MASK_SET_SET_SCREEN_SAVE_SECURE;
|
||||
|
||||
Stream_Read_UINT8(s, body); /* body (1 byte) */
|
||||
sysparam->setScreenSaveSecure = body != 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 err2or code
|
||||
*/
|
||||
UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
|
||||
BOOL extendedSpiSupported)
|
||||
{
|
||||
BYTE body;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!s || !sysparam)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 12))
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
|
||||
|
||||
switch (sysparam->param)
|
||||
{
|
||||
/* Client sysparams */
|
||||
case SPI_SET_DRAG_FULL_WINDOWS:
|
||||
body = sysparam->dragFullWindows ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_KEYBOARD_CUES:
|
||||
body = sysparam->keyboardCues ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_KEYBOARD_PREF:
|
||||
body = sysparam->keyboardPref ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_MOUSE_BUTTON_SWAP:
|
||||
body = sysparam->mouseButtonSwap ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_WORK_AREA:
|
||||
Stream_Write_UINT16(s, sysparam->workArea.left); /* left (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->workArea.top); /* top (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->workArea.right); /* right (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
|
||||
break;
|
||||
|
||||
case SPI_DISPLAY_CHANGE:
|
||||
Stream_Write_UINT16(s, sysparam->displayChange.left); /* left (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->displayChange.top); /* top (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->displayChange.right); /* right (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
|
||||
break;
|
||||
|
||||
case SPI_TASKBAR_POS:
|
||||
Stream_Write_UINT16(s, sysparam->taskbarPos.left); /* left (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->taskbarPos.top); /* top (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->taskbarPos.right); /* right (2 bytes) */
|
||||
Stream_Write_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
|
||||
break;
|
||||
|
||||
case SPI_SET_HIGH_CONTRAST:
|
||||
error = rail_write_high_contrast(s, &sysparam->highContrast);
|
||||
break;
|
||||
|
||||
case SPI_SETCARETWIDTH:
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (sysparam->caretWidth < 0x0001)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Write_UINT32(s, sysparam->caretWidth);
|
||||
break;
|
||||
|
||||
case SPI_SETSTICKYKEYS:
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Write_UINT32(s, sysparam->stickyKeys);
|
||||
break;
|
||||
|
||||
case SPI_SETTOGGLEKEYS:
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Write_UINT32(s, sysparam->toggleKeys);
|
||||
break;
|
||||
|
||||
case SPI_SETFILTERKEYS:
|
||||
if (!extendedSpiSupported)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
error = rail_write_filterkeys(s, &sysparam->filterKeys);
|
||||
break;
|
||||
|
||||
/* Server sysparams */
|
||||
case SPI_SETSCREENSAVEACTIVE:
|
||||
body = sysparam->setScreenSaveActive ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SETSCREENSAVESECURE:
|
||||
body = sysparam->setScreenSaveSecure ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
BOOL rail_is_extended_spi_supported(UINT32 channelFlags)
|
||||
{
|
||||
return channelFlags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED;
|
||||
}
|
||||
|
||||
@ -26,25 +26,34 @@
|
||||
|
||||
#include <freerdp/rail.h>
|
||||
|
||||
extern const char* const RAIL_ORDER_TYPE_STRINGS[];
|
||||
|
||||
#define RAIL_PDU_HEADER_LENGTH 4
|
||||
#define RAIL_PDU_HEADER_LENGTH 4
|
||||
|
||||
/* Fixed length of PDUs, excluding variable lengths */
|
||||
#define RAIL_HANDSHAKE_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_HANDSHAKE_EX_ORDER_LENGTH 8 /* fixed */
|
||||
#define RAIL_CLIENT_STATUS_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_EXEC_ORDER_LENGTH 8 /* variable */
|
||||
#define RAIL_SYSPARAM_ORDER_LENGTH 4 /* variable */
|
||||
#define RAIL_ACTIVATE_ORDER_LENGTH 5 /* fixed */
|
||||
#define RAIL_SYSMENU_ORDER_LENGTH 8 /* fixed */
|
||||
#define RAIL_SYSCOMMAND_ORDER_LENGTH 6 /* fixed */
|
||||
#define RAIL_NOTIFY_EVENT_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_WINDOW_MOVE_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_GET_APPID_REQ_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_LANGBAR_INFO_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_HANDSHAKE_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_HANDSHAKE_EX_ORDER_LENGTH 8 /* fixed */
|
||||
#define RAIL_CLIENT_STATUS_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_EXEC_ORDER_LENGTH 8 /* variable */
|
||||
#define RAIL_EXEC_RESULT_ORDER_LENGTH 12 /* variable */
|
||||
#define RAIL_SYSPARAM_ORDER_LENGTH 4 /* variable */
|
||||
#define RAIL_MINMAXINFO_ORDER_LENGTH 20 /* fixed */
|
||||
#define RAIL_LOCALMOVESIZE_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_ACTIVATE_ORDER_LENGTH 5 /* fixed */
|
||||
#define RAIL_SYSMENU_ORDER_LENGTH 8 /* fixed */
|
||||
#define RAIL_SYSCOMMAND_ORDER_LENGTH 6 /* fixed */
|
||||
#define RAIL_NOTIFY_EVENT_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_WINDOW_MOVE_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_SNAP_ARRANGE_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_GET_APPID_REQ_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_LANGBAR_INFO_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_LANGUAGEIME_INFO_ORDER_LENGTH 42 /* fixed */
|
||||
#define RAIL_COMPARTMENT_INFO_ORDER_LENGTH 16 /* fixed */
|
||||
#define RAIL_CLOAK_ORDER_LENGTH 5 /* fixed */
|
||||
#define RAIL_TASKBAR_INFO_ORDER_LENGTH 12 /* fixed */
|
||||
#define RAIL_Z_ORDER_SYNC_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_POWER_DISPLAY_REQUEST_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_GET_APPID_RESP_ORDER_LENGTH 524 /* fixed */
|
||||
#define RAIL_GET_APPID_RESP_EX_ORDER_LENGTH 1048 /* fixed */
|
||||
|
||||
BOOL rail_string_to_unicode_string(const char* string, RAIL_UNICODE_STRING* unicode_string);
|
||||
UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
void rail_write_handshake_order(wStream* s, const RAIL_HANDSHAKE_ORDER* handshake);
|
||||
UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
@ -54,4 +63,14 @@ wStream* rail_pdu_init(size_t length);
|
||||
UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength);
|
||||
void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength);
|
||||
|
||||
UINT rail_write_unicode_string(wStream* s, const RAIL_UNICODE_STRING* unicode_string);
|
||||
UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unicode_string);
|
||||
|
||||
UINT rail_read_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported);
|
||||
UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
|
||||
BOOL extendedSpiSupported);
|
||||
BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
|
||||
const char* rail_get_order_type_string(UINT16 orderType);
|
||||
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */
|
||||
|
||||
32
channels/rail/server/CMakeLists.txt
Normal file
32
channels/rail/server/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Mati Shabtay <matishabtay@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("rail")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
../rail_common.c
|
||||
../rail_common.h
|
||||
rail_main.c
|
||||
rail_main.h)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
1688
channels/rail/server/rail_main.c
Normal file
1688
channels/rail/server/rail_main.c
Normal file
File diff suppressed because it is too large
Load Diff
44
channels/rail/server/rail_main.h
Normal file
44
channels/rail/server/rail_main.h
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RAIL Virtual Channel Plugin
|
||||
*
|
||||
* Copyright 2019 Mati Shabtay <matishabtay@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_RAIL_SERVER_MAIN_H
|
||||
#define FREERDP_CHANNEL_RAIL_SERVER_MAIN_H
|
||||
|
||||
#include <freerdp/rail.h>
|
||||
#include <freerdp/server/rail.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include "../rail_common.h"
|
||||
|
||||
struct _rail_server_private
|
||||
{
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
HANDLE channelEvent;
|
||||
void* rail_channel;
|
||||
|
||||
wStream* input_stream;
|
||||
|
||||
DWORD channelFlags;
|
||||
};
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_SERVER_MAIN_H */
|
||||
22
channels/rdp2tcp/CMakeLists.txt
Normal file
22
channels/rdp2tcp/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# 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("rdp2tcp")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
10
channels/rdp2tcp/ChannelOptions.cmake
Normal file
10
channels/rdp2tcp/ChannelOptions.cmake
Normal file
@ -0,0 +1,10 @@
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
|
||||
define_channel_options(NAME "rdp2tcp" TYPE "static"
|
||||
DESCRIPTION "Tunneling TCP over RDP"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
||||
27
channels/rdp2tcp/client/CMakeLists.txt
Normal file
27
channels/rdp2tcp/client/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
||||
# 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("rdp2tcp")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
rdp2tcp_main.c)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
327
channels/rdp2tcp/client/rdp2tcp_main.c
Normal file
327
channels/rdp2tcp/client/rdp2tcp_main.c
Normal file
@ -0,0 +1,327 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* rdp2tcp Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2017 Artur Zaprzala
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/svc.h>
|
||||
|
||||
#define RDP2TCP_CHAN_NAME "rdp2tcp"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG CLIENT_TAG(RDP2TCP_CHAN_NAME)
|
||||
|
||||
static int const debug = 0;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE hStdOutputRead;
|
||||
HANDLE hStdInputWrite;
|
||||
HANDLE hProcess;
|
||||
HANDLE copyThread;
|
||||
HANDLE writeComplete;
|
||||
DWORD openHandle;
|
||||
void* initHandle;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
char buffer[16 * 1024];
|
||||
} Plugin;
|
||||
|
||||
static int init_external_addin(Plugin* plugin)
|
||||
{
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
STARTUPINFO siStartInfo;
|
||||
PROCESS_INFORMATION procInfo;
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
siStartInfo.cb = sizeof(STARTUPINFO);
|
||||
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
siStartInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
// Create pipes
|
||||
if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdout CreatePipe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdout SetHandleInformation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdin CreatePipe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdin SetHandleInformation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Execute plugin
|
||||
if (!CreateProcess(NULL,
|
||||
plugin->channelEntryPoints.pExtendedData, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
NULL, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&procInfo // receives PROCESS_INFORMATION
|
||||
))
|
||||
{
|
||||
WLog_ERR(TAG, "fork for addin");
|
||||
return -1;
|
||||
}
|
||||
|
||||
plugin->hProcess = procInfo.hProcess;
|
||||
CloseHandle(procInfo.hThread);
|
||||
CloseHandle(siStartInfo.hStdOutput);
|
||||
CloseHandle(siStartInfo.hStdInput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dumpData(char* data, unsigned length)
|
||||
{
|
||||
unsigned const limit = 98;
|
||||
unsigned l = length > limit ? limit / 2 : length;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < l; ++i)
|
||||
{
|
||||
printf("%02hhx", data[i]);
|
||||
}
|
||||
|
||||
if (length > limit)
|
||||
{
|
||||
printf("...");
|
||||
|
||||
for (i = length - l; i < length; ++i)
|
||||
printf("%02hhx", data[i]);
|
||||
}
|
||||
|
||||
puts("");
|
||||
}
|
||||
|
||||
static DWORD WINAPI copyThread(void* data)
|
||||
{
|
||||
Plugin* plugin = (Plugin*)data;
|
||||
size_t const bufsize = 16 * 1024;
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD dwRead;
|
||||
char* buffer = malloc(bufsize);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// if (!ReadFile(plugin->hStdOutputRead, plugin->buffer, sizeof plugin->buffer, &dwRead,
|
||||
// NULL))
|
||||
if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, NULL))
|
||||
{
|
||||
free(buffer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (debug > 1)
|
||||
{
|
||||
printf(">%8u ", (unsigned)dwRead);
|
||||
dumpData(buffer, dwRead);
|
||||
}
|
||||
|
||||
if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
|
||||
plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
|
||||
{
|
||||
free(buffer);
|
||||
fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
WaitForSingleObject(plugin->writeComplete, INFINITE);
|
||||
ResetEvent(plugin->writeComplete);
|
||||
}
|
||||
|
||||
fail:
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void closeChannel(Plugin* plugin)
|
||||
{
|
||||
if (debug)
|
||||
puts("rdp2tcp closing channel");
|
||||
|
||||
plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
|
||||
}
|
||||
|
||||
static void dataReceived(Plugin* plugin, void* pData, UINT32 dataLength, UINT32 totalLength,
|
||||
UINT32 dataFlags)
|
||||
{
|
||||
DWORD dwWritten;
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_SUSPEND)
|
||||
{
|
||||
if (debug)
|
||||
puts("rdp2tcp Channel Suspend");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_RESUME)
|
||||
{
|
||||
if (debug)
|
||||
puts("rdp2tcp Channel Resume");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (debug > 1)
|
||||
{
|
||||
printf("<%c%3u/%3u ", dataFlags & CHANNEL_FLAG_FIRST ? ' ' : '+', totalLength, dataLength);
|
||||
dumpData(pData, dataLength);
|
||||
}
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
if (!WriteFile(plugin->hStdInputWrite, &totalLength, sizeof(totalLength), &dwWritten, NULL))
|
||||
closeChannel(plugin);
|
||||
}
|
||||
|
||||
if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, NULL))
|
||||
closeChannel(plugin);
|
||||
}
|
||||
|
||||
static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam, DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength,
|
||||
UINT32 dataFlags)
|
||||
{
|
||||
Plugin* plugin = (Plugin*)lpUserParam;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||
dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_CANCELLED:
|
||||
free(pData);
|
||||
break;
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
SetEvent(plugin->writeComplete);
|
||||
free(pData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
|
||||
LPVOID pData, UINT dataLength)
|
||||
{
|
||||
Plugin* plugin = (Plugin*)lpUserParam;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
if (debug)
|
||||
puts("rdp2tcp connected");
|
||||
|
||||
plugin->writeComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
plugin->copyThread = CreateThread(NULL, 0, copyThread, plugin, 0, NULL);
|
||||
|
||||
if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
|
||||
pInitHandle, &plugin->openHandle, RDP2TCP_CHAN_NAME,
|
||||
VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
if (debug)
|
||||
puts("rdp2tcp disconnected");
|
||||
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
if (debug)
|
||||
puts("rdp2tcp terminated");
|
||||
|
||||
if (plugin->copyThread)
|
||||
{
|
||||
TerminateThread(plugin->copyThread, 0);
|
||||
CloseHandle(plugin->writeComplete);
|
||||
}
|
||||
|
||||
CloseHandle(plugin->hStdInputWrite);
|
||||
CloseHandle(plugin->hStdOutputRead);
|
||||
TerminateProcess(plugin->hProcess, 0);
|
||||
CloseHandle(plugin->hProcess);
|
||||
free(plugin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
|
||||
#else
|
||||
#define VirtualChannelEntryEx FREERDP_API VirtualChannelEntryEx
|
||||
#endif
|
||||
BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
|
||||
{
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
|
||||
CHANNEL_DEF channelDef;
|
||||
Plugin* plugin = (Plugin*)calloc(1, sizeof(Plugin));
|
||||
|
||||
if (!plugin)
|
||||
return FALSE;
|
||||
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
assert(pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX) &&
|
||||
pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
|
||||
plugin->initHandle = pInitHandle;
|
||||
plugin->channelEntryPoints = *pEntryPointsEx;
|
||||
|
||||
if (init_external_addin(plugin) < 0)
|
||||
return FALSE;
|
||||
|
||||
strncpy(channelDef.name, RDP2TCP_CHAN_NAME, sizeof(channelDef.name));
|
||||
channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
|
||||
|
||||
if (pEntryPointsEx->pVirtualChannelInitEx(plugin, NULL, pInitHandle, &channelDef, 1,
|
||||
VIRTUAL_CHANNEL_VERSION_WIN2000,
|
||||
VirtualChannelInitEventEx) != CHANNEL_RC_OK)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@ -41,9 +41,11 @@
|
||||
|
||||
#include "devman.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpdr.client")
|
||||
|
||||
static void devman_device_free(void* obj)
|
||||
{
|
||||
DEVICE* device = (DEVICE*) obj;
|
||||
DEVICE* device = (DEVICE*)obj;
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
@ -58,21 +60,21 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
||||
if (!rdpdr)
|
||||
return NULL;
|
||||
|
||||
devman = (DEVMAN*) calloc(1, sizeof(DEVMAN));
|
||||
devman = (DEVMAN*)calloc(1, sizeof(DEVMAN));
|
||||
|
||||
if (!devman)
|
||||
{
|
||||
WLog_INFO(TAG, "calloc failed!");
|
||||
WLog_Print(rdpdr->log, WLOG_INFO, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devman->plugin = (void*) rdpdr;
|
||||
devman->plugin = (void*)rdpdr;
|
||||
devman->id_sequence = 1;
|
||||
devman->devices = ListDictionary_New(TRUE);
|
||||
|
||||
if (!devman->devices)
|
||||
{
|
||||
WLog_INFO(TAG, "ListDictionary_New failed!");
|
||||
WLog_Print(rdpdr->log, WLOG_INFO, "ListDictionary_New failed!");
|
||||
free(devman);
|
||||
return NULL;
|
||||
}
|
||||
@ -94,7 +96,7 @@ void devman_unregister_device(DEVMAN* devman, void* key)
|
||||
if (!devman || !key)
|
||||
return;
|
||||
|
||||
device = (DEVICE*) ListDictionary_Remove(devman->devices, key);
|
||||
device = (DEVICE*)ListDictionary_Remove(devman->devices, key);
|
||||
|
||||
if (device)
|
||||
devman_device_free(device);
|
||||
@ -113,11 +115,11 @@ static UINT devman_register_device(DEVMAN* devman, DEVICE* device)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
device->id = devman->id_sequence++;
|
||||
key = (void*)(size_t) device->id;
|
||||
key = (void*)(size_t)device->id;
|
||||
|
||||
if (!ListDictionary_Add(devman->devices, key, device))
|
||||
{
|
||||
WLog_INFO(TAG, "ListDictionary_Add failed!");
|
||||
WLog_INFO(TAG, "ListDictionary_Add failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -127,12 +129,12 @@ static UINT devman_register_device(DEVMAN* devman, DEVICE* device)
|
||||
DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id)
|
||||
{
|
||||
DEVICE* device = NULL;
|
||||
void* key = (void*)(size_t) id;
|
||||
void* key = (void*)(size_t)id;
|
||||
|
||||
if (!devman)
|
||||
return NULL;
|
||||
|
||||
device = (DEVICE*) ListDictionary_GetItemValue(devman->devices, key);
|
||||
device = (DEVICE*)ListDictionary_GetItemValue(devman->devices, key);
|
||||
return device;
|
||||
}
|
||||
|
||||
@ -150,7 +152,7 @@ DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type)
|
||||
|
||||
for (x = 0; x < count; x++)
|
||||
{
|
||||
DEVICE* cur = (DEVICE*) ListDictionary_GetItemValue(devman->devices, (void*)keys[x]);
|
||||
DEVICE* cur = (DEVICE*)ListDictionary_GetItemValue(devman->devices, (void*)keys[x]);
|
||||
|
||||
if (!cur)
|
||||
continue;
|
||||
@ -167,23 +169,28 @@ DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type)
|
||||
return device;
|
||||
}
|
||||
|
||||
static char DRIVE_SERVICE_NAME[] = "drive";
|
||||
static char PRINTER_SERVICE_NAME[] = "printer";
|
||||
static char SMARTCARD_SERVICE_NAME[] = "smartcard";
|
||||
static char SERIAL_SERVICE_NAME[] = "serial";
|
||||
static char PARALLEL_SERVICE_NAME[] = "parallel";
|
||||
static const char DRIVE_SERVICE_NAME[] = "drive";
|
||||
static const char PRINTER_SERVICE_NAME[] = "printer";
|
||||
static const char SMARTCARD_SERVICE_NAME[] = "smartcard";
|
||||
static const char SERIAL_SERVICE_NAME[] = "serial";
|
||||
static const char PARALLEL_SERVICE_NAME[] = "parallel";
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext* rdpcontext)
|
||||
UINT devman_load_device_service(DEVMAN* devman, const RDPDR_DEVICE* device, rdpContext* rdpcontext)
|
||||
{
|
||||
char* ServiceName = NULL;
|
||||
const char* ServiceName = NULL;
|
||||
DEVICE_SERVICE_ENTRY_POINTS ep;
|
||||
PDEVICE_SERVICE_ENTRY entry = NULL;
|
||||
union {
|
||||
const RDPDR_DEVICE* cdp;
|
||||
RDPDR_DEVICE* dp;
|
||||
} devconv;
|
||||
|
||||
devconv.cdp = device;
|
||||
if (!devman || !device || !rdpcontext)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
@ -200,27 +207,27 @@ UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext
|
||||
|
||||
if (!ServiceName)
|
||||
{
|
||||
WLog_INFO(TAG, "ServiceName %s did not match!", ServiceName);
|
||||
WLog_INFO(TAG, "ServiceName %s did not match!", ServiceName);
|
||||
return ERROR_INVALID_NAME;
|
||||
}
|
||||
|
||||
if (device->Name)
|
||||
WLog_INFO(TAG, "Loading device service %s [%s] (static)", ServiceName, device->Name);
|
||||
WLog_INFO(TAG, "Loading device service %s [%s] (static)", ServiceName, device->Name);
|
||||
else
|
||||
WLog_INFO(TAG, "Loading device service %s (static)", ServiceName);
|
||||
WLog_INFO(TAG, "Loading device service %s (static)", ServiceName);
|
||||
|
||||
entry = (PDEVICE_SERVICE_ENTRY) freerdp_load_channel_addin_entry(ServiceName, NULL,
|
||||
"DeviceServiceEntry", 0);
|
||||
entry = (PDEVICE_SERVICE_ENTRY)freerdp_load_channel_addin_entry(ServiceName, NULL,
|
||||
"DeviceServiceEntry", 0);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
WLog_INFO(TAG, "freerdp_load_channel_addin_entry failed!");
|
||||
WLog_INFO(TAG, "freerdp_load_channel_addin_entry failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ep.devman = devman;
|
||||
ep.RegisterDevice = devman_register_device;
|
||||
ep.device = device;
|
||||
ep.device = devconv.dp;
|
||||
ep.rdpcontext = rdpcontext;
|
||||
return entry(&ep);
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
void devman_unregister_device(DEVMAN* devman, void* key);
|
||||
UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext* rdpcontext);
|
||||
UINT devman_load_device_service(DEVMAN* devman, const RDPDR_DEVICE* device, rdpContext* rdpcontext);
|
||||
DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id);
|
||||
DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type);
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ static UINT irp_complete(IRP* irp)
|
||||
rdpdrPlugin* rdpdr;
|
||||
UINT error;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) irp->devman->plugin;
|
||||
rdpdr = (rdpdrPlugin*)irp->devman->plugin;
|
||||
|
||||
pos = Stream_GetPosition(irp->output);
|
||||
Stream_SetPosition(irp->output, RDPDR_DEVICE_IO_RESPONSE_LENGTH - 4);
|
||||
@ -73,17 +73,16 @@ static UINT irp_complete(IRP* irp)
|
||||
error = rdpdr_send(rdpdr, irp->output);
|
||||
irp->output = NULL;
|
||||
|
||||
irp_free(irp);
|
||||
return error;
|
||||
return irp_free(irp);
|
||||
}
|
||||
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
|
||||
{
|
||||
IRP* irp;
|
||||
DEVICE* device;
|
||||
UINT32 DeviceId;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 20)
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20))
|
||||
{
|
||||
if (error)
|
||||
*error = ERROR_INVALID_DATA;
|
||||
@ -95,49 +94,48 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
||||
|
||||
if (!device)
|
||||
{
|
||||
WLog_WARN(TAG, "devman_get_device_by_id failed!");
|
||||
WLog_Print(log, WLOG_WARN, "devman_get_device_by_id failed!");
|
||||
if (error)
|
||||
*error = CHANNEL_RC_OK;
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
||||
irp = (IRP*) _aligned_malloc(sizeof(IRP), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
irp = (IRP*)_aligned_malloc(sizeof(IRP), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
|
||||
if (!irp)
|
||||
{
|
||||
WLog_ERR(TAG, "_aligned_malloc failed!");
|
||||
WLog_Print(log, WLOG_ERROR, "_aligned_malloc failed!");
|
||||
if (error)
|
||||
*error = CHANNEL_RC_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ZeroMemory(irp, sizeof(IRP));
|
||||
|
||||
irp->input = s;
|
||||
irp->device = device;
|
||||
irp->devman = devman;
|
||||
|
||||
Stream_Read_UINT32(s, irp->FileId); /* FileId (4 bytes) */
|
||||
Stream_Read_UINT32(s, irp->CompletionId); /* CompletionId (4 bytes) */
|
||||
Stream_Read_UINT32(s, irp->FileId); /* FileId (4 bytes) */
|
||||
Stream_Read_UINT32(s, irp->CompletionId); /* CompletionId (4 bytes) */
|
||||
Stream_Read_UINT32(s, irp->MajorFunction); /* MajorFunction (4 bytes) */
|
||||
Stream_Read_UINT32(s, irp->MinorFunction); /* MinorFunction (4 bytes) */
|
||||
|
||||
irp->output = Stream_New(NULL, 256);
|
||||
if (!irp->output)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
|
||||
_aligned_free(irp);
|
||||
if (error)
|
||||
*error = CHANNEL_RC_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
Stream_Write_UINT16(irp->output, RDPDR_CTYP_CORE); /* Component (2 bytes) */
|
||||
Stream_Write_UINT16(irp->output, RDPDR_CTYP_CORE); /* Component (2 bytes) */
|
||||
Stream_Write_UINT16(irp->output, PAKID_CORE_DEVICE_IOCOMPLETION); /* PacketId (2 bytes) */
|
||||
Stream_Write_UINT32(irp->output, DeviceId); /* DeviceId (4 bytes) */
|
||||
Stream_Write_UINT32(irp->output, irp->CompletionId); /* CompletionId (4 bytes) */
|
||||
Stream_Write_UINT32(irp->output, 0); /* IoStatus (4 bytes) */
|
||||
Stream_Write_UINT32(irp->output, DeviceId); /* DeviceId (4 bytes) */
|
||||
Stream_Write_UINT32(irp->output, irp->CompletionId); /* CompletionId (4 bytes) */
|
||||
Stream_Write_UINT32(irp->output, 0); /* IoStatus (4 bytes) */
|
||||
|
||||
irp->Complete = irp_complete;
|
||||
irp->Discard = irp_free;
|
||||
|
||||
@ -21,8 +21,9 @@
|
||||
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
||||
#define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error);
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user