feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
77
vendor/github.com/adrg/xdg/CODE_OF_CONDUCT.md
generated
vendored
77
vendor/github.com/adrg/xdg/CODE_OF_CONDUCT.md
generated
vendored
@@ -1,77 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age,
|
||||
body size, disability, ethnicity, sex characteristics, gender identity and
|
||||
expression, level of experience, education, socio-economic status, nationality,
|
||||
personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behaviour that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behaviour by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behaviour and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behaviour.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviour that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behaviour may be
|
||||
reported by contacting the project team at adrg@epistack.com. All complaints
|
||||
will be reviewed and investigated and will result in a response that is deemed
|
||||
necessary and appropriate to the circumstances. The project team is obligated to
|
||||
maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
135
vendor/github.com/adrg/xdg/CONTRIBUTING.md
generated
vendored
135
vendor/github.com/adrg/xdg/CONTRIBUTING.md
generated
vendored
@@ -1,135 +0,0 @@
|
||||
# Contributing to this project
|
||||
|
||||
Contributions in the form of pull requests, issues or just general feedback,
|
||||
are always welcome. Please take a moment to review this document in order to
|
||||
make the contribution process easy and effective for everyone involved.
|
||||
|
||||
Following these guidelines helps to communicate that you respect the time of
|
||||
the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue or assessing
|
||||
patches and features.
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
[features requests](#features) and [submitting pull
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests (use
|
||||
[Stack Overflow](http://stackoverflow.com) or IRC).
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
<a name="bugs"></a>
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
Good bug reports are extremely helpful - thank you!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
1. **Use the GitHub issue search** — check if the issue has already been
|
||||
reported.
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
3. **Isolate the problem** — create a reduced test case.
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report. What is
|
||||
your environment? What steps will reproduce the issue? What browser(s) and OS
|
||||
experience the problem? What would you expect to be the outcome? All these
|
||||
details will help people to fix any potential bugs.
|
||||
|
||||
Example:
|
||||
|
||||
> Short and descriptive example bug report title
|
||||
>
|
||||
> A summary of the issue and the browser/OS environment in which it occurs. If
|
||||
> suitable, include the steps required to reproduce the bug.
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> `<url>` - a link to the reduced test case
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
|
||||
<a name="features"></a>
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature. Please
|
||||
provide as much detail and context as possible.
|
||||
|
||||
|
||||
<a name="pull-requests"></a>
|
||||
## Pull requests
|
||||
|
||||
Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code, porting to a different language),
|
||||
otherwise you risk spending a lot of time working on something that the
|
||||
project's developers might not want to merge into the project.
|
||||
|
||||
Please adhere to the coding conventions used throughout a project (indentation,
|
||||
accurate comments, etc.) and any other requirements (such as test coverage).
|
||||
|
||||
Follow this process if you'd like your work considered for inclusion in the
|
||||
project:
|
||||
|
||||
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
|
||||
and configure the remotes:
|
||||
|
||||
```bash
|
||||
# Clone your fork of the repo into the current directory
|
||||
git clone https://github.com/<your-username>/<repo-name>
|
||||
# Navigate to the newly cloned directory
|
||||
cd <repo-name>
|
||||
# Assign the original repo to a remote called "upstream"
|
||||
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
|
||||
```
|
||||
|
||||
2. If you cloned a while ago, get the latest changes from upstream:
|
||||
|
||||
```bash
|
||||
git checkout <dev-branch>
|
||||
git pull upstream <dev-branch>
|
||||
```
|
||||
|
||||
3. Create a new topic branch (off the main project development branch) to
|
||||
contain your feature, change, or fix:
|
||||
|
||||
```bash
|
||||
git checkout -b <topic-branch-name>
|
||||
```
|
||||
|
||||
4. Commit your changes in logical chunks and use descriptive commit messages.
|
||||
Use [interactive rebase](https://help.github.com/articles/interactive-rebase)
|
||||
to tidy up your commits before making them public.
|
||||
|
||||
5. Locally merge (or rebase) the upstream development branch into your topic branch:
|
||||
|
||||
```bash
|
||||
git pull [--rebase] upstream <dev-branch>
|
||||
```
|
||||
|
||||
6. Push your topic branch up to your fork:
|
||||
|
||||
```bash
|
||||
git push origin <topic-branch-name>
|
||||
```
|
||||
|
||||
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
|
||||
with a clear title and description.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to
|
||||
license your work under the same license as that used by the project.
|
||||
21
vendor/github.com/adrg/xdg/LICENSE
generated
vendored
21
vendor/github.com/adrg/xdg/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Adrian-George Bostan <adrg@epistack.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
302
vendor/github.com/adrg/xdg/README.md
generated
vendored
302
vendor/github.com/adrg/xdg/README.md
generated
vendored
@@ -1,302 +0,0 @@
|
||||
<h1 align="center">
|
||||
<div>
|
||||
<img src="https://raw.githubusercontent.com/adrg/adrg.github.io/master/assets/projects/xdg/logo.svg" alt="xdg logo"/>
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">Go implementation of the XDG Base Directory Specification and XDG user directories.</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/adrg/xdg/actions/workflows/tests.yml">
|
||||
<img alt="Tests status" src="https://github.com/adrg/xdg/actions/workflows/tests.yml/badge.svg">
|
||||
</a>
|
||||
<a href="https://app.codecov.io/gh/adrg/xdg">
|
||||
<img alt="Code coverage" src="https://codecov.io/gh/adrg/xdg/branch/master/graphs/badge.svg?branch=master">
|
||||
</a>
|
||||
<a href="https://pkg.go.dev/github.com/adrg/xdg">
|
||||
<img alt="pkg.go.dev documentation" src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white">
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/MIT" rel="nofollow">
|
||||
<img alt="MIT license" src="https://img.shields.io/github/license/adrg/xdg">
|
||||
</a>
|
||||
<br />
|
||||
<a href="https://goreportcard.com/report/github.com/adrg/xdg">
|
||||
<img alt="Go report card" src="https://goreportcard.com/badge/github.com/adrg/xdg">
|
||||
</a>
|
||||
<a href="https://github.com/avelino/awesome-go#configuration">
|
||||
<img alt="Awesome Go" src="https://awesome.re/mentioned-badge.svg">
|
||||
</a>
|
||||
<a href="https://github.com/adrg/xdg/graphs/contributors">
|
||||
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/adrg/xdg" />
|
||||
</a>
|
||||
<a href="https://github.com/adrg/xdg/issues">
|
||||
<img alt="GitHub open issues" src="https://img.shields.io/github/issues-raw/adrg/xdg">
|
||||
</a>
|
||||
<a href="https://ko-fi.com/T6T72WATK">
|
||||
<img alt="Buy me a coffee" src="https://img.shields.io/static/v1.svg?label=%20&message=Buy%20me%20a%20coffee&color=579fbf&logo=buy%20me%20a%20coffee&logoColor=white">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Provides an implementation of the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
||||
The specification defines a set of standard paths for storing application files,
|
||||
including data and configuration files. For portability and flexibility reasons,
|
||||
applications should use the XDG defined locations instead of hardcoding paths.
|
||||
|
||||
The package also includes the locations of well known [user directories](https://wiki.archlinux.org/index.php/XDG_user_directories),
|
||||
support for the non-standard `XDG_BIN_HOME` directory, as well as other common directories such as fonts and applications.
|
||||
|
||||
The current implementation supports **most flavors of Unix**, **Windows**, **macOS** and **Plan 9**.
|
||||
On Windows, where XDG environment variables are not usually set, the package uses [Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/known-folders)
|
||||
as defaults. Therefore, appropriate locations are used for common [folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid) which may have been redirected.
|
||||
|
||||
See usage [examples](#usage) below. Full documentation can be found at https://pkg.go.dev/github.com/adrg/xdg.
|
||||
|
||||
## Installation
|
||||
go get github.com/adrg/xdg
|
||||
|
||||
## Default locations
|
||||
|
||||
The package defines sensible defaults for XDG variables which are empty or not
|
||||
present in the environment.
|
||||
|
||||
- On Unix-like operating systems, XDG environment variables are typically defined.
|
||||
Appropriate default locations are used for the environment variables which are not set.
|
||||
- On Windows, XDG environment variables are usually not set. If that is the case,
|
||||
the package relies on the appropriate [Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
|
||||
Sensible fallback locations are used for the folders which are not set.
|
||||
|
||||
### XDG Base Directory
|
||||
|
||||
<details open>
|
||||
<summary><strong>Unix-like operating systems</strong></summary>
|
||||
<br/>
|
||||
|
||||
| <a href="#xdg-base-directory"><img width="400" height="0"></a> | <a href="#xdg-base-directory"><img width="500" height="0"></a><p>Unix</p> | <a href="#xdg-base-directory"><img width="600" height="0"></a><p>macOS</p> | <a href="#xdg-base-directory"><img width="500" height="0"></a><p>Plan 9</p> |
|
||||
| :------------------------------------------------------------: | :-----------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------: |
|
||||
| <kbd><b>XDG_DATA_HOME</b></kbd> | <kbd>~/.local/share</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>$home/lib</kbd> |
|
||||
| <kbd><b>XDG_DATA_DIRS</b></kbd> | <kbd>/usr/local/share</kbd><br/><kbd>/usr/share</kbd> | <kbd>/Library/Application Support</kbd><kbd>~/.local/share</kbd> | <kbd>/lib</kbd> |
|
||||
| <kbd><b>XDG_CONFIG_HOME</b></kbd> | <kbd>~/.config</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>$home/lib</kbd> |
|
||||
| <kbd><b>XDG_CONFIG_DIRS</b></kbd> | <kbd>/etc/xdg</kbd> | <kbd>~/Library/Preferences</kbd><br/><kbd>/Library/Application Support</kbd><br/><kbd>/Library/Preferences</kbd><br/><kbd>~/.config</kbd> | <kbd>/lib</kbd> |
|
||||
| <kbd><b>XDG_STATE_HOME</b></kbd> | <kbd>~/.local/state</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>$home/lib/state</kbd> |
|
||||
| <kbd><b>XDG_CACHE_HOME</b></kbd> | <kbd>~/.cache</kbd> | <kbd>~/Library/Caches</kbd> | <kbd>$home/lib/cache</kbd> |
|
||||
| <kbd><b>XDG_RUNTIME_DIR</b></kbd> | <kbd>/run/user/$UID</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>/tmp</kbd> |
|
||||
| <kbd><b>XDG_BIN_HOME</b></kbd> | <kbd>~/.local/bin</kbd> | <kbd>~/.local/bin</kbd> | <kbd>$home/bin</kbd> |
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary><strong>Microsoft Windows</strong></summary>
|
||||
<br/>
|
||||
|
||||
| <a href="#xdg-base-directory"><img width="400" height="0"></a> | <a href="#xdg-base-directory"><img width="700" height="0"></a><p>Known Folder(s)</p> | <a href="#xdg-base-directory"><img width="900" height="0"></a><p>Fallback(s)</p> |
|
||||
| :------------------------------------------------------------: | :---------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------: |
|
||||
| <kbd><b>XDG_DATA_HOME</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
|
||||
| <kbd><b>XDG_DATA_DIRS</b></kbd> | <kbd>RoamingAppData</kbd><br/><kbd>ProgramData</kbd> | <kbd>%APPADATA%</kbd><br/><kbd>%ProgramData%</kbd> |
|
||||
| <kbd><b>XDG_CONFIG_HOME</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
|
||||
| <kbd><b>XDG_CONFIG_DIRS</b></kbd> | <kbd>ProgramData</kbd><br/><kbd>RoamingAppData</kbd> | <kbd>%ProgramData%</kbd><br/><kbd>%APPDATA%</kbd> |
|
||||
| <kbd><b>XDG_STATE_HOME</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
|
||||
| <kbd><b>XDG_CACHE_HOME</b></kbd> | <kbd>LocalAppData\cache</kbd> | <kbd>%LOCALAPPDATA%\cache</kbd> |
|
||||
| <kbd><b>XDG_RUNTIME_DIR</b></kbd> | <kbd>LocalAppData</kbd> | <kbd>%LOCALAPPDATA%</kbd> |
|
||||
| <kbd><b>XDG_BIN_HOME</b></kbd> | <kbd>UserProgramFiles</kbd> | <kbd>%LOCALAPPDATA%\Programs</kbd> |
|
||||
|
||||
</details>
|
||||
|
||||
### XDG user directories
|
||||
|
||||
XDG user directories environment variables are usually **not** set on most
|
||||
operating systems. However, if they are present in the environment, they take
|
||||
precedence. Appropriate fallback locations are used for the environment
|
||||
variables which are not set.
|
||||
|
||||
- On Unix-like operating systems (except macOS and Plan 9), the package reads the [user-dirs.dirs](https://man.archlinux.org/man/user-dirs.dirs.5.en) config file.
|
||||
- On Windows, the package uses the appropriate [Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
|
||||
|
||||
Lastly, default locations are used for any user directories which are not set,
|
||||
as shown in the following tables.
|
||||
|
||||
<details open>
|
||||
<summary><strong>Unix-like operating systems</strong></summary>
|
||||
<br/>
|
||||
|
||||
| <a href="#xdg-user-directories"><img width="500" height="0"></a> | <a href="#xdg-user-directories"><img width="500" height="0"></a><p>Unix</p> | <a href="#xdg-user-directories"><img width="500" height="0"></a><p>macOS</p> | <a href="#xdg-user-directories"><img width="500" height="0"></a><p>Plan 9</p> |
|
||||
| :--------------------------------------------------------------: | :-------------------------------------------------------------------------: | :---------------------------------------------------------------------------: | :---------------------------------------------------------------------------: |
|
||||
| <kbd><b>XDG_DESKTOP_DIR</b></kbd> | <kbd>~/Desktop</kbd> | <kbd>~/Desktop</kbd> | <kbd>$home/desktop</kbd> |
|
||||
| <kbd><b>XDG_DOWNLOAD_DIR</b></kbd> | <kbd>~/Downloads</kbd> | <kbd>~/Downloads</kbd> | <kbd>$home/downloads</kbd> |
|
||||
| <kbd><b>XDG_DOCUMENTS_DIR</b></kbd> | <kbd>~/Documents</kbd> | <kbd>~/Documents</kbd> | <kbd>$home/documents</kbd> |
|
||||
| <kbd><b>XDG_MUSIC_DIR</b></kbd> | <kbd>~/Music</kbd> | <kbd>~/Music</kbd> | <kbd>$home/music</kbd> |
|
||||
| <kbd><b>XDG_PICTURES_DIR</b></kbd> | <kbd>~/Pictures</kbd> | <kbd>~/Pictures</kbd> | <kbd>$home/pictures</kbd> |
|
||||
| <kbd><b>XDG_VIDEOS_DIR</b></kbd> | <kbd>~/Videos</kbd> | <kbd>~/Movies</kbd> | <kbd>$home/videos</kbd> |
|
||||
| <kbd><b>XDG_TEMPLATES_DIR</b></kbd> | <kbd>~/Templates</kbd> | <kbd>~/Templates</kbd> | <kbd>$home/templates</kbd> |
|
||||
| <kbd><b>XDG_PUBLICSHARE_DIR</b></kbd> | <kbd>~/Public</kbd> | <kbd>~/Public</kbd> | <kbd>$home/public</kbd> |
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary><strong>Microsoft Windows</strong></summary>
|
||||
<br/>
|
||||
|
||||
| <a href="#xdg-user-directories"><img width="500" height="0"></a> | <a href="#xdg-user-directories"><img width="600" height="0"></a><p>Known Folder(s)</p> | <a href="#xdg-user-directories"><img width="900" height="0"></a><p>Fallback(s)</p> |
|
||||
| :--------------------------------------------------------------: | :-----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------: |
|
||||
| <kbd><b>XDG_DESKTOP_DIR</b></kbd> | <kbd>Desktop</kbd> | <kbd>%USERPROFILE%\Desktop</kbd> |
|
||||
| <kbd><b>XDG_DOWNLOAD_DIR</b></kbd> | <kbd>Downloads</kbd> | <kbd>%USERPROFILE%\Downloads</kbd> |
|
||||
| <kbd><b>XDG_DOCUMENTS_DIR</b></kbd> | <kbd>Documents</kbd> | <kbd>%USERPROFILE%\Documents</kbd> |
|
||||
| <kbd><b>XDG_MUSIC_DIR</b></kbd> | <kbd>Music</kbd> | <kbd>%USERPROFILE%\Music</kbd> |
|
||||
| <kbd><b>XDG_PICTURES_DIR</b></kbd> | <kbd>Pictures</kbd> | <kbd>%USERPROFILE%\Pictures</kbd> |
|
||||
| <kbd><b>XDG_VIDEOS_DIR</b></kbd> | <kbd>Videos</kbd> | <kbd>%USERPROFILE%\Videos</kbd> |
|
||||
| <kbd><b>XDG_TEMPLATES_DIR</b></kbd> | <kbd>Templates</kbd> | <kbd>%APPDATA%\Microsoft\Windows\Templates</kbd> |
|
||||
| <kbd><b>XDG_PUBLICSHARE_DIR</b></kbd> | <kbd>Public</kbd> | <kbd>%PUBLIC%</kbd> |
|
||||
|
||||
</details>
|
||||
|
||||
### Other directories
|
||||
|
||||
<details open>
|
||||
<summary><strong>Unix-like operating systems</strong></summary>
|
||||
<br/>
|
||||
|
||||
| <a href="#other-directories"><img width="400" height="0"></a> | <a href="#other-directories"><img width="600" height="0"></a><p>Unix</p> | <a href="#other-directories"><img width="600" height="0"></a><p>macOS</p> | <a href="#other-directories"><img width="400" height="0"></a><p>Plan 9</p> |
|
||||
| :-----------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------: |
|
||||
| <kbd><b>Home</b></kbd> | <kbd>$HOME</kbd> | <kbd>$HOME</kbd> | <kbd>$home</kbd> |
|
||||
| <kbd><b>Applications</b></kbd> | <kbd>$XDG_DATA_HOME/applications</kbd><br/><kbd>~/.local/share/applications</kbd><br/><kbd>/usr/local/share/applications</kbd><br/><kbd>/usr/share/applications</kbd><br/><kbd>$XDG_DATA_DIRS/applications</kbd> | <kbd>/Applications</kbd> | <kbd>$home/bin</kbd><br/><kbd>/bin</kbd> |
|
||||
| <kbd><b>Fonts</b></kbd> | <kbd>$XDG_DATA_HOME/fonts</kbd><br/><kbd>~/.fonts</kbd><br/><kbd>~/.local/share/fonts</kbd><br/><kbd>/usr/local/share/fonts</kbd><br/><kbd>/usr/share/fonts</kbd><br/><kbd>$XDG_DATA_DIRS/fonts</kbd> | <kbd>~/Library/Fonts</kbd><br/><kbd>/Library/Fonts</kbd><br/><kbd>/System/Library/Fonts</kbd><br/><kbd>/Network/Library/Fonts</kbd> | <kbd>$home/lib/font</kbd><br/><kbd>/lib/font</kbd> |
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary><strong>Microsoft Windows</strong></summary>
|
||||
<br/>
|
||||
|
||||
| <a href="#other-directories"><img width="400" height="0"></a> | <a href="#other-directories"><img width="300" height="0"></a><p>Known Folder(s)</p> | <a href="#other-directories"><img width="1300" height="0"></a><p>Fallback(s)</p> |
|
||||
| :-----------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| <kbd><b>Home</b></kbd> | <kbd>Profile</kbd> | <kbd>%USERPROFILE%</kbd> |
|
||||
| <kbd><b>Applications</b></kbd> | <kbd>Programs</kbd><br/><kbd>CommonPrograms</kbd> <br/><kbd>ProgramFiles</kbd><br/><kbd>ProgramFilesCommon</kbd><br/><kbd>UserProgramFiles</kbd><br/><kbd>UserProgramFilesCommon</kbd> | <kbd>%APPDATA%\Microsoft\Windows\Start Menu\Programs</kbd><br/><kbd>%ProgramData%\Microsoft\Windows\Start Menu\Programs</kbd><br/><kbd>%ProgramFiles%</kbd><br/><kbd>%ProgramFiles%\Common Files</kbd><br/><kbd>%LOCALAPPDATA%\Programs</kbd><br/><kbd>%LOCALAPPDATA%\Programs\Common</kbd>|
|
||||
| <kbd><b>Fonts</b></kbd> | <kbd>Fonts</kbd> | <kbd>%SystemRoot%\Fonts</kbd><br/><kbd>%LOCALAPPDATA%\Microsoft\Windows\Fonts</kbd> |
|
||||
|
||||
</details>
|
||||
|
||||
## Usage
|
||||
|
||||
#### XDG Base Directory
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// XDG Base Directory paths.
|
||||
log.Println("Home data directory:", xdg.DataHome)
|
||||
log.Println("Data directories:", xdg.DataDirs)
|
||||
log.Println("Home config directory:", xdg.ConfigHome)
|
||||
log.Println("Config directories:", xdg.ConfigDirs)
|
||||
log.Println("Home state directory:", xdg.StateHome)
|
||||
log.Println("Cache directory:", xdg.CacheHome)
|
||||
log.Println("Runtime directory:", xdg.RuntimeDir)
|
||||
log.Println("Home binaries directory:", xdg.BinHome)
|
||||
|
||||
// Other common directories.
|
||||
log.Println("Home directory:", xdg.Home)
|
||||
log.Println("Application directories:", xdg.ApplicationDirs)
|
||||
log.Println("Font directories:", xdg.FontDirs)
|
||||
|
||||
// Obtain a suitable location for application config files.
|
||||
// ConfigFile takes one parameter which must contain the name of the file,
|
||||
// but it can also contain a set of parent directories. If the directories
|
||||
// don't exist, they will be created relative to the base config directory.
|
||||
// It is recommended for files to be saved inside an application directory
|
||||
// relative to the base directory rather than directly inside the base
|
||||
// directory (e.g. `appname/config.yaml` instead of `appname-config.yaml`).
|
||||
configFilePath, err := xdg.ConfigFile("appname/config.yaml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Save the config file at:", configFilePath)
|
||||
|
||||
// For other types of application files use:
|
||||
// xdg.DataFile()
|
||||
// xdg.StateFile()
|
||||
// xdg.CacheFile()
|
||||
// xdg.RuntimeFile()
|
||||
|
||||
// Finding application config files.
|
||||
// SearchConfigFile takes one parameter which must contain the name of
|
||||
// the file, but it can also contain a set of parent directories relative
|
||||
// to the config search paths (xdg.ConfigHome and xdg.ConfigDirs).
|
||||
configFilePath, err = xdg.SearchConfigFile("appname/config.yaml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Config file was found at:", configFilePath)
|
||||
|
||||
// For other types of application files use:
|
||||
// xdg.SearchDataFile()
|
||||
// xdg.SearchStateFile()
|
||||
// xdg.SearchCacheFile()
|
||||
// xdg.SearchRuntimeFile()
|
||||
}
|
||||
```
|
||||
|
||||
#### XDG user directories
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// XDG user directories.
|
||||
log.Println("Desktop directory:", xdg.UserDirs.Desktop)
|
||||
log.Println("Download directory:", xdg.UserDirs.Download)
|
||||
log.Println("Documents directory:", xdg.UserDirs.Documents)
|
||||
log.Println("Music directory:", xdg.UserDirs.Music)
|
||||
log.Println("Pictures directory:", xdg.UserDirs.Pictures)
|
||||
log.Println("Videos directory:", xdg.UserDirs.Videos)
|
||||
log.Println("Templates directory:", xdg.UserDirs.Templates)
|
||||
log.Println("Public directory:", xdg.UserDirs.PublicShare)
|
||||
}
|
||||
```
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/adrg/xdg)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions in the form of pull requests, issues or just general feedback,
|
||||
are always welcome.
|
||||
See [CONTRIBUTING.MD](CONTRIBUTING.md).
|
||||
|
||||
**Contributors**:
|
||||
[adrg](https://github.com/adrg),
|
||||
[wichert](https://github.com/wichert),
|
||||
[bouncepaw](https://github.com/bouncepaw),
|
||||
[gabriel-vasile](https://github.com/gabriel-vasile),
|
||||
[KalleDK](https://github.com/KalleDK),
|
||||
[nvkv](https://github.com/nvkv),
|
||||
[djdv](https://github.com/djdv),
|
||||
[rrjjvv](https://github.com/rrjjvv),
|
||||
[GreyXor](https://github.com/GreyXor),
|
||||
[Rican7](https://github.com/Rican7),
|
||||
[nothub](https://github.com/nothub).
|
||||
|
||||
## References
|
||||
|
||||
For more information see:
|
||||
* [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
|
||||
* [XDG user directories](https://wiki.archlinux.org/index.php/XDG_user_directories)
|
||||
* [Windows Known Folders](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid)
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014 Adrian-George Bostan.
|
||||
|
||||
This project is licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
See [LICENSE](LICENSE) for more details.
|
||||
82
vendor/github.com/adrg/xdg/base_dirs.go
generated
vendored
82
vendor/github.com/adrg/xdg/base_dirs.go
generated
vendored
@@ -1,82 +0,0 @@
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
)
|
||||
|
||||
// XDG Base Directory environment variables.
|
||||
const (
|
||||
envDataHome = "XDG_DATA_HOME"
|
||||
envDataDirs = "XDG_DATA_DIRS"
|
||||
envConfigHome = "XDG_CONFIG_HOME"
|
||||
envConfigDirs = "XDG_CONFIG_DIRS"
|
||||
envStateHome = "XDG_STATE_HOME"
|
||||
envCacheHome = "XDG_CACHE_HOME"
|
||||
envRuntimeDir = "XDG_RUNTIME_DIR"
|
||||
|
||||
// Non-standard.
|
||||
envBinHome = "XDG_BIN_HOME"
|
||||
)
|
||||
|
||||
type baseDirectories struct {
|
||||
dataHome string
|
||||
data []string
|
||||
configHome string
|
||||
config []string
|
||||
stateHome string
|
||||
cacheHome string
|
||||
runtime string
|
||||
|
||||
// Non-standard.
|
||||
binHome string
|
||||
fonts []string
|
||||
applications []string
|
||||
}
|
||||
|
||||
func (bd baseDirectories) dataFile(relPath string) (string, error) {
|
||||
return pathutil.Create(relPath, append([]string{bd.dataHome}, bd.data...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) configFile(relPath string) (string, error) {
|
||||
return pathutil.Create(relPath, append([]string{bd.configHome}, bd.config...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) stateFile(relPath string) (string, error) {
|
||||
return pathutil.Create(relPath, []string{bd.stateHome})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) cacheFile(relPath string) (string, error) {
|
||||
return pathutil.Create(relPath, []string{bd.cacheHome})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) runtimeFile(relPath string) (string, error) {
|
||||
var paths []string
|
||||
for _, p := range pathutil.Unique([]string{bd.runtime, os.TempDir()}) {
|
||||
if pathutil.Exists(p) {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
}
|
||||
return pathutil.Create(relPath, paths)
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchDataFile(relPath string) (string, error) {
|
||||
return pathutil.Search(relPath, append([]string{bd.dataHome}, bd.data...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchConfigFile(relPath string) (string, error) {
|
||||
return pathutil.Search(relPath, append([]string{bd.configHome}, bd.config...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchStateFile(relPath string) (string, error) {
|
||||
return pathutil.Search(relPath, []string{bd.stateHome})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchCacheFile(relPath string) (string, error) {
|
||||
return pathutil.Search(relPath, []string{bd.cacheHome})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchRuntimeFile(relPath string) (string, error) {
|
||||
return pathutil.Search(relPath, pathutil.Unique([]string{bd.runtime, os.TempDir()}))
|
||||
}
|
||||
11
vendor/github.com/adrg/xdg/codecov.yml
generated
vendored
11
vendor/github.com/adrg/xdg/codecov.yml
generated
vendored
@@ -1,11 +0,0 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 90%
|
||||
threshold: 1%
|
||||
patch:
|
||||
default:
|
||||
target: 100%
|
||||
ignore:
|
||||
- "paths_plan9.go"
|
||||
105
vendor/github.com/adrg/xdg/doc.go
generated
vendored
105
vendor/github.com/adrg/xdg/doc.go
generated
vendored
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Package xdg provides an implementation of the XDG Base Directory Specification.
|
||||
The specification defines a set of standard paths for storing application files
|
||||
including data and configuration files. For portability and flexibility reasons,
|
||||
applications should use the XDG defined locations instead of hardcoding paths.
|
||||
The package also includes the locations of well known user directories.
|
||||
|
||||
The current implementation supports most flavors of Unix, Windows, Mac OS and Plan 9.
|
||||
|
||||
For more information regarding the XDG Base Directory Specification see:
|
||||
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
For more information regarding the XDG user directories see:
|
||||
https://wiki.archlinux.org/index.php/XDG_user_directories
|
||||
|
||||
For more information regarding the Windows Known Folders see:
|
||||
https://docs.microsoft.com/en-us/windows/win32/shell/known-folders
|
||||
|
||||
# Usage
|
||||
|
||||
XDG Base Directory
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// XDG Base Directory paths.
|
||||
log.Println("Home data directory:", xdg.DataHome)
|
||||
log.Println("Data directories:", xdg.DataDirs)
|
||||
log.Println("Home config directory:", xdg.ConfigHome)
|
||||
log.Println("Config directories:", xdg.ConfigDirs)
|
||||
log.Println("Home state directory:", xdg.StateHome)
|
||||
log.Println("Cache directory:", xdg.CacheHome)
|
||||
log.Println("Runtime directory:", xdg.RuntimeDir)
|
||||
log.Println("Home binaries directory:", xdg.BinHome)
|
||||
|
||||
// Other common directories.
|
||||
log.Println("Home directory:", xdg.Home)
|
||||
log.Println("Application directories:", xdg.ApplicationDirs)
|
||||
log.Println("Font directories:", xdg.FontDirs)
|
||||
|
||||
// Obtain a suitable location for application config files.
|
||||
// ConfigFile takes one parameter which must contain the name of the file,
|
||||
// but it can also contain a set of parent directories. If the directories
|
||||
// don't exist, they will be created relative to the base config directory.
|
||||
// It is recommended for files to be saved inside an application directory
|
||||
// relative to the base directory rather than directly inside the base
|
||||
// directory (e.g. `appname/config.yaml` instead of `appname-config.yaml`).
|
||||
configFilePath, err := xdg.ConfigFile("appname/config.yaml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Save the config file at:", configFilePath)
|
||||
|
||||
// For other types of application files use:
|
||||
// xdg.DataFile()
|
||||
// xdg.StateFile()
|
||||
// xdg.CacheFile()
|
||||
// xdg.RuntimeFile()
|
||||
|
||||
// Finding application config files.
|
||||
// SearchConfigFile takes one parameter which must contain the name of
|
||||
// the file, but it can also contain a set of parent directories relative
|
||||
// to the config search paths (xdg.ConfigHome and xdg.ConfigDirs).
|
||||
configFilePath, err = xdg.SearchConfigFile("appname/config.yaml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Config file was found at:", configFilePath)
|
||||
|
||||
// For other types of application files use:
|
||||
// xdg.SearchDataFile()
|
||||
// xdg.SearchStateFile()
|
||||
// xdg.SearchCacheFile()
|
||||
// xdg.SearchRuntimeFile()
|
||||
}
|
||||
|
||||
XDG user directories
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// XDG user directories.
|
||||
log.Println("Desktop directory:", xdg.UserDirs.Desktop)
|
||||
log.Println("Download directory:", xdg.UserDirs.Download)
|
||||
log.Println("Documents directory:", xdg.UserDirs.Documents)
|
||||
log.Println("Music directory:", xdg.UserDirs.Music)
|
||||
log.Println("Pictures directory:", xdg.UserDirs.Pictures)
|
||||
log.Println("Videos directory:", xdg.UserDirs.Videos)
|
||||
log.Println("Templates directory:", xdg.UserDirs.Templates)
|
||||
log.Println("Public directory:", xdg.UserDirs.PublicShare)
|
||||
}
|
||||
*/
|
||||
package xdg
|
||||
114
vendor/github.com/adrg/xdg/internal/pathutil/pathutil.go
generated
vendored
114
vendor/github.com/adrg/xdg/internal/pathutil/pathutil.go
generated
vendored
@@ -1,114 +0,0 @@
|
||||
package pathutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Unique eliminates the duplicate paths from the provided slice and returns
|
||||
// the result. The paths are expanded using the `ExpandHome` function and only
|
||||
// absolute paths are kept. The items in the output slice are in the order in
|
||||
// which they occur in the input slice.
|
||||
func Unique(paths []string) []string {
|
||||
var (
|
||||
uniq []string
|
||||
registry = map[string]struct{}{}
|
||||
)
|
||||
|
||||
for _, p := range paths {
|
||||
if p = ExpandHome(p); p != "" && filepath.IsAbs(p) {
|
||||
if _, ok := registry[p]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
registry[p] = struct{}{}
|
||||
uniq = append(uniq, p)
|
||||
}
|
||||
}
|
||||
|
||||
return uniq
|
||||
}
|
||||
|
||||
// First returns the first absolute path from the provided slice.
|
||||
// The paths in the input slice are expanded using the `ExpandHome` function.
|
||||
func First(paths []string) string {
|
||||
for _, p := range paths {
|
||||
if p = ExpandHome(p); p != "" && filepath.IsAbs(p) {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Create returns a suitable location relative to which the file with the
|
||||
// specified `name` can be written. The first path from the provided `paths`
|
||||
// slice which is successfully created (or already exists) is used as a base
|
||||
// path for the file. The `name` parameter should contain the name of the file
|
||||
// which is going to be written in the location returned by this function, but
|
||||
// it can also contain a set of parent directories, which will be created
|
||||
// relative to the selected parent path.
|
||||
func Create(name string, paths []string) (string, error) {
|
||||
searchedPaths := make([]string, 0, len(paths))
|
||||
for _, p := range paths {
|
||||
p = filepath.Join(p, name)
|
||||
|
||||
dir := filepath.Dir(p)
|
||||
if Exists(dir) {
|
||||
return p, nil
|
||||
}
|
||||
if err := os.MkdirAll(dir, os.ModeDir|0o700); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
searchedPaths = append(searchedPaths, dir)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not create any of the following paths: %v",
|
||||
searchedPaths)
|
||||
}
|
||||
|
||||
// Search searches for the file with the specified `name` in the provided
|
||||
// slice of `paths`. The `name` parameter must contain the name of the file,
|
||||
// but it can also contain a set of parent directories.
|
||||
func Search(name string, paths []string) (string, error) {
|
||||
searchedPaths := make([]string, 0, len(paths))
|
||||
for _, p := range paths {
|
||||
p = filepath.Join(p, name)
|
||||
if Exists(p) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
searchedPaths = append(searchedPaths, filepath.Dir(p))
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not locate `%s` in any of the following paths: %v",
|
||||
filepath.Base(name), searchedPaths)
|
||||
}
|
||||
|
||||
// EnvPath returns the value of the environment variable with the specified
|
||||
// `name` if it is an absolute path, or the first absolute fallback path.
|
||||
// All paths are expanded using the `ExpandHome` function.
|
||||
func EnvPath(name string, fallbackPaths ...string) string {
|
||||
dir := ExpandHome(os.Getenv(name))
|
||||
if dir != "" && filepath.IsAbs(dir) {
|
||||
return dir
|
||||
}
|
||||
|
||||
return First(fallbackPaths)
|
||||
}
|
||||
|
||||
// EnvPathList reads the value of the environment variable with the specified
|
||||
// `name` and attempts to extract a list of absolute paths from it. If there
|
||||
// are none, a list of absolute fallback paths is returned instead. Duplicate
|
||||
// paths are removed from the returned slice. All paths are expanded using the
|
||||
// `ExpandHome` function.
|
||||
func EnvPathList(name string, fallbackPaths ...string) []string {
|
||||
dirs := Unique(filepath.SplitList(os.Getenv(name)))
|
||||
if len(dirs) != 0 {
|
||||
return dirs
|
||||
}
|
||||
|
||||
return Unique(fallbackPaths)
|
||||
}
|
||||
40
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_plan9.go
generated
vendored
40
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_plan9.go
generated
vendored
@@ -1,40 +0,0 @@
|
||||
package pathutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserHomeDir returns the home directory of the current user.
|
||||
func UserHomeDir() string {
|
||||
if home := os.Getenv("home"); home != "" {
|
||||
return home
|
||||
}
|
||||
|
||||
return "/"
|
||||
}
|
||||
|
||||
// Exists returns true if the specified path exists.
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || errors.Is(err, fs.ErrExist)
|
||||
}
|
||||
|
||||
// ExpandHome substitutes `~` and `$home` at the start of the specified `path`.
|
||||
func ExpandHome(path string) string {
|
||||
home := UserHomeDir()
|
||||
if path == "" || home == "" {
|
||||
return path
|
||||
}
|
||||
if path[0] == '~' {
|
||||
return filepath.Join(home, path[1:])
|
||||
}
|
||||
if strings.HasPrefix(path, "$home") {
|
||||
return filepath.Join(home, path[5:])
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
42
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_unix.go
generated
vendored
42
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_unix.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || nacl || linux || netbsd || openbsd || solaris
|
||||
|
||||
package pathutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserHomeDir returns the home directory of the current user.
|
||||
func UserHomeDir() string {
|
||||
if home := os.Getenv("HOME"); home != "" {
|
||||
return home
|
||||
}
|
||||
|
||||
return "/"
|
||||
}
|
||||
|
||||
// Exists returns true if the specified path exists.
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || errors.Is(err, fs.ErrExist)
|
||||
}
|
||||
|
||||
// ExpandHome substitutes `~` and `$HOME` at the start of the specified `path`.
|
||||
func ExpandHome(path string) string {
|
||||
home := UserHomeDir()
|
||||
if path == "" || home == "" {
|
||||
return path
|
||||
}
|
||||
if path[0] == '~' {
|
||||
return filepath.Join(home, path[1:])
|
||||
}
|
||||
if strings.HasPrefix(path, "$HOME") {
|
||||
return filepath.Join(home, path[5:])
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
71
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_windows.go
generated
vendored
71
vendor/github.com/adrg/xdg/internal/pathutil/pathutil_windows.go
generated
vendored
@@ -1,71 +0,0 @@
|
||||
package pathutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// UserHomeDir returns the home directory of the current user.
|
||||
func UserHomeDir() string {
|
||||
return KnownFolder(windows.FOLDERID_Profile, []string{"USERPROFILE"}, nil)
|
||||
}
|
||||
|
||||
// Exists returns true if the specified path exists.
|
||||
func Exists(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if fi != nil && fi.Mode()&os.ModeSymlink != 0 {
|
||||
_, err = filepath.EvalSymlinks(path)
|
||||
}
|
||||
|
||||
return err == nil || errors.Is(err, fs.ErrExist)
|
||||
}
|
||||
|
||||
// ExpandHome substitutes `%USERPROFILE%` at the start of the specified `path`.
|
||||
func ExpandHome(path string) string {
|
||||
home := UserHomeDir()
|
||||
if path == "" || home == "" {
|
||||
return path
|
||||
}
|
||||
if strings.HasPrefix(path, `%USERPROFILE%`) {
|
||||
return filepath.Join(home, path[13:])
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// KnownFolder returns the location of the folder with the specified ID.
|
||||
// If that fails, the folder location is determined by reading the provided
|
||||
// environment variables (the first non-empty read value is returned).
|
||||
// If that fails as well, the first non-empty fallback is returned.
|
||||
// If all of the above fails, the function returns an empty string.
|
||||
func KnownFolder(id *windows.KNOWNFOLDERID, envVars []string, fallbacks []string) string {
|
||||
if id != nil {
|
||||
flags := []uint32{windows.KF_FLAG_DEFAULT, windows.KF_FLAG_DEFAULT_PATH}
|
||||
for _, flag := range flags {
|
||||
p, _ := windows.KnownFolderPath(id, flag|windows.KF_FLAG_DONT_VERIFY)
|
||||
if p != "" {
|
||||
return p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, envVar := range envVars {
|
||||
p := os.Getenv(envVar)
|
||||
if p != "" {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
for _, fallback := range fallbacks {
|
||||
if fallback != "" {
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
82
vendor/github.com/adrg/xdg/internal/userdirs/config_unix.go
generated
vendored
82
vendor/github.com/adrg/xdg/internal/userdirs/config_unix.go
generated
vendored
@@ -1,82 +0,0 @@
|
||||
//go:build aix || dragonfly || freebsd || (js && wasm) || nacl || linux || netbsd || openbsd || solaris
|
||||
|
||||
package userdirs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
)
|
||||
|
||||
// ParseConfigFile parses the user directories config file at the
|
||||
// specified location.
|
||||
func ParseConfigFile(name string) (*Directories, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return ParseConfig(f)
|
||||
}
|
||||
|
||||
// ParseConfig parses the user directories config file contained in
|
||||
// the provided reader.
|
||||
func ParseConfig(r io.Reader) (*Directories, error) {
|
||||
dirs := &Directories{}
|
||||
fieldsMap := map[string]*string{
|
||||
EnvDesktopDir: &dirs.Desktop,
|
||||
EnvDownloadDir: &dirs.Download,
|
||||
EnvDocumentsDir: &dirs.Documents,
|
||||
EnvMusicDir: &dirs.Music,
|
||||
EnvPicturesDir: &dirs.Pictures,
|
||||
EnvVideosDir: &dirs.Videos,
|
||||
EnvTemplatesDir: &dirs.Templates,
|
||||
EnvPublicShareDir: &dirs.PublicShare,
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(line, "XDG_") {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(line, "=")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse key.
|
||||
field, ok := fieldsMap[strings.TrimSpace(parts[0])]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse value.
|
||||
runes := []rune(strings.TrimSpace(parts[1]))
|
||||
|
||||
lenRunes := len(runes)
|
||||
if lenRunes <= 2 || runes[0] != '"' {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 1; i < lenRunes; i++ {
|
||||
if runes[i] == '"' {
|
||||
*field = pathutil.ExpandHome(string(runes[1:i]))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dirs, nil
|
||||
}
|
||||
40
vendor/github.com/adrg/xdg/internal/userdirs/userdirs.go
generated
vendored
40
vendor/github.com/adrg/xdg/internal/userdirs/userdirs.go
generated
vendored
@@ -1,40 +0,0 @@
|
||||
package userdirs
|
||||
|
||||
// XDG user directories environment variables.
|
||||
const (
|
||||
EnvDesktopDir = "XDG_DESKTOP_DIR"
|
||||
EnvDownloadDir = "XDG_DOWNLOAD_DIR"
|
||||
EnvDocumentsDir = "XDG_DOCUMENTS_DIR"
|
||||
EnvMusicDir = "XDG_MUSIC_DIR"
|
||||
EnvPicturesDir = "XDG_PICTURES_DIR"
|
||||
EnvVideosDir = "XDG_VIDEOS_DIR"
|
||||
EnvTemplatesDir = "XDG_TEMPLATES_DIR"
|
||||
EnvPublicShareDir = "XDG_PUBLICSHARE_DIR"
|
||||
)
|
||||
|
||||
// Directories defines the locations of well known user directories.
|
||||
type Directories struct {
|
||||
// Desktop defines the location of the user's desktop directory.
|
||||
Desktop string
|
||||
|
||||
// Download defines a suitable location for user downloaded files.
|
||||
Download string
|
||||
|
||||
// Documents defines a suitable location for user document files.
|
||||
Documents string
|
||||
|
||||
// Music defines a suitable location for user audio files.
|
||||
Music string
|
||||
|
||||
// Pictures defines a suitable location for user image files.
|
||||
Pictures string
|
||||
|
||||
// VideosDir defines a suitable location for user video files.
|
||||
Videos string
|
||||
|
||||
// Templates defines a suitable location for user template files.
|
||||
Templates string
|
||||
|
||||
// PublicShare defines a suitable location for user shared files.
|
||||
PublicShare string
|
||||
}
|
||||
60
vendor/github.com/adrg/xdg/paths_darwin.go
generated
vendored
60
vendor/github.com/adrg/xdg/paths_darwin.go
generated
vendored
@@ -1,60 +0,0 @@
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
"github.com/adrg/xdg/internal/userdirs"
|
||||
)
|
||||
|
||||
func initDirs(home string) {
|
||||
initBaseDirs(home)
|
||||
initUserDirs(home)
|
||||
}
|
||||
|
||||
func initBaseDirs(home string) {
|
||||
homeAppSupport := filepath.Join(home, "Library", "Application Support")
|
||||
rootAppSupport := "/Library/Application Support"
|
||||
|
||||
// Initialize standard directories.
|
||||
baseDirs.dataHome = pathutil.EnvPath(envDataHome, homeAppSupport)
|
||||
baseDirs.data = pathutil.EnvPathList(envDataDirs,
|
||||
rootAppSupport,
|
||||
filepath.Join(home, ".local", "share"),
|
||||
)
|
||||
baseDirs.configHome = pathutil.EnvPath(envConfigHome, homeAppSupport)
|
||||
baseDirs.config = pathutil.EnvPathList(envConfigDirs,
|
||||
filepath.Join(home, "Library", "Preferences"),
|
||||
rootAppSupport,
|
||||
"/Library/Preferences",
|
||||
filepath.Join(home, ".config"),
|
||||
)
|
||||
baseDirs.stateHome = pathutil.EnvPath(envStateHome, homeAppSupport)
|
||||
baseDirs.cacheHome = pathutil.EnvPath(envCacheHome, filepath.Join(home, "Library", "Caches"))
|
||||
baseDirs.runtime = pathutil.EnvPath(envRuntimeDir, homeAppSupport)
|
||||
|
||||
// Initialize non-standard directories.
|
||||
baseDirs.binHome = pathutil.EnvPath(envBinHome, filepath.Join(home, ".local", "bin"))
|
||||
|
||||
baseDirs.applications = []string{
|
||||
"/Applications",
|
||||
}
|
||||
|
||||
baseDirs.fonts = []string{
|
||||
filepath.Join(home, "Library/Fonts"),
|
||||
"/Library/Fonts",
|
||||
"/System/Library/Fonts",
|
||||
"/Network/Library/Fonts",
|
||||
}
|
||||
}
|
||||
|
||||
func initUserDirs(home string) {
|
||||
UserDirs.Desktop = pathutil.EnvPath(userdirs.EnvDesktopDir, filepath.Join(home, "Desktop"))
|
||||
UserDirs.Download = pathutil.EnvPath(userdirs.EnvDownloadDir, filepath.Join(home, "Downloads"))
|
||||
UserDirs.Documents = pathutil.EnvPath(userdirs.EnvDocumentsDir, filepath.Join(home, "Documents"))
|
||||
UserDirs.Music = pathutil.EnvPath(userdirs.EnvMusicDir, filepath.Join(home, "Music"))
|
||||
UserDirs.Pictures = pathutil.EnvPath(userdirs.EnvPicturesDir, filepath.Join(home, "Pictures"))
|
||||
UserDirs.Videos = pathutil.EnvPath(userdirs.EnvVideosDir, filepath.Join(home, "Movies"))
|
||||
UserDirs.Templates = pathutil.EnvPath(userdirs.EnvTemplatesDir, filepath.Join(home, "Templates"))
|
||||
UserDirs.PublicShare = pathutil.EnvPath(userdirs.EnvPublicShareDir, filepath.Join(home, "Public"))
|
||||
}
|
||||
51
vendor/github.com/adrg/xdg/paths_plan9.go
generated
vendored
51
vendor/github.com/adrg/xdg/paths_plan9.go
generated
vendored
@@ -1,51 +0,0 @@
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
"github.com/adrg/xdg/internal/userdirs"
|
||||
)
|
||||
|
||||
func initDirs(home string) {
|
||||
initBaseDirs(home)
|
||||
initUserDirs(home)
|
||||
}
|
||||
|
||||
func initBaseDirs(home string) {
|
||||
homeLibDir := filepath.Join(home, "lib")
|
||||
rootLibDir := "/lib"
|
||||
|
||||
// Initialize standard directories.
|
||||
baseDirs.dataHome = pathutil.EnvPath(envDataHome, homeLibDir)
|
||||
baseDirs.data = pathutil.EnvPathList(envDataDirs, rootLibDir)
|
||||
baseDirs.configHome = pathutil.EnvPath(envConfigHome, homeLibDir)
|
||||
baseDirs.config = pathutil.EnvPathList(envConfigDirs, rootLibDir)
|
||||
baseDirs.stateHome = pathutil.EnvPath(envStateHome, filepath.Join(homeLibDir, "state"))
|
||||
baseDirs.cacheHome = pathutil.EnvPath(envCacheHome, filepath.Join(homeLibDir, "cache"))
|
||||
baseDirs.runtime = pathutil.EnvPath(envRuntimeDir, "/tmp")
|
||||
|
||||
// Initialize non-standard directories.
|
||||
baseDirs.binHome = pathutil.EnvPath(envBinHome, filepath.Join(home, "bin"))
|
||||
|
||||
baseDirs.applications = []string{
|
||||
filepath.Join(home, "bin"),
|
||||
"/bin",
|
||||
}
|
||||
|
||||
baseDirs.fonts = []string{
|
||||
filepath.Join(homeLibDir, "font"),
|
||||
"/lib/font",
|
||||
}
|
||||
}
|
||||
|
||||
func initUserDirs(home string) {
|
||||
UserDirs.Desktop = pathutil.EnvPath(userdirs.EnvDesktopDir, filepath.Join(home, "desktop"))
|
||||
UserDirs.Download = pathutil.EnvPath(userdirs.EnvDownloadDir, filepath.Join(home, "downloads"))
|
||||
UserDirs.Documents = pathutil.EnvPath(userdirs.EnvDocumentsDir, filepath.Join(home, "documents"))
|
||||
UserDirs.Music = pathutil.EnvPath(userdirs.EnvMusicDir, filepath.Join(home, "music"))
|
||||
UserDirs.Pictures = pathutil.EnvPath(userdirs.EnvPicturesDir, filepath.Join(home, "pictures"))
|
||||
UserDirs.Videos = pathutil.EnvPath(userdirs.EnvVideosDir, filepath.Join(home, "videos"))
|
||||
UserDirs.Templates = pathutil.EnvPath(userdirs.EnvTemplatesDir, filepath.Join(home, "templates"))
|
||||
UserDirs.PublicShare = pathutil.EnvPath(userdirs.EnvPublicShareDir, filepath.Join(home, "public"))
|
||||
}
|
||||
70
vendor/github.com/adrg/xdg/paths_unix.go
generated
vendored
70
vendor/github.com/adrg/xdg/paths_unix.go
generated
vendored
@@ -1,70 +0,0 @@
|
||||
//go:build aix || dragonfly || freebsd || (js && wasm) || nacl || linux || netbsd || openbsd || solaris
|
||||
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
"github.com/adrg/xdg/internal/userdirs"
|
||||
)
|
||||
|
||||
func initDirs(home string) {
|
||||
initBaseDirs(home)
|
||||
initUserDirs(home, baseDirs.configHome)
|
||||
}
|
||||
|
||||
func initBaseDirs(home string) {
|
||||
// Initialize standard directories.
|
||||
baseDirs.dataHome = pathutil.EnvPath(envDataHome, filepath.Join(home, ".local", "share"))
|
||||
baseDirs.data = pathutil.EnvPathList(envDataDirs, "/usr/local/share", "/usr/share")
|
||||
baseDirs.configHome = pathutil.EnvPath(envConfigHome, filepath.Join(home, ".config"))
|
||||
baseDirs.config = pathutil.EnvPathList(envConfigDirs, "/etc/xdg")
|
||||
baseDirs.stateHome = pathutil.EnvPath(envStateHome, filepath.Join(home, ".local", "state"))
|
||||
baseDirs.cacheHome = pathutil.EnvPath(envCacheHome, filepath.Join(home, ".cache"))
|
||||
baseDirs.runtime = pathutil.EnvPath(envRuntimeDir, filepath.Join("/run/user", strconv.Itoa(os.Getuid())))
|
||||
|
||||
// Initialize non-standard directories.
|
||||
baseDirs.binHome = pathutil.EnvPath(envBinHome, filepath.Join(home, ".local", "bin"))
|
||||
|
||||
appDirs := []string{
|
||||
filepath.Join(baseDirs.dataHome, "applications"),
|
||||
filepath.Join(home, ".local/share/applications"),
|
||||
"/usr/local/share/applications",
|
||||
"/usr/share/applications",
|
||||
}
|
||||
|
||||
fontDirs := []string{
|
||||
filepath.Join(baseDirs.dataHome, "fonts"),
|
||||
filepath.Join(home, ".fonts"),
|
||||
filepath.Join(home, ".local/share/fonts"),
|
||||
"/usr/local/share/fonts",
|
||||
"/usr/share/fonts",
|
||||
}
|
||||
|
||||
for _, dir := range baseDirs.data {
|
||||
appDirs = append(appDirs, filepath.Join(dir, "applications"))
|
||||
fontDirs = append(fontDirs, filepath.Join(dir, "fonts"))
|
||||
}
|
||||
|
||||
baseDirs.applications = pathutil.Unique(appDirs)
|
||||
baseDirs.fonts = pathutil.Unique(fontDirs)
|
||||
}
|
||||
|
||||
func initUserDirs(home, configHome string) {
|
||||
dirs, err := userdirs.ParseConfigFile(filepath.Join(configHome, "user-dirs.dirs"))
|
||||
if err != nil {
|
||||
dirs = &UserDirectories{}
|
||||
}
|
||||
|
||||
UserDirs.Desktop = pathutil.EnvPath(userdirs.EnvDesktopDir, dirs.Desktop, filepath.Join(home, "Desktop"))
|
||||
UserDirs.Download = pathutil.EnvPath(userdirs.EnvDownloadDir, dirs.Download, filepath.Join(home, "Downloads"))
|
||||
UserDirs.Documents = pathutil.EnvPath(userdirs.EnvDocumentsDir, dirs.Documents, filepath.Join(home, "Documents"))
|
||||
UserDirs.Music = pathutil.EnvPath(userdirs.EnvMusicDir, dirs.Music, filepath.Join(home, "Music"))
|
||||
UserDirs.Pictures = pathutil.EnvPath(userdirs.EnvPicturesDir, dirs.Pictures, filepath.Join(home, "Pictures"))
|
||||
UserDirs.Videos = pathutil.EnvPath(userdirs.EnvVideosDir, dirs.Videos, filepath.Join(home, "Videos"))
|
||||
UserDirs.Templates = pathutil.EnvPath(userdirs.EnvTemplatesDir, dirs.Templates, filepath.Join(home, "Templates"))
|
||||
UserDirs.PublicShare = pathutil.EnvPath(userdirs.EnvPublicShareDir, dirs.PublicShare, filepath.Join(home, "Public"))
|
||||
}
|
||||
196
vendor/github.com/adrg/xdg/paths_windows.go
generated
vendored
196
vendor/github.com/adrg/xdg/paths_windows.go
generated
vendored
@@ -1,196 +0,0 @@
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
"github.com/adrg/xdg/internal/userdirs"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func initDirs(home string) {
|
||||
kf := initKnownFolders(home)
|
||||
initBaseDirs(home, kf)
|
||||
initUserDirs(home, kf)
|
||||
}
|
||||
|
||||
func initBaseDirs(home string, kf *knownFolders) {
|
||||
// Initialize standard directories.
|
||||
baseDirs.dataHome = pathutil.EnvPath(envDataHome, kf.localAppData)
|
||||
baseDirs.data = pathutil.EnvPathList(envDataDirs, kf.roamingAppData, kf.programData)
|
||||
baseDirs.configHome = pathutil.EnvPath(envConfigHome, kf.localAppData)
|
||||
baseDirs.config = pathutil.EnvPathList(envConfigDirs, kf.programData, kf.roamingAppData)
|
||||
baseDirs.stateHome = pathutil.EnvPath(envStateHome, kf.localAppData)
|
||||
baseDirs.cacheHome = pathutil.EnvPath(envCacheHome, filepath.Join(kf.localAppData, "cache"))
|
||||
baseDirs.runtime = pathutil.EnvPath(envRuntimeDir, kf.localAppData)
|
||||
|
||||
// Initialize non-standard directories.
|
||||
baseDirs.binHome = pathutil.EnvPath(envBinHome, kf.userProgramFiles)
|
||||
|
||||
baseDirs.applications = []string{
|
||||
kf.programs,
|
||||
kf.commonPrograms,
|
||||
kf.programFiles,
|
||||
kf.programFilesCommon,
|
||||
kf.userProgramFiles,
|
||||
kf.userProgramFilesCommon,
|
||||
}
|
||||
|
||||
baseDirs.fonts = []string{
|
||||
kf.fonts,
|
||||
filepath.Join(kf.localAppData, "Microsoft", "Windows", "Fonts"),
|
||||
}
|
||||
}
|
||||
|
||||
func initUserDirs(home string, kf *knownFolders) {
|
||||
UserDirs.Desktop = pathutil.EnvPath(userdirs.EnvDesktopDir, kf.desktop)
|
||||
UserDirs.Download = pathutil.EnvPath(userdirs.EnvDownloadDir, kf.downloads)
|
||||
UserDirs.Documents = pathutil.EnvPath(userdirs.EnvDocumentsDir, kf.documents)
|
||||
UserDirs.Music = pathutil.EnvPath(userdirs.EnvMusicDir, kf.music)
|
||||
UserDirs.Pictures = pathutil.EnvPath(userdirs.EnvPicturesDir, kf.pictures)
|
||||
UserDirs.Videos = pathutil.EnvPath(userdirs.EnvVideosDir, kf.videos)
|
||||
UserDirs.Templates = pathutil.EnvPath(userdirs.EnvTemplatesDir, kf.templates)
|
||||
UserDirs.PublicShare = pathutil.EnvPath(userdirs.EnvPublicShareDir, kf.public)
|
||||
}
|
||||
|
||||
type knownFolders struct {
|
||||
systemDrive string
|
||||
systemRoot string
|
||||
programData string
|
||||
userProfile string
|
||||
userProfiles string
|
||||
roamingAppData string
|
||||
localAppData string
|
||||
desktop string
|
||||
downloads string
|
||||
documents string
|
||||
music string
|
||||
pictures string
|
||||
videos string
|
||||
templates string
|
||||
public string
|
||||
fonts string
|
||||
programs string
|
||||
commonPrograms string
|
||||
programFiles string
|
||||
programFilesCommon string
|
||||
userProgramFiles string
|
||||
userProgramFilesCommon string
|
||||
}
|
||||
|
||||
func initKnownFolders(home string) *knownFolders {
|
||||
kf := &knownFolders{
|
||||
userProfile: home,
|
||||
}
|
||||
kf.systemDrive = filepath.VolumeName(pathutil.KnownFolder(
|
||||
windows.FOLDERID_Windows,
|
||||
[]string{"SystemDrive", "SystemRoot", "windir"},
|
||||
[]string{home, `C:`},
|
||||
)) + string(filepath.Separator)
|
||||
kf.systemRoot = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Windows,
|
||||
[]string{"SystemRoot", "windir"},
|
||||
[]string{filepath.Join(kf.systemDrive, "Windows")},
|
||||
)
|
||||
kf.programData = pathutil.KnownFolder(
|
||||
windows.FOLDERID_ProgramData,
|
||||
[]string{"ProgramData", "ALLUSERSPROFILE"},
|
||||
[]string{filepath.Join(kf.systemDrive, "ProgramData")},
|
||||
)
|
||||
kf.userProfiles = pathutil.KnownFolder(
|
||||
windows.FOLDERID_UserProfiles,
|
||||
nil,
|
||||
[]string{filepath.Join(kf.systemDrive, "Users")},
|
||||
)
|
||||
kf.roamingAppData = pathutil.KnownFolder(
|
||||
windows.FOLDERID_RoamingAppData,
|
||||
[]string{"APPDATA"},
|
||||
[]string{filepath.Join(home, "AppData", "Roaming")},
|
||||
)
|
||||
kf.localAppData = pathutil.KnownFolder(
|
||||
windows.FOLDERID_LocalAppData,
|
||||
[]string{"LOCALAPPDATA"},
|
||||
[]string{filepath.Join(home, "AppData", "Local")},
|
||||
)
|
||||
kf.desktop = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Desktop,
|
||||
nil,
|
||||
[]string{filepath.Join(home, "Desktop")},
|
||||
)
|
||||
kf.downloads = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Downloads,
|
||||
nil,
|
||||
[]string{filepath.Join(home, "Downloads")},
|
||||
)
|
||||
kf.documents = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Documents,
|
||||
nil,
|
||||
[]string{filepath.Join(home, "Documents")},
|
||||
)
|
||||
kf.music = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Music,
|
||||
nil,
|
||||
[]string{filepath.Join(home, "Music")},
|
||||
)
|
||||
kf.pictures = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Pictures,
|
||||
nil,
|
||||
[]string{filepath.Join(home, "Pictures")},
|
||||
)
|
||||
kf.videos = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Videos,
|
||||
nil,
|
||||
[]string{filepath.Join(home, "Videos")},
|
||||
)
|
||||
kf.templates = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Templates,
|
||||
nil,
|
||||
[]string{filepath.Join(kf.roamingAppData, "Microsoft", "Windows", "Templates")},
|
||||
)
|
||||
kf.public = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Public,
|
||||
[]string{"PUBLIC"},
|
||||
[]string{filepath.Join(kf.userProfiles, "Public")},
|
||||
)
|
||||
kf.fonts = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Fonts,
|
||||
nil,
|
||||
[]string{filepath.Join(kf.systemRoot, "Fonts")},
|
||||
)
|
||||
kf.programs = pathutil.KnownFolder(
|
||||
windows.FOLDERID_Programs,
|
||||
nil,
|
||||
[]string{filepath.Join(kf.roamingAppData, "Microsoft", "Windows", "Start Menu", "Programs")},
|
||||
)
|
||||
kf.commonPrograms = pathutil.KnownFolder(
|
||||
windows.FOLDERID_CommonPrograms,
|
||||
nil,
|
||||
[]string{filepath.Join(kf.programData, "Microsoft", "Windows", "Start Menu", "Programs")},
|
||||
)
|
||||
kf.programFiles = pathutil.KnownFolder(
|
||||
windows.FOLDERID_ProgramFiles,
|
||||
[]string{"ProgramFiles"},
|
||||
[]string{filepath.Join(kf.systemDrive, "Program Files")},
|
||||
)
|
||||
kf.programFilesCommon = pathutil.KnownFolder(
|
||||
windows.FOLDERID_ProgramFilesCommon,
|
||||
nil,
|
||||
[]string{filepath.Join(kf.programFiles, "Common Files")},
|
||||
)
|
||||
kf.userProgramFiles = pathutil.KnownFolder(
|
||||
windows.FOLDERID_UserProgramFiles,
|
||||
nil,
|
||||
[]string{
|
||||
filepath.Join(kf.localAppData, "Programs"),
|
||||
},
|
||||
)
|
||||
kf.userProgramFilesCommon = pathutil.KnownFolder(
|
||||
windows.FOLDERID_UserProgramFilesCommon,
|
||||
nil,
|
||||
[]string{
|
||||
filepath.Join(kf.userProgramFiles, "Common"),
|
||||
},
|
||||
)
|
||||
|
||||
return kf
|
||||
}
|
||||
212
vendor/github.com/adrg/xdg/xdg.go
generated
vendored
212
vendor/github.com/adrg/xdg/xdg.go
generated
vendored
@@ -1,212 +0,0 @@
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"github.com/adrg/xdg/internal/pathutil"
|
||||
"github.com/adrg/xdg/internal/userdirs"
|
||||
)
|
||||
|
||||
// UserDirectories defines the locations of well known user directories.
|
||||
type UserDirectories = userdirs.Directories
|
||||
|
||||
var (
|
||||
// Home contains the path of the user's home directory.
|
||||
Home string
|
||||
|
||||
// DataHome defines the base directory relative to which user-specific
|
||||
// data files should be stored. This directory is defined by the
|
||||
// $XDG_DATA_HOME environment variable. If the variable is not set,
|
||||
// a default equal to $HOME/.local/share should be used.
|
||||
DataHome string
|
||||
|
||||
// DataDirs defines the preference-ordered set of base directories to
|
||||
// search for data files in addition to the DataHome base directory.
|
||||
// This set of directories is defined by the $XDG_DATA_DIRS environment
|
||||
// variable. If the variable is not set, the default directories
|
||||
// to be used are /usr/local/share and /usr/share, in that order. The
|
||||
// DataHome directory is considered more important than any of the
|
||||
// directories defined by DataDirs. Therefore, user data files should be
|
||||
// written relative to the DataHome directory, if possible.
|
||||
DataDirs []string
|
||||
|
||||
// ConfigHome defines the base directory relative to which user-specific
|
||||
// configuration files should be written. This directory is defined by
|
||||
// the $XDG_CONFIG_HOME environment variable. If the variable is
|
||||
// not set, a default equal to $HOME/.config should be used.
|
||||
ConfigHome string
|
||||
|
||||
// ConfigDirs defines the preference-ordered set of base directories to
|
||||
// search for configuration files in addition to the ConfigHome base
|
||||
// directory. This set of directories is defined by the $XDG_CONFIG_DIRS
|
||||
// environment variable. If the variable is not set, a default equal
|
||||
// to /etc/xdg should be used. The ConfigHome directory is considered
|
||||
// more important than any of the directories defined by ConfigDirs.
|
||||
// Therefore, user config files should be written relative to the
|
||||
// ConfigHome directory, if possible.
|
||||
ConfigDirs []string
|
||||
|
||||
// StateHome defines the base directory relative to which user-specific
|
||||
// state files should be stored. This directory is defined by the
|
||||
// $XDG_STATE_HOME environment variable. If the variable is not set,
|
||||
// a default equal to ~/.local/state should be used.
|
||||
StateHome string
|
||||
|
||||
// CacheHome defines the base directory relative to which user-specific
|
||||
// non-essential (cached) data should be written. This directory is
|
||||
// defined by the $XDG_CACHE_HOME environment variable. If the variable
|
||||
// is not set, a default equal to $HOME/.cache should be used.
|
||||
CacheHome string
|
||||
|
||||
// RuntimeDir defines the base directory relative to which user-specific
|
||||
// non-essential runtime files and other file objects (such as sockets,
|
||||
// named pipes, etc.) should be stored. This directory is defined by the
|
||||
// $XDG_RUNTIME_DIR environment variable. If the variable is not set,
|
||||
// applications should fall back to a replacement directory with similar
|
||||
// capabilities. Applications should use this directory for communication
|
||||
// and synchronization purposes and should not place larger files in it,
|
||||
// since it might reside in runtime memory and cannot necessarily be
|
||||
// swapped out to disk.
|
||||
RuntimeDir string
|
||||
|
||||
// BinHome defines the base directory relative to which user-specific
|
||||
// binary files should be written. This directory is defined by
|
||||
// the non-standard $XDG_BIN_HOME environment variable. If the variable is
|
||||
// not set, a default equal to $HOME/.local/bin should be used.
|
||||
BinHome string
|
||||
|
||||
// UserDirs defines the locations of well known user directories.
|
||||
UserDirs UserDirectories
|
||||
|
||||
// FontDirs defines the common locations where font files are stored.
|
||||
FontDirs []string
|
||||
|
||||
// ApplicationDirs defines the common locations of applications.
|
||||
ApplicationDirs []string
|
||||
|
||||
// baseDirs defines the locations of base directories.
|
||||
baseDirs baseDirectories
|
||||
)
|
||||
|
||||
func init() {
|
||||
Reload()
|
||||
}
|
||||
|
||||
// Reload refreshes base and user directories by reading the environment.
|
||||
// Defaults are applied for XDG variables which are empty or not present
|
||||
// in the environment.
|
||||
func Reload() {
|
||||
// Initialize home directory.
|
||||
Home = pathutil.UserHomeDir()
|
||||
|
||||
// Initialize base and user directories.
|
||||
initDirs(Home)
|
||||
|
||||
// Set standard directories.
|
||||
DataHome = baseDirs.dataHome
|
||||
DataDirs = baseDirs.data
|
||||
ConfigHome = baseDirs.configHome
|
||||
ConfigDirs = baseDirs.config
|
||||
StateHome = baseDirs.stateHome
|
||||
CacheHome = baseDirs.cacheHome
|
||||
RuntimeDir = baseDirs.runtime
|
||||
|
||||
// Set non-standard directories.
|
||||
BinHome = baseDirs.binHome
|
||||
FontDirs = baseDirs.fonts
|
||||
ApplicationDirs = baseDirs.applications
|
||||
}
|
||||
|
||||
// DataFile returns a suitable location for the specified data file.
|
||||
// The relPath parameter must contain the name of the data file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.data).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base data directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func DataFile(relPath string) (string, error) {
|
||||
return baseDirs.dataFile(relPath)
|
||||
}
|
||||
|
||||
// ConfigFile returns a suitable location for the specified config file.
|
||||
// The relPath parameter must contain the name of the config file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.yaml).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base config directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func ConfigFile(relPath string) (string, error) {
|
||||
return baseDirs.configFile(relPath)
|
||||
}
|
||||
|
||||
// StateFile returns a suitable location for the specified state file. State
|
||||
// files are usually volatile data files, not suitable to be stored relative
|
||||
// to the $XDG_DATA_HOME directory.
|
||||
// The relPath parameter must contain the name of the state file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.state).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base state directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func StateFile(relPath string) (string, error) {
|
||||
return baseDirs.stateFile(relPath)
|
||||
}
|
||||
|
||||
// CacheFile returns a suitable location for the specified cache file.
|
||||
// The relPath parameter must contain the name of the cache file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.cache).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base cache directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func CacheFile(relPath string) (string, error) {
|
||||
return baseDirs.cacheFile(relPath)
|
||||
}
|
||||
|
||||
// RuntimeFile returns a suitable location for the specified runtime file.
|
||||
// The relPath parameter must contain the name of the runtime file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.pid).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base runtime directory. If the base runtime directory does not exist,
|
||||
// the operating system's temporary directory is used as a fallback. On failure,
|
||||
// an error containing the attempted paths is returned.
|
||||
func RuntimeFile(relPath string) (string, error) {
|
||||
return baseDirs.runtimeFile(relPath)
|
||||
}
|
||||
|
||||
// SearchDataFile searches for specified file in the data search paths.
|
||||
// The relPath parameter must contain the name of the data file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.data). If the
|
||||
// file cannot be found, an error specifying the searched paths is returned.
|
||||
func SearchDataFile(relPath string) (string, error) {
|
||||
return baseDirs.searchDataFile(relPath)
|
||||
}
|
||||
|
||||
// SearchConfigFile searches for the specified file in config search paths.
|
||||
// The relPath parameter must contain the name of the config file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.yaml). If the
|
||||
// file cannot be found, an error specifying the searched paths is returned.
|
||||
func SearchConfigFile(relPath string) (string, error) {
|
||||
return baseDirs.searchConfigFile(relPath)
|
||||
}
|
||||
|
||||
// SearchStateFile searches for the specified file in the state search path.
|
||||
// The relPath parameter must contain the name of the state file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.state). If the
|
||||
// file cannot be found, an error specifying the searched path is returned.
|
||||
func SearchStateFile(relPath string) (string, error) {
|
||||
return baseDirs.searchStateFile(relPath)
|
||||
}
|
||||
|
||||
// SearchCacheFile searches for the specified file in the cache search path.
|
||||
// The relPath parameter must contain the name of the cache file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.cache). If the
|
||||
// file cannot be found, an error specifying the searched path is returned.
|
||||
func SearchCacheFile(relPath string) (string, error) {
|
||||
return baseDirs.searchCacheFile(relPath)
|
||||
}
|
||||
|
||||
// SearchRuntimeFile searches for the specified file in the runtime search path.
|
||||
// The relPath parameter must contain the name of the runtime file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.pid). The runtime
|
||||
// file is also searched in the operating system's temporary directory in order
|
||||
// to cover cases in which the runtime base directory does not exist or is not
|
||||
// accessible. If the file cannot be found, an error specifying the searched
|
||||
// paths is returned.
|
||||
func SearchRuntimeFile(relPath string) (string, error) {
|
||||
return baseDirs.searchRuntimeFile(relPath)
|
||||
}
|
||||
13
vendor/github.com/coder/websocket/LICENSE.txt
generated
vendored
13
vendor/github.com/coder/websocket/LICENSE.txt
generated
vendored
@@ -1,13 +0,0 @@
|
||||
Copyright (c) 2025 Coder
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
18
vendor/github.com/coder/websocket/Makefile
generated
vendored
18
vendor/github.com/coder/websocket/Makefile
generated
vendored
@@ -1,18 +0,0 @@
|
||||
.PHONY: all
|
||||
all: fmt lint test
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
./ci/fmt.sh
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
./ci/lint.sh
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
./ci/test.sh
|
||||
|
||||
.PHONY: bench
|
||||
bench:
|
||||
./ci/bench.sh
|
||||
162
vendor/github.com/coder/websocket/README.md
generated
vendored
162
vendor/github.com/coder/websocket/README.md
generated
vendored
@@ -1,162 +0,0 @@
|
||||
# websocket
|
||||
|
||||
[](https://pkg.go.dev/github.com/coder/websocket)
|
||||
[](https://coder.github.io/websocket/coverage.html)
|
||||
|
||||
websocket is a minimal and idiomatic WebSocket library for Go.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get github.com/coder/websocket
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Coder now maintains this project as explained in [this blog post](https://coder.com/blog/websocket).
|
||||
> We're grateful to [nhooyr](https://github.com/nhooyr) for authoring and maintaining this project from
|
||||
> 2019 to 2024.
|
||||
|
||||
## Highlights
|
||||
|
||||
- Minimal and idiomatic API
|
||||
- First class [context.Context](https://blog.golang.org/context) support
|
||||
- Fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
|
||||
- [Zero dependencies](https://pkg.go.dev/github.com/coder/websocket?tab=imports)
|
||||
- JSON helpers in the [wsjson](https://pkg.go.dev/github.com/coder/websocket/wsjson) subpackage
|
||||
- Zero alloc reads and writes
|
||||
- Concurrent writes
|
||||
- [Close handshake](https://pkg.go.dev/github.com/coder/websocket#Conn.Close)
|
||||
- [net.Conn](https://pkg.go.dev/github.com/coder/websocket#NetConn) wrapper
|
||||
- [Ping pong](https://pkg.go.dev/github.com/coder/websocket#Conn.Ping) API
|
||||
- [RFC 7692](https://tools.ietf.org/html/rfc7692) permessage-deflate compression
|
||||
- [CloseRead](https://pkg.go.dev/github.com/coder/websocket#Conn.CloseRead) helper for write only connections
|
||||
- Compile to [Wasm](https://pkg.go.dev/github.com/coder/websocket#hdr-Wasm)
|
||||
|
||||
## Roadmap
|
||||
|
||||
See GitHub issues for minor issues but the major future enhancements are:
|
||||
|
||||
- [ ] Perfect examples [#217](https://github.com/nhooyr/websocket/issues/217)
|
||||
- [ ] wstest.Pipe for in memory testing [#340](https://github.com/nhooyr/websocket/issues/340)
|
||||
- [ ] Ping pong heartbeat helper [#267](https://github.com/nhooyr/websocket/issues/267)
|
||||
- [ ] Ping pong instrumentation callbacks [#246](https://github.com/nhooyr/websocket/issues/246)
|
||||
- [ ] Graceful shutdown helpers [#209](https://github.com/nhooyr/websocket/issues/209)
|
||||
- [ ] Assembly for WebSocket masking [#16](https://github.com/nhooyr/websocket/issues/16)
|
||||
- WIP at [#326](https://github.com/nhooyr/websocket/pull/326), about 3x faster
|
||||
- [ ] HTTP/2 [#4](https://github.com/nhooyr/websocket/issues/4)
|
||||
- [ ] The holy grail [#402](https://github.com/nhooyr/websocket/issues/402)
|
||||
|
||||
## Examples
|
||||
|
||||
For a production quality example that demonstrates the complete API, see the
|
||||
[echo example](./internal/examples/echo).
|
||||
|
||||
For a full stack example, see the [chat example](./internal/examples/chat).
|
||||
|
||||
### Server
|
||||
|
||||
```go
|
||||
http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
||||
c, err := websocket.Accept(w, r, nil)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
defer c.CloseNow()
|
||||
|
||||
// Set the context as needed. Use of r.Context() is not recommended
|
||||
// to avoid surprising behavior (see http.Hijacker).
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
var v any
|
||||
err = wsjson.Read(ctx, c, &v)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
log.Printf("received: %v", v)
|
||||
|
||||
c.Close(websocket.StatusNormalClosure, "")
|
||||
})
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
defer c.CloseNow()
|
||||
|
||||
err = wsjson.Write(ctx, c, "hi")
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
c.Close(websocket.StatusNormalClosure, "")
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
### gorilla/websocket
|
||||
|
||||
Advantages of [gorilla/websocket](https://github.com/gorilla/websocket):
|
||||
|
||||
- Mature and widely used
|
||||
- [Prepared writes](https://pkg.go.dev/github.com/gorilla/websocket#PreparedMessage)
|
||||
- Configurable [buffer sizes](https://pkg.go.dev/github.com/gorilla/websocket#hdr-Buffers)
|
||||
- No extra goroutine per connection to support cancellation with context.Context. This costs github.com/coder/websocket 2 KB of memory per connection.
|
||||
- Will be removed soon with [context.AfterFunc](https://github.com/golang/go/issues/57928). See [#411](https://github.com/nhooyr/websocket/issues/411)
|
||||
|
||||
Advantages of github.com/coder/websocket:
|
||||
|
||||
- Minimal and idiomatic API
|
||||
- Compare godoc of [github.com/coder/websocket](https://pkg.go.dev/github.com/coder/websocket) with [gorilla/websocket](https://pkg.go.dev/github.com/gorilla/websocket) side by side.
|
||||
- [net.Conn](https://pkg.go.dev/github.com/coder/websocket#NetConn) wrapper
|
||||
- Zero alloc reads and writes ([gorilla/websocket#535](https://github.com/gorilla/websocket/issues/535))
|
||||
- Full [context.Context](https://blog.golang.org/context) support
|
||||
- Dial uses [net/http.Client](https://golang.org/pkg/net/http/#Client)
|
||||
- Will enable easy HTTP/2 support in the future
|
||||
- Gorilla writes directly to a net.Conn and so duplicates features of net/http.Client.
|
||||
- Concurrent writes
|
||||
- Close handshake ([gorilla/websocket#448](https://github.com/gorilla/websocket/issues/448))
|
||||
- Idiomatic [ping pong](https://pkg.go.dev/github.com/coder/websocket#Conn.Ping) API
|
||||
- Gorilla requires registering a pong callback before sending a Ping
|
||||
- Can target Wasm ([gorilla/websocket#432](https://github.com/gorilla/websocket/issues/432))
|
||||
- Transparent message buffer reuse with [wsjson](https://pkg.go.dev/github.com/coder/websocket/wsjson) subpackage
|
||||
- [1.75x](https://github.com/nhooyr/websocket/releases/tag/v1.7.4) faster WebSocket masking implementation in pure Go
|
||||
- Gorilla's implementation is slower and uses [unsafe](https://golang.org/pkg/unsafe/).
|
||||
Soon we'll have assembly and be 3x faster [#326](https://github.com/nhooyr/websocket/pull/326)
|
||||
- Full [permessage-deflate](https://tools.ietf.org/html/rfc7692) compression extension support
|
||||
- Gorilla only supports no context takeover mode
|
||||
- [CloseRead](https://pkg.go.dev/github.com/coder/websocket#Conn.CloseRead) helper for write only connections ([gorilla/websocket#492](https://github.com/gorilla/websocket/issues/492))
|
||||
|
||||
#### golang.org/x/net/websocket
|
||||
|
||||
[golang.org/x/net/websocket](https://pkg.go.dev/golang.org/x/net/websocket) is deprecated.
|
||||
See [golang/go/issues/18152](https://github.com/golang/go/issues/18152).
|
||||
|
||||
The [net.Conn](https://pkg.go.dev/github.com/coder/websocket#NetConn) can help in transitioning
|
||||
to github.com/coder/websocket.
|
||||
|
||||
#### gobwas/ws
|
||||
|
||||
[gobwas/ws](https://github.com/gobwas/ws) has an extremely flexible API that allows it to be used
|
||||
in an event driven style for performance. See the author's [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb).
|
||||
|
||||
However it is quite bloated. See https://pkg.go.dev/github.com/gobwas/ws
|
||||
|
||||
When writing idiomatic Go, github.com/coder/websocket will be faster and easier to use.
|
||||
|
||||
#### lesismal/nbio
|
||||
|
||||
[lesismal/nbio](https://github.com/lesismal/nbio) is similar to gobwas/ws in that the API is
|
||||
event driven for performance reasons.
|
||||
|
||||
However it is quite bloated. See https://pkg.go.dev/github.com/lesismal/nbio
|
||||
|
||||
When writing idiomatic Go, github.com/coder/websocket will be faster and easier to use.
|
||||
378
vendor/github.com/coder/websocket/accept.go
generated
vendored
378
vendor/github.com/coder/websocket/accept.go
generated
vendored
@@ -1,378 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
)
|
||||
|
||||
// AcceptOptions represents Accept's options.
|
||||
type AcceptOptions struct {
|
||||
// Subprotocols lists the WebSocket subprotocols that Accept will negotiate with the client.
|
||||
// The empty subprotocol will always be negotiated as per RFC 6455. If you would like to
|
||||
// reject it, close the connection when c.Subprotocol() == "".
|
||||
Subprotocols []string
|
||||
|
||||
// InsecureSkipVerify is used to disable Accept's origin verification behaviour.
|
||||
//
|
||||
// You probably want to use OriginPatterns instead.
|
||||
InsecureSkipVerify bool
|
||||
|
||||
// OriginPatterns lists the host patterns for authorized origins.
|
||||
// The request host is always authorized.
|
||||
// Use this to enable cross origin WebSockets.
|
||||
//
|
||||
// i.e javascript running on example.com wants to access a WebSocket server at chat.example.com.
|
||||
// In such a case, example.com is the origin and chat.example.com is the request host.
|
||||
// One would set this field to []string{"example.com"} to authorize example.com to connect.
|
||||
//
|
||||
// Each pattern is matched case insensitively with path.Match (see
|
||||
// https://golang.org/pkg/path/#Match). By default, it is matched
|
||||
// against the request origin host. If the pattern contains a URI
|
||||
// scheme ("://"), it will be matched against "scheme://host".
|
||||
//
|
||||
// Please ensure you understand the ramifications of enabling this.
|
||||
// If used incorrectly your WebSocket server will be open to CSRF attacks.
|
||||
//
|
||||
// Do not use * as a pattern to allow any origin, prefer to use InsecureSkipVerify instead
|
||||
// to bring attention to the danger of such a setting.
|
||||
OriginPatterns []string
|
||||
|
||||
// CompressionMode controls the compression mode.
|
||||
// Defaults to CompressionDisabled.
|
||||
//
|
||||
// See docs on CompressionMode for details.
|
||||
CompressionMode CompressionMode
|
||||
|
||||
// CompressionThreshold controls the minimum size of a message before compression is applied.
|
||||
//
|
||||
// Defaults to 512 bytes for CompressionNoContextTakeover and 128 bytes
|
||||
// for CompressionContextTakeover.
|
||||
CompressionThreshold int
|
||||
|
||||
// OnPingReceived is an optional callback invoked synchronously when a ping frame is received.
|
||||
//
|
||||
// The payload contains the application data of the ping frame.
|
||||
// If the callback returns false, the subsequent pong frame will not be sent.
|
||||
// To avoid blocking, any expensive processing should be performed asynchronously using a goroutine.
|
||||
OnPingReceived func(ctx context.Context, payload []byte) bool
|
||||
|
||||
// OnPongReceived is an optional callback invoked synchronously when a pong frame is received.
|
||||
//
|
||||
// The payload contains the application data of the pong frame.
|
||||
// To avoid blocking, any expensive processing should be performed asynchronously using a goroutine.
|
||||
//
|
||||
// Unlike OnPingReceived, this callback does not return a value because a pong frame
|
||||
// is a response to a ping and does not trigger any further frame transmission.
|
||||
OnPongReceived func(ctx context.Context, payload []byte)
|
||||
}
|
||||
|
||||
func (opts *AcceptOptions) cloneWithDefaults() *AcceptOptions {
|
||||
var o AcceptOptions
|
||||
if opts != nil {
|
||||
o = *opts
|
||||
}
|
||||
return &o
|
||||
}
|
||||
|
||||
// Accept accepts a WebSocket handshake from a client and upgrades the
|
||||
// the connection to a WebSocket.
|
||||
//
|
||||
// Accept will not allow cross origin requests by default.
|
||||
// See the InsecureSkipVerify and OriginPatterns options to allow cross origin requests.
|
||||
//
|
||||
// Accept will write a response to w on all errors.
|
||||
//
|
||||
// Note that using the http.Request Context after Accept returns may lead to
|
||||
// unexpected behavior (see http.Hijacker).
|
||||
func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) {
|
||||
return accept(w, r, opts)
|
||||
}
|
||||
|
||||
func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Conn, err error) {
|
||||
defer errd.Wrap(&err, "failed to accept WebSocket connection")
|
||||
|
||||
errCode, err := verifyClientRequest(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), errCode)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts = opts.cloneWithDefaults()
|
||||
if !opts.InsecureSkipVerify {
|
||||
err = authenticateOrigin(r, opts.OriginPatterns)
|
||||
if err != nil {
|
||||
if errors.Is(err, path.ErrBadPattern) {
|
||||
log.Printf("websocket: %v", err)
|
||||
err = errors.New(http.StatusText(http.StatusForbidden))
|
||||
}
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
hj, ok := hijacker(w)
|
||||
if !ok {
|
||||
err = errors.New("http.ResponseWriter does not implement http.Hijacker")
|
||||
http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.Header().Set("Upgrade", "websocket")
|
||||
w.Header().Set("Connection", "Upgrade")
|
||||
|
||||
key := r.Header.Get("Sec-WebSocket-Key")
|
||||
w.Header().Set("Sec-WebSocket-Accept", secWebSocketAccept(key))
|
||||
|
||||
subproto := selectSubprotocol(r, opts.Subprotocols)
|
||||
if subproto != "" {
|
||||
w.Header().Set("Sec-WebSocket-Protocol", subproto)
|
||||
}
|
||||
|
||||
copts, ok := selectDeflate(websocketExtensions(r.Header), opts.CompressionMode)
|
||||
if ok {
|
||||
w.Header().Set("Sec-WebSocket-Extensions", copts.String())
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusSwitchingProtocols)
|
||||
// See https://github.com/nhooyr/websocket/issues/166
|
||||
if ginWriter, ok := w.(interface {
|
||||
WriteHeaderNow()
|
||||
}); ok {
|
||||
ginWriter.WriteHeaderNow()
|
||||
}
|
||||
|
||||
netConn, brw, err := hj.Hijack()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to hijack connection: %w", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// https://github.com/golang/go/issues/32314
|
||||
b, _ := brw.Reader.Peek(brw.Reader.Buffered())
|
||||
brw.Reader.Reset(io.MultiReader(bytes.NewReader(b), netConn))
|
||||
|
||||
return newConn(connConfig{
|
||||
subprotocol: w.Header().Get("Sec-WebSocket-Protocol"),
|
||||
rwc: netConn,
|
||||
client: false,
|
||||
copts: copts,
|
||||
flateThreshold: opts.CompressionThreshold,
|
||||
onPingReceived: opts.OnPingReceived,
|
||||
onPongReceived: opts.OnPongReceived,
|
||||
|
||||
br: brw.Reader,
|
||||
bw: brw.Writer,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func verifyClientRequest(w http.ResponseWriter, r *http.Request) (errCode int, _ error) {
|
||||
if !r.ProtoAtLeast(1, 1) {
|
||||
return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: handshake request must be at least HTTP/1.1: %q", r.Proto)
|
||||
}
|
||||
|
||||
if !headerContainsTokenIgnoreCase(r.Header, "Connection", "Upgrade") {
|
||||
w.Header().Set("Connection", "Upgrade")
|
||||
w.Header().Set("Upgrade", "websocket")
|
||||
return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection"))
|
||||
}
|
||||
|
||||
if !headerContainsTokenIgnoreCase(r.Header, "Upgrade", "websocket") {
|
||||
w.Header().Set("Connection", "Upgrade")
|
||||
w.Header().Set("Upgrade", "websocket")
|
||||
return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade"))
|
||||
}
|
||||
|
||||
if r.Method != "GET" {
|
||||
return http.StatusMethodNotAllowed, fmt.Errorf("WebSocket protocol violation: handshake request method is not GET but %q", r.Method)
|
||||
}
|
||||
|
||||
if r.Header.Get("Sec-WebSocket-Version") != "13" {
|
||||
w.Header().Set("Sec-WebSocket-Version", "13")
|
||||
return http.StatusBadRequest, fmt.Errorf("unsupported WebSocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version"))
|
||||
}
|
||||
|
||||
websocketSecKeys := r.Header.Values("Sec-WebSocket-Key")
|
||||
if len(websocketSecKeys) == 0 {
|
||||
return http.StatusBadRequest, errors.New("WebSocket protocol violation: missing Sec-WebSocket-Key")
|
||||
}
|
||||
|
||||
if len(websocketSecKeys) > 1 {
|
||||
return http.StatusBadRequest, errors.New("WebSocket protocol violation: multiple Sec-WebSocket-Key headers")
|
||||
}
|
||||
|
||||
// The RFC states to remove any leading or trailing whitespace.
|
||||
websocketSecKey := strings.TrimSpace(websocketSecKeys[0])
|
||||
if v, err := base64.StdEncoding.DecodeString(websocketSecKey); err != nil || len(v) != 16 {
|
||||
return http.StatusBadRequest, fmt.Errorf("WebSocket protocol violation: invalid Sec-WebSocket-Key %q, must be a 16 byte base64 encoded string", websocketSecKey)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func authenticateOrigin(r *http.Request, originHosts []string) error {
|
||||
origin := r.Header.Get("Origin")
|
||||
if origin == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(origin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse Origin header %q: %w", origin, err)
|
||||
}
|
||||
|
||||
if strings.EqualFold(r.Host, u.Host) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, hostPattern := range originHosts {
|
||||
target := u.Host
|
||||
if strings.Contains(hostPattern, "://") {
|
||||
target = u.Scheme + "://" + u.Host
|
||||
}
|
||||
matched, err := match(hostPattern, target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse path pattern %q: %w", hostPattern, err)
|
||||
}
|
||||
if matched {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if u.Host == "" {
|
||||
return fmt.Errorf("request Origin %q is not a valid URL with a host", origin)
|
||||
}
|
||||
return fmt.Errorf("request Origin %q is not authorized for Host %q", u.Host, r.Host)
|
||||
}
|
||||
|
||||
func match(pattern, s string) (bool, error) {
|
||||
return path.Match(strings.ToLower(pattern), strings.ToLower(s))
|
||||
}
|
||||
|
||||
func selectSubprotocol(r *http.Request, subprotocols []string) string {
|
||||
cps := headerTokens(r.Header, "Sec-WebSocket-Protocol")
|
||||
for _, sp := range subprotocols {
|
||||
for _, cp := range cps {
|
||||
if strings.EqualFold(sp, cp) {
|
||||
return cp
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func selectDeflate(extensions []websocketExtension, mode CompressionMode) (*compressionOptions, bool) {
|
||||
if mode == CompressionDisabled {
|
||||
return nil, false
|
||||
}
|
||||
for _, ext := range extensions {
|
||||
switch ext.name {
|
||||
// We used to implement x-webkit-deflate-frame too for Safari but Safari has bugs...
|
||||
// See https://github.com/nhooyr/websocket/issues/218
|
||||
case "permessage-deflate":
|
||||
copts, ok := acceptDeflate(ext, mode)
|
||||
if ok {
|
||||
return copts, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func acceptDeflate(ext websocketExtension, mode CompressionMode) (*compressionOptions, bool) {
|
||||
copts := mode.opts()
|
||||
for _, p := range ext.params {
|
||||
switch p {
|
||||
case "client_no_context_takeover":
|
||||
copts.clientNoContextTakeover = true
|
||||
continue
|
||||
case "server_no_context_takeover":
|
||||
copts.serverNoContextTakeover = true
|
||||
continue
|
||||
case "client_max_window_bits",
|
||||
"server_max_window_bits=15":
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(p, "client_max_window_bits=") {
|
||||
// We can't adjust the deflate window, but decoding with a larger window is acceptable.
|
||||
continue
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
return copts, true
|
||||
}
|
||||
|
||||
func headerContainsTokenIgnoreCase(h http.Header, key, token string) bool {
|
||||
for _, t := range headerTokens(h, key) {
|
||||
if strings.EqualFold(t, token) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type websocketExtension struct {
|
||||
name string
|
||||
params []string
|
||||
}
|
||||
|
||||
func websocketExtensions(h http.Header) []websocketExtension {
|
||||
var exts []websocketExtension
|
||||
extStrs := headerTokens(h, "Sec-WebSocket-Extensions")
|
||||
for _, extStr := range extStrs {
|
||||
if extStr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
vals := strings.Split(extStr, ";")
|
||||
for i := range vals {
|
||||
vals[i] = strings.TrimSpace(vals[i])
|
||||
}
|
||||
|
||||
e := websocketExtension{
|
||||
name: vals[0],
|
||||
params: vals[1:],
|
||||
}
|
||||
|
||||
exts = append(exts, e)
|
||||
}
|
||||
return exts
|
||||
}
|
||||
|
||||
func headerTokens(h http.Header, key string) []string {
|
||||
key = textproto.CanonicalMIMEHeaderKey(key)
|
||||
var tokens []string
|
||||
for _, v := range h[key] {
|
||||
v = strings.TrimSpace(v)
|
||||
for _, t := range strings.Split(v, ",") {
|
||||
t = strings.TrimSpace(t)
|
||||
tokens = append(tokens, t)
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
|
||||
func secWebSocketAccept(secWebSocketKey string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(secWebSocketKey))
|
||||
h.Write(keyGUID)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
335
vendor/github.com/coder/websocket/close.go
generated
vendored
335
vendor/github.com/coder/websocket/close.go
generated
vendored
@@ -1,335 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
)
|
||||
|
||||
// StatusCode represents a WebSocket status code.
|
||||
// https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
type StatusCode int
|
||||
|
||||
// https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
|
||||
//
|
||||
// These are only the status codes defined by the protocol.
|
||||
//
|
||||
// You can define custom codes in the 3000-4999 range.
|
||||
// The 3000-3999 range is reserved for use by libraries, frameworks and applications.
|
||||
// The 4000-4999 range is reserved for private use.
|
||||
const (
|
||||
StatusNormalClosure StatusCode = 1000
|
||||
StatusGoingAway StatusCode = 1001
|
||||
StatusProtocolError StatusCode = 1002
|
||||
StatusUnsupportedData StatusCode = 1003
|
||||
|
||||
// 1004 is reserved and so unexported.
|
||||
statusReserved StatusCode = 1004
|
||||
|
||||
// StatusNoStatusRcvd cannot be sent in a close message.
|
||||
// It is reserved for when a close message is received without
|
||||
// a status code.
|
||||
StatusNoStatusRcvd StatusCode = 1005
|
||||
|
||||
// StatusAbnormalClosure is exported for use only with Wasm.
|
||||
// In non Wasm Go, the returned error will indicate whether the
|
||||
// connection was closed abnormally.
|
||||
StatusAbnormalClosure StatusCode = 1006
|
||||
|
||||
StatusInvalidFramePayloadData StatusCode = 1007
|
||||
StatusPolicyViolation StatusCode = 1008
|
||||
StatusMessageTooBig StatusCode = 1009
|
||||
StatusMandatoryExtension StatusCode = 1010
|
||||
StatusInternalError StatusCode = 1011
|
||||
StatusServiceRestart StatusCode = 1012
|
||||
StatusTryAgainLater StatusCode = 1013
|
||||
StatusBadGateway StatusCode = 1014
|
||||
|
||||
// StatusTLSHandshake is only exported for use with Wasm.
|
||||
// In non Wasm Go, the returned error will indicate whether there was
|
||||
// a TLS handshake failure.
|
||||
StatusTLSHandshake StatusCode = 1015
|
||||
)
|
||||
|
||||
// CloseError is returned when the connection is closed with a status and reason.
|
||||
//
|
||||
// Use Go 1.13's errors.As to check for this error.
|
||||
// Also see the CloseStatus helper.
|
||||
type CloseError struct {
|
||||
Code StatusCode
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (ce CloseError) Error() string {
|
||||
return fmt.Sprintf("status = %v and reason = %q", ce.Code, ce.Reason)
|
||||
}
|
||||
|
||||
// CloseStatus is a convenience wrapper around Go 1.13's errors.As to grab
|
||||
// the status code from a CloseError.
|
||||
//
|
||||
// -1 will be returned if the passed error is nil or not a CloseError.
|
||||
func CloseStatus(err error) StatusCode {
|
||||
var ce CloseError
|
||||
if errors.As(err, &ce) {
|
||||
return ce.Code
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Close performs the WebSocket close handshake with the given status code and reason.
|
||||
//
|
||||
// It will write a WebSocket close frame with a timeout of 5s and then wait 5s for
|
||||
// the peer to send a close frame.
|
||||
// All data messages received from the peer during the close handshake will be discarded.
|
||||
//
|
||||
// The connection can only be closed once. Additional calls to Close
|
||||
// are no-ops.
|
||||
//
|
||||
// The maximum length of reason must be 125 bytes. Avoid sending a dynamic reason.
|
||||
//
|
||||
// Close will unblock all goroutines interacting with the connection once
|
||||
// complete.
|
||||
func (c *Conn) Close(code StatusCode, reason string) (err error) {
|
||||
defer errd.Wrap(&err, "failed to close WebSocket")
|
||||
|
||||
if c.casClosing() {
|
||||
err = c.waitGoroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return net.ErrClosed
|
||||
}
|
||||
defer func() {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
err = nil
|
||||
}
|
||||
}()
|
||||
|
||||
err = c.closeHandshake(code, reason)
|
||||
|
||||
err2 := c.close()
|
||||
if err == nil && err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
|
||||
err2 = c.waitGoroutines()
|
||||
if err == nil && err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CloseNow closes the WebSocket connection without attempting a close handshake.
|
||||
// Use when you do not want the overhead of the close handshake.
|
||||
func (c *Conn) CloseNow() (err error) {
|
||||
defer errd.Wrap(&err, "failed to immediately close WebSocket")
|
||||
|
||||
if c.casClosing() {
|
||||
err = c.waitGoroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return net.ErrClosed
|
||||
}
|
||||
defer func() {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
err = nil
|
||||
}
|
||||
}()
|
||||
|
||||
err = c.close()
|
||||
|
||||
err2 := c.waitGoroutines()
|
||||
if err == nil && err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) closeHandshake(code StatusCode, reason string) error {
|
||||
err := c.writeClose(code, reason)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.waitCloseHandshake()
|
||||
if CloseStatus(err) != code {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) writeClose(code StatusCode, reason string) error {
|
||||
ce := CloseError{
|
||||
Code: code,
|
||||
Reason: reason,
|
||||
}
|
||||
|
||||
var p []byte
|
||||
var err error
|
||||
if ce.Code != StatusNoStatusRcvd {
|
||||
p, err = ce.bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
err = c.writeControl(ctx, opClose, p)
|
||||
// If the connection closed as we're writing we ignore the error as we might
|
||||
// have written the close frame, the peer responded and then someone else read it
|
||||
// and closed the connection.
|
||||
if err != nil && !errors.Is(err, net.ErrClosed) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) waitCloseHandshake() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
err := c.readMu.lock(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.readMu.unlock()
|
||||
|
||||
for i := int64(0); i < c.msgReader.payloadLength; i++ {
|
||||
_, err := c.br.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
h, err := c.readLoop(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := int64(0); i < h.payloadLength; i++ {
|
||||
_, err := c.br.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) waitGoroutines() error {
|
||||
t := time.NewTimer(time.Second * 15)
|
||||
defer t.Stop()
|
||||
|
||||
c.closeReadMu.Lock()
|
||||
closeRead := c.closeReadCtx != nil
|
||||
c.closeReadMu.Unlock()
|
||||
if closeRead {
|
||||
select {
|
||||
case <-c.closeReadDone:
|
||||
case <-t.C:
|
||||
return errors.New("failed to wait for close read goroutine to exit")
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
case <-t.C:
|
||||
return errors.New("failed to wait for connection to be closed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseClosePayload(p []byte) (CloseError, error) {
|
||||
if len(p) == 0 {
|
||||
return CloseError{
|
||||
Code: StatusNoStatusRcvd,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return CloseError{}, fmt.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p)
|
||||
}
|
||||
|
||||
ce := CloseError{
|
||||
Code: StatusCode(binary.BigEndian.Uint16(p)),
|
||||
Reason: string(p[2:]),
|
||||
}
|
||||
|
||||
if !validWireCloseCode(ce.Code) {
|
||||
return CloseError{}, fmt.Errorf("invalid status code %v", ce.Code)
|
||||
}
|
||||
|
||||
return ce, nil
|
||||
}
|
||||
|
||||
// See http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
|
||||
// and https://tools.ietf.org/html/rfc6455#section-7.4.1
|
||||
func validWireCloseCode(code StatusCode) bool {
|
||||
switch code {
|
||||
case statusReserved, StatusNoStatusRcvd, StatusAbnormalClosure, StatusTLSHandshake:
|
||||
return false
|
||||
}
|
||||
|
||||
if code >= StatusNormalClosure && code <= StatusBadGateway {
|
||||
return true
|
||||
}
|
||||
if code >= 3000 && code <= 4999 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ce CloseError) bytes() ([]byte, error) {
|
||||
p, err := ce.bytesErr()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to marshal close frame: %w", err)
|
||||
ce = CloseError{
|
||||
Code: StatusInternalError,
|
||||
}
|
||||
p, _ = ce.bytesErr()
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
const maxCloseReason = maxControlPayload - 2
|
||||
|
||||
func (ce CloseError) bytesErr() ([]byte, error) {
|
||||
if len(ce.Reason) > maxCloseReason {
|
||||
return nil, fmt.Errorf("reason string max is %v but got %q with length %v", maxCloseReason, ce.Reason, len(ce.Reason))
|
||||
}
|
||||
|
||||
if !validWireCloseCode(ce.Code) {
|
||||
return nil, fmt.Errorf("status code %v cannot be set", ce.Code)
|
||||
}
|
||||
|
||||
buf := make([]byte, 2+len(ce.Reason))
|
||||
binary.BigEndian.PutUint16(buf, uint16(ce.Code))
|
||||
copy(buf[2:], ce.Reason)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (c *Conn) casClosing() bool {
|
||||
return c.closing.Swap(true)
|
||||
}
|
||||
|
||||
func (c *Conn) isClosed() bool {
|
||||
select {
|
||||
case <-c.closed:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
234
vendor/github.com/coder/websocket/compress.go
generated
vendored
234
vendor/github.com/coder/websocket/compress.go
generated
vendored
@@ -1,234 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CompressionMode represents the modes available to the permessage-deflate extension.
|
||||
// See https://tools.ietf.org/html/rfc7692
|
||||
//
|
||||
// Works in all modern browsers except Safari which does not implement the permessage-deflate extension.
|
||||
//
|
||||
// Compression is only used if the peer supports the mode selected.
|
||||
type CompressionMode int
|
||||
|
||||
const (
|
||||
// CompressionDisabled disables the negotiation of the permessage-deflate extension.
|
||||
//
|
||||
// This is the default. Do not enable compression without benchmarking for your particular use case first.
|
||||
CompressionDisabled CompressionMode = iota
|
||||
|
||||
// CompressionContextTakeover compresses each message greater than 128 bytes reusing the 32 KB sliding window from
|
||||
// previous messages. i.e compression context across messages is preserved.
|
||||
//
|
||||
// As most WebSocket protocols are text based and repetitive, this compression mode can be very efficient.
|
||||
//
|
||||
// The memory overhead is a fixed 32 KB sliding window, a fixed 1.2 MB flate.Writer and a sync.Pool of 40 KB flate.Reader's
|
||||
// that are used when reading and then returned.
|
||||
//
|
||||
// Thus, it uses more memory than CompressionNoContextTakeover but compresses more efficiently.
|
||||
//
|
||||
// If the peer does not support CompressionContextTakeover then we will fall back to CompressionNoContextTakeover.
|
||||
CompressionContextTakeover
|
||||
|
||||
// CompressionNoContextTakeover compresses each message greater than 512 bytes. Each message is compressed with
|
||||
// a new 1.2 MB flate.Writer pulled from a sync.Pool. Each message is read with a 40 KB flate.Reader pulled from
|
||||
// a sync.Pool.
|
||||
//
|
||||
// This means less efficient compression as the sliding window from previous messages will not be used but the
|
||||
// memory overhead will be lower as there will be no fixed cost for the flate.Writer nor the 32 KB sliding window.
|
||||
// Especially if the connections are long lived and seldom written to.
|
||||
//
|
||||
// Thus, it uses less memory than CompressionContextTakeover but compresses less efficiently.
|
||||
//
|
||||
// If the peer does not support CompressionNoContextTakeover then we will fall back to CompressionDisabled.
|
||||
CompressionNoContextTakeover
|
||||
)
|
||||
|
||||
func (m CompressionMode) opts() *compressionOptions {
|
||||
return &compressionOptions{
|
||||
clientNoContextTakeover: m == CompressionNoContextTakeover,
|
||||
serverNoContextTakeover: m == CompressionNoContextTakeover,
|
||||
}
|
||||
}
|
||||
|
||||
type compressionOptions struct {
|
||||
clientNoContextTakeover bool
|
||||
serverNoContextTakeover bool
|
||||
}
|
||||
|
||||
func (copts *compressionOptions) String() string {
|
||||
s := "permessage-deflate"
|
||||
if copts.clientNoContextTakeover {
|
||||
s += "; client_no_context_takeover"
|
||||
}
|
||||
if copts.serverNoContextTakeover {
|
||||
s += "; server_no_context_takeover"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// These bytes are required to get flate.Reader to return.
|
||||
// They are removed when sending to avoid the overhead as
|
||||
// WebSocket framing tell's when the message has ended but then
|
||||
// we need to add them back otherwise flate.Reader keeps
|
||||
// trying to read more bytes.
|
||||
const deflateMessageTail = "\x00\x00\xff\xff"
|
||||
|
||||
type trimLastFourBytesWriter struct {
|
||||
w io.Writer
|
||||
tail []byte
|
||||
}
|
||||
|
||||
func (tw *trimLastFourBytesWriter) reset() {
|
||||
if tw != nil && tw.tail != nil {
|
||||
tw.tail = tw.tail[:0]
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *trimLastFourBytesWriter) Write(p []byte) (int, error) {
|
||||
if tw.tail == nil {
|
||||
tw.tail = make([]byte, 0, 4)
|
||||
}
|
||||
|
||||
extra := len(tw.tail) + len(p) - 4
|
||||
|
||||
if extra <= 0 {
|
||||
tw.tail = append(tw.tail, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Now we need to write as many extra bytes as we can from the previous tail.
|
||||
if extra > len(tw.tail) {
|
||||
extra = len(tw.tail)
|
||||
}
|
||||
if extra > 0 {
|
||||
_, err := tw.w.Write(tw.tail[:extra])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Shift remaining bytes in tail over.
|
||||
n := copy(tw.tail, tw.tail[extra:])
|
||||
tw.tail = tw.tail[:n]
|
||||
}
|
||||
|
||||
// If p is less than or equal to 4 bytes,
|
||||
// all of it is is part of the tail.
|
||||
if len(p) <= 4 {
|
||||
tw.tail = append(tw.tail, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Otherwise, only the last 4 bytes are.
|
||||
tw.tail = append(tw.tail, p[len(p)-4:]...)
|
||||
|
||||
p = p[:len(p)-4]
|
||||
n, err := tw.w.Write(p)
|
||||
return n + 4, err
|
||||
}
|
||||
|
||||
var flateReaderPool sync.Pool
|
||||
|
||||
func getFlateReader(r io.Reader, dict []byte) io.Reader {
|
||||
fr, ok := flateReaderPool.Get().(io.Reader)
|
||||
if !ok {
|
||||
return flate.NewReaderDict(r, dict)
|
||||
}
|
||||
fr.(flate.Resetter).Reset(r, dict)
|
||||
return fr
|
||||
}
|
||||
|
||||
func putFlateReader(fr io.Reader) {
|
||||
flateReaderPool.Put(fr)
|
||||
}
|
||||
|
||||
var flateWriterPool sync.Pool
|
||||
|
||||
func getFlateWriter(w io.Writer) *flate.Writer {
|
||||
fw, ok := flateWriterPool.Get().(*flate.Writer)
|
||||
if !ok {
|
||||
fw, _ = flate.NewWriter(w, flate.BestSpeed)
|
||||
return fw
|
||||
}
|
||||
fw.Reset(w)
|
||||
return fw
|
||||
}
|
||||
|
||||
func putFlateWriter(w *flate.Writer) {
|
||||
flateWriterPool.Put(w)
|
||||
}
|
||||
|
||||
type slidingWindow struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var (
|
||||
swPoolMu sync.RWMutex
|
||||
swPool = map[int]*sync.Pool{}
|
||||
)
|
||||
|
||||
func slidingWindowPool(n int) *sync.Pool {
|
||||
swPoolMu.RLock()
|
||||
p, ok := swPool[n]
|
||||
swPoolMu.RUnlock()
|
||||
if ok {
|
||||
return p
|
||||
}
|
||||
|
||||
p = &sync.Pool{}
|
||||
|
||||
swPoolMu.Lock()
|
||||
swPool[n] = p
|
||||
swPoolMu.Unlock()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (sw *slidingWindow) init(n int) {
|
||||
if sw.buf != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
n = 32768
|
||||
}
|
||||
|
||||
p := slidingWindowPool(n)
|
||||
sw2, ok := p.Get().(*slidingWindow)
|
||||
if ok {
|
||||
*sw = *sw2
|
||||
} else {
|
||||
sw.buf = make([]byte, 0, n)
|
||||
}
|
||||
}
|
||||
|
||||
func (sw *slidingWindow) close() {
|
||||
sw.buf = sw.buf[:0]
|
||||
swPoolMu.Lock()
|
||||
swPool[cap(sw.buf)].Put(sw)
|
||||
swPoolMu.Unlock()
|
||||
}
|
||||
|
||||
func (sw *slidingWindow) write(p []byte) {
|
||||
if len(p) >= cap(sw.buf) {
|
||||
sw.buf = sw.buf[:cap(sw.buf)]
|
||||
p = p[len(p)-cap(sw.buf):]
|
||||
copy(sw.buf, p)
|
||||
return
|
||||
}
|
||||
|
||||
left := cap(sw.buf) - len(sw.buf)
|
||||
if left < len(p) {
|
||||
// We need to shift spaceNeeded bytes from the end to make room for p at the end.
|
||||
spaceNeeded := len(p) - left
|
||||
copy(sw.buf, sw.buf[spaceNeeded:])
|
||||
sw.buf = sw.buf[:len(sw.buf)-spaceNeeded]
|
||||
}
|
||||
|
||||
sw.buf = append(sw.buf, p...)
|
||||
}
|
||||
306
vendor/github.com/coder/websocket/conn.go
generated
vendored
306
vendor/github.com/coder/websocket/conn.go
generated
vendored
@@ -1,306 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// MessageType represents the type of a WebSocket message.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.6
|
||||
type MessageType int
|
||||
|
||||
// MessageType constants.
|
||||
const (
|
||||
// MessageText is for UTF-8 encoded text messages like JSON.
|
||||
MessageText MessageType = iota + 1
|
||||
// MessageBinary is for binary messages like protobufs.
|
||||
MessageBinary
|
||||
)
|
||||
|
||||
// Conn represents a WebSocket connection.
|
||||
// All methods may be called concurrently except for Reader and Read.
|
||||
//
|
||||
// You must always read from the connection. Otherwise control
|
||||
// frames will not be handled. See Reader and CloseRead.
|
||||
//
|
||||
// Be sure to call Close on the connection when you
|
||||
// are finished with it to release associated resources.
|
||||
//
|
||||
// On any error from any method, the connection is closed
|
||||
// with an appropriate reason.
|
||||
//
|
||||
// This applies to context expirations as well unfortunately.
|
||||
// See https://github.com/nhooyr/websocket/issues/242#issuecomment-633182220
|
||||
type Conn struct {
|
||||
noCopy noCopy
|
||||
|
||||
subprotocol string
|
||||
rwc io.ReadWriteCloser
|
||||
client bool
|
||||
copts *compressionOptions
|
||||
flateThreshold int
|
||||
br *bufio.Reader
|
||||
bw *bufio.Writer
|
||||
|
||||
readTimeoutStop atomic.Pointer[func() bool]
|
||||
writeTimeoutStop atomic.Pointer[func() bool]
|
||||
|
||||
// Read state.
|
||||
readMu *mu
|
||||
readHeaderBuf [8]byte
|
||||
readControlBuf [maxControlPayload]byte
|
||||
msgReader *msgReader
|
||||
|
||||
// Write state.
|
||||
msgWriter *msgWriter
|
||||
writeFrameMu *mu
|
||||
writeBuf []byte
|
||||
writeHeaderBuf [8]byte
|
||||
writeHeader header
|
||||
|
||||
// Close handshake state.
|
||||
closeStateMu sync.RWMutex
|
||||
closeReceivedErr error
|
||||
closeSentErr error
|
||||
|
||||
// CloseRead state.
|
||||
closeReadMu sync.Mutex
|
||||
closeReadCtx context.Context
|
||||
closeReadDone chan struct{}
|
||||
|
||||
closing atomic.Bool
|
||||
closeMu sync.Mutex // Protects following.
|
||||
closed chan struct{}
|
||||
|
||||
pingCounter atomic.Int64
|
||||
activePingsMu sync.Mutex
|
||||
activePings map[string]chan<- struct{}
|
||||
onPingReceived func(context.Context, []byte) bool
|
||||
onPongReceived func(context.Context, []byte)
|
||||
}
|
||||
|
||||
type connConfig struct {
|
||||
subprotocol string
|
||||
rwc io.ReadWriteCloser
|
||||
client bool
|
||||
copts *compressionOptions
|
||||
flateThreshold int
|
||||
onPingReceived func(context.Context, []byte) bool
|
||||
onPongReceived func(context.Context, []byte)
|
||||
|
||||
br *bufio.Reader
|
||||
bw *bufio.Writer
|
||||
}
|
||||
|
||||
func newConn(cfg connConfig) *Conn {
|
||||
c := &Conn{
|
||||
subprotocol: cfg.subprotocol,
|
||||
rwc: cfg.rwc,
|
||||
client: cfg.client,
|
||||
copts: cfg.copts,
|
||||
flateThreshold: cfg.flateThreshold,
|
||||
|
||||
br: cfg.br,
|
||||
bw: cfg.bw,
|
||||
|
||||
closed: make(chan struct{}),
|
||||
activePings: make(map[string]chan<- struct{}),
|
||||
onPingReceived: cfg.onPingReceived,
|
||||
onPongReceived: cfg.onPongReceived,
|
||||
}
|
||||
|
||||
c.readMu = newMu(c)
|
||||
c.writeFrameMu = newMu(c)
|
||||
|
||||
c.msgReader = newMsgReader(c)
|
||||
|
||||
c.msgWriter = newMsgWriter(c)
|
||||
if c.client {
|
||||
c.writeBuf = extractBufioWriterBuf(c.bw, c.rwc)
|
||||
}
|
||||
|
||||
if c.flate() && c.flateThreshold == 0 {
|
||||
c.flateThreshold = 128
|
||||
if !c.msgWriter.flateContextTakeover() {
|
||||
c.flateThreshold = 512
|
||||
}
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(c, func(c *Conn) {
|
||||
c.close()
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Subprotocol returns the negotiated subprotocol.
|
||||
// An empty string means the default protocol.
|
||||
func (c *Conn) Subprotocol() string {
|
||||
return c.subprotocol
|
||||
}
|
||||
|
||||
func (c *Conn) close() error {
|
||||
c.closeMu.Lock()
|
||||
defer c.closeMu.Unlock()
|
||||
|
||||
if c.isClosed() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
runtime.SetFinalizer(c, nil)
|
||||
close(c.closed)
|
||||
|
||||
// Have to close after c.closed is closed to ensure any goroutine that wakes up
|
||||
// from the connection being closed also sees that c.closed is closed and returns
|
||||
// closeErr.
|
||||
err := c.rwc.Close()
|
||||
// With the close of rwc, these become safe to close.
|
||||
c.msgWriter.close()
|
||||
c.msgReader.close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) setupWriteTimeout(ctx context.Context) {
|
||||
stop := context.AfterFunc(ctx, func() {
|
||||
c.clearWriteTimeout()
|
||||
c.close()
|
||||
})
|
||||
swapTimeoutStop(&c.writeTimeoutStop, &stop)
|
||||
}
|
||||
|
||||
func (c *Conn) clearWriteTimeout() {
|
||||
swapTimeoutStop(&c.writeTimeoutStop, nil)
|
||||
}
|
||||
|
||||
func (c *Conn) setupReadTimeout(ctx context.Context) {
|
||||
stop := context.AfterFunc(ctx, func() {
|
||||
c.clearReadTimeout()
|
||||
c.close()
|
||||
})
|
||||
swapTimeoutStop(&c.readTimeoutStop, &stop)
|
||||
}
|
||||
|
||||
func (c *Conn) clearReadTimeout() {
|
||||
swapTimeoutStop(&c.readTimeoutStop, nil)
|
||||
}
|
||||
|
||||
func swapTimeoutStop(p *atomic.Pointer[func() bool], newStop *func() bool) {
|
||||
oldStop := p.Swap(newStop)
|
||||
if oldStop != nil {
|
||||
(*oldStop)()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) flate() bool {
|
||||
return c.copts != nil
|
||||
}
|
||||
|
||||
// Ping sends a ping to the peer and waits for a pong.
|
||||
// Use this to measure latency or ensure the peer is responsive.
|
||||
// Ping must be called concurrently with Reader as it does
|
||||
// not read from the connection but instead waits for a Reader call
|
||||
// to read the pong.
|
||||
//
|
||||
// TCP Keepalives should suffice for most use cases.
|
||||
func (c *Conn) Ping(ctx context.Context) error {
|
||||
p := c.pingCounter.Add(1)
|
||||
|
||||
err := c.ping(ctx, strconv.FormatInt(p, 10))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to ping: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) ping(ctx context.Context, p string) error {
|
||||
pong := make(chan struct{}, 1)
|
||||
|
||||
c.activePingsMu.Lock()
|
||||
c.activePings[p] = pong
|
||||
c.activePingsMu.Unlock()
|
||||
|
||||
defer func() {
|
||||
c.activePingsMu.Lock()
|
||||
delete(c.activePings, p)
|
||||
c.activePingsMu.Unlock()
|
||||
}()
|
||||
|
||||
err := c.writeControl(ctx, opPing, []byte(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
return net.ErrClosed
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("failed to wait for pong: %w", ctx.Err())
|
||||
case <-pong:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type mu struct {
|
||||
c *Conn
|
||||
ch chan struct{}
|
||||
}
|
||||
|
||||
func newMu(c *Conn) *mu {
|
||||
return &mu{
|
||||
c: c,
|
||||
ch: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mu) forceLock() {
|
||||
m.ch <- struct{}{}
|
||||
}
|
||||
|
||||
func (m *mu) tryLock() bool {
|
||||
select {
|
||||
case m.ch <- struct{}{}:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mu) lock(ctx context.Context) error {
|
||||
select {
|
||||
case <-m.c.closed:
|
||||
return net.ErrClosed
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("failed to acquire lock: %w", ctx.Err())
|
||||
case m.ch <- struct{}{}:
|
||||
// To make sure the connection is certainly alive.
|
||||
// As it's possible the send on m.ch was selected
|
||||
// over the receive on closed.
|
||||
select {
|
||||
case <-m.c.closed:
|
||||
// Make sure to release.
|
||||
m.unlock()
|
||||
return net.ErrClosed
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mu) unlock() {
|
||||
select {
|
||||
case <-m.ch:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type noCopy struct{}
|
||||
|
||||
func (*noCopy) Lock() {}
|
||||
347
vendor/github.com/coder/websocket/dial.go
generated
vendored
347
vendor/github.com/coder/websocket/dial.go
generated
vendored
@@ -1,347 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
)
|
||||
|
||||
// DialOptions represents Dial's options.
|
||||
type DialOptions struct {
|
||||
// HTTPClient is used for the connection.
|
||||
// Its Transport must return writable bodies for WebSocket handshakes.
|
||||
// http.Transport does beginning with Go 1.12.
|
||||
HTTPClient *http.Client
|
||||
|
||||
// HTTPHeader specifies the HTTP headers included in the handshake request.
|
||||
HTTPHeader http.Header
|
||||
|
||||
// Host optionally overrides the Host HTTP header to send. If empty, the value
|
||||
// of URL.Host will be used.
|
||||
Host string
|
||||
|
||||
// Subprotocols lists the WebSocket subprotocols to negotiate with the server.
|
||||
Subprotocols []string
|
||||
|
||||
// CompressionMode controls the compression mode.
|
||||
// Defaults to CompressionDisabled.
|
||||
//
|
||||
// See docs on CompressionMode for details.
|
||||
CompressionMode CompressionMode
|
||||
|
||||
// CompressionThreshold controls the minimum size of a message before compression is applied.
|
||||
//
|
||||
// Defaults to 512 bytes for CompressionNoContextTakeover and 128 bytes
|
||||
// for CompressionContextTakeover.
|
||||
CompressionThreshold int
|
||||
|
||||
// OnPingReceived is an optional callback invoked synchronously when a ping frame is received.
|
||||
//
|
||||
// The payload contains the application data of the ping frame.
|
||||
// If the callback returns false, the subsequent pong frame will not be sent.
|
||||
// To avoid blocking, any expensive processing should be performed asynchronously using a goroutine.
|
||||
OnPingReceived func(ctx context.Context, payload []byte) bool
|
||||
|
||||
// OnPongReceived is an optional callback invoked synchronously when a pong frame is received.
|
||||
//
|
||||
// The payload contains the application data of the pong frame.
|
||||
// To avoid blocking, any expensive processing should be performed asynchronously using a goroutine.
|
||||
//
|
||||
// Unlike OnPingReceived, this callback does not return a value because a pong frame
|
||||
// is a response to a ping and does not trigger any further frame transmission.
|
||||
OnPongReceived func(ctx context.Context, payload []byte)
|
||||
}
|
||||
|
||||
func (opts *DialOptions) cloneWithDefaults(ctx context.Context) (context.Context, context.CancelFunc, *DialOptions) {
|
||||
var cancel context.CancelFunc
|
||||
|
||||
var o DialOptions
|
||||
if opts != nil {
|
||||
o = *opts
|
||||
}
|
||||
if o.HTTPClient == nil {
|
||||
o.HTTPClient = http.DefaultClient
|
||||
}
|
||||
if o.HTTPClient.Timeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, o.HTTPClient.Timeout)
|
||||
|
||||
newClient := *o.HTTPClient
|
||||
newClient.Timeout = 0
|
||||
o.HTTPClient = &newClient
|
||||
}
|
||||
if o.HTTPHeader == nil {
|
||||
o.HTTPHeader = http.Header{}
|
||||
}
|
||||
newClient := *o.HTTPClient
|
||||
oldCheckRedirect := o.HTTPClient.CheckRedirect
|
||||
newClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
switch req.URL.Scheme {
|
||||
case "ws":
|
||||
req.URL.Scheme = "http"
|
||||
case "wss":
|
||||
req.URL.Scheme = "https"
|
||||
}
|
||||
if oldCheckRedirect != nil {
|
||||
return oldCheckRedirect(req, via)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
o.HTTPClient = &newClient
|
||||
|
||||
return ctx, cancel, &o
|
||||
}
|
||||
|
||||
// Dial performs a WebSocket handshake on url.
|
||||
//
|
||||
// The response is the WebSocket handshake response from the server.
|
||||
// You never need to close resp.Body yourself.
|
||||
//
|
||||
// If an error occurs, the returned response may be non nil.
|
||||
// However, you can only read the first 1024 bytes of the body.
|
||||
//
|
||||
// This function requires at least Go 1.12 as it uses a new feature
|
||||
// in net/http to perform WebSocket handshakes.
|
||||
// See docs on the HTTPClient option and https://github.com/golang/go/issues/26937#issuecomment-415855861
|
||||
//
|
||||
// URLs with http/https schemes will work and are interpreted as ws/wss.
|
||||
func Dial(ctx context.Context, u string, opts *DialOptions) (*Conn, *http.Response, error) {
|
||||
return dial(ctx, u, opts, nil)
|
||||
}
|
||||
|
||||
func dial(ctx context.Context, urls string, opts *DialOptions, rand io.Reader) (_ *Conn, _ *http.Response, err error) {
|
||||
defer errd.Wrap(&err, "failed to WebSocket dial")
|
||||
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel, opts = opts.cloneWithDefaults(ctx)
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
secWebSocketKey, err := secWebSocketKey(rand)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate Sec-WebSocket-Key: %w", err)
|
||||
}
|
||||
|
||||
var copts *compressionOptions
|
||||
if opts.CompressionMode != CompressionDisabled {
|
||||
copts = opts.CompressionMode.opts()
|
||||
}
|
||||
|
||||
resp, err := handshakeRequest(ctx, urls, opts, copts, secWebSocketKey)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
respBody := resp.Body
|
||||
resp.Body = nil
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// We read a bit of the body for easier debugging.
|
||||
r := io.LimitReader(respBody, 1024)
|
||||
|
||||
timer := time.AfterFunc(time.Second*3, func() {
|
||||
respBody.Close()
|
||||
})
|
||||
defer timer.Stop()
|
||||
|
||||
b, _ := io.ReadAll(r)
|
||||
respBody.Close()
|
||||
resp.Body = io.NopCloser(bytes.NewReader(b))
|
||||
}
|
||||
}()
|
||||
|
||||
copts, err = verifyServerResponse(opts, copts, secWebSocketKey, resp)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
rwc, ok := respBody.(io.ReadWriteCloser)
|
||||
if !ok {
|
||||
return nil, resp, fmt.Errorf("response body is not a io.ReadWriteCloser: %T", respBody)
|
||||
}
|
||||
|
||||
return newConn(connConfig{
|
||||
subprotocol: resp.Header.Get("Sec-WebSocket-Protocol"),
|
||||
rwc: rwc,
|
||||
client: true,
|
||||
copts: copts,
|
||||
flateThreshold: opts.CompressionThreshold,
|
||||
onPingReceived: opts.OnPingReceived,
|
||||
onPongReceived: opts.OnPongReceived,
|
||||
br: getBufioReader(rwc),
|
||||
bw: getBufioWriter(rwc),
|
||||
}), resp, nil
|
||||
}
|
||||
|
||||
func handshakeRequest(ctx context.Context, urls string, opts *DialOptions, copts *compressionOptions, secWebSocketKey string) (*http.Response, error) {
|
||||
u, err := url.Parse(urls)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse url: %w", err)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
u.Scheme = "http"
|
||||
case "wss":
|
||||
u.Scheme = "https"
|
||||
case "http", "https":
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected url scheme: %q", u.Scheme)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new http request: %w", err)
|
||||
}
|
||||
if len(opts.Host) > 0 {
|
||||
req.Host = opts.Host
|
||||
}
|
||||
req.Header = opts.HTTPHeader.Clone()
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Header.Set("Upgrade", "websocket")
|
||||
req.Header.Set("Sec-WebSocket-Version", "13")
|
||||
req.Header.Set("Sec-WebSocket-Key", secWebSocketKey)
|
||||
if len(opts.Subprotocols) > 0 {
|
||||
req.Header.Set("Sec-WebSocket-Protocol", strings.Join(opts.Subprotocols, ","))
|
||||
}
|
||||
if copts != nil {
|
||||
req.Header.Set("Sec-WebSocket-Extensions", copts.String())
|
||||
}
|
||||
|
||||
resp, err := opts.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send handshake request: %w", err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func secWebSocketKey(rr io.Reader) (string, error) {
|
||||
if rr == nil {
|
||||
rr = rand.Reader
|
||||
}
|
||||
b := make([]byte, 16)
|
||||
_, err := io.ReadFull(rr, b)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read random data from rand.Reader: %w", err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func verifyServerResponse(opts *DialOptions, copts *compressionOptions, secWebSocketKey string, resp *http.Response) (*compressionOptions, error) {
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
return nil, fmt.Errorf("expected handshake response status code %v but got %v", http.StatusSwitchingProtocols, resp.StatusCode)
|
||||
}
|
||||
|
||||
if !headerContainsTokenIgnoreCase(resp.Header, "Connection", "Upgrade") {
|
||||
return nil, fmt.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", resp.Header.Get("Connection"))
|
||||
}
|
||||
|
||||
if !headerContainsTokenIgnoreCase(resp.Header, "Upgrade", "WebSocket") {
|
||||
return nil, fmt.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", resp.Header.Get("Upgrade"))
|
||||
}
|
||||
|
||||
if resp.Header.Get("Sec-WebSocket-Accept") != secWebSocketAccept(secWebSocketKey) {
|
||||
return nil, fmt.Errorf("WebSocket protocol violation: invalid Sec-WebSocket-Accept %q, key %q",
|
||||
resp.Header.Get("Sec-WebSocket-Accept"),
|
||||
secWebSocketKey,
|
||||
)
|
||||
}
|
||||
|
||||
err := verifySubprotocol(opts.Subprotocols, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return verifyServerExtensions(copts, resp.Header)
|
||||
}
|
||||
|
||||
func verifySubprotocol(subprotos []string, resp *http.Response) error {
|
||||
proto := resp.Header.Get("Sec-WebSocket-Protocol")
|
||||
if proto == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, sp2 := range subprotos {
|
||||
if strings.EqualFold(sp2, proto) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("WebSocket protocol violation: unexpected Sec-WebSocket-Protocol from server: %q", proto)
|
||||
}
|
||||
|
||||
func verifyServerExtensions(copts *compressionOptions, h http.Header) (*compressionOptions, error) {
|
||||
exts := websocketExtensions(h)
|
||||
if len(exts) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ext := exts[0]
|
||||
if ext.name != "permessage-deflate" || len(exts) > 1 || copts == nil {
|
||||
return nil, fmt.Errorf("WebSocket protcol violation: unsupported extensions from server: %+v", exts[1:])
|
||||
}
|
||||
|
||||
_copts := *copts
|
||||
copts = &_copts
|
||||
|
||||
for _, p := range ext.params {
|
||||
switch p {
|
||||
case "client_no_context_takeover":
|
||||
copts.clientNoContextTakeover = true
|
||||
continue
|
||||
case "server_no_context_takeover":
|
||||
copts.serverNoContextTakeover = true
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(p, "server_max_window_bits=") {
|
||||
// We can't adjust the deflate window, but decoding with a larger window is acceptable.
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported permessage-deflate parameter: %q", p)
|
||||
}
|
||||
|
||||
return copts, nil
|
||||
}
|
||||
|
||||
var bufioReaderPool sync.Pool
|
||||
|
||||
func getBufioReader(r io.Reader) *bufio.Reader {
|
||||
br, ok := bufioReaderPool.Get().(*bufio.Reader)
|
||||
if !ok {
|
||||
return bufio.NewReader(r)
|
||||
}
|
||||
br.Reset(r)
|
||||
return br
|
||||
}
|
||||
|
||||
func putBufioReader(br *bufio.Reader) {
|
||||
bufioReaderPool.Put(br)
|
||||
}
|
||||
|
||||
var bufioWriterPool sync.Pool
|
||||
|
||||
func getBufioWriter(w io.Writer) *bufio.Writer {
|
||||
bw, ok := bufioWriterPool.Get().(*bufio.Writer)
|
||||
if !ok {
|
||||
return bufio.NewWriter(w)
|
||||
}
|
||||
bw.Reset(w)
|
||||
return bw
|
||||
}
|
||||
|
||||
func putBufioWriter(bw *bufio.Writer) {
|
||||
bufioWriterPool.Put(bw)
|
||||
}
|
||||
33
vendor/github.com/coder/websocket/doc.go
generated
vendored
33
vendor/github.com/coder/websocket/doc.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
// Package websocket implements the RFC 6455 WebSocket protocol.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc6455
|
||||
//
|
||||
// Use Dial to dial a WebSocket server.
|
||||
//
|
||||
// Use Accept to accept a WebSocket client.
|
||||
//
|
||||
// Conn represents the resulting WebSocket connection.
|
||||
//
|
||||
// The examples are the best way to understand how to correctly use the library.
|
||||
//
|
||||
// The wsjson subpackage contain helpers for JSON and protobuf messages.
|
||||
//
|
||||
// More documentation at https://github.com/coder/websocket.
|
||||
//
|
||||
// # Wasm
|
||||
//
|
||||
// The client side supports compiling to Wasm.
|
||||
// It wraps the WebSocket browser API.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
||||
//
|
||||
// Some important caveats to be aware of:
|
||||
//
|
||||
// - Accept always errors out
|
||||
// - Conn.Ping is no-op
|
||||
// - Conn.CloseNow is Close(StatusGoingAway, "")
|
||||
// - HTTPClient, HTTPHeader and CompressionMode in DialOptions are no-op
|
||||
// - *http.Response from Dial is &http.Response{} with a 101 status code on success
|
||||
package websocket // import "github.com/coder/websocket"
|
||||
8
vendor/github.com/coder/websocket/errors.go
generated
vendored
8
vendor/github.com/coder/websocket/errors.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrMessageTooBig is returned when a message exceeds the read limit.
|
||||
var ErrMessageTooBig = errors.New("websocket: message too big")
|
||||
173
vendor/github.com/coder/websocket/frame.go
generated
vendored
173
vendor/github.com/coder/websocket/frame.go
generated
vendored
@@ -1,173 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
)
|
||||
|
||||
// opcode represents a WebSocket opcode.
|
||||
type opcode int
|
||||
|
||||
// https://tools.ietf.org/html/rfc6455#section-11.8.
|
||||
const (
|
||||
opContinuation opcode = iota
|
||||
opText
|
||||
opBinary
|
||||
// 3 - 7 are reserved for further non-control frames.
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
opClose
|
||||
opPing
|
||||
opPong
|
||||
// 11-16 are reserved for further control frames.
|
||||
)
|
||||
|
||||
// header represents a WebSocket frame header.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.2.
|
||||
type header struct {
|
||||
fin bool
|
||||
rsv1 bool
|
||||
rsv2 bool
|
||||
rsv3 bool
|
||||
opcode opcode
|
||||
|
||||
payloadLength int64
|
||||
|
||||
masked bool
|
||||
maskKey uint32
|
||||
}
|
||||
|
||||
// readFrameHeader reads a header from the reader.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.2.
|
||||
func readFrameHeader(r *bufio.Reader, readBuf []byte) (h header, err error) {
|
||||
defer errd.Wrap(&err, "failed to read frame header")
|
||||
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
|
||||
h.fin = b&(1<<7) != 0
|
||||
h.rsv1 = b&(1<<6) != 0
|
||||
h.rsv2 = b&(1<<5) != 0
|
||||
h.rsv3 = b&(1<<4) != 0
|
||||
|
||||
h.opcode = opcode(b & 0xf)
|
||||
|
||||
b, err = r.ReadByte()
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
|
||||
h.masked = b&(1<<7) != 0
|
||||
|
||||
payloadLength := b &^ (1 << 7)
|
||||
switch {
|
||||
case payloadLength < 126:
|
||||
h.payloadLength = int64(payloadLength)
|
||||
case payloadLength == 126:
|
||||
_, err = io.ReadFull(r, readBuf[:2])
|
||||
h.payloadLength = int64(binary.BigEndian.Uint16(readBuf))
|
||||
case payloadLength == 127:
|
||||
_, err = io.ReadFull(r, readBuf)
|
||||
h.payloadLength = int64(binary.BigEndian.Uint64(readBuf))
|
||||
}
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
|
||||
if h.payloadLength < 0 {
|
||||
return header{}, fmt.Errorf("received negative payload length: %v", h.payloadLength)
|
||||
}
|
||||
|
||||
if h.masked {
|
||||
_, err = io.ReadFull(r, readBuf[:4])
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
h.maskKey = binary.LittleEndian.Uint32(readBuf)
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// maxControlPayload is the maximum length of a control frame payload.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.5.
|
||||
const maxControlPayload = 125
|
||||
|
||||
// writeFrameHeader writes the bytes of the header to w.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.2
|
||||
func writeFrameHeader(h header, w *bufio.Writer, buf []byte) (err error) {
|
||||
defer errd.Wrap(&err, "failed to write frame header")
|
||||
|
||||
var b byte
|
||||
if h.fin {
|
||||
b |= 1 << 7
|
||||
}
|
||||
if h.rsv1 {
|
||||
b |= 1 << 6
|
||||
}
|
||||
if h.rsv2 {
|
||||
b |= 1 << 5
|
||||
}
|
||||
if h.rsv3 {
|
||||
b |= 1 << 4
|
||||
}
|
||||
|
||||
b |= byte(h.opcode)
|
||||
|
||||
err = w.WriteByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lengthByte := byte(0)
|
||||
if h.masked {
|
||||
lengthByte |= 1 << 7
|
||||
}
|
||||
|
||||
switch {
|
||||
case h.payloadLength > math.MaxUint16:
|
||||
lengthByte |= 127
|
||||
case h.payloadLength > 125:
|
||||
lengthByte |= 126
|
||||
case h.payloadLength >= 0:
|
||||
lengthByte |= byte(h.payloadLength)
|
||||
}
|
||||
err = w.WriteByte(lengthByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case h.payloadLength > math.MaxUint16:
|
||||
binary.BigEndian.PutUint64(buf, uint64(h.payloadLength))
|
||||
_, err = w.Write(buf)
|
||||
case h.payloadLength > 125:
|
||||
binary.BigEndian.PutUint16(buf, uint16(h.payloadLength))
|
||||
_, err = w.Write(buf[:2])
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.masked {
|
||||
binary.LittleEndian.PutUint32(buf, h.maskKey)
|
||||
_, err = w.Write(buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
33
vendor/github.com/coder/websocket/hijack.go
generated
vendored
33
vendor/github.com/coder/websocket/hijack.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type rwUnwrapper interface {
|
||||
Unwrap() http.ResponseWriter
|
||||
}
|
||||
|
||||
// hijacker returns the Hijacker interface of the http.ResponseWriter.
|
||||
// It follows the Unwrap method of the http.ResponseWriter if available,
|
||||
// matching the behavior of http.ResponseController. If the Hijacker
|
||||
// interface is not found, it returns false.
|
||||
//
|
||||
// Since the http.ResponseController is not available in Go 1.19, and
|
||||
// does not support checking the presence of the Hijacker interface,
|
||||
// this function is used to provide a consistent way to check for the
|
||||
// Hijacker interface across Go versions.
|
||||
func hijacker(rw http.ResponseWriter) (http.Hijacker, bool) {
|
||||
for {
|
||||
switch t := rw.(type) {
|
||||
case http.Hijacker:
|
||||
return t, true
|
||||
case rwUnwrapper:
|
||||
rw = t.Unwrap()
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
25
vendor/github.com/coder/websocket/internal/bpool/bpool.go
generated
vendored
25
vendor/github.com/coder/websocket/internal/bpool/bpool.go
generated
vendored
@@ -1,25 +0,0 @@
|
||||
package bpool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bpool = sync.Pool{
|
||||
New: func() any {
|
||||
return &bytes.Buffer{}
|
||||
},
|
||||
}
|
||||
|
||||
// Get returns a buffer from the pool or creates a new one if
|
||||
// the pool is empty.
|
||||
func Get() *bytes.Buffer {
|
||||
b := bpool.Get()
|
||||
return b.(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// Put returns a buffer into the pool.
|
||||
func Put(b *bytes.Buffer) {
|
||||
b.Reset()
|
||||
bpool.Put(b)
|
||||
}
|
||||
14
vendor/github.com/coder/websocket/internal/errd/wrap.go
generated
vendored
14
vendor/github.com/coder/websocket/internal/errd/wrap.go
generated
vendored
@@ -1,14 +0,0 @@
|
||||
package errd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Wrap wraps err with fmt.Errorf if err is non nil.
|
||||
// Intended for use with defer and a named error return.
|
||||
// Inspired by https://github.com/golang/go/issues/32676.
|
||||
func Wrap(err *error, f string, v ...any) {
|
||||
if *err != nil {
|
||||
*err = fmt.Errorf(f+": %w", append(v, *err)...)
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/coder/websocket/internal/util/util.go
generated
vendored
15
vendor/github.com/coder/websocket/internal/util/util.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
package util
|
||||
|
||||
// WriterFunc is used to implement one off io.Writers.
|
||||
type WriterFunc func(p []byte) (int, error)
|
||||
|
||||
func (f WriterFunc) Write(p []byte) (int, error) {
|
||||
return f(p)
|
||||
}
|
||||
|
||||
// ReaderFunc is used to implement one off io.Readers.
|
||||
type ReaderFunc func(p []byte) (int, error)
|
||||
|
||||
func (f ReaderFunc) Read(p []byte) (int, error) {
|
||||
return f(p)
|
||||
}
|
||||
169
vendor/github.com/coder/websocket/internal/wsjs/wsjs_js.go
generated
vendored
169
vendor/github.com/coder/websocket/internal/wsjs/wsjs_js.go
generated
vendored
@@ -1,169 +0,0 @@
|
||||
//go:build js
|
||||
// +build js
|
||||
|
||||
// Package wsjs implements typed access to the browser javascript WebSocket API.
|
||||
//
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
||||
package wsjs
|
||||
|
||||
import (
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
func handleJSError(err *error, onErr func()) {
|
||||
r := recover()
|
||||
|
||||
if jsErr, ok := r.(js.Error); ok {
|
||||
*err = jsErr
|
||||
|
||||
if onErr != nil {
|
||||
onErr()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
|
||||
// New is a wrapper around the javascript WebSocket constructor.
|
||||
func New(url string, protocols []string) (c WebSocket, err error) {
|
||||
defer handleJSError(&err, func() {
|
||||
c = WebSocket{}
|
||||
})
|
||||
|
||||
jsProtocols := make([]any, len(protocols))
|
||||
for i, p := range protocols {
|
||||
jsProtocols[i] = p
|
||||
}
|
||||
|
||||
c = WebSocket{
|
||||
v: js.Global().Get("WebSocket").New(url, jsProtocols),
|
||||
}
|
||||
|
||||
c.setBinaryType("arraybuffer")
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// WebSocket is a wrapper around a javascript WebSocket object.
|
||||
type WebSocket struct {
|
||||
v js.Value
|
||||
}
|
||||
|
||||
func (c WebSocket) setBinaryType(typ string) {
|
||||
c.v.Set("binaryType", string(typ))
|
||||
}
|
||||
|
||||
func (c WebSocket) addEventListener(eventType string, fn func(e js.Value)) func() {
|
||||
f := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
fn(args[0])
|
||||
return nil
|
||||
})
|
||||
c.v.Call("addEventListener", eventType, f)
|
||||
|
||||
return func() {
|
||||
c.v.Call("removeEventListener", eventType, f)
|
||||
f.Release()
|
||||
}
|
||||
}
|
||||
|
||||
// CloseEvent is the type passed to a WebSocket close handler.
|
||||
type CloseEvent struct {
|
||||
Code uint16
|
||||
Reason string
|
||||
WasClean bool
|
||||
}
|
||||
|
||||
// OnClose registers a function to be called when the WebSocket is closed.
|
||||
func (c WebSocket) OnClose(fn func(CloseEvent)) (remove func()) {
|
||||
return c.addEventListener("close", func(e js.Value) {
|
||||
ce := CloseEvent{
|
||||
Code: uint16(e.Get("code").Int()),
|
||||
Reason: e.Get("reason").String(),
|
||||
WasClean: e.Get("wasClean").Bool(),
|
||||
}
|
||||
fn(ce)
|
||||
})
|
||||
}
|
||||
|
||||
// OnError registers a function to be called when there is an error
|
||||
// with the WebSocket.
|
||||
func (c WebSocket) OnError(fn func(e js.Value)) (remove func()) {
|
||||
return c.addEventListener("error", fn)
|
||||
}
|
||||
|
||||
// MessageEvent is the type passed to a message handler.
|
||||
type MessageEvent struct {
|
||||
// string or []byte.
|
||||
Data any
|
||||
|
||||
// There are more fields to the interface but we don't use them.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
|
||||
}
|
||||
|
||||
// OnMessage registers a function to be called when the WebSocket receives a message.
|
||||
func (c WebSocket) OnMessage(fn func(m MessageEvent)) (remove func()) {
|
||||
return c.addEventListener("message", func(e js.Value) {
|
||||
var data any
|
||||
|
||||
arrayBuffer := e.Get("data")
|
||||
if arrayBuffer.Type() == js.TypeString {
|
||||
data = arrayBuffer.String()
|
||||
} else {
|
||||
data = extractArrayBuffer(arrayBuffer)
|
||||
}
|
||||
|
||||
me := MessageEvent{
|
||||
Data: data,
|
||||
}
|
||||
fn(me)
|
||||
})
|
||||
}
|
||||
|
||||
// Subprotocol returns the WebSocket subprotocol in use.
|
||||
func (c WebSocket) Subprotocol() string {
|
||||
return c.v.Get("protocol").String()
|
||||
}
|
||||
|
||||
// OnOpen registers a function to be called when the WebSocket is opened.
|
||||
func (c WebSocket) OnOpen(fn func(e js.Value)) (remove func()) {
|
||||
return c.addEventListener("open", fn)
|
||||
}
|
||||
|
||||
// Close closes the WebSocket with the given code and reason.
|
||||
func (c WebSocket) Close(code int, reason string) (err error) {
|
||||
defer handleJSError(&err, nil)
|
||||
c.v.Call("close", code, reason)
|
||||
return err
|
||||
}
|
||||
|
||||
// SendText sends the given string as a text message
|
||||
// on the WebSocket.
|
||||
func (c WebSocket) SendText(v string) (err error) {
|
||||
defer handleJSError(&err, nil)
|
||||
c.v.Call("send", v)
|
||||
return err
|
||||
}
|
||||
|
||||
// SendBytes sends the given message as a binary message
|
||||
// on the WebSocket.
|
||||
func (c WebSocket) SendBytes(v []byte) (err error) {
|
||||
defer handleJSError(&err, nil)
|
||||
c.v.Call("send", uint8Array(v))
|
||||
return err
|
||||
}
|
||||
|
||||
func extractArrayBuffer(arrayBuffer js.Value) []byte {
|
||||
uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer)
|
||||
dst := make([]byte, uint8Array.Length())
|
||||
js.CopyBytesToGo(dst, uint8Array)
|
||||
return dst
|
||||
}
|
||||
|
||||
func uint8Array(src []byte) js.Value {
|
||||
uint8Array := js.Global().Get("Uint8Array").New(len(src))
|
||||
js.CopyBytesToJS(uint8Array, src)
|
||||
return uint8Array
|
||||
}
|
||||
128
vendor/github.com/coder/websocket/mask.go
generated
vendored
128
vendor/github.com/coder/websocket/mask.go
generated
vendored
@@ -1,128 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// maskGo applies the WebSocket masking algorithm to p
|
||||
// with the given key.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.3
|
||||
//
|
||||
// The returned value is the correctly rotated key to
|
||||
// to continue to mask/unmask the message.
|
||||
//
|
||||
// It is optimized for LittleEndian and expects the key
|
||||
// to be in little endian.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/31586
|
||||
func maskGo(b []byte, key uint32) uint32 {
|
||||
if len(b) >= 8 {
|
||||
key64 := uint64(key)<<32 | uint64(key)
|
||||
|
||||
// At some point in the future we can clean these unrolled loops up.
|
||||
// See https://github.com/golang/go/issues/31586#issuecomment-487436401
|
||||
|
||||
// Then we xor until b is less than 128 bytes.
|
||||
for len(b) >= 128 {
|
||||
v := binary.LittleEndian.Uint64(b)
|
||||
binary.LittleEndian.PutUint64(b, v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[8:16])
|
||||
binary.LittleEndian.PutUint64(b[8:16], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[16:24])
|
||||
binary.LittleEndian.PutUint64(b[16:24], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[24:32])
|
||||
binary.LittleEndian.PutUint64(b[24:32], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[32:40])
|
||||
binary.LittleEndian.PutUint64(b[32:40], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[40:48])
|
||||
binary.LittleEndian.PutUint64(b[40:48], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[48:56])
|
||||
binary.LittleEndian.PutUint64(b[48:56], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[56:64])
|
||||
binary.LittleEndian.PutUint64(b[56:64], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[64:72])
|
||||
binary.LittleEndian.PutUint64(b[64:72], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[72:80])
|
||||
binary.LittleEndian.PutUint64(b[72:80], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[80:88])
|
||||
binary.LittleEndian.PutUint64(b[80:88], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[88:96])
|
||||
binary.LittleEndian.PutUint64(b[88:96], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[96:104])
|
||||
binary.LittleEndian.PutUint64(b[96:104], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[104:112])
|
||||
binary.LittleEndian.PutUint64(b[104:112], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[112:120])
|
||||
binary.LittleEndian.PutUint64(b[112:120], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[120:128])
|
||||
binary.LittleEndian.PutUint64(b[120:128], v^key64)
|
||||
b = b[128:]
|
||||
}
|
||||
|
||||
// Then we xor until b is less than 64 bytes.
|
||||
for len(b) >= 64 {
|
||||
v := binary.LittleEndian.Uint64(b)
|
||||
binary.LittleEndian.PutUint64(b, v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[8:16])
|
||||
binary.LittleEndian.PutUint64(b[8:16], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[16:24])
|
||||
binary.LittleEndian.PutUint64(b[16:24], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[24:32])
|
||||
binary.LittleEndian.PutUint64(b[24:32], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[32:40])
|
||||
binary.LittleEndian.PutUint64(b[32:40], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[40:48])
|
||||
binary.LittleEndian.PutUint64(b[40:48], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[48:56])
|
||||
binary.LittleEndian.PutUint64(b[48:56], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[56:64])
|
||||
binary.LittleEndian.PutUint64(b[56:64], v^key64)
|
||||
b = b[64:]
|
||||
}
|
||||
|
||||
// Then we xor until b is less than 32 bytes.
|
||||
for len(b) >= 32 {
|
||||
v := binary.LittleEndian.Uint64(b)
|
||||
binary.LittleEndian.PutUint64(b, v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[8:16])
|
||||
binary.LittleEndian.PutUint64(b[8:16], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[16:24])
|
||||
binary.LittleEndian.PutUint64(b[16:24], v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[24:32])
|
||||
binary.LittleEndian.PutUint64(b[24:32], v^key64)
|
||||
b = b[32:]
|
||||
}
|
||||
|
||||
// Then we xor until b is less than 16 bytes.
|
||||
for len(b) >= 16 {
|
||||
v := binary.LittleEndian.Uint64(b)
|
||||
binary.LittleEndian.PutUint64(b, v^key64)
|
||||
v = binary.LittleEndian.Uint64(b[8:16])
|
||||
binary.LittleEndian.PutUint64(b[8:16], v^key64)
|
||||
b = b[16:]
|
||||
}
|
||||
|
||||
// Then we xor until b is less than 8 bytes.
|
||||
for len(b) >= 8 {
|
||||
v := binary.LittleEndian.Uint64(b)
|
||||
binary.LittleEndian.PutUint64(b, v^key64)
|
||||
b = b[8:]
|
||||
}
|
||||
}
|
||||
|
||||
// Then we xor until b is less than 4 bytes.
|
||||
for len(b) >= 4 {
|
||||
v := binary.LittleEndian.Uint32(b)
|
||||
binary.LittleEndian.PutUint32(b, v^key)
|
||||
b = b[4:]
|
||||
}
|
||||
|
||||
// xor remaining bytes.
|
||||
for i := range b {
|
||||
b[i] ^= byte(key)
|
||||
key = bits.RotateLeft32(key, -8)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
127
vendor/github.com/coder/websocket/mask_amd64.s
generated
vendored
127
vendor/github.com/coder/websocket/mask_amd64.s
generated
vendored
@@ -1,127 +0,0 @@
|
||||
#include "textflag.h"
|
||||
|
||||
// func maskAsm(b *byte, len int, key uint32)
|
||||
TEXT ·maskAsm(SB), NOSPLIT, $0-28
|
||||
// AX = b
|
||||
// CX = len (left length)
|
||||
// SI = key (uint32)
|
||||
// DI = uint64(SI) | uint64(SI)<<32
|
||||
MOVQ b+0(FP), AX
|
||||
MOVQ len+8(FP), CX
|
||||
MOVL key+16(FP), SI
|
||||
|
||||
// calculate the DI
|
||||
// DI = SI<<32 | SI
|
||||
MOVL SI, DI
|
||||
MOVQ DI, DX
|
||||
SHLQ $32, DI
|
||||
ORQ DX, DI
|
||||
|
||||
CMPQ CX, $15
|
||||
JLE less_than_16
|
||||
CMPQ CX, $63
|
||||
JLE less_than_64
|
||||
CMPQ CX, $128
|
||||
JLE sse
|
||||
TESTQ $31, AX
|
||||
JNZ unaligned
|
||||
|
||||
unaligned_loop_1byte:
|
||||
XORB SI, (AX)
|
||||
INCQ AX
|
||||
DECQ CX
|
||||
ROLL $24, SI
|
||||
TESTQ $7, AX
|
||||
JNZ unaligned_loop_1byte
|
||||
|
||||
// calculate DI again since SI was modified
|
||||
// DI = SI<<32 | SI
|
||||
MOVL SI, DI
|
||||
MOVQ DI, DX
|
||||
SHLQ $32, DI
|
||||
ORQ DX, DI
|
||||
|
||||
TESTQ $31, AX
|
||||
JZ sse
|
||||
|
||||
unaligned:
|
||||
TESTQ $7, AX // AND $7 & len, if not zero jump to loop_1b.
|
||||
JNZ unaligned_loop_1byte
|
||||
|
||||
unaligned_loop:
|
||||
// we don't need to check the CX since we know it's above 128
|
||||
XORQ DI, (AX)
|
||||
ADDQ $8, AX
|
||||
SUBQ $8, CX
|
||||
TESTQ $31, AX
|
||||
JNZ unaligned_loop
|
||||
JMP sse
|
||||
|
||||
sse:
|
||||
CMPQ CX, $0x40
|
||||
JL less_than_64
|
||||
MOVQ DI, X0
|
||||
PUNPCKLQDQ X0, X0
|
||||
|
||||
sse_loop:
|
||||
MOVOU 0*16(AX), X1
|
||||
MOVOU 1*16(AX), X2
|
||||
MOVOU 2*16(AX), X3
|
||||
MOVOU 3*16(AX), X4
|
||||
PXOR X0, X1
|
||||
PXOR X0, X2
|
||||
PXOR X0, X3
|
||||
PXOR X0, X4
|
||||
MOVOU X1, 0*16(AX)
|
||||
MOVOU X2, 1*16(AX)
|
||||
MOVOU X3, 2*16(AX)
|
||||
MOVOU X4, 3*16(AX)
|
||||
ADDQ $0x40, AX
|
||||
SUBQ $0x40, CX
|
||||
CMPQ CX, $0x40
|
||||
JAE sse_loop
|
||||
|
||||
less_than_64:
|
||||
TESTQ $32, CX
|
||||
JZ less_than_32
|
||||
XORQ DI, (AX)
|
||||
XORQ DI, 8(AX)
|
||||
XORQ DI, 16(AX)
|
||||
XORQ DI, 24(AX)
|
||||
ADDQ $32, AX
|
||||
|
||||
less_than_32:
|
||||
TESTQ $16, CX
|
||||
JZ less_than_16
|
||||
XORQ DI, (AX)
|
||||
XORQ DI, 8(AX)
|
||||
ADDQ $16, AX
|
||||
|
||||
less_than_16:
|
||||
TESTQ $8, CX
|
||||
JZ less_than_8
|
||||
XORQ DI, (AX)
|
||||
ADDQ $8, AX
|
||||
|
||||
less_than_8:
|
||||
TESTQ $4, CX
|
||||
JZ less_than_4
|
||||
XORL SI, (AX)
|
||||
ADDQ $4, AX
|
||||
|
||||
less_than_4:
|
||||
TESTQ $2, CX
|
||||
JZ less_than_2
|
||||
XORW SI, (AX)
|
||||
ROLL $16, SI
|
||||
ADDQ $2, AX
|
||||
|
||||
less_than_2:
|
||||
TESTQ $1, CX
|
||||
JZ done
|
||||
XORB SI, (AX)
|
||||
ROLL $24, SI
|
||||
|
||||
done:
|
||||
MOVL SI, ret+24(FP)
|
||||
RET
|
||||
72
vendor/github.com/coder/websocket/mask_arm64.s
generated
vendored
72
vendor/github.com/coder/websocket/mask_arm64.s
generated
vendored
@@ -1,72 +0,0 @@
|
||||
#include "textflag.h"
|
||||
|
||||
// func maskAsm(b *byte, len int, key uint32)
|
||||
TEXT ·maskAsm(SB), NOSPLIT, $0-28
|
||||
// R0 = b
|
||||
// R1 = len
|
||||
// R3 = key (uint32)
|
||||
// R2 = uint64(key)<<32 | uint64(key)
|
||||
MOVD b_ptr+0(FP), R0
|
||||
MOVD b_len+8(FP), R1
|
||||
MOVWU key+16(FP), R3
|
||||
MOVD R3, R2
|
||||
ORR R2<<32, R2, R2
|
||||
VDUP R2, V0.D2
|
||||
CMP $64, R1
|
||||
BLT less_than_64
|
||||
|
||||
loop_64:
|
||||
VLD1 (R0), [V1.B16, V2.B16, V3.B16, V4.B16]
|
||||
VEOR V1.B16, V0.B16, V1.B16
|
||||
VEOR V2.B16, V0.B16, V2.B16
|
||||
VEOR V3.B16, V0.B16, V3.B16
|
||||
VEOR V4.B16, V0.B16, V4.B16
|
||||
VST1.P [V1.B16, V2.B16, V3.B16, V4.B16], 64(R0)
|
||||
SUBS $64, R1
|
||||
CMP $64, R1
|
||||
BGE loop_64
|
||||
|
||||
less_than_64:
|
||||
CBZ R1, end
|
||||
TBZ $5, R1, less_than_32
|
||||
VLD1 (R0), [V1.B16, V2.B16]
|
||||
VEOR V1.B16, V0.B16, V1.B16
|
||||
VEOR V2.B16, V0.B16, V2.B16
|
||||
VST1.P [V1.B16, V2.B16], 32(R0)
|
||||
|
||||
less_than_32:
|
||||
TBZ $4, R1, less_than_16
|
||||
LDP (R0), (R11, R12)
|
||||
EOR R11, R2, R11
|
||||
EOR R12, R2, R12
|
||||
STP.P (R11, R12), 16(R0)
|
||||
|
||||
less_than_16:
|
||||
TBZ $3, R1, less_than_8
|
||||
MOVD (R0), R11
|
||||
EOR R2, R11, R11
|
||||
MOVD.P R11, 8(R0)
|
||||
|
||||
less_than_8:
|
||||
TBZ $2, R1, less_than_4
|
||||
MOVWU (R0), R11
|
||||
EORW R2, R11, R11
|
||||
MOVWU.P R11, 4(R0)
|
||||
|
||||
less_than_4:
|
||||
TBZ $1, R1, less_than_2
|
||||
MOVHU (R0), R11
|
||||
EORW R3, R11, R11
|
||||
MOVHU.P R11, 2(R0)
|
||||
RORW $16, R3
|
||||
|
||||
less_than_2:
|
||||
TBZ $0, R1, end
|
||||
MOVBU (R0), R11
|
||||
EORW R3, R11, R11
|
||||
MOVBU.P R11, 1(R0)
|
||||
RORW $8, R3
|
||||
|
||||
end:
|
||||
MOVWU R3, ret+24(FP)
|
||||
RET
|
||||
26
vendor/github.com/coder/websocket/mask_asm.go
generated
vendored
26
vendor/github.com/coder/websocket/mask_asm.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
//go:build amd64 || arm64
|
||||
|
||||
package websocket
|
||||
|
||||
func mask(b []byte, key uint32) uint32 {
|
||||
// TODO: Will enable in v1.9.0.
|
||||
return maskGo(b, key)
|
||||
/*
|
||||
if len(b) > 0 {
|
||||
return maskAsm(&b[0], len(b), key)
|
||||
}
|
||||
return key
|
||||
*/
|
||||
}
|
||||
|
||||
// @nhooyr: I am not confident that the amd64 or the arm64 implementations of this
|
||||
// function are perfect. There are almost certainly missing optimizations or
|
||||
// opportunities for simplification. I'm confident there are no bugs though.
|
||||
// For example, the arm64 implementation doesn't align memory like the amd64.
|
||||
// Or the amd64 implementation could use AVX512 instead of just AVX2.
|
||||
// The AVX2 code I had to disable anyway as it wasn't performing as expected.
|
||||
// See https://github.com/nhooyr/websocket/pull/326#issuecomment-1771138049
|
||||
//
|
||||
//go:noescape
|
||||
//lint:ignore U1000 disabled till v1.9.0
|
||||
func maskAsm(b *byte, len int, key uint32) uint32
|
||||
7
vendor/github.com/coder/websocket/mask_go.go
generated
vendored
7
vendor/github.com/coder/websocket/mask_go.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
//go:build !amd64 && !arm64 && !js
|
||||
|
||||
package websocket
|
||||
|
||||
func mask(b []byte, key uint32) uint32 {
|
||||
return maskGo(b, key)
|
||||
}
|
||||
233
vendor/github.com/coder/websocket/netconn.go
generated
vendored
233
vendor/github.com/coder/websocket/netconn.go
generated
vendored
@@ -1,233 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NetConn converts a *websocket.Conn into a net.Conn.
|
||||
//
|
||||
// It's for tunneling arbitrary protocols over WebSockets.
|
||||
// Few users of the library will need this but it's tricky to implement
|
||||
// correctly and so provided in the library.
|
||||
// See https://github.com/nhooyr/websocket/issues/100.
|
||||
//
|
||||
// Every Write to the net.Conn will correspond to a message write of
|
||||
// the given type on *websocket.Conn.
|
||||
//
|
||||
// The passed ctx bounds the lifetime of the net.Conn. If cancelled,
|
||||
// all reads and writes on the net.Conn will be cancelled.
|
||||
//
|
||||
// If a message is read that is not of the correct type, the connection
|
||||
// will be closed with StatusUnsupportedData and an error will be returned.
|
||||
//
|
||||
// Close will close the *websocket.Conn with StatusNormalClosure.
|
||||
//
|
||||
// When a deadline is hit and there is an active read or write goroutine, the
|
||||
// connection will be closed. This is different from most net.Conn implementations
|
||||
// where only the reading/writing goroutines are interrupted but the connection
|
||||
// is kept alive.
|
||||
//
|
||||
// The Addr methods will return the real addresses for connections obtained
|
||||
// from websocket.Accept. But for connections obtained from websocket.Dial, a mock net.Addr
|
||||
// will be returned that gives "websocket" for Network() and "websocket/unknown-addr" for
|
||||
// String(). This is because websocket.Dial only exposes a io.ReadWriteCloser instead of the
|
||||
// full net.Conn to us.
|
||||
//
|
||||
// When running as WASM, the Addr methods will always return the mock address described above.
|
||||
//
|
||||
// A received StatusNormalClosure or StatusGoingAway close frame will be translated to
|
||||
// io.EOF when reading.
|
||||
//
|
||||
// Furthermore, the ReadLimit is set to -1 to disable it.
|
||||
func NetConn(ctx context.Context, c *Conn, msgType MessageType) net.Conn {
|
||||
c.SetReadLimit(-1)
|
||||
|
||||
nc := &netConn{
|
||||
c: c,
|
||||
msgType: msgType,
|
||||
readMu: newMu(c),
|
||||
writeMu: newMu(c),
|
||||
}
|
||||
|
||||
nc.writeCtx, nc.writeCancel = context.WithCancel(ctx)
|
||||
nc.readCtx, nc.readCancel = context.WithCancel(ctx)
|
||||
|
||||
nc.writeTimer = time.AfterFunc(math.MaxInt64, func() {
|
||||
if !nc.writeMu.tryLock() {
|
||||
// If the lock cannot be acquired, then there is an
|
||||
// active write goroutine and so we should cancel the context.
|
||||
nc.writeCancel()
|
||||
return
|
||||
}
|
||||
defer nc.writeMu.unlock()
|
||||
|
||||
// Prevents future writes from writing until the deadline is reset.
|
||||
nc.writeExpired.Store(1)
|
||||
})
|
||||
if !nc.writeTimer.Stop() {
|
||||
<-nc.writeTimer.C
|
||||
}
|
||||
|
||||
nc.readTimer = time.AfterFunc(math.MaxInt64, func() {
|
||||
if !nc.readMu.tryLock() {
|
||||
// If the lock cannot be acquired, then there is an
|
||||
// active read goroutine and so we should cancel the context.
|
||||
nc.readCancel()
|
||||
return
|
||||
}
|
||||
defer nc.readMu.unlock()
|
||||
|
||||
// Prevents future reads from reading until the deadline is reset.
|
||||
nc.readExpired.Store(1)
|
||||
})
|
||||
if !nc.readTimer.Stop() {
|
||||
<-nc.readTimer.C
|
||||
}
|
||||
|
||||
return nc
|
||||
}
|
||||
|
||||
type netConn struct {
|
||||
c *Conn
|
||||
msgType MessageType
|
||||
|
||||
writeTimer *time.Timer
|
||||
writeMu *mu
|
||||
writeExpired atomic.Int64
|
||||
writeCtx context.Context
|
||||
writeCancel context.CancelFunc
|
||||
|
||||
readTimer *time.Timer
|
||||
readMu *mu
|
||||
readExpired atomic.Int64
|
||||
readCtx context.Context
|
||||
readCancel context.CancelFunc
|
||||
readEOFed bool
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
var _ net.Conn = &netConn{}
|
||||
|
||||
func (nc *netConn) Close() error {
|
||||
nc.writeTimer.Stop()
|
||||
nc.writeCancel()
|
||||
nc.readTimer.Stop()
|
||||
nc.readCancel()
|
||||
return nc.c.Close(StatusNormalClosure, "")
|
||||
}
|
||||
|
||||
func (nc *netConn) Write(p []byte) (int, error) {
|
||||
nc.writeMu.forceLock()
|
||||
defer nc.writeMu.unlock()
|
||||
|
||||
if nc.writeExpired.Load() == 1 {
|
||||
return 0, fmt.Errorf("failed to write: %w", context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
err := nc.c.Write(nc.writeCtx, nc.msgType, p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (nc *netConn) Read(p []byte) (int, error) {
|
||||
nc.readMu.forceLock()
|
||||
defer nc.readMu.unlock()
|
||||
|
||||
for {
|
||||
n, err := nc.read(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (nc *netConn) read(p []byte) (int, error) {
|
||||
if nc.readExpired.Load() == 1 {
|
||||
return 0, fmt.Errorf("failed to read: %w", context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
if nc.readEOFed {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if nc.reader == nil {
|
||||
typ, r, err := nc.c.Reader(nc.readCtx)
|
||||
if err != nil {
|
||||
switch CloseStatus(err) {
|
||||
case StatusNormalClosure, StatusGoingAway:
|
||||
nc.readEOFed = true
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
if typ != nc.msgType {
|
||||
err := fmt.Errorf("unexpected frame type read (expected %v): %v", nc.msgType, typ)
|
||||
nc.c.Close(StatusUnsupportedData, err.Error())
|
||||
return 0, err
|
||||
}
|
||||
nc.reader = r
|
||||
}
|
||||
|
||||
n, err := nc.reader.Read(p)
|
||||
if err == io.EOF {
|
||||
nc.reader = nil
|
||||
err = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
type websocketAddr struct{}
|
||||
|
||||
func (a websocketAddr) Network() string {
|
||||
return "websocket"
|
||||
}
|
||||
|
||||
func (a websocketAddr) String() string {
|
||||
return "websocket/unknown-addr"
|
||||
}
|
||||
|
||||
func (nc *netConn) SetDeadline(t time.Time) error {
|
||||
nc.SetWriteDeadline(t)
|
||||
nc.SetReadDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nc *netConn) SetWriteDeadline(t time.Time) error {
|
||||
nc.writeExpired.Store(0)
|
||||
if t.IsZero() {
|
||||
nc.writeTimer.Stop()
|
||||
} else {
|
||||
dur := time.Until(t)
|
||||
if dur <= 0 {
|
||||
dur = 1
|
||||
}
|
||||
nc.writeTimer.Reset(dur)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nc *netConn) SetReadDeadline(t time.Time) error {
|
||||
nc.readExpired.Store(0)
|
||||
if t.IsZero() {
|
||||
nc.readTimer.Stop()
|
||||
} else {
|
||||
dur := time.Until(t)
|
||||
if dur <= 0 {
|
||||
dur = 1
|
||||
}
|
||||
nc.readTimer.Reset(dur)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
11
vendor/github.com/coder/websocket/netconn_js.go
generated
vendored
11
vendor/github.com/coder/websocket/netconn_js.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import "net"
|
||||
|
||||
func (nc *netConn) RemoteAddr() net.Addr {
|
||||
return websocketAddr{}
|
||||
}
|
||||
|
||||
func (nc *netConn) LocalAddr() net.Addr {
|
||||
return websocketAddr{}
|
||||
}
|
||||
19
vendor/github.com/coder/websocket/netconn_notjs.go
generated
vendored
19
vendor/github.com/coder/websocket/netconn_notjs.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import "net"
|
||||
|
||||
func (nc *netConn) RemoteAddr() net.Addr {
|
||||
if unc, ok := nc.c.rwc.(net.Conn); ok {
|
||||
return unc.RemoteAddr()
|
||||
}
|
||||
return websocketAddr{}
|
||||
}
|
||||
|
||||
func (nc *netConn) LocalAddr() net.Addr {
|
||||
if unc, ok := nc.c.rwc.(net.Conn); ok {
|
||||
return unc.LocalAddr()
|
||||
}
|
||||
return websocketAddr{}
|
||||
}
|
||||
540
vendor/github.com/coder/websocket/read.go
generated
vendored
540
vendor/github.com/coder/websocket/read.go
generated
vendored
@@ -1,540 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
"github.com/coder/websocket/internal/util"
|
||||
)
|
||||
|
||||
// Reader reads from the connection until there is a WebSocket
|
||||
// data message to be read. It will handle ping, pong and close frames as appropriate.
|
||||
//
|
||||
// It returns the type of the message and an io.Reader to read it.
|
||||
// The passed context will also bound the reader.
|
||||
// Ensure you read to EOF otherwise the connection will hang.
|
||||
//
|
||||
// Call CloseRead if you do not expect any data messages from the peer.
|
||||
//
|
||||
// Only one Reader may be open at a time.
|
||||
//
|
||||
// If you need a separate timeout on the Reader call and the Read itself,
|
||||
// use time.AfterFunc to cancel the context passed in.
|
||||
// See https://github.com/nhooyr/websocket/issues/87#issue-451703332
|
||||
// Most users should not need this.
|
||||
func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error) {
|
||||
return c.reader(ctx)
|
||||
}
|
||||
|
||||
// Read is a convenience method around Reader to read a single message
|
||||
// from the connection.
|
||||
func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error) {
|
||||
typ, r, err := c.Reader(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(r)
|
||||
return typ, b, err
|
||||
}
|
||||
|
||||
// CloseRead starts a goroutine to read from the connection until it is closed
|
||||
// or a data message is received.
|
||||
//
|
||||
// Once CloseRead is called you cannot read any messages from the connection.
|
||||
// The returned context will be cancelled when the connection is closed.
|
||||
//
|
||||
// If a data message is received, the connection will be closed with StatusPolicyViolation.
|
||||
//
|
||||
// Call CloseRead when you do not expect to read any more messages.
|
||||
// Since it actively reads from the connection, it will ensure that ping, pong and close
|
||||
// frames are responded to. This means c.Ping and c.Close will still work as expected.
|
||||
//
|
||||
// This function is idempotent.
|
||||
func (c *Conn) CloseRead(ctx context.Context) context.Context {
|
||||
c.closeReadMu.Lock()
|
||||
ctx2 := c.closeReadCtx
|
||||
if ctx2 != nil {
|
||||
c.closeReadMu.Unlock()
|
||||
return ctx2
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
c.closeReadCtx = ctx
|
||||
c.closeReadDone = make(chan struct{})
|
||||
c.closeReadMu.Unlock()
|
||||
|
||||
go func() {
|
||||
defer close(c.closeReadDone)
|
||||
defer cancel()
|
||||
defer c.close()
|
||||
_, _, err := c.Reader(ctx)
|
||||
if err == nil {
|
||||
c.Close(StatusPolicyViolation, "unexpected data message")
|
||||
}
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
|
||||
// SetReadLimit sets the max number of bytes to read for a single message.
|
||||
// It applies to the Reader and Read methods.
|
||||
//
|
||||
// By default, the connection has a message read limit of 32768 bytes.
|
||||
//
|
||||
// When the limit is hit, reads return an error wrapping ErrMessageTooBig and
|
||||
// the connection is closed with StatusMessageTooBig.
|
||||
//
|
||||
// Set to -1 to disable.
|
||||
func (c *Conn) SetReadLimit(n int64) {
|
||||
if n >= 0 {
|
||||
// We read one more byte than the limit in case
|
||||
// there is a fin frame that needs to be read.
|
||||
n++
|
||||
}
|
||||
|
||||
c.msgReader.limitReader.limit.Store(n)
|
||||
}
|
||||
|
||||
const defaultReadLimit = 32768
|
||||
|
||||
func newMsgReader(c *Conn) *msgReader {
|
||||
mr := &msgReader{
|
||||
c: c,
|
||||
fin: true,
|
||||
}
|
||||
mr.readFunc = mr.read
|
||||
|
||||
mr.limitReader = newLimitReader(c, mr.readFunc, defaultReadLimit+1)
|
||||
return mr
|
||||
}
|
||||
|
||||
func (mr *msgReader) resetFlate() {
|
||||
if mr.flateContextTakeover() {
|
||||
if mr.dict == nil {
|
||||
mr.dict = &slidingWindow{}
|
||||
}
|
||||
mr.dict.init(32768)
|
||||
}
|
||||
if mr.flateBufio == nil {
|
||||
mr.flateBufio = getBufioReader(mr.readFunc)
|
||||
}
|
||||
|
||||
if mr.flateContextTakeover() {
|
||||
mr.flateReader = getFlateReader(mr.flateBufio, mr.dict.buf)
|
||||
} else {
|
||||
mr.flateReader = getFlateReader(mr.flateBufio, nil)
|
||||
}
|
||||
mr.limitReader.r = mr.flateReader
|
||||
mr.flateTail.Reset(deflateMessageTail)
|
||||
}
|
||||
|
||||
func (mr *msgReader) putFlateReader() {
|
||||
if mr.flateReader != nil {
|
||||
putFlateReader(mr.flateReader)
|
||||
mr.flateReader = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *msgReader) close() {
|
||||
mr.c.readMu.forceLock()
|
||||
mr.putFlateReader()
|
||||
if mr.dict != nil {
|
||||
mr.dict.close()
|
||||
mr.dict = nil
|
||||
}
|
||||
if mr.flateBufio != nil {
|
||||
putBufioReader(mr.flateBufio)
|
||||
}
|
||||
|
||||
if mr.c.client {
|
||||
putBufioReader(mr.c.br)
|
||||
mr.c.br = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *msgReader) flateContextTakeover() bool {
|
||||
if mr.c.client {
|
||||
return !mr.c.copts.serverNoContextTakeover
|
||||
}
|
||||
return !mr.c.copts.clientNoContextTakeover
|
||||
}
|
||||
|
||||
func (c *Conn) readRSV1Illegal(h header) bool {
|
||||
// If compression is disabled, rsv1 is illegal.
|
||||
if !c.flate() {
|
||||
return true
|
||||
}
|
||||
// rsv1 is only allowed on data frames beginning messages.
|
||||
if h.opcode != opText && h.opcode != opBinary {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Conn) readLoop(ctx context.Context) (header, error) {
|
||||
for {
|
||||
h, err := c.readFrameHeader(ctx)
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
|
||||
if h.rsv1 && c.readRSV1Illegal(h) || h.rsv2 || h.rsv3 {
|
||||
err := fmt.Errorf("received header with unexpected rsv bits set: %v:%v:%v", h.rsv1, h.rsv2, h.rsv3)
|
||||
c.writeError(StatusProtocolError, err)
|
||||
return header{}, err
|
||||
}
|
||||
|
||||
if !c.client && !h.masked {
|
||||
return header{}, errors.New("received unmasked frame from client")
|
||||
}
|
||||
|
||||
switch h.opcode {
|
||||
case opClose, opPing, opPong:
|
||||
err = c.handleControl(ctx, h)
|
||||
if err != nil {
|
||||
// Pass through CloseErrors when receiving a close frame.
|
||||
if h.opcode == opClose && CloseStatus(err) != -1 {
|
||||
return header{}, err
|
||||
}
|
||||
return header{}, fmt.Errorf("failed to handle control frame %v: %w", h.opcode, err)
|
||||
}
|
||||
case opContinuation, opText, opBinary:
|
||||
return h, nil
|
||||
default:
|
||||
err := fmt.Errorf("received unknown opcode %v", h.opcode)
|
||||
c.writeError(StatusProtocolError, err)
|
||||
return header{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepareRead sets the readTimeout context and returns a done function
|
||||
// to be called after the read is done. It also returns an error if the
|
||||
// connection is closed. The reference to the error is used to assign
|
||||
// an error depending on if the connection closed or the context timed
|
||||
// out during use. Typically, the referenced error is a named return
|
||||
// variable of the function calling this method.
|
||||
func (c *Conn) prepareRead(ctx context.Context, err *error) (func(), error) {
|
||||
select {
|
||||
case <-c.closed:
|
||||
return nil, net.ErrClosed
|
||||
default:
|
||||
}
|
||||
c.setupReadTimeout(ctx)
|
||||
|
||||
done := func() {
|
||||
c.clearReadTimeout()
|
||||
select {
|
||||
case <-c.closed:
|
||||
if *err != nil {
|
||||
*err = net.ErrClosed
|
||||
}
|
||||
default:
|
||||
}
|
||||
if *err != nil && ctx.Err() != nil {
|
||||
*err = ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
c.closeStateMu.Lock()
|
||||
closeReceivedErr := c.closeReceivedErr
|
||||
c.closeStateMu.Unlock()
|
||||
if closeReceivedErr != nil {
|
||||
defer done()
|
||||
return nil, closeReceivedErr
|
||||
}
|
||||
|
||||
return done, nil
|
||||
}
|
||||
|
||||
func (c *Conn) readFrameHeader(ctx context.Context) (_ header, err error) {
|
||||
readDone, err := c.prepareRead(ctx, &err)
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
defer readDone()
|
||||
|
||||
h, err := readFrameHeader(c.br, c.readHeaderBuf[:])
|
||||
if err != nil {
|
||||
return header{}, err
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (c *Conn) readFramePayload(ctx context.Context, p []byte) (_ int, err error) {
|
||||
readDone, err := c.prepareRead(ctx, &err)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer readDone()
|
||||
|
||||
n, err := io.ReadFull(c.br, p)
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("failed to read frame payload: %w", err)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Conn) handleControl(ctx context.Context, h header) (err error) {
|
||||
if h.payloadLength < 0 || h.payloadLength > maxControlPayload {
|
||||
err := fmt.Errorf("received control frame payload with invalid length: %d", h.payloadLength)
|
||||
c.writeError(StatusProtocolError, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !h.fin {
|
||||
err := errors.New("received fragmented control frame")
|
||||
c.writeError(StatusProtocolError, err)
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
b := c.readControlBuf[:h.payloadLength]
|
||||
_, err = c.readFramePayload(ctx, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.masked {
|
||||
mask(b, h.maskKey)
|
||||
}
|
||||
|
||||
switch h.opcode {
|
||||
case opPing:
|
||||
if c.onPingReceived != nil {
|
||||
if !c.onPingReceived(ctx, b) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return c.writeControl(ctx, opPong, b)
|
||||
case opPong:
|
||||
if c.onPongReceived != nil {
|
||||
c.onPongReceived(ctx, b)
|
||||
}
|
||||
c.activePingsMu.Lock()
|
||||
pong, ok := c.activePings[string(b)]
|
||||
c.activePingsMu.Unlock()
|
||||
if ok {
|
||||
select {
|
||||
case pong <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// opClose
|
||||
|
||||
ce, err := parseClosePayload(b)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("received invalid close payload: %w", err)
|
||||
c.writeError(StatusProtocolError, err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = fmt.Errorf("received close frame: %w", ce)
|
||||
c.closeStateMu.Lock()
|
||||
c.closeReceivedErr = err
|
||||
closeSent := c.closeSentErr != nil
|
||||
c.closeStateMu.Unlock()
|
||||
|
||||
// Only unlock readMu if this connection is being closed becaue
|
||||
// c.close will try to acquire the readMu lock. We unlock for
|
||||
// writeClose as well because it may also call c.close.
|
||||
if !closeSent {
|
||||
c.readMu.unlock()
|
||||
_ = c.writeClose(ce.Code, ce.Reason)
|
||||
}
|
||||
if !c.casClosing() {
|
||||
c.readMu.unlock()
|
||||
_ = c.close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) reader(ctx context.Context) (_ MessageType, _ io.Reader, err error) {
|
||||
defer errd.Wrap(&err, "failed to get reader")
|
||||
|
||||
err = c.readMu.lock(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer c.readMu.unlock()
|
||||
|
||||
if !c.msgReader.fin {
|
||||
return 0, nil, errors.New("previous message not read to completion")
|
||||
}
|
||||
|
||||
h, err := c.readLoop(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if h.opcode == opContinuation {
|
||||
err := errors.New("received continuation frame without text or binary frame")
|
||||
c.writeError(StatusProtocolError, err)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
c.msgReader.reset(ctx, h)
|
||||
|
||||
return MessageType(h.opcode), c.msgReader, nil
|
||||
}
|
||||
|
||||
type msgReader struct {
|
||||
c *Conn
|
||||
|
||||
ctx context.Context
|
||||
flate bool
|
||||
flateReader io.Reader
|
||||
flateBufio *bufio.Reader
|
||||
flateTail strings.Reader
|
||||
limitReader *limitReader
|
||||
dict *slidingWindow
|
||||
|
||||
fin bool
|
||||
payloadLength int64
|
||||
maskKey uint32
|
||||
|
||||
// util.ReaderFunc(mr.Read) to avoid continuous allocations.
|
||||
readFunc util.ReaderFunc
|
||||
}
|
||||
|
||||
func (mr *msgReader) reset(ctx context.Context, h header) {
|
||||
mr.ctx = ctx
|
||||
mr.flate = h.rsv1
|
||||
mr.limitReader.reset(mr.readFunc)
|
||||
|
||||
if mr.flate {
|
||||
mr.resetFlate()
|
||||
}
|
||||
|
||||
mr.setFrame(h)
|
||||
}
|
||||
|
||||
func (mr *msgReader) setFrame(h header) {
|
||||
mr.fin = h.fin
|
||||
mr.payloadLength = h.payloadLength
|
||||
mr.maskKey = h.maskKey
|
||||
}
|
||||
|
||||
func (mr *msgReader) Read(p []byte) (n int, err error) {
|
||||
err = mr.c.readMu.lock(mr.ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read: %w", err)
|
||||
}
|
||||
defer mr.c.readMu.unlock()
|
||||
|
||||
n, err = mr.limitReader.Read(p)
|
||||
if mr.flate && mr.flateContextTakeover() {
|
||||
p = p[:n]
|
||||
mr.dict.write(p)
|
||||
}
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) && mr.fin && mr.flate {
|
||||
mr.putFlateReader()
|
||||
return n, io.EOF
|
||||
}
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("failed to read: %w", err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (mr *msgReader) read(p []byte) (int, error) {
|
||||
for {
|
||||
if mr.payloadLength == 0 {
|
||||
if mr.fin {
|
||||
if mr.flate {
|
||||
return mr.flateTail.Read(p)
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
h, err := mr.c.readLoop(mr.ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if h.opcode != opContinuation {
|
||||
err := errors.New("received new data message without finishing the previous message")
|
||||
mr.c.writeError(StatusProtocolError, err)
|
||||
return 0, err
|
||||
}
|
||||
mr.setFrame(h)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if int64(len(p)) > mr.payloadLength {
|
||||
p = p[:mr.payloadLength]
|
||||
}
|
||||
|
||||
n, err := mr.c.readFramePayload(mr.ctx, p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
mr.payloadLength -= int64(n)
|
||||
|
||||
if !mr.c.client {
|
||||
mr.maskKey = mask(p, mr.maskKey)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
type limitReader struct {
|
||||
c *Conn
|
||||
r io.Reader
|
||||
limit atomic.Int64
|
||||
n int64
|
||||
}
|
||||
|
||||
func newLimitReader(c *Conn, r io.Reader, limit int64) *limitReader {
|
||||
lr := &limitReader{
|
||||
c: c,
|
||||
}
|
||||
lr.limit.Store(limit)
|
||||
lr.reset(r)
|
||||
return lr
|
||||
}
|
||||
|
||||
func (lr *limitReader) reset(r io.Reader) {
|
||||
lr.n = lr.limit.Load()
|
||||
lr.r = r
|
||||
}
|
||||
|
||||
func (lr *limitReader) Read(p []byte) (int, error) {
|
||||
if lr.n < 0 {
|
||||
return lr.r.Read(p)
|
||||
}
|
||||
|
||||
if lr.n == 0 {
|
||||
reason := fmt.Errorf("read limited at %d bytes", lr.limit.Load())
|
||||
lr.c.writeError(StatusMessageTooBig, reason)
|
||||
return 0, fmt.Errorf("%w: %v", ErrMessageTooBig, reason)
|
||||
}
|
||||
|
||||
if int64(len(p)) > lr.n {
|
||||
p = p[:lr.n]
|
||||
}
|
||||
n, err := lr.r.Read(p)
|
||||
lr.n -= int64(n)
|
||||
if lr.n < 0 {
|
||||
lr.n = 0
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
91
vendor/github.com/coder/websocket/stringer.go
generated
vendored
91
vendor/github.com/coder/websocket/stringer.go
generated
vendored
@@ -1,91 +0,0 @@
|
||||
// Code generated by "stringer -type=opcode,MessageType,StatusCode -output=stringer.go"; DO NOT EDIT.
|
||||
|
||||
package websocket
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[opContinuation-0]
|
||||
_ = x[opText-1]
|
||||
_ = x[opBinary-2]
|
||||
_ = x[opClose-8]
|
||||
_ = x[opPing-9]
|
||||
_ = x[opPong-10]
|
||||
}
|
||||
|
||||
const (
|
||||
_opcode_name_0 = "opContinuationopTextopBinary"
|
||||
_opcode_name_1 = "opCloseopPingopPong"
|
||||
)
|
||||
|
||||
var (
|
||||
_opcode_index_0 = [...]uint8{0, 14, 20, 28}
|
||||
_opcode_index_1 = [...]uint8{0, 7, 13, 19}
|
||||
)
|
||||
|
||||
func (i opcode) String() string {
|
||||
switch {
|
||||
case 0 <= i && i <= 2:
|
||||
return _opcode_name_0[_opcode_index_0[i]:_opcode_index_0[i+1]]
|
||||
case 8 <= i && i <= 10:
|
||||
i -= 8
|
||||
return _opcode_name_1[_opcode_index_1[i]:_opcode_index_1[i+1]]
|
||||
default:
|
||||
return "opcode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[MessageText-1]
|
||||
_ = x[MessageBinary-2]
|
||||
}
|
||||
|
||||
const _MessageType_name = "MessageTextMessageBinary"
|
||||
|
||||
var _MessageType_index = [...]uint8{0, 11, 24}
|
||||
|
||||
func (i MessageType) String() string {
|
||||
i -= 1
|
||||
if i < 0 || i >= MessageType(len(_MessageType_index)-1) {
|
||||
return "MessageType(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
}
|
||||
return _MessageType_name[_MessageType_index[i]:_MessageType_index[i+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[StatusNormalClosure-1000]
|
||||
_ = x[StatusGoingAway-1001]
|
||||
_ = x[StatusProtocolError-1002]
|
||||
_ = x[StatusUnsupportedData-1003]
|
||||
_ = x[statusReserved-1004]
|
||||
_ = x[StatusNoStatusRcvd-1005]
|
||||
_ = x[StatusAbnormalClosure-1006]
|
||||
_ = x[StatusInvalidFramePayloadData-1007]
|
||||
_ = x[StatusPolicyViolation-1008]
|
||||
_ = x[StatusMessageTooBig-1009]
|
||||
_ = x[StatusMandatoryExtension-1010]
|
||||
_ = x[StatusInternalError-1011]
|
||||
_ = x[StatusServiceRestart-1012]
|
||||
_ = x[StatusTryAgainLater-1013]
|
||||
_ = x[StatusBadGateway-1014]
|
||||
_ = x[StatusTLSHandshake-1015]
|
||||
}
|
||||
|
||||
const _StatusCode_name = "StatusNormalClosureStatusGoingAwayStatusProtocolErrorStatusUnsupportedDatastatusReservedStatusNoStatusRcvdStatusAbnormalClosureStatusInvalidFramePayloadDataStatusPolicyViolationStatusMessageTooBigStatusMandatoryExtensionStatusInternalErrorStatusServiceRestartStatusTryAgainLaterStatusBadGatewayStatusTLSHandshake"
|
||||
|
||||
var _StatusCode_index = [...]uint16{0, 19, 34, 53, 74, 88, 106, 127, 156, 177, 196, 220, 239, 259, 278, 294, 312}
|
||||
|
||||
func (i StatusCode) String() string {
|
||||
i -= 1000
|
||||
if i < 0 || i >= StatusCode(len(_StatusCode_index)-1) {
|
||||
return "StatusCode(" + strconv.FormatInt(int64(i+1000), 10) + ")"
|
||||
}
|
||||
return _StatusCode_name[_StatusCode_index[i]:_StatusCode_index[i+1]]
|
||||
}
|
||||
384
vendor/github.com/coder/websocket/write.go
generated
vendored
384
vendor/github.com/coder/websocket/write.go
generated
vendored
@@ -1,384 +0,0 @@
|
||||
//go:build !js
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/flate"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
"github.com/coder/websocket/internal/util"
|
||||
)
|
||||
|
||||
// Writer returns a writer bounded by the context that will write
|
||||
// a WebSocket message of type dataType to the connection.
|
||||
//
|
||||
// You must close the writer once you have written the entire message.
|
||||
//
|
||||
// Only one writer can be open at a time, multiple calls will block until the previous writer
|
||||
// is closed.
|
||||
func (c *Conn) Writer(ctx context.Context, typ MessageType) (io.WriteCloser, error) {
|
||||
w, err := c.writer(ctx, typ)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get writer: %w", err)
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Write writes a message to the connection.
|
||||
//
|
||||
// See the Writer method if you want to stream a message.
|
||||
//
|
||||
// If compression is disabled or the compression threshold is not met, then it
|
||||
// will write the message in a single frame.
|
||||
func (c *Conn) Write(ctx context.Context, typ MessageType, p []byte) error {
|
||||
_, err := c.write(ctx, typ, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write msg: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type msgWriter struct {
|
||||
c *Conn
|
||||
|
||||
mu *mu
|
||||
writeMu *mu
|
||||
closed bool
|
||||
|
||||
ctx context.Context
|
||||
opcode opcode
|
||||
flate bool
|
||||
|
||||
trimWriter *trimLastFourBytesWriter
|
||||
flateWriter *flate.Writer
|
||||
}
|
||||
|
||||
func newMsgWriter(c *Conn) *msgWriter {
|
||||
mw := &msgWriter{
|
||||
c: c,
|
||||
mu: newMu(c),
|
||||
writeMu: newMu(c),
|
||||
}
|
||||
return mw
|
||||
}
|
||||
|
||||
func (mw *msgWriter) ensureFlate() {
|
||||
if mw.trimWriter == nil {
|
||||
mw.trimWriter = &trimLastFourBytesWriter{
|
||||
w: util.WriterFunc(mw.write),
|
||||
}
|
||||
}
|
||||
|
||||
if mw.flateWriter == nil {
|
||||
mw.flateWriter = getFlateWriter(mw.trimWriter)
|
||||
}
|
||||
mw.flate = true
|
||||
}
|
||||
|
||||
func (mw *msgWriter) flateContextTakeover() bool {
|
||||
if mw.c.client {
|
||||
return !mw.c.copts.clientNoContextTakeover
|
||||
}
|
||||
return !mw.c.copts.serverNoContextTakeover
|
||||
}
|
||||
|
||||
func (c *Conn) writer(ctx context.Context, typ MessageType) (io.WriteCloser, error) {
|
||||
err := c.msgWriter.reset(ctx, typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.msgWriter, nil
|
||||
}
|
||||
|
||||
func (c *Conn) write(ctx context.Context, typ MessageType, p []byte) (int, error) {
|
||||
mw, err := c.writer(ctx, typ)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !c.flate() {
|
||||
defer c.msgWriter.mu.unlock()
|
||||
return c.writeFrame(ctx, true, false, c.msgWriter.opcode, p)
|
||||
}
|
||||
|
||||
n, err := mw.Write(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
err = mw.Close()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (mw *msgWriter) reset(ctx context.Context, typ MessageType) error {
|
||||
err := mw.mu.lock(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mw.ctx = ctx
|
||||
mw.opcode = opcode(typ)
|
||||
mw.flate = false
|
||||
mw.closed = false
|
||||
|
||||
mw.trimWriter.reset()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *msgWriter) putFlateWriter() {
|
||||
if mw.flateWriter != nil {
|
||||
putFlateWriter(mw.flateWriter)
|
||||
mw.flateWriter = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes the given bytes to the WebSocket connection.
|
||||
func (mw *msgWriter) Write(p []byte) (_ int, err error) {
|
||||
err = mw.writeMu.lock(mw.ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to write: %w", err)
|
||||
}
|
||||
defer mw.writeMu.unlock()
|
||||
|
||||
if mw.closed {
|
||||
return 0, errors.New("cannot use closed writer")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to write: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if mw.c.flate() {
|
||||
// Only enables flate if the length crosses the
|
||||
// threshold on the first frame
|
||||
if mw.opcode != opContinuation && len(p) >= mw.c.flateThreshold {
|
||||
mw.ensureFlate()
|
||||
}
|
||||
}
|
||||
|
||||
if mw.flate {
|
||||
return mw.flateWriter.Write(p)
|
||||
}
|
||||
|
||||
return mw.write(p)
|
||||
}
|
||||
|
||||
func (mw *msgWriter) write(p []byte) (int, error) {
|
||||
n, err := mw.c.writeFrame(mw.ctx, false, mw.flate, mw.opcode, p)
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("failed to write data frame: %w", err)
|
||||
}
|
||||
mw.opcode = opContinuation
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Close flushes the frame to the connection.
|
||||
func (mw *msgWriter) Close() (err error) {
|
||||
defer errd.Wrap(&err, "failed to close writer")
|
||||
|
||||
err = mw.writeMu.lock(mw.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mw.writeMu.unlock()
|
||||
|
||||
if mw.closed {
|
||||
return errors.New("writer already closed")
|
||||
}
|
||||
mw.closed = true
|
||||
|
||||
if mw.flate {
|
||||
err = mw.flateWriter.Flush()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to flush flate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = mw.c.writeFrame(mw.ctx, true, mw.flate, mw.opcode, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write fin frame: %w", err)
|
||||
}
|
||||
|
||||
if mw.flate && !mw.flateContextTakeover() {
|
||||
mw.putFlateWriter()
|
||||
}
|
||||
mw.mu.unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *msgWriter) close() {
|
||||
if mw.c.client {
|
||||
mw.c.writeFrameMu.forceLock()
|
||||
putBufioWriter(mw.c.bw)
|
||||
}
|
||||
|
||||
mw.writeMu.forceLock()
|
||||
mw.putFlateWriter()
|
||||
}
|
||||
|
||||
func (c *Conn) writeControl(ctx context.Context, opcode opcode, p []byte) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
_, err := c.writeFrame(ctx, true, false, opcode, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write control frame %v: %w", opcode, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeFrame handles all writes to the connection.
|
||||
func (c *Conn) writeFrame(ctx context.Context, fin bool, flate bool, opcode opcode, p []byte) (_ int, err error) {
|
||||
err = c.writeFrameMu.lock(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer c.writeFrameMu.unlock()
|
||||
|
||||
defer func() {
|
||||
if c.isClosed() && opcode == opClose {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
err = ctx.Err()
|
||||
} else if c.isClosed() {
|
||||
err = net.ErrClosed
|
||||
}
|
||||
err = fmt.Errorf("failed to write frame: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
c.closeStateMu.Lock()
|
||||
closeSentErr := c.closeSentErr
|
||||
c.closeStateMu.Unlock()
|
||||
if closeSentErr != nil {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
return 0, net.ErrClosed
|
||||
default:
|
||||
}
|
||||
c.setupWriteTimeout(ctx)
|
||||
defer c.clearWriteTimeout()
|
||||
|
||||
c.writeHeader.fin = fin
|
||||
c.writeHeader.opcode = opcode
|
||||
c.writeHeader.payloadLength = int64(len(p))
|
||||
|
||||
if c.client {
|
||||
c.writeHeader.masked = true
|
||||
_, err = io.ReadFull(rand.Reader, c.writeHeaderBuf[:4])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to generate masking key: %w", err)
|
||||
}
|
||||
c.writeHeader.maskKey = binary.LittleEndian.Uint32(c.writeHeaderBuf[:])
|
||||
}
|
||||
|
||||
c.writeHeader.rsv1 = false
|
||||
if flate && (opcode == opText || opcode == opBinary) {
|
||||
c.writeHeader.rsv1 = true
|
||||
}
|
||||
|
||||
err = writeFrameHeader(c.writeHeader, c.bw, c.writeHeaderBuf[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := c.writeFramePayload(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
if c.writeHeader.fin {
|
||||
err = c.bw.Flush()
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("failed to flush: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if opcode == opClose {
|
||||
c.closeStateMu.Lock()
|
||||
c.closeSentErr = fmt.Errorf("sent close frame: %w", net.ErrClosed)
|
||||
closeReceived := c.closeReceivedErr != nil
|
||||
c.closeStateMu.Unlock()
|
||||
|
||||
if closeReceived && !c.casClosing() {
|
||||
c.writeFrameMu.unlock()
|
||||
_ = c.close()
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Conn) writeFramePayload(p []byte) (n int, err error) {
|
||||
defer errd.Wrap(&err, "failed to write frame payload")
|
||||
|
||||
if !c.writeHeader.masked {
|
||||
return c.bw.Write(p)
|
||||
}
|
||||
|
||||
maskKey := c.writeHeader.maskKey
|
||||
for len(p) > 0 {
|
||||
// If the buffer is full, we need to flush.
|
||||
if c.bw.Available() == 0 {
|
||||
err = c.bw.Flush()
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
// Start of next write in the buffer.
|
||||
i := c.bw.Buffered()
|
||||
|
||||
j := min(len(p), c.bw.Available())
|
||||
|
||||
_, err := c.bw.Write(p[:j])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
maskKey = mask(c.writeBuf[i:c.bw.Buffered()], maskKey)
|
||||
|
||||
p = p[j:]
|
||||
n += j
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// extractBufioWriterBuf grabs the []byte backing a *bufio.Writer
|
||||
// and returns it.
|
||||
func extractBufioWriterBuf(bw *bufio.Writer, w io.Writer) []byte {
|
||||
var writeBuf []byte
|
||||
bw.Reset(util.WriterFunc(func(p2 []byte) (int, error) {
|
||||
writeBuf = p2[:cap(p2)]
|
||||
return len(p2), nil
|
||||
}))
|
||||
|
||||
bw.WriteByte(0)
|
||||
bw.Flush()
|
||||
|
||||
bw.Reset(w)
|
||||
|
||||
return writeBuf
|
||||
}
|
||||
|
||||
func (c *Conn) writeError(code StatusCode, err error) {
|
||||
c.writeClose(code, err.Error())
|
||||
}
|
||||
598
vendor/github.com/coder/websocket/ws_js.go
generated
vendored
598
vendor/github.com/coder/websocket/ws_js.go
generated
vendored
@@ -1,598 +0,0 @@
|
||||
package websocket // import "github.com/coder/websocket"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall/js"
|
||||
|
||||
"github.com/coder/websocket/internal/bpool"
|
||||
"github.com/coder/websocket/internal/wsjs"
|
||||
)
|
||||
|
||||
// opcode represents a WebSocket opcode.
|
||||
type opcode int
|
||||
|
||||
// https://tools.ietf.org/html/rfc6455#section-11.8.
|
||||
const (
|
||||
opContinuation opcode = iota
|
||||
opText
|
||||
opBinary
|
||||
// 3 - 7 are reserved for further non-control frames.
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
opClose
|
||||
opPing
|
||||
opPong
|
||||
// 11-16 are reserved for further control frames.
|
||||
)
|
||||
|
||||
// Conn provides a wrapper around the browser WebSocket API.
|
||||
type Conn struct {
|
||||
noCopy noCopy
|
||||
ws wsjs.WebSocket
|
||||
|
||||
// read limit for a message in bytes.
|
||||
msgReadLimit atomic.Int64
|
||||
|
||||
closeReadMu sync.Mutex
|
||||
closeReadCtx context.Context
|
||||
|
||||
closingMu sync.Mutex
|
||||
closeOnce sync.Once
|
||||
closed chan struct{}
|
||||
closeErrOnce sync.Once
|
||||
closeErr error
|
||||
closeWasClean bool
|
||||
|
||||
releaseOnClose func()
|
||||
releaseOnError func()
|
||||
releaseOnMessage func()
|
||||
|
||||
readSignal chan struct{}
|
||||
readBufMu sync.Mutex
|
||||
readBuf []wsjs.MessageEvent
|
||||
}
|
||||
|
||||
func (c *Conn) close(err error, wasClean bool) {
|
||||
c.closeOnce.Do(func() {
|
||||
runtime.SetFinalizer(c, nil)
|
||||
|
||||
if !wasClean {
|
||||
err = fmt.Errorf("unclean connection close: %w", err)
|
||||
}
|
||||
c.setCloseErr(err)
|
||||
c.closeWasClean = wasClean
|
||||
close(c.closed)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Conn) init() {
|
||||
c.closed = make(chan struct{})
|
||||
c.readSignal = make(chan struct{}, 1)
|
||||
|
||||
c.msgReadLimit.Store(32768)
|
||||
|
||||
c.releaseOnClose = c.ws.OnClose(func(e wsjs.CloseEvent) {
|
||||
err := CloseError{
|
||||
Code: StatusCode(e.Code),
|
||||
Reason: e.Reason,
|
||||
}
|
||||
// We do not know if we sent or received this close as
|
||||
// its possible the browser triggered it without us
|
||||
// explicitly sending it.
|
||||
c.close(err, e.WasClean)
|
||||
|
||||
c.releaseOnClose()
|
||||
c.releaseOnError()
|
||||
c.releaseOnMessage()
|
||||
})
|
||||
|
||||
c.releaseOnError = c.ws.OnError(func(v js.Value) {
|
||||
c.setCloseErr(errors.New(v.Get("message").String()))
|
||||
c.closeWithInternal()
|
||||
})
|
||||
|
||||
c.releaseOnMessage = c.ws.OnMessage(func(e wsjs.MessageEvent) {
|
||||
c.readBufMu.Lock()
|
||||
defer c.readBufMu.Unlock()
|
||||
|
||||
c.readBuf = append(c.readBuf, e)
|
||||
|
||||
// Lets the read goroutine know there is definitely something in readBuf.
|
||||
select {
|
||||
case c.readSignal <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
})
|
||||
|
||||
runtime.SetFinalizer(c, func(c *Conn) {
|
||||
c.setCloseErr(errors.New("connection garbage collected"))
|
||||
c.closeWithInternal()
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Conn) closeWithInternal() {
|
||||
c.Close(StatusInternalError, "something went wrong")
|
||||
}
|
||||
|
||||
// Read attempts to read a message from the connection.
|
||||
// The maximum time spent waiting is bounded by the context.
|
||||
func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error) {
|
||||
c.closeReadMu.Lock()
|
||||
closedRead := c.closeReadCtx != nil
|
||||
c.closeReadMu.Unlock()
|
||||
if closedRead {
|
||||
return 0, nil, errors.New("WebSocket connection read closed")
|
||||
}
|
||||
|
||||
typ, p, err := c.read(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("failed to read: %w", err)
|
||||
}
|
||||
readLimit := c.msgReadLimit.Load()
|
||||
if readLimit >= 0 && int64(len(p)) > readLimit {
|
||||
reason := fmt.Errorf("read limited at %d bytes", c.msgReadLimit.Load())
|
||||
c.Close(StatusMessageTooBig, reason.Error())
|
||||
return 0, nil, fmt.Errorf("%w: %v", ErrMessageTooBig, reason)
|
||||
}
|
||||
return typ, p, nil
|
||||
}
|
||||
|
||||
func (c *Conn) read(ctx context.Context) (MessageType, []byte, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.Close(StatusPolicyViolation, "read timed out")
|
||||
return 0, nil, ctx.Err()
|
||||
case <-c.readSignal:
|
||||
case <-c.closed:
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
|
||||
c.readBufMu.Lock()
|
||||
defer c.readBufMu.Unlock()
|
||||
|
||||
me := c.readBuf[0]
|
||||
// We copy the messages forward and decrease the size
|
||||
// of the slice to avoid reallocating.
|
||||
copy(c.readBuf, c.readBuf[1:])
|
||||
c.readBuf = c.readBuf[:len(c.readBuf)-1]
|
||||
|
||||
if len(c.readBuf) > 0 {
|
||||
// Next time we read, we'll grab the message.
|
||||
select {
|
||||
case c.readSignal <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
switch p := me.Data.(type) {
|
||||
case string:
|
||||
return MessageText, []byte(p), nil
|
||||
case []byte:
|
||||
return MessageBinary, p, nil
|
||||
default:
|
||||
panic("websocket: unexpected data type from wsjs OnMessage: " + reflect.TypeOf(me.Data).String())
|
||||
}
|
||||
}
|
||||
|
||||
// Ping is mocked out for Wasm.
|
||||
func (c *Conn) Ping(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes a message of the given type to the connection.
|
||||
// Always non blocking.
|
||||
func (c *Conn) Write(ctx context.Context, typ MessageType, p []byte) error {
|
||||
err := c.write(typ, p)
|
||||
if err != nil {
|
||||
// Have to ensure the WebSocket is closed after a write error
|
||||
// to match the Go API. It can only error if the message type
|
||||
// is unexpected or the passed bytes contain invalid UTF-8 for
|
||||
// MessageText.
|
||||
err := fmt.Errorf("failed to write: %w", err)
|
||||
c.setCloseErr(err)
|
||||
c.closeWithInternal()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) write(typ MessageType, p []byte) error {
|
||||
if c.isClosed() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
switch typ {
|
||||
case MessageBinary:
|
||||
return c.ws.SendBytes(p)
|
||||
case MessageText:
|
||||
return c.ws.SendText(string(p))
|
||||
default:
|
||||
return fmt.Errorf("unexpected message type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the WebSocket with the given code and reason.
|
||||
// It will wait until the peer responds with a close frame
|
||||
// or the connection is closed.
|
||||
// It thus performs the full WebSocket close handshake.
|
||||
func (c *Conn) Close(code StatusCode, reason string) error {
|
||||
err := c.exportedClose(code, reason)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to close WebSocket: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseNow closes the WebSocket connection without attempting a close handshake.
|
||||
// Use when you do not want the overhead of the close handshake.
|
||||
//
|
||||
// note: No different from Close(StatusGoingAway, "") in WASM as there is no way to close
|
||||
// a WebSocket without the close handshake.
|
||||
func (c *Conn) CloseNow() error {
|
||||
return c.Close(StatusGoingAway, "")
|
||||
}
|
||||
|
||||
func (c *Conn) exportedClose(code StatusCode, reason string) error {
|
||||
c.closingMu.Lock()
|
||||
defer c.closingMu.Unlock()
|
||||
|
||||
if c.isClosed() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
ce := fmt.Errorf("sent close: %w", CloseError{
|
||||
Code: code,
|
||||
Reason: reason,
|
||||
})
|
||||
|
||||
c.setCloseErr(ce)
|
||||
err := c.ws.Close(int(code), reason)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-c.closed
|
||||
if !c.closeWasClean {
|
||||
return c.closeErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subprotocol returns the negotiated subprotocol.
|
||||
// An empty string means the default protocol.
|
||||
func (c *Conn) Subprotocol() string {
|
||||
return c.ws.Subprotocol()
|
||||
}
|
||||
|
||||
// DialOptions represents the options available to pass to Dial.
|
||||
type DialOptions struct {
|
||||
// Subprotocols lists the subprotocols to negotiate with the server.
|
||||
Subprotocols []string
|
||||
}
|
||||
|
||||
// Dial creates a new WebSocket connection to the given url with the given options.
|
||||
// The passed context bounds the maximum time spent waiting for the connection to open.
|
||||
// The returned *http.Response is always nil or a mock. It's only in the signature
|
||||
// to match the core API.
|
||||
func Dial(ctx context.Context, url string, opts *DialOptions) (*Conn, *http.Response, error) {
|
||||
c, resp, err := dial(ctx, url, opts)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to WebSocket dial %q: %w", url, err)
|
||||
}
|
||||
return c, resp, nil
|
||||
}
|
||||
|
||||
func dial(ctx context.Context, url string, opts *DialOptions) (*Conn, *http.Response, error) {
|
||||
if opts == nil {
|
||||
opts = &DialOptions{}
|
||||
}
|
||||
|
||||
url = strings.Replace(url, "http://", "ws://", 1)
|
||||
url = strings.Replace(url, "https://", "wss://", 1)
|
||||
|
||||
ws, err := wsjs.New(url, opts.Subprotocols)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
ws: ws,
|
||||
}
|
||||
c.init()
|
||||
|
||||
opench := make(chan struct{})
|
||||
releaseOpen := ws.OnOpen(func(e js.Value) {
|
||||
close(opench)
|
||||
})
|
||||
defer releaseOpen()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.Close(StatusPolicyViolation, "dial timed out")
|
||||
return nil, nil, ctx.Err()
|
||||
case <-opench:
|
||||
return c, &http.Response{
|
||||
StatusCode: http.StatusSwitchingProtocols,
|
||||
}, nil
|
||||
case <-c.closed:
|
||||
return nil, nil, net.ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
// Reader attempts to read a message from the connection.
|
||||
// The maximum time spent waiting is bounded by the context.
|
||||
func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error) {
|
||||
typ, p, err := c.Read(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return typ, bytes.NewReader(p), nil
|
||||
}
|
||||
|
||||
// Writer returns a writer to write a WebSocket data message to the connection.
|
||||
// It buffers the entire message in memory and then sends it when the writer
|
||||
// is closed.
|
||||
func (c *Conn) Writer(ctx context.Context, typ MessageType) (io.WriteCloser, error) {
|
||||
return &writer{
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
typ: typ,
|
||||
b: bpool.Get(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
closed bool
|
||||
|
||||
c *Conn
|
||||
ctx context.Context
|
||||
typ MessageType
|
||||
|
||||
b *bytes.Buffer
|
||||
}
|
||||
|
||||
func (w *writer) Write(p []byte) (int, error) {
|
||||
if w.closed {
|
||||
return 0, errors.New("cannot write to closed writer")
|
||||
}
|
||||
n, err := w.b.Write(p)
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("failed to write message: %w", err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w *writer) Close() error {
|
||||
if w.closed {
|
||||
return errors.New("cannot close closed writer")
|
||||
}
|
||||
w.closed = true
|
||||
defer bpool.Put(w.b)
|
||||
|
||||
err := w.c.Write(w.ctx, w.typ, w.b.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to close writer: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseRead implements *Conn.CloseRead for wasm.
|
||||
func (c *Conn) CloseRead(ctx context.Context) context.Context {
|
||||
c.closeReadMu.Lock()
|
||||
ctx2 := c.closeReadCtx
|
||||
if ctx2 != nil {
|
||||
c.closeReadMu.Unlock()
|
||||
return ctx2
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
c.closeReadCtx = ctx
|
||||
c.closeReadMu.Unlock()
|
||||
|
||||
go func() {
|
||||
defer cancel()
|
||||
defer c.CloseNow()
|
||||
_, _, err := c.read(ctx)
|
||||
if err != nil {
|
||||
c.Close(StatusPolicyViolation, "unexpected data message")
|
||||
}
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
|
||||
// SetReadLimit implements *Conn.SetReadLimit for wasm.
|
||||
func (c *Conn) SetReadLimit(n int64) {
|
||||
c.msgReadLimit.Store(n)
|
||||
}
|
||||
|
||||
func (c *Conn) setCloseErr(err error) {
|
||||
c.closeErrOnce.Do(func() {
|
||||
c.closeErr = fmt.Errorf("WebSocket closed: %w", err)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Conn) isClosed() bool {
|
||||
select {
|
||||
case <-c.closed:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AcceptOptions represents Accept's options.
|
||||
type AcceptOptions struct {
|
||||
Subprotocols []string
|
||||
InsecureSkipVerify bool
|
||||
OriginPatterns []string
|
||||
CompressionMode CompressionMode
|
||||
CompressionThreshold int
|
||||
}
|
||||
|
||||
// Accept is stubbed out for Wasm.
|
||||
func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// StatusCode represents a WebSocket status code.
|
||||
// https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
type StatusCode int
|
||||
|
||||
// https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
|
||||
//
|
||||
// These are only the status codes defined by the protocol.
|
||||
//
|
||||
// You can define custom codes in the 3000-4999 range.
|
||||
// The 3000-3999 range is reserved for use by libraries, frameworks and applications.
|
||||
// The 4000-4999 range is reserved for private use.
|
||||
const (
|
||||
StatusNormalClosure StatusCode = 1000
|
||||
StatusGoingAway StatusCode = 1001
|
||||
StatusProtocolError StatusCode = 1002
|
||||
StatusUnsupportedData StatusCode = 1003
|
||||
|
||||
// 1004 is reserved and so unexported.
|
||||
statusReserved StatusCode = 1004
|
||||
|
||||
// StatusNoStatusRcvd cannot be sent in a close message.
|
||||
// It is reserved for when a close message is received without
|
||||
// a status code.
|
||||
StatusNoStatusRcvd StatusCode = 1005
|
||||
|
||||
// StatusAbnormalClosure is exported for use only with Wasm.
|
||||
// In non Wasm Go, the returned error will indicate whether the
|
||||
// connection was closed abnormally.
|
||||
StatusAbnormalClosure StatusCode = 1006
|
||||
|
||||
StatusInvalidFramePayloadData StatusCode = 1007
|
||||
StatusPolicyViolation StatusCode = 1008
|
||||
StatusMessageTooBig StatusCode = 1009
|
||||
StatusMandatoryExtension StatusCode = 1010
|
||||
StatusInternalError StatusCode = 1011
|
||||
StatusServiceRestart StatusCode = 1012
|
||||
StatusTryAgainLater StatusCode = 1013
|
||||
StatusBadGateway StatusCode = 1014
|
||||
|
||||
// StatusTLSHandshake is only exported for use with Wasm.
|
||||
// In non Wasm Go, the returned error will indicate whether there was
|
||||
// a TLS handshake failure.
|
||||
StatusTLSHandshake StatusCode = 1015
|
||||
)
|
||||
|
||||
// CloseError is returned when the connection is closed with a status and reason.
|
||||
//
|
||||
// Use Go 1.13's errors.As to check for this error.
|
||||
// Also see the CloseStatus helper.
|
||||
type CloseError struct {
|
||||
Code StatusCode
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (ce CloseError) Error() string {
|
||||
return fmt.Sprintf("status = %v and reason = %q", ce.Code, ce.Reason)
|
||||
}
|
||||
|
||||
// CloseStatus is a convenience wrapper around Go 1.13's errors.As to grab
|
||||
// the status code from a CloseError.
|
||||
//
|
||||
// -1 will be returned if the passed error is nil or not a CloseError.
|
||||
func CloseStatus(err error) StatusCode {
|
||||
var ce CloseError
|
||||
if errors.As(err, &ce) {
|
||||
return ce.Code
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// CompressionMode represents the modes available to the deflate extension.
|
||||
// See https://tools.ietf.org/html/rfc7692
|
||||
// Works in all browsers except Safari which does not implement the deflate extension.
|
||||
type CompressionMode int
|
||||
|
||||
const (
|
||||
// CompressionNoContextTakeover grabs a new flate.Reader and flate.Writer as needed
|
||||
// for every message. This applies to both server and client side.
|
||||
//
|
||||
// This means less efficient compression as the sliding window from previous messages
|
||||
// will not be used but the memory overhead will be lower if the connections
|
||||
// are long lived and seldom used.
|
||||
//
|
||||
// The message will only be compressed if greater than 512 bytes.
|
||||
CompressionNoContextTakeover CompressionMode = iota
|
||||
|
||||
// CompressionContextTakeover uses a flate.Reader and flate.Writer per connection.
|
||||
// This enables reusing the sliding window from previous messages.
|
||||
// As most WebSocket protocols are repetitive, this can be very efficient.
|
||||
// It carries an overhead of 8 kB for every connection compared to CompressionNoContextTakeover.
|
||||
//
|
||||
// If the peer negotiates NoContextTakeover on the client or server side, it will be
|
||||
// used instead as this is required by the RFC.
|
||||
CompressionContextTakeover
|
||||
|
||||
// CompressionDisabled disables the deflate extension.
|
||||
//
|
||||
// Use this if you are using a predominantly binary protocol with very
|
||||
// little duplication in between messages or CPU and memory are more
|
||||
// important than bandwidth.
|
||||
CompressionDisabled
|
||||
)
|
||||
|
||||
// MessageType represents the type of a WebSocket message.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.6
|
||||
type MessageType int
|
||||
|
||||
// MessageType constants.
|
||||
const (
|
||||
// MessageText is for UTF-8 encoded text messages like JSON.
|
||||
MessageText MessageType = iota + 1
|
||||
// MessageBinary is for binary messages like protobufs.
|
||||
MessageBinary
|
||||
)
|
||||
|
||||
type mu struct {
|
||||
c *Conn
|
||||
ch chan struct{}
|
||||
}
|
||||
|
||||
func newMu(c *Conn) *mu {
|
||||
return &mu{
|
||||
c: c,
|
||||
ch: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mu) forceLock() {
|
||||
m.ch <- struct{}{}
|
||||
}
|
||||
|
||||
func (m *mu) tryLock() bool {
|
||||
select {
|
||||
case m.ch <- struct{}{}:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mu) unlock() {
|
||||
select {
|
||||
case <-m.ch:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type noCopy struct{}
|
||||
|
||||
func (*noCopy) Lock() {}
|
||||
68
vendor/github.com/coder/websocket/wsjson/wsjson.go
generated
vendored
68
vendor/github.com/coder/websocket/wsjson/wsjson.go
generated
vendored
@@ -1,68 +0,0 @@
|
||||
// Package wsjson provides helpers for reading and writing JSON messages.
|
||||
package wsjson // import "github.com/coder/websocket/wsjson"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/internal/bpool"
|
||||
"github.com/coder/websocket/internal/errd"
|
||||
"github.com/coder/websocket/internal/util"
|
||||
)
|
||||
|
||||
// Read reads a JSON message from c into v.
|
||||
// It will reuse buffers in between calls to avoid allocations.
|
||||
func Read(ctx context.Context, c *websocket.Conn, v any) error {
|
||||
return read(ctx, c, v)
|
||||
}
|
||||
|
||||
func read(ctx context.Context, c *websocket.Conn, v any) (err error) {
|
||||
defer errd.Wrap(&err, "failed to read JSON message")
|
||||
|
||||
_, r, err := c.Reader(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bpool.Get()
|
||||
defer bpool.Put(b)
|
||||
|
||||
_, err = b.ReadFrom(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b.Bytes(), v)
|
||||
if err != nil {
|
||||
c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal JSON")
|
||||
return fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the JSON message v to c.
|
||||
// It will reuse buffers in between calls to avoid allocations.
|
||||
func Write(ctx context.Context, c *websocket.Conn, v any) error {
|
||||
return write(ctx, c, v)
|
||||
}
|
||||
|
||||
func write(ctx context.Context, c *websocket.Conn, v any) (err error) {
|
||||
defer errd.Wrap(&err, "failed to write JSON message")
|
||||
|
||||
// json.Marshal cannot reuse buffers between calls as it has to return
|
||||
// a copy of the byte slice but Encoder does as it directly writes to w.
|
||||
err = json.NewEncoder(util.WriterFunc(func(p []byte) (int, error) {
|
||||
err := c.Write(ctx, websocket.MessageText, p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
})).Encode(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal JSON: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
1
vendor/github.com/danieljoos/wincred/.gitattributes
generated
vendored
1
vendor/github.com/danieljoos/wincred/.gitattributes
generated
vendored
@@ -1 +0,0 @@
|
||||
*.go text eol=lf
|
||||
25
vendor/github.com/danieljoos/wincred/.gitignore
generated
vendored
25
vendor/github.com/danieljoos/wincred/.gitignore
generated
vendored
@@ -1,25 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
coverage.txt
|
||||
21
vendor/github.com/danieljoos/wincred/LICENSE
generated
vendored
21
vendor/github.com/danieljoos/wincred/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Daniel Joos
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
145
vendor/github.com/danieljoos/wincred/README.md
generated
vendored
145
vendor/github.com/danieljoos/wincred/README.md
generated
vendored
@@ -1,145 +0,0 @@
|
||||
wincred
|
||||
=======
|
||||
|
||||
Go wrapper around the Windows Credential Manager API functions.
|
||||
|
||||
[](https://github.com/danieljoos/wincred/releases/latest)
|
||||
[](https://github.com/danieljoos/wincred/actions?query=workflow%3Atest)
|
||||
[](https://goreportcard.com/report/github.com/danieljoos/wincred)
|
||||
[](https://codecov.io/gh/danieljoos/wincred)
|
||||
[](https://pkg.go.dev/github.com/danieljoos/wincred)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```Go
|
||||
go get github.com/danieljoos/wincred
|
||||
```
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
See the following examples:
|
||||
|
||||
### Create and store a new generic credential object
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/danieljoos/wincred"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cred := wincred.NewGenericCredential("myGoApplication")
|
||||
cred.CredentialBlob = []byte("my secret")
|
||||
err := cred.Write()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Retrieve a credential object
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/danieljoos/wincred"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cred, err := wincred.GetGenericCredential("myGoApplication")
|
||||
if err == nil {
|
||||
fmt.Println(string(cred.CredentialBlob))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Remove a credential object
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/danieljoos/wincred"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cred, err := wincred.GetGenericCredential("myGoApplication")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
cred.Delete()
|
||||
}
|
||||
```
|
||||
|
||||
### List all available credentials
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/danieljoos/wincred"
|
||||
)
|
||||
|
||||
func main() {
|
||||
creds, err := wincred.List()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
for i := range(creds) {
|
||||
fmt.Println(creds[i].TargetName)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hints
|
||||
-----
|
||||
|
||||
### Encoding
|
||||
|
||||
The credential objects simply store byte arrays without specific meaning or encoding.
|
||||
For sharing between different applications, it might make sense to apply an explicit string encoding - for example **UTF-16 LE** (used nearly everywhere in the Win32 API).
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/danieljoos/wincred"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cred := wincred.NewGenericCredential("myGoApplication")
|
||||
|
||||
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
|
||||
blob, _, err := transform.Bytes(encoder, []byte("mysecret"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cred.CredentialBlob = blob
|
||||
err = cred.Write()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
The size of a credential blob is limited to **2560 Bytes** by the Windows API.
|
||||
110
vendor/github.com/danieljoos/wincred/conversion.go
generated
vendored
110
vendor/github.com/danieljoos/wincred/conversion.go
generated
vendored
@@ -1,110 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package wincred
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
syscall "golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// utf16ToByte creates a byte array from a given UTF 16 char array.
|
||||
func utf16ToByte(wstr []uint16) (result []byte) {
|
||||
result = make([]byte, len(wstr)*2)
|
||||
for i := range wstr {
|
||||
binary.LittleEndian.PutUint16(result[(i*2):(i*2)+2], wstr[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// utf16FromString creates a UTF16 char array from a string.
|
||||
func utf16FromString(str string) []uint16 {
|
||||
res, err := syscall.UTF16FromString(str)
|
||||
if err != nil {
|
||||
return []uint16{}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// goBytes copies the given C byte array to a Go byte array (see `C.GoBytes`).
|
||||
// This function avoids having cgo as dependency.
|
||||
func goBytes(src *byte, len uint32) []byte {
|
||||
if src == nil || len == 0 {
|
||||
return []byte{}
|
||||
}
|
||||
rv := make([]byte, len)
|
||||
copy(rv, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(src)),
|
||||
Len: int(len),
|
||||
Cap: int(len),
|
||||
})))
|
||||
return rv
|
||||
}
|
||||
|
||||
// Convert the given CREDENTIAL struct to a more usable structure
|
||||
func sysToCredential(cred *sysCREDENTIAL) (result *Credential) {
|
||||
if cred == nil {
|
||||
return nil
|
||||
}
|
||||
result = new(Credential)
|
||||
result.Comment = syscall.UTF16PtrToString(cred.Comment)
|
||||
result.TargetName = syscall.UTF16PtrToString(cred.TargetName)
|
||||
result.TargetAlias = syscall.UTF16PtrToString(cred.TargetAlias)
|
||||
result.UserName = syscall.UTF16PtrToString(cred.UserName)
|
||||
result.LastWritten = time.Unix(0, cred.LastWritten.Nanoseconds())
|
||||
result.Persist = CredentialPersistence(cred.Persist)
|
||||
result.CredentialBlob = goBytes(cred.CredentialBlob, cred.CredentialBlobSize)
|
||||
result.Attributes = make([]CredentialAttribute, cred.AttributeCount)
|
||||
attrSlice := *(*[]sysCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(cred.Attributes)),
|
||||
Len: int(cred.AttributeCount),
|
||||
Cap: int(cred.AttributeCount),
|
||||
}))
|
||||
for i, attr := range attrSlice {
|
||||
resultAttr := &result.Attributes[i]
|
||||
resultAttr.Keyword = syscall.UTF16PtrToString(attr.Keyword)
|
||||
resultAttr.Value = goBytes(attr.Value, attr.ValueSize)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Convert the given Credential object back to a CREDENTIAL struct, which can be used for calling the
|
||||
// Windows APIs
|
||||
func sysFromCredential(cred *Credential) (result *sysCREDENTIAL) {
|
||||
if cred == nil {
|
||||
return nil
|
||||
}
|
||||
result = new(sysCREDENTIAL)
|
||||
result.Flags = 0
|
||||
result.Type = 0
|
||||
result.TargetName, _ = syscall.UTF16PtrFromString(cred.TargetName)
|
||||
result.Comment, _ = syscall.UTF16PtrFromString(cred.Comment)
|
||||
result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano())
|
||||
result.CredentialBlobSize = uint32(len(cred.CredentialBlob))
|
||||
if len(cred.CredentialBlob) > 0 {
|
||||
result.CredentialBlob = &cred.CredentialBlob[0]
|
||||
}
|
||||
result.Persist = uint32(cred.Persist)
|
||||
result.AttributeCount = uint32(len(cred.Attributes))
|
||||
attributes := make([]sysCREDENTIAL_ATTRIBUTE, len(cred.Attributes))
|
||||
if len(attributes) > 0 {
|
||||
result.Attributes = &attributes[0]
|
||||
}
|
||||
for i := range cred.Attributes {
|
||||
inAttr := &cred.Attributes[i]
|
||||
outAttr := &attributes[i]
|
||||
outAttr.Keyword, _ = syscall.UTF16PtrFromString(inAttr.Keyword)
|
||||
outAttr.Flags = 0
|
||||
outAttr.ValueSize = uint32(len(inAttr.Value))
|
||||
if len(inAttr.Value) > 0 {
|
||||
outAttr.Value = &inAttr.Value[0]
|
||||
}
|
||||
}
|
||||
result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias)
|
||||
result.UserName, _ = syscall.UTF16PtrFromString(cred.UserName)
|
||||
|
||||
return
|
||||
}
|
||||
11
vendor/github.com/danieljoos/wincred/conversion_unsupported.go
generated
vendored
11
vendor/github.com/danieljoos/wincred/conversion_unsupported.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package wincred
|
||||
|
||||
func utf16ToByte(...interface{}) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func utf16FromString(...interface{}) []uint16 {
|
||||
return nil
|
||||
}
|
||||
151
vendor/github.com/danieljoos/wincred/sys.go
generated
vendored
151
vendor/github.com/danieljoos/wincred/sys.go
generated
vendored
@@ -1,151 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package wincred
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
procCredRead = modadvapi32.NewProc("CredReadW")
|
||||
procCredWrite proc = modadvapi32.NewProc("CredWriteW")
|
||||
procCredDelete proc = modadvapi32.NewProc("CredDeleteW")
|
||||
procCredFree proc = modadvapi32.NewProc("CredFree")
|
||||
procCredEnumerate = modadvapi32.NewProc("CredEnumerateW")
|
||||
)
|
||||
|
||||
// Interface for syscall.Proc: helps testing
|
||||
type proc interface {
|
||||
Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
|
||||
type sysCREDENTIAL struct {
|
||||
Flags uint32
|
||||
Type uint32
|
||||
TargetName *uint16
|
||||
Comment *uint16
|
||||
LastWritten windows.Filetime
|
||||
CredentialBlobSize uint32
|
||||
CredentialBlob *byte
|
||||
Persist uint32
|
||||
AttributeCount uint32
|
||||
Attributes *sysCREDENTIAL_ATTRIBUTE
|
||||
TargetAlias *uint16
|
||||
UserName *uint16
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credential_attributew
|
||||
type sysCREDENTIAL_ATTRIBUTE struct {
|
||||
Keyword *uint16
|
||||
Flags uint32
|
||||
ValueSize uint32
|
||||
Value *byte
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
|
||||
type sysCRED_TYPE uint32
|
||||
|
||||
const (
|
||||
sysCRED_TYPE_GENERIC sysCRED_TYPE = 0x1
|
||||
sysCRED_TYPE_DOMAIN_PASSWORD sysCRED_TYPE = 0x2
|
||||
sysCRED_TYPE_DOMAIN_CERTIFICATE sysCRED_TYPE = 0x3
|
||||
sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD sysCRED_TYPE = 0x4
|
||||
sysCRED_TYPE_GENERIC_CERTIFICATE sysCRED_TYPE = 0x5
|
||||
sysCRED_TYPE_DOMAIN_EXTENDED sysCRED_TYPE = 0x6
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes
|
||||
sysERROR_NOT_FOUND = windows.Errno(1168)
|
||||
sysERROR_INVALID_PARAMETER = windows.Errno(87)
|
||||
sysERROR_BAD_USERNAME = windows.Errno(2202)
|
||||
)
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw
|
||||
func sysCredRead(targetName string, typ sysCRED_TYPE) (*Credential, error) {
|
||||
var pcred *sysCREDENTIAL
|
||||
targetNamePtr, _ := windows.UTF16PtrFromString(targetName)
|
||||
ret, _, err := syscall.SyscallN(
|
||||
procCredRead.Addr(),
|
||||
uintptr(unsafe.Pointer(targetNamePtr)),
|
||||
uintptr(typ),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&pcred)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return nil, err
|
||||
}
|
||||
defer procCredFree.Call(uintptr(unsafe.Pointer(pcred)))
|
||||
|
||||
return sysToCredential(pcred), nil
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credwritew
|
||||
func sysCredWrite(cred *Credential, typ sysCRED_TYPE) error {
|
||||
ncred := sysFromCredential(cred)
|
||||
ncred.Type = uint32(typ)
|
||||
ret, _, err := procCredWrite.Call(
|
||||
uintptr(unsafe.Pointer(ncred)),
|
||||
0,
|
||||
)
|
||||
// Make sure everything reachable from ncred stays alive through the call.
|
||||
runtime.KeepAlive(ncred)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creddeletew
|
||||
func sysCredDelete(cred *Credential, typ sysCRED_TYPE) error {
|
||||
targetNamePtr, _ := windows.UTF16PtrFromString(cred.TargetName)
|
||||
ret, _, err := procCredDelete.Call(
|
||||
uintptr(unsafe.Pointer(targetNamePtr)),
|
||||
uintptr(typ),
|
||||
0,
|
||||
)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credenumeratew
|
||||
func sysCredEnumerate(filter string, all bool) ([]*Credential, error) {
|
||||
var count int
|
||||
var pcreds uintptr
|
||||
var filterPtr *uint16
|
||||
if !all {
|
||||
filterPtr, _ = windows.UTF16PtrFromString(filter)
|
||||
}
|
||||
ret, _, err := syscall.SyscallN(
|
||||
procCredEnumerate.Addr(),
|
||||
uintptr(unsafe.Pointer(filterPtr)),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&count)),
|
||||
uintptr(unsafe.Pointer(&pcreds)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return nil, err
|
||||
}
|
||||
defer procCredFree.Call(pcreds)
|
||||
credsSlice := *(*[]*sysCREDENTIAL)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: pcreds,
|
||||
Len: count,
|
||||
Cap: count,
|
||||
}))
|
||||
creds := make([]*Credential, count, count)
|
||||
for i, cred := range credsSlice {
|
||||
creds[i] = sysToCredential(cred)
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
38
vendor/github.com/danieljoos/wincred/sys_unsupported.go
generated
vendored
38
vendor/github.com/danieljoos/wincred/sys_unsupported.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package wincred
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
sysCRED_TYPE_GENERIC = 0
|
||||
sysCRED_TYPE_DOMAIN_PASSWORD = 0
|
||||
sysCRED_TYPE_DOMAIN_CERTIFICATE = 0
|
||||
sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0
|
||||
sysCRED_TYPE_GENERIC_CERTIFICATE = 0
|
||||
sysCRED_TYPE_DOMAIN_EXTENDED = 0
|
||||
|
||||
sysERROR_NOT_FOUND = syscall.Errno(1)
|
||||
sysERROR_INVALID_PARAMETER = syscall.Errno(1)
|
||||
sysERROR_BAD_USERNAME = syscall.Errno(1)
|
||||
)
|
||||
|
||||
func sysCredRead(...interface{}) (*Credential, error) {
|
||||
return nil, errors.New("Operation not supported")
|
||||
}
|
||||
|
||||
func sysCredWrite(...interface{}) error {
|
||||
return errors.New("Operation not supported")
|
||||
}
|
||||
|
||||
func sysCredDelete(...interface{}) error {
|
||||
return errors.New("Operation not supported")
|
||||
}
|
||||
|
||||
func sysCredEnumerate(...interface{}) ([]*Credential, error) {
|
||||
return nil, errors.New("Operation not supported")
|
||||
}
|
||||
69
vendor/github.com/danieljoos/wincred/types.go
generated
vendored
69
vendor/github.com/danieljoos/wincred/types.go
generated
vendored
@@ -1,69 +0,0 @@
|
||||
package wincred
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// CredentialPersistence describes one of three persistence modes of a credential.
|
||||
// A detailed description of the available modes can be found on
|
||||
// Docs: https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
|
||||
type CredentialPersistence uint32
|
||||
|
||||
const (
|
||||
// PersistSession indicates that the credential only persists for the life
|
||||
// of the current Windows login session. Such a credential is not visible in
|
||||
// any other logon session, even from the same user.
|
||||
PersistSession CredentialPersistence = 0x1
|
||||
|
||||
// PersistLocalMachine indicates that the credential persists for this and
|
||||
// all subsequent logon sessions on this local machine/computer. It is
|
||||
// however not visible for logon sessions of this user on a different
|
||||
// machine.
|
||||
PersistLocalMachine CredentialPersistence = 0x2
|
||||
|
||||
// PersistEnterprise indicates that the credential persists for this and all
|
||||
// subsequent logon sessions for this user. It is also visible for logon
|
||||
// sessions on different computers.
|
||||
PersistEnterprise CredentialPersistence = 0x3
|
||||
)
|
||||
|
||||
// CredentialAttribute represents an application-specific attribute of a credential.
|
||||
type CredentialAttribute struct {
|
||||
Keyword string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// Credential is the basic credential structure.
|
||||
// A credential is identified by its target name.
|
||||
// The actual credential secret is available in the CredentialBlob field.
|
||||
type Credential struct {
|
||||
TargetName string
|
||||
Comment string
|
||||
LastWritten time.Time
|
||||
CredentialBlob []byte
|
||||
Attributes []CredentialAttribute
|
||||
TargetAlias string
|
||||
UserName string
|
||||
Persist CredentialPersistence
|
||||
}
|
||||
|
||||
// GenericCredential holds a credential for generic usage.
|
||||
// It is typically defined and used by applications that need to manage user
|
||||
// secrets.
|
||||
//
|
||||
// More information about the available kinds of credentials of the Windows
|
||||
// Credential Management API can be found on Docs:
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials
|
||||
type GenericCredential struct {
|
||||
Credential
|
||||
}
|
||||
|
||||
// DomainPassword holds a domain credential that is typically used by the
|
||||
// operating system for user logon.
|
||||
//
|
||||
// More information about the available kinds of credentials of the Windows
|
||||
// Credential Management API can be found on Docs:
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials
|
||||
type DomainPassword struct {
|
||||
Credential
|
||||
}
|
||||
114
vendor/github.com/danieljoos/wincred/wincred.go
generated
vendored
114
vendor/github.com/danieljoos/wincred/wincred.go
generated
vendored
@@ -1,114 +0,0 @@
|
||||
// Package wincred provides primitives for accessing the Windows Credentials Management API.
|
||||
// This includes functions for retrieval, listing and storage of credentials as well as Go structures for convenient access to the credential data.
|
||||
//
|
||||
// A more detailed description of Windows Credentials Management can be found on
|
||||
// Docs: https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/credentials-management
|
||||
package wincred
|
||||
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
// ErrElementNotFound is the error that is returned if a requested element cannot be found.
|
||||
// This error constant can be used to check if a credential could not be found.
|
||||
ErrElementNotFound = sysERROR_NOT_FOUND
|
||||
|
||||
// ErrInvalidParameter is the error that is returned for invalid parameters.
|
||||
// This error constant can be used to check if the given function parameters were invalid.
|
||||
// For example when trying to create a new generic credential with an empty target name.
|
||||
ErrInvalidParameter = sysERROR_INVALID_PARAMETER
|
||||
|
||||
// ErrBadUsername is returned when the credential's username is invalid.
|
||||
ErrBadUsername = sysERROR_BAD_USERNAME
|
||||
)
|
||||
|
||||
// GetGenericCredential fetches the generic credential with the given name from Windows credential manager.
|
||||
// It returns nil and an error if the credential could not be found or an error occurred.
|
||||
func GetGenericCredential(targetName string) (*GenericCredential, error) {
|
||||
cred, err := sysCredRead(targetName, sysCRED_TYPE_GENERIC)
|
||||
if cred != nil {
|
||||
return &GenericCredential{Credential: *cred}, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NewGenericCredential creates a new generic credential object with the given name.
|
||||
// The persist mode of the newly created object is set to a default value that indicates local-machine-wide storage.
|
||||
// The credential object is NOT yet persisted to the Windows credential vault.
|
||||
func NewGenericCredential(targetName string) (result *GenericCredential) {
|
||||
result = new(GenericCredential)
|
||||
result.TargetName = targetName
|
||||
result.Persist = PersistLocalMachine
|
||||
return
|
||||
}
|
||||
|
||||
// Write persists the generic credential object to Windows credential manager.
|
||||
func (t *GenericCredential) Write() (err error) {
|
||||
err = sysCredWrite(&t.Credential, sysCRED_TYPE_GENERIC)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes the credential object from Windows credential manager.
|
||||
func (t *GenericCredential) Delete() (err error) {
|
||||
err = sysCredDelete(&t.Credential, sysCRED_TYPE_GENERIC)
|
||||
return
|
||||
}
|
||||
|
||||
// GetDomainPassword fetches the domain-password credential with the given target host name from Windows credential manager.
|
||||
// It returns nil and an error if the credential could not be found or an error occurred.
|
||||
func GetDomainPassword(targetName string) (*DomainPassword, error) {
|
||||
cred, err := sysCredRead(targetName, sysCRED_TYPE_DOMAIN_PASSWORD)
|
||||
if cred != nil {
|
||||
return &DomainPassword{Credential: *cred}, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NewDomainPassword creates a new domain-password credential used for login to the given target host name.
|
||||
// The persist mode of the newly created object is set to a default value that indicates local-machine-wide storage.
|
||||
// The credential object is NOT yet persisted to the Windows credential vault.
|
||||
func NewDomainPassword(targetName string) (result *DomainPassword) {
|
||||
result = new(DomainPassword)
|
||||
result.TargetName = targetName
|
||||
result.Persist = PersistLocalMachine
|
||||
return
|
||||
}
|
||||
|
||||
// Write persists the domain-password credential to Windows credential manager.
|
||||
func (t *DomainPassword) Write() (err error) {
|
||||
err = sysCredWrite(&t.Credential, sysCRED_TYPE_DOMAIN_PASSWORD)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes the domain-password credential from Windows credential manager.
|
||||
func (t *DomainPassword) Delete() (err error) {
|
||||
err = sysCredDelete(&t.Credential, sysCRED_TYPE_DOMAIN_PASSWORD)
|
||||
return
|
||||
}
|
||||
|
||||
// SetPassword sets the CredentialBlob field of a domain password credential to the given string.
|
||||
func (t *DomainPassword) SetPassword(pw string) {
|
||||
t.CredentialBlob = utf16ToByte(utf16FromString(pw))
|
||||
}
|
||||
|
||||
// List retrieves all credentials of the Credentials store.
|
||||
func List() ([]*Credential, error) {
|
||||
creds, err := sysCredEnumerate("", true)
|
||||
if err != nil && errors.Is(err, ErrElementNotFound) {
|
||||
// Ignore ERROR_NOT_FOUND and return an empty list instead
|
||||
creds = []*Credential{}
|
||||
err = nil
|
||||
}
|
||||
return creds, err
|
||||
}
|
||||
|
||||
// FilteredList retrieves the list of credentials from the Credentials store that match the given filter.
|
||||
// The filter string defines the prefix followed by an asterisk for the `TargetName` attribute of the credentials.
|
||||
func FilteredList(filter string) ([]*Credential, error) {
|
||||
creds, err := sysCredEnumerate(filter, false)
|
||||
if err != nil && errors.Is(err, ErrElementNotFound) {
|
||||
// Ignore ERROR_NOT_FOUND and return an empty list instead
|
||||
creds = []*Credential{}
|
||||
err = nil
|
||||
}
|
||||
return creds, err
|
||||
}
|
||||
8
vendor/github.com/go-ole/go-ole/.travis.yml
generated
vendored
8
vendor/github.com/go-ole/go-ole/.travis.yml
generated
vendored
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- tip
|
||||
49
vendor/github.com/go-ole/go-ole/ChangeLog.md
generated
vendored
49
vendor/github.com/go-ole/go-ole/ChangeLog.md
generated
vendored
@@ -1,49 +0,0 @@
|
||||
# Version 1.x.x
|
||||
|
||||
* **Add more test cases and reference new test COM server project.** (Placeholder for future additions)
|
||||
|
||||
# Version 1.2.0-alphaX
|
||||
|
||||
**Minimum supported version is now Go 1.4. Go 1.1 support is deprecated, but should still build.**
|
||||
|
||||
* Added CI configuration for Travis-CI and AppVeyor.
|
||||
* Added test InterfaceID and ClassID for the COM Test Server project.
|
||||
* Added more inline documentation (#83).
|
||||
* Added IEnumVARIANT implementation (#88).
|
||||
* Added IEnumVARIANT test cases (#99, #100, #101).
|
||||
* Added support for retrieving `time.Time` from VARIANT (#92).
|
||||
* Added test case for IUnknown (#64).
|
||||
* Added test case for IDispatch (#64).
|
||||
* Added test cases for scalar variants (#64, #76).
|
||||
|
||||
# Version 1.1.1
|
||||
|
||||
* Fixes for Linux build.
|
||||
* Fixes for Windows build.
|
||||
|
||||
# Version 1.1.0
|
||||
|
||||
The change to provide building on all platforms is a new feature. The increase in minor version reflects that and allows those who wish to stay on 1.0.x to continue to do so. Support for 1.0.x will be limited to bug fixes.
|
||||
|
||||
* Move GUID out of variables.go into its own file to make new documentation available.
|
||||
* Move OleError out of ole.go into its own file to make new documentation available.
|
||||
* Add documentation to utility functions.
|
||||
* Add documentation to variant receiver functions.
|
||||
* Add documentation to ole structures.
|
||||
* Make variant available to other systems outside of Windows.
|
||||
* Make OLE structures available to other systems outside of Windows.
|
||||
|
||||
## New Features
|
||||
|
||||
* Library should now be built on all platforms supported by Go. Library will NOOP on any platform that is not Windows.
|
||||
* More functions are now documented and available on godoc.org.
|
||||
|
||||
# Version 1.0.1
|
||||
|
||||
1. Fix package references from repository location change.
|
||||
|
||||
# Version 1.0.0
|
||||
|
||||
This version is stable enough for use. The COM API is still incomplete, but provides enough functionality for accessing COM servers using IDispatch interface.
|
||||
|
||||
There is no changelog for this version. Check commits for history.
|
||||
21
vendor/github.com/go-ole/go-ole/LICENSE
generated
vendored
21
vendor/github.com/go-ole/go-ole/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
46
vendor/github.com/go-ole/go-ole/README.md
generated
vendored
46
vendor/github.com/go-ole/go-ole/README.md
generated
vendored
@@ -1,46 +0,0 @@
|
||||
# Go OLE
|
||||
|
||||
[](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28)
|
||||
[](https://travis-ci.org/go-ole/go-ole)
|
||||
[](https://godoc.org/github.com/go-ole/go-ole)
|
||||
|
||||
Go bindings for Windows COM using shared libraries instead of cgo.
|
||||
|
||||
By Yasuhiro Matsumoto.
|
||||
|
||||
## Install
|
||||
|
||||
To experiment with go-ole, you can just compile and run the example program:
|
||||
|
||||
```
|
||||
go get github.com/go-ole/go-ole
|
||||
cd /path/to/go-ole/
|
||||
go test
|
||||
|
||||
cd /path/to/go-ole/example/excel
|
||||
go run excel.go
|
||||
```
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Continuous integration configuration has been added for both Travis-CI and AppVeyor. You will have to add these to your own account for your fork in order for it to run.
|
||||
|
||||
**Travis-CI**
|
||||
|
||||
Travis-CI was added to check builds on Linux to ensure that `go get` works when cross building. Currently, Travis-CI is not used to test cross-building, but this may be changed in the future. It is also not currently possible to test the library on Linux, since COM API is specific to Windows and it is not currently possible to run a COM server on Linux or even connect to a remote COM server.
|
||||
|
||||
**AppVeyor**
|
||||
|
||||
AppVeyor is used to build on Windows using the (in-development) test COM server. It is currently only used to test the build and ensure that the code works on Windows. It will be used to register a COM server and then run the test cases based on the test COM server.
|
||||
|
||||
The tests currently do run and do pass and this should be maintained with commits.
|
||||
|
||||
## Versioning
|
||||
|
||||
Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch.
|
||||
|
||||
This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed.
|
||||
|
||||
## LICENSE
|
||||
|
||||
Under the MIT License: http://mattn.mit-license.org/2013
|
||||
13
vendor/github.com/go-ole/go-ole/SECURITY.md
generated
vendored
13
vendor/github.com/go-ole/go-ole/SECURITY.md
generated
vendored
@@ -1,13 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are applied only to the latest release.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
||||
|
||||
Please disclose it at [security advisory](https://github.com/go-ole/go-ole/security/advisories/new).
|
||||
|
||||
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
|
||||
68
vendor/github.com/go-ole/go-ole/appveyor.yml
generated
vendored
68
vendor/github.com/go-ole/go-ole/appveyor.yml
generated
vendored
@@ -1,68 +0,0 @@
|
||||
# Notes:
|
||||
# - Minimal appveyor.yml file is an empty file. All sections are optional.
|
||||
# - Indent each level of configuration with 2 spaces. Do not use tabs!
|
||||
# - All section names are case-sensitive.
|
||||
# - Section names should be unique on each level.
|
||||
|
||||
version: "1.3.0.{build}-alpha-{branch}"
|
||||
|
||||
os: Visual Studio 2019
|
||||
|
||||
build: off
|
||||
|
||||
skip_tags: true
|
||||
|
||||
clone_folder: c:\gopath\src\github.com\go-ole\go-ole
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
GOROOT: c:\go
|
||||
DOWNLOADPLATFORM: "x64"
|
||||
|
||||
before_test:
|
||||
# - Download COM Server
|
||||
- ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip"
|
||||
- 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL
|
||||
- c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat
|
||||
|
||||
test_script:
|
||||
- go test -v -cover ./...
|
||||
# go vet has false positives on unsafe.Pointer with windows/sys. Disabling since it is recommended to use go test instead.
|
||||
# - go vet ./...
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- v1.2
|
||||
- v1.1
|
||||
- v1.0
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- environment:
|
||||
GOROOT: C:\go-x86
|
||||
DOWNLOADPLATFORM: "x86"
|
||||
- environment:
|
||||
GOROOT: C:\go118
|
||||
DOWNLOADPLATFORM: "x64"
|
||||
- environment:
|
||||
GOROOT: C:\go118-x86
|
||||
DOWNLOADPLATFORM: "x86"
|
||||
|
||||
install:
|
||||
- go version
|
||||
- go env
|
||||
- go get -u golang.org/x/tools/cmd/cover
|
||||
- go get -u golang.org/x/tools/cmd/godoc
|
||||
- go get -u golang.org/x/tools/cmd/stringer
|
||||
|
||||
build_script:
|
||||
- cd c:\gopath\src\github.com\go-ole\go-ole
|
||||
- go get -v -t ./...
|
||||
- go build
|
||||
|
||||
# disable automatic tests
|
||||
test: on
|
||||
|
||||
# disable deployment
|
||||
deploy: off
|
||||
386
vendor/github.com/go-ole/go-ole/com.go
generated
vendored
386
vendor/github.com/go-ole/go-ole/com.go
generated
vendored
@@ -1,386 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procCoInitialize = modole32.NewProc("CoInitialize")
|
||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
|
||||
procCoUninitialize = modole32.NewProc("CoUninitialize")
|
||||
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
||||
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
||||
procCLSIDFromProgID = modole32.NewProc("CLSIDFromProgID")
|
||||
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
|
||||
procStringFromCLSID = modole32.NewProc("StringFromCLSID")
|
||||
procStringFromIID = modole32.NewProc("StringFromIID")
|
||||
procIIDFromString = modole32.NewProc("IIDFromString")
|
||||
procCoGetObject = modole32.NewProc("CoGetObject")
|
||||
procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID")
|
||||
procCopyMemory = modkernel32.NewProc("RtlMoveMemory")
|
||||
procVariantInit = modoleaut32.NewProc("VariantInit")
|
||||
procVariantClear = modoleaut32.NewProc("VariantClear")
|
||||
procVariantTimeToSystemTime = modoleaut32.NewProc("VariantTimeToSystemTime")
|
||||
procSysAllocString = modoleaut32.NewProc("SysAllocString")
|
||||
procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen")
|
||||
procSysFreeString = modoleaut32.NewProc("SysFreeString")
|
||||
procSysStringLen = modoleaut32.NewProc("SysStringLen")
|
||||
procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo")
|
||||
procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch")
|
||||
procGetActiveObject = modoleaut32.NewProc("GetActiveObject")
|
||||
|
||||
procGetMessageW = moduser32.NewProc("GetMessageW")
|
||||
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
|
||||
)
|
||||
|
||||
// This is to enable calling COM Security initialization multiple times
|
||||
var bSecurityInit bool = false
|
||||
|
||||
// coInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func coInitialize() (err error) {
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
|
||||
// Suggests that no value should be passed to CoInitialized.
|
||||
// Could just be Call() since the parameter is optional. <-- Needs testing to be sure.
|
||||
hr, _, _ := procCoInitialize.Call(uintptr(0))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// coInitializeEx initializes COM library with concurrency model.
|
||||
func coInitializeEx(coinit uint32) (err error) {
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx
|
||||
// Suggests that the first parameter is not only optional but should always be NULL.
|
||||
hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// coInitializeSecurity: Registers security and sets the default security values
|
||||
// for the process.
|
||||
func coInitializeSecurity(cAuthSvc int32,
|
||||
dwAuthnLevel uint32,
|
||||
dwImpLevel uint32,
|
||||
dwCapabilities uint32) (err error) {
|
||||
// Check COM Security initialization has done previously
|
||||
if !bSecurityInit {
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity
|
||||
hr, _, _ := procCoInitializeSecurity.Call(
|
||||
uintptr(0), // Allow *all* VSS writers to communicate back!
|
||||
uintptr(cAuthSvc), // Default COM authentication service
|
||||
uintptr(0), // Default COM authorization service
|
||||
uintptr(0), // Reserved parameter
|
||||
uintptr(dwAuthnLevel), // Strongest COM authentication level
|
||||
uintptr(dwImpLevel), // Minimal impersonation abilities
|
||||
uintptr(0), // Default COM authentication settings
|
||||
uintptr(dwCapabilities), // Cloaking
|
||||
uintptr(0)) // eserved parameter
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
} else {
|
||||
// COM Security initialization done make global flag true.
|
||||
bSecurityInit = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CoInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func CoInitialize(p uintptr) (err error) {
|
||||
// p is ignored and won't be used.
|
||||
// Avoid any variable not used errors.
|
||||
p = uintptr(0)
|
||||
return coInitialize()
|
||||
}
|
||||
|
||||
// CoInitializeEx initializes COM library with concurrency model.
|
||||
func CoInitializeEx(p uintptr, coinit uint32) (err error) {
|
||||
// Avoid any variable not used errors.
|
||||
p = uintptr(0)
|
||||
return coInitializeEx(coinit)
|
||||
}
|
||||
|
||||
// CoUninitialize uninitializes COM Library.
|
||||
func CoUninitialize() {
|
||||
procCoUninitialize.Call()
|
||||
}
|
||||
|
||||
// CoInitializeSecurity: Registers security and sets the default security values
|
||||
// for the process.
|
||||
func CoInitializeSecurity(cAuthSvc int32,
|
||||
dwAuthnLevel uint32,
|
||||
dwImpLevel uint32,
|
||||
dwCapabilities uint32) (err error) {
|
||||
return coInitializeSecurity(cAuthSvc, dwAuthnLevel, dwImpLevel, dwCapabilities)
|
||||
}
|
||||
|
||||
// CoTaskMemFree frees memory pointer.
|
||||
func CoTaskMemFree(memptr uintptr) {
|
||||
procCoTaskMemFree.Call(memptr)
|
||||
}
|
||||
|
||||
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
|
||||
//
|
||||
// The Programmatic Identifier must be registered, because it will be looked up
|
||||
// in the Windows Registry. The registry entry has the following keys: CLSID,
|
||||
// Insertable, Protocol and Shell
|
||||
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
|
||||
//
|
||||
// programID identifies the class id with less precision and is not guaranteed
|
||||
// to be unique. These are usually found in the registry under
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
|
||||
// "Program.Component.Version" with version being optional.
|
||||
//
|
||||
// CLSIDFromProgID in Windows API.
|
||||
func CLSIDFromProgID(progId string) (clsid *GUID, err error) {
|
||||
var guid GUID
|
||||
lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
|
||||
hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
clsid = &guid
|
||||
return
|
||||
}
|
||||
|
||||
// CLSIDFromString retrieves Class ID from string representation.
|
||||
//
|
||||
// This is technically the string version of the GUID and will convert the
|
||||
// string to object.
|
||||
//
|
||||
// CLSIDFromString in Windows API.
|
||||
func CLSIDFromString(str string) (clsid *GUID, err error) {
|
||||
var guid GUID
|
||||
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
|
||||
hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
clsid = &guid
|
||||
return
|
||||
}
|
||||
|
||||
// StringFromCLSID returns GUID formated string from GUID object.
|
||||
func StringFromCLSID(clsid *GUID) (str string, err error) {
|
||||
var p *uint16
|
||||
hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
str = LpOleStrToString(p)
|
||||
return
|
||||
}
|
||||
|
||||
// IIDFromString returns GUID from program ID.
|
||||
func IIDFromString(progId string) (clsid *GUID, err error) {
|
||||
var guid GUID
|
||||
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
|
||||
hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
clsid = &guid
|
||||
return
|
||||
}
|
||||
|
||||
// StringFromIID returns GUID formatted string from GUID object.
|
||||
func StringFromIID(iid *GUID) (str string, err error) {
|
||||
var p *uint16
|
||||
hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
str = LpOleStrToString(p)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateInstance of single uninitialized object with GUID.
|
||||
func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
|
||||
if iid == nil {
|
||||
iid = IID_IUnknown
|
||||
}
|
||||
hr, _, _ := procCoCreateInstance.Call(
|
||||
uintptr(unsafe.Pointer(clsid)),
|
||||
0,
|
||||
CLSCTX_SERVER,
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetActiveObject retrieves pointer to active object.
|
||||
func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
|
||||
if iid == nil {
|
||||
iid = IID_IUnknown
|
||||
}
|
||||
hr, _, _ := procGetActiveObject.Call(
|
||||
uintptr(unsafe.Pointer(clsid)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type BindOpts struct {
|
||||
CbStruct uint32
|
||||
GrfFlags uint32
|
||||
GrfMode uint32
|
||||
TickCountDeadline uint32
|
||||
}
|
||||
|
||||
// GetObject retrieves pointer to active object.
|
||||
func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) {
|
||||
if bindOpts != nil {
|
||||
bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{}))
|
||||
}
|
||||
if iid == nil {
|
||||
iid = IID_IUnknown
|
||||
}
|
||||
hr, _, _ := procCoGetObject.Call(
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))),
|
||||
uintptr(unsafe.Pointer(bindOpts)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VariantInit initializes variant.
|
||||
func VariantInit(v *VARIANT) (err error) {
|
||||
hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VariantClear clears value in Variant settings to VT_EMPTY.
|
||||
func VariantClear(v *VARIANT) (err error) {
|
||||
hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SysAllocString allocates memory for string and copies string into memory.
|
||||
func SysAllocString(v string) (ss *int16) {
|
||||
pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
|
||||
ss = (*int16)(unsafe.Pointer(pss))
|
||||
return
|
||||
}
|
||||
|
||||
// SysAllocStringLen copies up to length of given string returning pointer.
|
||||
func SysAllocStringLen(v string) (ss *int16) {
|
||||
utf16 := utf16.Encode([]rune(v + "\x00"))
|
||||
ptr := &utf16[0]
|
||||
|
||||
pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1))
|
||||
ss = (*int16)(unsafe.Pointer(pss))
|
||||
return
|
||||
}
|
||||
|
||||
// SysFreeString frees string system memory. This must be called with SysAllocString.
|
||||
func SysFreeString(v *int16) (err error) {
|
||||
hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SysStringLen is the length of the system allocated string.
|
||||
func SysStringLen(v *int16) uint32 {
|
||||
l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
|
||||
return uint32(l)
|
||||
}
|
||||
|
||||
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
|
||||
//
|
||||
// This handles default IDispatch implementation for objects. It haves a few
|
||||
// limitations with only supporting one language. It will also only return
|
||||
// default exception codes.
|
||||
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) {
|
||||
hr, _, _ := procCreateStdDispatch.Call(
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
v,
|
||||
uintptr(unsafe.Pointer(ptinfo)),
|
||||
uintptr(unsafe.Pointer(&disp)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
|
||||
//
|
||||
// This will not handle the full implementation of the interface.
|
||||
func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) {
|
||||
hr, _, _ := procCreateDispTypeInfo.Call(
|
||||
uintptr(unsafe.Pointer(idata)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&pptinfo)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// copyMemory moves location of a block of memory.
|
||||
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {
|
||||
procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length))
|
||||
}
|
||||
|
||||
// GetUserDefaultLCID retrieves current user default locale.
|
||||
func GetUserDefaultLCID() (lcid uint32) {
|
||||
ret, _, _ := procGetUserDefaultLCID.Call()
|
||||
lcid = uint32(ret)
|
||||
return
|
||||
}
|
||||
|
||||
// GetMessage in message queue from runtime.
|
||||
//
|
||||
// This function appears to block. PeekMessage does not block.
|
||||
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
|
||||
r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax))
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
// DispatchMessage to window procedure.
|
||||
func DispatchMessage(msg *Msg) (ret int32) {
|
||||
r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
174
vendor/github.com/go-ole/go-ole/com_func.go
generated
vendored
174
vendor/github.com/go-ole/go-ole/com_func.go
generated
vendored
@@ -1,174 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// coInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func coInitialize() error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// coInitializeEx initializes COM library with concurrency model.
|
||||
func coInitializeEx(coinit uint32) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CoInitialize initializes COM library on current thread.
|
||||
//
|
||||
// MSDN documentation suggests that this function should not be called. Call
|
||||
// CoInitializeEx() instead. The reason has to do with threading and this
|
||||
// function is only for single-threaded apartments.
|
||||
//
|
||||
// That said, most users of the library have gotten away with just this
|
||||
// function. If you are experiencing threading issues, then use
|
||||
// CoInitializeEx().
|
||||
func CoInitialize(p uintptr) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CoInitializeEx initializes COM library with concurrency model.
|
||||
func CoInitializeEx(p uintptr, coinit uint32) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CoUninitialize uninitializes COM Library.
|
||||
func CoUninitialize() {}
|
||||
|
||||
// CoTaskMemFree frees memory pointer.
|
||||
func CoTaskMemFree(memptr uintptr) {}
|
||||
|
||||
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
|
||||
//
|
||||
// The Programmatic Identifier must be registered, because it will be looked up
|
||||
// in the Windows Registry. The registry entry has the following keys: CLSID,
|
||||
// Insertable, Protocol and Shell
|
||||
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
|
||||
//
|
||||
// programID identifies the class id with less precision and is not guaranteed
|
||||
// to be unique. These are usually found in the registry under
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
|
||||
// "Program.Component.Version" with version being optional.
|
||||
//
|
||||
// CLSIDFromProgID in Windows API.
|
||||
func CLSIDFromProgID(progId string) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CLSIDFromString retrieves Class ID from string representation.
|
||||
//
|
||||
// This is technically the string version of the GUID and will convert the
|
||||
// string to object.
|
||||
//
|
||||
// CLSIDFromString in Windows API.
|
||||
func CLSIDFromString(str string) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// StringFromCLSID returns GUID formated string from GUID object.
|
||||
func StringFromCLSID(clsid *GUID) (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// IIDFromString returns GUID from program ID.
|
||||
func IIDFromString(progId string) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// StringFromIID returns GUID formatted string from GUID object.
|
||||
func StringFromIID(iid *GUID) (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CreateInstance of single uninitialized object with GUID.
|
||||
func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// GetActiveObject retrieves pointer to active object.
|
||||
func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// VariantInit initializes variant.
|
||||
func VariantInit(v *VARIANT) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// VariantClear clears value in Variant settings to VT_EMPTY.
|
||||
func VariantClear(v *VARIANT) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// SysAllocString allocates memory for string and copies string into memory.
|
||||
func SysAllocString(v string) *int16 {
|
||||
u := int16(0)
|
||||
return &u
|
||||
}
|
||||
|
||||
// SysAllocStringLen copies up to length of given string returning pointer.
|
||||
func SysAllocStringLen(v string) *int16 {
|
||||
u := int16(0)
|
||||
return &u
|
||||
}
|
||||
|
||||
// SysFreeString frees string system memory. This must be called with SysAllocString.
|
||||
func SysFreeString(v *int16) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// SysStringLen is the length of the system allocated string.
|
||||
func SysStringLen(v *int16) uint32 {
|
||||
return uint32(0)
|
||||
}
|
||||
|
||||
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
|
||||
//
|
||||
// This handles default IDispatch implementation for objects. It haves a few
|
||||
// limitations with only supporting one language. It will also only return
|
||||
// default exception codes.
|
||||
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
|
||||
//
|
||||
// This will not handle the full implementation of the interface.
|
||||
func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// copyMemory moves location of a block of memory.
|
||||
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {}
|
||||
|
||||
// GetUserDefaultLCID retrieves current user default locale.
|
||||
func GetUserDefaultLCID() uint32 {
|
||||
return uint32(0)
|
||||
}
|
||||
|
||||
// GetMessage in message queue from runtime.
|
||||
//
|
||||
// This function appears to block. PeekMessage does not block.
|
||||
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) {
|
||||
return int32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// DispatchMessage to window procedure.
|
||||
func DispatchMessage(msg *Msg) int32 {
|
||||
return int32(0)
|
||||
}
|
||||
|
||||
func GetVariantDate(value uint64) (time.Time, error) {
|
||||
return time.Now(), NewError(E_NOTIMPL)
|
||||
}
|
||||
192
vendor/github.com/go-ole/go-ole/connect.go
generated
vendored
192
vendor/github.com/go-ole/go-ole/connect.go
generated
vendored
@@ -1,192 +0,0 @@
|
||||
package ole
|
||||
|
||||
// Connection contains IUnknown for fluent interface interaction.
|
||||
//
|
||||
// Deprecated. Use oleutil package instead.
|
||||
type Connection struct {
|
||||
Object *IUnknown // Access COM
|
||||
}
|
||||
|
||||
// Initialize COM.
|
||||
func (*Connection) Initialize() (err error) {
|
||||
return coInitialize()
|
||||
}
|
||||
|
||||
// Uninitialize COM.
|
||||
func (*Connection) Uninitialize() {
|
||||
CoUninitialize()
|
||||
}
|
||||
|
||||
// Create IUnknown object based first on ProgId and then from String.
|
||||
func (c *Connection) Create(progId string) (err error) {
|
||||
var clsid *GUID
|
||||
clsid, err = CLSIDFromProgID(progId)
|
||||
if err != nil {
|
||||
clsid, err = CLSIDFromString(progId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
unknown, err := CreateInstance(clsid, IID_IUnknown)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Object = unknown
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Release IUnknown object.
|
||||
func (c *Connection) Release() {
|
||||
c.Object.Release()
|
||||
}
|
||||
|
||||
// Load COM object from list of programIDs or strings.
|
||||
func (c *Connection) Load(names ...string) (errors []error) {
|
||||
var tempErrors []error = make([]error, len(names))
|
||||
var numErrors int = 0
|
||||
for _, name := range names {
|
||||
err := c.Create(name)
|
||||
if err != nil {
|
||||
tempErrors = append(tempErrors, err)
|
||||
numErrors += 1
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
copy(errors, tempErrors[0:numErrors])
|
||||
return
|
||||
}
|
||||
|
||||
// Dispatch returns Dispatch object.
|
||||
func (c *Connection) Dispatch() (object *Dispatch, err error) {
|
||||
dispatch, err := c.Object.QueryInterface(IID_IDispatch)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
object = &Dispatch{dispatch}
|
||||
return
|
||||
}
|
||||
|
||||
// Dispatch stores IDispatch object.
|
||||
type Dispatch struct {
|
||||
Object *IDispatch // Dispatch object.
|
||||
}
|
||||
|
||||
// Call method on IDispatch with parameters.
|
||||
func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) {
|
||||
id, err := d.GetId(method)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_METHOD, params)
|
||||
return
|
||||
}
|
||||
|
||||
// MustCall method on IDispatch with parameters.
|
||||
func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) {
|
||||
id, err := d.GetId(method)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_METHOD, params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get property on IDispatch with parameters.
|
||||
func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
|
||||
return
|
||||
}
|
||||
|
||||
// MustGet property on IDispatch with parameters.
|
||||
func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set property on IDispatch with parameters.
|
||||
func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
|
||||
return
|
||||
}
|
||||
|
||||
// MustSet property on IDispatch with parameters.
|
||||
func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) {
|
||||
id, err := d.GetId(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetId retrieves ID of name on IDispatch.
|
||||
func (d *Dispatch) GetId(name string) (id int32, err error) {
|
||||
var dispid []int32
|
||||
dispid, err = d.Object.GetIDsOfName([]string{name})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
id = dispid[0]
|
||||
return
|
||||
}
|
||||
|
||||
// GetIds retrieves all IDs of names on IDispatch.
|
||||
func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) {
|
||||
dispid, err = d.Object.GetIDsOfName(names)
|
||||
return
|
||||
}
|
||||
|
||||
// Invoke IDispatch on DisplayID of dispatch type with parameters.
|
||||
//
|
||||
// There have been problems where if send cascading params..., it would error
|
||||
// out because the parameters would be empty.
|
||||
func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) {
|
||||
if len(params) < 1 {
|
||||
result, err = d.Object.Invoke(id, dispatch)
|
||||
} else {
|
||||
result, err = d.Object.Invoke(id, dispatch, params...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Release IDispatch object.
|
||||
func (d *Dispatch) Release() {
|
||||
d.Object.Release()
|
||||
}
|
||||
|
||||
// Connect initializes COM and attempts to load IUnknown based on given names.
|
||||
func Connect(names ...string) (connection *Connection) {
|
||||
connection.Initialize()
|
||||
connection.Load(names...)
|
||||
return
|
||||
}
|
||||
153
vendor/github.com/go-ole/go-ole/constants.go
generated
vendored
153
vendor/github.com/go-ole/go-ole/constants.go
generated
vendored
@@ -1,153 +0,0 @@
|
||||
package ole
|
||||
|
||||
const (
|
||||
CLSCTX_INPROC_SERVER = 1
|
||||
CLSCTX_INPROC_HANDLER = 2
|
||||
CLSCTX_LOCAL_SERVER = 4
|
||||
CLSCTX_INPROC_SERVER16 = 8
|
||||
CLSCTX_REMOTE_SERVER = 16
|
||||
CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER
|
||||
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
|
||||
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
|
||||
)
|
||||
|
||||
const (
|
||||
COINIT_APARTMENTTHREADED = 0x2
|
||||
COINIT_MULTITHREADED = 0x0
|
||||
COINIT_DISABLE_OLE1DDE = 0x4
|
||||
COINIT_SPEED_OVER_MEMORY = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
DISPATCH_METHOD = 1
|
||||
DISPATCH_PROPERTYGET = 2
|
||||
DISPATCH_PROPERTYPUT = 4
|
||||
DISPATCH_PROPERTYPUTREF = 8
|
||||
)
|
||||
|
||||
const (
|
||||
S_OK = 0x00000000
|
||||
E_UNEXPECTED = 0x8000FFFF
|
||||
E_NOTIMPL = 0x80004001
|
||||
E_OUTOFMEMORY = 0x8007000E
|
||||
E_INVALIDARG = 0x80070057
|
||||
E_NOINTERFACE = 0x80004002
|
||||
E_POINTER = 0x80004003
|
||||
E_HANDLE = 0x80070006
|
||||
E_ABORT = 0x80004004
|
||||
E_FAIL = 0x80004005
|
||||
E_ACCESSDENIED = 0x80070005
|
||||
E_PENDING = 0x8000000A
|
||||
|
||||
CO_E_CLASSSTRING = 0x800401F3
|
||||
)
|
||||
|
||||
const (
|
||||
CC_FASTCALL = iota
|
||||
CC_CDECL
|
||||
CC_MSCPASCAL
|
||||
CC_PASCAL = CC_MSCPASCAL
|
||||
CC_MACPASCAL
|
||||
CC_STDCALL
|
||||
CC_FPFASTCALL
|
||||
CC_SYSCALL
|
||||
CC_MPWCDECL
|
||||
CC_MPWPASCAL
|
||||
CC_MAX = CC_MPWPASCAL
|
||||
)
|
||||
|
||||
type VT uint16
|
||||
|
||||
const (
|
||||
VT_EMPTY VT = 0x0
|
||||
VT_NULL VT = 0x1
|
||||
VT_I2 VT = 0x2
|
||||
VT_I4 VT = 0x3
|
||||
VT_R4 VT = 0x4
|
||||
VT_R8 VT = 0x5
|
||||
VT_CY VT = 0x6
|
||||
VT_DATE VT = 0x7
|
||||
VT_BSTR VT = 0x8
|
||||
VT_DISPATCH VT = 0x9
|
||||
VT_ERROR VT = 0xa
|
||||
VT_BOOL VT = 0xb
|
||||
VT_VARIANT VT = 0xc
|
||||
VT_UNKNOWN VT = 0xd
|
||||
VT_DECIMAL VT = 0xe
|
||||
VT_I1 VT = 0x10
|
||||
VT_UI1 VT = 0x11
|
||||
VT_UI2 VT = 0x12
|
||||
VT_UI4 VT = 0x13
|
||||
VT_I8 VT = 0x14
|
||||
VT_UI8 VT = 0x15
|
||||
VT_INT VT = 0x16
|
||||
VT_UINT VT = 0x17
|
||||
VT_VOID VT = 0x18
|
||||
VT_HRESULT VT = 0x19
|
||||
VT_PTR VT = 0x1a
|
||||
VT_SAFEARRAY VT = 0x1b
|
||||
VT_CARRAY VT = 0x1c
|
||||
VT_USERDEFINED VT = 0x1d
|
||||
VT_LPSTR VT = 0x1e
|
||||
VT_LPWSTR VT = 0x1f
|
||||
VT_RECORD VT = 0x24
|
||||
VT_INT_PTR VT = 0x25
|
||||
VT_UINT_PTR VT = 0x26
|
||||
VT_FILETIME VT = 0x40
|
||||
VT_BLOB VT = 0x41
|
||||
VT_STREAM VT = 0x42
|
||||
VT_STORAGE VT = 0x43
|
||||
VT_STREAMED_OBJECT VT = 0x44
|
||||
VT_STORED_OBJECT VT = 0x45
|
||||
VT_BLOB_OBJECT VT = 0x46
|
||||
VT_CF VT = 0x47
|
||||
VT_CLSID VT = 0x48
|
||||
VT_BSTR_BLOB VT = 0xfff
|
||||
VT_VECTOR VT = 0x1000
|
||||
VT_ARRAY VT = 0x2000
|
||||
VT_BYREF VT = 0x4000
|
||||
VT_RESERVED VT = 0x8000
|
||||
VT_ILLEGAL VT = 0xffff
|
||||
VT_ILLEGALMASKED VT = 0xfff
|
||||
VT_TYPEMASK VT = 0xfff
|
||||
)
|
||||
|
||||
const (
|
||||
DISPID_UNKNOWN = -1
|
||||
DISPID_VALUE = 0
|
||||
DISPID_PROPERTYPUT = -3
|
||||
DISPID_NEWENUM = -4
|
||||
DISPID_EVALUATE = -5
|
||||
DISPID_CONSTRUCTOR = -6
|
||||
DISPID_DESTRUCTOR = -7
|
||||
DISPID_COLLECT = -8
|
||||
)
|
||||
|
||||
const (
|
||||
TKIND_ENUM = 1
|
||||
TKIND_RECORD = 2
|
||||
TKIND_MODULE = 3
|
||||
TKIND_INTERFACE = 4
|
||||
TKIND_DISPATCH = 5
|
||||
TKIND_COCLASS = 6
|
||||
TKIND_ALIAS = 7
|
||||
TKIND_UNION = 8
|
||||
TKIND_MAX = 9
|
||||
)
|
||||
|
||||
// Safe Array Feature Flags
|
||||
|
||||
const (
|
||||
FADF_AUTO = 0x0001
|
||||
FADF_STATIC = 0x0002
|
||||
FADF_EMBEDDED = 0x0004
|
||||
FADF_FIXEDSIZE = 0x0010
|
||||
FADF_RECORD = 0x0020
|
||||
FADF_HAVEIID = 0x0040
|
||||
FADF_HAVEVARTYPE = 0x0080
|
||||
FADF_BSTR = 0x0100
|
||||
FADF_UNKNOWN = 0x0200
|
||||
FADF_DISPATCH = 0x0400
|
||||
FADF_VARIANT = 0x0800
|
||||
FADF_RESERVED = 0xF008
|
||||
)
|
||||
51
vendor/github.com/go-ole/go-ole/error.go
generated
vendored
51
vendor/github.com/go-ole/go-ole/error.go
generated
vendored
@@ -1,51 +0,0 @@
|
||||
package ole
|
||||
|
||||
// OleError stores COM errors.
|
||||
type OleError struct {
|
||||
hr uintptr
|
||||
description string
|
||||
subError error
|
||||
}
|
||||
|
||||
// NewError creates new error with HResult.
|
||||
func NewError(hr uintptr) *OleError {
|
||||
return &OleError{hr: hr}
|
||||
}
|
||||
|
||||
// NewErrorWithDescription creates new COM error with HResult and description.
|
||||
func NewErrorWithDescription(hr uintptr, description string) *OleError {
|
||||
return &OleError{hr: hr, description: description}
|
||||
}
|
||||
|
||||
// NewErrorWithSubError creates new COM error with parent error.
|
||||
func NewErrorWithSubError(hr uintptr, description string, err error) *OleError {
|
||||
return &OleError{hr: hr, description: description, subError: err}
|
||||
}
|
||||
|
||||
// Code is the HResult.
|
||||
func (v *OleError) Code() uintptr {
|
||||
return uintptr(v.hr)
|
||||
}
|
||||
|
||||
// String description, either manually set or format message with error code.
|
||||
func (v *OleError) String() string {
|
||||
if v.description != "" {
|
||||
return errstr(int(v.hr)) + " (" + v.description + ")"
|
||||
}
|
||||
return errstr(int(v.hr))
|
||||
}
|
||||
|
||||
// Error implements error interface.
|
||||
func (v *OleError) Error() string {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
// Description retrieves error summary, if there is one.
|
||||
func (v *OleError) Description() string {
|
||||
return v.description
|
||||
}
|
||||
|
||||
// SubError returns parent error, if there is one.
|
||||
func (v *OleError) SubError() error {
|
||||
return v.subError
|
||||
}
|
||||
8
vendor/github.com/go-ole/go-ole/error_func.go
generated
vendored
8
vendor/github.com/go-ole/go-ole/error_func.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
// errstr converts error code to string.
|
||||
func errstr(errno int) string {
|
||||
return ""
|
||||
}
|
||||
24
vendor/github.com/go-ole/go-ole/error_windows.go
generated
vendored
24
vendor/github.com/go-ole/go-ole/error_windows.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// errstr converts error code to string.
|
||||
func errstr(errno int) string {
|
||||
// ask windows for the remaining errors
|
||||
var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
b := make([]uint16, 300)
|
||||
n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err)
|
||||
}
|
||||
// trim terminating \r and \n
|
||||
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
|
||||
}
|
||||
return string(utf16.Decode(b[:n]))
|
||||
}
|
||||
284
vendor/github.com/go-ole/go-ole/guid.go
generated
vendored
284
vendor/github.com/go-ole/go-ole/guid.go
generated
vendored
@@ -1,284 +0,0 @@
|
||||
package ole
|
||||
|
||||
var (
|
||||
// IID_NULL is null Interface ID, used when no other Interface ID is known.
|
||||
IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}")
|
||||
|
||||
// IID_IUnknown is for IUnknown interfaces.
|
||||
IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}")
|
||||
|
||||
// IID_IDispatch is for IDispatch interfaces.
|
||||
IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}")
|
||||
|
||||
// IID_IEnumVariant is for IEnumVariant interfaces
|
||||
IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}")
|
||||
|
||||
// IID_IConnectionPointContainer is for IConnectionPointContainer interfaces.
|
||||
IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}")
|
||||
|
||||
// IID_IConnectionPoint is for IConnectionPoint interfaces.
|
||||
IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}")
|
||||
|
||||
// IID_IInspectable is for IInspectable interfaces.
|
||||
IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}")
|
||||
|
||||
// IID_IProvideClassInfo is for IProvideClassInfo interfaces.
|
||||
IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}")
|
||||
)
|
||||
|
||||
// These are for testing and not part of any library.
|
||||
var (
|
||||
// IID_ICOMTestString is for ICOMTestString interfaces.
|
||||
//
|
||||
// {E0133EB4-C36F-469A-9D3D-C66B84BE19ED}
|
||||
IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}")
|
||||
|
||||
// IID_ICOMTestInt8 is for ICOMTestInt8 interfaces.
|
||||
//
|
||||
// {BEB06610-EB84-4155-AF58-E2BFF53680B4}
|
||||
IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}")
|
||||
|
||||
// IID_ICOMTestInt16 is for ICOMTestInt16 interfaces.
|
||||
//
|
||||
// {DAA3F9FA-761E-4976-A860-8364CE55F6FC}
|
||||
IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}")
|
||||
|
||||
// IID_ICOMTestInt32 is for ICOMTestInt32 interfaces.
|
||||
//
|
||||
// {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}
|
||||
IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}")
|
||||
|
||||
// IID_ICOMTestInt64 is for ICOMTestInt64 interfaces.
|
||||
//
|
||||
// {8D437CBC-B3ED-485C-BC32-C336432A1623}
|
||||
IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}")
|
||||
|
||||
// IID_ICOMTestFloat is for ICOMTestFloat interfaces.
|
||||
//
|
||||
// {BF1ED004-EA02-456A-AA55-2AC8AC6B054C}
|
||||
IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}")
|
||||
|
||||
// IID_ICOMTestDouble is for ICOMTestDouble interfaces.
|
||||
//
|
||||
// {BF908A81-8687-4E93-999F-D86FAB284BA0}
|
||||
IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}")
|
||||
|
||||
// IID_ICOMTestBoolean is for ICOMTestBoolean interfaces.
|
||||
//
|
||||
// {D530E7A6-4EE8-40D1-8931-3D63B8605010}
|
||||
IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}")
|
||||
|
||||
// IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces.
|
||||
//
|
||||
// {6485B1EF-D780-4834-A4FE-1EBB51746CA3}
|
||||
IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}")
|
||||
|
||||
// IID_ICOMTestTypes is for ICOMTestTypes interfaces.
|
||||
//
|
||||
// {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}
|
||||
IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}")
|
||||
|
||||
// CLSID_COMEchoTestObject is for COMEchoTestObject class.
|
||||
//
|
||||
// {3C24506A-AE9E-4D50-9157-EF317281F1B0}
|
||||
CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}")
|
||||
|
||||
// CLSID_COMTestScalarClass is for COMTestScalarClass class.
|
||||
//
|
||||
// {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}
|
||||
CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}")
|
||||
)
|
||||
|
||||
const hextable = "0123456789ABCDEF"
|
||||
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
|
||||
|
||||
// GUID is Windows API specific GUID type.
|
||||
//
|
||||
// This exists to match Windows GUID type for direct passing for COM.
|
||||
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
|
||||
type GUID struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
// NewGUID converts the given string into a globally unique identifier that is
|
||||
// compliant with the Windows API.
|
||||
//
|
||||
// The supplied string may be in any of these formats:
|
||||
//
|
||||
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// The conversion of the supplied string is not case-sensitive.
|
||||
func NewGUID(guid string) *GUID {
|
||||
d := []byte(guid)
|
||||
var d1, d2, d3, d4a, d4b []byte
|
||||
|
||||
switch len(d) {
|
||||
case 38:
|
||||
if d[0] != '{' || d[37] != '}' {
|
||||
return nil
|
||||
}
|
||||
d = d[1:37]
|
||||
fallthrough
|
||||
case 36:
|
||||
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
d1 = d[0:8]
|
||||
d2 = d[9:13]
|
||||
d3 = d[14:18]
|
||||
d4a = d[19:23]
|
||||
d4b = d[24:36]
|
||||
case 32:
|
||||
d1 = d[0:8]
|
||||
d2 = d[8:12]
|
||||
d3 = d[12:16]
|
||||
d4a = d[16:20]
|
||||
d4b = d[20:32]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
var g GUID
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
g.Data1, ok1 = decodeHexUint32(d1)
|
||||
g.Data2, ok2 = decodeHexUint16(d2)
|
||||
g.Data3, ok3 = decodeHexUint16(d3)
|
||||
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
|
||||
if ok1 && ok2 && ok3 && ok4 {
|
||||
return &g
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeHexUint32(src []byte) (value uint32, ok bool) {
|
||||
var b1, b2, b3, b4 byte
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
b3, ok3 = decodeHexByte(src[4], src[5])
|
||||
b4, ok4 = decodeHexByte(src[6], src[7])
|
||||
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
|
||||
ok = ok1 && ok2 && ok3 && ok4
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexUint16(src []byte) (value uint16, ok bool) {
|
||||
var b1, b2 byte
|
||||
var ok1, ok2 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
value = (uint16(b1) << 8) | uint16(b2)
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
|
||||
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
|
||||
value[0], ok1 = decodeHexByte(s1[0], s1[1])
|
||||
value[1], ok2 = decodeHexByte(s1[2], s1[3])
|
||||
value[2], ok3 = decodeHexByte(s2[0], s2[1])
|
||||
value[3], ok4 = decodeHexByte(s2[2], s2[3])
|
||||
value[4], ok5 = decodeHexByte(s2[4], s2[5])
|
||||
value[5], ok6 = decodeHexByte(s2[6], s2[7])
|
||||
value[6], ok7 = decodeHexByte(s2[8], s2[9])
|
||||
value[7], ok8 = decodeHexByte(s2[10], s2[11])
|
||||
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
|
||||
var n1, n2 byte
|
||||
var ok1, ok2 bool
|
||||
n1, ok1 = decodeHexChar(c1)
|
||||
n2, ok2 = decodeHexChar(c2)
|
||||
value = (n1 << 4) | n2
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexChar(c byte) (byte, bool) {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0', true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10, true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// String converts the GUID to string form. It will adhere to this pattern:
|
||||
//
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// If the GUID is nil, the string representation of an empty GUID is returned:
|
||||
//
|
||||
// {00000000-0000-0000-0000-000000000000}
|
||||
func (guid *GUID) String() string {
|
||||
if guid == nil {
|
||||
return emptyGUID
|
||||
}
|
||||
|
||||
var c [38]byte
|
||||
c[0] = '{'
|
||||
putUint32Hex(c[1:9], guid.Data1)
|
||||
c[9] = '-'
|
||||
putUint16Hex(c[10:14], guid.Data2)
|
||||
c[14] = '-'
|
||||
putUint16Hex(c[15:19], guid.Data3)
|
||||
c[19] = '-'
|
||||
putByteHex(c[20:24], guid.Data4[0:2])
|
||||
c[24] = '-'
|
||||
putByteHex(c[25:37], guid.Data4[2:8])
|
||||
c[37] = '}'
|
||||
return string(c[:])
|
||||
}
|
||||
|
||||
func putUint32Hex(b []byte, v uint32) {
|
||||
b[0] = hextable[byte(v>>24)>>4]
|
||||
b[1] = hextable[byte(v>>24)&0x0f]
|
||||
b[2] = hextable[byte(v>>16)>>4]
|
||||
b[3] = hextable[byte(v>>16)&0x0f]
|
||||
b[4] = hextable[byte(v>>8)>>4]
|
||||
b[5] = hextable[byte(v>>8)&0x0f]
|
||||
b[6] = hextable[byte(v)>>4]
|
||||
b[7] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putUint16Hex(b []byte, v uint16) {
|
||||
b[0] = hextable[byte(v>>8)>>4]
|
||||
b[1] = hextable[byte(v>>8)&0x0f]
|
||||
b[2] = hextable[byte(v)>>4]
|
||||
b[3] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putByteHex(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i*2] = hextable[src[i]>>4]
|
||||
dst[i*2+1] = hextable[src[i]&0x0f]
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqualGUID compares two GUID.
|
||||
//
|
||||
// Not constant time comparison.
|
||||
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
|
||||
return guid1.Data1 == guid2.Data1 &&
|
||||
guid1.Data2 == guid2.Data2 &&
|
||||
guid1.Data3 == guid2.Data3 &&
|
||||
guid1.Data4[0] == guid2.Data4[0] &&
|
||||
guid1.Data4[1] == guid2.Data4[1] &&
|
||||
guid1.Data4[2] == guid2.Data4[2] &&
|
||||
guid1.Data4[3] == guid2.Data4[3] &&
|
||||
guid1.Data4[4] == guid2.Data4[4] &&
|
||||
guid1.Data4[5] == guid2.Data4[5] &&
|
||||
guid1.Data4[6] == guid2.Data4[6] &&
|
||||
guid1.Data4[7] == guid2.Data4[7]
|
||||
}
|
||||
20
vendor/github.com/go-ole/go-ole/iconnectionpoint.go
generated
vendored
20
vendor/github.com/go-ole/go-ole/iconnectionpoint.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IConnectionPoint struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IConnectionPointVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetConnectionInterface uintptr
|
||||
GetConnectionPointContainer uintptr
|
||||
Advise uintptr
|
||||
Unadvise uintptr
|
||||
EnumConnections uintptr
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) VTable() *IConnectionPointVtbl {
|
||||
return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
21
vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go
generated
vendored
21
vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
|
||||
return int32(0)
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) {
|
||||
return uint32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Unadvise(cookie uint32) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
43
vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go
generated
vendored
43
vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
|
||||
// XXX: This doesn't look like it does what it's supposed to
|
||||
return release((*IUnknown)(unsafe.Pointer(v)))
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().Advise,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(unknown)),
|
||||
uintptr(unsafe.Pointer(&cookie)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().Unadvise,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(cookie),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
17
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go
generated
vendored
17
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go
generated
vendored
@@ -1,17 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IConnectionPointContainer struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IConnectionPointContainerVtbl struct {
|
||||
IUnknownVtbl
|
||||
EnumConnectionPoints uintptr
|
||||
FindConnectionPoint uintptr
|
||||
}
|
||||
|
||||
func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl {
|
||||
return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
11
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go
generated
vendored
11
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
25
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go
generated
vendored
25
vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go
generated
vendored
@@ -1,25 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().FindConnectionPoint,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(point)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
94
vendor/github.com/go-ole/go-ole/idispatch.go
generated
vendored
94
vendor/github.com/go-ole/go-ole/idispatch.go
generated
vendored
@@ -1,94 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IDispatch struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IDispatchVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
}
|
||||
|
||||
func (v *IDispatch) VTable() *IDispatchVtbl {
|
||||
return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) {
|
||||
dispid, err = getIDsOfName(v, names)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
|
||||
result, err = invoke(v, dispid, dispatch, params...)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) {
|
||||
c, err = getTypeInfoCount(v)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) {
|
||||
tinfo, err = getTypeInfo(v)
|
||||
return
|
||||
}
|
||||
|
||||
// GetSingleIDOfName is a helper that returns single display ID for IDispatch name.
|
||||
//
|
||||
// This replaces the common pattern of attempting to get a single name from the list of available
|
||||
// IDs. It gives the first ID, if it is available.
|
||||
func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) {
|
||||
var displayIDs []int32
|
||||
displayIDs, err = v.GetIDsOfName([]string{name})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
displayID = displayIDs[0]
|
||||
return
|
||||
}
|
||||
|
||||
// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke.
|
||||
//
|
||||
// Accepts name and will attempt to retrieve Display ID to pass to Invoke.
|
||||
//
|
||||
// Passing params as an array is a workaround that could be fixed in later versions of Go that
|
||||
// prevent passing empty params. During testing it was discovered that this is an acceptable way of
|
||||
// getting around not being able to pass params normally.
|
||||
func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) {
|
||||
displayID, err := v.GetSingleIDOfName(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(params) < 1 {
|
||||
result, err = v.Invoke(displayID, dispatch)
|
||||
} else {
|
||||
result, err = v.Invoke(displayID, dispatch, params...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CallMethod invokes named function with arguments on object.
|
||||
func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) {
|
||||
return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params)
|
||||
}
|
||||
|
||||
// GetProperty retrieves the property with the name with the ability to pass arguments.
|
||||
//
|
||||
// Most of the time you will not need to pass arguments as most objects do not allow for this
|
||||
// feature. Or at least, should not allow for this feature. Some servers don't follow best practices
|
||||
// and this is provided for those edge cases.
|
||||
func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) {
|
||||
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params)
|
||||
}
|
||||
|
||||
// PutProperty attempts to mutate a property in the object.
|
||||
func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) {
|
||||
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params)
|
||||
}
|
||||
19
vendor/github.com/go-ole/go-ole/idispatch_func.go
generated
vendored
19
vendor/github.com/go-ole/go-ole/idispatch_func.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) {
|
||||
return []int32{}, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func getTypeInfoCount(disp *IDispatch) (uint32, error) {
|
||||
return uint32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
203
vendor/github.com/go-ole/go-ole/idispatch_windows.go
generated
vendored
203
vendor/github.com/go-ole/go-ole/idispatch_windows.go
generated
vendored
@@ -1,203 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) {
|
||||
wnames := make([]*uint16, len(names))
|
||||
for i := 0; i < len(names); i++ {
|
||||
wnames[i] = syscall.StringToUTF16Ptr(names[i])
|
||||
}
|
||||
dispid = make([]int32, len(names))
|
||||
namelen := uint32(len(names))
|
||||
hr, _, _ := syscall.Syscall6(
|
||||
disp.VTable().GetIDsOfNames,
|
||||
6,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(IID_NULL)),
|
||||
uintptr(unsafe.Pointer(&wnames[0])),
|
||||
uintptr(namelen),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&dispid[0])))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTypeInfoCount(disp *IDispatch) (c uint32, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
disp.VTable().GetTypeInfoCount,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(&c)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
disp.VTable().GetTypeInfo,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&tinfo)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
|
||||
var dispparams DISPPARAMS
|
||||
|
||||
if dispatch&DISPATCH_PROPERTYPUT != 0 {
|
||||
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||
dispparams.cNamedArgs = 1
|
||||
} else if dispatch&DISPATCH_PROPERTYPUTREF != 0 {
|
||||
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||
dispparams.cNamedArgs = 1
|
||||
}
|
||||
var vargs []VARIANT
|
||||
if len(params) > 0 {
|
||||
vargs = make([]VARIANT, len(params))
|
||||
for i, v := range params {
|
||||
//n := len(params)-i-1
|
||||
n := len(params) - i - 1
|
||||
VariantInit(&vargs[n])
|
||||
switch vv := v.(type) {
|
||||
case bool:
|
||||
if vv {
|
||||
vargs[n] = NewVariant(VT_BOOL, 0xffff)
|
||||
} else {
|
||||
vargs[n] = NewVariant(VT_BOOL, 0)
|
||||
}
|
||||
case *bool:
|
||||
vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool)))))
|
||||
case uint8:
|
||||
vargs[n] = NewVariant(VT_I1, int64(v.(uint8)))
|
||||
case *uint8:
|
||||
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
|
||||
case int8:
|
||||
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
|
||||
case *int8:
|
||||
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int8)))))
|
||||
case int16:
|
||||
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
|
||||
case *int16:
|
||||
vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16)))))
|
||||
case uint16:
|
||||
vargs[n] = NewVariant(VT_UI2, int64(v.(uint16)))
|
||||
case *uint16:
|
||||
vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16)))))
|
||||
case int32:
|
||||
vargs[n] = NewVariant(VT_I4, int64(v.(int32)))
|
||||
case *int32:
|
||||
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32)))))
|
||||
case uint32:
|
||||
vargs[n] = NewVariant(VT_UI4, int64(v.(uint32)))
|
||||
case *uint32:
|
||||
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32)))))
|
||||
case int64:
|
||||
vargs[n] = NewVariant(VT_I8, int64(v.(int64)))
|
||||
case *int64:
|
||||
vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64)))))
|
||||
case uint64:
|
||||
vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64))))
|
||||
case *uint64:
|
||||
vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64)))))
|
||||
case int:
|
||||
vargs[n] = NewVariant(VT_I4, int64(v.(int)))
|
||||
case *int:
|
||||
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int)))))
|
||||
case uint:
|
||||
vargs[n] = NewVariant(VT_UI4, int64(v.(uint)))
|
||||
case *uint:
|
||||
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint)))))
|
||||
case float32:
|
||||
vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv)))
|
||||
case *float32:
|
||||
vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32)))))
|
||||
case float64:
|
||||
vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv)))
|
||||
case *float64:
|
||||
vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64)))))
|
||||
case *big.Int:
|
||||
vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64())
|
||||
case string:
|
||||
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string))))))
|
||||
case *string:
|
||||
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string)))))
|
||||
case time.Time:
|
||||
s := vv.Format("2006-01-02 15:04:05")
|
||||
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s)))))
|
||||
case *time.Time:
|
||||
s := vv.Format("2006-01-02 15:04:05")
|
||||
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s))))
|
||||
case *IDispatch:
|
||||
vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch)))))
|
||||
case **IDispatch:
|
||||
vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch)))))
|
||||
case nil:
|
||||
vargs[n] = NewVariant(VT_NULL, 0)
|
||||
case *VARIANT:
|
||||
vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT)))))
|
||||
case []byte:
|
||||
safeByteArray := safeArrayFromByteSlice(v.([]byte))
|
||||
vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray))))
|
||||
defer VariantClear(&vargs[n])
|
||||
case []string:
|
||||
safeByteArray := safeArrayFromStringSlice(v.([]string))
|
||||
vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray))))
|
||||
defer VariantClear(&vargs[n])
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
|
||||
dispparams.cArgs = uint32(len(params))
|
||||
}
|
||||
|
||||
result = new(VARIANT)
|
||||
var excepInfo EXCEPINFO
|
||||
VariantInit(result)
|
||||
hr, _, _ := syscall.Syscall9(
|
||||
disp.VTable().Invoke,
|
||||
9,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(dispid),
|
||||
uintptr(unsafe.Pointer(IID_NULL)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(dispatch),
|
||||
uintptr(unsafe.Pointer(&dispparams)),
|
||||
uintptr(unsafe.Pointer(result)),
|
||||
uintptr(unsafe.Pointer(&excepInfo)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
excepInfo.renderStrings()
|
||||
excepInfo.Clear()
|
||||
err = NewErrorWithSubError(hr, excepInfo.description, excepInfo)
|
||||
}
|
||||
for i, varg := range vargs {
|
||||
n := len(params) - i - 1
|
||||
if varg.VT == VT_BSTR && varg.Val != 0 {
|
||||
SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
|
||||
}
|
||||
if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 {
|
||||
*(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val))))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
19
vendor/github.com/go-ole/go-ole/ienumvariant.go
generated
vendored
19
vendor/github.com/go-ole/go-ole/ienumvariant.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IEnumVARIANT struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IEnumVARIANTVtbl struct {
|
||||
IUnknownVtbl
|
||||
Next uintptr
|
||||
Skip uintptr
|
||||
Reset uintptr
|
||||
Clone uintptr
|
||||
}
|
||||
|
||||
func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl {
|
||||
return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
19
vendor/github.com/go-ole/go-ole/ienumvariant_func.go
generated
vendored
19
vendor/github.com/go-ole/go-ole/ienumvariant_func.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Reset() error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Skip(celt uint) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) {
|
||||
return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL)
|
||||
}
|
||||
63
vendor/github.com/go-ole/go-ole/ienumvariant_windows.go
generated
vendored
63
vendor/github.com/go-ole/go-ole/ienumvariant_windows.go
generated
vendored
@@ -1,63 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (enum *IEnumVARIANT) Clone() (cloned *IEnumVARIANT, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
enum.VTable().Clone,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
uintptr(unsafe.Pointer(&cloned)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Reset() (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
enum.VTable().Reset,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
0,
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Skip(celt uint) (err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
enum.VTable().Skip,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
uintptr(celt),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (enum *IEnumVARIANT) Next(celt uint) (array VARIANT, length uint, err error) {
|
||||
hr, _, _ := syscall.Syscall6(
|
||||
enum.VTable().Next,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(enum)),
|
||||
uintptr(celt),
|
||||
uintptr(unsafe.Pointer(&array)),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
18
vendor/github.com/go-ole/go-ole/iinspectable.go
generated
vendored
18
vendor/github.com/go-ole/go-ole/iinspectable.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IInspectable struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IInspectableVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetIIds uintptr
|
||||
GetRuntimeClassName uintptr
|
||||
GetTrustLevel uintptr
|
||||
}
|
||||
|
||||
func (v *IInspectable) VTable() *IInspectableVtbl {
|
||||
return (*IInspectableVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
15
vendor/github.com/go-ole/go-ole/iinspectable_func.go
generated
vendored
15
vendor/github.com/go-ole/go-ole/iinspectable_func.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (v *IInspectable) GetIids() ([]*GUID, error) {
|
||||
return []*GUID{}, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetRuntimeClassName() (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetTrustLevel() (uint32, error) {
|
||||
return uint32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
72
vendor/github.com/go-ole/go-ole/iinspectable_windows.go
generated
vendored
72
vendor/github.com/go-ole/go-ole/iinspectable_windows.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *IInspectable) GetIids() (iids []*GUID, err error) {
|
||||
var count uint32
|
||||
var array uintptr
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetIIds,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&count)),
|
||||
uintptr(unsafe.Pointer(&array)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
return
|
||||
}
|
||||
defer CoTaskMemFree(array)
|
||||
|
||||
iids = make([]*GUID, count)
|
||||
byteCount := count * uint32(unsafe.Sizeof(GUID{}))
|
||||
slicehdr := reflect.SliceHeader{Data: array, Len: int(byteCount), Cap: int(byteCount)}
|
||||
byteSlice := *(*[]byte)(unsafe.Pointer(&slicehdr))
|
||||
reader := bytes.NewReader(byteSlice)
|
||||
for i := range iids {
|
||||
guid := GUID{}
|
||||
err = binary.Read(reader, binary.LittleEndian, &guid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iids[i] = &guid
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetRuntimeClassName() (s string, err error) {
|
||||
var hstring HString
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetRuntimeClassName,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&hstring)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
return
|
||||
}
|
||||
s = hstring.String()
|
||||
DeleteHString(hstring)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IInspectable) GetTrustLevel() (level uint32, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetTrustLevel,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&level)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
21
vendor/github.com/go-ole/go-ole/iprovideclassinfo.go
generated
vendored
21
vendor/github.com/go-ole/go-ole/iprovideclassinfo.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IProvideClassInfo struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type IProvideClassInfoVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetClassInfo uintptr
|
||||
}
|
||||
|
||||
func (v *IProvideClassInfo) VTable() *IProvideClassInfoVtbl {
|
||||
return (*IProvideClassInfoVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IProvideClassInfo) GetClassInfo() (cinfo *ITypeInfo, err error) {
|
||||
cinfo, err = getClassInfo(v)
|
||||
return
|
||||
}
|
||||
7
vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go
generated
vendored
7
vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
21
vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go
generated
vendored
21
vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
disp.VTable().GetClassInfo,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(&tinfo)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
34
vendor/github.com/go-ole/go-ole/itypeinfo.go
generated
vendored
34
vendor/github.com/go-ole/go-ole/itypeinfo.go
generated
vendored
@@ -1,34 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type ITypeInfo struct {
|
||||
IUnknown
|
||||
}
|
||||
|
||||
type ITypeInfoVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetTypeAttr uintptr
|
||||
GetTypeComp uintptr
|
||||
GetFuncDesc uintptr
|
||||
GetVarDesc uintptr
|
||||
GetNames uintptr
|
||||
GetRefTypeOfImplType uintptr
|
||||
GetImplTypeFlags uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
GetDocumentation uintptr
|
||||
GetDllEntry uintptr
|
||||
GetRefTypeInfo uintptr
|
||||
AddressOfMember uintptr
|
||||
CreateInstance uintptr
|
||||
GetMops uintptr
|
||||
GetContainingTypeLib uintptr
|
||||
ReleaseTypeAttr uintptr
|
||||
ReleaseFuncDesc uintptr
|
||||
ReleaseVarDesc uintptr
|
||||
}
|
||||
|
||||
func (v *ITypeInfo) VTable() *ITypeInfoVtbl {
|
||||
return (*ITypeInfoVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
7
vendor/github.com/go-ole/go-ole/itypeinfo_func.go
generated
vendored
7
vendor/github.com/go-ole/go-ole/itypeinfo_func.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func (v *ITypeInfo) GetTypeAttr() (*TYPEATTR, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
21
vendor/github.com/go-ole/go-ole/itypeinfo_windows.go
generated
vendored
21
vendor/github.com/go-ole/go-ole/itypeinfo_windows.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (v *ITypeInfo) GetTypeAttr() (tattr *TYPEATTR, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
uintptr(v.VTable().GetTypeAttr),
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&tattr)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
57
vendor/github.com/go-ole/go-ole/iunknown.go
generated
vendored
57
vendor/github.com/go-ole/go-ole/iunknown.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
package ole
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type IUnknown struct {
|
||||
RawVTable *interface{}
|
||||
}
|
||||
|
||||
type IUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
}
|
||||
|
||||
type UnknownLike interface {
|
||||
QueryInterface(iid *GUID) (disp *IDispatch, err error)
|
||||
AddRef() int32
|
||||
Release() int32
|
||||
}
|
||||
|
||||
func (v *IUnknown) VTable() *IUnknownVtbl {
|
||||
return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IUnknown) PutQueryInterface(interfaceID *GUID, obj interface{}) error {
|
||||
return reflectQueryInterface(v, v.VTable().QueryInterface, interfaceID, obj)
|
||||
}
|
||||
|
||||
func (v *IUnknown) IDispatch(interfaceID *GUID) (dispatch *IDispatch, err error) {
|
||||
err = v.PutQueryInterface(interfaceID, &dispatch)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IUnknown) IEnumVARIANT(interfaceID *GUID) (enum *IEnumVARIANT, err error) {
|
||||
err = v.PutQueryInterface(interfaceID, &enum)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) {
|
||||
return queryInterface(v, iid)
|
||||
}
|
||||
|
||||
func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) {
|
||||
unk, err := queryInterface(v, iid)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return unk
|
||||
}
|
||||
|
||||
func (v *IUnknown) AddRef() int32 {
|
||||
return addRef(v)
|
||||
}
|
||||
|
||||
func (v *IUnknown) Release() int32 {
|
||||
return release(v)
|
||||
}
|
||||
19
vendor/github.com/go-ole/go-ole/iunknown_func.go
generated
vendored
19
vendor/github.com/go-ole/go-ole/iunknown_func.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
func addRef(unk *IUnknown) int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func release(unk *IUnknown) int32 {
|
||||
return 0
|
||||
}
|
||||
58
vendor/github.com/go-ole/go-ole/iunknown_windows.go
generated
vendored
58
vendor/github.com/go-ole/go-ole/iunknown_windows.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) {
|
||||
selfValue := reflect.ValueOf(self).Elem()
|
||||
objValue := reflect.ValueOf(obj).Elem()
|
||||
|
||||
hr, _, _ := syscall.Syscall(
|
||||
method,
|
||||
3,
|
||||
selfValue.UnsafeAddr(),
|
||||
uintptr(unsafe.Pointer(interfaceID)),
|
||||
objValue.Addr().Pointer())
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
unk.VTable().QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
uintptr(unsafe.Pointer(iid)),
|
||||
uintptr(unsafe.Pointer(&disp)))
|
||||
if hr != 0 {
|
||||
err = NewError(hr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addRef(unk *IUnknown) int32 {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
unk.VTable().AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
0,
|
||||
0)
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func release(unk *IUnknown) int32 {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
unk.VTable().Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(unk)),
|
||||
0,
|
||||
0)
|
||||
return int32(ret)
|
||||
}
|
||||
190
vendor/github.com/go-ole/go-ole/ole.go
generated
vendored
190
vendor/github.com/go-ole/go-ole/ole.go
generated
vendored
@@ -1,190 +0,0 @@
|
||||
package ole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DISPPARAMS are the arguments that passed to methods or property.
|
||||
type DISPPARAMS struct {
|
||||
rgvarg uintptr
|
||||
rgdispidNamedArgs uintptr
|
||||
cArgs uint32
|
||||
cNamedArgs uint32
|
||||
}
|
||||
|
||||
// EXCEPINFO defines exception info.
|
||||
type EXCEPINFO struct {
|
||||
wCode uint16
|
||||
wReserved uint16
|
||||
bstrSource *uint16
|
||||
bstrDescription *uint16
|
||||
bstrHelpFile *uint16
|
||||
dwHelpContext uint32
|
||||
pvReserved uintptr
|
||||
pfnDeferredFillIn uintptr
|
||||
scode uint32
|
||||
|
||||
// Go-specific part. Don't move upper cos it'll break structure layout for native code.
|
||||
rendered bool
|
||||
source string
|
||||
description string
|
||||
helpFile string
|
||||
}
|
||||
|
||||
// renderStrings translates BSTR strings to Go ones so `.Error` and `.String`
|
||||
// could be safely called after `.Clear`. We need this when we can't rely on
|
||||
// a caller to call `.Clear`.
|
||||
func (e *EXCEPINFO) renderStrings() {
|
||||
e.rendered = true
|
||||
if e.bstrSource == nil {
|
||||
e.source = "<nil>"
|
||||
} else {
|
||||
e.source = BstrToString(e.bstrSource)
|
||||
}
|
||||
if e.bstrDescription == nil {
|
||||
e.description = "<nil>"
|
||||
} else {
|
||||
e.description = BstrToString(e.bstrDescription)
|
||||
}
|
||||
if e.bstrHelpFile == nil {
|
||||
e.helpFile = "<nil>"
|
||||
} else {
|
||||
e.helpFile = BstrToString(e.bstrHelpFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear frees BSTR strings inside an EXCEPINFO and set it to NULL.
|
||||
func (e *EXCEPINFO) Clear() {
|
||||
freeBSTR := func(s *uint16) {
|
||||
// SysFreeString don't return errors and is safe for call's on NULL.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysfreestring
|
||||
_ = SysFreeString((*int16)(unsafe.Pointer(s)))
|
||||
}
|
||||
|
||||
if e.bstrSource != nil {
|
||||
freeBSTR(e.bstrSource)
|
||||
e.bstrSource = nil
|
||||
}
|
||||
if e.bstrDescription != nil {
|
||||
freeBSTR(e.bstrDescription)
|
||||
e.bstrDescription = nil
|
||||
}
|
||||
if e.bstrHelpFile != nil {
|
||||
freeBSTR(e.bstrHelpFile)
|
||||
e.bstrHelpFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
// WCode return wCode in EXCEPINFO.
|
||||
func (e EXCEPINFO) WCode() uint16 {
|
||||
return e.wCode
|
||||
}
|
||||
|
||||
// SCODE return scode in EXCEPINFO.
|
||||
func (e EXCEPINFO) SCODE() uint32 {
|
||||
return e.scode
|
||||
}
|
||||
|
||||
// String convert EXCEPINFO to string.
|
||||
func (e EXCEPINFO) String() string {
|
||||
if !e.rendered {
|
||||
e.renderStrings()
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x",
|
||||
e.wCode, e.source, e.description, e.helpFile, e.dwHelpContext, e.scode,
|
||||
)
|
||||
}
|
||||
|
||||
// Error implements error interface and returns error string.
|
||||
func (e EXCEPINFO) Error() string {
|
||||
if !e.rendered {
|
||||
e.renderStrings()
|
||||
}
|
||||
|
||||
if e.description != "<nil>" {
|
||||
return strings.TrimSpace(e.description)
|
||||
}
|
||||
|
||||
code := e.scode
|
||||
if e.wCode != 0 {
|
||||
code = uint32(e.wCode)
|
||||
}
|
||||
return fmt.Sprintf("%v: %#x", e.source, code)
|
||||
}
|
||||
|
||||
// PARAMDATA defines parameter data type.
|
||||
type PARAMDATA struct {
|
||||
Name *int16
|
||||
Vt uint16
|
||||
}
|
||||
|
||||
// METHODDATA defines method info.
|
||||
type METHODDATA struct {
|
||||
Name *uint16
|
||||
Data *PARAMDATA
|
||||
Dispid int32
|
||||
Meth uint32
|
||||
CC int32
|
||||
CArgs uint32
|
||||
Flags uint16
|
||||
VtReturn uint32
|
||||
}
|
||||
|
||||
// INTERFACEDATA defines interface info.
|
||||
type INTERFACEDATA struct {
|
||||
MethodData *METHODDATA
|
||||
CMembers uint32
|
||||
}
|
||||
|
||||
// Point is 2D vector type.
|
||||
type Point struct {
|
||||
X int32
|
||||
Y int32
|
||||
}
|
||||
|
||||
// Msg is message between processes.
|
||||
type Msg struct {
|
||||
Hwnd uint32
|
||||
Message uint32
|
||||
Wparam int32
|
||||
Lparam int32
|
||||
Time uint32
|
||||
Pt Point
|
||||
}
|
||||
|
||||
// TYPEDESC defines data type.
|
||||
type TYPEDESC struct {
|
||||
Hreftype uint32
|
||||
VT uint16
|
||||
}
|
||||
|
||||
// IDLDESC defines IDL info.
|
||||
type IDLDESC struct {
|
||||
DwReserved uint32
|
||||
WIDLFlags uint16
|
||||
}
|
||||
|
||||
// TYPEATTR defines type info.
|
||||
type TYPEATTR struct {
|
||||
Guid GUID
|
||||
Lcid uint32
|
||||
dwReserved uint32
|
||||
MemidConstructor int32
|
||||
MemidDestructor int32
|
||||
LpstrSchema *uint16
|
||||
CbSizeInstance uint32
|
||||
Typekind int32
|
||||
CFuncs uint16
|
||||
CVars uint16
|
||||
CImplTypes uint16
|
||||
CbSizeVft uint16
|
||||
CbAlignment uint16
|
||||
WTypeFlags uint16
|
||||
WMajorVerNum uint16
|
||||
WMinorVerNum uint16
|
||||
TdescAlias TYPEDESC
|
||||
IdldescType IDLDESC
|
||||
}
|
||||
27
vendor/github.com/go-ole/go-ole/safearray.go
generated
vendored
27
vendor/github.com/go-ole/go-ole/safearray.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
// Package is meant to retrieve and process safe array data returned from COM.
|
||||
|
||||
package ole
|
||||
|
||||
// SafeArrayBound defines the SafeArray boundaries.
|
||||
type SafeArrayBound struct {
|
||||
Elements uint32
|
||||
LowerBound int32
|
||||
}
|
||||
|
||||
// SafeArray is how COM handles arrays.
|
||||
type SafeArray struct {
|
||||
Dimensions uint16
|
||||
FeaturesFlag uint16
|
||||
ElementsSize uint32
|
||||
LocksAmount uint32
|
||||
Data uint32
|
||||
Bounds [16]byte
|
||||
}
|
||||
|
||||
// SAFEARRAY is obsolete, exists for backwards compatibility.
|
||||
// Use SafeArray
|
||||
type SAFEARRAY SafeArray
|
||||
|
||||
// SAFEARRAYBOUND is obsolete, exists for backwards compatibility.
|
||||
// Use SafeArrayBound
|
||||
type SAFEARRAYBOUND SafeArrayBound
|
||||
211
vendor/github.com/go-ole/go-ole/safearray_func.go
generated
vendored
211
vendor/github.com/go-ole/go-ole/safearray_func.go
generated
vendored
@@ -1,211 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// safeArrayAccessData returns raw array pointer.
|
||||
//
|
||||
// AKA: SafeArrayAccessData in Windows API.
|
||||
func safeArrayAccessData(safearray *SafeArray) (uintptr, error) {
|
||||
return uintptr(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayUnaccessData releases raw array.
|
||||
//
|
||||
// AKA: SafeArrayUnaccessData in Windows API.
|
||||
func safeArrayUnaccessData(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayAllocData allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocData in Windows API.
|
||||
func safeArrayAllocData(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptor allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptor in Windows API.
|
||||
func safeArrayAllocDescriptor(dimensions uint32) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptorEx allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptorEx in Windows API.
|
||||
func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCopy returns copy of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCopy in Windows API.
|
||||
func safeArrayCopy(original *SafeArray) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCopyData duplicates SafeArray into another SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayCopyData in Windows API.
|
||||
func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreate creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreate in Windows API.
|
||||
func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreateEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateEx in Windows API.
|
||||
func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreateVector creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVector in Windows API.
|
||||
func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayCreateVectorEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVectorEx in Windows API.
|
||||
func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (*SafeArray, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayDestroy destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroy in Windows API.
|
||||
func safeArrayDestroy(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayDestroyData destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyData in Windows API.
|
||||
func safeArrayDestroyData(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayDestroyDescriptor destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyDescriptor in Windows API.
|
||||
func safeArrayDestroyDescriptor(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetDim is the amount of dimensions in the SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetDim in Windows API.
|
||||
func safeArrayGetDim(safearray *SafeArray) (*uint32, error) {
|
||||
u := uint32(0)
|
||||
return &u, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetElementSize is the element size in bytes.
|
||||
//
|
||||
// AKA: SafeArrayGetElemsize in Windows API.
|
||||
func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) {
|
||||
u := uint32(0)
|
||||
return &u, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetElement retrieves element at given index.
|
||||
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetElement retrieves element at given index and converts to string.
|
||||
func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) {
|
||||
return "", NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetIID is the InterfaceID of the elements in the SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetIID in Windows API.
|
||||
func safeArrayGetIID(safearray *SafeArray) (*GUID, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetLBound returns lower bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetLBound in Windows API.
|
||||
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) {
|
||||
return int32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetUBound returns upper bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetUBound in Windows API.
|
||||
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) {
|
||||
return int32(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetVartype returns data type of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetVartype in Windows API.
|
||||
func safeArrayGetVartype(safearray *SafeArray) (uint16, error) {
|
||||
return uint16(0), NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayLock locks SafeArray for reading to modify SafeArray.
|
||||
//
|
||||
// This must be called during some calls to ensure that another process does not
|
||||
// read or write to the SafeArray during editing.
|
||||
//
|
||||
// AKA: SafeArrayLock in Windows API.
|
||||
func safeArrayLock(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayUnlock unlocks SafeArray for reading.
|
||||
//
|
||||
// AKA: SafeArrayUnlock in Windows API.
|
||||
func safeArrayUnlock(safearray *SafeArray) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayPutElement stores the data element at the specified location in the
|
||||
// array.
|
||||
//
|
||||
// AKA: SafeArrayPutElement in Windows API.
|
||||
func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArrayGetRecordInfo accesses IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArrayGetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArrayGetRecordInfo(safearray *SafeArray) (interface{}, error) {
|
||||
return nil, NewError(E_NOTIMPL)
|
||||
}
|
||||
|
||||
// safeArraySetRecordInfo mutates IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArraySetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) error {
|
||||
return NewError(E_NOTIMPL)
|
||||
}
|
||||
337
vendor/github.com/go-ole/go-ole/safearray_windows.go
generated
vendored
337
vendor/github.com/go-ole/go-ole/safearray_windows.go
generated
vendored
@@ -1,337 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procSafeArrayAccessData = modoleaut32.NewProc("SafeArrayAccessData")
|
||||
procSafeArrayAllocData = modoleaut32.NewProc("SafeArrayAllocData")
|
||||
procSafeArrayAllocDescriptor = modoleaut32.NewProc("SafeArrayAllocDescriptor")
|
||||
procSafeArrayAllocDescriptorEx = modoleaut32.NewProc("SafeArrayAllocDescriptorEx")
|
||||
procSafeArrayCopy = modoleaut32.NewProc("SafeArrayCopy")
|
||||
procSafeArrayCopyData = modoleaut32.NewProc("SafeArrayCopyData")
|
||||
procSafeArrayCreate = modoleaut32.NewProc("SafeArrayCreate")
|
||||
procSafeArrayCreateEx = modoleaut32.NewProc("SafeArrayCreateEx")
|
||||
procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector")
|
||||
procSafeArrayCreateVectorEx = modoleaut32.NewProc("SafeArrayCreateVectorEx")
|
||||
procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy")
|
||||
procSafeArrayDestroyData = modoleaut32.NewProc("SafeArrayDestroyData")
|
||||
procSafeArrayDestroyDescriptor = modoleaut32.NewProc("SafeArrayDestroyDescriptor")
|
||||
procSafeArrayGetDim = modoleaut32.NewProc("SafeArrayGetDim")
|
||||
procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement")
|
||||
procSafeArrayGetElemsize = modoleaut32.NewProc("SafeArrayGetElemsize")
|
||||
procSafeArrayGetIID = modoleaut32.NewProc("SafeArrayGetIID")
|
||||
procSafeArrayGetLBound = modoleaut32.NewProc("SafeArrayGetLBound")
|
||||
procSafeArrayGetUBound = modoleaut32.NewProc("SafeArrayGetUBound")
|
||||
procSafeArrayGetVartype = modoleaut32.NewProc("SafeArrayGetVartype")
|
||||
procSafeArrayLock = modoleaut32.NewProc("SafeArrayLock")
|
||||
procSafeArrayPtrOfIndex = modoleaut32.NewProc("SafeArrayPtrOfIndex")
|
||||
procSafeArrayUnaccessData = modoleaut32.NewProc("SafeArrayUnaccessData")
|
||||
procSafeArrayUnlock = modoleaut32.NewProc("SafeArrayUnlock")
|
||||
procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement")
|
||||
//procSafeArrayRedim = modoleaut32.NewProc("SafeArrayRedim") // TODO
|
||||
//procSafeArraySetIID = modoleaut32.NewProc("SafeArraySetIID") // TODO
|
||||
procSafeArrayGetRecordInfo = modoleaut32.NewProc("SafeArrayGetRecordInfo")
|
||||
procSafeArraySetRecordInfo = modoleaut32.NewProc("SafeArraySetRecordInfo")
|
||||
)
|
||||
|
||||
// safeArrayAccessData returns raw array pointer.
|
||||
//
|
||||
// AKA: SafeArrayAccessData in Windows API.
|
||||
// Todo: Test
|
||||
func safeArrayAccessData(safearray *SafeArray) (element uintptr, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayAccessData.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&element))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayUnaccessData releases raw array.
|
||||
//
|
||||
// AKA: SafeArrayUnaccessData in Windows API.
|
||||
func safeArrayUnaccessData(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayUnaccessData.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayAllocData allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocData in Windows API.
|
||||
func safeArrayAllocData(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayAllocData.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptor allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptor in Windows API.
|
||||
func safeArrayAllocDescriptor(dimensions uint32) (safearray *SafeArray, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayAllocDescriptor.Call(uintptr(dimensions), uintptr(unsafe.Pointer(&safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayAllocDescriptorEx allocates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayAllocDescriptorEx in Windows API.
|
||||
func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (safearray *SafeArray, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayAllocDescriptorEx.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(dimensions),
|
||||
uintptr(unsafe.Pointer(&safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCopy returns copy of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCopy in Windows API.
|
||||
func safeArrayCopy(original *SafeArray) (safearray *SafeArray, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayCopy.Call(
|
||||
uintptr(unsafe.Pointer(original)),
|
||||
uintptr(unsafe.Pointer(&safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCopyData duplicates SafeArray into another SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayCopyData in Windows API.
|
||||
func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) (err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayCopyData.Call(
|
||||
uintptr(unsafe.Pointer(original)),
|
||||
uintptr(unsafe.Pointer(duplicate))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreate creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreate in Windows API.
|
||||
func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreate.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(dimensions),
|
||||
uintptr(unsafe.Pointer(bounds)))
|
||||
safearray = (*SafeArray)(unsafe.Pointer(&sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreateEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateEx in Windows API.
|
||||
func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreateEx.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(dimensions),
|
||||
uintptr(unsafe.Pointer(bounds)),
|
||||
extra)
|
||||
safearray = (*SafeArray)(unsafe.Pointer(sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreateVector creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVector in Windows API.
|
||||
func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreateVector.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(lowerBound),
|
||||
uintptr(length))
|
||||
safearray = (*SafeArray)(unsafe.Pointer(sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayCreateVectorEx creates SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayCreateVectorEx in Windows API.
|
||||
func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (safearray *SafeArray, err error) {
|
||||
sa, _, err := procSafeArrayCreateVectorEx.Call(
|
||||
uintptr(variantType),
|
||||
uintptr(lowerBound),
|
||||
uintptr(length),
|
||||
extra)
|
||||
safearray = (*SafeArray)(unsafe.Pointer(sa))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayDestroy destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroy in Windows API.
|
||||
func safeArrayDestroy(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayDestroyData destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyData in Windows API.
|
||||
func safeArrayDestroyData(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayDestroyData.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayDestroyDescriptor destroys SafeArray object.
|
||||
//
|
||||
// AKA: SafeArrayDestroyDescriptor in Windows API.
|
||||
func safeArrayDestroyDescriptor(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayDestroyDescriptor.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetDim is the amount of dimensions in the SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetDim in Windows API.
|
||||
func safeArrayGetDim(safearray *SafeArray) (dimensions *uint32, err error) {
|
||||
l, _, err := procSafeArrayGetDim.Call(uintptr(unsafe.Pointer(safearray)))
|
||||
dimensions = (*uint32)(unsafe.Pointer(l))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetElementSize is the element size in bytes.
|
||||
//
|
||||
// AKA: SafeArrayGetElemsize in Windows API.
|
||||
func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) {
|
||||
l, _, err := procSafeArrayGetElemsize.Call(uintptr(unsafe.Pointer(safearray)))
|
||||
length = (*uint32)(unsafe.Pointer(l))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetElement retrieves element at given index.
|
||||
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
|
||||
return convertHresultToError(
|
||||
procSafeArrayGetElement.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&index)),
|
||||
uintptr(pv)))
|
||||
}
|
||||
|
||||
// safeArrayGetElementString retrieves element at given index and converts to string.
|
||||
func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) {
|
||||
var element *int16
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetElement.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&index)),
|
||||
uintptr(unsafe.Pointer(&element))))
|
||||
str = BstrToString(*(**uint16)(unsafe.Pointer(&element)))
|
||||
SysFreeString(element)
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetIID is the InterfaceID of the elements in the SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetIID in Windows API.
|
||||
func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetIID.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&guid))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetLBound returns lower bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetLBound in Windows API.
|
||||
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetLBound.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(dimension),
|
||||
uintptr(unsafe.Pointer(&lowerBound))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetUBound returns upper bounds of SafeArray.
|
||||
//
|
||||
// SafeArrays may have multiple dimensions. Meaning, it could be
|
||||
// multidimensional array.
|
||||
//
|
||||
// AKA: SafeArrayGetUBound in Windows API.
|
||||
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetUBound.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(dimension),
|
||||
uintptr(unsafe.Pointer(&upperBound))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetVartype returns data type of SafeArray.
|
||||
//
|
||||
// AKA: SafeArrayGetVartype in Windows API.
|
||||
func safeArrayGetVartype(safearray *SafeArray) (varType uint16, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetVartype.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&varType))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayLock locks SafeArray for reading to modify SafeArray.
|
||||
//
|
||||
// This must be called during some calls to ensure that another process does not
|
||||
// read or write to the SafeArray during editing.
|
||||
//
|
||||
// AKA: SafeArrayLock in Windows API.
|
||||
func safeArrayLock(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayLock.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayUnlock unlocks SafeArray for reading.
|
||||
//
|
||||
// AKA: SafeArrayUnlock in Windows API.
|
||||
func safeArrayUnlock(safearray *SafeArray) (err error) {
|
||||
err = convertHresultToError(procSafeArrayUnlock.Call(uintptr(unsafe.Pointer(safearray))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayPutElement stores the data element at the specified location in the
|
||||
// array.
|
||||
//
|
||||
// AKA: SafeArrayPutElement in Windows API.
|
||||
func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) (err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayPutElement.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&index)),
|
||||
uintptr(unsafe.Pointer(element))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArrayGetRecordInfo accesses IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArrayGetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArrayGetRecordInfo(safearray *SafeArray) (recordInfo interface{}, err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArrayGetRecordInfo.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&recordInfo))))
|
||||
return
|
||||
}
|
||||
|
||||
// safeArraySetRecordInfo mutates IRecordInfo info for custom types.
|
||||
//
|
||||
// AKA: SafeArraySetRecordInfo in Windows API.
|
||||
//
|
||||
// XXX: Must implement IRecordInfo interface for this to return.
|
||||
func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) (err error) {
|
||||
err = convertHresultToError(
|
||||
procSafeArraySetRecordInfo.Call(
|
||||
uintptr(unsafe.Pointer(safearray)),
|
||||
uintptr(unsafe.Pointer(&recordInfo))))
|
||||
return
|
||||
}
|
||||
140
vendor/github.com/go-ole/go-ole/safearrayconversion.go
generated
vendored
140
vendor/github.com/go-ole/go-ole/safearrayconversion.go
generated
vendored
@@ -1,140 +0,0 @@
|
||||
// Helper for converting SafeArray to array of objects.
|
||||
|
||||
package ole
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SafeArrayConversion struct {
|
||||
Array *SafeArray
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) ToStringArray() (strings []string) {
|
||||
totalElements, _ := sac.TotalElements(0)
|
||||
strings = make([]string, totalElements)
|
||||
|
||||
for i := int32(0); i < totalElements; i++ {
|
||||
strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) {
|
||||
totalElements, _ := sac.TotalElements(0)
|
||||
bytes = make([]byte, totalElements)
|
||||
|
||||
for i := int32(0); i < totalElements; i++ {
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)]))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) {
|
||||
totalElements, _ := sac.TotalElements(0)
|
||||
values = make([]interface{}, totalElements)
|
||||
vt, _ := safeArrayGetVartype(sac.Array)
|
||||
|
||||
for i := int32(0); i < totalElements; i++ {
|
||||
switch VT(vt) {
|
||||
case VT_BOOL:
|
||||
var v bool
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I1:
|
||||
var v int8
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I2:
|
||||
var v int16
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I4:
|
||||
var v int32
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_I8:
|
||||
var v int64
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI1:
|
||||
var v uint8
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI2:
|
||||
var v uint16
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI4:
|
||||
var v uint32
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_UI8:
|
||||
var v uint64
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_R4:
|
||||
var v float32
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_R8:
|
||||
var v float64
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v
|
||||
case VT_BSTR:
|
||||
v , _ := safeArrayGetElementString(sac.Array, i)
|
||||
values[i] = v
|
||||
case VT_VARIANT:
|
||||
var v VARIANT
|
||||
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
|
||||
values[i] = v.Value()
|
||||
v.Clear()
|
||||
default:
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) GetType() (varType uint16, err error) {
|
||||
return safeArrayGetVartype(sac.Array)
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) GetDimensions() (dimensions *uint32, err error) {
|
||||
return safeArrayGetDim(sac.Array)
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) {
|
||||
return safeArrayGetElementSize(sac.Array)
|
||||
}
|
||||
|
||||
func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) {
|
||||
if index < 1 {
|
||||
index = 1
|
||||
}
|
||||
|
||||
// Get array bounds
|
||||
var LowerBounds int32
|
||||
var UpperBounds int32
|
||||
|
||||
LowerBounds, err = safeArrayGetLBound(sac.Array, index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
UpperBounds, err = safeArrayGetUBound(sac.Array, index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
totalElements = UpperBounds - LowerBounds + 1
|
||||
return
|
||||
}
|
||||
|
||||
// Release Safe Array memory
|
||||
func (sac *SafeArrayConversion) Release() {
|
||||
safeArrayDestroy(sac.Array)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user