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
|
- libgsm1-dev
|
||||||
- libavcodec-dev
|
- libavcodec-dev
|
||||||
- libavutil-dev
|
- libavutil-dev
|
||||||
- libx264-dev
|
|
||||||
- libxext-dev
|
- libxext-dev
|
||||||
|
- ninja-build
|
||||||
|
- libsystemd-dev
|
||||||
|
- libwayland-dev
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- ulimit -c unlimited -S
|
- ulimit -c unlimited -S
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- sudo hostname travis-ci.local
|
- 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
|
||||||
- make test
|
- make test
|
||||||
|
|||||||
369
CMakeLists.txt
369
CMakeLists.txt
@ -34,9 +34,9 @@ if(NOT DEFINED FREERDP_VENDOR)
|
|||||||
set(FREERDP_VENDOR 1)
|
set(FREERDP_VENDOR 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_COLOR_MAKEFILE ON)
|
option(CMAKE_COLOR_MAKEFILE "colorful CMake makefile" ON)
|
||||||
|
option(CMAKE_VERBOSE_MAKEFILE "verbose CMake makefile" ON)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
option(CMAKE_POSITION_INDEPENDENT_CODE "build with position independent code (-fPIC or -fPIE)" ON)
|
||||||
|
|
||||||
# Include our extra modules
|
# Include our extra modules
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
||||||
@ -51,6 +51,10 @@ endif()
|
|||||||
include(CheckCmakeCompat)
|
include(CheckCmakeCompat)
|
||||||
|
|
||||||
# Include cmake modules
|
# Include cmake modules
|
||||||
|
if(WITH_CLANG_FORMAT)
|
||||||
|
include(ClangFormat)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
include(CheckSymbolExists)
|
include(CheckSymbolExists)
|
||||||
@ -70,6 +74,10 @@ include(InstallFreeRDPMan)
|
|||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
include(SetFreeRDPCMakeInstallDir)
|
include(SetFreeRDPCMakeInstallDir)
|
||||||
|
|
||||||
|
if (DEFINE_NO_DEPRECATED)
|
||||||
|
add_definitions(-DDEFINE_NO_DEPRECATED)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Soname versioning
|
# Soname versioning
|
||||||
set(BUILD_NUMBER 0)
|
set(BUILD_NUMBER 0)
|
||||||
if ($ENV{BUILD_NUMBER})
|
if ($ENV{BUILD_NUMBER})
|
||||||
@ -77,22 +85,22 @@ if ($ENV{BUILD_NUMBER})
|
|||||||
endif()
|
endif()
|
||||||
set(WITH_LIBRARY_VERSIONING "ON")
|
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")
|
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)
|
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||||
git_get_exact_tag(_GIT_TAG --tags --always)
|
git_get_exact_tag(_GIT_TAG --tags --always)
|
||||||
if (NOT ${_GIT_TAG} STREQUAL "n/a")
|
if (NOT ${_GIT_TAG} STREQUAL "n/a")
|
||||||
set(RAW_VERSTION_STRING ${_GIT_TAG})
|
set(RAW_VERSION_STRING ${_GIT_TAG})
|
||||||
endif()
|
endif()
|
||||||
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]+)-?(.*)")
|
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}" "\\1" FREERDP_VERSION_MAJOR "${RAW_VERSION_STRING}")
|
||||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\2" FREERDP_VERSION_MINOR "${RAW_VERSTION_STRING}")
|
string(REGEX REPLACE "${VERSION_REGEX}" "\\2" FREERDP_VERSION_MINOR "${RAW_VERSION_STRING}")
|
||||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\3" FREERDP_VERSION_REVISION "${RAW_VERSTION_STRING}")
|
string(REGEX REPLACE "${VERSION_REGEX}" "\\3" FREERDP_VERSION_REVISION "${RAW_VERSION_STRING}")
|
||||||
string(REGEX REPLACE "${VERSION_REGEX}" "\\4" FREERDP_VERSION_SUFFIX "${RAW_VERSTION_STRING}")
|
string(REGEX REPLACE "${VERSION_REGEX}" "\\4" FREERDP_VERSION_SUFFIX "${RAW_VERSION_STRING}")
|
||||||
|
|
||||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}")
|
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}")
|
||||||
set(FREERDP_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}.${FREERDP_VERSION_REVISION}")
|
set(FREERDP_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}.${FREERDP_VERSION_REVISION}")
|
||||||
@ -103,6 +111,24 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
message("FREERDP_VERSION=${FREERDP_VERSION_FULL}")
|
message("FREERDP_VERSION=${FREERDP_VERSION_FULL}")
|
||||||
|
|
||||||
|
if(EXISTS "${PROJECT_SOURCE_DIR}/.source_version" )
|
||||||
|
file(READ ${PROJECT_SOURCE_DIR}/.source_version GIT_REVISION)
|
||||||
|
|
||||||
|
string(STRIP ${GIT_REVISION} GIT_REVISION)
|
||||||
|
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||||
|
git_get_exact_tag(GIT_REVISION --tags --always)
|
||||||
|
|
||||||
|
if (${GIT_REVISION} STREQUAL "n/a")
|
||||||
|
git_rev_parse (GIT_REVISION --short)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT GIT_REVISION)
|
||||||
|
set(GIT_REVISION ${FREERDP_VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Git Revision ${GIT_REVISION}")
|
||||||
|
|
||||||
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
||||||
|
|
||||||
# Compatibility options
|
# Compatibility options
|
||||||
@ -139,25 +165,11 @@ if(CCACHE AND WITH_CCACHE)
|
|||||||
endif()
|
endif()
|
||||||
endif(CCACHE AND WITH_CCACHE)
|
endif(CCACHE AND WITH_CCACHE)
|
||||||
|
|
||||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_version" )
|
|
||||||
file(READ ${CMAKE_SOURCE_DIR}/.source_version GIT_REVISION)
|
|
||||||
|
|
||||||
string(STRIP ${GIT_REVISION} GIT_REVISION)
|
|
||||||
else()
|
|
||||||
git_get_exact_tag(GIT_REVISION --tags --always)
|
|
||||||
|
|
||||||
if (${GIT_REVISION} STREQUAL "n/a")
|
|
||||||
git_rev_parse (GIT_REVISION --short)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_CROSSCOMPILING)
|
if(CMAKE_CROSSCOMPILING)
|
||||||
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||||
endif(CMAKE_CROSSCOMPILING)
|
endif(CMAKE_CROSSCOMPILING)
|
||||||
# /Allow to search the host machine for git/ccache
|
# /Allow to search the host machine for git/ccache
|
||||||
|
|
||||||
message(STATUS "Git Revision ${GIT_REVISION}")
|
|
||||||
|
|
||||||
# Turn on solution folders (2.8.4+)
|
# Turn on solution folders (2.8.4+)
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
@ -213,7 +225,7 @@ endif()
|
|||||||
if(MSVC)
|
if(MSVC)
|
||||||
include(MSVCRuntime)
|
include(MSVCRuntime)
|
||||||
if(NOT DEFINED MSVC_RUNTIME)
|
if(NOT DEFINED MSVC_RUNTIME)
|
||||||
set(MSVC_RUNTIME "dynamic")
|
set(MSVC_RUNTIME "dynamic" CACHE STRING "MSVC runtime type [dynamic|static]")
|
||||||
endif()
|
endif()
|
||||||
if(MSVC_RUNTIME STREQUAL "static")
|
if(MSVC_RUNTIME STREQUAL "static")
|
||||||
if(BUILD_SHARED_LIBS)
|
if(BUILD_SHARED_LIBS)
|
||||||
@ -337,106 +349,99 @@ if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
|||||||
endif()
|
endif()
|
||||||
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
|
# Enable address sanitizer, where supported and when required
|
||||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCC)
|
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)
|
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 "")
|
file(WRITE ${CMAKE_BINARY_DIR}/foo.txt "")
|
||||||
if(WITH_SANITIZE_ADDRESS)
|
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=address" fsanitize-address)
|
||||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
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)
|
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_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_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_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
|
||||||
|
|
||||||
if(fsanitize-blacklist)
|
if(fsanitize-blacklist)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-address-sanitizer.txt")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-address-sanitizer.txt")
|
||||||
endif(fsanitize-blacklist)
|
endif(fsanitize-blacklist)
|
||||||
|
|
||||||
if(fsanitize-address-use-after-scope)
|
if(fsanitize-address-use-after-scope)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-address-use-after-scope")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-address-use-after-scope")
|
||||||
endif(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()
|
|
||||||
elseif(WITH_SANITIZE_MEMORY)
|
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=memory" fsanitize-memory)
|
||||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
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-use-after-dtor" fsanitize-memory-use-after-dtor)
|
||||||
CHECK_C_COMPILER_FLAG ("-fsanitize-memory-track-origins" fsanitize-memory-track-origins)
|
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_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_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_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory")
|
|
||||||
|
|
||||||
if(fsanitize-blacklist)
|
if(fsanitize-blacklist)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-memory-sanitizer.txt")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-memory-sanitizer.txt")
|
||||||
endif(fsanitize-blacklist)
|
endif(fsanitize-blacklist)
|
||||||
|
|
||||||
if (fsanitize-memory-use-after-dtor)
|
if (fsanitize-memory-use-after-dtor)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-use-after-dtor")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-use-after-dtor")
|
||||||
endif(fsanitize-memory-use-after-dtor)
|
endif(fsanitize-memory-use-after-dtor)
|
||||||
|
|
||||||
if (fsanitize-memory-track-origins)
|
if (fsanitize-memory-track-origins)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins")
|
||||||
endif(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()
|
|
||||||
elseif(WITH_SANITIZE_THREAD)
|
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=thread" fsanitize-thread)
|
||||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
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} -fsanitize=thread")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
if(fsanitize-blacklist)
|
||||||
endif()
|
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()
|
endif()
|
||||||
|
|
||||||
file(REMOVE ${CMAKE_BINARY_DIR}/foo.txt)
|
file(REMOVE ${CMAKE_BINARY_DIR}/foo.txt)
|
||||||
|
set(CMAKE_REQUIRED_LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS_SAVED})
|
||||||
|
|
||||||
if (WITH_NO_UNDEFINED)
|
if (WITH_NO_UNDEFINED)
|
||||||
set(CMAKE_REQUIRED_FLAGS "-Wl,--no-undefined")
|
|
||||||
CHECK_C_COMPILER_FLAG (-Wl,--no-undefined no-undefined)
|
CHECK_C_COMPILER_FLAG (-Wl,--no-undefined no-undefined)
|
||||||
unset(CMAKE_REQUIRED_FLAGS)
|
|
||||||
|
|
||||||
if(no-undefined)
|
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" )
|
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" )
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVED})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
@ -493,7 +498,7 @@ if(WIN32)
|
|||||||
string(TIMESTAMP RC_VERSION_YEAR "%Y")
|
string(TIMESTAMP RC_VERSION_YEAR "%Y")
|
||||||
|
|
||||||
if(NOT DEFINED CMAKE_WINDOWS_VERSION)
|
if(NOT DEFINED CMAKE_WINDOWS_VERSION)
|
||||||
set(CMAKE_WINDOWS_VERSION "WINXP")
|
set(CMAKE_WINDOWS_VERSION "WIN7")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_WINDOWS_VERSION STREQUAL "WINXP")
|
if(CMAKE_WINDOWS_VERSION STREQUAL "WINXP")
|
||||||
@ -510,7 +515,7 @@ if(WIN32)
|
|||||||
set(RC_VERSION_VENDOR ${VENDOR})
|
set(RC_VERSION_VENDOR ${VENDOR})
|
||||||
set(RC_VERSION_PRODUCT ${PRODUCT})
|
set(RC_VERSION_PRODUCT ${PRODUCT})
|
||||||
set(RC_VERSION_PATCH ${BUILD_NUMBER})
|
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)
|
if (FREERDP_EXTERNAL_SSL_PATH)
|
||||||
set(OPENSSL_ROOT_DIR ${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)
|
if(NOT IOS)
|
||||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||||
check_include_files(execinfo.h HAVE_EXECINFO_H)
|
|
||||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
||||||
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
||||||
@ -535,6 +539,18 @@ if(NOT IOS)
|
|||||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||||
|
check_include_files(execinfo.h HAVE_EXECINFO_HEADER)
|
||||||
|
if (HAVE_EXECINFO_HEADER)
|
||||||
|
check_symbol_exists(backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
|
||||||
|
check_symbol_exists(backtrace_symbols execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS)
|
||||||
|
check_symbol_exists(backtrace_symbols_fd execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
|
||||||
|
|
||||||
|
# Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define
|
||||||
|
# the backtrace functions. Disable detection for these cases
|
||||||
|
if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS AND HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
|
||||||
|
set(HAVE_EXECINFO_H ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
set(HAVE_FCNTL_H 1)
|
set(HAVE_FCNTL_H 1)
|
||||||
set(HAVE_UNISTD_H 1)
|
set(HAVE_UNISTD_H 1)
|
||||||
@ -593,6 +609,11 @@ if(ANDROID)
|
|||||||
set (WITH_NEON OFF)
|
set (WITH_NEON OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(ANDROID_ABI STREQUAL arm64-v8a)
|
||||||
|
# https://github.com/android/ndk/issues/910
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
|
||||||
|
endif()
|
||||||
|
|
||||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||||
add_definitions(-DNDK_DEBUG=1)
|
add_definitions(-DNDK_DEBUG=1)
|
||||||
|
|
||||||
@ -611,7 +632,7 @@ if(ANDROID)
|
|||||||
endif()
|
endif()
|
||||||
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}/ )
|
list (APPEND CMAKE_LIBRARY_PATH ${FREERDP_EXTERNAL_PATH}/${ANDROID_ABI}/ )
|
||||||
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH )
|
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH )
|
||||||
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH )
|
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH )
|
||||||
@ -622,26 +643,6 @@ if(ANDROID)
|
|||||||
endif(WITH_GPROF)
|
endif(WITH_GPROF)
|
||||||
endif()
|
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)
|
if(WITH_VALGRIND_MEMCHECK)
|
||||||
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||||
else()
|
else()
|
||||||
@ -667,6 +668,11 @@ if(UNIX OR CYGWIN)
|
|||||||
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES m)
|
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES m)
|
||||||
set(X11_FEATURE_TYPE "RECOMMENDED")
|
set(X11_FEATURE_TYPE "RECOMMENDED")
|
||||||
set(WAYLAND_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()
|
else()
|
||||||
set(X11_FEATURE_TYPE "DISABLED")
|
set(X11_FEATURE_TYPE "DISABLED")
|
||||||
set(WAYLAND_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_PURPOSE "sound")
|
||||||
set(ALSA_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection")
|
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_PURPOSE "sound")
|
||||||
set(PULSE_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection")
|
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_PURPOSE "printing")
|
||||||
set(CUPS_FEATURE_DESCRIPTION "printer device redirection")
|
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_PURPOSE "smart card")
|
||||||
set(PCSC_FEATURE_DESCRIPTION "smart card device redirection")
|
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_TYPE "OPTIONAL")
|
||||||
set(VAAPI_FEATURE_PURPOSE "multimedia")
|
set(VAAPI_FEATURE_PURPOSE "multimedia")
|
||||||
set(VAAPI_FEATURE_DESCRIPTION "VA-API hardware acceleration for video playback")
|
set(VAAPI_FEATURE_DESCRIPTION "[experimental] VA-API hardware acceleration for video playback")
|
||||||
|
|
||||||
set(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(IPP_FEATURE_TYPE "OPTIONAL")
|
set(IPP_FEATURE_TYPE "OPTIONAL")
|
||||||
set(IPP_FEATURE_PURPOSE "performance")
|
set(IPP_FEATURE_PURPOSE "performance")
|
||||||
@ -742,14 +740,14 @@ set(JPEG_FEATURE_TYPE "OPTIONAL")
|
|||||||
set(JPEG_FEATURE_PURPOSE "codec")
|
set(JPEG_FEATURE_PURPOSE "codec")
|
||||||
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
||||||
|
|
||||||
set(X264_FEATURE_TYPE "OPTIONAL")
|
|
||||||
set(X264_FEATURE_PURPOSE "codec")
|
|
||||||
set(X264_FEATURE_DESCRIPTION "use x264 library")
|
|
||||||
|
|
||||||
set(OPENH264_FEATURE_TYPE "OPTIONAL")
|
set(OPENH264_FEATURE_TYPE "OPTIONAL")
|
||||||
set(OPENH264_FEATURE_PURPOSE "codec")
|
set(OPENH264_FEATURE_PURPOSE "codec")
|
||||||
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
||||||
|
|
||||||
|
set(OPENCL_FEATURE_TYPE "OPTIONAL")
|
||||||
|
set(OPENCL_FEATURE_PURPOSE "codec")
|
||||||
|
set(OPENCL_FEATURE_DESCRIPTION "[experimental] use OpenCL library")
|
||||||
|
|
||||||
set(GSM_FEATURE_TYPE "OPTIONAL")
|
set(GSM_FEATURE_TYPE "OPTIONAL")
|
||||||
set(GSM_FEATURE_PURPOSE "codec")
|
set(GSM_FEATURE_PURPOSE "codec")
|
||||||
set(GSM_FEATURE_DESCRIPTION "GSM audio codec library")
|
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_TYPE "OPTIONAL")
|
||||||
set(FAAC_FEATURE_PURPOSE "codec")
|
set(FAAC_FEATURE_PURPOSE "codec")
|
||||||
set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library")
|
set(FAAC_FEATURE_DESCRIPTION "[experimental] FAAC AAC audio codec library")
|
||||||
|
|
||||||
set(SOXR_FEATURE_TYPE "OPTIONAL")
|
set(SOXR_FEATURE_TYPE "OPTIONAL")
|
||||||
set(SOXR_FEATURE_PURPOSE "codec")
|
set(SOXR_FEATURE_PURPOSE "codec")
|
||||||
@ -772,7 +770,7 @@ set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library")
|
|||||||
|
|
||||||
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
|
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
|
||||||
set(GSSAPI_FEATURE_PURPOSE "auth")
|
set(GSSAPI_FEATURE_PURPOSE "auth")
|
||||||
set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support")
|
set(GSSAPI_FEATURE_DESCRIPTION "[experimental] add kerberos support")
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(X11_FEATURE_TYPE "DISABLED")
|
set(X11_FEATURE_TYPE "DISABLED")
|
||||||
@ -785,15 +783,12 @@ if(WIN32)
|
|||||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||||
set(FFMPEG_FEATURE_TYPE "DISABLED")
|
set(FFMPEG_FEATURE_TYPE "DISABLED")
|
||||||
set(VAAPI_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")
|
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(FFMPEG_FEATURE_TYPE "OPTIONAL")
|
set(FFMPEG_FEATURE_TYPE "OPTIONAL")
|
||||||
set(VAAPI_FEATURE_TYPE "DISABLED")
|
set(VAAPI_FEATURE_TYPE "DISABLED")
|
||||||
set(GSTREAMER_1_0_FEATURE_TYPE "OPTIONAL")
|
|
||||||
set(X11_FEATURE_TYPE "OPTIONAL")
|
set(X11_FEATURE_TYPE "OPTIONAL")
|
||||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||||
set(OSS_FEATURE_TYPE "DISABLED")
|
set(OSS_FEATURE_TYPE "DISABLED")
|
||||||
@ -803,8 +798,6 @@ if(APPLE)
|
|||||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||||
set(GSTREAMER_1_0_FEATURE_TYPE "DISABLED")
|
|
||||||
set(GSTREAMER_0_10_FEATURE_TYPE "DISABLED")
|
|
||||||
endif()
|
endif()
|
||||||
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
||||||
endif()
|
endif()
|
||||||
@ -832,10 +825,7 @@ if(ANDROID)
|
|||||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||||
set(FFMPEG_FEATURE_TYPE "DISABLED")
|
|
||||||
set(VAAPI_FEATURE_TYPE "DISABLED")
|
set(VAAPI_FEATURE_TYPE "DISABLED")
|
||||||
set(GSTREAMER_1_0_FEATURE_TYPE "DISABLED")
|
|
||||||
set(GSTREAMER_0_10_FEATURE_TYPE "DISABLED")
|
|
||||||
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
||||||
endif()
|
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(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(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION})
|
||||||
find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION})
|
|
||||||
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
||||||
|
find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION})
|
||||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_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(LAME ${LAME_FEATURE_TYPE} ${LAME_FEATURE_PURPOSE} ${LAME_FEATURE_DESCRIPTION})
|
||||||
find_feature(FAAD2 ${FAAD2_FEATURE_TYPE} ${FAAD2_FEATURE_PURPOSE} ${FAAD2_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})
|
find_feature(GSSAPI ${GSSAPI_FEATURE_TYPE} ${GSSAPI_FEATURE_PURPOSE} ${GSSAPI_FEATURE_DESCRIPTION})
|
||||||
|
|
||||||
if (WITH_OPENH264 AND NOT WITH_OPENH264_LOADING)
|
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)
|
endif (WITH_OPENH264 AND NOT WITH_OPENH264_LOADING)
|
||||||
|
|
||||||
if ((WITH_FFMPEG OR WITH_DSP_FFMPEG) AND NOT FFMPEG_FOUND)
|
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.
|
# Deactivate FFmpeg backend for sound, if the version is too old.
|
||||||
# See libfreerdp/codec/dsp_ffmpeg.h
|
# See libfreerdp/codec/dsp_ffmpeg.h
|
||||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||||
|
if (EXISTS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h")
|
||||||
|
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h" AV_VERSION_FILE2 REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||||
|
list(APPEND AV_VERSION_FILE ${AV_VERSION_FILE2})
|
||||||
|
endif()
|
||||||
|
|
||||||
FOREACH(item ${AV_VERSION_FILE})
|
FOREACH(item ${AV_VERSION_FILE})
|
||||||
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
||||||
IF(litem)
|
IF(litem)
|
||||||
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
||||||
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
||||||
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||||
message(ERROR "invalid entry in libavcodec version header ${item}")
|
message(ERROR "invalid entry in libavcodec version header ${item}")
|
||||||
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||||
list(GET VSPLIT_LINE 0 VNAME)
|
list(GET VSPLIT_LINE 0 VNAME)
|
||||||
list(GET VSPLIT_LINE 1 VVALUE)
|
list(GET VSPLIT_LINE 1 VVALUE)
|
||||||
set(${VNAME} ${VVALUE})
|
set(${VNAME} ${VVALUE})
|
||||||
ENDIF(litem)
|
ENDIF(litem)
|
||||||
ENDFOREACH(item ${AV_VERSION_FILE})
|
ENDFOREACH(item ${AV_VERSION_FILE})
|
||||||
|
|
||||||
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
||||||
@ -945,11 +937,7 @@ if(MBEDTLS_FOUND)
|
|||||||
add_definitions("-DWITH_MBEDTLS")
|
add_definitions("-DWITH_MBEDTLS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (TARGET_ARCH MATCHES "sparc")
|
if (WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG OR WITH_MEDIACODEC)
|
||||||
set(HAVE_ALIGNED_REQUIRED 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WITH_X264 OR WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG)
|
|
||||||
set(WITH_GFX_H264 ON)
|
set(WITH_GFX_H264 ON)
|
||||||
else()
|
else()
|
||||||
set(WITH_GFX_H264 OFF)
|
set(WITH_GFX_H264 OFF)
|
||||||
@ -957,32 +945,42 @@ endif()
|
|||||||
|
|
||||||
# Android expects all libraries to be loadable
|
# Android expects all libraries to be loadable
|
||||||
# without paths.
|
# without paths.
|
||||||
if (ANDROID)
|
if (ANDROID OR WIN32 OR MAC_BUNDLE)
|
||||||
set(FREERDP_DATA_PATH "share")
|
set(FREERDP_DATA_PATH "share")
|
||||||
set(FREERDP_INSTALL_PREFIX ".")
|
if (NOT FREERDP_INSTALL_PREFIX)
|
||||||
set(FREERDP_LIBRARY_PATH ".")
|
set(FREERDP_INSTALL_PREFIX ".")
|
||||||
set(FREERDP_PLUGIN_PATH ".")
|
endif()
|
||||||
set(FREERDP_ADDIN_PATH ".")
|
set(FREERDP_LIBRARY_PATH ".")
|
||||||
else (ANDROID)
|
set(FREERDP_PLUGIN_PATH ".")
|
||||||
|
else()
|
||||||
set(FREERDP_DATA_PATH "${CMAKE_INSTALL_PREFIX}/share/freerdp${FREERDP_VERSION_MAJOR}")
|
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_LIBRARY_PATH "${CMAKE_INSTALL_LIBDIR}")
|
||||||
set(FREERDP_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}")
|
set(FREERDP_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}")
|
||||||
set(FREERDP_ADDIN_PATH "${FREERDP_PLUGIN_PATH}")
|
endif()
|
||||||
endif(ANDROID)
|
set(FREERDP_ADDIN_PATH "${FREERDP_PLUGIN_PATH}")
|
||||||
|
|
||||||
# Path to put extensions
|
# Path to put extensions
|
||||||
set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}/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
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_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
|
# RPATH configuration
|
||||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||||
@ -992,7 +990,16 @@ if (APPLE)
|
|||||||
else (APPLE)
|
else (APPLE)
|
||||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
if (NOT FREEBSD)
|
if (NOT FREEBSD)
|
||||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
if (NOT BUILTIN_CHANNELS)
|
||||||
|
if (NOT DEFINED WITH_PLUGIN_RPATH_ONLY)
|
||||||
|
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..:\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
|
||||||
|
else()
|
||||||
|
# we need to supply this run path, even if not using RPATH in general
|
||||||
|
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
@ -1055,15 +1062,6 @@ add_subdirectory(include)
|
|||||||
|
|
||||||
add_subdirectory(libfreerdp)
|
add_subdirectory(libfreerdp)
|
||||||
|
|
||||||
if (IOS)
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0")
|
|
||||||
if (IOS_PLATFORM MATCHES "SIMULATOR")
|
|
||||||
set(CMAKE_OSX_SYSROOT "iphonesimulator")
|
|
||||||
else()
|
|
||||||
set(CMAKE_OSX_SYSROOT "iphoneos")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# RdTk
|
# RdTk
|
||||||
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
|
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
|
||||||
include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
|
include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
|
||||||
@ -1098,6 +1096,9 @@ if(WITH_SERVER)
|
|||||||
add_subdirectory(server)
|
add_subdirectory(server)
|
||||||
endif()
|
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
|
# Packaging
|
||||||
|
|
||||||
set(CMAKE_CPACK_INCLUDE_FILE "CMakeCPack.cmake")
|
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.
|
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
|
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.
|
interoperability can finally liberate your computing experience.
|
||||||
|
|
||||||
Resources
|
## Resources
|
||||||
---------
|
|
||||||
|
|
||||||
Project website: https://www.freerdp.com/
|
Project website: https://www.freerdp.com/
|
||||||
Issue tracker: https://github.com/FreeRDP/FreeRDP/issues
|
Issue tracker: https://github.com/FreeRDP/FreeRDP/issues
|
||||||
Sources: https://github.com/FreeRDP/FreeRDP/
|
Sources: https://github.com/FreeRDP/FreeRDP/
|
||||||
Downloads: https://pub.freerdp.com/releases/
|
Downloads: https://pub.freerdp.com/releases/
|
||||||
Wiki: https://github.com/FreeRDP/FreeRDP/wiki
|
Wiki: https://github.com/FreeRDP/FreeRDP/wiki
|
||||||
API documentation: https://pub.freerdp.com/api/
|
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
|
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:
|
Information regarding the Microsoft Open Specifications can be found at:
|
||||||
http://www.microsoft.com/openspecifications/
|
http://www.microsoft.com/openspecifications/
|
||||||
@ -27,8 +24,7 @@ http://www.microsoft.com/openspecifications/
|
|||||||
A list of reference documentation is maintained here:
|
A list of reference documentation is maintained here:
|
||||||
https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation
|
https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation
|
||||||
|
|
||||||
Compilation
|
## Compilation
|
||||||
-----------
|
|
||||||
|
|
||||||
Instructions on how to get started compiling FreeRDP can be found on the wiki:
|
Instructions on how to get started compiling FreeRDP can be found on the wiki:
|
||||||
https://github.com/FreeRDP/FreeRDP/wiki/Compilation
|
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}" CHANNEL_OPTION)
|
||||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
|
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
|
||||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
|
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
|
||||||
|
string(TOUPPER "${CHANNEL_TYPE}" CHANNEL_TYPE)
|
||||||
|
|
||||||
if(${${CHANNEL_CLIENT_OPTION}})
|
if(${${CHANNEL_CLIENT_OPTION}})
|
||||||
set(OPTION_CLIENT_DEFAULT ${${CHANNEL_CLIENT_OPTION}})
|
set(OPTION_CLIENT_DEFAULT ${${CHANNEL_CLIENT_OPTION}})
|
||||||
endif()
|
endif()
|
||||||
@ -52,23 +53,30 @@ macro(define_channel_options)
|
|||||||
set(CHANNEL_DEFAULT ${OPTION_DEFAULT})
|
set(CHANNEL_DEFAULT ${OPTION_DEFAULT})
|
||||||
|
|
||||||
set(CHANNEL_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel")
|
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)
|
endmacro(define_channel_options)
|
||||||
|
|
||||||
macro(define_channel_client_options _channel_client_default)
|
macro(define_channel_client_options _channel_client_default)
|
||||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
|
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")
|
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)
|
${_channel_client_default} "${CHANNEL_OPTION}" OFF)
|
||||||
endmacro(define_channel_client_options)
|
endmacro(define_channel_client_options)
|
||||||
|
|
||||||
macro(define_channel_server_options _channel_server_default)
|
macro(define_channel_server_options _channel_server_default)
|
||||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
|
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")
|
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)
|
${_channel_server_default} "${CHANNEL_OPTION}" OFF)
|
||||||
endmacro(define_channel_server_options)
|
endmacro(define_channel_server_options)
|
||||||
|
|
||||||
@ -167,7 +175,12 @@ macro(client_channel_install _targets _destination)
|
|||||||
endmacro(client_channel_install)
|
endmacro(client_channel_install)
|
||||||
|
|
||||||
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
|
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.
|
# On windows create dll version information.
|
||||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||||
if (WIN32)
|
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)
|
set ( ${_module_prefix}_SRCS ${${_module_prefix}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||||
else()
|
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
else()
|
||||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||||
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
|
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||||
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
|
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
|
||||||
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
|
||||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
||||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||||
endif()
|
|
||||||
endif()
|
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)
|
endmacro(add_channel_client_library)
|
||||||
|
|
||||||
macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_name _type _dynamic _entry)
|
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.
|
# On windows create dll version information.
|
||||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||||
if (WIN32)
|
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)
|
set ( ${_module_prefix}_SRCS ${${_module_prefix}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||||
else()
|
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
else()
|
||||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||||
set(${_module_prefix}_TYPE ${_type} PARENT_SCOPE)
|
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||||
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
set(${_module_prefix}_TYPE ${_type} PARENT_SCOPE)
|
||||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
|
||||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
||||||
endif()
|
target_link_libraries(${_module_name} ${${_module_prefix}_LIBS})
|
||||||
endif()
|
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)
|
endmacro(add_channel_client_subsystem_library)
|
||||||
|
|
||||||
macro(add_channel_server_library _module_prefix _module_name _channel_name _dynamic _entry)
|
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.
|
# On windows create dll version information.
|
||||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@ -266,6 +295,23 @@ endmacro(add_channel_server_library)
|
|||||||
set(FILENAME "ChannelOptions.cmake")
|
set(FILENAME "ChannelOptions.cmake")
|
||||||
file(GLOB FILEPATHS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/${FILENAME}")
|
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})
|
foreach(FILEPATH ${FILEPATHS})
|
||||||
if(${FILEPATH} MATCHES "^([^/]*)/+${FILENAME}")
|
if(${FILEPATH} MATCHES "^([^/]*)/+${FILENAME}")
|
||||||
string(REGEX REPLACE "^([^/]*)/+${FILENAME}" "\\1" DIR ${FILEPATH})
|
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 2022 Armin Novak <anovak@thincast.com>
|
||||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
# Copyright 2022 Thincast Technologies GmbH
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -15,8 +16,12 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
option(WITH_OPENSLES "Enable sound and microphone redirection using OpenSLES" ON)
|
define_channel("ainput")
|
||||||
|
|
||||||
set(ANDROID_APP_TARGET_SDK 21 CACHE STRING "Application target android SDK")
|
if(WITH_CLIENT_CHANNELS)
|
||||||
set(ANDROID_APP_MIN_SDK 14 CACHE STRING "Application minimum android SDK requirement")
|
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WITH_SERVER_CHANNELS)
|
||||||
|
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||||
|
endif()
|
||||||
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,
|
static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
|
||||||
snd_pcm_t* capture_handle)
|
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
SSIZE_T s;
|
||||||
UINT32 channels = alsa->aformat.nChannels;
|
UINT32 channels = alsa->aformat.nChannels;
|
||||||
snd_pcm_hw_params_t* hw_params;
|
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)
|
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||||
{
|
{
|
||||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_hw_params_malloc (%s)",
|
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_hw_params_malloc (%s)", snd_strerror(error));
|
||||||
snd_strerror(error));
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_pcm_hw_params_any(capture_handle, hw_params);
|
snd_pcm_hw_params_any(capture_handle, hw_params);
|
||||||
snd_pcm_hw_params_set_access(capture_handle, hw_params,
|
snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
||||||
snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
|
snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
|
||||||
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params,
|
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->aformat.nSamplesPerSec, NULL);
|
||||||
&alsa->aformat.nSamplesPerSec, NULL);
|
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &channels);
|
||||||
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params,
|
|
||||||
&channels);
|
|
||||||
snd_pcm_hw_params(capture_handle, hw_params);
|
snd_pcm_hw_params(capture_handle, hw_params);
|
||||||
snd_pcm_hw_params_free(hw_params);
|
snd_pcm_hw_params_free(hw_params);
|
||||||
snd_pcm_prepare(capture_handle);
|
snd_pcm_prepare(capture_handle);
|
||||||
alsa->aformat.nChannels = channels;
|
if (channels > UINT16_MAX)
|
||||||
alsa->bytes_per_frame = snd_pcm_format_size(format, 1) * channels;
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,12 +125,11 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
|||||||
long error;
|
long error;
|
||||||
BYTE* buffer;
|
BYTE* buffer;
|
||||||
snd_pcm_t* capture_handle = NULL;
|
snd_pcm_t* capture_handle = NULL;
|
||||||
AudinALSADevice* alsa = (AudinALSADevice*) arg;
|
AudinALSADevice* alsa = (AudinALSADevice*)arg;
|
||||||
DWORD status;
|
DWORD status;
|
||||||
WLog_Print(alsa->log, WLOG_DEBUG, "in");
|
WLog_Print(alsa->log, WLOG_DEBUG, "in");
|
||||||
|
|
||||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name,
|
if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||||
SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
|
||||||
{
|
{
|
||||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(error));
|
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(error));
|
||||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||||
@ -141,7 +142,8 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
|||||||
goto out;
|
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)
|
if (!buffer)
|
||||||
{
|
{
|
||||||
@ -181,12 +183,13 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = alsa->receive(&alsa->aformat,
|
error =
|
||||||
buffer, error * alsa->bytes_per_frame, alsa->user_data);
|
alsa->receive(&alsa->aformat, buffer, error * alsa->bytes_per_frame, alsa->user_data);
|
||||||
|
|
||||||
if (error)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,8 +203,7 @@ out:
|
|||||||
WLog_Print(alsa->log, WLOG_DEBUG, "out");
|
WLog_Print(alsa->log, WLOG_DEBUG, "out");
|
||||||
|
|
||||||
if (error && alsa->rdpcontext)
|
if (error && alsa->rdpcontext)
|
||||||
setChannelError(alsa->rdpcontext, error,
|
setChannelError(alsa->rdpcontext, error, "audin_alsa_thread_func reported an error");
|
||||||
"audin_alsa_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
ExitThread(error);
|
||||||
return error;
|
return error;
|
||||||
@ -214,7 +216,7 @@ out:
|
|||||||
*/
|
*/
|
||||||
static UINT audin_alsa_free(IAudinDevice* device)
|
static UINT audin_alsa_free(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||||
|
|
||||||
if (alsa)
|
if (alsa)
|
||||||
free(alsa->device_name);
|
free(alsa->device_name);
|
||||||
@ -223,8 +225,7 @@ static UINT audin_alsa_free(IAudinDevice* device)
|
|||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||||
const AUDIO_FORMAT* format)
|
|
||||||
{
|
{
|
||||||
if (!device || !format)
|
if (!device || !format)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -232,8 +233,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
|||||||
switch (format->wFormatTag)
|
switch (format->wFormatTag)
|
||||||
{
|
{
|
||||||
case WAVE_FORMAT_PCM:
|
case WAVE_FORMAT_PCM:
|
||||||
if (format->cbSize == 0 &&
|
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
|
||||||
(format->nSamplesPerSec <= 48000) &&
|
|
||||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||||
(format->nChannels == 1 || format->nChannels == 2))
|
(format->nChannels == 1 || format->nChannels == 2))
|
||||||
{
|
{
|
||||||
@ -242,10 +242,6 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WAVE_FORMAT_ALAW:
|
|
||||||
case WAVE_FORMAT_MULAW:
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
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,
|
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||||
UINT32 FramesPerPacket)
|
UINT32 FramesPerPacket)
|
||||||
{
|
{
|
||||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||||
|
|
||||||
if (!alsa || !format)
|
if (!alsa || !format)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
|
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||||
void* user_data)
|
|
||||||
{
|
{
|
||||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||||
|
|
||||||
if (!device || !receive || !user_data)
|
if (!device || !receive || !user_data)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -297,8 +292,7 @@ static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(alsa->thread = CreateThread(NULL, 0,
|
if (!(alsa->thread = CreateThread(NULL, 0, audin_alsa_thread_func, alsa, 0, NULL)))
|
||||||
audin_alsa_thread_func, alsa, 0, NULL)))
|
|
||||||
{
|
{
|
||||||
WLog_Print(alsa->log, WLOG_ERROR, "CreateThread failed!");
|
WLog_Print(alsa->log, WLOG_ERROR, "CreateThread failed!");
|
||||||
goto error_out;
|
goto error_out;
|
||||||
@ -319,7 +313,7 @@ error_out:
|
|||||||
static UINT audin_alsa_close(IAudinDevice* device)
|
static UINT audin_alsa_close(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||||
|
|
||||||
if (!alsa)
|
if (!alsa)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -331,7 +325,8 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
|||||||
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,28 +341,24 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
|||||||
return error;
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* args)
|
||||||
ADDIN_ARGV* args)
|
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
NULL, NULL, -1, NULL, "audio device name" },
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||||
audin_alsa_args, flags, alsa, 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)
|
if (status < 0)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -379,8 +370,7 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
|||||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||||
CommandLineSwitchCase(arg, "dev")
|
|
||||||
{
|
{
|
||||||
alsa->device_name = _strdup(arg->Value);
|
alsa->device_name = _strdup(arg->Value);
|
||||||
|
|
||||||
@ -391,16 +381,15 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#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
|
#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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -408,13 +397,12 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
pEntryPoints)
|
|
||||||
{
|
{
|
||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV* args;
|
||||||
AudinALSADevice* alsa;
|
AudinALSADevice* alsa;
|
||||||
UINT error;
|
UINT error;
|
||||||
alsa = (AudinALSADevice*) calloc(1, sizeof(AudinALSADevice));
|
alsa = (AudinALSADevice*)calloc(1, sizeof(AudinALSADevice));
|
||||||
|
|
||||||
if (!alsa)
|
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)))
|
if ((error = audin_alsa_parse_addin_args(alsa, args)))
|
||||||
{
|
{
|
||||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_parse_addin_args failed with errorcode %"PRIu32"!",
|
WLog_Print(alsa->log, WLOG_ERROR,
|
||||||
error);
|
"audin_alsa_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||||
goto error_out;
|
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.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
alsa->aformat.nSamplesPerSec = 44100;
|
alsa->aformat.nSamplesPerSec = 44100;
|
||||||
|
|
||||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)alsa)))
|
||||||
(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;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,16 +39,22 @@
|
|||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <freerdp/freerdp.h>
|
#include <freerdp/freerdp.h>
|
||||||
#include <freerdp/codec/dsp.h>
|
#include <freerdp/codec/dsp.h>
|
||||||
|
#include <freerdp/channels/audin.h>
|
||||||
|
|
||||||
#include "audin_main.h"
|
#include "audin_main.h"
|
||||||
|
|
||||||
#define MSG_SNDIN_VERSION 0x01
|
#define SNDIN_VERSION 0x02
|
||||||
#define MSG_SNDIN_FORMATS 0x02
|
|
||||||
#define MSG_SNDIN_OPEN 0x03
|
enum
|
||||||
#define MSG_SNDIN_OPEN_REPLY 0x04
|
{
|
||||||
#define MSG_SNDIN_DATA_INCOMING 0x05
|
MSG_SNDIN_VERSION = 0x01,
|
||||||
#define MSG_SNDIN_DATA 0x06
|
MSG_SNDIN_FORMATS = 0x02,
|
||||||
#define MSG_SNDIN_FORMATCHANGE 0x07
|
MSG_SNDIN_OPEN = 0x03,
|
||||||
|
MSG_SNDIN_OPEN_REPLY = 0x04,
|
||||||
|
MSG_SNDIN_DATA_INCOMING = 0x05,
|
||||||
|
MSG_SNDIN_DATA = 0x06,
|
||||||
|
MSG_SNDIN_FORMATCHANGE = 0x07
|
||||||
|
} MSG_SNDIN_CMD;
|
||||||
|
|
||||||
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
|
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
|
||||||
struct _AUDIN_LISTENER_CALLBACK
|
struct _AUDIN_LISTENER_CALLBACK
|
||||||
@ -100,12 +106,17 @@ struct _AUDIN_PLUGIN
|
|||||||
|
|
||||||
FREERDP_DSP_CONTEXT* dsp_context;
|
FREERDP_DSP_CONTEXT* dsp_context;
|
||||||
wLog* log;
|
wLog* log;
|
||||||
|
|
||||||
|
IWTSListener* listener;
|
||||||
|
|
||||||
|
BOOL initialized;
|
||||||
|
UINT32 version;
|
||||||
};
|
};
|
||||||
|
|
||||||
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
||||||
|
|
||||||
static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
|
static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
|
||||||
BOOL freeStream)
|
BOOL freeStream)
|
||||||
{
|
{
|
||||||
UINT error;
|
UINT error;
|
||||||
|
|
||||||
@ -116,9 +127,8 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
|||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
Stream_SealLength(out);
|
Stream_SealLength(out);
|
||||||
error = callback->channel->Write(callback->channel,
|
error =
|
||||||
Stream_Length(out),
|
callback->channel->Write(callback->channel, Stream_Length(out), Stream_Buffer(out), NULL);
|
||||||
Stream_Buffer(out), NULL);
|
|
||||||
|
|
||||||
if (freeStream)
|
if (freeStream)
|
||||||
Stream_Free(out, TRUE);
|
Stream_Free(out, TRUE);
|
||||||
@ -126,7 +136,6 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* 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)
|
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||||
{
|
{
|
||||||
wStream* out;
|
wStream* out;
|
||||||
const UINT32 ClientVersion = 0x01;
|
const UINT32 ClientVersion = SNDIN_VERSION;
|
||||||
UINT32 ServerVersion;
|
UINT32 ServerVersion;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, ServerVersion);
|
Stream_Read_UINT32(s, ServerVersion);
|
||||||
WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%"PRIu32", ClientVersion=%"PRIu32, ServerVersion,
|
WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%" PRIu32 ", ClientVersion=%" PRIu32,
|
||||||
ClientVersion);
|
ServerVersion, ClientVersion);
|
||||||
|
|
||||||
/* Do not answer server packet, we do not support the channel version. */
|
/* Do not answer server packet, we do not support the channel version. */
|
||||||
if (ServerVersion != ClientVersion)
|
if (ServerVersion > ClientVersion)
|
||||||
{
|
{
|
||||||
WLog_Print(audin->log, WLOG_WARN,
|
WLog_Print(audin->log, WLOG_WARN,
|
||||||
"Incompatible channel version server=%"PRIu32", client supports version=%"PRIu32, ServerVersion,
|
"Incompatible channel version server=%" PRIu32
|
||||||
ClientVersion);
|
", client supports version=%" PRIu32,
|
||||||
|
ServerVersion, ClientVersion);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
audin->version = ServerVersion;
|
||||||
|
|
||||||
out = Stream_New(NULL, 5);
|
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;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, NumFormats);
|
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))
|
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;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,11 +282,11 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
|
cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
|
||||||
Stream_SetPosition(out, 0);
|
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, 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);
|
Stream_SetPosition(out, cbSizeFormatsPacket);
|
||||||
error = audin_channel_write_and_free(callback, out, FALSE);
|
error = audin_channel_write_and_free(callback, out, FALSE);
|
||||||
out:
|
out:
|
||||||
@ -296,7 +307,7 @@ out:
|
|||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
|
static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
|
||||||
UINT32 NewFormat)
|
UINT32 NewFormat)
|
||||||
{
|
{
|
||||||
wStream* out = Stream_New(NULL, 5);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format,
|
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, const BYTE* data, size_t size,
|
||||||
const BYTE* data, size_t size, void* user_data)
|
void* user_data)
|
||||||
{
|
{
|
||||||
UINT error;
|
UINT error;
|
||||||
BOOL compatible;
|
BOOL compatible;
|
||||||
AUDIN_PLUGIN* audin;
|
AUDIN_PLUGIN* audin;
|
||||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
|
||||||
|
|
||||||
if (!callback)
|
if (!callback)
|
||||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||||
@ -382,7 +393,7 @@ static UINT audin_receive_wave_data(const AUDIO_FORMAT* format,
|
|||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
audio_format_print(audin->log, WLOG_TRACE, audin->format);
|
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);
|
Stream_GetPosition(audin->data) - 1);
|
||||||
|
|
||||||
if ((error = audin_send_incoming_data_pdu(callback)))
|
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)
|
if (!supported)
|
||||||
{
|
{
|
||||||
/* Default sample rates supported by most backends. */
|
/* Default sample rates supported by most backends. */
|
||||||
const UINT32 samplerates[] = {
|
const UINT32 samplerates[] = { 96000, 48000, 44100, 22050 };
|
||||||
96000,
|
|
||||||
48000,
|
|
||||||
44100,
|
|
||||||
22050
|
|
||||||
};
|
|
||||||
BOOL test = FALSE;
|
BOOL test = FALSE;
|
||||||
|
|
||||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
@ -425,7 +431,7 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb
|
|||||||
if (!test)
|
if (!test)
|
||||||
{
|
{
|
||||||
size_t x;
|
size_t x;
|
||||||
for (x=0; x<ARRAYSIZE(samplerates); x++)
|
for (x = 0; x < ARRAYSIZE(samplerates); x++)
|
||||||
{
|
{
|
||||||
format.nSamplesPerSec = samplerates[x];
|
format.nSamplesPerSec = samplerates[x];
|
||||||
test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
IFCALLRET(audin->device->SetFormat, error,
|
IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
|
||||||
audin->device, &format,
|
|
||||||
audin->FramesPerPacket);
|
|
||||||
|
|
||||||
if (error != CHANNEL_RC_OK)
|
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;
|
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,
|
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||||
audin_receive_wave_data, callback);
|
|
||||||
|
|
||||||
if (error != CHANNEL_RC_OK)
|
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;
|
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, FramesPerPacket);
|
||||||
Stream_Read_UINT32(s, initialFormat);
|
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);
|
FramesPerPacket, initialFormat);
|
||||||
audin->FramesPerPacket = FramesPerPacket;
|
audin->FramesPerPacket = FramesPerPacket;
|
||||||
|
|
||||||
if (initialFormat >= callback->formats_count)
|
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);
|
initialFormat, callback->formats_count);
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
@ -523,12 +523,12 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
|
|||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, NewFormat);
|
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)
|
if (NewFormat >= 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)", NewFormat,
|
||||||
NewFormat, callback->formats_count);
|
callback->formats_count);
|
||||||
return ERROR_INVALID_DATA;
|
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)
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,12 +564,12 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
|||||||
UINT error;
|
UINT error;
|
||||||
BYTE MessageId;
|
BYTE MessageId;
|
||||||
AUDIN_PLUGIN* audin;
|
AUDIN_PLUGIN* audin;
|
||||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
|
||||||
|
|
||||||
if (!callback || !data)
|
if (!callback || !data)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
audin = (AUDIN_PLUGIN*) callback->plugin;
|
audin = (AUDIN_PLUGIN*)callback->plugin;
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
@ -578,7 +578,7 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
|||||||
return ERROR_NO_DATA;
|
return ERROR_NO_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT8(data, MessageId);
|
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)
|
switch (MessageId)
|
||||||
{
|
{
|
||||||
@ -599,7 +599,7 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
error = ERROR_INVALID_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -614,8 +614,8 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
|||||||
*/
|
*/
|
||||||
static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||||
{
|
{
|
||||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
|
||||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||||
|
|
||||||
@ -624,7 +624,7 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
|||||||
IFCALLRET(audin->device->Close, error, audin->device);
|
IFCALLRET(audin->device->Close, error, audin->device);
|
||||||
|
|
||||||
if (error != CHANNEL_RC_OK)
|
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;
|
audin->format = NULL;
|
||||||
@ -639,19 +639,19 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
|||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
IWTSVirtualChannel* pChannel, BYTE* Data,
|
||||||
IWTSVirtualChannelCallback** ppCallback)
|
BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
|
||||||
{
|
{
|
||||||
AUDIN_CHANNEL_CALLBACK* callback;
|
AUDIN_CHANNEL_CALLBACK* callback;
|
||||||
AUDIN_PLUGIN* audin;
|
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)
|
if (!listener_callback || !listener_callback->plugin)
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
audin = (AUDIN_PLUGIN*) listener_callback->plugin;
|
audin = (AUDIN_PLUGIN*)listener_callback->plugin;
|
||||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
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)
|
if (!callback)
|
||||||
{
|
{
|
||||||
@ -664,7 +664,7 @@ static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
|
|||||||
callback->plugin = listener_callback->plugin;
|
callback->plugin = listener_callback->plugin;
|
||||||
callback->channel_mgr = listener_callback->channel_mgr;
|
callback->channel_mgr = listener_callback->channel_mgr;
|
||||||
callback->channel = pChannel;
|
callback->channel = pChannel;
|
||||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||||
return CHANNEL_RC_OK;
|
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)
|
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)
|
if (!audin)
|
||||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||||
@ -683,8 +684,14 @@ static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManag
|
|||||||
if (!pChannelMgr)
|
if (!pChannelMgr)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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, "...");
|
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)
|
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->iface.OnNewChannelConnection = audin_on_new_channel_connection;
|
||||||
audin->listener_callback->plugin = pPlugin;
|
audin->listener_callback->plugin = pPlugin;
|
||||||
audin->listener_callback->channel_mgr = pChannelMgr;
|
audin->listener_callback->channel_mgr = pChannelMgr;
|
||||||
return pChannelMgr->CreateListener(pChannelMgr, "AUDIO_INPUT", 0,
|
rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
|
||||||
(IWTSListenerCallback*) audin->listener_callback, NULL);
|
&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)
|
static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||||
{
|
{
|
||||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||||
|
|
||||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
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)
|
if (audin->device)
|
||||||
{
|
{
|
||||||
@ -721,7 +738,7 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
|||||||
|
|
||||||
if (error != CHANNEL_RC_OK)
|
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
|
// dont stop on error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,7 +756,7 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
|||||||
|
|
||||||
static UINT audin_plugin_attached(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;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
@ -751,7 +768,7 @@ static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
|
|||||||
|
|
||||||
static UINT audin_plugin_detached(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;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
@ -768,7 +785,7 @@ static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
|
|||||||
*/
|
*/
|
||||||
static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
|
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)
|
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;
|
PFREERDP_AUDIN_DEVICE_ENTRY entry;
|
||||||
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
|
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
|
||||||
UINT error;
|
UINT error;
|
||||||
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", (LPSTR) name, NULL,
|
entry = (PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_channel_addin_entry("audin", (LPSTR)name,
|
||||||
0);
|
NULL, 0);
|
||||||
|
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
{
|
{
|
||||||
@ -802,14 +819,14 @@ static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, char* name, ADDIN_ARGV
|
|||||||
return ERROR_INVALID_FUNCTION;
|
return ERROR_INVALID_FUNCTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
entryPoints.plugin = (IWTSPlugin*) audin;
|
entryPoints.plugin = (IWTSPlugin*)audin;
|
||||||
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
|
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
|
||||||
entryPoints.args = args;
|
entryPoints.args = args;
|
||||||
entryPoints.rdpcontext = audin->rdpcontext;
|
entryPoints.rdpcontext = audin->rdpcontext;
|
||||||
|
|
||||||
if ((error = entry(&entryPoints)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,29 +872,28 @@ static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
|
|||||||
return CHANNEL_RC_OK;
|
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)
|
BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
UINT error;
|
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)
|
if (!args || args->argc == 1)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
flags =
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||||
audin_args, flags, audin, NULL, NULL);
|
status =
|
||||||
|
CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin, NULL, NULL);
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -890,12 +906,12 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
|||||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
|
||||||
CommandLineSwitchCase(arg, "sys")
|
|
||||||
{
|
{
|
||||||
if ((error = audin_set_subsystem(audin, arg->Value)))
|
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;
|
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)))
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -936,16 +953,15 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define DVCPluginEntry audin_DVCPluginEntry
|
#define DVCPluginEntry audin_DVCPluginEntry
|
||||||
#else
|
#else
|
||||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -966,34 +982,34 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
struct SubsystemEntry entries[] =
|
struct SubsystemEntry entries[] =
|
||||||
{
|
{
|
||||||
#if defined(WITH_PULSE)
|
#if defined(WITH_PULSE)
|
||||||
{"pulse", ""},
|
{ "pulse", "" },
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_OSS)
|
#if defined(WITH_OSS)
|
||||||
{"oss", "default"},
|
{ "oss", "default" },
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_ALSA)
|
#if defined(WITH_ALSA)
|
||||||
{"alsa", "default"},
|
{ "alsa", "default" },
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_OPENSLES)
|
#if defined(WITH_OPENSLES)
|
||||||
{"opensles", "default"},
|
{ "opensles", "default" },
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_WINMM)
|
#if defined(WITH_WINMM)
|
||||||
{"winmm", "default"},
|
{ "winmm", "default" },
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_MACAUDIO)
|
#if defined(WITH_MACAUDIO)
|
||||||
{"mac", "default"},
|
{ "mac", "default" },
|
||||||
#endif
|
#endif
|
||||||
{NULL, NULL}
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
struct SubsystemEntry* entry = &entries[0];
|
struct SubsystemEntry* entry = &entries[0];
|
||||||
assert(pEntryPoints);
|
assert(pEntryPoints);
|
||||||
assert(pEntryPoints->GetPlugin);
|
assert(pEntryPoints->GetPlugin);
|
||||||
audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
|
audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "audin");
|
||||||
|
|
||||||
if (audin != NULL)
|
if (audin != NULL)
|
||||||
return CHANNEL_RC_ALREADY_INITIALIZED;
|
return CHANNEL_RC_ALREADY_INITIALIZED;
|
||||||
|
|
||||||
audin = (AUDIN_PLUGIN*) calloc(1, sizeof(AUDIN_PLUGIN));
|
audin = (AUDIN_PLUGIN*)calloc(1, sizeof(AUDIN_PLUGIN));
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
{
|
{
|
||||||
@ -1024,8 +1040,8 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
audin->iface.Attached = audin_plugin_attached;
|
audin->iface.Attached = audin_plugin_attached;
|
||||||
audin->iface.Detached = audin_plugin_detached;
|
audin->iface.Detached = audin_plugin_detached;
|
||||||
args = pEntryPoints->GetPluginData(pEntryPoints);
|
args = pEntryPoints->GetPluginData(pEntryPoints);
|
||||||
audin->rdpcontext = ((freerdp*)((rdpSettings*) pEntryPoints->GetRdpSettings(
|
audin->rdpcontext =
|
||||||
pEntryPoints))->instance)->context;
|
((freerdp*)((rdpSettings*)pEntryPoints->GetRdpSettings(pEntryPoints))->instance)->context;
|
||||||
|
|
||||||
if (args)
|
if (args)
|
||||||
{
|
{
|
||||||
@ -1037,8 +1053,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
{
|
{
|
||||||
if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
|
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->subsystem, error);
|
audin->log, WLOG_ERROR,
|
||||||
|
"Unable to load microphone redirection subsystem %s because of error %" PRIu32 "",
|
||||||
|
audin->subsystem, error);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1048,17 +1066,20 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
{
|
{
|
||||||
if ((error = audin_set_subsystem(audin, entry->subsystem)))
|
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);
|
entry->subsystem, error);
|
||||||
}
|
}
|
||||||
else if ((error = audin_set_device_name(audin, entry->device)))
|
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);
|
entry->subsystem, error);
|
||||||
}
|
}
|
||||||
else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
|
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);
|
entry->subsystem, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1067,13 +1088,19 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (audin->device == NULL)
|
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:
|
out:
|
||||||
|
audin_plugin_terminated((IWTSPlugin*)audin);
|
||||||
if (error != CHANNEL_RC_OK)
|
|
||||||
audin_plugin_terminated((IWTSPlugin*)audin);
|
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,4 +33,3 @@
|
|||||||
#define TAG CHANNELS_TAG("audin.client")
|
#define TAG CHANNELS_TAG("audin.client")
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H */
|
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H */
|
||||||
|
|
||||||
|
|||||||
@ -18,17 +18,18 @@
|
|||||||
|
|
||||||
define_channel_client_subsystem("audin" "mac" "")
|
define_channel_client_subsystem("audin" "mac" "")
|
||||||
FIND_LIBRARY(CORE_AUDIO CoreAudio)
|
FIND_LIBRARY(CORE_AUDIO CoreAudio)
|
||||||
|
FIND_LIBRARY(AVFOUNDATION AVFoundation)
|
||||||
FIND_LIBRARY(AUDIO_TOOL AudioToolbox)
|
FIND_LIBRARY(AUDIO_TOOL AudioToolbox)
|
||||||
FIND_LIBRARY(APP_SERVICES ApplicationServices)
|
FIND_LIBRARY(APP_SERVICES ApplicationServices)
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
set(${MODULE_PREFIX}_SRCS
|
||||||
audin_mac.c)
|
audin_mac.m)
|
||||||
|
|
||||||
include_directories(..)
|
include_directories(..)
|
||||||
include_directories(${MAC_INCLUDE_DIRS})
|
include_directories(${MAC_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
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})
|
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||||
|
|||||||
@ -33,8 +33,14 @@
|
|||||||
#include <winpr/debug.h>
|
#include <winpr/debug.h>
|
||||||
#include <winpr/cmdline.h>
|
#include <winpr/cmdline.h>
|
||||||
|
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
|
||||||
#define __COREFOUNDATION_CFPLUGINCOM__ 1
|
#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/CoreAudioTypes.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
@ -46,7 +52,7 @@
|
|||||||
|
|
||||||
#include "audin_main.h"
|
#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)
|
/* Fix for #4462: Provide type alias if not declared (Mac OS < 10.10)
|
||||||
* https://developer.apple.com/documentation/coreaudio/audioformatid
|
* https://developer.apple.com/documentation/coreaudio/audioformatid
|
||||||
@ -68,17 +74,18 @@ typedef struct _AudinMacDevice
|
|||||||
int dev_unit;
|
int dev_unit;
|
||||||
|
|
||||||
AudinReceive receive;
|
AudinReceive receive;
|
||||||
void* user_data;
|
void *user_data;
|
||||||
|
|
||||||
rdpContext* rdpcontext;
|
rdpContext *rdpcontext;
|
||||||
|
|
||||||
|
bool isAuthorized;
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
AudioQueueRef audioQueue;
|
AudioQueueRef audioQueue;
|
||||||
AudioStreamBasicDescription audioFormat;
|
AudioStreamBasicDescription audioFormat;
|
||||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||||
} AudinMacDevice;
|
} AudinMacDevice;
|
||||||
|
|
||||||
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT* format)
|
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT *format)
|
||||||
{
|
{
|
||||||
switch (format->wFormatTag)
|
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)
|
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;
|
AudioFormatID req_fmt = 0;
|
||||||
|
|
||||||
|
if (!mac->isAuthorized)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if (device == NULL || format == NULL)
|
if (device == NULL || format == NULL)
|
||||||
return FALSE;
|
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
|
* @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)
|
UINT32 FramesPerPacket)
|
||||||
{
|
{
|
||||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||||
|
|
||||||
|
if (!mac->isAuthorized)
|
||||||
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
if (device == NULL || format == NULL)
|
if (device == NULL || format == NULL)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -133,34 +147,35 @@ static UINT audin_mac_set_format(IAudinDevice* device, const AUDIO_FORMAT* forma
|
|||||||
mac->FramesPerPacket = FramesPerPacket;
|
mac->FramesPerPacket = FramesPerPacket;
|
||||||
mac->format = *format;
|
mac->format = *format;
|
||||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||||
audio_format_get_tag_string(format->wFormatTag),
|
audio_format_get_tag_string(format->wFormatTag), format->nChannels,
|
||||||
format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
|
format->nSamplesPerSec, format->wBitsPerSample);
|
||||||
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||||
|
|
||||||
if (format->wBitsPerSample == 0)
|
if (format->wBitsPerSample == 0)
|
||||||
mac->audioFormat.mBitsPerChannel = 16;
|
mac->audioFormat.mBitsPerChannel = 16;
|
||||||
|
|
||||||
mac->audioFormat.mBytesPerFrame = 0;
|
|
||||||
mac->audioFormat.mBytesPerPacket = 0;
|
|
||||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||||
|
mac->audioFormat.mFramesPerPacket = 1;
|
||||||
|
|
||||||
|
mac->audioFormat.mBytesPerFrame =
|
||||||
|
mac->audioFormat.mChannelsPerFrame * (mac->audioFormat.mBitsPerChannel / 8);
|
||||||
|
mac->audioFormat.mBytesPerPacket =
|
||||||
|
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
|
||||||
|
|
||||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||||
mac->audioFormat.mFramesPerPacket = 1;
|
|
||||||
mac->audioFormat.mReserved = 0;
|
mac->audioFormat.mReserved = 0;
|
||||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mac_audio_queue_input_cb(void* aqData,
|
static void mac_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||||
AudioQueueRef inAQ,
|
const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
|
||||||
AudioQueueBufferRef inBuffer,
|
const AudioStreamPacketDescription *inPacketDesc)
|
||||||
const AudioTimeStamp* inStartTime,
|
|
||||||
UInt32 inNumPackets,
|
|
||||||
const AudioStreamPacketDescription* inPacketDesc)
|
|
||||||
{
|
{
|
||||||
AudinMacDevice* mac = (AudinMacDevice*)aqData;
|
AudinMacDevice *mac = (AudinMacDevice *)aqData;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
const BYTE* buffer = inBuffer->mAudioData;
|
const BYTE *buffer = inBuffer->mAudioData;
|
||||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||||
(void)inAQ;
|
(void)inAQ;
|
||||||
(void)inStartTime;
|
(void)inStartTime;
|
||||||
@ -174,17 +189,20 @@ static void mac_audio_queue_input_cb(void* aqData,
|
|||||||
|
|
||||||
if (error)
|
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);
|
SetLastError(ERROR_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT audin_mac_close(IAudinDevice* device)
|
static UINT audin_mac_close(IAudinDevice *device)
|
||||||
{
|
{
|
||||||
UINT errCode = CHANNEL_RC_OK;
|
UINT errCode = CHANNEL_RC_OK;
|
||||||
char errString[1024];
|
char errString[1024];
|
||||||
OSStatus devStat;
|
OSStatus devStat;
|
||||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||||
|
|
||||||
|
if (!mac->isAuthorized)
|
||||||
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -196,7 +214,7 @@ static UINT audin_mac_close(IAudinDevice* device)
|
|||||||
if (devStat != 0)
|
if (devStat != 0)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +228,7 @@ static UINT audin_mac_close(IAudinDevice* device)
|
|||||||
if (devStat != 0)
|
if (devStat != 0)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,22 +240,26 @@ static UINT audin_mac_close(IAudinDevice* device)
|
|||||||
return errCode;
|
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;
|
DWORD errCode;
|
||||||
char errString[1024];
|
char errString[1024];
|
||||||
OSStatus devStat;
|
OSStatus devStat;
|
||||||
size_t index;
|
size_t index;
|
||||||
|
|
||||||
|
if (!mac->isAuthorized)
|
||||||
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
mac->receive = receive;
|
mac->receive = receive;
|
||||||
mac->user_data = user_data;
|
mac->user_data = user_data;
|
||||||
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb,
|
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb, mac, NULL,
|
||||||
mac, NULL, kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
||||||
|
|
||||||
if (devStat != 0)
|
if (devStat != 0)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
@ -251,20 +273,17 @@ static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* use
|
|||||||
if (devStat != 0)
|
if (devStat != 0)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue,
|
devStat = AudioQueueEnqueueBuffer(mac->audioQueue, mac->audioBuffers[index], 0, NULL);
|
||||||
mac->audioBuffers[index],
|
|
||||||
0,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (devStat != 0)
|
if (devStat != 0)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
@ -275,7 +294,7 @@ static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* use
|
|||||||
if (devStat != 0)
|
if (devStat != 0)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
@ -287,9 +306,9 @@ err_out:
|
|||||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
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;
|
int error;
|
||||||
|
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
@ -304,28 +323,27 @@ static UINT audin_mac_free(IAudinDevice* device)
|
|||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static COMMAND_LINE_ARGUMENT_A audin_mac_args[] =
|
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *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)
|
|
||||||
{
|
{
|
||||||
DWORD errCode;
|
DWORD errCode;
|
||||||
char errString[1024];
|
char errString[1024];
|
||||||
int status;
|
int status;
|
||||||
char* str_num, *eptr;
|
char *str_num, *eptr;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A *arg;
|
||||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
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)
|
if (args->argc == 1)
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
flags =
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags,
|
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||||
mac, NULL, NULL);
|
status =
|
||||||
|
CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags, mac, NULL, NULL);
|
||||||
|
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||||
CommandLineSwitchCase(arg, "dev")
|
|
||||||
{
|
{
|
||||||
str_num = _strdup(arg->Value);
|
str_num = _strdup(arg->Value);
|
||||||
|
|
||||||
@ -358,31 +375,30 @@ static UINT audin_mac_parse_addin_args(AudinMacDevice* device, ADDIN_ARGV* args)
|
|||||||
free(str_num);
|
free(str_num);
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#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
|
#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
|
#endif
|
||||||
|
|
||||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
{
|
{
|
||||||
DWORD errCode;
|
DWORD errCode;
|
||||||
char errString[1024];
|
char errString[1024];
|
||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV *args;
|
||||||
AudinMacDevice* mac;
|
AudinMacDevice *mac;
|
||||||
UINT error;
|
UINT error;
|
||||||
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
|
mac = (AudinMacDevice *)calloc(1, sizeof(AudinMacDevice));
|
||||||
|
|
||||||
if (!mac)
|
if (!mac)
|
||||||
{
|
{
|
||||||
errCode = GetLastError();
|
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);
|
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
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)))
|
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;
|
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;
|
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;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
free(mac);
|
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)
|
static void audin_receive(void* context, const void* data, size_t size)
|
||||||
{
|
{
|
||||||
UINT error;
|
UINT error;
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) context;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)context;
|
||||||
|
|
||||||
if (!opensles || !data)
|
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)
|
static UINT audin_opensles_free(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
|
|
||||||
if (!opensles)
|
if (!opensles)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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->device_name);
|
||||||
free(opensles);
|
free(opensles);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL audin_opensles_format_supported(IAudinDevice* device,
|
static BOOL audin_opensles_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||||
const AUDIO_FORMAT* format)
|
|
||||||
{
|
{
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
|
|
||||||
if (!opensles || !format)
|
if (!opensles || !format)
|
||||||
return FALSE;
|
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);
|
assert(format);
|
||||||
|
|
||||||
switch (format->wFormatTag)
|
switch (format->wFormatTag)
|
||||||
{
|
{
|
||||||
case WAVE_FORMAT_PCM: /* PCM */
|
case WAVE_FORMAT_PCM: /* PCM */
|
||||||
if (format->cbSize == 0 &&
|
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
|
||||||
(format->nSamplesPerSec <= 48000) &&
|
|
||||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||||
(format->nChannels >= 1 && format->nChannels <= 2))
|
(format->nChannels >= 1 && format->nChannels <= 2))
|
||||||
{
|
{
|
||||||
@ -123,9 +121,8 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04X"PRIX16"] not supported",
|
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04X" PRIX16 "] not supported",
|
||||||
audio_format_get_tag_string(format->wFormatTag),
|
audio_format_get_tag_string(format->wFormatTag), format->wFormatTag);
|
||||||
format->wFormatTag);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,16 +134,16 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device,
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_opensles_set_format(IAudinDevice* device,
|
static UINT audin_opensles_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||||
const AUDIO_FORMAT* format, UINT32 FramesPerPacket)
|
UINT32 FramesPerPacket)
|
||||||
{
|
{
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
|
|
||||||
if (!opensles || !format)
|
if (!opensles || !format)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%"PRIu32"",
|
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%" PRIu32 "",
|
||||||
(void*) device, (void*) format, FramesPerPacket);
|
(void*)device, (void*)format, FramesPerPacket);
|
||||||
assert(format);
|
assert(format);
|
||||||
|
|
||||||
opensles->format = *format;
|
opensles->format = *format;
|
||||||
@ -177,13 +174,13 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_Print(opensles->log, WLOG_ERROR, "Encoding '%"PRIu16"' [%04"PRIX16"] not supported",
|
WLog_Print(opensles->log, WLOG_ERROR,
|
||||||
format->wFormatTag,
|
"Encoding '%" PRIu16 "' [%04" PRIX16 "] not supported", format->wFormatTag,
|
||||||
format->wFormatTag);
|
format->wFormatTag);
|
||||||
return ERROR_UNSUPPORTED_TYPE;
|
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);
|
opensles->frames_per_packet);
|
||||||
return CHANNEL_RC_OK;
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||||
void* user_data)
|
|
||||||
{
|
{
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
|
|
||||||
if (!opensles || !receive || !user_data)
|
if (!opensles || !receive || !user_data)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*) device,
|
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*)device,
|
||||||
(void*) receive,
|
(void*)receive, (void*)user_data);
|
||||||
(void*) user_data);
|
|
||||||
|
|
||||||
if (opensles->stream)
|
if (opensles->stream)
|
||||||
goto error_out;
|
goto error_out;
|
||||||
|
|
||||||
if (!(opensles->stream = android_OpenRecDevice(
|
if (!(opensles->stream = android_OpenRecDevice(
|
||||||
opensles, audin_receive,
|
opensles, audin_receive, opensles->format.nSamplesPerSec, opensles->format.nChannels,
|
||||||
opensles->format.nSamplesPerSec,
|
opensles->frames_per_packet, opensles->format.wBitsPerSample)))
|
||||||
opensles->format.nChannels,
|
|
||||||
opensles->frames_per_packet,
|
|
||||||
opensles->format.wBitsPerSample)))
|
|
||||||
{
|
{
|
||||||
WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
|
WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
|
||||||
goto error_out;
|
goto error_out;
|
||||||
@ -223,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
|||||||
opensles->user_data = user_data;
|
opensles->user_data = user_data;
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
audin_opensles_close(opensles);
|
audin_opensles_close(device);
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,12 +226,12 @@ error_out:
|
|||||||
*/
|
*/
|
||||||
UINT audin_opensles_close(IAudinDevice* device)
|
UINT audin_opensles_close(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
|
|
||||||
if (!opensles)
|
if (!opensles)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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);
|
android_CloseRecDevice(opensles->stream);
|
||||||
opensles->receive = NULL;
|
opensles->receive = NULL;
|
||||||
opensles->user_data = NULL;
|
opensles->user_data = NULL;
|
||||||
@ -247,31 +239,28 @@ UINT audin_opensles_close(IAudinDevice* device)
|
|||||||
return CHANNEL_RC_OK;
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, ADDIN_ARGV* args)
|
||||||
ADDIN_ARGV* args)
|
|
||||||
{
|
{
|
||||||
UINT status;
|
UINT status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
const COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, args=%p", (void*) device, (void*) args);
|
COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL,
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
"audio device name" },
|
||||||
audin_opensles_args, flags, opensles, NULL, NULL);
|
{ 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)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
@ -283,8 +272,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
|||||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||||
CommandLineSwitchCase(arg, "dev")
|
|
||||||
{
|
{
|
||||||
opensles->device_name = _strdup(arg->Value);
|
opensles->device_name = _strdup(arg->Value);
|
||||||
|
|
||||||
@ -295,18 +283,15 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define freerdp_audin_client_subsystem_entry \
|
#define freerdp_audin_client_subsystem_entry opensles_freerdp_audin_client_subsystem_entry
|
||||||
opensles_freerdp_audin_client_subsystem_entry
|
|
||||||
#else
|
#else
|
||||||
#define freerdp_audin_client_subsystem_entry \
|
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||||
FREERDP_API freerdp_audin_client_subsystem_entry
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,13 +299,12 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
UINT freerdp_audin_client_subsystem_entry(
|
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
|
||||||
{
|
{
|
||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV* args;
|
||||||
AudinOpenSLESDevice* opensles;
|
AudinOpenSLESDevice* opensles;
|
||||||
UINT error;
|
UINT error;
|
||||||
opensles = (AudinOpenSLESDevice*) calloc(1, sizeof(AudinOpenSLESDevice));
|
opensles = (AudinOpenSLESDevice*)calloc(1, sizeof(AudinOpenSLESDevice));
|
||||||
|
|
||||||
if (!opensles)
|
if (!opensles)
|
||||||
{
|
{
|
||||||
@ -340,13 +324,14 @@ UINT freerdp_audin_client_subsystem_entry(
|
|||||||
if ((error = audin_opensles_parse_addin_args(opensles, args)))
|
if ((error = audin_opensles_parse_addin_args(opensles, args)))
|
||||||
{
|
{
|
||||||
WLog_Print(opensles->log, WLOG_ERROR,
|
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;
|
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;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,14 +6,14 @@ All rights reserved.
|
|||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
* Redistributions of source code must retain the above copyright
|
* Redistributions of source code must retain the above copyright
|
||||||
notice, this list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* Neither the name of the <organization> nor the
|
||||||
names of its contributors may be used to endorse or promote products
|
names of its contributors may be used to endorse or promote products
|
||||||
derived from this software without specific prior written permission.
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
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 "audin_main.h"
|
||||||
#include "opensl_io.h"
|
#include "opensl_io.h"
|
||||||
#define CONV16BIT 32768
|
#define CONV16BIT 32768
|
||||||
#define CONVMYFLT (1./32768.)
|
#define CONVMYFLT (1. / 32768.)
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -66,7 +66,6 @@ struct opensl_stream
|
|||||||
opensl_receive_t receive;
|
opensl_receive_t receive;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||||
|
|
||||||
// creates the OpenSL ES audio engine
|
// creates the OpenSL ES audio engine
|
||||||
@ -76,22 +75,24 @@ static SLresult openSLCreateEngine(OPENSL_STREAM* p)
|
|||||||
// create engine
|
// create engine
|
||||||
result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
|
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
|
// realize the engine
|
||||||
result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
|
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
|
// get the engine interface, which is needed in order to create other objects
|
||||||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE,
|
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
|
||||||
&(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!
|
// get the volume interface - important, this is optional!
|
||||||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME,
|
result =
|
||||||
&(p->deviceVolume));
|
(*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME, &(p->deviceVolume));
|
||||||
|
|
||||||
if (result != SL_RESULT_SUCCESS)
|
if (result != SL_RESULT_SUCCESS)
|
||||||
{
|
{
|
||||||
@ -169,10 +170,9 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configure audio source
|
// configure audio source
|
||||||
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL
|
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
|
||||||
};
|
SLDataSource audioSrc = { &loc_dev, NULL };
|
||||||
SLDataSource audioSrc = {&loc_dev, NULL};
|
|
||||||
// configure audio sink
|
// configure audio sink
|
||||||
int speakers;
|
int speakers;
|
||||||
|
|
||||||
@ -181,7 +181,8 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
|||||||
else
|
else
|
||||||
speakers = SL_SPEAKER_FRONT_CENTER;
|
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;
|
SLDataFormat_PCM format_pcm;
|
||||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||||
format_pcm.numChannels = channels;
|
format_pcm.numChannels = channels;
|
||||||
@ -202,41 +203,46 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
|||||||
else
|
else
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
SLDataSink audioSnk = {&loc_bq, &format_pcm};
|
SLDataSink audioSnk = { &loc_bq, &format_pcm };
|
||||||
// create audio recorder
|
// create audio recorder
|
||||||
// (requires the RECORD_AUDIO permission)
|
// (requires the RECORD_AUDIO permission)
|
||||||
const SLInterfaceID id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
|
const SLInterfaceID id[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
||||||
const SLboolean req[] = {SL_BOOLEAN_TRUE};
|
const SLboolean req[] = { SL_BOOLEAN_TRUE };
|
||||||
result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine,
|
result = (*p->engineEngine)
|
||||||
&(p->recorderObject), &audioSrc, &audioSnk, 1, id, req);
|
->CreateAudioRecorder(p->engineEngine, &(p->recorderObject), &audioSrc,
|
||||||
|
&audioSnk, 1, id, req);
|
||||||
assert(!result);
|
assert(!result);
|
||||||
|
|
||||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
if (SL_RESULT_SUCCESS != result)
|
||||||
|
goto end_recopen;
|
||||||
|
|
||||||
// realize the audio recorder
|
// realize the audio recorder
|
||||||
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
|
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
|
||||||
assert(!result);
|
assert(!result);
|
||||||
|
|
||||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
if (SL_RESULT_SUCCESS != result)
|
||||||
|
goto end_recopen;
|
||||||
|
|
||||||
// get the record interface
|
// get the record interface
|
||||||
result = (*p->recorderObject)->GetInterface(p->recorderObject,
|
result = (*p->recorderObject)
|
||||||
SL_IID_RECORD, &(p->recorderRecord));
|
->GetInterface(p->recorderObject, SL_IID_RECORD, &(p->recorderRecord));
|
||||||
assert(!result);
|
assert(!result);
|
||||||
|
|
||||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
if (SL_RESULT_SUCCESS != result)
|
||||||
|
goto end_recopen;
|
||||||
|
|
||||||
// get the buffer queue interface
|
// get the buffer queue interface
|
||||||
result = (*p->recorderObject)->GetInterface(p->recorderObject,
|
result = (*p->recorderObject)
|
||||||
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
->GetInterface(p->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
&(p->recorderBufferQueue));
|
&(p->recorderBufferQueue));
|
||||||
assert(!result);
|
assert(!result);
|
||||||
|
|
||||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
if (SL_RESULT_SUCCESS != result)
|
||||||
|
goto end_recopen;
|
||||||
|
|
||||||
// register callback on the buffer queue
|
// register callback on the buffer queue
|
||||||
result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue,
|
result = (*p->recorderBufferQueue)
|
||||||
bqRecorderCallback, p);
|
->RegisterCallback(p->recorderBufferQueue, bqRecorderCallback, p);
|
||||||
assert(!result);
|
assert(!result);
|
||||||
|
|
||||||
if (SL_RESULT_SUCCESS != result)
|
if (SL_RESULT_SUCCESS != result)
|
||||||
@ -245,7 +251,8 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
|||||||
end_recopen:
|
end_recopen:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else return SL_RESULT_SUCCESS;
|
else
|
||||||
|
return SL_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the OpenSL IO and destroy the audio engine
|
// 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
|
// open the android audio device for input
|
||||||
OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
|
OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive, int sr,
|
||||||
int sr,
|
int inchannels, int bufferframes, int bits_per_sample)
|
||||||
int inchannels,
|
|
||||||
int bufferframes, int bits_per_sample)
|
|
||||||
{
|
{
|
||||||
OPENSL_STREAM* p;
|
OPENSL_STREAM* p;
|
||||||
|
|
||||||
if (!context || !receive)
|
if (!context || !receive)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
p = (OPENSL_STREAM*) calloc(1, sizeof(OPENSL_STREAM));
|
p = (OPENSL_STREAM*)calloc(1, sizeof(OPENSL_STREAM));
|
||||||
|
|
||||||
if (!p)
|
if (!p)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -337,12 +342,9 @@ OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
|
|||||||
if (!p->prep || !p->next)
|
if (!p->prep || !p->next)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, p->next->data, p->next->size);
|
||||||
p->next->data, p->next->size);
|
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, p->prep->data, p->prep->size);
|
||||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
(*p->recorderRecord)->SetRecordState(p->recorderRecord, SL_RECORDSTATE_RECORDING);
|
||||||
p->prep->data, p->prep->size);
|
|
||||||
(*p->recorderRecord)->SetRecordState(p->recorderRecord,
|
|
||||||
SL_RECORDSTATE_RECORDING);
|
|
||||||
return p;
|
return p;
|
||||||
fail:
|
fail:
|
||||||
android_CloseRecDevice(p);
|
android_CloseRecDevice(p);
|
||||||
@ -364,7 +366,7 @@ void android_CloseRecDevice(OPENSL_STREAM* p)
|
|||||||
// this callback handler is called every time a buffer finishes recording
|
// this callback handler is called every time a buffer finishes recording
|
||||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
||||||
{
|
{
|
||||||
OPENSL_STREAM* p = (OPENSL_STREAM*) context;
|
OPENSL_STREAM* p = (OPENSL_STREAM*)context;
|
||||||
queue_element* e;
|
queue_element* e;
|
||||||
|
|
||||||
if (!p)
|
if (!p)
|
||||||
@ -382,7 +384,5 @@ static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
|||||||
|
|
||||||
p->next = p->prep;
|
p->next = p->prep;
|
||||||
p->prep = e;
|
p->prep = e;
|
||||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, e->data, e->size);
|
||||||
e->data, e->size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,14 +6,14 @@ All rights reserved.
|
|||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
* Redistributions of source code must retain the above copyright
|
* Redistributions of source code must retain the above copyright
|
||||||
notice, this list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* Neither the name of the <organization> nor the
|
||||||
names of its contributors may be used to endorse or promote products
|
names of its contributors may be used to endorse or promote products
|
||||||
derived from this software without specific prior written permission.
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
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>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#endif
|
#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
|
Open the audio device with a given sampling rate (sr), input and output channels and IO buffer
|
||||||
in frames. Returns a handle to the OpenSL stream
|
size in frames. Returns a handle to the OpenSL stream
|
||||||
*/
|
*/
|
||||||
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context,
|
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
|
||||||
opensl_receive_t receive, int sr,
|
int sr, int inchannels, int bufferframes,
|
||||||
int inchannels,
|
int bits_per_sample);
|
||||||
int bufferframes, int bits_per_sample);
|
/*
|
||||||
/*
|
Close the audio device
|
||||||
Close the audio device
|
*/
|
||||||
*/
|
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
|
||||||
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
};
|
};
|
||||||
|
|||||||
@ -69,11 +69,10 @@ typedef struct _AudinOSSDevice
|
|||||||
} AudinOSSDevice;
|
} AudinOSSDevice;
|
||||||
|
|
||||||
#define OSS_LOG_ERR(_text, _error) \
|
#define OSS_LOG_ERR(_text, _error) \
|
||||||
if (_error != 0) \
|
if (_error != 0) \
|
||||||
WLog_ERR(TAG, "%s: %i - %s\n", _text, _error, strerror(_error));
|
WLog_ERR(TAG, "%s: %i - %s\n", _text, _error, strerror(_error));
|
||||||
|
|
||||||
|
static UINT32 audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||||
static int audin_oss_get_format(const AUDIO_FORMAT* format)
|
|
||||||
{
|
{
|
||||||
switch (format->wFormatTag)
|
switch (format->wFormatTag)
|
||||||
{
|
{
|
||||||
@ -99,8 +98,7 @@ static int audin_oss_get_format(const AUDIO_FORMAT* format)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL audin_oss_format_supported(IAudinDevice* device,
|
static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||||
const AUDIO_FORMAT* format)
|
|
||||||
{
|
{
|
||||||
if (device == NULL || format == NULL)
|
if (device == NULL || format == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -108,18 +106,13 @@ static BOOL audin_oss_format_supported(IAudinDevice* device,
|
|||||||
switch (format->wFormatTag)
|
switch (format->wFormatTag)
|
||||||
{
|
{
|
||||||
case WAVE_FORMAT_PCM:
|
case WAVE_FORMAT_PCM:
|
||||||
if (format->cbSize != 0 ||
|
if (format->cbSize != 0 || format->nSamplesPerSec > 48000 ||
|
||||||
format->nSamplesPerSec > 48000 ||
|
|
||||||
(format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
|
(format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
|
||||||
(format->nChannels != 1 && format->nChannels != 2))
|
(format->nChannels != 1 && format->nChannels != 2))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WAVE_FORMAT_ALAW:
|
|
||||||
case WAVE_FORMAT_MULAW:
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -151,7 +144,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
|||||||
char mixer_name[PATH_MAX] = "/dev/mixer";
|
char mixer_name[PATH_MAX] = "/dev/mixer";
|
||||||
int pcm_handle = -1, mixer_handle;
|
int pcm_handle = -1, mixer_handle;
|
||||||
BYTE* buffer = NULL;
|
BYTE* buffer = NULL;
|
||||||
int tmp;
|
unsigned long tmp;
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
|
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
|
||||||
UINT error = 0;
|
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)
|
if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
|
||||||
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
|
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
|
||||||
|
|
||||||
buffer_size = (oss->FramesPerPacket * oss->format.nChannels *
|
buffer_size = (oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8));
|
||||||
(oss->format.wBitsPerSample / 8));
|
|
||||||
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
|
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
|
||||||
|
|
||||||
if (NULL == buffer)
|
if (NULL == buffer)
|
||||||
@ -246,33 +238,34 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
SSIZE_T stmp;
|
||||||
status = WaitForSingleObject(oss->stopEvent, 0);
|
status = WaitForSingleObject(oss->stopEvent, 0);
|
||||||
|
|
||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == WAIT_OBJECT_0)
|
if (status == WAIT_OBJECT_0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
tmp = read(pcm_handle, buffer, buffer_size);
|
stmp = read(pcm_handle, buffer, buffer_size);
|
||||||
|
|
||||||
/* Error happen. */
|
/* Error happen. */
|
||||||
if (tmp < 0)
|
if (stmp < 0)
|
||||||
{
|
{
|
||||||
OSS_LOG_ERR("read() error", errno);
|
OSS_LOG_ERR("read() error", errno);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp < buffer_size) /* Not enouth data. */
|
if ((size_t)stmp < buffer_size) /* Not enouth data. */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,8 +273,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
|||||||
err_out:
|
err_out:
|
||||||
|
|
||||||
if (error && oss && oss->rdpcontext)
|
if (error && oss && oss->rdpcontext)
|
||||||
setChannelError(oss->rdpcontext, error,
|
setChannelError(oss->rdpcontext, error, "audin_oss_thread_func reported an error");
|
||||||
"audin_oss_thread_func reported an error");
|
|
||||||
|
|
||||||
if (pcm_handle != -1)
|
if (pcm_handle != -1)
|
||||||
{
|
{
|
||||||
@ -299,8 +291,7 @@ err_out:
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive,
|
static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||||
void* user_data)
|
|
||||||
{
|
{
|
||||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||||
oss->receive = receive;
|
oss->receive = receive;
|
||||||
@ -343,7 +334,7 @@ static UINT audin_oss_close(IAudinDevice* device)
|
|||||||
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
|
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,7 +357,7 @@ static UINT audin_oss_close(IAudinDevice* device)
|
|||||||
static UINT audin_oss_free(IAudinDevice* device)
|
static UINT audin_oss_free(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||||
int error;
|
UINT error;
|
||||||
|
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -380,12 +371,6 @@ static UINT audin_oss_free(IAudinDevice* device)
|
|||||||
return CHANNEL_RC_OK;
|
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
|
* 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)
|
static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
char* str_num, *eptr;
|
char *str_num, *eptr;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
COMMAND_LINE_ARGUMENT_A audin_oss_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
NULL, NULL, -1, NULL, "audio device name" },
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||||
audin_oss_args, flags, oss, 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)
|
if (status < 0)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||||
CommandLineSwitchCase(arg, "dev")
|
|
||||||
{
|
{
|
||||||
str_num = _strdup(arg->Value);
|
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;
|
return CHANNEL_RC_NULL_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
oss->dev_unit = val;
|
oss->dev_unit = (INT32)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oss->dev_unit < 0 || *eptr != '\0')
|
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);
|
free(str_num);
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#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
|
#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
|
#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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
pEntryPoints)
|
|
||||||
{
|
{
|
||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV* args;
|
||||||
AudinOSSDevice* oss;
|
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)))
|
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;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
|
||||||
(IAudinDevice*) oss)))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,27 +62,67 @@ typedef struct _AudinPulseDevice
|
|||||||
wLog* log;
|
wLog* log;
|
||||||
} AudinPulseDevice;
|
} 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)
|
static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||||
{
|
{
|
||||||
pa_context_state_t state;
|
pa_context_state_t state;
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||||
state = pa_context_get_state(context);
|
state = pa_context_get_state(context);
|
||||||
|
|
||||||
|
WLog_Print(pulse->log, WLOG_DEBUG, "context state %s", pulse_context_state_string(state));
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PA_CONTEXT_READY:
|
case PA_CONTEXT_READY:
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "PA_CONTEXT_READY");
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PA_CONTEXT_FAILED:
|
case PA_CONTEXT_FAILED:
|
||||||
case PA_CONTEXT_TERMINATED:
|
case PA_CONTEXT_TERMINATED:
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +135,7 @@ static void audin_pulse_context_state_callback(pa_context* context, void* userda
|
|||||||
static UINT audin_pulse_connect(IAudinDevice* device)
|
static UINT audin_pulse_connect(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
pa_context_state_t state;
|
pa_context_state_t state;
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
|
|
||||||
if (!pulse->context)
|
if (!pulse->context)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -126,8 +166,8 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
|||||||
|
|
||||||
if (!PA_CONTEXT_IS_GOOD(state))
|
if (!PA_CONTEXT_IS_GOOD(state))
|
||||||
{
|
{
|
||||||
WLog_Print(pulse->log, WLOG_ERROR, "bad context state (%d)",
|
WLog_Print(pulse->log, WLOG_ERROR, "bad context state (%s: %d)",
|
||||||
pa_context_errno(pulse->context));
|
pulse_context_state_string(state), pa_context_errno(pulse->context));
|
||||||
pa_context_disconnect(pulse->context);
|
pa_context_disconnect(pulse->context);
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
@ -136,7 +176,7 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +187,7 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
|||||||
*/
|
*/
|
||||||
static UINT audin_pulse_free(IAudinDevice* device)
|
static UINT audin_pulse_free(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
|
|
||||||
if (!pulse)
|
if (!pulse)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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)
|
static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||||
{
|
{
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
|
|
||||||
if (!pulse || !format)
|
if (!pulse || !format)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -187,8 +227,7 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
|||||||
switch (format->wFormatTag)
|
switch (format->wFormatTag)
|
||||||
{
|
{
|
||||||
case WAVE_FORMAT_PCM:
|
case WAVE_FORMAT_PCM:
|
||||||
if (format->cbSize == 0 &&
|
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
|
||||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||||
{
|
{
|
||||||
@ -197,18 +236,6 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WAVE_FORMAT_ALAW: /* A-LAW */
|
|
||||||
case WAVE_FORMAT_MULAW: /* U-LAW */
|
|
||||||
if (format->cbSize == 0 &&
|
|
||||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
|
||||||
(format->wBitsPerSample == 8) &&
|
|
||||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
|
||||||
{
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -225,7 +252,7 @@ static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
|||||||
UINT32 FramesPerPacket)
|
UINT32 FramesPerPacket)
|
||||||
{
|
{
|
||||||
pa_sample_spec sample_spec = { 0 };
|
pa_sample_spec sample_spec = { 0 };
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
|
|
||||||
if (!pulse || !format)
|
if (!pulse || !format)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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)
|
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||||
{
|
{
|
||||||
pa_stream_state_t state;
|
pa_stream_state_t state;
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||||
state = pa_stream_get_state(stream);
|
state = pa_stream_get_state(stream);
|
||||||
|
|
||||||
|
WLog_Print(pulse->log, WLOG_DEBUG, "stream state %s", pulse_stream_state_string(state));
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PA_STREAM_READY:
|
case PA_STREAM_READY:
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "PA_STREAM_READY");
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PA_STREAM_FAILED:
|
case PA_STREAM_FAILED:
|
||||||
case PA_STREAM_TERMINATED:
|
case PA_STREAM_TERMINATED:
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
|
||||||
break;
|
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)
|
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||||
{
|
{
|
||||||
const void* data;
|
const void* data;
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
pa_stream_peek(stream, &data, &length);
|
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);
|
pa_stream_drop(stream);
|
||||||
|
|
||||||
if (error && pulse->rdpcontext)
|
if (error && pulse->rdpcontext)
|
||||||
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
|
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* 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)
|
static UINT audin_pulse_close(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
|
|
||||||
if (!pulse)
|
if (!pulse)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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_stream_state_t state;
|
||||||
pa_buffer_attr buffer_attr = { 0 };
|
pa_buffer_attr buffer_attr = { 0 };
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
|
|
||||||
if (!pulse || !receive || !user_data)
|
if (!pulse || !receive || !user_data)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -363,8 +388,7 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
|||||||
pulse->receive = receive;
|
pulse->receive = receive;
|
||||||
pulse->user_data = user_data;
|
pulse->user_data = user_data;
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
pulse->stream = pa_stream_new(pulse->context, "freerdp_audin",
|
pulse->stream = pa_stream_new(pulse->context, "freerdp_audin", &pulse->sample_spec, NULL);
|
||||||
&pulse->sample_spec, NULL);
|
|
||||||
|
|
||||||
if (!pulse->stream)
|
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);
|
pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
|
||||||
pa_stream_set_state_callback(pulse->stream,
|
pa_stream_set_state_callback(pulse->stream, audin_pulse_stream_state_callback, pulse);
|
||||||
audin_pulse_stream_state_callback, pulse);
|
pa_stream_set_read_callback(pulse->stream, audin_pulse_stream_request_callback, pulse);
|
||||||
pa_stream_set_read_callback(pulse->stream,
|
buffer_attr.maxlength = (UINT32)-1;
|
||||||
audin_pulse_stream_request_callback, pulse);
|
buffer_attr.tlength = (UINT32)-1;
|
||||||
buffer_attr.maxlength = (UINT32) - 1;
|
buffer_attr.prebuf = (UINT32)-1;
|
||||||
buffer_attr.tlength = (UINT32) - 1;
|
buffer_attr.minreq = (UINT32)-1;
|
||||||
buffer_attr.prebuf = (UINT32) - 1;
|
|
||||||
buffer_attr.minreq = (UINT32) - 1;
|
|
||||||
/* 500ms latency */
|
/* 500ms latency */
|
||||||
buffer_attr.fragsize = pulse->bytes_per_frame * pulse->frames_per_packet;
|
buffer_attr.fragsize = pulse->bytes_per_frame * pulse->frames_per_packet;
|
||||||
|
|
||||||
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
|
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
|
||||||
buffer_attr.fragsize += pulse->format.nBlockAlign - buffer_attr.fragsize %
|
buffer_attr.fragsize +=
|
||||||
pulse->format.nBlockAlign;
|
pulse->format.nBlockAlign - buffer_attr.fragsize % pulse->format.nBlockAlign;
|
||||||
|
|
||||||
if (pa_stream_connect_record(pulse->stream,
|
if (pa_stream_connect_record(pulse->stream, pulse->device_name, &buffer_attr,
|
||||||
pulse->device_name,
|
PA_STREAM_ADJUST_LATENCY) < 0)
|
||||||
&buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
|
|
||||||
{
|
{
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_stream_connect_playback failed (%d)",
|
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))
|
if (!PA_STREAM_IS_GOOD(state))
|
||||||
{
|
{
|
||||||
audin_pulse_close(device);
|
audin_pulse_close(device);
|
||||||
WLog_Print(pulse->log, WLOG_ERROR, "bad stream state (%d)",
|
WLog_Print(pulse->log, WLOG_ERROR, "bad stream state (%s: %d)",
|
||||||
pa_context_errno(pulse->context));
|
pulse_stream_state_string(state), pa_context_errno(pulse->context));
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
return pa_context_errno(pulse->context);
|
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;
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
@ -441,10 +456,15 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
|||||||
int status;
|
int status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
COMMAND_LINE_ARGUMENT_A audin_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_pulse_args, flags,
|
NULL, NULL, -1, NULL, "audio device name" },
|
||||||
pulse, NULL, NULL);
|
{ 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)
|
if (status < 0)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||||
CommandLineSwitchCase(arg, "dev")
|
|
||||||
{
|
{
|
||||||
pulse->device_name = _strdup(arg->Value);
|
pulse->device_name = _strdup(arg->Value);
|
||||||
|
|
||||||
@ -468,16 +487,15 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#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
|
#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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -490,7 +508,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
|||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV* args;
|
||||||
AudinPulseDevice* pulse;
|
AudinPulseDevice* pulse;
|
||||||
UINT error;
|
UINT error;
|
||||||
pulse = (AudinPulseDevice*) calloc(1, sizeof(AudinPulseDevice));
|
pulse = (AudinPulseDevice*)calloc(1, sizeof(AudinPulseDevice));
|
||||||
|
|
||||||
if (!pulse)
|
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)))
|
if ((error = audin_pulse_parse_addin_args(pulse, args)))
|
||||||
{
|
{
|
||||||
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_parse_addin_args failed with error %"PRIu32"!",
|
WLog_Print(pulse->log, WLOG_ERROR,
|
||||||
error);
|
"audin_pulse_parse_addin_args failed with error %" PRIu32 "!", error);
|
||||||
goto error_out;
|
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);
|
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");
|
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_connect failed");
|
||||||
goto error_out;
|
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;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
audin_pulse_free((IAudinDevice*)pulse);
|
audin_pulse_free(&pulse->iface);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ typedef struct _AudinWinmmDevice
|
|||||||
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
||||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||||
{
|
{
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)dwInstance;
|
||||||
PWAVEHDR pWaveHdr;
|
PWAVEHDR pWaveHdr;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
MMRESULT mmResult;
|
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 (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
|
||||||
{
|
{
|
||||||
if (pWaveHdr->dwBytesRecorded
|
if (pWaveHdr->dwBytesRecorded &&
|
||||||
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
!(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||||
{
|
{
|
||||||
AUDIO_FORMAT format;
|
AUDIO_FORMAT format;
|
||||||
format.cbSize = winmm->pwfx_cur->cbSize;
|
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.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
|
||||||
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
|
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
|
||||||
|
|
||||||
if ((error = winmm->receive(&format, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded,
|
if ((error = winmm->receive(&format, pWaveHdr->lpData,
|
||||||
winmm->user_data)))
|
pWaveHdr->dwBytesRecorded, winmm->user_data)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
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");
|
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL log_mmresult(AudinWinmmDevice* winmm, const char* what, MMRESULT result)
|
||||||
|
{
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
{
|
||||||
|
CHAR buffer[8192] = { 0 };
|
||||||
|
CHAR msg[8192] = { 0 };
|
||||||
|
CHAR cmsg[8192] = { 0 };
|
||||||
|
waveInGetErrorTextA(result, buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
_snprintf(msg, sizeof(msg) - 1, "%s failed. %" PRIu32 " [%s]", what, result, buffer);
|
||||||
|
_snprintf(cmsg, sizeof(cmsg) - 1, "audin_winmm_thread_func reported an error '%s'", msg);
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "%s", msg);
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, cmsg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL test_format_supported(const PWAVEFORMATEX pwfx)
|
||||||
|
{
|
||||||
|
MMRESULT rc;
|
||||||
|
WAVEINCAPSA caps = { 0 };
|
||||||
|
|
||||||
|
rc = waveInGetDevCapsA(WAVE_MAPPER, &caps, sizeof(caps));
|
||||||
|
if (rc != MMSYSERR_NOERROR)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
switch (pwfx->nChannels)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
if ((caps.dwFormats &
|
||||||
|
(WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08 |
|
||||||
|
WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_96M16)) == 0)
|
||||||
|
return FALSE;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if ((caps.dwFormats &
|
||||||
|
(WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08 |
|
||||||
|
WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_96S16)) == 0)
|
||||||
|
return FALSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0,
|
||||||
|
WAVE_FORMAT_QUERY | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||||
|
return (rc == MMSYSERR_NOERROR);
|
||||||
|
}
|
||||||
|
|
||||||
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||||
{
|
{
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) arg;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
|
||||||
char* buffer;
|
char* buffer;
|
||||||
int size, i;
|
int size, i;
|
||||||
WAVEHDR waveHdr[4];
|
WAVEHDR waveHdr[4] = { 0 };
|
||||||
DWORD status;
|
DWORD status;
|
||||||
MMRESULT rc;
|
MMRESULT rc;
|
||||||
|
|
||||||
if (!winmm->hWaveIn)
|
if (!winmm->hWaveIn)
|
||||||
{
|
{
|
||||||
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
MMRESULT rc;
|
||||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
|
rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
|
||||||
{
|
(DWORD_PTR)winmm,
|
||||||
if (winmm->rdpcontext)
|
CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
if (!log_mmresult(winmm, "waveInOpen", rc))
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size = (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
|
size =
|
||||||
7) / 8;
|
(winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
|
||||||
|
7) /
|
||||||
|
8;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
buffer = (char*) malloc(size);
|
buffer = (char*)malloc(size);
|
||||||
|
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
@ -147,36 +197,22 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
waveHdr[i].lpData = buffer;
|
waveHdr[i].lpData = buffer;
|
||||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != rc)
|
if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInPrepareHeader failed. %"PRIu32"", rc);
|
|
||||||
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != rc)
|
if (!log_mmresult(winmm, "waveInAddBuffer", rc))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInAddBuffer failed. %"PRIu32"", rc);
|
|
||||||
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = waveInStart(winmm->hWaveIn);
|
rc = waveInStart(winmm->hWaveIn);
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != rc)
|
if (!log_mmresult(winmm, "waveInStart", rc))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInStart failed. %"PRIu32"", rc);
|
|
||||||
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||||
@ -192,26 +228,17 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
rc = waveInReset(winmm->hWaveIn);
|
rc = waveInReset(winmm->hWaveIn);
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != rc)
|
if (!log_mmresult(winmm, "waveInReset", rc))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInReset failed. %"PRIu32"", rc);
|
|
||||||
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != rc)
|
if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInUnprepareHeader failed. %"PRIu32"", rc);
|
|
||||||
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(waveHdr[i].lpData);
|
free(waveHdr[i].lpData);
|
||||||
@ -219,13 +246,8 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
rc = waveInClose(winmm->hWaveIn);
|
rc = waveInClose(winmm->hWaveIn);
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != rc)
|
if (!log_mmresult(winmm, "waveInClose", rc))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInClose failed. %"PRIu32"", rc);
|
|
||||||
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
|
||||||
"audin_winmm_thread_func reported an error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
winmm->hWaveIn = NULL;
|
winmm->hWaveIn = NULL;
|
||||||
@ -240,7 +262,7 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
static UINT audin_winmm_free(IAudinDevice* device)
|
static UINT audin_winmm_free(IAudinDevice* device)
|
||||||
{
|
{
|
||||||
UINT32 i;
|
UINT32 i;
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||||
|
|
||||||
if (!winmm)
|
if (!winmm)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -265,7 +287,7 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
|||||||
{
|
{
|
||||||
DWORD status;
|
DWORD status;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||||
|
|
||||||
if (!winmm)
|
if (!winmm)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -276,7 +298,8 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
|||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +321,7 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
|||||||
UINT32 FramesPerPacket)
|
UINT32 FramesPerPacket)
|
||||||
{
|
{
|
||||||
UINT32 i;
|
UINT32 i;
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||||
|
|
||||||
if (!winmm || !format)
|
if (!winmm || !format)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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++)
|
for (i = 0; i < winmm->cFormats; i++)
|
||||||
{
|
{
|
||||||
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag
|
const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
|
||||||
&& winmm->ppwfx[i]->nChannels == format->nChannels
|
if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
|
||||||
&& winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
|
(ppwfx->wBitsPerSample == format->wBitsPerSample) &&
|
||||||
|
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
|
||||||
{
|
{
|
||||||
winmm->pwfx_cur = winmm->ppwfx[i];
|
/* BUG: Many devices report to support stereo recording but fail here.
|
||||||
break;
|
* Ensure we always use mono. */
|
||||||
|
if (ppwfx->nChannels > 1)
|
||||||
|
{
|
||||||
|
ppwfx->nChannels = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ppwfx->nBlockAlign != 2)
|
||||||
|
{
|
||||||
|
ppwfx->nBlockAlign = 2;
|
||||||
|
ppwfx->nAvgBytesPerSec = ppwfx->nSamplesPerSec * ppwfx->nBlockAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!test_format_supported(ppwfx))
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
winmm->pwfx_cur = ppwfx;
|
||||||
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||||
{
|
{
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||||
PWAVEFORMATEX pwfx;
|
PWAVEFORMATEX pwfx;
|
||||||
BYTE* data;
|
BYTE* data;
|
||||||
|
|
||||||
if (!winmm || !format)
|
if (!winmm || !format)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (format->wFormatTag != WAVE_FORMAT_PCM)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||||
|
|
||||||
if (!pwfx)
|
if (!pwfx)
|
||||||
@ -342,29 +384,27 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
|||||||
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
||||||
memcpy(data, format->data, format->cbSize);
|
memcpy(data, format->data, format->cbSize);
|
||||||
|
|
||||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
|
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||||
|
|
||||||
|
if (!test_format_supported(pwfx))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||||
{
|
{
|
||||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
PWAVEFORMATEX* tmp_ppwfx;
|
||||||
|
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
|
if (!tmp_ppwfx)
|
||||||
{
|
goto fail;
|
||||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
|
||||||
{
|
|
||||||
PWAVEFORMATEX* tmp_ppwfx;
|
|
||||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
|
||||||
|
|
||||||
if (!tmp_ppwfx)
|
winmm->ppwfx_size *= 2;
|
||||||
return FALSE;
|
winmm->ppwfx = tmp_ppwfx;
|
||||||
|
|
||||||
winmm->ppwfx_size *= 2;
|
|
||||||
winmm->ppwfx = tmp_ppwfx;
|
|
||||||
}
|
|
||||||
|
|
||||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail:
|
||||||
free(pwfx);
|
free(pwfx);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -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)
|
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)
|
if (!winmm || !receive || !user_data)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -390,8 +430,7 @@ static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* u
|
|||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(winmm->thread = CreateThread(NULL, 0,
|
if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
|
||||||
audin_winmm_thread_func, winmm, 0, NULL)))
|
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
|
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
|
||||||
CloseHandle(winmm->stopEvent);
|
CloseHandle(winmm->stopEvent);
|
||||||
@ -402,12 +441,6 @@ static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* u
|
|||||||
return CHANNEL_RC_OK;
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
@ -418,10 +451,15 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
|||||||
int status;
|
int status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
COMMAND_LINE_ARGUMENT_A audin_winmm_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_winmm_args, flags,
|
NULL, NULL, -1, NULL, "audio device name" },
|
||||||
winmm, NULL, NULL);
|
{ 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;
|
arg = audin_winmm_args;
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -429,8 +467,7 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
|||||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CommandLineSwitchStart(arg)
|
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||||
CommandLineSwitchCase(arg, "dev")
|
|
||||||
{
|
{
|
||||||
winmm->device_name = _strdup(arg->Value);
|
winmm->device_name = _strdup(arg->Value);
|
||||||
|
|
||||||
@ -441,16 +478,15 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
}
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#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
|
#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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -463,7 +499,14 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
|||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV* args;
|
||||||
AudinWinmmDevice* winmm;
|
AudinWinmmDevice* winmm;
|
||||||
UINT error;
|
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)
|
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)))
|
if ((error = audin_winmm_parse_addin_args(winmm, args)))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_ERROR, "audin_winmm_parse_addin_args failed with error %"PRIu32"!",
|
WLog_Print(winmm->log, WLOG_ERROR,
|
||||||
error);
|
"audin_winmm_parse_addin_args failed with error %" PRIu32 "!", error);
|
||||||
goto error_out;
|
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_size = 10;
|
||||||
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
|
||||||
|
|
||||||
if (!winmm->ppwfx)
|
if (!winmm->ppwfx)
|
||||||
{
|
{
|
||||||
@ -509,9 +552,10 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
|
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||||
|
error);
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,17 +35,18 @@
|
|||||||
#include <freerdp/codec/dsp.h>
|
#include <freerdp/codec/dsp.h>
|
||||||
#include <freerdp/codec/audio.h>
|
#include <freerdp/codec/audio.h>
|
||||||
#include <freerdp/channels/wtsvc.h>
|
#include <freerdp/channels/wtsvc.h>
|
||||||
|
#include <freerdp/channels/audin.h>
|
||||||
#include <freerdp/server/audin.h>
|
#include <freerdp/server/audin.h>
|
||||||
#include <freerdp/channels/log.h>
|
#include <freerdp/channels/log.h>
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("audin.server")
|
#define TAG CHANNELS_TAG("audin.server")
|
||||||
#define MSG_SNDIN_VERSION 0x01
|
#define MSG_SNDIN_VERSION 0x01
|
||||||
#define MSG_SNDIN_FORMATS 0x02
|
#define MSG_SNDIN_FORMATS 0x02
|
||||||
#define MSG_SNDIN_OPEN 0x03
|
#define MSG_SNDIN_OPEN 0x03
|
||||||
#define MSG_SNDIN_OPEN_REPLY 0x04
|
#define MSG_SNDIN_OPEN_REPLY 0x04
|
||||||
#define MSG_SNDIN_DATA_INCOMING 0x05
|
#define MSG_SNDIN_DATA_INCOMING 0x05
|
||||||
#define MSG_SNDIN_DATA 0x06
|
#define MSG_SNDIN_DATA 0x06
|
||||||
#define MSG_SNDIN_FORMATCHANGE 0x07
|
#define MSG_SNDIN_FORMATCHANGE 0x07
|
||||||
|
|
||||||
typedef struct _audin_server
|
typedef struct _audin_server
|
||||||
{
|
{
|
||||||
@ -69,15 +70,13 @@ typedef struct _audin_server
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_server_select_format(audin_server_context* context,
|
static UINT audin_server_select_format(audin_server_context* context, size_t client_format_index)
|
||||||
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)
|
if (client_format_index >= context->num_client_formats)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG,
|
WLog_ERR(TAG, "error in protocol: client_format_index >= context->num_client_formats!");
|
||||||
"error in protocol: client_format_index >= context->num_client_formats!");
|
|
||||||
return ERROR_INVALID_DATA;
|
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_UINT8(s, MSG_SNDIN_VERSION);
|
||||||
Stream_Write_UINT32(s, 1); /* Version (4 bytes) */
|
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))
|
Stream_GetPosition(s), &written))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_server_recv_version(audin_server* audin, wStream* s,
|
static UINT audin_server_recv_version(audin_server* audin, wStream* s, UINT32 length)
|
||||||
UINT32 length)
|
|
||||||
{
|
{
|
||||||
UINT32 Version;
|
UINT32 Version;
|
||||||
|
|
||||||
if (length < 4)
|
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);
|
length);
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
@ -140,7 +138,7 @@ static UINT audin_server_recv_version(audin_server* audin, wStream* s,
|
|||||||
|
|
||||||
if (Version < 1)
|
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;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,16 +156,12 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
|||||||
ULONG written;
|
ULONG written;
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
Stream_Write_UINT8(s, MSG_SNDIN_FORMATS);
|
Stream_Write_UINT8(s, MSG_SNDIN_FORMATS);
|
||||||
Stream_Write_UINT32(s,
|
Stream_Write_UINT32(s, audin->context.num_server_formats); /* NumFormats (4 bytes) */
|
||||||
audin->context.num_server_formats); /* NumFormats (4 bytes) */
|
Stream_Write_UINT32(s, 0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */
|
||||||
Stream_Write_UINT32(s,
|
|
||||||
0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */
|
|
||||||
|
|
||||||
for (i = 0; i < audin->context.num_server_formats; i++)
|
for (i = 0; i < audin->context.num_server_formats; i++)
|
||||||
{
|
{
|
||||||
AUDIO_FORMAT format = audin->context.server_formats[i];
|
AUDIO_FORMAT format = audin->context.server_formats[i];
|
||||||
// TODO: Eliminate this
|
|
||||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
|
||||||
|
|
||||||
if (!audio_format_write(s, &format))
|
if (!audio_format_write(s, &format))
|
||||||
{
|
{
|
||||||
@ -176,8 +170,10 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s),
|
||||||
Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
static UINT audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 length)
|
||||||
UINT32 length)
|
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
UINT success = CHANNEL_RC_OK;
|
UINT success = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (length < 8)
|
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);
|
length);
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Read_UINT32(s,
|
Stream_Read_UINT32(s, audin->context.num_client_formats); /* NumFormats (4 bytes) */
|
||||||
audin->context.num_client_formats); /* NumFormats (4 bytes) */
|
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */
|
||||||
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */
|
|
||||||
length -= 8;
|
length -= 8;
|
||||||
|
|
||||||
if (audin->context.num_client_formats <= 0)
|
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);
|
audio_formats_free(audin->context.client_formats, i);
|
||||||
audin->context.client_formats = NULL;
|
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;
|
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);
|
IFCALLRET(audin->context.Opening, success, &audin->context);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
WLog_ERR(TAG, "context.Opening failed with error %"PRIu32"", success);
|
WLog_ERR(TAG, "context.Opening failed with error %" PRIu32 "", success);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -257,24 +251,24 @@ static UINT audin_server_send_open(audin_server* audin, wStream* s)
|
|||||||
audin->opened = TRUE;
|
audin->opened = TRUE;
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
Stream_Write_UINT8(s, MSG_SNDIN_OPEN);
|
Stream_Write_UINT8(s, MSG_SNDIN_OPEN);
|
||||||
Stream_Write_UINT32(s,
|
Stream_Write_UINT32(s, audin->context.frames_per_packet); /* FramesPerPacket (4 bytes) */
|
||||||
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.selected_client_format); /* initialFormat (4 bytes) */
|
|
||||||
/*
|
/*
|
||||||
* [MS-RDPEAI] 3.2.5.1.6
|
* [MS-RDPEAI] 3.2.5.1.6
|
||||||
* The second format specify the format that SHOULD be used to capture data from
|
* The second format specify the format that SHOULD be used to capture data from
|
||||||
* the actual audio input device.
|
* the actual audio input device.
|
||||||
*/
|
*/
|
||||||
Stream_Write_UINT16(s, 1); /* wFormatTag = PCM */
|
Stream_Write_UINT16(s, 1); /* wFormatTag = PCM */
|
||||||
Stream_Write_UINT16(s, 2); /* nChannels */
|
Stream_Write_UINT16(s, 2); /* nChannels */
|
||||||
Stream_Write_UINT32(s, 44100); /* nSamplesPerSec */
|
Stream_Write_UINT32(s, 44100); /* nSamplesPerSec */
|
||||||
Stream_Write_UINT32(s, 44100 * 2 * 2); /* nAvgBytesPerSec */
|
Stream_Write_UINT32(s, 44100 * 2 * 2); /* nAvgBytesPerSec */
|
||||||
Stream_Write_UINT16(s, 4); /* nBlockAlign */
|
Stream_Write_UINT16(s, 4); /* nBlockAlign */
|
||||||
Stream_Write_UINT16(s, 16); /* wBitsPerSample */
|
Stream_Write_UINT16(s, 16); /* wBitsPerSample */
|
||||||
Stream_Write_UINT16(s, 0); /* cbSize */
|
Stream_Write_UINT16(s, 0); /* cbSize */
|
||||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s),
|
||||||
Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s,
|
static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s, UINT32 length)
|
||||||
UINT32 length)
|
|
||||||
{
|
{
|
||||||
UINT32 Result;
|
UINT32 Result;
|
||||||
UINT success = CHANNEL_RC_OK;
|
UINT success = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (length < 4)
|
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);
|
length);
|
||||||
return ERROR_INVALID_DATA;
|
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);
|
IFCALLRET(audin->context.OpenResult, success, &audin->context, Result);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
WLog_ERR(TAG, "context.OpenResult failed with error %"PRIu32"", success);
|
WLog_ERR(TAG, "context.OpenResult failed with error %" PRIu32 "", success);
|
||||||
|
|
||||||
return 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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT audin_server_recv_data(audin_server* audin, wStream* s,
|
static UINT audin_server_recv_data(audin_server* audin, wStream* s, UINT32 length)
|
||||||
UINT32 length)
|
|
||||||
{
|
{
|
||||||
AUDIO_FORMAT* format;
|
AUDIO_FORMAT* format;
|
||||||
int sbytes_per_sample;
|
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);
|
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, &dformat, out, frames);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", success);
|
WLog_ERR(TAG, "context.ReceiveSamples failed with error %" PRIu32 "", success);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
WLog_ERR(TAG, "freerdp_dsp_decode failed!");
|
WLog_ERR(TAG, "freerdp_dsp_decode failed!");
|
||||||
@ -365,7 +357,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
BOOL ready = FALSE;
|
BOOL ready = FALSE;
|
||||||
HANDLE ChannelEvent;
|
HANDLE ChannelEvent;
|
||||||
DWORD BytesReturned = 0;
|
DWORD BytesReturned = 0;
|
||||||
audin_server* audin = (audin_server*) arg;
|
audin_server* audin = (audin_server*)arg;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
DWORD status;
|
DWORD status;
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
@ -395,26 +387,25 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE,
|
if ((status = WaitForMultipleObjects(nCount, events, FALSE, 100)) == WAIT_OBJECT_0)
|
||||||
100)) == WAIT_OBJECT_0)
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady,
|
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer,
|
||||||
&buffer, &BytesReturned) == FALSE)
|
&BytesReturned) == FALSE)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
|
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ready = *((BOOL*) buffer);
|
ready = *((BOOL*)buffer);
|
||||||
WTSFreeMemory(buffer);
|
WTSFreeMemory(buffer);
|
||||||
|
|
||||||
if (ready)
|
if (ready)
|
||||||
@ -434,21 +425,20 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
{
|
{
|
||||||
if ((error = audin_server_send_version(audin, s)))
|
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;
|
goto out_capacity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ready)
|
while (ready)
|
||||||
{
|
{
|
||||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE,
|
if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0)
|
||||||
INFINITE)) == WAIT_OBJECT_0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +457,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||||
break;
|
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)
|
Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||||
@ -483,13 +473,15 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
case MSG_SNDIN_VERSION:
|
case MSG_SNDIN_VERSION:
|
||||||
if ((error = audin_server_recv_version(audin, s, BytesReturned)))
|
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;
|
goto out_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = audin_server_send_formats(audin, s)))
|
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;
|
goto out_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,13 +490,14 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
case MSG_SNDIN_FORMATS:
|
case MSG_SNDIN_FORMATS:
|
||||||
if ((error = audin_server_recv_formats(audin, s, BytesReturned)))
|
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;
|
goto out_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = audin_server_send_open(audin, s)))
|
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;
|
goto out_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +506,8 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
case MSG_SNDIN_OPEN_REPLY:
|
case MSG_SNDIN_OPEN_REPLY:
|
||||||
if ((error = audin_server_recv_open_reply(audin, s, BytesReturned)))
|
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;
|
goto out_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +519,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
case MSG_SNDIN_DATA:
|
case MSG_SNDIN_DATA:
|
||||||
if ((error = audin_server_recv_data(audin, s, BytesReturned)))
|
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;
|
goto out_capacity;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -535,7 +529,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %"PRIu8"", MessageId);
|
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,23 +550,25 @@ out:
|
|||||||
|
|
||||||
static BOOL audin_server_open(audin_server_context* context)
|
static BOOL audin_server_open(audin_server_context* context)
|
||||||
{
|
{
|
||||||
audin_server* audin = (audin_server*) context;
|
audin_server* audin = (audin_server*)context;
|
||||||
|
|
||||||
if (!audin->thread)
|
if (!audin->thread)
|
||||||
{
|
{
|
||||||
PULONG pSessionId = NULL;
|
PULONG pSessionId = NULL;
|
||||||
DWORD BytesReturned = 0;
|
DWORD BytesReturned = 0;
|
||||||
audin->SessionId = WTS_CURRENT_SESSION;
|
audin->SessionId = WTS_CURRENT_SESSION;
|
||||||
|
UINT32 channelId;
|
||||||
|
BOOL status = TRUE;
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION,
|
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned))
|
(LPSTR*)&pSessionId, &BytesReturned))
|
||||||
{
|
{
|
||||||
audin->SessionId = (DWORD) * pSessionId;
|
audin->SessionId = (DWORD)*pSessionId;
|
||||||
WTSFreeMemory(pSessionId);
|
WTSFreeMemory(pSessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId,
|
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId, AUDIN_DVC_CHANNEL_NAME,
|
||||||
"AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
|
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||||
|
|
||||||
if (!audin->audin_channel)
|
if (!audin->audin_channel)
|
||||||
{
|
{
|
||||||
@ -580,13 +576,23 @@ static BOOL audin_server_open(audin_server_context* context)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channelId = WTSChannelGetIdByHandle(audin->audin_channel);
|
||||||
|
|
||||||
|
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||||
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
WLog_ERR(TAG, "CreateEvent failed!");
|
||||||
return FALSE;
|
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!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
CloseHandle(audin->stopEvent);
|
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)
|
static BOOL audin_server_is_open(audin_server_context* context)
|
||||||
{
|
{
|
||||||
audin_server* audin = (audin_server*) context;
|
audin_server* audin = (audin_server*)context;
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
return FALSE;
|
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)
|
static BOOL audin_server_close(audin_server_context* context)
|
||||||
{
|
{
|
||||||
audin_server* audin = (audin_server*) context;
|
audin_server* audin = (audin_server*)context;
|
||||||
|
|
||||||
if (audin->thread)
|
if (audin->thread)
|
||||||
{
|
{
|
||||||
@ -621,7 +627,7 @@ static BOOL audin_server_close(audin_server_context* context)
|
|||||||
|
|
||||||
if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -668,12 +674,12 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (audin_server_context*) audin;
|
return (audin_server_context*)audin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audin_server_context_free(audin_server_context* context)
|
void audin_server_context_free(audin_server_context* context)
|
||||||
{
|
{
|
||||||
audin_server* audin = (audin_server*) context;
|
audin_server* audin = (audin_server*)context;
|
||||||
|
|
||||||
if (!audin)
|
if (!audin)
|
||||||
return;
|
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_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||||
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||||
if(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL ${STATIC_ENTRY})
|
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
|
||||||
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
|
if(${ENTRY} STREQUAL ${STATIC_ENTRY})
|
||||||
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
|
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${STATIC_MODULE_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}")
|
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${ENTRY}")
|
||||||
if(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL "VirtualChannelEntry")
|
if(${ENTRY} STREQUAL "VirtualChannelEntry")
|
||||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
|
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
|
||||||
elseif(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL "VirtualChannelEntryEx")
|
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS,PVOID);")
|
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS,PVOID);")
|
||||||
else()
|
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
|
||||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(void);")
|
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()
|
endif()
|
||||||
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
|
endforeach()
|
||||||
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", (static_entry_fkt)${ENTRY_POINT_NAME} },")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
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")
|
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_subsystem_entry")
|
||||||
endif()
|
endif()
|
||||||
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
|
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}")
|
set(CLIENT_STATIC_SUBSYSTEM_IMPORTS "${CLIENT_STATIC_SUBSYSTEM_IMPORTS}\n${SUBSYSTEM_IMPORT}")
|
||||||
endforeach()
|
endforeach()
|
||||||
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ NULL, NULL, NULL }\n};")
|
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ NULL, NULL, NULL }\n};")
|
||||||
set(CLIENT_STATIC_SUBSYSTEM_TABLES "${CLIENT_STATIC_SUBSYSTEM_TABLES}\n${SUBSYSTEM_TABLE}")
|
set(CLIENT_STATIC_SUBSYSTEM_TABLES "${CLIENT_STATIC_SUBSYSTEM_TABLES}\n${SUBSYSTEM_TABLE}")
|
||||||
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${${STATIC_MODULE}_CLIENT_ENTRY}")
|
foreach(ENTRY ${${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} },")
|
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()
|
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)
|
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[];
|
extern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];
|
||||||
|
|
||||||
void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
|
static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
|
||||||
const char* identifier)
|
const char* identifier)
|
||||||
{
|
{
|
||||||
int index = 0;
|
size_t index = 0;
|
||||||
STATIC_ENTRY* pEntry;
|
STATIC_ENTRY* pEntry;
|
||||||
pEntry = (STATIC_ENTRY*) &table->table[index++];
|
pEntry = (STATIC_ENTRY*)&table->table[index++];
|
||||||
|
|
||||||
while (pEntry->entry != NULL)
|
while (pEntry->entry != NULL)
|
||||||
{
|
{
|
||||||
if (strcmp(pEntry->name, identifier) == 0)
|
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;
|
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)
|
void* freerdp_channels_client_find_static_entry(const char* name, const char* identifier)
|
||||||
{
|
{
|
||||||
int index = 0;
|
size_t index = 0;
|
||||||
STATIC_ENTRY_TABLE* pEntry;
|
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)
|
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);
|
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;
|
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[];
|
extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
|
||||||
|
|
||||||
FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR pszSubsystem,
|
static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName,
|
||||||
LPSTR pszType, DWORD dwFlags)
|
LPCSTR pszSubsystem,
|
||||||
|
LPCSTR pszType, DWORD dwFlags)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
DWORD nAddins;
|
DWORD nAddins;
|
||||||
FREERDP_ADDIN** ppAddins = NULL;
|
FREERDP_ADDIN** ppAddins = NULL;
|
||||||
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
||||||
nAddins = 0;
|
nAddins = 0;
|
||||||
ppAddins = (FREERDP_ADDIN**) calloc(128, sizeof(FREERDP_ADDIN*));
|
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
|
||||||
|
|
||||||
if (!ppAddins)
|
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++)
|
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)
|
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_STATIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
ppAddins[nAddins++] = pAddin;
|
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++)
|
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)
|
if (!pAddin)
|
||||||
{
|
{
|
||||||
@ -129,7 +130,8 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
|||||||
goto error_out;
|
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);
|
sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s", subsystems[j].name);
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||||
@ -145,8 +147,8 @@ error_out:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSubsystem,
|
static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
|
||||||
LPSTR pszType, DWORD dwFlags)
|
LPCSTR pszType, DWORD dwFlags)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
int nDashes;
|
int nDashes;
|
||||||
@ -163,11 +165,11 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||||||
size_t cchInstallPrefix;
|
size_t cchInstallPrefix;
|
||||||
FREERDP_ADDIN** ppAddins;
|
FREERDP_ADDIN** ppAddins;
|
||||||
WIN32_FIND_DATAA FindData;
|
WIN32_FIND_DATAA FindData;
|
||||||
cchAddinPath = strlen(pszAddinPath);
|
cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
|
||||||
cchInstallPrefix = strlen(pszInstallPrefix);
|
cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
|
||||||
pszExtension = PathGetSharedLibraryExtensionA(0);
|
pszExtension = PathGetSharedLibraryExtensionA(0);
|
||||||
cchPattern = 128 + strlen(pszExtension) + 2;
|
cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
|
||||||
pszPattern = (LPSTR) malloc(cchPattern + 1);
|
pszPattern = (LPSTR)malloc(cchPattern + 1);
|
||||||
|
|
||||||
if (!pszPattern)
|
if (!pszPattern)
|
||||||
{
|
{
|
||||||
@ -177,28 +179,28 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||||||
|
|
||||||
if (pszName && pszSubsystem && pszType)
|
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);
|
pszName, pszSubsystem, pszType, pszExtension);
|
||||||
}
|
}
|
||||||
else if (pszName && pszType)
|
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);
|
pszName, pszType, pszExtension);
|
||||||
}
|
}
|
||||||
else if (pszName)
|
else if (pszName)
|
||||||
{
|
{
|
||||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client*.%s",
|
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s", pszName,
|
||||||
pszName, pszExtension);
|
pszExtension);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"?-client*.%s",
|
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
|
||||||
pszExtension);
|
pszExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
cchPattern = strlen(pszPattern);
|
cchPattern = strnlen(pszPattern, cchPattern);
|
||||||
cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
|
cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
|
||||||
pszSearchPath = (LPSTR) malloc(cchSearchPath + 1);
|
pszSearchPath = (LPSTR)malloc(cchSearchPath + 1);
|
||||||
|
|
||||||
if (!pszSearchPath)
|
if (!pszSearchPath)
|
||||||
{
|
{
|
||||||
@ -215,7 +217,7 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||||||
hFind = FindFirstFileA(pszSearchPath, &FindData);
|
hFind = FindFirstFileA(pszSearchPath, &FindData);
|
||||||
free(pszSearchPath);
|
free(pszSearchPath);
|
||||||
nAddins = 0;
|
nAddins = 0;
|
||||||
ppAddins = (FREERDP_ADDIN**) calloc(128, sizeof(FREERDP_ADDIN*));
|
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
|
||||||
|
|
||||||
if (!ppAddins)
|
if (!ppAddins)
|
||||||
{
|
{
|
||||||
@ -229,10 +231,8 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
char* p[5];
|
BOOL used = FALSE;
|
||||||
FREERDP_ADDIN* pAddin;
|
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||||
nDashes = 0;
|
|
||||||
pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
|
||||||
|
|
||||||
if (!pAddin)
|
if (!pAddin)
|
||||||
{
|
{
|
||||||
@ -240,59 +240,117 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nDashes = 0;
|
||||||
for (index = 0; FindData.cFileName[index]; index++)
|
for (index = 0; FindData.cFileName[index]; index++)
|
||||||
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
|
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
|
||||||
|
|
||||||
if (nDashes == 1)
|
if (nDashes == 1)
|
||||||
{
|
{
|
||||||
|
size_t len;
|
||||||
|
char* p[2] = { 0 };
|
||||||
/* <name>-client.<extension> */
|
/* <name>-client.<extension> */
|
||||||
p[0] = FindData.cFileName;
|
p[0] = FindData.cFileName;
|
||||||
p[1] = strchr(p[0], '-') + 1;
|
p[1] = strchr(p[0], '-') + 1;
|
||||||
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
|
||||||
|
len = p[1] - p[0];
|
||||||
|
if (len < 1)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||||
|
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
ppAddins[nAddins++] = pAddin;
|
ppAddins[nAddins++] = pAddin;
|
||||||
|
|
||||||
|
used = TRUE;
|
||||||
}
|
}
|
||||||
else if (nDashes == 2)
|
else if (nDashes == 2)
|
||||||
{
|
{
|
||||||
|
size_t len;
|
||||||
|
char* p[4] = { 0 };
|
||||||
/* <name>-client-<subsystem>.<extension> */
|
/* <name>-client-<subsystem>.<extension> */
|
||||||
p[0] = FindData.cFileName;
|
p[0] = FindData.cFileName;
|
||||||
p[1] = strchr(p[0], '-') + 1;
|
p[1] = strchr(p[0], '-') + 1;
|
||||||
p[2] = strchr(p[1], '-') + 1;
|
p[2] = strchr(p[1], '-') + 1;
|
||||||
p[3] = strchr(p[2], '.') + 1;
|
p[3] = strchr(p[2], '.') + 1;
|
||||||
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
|
||||||
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
|
len = p[1] - p[0];
|
||||||
|
if (len < 1)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||||
|
|
||||||
|
len = p[3] - p[2];
|
||||||
|
if (len < 1)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
||||||
|
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||||
ppAddins[nAddins++] = pAddin;
|
ppAddins[nAddins++] = pAddin;
|
||||||
|
|
||||||
|
used = TRUE;
|
||||||
}
|
}
|
||||||
else if (nDashes == 3)
|
else if (nDashes == 3)
|
||||||
{
|
{
|
||||||
|
size_t len;
|
||||||
|
char* p[5] = { 0 };
|
||||||
/* <name>-client-<subsystem>-<type>.<extension> */
|
/* <name>-client-<subsystem>-<type>.<extension> */
|
||||||
p[0] = FindData.cFileName;
|
p[0] = FindData.cFileName;
|
||||||
p[1] = strchr(p[0], '-') + 1;
|
p[1] = strchr(p[0], '-') + 1;
|
||||||
p[2] = strchr(p[1], '-') + 1;
|
p[2] = strchr(p[1], '-') + 1;
|
||||||
p[3] = strchr(p[2], '-') + 1;
|
p[3] = strchr(p[2], '-') + 1;
|
||||||
p[4] = strchr(p[3], '.') + 1;
|
p[4] = strchr(p[3], '.') + 1;
|
||||||
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
|
||||||
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
|
len = p[1] - p[0];
|
||||||
strncpy(pAddin->cType, p[3], (p[4] - p[3]) - 1);
|
if (len < 1)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||||
|
|
||||||
|
len = p[3] - p[2];
|
||||||
|
if (len < 1)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
||||||
|
|
||||||
|
len = p[4] - p[3];
|
||||||
|
if (len < 1)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
|
||||||
|
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
|
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
|
||||||
ppAddins[nAddins++] = pAddin;
|
ppAddins[nAddins++] = pAddin;
|
||||||
|
|
||||||
|
used = TRUE;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
skip:
|
||||||
|
if (!used)
|
||||||
free(pAddin);
|
free(pAddin);
|
||||||
}
|
|
||||||
}
|
} while (FindNextFileA(hFind, &FindData));
|
||||||
while (FindNextFileA(hFind, &FindData));
|
|
||||||
|
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
ppAddins[nAddins] = NULL;
|
ppAddins[nAddins] = NULL;
|
||||||
@ -303,8 +361,8 @@ error_out:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FREERDP_ADDIN** freerdp_channels_list_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType,
|
FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
|
||||||
DWORD dwFlags)
|
DWORD dwFlags)
|
||||||
{
|
{
|
||||||
if (dwFlags & FREERDP_ADDIN_STATIC)
|
if (dwFlags & FREERDP_ADDIN_STATIC)
|
||||||
return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
|
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)
|
void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
|
||||||
{
|
{
|
||||||
int index;
|
size_t index;
|
||||||
|
|
||||||
if (!ppAddins)
|
if (!ppAddins)
|
||||||
return;
|
return;
|
||||||
@ -329,48 +387,68 @@ void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
|
|||||||
|
|
||||||
extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
|
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;
|
size_t i;
|
||||||
STATIC_ENTRY* entry;
|
|
||||||
|
|
||||||
for (i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; 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 TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPSTR pszSubsystem,
|
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
|
||||||
LPSTR pszType, DWORD dwFlags)
|
LPCSTR pszType, DWORD dwFlags)
|
||||||
{
|
{
|
||||||
int i, j;
|
const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
|
||||||
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
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)
|
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 (pszType)
|
||||||
{
|
{
|
||||||
if (strcmp(subsystems[j].type, pszType) == 0)
|
if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
|
||||||
return (PVIRTUALCHANNELENTRY) subsystems[j].entry;
|
return (PVIRTUALCHANNELENTRY)subsystems->entry;
|
||||||
}
|
}
|
||||||
else
|
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 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <freerdp/channels/rdpdr.h>
|
||||||
#include "tables.h"
|
#include "tables.h"
|
||||||
|
|
||||||
${CLIENT_STATIC_TYPEDEFS}
|
${CLIENT_STATIC_TYPEDEFS}
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
struct _STATIC_ENTRY
|
struct _STATIC_ENTRY
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
UINT(*entry)();
|
UINT (*entry)();
|
||||||
};
|
};
|
||||||
typedef struct _STATIC_ENTRY STATIC_ENTRY;
|
typedef struct _STATIC_ENTRY STATIC_ENTRY;
|
||||||
|
|
||||||
@ -37,14 +37,15 @@ struct _STATIC_SUBSYSTEM_ENTRY
|
|||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
const char* type;
|
const char* type;
|
||||||
void (*entry)(void);
|
UINT (*entry)();
|
||||||
};
|
};
|
||||||
typedef struct _STATIC_SUBSYSTEM_ENTRY STATIC_SUBSYSTEM_ENTRY;
|
typedef struct _STATIC_SUBSYSTEM_ENTRY STATIC_SUBSYSTEM_ENTRY;
|
||||||
|
|
||||||
struct _STATIC_ADDIN_TABLE
|
struct _STATIC_ADDIN_TABLE
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
UINT(*entry)();
|
const char* type;
|
||||||
|
UINT (*entry)();
|
||||||
const STATIC_SUBSYSTEM_ENTRY* table;
|
const STATIC_SUBSYSTEM_ENTRY* table;
|
||||||
};
|
};
|
||||||
typedef struct _STATIC_ADDIN_TABLE STATIC_ADDIN_TABLE;
|
typedef struct _STATIC_ADDIN_TABLE STATIC_ADDIN_TABLE;
|
||||||
|
|||||||
@ -21,7 +21,10 @@ set(${MODULE_PREFIX}_SRCS
|
|||||||
cliprdr_format.c
|
cliprdr_format.c
|
||||||
cliprdr_format.h
|
cliprdr_format.h
|
||||||
cliprdr_main.c
|
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")
|
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||||
|
|
||||||
|
|||||||
@ -33,22 +33,17 @@
|
|||||||
|
|
||||||
#include "cliprdr_main.h"
|
#include "cliprdr_main.h"
|
||||||
#include "cliprdr_format.h"
|
#include "cliprdr_format.h"
|
||||||
|
#include "../cliprdr_common.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @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;
|
CLIPRDR_FORMAT_LIST formatList = { 0 };
|
||||||
size_t position;
|
|
||||||
BOOL asciiNames;
|
|
||||||
int formatNameLength;
|
|
||||||
char* szFormatName;
|
|
||||||
WCHAR* wszFormatName;
|
|
||||||
CLIPRDR_FORMAT* formats = NULL;
|
|
||||||
CLIPRDR_FORMAT_LIST formatList;
|
|
||||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
@ -58,185 +53,24 @@ UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
asciiNames = (msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
|
|
||||||
|
|
||||||
formatList.msgType = CB_FORMAT_LIST;
|
formatList.msgType = CB_FORMAT_LIST;
|
||||||
formatList.msgFlags = msgFlags;
|
formatList.msgFlags = msgFlags;
|
||||||
formatList.dataLen = dataLen;
|
formatList.dataLen = dataLen;
|
||||||
|
|
||||||
index = 0;
|
if ((error = cliprdr_read_format_list(s, &formatList, cliprdr->useLongFormatNames)))
|
||||||
formatList.numFormats = 0;
|
goto error_out;
|
||||||
position = Stream_GetPosition(s);
|
|
||||||
|
|
||||||
if (!formatList.dataLen)
|
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "",
|
||||||
{
|
formatList.numFormats);
|
||||||
/* 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);
|
|
||||||
|
|
||||||
if (context->ServerFormatList)
|
if (context->ServerFormatList)
|
||||||
{
|
{
|
||||||
if ((error = context->ServerFormatList(context, &formatList)))
|
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:
|
error_out:
|
||||||
if (formats)
|
cliprdr_free_format_list(&formatList);
|
||||||
{
|
|
||||||
for (index = 0; index < formatList.numFormats; index++)
|
|
||||||
{
|
|
||||||
free(formats[index].formatName);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(formats);
|
|
||||||
}
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,9 +79,10 @@ error_out:
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @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);
|
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||||
UINT error = CHANNEL_RC_OK;
|
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);
|
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "ServerFormatListResponse failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "ServerFormatListResponse failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
return 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
|
* @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;
|
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
|
||||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
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.msgFlags = msgFlags;
|
||||||
formatDataRequest.dataLen = dataLen;
|
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);
|
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
return 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
|
* @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;
|
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
|
||||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
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.msgType = CB_FORMAT_DATA_RESPONSE;
|
||||||
formatDataResponse.msgFlags = msgFlags;
|
formatDataResponse.msgFlags = msgFlags;
|
||||||
formatDataResponse.dataLen = dataLen;
|
formatDataResponse.dataLen = dataLen;
|
||||||
formatDataResponse.requestedFormatData = NULL;
|
|
||||||
|
|
||||||
if (dataLen)
|
if ((error = cliprdr_read_format_data_response(s, &formatDataResponse)))
|
||||||
formatDataResponse.requestedFormatData = (BYTE*) Stream_Pointer(s);
|
return error;
|
||||||
|
|
||||||
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
|
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT64 filetime_to_uint64(FILETIME value)
|
|
||||||
{
|
|
||||||
UINT64 converted = 0;
|
|
||||||
converted |= (UINT32) value.dwHighDateTime;
|
|
||||||
converted <<= 32;
|
|
||||||
converted |= (UINT32) value.dwLowDateTime;
|
|
||||||
return converted;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILETIME uint64_to_filetime(UINT64 value)
|
|
||||||
{
|
|
||||||
FILETIME converted;
|
|
||||||
converted.dwLowDateTime = (UINT32) (value >> 0);
|
|
||||||
converted.dwHighDateTime = (UINT32) (value >> 32);
|
|
||||||
return converted;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a packed file list.
|
|
||||||
*
|
|
||||||
* The resulting array must be freed with the `free()` function.
|
|
||||||
*
|
|
||||||
* @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
|
|
||||||
* @param [in] format_data_length length of `format_data` in bytes.
|
|
||||||
* @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
|
|
||||||
* @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
|
|
||||||
*
|
|
||||||
* @returns 0 on success, otherwise a Win32 error code.
|
|
||||||
*/
|
|
||||||
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
|
|
||||||
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
|
#ifndef FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H
|
||||||
#define 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(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
UINT16 msgFlags);
|
||||||
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||||
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
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 */
|
#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 streamFileClipEnabled;
|
||||||
BOOL fileClipNoFilePaths;
|
BOOL fileClipNoFilePaths;
|
||||||
BOOL canLockClipData;
|
BOOL canLockClipData;
|
||||||
|
BOOL hasHugeFileSupport;
|
||||||
};
|
};
|
||||||
typedef struct cliprdr_plugin cliprdrPlugin;
|
typedef struct cliprdr_plugin cliprdrPlugin;
|
||||||
|
|
||||||
@ -57,7 +58,10 @@ CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
|
|||||||
#ifdef WITH_DEBUG_CLIPRDR
|
#ifdef WITH_DEBUG_CLIPRDR
|
||||||
#define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
|
#define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define DEBUG_CLIPRDR(...) do { } while (0)
|
#define DEBUG_CLIPRDR(...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */
|
#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
|
set(${MODULE_PREFIX}_SRCS
|
||||||
cliprdr_main.c
|
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")
|
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 TAG CHANNELS_TAG("cliprdr.server")
|
||||||
|
|
||||||
#define CLIPRDR_HEADER_LENGTH 8
|
#define CLIPRDR_HEADER_LENGTH 8
|
||||||
|
|
||||||
struct _cliprdr_server_private
|
struct _cliprdr_server_private
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,3 +20,7 @@ define_channel("disp")
|
|||||||
if(WITH_CLIENT_CHANNELS)
|
if(WITH_CLIENT_CHANNELS)
|
||||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||||
endif()
|
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
|
set(${MODULE_PREFIX}_SRCS
|
||||||
disp_main.c
|
disp_main.c
|
||||||
disp_main.h)
|
disp_main.h
|
||||||
|
../disp_common.c
|
||||||
|
../disp_common.h)
|
||||||
|
|
||||||
include_directories(..)
|
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 <freerdp/addin.h>
|
||||||
|
|
||||||
#include "disp_main.h"
|
#include "disp_main.h"
|
||||||
|
#include "../disp_common.h"
|
||||||
|
|
||||||
struct _DISP_CHANNEL_CALLBACK
|
struct _DISP_CHANNEL_CALLBACK
|
||||||
{
|
{
|
||||||
@ -71,6 +72,7 @@ struct _DISP_PLUGIN
|
|||||||
UINT32 MaxNumMonitors;
|
UINT32 MaxNumMonitors;
|
||||||
UINT32 MaxMonitorAreaFactorA;
|
UINT32 MaxMonitorAreaFactorA;
|
||||||
UINT32 MaxMonitorAreaFactorB;
|
UINT32 MaxMonitorAreaFactorB;
|
||||||
|
BOOL initialized;
|
||||||
};
|
};
|
||||||
typedef struct _DISP_PLUGIN DISP_PLUGIN;
|
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
|
* @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;
|
UINT status;
|
||||||
wStream* s;
|
wStream* s;
|
||||||
UINT32 type;
|
|
||||||
UINT32 index;
|
UINT32 index;
|
||||||
UINT32 length;
|
|
||||||
DISP_PLUGIN* disp;
|
DISP_PLUGIN* disp;
|
||||||
UINT32 MonitorLayoutSize;
|
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;
|
if (!s)
|
||||||
|
|
||||||
length = 8 + 8 + (NumMonitors * MonitorLayoutSize);
|
|
||||||
|
|
||||||
type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
|
|
||||||
|
|
||||||
s = Stream_New(NULL, length);
|
|
||||||
if(!s)
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_New failed!");
|
WLog_ERR(TAG, "Stream_New failed!");
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write_UINT32(s, type); /* Type (4 bytes) */
|
if ((status = disp_write_header(s, &header)))
|
||||||
Stream_Write_UINT32(s, length); /* Length (4 bytes) */
|
{
|
||||||
|
WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (NumMonitors > disp->MaxNumMonitors)
|
if (NumMonitors > disp->MaxNumMonitors)
|
||||||
NumMonitors = disp->MaxNumMonitors;
|
NumMonitors = disp->MaxNumMonitors;
|
||||||
|
|
||||||
Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
|
Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
|
||||||
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
|
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
|
||||||
|
WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%" PRIu32 "",
|
||||||
WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", NumMonitors);
|
NumMonitors);
|
||||||
|
|
||||||
for (index = 0; index < NumMonitors; index++)
|
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)
|
if (Monitors[index].Height > 8192)
|
||||||
Monitors[index].Height = 8192;
|
Monitors[index].Height = 8192;
|
||||||
|
|
||||||
Stream_Write_UINT32(s, Monitors[index].Flags); /* Flags (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].Left); /* Left (4 bytes) */
|
||||||
Stream_Write_UINT32(s, Monitors[index].Top); /* Top (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].Width); /* Width (4 bytes) */
|
||||||
Stream_Write_UINT32(s, Monitors[index].Height); /* Height (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].PhysicalWidth); /* PhysicalWidth (4 bytes) */
|
||||||
Stream_Write_UINT32(s, Monitors[index].PhysicalHeight); /* PhysicalHeight (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].Orientation); /* Orientation (4 bytes) */
|
||||||
Stream_Write_UINT32(s, Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
Stream_Write_UINT32(s,
|
||||||
|
Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
||||||
Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
||||||
|
WLog_DBG(TAG,
|
||||||
WLog_DBG(TAG, "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index,
|
"\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32
|
||||||
Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top, Monitors[index].Width,
|
"x%" PRIu32 ")",
|
||||||
Monitors[index].Height);
|
index, Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top,
|
||||||
WLog_DBG(TAG, "\t PhysicalWidth: %"PRIu32" PhysicalHeight: %"PRIu32" Orientation: %"PRIu32"",
|
Monitors[index].Width, Monitors[index].Height);
|
||||||
Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight, Monitors[index].Orientation);
|
WLog_DBG(TAG,
|
||||||
|
"\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32 " Orientation: %" PRIu32
|
||||||
|
"",
|
||||||
|
Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight,
|
||||||
|
Monitors[index].Orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
Stream_SealLength(s);
|
Stream_SealLength(s);
|
||||||
|
status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
|
||||||
status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), NULL);
|
NULL);
|
||||||
|
|
||||||
Stream_Free(s, TRUE);
|
Stream_Free(s, TRUE);
|
||||||
|
|
||||||
return status;
|
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
|
* @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;
|
DISP_PLUGIN* disp;
|
||||||
DispClientContext *context;
|
DispClientContext* context;
|
||||||
UINT ret = CHANNEL_RC_OK;
|
UINT ret = CHANNEL_RC_OK;
|
||||||
|
disp = (DISP_PLUGIN*)callback->plugin;
|
||||||
disp = (DISP_PLUGIN*) callback->plugin;
|
context = (DispClientContext*)disp->iface.pInterface;
|
||||||
context = (DispClientContext *)disp->iface.pInterface;
|
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 12)
|
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;
|
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->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
|
||||||
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
|
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
|
||||||
|
|
||||||
if (context->DisplayControlCaps)
|
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;
|
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
|
* @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 error;
|
||||||
UINT32 length;
|
DISPLAY_CONTROL_HEADER header;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 8)
|
if (Stream_GetRemainingLength(s) < 8)
|
||||||
{
|
{
|
||||||
@ -207,18 +214,25 @@ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
|||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Read_UINT32(s, type); /* Type (4 bytes) */
|
if ((error = disp_read_header(s, &header)))
|
||||||
Stream_Read_UINT32(s, length); /* Length (4 bytes) */
|
{
|
||||||
|
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:
|
case DISPLAY_CONTROL_PDU_TYPE_CAPS:
|
||||||
return disp_recv_display_control_caps_pdu(callback, s);
|
return disp_recv_display_control_caps_pdu(callback, s);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_ERR(TAG, "Type %"PRIu32" not recognized!", type);
|
WLog_ERR(TAG, "Type %" PRIu32 " not recognized!", header.type);
|
||||||
return ERROR_INTERNAL_ERROR;
|
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
|
* @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);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||||
IWTSVirtualChannelCallback** ppCallback)
|
IWTSVirtualChannelCallback** ppCallback)
|
||||||
{
|
{
|
||||||
DISP_CHANNEL_CALLBACK* callback;
|
DISP_CHANNEL_CALLBACK* callback;
|
||||||
DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*) pListenerCallback;
|
DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*)pListenerCallback;
|
||||||
|
callback = (DISP_CHANNEL_CALLBACK*)calloc(1, sizeof(DISP_CHANNEL_CALLBACK));
|
||||||
callback = (DISP_CHANNEL_CALLBACK*) calloc(1, sizeof(DISP_CHANNEL_CALLBACK));
|
|
||||||
|
|
||||||
if (!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_mgr = listener_callback->channel_mgr;
|
||||||
callback->channel = pChannel;
|
callback->channel = pChannel;
|
||||||
listener_callback->channel_callback = callback;
|
listener_callback->channel_callback = callback;
|
||||||
|
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
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)
|
static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||||
{
|
{
|
||||||
UINT status;
|
UINT status;
|
||||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin;
|
DISP_PLUGIN* disp = (DISP_PLUGIN*)pPlugin;
|
||||||
|
if (disp->initialized)
|
||||||
disp->listener_callback = (DISP_LISTENER_CALLBACK*) calloc(1, sizeof(DISP_LISTENER_CALLBACK));
|
{
|
||||||
|
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)
|
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->iface.OnNewChannelConnection = disp_on_new_channel_connection;
|
||||||
disp->listener_callback->plugin = pPlugin;
|
disp->listener_callback->plugin = pPlugin;
|
||||||
disp->listener_callback->channel_mgr = pChannelMgr;
|
disp->listener_callback->channel_mgr = pChannelMgr;
|
||||||
|
|
||||||
status = pChannelMgr->CreateListener(pChannelMgr, DISP_DVC_CHANNEL_NAME, 0,
|
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->listener->pInterface = disp->iface.pInterface;
|
||||||
|
|
||||||
|
disp->initialized = status == CHANNEL_RC_OK;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +328,15 @@ static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
|||||||
*/
|
*/
|
||||||
static UINT disp_plugin_terminated(IWTSPlugin* pPlugin)
|
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->listener_callback);
|
||||||
free(disp->iface.pInterface);
|
free(disp->iface.pInterface);
|
||||||
free(pPlugin);
|
free(pPlugin);
|
||||||
@ -331,18 +352,18 @@ static UINT disp_plugin_terminated(IWTSPlugin* pPlugin)
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @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;
|
DISP_CHANNEL_CALLBACK* callback = disp->listener_callback->channel_callback;
|
||||||
|
|
||||||
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
|
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define DVCPluginEntry disp_DVCPluginEntry
|
#define DVCPluginEntry disp_DVCPluginEntry
|
||||||
#else
|
#else
|
||||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -355,11 +376,12 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
DISP_PLUGIN* disp;
|
DISP_PLUGIN* disp;
|
||||||
DispClientContext* context;
|
DispClientContext* context;
|
||||||
|
disp = (DISP_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "disp");
|
||||||
|
|
||||||
disp = (DISP_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "disp");
|
|
||||||
if (!disp)
|
if (!disp)
|
||||||
{
|
{
|
||||||
disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN));
|
disp = (DISP_PLUGIN*)calloc(1, sizeof(DISP_PLUGIN));
|
||||||
|
|
||||||
if (!disp)
|
if (!disp)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
@ -373,8 +395,8 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
disp->MaxNumMonitors = 16;
|
disp->MaxNumMonitors = 16;
|
||||||
disp->MaxMonitorAreaFactorA = 8192;
|
disp->MaxMonitorAreaFactorA = 8192;
|
||||||
disp->MaxMonitorAreaFactorB = 8192;
|
disp->MaxMonitorAreaFactorB = 8192;
|
||||||
|
context = (DispClientContext*)calloc(1, sizeof(DispClientContext));
|
||||||
|
|
||||||
context = (DispClientContext*) calloc(1, sizeof(DispClientContext));
|
|
||||||
if (!context)
|
if (!context)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
@ -382,12 +404,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->handle = (void*) disp;
|
context->handle = (void*)disp;
|
||||||
context->SendMonitorLayout = disp_send_monitor_layout;
|
context->SendMonitorLayout = disp_send_monitor_layout;
|
||||||
|
disp->iface.pInterface = (void*)context;
|
||||||
disp->iface.pInterface = (void*) context;
|
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*)disp);
|
||||||
|
|
||||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -33,10 +33,6 @@
|
|||||||
|
|
||||||
#include <freerdp/client/disp.h>
|
#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")
|
#define TAG CHANNELS_TAG("disp.client")
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_DISP_CLIENT_MAIN_H */
|
#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;
|
typedef struct drdynvc_plugin drdynvcPlugin;
|
||||||
|
|
||||||
#define MAX_PLUGINS 32
|
|
||||||
|
|
||||||
struct _DVCMAN
|
struct _DVCMAN
|
||||||
{
|
{
|
||||||
IWTSVirtualChannelManager iface;
|
IWTSVirtualChannelManager iface;
|
||||||
|
|
||||||
drdynvcPlugin* drdynvc;
|
drdynvcPlugin* drdynvc;
|
||||||
|
|
||||||
int num_plugins;
|
wArrayList* plugin_names;
|
||||||
const char* plugin_names[MAX_PLUGINS];
|
wArrayList* plugins;
|
||||||
IWTSPlugin* plugins[MAX_PLUGINS];
|
|
||||||
|
|
||||||
int num_listeners;
|
|
||||||
IWTSListener* listeners[MAX_PLUGINS];
|
|
||||||
|
|
||||||
|
wArrayList* listeners;
|
||||||
wArrayList* channels;
|
wArrayList* channels;
|
||||||
wStreamPool* pool;
|
wStreamPool* pool;
|
||||||
};
|
};
|
||||||
@ -106,11 +101,11 @@ enum _DRDYNVC_STATE
|
|||||||
};
|
};
|
||||||
typedef enum _DRDYNVC_STATE DRDYNVC_STATE;
|
typedef enum _DRDYNVC_STATE DRDYNVC_STATE;
|
||||||
|
|
||||||
#define CREATE_REQUEST_PDU 0x01
|
#define CREATE_REQUEST_PDU 0x01
|
||||||
#define DATA_FIRST_PDU 0x02
|
#define DATA_FIRST_PDU 0x02
|
||||||
#define DATA_PDU 0x03
|
#define DATA_PDU 0x03
|
||||||
#define CLOSE_REQUEST_PDU 0x04
|
#define CLOSE_REQUEST_PDU 0x04
|
||||||
#define CAPABILITY_REQUEST_PDU 0x05
|
#define CAPABILITY_REQUEST_PDU 0x05
|
||||||
|
|
||||||
struct drdynvc_plugin
|
struct drdynvc_plugin
|
||||||
{
|
{
|
||||||
@ -127,7 +122,7 @@ struct drdynvc_plugin
|
|||||||
DRDYNVC_STATE state;
|
DRDYNVC_STATE state;
|
||||||
DrdynvcClientContext* context;
|
DrdynvcClientContext* context;
|
||||||
|
|
||||||
int version;
|
UINT16 version;
|
||||||
int PriorityCharge0;
|
int PriorityCharge0;
|
||||||
int PriorityCharge1;
|
int PriorityCharge1;
|
||||||
int PriorityCharge2;
|
int PriorityCharge2;
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
#define TAG CHANNELS_TAG("drdynvc.server")
|
#define TAG CHANNELS_TAG("drdynvc.server")
|
||||||
|
|
||||||
|
|
||||||
static DWORD WINAPI drdynvc_server_thread(LPVOID arg)
|
static DWORD WINAPI drdynvc_server_thread(LPVOID arg)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
@ -121,8 +120,8 @@ static DWORD WINAPI drdynvc_server_thread(LPVOID arg)
|
|||||||
*/
|
*/
|
||||||
static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
||||||
{
|
{
|
||||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm,
|
context->priv->ChannelHandle =
|
||||||
WTS_CURRENT_SESSION, "drdynvc");
|
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "drdynvc");
|
||||||
|
|
||||||
if (!context->priv->ChannelHandle)
|
if (!context->priv->ChannelHandle)
|
||||||
{
|
{
|
||||||
@ -136,7 +135,8 @@ static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
|||||||
return ERROR_INTERNAL_ERROR;
|
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!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
CloseHandle(context->priv->StopEvent);
|
CloseHandle(context->priv->StopEvent);
|
||||||
@ -160,7 +160,7 @@ static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
|||||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,14 +171,14 @@ static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
|||||||
DrdynvcServerContext* drdynvc_server_context_new(HANDLE vcm)
|
DrdynvcServerContext* drdynvc_server_context_new(HANDLE vcm)
|
||||||
{
|
{
|
||||||
DrdynvcServerContext* context;
|
DrdynvcServerContext* context;
|
||||||
context = (DrdynvcServerContext*) calloc(1, sizeof(DrdynvcServerContext));
|
context = (DrdynvcServerContext*)calloc(1, sizeof(DrdynvcServerContext));
|
||||||
|
|
||||||
if (context)
|
if (context)
|
||||||
{
|
{
|
||||||
context->vcm = vcm;
|
context->vcm = vcm;
|
||||||
context->Start = drdynvc_server_start;
|
context->Start = drdynvc_server_start;
|
||||||
context->Stop = drdynvc_server_stop;
|
context->Stop = drdynvc_server_stop;
|
||||||
context->priv = (DrdynvcServerPrivate*) calloc(1, sizeof(DrdynvcServerPrivate));
|
context->priv = (DrdynvcServerPrivate*)calloc(1, sizeof(DrdynvcServerPrivate));
|
||||||
|
|
||||||
if (!context->priv)
|
if (!context->priv)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -46,15 +46,29 @@
|
|||||||
#include "drive_file.h"
|
#include "drive_file.h"
|
||||||
|
|
||||||
#ifdef WITH_DEBUG_RDPDR
|
#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
|
#else
|
||||||
#define DEBUG_WSTR(msg, wstr) do { } while (0)
|
#define DEBUG_WSTR(msg, wstr) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void drive_file_fix_path(WCHAR* path)
|
static BOOL drive_file_fix_path(WCHAR* path, size_t length)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t length = _wcslen(path);
|
|
||||||
|
if ((length == 0) || (length > UINT32_MAX))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
WINPR_ASSERT(path);
|
||||||
|
|
||||||
for (i = 0; i < length; i++)
|
for (i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@ -65,57 +79,82 @@ static void drive_file_fix_path(WCHAR* path)
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
||||||
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
|
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
|
||||||
return;
|
return FALSE;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
if ((length == 1) && (path[0] == L'/'))
|
if ((length == 1) && (path[0] == L'/'))
|
||||||
return;
|
return FALSE;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((length > 0) && (path[length - 1] == L'/'))
|
if ((length > 0) && (path[length - 1] == L'/'))
|
||||||
path[length - 1] = L'\0';
|
path[length - 1] = L'\0';
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
|
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
|
||||||
size_t PathLength)
|
size_t PathWCharLength)
|
||||||
{
|
{
|
||||||
WCHAR* fullpath;
|
BOOL ok = FALSE;
|
||||||
size_t base_path_length;
|
WCHAR* fullpath = NULL;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
if (!base_path || !path)
|
if (!base_path || (!path && (PathWCharLength > 0)))
|
||||||
return NULL;
|
goto fail;
|
||||||
|
|
||||||
base_path_length = _wcslen(base_path) * 2;
|
const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
|
||||||
fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR));
|
length = base_path_length + PathWCharLength + 1;
|
||||||
|
fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
|
||||||
|
|
||||||
if (!fullpath)
|
if (!fullpath)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
|
||||||
|
if (path)
|
||||||
|
CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
|
||||||
|
|
||||||
|
if (!drive_file_fix_path(fullpath, length))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Ensure the path does not contain sequences like '..' */
|
||||||
|
const WCHAR dotdot[] = { '.', '.', '\0' };
|
||||||
|
if (_wcsstr(&fullpath[base_path_length], dotdot))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "malloc failed!");
|
char abuffer[MAX_PATH] = { 0 };
|
||||||
return NULL;
|
ConvertFromUnicode(CP_UTF8, 0, &fullpath[base_path_length], -1, (char**)&abuffer,
|
||||||
|
ARRAYSIZE(abuffer) - 1, NULL, NULL);
|
||||||
|
|
||||||
|
WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
|
||||||
|
&abuffer[base_path_length]);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyMemory(fullpath, base_path, base_path_length);
|
ok = TRUE;
|
||||||
CopyMemory((char*)fullpath + base_path_length, path, PathLength);
|
fail:
|
||||||
drive_file_fix_path(fullpath);
|
if (!ok)
|
||||||
|
{
|
||||||
|
free(fullpath);
|
||||||
|
fullpath = NULL;
|
||||||
|
}
|
||||||
return fullpath;
|
return fullpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL drive_file_remove_dir(const WCHAR* path)
|
static BOOL drive_file_remove_dir(const WCHAR* path)
|
||||||
{
|
{
|
||||||
WIN32_FIND_DATAW findFileData;
|
WIN32_FIND_DATAW findFileData = { 0 };
|
||||||
BOOL ret = TRUE;
|
BOOL ret = TRUE;
|
||||||
HANDLE dir;
|
HANDLE dir = INVALID_HANDLE_VALUE;
|
||||||
WCHAR* fullpath;
|
WCHAR* fullpath = NULL;
|
||||||
WCHAR* path_slash;
|
WCHAR* path_slash = NULL;
|
||||||
size_t base_path_length;
|
size_t base_path_length = 0;
|
||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
base_path_length = _wcslen(path) * 2;
|
base_path_length = _wcslen(path);
|
||||||
path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3);
|
path_slash = (WCHAR*)calloc(base_path_length + 3, sizeof(WCHAR));
|
||||||
|
|
||||||
if (!path_slash)
|
if (!path_slash)
|
||||||
{
|
{
|
||||||
@ -123,12 +162,11 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyMemory(path_slash, path, base_path_length);
|
CopyMemory(path_slash, path, base_path_length * sizeof(WCHAR));
|
||||||
path_slash[base_path_length / 2] = L'/';
|
path_slash[base_path_length] = L'/';
|
||||||
path_slash[base_path_length / 2 + 1] = L'*';
|
path_slash[base_path_length + 1] = L'*';
|
||||||
DEBUG_WSTR("Search in %s", path_slash);
|
DEBUG_WSTR("Search in %s", path_slash);
|
||||||
dir = FindFirstFileW(path_slash, &findFileData);
|
dir = FindFirstFileW(path_slash, &findFileData);
|
||||||
path_slash[base_path_length / 2 + 1] = 0;
|
|
||||||
|
|
||||||
if (dir == INVALID_HANDLE_VALUE)
|
if (dir == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
@ -138,15 +176,15 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
size_t len = _wcslen(findFileData.cFileName);
|
const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
|
||||||
|
|
||||||
if ((len == 1 && findFileData.cFileName[0] == L'.') || (len == 2 &&
|
if ((len == 1 && findFileData.cFileName[0] == L'.') ||
|
||||||
findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
|
(len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2);
|
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len);
|
||||||
DEBUG_WSTR("Delete %s", fullpath);
|
DEBUG_WSTR("Delete %s", fullpath);
|
||||||
|
|
||||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
@ -162,8 +200,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
|||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
break;
|
break;
|
||||||
}
|
} while (ret && FindNextFileW(dir, &findFileData) != 0);
|
||||||
while (ret && FindNextFileW(dir, &findFileData) != 0);
|
|
||||||
|
|
||||||
FindClose(dir);
|
FindClose(dir);
|
||||||
|
|
||||||
@ -238,7 +275,8 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
|||||||
if (file->is_dir)
|
if (file->is_dir)
|
||||||
{
|
{
|
||||||
/* Should only create the directory if the disposition allows for it */
|
/* 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)
|
if (CreateDirectoryW(file->fullpath, NULL) != 0)
|
||||||
{
|
{
|
||||||
@ -255,27 +293,33 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
|||||||
{
|
{
|
||||||
switch (file->CreateDisposition)
|
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;
|
CreateDisposition = CREATE_ALWAYS;
|
||||||
break;
|
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;
|
CreateDisposition = OPEN_EXISTING;
|
||||||
break;
|
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;
|
CreateDisposition = CREATE_NEW;
|
||||||
break;
|
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;
|
CreateDisposition = OPEN_ALWAYS;
|
||||||
break;
|
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;
|
CreateDisposition = TRUNCATE_EXISTING;
|
||||||
break;
|
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;
|
CreateDisposition = CREATE_ALWAYS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -286,11 +330,11 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
|||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
file->SharedAccess = 0;
|
file->SharedAccess = 0;
|
||||||
#endif
|
#endif
|
||||||
file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess,
|
file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess, file->SharedAccess,
|
||||||
file->SharedAccess, NULL, CreateDisposition,
|
NULL, CreateDisposition, file->FileAttributes, NULL);
|
||||||
file->FileAttributes, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
if (file->file_handle == INVALID_HANDLE_VALUE)
|
if (file->file_handle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
/* Get the error message, if any. */
|
/* Get the error message, if any. */
|
||||||
@ -298,31 +342,34 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
|||||||
|
|
||||||
if (errorMessageID != 0)
|
if (errorMessageID != 0)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
|
||||||
LPSTR messageBuffer = NULL;
|
LPSTR messageBuffer = NULL;
|
||||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
size_t size =
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
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);
|
WLog_ERR(TAG, "Error in drive_file_init: %s %s", messageBuffer, file->fullpath);
|
||||||
/* Free the buffer. */
|
/* Free the buffer. */
|
||||||
LocalFree(messageBuffer);
|
LocalFree(messageBuffer);
|
||||||
#endif
|
/* restore original error code */
|
||||||
|
SetLastError(errorMessageID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return file->file_handle != INVALID_HANDLE_VALUE;
|
return file->file_handle != INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
|
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
||||||
UINT32 DesiredAccess, UINT32 CreateDisposition,
|
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
|
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
|
||||||
{
|
{
|
||||||
DRIVE_FILE* file;
|
DRIVE_FILE* file;
|
||||||
|
|
||||||
if (!base_path || !path)
|
if (!base_path || (!path && (PathWCharLength > 0)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
file = (DRIVE_FILE*) calloc(1, sizeof(DRIVE_FILE));
|
file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
|
||||||
|
|
||||||
if (!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->CreateDisposition = CreateDisposition;
|
||||||
file->CreateOptions = CreateOptions;
|
file->CreateOptions = CreateOptions;
|
||||||
file->SharedAccess = SharedAccess;
|
file->SharedAccess = SharedAccess;
|
||||||
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength));
|
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength));
|
||||||
|
|
||||||
if (!drive_file_init(file))
|
if (!drive_file_init(file))
|
||||||
{
|
{
|
||||||
|
DWORD lastError = GetLastError();
|
||||||
drive_file_free(file);
|
drive_file_free(file);
|
||||||
|
SetLastError(lastError);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,15 +510,23 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||||||
goto out_fail;
|
goto out_fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(output, 36); /* Length */
|
Stream_Write_UINT32(output, 36); /* Length */
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftCreationTime.dwLowDateTime); /* CreationTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftCreationTime.dwHighDateTime); /* CreationTime */
|
fileAttributes.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
fileAttributes.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
fileAttributes.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
output, fileAttributes.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, fileAttributes.dwFileAttributes); /* FileAttributes */
|
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! */
|
/* Reserved(4), MUST NOT be added! */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -479,15 +536,16 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 22))
|
if (!Stream_EnsureRemainingCapacity(output, 4 + 22))
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(output, 22); /* Length */
|
Stream_Write_UINT32(output, 22); /* Length */
|
||||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* AllocationSize */
|
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* AllocationSize */
|
||||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeHigh); /* 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, fileAttributes.nFileSizeHigh); /* EndOfFile */
|
||||||
Stream_Write_UINT32(output, 0); /* NumberOfLinks */
|
Stream_Write_UINT32(output, 0); /* NumberOfLinks */
|
||||||
Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
|
Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
|
||||||
Stream_Write_UINT8(output, fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? TRUE :
|
Stream_Write_UINT8(output, fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
|
||||||
FALSE); /* Directory */
|
? TRUE
|
||||||
|
: FALSE); /* Directory */
|
||||||
/* Reserved(2), MUST NOT be added! */
|
/* Reserved(2), MUST NOT be added! */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -497,9 +555,9 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 8))
|
if (!Stream_EnsureRemainingCapacity(output, 4 + 8))
|
||||||
goto out_fail;
|
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, fileAttributes.dwFileAttributes); /* FileAttributes */
|
||||||
Stream_Write_UINT32(output, 0); /* ReparseTag */
|
Stream_Write_UINT32(output, 0); /* ReparseTag */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -556,47 +614,49 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
|
|
||||||
if (file->file_handle == INVALID_HANDLE_VALUE)
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (liCreationTime.QuadPart != 0)
|
if (liCreationTime.QuadPart != 0)
|
||||||
{
|
{
|
||||||
ftCreationTime.dwHighDateTime = liCreationTime.HighPart;
|
ftCreationTime.dwHighDateTime = liCreationTime.u.HighPart;
|
||||||
ftCreationTime.dwLowDateTime = liCreationTime.LowPart;
|
ftCreationTime.dwLowDateTime = liCreationTime.u.LowPart;
|
||||||
pftCreationTime = &ftCreationTime;
|
pftCreationTime = &ftCreationTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (liLastAccessTime.QuadPart != 0)
|
if (liLastAccessTime.QuadPart != 0)
|
||||||
{
|
{
|
||||||
ftLastAccessTime.dwHighDateTime = liLastAccessTime.HighPart;
|
ftLastAccessTime.dwHighDateTime = liLastAccessTime.u.HighPart;
|
||||||
ftLastAccessTime.dwLowDateTime = liLastAccessTime.LowPart;
|
ftLastAccessTime.dwLowDateTime = liLastAccessTime.u.LowPart;
|
||||||
pftLastAccessTime = &ftLastAccessTime;
|
pftLastAccessTime = &ftLastAccessTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (liLastWriteTime.QuadPart != 0)
|
if (liLastWriteTime.QuadPart != 0)
|
||||||
{
|
{
|
||||||
ftLastWriteTime.dwHighDateTime = liLastWriteTime.HighPart;
|
ftLastWriteTime.dwHighDateTime = liLastWriteTime.u.HighPart;
|
||||||
ftLastWriteTime.dwLowDateTime = liLastWriteTime.LowPart;
|
ftLastWriteTime.dwLowDateTime = liLastWriteTime.u.LowPart;
|
||||||
pftLastWriteTime = &ftLastWriteTime;
|
pftLastWriteTime = &ftLastWriteTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart)
|
if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart)
|
||||||
{
|
{
|
||||||
ftLastWriteTime.dwHighDateTime = liChangeTime.HighPart;
|
ftLastWriteTime.dwHighDateTime = liChangeTime.u.HighPart;
|
||||||
ftLastWriteTime.dwLowDateTime = liChangeTime.LowPart;
|
ftLastWriteTime.dwLowDateTime = liChangeTime.u.LowPart;
|
||||||
pftLastWriteTime = &ftLastWriteTime;
|
pftLastWriteTime = &ftLastWriteTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_WSTR("SetFileTime %s", file->fullpath);
|
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);
|
WLog_ERR(TAG, "Unable to set file time to %s", file->fullpath);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFileAttributesW(file->fullpath, FileAttributes);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileEndOfFileInformation:
|
case FileEndOfFileInformation:
|
||||||
@ -611,8 +671,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
|
|
||||||
if (file->file_handle == INVALID_HANDLE_VALUE)
|
if (file->file_handle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Unable to truncate %s to %"PRId64" (%"PRId32")", file->fullpath, size,
|
WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", file->fullpath,
|
||||||
GetLastError());
|
size, GetLastError());
|
||||||
return FALSE;
|
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))
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,7 +689,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
|
|
||||||
if (SetEndOfFile(file->file_handle) == 0)
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,13 +741,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
|
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
|
||||||
FileNameLength);
|
FileNameLength / sizeof(WCHAR));
|
||||||
|
|
||||||
if (!fullpath)
|
if (!fullpath)
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
@ -699,7 +758,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
DEBUG_WSTR("MoveFileExW %s", file->fullpath);
|
DEBUG_WSTR("MoveFileExW %s", file->fullpath);
|
||||||
|
|
||||||
if (MoveFileExW(file->fullpath, 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))
|
if (!drive_file_set_fullpath(file, fullpath))
|
||||||
return FALSE;
|
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,
|
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||||
const WCHAR* path, UINT32 PathLength, wStream* output)
|
const WCHAR* path, UINT32 PathWCharLength, wStream* output)
|
||||||
{
|
{
|
||||||
size_t length;
|
size_t length;
|
||||||
WCHAR* ent_path;
|
WCHAR* ent_path;
|
||||||
@ -737,7 +797,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||||||
if (file->find_handle != INVALID_HANDLE_VALUE)
|
if (file->find_handle != INVALID_HANDLE_VALUE)
|
||||||
FindClose(file->find_handle);
|
FindClose(file->find_handle);
|
||||||
|
|
||||||
ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength);
|
ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength);
|
||||||
/* open new search handle and retrieve the first entry */
|
/* open new search handle and retrieve the first entry */
|
||||||
file->find_handle = FindFirstFileW(ent_path, &file->find_data);
|
file->find_handle = FindFirstFileW(ent_path, &file->find_data);
|
||||||
free(ent_path);
|
free(ent_path);
|
||||||
@ -762,22 +822,30 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||||||
goto out_fail;
|
goto out_fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(output, (UINT32)(64 + length)); /* Length */
|
Stream_Write_UINT32(output, (UINT32)(64 + length)); /* Length */
|
||||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
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, 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);
|
Stream_Write(output, file->find_data.cFileName, length);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -791,23 +859,31 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||||||
goto out_fail;
|
goto out_fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(output, (UINT32)(68 + length)); /* Length */
|
Stream_Write_UINT32(output, (UINT32)(68 + length)); /* Length */
|
||||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
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, file->find_data.dwFileAttributes); /* FileAttributes */
|
||||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||||
Stream_Write_UINT32(output, 0); /* EaSize */
|
Stream_Write_UINT32(output, 0); /* EaSize */
|
||||||
Stream_Write(output, file->find_data.cFileName, length);
|
Stream_Write(output, file->find_data.cFileName, length);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -821,24 +897,32 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||||||
goto out_fail;
|
goto out_fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(output, (UINT32)(93 + length)); /* Length */
|
Stream_Write_UINT32(output, (UINT32)(93 + length)); /* Length */
|
||||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
Stream_Write_UINT32(
|
||||||
Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */
|
file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */
|
Stream_Write_UINT32(output,
|
||||||
Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */
|
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, file->find_data.dwFileAttributes); /* FileAttributes */
|
||||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||||
Stream_Write_UINT32(output, 0); /* EaSize */
|
Stream_Write_UINT32(output, 0); /* EaSize */
|
||||||
Stream_Write_UINT8(output, 0); /* ShortNameLength */
|
Stream_Write_UINT8(output, 0); /* ShortNameLength */
|
||||||
/* Reserved(1), MUST NOT be added! */
|
/* Reserved(1), MUST NOT be added! */
|
||||||
Stream_Zero(output, 24); /* ShortName */
|
Stream_Zero(output, 24); /* ShortName */
|
||||||
Stream_Write(output, file->find_data.cFileName, length);
|
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;
|
goto out_fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(output, (UINT32)(12 + length)); /* Length */
|
Stream_Write_UINT32(output, (UINT32)(12 + length)); /* Length */
|
||||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||||
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */
|
||||||
Stream_Write(output, file->find_data.cFileName, length);
|
Stream_Write(output, file->find_data.cFileName, length);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_ERR(TAG, "unhandled FsInformationClass %"PRIu32, FsInformationClass);
|
WLog_ERR(TAG, "unhandled FsInformationClass %" PRIu32, FsInformationClass);
|
||||||
/* Unhandled FsInformationClass */
|
/* Unhandled FsInformationClass */
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
}
|
}
|
||||||
@ -869,6 +953,6 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
out_fail:
|
out_fail:
|
||||||
Stream_Write_UINT32(output, 0); /* Length */
|
Stream_Write_UINT32(output, 0); /* Length */
|
||||||
Stream_Write_UINT8(output, 0); /* Padding */
|
Stream_Write_UINT8(output, 0); /* Padding */
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,8 +51,8 @@ struct _DRIVE_FILE
|
|||||||
UINT32 CreateOptions;
|
UINT32 CreateOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
|
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
||||||
UINT32 DesiredAccess, UINT32 CreateDisposition,
|
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
|
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
|
||||||
BOOL drive_file_free(DRIVE_FILE* file);
|
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,
|
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
|
||||||
wStream* input);
|
wStream* input);
|
||||||
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||||
const WCHAR* path, UINT32 PathLength, wStream* output);
|
const WCHAR* path, UINT32 PathWCharLength, wStream* output);
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
|
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
|
||||||
|
|||||||
@ -102,7 +102,7 @@ static DWORD drive_map_windows_err(DWORD fs_errno)
|
|||||||
|
|
||||||
case ERROR_FILE_EXISTS:
|
case ERROR_FILE_EXISTS:
|
||||||
case ERROR_ALREADY_EXISTS:
|
case ERROR_ALREADY_EXISTS:
|
||||||
rc = STATUS_OBJECT_NAME_COLLISION;
|
rc = STATUS_OBJECT_NAME_COLLISION;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ERROR_INVALID_NAME:
|
case ERROR_INVALID_NAME:
|
||||||
@ -127,7 +127,7 @@ static DWORD drive_map_windows_err(DWORD fs_errno)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
rc = STATUS_UNSUCCESSFUL;
|
rc = STATUS_UNSUCCESSFUL;
|
||||||
WLog_ERR(TAG, "Error code not found: %"PRIu32"", fs_errno);
|
WLog_ERR(TAG, "Error code not found: %" PRIu32 "", fs_errno);
|
||||||
break;
|
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)
|
static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
|
||||||
{
|
{
|
||||||
DRIVE_FILE* file = NULL;
|
DRIVE_FILE* file = NULL;
|
||||||
void* key = (void*)(size_t) id;
|
void* key = (void*)(size_t)id;
|
||||||
|
|
||||||
if (!drive)
|
if (!drive)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
file = (DRIVE_FILE*) ListDictionary_GetItemValue(drive->files, key);
|
file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,10 +182,10 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
if (Stream_GetRemainingLength(irp->input) < PathLength)
|
if (Stream_GetRemainingLength(irp->input) < PathLength)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
path = (WCHAR*) Stream_Pointer(irp->input);
|
path = (const WCHAR*)Stream_Pointer(irp->input);
|
||||||
FileId = irp->devman->id_sequence++;
|
FileId = irp->devman->id_sequence++;
|
||||||
file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition,
|
file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
|
||||||
CreateOptions, FileAttributes, SharedAccess);
|
CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
@ -195,7 +195,7 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
void* key = (void*)(size_t) file->id;
|
void* key = (void*)(size_t)file->id;
|
||||||
|
|
||||||
if (!ListDictionary_Add(drive->files, key, file))
|
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;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
file = drive_get_file_by_id(drive, irp->FileId);
|
file = drive_get_file_by_id(drive, irp->FileId);
|
||||||
key = (void*)(size_t) irp->FileId;
|
key = (void*)(size_t)irp->FileId;
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||||
@ -331,6 +331,7 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
DRIVE_FILE* file;
|
DRIVE_FILE* file;
|
||||||
UINT32 Length;
|
UINT32 Length;
|
||||||
UINT64 Offset;
|
UINT64 Offset;
|
||||||
|
void* ptr;
|
||||||
|
|
||||||
if (!drive || !irp || !irp->input || !irp->output || !irp->Complete)
|
if (!drive || !irp || !irp->input || !irp->output || !irp->Complete)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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_UINT32(irp->input, Length);
|
||||||
Stream_Read_UINT64(irp->input, Offset);
|
Stream_Read_UINT64(irp->input, Offset);
|
||||||
Stream_Seek(irp->input, 20); /* Padding */
|
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);
|
file = drive_get_file_by_id(drive, irp->FileId);
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
@ -353,7 +357,7 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||||
Length = 0;
|
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());
|
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||||
Length = 0;
|
Length = 0;
|
||||||
@ -421,8 +425,7 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
{
|
{
|
||||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
else if (!drive_file_set_information(file, FsInformationClass, Length,
|
else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input))
|
||||||
irp->input))
|
|
||||||
{
|
{
|
||||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
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);
|
return irp->Complete(irp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
|
||||||
IRP* irp)
|
|
||||||
{
|
{
|
||||||
UINT32 FsInformationClass;
|
UINT32 FsInformationClass;
|
||||||
wStream* output = NULL;
|
wStream* output = NULL;
|
||||||
char* volumeLabel = {"FREERDP"};
|
char* volumeLabel = { "FREERDP" };
|
||||||
char* diskType = {"FAT32"};
|
char* diskType = { "FAT32" };
|
||||||
WCHAR* outStr = NULL;
|
WCHAR* outStr = NULL;
|
||||||
int length;
|
int length;
|
||||||
DWORD lpSectorsPerCluster;
|
DWORD lpSectorsPerCluster;
|
||||||
@ -489,10 +490,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
|||||||
|
|
||||||
GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
|
GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
|
||||||
Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
|
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, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
|
||||||
Stream_Write_UINT32(output, length); /* VolumeLabelLength */
|
Stream_Write_UINT32(output, length); /* VolumeLabelLength */
|
||||||
Stream_Write_UINT8(output, 0); /* SupportsObjects */
|
Stream_Write_UINT8(output, 0); /* SupportsObjects */
|
||||||
/* Reserved(1), MUST NOT be added! */
|
/* Reserved(1), MUST NOT be added! */
|
||||||
Stream_Write(output, outStr, length); /* VolumeLabel (Unicode) */
|
Stream_Write(output, outStr, length); /* VolumeLabel (Unicode) */
|
||||||
free(outStr);
|
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, lpTotalNumberOfClusters); /* TotalAllocationUnits */
|
||||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
||||||
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
||||||
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFsAttributeInformation:
|
case FileFsAttributeInformation:
|
||||||
@ -532,13 +534,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
|||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write_UINT32(output,
|
Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
|
||||||
FILE_CASE_SENSITIVE_SEARCH |
|
FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
|
||||||
FILE_CASE_PRESERVED_NAMES |
|
Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
|
||||||
FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
|
Stream_Write_UINT32(output, length); /* FileSystemNameLength */
|
||||||
Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
|
Stream_Write(output, outStr, length); /* FileSystemName (Unicode) */
|
||||||
Stream_Write_UINT32(output, length); /* FileSystemNameLength */
|
|
||||||
Stream_Write(output, outStr, length); /* FileSystemName (Unicode) */
|
|
||||||
free(outStr);
|
free(outStr);
|
||||||
break;
|
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, lpTotalNumberOfClusters); /* TotalAllocationUnits */
|
||||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
|
Stream_Write_UINT64(output,
|
||||||
|
lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
|
||||||
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
|
||||||
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
|
||||||
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileFsDeviceInformation:
|
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, FILE_DEVICE_DISK); /* DeviceType */
|
||||||
Stream_Write_UINT32(output, 0); /* Characteristics */
|
Stream_Write_UINT32(output, 0); /* Characteristics */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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_UINT8(irp->input, InitialQuery);
|
||||||
Stream_Read_UINT32(irp->input, PathLength);
|
Stream_Read_UINT32(irp->input, PathLength);
|
||||||
Stream_Seek(irp->input, 23); /* Padding */
|
Stream_Seek(irp->input, 23); /* Padding */
|
||||||
path = (WCHAR*) Stream_Pointer(irp->input);
|
path = (WCHAR*)Stream_Pointer(irp->input);
|
||||||
|
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
file = drive_get_file_by_id(drive, irp->FileId);
|
file = drive_get_file_by_id(drive, irp->FileId);
|
||||||
|
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
@ -635,8 +639,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||||
Stream_Write_UINT32(irp->output, 0); /* Length */
|
Stream_Write_UINT32(irp->output, 0); /* Length */
|
||||||
}
|
}
|
||||||
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength,
|
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
|
||||||
irp->output))
|
PathLength / sizeof(WCHAR), irp->output))
|
||||||
{
|
{
|
||||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||||
}
|
}
|
||||||
@ -754,7 +758,7 @@ static DWORD WINAPI drive_thread_func(LPVOID arg)
|
|||||||
{
|
{
|
||||||
IRP* irp;
|
IRP* irp;
|
||||||
wMessage message;
|
wMessage message;
|
||||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) arg;
|
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (!drive)
|
if (!drive)
|
||||||
@ -782,13 +786,13 @@ static DWORD WINAPI drive_thread_func(LPVOID arg)
|
|||||||
if (message.id == WMQ_QUIT)
|
if (message.id == WMQ_QUIT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
irp = (IRP*) message.wParam;
|
irp = (IRP*)message.wParam;
|
||||||
|
|
||||||
if (irp)
|
if (irp)
|
||||||
{
|
{
|
||||||
if ((error = drive_process_irp(drive, 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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -810,12 +814,12 @@ fail:
|
|||||||
*/
|
*/
|
||||||
static UINT drive_irp_request(DEVICE* device, IRP* irp)
|
static UINT drive_irp_request(DEVICE* device, IRP* irp)
|
||||||
{
|
{
|
||||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) device;
|
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
|
||||||
|
|
||||||
if (!drive)
|
if (!drive)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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!");
|
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
@ -847,17 +851,17 @@ static UINT drive_free_int(DRIVE_DEVICE* drive)
|
|||||||
*/
|
*/
|
||||||
static UINT drive_free(DEVICE* device)
|
static UINT drive_free(DEVICE* device)
|
||||||
{
|
{
|
||||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) device;
|
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (!drive)
|
if (!drive)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
if (MessageQueue_PostQuit(drive->IrpQueue, 0)
|
if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
|
||||||
&& (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
|
(WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,7 +873,7 @@ static UINT drive_free(DEVICE* device)
|
|||||||
*/
|
*/
|
||||||
static void drive_file_objfree(void* obj)
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
|
||||||
const char* name, const char* path, BOOL automount)
|
const char* path, BOOL automount)
|
||||||
{
|
{
|
||||||
size_t i, length;
|
size_t i, length;
|
||||||
DRIVE_DEVICE* drive;
|
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])
|
if (name[0] && path[0])
|
||||||
{
|
{
|
||||||
size_t pathLength = strnlen(path, MAX_PATH);
|
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)
|
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.type = RDPDR_DTYP_FILESYSTEM;
|
||||||
drive->device.name = name;
|
|
||||||
drive->device.IRPRequest = drive_irp_request;
|
drive->device.IRPRequest = drive_irp_request;
|
||||||
drive->device.Free = drive_free;
|
drive->device.Free = drive_free;
|
||||||
drive->rdpcontext = pEntryPoints->rdpcontext;
|
drive->rdpcontext = pEntryPoints->rdpcontext;
|
||||||
@ -911,11 +921,34 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
|||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i <= length; i++)
|
for (i = 0; i < length; i++)
|
||||||
Stream_Write_UINT8(drive->device.data, name[i] < 0 ? '_' : name[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] == '/'))
|
if ((pathLength > 1) && (path[pathLength - 1] == '/'))
|
||||||
pathLength --;
|
pathLength--;
|
||||||
|
|
||||||
if (ConvertToUnicode(sys_code_page, 0, path, pathLength, &drive->path, 0) <= 0)
|
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;
|
goto out_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)drive)))
|
||||||
(DEVICE*) drive)))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "RegisterDevice failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(drive->thread = CreateThread(NULL, 0, drive_thread_func, drive,
|
if (!(drive->thread =
|
||||||
CREATE_SUSPENDED, NULL)))
|
CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
goto out_error;
|
goto out_error;
|
||||||
@ -967,9 +999,9 @@ out_error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define DeviceServiceEntry drive_DeviceServiceEntry
|
#define DeviceServiceEntry drive_DeviceServiceEntry
|
||||||
#else
|
#else
|
||||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -988,7 +1020,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||||||
char* bufdup;
|
char* bufdup;
|
||||||
char* devdup;
|
char* devdup;
|
||||||
#endif
|
#endif
|
||||||
drive = (RDPDR_DRIVE*) pEntryPoints->device;
|
drive = (RDPDR_DRIVE*)pEntryPoints->device;
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
sys_code_page = CP_UTF8;
|
sys_code_page = CP_UTF8;
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "echo_main.h"
|
#include "echo_main.h"
|
||||||
#include <freerdp/channels/log.h>
|
#include <freerdp/channels/log.h>
|
||||||
|
#include <freerdp/channels/echo.h>
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("echo.client")
|
#define TAG CHANNELS_TAG("echo.client")
|
||||||
|
|
||||||
@ -59,6 +60,8 @@ struct _ECHO_PLUGIN
|
|||||||
IWTSPlugin iface;
|
IWTSPlugin iface;
|
||||||
|
|
||||||
ECHO_LISTENER_CALLBACK* listener_callback;
|
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
|
* @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);
|
BYTE* pBuffer = Stream_Pointer(data);
|
||||||
UINT32 cbSize = Stream_GetRemainingLength(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)
|
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);
|
free(callback);
|
||||||
|
|
||||||
@ -96,13 +99,13 @@ static UINT echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
|||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||||
IWTSVirtualChannelCallback** ppCallback)
|
IWTSVirtualChannelCallback** ppCallback)
|
||||||
{
|
{
|
||||||
ECHO_CHANNEL_CALLBACK* callback;
|
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)
|
if (!callback)
|
||||||
{
|
{
|
||||||
@ -116,7 +119,7 @@ static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
|||||||
callback->channel_mgr = listener_callback->channel_mgr;
|
callback->channel_mgr = listener_callback->channel_mgr;
|
||||||
callback->channel = pChannel;
|
callback->channel = pChannel;
|
||||||
|
|
||||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
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)
|
static UINT echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||||
{
|
{
|
||||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*) pPlugin;
|
UINT status;
|
||||||
|
ECHO_PLUGIN* echo = (ECHO_PLUGIN*)pPlugin;
|
||||||
echo->listener_callback = (ECHO_LISTENER_CALLBACK*) calloc(1, sizeof(ECHO_LISTENER_CALLBACK));
|
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)
|
if (!echo->listener_callback)
|
||||||
{
|
{
|
||||||
@ -142,8 +150,11 @@ static UINT echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
|||||||
echo->listener_callback->plugin = pPlugin;
|
echo->listener_callback->plugin = pPlugin;
|
||||||
echo->listener_callback->channel_mgr = pChannelMgr;
|
echo->listener_callback->channel_mgr = pChannelMgr;
|
||||||
|
|
||||||
return pChannelMgr->CreateListener(pChannelMgr, "ECHO", 0,
|
status = pChannelMgr->CreateListener(pChannelMgr, ECHO_DVC_CHANNEL_NAME, 0,
|
||||||
(IWTSListenerCallback*) echo->listener_callback, NULL);
|
&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)
|
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);
|
free(echo);
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define DVCPluginEntry echo_DVCPluginEntry
|
#define DVCPluginEntry echo_DVCPluginEntry
|
||||||
#else
|
#else
|
||||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,11 +192,11 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
UINT status = CHANNEL_RC_OK;
|
UINT status = CHANNEL_RC_OK;
|
||||||
ECHO_PLUGIN* echo;
|
ECHO_PLUGIN* echo;
|
||||||
|
|
||||||
echo = (ECHO_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "echo");
|
echo = (ECHO_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "echo");
|
||||||
|
|
||||||
if (!echo)
|
if (!echo)
|
||||||
{
|
{
|
||||||
echo = (ECHO_PLUGIN*) calloc(1, sizeof(ECHO_PLUGIN));
|
echo = (ECHO_PLUGIN*)calloc(1, sizeof(ECHO_PLUGIN));
|
||||||
|
|
||||||
if (!echo)
|
if (!echo)
|
||||||
{
|
{
|
||||||
@ -193,7 +209,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
echo->iface.Disconnected = NULL;
|
echo->iface.Disconnected = NULL;
|
||||||
echo->iface.Terminated = echo_plugin_terminated;
|
echo->iface.Terminated = echo_plugin_terminated;
|
||||||
|
|
||||||
status = pEntryPoints->RegisterPlugin(pEntryPoints, "echo", (IWTSPlugin*) echo);
|
status = pEntryPoints->RegisterPlugin(pEntryPoints, "echo", (IWTSPlugin*)echo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
@ -33,8 +33,10 @@
|
|||||||
#ifdef WITH_DEBUG_DVC
|
#ifdef WITH_DEBUG_DVC
|
||||||
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
|
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define DEBUG_DVC(...) do { } while (0)
|
#define DEBUG_DVC(...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_ECHO_CLIENT_MAIN_H */
|
#endif /* FREERDP_CHANNEL_ECHO_CLIENT_MAIN_H */
|
||||||
|
|
||||||
|
|||||||
@ -66,14 +66,14 @@ static UINT echo_server_open_channel(echo_server* echo)
|
|||||||
DWORD BytesReturned = 0;
|
DWORD BytesReturned = 0;
|
||||||
PULONG pSessionId = NULL;
|
PULONG pSessionId = NULL;
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION,
|
if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned) == FALSE)
|
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo->SessionId = (DWORD) * pSessionId;
|
echo->SessionId = (DWORD)*pSessionId;
|
||||||
WTSFreeMemory(pSessionId);
|
WTSFreeMemory(pSessionId);
|
||||||
hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
|
hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
|
||||||
StartTick = GetTickCount();
|
StartTick = GetTickCount();
|
||||||
@ -83,15 +83,29 @@ static UINT echo_server_open_channel(echo_server* echo)
|
|||||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
Error = GetLastError();
|
Error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", Error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId,
|
echo->echo_channel =
|
||||||
"ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||||
|
|
||||||
if (echo->echo_channel)
|
if (echo->echo_channel)
|
||||||
|
{
|
||||||
|
UINT32 channelId;
|
||||||
|
BOOL status = TRUE;
|
||||||
|
|
||||||
|
channelId = WTSChannelGetIdByHandle(echo->echo_channel);
|
||||||
|
|
||||||
|
IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||||
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Error = GetLastError();
|
Error = GetLastError();
|
||||||
|
|
||||||
@ -114,19 +128,19 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
|||||||
BOOL ready = FALSE;
|
BOOL ready = FALSE;
|
||||||
HANDLE ChannelEvent;
|
HANDLE ChannelEvent;
|
||||||
DWORD BytesReturned = 0;
|
DWORD BytesReturned = 0;
|
||||||
echo_server* echo = (echo_server*) arg;
|
echo_server* echo = (echo_server*)arg;
|
||||||
UINT error;
|
UINT error;
|
||||||
DWORD status;
|
DWORD status;
|
||||||
|
|
||||||
if ((error = echo_server_open_channel(echo)))
|
if ((error = echo_server_open_channel(echo)))
|
||||||
{
|
{
|
||||||
UINT error2 = 0;
|
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,
|
IFCALLRET(echo->context.OpenResult, error2, &echo->context,
|
||||||
ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED);
|
ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED);
|
||||||
|
|
||||||
if (error2)
|
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);
|
error2);
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
@ -158,7 +172,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
|||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +182,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
|||||||
ECHO_SERVER_OPEN_RESULT_CLOSED);
|
ECHO_SERVER_OPEN_RESULT_CLOSED);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "OpenResult failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -180,21 +194,20 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
|||||||
ECHO_SERVER_OPEN_RESULT_ERROR);
|
ECHO_SERVER_OPEN_RESULT_ERROR);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "OpenResult failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ready = *((BOOL*) buffer);
|
ready = *((BOOL*)buffer);
|
||||||
WTSFreeMemory(buffer);
|
WTSFreeMemory(buffer);
|
||||||
|
|
||||||
if (ready)
|
if (ready)
|
||||||
{
|
{
|
||||||
IFCALLRET(echo->context.OpenResult, error, &echo->context,
|
IFCALLRET(echo->context.OpenResult, error, &echo->context, ECHO_SERVER_OPEN_RESULT_OK);
|
||||||
ECHO_SERVER_OPEN_RESULT_OK);
|
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "OpenResult failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -217,7 +230,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
|||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,20 +250,20 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR) Stream_Buffer(s),
|
if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||||
(ULONG) Stream_Capacity(s), &BytesReturned) == FALSE)
|
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
IFCALLRET(echo->context.Response, error, &echo->context,
|
IFCALLRET(echo->context.Response, error, &echo->context, (BYTE*)Stream_Buffer(s),
|
||||||
(BYTE*) Stream_Buffer(s), BytesReturned);
|
BytesReturned);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Response failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,7 +288,7 @@ out:
|
|||||||
*/
|
*/
|
||||||
static UINT echo_server_open(echo_server_context* context)
|
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)
|
if (echo->thread == NULL)
|
||||||
{
|
{
|
||||||
@ -285,7 +298,7 @@ static UINT echo_server_open(echo_server_context* context)
|
|||||||
return ERROR_INTERNAL_ERROR;
|
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!");
|
WLog_ERR(TAG, "CreateEvent failed!");
|
||||||
CloseHandle(echo->stopEvent);
|
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)
|
static UINT echo_server_close(echo_server_context* context)
|
||||||
{
|
{
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
echo_server* echo = (echo_server*) context;
|
echo_server* echo = (echo_server*)context;
|
||||||
|
|
||||||
if (echo->thread)
|
if (echo->thread)
|
||||||
{
|
{
|
||||||
@ -314,7 +327,7 @@ static UINT echo_server_close(echo_server_context* context)
|
|||||||
if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
|
if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,17 +340,16 @@ static UINT echo_server_close(echo_server_context* context)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL echo_server_request(echo_server_context* context,
|
static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length)
|
||||||
const BYTE* buffer, UINT32 length)
|
|
||||||
{
|
{
|
||||||
echo_server* echo = (echo_server*) context;
|
echo_server* echo = (echo_server*)context;
|
||||||
return WTSVirtualChannelWrite(echo->echo_channel, (PCHAR) buffer, length, NULL);
|
return WTSVirtualChannelWrite(echo->echo_channel, (PCHAR)buffer, length, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
echo_server_context* echo_server_context_new(HANDLE vcm)
|
echo_server_context* echo_server_context_new(HANDLE vcm)
|
||||||
{
|
{
|
||||||
echo_server* echo;
|
echo_server* echo;
|
||||||
echo = (echo_server*) calloc(1, sizeof(echo_server));
|
echo = (echo_server*)calloc(1, sizeof(echo_server));
|
||||||
|
|
||||||
if (echo)
|
if (echo)
|
||||||
{
|
{
|
||||||
@ -349,12 +361,12 @@ echo_server_context* echo_server_context_new(HANDLE vcm)
|
|||||||
else
|
else
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
|
|
||||||
return (echo_server_context*) echo;
|
return (echo_server_context*)echo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void echo_server_context_free(echo_server_context* context)
|
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);
|
echo_server_close(context);
|
||||||
free(echo);
|
free(echo);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -37,20 +37,6 @@
|
|||||||
|
|
||||||
#define TAG CHANNELS_TAG("encomsp.client")
|
#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;
|
typedef struct encomsp_plugin encomspPlugin;
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_ENCOMSP_CLIENT_MAIN_H */
|
#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)
|
if (Stream_GetRemainingLength(s) < ENCOMSP_ORDER_HEADER_SIZE)
|
||||||
return ERROR_INVALID_DATA;
|
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) */
|
Stream_Read_UINT16(s, header->Length); /* Length (2 bytes) */
|
||||||
return CHANNEL_RC_OK;
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT encomsp_recv_change_participant_control_level_pdu(
|
static UINT encomsp_recv_change_participant_control_level_pdu(EncomspServerContext* context,
|
||||||
EncomspServerContext* context, wStream* s, ENCOMSP_ORDER_HEADER* header)
|
wStream* s,
|
||||||
|
ENCOMSP_ORDER_HEADER* header)
|
||||||
{
|
{
|
||||||
int beg, end;
|
int beg, end;
|
||||||
ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu;
|
ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu;
|
||||||
UINT error = CHANNEL_RC_OK;
|
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));
|
CopyMemory(&pdu, header, sizeof(ENCOMSP_ORDER_HEADER));
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 6)
|
if (Stream_GetRemainingLength(s) < 6)
|
||||||
@ -98,9 +99,9 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
|||||||
return ERROR_INVALID_DATA;
|
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) */
|
Stream_Read_UINT32(s, pdu.ParticipantId); /* ParticipantId (4 bytes) */
|
||||||
end = (int) Stream_GetPosition(s);
|
end = (int)Stream_GetPosition(s);
|
||||||
|
|
||||||
if ((beg + header->Length) < end)
|
if ((beg + header->Length) < end)
|
||||||
{
|
{
|
||||||
@ -110,7 +111,7 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
|||||||
|
|
||||||
if ((beg + header->Length) > end)
|
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!");
|
WLog_ERR(TAG, "Not enough data!");
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
@ -122,7 +123,7 @@ static UINT encomsp_recv_change_participant_control_level_pdu(
|
|||||||
IFCALLRET(context->ChangeParticipantControlLevel, error, context, &pdu);
|
IFCALLRET(context->ChangeParticipantControlLevel, error, context, &pdu);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "context->ChangeParticipantControlLevel failed with error %"PRIu32"",
|
WLog_ERR(TAG, "context->ChangeParticipantControlLevel failed with error %" PRIu32 "",
|
||||||
error);
|
error);
|
||||||
|
|
||||||
return 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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
static UINT encomsp_server_receive_pdu(EncomspServerContext* context, wStream* s)
|
||||||
wStream* s)
|
|
||||||
{
|
{
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
ENCOMSP_ORDER_HEADER header;
|
ENCOMSP_ORDER_HEADER header;
|
||||||
@ -143,21 +143,22 @@ static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
|||||||
{
|
{
|
||||||
if ((error = encomsp_read_header(s, &header)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
WLog_INFO(TAG, "EncomspReceive: Type: %"PRIu16" Length: %"PRIu16"", header.Type,
|
WLog_INFO(TAG, "EncomspReceive: Type: %" PRIu16 " Length: %" PRIu16 "", header.Type,
|
||||||
header.Length);
|
header.Length);
|
||||||
|
|
||||||
switch (header.Type)
|
switch (header.Type)
|
||||||
{
|
{
|
||||||
case ODTYPE_PARTICIPANT_CTRL_CHANGED:
|
case ODTYPE_PARTICIPANT_CTRL_CHANGED:
|
||||||
if ((error = encomsp_recv_change_participant_control_level_pdu(context, s,
|
if ((error =
|
||||||
&header)))
|
encomsp_recv_change_participant_control_level_pdu(context, s, &header)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG,
|
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);
|
error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -165,7 +166,7 @@ static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_ERR(TAG, "header.Type unknown %"PRIu16"!", header.Type);
|
WLog_ERR(TAG, "header.Type unknown %" PRIu16 "!", header.Type);
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -186,7 +187,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
EncomspServerContext* context;
|
EncomspServerContext* context;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
DWORD status;
|
DWORD status;
|
||||||
context = (EncomspServerContext*) arg;
|
context = (EncomspServerContext*)arg;
|
||||||
|
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
BytesReturned = 0;
|
BytesReturned = 0;
|
||||||
@ -200,8 +201,8 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle,
|
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
|
||||||
&buffer, &BytesReturned) == TRUE)
|
&BytesReturned) == TRUE)
|
||||||
{
|
{
|
||||||
if (BytesReturned == sizeof(HANDLE))
|
if (BytesReturned == sizeof(HANDLE))
|
||||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||||
@ -220,7 +221,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
if (status == WAIT_FAILED)
|
if (status == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,8 +251,8 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, (PCHAR)Stream_Buffer(s),
|
||||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
Stream_Capacity(s), &BytesReturned))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
@ -260,7 +261,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
|
|
||||||
if (Stream_GetPosition(s) >= ENCOMSP_ORDER_HEADER_SIZE)
|
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))
|
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)))
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,8 +284,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
|||||||
out:
|
out:
|
||||||
|
|
||||||
if (error && context->rdpcontext)
|
if (error && context->rdpcontext)
|
||||||
setChannelError(context->rdpcontext, error,
|
setChannelError(context->rdpcontext, error, "encomsp_server_thread reported an error");
|
||||||
"encomsp_server_thread reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
ExitThread(error);
|
||||||
return error;
|
return error;
|
||||||
@ -296,8 +297,8 @@ out:
|
|||||||
*/
|
*/
|
||||||
static UINT encomsp_server_start(EncomspServerContext* context)
|
static UINT encomsp_server_start(EncomspServerContext* context)
|
||||||
{
|
{
|
||||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm,
|
context->priv->ChannelHandle =
|
||||||
WTS_CURRENT_SESSION, "encomsp");
|
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "encomsp");
|
||||||
|
|
||||||
if (!context->priv->ChannelHandle)
|
if (!context->priv->ChannelHandle)
|
||||||
return CHANNEL_RC_BAD_CHANNEL;
|
return CHANNEL_RC_BAD_CHANNEL;
|
||||||
@ -308,8 +309,8 @@ static UINT encomsp_server_start(EncomspServerContext* context)
|
|||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(context->priv->Thread = CreateThread(NULL, 0,
|
if (!(context->priv->Thread =
|
||||||
encomsp_server_thread, (void*) context, 0, NULL)))
|
CreateThread(NULL, 0, encomsp_server_thread, (void*)context, 0, NULL)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
CloseHandle(context->priv->StopEvent);
|
CloseHandle(context->priv->StopEvent);
|
||||||
@ -333,7 +334,7 @@ static UINT encomsp_server_stop(EncomspServerContext* context)
|
|||||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,14 +346,14 @@ static UINT encomsp_server_stop(EncomspServerContext* context)
|
|||||||
EncomspServerContext* encomsp_server_context_new(HANDLE vcm)
|
EncomspServerContext* encomsp_server_context_new(HANDLE vcm)
|
||||||
{
|
{
|
||||||
EncomspServerContext* context;
|
EncomspServerContext* context;
|
||||||
context = (EncomspServerContext*) calloc(1, sizeof(EncomspServerContext));
|
context = (EncomspServerContext*)calloc(1, sizeof(EncomspServerContext));
|
||||||
|
|
||||||
if (context)
|
if (context)
|
||||||
{
|
{
|
||||||
context->vcm = vcm;
|
context->vcm = vcm;
|
||||||
context->Start = encomsp_server_start;
|
context->Start = encomsp_server_start;
|
||||||
context->Stop = encomsp_server_stop;
|
context->Stop = encomsp_server_stop;
|
||||||
context->priv = (EncomspServerPrivate*) calloc(1, sizeof(EncomspServerPrivate));
|
context->priv = (EncomspServerPrivate*)calloc(1, sizeof(EncomspServerPrivate));
|
||||||
|
|
||||||
if (!context->priv)
|
if (!context->priv)
|
||||||
{
|
{
|
||||||
@ -371,7 +372,7 @@ void encomsp_server_context_free(EncomspServerContext* context)
|
|||||||
{
|
{
|
||||||
if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
|
if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
|
||||||
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||||
|
|
||||||
free(context->priv);
|
free(context->priv);
|
||||||
free(context);
|
free(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,9 +25,10 @@ include_directories(..)
|
|||||||
|
|
||||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||||
|
if (NOT BUILTIN_CHANNELS OR NOT BUILD_SHARED_LIBS)
|
||||||
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
#include <winpr/synch.h>
|
#include <winpr/synch.h>
|
||||||
#include <winpr/interlocked.h>
|
|
||||||
#include <winpr/print.h>
|
#include <winpr/print.h>
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/cmdline.h>
|
#include <winpr/cmdline.h>
|
||||||
@ -69,47 +68,30 @@ struct _GEOMETRY_PLUGIN
|
|||||||
GEOMETRY_LISTENER_CALLBACK* listener_callback;
|
GEOMETRY_LISTENER_CALLBACK* listener_callback;
|
||||||
|
|
||||||
GeometryClientContext* context;
|
GeometryClientContext* context;
|
||||||
|
BOOL initialized;
|
||||||
};
|
};
|
||||||
typedef struct _GEOMETRY_PLUGIN GEOMETRY_PLUGIN;
|
typedef struct _GEOMETRY_PLUGIN GEOMETRY_PLUGIN;
|
||||||
|
|
||||||
|
static UINT32 mappedGeometryHash(UINT64* g)
|
||||||
static UINT32 mappedGeometryHash(UINT64 *g)
|
|
||||||
{
|
{
|
||||||
return (UINT32)((*g >> 32) + (*g & 0xffffffff));
|
return (UINT32)((*g >> 32) + (*g & 0xffffffff));
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL mappedGeometryKeyCompare(UINT64 *g1, UINT64 *g2)
|
static BOOL mappedGeometryKeyCompare(UINT64* g1, UINT64* g2)
|
||||||
{
|
{
|
||||||
return *g1 == *g2;
|
return *g1 == *g2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mappedGeometryRef(MAPPED_GEOMETRY *g)
|
static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
data->nRectCount = 0;
|
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;
|
UINT32 dwSize, iType;
|
||||||
INT32 right, bottom;
|
INT32 right, bottom;
|
||||||
|
INT32 x, y, w, h;
|
||||||
|
|
||||||
if (len < 32)
|
if (len < 32)
|
||||||
{
|
{
|
||||||
@ -129,18 +111,26 @@ static UINT32 geometry_read_RGNDATA(wStream *s, UINT32 len, FREERDP_RGNDATA *rgn
|
|||||||
|
|
||||||
if (iType != RDH_RECTANGLE)
|
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;
|
return ERROR_UNSUPPORTED_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Read_UINT32(s, rgndata->nRectCount);
|
Stream_Read_UINT32(s, rgndata->nRectCount);
|
||||||
Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
|
Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
|
||||||
Stream_Read_INT32(s, rgndata->boundingRect.x);
|
Stream_Read_INT32(s, x);
|
||||||
Stream_Read_INT32(s, rgndata->boundingRect.y);
|
Stream_Read_INT32(s, y);
|
||||||
Stream_Read_INT32(s, right);
|
Stream_Read_INT32(s, right);
|
||||||
Stream_Read_INT32(s, bottom);
|
Stream_Read_INT32(s, bottom);
|
||||||
rgndata->boundingRect.width = right - rgndata->boundingRect.x;
|
if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
|
||||||
rgndata->boundingRect.height = bottom - rgndata->boundingRect.y;
|
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;
|
len -= 32;
|
||||||
|
|
||||||
if (len / (4 * 4) < rgndata->nRectCount)
|
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)
|
if (rgndata->nRectCount)
|
||||||
{
|
{
|
||||||
int i;
|
UINT32 i;
|
||||||
RDP_RECT *tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
|
RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
|
||||||
|
|
||||||
if (!tmp)
|
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;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
rgndata->rects = tmp;
|
rgndata->rects = tmp;
|
||||||
|
|
||||||
for (i = 0; i < rgndata->nRectCount; i++)
|
for (i = 0; i < rgndata->nRectCount; i++)
|
||||||
{
|
{
|
||||||
Stream_Read_INT32(s, rgndata->rects[i].x);
|
Stream_Read_INT32(s, x);
|
||||||
Stream_Read_INT32(s, rgndata->rects[i].y);
|
Stream_Read_INT32(s, y);
|
||||||
Stream_Read_INT32(s, right);
|
Stream_Read_INT32(s, right);
|
||||||
Stream_Read_INT32(s, bottom);
|
Stream_Read_INT32(s, bottom);
|
||||||
rgndata->rects[i].width = right - rgndata->rects[i].x;
|
if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
|
||||||
rgndata->rects[i].height = bottom - rgndata->rects[i].y;
|
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)
|
static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
||||||
{
|
{
|
||||||
UINT32 length, cbGeometryBuffer;
|
UINT32 length, cbGeometryBuffer;
|
||||||
MAPPED_GEOMETRY *mappedGeometry;
|
MAPPED_GEOMETRY* mappedGeometry;
|
||||||
GEOMETRY_PLUGIN* geometry;
|
GEOMETRY_PLUGIN* geometry;
|
||||||
GeometryClientContext *context;
|
GeometryClientContext* context;
|
||||||
UINT ret = CHANNEL_RC_OK;
|
UINT ret = CHANNEL_RC_OK;
|
||||||
UINT32 version, updateType, geometryType;
|
UINT32 version, updateType, geometryType;
|
||||||
UINT64 id;
|
UINT64 id;
|
||||||
|
|
||||||
geometry = (GEOMETRY_PLUGIN*) callback->plugin;
|
geometry = (GEOMETRY_PLUGIN*)callback->plugin;
|
||||||
context = (GeometryClientContext*)geometry->iface.pInterface;
|
context = (GeometryClientContext*)geometry->iface.pInterface;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
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);
|
mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
|
||||||
|
|
||||||
if (updateType == GEOMETRY_CLEAR )
|
if (updateType == GEOMETRY_CLEAR)
|
||||||
{
|
{
|
||||||
if (!mappedGeometry)
|
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;
|
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;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
if (!HashTable_Remove(context->geometries, &id))
|
if (!HashTable_Remove(context->geometries, &id))
|
||||||
@ -236,7 +235,7 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
|||||||
if (!mappedGeometry)
|
if (!mappedGeometry)
|
||||||
{
|
{
|
||||||
newOne = TRUE;
|
newOne = TRUE;
|
||||||
WLog_DBG(TAG, "creating geometry 0x%"PRIx64"", id);
|
WLog_DBG(TAG, "creating geometry 0x%" PRIx64 "", id);
|
||||||
mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
|
mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
|
||||||
if (!mappedGeometry)
|
if (!mappedGeometry)
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
@ -244,16 +243,17 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
|||||||
mappedGeometry->refCounter = 1;
|
mappedGeometry->refCounter = 1;
|
||||||
mappedGeometry->mappingId = id;
|
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);
|
free(mappedGeometry);
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WLog_DBG(TAG, "updating geometry 0x%"PRIx64"", id);
|
WLog_DBG(TAG, "updating geometry 0x%" PRIx64 "", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Read_UINT64(s, mappedGeometry->topLevelId);
|
Stream_Read_UINT64(s, mappedGeometry->topLevelId);
|
||||||
@ -290,7 +290,8 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
|||||||
|
|
||||||
if (newOne)
|
if (newOne)
|
||||||
{
|
{
|
||||||
if (context->MappedGeometryAdded && !context->MappedGeometryAdded(context, mappedGeometry))
|
if (context->MappedGeometryAdded &&
|
||||||
|
!context->MappedGeometryAdded(context, mappedGeometry))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "geometry added callback failed");
|
WLog_ERR(TAG, "geometry added callback failed");
|
||||||
ret = ERROR_INTERNAL_ERROR;
|
ret = ERROR_INTERNAL_ERROR;
|
||||||
@ -298,7 +299,8 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mappedGeometry->MappedGeometryUpdate && !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
|
if (mappedGeometry->MappedGeometryUpdate &&
|
||||||
|
!mappedGeometry->MappedGeometryUpdate(mappedGeometry))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "geometry update callback failed");
|
WLog_ERR(TAG, "geometry update callback failed");
|
||||||
ret = ERROR_INTERNAL_ERROR;
|
ret = ERROR_INTERNAL_ERROR;
|
||||||
@ -307,11 +309,10 @@ static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "unknown updateType=%"PRIu32"", updateType);
|
WLog_ERR(TAG, "unknown updateType=%" PRIu32 "", updateType);
|
||||||
ret = CHANNEL_RC_OK;
|
ret = CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
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)
|
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);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
IWTSVirtualChannel* pChannel, BYTE* Data,
|
||||||
IWTSVirtualChannelCallback** ppCallback)
|
BOOL* pbAccept,
|
||||||
|
IWTSVirtualChannelCallback** ppCallback)
|
||||||
{
|
{
|
||||||
GEOMETRY_CHANNEL_CALLBACK* callback;
|
GEOMETRY_CHANNEL_CALLBACK* callback;
|
||||||
GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*) pListenerCallback;
|
GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*)pListenerCallback;
|
||||||
callback = (GEOMETRY_CHANNEL_CALLBACK*) calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK));
|
|
||||||
|
WINPR_UNUSED(Data);
|
||||||
|
WINPR_UNUSED(pbAccept);
|
||||||
|
|
||||||
|
callback = (GEOMETRY_CHANNEL_CALLBACK*)calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK));
|
||||||
|
|
||||||
if (!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_mgr = listener_callback->channel_mgr;
|
||||||
callback->channel = pChannel;
|
callback->channel = pChannel;
|
||||||
listener_callback->channel_callback = callback;
|
listener_callback->channel_callback = callback;
|
||||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||||
return CHANNEL_RC_OK;
|
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)
|
static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||||
{
|
{
|
||||||
UINT status;
|
UINT status;
|
||||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*) pPlugin;
|
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
|
||||||
geometry->listener_callback = (GEOMETRY_LISTENER_CALLBACK*) calloc(1,
|
if (geometry->initialized)
|
||||||
sizeof(GEOMETRY_LISTENER_CALLBACK));
|
{
|
||||||
|
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)
|
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->iface.OnNewChannelConnection = geometry_on_new_channel_connection;
|
||||||
geometry->listener_callback->plugin = pPlugin;
|
geometry->listener_callback->plugin = pPlugin;
|
||||||
geometry->listener_callback->channel_mgr = pChannelMgr;
|
geometry->listener_callback->channel_mgr = pChannelMgr;
|
||||||
status = pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0,
|
status =
|
||||||
(IWTSListenerCallback*) geometry->listener_callback, &(geometry->listener));
|
pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0,
|
||||||
|
&geometry->listener_callback->iface, &(geometry->listener));
|
||||||
geometry->listener->pInterface = geometry->iface.pInterface;
|
geometry->listener->pInterface = geometry->iface.pInterface;
|
||||||
|
|
||||||
|
geometry->initialized = status == CHANNEL_RC_OK;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,8 +414,15 @@ static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelMa
|
|||||||
*/
|
*/
|
||||||
static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
|
static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
|
||||||
{
|
{
|
||||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*) pPlugin;
|
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
|
||||||
GeometryClientContext* context = (GeometryClientContext *)geometry->iface.pInterface;
|
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)
|
if (context)
|
||||||
HashTable_Free(context->geometries);
|
HashTable_Free(context->geometries);
|
||||||
@ -417,9 +438,9 @@ static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define DVCPluginEntry geometry_DVCPluginEntry
|
#define DVCPluginEntry geometry_DVCPluginEntry
|
||||||
#else
|
#else
|
||||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -432,11 +453,11 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
GEOMETRY_PLUGIN* geometry;
|
GEOMETRY_PLUGIN* geometry;
|
||||||
GeometryClientContext* context;
|
GeometryClientContext* context;
|
||||||
geometry = (GEOMETRY_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "geometry");
|
geometry = (GEOMETRY_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "geometry");
|
||||||
|
|
||||||
if (!geometry)
|
if (!geometry)
|
||||||
{
|
{
|
||||||
geometry = (GEOMETRY_PLUGIN*) calloc(1, sizeof(GEOMETRY_PLUGIN));
|
geometry = (GEOMETRY_PLUGIN*)calloc(1, sizeof(GEOMETRY_PLUGIN));
|
||||||
|
|
||||||
if (!geometry)
|
if (!geometry)
|
||||||
{
|
{
|
||||||
@ -448,7 +469,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
geometry->iface.Connected = NULL;
|
geometry->iface.Connected = NULL;
|
||||||
geometry->iface.Disconnected = NULL;
|
geometry->iface.Disconnected = NULL;
|
||||||
geometry->iface.Terminated = geometry_plugin_terminated;
|
geometry->iface.Terminated = geometry_plugin_terminated;
|
||||||
context = (GeometryClientContext*) calloc(1, sizeof(GeometryClientContext));
|
context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
{
|
{
|
||||||
@ -461,10 +482,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
context->geometries->keyCompare = (HASH_TABLE_KEY_COMPARE_FN)mappedGeometryKeyCompare;
|
context->geometries->keyCompare = (HASH_TABLE_KEY_COMPARE_FN)mappedGeometryKeyCompare;
|
||||||
context->geometries->valueFree = (HASH_TABLE_VALUE_FREE_FN)mappedGeometryUnref;
|
context->geometries->valueFree = (HASH_TABLE_VALUE_FREE_FN)mappedGeometryUnref;
|
||||||
|
|
||||||
context->handle = (void*) geometry;
|
context->handle = (void*)geometry;
|
||||||
geometry->iface.pInterface = (void*) context;
|
geometry->iface.pInterface = (void*)context;
|
||||||
geometry->context = context;
|
geometry->context = context;
|
||||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*) geometry);
|
error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*)geometry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -477,5 +498,4 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
error_context:
|
error_context:
|
||||||
free(geometry);
|
free(geometry);
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,4 @@
|
|||||||
#include <freerdp/addin.h>
|
#include <freerdp/addin.h>
|
||||||
#include <freerdp/client/geometry.h>
|
#include <freerdp/client/geometry.h>
|
||||||
|
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_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;
|
char* path = NULL;
|
||||||
int status;
|
int status;
|
||||||
|
WCHAR* ptr;
|
||||||
UINT32 PathLength;
|
UINT32 PathLength;
|
||||||
Stream_Seek(irp->input, 28);
|
if (!Stream_SafeSeek(irp->input, 28))
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
/* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
|
/* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
|
||||||
/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
|
/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
|
||||||
|
if (Stream_GetRemainingLength(irp->input) < 4)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
Stream_Read_UINT32(irp->input, PathLength);
|
Stream_Read_UINT32(irp->input, PathLength);
|
||||||
status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input),
|
ptr = (WCHAR*)Stream_Pointer(irp->input);
|
||||||
PathLength / 2, &path, 0, NULL, NULL);
|
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 (status < 1)
|
||||||
if (!(path = (char*) calloc(1, 1)))
|
if (!(path = (char*)calloc(1, 1)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
@ -149,9 +155,11 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
UINT64 Offset;
|
UINT64 Offset;
|
||||||
ssize_t status;
|
ssize_t status;
|
||||||
BYTE* buffer = NULL;
|
BYTE* buffer = NULL;
|
||||||
|
if (Stream_GetRemainingLength(irp->input) < 12)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
Stream_Read_UINT32(irp->input, Length);
|
Stream_Read_UINT32(irp->input, Length);
|
||||||
Stream_Read_UINT64(irp->input, Offset);
|
Stream_Read_UINT64(irp->input, Offset);
|
||||||
buffer = (BYTE*) malloc(Length);
|
buffer = (BYTE*)calloc(Length, sizeof(BYTE));
|
||||||
|
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
{
|
{
|
||||||
@ -170,6 +178,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Length = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write_UINT32(irp->output, Length);
|
Stream_Write_UINT32(irp->output, Length);
|
||||||
@ -201,14 +210,22 @@ static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
UINT32 Length;
|
UINT32 Length;
|
||||||
UINT64 Offset;
|
UINT64 Offset;
|
||||||
ssize_t status;
|
ssize_t status;
|
||||||
|
void* ptr;
|
||||||
|
if (Stream_GetRemainingLength(irp->input) > 12)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(irp->input, Length);
|
Stream_Read_UINT32(irp->input, Length);
|
||||||
Stream_Read_UINT64(irp->input, Offset);
|
Stream_Read_UINT64(irp->input, Offset);
|
||||||
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;
|
len = Length;
|
||||||
|
|
||||||
while (len > 0)
|
while (len > 0)
|
||||||
{
|
{
|
||||||
status = write(parallel->file, Stream_Pointer(irp->input), len);
|
status = write(parallel->file, ptr, len);
|
||||||
|
|
||||||
if (status < 0)
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel,
|
static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||||
IRP* irp)
|
|
||||||
{
|
{
|
||||||
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
||||||
return irp->Complete(irp);
|
return irp->Complete(irp);
|
||||||
@ -252,7 +268,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
case IRP_MJ_CREATE:
|
case IRP_MJ_CREATE:
|
||||||
if ((error = parallel_process_irp_create(parallel, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +277,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
case IRP_MJ_CLOSE:
|
case IRP_MJ_CLOSE:
|
||||||
if ((error = parallel_process_irp_close(parallel, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +286,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
case IRP_MJ_READ:
|
case IRP_MJ_READ:
|
||||||
if ((error = parallel_process_irp_read(parallel, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +295,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
case IRP_MJ_WRITE:
|
case IRP_MJ_WRITE:
|
||||||
if ((error = parallel_process_irp_write(parallel, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +304,7 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
case IRP_MJ_DEVICE_CONTROL:
|
case IRP_MJ_DEVICE_CONTROL:
|
||||||
if ((error = parallel_process_irp_device_control(parallel, irp)))
|
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);
|
error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -308,7 +324,7 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
|||||||
{
|
{
|
||||||
IRP* irp;
|
IRP* irp;
|
||||||
wMessage message;
|
wMessage message;
|
||||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) arg;
|
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
@ -330,18 +346,17 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
|||||||
if (message.id == WMQ_QUIT)
|
if (message.id == WMQ_QUIT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
irp = (IRP*) message.wParam;
|
irp = (IRP*)message.wParam;
|
||||||
|
|
||||||
if ((error = parallel_process_irp(parallel, irp)))
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error && parallel->rdpcontext)
|
if (error && parallel->rdpcontext)
|
||||||
setChannelError(parallel->rdpcontext, error,
|
setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
|
||||||
"parallel_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
ExitThread(error);
|
||||||
return error;
|
return error;
|
||||||
@ -354,9 +369,9 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
|||||||
*/
|
*/
|
||||||
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
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!");
|
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
@ -373,13 +388,13 @@ static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
|||||||
static UINT parallel_free(DEVICE* device)
|
static UINT parallel_free(DEVICE* device)
|
||||||
{
|
{
|
||||||
UINT error;
|
UINT error;
|
||||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) device;
|
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
|
||||||
|
|
||||||
if (!MessageQueue_PostQuit(parallel->queue, 0)
|
if (!MessageQueue_PostQuit(parallel->queue, 0) ||
|
||||||
|| (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
(WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,9 +406,9 @@ static UINT parallel_free(DEVICE* device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
#define DeviceServiceEntry parallel_DeviceServiceEntry
|
#define DeviceServiceEntry parallel_DeviceServiceEntry
|
||||||
#else
|
#else
|
||||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -410,7 +425,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||||||
RDPDR_PARALLEL* device;
|
RDPDR_PARALLEL* device;
|
||||||
PARALLEL_DEVICE* parallel;
|
PARALLEL_DEVICE* parallel;
|
||||||
UINT error;
|
UINT error;
|
||||||
device = (RDPDR_PARALLEL*) pEntryPoints->device;
|
device = (RDPDR_PARALLEL*)pEntryPoints->device;
|
||||||
name = device->Name;
|
name = device->Name;
|
||||||
path = device->Path;
|
path = device->Path;
|
||||||
|
|
||||||
@ -422,7 +437,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||||||
|
|
||||||
if (name[0] && path[0])
|
if (name[0] && path[0])
|
||||||
{
|
{
|
||||||
parallel = (PARALLEL_DEVICE*) calloc(1, sizeof(PARALLEL_DEVICE));
|
parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
|
||||||
|
|
||||||
if (!parallel)
|
if (!parallel)
|
||||||
{
|
{
|
||||||
@ -458,15 +473,14 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)parallel)))
|
||||||
(DEVICE*) parallel)))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "RegisterDevice failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(parallel->thread = CreateThread(NULL, 0,
|
if (!(parallel->thread =
|
||||||
parallel_thread_func, (void*) parallel, 0, NULL)))
|
CreateThread(NULL, 0, parallel_thread_func, (void*)parallel, 0, NULL)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
|
|||||||
@ -18,39 +18,23 @@
|
|||||||
define_channel_client("printer")
|
define_channel_client("printer")
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
set(${MODULE_PREFIX}_SRCS
|
||||||
printer_main.c
|
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()
|
|
||||||
|
|
||||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
|
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
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})
|
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||||
|
|
||||||
|
|
||||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_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)
|
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
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"
|
#include "../printer.h"
|
||||||
|
|
||||||
#ifdef WITH_CUPS
|
#include <freerdp/client/printer.h>
|
||||||
#include "printer_cups.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "printer_main.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(_UWP)
|
|
||||||
#include "printer_win.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <freerdp/channels/log.h>
|
#include <freerdp/channels/log.h>
|
||||||
|
|
||||||
@ -79,26 +71,20 @@ typedef enum
|
|||||||
PRN_CONF_PNP = 1,
|
PRN_CONF_PNP = 1,
|
||||||
PRN_CONF_DRIVER = 2,
|
PRN_CONF_DRIVER = 2,
|
||||||
PRN_CONF_DATA = 3
|
PRN_CONF_DATA = 3
|
||||||
}
|
} prn_conf_t;
|
||||||
prn_conf_t;
|
|
||||||
|
|
||||||
static const char* filemap[] =
|
static const char* filemap[] = { "PortDosName", "PnPName", "DriverName",
|
||||||
{
|
"CachedPrinterConfigData" };
|
||||||
"PortDosName",
|
|
||||||
"PnPName",
|
|
||||||
"DriverName",
|
|
||||||
"CachedPrinterConfigData"
|
|
||||||
};
|
|
||||||
|
|
||||||
static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* name, size_t length)
|
static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* name, size_t length)
|
||||||
{
|
{
|
||||||
char* dir = GetCombinedPath(settings->ConfigPath, "printers");
|
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);
|
char* config = GetCombinedPath(dir, bname);
|
||||||
|
|
||||||
if (config && !PathFileExistsA(config))
|
if (config && !winpr_PathFileExists(config))
|
||||||
{
|
{
|
||||||
if (!PathMakePathA(config, NULL))
|
if (!winpr_PathMakePath(config, NULL))
|
||||||
{
|
{
|
||||||
free(config);
|
free(config);
|
||||||
config = NULL;
|
config = NULL;
|
||||||
@ -121,11 +107,10 @@ static BOOL printer_write_setting(const char* path, prn_conf_t type, const void*
|
|||||||
const char* name = filemap[type];
|
const char* name = filemap[type];
|
||||||
char* abs = GetCombinedPath(path, name);
|
char* abs = GetCombinedPath(path, name);
|
||||||
|
|
||||||
if (!abs)
|
if (!abs || (length > INT32_MAX))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
file = CreateFileA(abs, GENERIC_WRITE, 0,
|
file = CreateFileA(abs, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
free(abs);
|
free(abs);
|
||||||
|
|
||||||
if (file == INVALID_HANDLE_VALUE)
|
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)
|
if (!base64)
|
||||||
goto fail;
|
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);
|
rc = WriteFile(file, base64, b64len, &written, NULL);
|
||||||
|
|
||||||
if (b64len != written)
|
if (b64len != written)
|
||||||
@ -158,27 +145,26 @@ static BOOL printer_config_valid(const char* path)
|
|||||||
if (!path)
|
if (!path)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!PathFileExistsA(path))
|
if (!winpr_PathFileExists(path))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 lowSize, highSize;
|
||||||
DWORD read = 0;
|
DWORD read = 0;
|
||||||
BOOL rc = FALSE;
|
BOOL rc = FALSE;
|
||||||
HANDLE file;
|
HANDLE file;
|
||||||
BYTE* fdata = NULL;
|
char* fdata = NULL;
|
||||||
const char* name = filemap[type];
|
const char* name = filemap[type];
|
||||||
char* abs = GetCombinedPath(path, name);
|
char* abs = GetCombinedPath(path, name);
|
||||||
|
|
||||||
if (!abs)
|
if (!abs)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
file = CreateFileA(abs, GENERIC_READ, 0,
|
file = CreateFileA(abs, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
free(abs);
|
free(abs);
|
||||||
|
|
||||||
if (file == INVALID_HANDLE_VALUE)
|
if (file == INVALID_HANDLE_VALUE)
|
||||||
@ -205,13 +191,13 @@ static BOOL printer_read_setting(const char* path, prn_conf_t type, void** data,
|
|||||||
fail:
|
fail:
|
||||||
CloseHandle(file);
|
CloseHandle(file);
|
||||||
|
|
||||||
if (rc)
|
if (rc && (lowSize <= INT_MAX))
|
||||||
{
|
{
|
||||||
int blen = 0;
|
int blen = 0;
|
||||||
crypto_base64_decode(fdata, lowSize, data, &blen);
|
crypto_base64_decode(fdata, (int)lowSize, (BYTE**)data, &blen);
|
||||||
|
|
||||||
if (*data)
|
if (*data && (blen > 0))
|
||||||
*length = blen;
|
*length = (UINT32)blen;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rc = FALSE;
|
rc = FALSE;
|
||||||
@ -228,9 +214,8 @@ fail:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL printer_save_to_config(const rdpSettings* settings,
|
static BOOL printer_save_to_config(const rdpSettings* settings, const char* PortDosName,
|
||||||
const char* PortDosName, size_t PortDosNameLen,
|
size_t PortDosNameLen, const WCHAR* PnPName, size_t PnPNameLen,
|
||||||
const WCHAR* PnPName, size_t PnPNameLen,
|
|
||||||
const WCHAR* DriverName, size_t DriverNameLen,
|
const WCHAR* DriverName, size_t DriverNameLen,
|
||||||
const WCHAR* PrinterName, size_t PrintNameLen,
|
const WCHAR* PrinterName, size_t PrintNameLen,
|
||||||
const BYTE* CachedPrinterConfigData, size_t CacheFieldsLen)
|
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))
|
if (!printer_config_valid(path))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
rc = RemoveDirectoryA(path);
|
rc = winpr_RemoveDirectory(path);
|
||||||
fail:
|
fail:
|
||||||
free(path);
|
free(path);
|
||||||
return rc;
|
return rc;
|
||||||
@ -290,7 +275,7 @@ static BOOL printer_move_config(const rdpSettings* settings, const WCHAR* oldNam
|
|||||||
char* newPath = get_printer_config_path(settings, newName, newLength);
|
char* newPath = get_printer_config_path(settings, newName, newLength);
|
||||||
|
|
||||||
if (printer_config_valid(oldPath))
|
if (printer_config_valid(oldPath))
|
||||||
rc = MoveFileA(oldPath, newPath);
|
rc = winpr_MoveFile(oldPath, newPath);
|
||||||
|
|
||||||
free(oldPath);
|
free(oldPath);
|
||||||
free(newPath);
|
free(newPath);
|
||||||
@ -306,13 +291,13 @@ static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* pr
|
|||||||
char* path = NULL;
|
char* path = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
UINT32 flags = 0;
|
UINT32 flags = 0;
|
||||||
WCHAR* DriverName = NULL;
|
void* DriverName = NULL;
|
||||||
size_t DriverNameLen = 0;
|
UINT32 DriverNameLen = 0;
|
||||||
WCHAR* PnPName = NULL;
|
void* PnPName = NULL;
|
||||||
size_t PnPNameLen = 0;
|
UINT32 PnPNameLen = 0;
|
||||||
BYTE* CachedPrinterConfigData = NULL;
|
void* CachedPrinterConfigData = NULL;
|
||||||
size_t CachedFieldsLen = 0;
|
UINT32 CachedFieldsLen = 0;
|
||||||
size_t PrinterNameLen = 0;
|
UINT32 PrinterNameLen = 0;
|
||||||
|
|
||||||
if (!settings || !printer)
|
if (!settings || !printer)
|
||||||
return FALSE;
|
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))
|
if (!printer_read_setting(path, PRN_CONF_DRIVER, &DriverName, &DriverNameLen))
|
||||||
{
|
{
|
||||||
DriverNameLen = ConvertToUnicode(CP_UTF8, 0, printer->driver, -1, &DriverName,
|
DriverNameLen =
|
||||||
0) * 2 + 1;
|
ConvertToUnicode(CP_UTF8, 0, printer->driver, -1, (LPWSTR*)&DriverName, 0) * 2 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!printer_read_setting(path, PRN_CONF_DATA, &CachedPrinterConfigData, &CachedFieldsLen))
|
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;
|
goto fail;
|
||||||
|
|
||||||
Stream_Write_UINT32(printer_dev->device.data, flags);
|
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, PnPNameLen); /* PnPNameLen */
|
||||||
Stream_Write_UINT32(printer_dev->device.data, DriverNameLen);
|
Stream_Write_UINT32(printer_dev->device.data, DriverNameLen);
|
||||||
Stream_Write_UINT32(printer_dev->device.data, PrinterNameLen);
|
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;
|
rdpPrintJob* printjob = NULL;
|
||||||
|
|
||||||
if (printer_dev->printer)
|
if (printer_dev->printer)
|
||||||
printjob = printer_dev->printer->CreatePrintJob(printer_dev->printer,
|
printjob =
|
||||||
irp->devman->id_sequence++);
|
printer_dev->printer->CreatePrintJob(printer_dev->printer, irp->devman->id_sequence++);
|
||||||
|
|
||||||
if (printjob)
|
if (printjob)
|
||||||
{
|
{
|
||||||
@ -467,8 +452,7 @@ static UINT printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
rdpPrintJob* printjob = NULL;
|
rdpPrintJob* printjob = NULL;
|
||||||
|
|
||||||
if (printer_dev->printer)
|
if (printer_dev->printer)
|
||||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer,
|
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
|
||||||
irp->FileId);
|
|
||||||
|
|
||||||
if (!printjob)
|
if (!printjob)
|
||||||
{
|
{
|
||||||
@ -494,13 +478,18 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
UINT64 Offset;
|
UINT64 Offset;
|
||||||
rdpPrintJob* printjob = NULL;
|
rdpPrintJob* printjob = NULL;
|
||||||
UINT error = CHANNEL_RC_OK;
|
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_UINT32(irp->input, Length);
|
||||||
Stream_Read_UINT64(irp->input, Offset);
|
Stream_Read_UINT64(irp->input, Offset);
|
||||||
Stream_Seek(irp->input, 20); /* Padding */
|
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)
|
if (printer_dev->printer)
|
||||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer,
|
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
|
||||||
irp->FileId);
|
|
||||||
|
|
||||||
if (!printjob)
|
if (!printjob)
|
||||||
{
|
{
|
||||||
@ -509,12 +498,12 @@ static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error = printjob->Write(printjob, Stream_Pointer(irp->input), Length);
|
error = printjob->Write(printjob, ptr, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "printjob->Write failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "printjob->Write failed with error %" PRIu32 "!", error);
|
||||||
return 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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT printer_process_irp_device_control(PRINTER_DEVICE* printer_dev,
|
static UINT printer_process_irp_device_control(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||||
IRP* irp)
|
|
||||||
{
|
{
|
||||||
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
||||||
return irp->Complete(irp);
|
return irp->Complete(irp);
|
||||||
@ -549,7 +537,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
case IRP_MJ_CREATE:
|
case IRP_MJ_CREATE:
|
||||||
if ((error = printer_process_irp_create(printer_dev, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,7 +546,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
case IRP_MJ_CLOSE:
|
case IRP_MJ_CLOSE:
|
||||||
if ((error = printer_process_irp_close(printer_dev, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,7 +555,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
case IRP_MJ_WRITE:
|
case IRP_MJ_WRITE:
|
||||||
if ((error = printer_process_irp_write(printer_dev, irp)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,7 +564,7 @@ static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||||||
case IRP_MJ_DEVICE_CONTROL:
|
case IRP_MJ_DEVICE_CONTROL:
|
||||||
if ((error = printer_process_irp_device_control(printer_dev, irp)))
|
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);
|
error);
|
||||||
return 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)
|
static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||||
{
|
{
|
||||||
IRP* irp;
|
IRP* irp;
|
||||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) arg;
|
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)arg;
|
||||||
HANDLE obj[] = {printer_dev->event, printer_dev->stopEvent};
|
HANDLE obj[] = { printer_dev->event, printer_dev->stopEvent };
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
@ -606,7 +594,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
|||||||
if (rc == WAIT_FAILED)
|
if (rc == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,7 +604,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
ResetEvent(printer_dev->event);
|
ResetEvent(printer_dev->event);
|
||||||
irp = (IRP*) InterlockedPopEntrySList(printer_dev->pIrpList);
|
irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList);
|
||||||
|
|
||||||
if (irp == NULL)
|
if (irp == NULL)
|
||||||
{
|
{
|
||||||
@ -627,14 +615,13 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
if ((error = printer_process_irp(printer_dev, irp)))
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error && printer_dev->rdpcontext)
|
if (error && printer_dev->rdpcontext)
|
||||||
setChannelError(printer_dev->rdpcontext, error,
|
setChannelError(printer_dev->rdpcontext, error, "printer_thread_func reported an error");
|
||||||
"printer_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
ExitThread(error);
|
||||||
return error;
|
return error;
|
||||||
@ -647,7 +634,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
|||||||
*/
|
*/
|
||||||
static UINT printer_irp_request(DEVICE* device, IRP* irp)
|
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));
|
InterlockedPushEntrySList(printer_dev->pIrpList, &(irp->ItemEntry));
|
||||||
SetEvent(printer_dev->event);
|
SetEvent(printer_dev->event);
|
||||||
return CHANNEL_RC_OK;
|
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)
|
static UINT printer_custom_component(DEVICE* device, UINT16 component, UINT16 packetId, wStream* s)
|
||||||
{
|
{
|
||||||
UINT32 eventID;
|
UINT32 eventID;
|
||||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
|
||||||
const rdpSettings* settings = printer_dev->rdpcontext->settings;
|
const rdpSettings* settings = printer_dev->rdpcontext->settings;
|
||||||
|
|
||||||
if (component != RDPDR_CTYP_PRN)
|
if (component != RDPDR_CTYP_PRN)
|
||||||
@ -673,156 +660,156 @@ static UINT printer_custom_component(DEVICE* device, UINT16 component, UINT16 pa
|
|||||||
switch (eventID)
|
switch (eventID)
|
||||||
{
|
{
|
||||||
case RDPDR_ADD_PRINTER_EVENT:
|
case RDPDR_ADD_PRINTER_EVENT:
|
||||||
{
|
{
|
||||||
char PortDosName[8];
|
char PortDosName[8];
|
||||||
UINT32 PnPNameLen, DriverNameLen, PrintNameLen, CacheFieldsLen;
|
UINT32 PnPNameLen, DriverNameLen, PrintNameLen, CacheFieldsLen;
|
||||||
const WCHAR* PnPName, *DriverName, *PrinterName;
|
const WCHAR *PnPName, *DriverName, *PrinterName;
|
||||||
const BYTE* CachedPrinterConfigData;
|
const BYTE* CachedPrinterConfigData;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 24)
|
if (Stream_GetRemainingLength(s) < 24)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read(s, PortDosName, sizeof(PortDosName));
|
Stream_Read(s, PortDosName, sizeof(PortDosName));
|
||||||
Stream_Read_UINT32(s, PnPNameLen);
|
Stream_Read_UINT32(s, PnPNameLen);
|
||||||
Stream_Read_UINT32(s, DriverNameLen);
|
Stream_Read_UINT32(s, DriverNameLen);
|
||||||
Stream_Read_UINT32(s, PrintNameLen);
|
Stream_Read_UINT32(s, PrintNameLen);
|
||||||
Stream_Read_UINT32(s, CacheFieldsLen);
|
Stream_Read_UINT32(s, CacheFieldsLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < PnPNameLen)
|
if (Stream_GetRemainingLength(s) < PnPNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
PnPName = (const WCHAR*)Stream_Pointer(s);
|
PnPName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, PnPNameLen);
|
Stream_Seek(s, PnPNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < DriverNameLen)
|
if (Stream_GetRemainingLength(s) < DriverNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
DriverName = (const WCHAR*)Stream_Pointer(s);
|
DriverName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, DriverNameLen);
|
Stream_Seek(s, DriverNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < PrintNameLen)
|
if (Stream_GetRemainingLength(s) < PrintNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, PrintNameLen);
|
Stream_Seek(s, PrintNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < CacheFieldsLen)
|
if (Stream_GetRemainingLength(s) < CacheFieldsLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
CachedPrinterConfigData = Stream_Pointer(s);
|
CachedPrinterConfigData = Stream_Pointer(s);
|
||||||
Stream_Seek(s, CacheFieldsLen);
|
Stream_Seek(s, CacheFieldsLen);
|
||||||
|
|
||||||
if (!printer_save_to_config(settings,
|
if (!printer_save_to_config(settings, PortDosName, sizeof(PortDosName), PnPName,
|
||||||
PortDosName, sizeof(PortDosName),
|
PnPNameLen, DriverName, DriverNameLen, PrinterName,
|
||||||
PnPName, PnPNameLen,
|
PrintNameLen, CachedPrinterConfigData,
|
||||||
DriverName, DriverNameLen,
|
CacheFieldsLen))
|
||||||
PrinterName, PrintNameLen,
|
return ERROR_INTERNAL_ERROR;
|
||||||
CachedPrinterConfigData, CacheFieldsLen))
|
}
|
||||||
return ERROR_INTERNAL_ERROR;
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDPDR_UPDATE_PRINTER_EVENT:
|
case RDPDR_UPDATE_PRINTER_EVENT:
|
||||||
{
|
{
|
||||||
UINT32 PrinterNameLen, ConfigDataLen;
|
UINT32 PrinterNameLen, ConfigDataLen;
|
||||||
const WCHAR* PrinterName;
|
const WCHAR* PrinterName;
|
||||||
const BYTE* ConfigData;
|
const BYTE* ConfigData;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 8)
|
if (Stream_GetRemainingLength(s) < 8)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, PrinterNameLen);
|
Stream_Read_UINT32(s, PrinterNameLen);
|
||||||
Stream_Read_UINT32(s, ConfigDataLen);
|
Stream_Read_UINT32(s, ConfigDataLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, PrinterNameLen);
|
Stream_Seek(s, PrinterNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < ConfigDataLen)
|
if (Stream_GetRemainingLength(s) < ConfigDataLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
ConfigData = Stream_Pointer(s);
|
ConfigData = Stream_Pointer(s);
|
||||||
Stream_Seek(s, ConfigDataLen);
|
Stream_Seek(s, ConfigDataLen);
|
||||||
|
|
||||||
if (!printer_update_to_config(settings, PrinterName, PrinterNameLen, ConfigData, ConfigDataLen))
|
if (!printer_update_to_config(settings, PrinterName, PrinterNameLen, ConfigData,
|
||||||
return ERROR_INTERNAL_ERROR;
|
ConfigDataLen))
|
||||||
}
|
return ERROR_INTERNAL_ERROR;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case RDPDR_DELETE_PRINTER_EVENT:
|
case RDPDR_DELETE_PRINTER_EVENT:
|
||||||
{
|
{
|
||||||
UINT32 PrinterNameLen;
|
UINT32 PrinterNameLen;
|
||||||
const WCHAR* PrinterName;
|
const WCHAR* PrinterName;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, PrinterNameLen);
|
Stream_Read_UINT32(s, PrinterNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
if (Stream_GetRemainingLength(s) < PrinterNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
PrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, PrinterNameLen);
|
Stream_Seek(s, PrinterNameLen);
|
||||||
printer_remove_config(settings, PrinterName, PrinterNameLen);
|
printer_remove_config(settings, PrinterName, PrinterNameLen);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RDPDR_RENAME_PRINTER_EVENT:
|
case RDPDR_RENAME_PRINTER_EVENT:
|
||||||
{
|
{
|
||||||
UINT32 OldPrinterNameLen, NewPrinterNameLen;
|
UINT32 OldPrinterNameLen, NewPrinterNameLen;
|
||||||
const WCHAR* OldPrinterName;
|
const WCHAR* OldPrinterName;
|
||||||
const WCHAR* NewPrinterName;
|
const WCHAR* NewPrinterName;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 8)
|
if (Stream_GetRemainingLength(s) < 8)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, OldPrinterNameLen);
|
Stream_Read_UINT32(s, OldPrinterNameLen);
|
||||||
Stream_Read_UINT32(s, NewPrinterNameLen);
|
Stream_Read_UINT32(s, NewPrinterNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < OldPrinterNameLen)
|
if (Stream_GetRemainingLength(s) < OldPrinterNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
OldPrinterName = (const WCHAR*)Stream_Pointer(s);
|
OldPrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, OldPrinterNameLen);
|
Stream_Seek(s, OldPrinterNameLen);
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < NewPrinterNameLen)
|
if (Stream_GetRemainingLength(s) < NewPrinterNameLen)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
NewPrinterName = (const WCHAR*)Stream_Pointer(s);
|
NewPrinterName = (const WCHAR*)Stream_Pointer(s);
|
||||||
Stream_Seek(s, NewPrinterNameLen);
|
Stream_Seek(s, NewPrinterNameLen);
|
||||||
|
|
||||||
if (!printer_move_config(settings, OldPrinterName, OldPrinterNameLen, NewPrinterName,
|
if (!printer_move_config(settings, OldPrinterName, OldPrinterNameLen,
|
||||||
NewPrinterNameLen))
|
NewPrinterName, NewPrinterNameLen))
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PAKID_PRN_USING_XPS:
|
case PAKID_PRN_USING_XPS:
|
||||||
{
|
{
|
||||||
UINT32 flags;
|
UINT32 flags;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, flags);
|
Stream_Read_UINT32(s, flags);
|
||||||
WLog_ERR(TAG,
|
WLog_ERR(TAG,
|
||||||
"Ignoring unhandled message PAKID_PRN_USING_XPS [printerID=%08"PRIx32", flags=%08"PRIx32"]",
|
"Ignoring unhandled message PAKID_PRN_USING_XPS [printerID=%08" PRIx32
|
||||||
eventID, flags);
|
", flags=%08" PRIx32 "]",
|
||||||
}
|
eventID, flags);
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
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)
|
static UINT printer_free(DEVICE* device)
|
||||||
{
|
{
|
||||||
IRP* irp;
|
IRP* irp;
|
||||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
|
||||||
UINT error;
|
UINT error;
|
||||||
SetEvent(printer_dev->stopEvent);
|
SetEvent(printer_dev->stopEvent);
|
||||||
|
|
||||||
if (WaitForSingleObject(printer_dev->thread, INFINITE) == WAIT_FAILED)
|
if (WaitForSingleObject(printer_dev->thread, INFINITE) == WAIT_FAILED)
|
||||||
{
|
{
|
||||||
error = GetLastError();
|
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.
|
/* The analyzer is confused by this premature return value.
|
||||||
* Since this case can not be handled gracefully silence the
|
* Since this case can not be handled gracefully silence the
|
||||||
@ -854,7 +841,7 @@ static UINT printer_free(DEVICE* device)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((irp = (IRP*) InterlockedPopEntrySList(printer_dev->pIrpList)) != NULL)
|
while ((irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList)) != NULL)
|
||||||
irp->Discard(irp);
|
irp->Discard(irp);
|
||||||
|
|
||||||
CloseHandle(printer_dev->thread);
|
CloseHandle(printer_dev->thread);
|
||||||
@ -863,7 +850,7 @@ static UINT printer_free(DEVICE* device)
|
|||||||
_aligned_free(printer_dev->pIrpList);
|
_aligned_free(printer_dev->pIrpList);
|
||||||
|
|
||||||
if (printer_dev->printer)
|
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);
|
Stream_Free(printer_dev->device.data, TRUE);
|
||||||
free(printer_dev);
|
free(printer_dev);
|
||||||
@ -875,12 +862,11 @@ static UINT printer_free(DEVICE* device)
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
static UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* printer)
|
||||||
rdpPrinter* printer)
|
|
||||||
{
|
{
|
||||||
PRINTER_DEVICE* printer_dev;
|
PRINTER_DEVICE* printer_dev;
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
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)
|
if (!printer_dev)
|
||||||
{
|
{
|
||||||
@ -893,7 +879,7 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
|||||||
if (!printer_dev->device.data)
|
if (!printer_dev->device.data)
|
||||||
goto error_out;
|
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.type = RDPDR_DTYP_PRINT;
|
||||||
printer_dev->device.name = printer_dev->port;
|
printer_dev->device.name = printer_dev->port;
|
||||||
printer_dev->device.IRPRequest = printer_irp_request;
|
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->device.Free = printer_free;
|
||||||
printer_dev->rdpcontext = pEntryPoints->rdpcontext;
|
printer_dev->rdpcontext = pEntryPoints->rdpcontext;
|
||||||
printer_dev->printer = printer;
|
printer_dev->printer = printer;
|
||||||
printer_dev->pIrpList = (WINPR_PSLIST_HEADER) _aligned_malloc(sizeof(
|
printer_dev->pIrpList = (WINPR_PSLIST_HEADER)_aligned_malloc(sizeof(WINPR_SLIST_HEADER),
|
||||||
WINPR_SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
|
MEMORY_ALLOCATION_ALIGNMENT);
|
||||||
|
|
||||||
if (!printer_dev->pIrpList)
|
if (!printer_dev->pIrpList)
|
||||||
{
|
{
|
||||||
@ -930,102 +916,158 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)printer_dev)))
|
||||||
(DEVICE*) printer_dev)))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "RegisterDevice failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(printer_dev->thread = CreateThread(NULL, 0, printer_thread_func, (void*) printer_dev, 0,
|
if (!(printer_dev->thread =
|
||||||
NULL)))
|
CreateThread(NULL, 0, printer_thread_func, (void*)printer_dev, 0, NULL)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printer->AddRef(printer);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
printer_free(&printer_dev->device);
|
printer_free(&printer_dev->device);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
static rdpPrinterDriver* printer_load_backend(const char* backend)
|
||||||
#define DeviceServiceEntry printer_DeviceServiceEntry
|
{
|
||||||
#else
|
typedef rdpPrinterDriver* (*backend_load_t)(void);
|
||||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
union {
|
||||||
#endif
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @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;
|
int i;
|
||||||
char* name;
|
char* name;
|
||||||
char* driver_name;
|
char* driver_name;
|
||||||
rdpPrinter* printer;
|
BOOL default_backend = TRUE;
|
||||||
rdpPrinter** printers;
|
RDPDR_PRINTER* device = NULL;
|
||||||
RDPDR_PRINTER* device;
|
|
||||||
rdpPrinterDriver* driver = NULL;
|
rdpPrinterDriver* driver = NULL;
|
||||||
UINT error;
|
UINT error = CHANNEL_RC_OK;
|
||||||
#ifdef WITH_CUPS
|
|
||||||
driver = printer_cups_get_driver();
|
if (!pEntryPoints || !pEntryPoints->device)
|
||||||
#endif
|
return ERROR_INVALID_PARAMETER;
|
||||||
#if defined(_WIN32) && !defined(_UWP)
|
|
||||||
driver = printer_win_get_driver();
|
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
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
driver = printer_load_backend(backend);
|
||||||
|
}
|
||||||
|
|
||||||
if (!driver)
|
if (!driver)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Could not get a printer driver!");
|
WLog_ERR(TAG, "Could not get a printer driver!");
|
||||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
device = (RDPDR_PRINTER*) pEntryPoints->device;
|
|
||||||
name = device->Name;
|
|
||||||
driver_name = device->DriverName;
|
|
||||||
|
|
||||||
if (name && name[0])
|
if (name && name[0])
|
||||||
{
|
{
|
||||||
printer = driver->GetPrinter(driver, name, driver_name);
|
rdpPrinter* printer = driver->GetPrinter(driver, name, driver_name);
|
||||||
|
|
||||||
if (!printer)
|
if (!printer)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Could not get printer %s!", name);
|
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))
|
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)))
|
if ((error = printer_register(pEntryPoints, printer)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "printer_register failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "printer_register failed with error %" PRIu32 "!", error);
|
||||||
return error;
|
printer->ReleaseRef(printer);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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)))
|
if ((error = printer_register(pEntryPoints, printer)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "printer_register failed with error %"PRIu32"!", error);
|
WLog_ERR(TAG, "printer_register failed with error %" PRIu32 "!", error);
|
||||||
free(printers);
|
break;
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
#define FREERDP_CHANNEL_PRINTER_PRINTER_H
|
||||||
|
|
||||||
/* SERVER_PRINTER_CACHE_EVENT.cachedata */
|
/* SERVER_PRINTER_CACHE_EVENT.cachedata */
|
||||||
#define RDPDR_ADD_PRINTER_EVENT 0x00000001
|
#define RDPDR_ADD_PRINTER_EVENT 0x00000001
|
||||||
#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
|
#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
|
||||||
#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
|
#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
|
||||||
#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
|
#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
|
||||||
|
|
||||||
/* DR_PRN_DEVICE_ANNOUNCE.Flags */
|
/* DR_PRN_DEVICE_ANNOUNCE.Flags */
|
||||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
|
#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
|
||||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
|
#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
|
||||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
|
#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
|
||||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
|
#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
|
||||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
|
#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
|
||||||
|
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_PRINTER_PRINTER_H */
|
#endif /* FREERDP_CHANNEL_PRINTER_PRINTER_H */
|
||||||
|
|||||||
@ -20,3 +20,7 @@ define_channel("rail")
|
|||||||
if(WITH_CLIENT_CHANNELS)
|
if(WITH_CLIENT_CHANNELS)
|
||||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||||
endif()
|
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)
|
if (!rail)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
pInterface = (RailClientContext*) rail->channelEntryPoints.pInterface;
|
pInterface = (RailClientContext*)rail->channelEntryPoints.pInterface;
|
||||||
return pInterface;
|
return pInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,15 +56,18 @@ static UINT rail_send(railPlugin* rail, wStream* s)
|
|||||||
UINT status;
|
UINT status;
|
||||||
|
|
||||||
if (!rail)
|
if (!rail)
|
||||||
|
{
|
||||||
|
Stream_Free(s, TRUE);
|
||||||
return CHANNEL_RC_BAD_INIT_HANDLE;
|
return CHANNEL_RC_BAD_INIT_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
status = rail->channelEntryPoints.pVirtualChannelWriteEx(rail->InitHandle, rail->OpenHandle,
|
status = rail->channelEntryPoints.pVirtualChannelWriteEx(
|
||||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
rail->InitHandle, rail->OpenHandle, Stream_Buffer(s), (UINT32)Stream_GetPosition(s), s);
|
||||||
|
|
||||||
if (status != CHANNEL_RC_OK)
|
if (status != CHANNEL_RC_OK)
|
||||||
{
|
{
|
||||||
Stream_Free(s, TRUE);
|
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);
|
WTSErrorToString(status), status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,13 +79,15 @@ static UINT rail_send(railPlugin* rail, wStream* s)
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @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;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
length = Stream_GetPosition(src);
|
||||||
s = Stream_New(NULL, length);
|
s = Stream_New(NULL, length);
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
@ -91,7 +96,7 @@ UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length)
|
|||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write(s, data, length);
|
Stream_Write(s, Stream_Buffer(src), length);
|
||||||
return rail_send(rail, s);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_client_execute(RailClientContext* context,
|
static UINT rail_client_execute(RailClientContext* context, const RAIL_EXEC_ORDER* exec)
|
||||||
const RAIL_EXEC_ORDER* exec)
|
|
||||||
{
|
{
|
||||||
char* exeOrFile;
|
char* exeOrFile;
|
||||||
UINT error;
|
UINT error;
|
||||||
@ -118,25 +122,19 @@ static UINT rail_client_execute(RailClientContext* context,
|
|||||||
if (!context || !exec)
|
if (!context || !exec)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
exeOrFile = exec->RemoteApplicationProgram;
|
exeOrFile = exec->RemoteApplicationProgram;
|
||||||
flags = exec->flags;
|
flags = exec->flags;
|
||||||
|
|
||||||
if (!exeOrFile)
|
if (!exeOrFile)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
if (strnlen(exeOrFile, MAX_PATH) >= 2)
|
if (!utf8_string_to_rail_string(exec->RemoteApplicationProgram,
|
||||||
{
|
&ruExeOrFile) || /* RemoteApplicationProgram */
|
||||||
if (strncmp(exeOrFile, "||", 2) != 0)
|
!utf8_string_to_rail_string(exec->RemoteApplicationWorkingDir,
|
||||||
flags |= RAIL_EXEC_FLAG_FILE;
|
&ruWorkingDir) || /* ShellWorkingDirectory */
|
||||||
}
|
!utf8_string_to_rail_string(exec->RemoteApplicationArguments,
|
||||||
|
&ruArguments)) /* RemoteApplicationCmdLine */
|
||||||
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 */
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
else
|
else
|
||||||
error = rail_send_client_exec_order(rail, flags, &ruExeOrFile, &ruWorkingDir, &ruArguments);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_client_activate(RailClientContext* context,
|
static UINT rail_client_activate(RailClientContext* context, const RAIL_ACTIVATE_ORDER* activate)
|
||||||
const RAIL_ACTIVATE_ORDER* activate)
|
|
||||||
{
|
{
|
||||||
railPlugin* rail;
|
railPlugin* rail;
|
||||||
|
|
||||||
if (!context || !activate)
|
if (!context || !activate)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_activate_order(rail, activate);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_send_client_sysparam(RailClientContext* context,
|
static UINT rail_send_client_sysparam(RailClientContext* context, RAIL_SYSPARAM_ORDER* sysparam)
|
||||||
RAIL_SYSPARAM_ORDER* sysparam)
|
|
||||||
{
|
{
|
||||||
wStream* s;
|
wStream* s;
|
||||||
size_t length = RAIL_SYSPARAM_ORDER_LENGTH;
|
size_t length = RAIL_SYSPARAM_ORDER_LENGTH;
|
||||||
railPlugin* rail;
|
railPlugin* rail;
|
||||||
UINT error;
|
UINT error;
|
||||||
|
BOOL extendedSpiSupported;
|
||||||
|
|
||||||
if (!context || !sysparam)
|
if (!context || !sysparam)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
|
|
||||||
switch (sysparam->param)
|
switch (sysparam->param)
|
||||||
{
|
{
|
||||||
@ -201,9 +198,18 @@ static UINT rail_send_client_sysparam(RailClientContext* context,
|
|||||||
length += sysparam->highContrast.colorSchemeLength + 10;
|
length += sysparam->highContrast.colorSchemeLength + 10;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case SPI_SETFILTERKEYS:
|
||||||
length += 8;
|
length += 20;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SPI_SETSTICKYKEYS:
|
||||||
|
case SPI_SETCARETWIDTH:
|
||||||
|
case SPI_SETTOGGLEKEYS:
|
||||||
|
length += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ERROR_BAD_ARGUMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = rail_pdu_init(length);
|
s = rail_pdu_init(length);
|
||||||
@ -214,16 +220,17 @@ static UINT rail_send_client_sysparam(RailClientContext* context,
|
|||||||
return CHANNEL_RC_NO_MEMORY;
|
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);
|
Stream_Free(s, TRUE);
|
||||||
return error;
|
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);
|
Stream_Free(s, TRUE);
|
||||||
@ -252,7 +259,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,7 +270,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,7 +281,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,7 +292,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,7 +303,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,7 +314,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,7 +325,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
|
|
||||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,20 +333,6 @@ static UINT rail_client_system_param(RailClientContext* context,
|
|||||||
return error;
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
@ -353,7 +346,7 @@ static UINT rail_client_system_command(RailClientContext* context,
|
|||||||
if (!context || !syscommand)
|
if (!context || !syscommand)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_syscommand_order(rail, syscommand);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_client_handshake(RailClientContext* context,
|
static UINT rail_client_handshake(RailClientContext* context, const RAIL_HANDSHAKE_ORDER* handshake)
|
||||||
const RAIL_HANDSHAKE_ORDER* handshake)
|
|
||||||
{
|
{
|
||||||
railPlugin* rail;
|
railPlugin* rail;
|
||||||
|
|
||||||
if (!context || !handshake)
|
if (!context || !handshake)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_handshake_order(rail, handshake);
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
@ -432,7 +379,7 @@ static UINT rail_client_notify_event(RailClientContext* context,
|
|||||||
if (!context || !notifyEvent)
|
if (!context || !notifyEvent)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_notify_event_order(rail, notifyEvent);
|
return rail_send_client_notify_event_order(rail, notifyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,38 +396,10 @@ static UINT rail_client_window_move(RailClientContext* context,
|
|||||||
if (!context || !windowMove)
|
if (!context || !windowMove)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_window_move_order(rail, windowMove);
|
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
|
* Function description
|
||||||
*
|
*
|
||||||
@ -494,7 +413,7 @@ static UINT rail_client_information(RailClientContext* context,
|
|||||||
if (!context || !clientStatus)
|
if (!context || !clientStatus)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_status_order(rail, clientStatus);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_client_system_menu(RailClientContext* context,
|
static UINT rail_client_system_menu(RailClientContext* context, const RAIL_SYSMENU_ORDER* sysmenu)
|
||||||
const RAIL_SYSMENU_ORDER* sysmenu)
|
|
||||||
{
|
{
|
||||||
railPlugin* rail;
|
railPlugin* rail;
|
||||||
|
|
||||||
if (!context || !sysmenu)
|
if (!context || !sysmenu)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_sysmenu_order(rail, sysmenu);
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_client_language_bar_info(RailClientContext* context,
|
static UINT rail_client_language_bar_info(RailClientContext* context,
|
||||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||||
{
|
{
|
||||||
railPlugin* rail;
|
railPlugin* rail;
|
||||||
|
|
||||||
if (!context || !langBarInfo)
|
if (!context || !langBarInfo)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_langbar_info_order(rail, langBarInfo);
|
return rail_send_client_langbar_info_order(rail, langBarInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static UINT rail_client_language_ime_info(RailClientContext* context,
|
||||||
* Function description
|
const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT rail_server_language_bar_info(RailClientContext* context,
|
|
||||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
|
||||||
{
|
{
|
||||||
if (!context || !langBarInfo)
|
railPlugin* rail;
|
||||||
|
|
||||||
|
if (!context || !langImeInfo)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -566,29 +468,49 @@ static UINT rail_server_execute_result(RailClientContext* context,
|
|||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_client_get_appid_request(RailClientContext* context,
|
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;
|
railPlugin* rail;
|
||||||
|
|
||||||
if (!context || !getAppIdReq || !context->handle)
|
if (!context || !getAppIdReq || !context->handle)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
rail = (railPlugin*) context->handle;
|
rail = (railPlugin*)context->handle;
|
||||||
return rail_send_client_get_appid_req_order(rail, getAppIdReq);
|
return rail_send_client_get_appid_req_order(rail, getAppIdReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static UINT rail_client_compartment_info(RailClientContext* context,
|
||||||
* Function description
|
const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
|
||||||
*
|
|
||||||
* @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)
|
|
||||||
{
|
{
|
||||||
if (!context || !getAppIdResp)
|
railPlugin* rail;
|
||||||
|
|
||||||
|
if (!context || !compartmentInfo || !context->handle)
|
||||||
return ERROR_INVALID_PARAMETER;
|
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
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
static UINT rail_virtual_channel_event_data_received(railPlugin* rail, void* pData,
|
||||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
UINT32 dataLength, UINT32 totalLength,
|
||||||
|
UINT32 dataFlags)
|
||||||
{
|
{
|
||||||
wStream* data_in;
|
wStream* data_in;
|
||||||
|
|
||||||
@ -622,7 +545,7 @@ static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
|||||||
|
|
||||||
data_in = rail->data_in;
|
data_in = rail->data_in;
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(data_in, (int) dataLength))
|
if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
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))
|
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;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,7 +565,7 @@ static UINT rail_virtual_channel_event_data_received(railPlugin* rail,
|
|||||||
Stream_SealLength(data_in);
|
Stream_SealLength(data_in);
|
||||||
Stream_SetPosition(data_in, 0);
|
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!");
|
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||||
return ERROR_INTERNAL_ERROR;
|
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,
|
static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
|
||||||
UINT event,
|
UINT event, LPVOID pData,
|
||||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
UINT32 dataLength, UINT32 totalLength,
|
||||||
|
UINT32 dataFlags)
|
||||||
{
|
{
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
railPlugin* rail = (railPlugin*) lpUserParam;
|
railPlugin* rail = (railPlugin*)lpUserParam;
|
||||||
|
|
||||||
if (!rail || (rail->OpenHandle != openHandle))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "error no match");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event)
|
switch (event)
|
||||||
{
|
{
|
||||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
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,
|
if ((error = rail_virtual_channel_event_data_received(rail, pData, dataLength,
|
||||||
totalLength, dataFlags)))
|
totalLength, dataFlags)))
|
||||||
WLog_ERR(TAG, "rail_virtual_channel_event_data_received failed with error %"PRIu32"!",
|
WLog_ERR(TAG,
|
||||||
|
"rail_virtual_channel_event_data_received failed with error %" PRIu32 "!",
|
||||||
error);
|
error);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CHANNEL_EVENT_WRITE_CANCELLED:
|
||||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||||
break;
|
{
|
||||||
|
wStream* s = (wStream*)pData;
|
||||||
|
Stream_Free(s, TRUE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case CHANNEL_EVENT_USER:
|
case CHANNEL_EVENT_USER:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error && rail->rdpcontext)
|
if (error && rail && rail->rdpcontext)
|
||||||
setChannelError(rail->rdpcontext, error,
|
setChannelError(rail->rdpcontext, error,
|
||||||
"rail_virtual_channel_open_event reported an 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;
|
wStream* data;
|
||||||
wMessage message;
|
wMessage message;
|
||||||
railPlugin* rail = (railPlugin*) arg;
|
railPlugin* rail = (railPlugin*)arg;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
@ -717,13 +646,13 @@ static DWORD WINAPI rail_virtual_channel_client_thread(LPVOID arg)
|
|||||||
|
|
||||||
if (message.id == 0)
|
if (message.id == 0)
|
||||||
{
|
{
|
||||||
data = (wStream*) message.wParam;
|
data = (wStream*)message.wParam;
|
||||||
error = rail_order_recv(rail, data);
|
error = rail_order_recv(rail, data);
|
||||||
Stream_Free(data, TRUE);
|
Stream_Free(data, TRUE);
|
||||||
|
|
||||||
if (error)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -742,20 +671,30 @@ static DWORD WINAPI rail_virtual_channel_client_thread(LPVOID arg)
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData,
|
static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData, UINT32 dataLength)
|
||||||
UINT32 dataLength)
|
|
||||||
{
|
{
|
||||||
|
RailClientContext* context = rail_get_client_interface(rail);
|
||||||
UINT status;
|
UINT status;
|
||||||
status = rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle,
|
status = rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle, &rail->OpenHandle,
|
||||||
&rail->OpenHandle, rail->channelDef.name, rail_virtual_channel_open_event_ex);
|
rail->channelDef.name,
|
||||||
|
rail_virtual_channel_open_event_ex);
|
||||||
|
|
||||||
if (status != CHANNEL_RC_OK)
|
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);
|
WTSErrorToString(status), status);
|
||||||
return 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);
|
rail->queue = MessageQueue_New(NULL);
|
||||||
|
|
||||||
if (!rail->queue)
|
if (!rail->queue)
|
||||||
@ -764,9 +703,8 @@ static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData,
|
|||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(rail->thread = CreateThread(NULL, 0,
|
if (!(rail->thread =
|
||||||
rail_virtual_channel_client_thread, (void*) rail, 0,
|
CreateThread(NULL, 0, rail_virtual_channel_client_thread, (void*)rail, 0, NULL)))
|
||||||
NULL)))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
WLog_ERR(TAG, "CreateThread failed!");
|
||||||
MessageQueue_Free(rail->queue);
|
MessageQueue_Free(rail->queue);
|
||||||
@ -789,11 +727,11 @@ static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
|
|||||||
if (rail->OpenHandle == 0)
|
if (rail->OpenHandle == 0)
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
if (MessageQueue_PostQuit(rail->queue, 0)
|
if (MessageQueue_PostQuit(rail->queue, 0) &&
|
||||||
&& (WaitForSingleObject(rail->thread, INFINITE) == WAIT_FAILED))
|
(WaitForSingleObject(rail->thread, INFINITE) == WAIT_FAILED))
|
||||||
{
|
{
|
||||||
rc = GetLastError();
|
rc = GetLastError();
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", rc);
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,8 +743,8 @@ static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
|
|||||||
|
|
||||||
if (CHANNEL_RC_OK != rc)
|
if (CHANNEL_RC_OK != rc)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08"PRIX32"]",
|
WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
|
||||||
WTSErrorToString(rc), rc);
|
rc);
|
||||||
return 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,
|
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;
|
UINT error = CHANNEL_RC_OK;
|
||||||
railPlugin* rail = (railPlugin*) lpUserParam;
|
railPlugin* rail = (railPlugin*)lpUserParam;
|
||||||
|
|
||||||
if (!rail || (rail->InitHandle != pInitHandle))
|
if (!rail || (rail->InitHandle != pInitHandle))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "error no match");
|
WLog_ERR(TAG, "error no match");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -844,14 +782,15 @@ static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPV
|
|||||||
{
|
{
|
||||||
case CHANNEL_EVENT_CONNECTED:
|
case CHANNEL_EVENT_CONNECTED:
|
||||||
if ((error = rail_virtual_channel_event_connected(rail, pData, dataLength)))
|
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);
|
error);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CHANNEL_EVENT_DISCONNECTED:
|
case CHANNEL_EVENT_DISCONNECTED:
|
||||||
if ((error = rail_virtual_channel_event_disconnected(rail)))
|
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);
|
error);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -867,11 +806,12 @@ static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPV
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error && rail->rdpcontext)
|
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 */
|
/* rail is always built-in */
|
||||||
#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
|
#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
|
||||||
|
|
||||||
BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
|
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;
|
RailClientContext* context = NULL;
|
||||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
|
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
|
||||||
BOOL isFreerdp = FALSE;
|
BOOL isFreerdp = FALSE;
|
||||||
rail = (railPlugin*) calloc(1, sizeof(railPlugin));
|
rail = (railPlugin*)calloc(1, sizeof(railPlugin));
|
||||||
|
|
||||||
if (!rail)
|
if (!rail)
|
||||||
{
|
{
|
||||||
@ -888,18 +828,17 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
rail->channelDef.options =
|
/* Default to automatically replying to server handshakes */
|
||||||
CHANNEL_OPTION_INITIALIZED |
|
rail->sendHandshake = TRUE;
|
||||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
rail->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||||
CHANNEL_OPTION_COMPRESS_RDP |
|
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
|
||||||
sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), "rail");
|
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
|
||||||
|
|
||||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
||||||
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
||||||
{
|
{
|
||||||
context = (RailClientContext*) calloc(1, sizeof(RailClientContext));
|
context = (RailClientContext*)calloc(1, sizeof(RailClientContext));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
{
|
{
|
||||||
@ -908,28 +847,23 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->handle = (void*) rail;
|
context->handle = (void*)rail;
|
||||||
context->custom = NULL;
|
context->custom = NULL;
|
||||||
context->ClientExecute = rail_client_execute;
|
context->ClientExecute = rail_client_execute;
|
||||||
context->ClientActivate = rail_client_activate;
|
context->ClientActivate = rail_client_activate;
|
||||||
context->ClientSystemParam = rail_client_system_param;
|
context->ClientSystemParam = rail_client_system_param;
|
||||||
context->ServerSystemParam = rail_server_system_param;
|
|
||||||
context->ClientSystemCommand = rail_client_system_command;
|
context->ClientSystemCommand = rail_client_system_command;
|
||||||
context->ClientHandshake = rail_client_handshake;
|
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->ClientNotifyEvent = rail_client_notify_event;
|
||||||
context->ClientWindowMove = rail_client_window_move;
|
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->ClientInformation = rail_client_information;
|
||||||
context->ClientSystemMenu = rail_client_system_menu;
|
context->ClientSystemMenu = rail_client_system_menu;
|
||||||
context->ClientLanguageBarInfo = rail_client_language_bar_info;
|
context->ClientLanguageBarInfo = rail_client_language_bar_info;
|
||||||
context->ServerLanguageBarInfo = rail_server_language_bar_info;
|
context->ClientLanguageIMEInfo = rail_client_language_ime_info;
|
||||||
context->ServerExecuteResult = rail_server_execute_result;
|
|
||||||
context->ClientGetAppIdRequest = rail_client_get_appid_request;
|
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->rdpcontext = pEntryPointsEx->context;
|
||||||
rail->context = context;
|
rail->context = context;
|
||||||
isFreerdp = TRUE;
|
isFreerdp = TRUE;
|
||||||
@ -937,17 +871,15 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
|||||||
|
|
||||||
rail->log = WLog_Get("com.freerdp.channels.rail.client");
|
rail->log = WLog_Get("com.freerdp.channels.rail.client");
|
||||||
WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntryEx");
|
WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntryEx");
|
||||||
CopyMemory(&(rail->channelEntryPoints), pEntryPoints,
|
CopyMemory(&(rail->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
|
||||||
sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
|
|
||||||
rail->InitHandle = pInitHandle;
|
rail->InitHandle = pInitHandle;
|
||||||
rc = rail->channelEntryPoints.pVirtualChannelInitEx(rail, context, pInitHandle,
|
rc = rail->channelEntryPoints.pVirtualChannelInitEx(
|
||||||
&rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
|
rail, context, pInitHandle, &rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
|
||||||
rail_virtual_channel_init_event_ex);
|
rail_virtual_channel_init_event_ex);
|
||||||
|
|
||||||
if (CHANNEL_RC_OK != rc)
|
if (CHANNEL_RC_OK != rc)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "failed with %s [%08"PRIX32"]",
|
WLog_ERR(TAG, "failed with %s [%08" PRIX32 "]", WTSErrorToString(rc), rc);
|
||||||
WTSErrorToString(rc), rc);
|
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,10 +50,14 @@ struct rail_plugin
|
|||||||
DWORD OpenHandle;
|
DWORD OpenHandle;
|
||||||
wMessageQueue* queue;
|
wMessageQueue* queue;
|
||||||
rdpContext* rdpcontext;
|
rdpContext* rdpcontext;
|
||||||
|
DWORD channelBuildNumber;
|
||||||
|
DWORD channelFlags;
|
||||||
|
RAIL_CLIENT_STATUS_ORDER clientStatus;
|
||||||
|
BOOL sendHandshake;
|
||||||
};
|
};
|
||||||
typedef struct rail_plugin railPlugin;
|
typedef struct rail_plugin railPlugin;
|
||||||
|
|
||||||
RailClientContext* rail_get_client_interface(railPlugin* rail);
|
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 */
|
#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")
|
#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_order_recv(railPlugin* rail, wStream* s);
|
||||||
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType);
|
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_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_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_ORDER* clientStatus);
|
||||||
UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
|
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);
|
const RAIL_UNICODE_STRING* arguments);
|
||||||
UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER* activate);
|
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_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_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||||
|
|
||||||
UINT rail_send_client_notify_event_order(railPlugin* rail,
|
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_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE_ORDER* windowMove);
|
||||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail,
|
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,
|
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 */
|
#endif /* FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H */
|
||||||
|
|||||||
@ -23,56 +23,76 @@
|
|||||||
#include "rail_common.h"
|
#include "rail_common.h"
|
||||||
|
|
||||||
#include <winpr/crt.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)
|
||||||
{
|
{
|
||||||
"",
|
switch (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))
|
|
||||||
{
|
{
|
||||||
free(buffer);
|
case TS_RAIL_ORDER_EXEC:
|
||||||
return FALSE;
|
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;
|
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
|
||||||
unicode_string->length = (UINT16) length * sizeof(WCHAR);
|
{
|
||||||
return TRUE;
|
_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)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
return ERROR_INVALID_DATA;
|
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) */
|
Stream_Read_UINT16(s, *orderLength); /* orderLength (2 bytes) */
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
|
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) */
|
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)
|
if (Stream_GetRemainingLength(s) < 8)
|
||||||
return ERROR_INVALID_DATA;
|
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) */
|
Stream_Read_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
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) */
|
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>
|
#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 */
|
/* Fixed length of PDUs, excluding variable lengths */
|
||||||
#define RAIL_HANDSHAKE_ORDER_LENGTH 4 /* fixed */
|
#define RAIL_HANDSHAKE_ORDER_LENGTH 4 /* fixed */
|
||||||
#define RAIL_HANDSHAKE_EX_ORDER_LENGTH 8 /* fixed */
|
#define RAIL_HANDSHAKE_EX_ORDER_LENGTH 8 /* fixed */
|
||||||
#define RAIL_CLIENT_STATUS_ORDER_LENGTH 4 /* fixed */
|
#define RAIL_CLIENT_STATUS_ORDER_LENGTH 4 /* fixed */
|
||||||
#define RAIL_EXEC_ORDER_LENGTH 8 /* variable */
|
#define RAIL_EXEC_ORDER_LENGTH 8 /* variable */
|
||||||
#define RAIL_SYSPARAM_ORDER_LENGTH 4 /* variable */
|
#define RAIL_EXEC_RESULT_ORDER_LENGTH 12 /* variable */
|
||||||
#define RAIL_ACTIVATE_ORDER_LENGTH 5 /* fixed */
|
#define RAIL_SYSPARAM_ORDER_LENGTH 4 /* variable */
|
||||||
#define RAIL_SYSMENU_ORDER_LENGTH 8 /* fixed */
|
#define RAIL_MINMAXINFO_ORDER_LENGTH 20 /* fixed */
|
||||||
#define RAIL_SYSCOMMAND_ORDER_LENGTH 6 /* fixed */
|
#define RAIL_LOCALMOVESIZE_ORDER_LENGTH 12 /* fixed */
|
||||||
#define RAIL_NOTIFY_EVENT_ORDER_LENGTH 12 /* fixed */
|
#define RAIL_ACTIVATE_ORDER_LENGTH 5 /* fixed */
|
||||||
#define RAIL_WINDOW_MOVE_ORDER_LENGTH 12 /* fixed */
|
#define RAIL_SYSMENU_ORDER_LENGTH 8 /* fixed */
|
||||||
#define RAIL_GET_APPID_REQ_ORDER_LENGTH 4 /* fixed */
|
#define RAIL_SYSCOMMAND_ORDER_LENGTH 6 /* fixed */
|
||||||
#define RAIL_LANGBAR_INFO_ORDER_LENGTH 4 /* 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);
|
UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||||
void rail_write_handshake_order(wStream* s, const 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);
|
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);
|
UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength);
|
||||||
void rail_write_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 */
|
#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"
|
#include "devman.h"
|
||||||
|
|
||||||
|
#define TAG CHANNELS_TAG("rdpdr.client")
|
||||||
|
|
||||||
static void devman_device_free(void* obj)
|
static void devman_device_free(void* obj)
|
||||||
{
|
{
|
||||||
DEVICE* device = (DEVICE*) obj;
|
DEVICE* device = (DEVICE*)obj;
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
return;
|
return;
|
||||||
@ -58,21 +60,21 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
|||||||
if (!rdpdr)
|
if (!rdpdr)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
devman = (DEVMAN*) calloc(1, sizeof(DEVMAN));
|
devman = (DEVMAN*)calloc(1, sizeof(DEVMAN));
|
||||||
|
|
||||||
if (!devman)
|
if (!devman)
|
||||||
{
|
{
|
||||||
WLog_INFO(TAG, "calloc failed!");
|
WLog_Print(rdpdr->log, WLOG_INFO, "calloc failed!");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
devman->plugin = (void*) rdpdr;
|
devman->plugin = (void*)rdpdr;
|
||||||
devman->id_sequence = 1;
|
devman->id_sequence = 1;
|
||||||
devman->devices = ListDictionary_New(TRUE);
|
devman->devices = ListDictionary_New(TRUE);
|
||||||
|
|
||||||
if (!devman->devices)
|
if (!devman->devices)
|
||||||
{
|
{
|
||||||
WLog_INFO(TAG, "ListDictionary_New failed!");
|
WLog_Print(rdpdr->log, WLOG_INFO, "ListDictionary_New failed!");
|
||||||
free(devman);
|
free(devman);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -94,7 +96,7 @@ void devman_unregister_device(DEVMAN* devman, void* key)
|
|||||||
if (!devman || !key)
|
if (!devman || !key)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
device = (DEVICE*) ListDictionary_Remove(devman->devices, key);
|
device = (DEVICE*)ListDictionary_Remove(devman->devices, key);
|
||||||
|
|
||||||
if (device)
|
if (device)
|
||||||
devman_device_free(device);
|
devman_device_free(device);
|
||||||
@ -113,11 +115,11 @@ static UINT devman_register_device(DEVMAN* devman, DEVICE* device)
|
|||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
device->id = devman->id_sequence++;
|
device->id = devman->id_sequence++;
|
||||||
key = (void*)(size_t) device->id;
|
key = (void*)(size_t)device->id;
|
||||||
|
|
||||||
if (!ListDictionary_Add(devman->devices, key, device))
|
if (!ListDictionary_Add(devman->devices, key, device))
|
||||||
{
|
{
|
||||||
WLog_INFO(TAG, "ListDictionary_Add failed!");
|
WLog_INFO(TAG, "ListDictionary_Add failed!");
|
||||||
return ERROR_INTERNAL_ERROR;
|
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* devman_get_device_by_id(DEVMAN* devman, UINT32 id)
|
||||||
{
|
{
|
||||||
DEVICE* device = NULL;
|
DEVICE* device = NULL;
|
||||||
void* key = (void*)(size_t) id;
|
void* key = (void*)(size_t)id;
|
||||||
|
|
||||||
if (!devman)
|
if (!devman)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
device = (DEVICE*) ListDictionary_GetItemValue(devman->devices, key);
|
device = (DEVICE*)ListDictionary_GetItemValue(devman->devices, key);
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type)
|
|||||||
|
|
||||||
for (x = 0; x < count; x++)
|
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)
|
if (!cur)
|
||||||
continue;
|
continue;
|
||||||
@ -167,23 +169,28 @@ DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type)
|
|||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char DRIVE_SERVICE_NAME[] = "drive";
|
static const char DRIVE_SERVICE_NAME[] = "drive";
|
||||||
static char PRINTER_SERVICE_NAME[] = "printer";
|
static const char PRINTER_SERVICE_NAME[] = "printer";
|
||||||
static char SMARTCARD_SERVICE_NAME[] = "smartcard";
|
static const char SMARTCARD_SERVICE_NAME[] = "smartcard";
|
||||||
static char SERIAL_SERVICE_NAME[] = "serial";
|
static const char SERIAL_SERVICE_NAME[] = "serial";
|
||||||
static char PARALLEL_SERVICE_NAME[] = "parallel";
|
static const char PARALLEL_SERVICE_NAME[] = "parallel";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @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;
|
DEVICE_SERVICE_ENTRY_POINTS ep;
|
||||||
PDEVICE_SERVICE_ENTRY entry = NULL;
|
PDEVICE_SERVICE_ENTRY entry = NULL;
|
||||||
|
union {
|
||||||
|
const RDPDR_DEVICE* cdp;
|
||||||
|
RDPDR_DEVICE* dp;
|
||||||
|
} devconv;
|
||||||
|
|
||||||
|
devconv.cdp = device;
|
||||||
if (!devman || !device || !rdpcontext)
|
if (!devman || !device || !rdpcontext)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
@ -200,27 +207,27 @@ UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext
|
|||||||
|
|
||||||
if (!ServiceName)
|
if (!ServiceName)
|
||||||
{
|
{
|
||||||
WLog_INFO(TAG, "ServiceName %s did not match!", ServiceName);
|
WLog_INFO(TAG, "ServiceName %s did not match!", ServiceName);
|
||||||
return ERROR_INVALID_NAME;
|
return ERROR_INVALID_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->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
|
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,
|
entry = (PDEVICE_SERVICE_ENTRY)freerdp_load_channel_addin_entry(ServiceName, NULL,
|
||||||
"DeviceServiceEntry", 0);
|
"DeviceServiceEntry", 0);
|
||||||
|
|
||||||
if (!entry)
|
if (!entry)
|
||||||
{
|
{
|
||||||
WLog_INFO(TAG, "freerdp_load_channel_addin_entry failed!");
|
WLog_INFO(TAG, "freerdp_load_channel_addin_entry failed!");
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ep.devman = devman;
|
ep.devman = devman;
|
||||||
ep.RegisterDevice = devman_register_device;
|
ep.RegisterDevice = devman_register_device;
|
||||||
ep.device = device;
|
ep.device = devconv.dp;
|
||||||
ep.rdpcontext = rdpcontext;
|
ep.rdpcontext = rdpcontext;
|
||||||
return entry(&ep);
|
return entry(&ep);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
#include "rdpdr_main.h"
|
#include "rdpdr_main.h"
|
||||||
|
|
||||||
void devman_unregister_device(DEVMAN* devman, void* key);
|
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_id(DEVMAN* devman, UINT32 id);
|
||||||
DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type);
|
DEVICE* devman_get_device_by_type(DEVMAN* devman, UINT32 type);
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@ static UINT irp_complete(IRP* irp)
|
|||||||
rdpdrPlugin* rdpdr;
|
rdpdrPlugin* rdpdr;
|
||||||
UINT error;
|
UINT error;
|
||||||
|
|
||||||
rdpdr = (rdpdrPlugin*) irp->devman->plugin;
|
rdpdr = (rdpdrPlugin*)irp->devman->plugin;
|
||||||
|
|
||||||
pos = Stream_GetPosition(irp->output);
|
pos = Stream_GetPosition(irp->output);
|
||||||
Stream_SetPosition(irp->output, RDPDR_DEVICE_IO_RESPONSE_LENGTH - 4);
|
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);
|
error = rdpdr_send(rdpdr, irp->output);
|
||||||
irp->output = NULL;
|
irp->output = NULL;
|
||||||
|
|
||||||
irp_free(irp);
|
return irp_free(irp);
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
|
||||||
{
|
{
|
||||||
IRP* irp;
|
IRP* irp;
|
||||||
DEVICE* device;
|
DEVICE* device;
|
||||||
UINT32 DeviceId;
|
UINT32 DeviceId;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 20)
|
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20))
|
||||||
{
|
{
|
||||||
if (error)
|
if (error)
|
||||||
*error = ERROR_INVALID_DATA;
|
*error = ERROR_INVALID_DATA;
|
||||||
@ -95,49 +94,48 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
|||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
WLog_WARN(TAG, "devman_get_device_by_id failed!");
|
WLog_Print(log, WLOG_WARN, "devman_get_device_by_id failed!");
|
||||||
if (error)
|
if (error)
|
||||||
*error = CHANNEL_RC_OK;
|
*error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
};
|
};
|
||||||
|
|
||||||
irp = (IRP*) _aligned_malloc(sizeof(IRP), MEMORY_ALLOCATION_ALIGNMENT);
|
irp = (IRP*)_aligned_malloc(sizeof(IRP), MEMORY_ALLOCATION_ALIGNMENT);
|
||||||
|
|
||||||
if (!irp)
|
if (!irp)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "_aligned_malloc failed!");
|
WLog_Print(log, WLOG_ERROR, "_aligned_malloc failed!");
|
||||||
if (error)
|
if (error)
|
||||||
*error = CHANNEL_RC_NO_MEMORY;
|
*error = CHANNEL_RC_NO_MEMORY;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ZeroMemory(irp, sizeof(IRP));
|
ZeroMemory(irp, sizeof(IRP));
|
||||||
|
|
||||||
irp->input = s;
|
irp->input = s;
|
||||||
irp->device = device;
|
irp->device = device;
|
||||||
irp->devman = devman;
|
irp->devman = devman;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, irp->FileId); /* FileId (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->CompletionId); /* CompletionId (4 bytes) */
|
||||||
Stream_Read_UINT32(s, irp->MajorFunction); /* MajorFunction (4 bytes) */
|
Stream_Read_UINT32(s, irp->MajorFunction); /* MajorFunction (4 bytes) */
|
||||||
Stream_Read_UINT32(s, irp->MinorFunction); /* MinorFunction (4 bytes) */
|
Stream_Read_UINT32(s, irp->MinorFunction); /* MinorFunction (4 bytes) */
|
||||||
|
|
||||||
irp->output = Stream_New(NULL, 256);
|
irp->output = Stream_New(NULL, 256);
|
||||||
if (!irp->output)
|
if (!irp->output)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_New failed!");
|
WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
|
||||||
_aligned_free(irp);
|
_aligned_free(irp);
|
||||||
if (error)
|
if (error)
|
||||||
*error = CHANNEL_RC_NO_MEMORY;
|
*error = CHANNEL_RC_NO_MEMORY;
|
||||||
return NULL;
|
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_UINT16(irp->output, PAKID_CORE_DEVICE_IOCOMPLETION); /* PacketId (2 bytes) */
|
||||||
Stream_Write_UINT32(irp->output, DeviceId); /* DeviceId (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, irp->CompletionId); /* CompletionId (4 bytes) */
|
||||||
Stream_Write_UINT32(irp->output, 0); /* IoStatus (4 bytes) */
|
Stream_Write_UINT32(irp->output, 0); /* IoStatus (4 bytes) */
|
||||||
|
|
||||||
irp->Complete = irp_complete;
|
irp->Complete = irp_complete;
|
||||||
irp->Discard = irp_free;
|
irp->Discard = irp_free;
|
||||||
|
|||||||
@ -21,8 +21,9 @@
|
|||||||
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
||||||
#define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
#define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
||||||
|
|
||||||
|
#include <winpr/wlog.h>
|
||||||
#include "rdpdr_main.h"
|
#include "rdpdr_main.h"
|
||||||
|
|
||||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error);
|
IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error);
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user