mirror of
https://github.com/nkanaev/yarr.git
synced 2025-11-05 01:00:39 +00:00
Compare commits
39 Commits
4983e18e23
...
gh-actions
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
348693fa95 | ||
|
|
b40c6fc9e4 | ||
|
|
73fd637b23 | ||
|
|
b8afa82a81 | ||
|
|
097a2da5cb | ||
|
|
e6d32946c1 | ||
|
|
fe4eaa4b8d | ||
|
|
48a671b285 | ||
|
|
011c9c7668 | ||
|
|
f06fc1f750 | ||
|
|
0e88d4284d | ||
|
|
1615c6869f | ||
|
|
800f43b299 | ||
|
|
15bff0a0c4 | ||
|
|
e1481f4aac | ||
|
|
7ef97ee6db | ||
|
|
d785fe4c5a | ||
|
|
5254df53dc | ||
|
|
7301eab99c | ||
|
|
ad138c3017 | ||
|
|
b09c95d7ea | ||
|
|
64611a0dd3 | ||
|
|
321ad7608f | ||
|
|
2a8b6ea935 | ||
|
|
e9cbea500b | ||
|
|
223039b2c6 | ||
|
|
7402dfc4e6 | ||
|
|
6b12715506 | ||
|
|
2dc58c5c8e | ||
|
|
0cef51c6ac | ||
|
|
2a4d974965 | ||
|
|
f71792d6a5 | ||
|
|
b571042c5d | ||
|
|
349c966c63 | ||
|
|
4a42b239cc | ||
|
|
b9b3d2350c | ||
|
|
b13cd85f0b | ||
|
|
daffd721eb | ||
|
|
24232d72e9 |
157
.github/workflows/build.yml
vendored
157
.github/workflows/build.yml
vendored
@@ -1,8 +1,14 @@
|
||||
name: build
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*', 'test*']
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [Test]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
build_macos:
|
||||
@@ -18,7 +24,7 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
- name: Cache Go Modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
@@ -27,7 +33,7 @@ jobs:
|
||||
- name: "Build"
|
||||
run: make build_macos
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: _output/macos/yarr.app
|
||||
@@ -45,7 +51,7 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
- name: Cache Go Modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
@@ -54,7 +60,7 @@ jobs:
|
||||
- name: "Build"
|
||||
run: make build_windows
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: _output/windows/yarr.exe
|
||||
@@ -72,38 +78,99 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
- name: Cache Go Modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
key: ${{ runner.os }}-amd64-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
${{ runner.os }}-go-amd64
|
||||
- name: "Build"
|
||||
run: make build_linux
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux
|
||||
path: _output/linux/yarr
|
||||
|
||||
build_linux-arm:
|
||||
name: Build for Linux ARM
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: >
|
||||
sudo apt-get install -y
|
||||
gcc-arm-linux-gnueabihf
|
||||
libc6-dev-armhf-cross
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: "Setup Go"
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
- name: Cache Go Modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-armv7-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-armv7
|
||||
- name: "Build"
|
||||
env:
|
||||
CC: arm-linux-gnueabihf-gcc
|
||||
GOARCH: arm
|
||||
GOARM: 7
|
||||
run: make build_linux
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux_arm
|
||||
path: _output/linux/yarr
|
||||
|
||||
build_linux-arm64:
|
||||
name: Build for Linux ARM64
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: >
|
||||
sudo apt-get install -y
|
||||
gcc-aarch64-linux-gnu
|
||||
libc6-dev-arm64-cross
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: "Setup Go"
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
- name: Cache Go Modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-arm64-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-arm64
|
||||
- name: "Build"
|
||||
env:
|
||||
CC: aarch64-linux-gnu-gcc
|
||||
GOARCH: arm64
|
||||
run: make build_linux
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux_arm64
|
||||
path: _output/linux/yarr
|
||||
|
||||
create_release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !contains(github.ref, 'test') }}
|
||||
needs: [build_macos, build_windows, build_linux]
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'test') }}
|
||||
needs: [build_macos, build_windows, build_linux, build_linux-arm, build_linux-arm64]
|
||||
steps:
|
||||
- name: Create Release
|
||||
uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4.1.7
|
||||
with:
|
||||
path: .
|
||||
- name: Preparation
|
||||
@@ -111,34 +178,24 @@ jobs:
|
||||
ls -R
|
||||
chmod u+x macos/Contents/MacOS/yarr
|
||||
chmod u+x linux/yarr
|
||||
chmod u+x linux_arm/yarr
|
||||
chmod u+x linux_arm64/yarr
|
||||
|
||||
mv macos yarr.app && zip -r yarr-macos.zip yarr.app
|
||||
mv windows/yarr.exe . && zip yarr-windows.zip yarr.exe
|
||||
mv linux/yarr . && zip yarr-linux.zip yarr
|
||||
- name: Upload MacOS
|
||||
uses: actions/upload-release-asset@v1
|
||||
mv macos yarr.app && zip -r yarr-${GITHUB_REF_NAME}-macos64.zip yarr.app
|
||||
( cd windows && zip ../yarr-${GITHUB_REF_NAME}-windows64.zip yarr.exe )
|
||||
( cd linux && zip ../yarr-${GITHUB_REF_NAME}-linux64.zip yarr )
|
||||
( cd linux_arm && zip ../yarr-${GITHUB_REF_NAME}-linux_arm.zip yarr )
|
||||
( cd linux_arm64 && zip ../yarr-${GITHUB_REF_NAME}-linux_arm64.zip yarr )
|
||||
- name: Upload Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./yarr-macos.zip
|
||||
asset_name: yarr-${{ github.ref }}-macos64.zip
|
||||
asset_content_type: application/zip
|
||||
- name: Upload Windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./yarr-windows.zip
|
||||
asset_name: yarr-${{ github.ref }}-windows64.zip
|
||||
asset_content_type: application/zip
|
||||
- name: Upload Linux
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./yarr-linux.zip
|
||||
asset_name: yarr-${{ github.ref }}-linux64.zip
|
||||
asset_content_type: application/zip
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: |
|
||||
yarr-${{ github.ref_name }}-macos64.zip
|
||||
yarr-${{ github.ref_name }}-windows64.zip
|
||||
yarr-${{ github.ref_name }}-linux64.zip
|
||||
yarr-${{ github.ref_name }}-linux_arm.zip
|
||||
yarr-${{ github.ref_name }}-linux_arm64.zip
|
||||
|
||||
19
.github/workflows/test.yml
vendored
Normal file
19
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Test
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
/_output
|
||||
/yarr
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
*.syso
|
||||
versioninfo.rc
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/nkanaev/yarr/src/platform"
|
||||
"github.com/nkanaev/yarr/src/server"
|
||||
"github.com/nkanaev/yarr/src/storage"
|
||||
"github.com/nkanaev/yarr/src/worker"
|
||||
)
|
||||
|
||||
var Version string = "0.0"
|
||||
@@ -89,12 +90,12 @@ func main() {
|
||||
log.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
configPath, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to get config dir: ", err)
|
||||
}
|
||||
|
||||
if db == "" {
|
||||
configPath, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to get config dir: ", err)
|
||||
}
|
||||
|
||||
storagePath := filepath.Join(configPath, "yarr")
|
||||
if err := os.MkdirAll(storagePath, 0755); err != nil {
|
||||
log.Fatal("Failed to create app config dir: ", err)
|
||||
@@ -105,6 +106,7 @@ func main() {
|
||||
log.Printf("using db file %s", db)
|
||||
|
||||
var username, password string
|
||||
var err error
|
||||
if authfile != "" {
|
||||
f, err := os.Open(authfile)
|
||||
if err != nil {
|
||||
@@ -131,6 +133,7 @@ func main() {
|
||||
log.Fatal("Failed to initialise database: ", err)
|
||||
}
|
||||
|
||||
worker.SetVersion(Version)
|
||||
srv := server.NewServer(store, addr)
|
||||
|
||||
if basepath != "" {
|
||||
|
||||
@@ -2,12 +2,21 @@
|
||||
|
||||
- (new) Fever API support (thanks to @icefed)
|
||||
- (new) editable feed link (thanks to @adaszko)
|
||||
- (new) switch to feed by clicking the title in the article page (thanks to @tarasglek for suggestion)
|
||||
- (new) support multiple media links
|
||||
- (fix) duplicate articles caused by the same feed addition (thanks to @adaszko)
|
||||
- (fix) relative article links (thanks to @adazsko for the report)
|
||||
- (fix) atom article links stored in id element (thanks to @adazsko for the report)
|
||||
- (fix) parsing atom feed titles (thanks to @wnh)
|
||||
- (fix) sorting same-day batch articles (thanks to @lamescholar for the report)
|
||||
- (fix) showing login page in the selected theme (thanks to @feddiriko for the report)
|
||||
- (fix) parsing atom feeds with html elements (thanks to @tillcash & @toBeOfUse for the report, @krkk for the fix)
|
||||
- (fix) parsing feeds with missing guids (thanks to @hoyii for the report)
|
||||
- (fix) sending actual client version to servers (thanks to @aidanholm)
|
||||
- (fix) error caused by missing config dir (thanks to @timster)
|
||||
- (etc) load external images with no-referrer policy (thanks to @tillcash for the report)
|
||||
- (etc) open external links with no-referrer policy (thanks to @donovanglover)
|
||||
- (etc) show article content in the list if title is missing (thanks to @asimpson for suggestion)
|
||||
|
||||
# v2.4 (2023-08-15)
|
||||
|
||||
|
||||
68
doc/samples.yml
Normal file
68
doc/samples.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
- site: https://vimeo.com/channels/staffpicks/videos
|
||||
feed: https://vimeo.com/channels/staffpicks/videos/rss
|
||||
tags: [vimeo, image]
|
||||
|
||||
- site: https://www.youtube.com/@everyframeapainting/videos
|
||||
feed: https://www.youtube.com/feeds/videos.xml?channel_id=UCjFqcJQXGZ6T6sxyFB-5i6A"
|
||||
tags: [youtube, image]
|
||||
|
||||
- site: https://iwdrm.tumblr.com/
|
||||
feed: https://iwdrm.tumblr.com/rss
|
||||
tags: [tumblr, image]
|
||||
|
||||
- site: https://falseknees.tumblr.com/
|
||||
feed: https://falseknees.tumblr.com/rss
|
||||
tags: [tumblr, image]
|
||||
|
||||
- site: https://accidentallyquadratic.tumblr.com/
|
||||
feed: https://accidentallyquadratic.tumblr.com/rss
|
||||
info: text blog with code sections
|
||||
tags: [tumblr, text, code]
|
||||
|
||||
- site: https://www.flickr.com/photos/maratsafin/
|
||||
feed: https://www.flickr.com/services/feeds/photos_public.gne?id=59021497@N07&lang=en-us&format=atom
|
||||
tags: [flickr, image]
|
||||
|
||||
- site: https://www.reddit.com/r/comics
|
||||
feed: https://www.reddit.com/r/comics.rss
|
||||
tags: [reddit, image]
|
||||
|
||||
- site: https://www.reddit.com/r/AITAH
|
||||
feed: https://www.reddit.com/r/AITAH.rss
|
||||
tags: [reddit, text]
|
||||
|
||||
- site: https://idothei.wordpress.com/
|
||||
feed: https://idothei.wordpress.com/feed/
|
||||
tags: [wordpress, text]
|
||||
|
||||
- site: https://www.vidarholen.net/contents/blog/
|
||||
feed: https://www.vidarholen.net/contents/blog/?feed=rss2
|
||||
tags: [wordpress, text]
|
||||
|
||||
- site: https://blog.posthaven.com/
|
||||
feed: https://blog.posthaven.com/posts.atom
|
||||
tags: [posthaven, text]
|
||||
|
||||
- site: https://medium.com/@dailynewsletter
|
||||
feed: https://medium.com/feed/@dailynewsletter
|
||||
tags: [medium, text]
|
||||
|
||||
- site: https://thereveal.substack.com/
|
||||
feed: https://thereveal.substack.com/feed
|
||||
tags: [substack, text]
|
||||
|
||||
- site: https://tema.livejournal.com/
|
||||
feed: https://tema.livejournal.com/data/rss
|
||||
tags: [livejournal, text]
|
||||
|
||||
- site: https://mametter.hatenablog.com/
|
||||
feed: https://mametter.hatenablog.com/feed
|
||||
tags: [hatena, text]
|
||||
|
||||
- site: https://juliepowell.blogspot.com/
|
||||
feed: https://juliepowell.blogspot.com/feeds/posts/default
|
||||
tags: [blogger, text]
|
||||
|
||||
- site: https://micro.blog/val
|
||||
feed: https://micro.blog/posts/val
|
||||
tags: [json, microblog]
|
||||
@@ -2,7 +2,8 @@ FROM golang:alpine3.18 AS build
|
||||
RUN apk add build-base git
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN make build_linux
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/root/go/pkg \
|
||||
make build_linux
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk add --no-cache ca-certificates && \
|
||||
|
||||
8
go.mod
8
go.mod
@@ -1,11 +1,11 @@
|
||||
module github.com/nkanaev/yarr
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/mattn/go-sqlite3 v1.14.7
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/sys v0.28.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.13.0 // indirect
|
||||
require golang.org/x/text v0.21.0 // indirect
|
||||
|
||||
49
go.sum
49
go.sum
@@ -1,45 +1,70 @@
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
19
makefile
19
makefile
@@ -1,33 +1,34 @@
|
||||
VERSION=2.4
|
||||
GITHASH=$(shell git rev-parse --short=8 HEAD)
|
||||
|
||||
CGO_ENABLED=1
|
||||
GO_TAGS = sqlite_foreign_keys sqlite_json
|
||||
GO_LDFLAGS = -s -w -X 'main.Version=$(VERSION)' -X 'main.GitHash=$(GITHASH)'
|
||||
|
||||
GO_LDFLAGS = -s -w
|
||||
GO_LDFLAGS := $(GO_LDFLAGS) -X 'main.Version=$(VERSION)' -X 'main.GitHash=$(GITHASH)'
|
||||
export GOARCH ?= amd64
|
||||
export CGO_ENABLED = 1
|
||||
|
||||
build_default:
|
||||
mkdir -p _output
|
||||
go build -tags "sqlite_foreign_keys" -ldflags="$(GO_LDFLAGS)" -o _output/yarr ./cmd/yarr
|
||||
go build -tags "$(GO_TAGS)" -ldflags="$(GO_LDFLAGS)" -o _output/yarr ./cmd/yarr
|
||||
|
||||
build_macos:
|
||||
mkdir -p _output/macos
|
||||
GOOS=darwin GOARCH=amd64 go build -tags "sqlite_foreign_keys macos" -ldflags="$(GO_LDFLAGS)" -o _output/macos/yarr ./cmd/yarr
|
||||
GOOS=darwin go build -tags "$(GO_TAGS) macos" -ldflags="$(GO_LDFLAGS)" -o _output/macos/yarr ./cmd/yarr
|
||||
cp src/platform/icon.png _output/macos/icon.png
|
||||
go run ./cmd/package_macos -outdir _output/macos -version "$(VERSION)"
|
||||
|
||||
build_linux:
|
||||
mkdir -p _output/linux
|
||||
GOOS=linux GOARCH=amd64 go build -tags "sqlite_foreign_keys linux" -ldflags="$(GO_LDFLAGS)" -o _output/linux/yarr ./cmd/yarr
|
||||
GOOS=linux go build -tags "$(GO_TAGS) linux" -ldflags="$(GO_LDFLAGS)" -o _output/linux/yarr ./cmd/yarr
|
||||
|
||||
build_windows:
|
||||
mkdir -p _output/windows
|
||||
go run ./cmd/generate_versioninfo -version "$(VERSION)" -outfile src/platform/versioninfo.rc
|
||||
windres -i src/platform/versioninfo.rc -O coff -o src/platform/versioninfo.syso
|
||||
GOOS=windows GOARCH=amd64 go build -tags "sqlite_foreign_keys windows" -ldflags="$(GO_LDFLAGS) -H windowsgui" -o _output/windows/yarr.exe ./cmd/yarr
|
||||
GOOS=windows go build -tags "$(GO_TAGS) windows" -ldflags="$(GO_LDFLAGS) -H windowsgui" -o _output/windows/yarr.exe ./cmd/yarr
|
||||
|
||||
serve:
|
||||
go run -tags "sqlite_foreign_keys" ./cmd/yarr -db local.db
|
||||
go run -tags "$(GO_TAGS)" ./cmd/yarr -db local.db
|
||||
|
||||
test:
|
||||
go test -tags "sqlite_foreign_keys" ./...
|
||||
go test -tags "$(GO_TAGS)" ./...
|
||||
|
||||
@@ -25,18 +25,21 @@
|
||||
<div class="flex-grow-1"></div>
|
||||
<button class="toolbar-item"
|
||||
:class="{active: filterSelected == 'unread'}"
|
||||
:aria-pressed="filterSelected == 'unread'"
|
||||
title="Unread"
|
||||
@click="filterSelected = 'unread'">
|
||||
<span class="icon">{% inline "circle-full.svg" %}</span>
|
||||
</button>
|
||||
<button class="toolbar-item"
|
||||
:class="{active: filterSelected == 'starred'}"
|
||||
:aria-pressed="filterSelected == 'starred'"
|
||||
title="Starred"
|
||||
@click="filterSelected = 'starred'">
|
||||
<span class="icon">{% inline "star-full.svg" %}</span>
|
||||
</button>
|
||||
<button class="toolbar-item"
|
||||
:class="{active: filterSelected == ''}"
|
||||
:aria-pressed="filterSelected == ''"
|
||||
title="All"
|
||||
@click="filterSelected = ''">
|
||||
<span class="icon">{% inline "assorted.svg" %}</span>
|
||||
@@ -59,10 +62,12 @@
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<header class="dropdown-header">Theme</header>
|
||||
<header class="dropdown-header" role="heading" aria-level="2">Theme</header>
|
||||
<div class="row text-center m-0">
|
||||
<button class="btn btn-link col-4 px-0 rounded-0"
|
||||
:class="'theme-'+t"
|
||||
:aria-label="t"
|
||||
:aria-pressed="theme.name == t"
|
||||
@click.stop="theme.name = t"
|
||||
v-for="t in ['light', 'sepia', 'night']">
|
||||
<span class="icon" v-if="theme.name == t">{% inline "check.svg" %}</span>
|
||||
@@ -71,25 +76,25 @@
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<header class="dropdown-header">Auto Refresh</header>
|
||||
<header class="dropdown-header" role="heading" aria-level="2">Auto Refresh</header>
|
||||
<div class="row text-center m-0">
|
||||
<button class="dropdown-item col-4 px-0" :class="{active: !refreshRate}" @click.stop="refreshRate = 0">0</button>
|
||||
<button class="dropdown-item col-4 px-0" :class="{active: refreshRate == 10}" @click.stop="refreshRate = 10">10m</button>
|
||||
<button class="dropdown-item col-4 px-0" :class="{active: refreshRate == 30}" @click.stop="refreshRate = 30">30m</button>
|
||||
<button class="dropdown-item col-4 px-0" :class="{active: refreshRate == 60}" @click.stop="refreshRate = 60">1h</button>
|
||||
<button class="dropdown-item col-4 px-0" :class="{active: refreshRate == 120}" @click.stop="refreshRate = 120">2h</button>
|
||||
<button class="dropdown-item col-4 px-0" :class="{active: refreshRate == 240}" @click.stop="refreshRate = 240">4h</button>
|
||||
<button class="dropdown-item col-4 px-0" :aria-pressed="!refreshRate" :class="{active: !refreshRate}" @click.stop="refreshRate = 0">0</button>
|
||||
<button class="dropdown-item col-4 px-0" :aria-pressed="refreshRate == 10" :class="{active: refreshRate == 10}" @click.stop="refreshRate = 10">10m</button>
|
||||
<button class="dropdown-item col-4 px-0" :aria-pressed="refreshRate == 30" :class="{active: refreshRate == 30}" @click.stop="refreshRate = 30">30m</button>
|
||||
<button class="dropdown-item col-4 px-0" :aria-pressed="refreshRate == 60" :class="{active: refreshRate == 60}" @click.stop="refreshRate = 60">1h</button>
|
||||
<button class="dropdown-item col-4 px-0" :aria-pressed="refreshRate == 120" :class="{active: refreshRate == 120}" @click.stop="refreshRate = 120">2h</button>
|
||||
<button class="dropdown-item col-4 px-0" :aria-pressed="refreshRate == 240" :class="{active: refreshRate == 240}" @click.stop="refreshRate = 240">4h</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<header class="dropdown-header">Show first</header>
|
||||
<header class="dropdown-header" role="heading" aria-level="2">Show first</header>
|
||||
<div class="d-flex text-center">
|
||||
<button class="dropdown-item px-0" :class="{active: itemSortNewestFirst}" @click.stop="itemSortNewestFirst=true">New</button>
|
||||
<button class="dropdown-item px-0" :class="{active: !itemSortNewestFirst}" @click.stop="itemSortNewestFirst=false">Old</button>
|
||||
<button class="dropdown-item px-0" :aria-pressed="itemSortNewestFirst" :class="{active: itemSortNewestFirst}" @click.stop="itemSortNewestFirst=true">New</button>
|
||||
<button class="dropdown-item px-0" :aria-pressed="!itemSortNewestFirst" :class="{active: !itemSortNewestFirst}" @click.stop="itemSortNewestFirst=false">Old</button>
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<header class="dropdown-header">Subscriptions</header>
|
||||
<header class="dropdown-header" role="heading" aria-level="2">Subscriptions</header>
|
||||
<form id="opml-import-form" enctype="multipart/form-data" tabindex="-1">
|
||||
<input type="file"
|
||||
id="opml-import"
|
||||
@@ -206,12 +211,12 @@
|
||||
<template v-slot:button>
|
||||
<span class="icon">{% inline "more-horizontal.svg" %}</span>
|
||||
</template>
|
||||
<header class="dropdown-header">{{ current.feed.title }}</header>
|
||||
<a class="dropdown-item" :href="current.feed.link" target="_blank" v-if="current.feed.link">
|
||||
<header class="dropdown-header" role="heading" aria-level="2">{{ current.feed.title }}</header>
|
||||
<a class="dropdown-item" :href="current.feed.link" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer" v-if="current.feed.link">
|
||||
<span class="icon mr-1">{% inline "globe.svg" %}</span>
|
||||
Website
|
||||
</a>
|
||||
<a class="dropdown-item" :href="current.feed.feed_link" target="_blank" v-if="current.feed.feed_link">
|
||||
<a class="dropdown-item" :href="current.feed.feed_link" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer" v-if="current.feed.feed_link">
|
||||
<span class="icon mr-1">{% inline "rss.svg" %}</span>
|
||||
Feed Link
|
||||
</a>
|
||||
@@ -225,7 +230,7 @@
|
||||
Change Link
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<header class="dropdown-header">Move to...</header>
|
||||
<header class="dropdown-header" role="heading" aria-level="2">Move to...</header>
|
||||
<button class="dropdown-item"
|
||||
v-if="folder.id != current.feed.folder_id"
|
||||
v-for="folder in folders"
|
||||
@@ -255,7 +260,7 @@
|
||||
<template v-slot:button>
|
||||
<span class="icon">{% inline "more-horizontal.svg" %}</span>
|
||||
</template>
|
||||
<header class="dropdown-header">{{ current.folder.title }}</header>
|
||||
<header class="dropdown-header" role="heading" aria-level="2">{{ current.folder.title }}</header>
|
||||
<button class="dropdown-item" @click="renameFolder(current.folder)">
|
||||
<span class="icon mr-1">{% inline "edit.svg" %}</span>
|
||||
Rename
|
||||
@@ -326,10 +331,16 @@
|
||||
title="Read Here">
|
||||
<span class="icon" :class="{'icon-loading': loading.readability}">{% inline "book-open.svg" %}</span>
|
||||
</button>
|
||||
<a class="toolbar-item" :href="itemSelectedDetails.link" target="_blank" title="Open Link">
|
||||
<a class="toolbar-item" :href="itemSelectedDetails.link" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer" title="Open Link">
|
||||
<span class="icon">{% inline "external-link.svg" %}</span>
|
||||
</a>
|
||||
<div class="flex-grow-1"></div>
|
||||
<button class="toolbar-item" @click="navigateToItem(-1)" title="Previous Article" :disabled="itemSelected == items[0].id">
|
||||
<span class="icon">{% inline "chevron-left.svg" %}</span>
|
||||
</button>
|
||||
<button class="toolbar-item" @click="navigateToItem(+1)" title="Next Article" :disabled="itemSelected == items[items.length - 1].id">
|
||||
<span class="icon">{% inline "chevron-right.svg" %}</span>
|
||||
</button>
|
||||
<button class="toolbar-item" @click="itemSelected=null" title="Close Article">
|
||||
<span class="icon">{% inline "x.svg" %}</span>
|
||||
</button>
|
||||
@@ -351,8 +362,14 @@
|
||||
</div>
|
||||
<hr>
|
||||
<div v-if="!itemSelectedReadability">
|
||||
<img :src="itemSelectedDetails.image" v-if="itemSelectedDetails.image" class="mb-3">
|
||||
<audio class="w-100" controls v-if="itemSelectedDetails.podcast_url" :src="itemSelectedDetails.podcast_url"></audio>
|
||||
<div v-if="contentImages.length">
|
||||
<figure v-for="media in contentImages">
|
||||
<img :src="media.url" loading="lazy">
|
||||
<figcaption v-if="media.description" v-html="media.description"></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<audio class="w-100" controls v-for="media in contentAudios" :src="media.url"></audio>
|
||||
<video class="w-100" controls v-for="media in contentVideos" :src="media.url"></video>
|
||||
</div>
|
||||
<div v-html="itemSelectedContent"></div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
|
||||
var TITLE = document.title
|
||||
|
||||
function scrollto(target, scroll) {
|
||||
var padding = 10
|
||||
var targetRect = target.getBoundingClientRect()
|
||||
var scrollRect = scroll.getBoundingClientRect()
|
||||
|
||||
// target
|
||||
var relativeOffset = targetRect.y - scrollRect.y
|
||||
var absoluteOffset = relativeOffset + scroll.scrollTop
|
||||
|
||||
if (padding <= relativeOffset && relativeOffset + targetRect.height <= scrollRect.height - padding) return
|
||||
|
||||
var newPos = scroll.scrollTop
|
||||
if (relativeOffset < padding) {
|
||||
newPos = absoluteOffset - padding
|
||||
} else {
|
||||
newPos = absoluteOffset - scrollRect.height + targetRect.height + padding
|
||||
}
|
||||
scroll.scrollTop = Math.round(newPos)
|
||||
}
|
||||
|
||||
var debounce = function(callback, wait) {
|
||||
var timeout
|
||||
return function() {
|
||||
@@ -278,6 +298,18 @@ var vm = new Vue({
|
||||
|
||||
return this.itemSelectedDetails.content || ''
|
||||
},
|
||||
contentImages: function() {
|
||||
if (!this.itemSelectedDetails) return []
|
||||
return (this.itemSelectedDetails.media_links || []).filter(l => l.type === 'image')
|
||||
},
|
||||
contentAudios: function() {
|
||||
if (!this.itemSelectedDetails) return []
|
||||
return (this.itemSelectedDetails.media_links || []).filter(l => l.type === 'audio')
|
||||
},
|
||||
contentVideos: function() {
|
||||
if (!this.itemSelectedDetails) return []
|
||||
return (this.itemSelectedDetails.media_links || []).filter(l => l.type === 'video')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'theme': {
|
||||
@@ -407,7 +439,7 @@ var vm = new Vue({
|
||||
vm.feeds = values[1]
|
||||
})
|
||||
},
|
||||
refreshItems: function(loadMore) {
|
||||
refreshItems: function(loadMore = false) {
|
||||
if (this.feedSelected === null) {
|
||||
vm.items = []
|
||||
vm.itemsHasMore = false
|
||||
@@ -420,7 +452,7 @@ var vm = new Vue({
|
||||
}
|
||||
|
||||
this.loading.items = true
|
||||
api.items.list(query).then(function(data) {
|
||||
return api.items.list(query).then(function(data) {
|
||||
if (loadMore) {
|
||||
vm.items = vm.items.concat(data.list)
|
||||
} else {
|
||||
@@ -443,13 +475,17 @@ var vm = new Vue({
|
||||
var scale = (parseFloat(getComputedStyle(document.documentElement).fontSize) || 16) / 16
|
||||
|
||||
var el = this.$refs.itemlist
|
||||
|
||||
if (el.scrollHeight === 0) return false // element is invisible (responsive design)
|
||||
|
||||
var closeToBottom = (el.scrollHeight - el.scrollTop - el.offsetHeight) < bottomSpace * scale
|
||||
return closeToBottom
|
||||
},
|
||||
loadMoreItems: function(event, el) {
|
||||
if (!this.itemsHasMore) return
|
||||
if (this.loading.items) return
|
||||
if (this.itemListCloseToBottom()) this.refreshItems(true)
|
||||
if (this.itemListCloseToBottom()) return this.refreshItems(true)
|
||||
if (this.itemSelected && this.itemSelected === this.items[this.items.length - 1].id) return this.refreshItems(true)
|
||||
},
|
||||
markItemsRead: function() {
|
||||
var query = this.getItemsQuery()
|
||||
@@ -683,6 +719,65 @@ var vm = new Vue({
|
||||
this.filteredFolderStats = statsFolders
|
||||
this.filteredTotalStats = statsTotal
|
||||
},
|
||||
// navigation helper, navigate relative to selected item
|
||||
navigateToItem: function(relativePosition) {
|
||||
let vm = this
|
||||
if (vm.itemSelected == null) {
|
||||
// if no item is selected, select first
|
||||
if (vm.items.length !== 0) vm.itemSelected = vm.items[0].id
|
||||
return
|
||||
}
|
||||
|
||||
var itemPosition = vm.items.findIndex(function(x) { return x.id === vm.itemSelected })
|
||||
if (itemPosition === -1) {
|
||||
if (vm.items.length !== 0) vm.itemSelected = vm.items[0].id
|
||||
return
|
||||
}
|
||||
|
||||
var newPosition = itemPosition + relativePosition
|
||||
if (newPosition < 0 || newPosition >= vm.items.length) return
|
||||
|
||||
vm.itemSelected = vm.items[newPosition].id
|
||||
|
||||
vm.$nextTick(function() {
|
||||
var scroll = document.querySelector('#item-list-scroll')
|
||||
|
||||
var handle = scroll.querySelector('input[type=radio]:checked')
|
||||
var target = handle && handle.parentElement
|
||||
|
||||
if (target && scroll) scrollto(target, scroll)
|
||||
|
||||
vm.loadMoreItems()
|
||||
})
|
||||
},
|
||||
// navigation helper, navigate relative to selected feed
|
||||
navigateToFeed: function(relativePosition) {
|
||||
let vm = this
|
||||
var navigationList = Array.from(document.querySelectorAll('#col-feed-list input[name=feed]'))
|
||||
.filter(function(r) { return r.offsetParent !== null && r.value !== 'folder:null' })
|
||||
.map(function(r) { return r.value })
|
||||
|
||||
var currentFeedPosition = navigationList.indexOf(vm.feedSelected)
|
||||
|
||||
if (currentFeedPosition == -1) {
|
||||
vm.feedSelected = ''
|
||||
return
|
||||
}
|
||||
|
||||
var newPosition = currentFeedPosition+relativePosition
|
||||
if (newPosition < 0 || newPosition >= navigationList.length) return
|
||||
|
||||
vm.feedSelected = navigationList[newPosition]
|
||||
|
||||
vm.$nextTick(function() {
|
||||
var scroll = document.querySelector('#feed-list-scroll')
|
||||
|
||||
var handle = scroll.querySelector('input[type=radio]:checked')
|
||||
var target = handle && handle.parentElement
|
||||
|
||||
if (target && scroll) scrollto(target, scroll)
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,79 +1,4 @@
|
||||
function scrollto(target, scroll) {
|
||||
var padding = 10
|
||||
var targetRect = target.getBoundingClientRect()
|
||||
var scrollRect = scroll.getBoundingClientRect()
|
||||
|
||||
// target
|
||||
var relativeOffset = targetRect.y - scrollRect.y
|
||||
var absoluteOffset = relativeOffset + scroll.scrollTop
|
||||
|
||||
if (padding <= relativeOffset && relativeOffset + targetRect.height <= scrollRect.height - padding) return
|
||||
|
||||
var newPos = scroll.scrollTop
|
||||
if (relativeOffset < padding) {
|
||||
newPos = absoluteOffset - padding
|
||||
} else {
|
||||
newPos = absoluteOffset - scrollRect.height + targetRect.height + padding
|
||||
}
|
||||
scroll.scrollTop = Math.round(newPos)
|
||||
}
|
||||
|
||||
var helperFunctions = {
|
||||
// navigation helper, navigate relative to selected item
|
||||
navigateToItem: function(relativePosition) {
|
||||
if (vm.itemSelected == null) {
|
||||
// if no item is selected, select first
|
||||
if (vm.items.length !== 0) vm.itemSelected = vm.items[0].id
|
||||
return
|
||||
}
|
||||
|
||||
var itemPosition = vm.items.findIndex(function(x) { return x.id === vm.itemSelected })
|
||||
if (itemPosition === -1) {
|
||||
if (vm.items.length !== 0) vm.itemSelected = vm.items[0].id
|
||||
return
|
||||
}
|
||||
|
||||
var newPosition = itemPosition + relativePosition
|
||||
if (newPosition < 0 || newPosition >= vm.items.length) return
|
||||
|
||||
vm.itemSelected = vm.items[newPosition].id
|
||||
|
||||
vm.$nextTick(function() {
|
||||
var scroll = document.querySelector('#item-list-scroll')
|
||||
|
||||
var handle = scroll.querySelector('input[type=radio]:checked')
|
||||
var target = handle && handle.parentElement
|
||||
|
||||
if (target && scroll) scrollto(target, scroll)
|
||||
})
|
||||
},
|
||||
// navigation helper, navigate relative to selected feed
|
||||
navigateToFeed: function(relativePosition) {
|
||||
var navigationList = Array.from(document.querySelectorAll('#col-feed-list input[name=feed]'))
|
||||
.filter(function(r) { return r.offsetParent !== null && r.value !== 'folder:null' })
|
||||
.map(function(r) { return r.value })
|
||||
|
||||
var currentFeedPosition = navigationList.indexOf(vm.feedSelected)
|
||||
|
||||
if (currentFeedPosition == -1) {
|
||||
vm.feedSelected = ''
|
||||
return
|
||||
}
|
||||
|
||||
var newPosition = currentFeedPosition+relativePosition
|
||||
if (newPosition < 0 || newPosition >= navigationList.length) return
|
||||
|
||||
vm.feedSelected = navigationList[newPosition]
|
||||
|
||||
vm.$nextTick(function() {
|
||||
var scroll = document.querySelector('#feed-list-scroll')
|
||||
|
||||
var handle = scroll.querySelector('input[type=radio]:checked')
|
||||
var target = handle && handle.parentElement
|
||||
|
||||
if (target && scroll) scrollto(target, scroll)
|
||||
})
|
||||
},
|
||||
scrollContent: function(direction) {
|
||||
var padding = 40
|
||||
var scroll = document.querySelector('.content')
|
||||
@@ -92,7 +17,7 @@ var helperFunctions = {
|
||||
var shortcutFunctions = {
|
||||
openItemLink: function() {
|
||||
if (vm.itemSelectedDetails && vm.itemSelectedDetails.link) {
|
||||
window.open(vm.itemSelectedDetails.link, '_blank')
|
||||
window.open(vm.itemSelectedDetails.link, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
},
|
||||
toggleReadability: function() {
|
||||
@@ -118,16 +43,16 @@ var shortcutFunctions = {
|
||||
document.getElementById("searchbar").focus()
|
||||
},
|
||||
nextItem(){
|
||||
helperFunctions.navigateToItem(+1)
|
||||
vm.navigateToItem(+1)
|
||||
},
|
||||
previousItem() {
|
||||
helperFunctions.navigateToItem(-1)
|
||||
vm.navigateToItem(-1)
|
||||
},
|
||||
nextFeed(){
|
||||
helperFunctions.navigateToFeed(+1)
|
||||
vm.navigateToFeed(+1)
|
||||
},
|
||||
previousFeed() {
|
||||
helperFunctions.navigateToFeed(-1)
|
||||
vm.navigateToFeed(-1)
|
||||
},
|
||||
scrollForward: function() {
|
||||
helperFunctions.scrollContent(+1)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
@@ -61,3 +62,16 @@ func ExtractText(content string) string {
|
||||
text = whitespaceRegex.ReplaceAllLiteralString(text, " ")
|
||||
return text
|
||||
}
|
||||
|
||||
func TruncateText(input string, size int) string {
|
||||
runes := []rune(input)
|
||||
if len(runes) <= size {
|
||||
return input
|
||||
}
|
||||
for i := size - 1; i > 0; i-- {
|
||||
if unicode.IsSpace(runes[i]) {
|
||||
return string(runes[:i]) + " ..."
|
||||
}
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
@@ -24,3 +24,21 @@ func TestExtractText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateText(t *testing.T) {
|
||||
input := "Lorem ipsum — классический текст-«рыба»"
|
||||
|
||||
size := 30
|
||||
want := "Lorem ipsum — классический ..."
|
||||
have := TruncateText(input, size)
|
||||
if want != have {
|
||||
t.Errorf("\nsize: %d\nwant: %#v\nhave: %#v", size, want, have)
|
||||
}
|
||||
|
||||
size = 1000
|
||||
want = input
|
||||
have = TruncateText(input, size)
|
||||
if want != have {
|
||||
t.Errorf("\nsize: %d\nwant: %#v\nhave: %#v", size, want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func getExtraAttributes(tagName string) ([]string, []string) {
|
||||
case "iframe":
|
||||
return []string{"sandbox", "loading"}, []string{`sandbox="allow-scripts allow-same-origin allow-popups"`, `loading="lazy"`}
|
||||
case "img":
|
||||
return []string{"loading"}, []string{`loading="lazy"`}
|
||||
return []string{"loading"}, []string{`loading="lazy"`, `referrerpolicy="no-referrer"`}
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ import "testing"
|
||||
|
||||
func TestValidInput(t *testing.T) {
|
||||
input := `<p>This is a <strong>text</strong> with an image: <img src="http://example.org/" alt="Test" loading="lazy">.</p>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `<p>This is a <strong>text</strong> with an image: <img src="http://example.org/" alt="Test" loading="lazy" referrerpolicy="no-referrer">.</p>`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if input != output {
|
||||
t.Errorf(`Wrong output: "%s" != "%s"`, input, output)
|
||||
if have != want {
|
||||
t.Errorf("Wrong output: \nwant: %#v\nhave: %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,31 +28,31 @@ func TestImgWithTextDataURL(t *testing.T) {
|
||||
|
||||
func TestImgWithDataURL(t *testing.T) {
|
||||
input := `<img src="data:image/gif;base64,test" alt="Example">`
|
||||
expected := `<img src="data:image/gif;base64,test" alt="Example" loading="lazy">`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `<img src="data:image/gif;base64,test" alt="Example" loading="lazy" referrerpolicy="no-referrer">`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if output != expected {
|
||||
t.Errorf(`Wrong output: %s`, output)
|
||||
if have != want {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImgWithSrcset(t *testing.T) {
|
||||
input := `<img srcset="example-320w.jpg, example-480w.jpg 1.5x, example-640w.jpg 2x, example-640w.jpg 640w" src="example-640w.jpg" alt="Example">`
|
||||
expected := `<img srcset="http://example.org/example-320w.jpg, http://example.org/example-480w.jpg 1.5x, http://example.org/example-640w.jpg 2x, http://example.org/example-640w.jpg 640w" src="http://example.org/example-640w.jpg" alt="Example" loading="lazy">`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `<img srcset="http://example.org/example-320w.jpg, http://example.org/example-480w.jpg 1.5x, http://example.org/example-640w.jpg 2x, http://example.org/example-640w.jpg 640w" src="http://example.org/example-640w.jpg" alt="Example" loading="lazy" referrerpolicy="no-referrer">`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if output != expected {
|
||||
t.Errorf(`Wrong output: %s`, output)
|
||||
if have != want {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImgWithSrcsetAndDataURL(t *testing.T) {
|
||||
input := `<img srcset="data:image/gif;base64,test" src="http://example.org/example-320w.jpg" alt="Example">`
|
||||
expected := `<img srcset="data:image/gif;base64,test" src="http://example.org/example-320w.jpg" alt="Example" loading="lazy">`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `<img srcset="data:image/gif;base64,test" src="http://example.org/example-320w.jpg" alt="Example" loading="lazy" referrerpolicy="no-referrer">`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if output != expected {
|
||||
t.Errorf(`Wrong output: %s`, output)
|
||||
if have != want {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,16 +68,16 @@ func TestSourceWithSrcsetAndMedia(t *testing.T) {
|
||||
|
||||
func TestMediumImgWithSrcset(t *testing.T) {
|
||||
input := `<img alt="Image for post" class="t u v ef aj" src="https://miro.medium.com/max/5460/1*aJ9JibWDqO81qMfNtqgqrw.jpeg" srcset="https://miro.medium.com/max/552/1*aJ9JibWDqO81qMfNtqgqrw.jpeg 276w, https://miro.medium.com/max/1000/1*aJ9JibWDqO81qMfNtqgqrw.jpeg 500w" sizes="500px" width="2730" height="3407">`
|
||||
expected := `<img alt="Image for post" src="https://miro.medium.com/max/5460/1*aJ9JibWDqO81qMfNtqgqrw.jpeg" srcset="https://miro.medium.com/max/552/1*aJ9JibWDqO81qMfNtqgqrw.jpeg 276w, https://miro.medium.com/max/1000/1*aJ9JibWDqO81qMfNtqgqrw.jpeg 500w" sizes="500px" loading="lazy">`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `<img alt="Image for post" src="https://miro.medium.com/max/5460/1*aJ9JibWDqO81qMfNtqgqrw.jpeg" srcset="https://miro.medium.com/max/552/1*aJ9JibWDqO81qMfNtqgqrw.jpeg 276w, https://miro.medium.com/max/1000/1*aJ9JibWDqO81qMfNtqgqrw.jpeg 500w" sizes="500px" loading="lazy" referrerpolicy="no-referrer">`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if output != expected {
|
||||
t.Errorf(`Wrong output: %s`, output)
|
||||
if have != want {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfClosingTags(t *testing.T) {
|
||||
input := `<p>This <br> is a <strong>text</strong> <br/>with an image: <img src="http://example.org/" alt="Test" loading="lazy"/>.</p>`
|
||||
input := `<p>This <br> is a <strong>text</strong><br/>.</p>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if input != output {
|
||||
@@ -95,11 +96,11 @@ func TestTable(t *testing.T) {
|
||||
|
||||
func TestRelativeURL(t *testing.T) {
|
||||
input := `This <a href="/test.html">link is relative</a> and this image: <img src="../folder/image.png"/>`
|
||||
expected := `This <a href="http://example.org/test.html" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">link is relative</a> and this image: <img src="http://example.org/folder/image.png" loading="lazy"/>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `This <a href="http://example.org/test.html" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">link is relative</a> and this image: <img src="http://example.org/folder/image.png" loading="lazy" referrerpolicy="no-referrer"/>`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Wrong output: "%s" != "%s"`, expected, output)
|
||||
if want != have {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,11 +166,11 @@ func TestInvalidNestedTag(t *testing.T) {
|
||||
|
||||
func TestValidIFrame(t *testing.T) {
|
||||
input := `<iframe src="http://example.org/"></iframe>`
|
||||
expected := `<iframe src="http://example.org/" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
want := `<iframe src="http://example.org/" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
have := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", expected, output)
|
||||
if want != have {
|
||||
t.Errorf("Wrong output:\nwant: %s\nhave: %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package scraper
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nkanaev/yarr/src/content/htmlutil"
|
||||
@@ -35,6 +36,18 @@ func FindFeeds(body string, base string) map[string]string {
|
||||
link := htmlutil.AbsoluteUrl(href, base)
|
||||
if link != "" {
|
||||
candidates[link] = name
|
||||
|
||||
l, err := url.Parse(link)
|
||||
if err == nil && l.Host == "www.youtube.com" && l.Path == "/feeds/videos.xml" {
|
||||
// https://wiki.archiveteam.org/index.php/YouTube/Technical_details#Playlists
|
||||
channelID, found := strings.CutPrefix(l.Query().Get("channel_id"), "UC")
|
||||
if found {
|
||||
const url string = "https://www.youtube.com/feeds/videos.xml?playlist_id="
|
||||
candidates[url+"UULF"+channelID] = name + " - Videos"
|
||||
candidates[url+"UULV"+channelID] = name + " - Live Streams"
|
||||
candidates[url+"UUSH"+channelID] = name + " - Short videos"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package parser
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"html"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
@@ -58,7 +57,7 @@ func (a *atomText) String() string {
|
||||
if a.Type == "xhtml" {
|
||||
data = a.XML
|
||||
}
|
||||
return html.UnescapeString(strings.TrimSpace(data))
|
||||
return strings.TrimSpace(data)
|
||||
}
|
||||
|
||||
func (links atomLinks) First(rel string) string {
|
||||
@@ -90,15 +89,16 @@ func ParseAtom(r io.Reader) (*Feed, error) {
|
||||
guidFromID = srcitem.ID + "::" + srcitem.Updated
|
||||
}
|
||||
|
||||
mediaLinks := srcitem.mediaLinks()
|
||||
|
||||
link := firstNonEmpty(srcitem.OrigLink, srcitem.Links.First("alternate"), srcitem.Links.First(""), linkFromID)
|
||||
dstfeed.Items = append(dstfeed.Items, Item{
|
||||
GUID: firstNonEmpty(guidFromID, srcitem.ID, link),
|
||||
Date: dateParse(firstNonEmpty(srcitem.Published, srcitem.Updated)),
|
||||
URL: link,
|
||||
Title: srcitem.Title.Text(),
|
||||
Content: firstNonEmpty(srcitem.Content.String(), srcitem.Summary.String(), srcitem.firstMediaDescription()),
|
||||
ImageURL: srcitem.firstMediaThumbnail(),
|
||||
AudioURL: "",
|
||||
GUID: firstNonEmpty(guidFromID, srcitem.ID, link),
|
||||
Date: dateParse(firstNonEmpty(srcitem.Published, srcitem.Updated)),
|
||||
URL: link,
|
||||
Title: srcitem.Title.Text(),
|
||||
Content: firstNonEmpty(srcitem.Content.String(), srcitem.Summary.String(), srcitem.firstMediaDescription()),
|
||||
MediaLinks: mediaLinks,
|
||||
})
|
||||
}
|
||||
return dstfeed, nil
|
||||
|
||||
@@ -40,13 +40,11 @@ func TestAtom(t *testing.T) {
|
||||
SiteURL: "http://example.org/",
|
||||
Items: []Item{
|
||||
{
|
||||
GUID: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
|
||||
Date: time.Unix(1071340202, 0).UTC(),
|
||||
URL: "http://example.org/2003/12/13/atom03.html",
|
||||
Title: "Atom-Powered Robots Run Amok",
|
||||
Content: `<div xmlns="http://www.w3.org/1999/xhtml"><p>This is the entry content.</p></div>`,
|
||||
ImageURL: "",
|
||||
AudioURL: "",
|
||||
GUID: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
|
||||
Date: time.Unix(1071340202, 0).UTC(),
|
||||
URL: "http://example.org/2003/12/13/atom03.html",
|
||||
Title: "Atom-Powered Robots Run Amok",
|
||||
Content: `<div xmlns="http://www.w3.org/1999/xhtml"><p>This is the entry content.</p></div>`,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -141,9 +139,15 @@ func TestAtomImageLink(t *testing.T) {
|
||||
</entry>
|
||||
</feed>
|
||||
`))
|
||||
have := feed.Items[0].ImageURL
|
||||
want := `https://example.com/image.png?width=100&height=100`
|
||||
if want != have {
|
||||
if len(feed.Items[0].MediaLinks) != 1 {
|
||||
t.Fatalf("Expected 1 media link, got: %#v", feed.Items[0].MediaLinks)
|
||||
}
|
||||
have := feed.Items[0].MediaLinks[0]
|
||||
want := MediaLink{
|
||||
URL: `https://example.com/image.png?width=100&height=100`,
|
||||
Type: "image",
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Fatalf("item.image_url doesn't match\nwant: %#v\nhave: %#v\n", want, have)
|
||||
}
|
||||
}
|
||||
@@ -165,8 +169,8 @@ func TestAtomImageLinkDuplicated(t *testing.T) {
|
||||
if want != have {
|
||||
t.Fatalf("want: %#v\nhave: %#v\n", want, have)
|
||||
}
|
||||
if feed.Items[0].ImageURL != "" {
|
||||
t.Fatal("item.image_url must be unset if present in the content")
|
||||
if len(feed.Items[0].MediaLinks) != 0 {
|
||||
t.Fatal("item media link must be excluded if present in the content")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,25 +196,41 @@ func TestAtomLinkInID(t *testing.T) {
|
||||
have := feed.Items
|
||||
want := []Item{
|
||||
Item{
|
||||
GUID: "https://example.com/posts/1::2003-12-13T09:17:51",
|
||||
Date: time.Date(2003, time.December, 13, 9, 17, 51, 0, time.UTC),
|
||||
URL: "https://example.com/posts/1",
|
||||
Title: "one updated",
|
||||
},
|
||||
GUID: "https://example.com/posts/1::2003-12-13T09:17:51",
|
||||
Date: time.Date(2003, time.December, 13, 9, 17, 51, 0, time.UTC),
|
||||
URL: "https://example.com/posts/1",
|
||||
Title: "one updated",
|
||||
},
|
||||
Item{
|
||||
GUID: "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6",
|
||||
Date: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), URL: "",
|
||||
Title: "two",
|
||||
},
|
||||
Item{
|
||||
GUID: "https://example.com/posts/1::",
|
||||
Date: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
URL: "https://example.com/posts/1",
|
||||
Title: "one",
|
||||
Content: "",
|
||||
},
|
||||
Date: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), URL: "",
|
||||
Title: "two",
|
||||
},
|
||||
Item{
|
||||
GUID: "https://example.com/posts/1::",
|
||||
Date: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
URL: "https://example.com/posts/1",
|
||||
Title: "one",
|
||||
Content: "",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Fatalf("\nwant: %#v\nhave: %#v\n", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtomDoesntEscapeHTMLTags(t *testing.T) {
|
||||
feed, _ := Parse(strings.NewReader(`
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<entry><summary type="html">&lt;script&gt;alert(1);&lt;/script&gt;</summary></entry>
|
||||
</feed>
|
||||
`))
|
||||
have := feed.Items[0].Content
|
||||
want := "<script>alert(1);</script>"
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Logf("want: %#v", want)
|
||||
t.Logf("have: %#v", have)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -119,6 +120,7 @@ func ParseAndFix(r io.Reader, baseURL, fallbackEncoding string) (*Feed, error) {
|
||||
}
|
||||
feed.TranslateURLs(baseURL)
|
||||
feed.SetMissingDatesTo(time.Now())
|
||||
feed.SetMissingGUIDs()
|
||||
return feed, nil
|
||||
}
|
||||
|
||||
@@ -132,11 +134,14 @@ func (feed *Feed) cleanup() {
|
||||
feed.Items[i].Title = strings.TrimSpace(htmlutil.ExtractText(item.Title))
|
||||
feed.Items[i].Content = strings.TrimSpace(item.Content)
|
||||
|
||||
if item.ImageURL != "" && strings.Contains(item.Content, item.ImageURL) {
|
||||
feed.Items[i].ImageURL = ""
|
||||
}
|
||||
if item.AudioURL != "" && strings.Contains(item.Content, item.AudioURL) {
|
||||
feed.Items[i].AudioURL = ""
|
||||
if len(feed.Items[i].MediaLinks) > 0 {
|
||||
mediaLinks := make([]MediaLink, 0)
|
||||
for _, link := range item.MediaLinks {
|
||||
if !strings.Contains(item.Content, link.URL) {
|
||||
mediaLinks = append(mediaLinks, link)
|
||||
}
|
||||
}
|
||||
feed.Items[i].MediaLinks = mediaLinks
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,3 +173,12 @@ func (feed *Feed) TranslateURLs(base string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (feed *Feed) SetMissingGUIDs() {
|
||||
for i, item := range feed.Items {
|
||||
if item.GUID == "" {
|
||||
id := strings.Join([]string{item.Title, item.Date.Format(time.RFC3339), item.URL}, ";;")
|
||||
feed.Items[i].GUID = fmt.Sprintf("%x", sha256.Sum256([]byte(id)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,3 +150,32 @@ func TestParseCleanIllegalCharsInNonUTF8(t *testing.T) {
|
||||
t.Fatalf("invalid feed, got: %v", feed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMissingGUID(t *testing.T) {
|
||||
data := `
|
||||
<?xml version="1.0" encoding="windows-1251"?>
|
||||
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||||
<channel>
|
||||
<item>
|
||||
<title>foo</title>
|
||||
</item>
|
||||
<item>
|
||||
<title>bar</title>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
`
|
||||
feed, err := ParseAndFix(strings.NewReader(data), "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(feed.Items) != 2 {
|
||||
t.Fatalf("expected 2 items, got %d", len(feed.Items))
|
||||
}
|
||||
if feed.Items[0].GUID == "" || feed.Items[1].GUID == "" {
|
||||
t.Fatalf("item GUIDs are missing, got %#v", feed.Items)
|
||||
}
|
||||
if feed.Items[0].GUID == feed.Items[1].GUID {
|
||||
t.Fatalf("item GUIDs are not unique, got %#v", feed.Items)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type media struct {
|
||||
MediaGroups []mediaGroup `xml:"http://search.yahoo.com/mrss/ group"`
|
||||
MediaContents []mediaContent `xml:"http://search.yahoo.com/mrss/ content"`
|
||||
@@ -8,12 +12,17 @@ type media struct {
|
||||
}
|
||||
|
||||
type mediaGroup struct {
|
||||
MediaContent []mediaContent `xml:"http://search.yahoo.com/mrss/ content"`
|
||||
MediaThumbnails []mediaThumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
|
||||
MediaDescriptions []mediaDescription `xml:"http://search.yahoo.com/mrss/ description"`
|
||||
}
|
||||
|
||||
type mediaContent struct {
|
||||
MediaThumbnails []mediaThumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
|
||||
MediaThumbnails []mediaThumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
|
||||
MediaType string `xml:"type,attr"`
|
||||
MediaMedium string `xml:"medium,attr"`
|
||||
MediaURL string `xml:"url,attr"`
|
||||
MediaDescription mediaDescription `xml:"http://search.yahoo.com/mrss/ description"`
|
||||
}
|
||||
|
||||
type mediaThumbnail struct {
|
||||
@@ -21,8 +30,8 @@ type mediaThumbnail struct {
|
||||
}
|
||||
|
||||
type mediaDescription struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Description string `xml:",chardata"`
|
||||
Type string `xml:"type,attr"`
|
||||
Text string `xml:",chardata"`
|
||||
}
|
||||
|
||||
func (m *media) firstMediaThumbnail() string {
|
||||
@@ -44,12 +53,59 @@ func (m *media) firstMediaThumbnail() string {
|
||||
|
||||
func (m *media) firstMediaDescription() string {
|
||||
for _, d := range m.MediaDescriptions {
|
||||
return plain2html(d.Description)
|
||||
return plain2html(d.Text)
|
||||
}
|
||||
for _, g := range m.MediaGroups {
|
||||
for _, d := range g.MediaDescriptions {
|
||||
return plain2html(d.Description)
|
||||
return plain2html(d.Text)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *media) mediaLinks() []MediaLink {
|
||||
links := make([]MediaLink, 0)
|
||||
for _, thumbnail := range m.MediaThumbnails {
|
||||
links = append(links, MediaLink{URL: thumbnail.URL, Type: "image"})
|
||||
}
|
||||
for _, group := range m.MediaGroups {
|
||||
for _, thumbnail := range group.MediaThumbnails {
|
||||
links = append(links, MediaLink{
|
||||
URL: thumbnail.URL,
|
||||
Type: "image",
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, content := range m.MediaContents {
|
||||
if content.MediaURL != "" {
|
||||
url := content.MediaURL
|
||||
description := content.MediaDescription.Text
|
||||
if strings.HasPrefix(content.MediaType, "image/") {
|
||||
links = append(links, MediaLink{URL: url, Type: "image", Description: description})
|
||||
} else if strings.HasPrefix(content.MediaType, "audio/") {
|
||||
links = append(links, MediaLink{URL: url, Type: "audio", Description: description})
|
||||
} else if strings.HasPrefix(content.MediaType, "video/") {
|
||||
links = append(links, MediaLink{URL: url, Type: "video", Description: description})
|
||||
} else if content.MediaMedium == "image" || content.MediaMedium == "audio" || content.MediaMedium == "video" {
|
||||
links = append(links, MediaLink{URL: url, Type: content.MediaMedium, Description: description})
|
||||
} else {
|
||||
if len(content.MediaThumbnails) > 0 {
|
||||
links = append(links, MediaLink{
|
||||
URL: content.MediaThumbnails[0].URL,
|
||||
Type: "image",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, thumbnail := range content.MediaThumbnails {
|
||||
links = append(links, MediaLink{
|
||||
URL: thumbnail.URL,
|
||||
Type: "image",
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(links) == 0 {
|
||||
return nil
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@ type Item struct {
|
||||
URL string
|
||||
Title string
|
||||
|
||||
Content string
|
||||
ImageURL string
|
||||
AudioURL string
|
||||
Content string
|
||||
MediaLinks []MediaLink
|
||||
}
|
||||
|
||||
type MediaLink struct {
|
||||
URL string
|
||||
Type string
|
||||
Description string
|
||||
}
|
||||
|
||||
@@ -74,14 +74,14 @@ func ParseRSS(r io.Reader) (*Feed, error) {
|
||||
SiteURL: srcfeed.Link,
|
||||
}
|
||||
for _, srcitem := range srcfeed.Items {
|
||||
podcastURL := ""
|
||||
mediaLinks := srcitem.mediaLinks()
|
||||
for _, e := range srcitem.Enclosures {
|
||||
if strings.HasPrefix(e.Type, "audio/") {
|
||||
podcastURL = e.URL
|
||||
|
||||
podcastURL := e.URL
|
||||
if srcitem.OrigEnclosureLink != "" && strings.Contains(podcastURL, path.Base(srcitem.OrigEnclosureLink)) {
|
||||
podcastURL = srcitem.OrigEnclosureLink
|
||||
}
|
||||
mediaLinks = append(mediaLinks, MediaLink{URL: podcastURL, Type: "audio"})
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -92,13 +92,12 @@ func ParseRSS(r io.Reader) (*Feed, error) {
|
||||
}
|
||||
|
||||
dstfeed.Items = append(dstfeed.Items, Item{
|
||||
GUID: firstNonEmpty(srcitem.GUID.GUID, srcitem.Link),
|
||||
Date: dateParse(firstNonEmpty(srcitem.DublinCoreDate, srcitem.PubDate)),
|
||||
URL: firstNonEmpty(srcitem.OrigLink, srcitem.Link, permalink),
|
||||
Title: srcitem.Title,
|
||||
Content: firstNonEmpty(srcitem.ContentEncoded, srcitem.Description),
|
||||
AudioURL: podcastURL,
|
||||
ImageURL: srcitem.firstMediaThumbnail(),
|
||||
GUID: firstNonEmpty(srcitem.GUID.GUID, srcitem.Link),
|
||||
Date: dateParse(firstNonEmpty(srcitem.DublinCoreDate, srcitem.PubDate)),
|
||||
URL: firstNonEmpty(srcitem.OrigLink, srcitem.Link, permalink),
|
||||
Title: srcitem.Title,
|
||||
Content: firstNonEmpty(srcitem.ContentEncoded, srcitem.Description, srcitem.firstMediaDescription()),
|
||||
MediaLinks: mediaLinks,
|
||||
})
|
||||
}
|
||||
return dstfeed, nil
|
||||
|
||||
@@ -75,9 +75,15 @@ func TestRSSMediaContentThumbnail(t *testing.T) {
|
||||
</channel>
|
||||
</rss>
|
||||
`))
|
||||
have := feed.Items[0].ImageURL
|
||||
want := "https://i.vimeocdn.com/video/1092705247_960.jpg"
|
||||
if have != want {
|
||||
if len(feed.Items[0].MediaLinks) != 1 {
|
||||
t.Fatalf("Expected 1 media link, got %#v", feed.Items[0].MediaLinks)
|
||||
}
|
||||
have := feed.Items[0].MediaLinks[0]
|
||||
want := MediaLink{
|
||||
URL: "https://i.vimeocdn.com/video/1092705247_960.jpg",
|
||||
Type: "image",
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Logf("want: %#v", want)
|
||||
t.Logf("have: %#v", have)
|
||||
t.FailNow()
|
||||
@@ -127,9 +133,15 @@ func TestRSSPodcast(t *testing.T) {
|
||||
</channel>
|
||||
</rss>
|
||||
`))
|
||||
have := feed.Items[0].AudioURL
|
||||
want := "http://example.com/audio.ext"
|
||||
if want != have {
|
||||
if len(feed.Items[0].MediaLinks) != 1 {
|
||||
t.Fatal("Invalid media links")
|
||||
}
|
||||
have := feed.Items[0].MediaLinks[0]
|
||||
want := MediaLink{
|
||||
URL: "http://example.com/audio.ext",
|
||||
Type: "audio",
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Logf("want: %#v", want)
|
||||
t.Logf("have: %#v", have)
|
||||
t.FailNow()
|
||||
@@ -147,9 +159,15 @@ func TestRSSOpusPodcast(t *testing.T) {
|
||||
</channel>
|
||||
</rss>
|
||||
`))
|
||||
have := feed.Items[0].AudioURL
|
||||
want := "http://example.com/audio.ext"
|
||||
if want != have {
|
||||
if len(feed.Items[0].MediaLinks) != 1 {
|
||||
t.Fatal("Invalid media links")
|
||||
}
|
||||
have := feed.Items[0].MediaLinks[0]
|
||||
want := MediaLink{
|
||||
URL: "http://example.com/audio.ext",
|
||||
Type: "audio",
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Logf("want: %#v", want)
|
||||
t.Logf("have: %#v", have)
|
||||
t.FailNow()
|
||||
@@ -176,8 +194,9 @@ func TestRSSPodcastDuplicated(t *testing.T) {
|
||||
if want != have {
|
||||
t.Fatalf("content doesn't match\nwant: %#v\nhave: %#v\n", want, have)
|
||||
}
|
||||
if feed.Items[0].AudioURL != "" {
|
||||
t.Fatal("item.audio_url must be unset if present in the content")
|
||||
|
||||
if len(feed.Items[0].MediaLinks) != 0 {
|
||||
t.Fatal("item media must be excluded if present in the content")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,8 +242,47 @@ func TestRSSIsPermalink(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i := 0; i < len(want); i++ {
|
||||
if want[i] != have[i] {
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("Failed to handle isPermalink\nwant: %#v\nhave: %#v\n", want[i], have[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSSMultipleMedia(t *testing.T) {
|
||||
feed, _ := Parse(strings.NewReader(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
|
||||
<channel>
|
||||
<item>
|
||||
<guid isPermaLink="true">http://example.com/posts/1</guid>
|
||||
<media:content url="https://example.com/path/to/image1.png" type="image/png" fileSize="1000" medium="image">
|
||||
<media:description type="plain">description 1</media:description>
|
||||
</media:content>
|
||||
<media:content url="https://example.com/path/to/image2.png" type="image/png" fileSize="2000" medium="image">
|
||||
<media:description type="plain">description 2</media:description>
|
||||
</media:content>
|
||||
<media:content url="https://example.com/path/to/video1.mp4" type="video/mp4" fileSize="2000" medium="image">
|
||||
<media:description type="plain">video description</media:description>
|
||||
</media:content>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
`))
|
||||
have := feed.Items
|
||||
want := []Item{
|
||||
{
|
||||
GUID: "http://example.com/posts/1",
|
||||
URL: "http://example.com/posts/1",
|
||||
MediaLinks: []MediaLink{
|
||||
{URL:"https://example.com/path/to/image1.png", Type:"image", Description:"description 1"},
|
||||
{URL:"https://example.com/path/to/image2.png", Type:"image", Description:"description 2"},
|
||||
{URL:"https://example.com/path/to/video1.mp4", Type:"video", Description:"video description"},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Logf("want: %#v", want)
|
||||
t.Logf("have: %#v", have)
|
||||
t.Fatal("invalid rss")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
func Start(s *server.Server) {
|
||||
systrayOnReady := func() {
|
||||
systray.SetIcon(Icon)
|
||||
systray.SetTooltip("yarr")
|
||||
|
||||
menuOpen := systray.AddMenuItem("Open", "")
|
||||
systray.AddSeparator()
|
||||
|
||||
@@ -51,12 +51,12 @@ func (m *Middleware) Handler(c *router.Context) {
|
||||
c.HTML(http.StatusOK, assets.Template("login.html"), map[string]interface{}{
|
||||
"username": username,
|
||||
"error": "Invalid username/password",
|
||||
"settings": m.DB.GetSettings(),
|
||||
"settings": m.DB.GetSettings(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
c.HTML(http.StatusOK, assets.Template("login.html"), map[string]interface{}{
|
||||
"settings": m.DB.GetSettings(),
|
||||
})
|
||||
"settings": m.DB.GetSettings(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func (s *Server) handler() http.Handler {
|
||||
Username: s.Username,
|
||||
Password: s.Password,
|
||||
Public: []string{"/static", "/fever"},
|
||||
DB: s.db,
|
||||
DB: s.db,
|
||||
}
|
||||
r.Use(a.Handler)
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func (s *Server) handleManifest(c *router.Context) {
|
||||
"short_name": "yarr",
|
||||
"description": "yet another rss reader",
|
||||
"display": "standalone",
|
||||
"start_url": s.BasePath,
|
||||
"start_url": "/" + strings.TrimPrefix(s.BasePath, "/"),
|
||||
"icons": []map[string]interface{}{
|
||||
{
|
||||
"src": s.BasePath + "/static/graphicarts/favicon.png",
|
||||
@@ -329,6 +329,9 @@ func (s *Server) handleItem(c *router.Context) {
|
||||
}
|
||||
|
||||
item.Content = sanitizer.Sanitize(item.Link, item.Content)
|
||||
for i, link := range item.MediaLinks {
|
||||
item.MediaLinks[i].Description = sanitizer.Sanitize(item.Link, link.Description)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, item)
|
||||
} else if c.Req.Method == "PUT" {
|
||||
@@ -371,12 +374,19 @@ func (s *Server) handleItemList(c *router.Context) {
|
||||
}
|
||||
newestFirst := query.Get("oldest_first") != "true"
|
||||
|
||||
items := s.db.ListItems(filter, perPage+1, newestFirst, false)
|
||||
items := s.db.ListItems(filter, perPage+1, newestFirst, true)
|
||||
hasMore := false
|
||||
if len(items) == perPage+1 {
|
||||
hasMore = true
|
||||
items = items[:perPage]
|
||||
}
|
||||
|
||||
for i, item := range items {
|
||||
if item.Title == "" {
|
||||
text := htmlutil.ExtractText(item.Content)
|
||||
items[i].Title = htmlutil.TruncateText(text, 140)
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"list": items,
|
||||
"has_more": hasMore,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -44,17 +45,35 @@ func (s *ItemStatus) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type MediaLink struct {
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
type MediaLinks []MediaLink
|
||||
|
||||
func (m *MediaLinks) Scan(src any) error {
|
||||
if data, ok := src.([]byte); ok {
|
||||
return json.Unmarshal(data, m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MediaLinks) Value() (driver.Value, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Id int64 `json:"id"`
|
||||
GUID string `json:"guid"`
|
||||
FeedId int64 `json:"feed_id"`
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"`
|
||||
Content string `json:"content,omitempty"`
|
||||
Date time.Time `json:"date"`
|
||||
Status ItemStatus `json:"status"`
|
||||
ImageURL *string `json:"image"`
|
||||
AudioURL *string `json:"podcast_url"`
|
||||
Id int64 `json:"id"`
|
||||
GUID string `json:"guid"`
|
||||
FeedId int64 `json:"feed_id"`
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"`
|
||||
Content string `json:"content,omitempty"`
|
||||
Date time.Time `json:"date"`
|
||||
Status ItemStatus `json:"status"`
|
||||
MediaLinks MediaLinks `json:"media_links"`
|
||||
}
|
||||
|
||||
type ItemFilter struct {
|
||||
@@ -79,22 +98,21 @@ type MarkFilter struct {
|
||||
type ItemList []Item
|
||||
|
||||
func (list ItemList) Len() int {
|
||||
return len(list)
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (list ItemList) SortKey(i int) string {
|
||||
return list[i].Date.Format(time.RFC3339) + "::" + list[i].GUID
|
||||
return list[i].Date.Format(time.RFC3339) + "::" + list[i].GUID
|
||||
}
|
||||
|
||||
func (list ItemList) Less(i, j int) bool {
|
||||
return list.SortKey(i) < list.SortKey(j)
|
||||
return list.SortKey(i) < list.SortKey(j)
|
||||
}
|
||||
|
||||
func (list ItemList) Swap(i, j int) {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
|
||||
func (s *Storage) CreateItems(items []Item) bool {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
@@ -104,20 +122,24 @@ func (s *Storage) CreateItems(items []Item) bool {
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
itemsSorted := ItemList(items)
|
||||
sort.Sort(itemsSorted)
|
||||
itemsSorted := ItemList(items)
|
||||
sort.Sort(itemsSorted)
|
||||
|
||||
for _, item := range itemsSorted {
|
||||
_, err = tx.Exec(`
|
||||
insert into items (
|
||||
guid, feed_id, title, link, date,
|
||||
content, image, podcast_url,
|
||||
content, media_links,
|
||||
date_arrived, status
|
||||
)
|
||||
values (?, ?, ?, ?, strftime('%Y-%m-%d %H:%M:%f', ?), ?, ?, ?, ?, ?)
|
||||
values (
|
||||
?, ?, ?, ?, strftime('%Y-%m-%d %H:%M:%f', ?),
|
||||
?, ?,
|
||||
?, ?
|
||||
)
|
||||
on conflict (feed_id, guid) do nothing`,
|
||||
item.GUID, item.FeedId, item.Title, item.Link, item.Date,
|
||||
item.Content, item.ImageURL, item.AudioURL,
|
||||
item.Content, item.MediaLinks,
|
||||
now, UNREAD,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -232,7 +254,7 @@ func (s *Storage) ListItems(filter ItemFilter, limit int, newestFirst bool, with
|
||||
order = "i.id desc"
|
||||
}
|
||||
|
||||
selectCols := "i.id, i.guid, i.feed_id, i.title, i.link, i.date, i.status, i.image, i.podcast_url"
|
||||
selectCols := "i.id, i.guid, i.feed_id, i.title, i.link, i.date, i.status, i.media_links"
|
||||
if withContent {
|
||||
selectCols += ", i.content"
|
||||
} else {
|
||||
@@ -255,7 +277,7 @@ func (s *Storage) ListItems(filter ItemFilter, limit int, newestFirst bool, with
|
||||
err = rows.Scan(
|
||||
&x.Id, &x.GUID, &x.FeedId,
|
||||
&x.Title, &x.Link, &x.Date,
|
||||
&x.Status, &x.ImageURL, &x.AudioURL, &x.Content,
|
||||
&x.Status, &x.MediaLinks, &x.Content,
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -271,12 +293,12 @@ func (s *Storage) GetItem(id int64) *Item {
|
||||
err := s.db.QueryRow(`
|
||||
select
|
||||
i.id, i.guid, i.feed_id, i.title, i.link, i.content,
|
||||
i.date, i.status, i.image, i.podcast_url
|
||||
i.date, i.status, i.media_links
|
||||
from items i
|
||||
where i.id = ?
|
||||
`, id).Scan(
|
||||
&i.Id, &i.GUID, &i.FeedId, &i.Title, &i.Link, &i.Content,
|
||||
&i.Date, &i.Status, &i.ImageURL, &i.AudioURL,
|
||||
&i.Date, &i.Status, &i.MediaLinks,
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
|
||||
@@ -77,12 +77,12 @@ func getItem(db *Storage, guid string) *Item {
|
||||
err := db.db.QueryRow(`
|
||||
select
|
||||
i.id, i.guid, i.feed_id, i.title, i.link, i.content,
|
||||
i.date, i.status, i.image, i.podcast_url
|
||||
i.date, i.status, i.media_links
|
||||
from items i
|
||||
where i.guid = ?
|
||||
`, guid).Scan(
|
||||
&i.Id, &i.GUID, &i.FeedId, &i.Title, &i.Link, &i.Content,
|
||||
&i.Date, &i.Status, &i.ImageURL, &i.AudioURL,
|
||||
&i.Date, &i.Status, &i.MediaLinks,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -16,13 +16,17 @@ var migrations = []func(*sql.Tx) error{
|
||||
m06_fill_missing_dates,
|
||||
m07_add_feed_size,
|
||||
m08_normalize_datetime,
|
||||
m09_change_item_index,
|
||||
m10_add_item_medialinks,
|
||||
}
|
||||
|
||||
var maxVersion = int64(len(migrations))
|
||||
|
||||
func migrate(db *sql.DB) error {
|
||||
var version int64
|
||||
db.QueryRow("pragma user_version").Scan(&version)
|
||||
if err := db.QueryRow("pragma user_version").Scan(&version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version >= maxVersion {
|
||||
return nil
|
||||
@@ -294,3 +298,37 @@ func m08_normalize_datetime(tx *sql.Tx) error {
|
||||
_, err = tx.Exec(`update items set date = strftime('%Y-%m-%d %H:%M:%f', date);`)
|
||||
return err
|
||||
}
|
||||
|
||||
func m09_change_item_index(tx *sql.Tx) error {
|
||||
sql := `
|
||||
drop index if exists idx_item_status;
|
||||
create index if not exists idx_item__date_id_status on items(date,id,status);
|
||||
`
|
||||
_, err := tx.Exec(sql)
|
||||
return err
|
||||
}
|
||||
|
||||
func m10_add_item_medialinks(tx *sql.Tx) error {
|
||||
sql := `
|
||||
alter table items add column media_links blob;
|
||||
update items set media_links =
|
||||
iif(
|
||||
coalesce(image, '') != '' and coalesce(podcast_url, '') != '',
|
||||
json_array(json_object('type', 'image', 'url', image), json_object('type', 'audio', 'url', podcast_url)),
|
||||
iif(
|
||||
coalesce(image, '') != '',
|
||||
json_array(json_object('type', 'image', 'url', image)),
|
||||
iif(
|
||||
coalesce(podcast_url, '') != '',
|
||||
json_array(json_object('type', 'audio', 'url', podcast_url)),
|
||||
null
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
alter table items drop column image;
|
||||
alter table items drop column podcast_url;
|
||||
`
|
||||
_, err := tx.Exec(sql)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package storage
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
@@ -10,14 +13,17 @@ type Storage struct {
|
||||
}
|
||||
|
||||
func New(path string) (*Storage, error) {
|
||||
if pos := strings.IndexRune(path, '?'); pos == -1 {
|
||||
params := "_journal=WAL&_sync=NORMAL&_busy_timeout=5000&cache=shared"
|
||||
log.Printf("opening db with params: %s", params)
|
||||
path = path + "?" + params
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlite3", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: https://foxcpp.dev/articles/the-right-way-to-use-go-sqlite3
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
if err = migrate(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build darwin || windows
|
||||
// +build darwin windows
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build never
|
||||
// +build never
|
||||
|
||||
package systray
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package systray
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package systray
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package systray
|
||||
|
||||
@@ -32,6 +32,10 @@ func (c *Client) getConditional(url, lastModified, etag string) (*http.Response,
|
||||
|
||||
var client *Client
|
||||
|
||||
func SetVersion(num string) {
|
||||
client.userAgent = "Yarr/" + num
|
||||
}
|
||||
|
||||
func init() {
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
|
||||
@@ -143,24 +143,19 @@ func ConvertItems(items []parser.Item, feed storage.Feed) []storage.Item {
|
||||
result := make([]storage.Item, len(items))
|
||||
for i, item := range items {
|
||||
item := item
|
||||
var audioURL *string = nil
|
||||
if item.AudioURL != "" {
|
||||
audioURL = &item.AudioURL
|
||||
}
|
||||
var imageURL *string = nil
|
||||
if item.ImageURL != "" {
|
||||
imageURL = &item.ImageURL
|
||||
mediaLinks := make(storage.MediaLinks, 0)
|
||||
for _, link := range item.MediaLinks {
|
||||
mediaLinks = append(mediaLinks, storage.MediaLink(link))
|
||||
}
|
||||
result[i] = storage.Item{
|
||||
GUID: item.GUID,
|
||||
FeedId: feed.Id,
|
||||
Title: item.Title,
|
||||
Link: item.URL,
|
||||
Content: item.Content,
|
||||
Date: item.Date,
|
||||
Status: storage.UNREAD,
|
||||
ImageURL: imageURL,
|
||||
AudioURL: audioURL,
|
||||
GUID: item.GUID,
|
||||
FeedId: feed.Id,
|
||||
Title: item.Title,
|
||||
Link: item.URL,
|
||||
Content: item.Content,
|
||||
Date: item.Date,
|
||||
Status: storage.UNREAD,
|
||||
MediaLinks: mediaLinks,
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
4
vendor/golang.org/x/net/LICENSE
generated
vendored
4
vendor/golang.org/x/net/LICENSE
generated
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
9
vendor/golang.org/x/net/html/doc.go
generated
vendored
9
vendor/golang.org/x/net/html/doc.go
generated
vendored
@@ -78,16 +78,11 @@ example, to process each anchor node in depth-first order:
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
var f func(*html.Node)
|
||||
f = func(n *html.Node) {
|
||||
for n := range doc.Descendants() {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
// Do something with n...
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c)
|
||||
}
|
||||
}
|
||||
f(doc)
|
||||
|
||||
The relevant specifications include:
|
||||
https://html.spec.whatwg.org/multipage/syntax.html and
|
||||
@@ -104,7 +99,7 @@ tokenization, and tokenization and tree construction stages of the WHATWG HTML
|
||||
parsing specification respectively. While the tokenizer parses and normalizes
|
||||
individual HTML tokens, only the parser constructs the DOM tree from the
|
||||
tokenized HTML, as described in the tree construction stage of the
|
||||
specification, dynamically modifying or extending the docuemnt's DOM tree.
|
||||
specification, dynamically modifying or extending the document's DOM tree.
|
||||
|
||||
If your use case requires semantically well-formed HTML documents, as defined by
|
||||
the WHATWG specification, the parser should be used rather than the tokenizer.
|
||||
|
||||
2
vendor/golang.org/x/net/html/doctype.go
generated
vendored
2
vendor/golang.org/x/net/html/doctype.go
generated
vendored
@@ -87,7 +87,7 @@ func parseDoctype(s string) (n *Node, quirks bool) {
|
||||
}
|
||||
}
|
||||
if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" &&
|
||||
strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" {
|
||||
strings.EqualFold(lastAttr.Val, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
|
||||
quirks = true
|
||||
}
|
||||
}
|
||||
|
||||
3
vendor/golang.org/x/net/html/foreign.go
generated
vendored
3
vendor/golang.org/x/net/html/foreign.go
generated
vendored
@@ -40,8 +40,7 @@ func htmlIntegrationPoint(n *Node) bool {
|
||||
if n.Data == "annotation-xml" {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == "encoding" {
|
||||
val := strings.ToLower(a.Val)
|
||||
if val == "text/html" || val == "application/xhtml+xml" {
|
||||
if strings.EqualFold(a.Val, "text/html") || strings.EqualFold(a.Val, "application/xhtml+xml") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
56
vendor/golang.org/x/net/html/iter.go
generated
vendored
Normal file
56
vendor/golang.org/x/net/html/iter.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.23
|
||||
|
||||
package html
|
||||
|
||||
import "iter"
|
||||
|
||||
// Ancestors returns an iterator over the ancestors of n, starting with n.Parent.
|
||||
//
|
||||
// Mutating a Node or its parents while iterating may have unexpected results.
|
||||
func (n *Node) Ancestors() iter.Seq[*Node] {
|
||||
_ = n.Parent // eager nil check
|
||||
|
||||
return func(yield func(*Node) bool) {
|
||||
for p := n.Parent; p != nil && yield(p); p = p.Parent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ChildNodes returns an iterator over the immediate children of n,
|
||||
// starting with n.FirstChild.
|
||||
//
|
||||
// Mutating a Node or its children while iterating may have unexpected results.
|
||||
func (n *Node) ChildNodes() iter.Seq[*Node] {
|
||||
_ = n.FirstChild // eager nil check
|
||||
|
||||
return func(yield func(*Node) bool) {
|
||||
for c := n.FirstChild; c != nil && yield(c); c = c.NextSibling {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Descendants returns an iterator over all nodes recursively beneath
|
||||
// n, excluding n itself. Nodes are visited in depth-first preorder.
|
||||
//
|
||||
// Mutating a Node or its descendants while iterating may have unexpected results.
|
||||
func (n *Node) Descendants() iter.Seq[*Node] {
|
||||
_ = n.FirstChild // eager nil check
|
||||
|
||||
return func(yield func(*Node) bool) {
|
||||
n.descendants(yield)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) descendants(yield func(*Node) bool) bool {
|
||||
for c := range n.ChildNodes() {
|
||||
if !yield(c) || !c.descendants(yield) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
4
vendor/golang.org/x/net/html/node.go
generated
vendored
4
vendor/golang.org/x/net/html/node.go
generated
vendored
@@ -38,6 +38,10 @@ var scopeMarker = Node{Type: scopeMarkerNode}
|
||||
// that it looks like "a<b" rather than "a<b". For element nodes, DataAtom
|
||||
// is the atom for Data, or zero if Data is not a known tag name.
|
||||
//
|
||||
// Node trees may be navigated using the link fields (Parent,
|
||||
// FirstChild, and so on) or a range loop over iterators such as
|
||||
// [Node.Descendants].
|
||||
//
|
||||
// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
|
||||
// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
|
||||
// "svg" is short for "http://www.w3.org/2000/svg".
|
||||
|
||||
8
vendor/golang.org/x/net/html/parse.go
generated
vendored
8
vendor/golang.org/x/net/html/parse.go
generated
vendored
@@ -840,6 +840,10 @@ func afterHeadIM(p *parser) bool {
|
||||
|
||||
p.parseImpliedToken(StartTagToken, a.Body, a.Body.String())
|
||||
p.framesetOK = true
|
||||
if p.tok.Type == ErrorToken {
|
||||
// Stop parsing.
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1031,7 +1035,7 @@ func inBodyIM(p *parser) bool {
|
||||
if p.tok.DataAtom == a.Input {
|
||||
for _, t := range p.tok.Attr {
|
||||
if t.Key == "type" {
|
||||
if strings.ToLower(t.Val) == "hidden" {
|
||||
if strings.EqualFold(t.Val, "hidden") {
|
||||
// Skip setting framesetOK = false
|
||||
return true
|
||||
}
|
||||
@@ -1459,7 +1463,7 @@ func inTableIM(p *parser) bool {
|
||||
return inHeadIM(p)
|
||||
case a.Input:
|
||||
for _, t := range p.tok.Attr {
|
||||
if t.Key == "type" && strings.ToLower(t.Val) == "hidden" {
|
||||
if t.Key == "type" && strings.EqualFold(t.Val, "hidden") {
|
||||
p.addElement()
|
||||
p.oe.pop()
|
||||
return true
|
||||
|
||||
12
vendor/golang.org/x/net/html/token.go
generated
vendored
12
vendor/golang.org/x/net/html/token.go
generated
vendored
@@ -910,9 +910,6 @@ func (z *Tokenizer) readTagAttrKey() {
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t', '\f', '/':
|
||||
z.pendingAttr[0].end = z.raw.end - 1
|
||||
return
|
||||
case '=':
|
||||
if z.pendingAttr[0].start+1 == z.raw.end {
|
||||
// WHATWG 13.2.5.32, if we see an equals sign before the attribute name
|
||||
@@ -920,7 +917,9 @@ func (z *Tokenizer) readTagAttrKey() {
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
case '>':
|
||||
case ' ', '\n', '\r', '\t', '\f', '/', '>':
|
||||
// WHATWG 13.2.5.33 Attribute name state
|
||||
// We need to reconsume the char in the after attribute name state to support the / character
|
||||
z.raw.end--
|
||||
z.pendingAttr[0].end = z.raw.end
|
||||
return
|
||||
@@ -939,6 +938,11 @@ func (z *Tokenizer) readTagAttrVal() {
|
||||
if z.err != nil {
|
||||
return
|
||||
}
|
||||
if c == '/' {
|
||||
// WHATWG 13.2.5.34 After attribute name state
|
||||
// U+002F SOLIDUS (/) - Switch to the self-closing start tag state.
|
||||
return
|
||||
}
|
||||
if c != '=' {
|
||||
z.raw.end--
|
||||
return
|
||||
|
||||
4
vendor/golang.org/x/sys/LICENSE
generated
vendored
4
vendor/golang.org/x/sys/LICENSE
generated
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
3
vendor/golang.org/x/sys/windows/aliases.go
generated
vendored
3
vendor/golang.org/x/sys/windows/aliases.go
generated
vendored
@@ -2,8 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && go1.9
|
||||
// +build windows,go1.9
|
||||
//go:build windows
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
2
vendor/golang.org/x/sys/windows/dll_windows.go
generated
vendored
2
vendor/golang.org/x/sys/windows/dll_windows.go
generated
vendored
@@ -65,7 +65,7 @@ func LoadDLL(name string) (dll *DLL, err error) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// MustLoadDLL is like LoadDLL but panics if load operation failes.
|
||||
// MustLoadDLL is like LoadDLL but panics if load operation fails.
|
||||
func MustLoadDLL(name string) *DLL {
|
||||
d, e := LoadDLL(name)
|
||||
if e != nil {
|
||||
|
||||
9
vendor/golang.org/x/sys/windows/empty.s
generated
vendored
9
vendor/golang.org/x/sys/windows/empty.s
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.12
|
||||
// +build !go1.12
|
||||
|
||||
// This file is here to allow bodyless functions with go:linkname for Go 1.11
|
||||
// and earlier (see https://golang.org/issue/23311).
|
||||
17
vendor/golang.org/x/sys/windows/env_windows.go
generated
vendored
17
vendor/golang.org/x/sys/windows/env_windows.go
generated
vendored
@@ -37,14 +37,17 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) {
|
||||
return nil, err
|
||||
}
|
||||
defer DestroyEnvironmentBlock(block)
|
||||
blockp := unsafe.Pointer(block)
|
||||
for {
|
||||
entry := UTF16PtrToString((*uint16)(blockp))
|
||||
if len(entry) == 0 {
|
||||
break
|
||||
size := unsafe.Sizeof(*block)
|
||||
for *block != 0 {
|
||||
// find NUL terminator
|
||||
end := unsafe.Pointer(block)
|
||||
for *(*uint16)(end) != 0 {
|
||||
end = unsafe.Add(end, size)
|
||||
}
|
||||
env = append(env, entry)
|
||||
blockp = unsafe.Add(blockp, 2*(len(entry)+1))
|
||||
|
||||
entry := unsafe.Slice(block, (uintptr(end)-uintptr(unsafe.Pointer(block)))/size)
|
||||
env = append(env, UTF16ToString(entry))
|
||||
block = (*uint16)(unsafe.Add(end, size))
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/eventlog.go
generated
vendored
1
vendor/golang.org/x/sys/windows/eventlog.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/mksyscall.go
generated
vendored
1
vendor/golang.org/x/sys/windows/mksyscall.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build generate
|
||||
// +build generate
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/race.go
generated
vendored
1
vendor/golang.org/x/sys/windows/race.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && race
|
||||
// +build windows,race
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/race0.go
generated
vendored
1
vendor/golang.org/x/sys/windows/race0.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && !race
|
||||
// +build windows,!race
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
25
vendor/golang.org/x/sys/windows/security_windows.go
generated
vendored
25
vendor/golang.org/x/sys/windows/security_windows.go
generated
vendored
@@ -68,6 +68,7 @@ type UserInfo10 struct {
|
||||
//sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo
|
||||
//sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation
|
||||
//sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree
|
||||
//sys NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) = netapi32.NetUserEnum
|
||||
|
||||
const (
|
||||
// do not reorder
|
||||
@@ -893,7 +894,7 @@ type ACL struct {
|
||||
aclRevision byte
|
||||
sbz1 byte
|
||||
aclSize uint16
|
||||
aceCount uint16
|
||||
AceCount uint16
|
||||
sbz2 uint16
|
||||
}
|
||||
|
||||
@@ -1086,6 +1087,27 @@ type EXPLICIT_ACCESS struct {
|
||||
Trustee TRUSTEE
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
|
||||
type ACE_HEADER struct {
|
||||
AceType uint8
|
||||
AceFlags uint8
|
||||
AceSize uint16
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace
|
||||
type ACCESS_ALLOWED_ACE struct {
|
||||
Header ACE_HEADER
|
||||
Mask ACCESS_MASK
|
||||
SidStart uint32
|
||||
}
|
||||
|
||||
const (
|
||||
// Constants for AceType
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
|
||||
ACCESS_ALLOWED_ACE_TYPE = 0
|
||||
ACCESS_DENIED_ACE_TYPE = 1
|
||||
)
|
||||
|
||||
// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
|
||||
type TrusteeValue uintptr
|
||||
|
||||
@@ -1157,6 +1179,7 @@ type OBJECTS_AND_NAME struct {
|
||||
//sys makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD
|
||||
|
||||
//sys setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW
|
||||
//sys GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (err error) = advapi32.GetAce
|
||||
|
||||
// Control returns the security descriptor control bits.
|
||||
func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) {
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/service.go
generated
vendored
1
vendor/golang.org/x/sys/windows/service.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/str.go
generated
vendored
1
vendor/golang.org/x/sys/windows/str.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
|
||||
1
vendor/golang.org/x/sys/windows/syscall.go
generated
vendored
1
vendor/golang.org/x/sys/windows/syscall.go
generated
vendored
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
// Package windows contains an interface to the low-level operating system
|
||||
// primitives. OS details vary depending on the underlying system, and
|
||||
|
||||
144
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
144
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
@@ -17,8 +17,10 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Handle uintptr
|
||||
type HWND uintptr
|
||||
type (
|
||||
Handle uintptr
|
||||
HWND uintptr
|
||||
)
|
||||
|
||||
const (
|
||||
InvalidHandle = ^Handle(0)
|
||||
@@ -125,8 +127,7 @@ func UTF16PtrToString(p *uint16) string {
|
||||
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
|
||||
}
|
||||
|
||||
return string(utf16.Decode(unsafe.Slice(p, n)))
|
||||
return UTF16ToString(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
func Getpagesize() int { return 4096 }
|
||||
@@ -155,6 +156,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
|
||||
//sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
|
||||
//sys SetDefaultDllDirectories(directoryFlags uint32) (err error)
|
||||
//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory
|
||||
//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory
|
||||
//sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW
|
||||
//sys GetVersion() (ver uint32, err error)
|
||||
//sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
|
||||
@@ -164,6 +167,9 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW
|
||||
//sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) [failretval==InvalidHandle] = CreateNamedPipeW
|
||||
//sys ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error)
|
||||
//sys DisconnectNamedPipe(pipe Handle) (err error)
|
||||
//sys GetNamedPipeClientProcessId(pipe Handle, clientProcessID *uint32) (err error)
|
||||
//sys GetNamedPipeServerProcessId(pipe Handle, serverProcessID *uint32) (err error)
|
||||
//sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error)
|
||||
//sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||
//sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState
|
||||
@@ -192,6 +198,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW
|
||||
//sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
|
||||
//sys SetEndOfFile(handle Handle) (err error)
|
||||
//sys SetFileValidData(handle Handle, validDataLength int64) (err error)
|
||||
//sys GetSystemTimeAsFileTime(time *Filetime)
|
||||
//sys GetSystemTimePreciseAsFileTime(time *Filetime)
|
||||
//sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) [failretval==0xffffffff]
|
||||
@@ -208,6 +215,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error)
|
||||
//sys ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) [failretval<=32] = shell32.ShellExecuteW
|
||||
//sys GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) = user32.GetWindowThreadProcessId
|
||||
//sys LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) [failretval==0] = user32.LoadKeyboardLayoutW
|
||||
//sys UnloadKeyboardLayout(hkl Handle) (err error) = user32.UnloadKeyboardLayout
|
||||
//sys GetKeyboardLayout(tid uint32) (hkl Handle) = user32.GetKeyboardLayout
|
||||
//sys ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32) = user32.ToUnicodeEx
|
||||
//sys GetShellWindow() (shellWindow HWND) = user32.GetShellWindow
|
||||
//sys MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW
|
||||
//sys ExitWindowsEx(flags uint32, reason uint32) (err error) = user32.ExitWindowsEx
|
||||
@@ -233,6 +244,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock
|
||||
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
|
||||
//sys getTickCount64() (ms uint64) = kernel32.GetTickCount64
|
||||
//sys GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
|
||||
//sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
|
||||
//sys GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
|
||||
//sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW
|
||||
@@ -303,6 +315,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
|
||||
//sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
|
||||
//sys setConsoleCursorPosition(console Handle, position uint32) (err error) = kernel32.SetConsoleCursorPosition
|
||||
//sys GetConsoleCP() (cp uint32, err error) = kernel32.GetConsoleCP
|
||||
//sys GetConsoleOutputCP() (cp uint32, err error) = kernel32.GetConsoleOutputCP
|
||||
//sys SetConsoleCP(cp uint32) (err error) = kernel32.SetConsoleCP
|
||||
//sys SetConsoleOutputCP(cp uint32) (err error) = kernel32.SetConsoleOutputCP
|
||||
//sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW
|
||||
//sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW
|
||||
//sys resizePseudoConsole(pconsole Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole
|
||||
@@ -345,8 +361,19 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost
|
||||
//sys GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32)
|
||||
//sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error)
|
||||
//sys ClearCommBreak(handle Handle) (err error)
|
||||
//sys ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error)
|
||||
//sys EscapeCommFunction(handle Handle, dwFunc uint32) (err error)
|
||||
//sys GetCommState(handle Handle, lpDCB *DCB) (err error)
|
||||
//sys GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error)
|
||||
//sys GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
||||
//sys PurgeComm(handle Handle, dwFlags uint32) (err error)
|
||||
//sys SetCommBreak(handle Handle) (err error)
|
||||
//sys SetCommMask(handle Handle, dwEvtMask uint32) (err error)
|
||||
//sys SetCommState(handle Handle, lpDCB *DCB) (err error)
|
||||
//sys SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
||||
//sys SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error)
|
||||
//sys WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error)
|
||||
//sys GetActiveProcessorCount(groupNumber uint16) (ret uint32)
|
||||
//sys GetMaximumProcessorCount(groupNumber uint16) (ret uint32)
|
||||
//sys EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) = user32.EnumWindows
|
||||
@@ -700,20 +727,12 @@ func DurationSinceBoot() time.Duration {
|
||||
}
|
||||
|
||||
func Ftruncate(fd Handle, length int64) (err error) {
|
||||
curoffset, e := Seek(fd, 0, 1)
|
||||
if e != nil {
|
||||
return e
|
||||
type _FILE_END_OF_FILE_INFO struct {
|
||||
EndOfFile int64
|
||||
}
|
||||
defer Seek(fd, curoffset, 0)
|
||||
_, e = Seek(fd, length, 0)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
e = SetEndOfFile(fd)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
var info _FILE_END_OF_FILE_INFO
|
||||
info.EndOfFile = length
|
||||
return SetFileInformationByHandle(fd, FileEndOfFileInfo, (*byte)(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)))
|
||||
}
|
||||
|
||||
func Gettimeofday(tv *Timeval) (err error) {
|
||||
@@ -869,6 +888,11 @@ const socket_error = uintptr(^uint32(0))
|
||||
//sys GetACP() (acp uint32) = kernel32.GetACP
|
||||
//sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar
|
||||
//sys getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) = iphlpapi.GetBestInterfaceEx
|
||||
//sys GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) = iphlpapi.GetIfEntry2Ex
|
||||
//sys GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) = iphlpapi.GetUnicastIpAddressEntry
|
||||
//sys NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyIpInterfaceChange
|
||||
//sys NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyUnicastIpAddressChange
|
||||
//sys CancelMibChangeNotify2(notificationHandle Handle) (errcode error) = iphlpapi.CancelMibChangeNotify2
|
||||
|
||||
// For testing: clients can set this flag to force
|
||||
// creation of IPv6 sockets to return EAFNOSUPPORT.
|
||||
@@ -969,7 +993,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) {
|
||||
if n > 0 {
|
||||
sl += int32(n) + 1
|
||||
}
|
||||
if sa.raw.Path[0] == '@' {
|
||||
if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) {
|
||||
// Check sl > 3 so we don't change unnamed socket behavior.
|
||||
sa.raw.Path[0] = 0
|
||||
// Don't count trailing NUL for abstract address.
|
||||
sl--
|
||||
@@ -1352,9 +1377,11 @@ func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) {
|
||||
func SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error) {
|
||||
return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4)
|
||||
}
|
||||
|
||||
func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
|
||||
return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq)))
|
||||
}
|
||||
|
||||
func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) {
|
||||
return syscall.EWINDOWS
|
||||
}
|
||||
@@ -1657,13 +1684,16 @@ func (s NTStatus) Error() string {
|
||||
// do not use NTUnicodeString, and instead UTF16PtrFromString should be used for
|
||||
// the more common *uint16 string type.
|
||||
func NewNTUnicodeString(s string) (*NTUnicodeString, error) {
|
||||
var u NTUnicodeString
|
||||
s16, err := UTF16PtrFromString(s)
|
||||
s16, err := UTF16FromString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
RtlInitUnicodeString(&u, s16)
|
||||
return &u, nil
|
||||
n := uint16(len(s16) * 2)
|
||||
return &NTUnicodeString{
|
||||
Length: n - 2, // subtract 2 bytes for the NULL terminator
|
||||
MaximumLength: n,
|
||||
Buffer: &s16[0],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Slice returns a uint16 slice that aliases the data in the NTUnicodeString.
|
||||
@@ -1830,3 +1860,73 @@ func ResizePseudoConsole(pconsole Handle, size Coord) error {
|
||||
// accept arguments that can be casted to uintptr, and Coord can't.
|
||||
return resizePseudoConsole(pconsole, *((*uint32)(unsafe.Pointer(&size))))
|
||||
}
|
||||
|
||||
// DCB constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-dcb.
|
||||
const (
|
||||
CBR_110 = 110
|
||||
CBR_300 = 300
|
||||
CBR_600 = 600
|
||||
CBR_1200 = 1200
|
||||
CBR_2400 = 2400
|
||||
CBR_4800 = 4800
|
||||
CBR_9600 = 9600
|
||||
CBR_14400 = 14400
|
||||
CBR_19200 = 19200
|
||||
CBR_38400 = 38400
|
||||
CBR_57600 = 57600
|
||||
CBR_115200 = 115200
|
||||
CBR_128000 = 128000
|
||||
CBR_256000 = 256000
|
||||
|
||||
DTR_CONTROL_DISABLE = 0x00000000
|
||||
DTR_CONTROL_ENABLE = 0x00000010
|
||||
DTR_CONTROL_HANDSHAKE = 0x00000020
|
||||
|
||||
RTS_CONTROL_DISABLE = 0x00000000
|
||||
RTS_CONTROL_ENABLE = 0x00001000
|
||||
RTS_CONTROL_HANDSHAKE = 0x00002000
|
||||
RTS_CONTROL_TOGGLE = 0x00003000
|
||||
|
||||
NOPARITY = 0
|
||||
ODDPARITY = 1
|
||||
EVENPARITY = 2
|
||||
MARKPARITY = 3
|
||||
SPACEPARITY = 4
|
||||
|
||||
ONESTOPBIT = 0
|
||||
ONE5STOPBITS = 1
|
||||
TWOSTOPBITS = 2
|
||||
)
|
||||
|
||||
// EscapeCommFunction constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-escapecommfunction.
|
||||
const (
|
||||
SETXOFF = 1
|
||||
SETXON = 2
|
||||
SETRTS = 3
|
||||
CLRRTS = 4
|
||||
SETDTR = 5
|
||||
CLRDTR = 6
|
||||
SETBREAK = 8
|
||||
CLRBREAK = 9
|
||||
)
|
||||
|
||||
// PurgeComm constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-purgecomm.
|
||||
const (
|
||||
PURGE_TXABORT = 0x0001
|
||||
PURGE_RXABORT = 0x0002
|
||||
PURGE_TXCLEAR = 0x0004
|
||||
PURGE_RXCLEAR = 0x0008
|
||||
)
|
||||
|
||||
// SetCommMask constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommmask.
|
||||
const (
|
||||
EV_RXCHAR = 0x0001
|
||||
EV_RXFLAG = 0x0002
|
||||
EV_TXEMPTY = 0x0004
|
||||
EV_CTS = 0x0008
|
||||
EV_DSR = 0x0010
|
||||
EV_RLSD = 0x0020
|
||||
EV_BREAK = 0x0040
|
||||
EV_ERR = 0x0080
|
||||
EV_RING = 0x0100
|
||||
)
|
||||
|
||||
251
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
251
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
@@ -176,6 +176,7 @@ const (
|
||||
WAIT_FAILED = 0xFFFFFFFF
|
||||
|
||||
// Access rights for process.
|
||||
PROCESS_ALL_ACCESS = 0xFFFF
|
||||
PROCESS_CREATE_PROCESS = 0x0080
|
||||
PROCESS_CREATE_THREAD = 0x0002
|
||||
PROCESS_DUP_HANDLE = 0x0040
|
||||
@@ -1060,6 +1061,7 @@ const (
|
||||
SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6
|
||||
SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4
|
||||
SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
|
||||
SIO_UDP_NETRESET = IOC_IN | IOC_VENDOR | 15
|
||||
|
||||
// cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460
|
||||
|
||||
@@ -1094,7 +1096,33 @@ const (
|
||||
|
||||
SOMAXCONN = 0x7fffffff
|
||||
|
||||
TCP_NODELAY = 1
|
||||
TCP_NODELAY = 1
|
||||
TCP_EXPEDITED_1122 = 2
|
||||
TCP_KEEPALIVE = 3
|
||||
TCP_MAXSEG = 4
|
||||
TCP_MAXRT = 5
|
||||
TCP_STDURG = 6
|
||||
TCP_NOURG = 7
|
||||
TCP_ATMARK = 8
|
||||
TCP_NOSYNRETRIES = 9
|
||||
TCP_TIMESTAMPS = 10
|
||||
TCP_OFFLOAD_PREFERENCE = 11
|
||||
TCP_CONGESTION_ALGORITHM = 12
|
||||
TCP_DELAY_FIN_ACK = 13
|
||||
TCP_MAXRTMS = 14
|
||||
TCP_FASTOPEN = 15
|
||||
TCP_KEEPCNT = 16
|
||||
TCP_KEEPIDLE = TCP_KEEPALIVE
|
||||
TCP_KEEPINTVL = 17
|
||||
TCP_FAIL_CONNECT_ON_ICMP_ERROR = 18
|
||||
TCP_ICMP_ERROR_INFO = 19
|
||||
|
||||
UDP_NOCHECKSUM = 1
|
||||
UDP_SEND_MSG_SIZE = 2
|
||||
UDP_RECV_MAX_COALESCED_SIZE = 3
|
||||
UDP_CHECKSUM_COVERAGE = 20
|
||||
|
||||
UDP_COALESCED_INFO = 3
|
||||
|
||||
SHUT_RD = 0
|
||||
SHUT_WR = 1
|
||||
@@ -1977,7 +2005,21 @@ const (
|
||||
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20
|
||||
)
|
||||
|
||||
const GAA_FLAG_INCLUDE_PREFIX = 0x00000010
|
||||
// Flags for GetAdaptersAddresses, see
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses.
|
||||
const (
|
||||
GAA_FLAG_SKIP_UNICAST = 0x1
|
||||
GAA_FLAG_SKIP_ANYCAST = 0x2
|
||||
GAA_FLAG_SKIP_MULTICAST = 0x4
|
||||
GAA_FLAG_SKIP_DNS_SERVER = 0x8
|
||||
GAA_FLAG_INCLUDE_PREFIX = 0x10
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME = 0x20
|
||||
GAA_FLAG_INCLUDE_WINS_INFO = 0x40
|
||||
GAA_FLAG_INCLUDE_GATEWAYS = 0x80
|
||||
GAA_FLAG_INCLUDE_ALL_INTERFACES = 0x100
|
||||
GAA_FLAG_INCLUDE_ALL_COMPARTMENTS = 0x200
|
||||
GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER = 0x400
|
||||
)
|
||||
|
||||
const (
|
||||
IF_TYPE_OTHER = 1
|
||||
@@ -1991,6 +2033,50 @@ const (
|
||||
IF_TYPE_IEEE1394 = 144
|
||||
)
|
||||
|
||||
// Enum NL_PREFIX_ORIGIN for [IpAdapterUnicastAddress], see
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin
|
||||
const (
|
||||
IpPrefixOriginOther = 0
|
||||
IpPrefixOriginManual = 1
|
||||
IpPrefixOriginWellKnown = 2
|
||||
IpPrefixOriginDhcp = 3
|
||||
IpPrefixOriginRouterAdvertisement = 4
|
||||
IpPrefixOriginUnchanged = 1 << 4
|
||||
)
|
||||
|
||||
// Enum NL_SUFFIX_ORIGIN for [IpAdapterUnicastAddress], see
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_suffix_origin
|
||||
const (
|
||||
NlsoOther = 0
|
||||
NlsoManual = 1
|
||||
NlsoWellKnown = 2
|
||||
NlsoDhcp = 3
|
||||
NlsoLinkLayerAddress = 4
|
||||
NlsoRandom = 5
|
||||
IpSuffixOriginOther = 0
|
||||
IpSuffixOriginManual = 1
|
||||
IpSuffixOriginWellKnown = 2
|
||||
IpSuffixOriginDhcp = 3
|
||||
IpSuffixOriginLinkLayerAddress = 4
|
||||
IpSuffixOriginRandom = 5
|
||||
IpSuffixOriginUnchanged = 1 << 4
|
||||
)
|
||||
|
||||
// Enum NL_DAD_STATE for [IpAdapterUnicastAddress], see
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_dad_state
|
||||
const (
|
||||
NldsInvalid = 0
|
||||
NldsTentative = 1
|
||||
NldsDuplicate = 2
|
||||
NldsDeprecated = 3
|
||||
NldsPreferred = 4
|
||||
IpDadStateInvalid = 0
|
||||
IpDadStateTentative = 1
|
||||
IpDadStateDuplicate = 2
|
||||
IpDadStateDeprecated = 3
|
||||
IpDadStatePreferred = 4
|
||||
)
|
||||
|
||||
type SocketAddress struct {
|
||||
Sockaddr *syscall.RawSockaddrAny
|
||||
SockaddrLength int32
|
||||
@@ -2118,6 +2204,132 @@ const (
|
||||
IfOperStatusLowerLayerDown = 7
|
||||
)
|
||||
|
||||
const (
|
||||
IF_MAX_PHYS_ADDRESS_LENGTH = 32
|
||||
IF_MAX_STRING_SIZE = 256
|
||||
)
|
||||
|
||||
// MIB_IF_ENTRY_LEVEL enumeration from netioapi.h or
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getifentry2ex.
|
||||
const (
|
||||
MibIfEntryNormal = 0
|
||||
MibIfEntryNormalWithoutStatistics = 2
|
||||
)
|
||||
|
||||
// MIB_NOTIFICATION_TYPE enumeration from netioapi.h or
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ne-netioapi-mib_notification_type.
|
||||
const (
|
||||
MibParameterNotification = 0
|
||||
MibAddInstance = 1
|
||||
MibDeleteInstance = 2
|
||||
MibInitialNotification = 3
|
||||
)
|
||||
|
||||
// MibIfRow2 stores information about a particular interface. See
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_if_row2.
|
||||
type MibIfRow2 struct {
|
||||
InterfaceLuid uint64
|
||||
InterfaceIndex uint32
|
||||
InterfaceGuid GUID
|
||||
Alias [IF_MAX_STRING_SIZE + 1]uint16
|
||||
Description [IF_MAX_STRING_SIZE + 1]uint16
|
||||
PhysicalAddressLength uint32
|
||||
PhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]uint8
|
||||
PermanentPhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]uint8
|
||||
Mtu uint32
|
||||
Type uint32
|
||||
TunnelType uint32
|
||||
MediaType uint32
|
||||
PhysicalMediumType uint32
|
||||
AccessType uint32
|
||||
DirectionType uint32
|
||||
InterfaceAndOperStatusFlags uint8
|
||||
OperStatus uint32
|
||||
AdminStatus uint32
|
||||
MediaConnectState uint32
|
||||
NetworkGuid GUID
|
||||
ConnectionType uint32
|
||||
TransmitLinkSpeed uint64
|
||||
ReceiveLinkSpeed uint64
|
||||
InOctets uint64
|
||||
InUcastPkts uint64
|
||||
InNUcastPkts uint64
|
||||
InDiscards uint64
|
||||
InErrors uint64
|
||||
InUnknownProtos uint64
|
||||
InUcastOctets uint64
|
||||
InMulticastOctets uint64
|
||||
InBroadcastOctets uint64
|
||||
OutOctets uint64
|
||||
OutUcastPkts uint64
|
||||
OutNUcastPkts uint64
|
||||
OutDiscards uint64
|
||||
OutErrors uint64
|
||||
OutUcastOctets uint64
|
||||
OutMulticastOctets uint64
|
||||
OutBroadcastOctets uint64
|
||||
OutQLen uint64
|
||||
}
|
||||
|
||||
// MIB_UNICASTIPADDRESS_ROW stores information about a unicast IP address. See
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_row.
|
||||
type MibUnicastIpAddressRow struct {
|
||||
Address RawSockaddrInet6 // SOCKADDR_INET union
|
||||
InterfaceLuid uint64
|
||||
InterfaceIndex uint32
|
||||
PrefixOrigin uint32
|
||||
SuffixOrigin uint32
|
||||
ValidLifetime uint32
|
||||
PreferredLifetime uint32
|
||||
OnLinkPrefixLength uint8
|
||||
SkipAsSource uint8
|
||||
DadState uint32
|
||||
ScopeId uint32
|
||||
CreationTimeStamp Filetime
|
||||
}
|
||||
|
||||
const ScopeLevelCount = 16
|
||||
|
||||
// MIB_IPINTERFACE_ROW stores interface management information for a particular IP address family on a network interface.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipinterface_row.
|
||||
type MibIpInterfaceRow struct {
|
||||
Family uint16
|
||||
InterfaceLuid uint64
|
||||
InterfaceIndex uint32
|
||||
MaxReassemblySize uint32
|
||||
InterfaceIdentifier uint64
|
||||
MinRouterAdvertisementInterval uint32
|
||||
MaxRouterAdvertisementInterval uint32
|
||||
AdvertisingEnabled uint8
|
||||
ForwardingEnabled uint8
|
||||
WeakHostSend uint8
|
||||
WeakHostReceive uint8
|
||||
UseAutomaticMetric uint8
|
||||
UseNeighborUnreachabilityDetection uint8
|
||||
ManagedAddressConfigurationSupported uint8
|
||||
OtherStatefulConfigurationSupported uint8
|
||||
AdvertiseDefaultRoute uint8
|
||||
RouterDiscoveryBehavior uint32
|
||||
DadTransmits uint32
|
||||
BaseReachableTime uint32
|
||||
RetransmitTime uint32
|
||||
PathMtuDiscoveryTimeout uint32
|
||||
LinkLocalAddressBehavior uint32
|
||||
LinkLocalAddressTimeout uint32
|
||||
ZoneIndices [ScopeLevelCount]uint32
|
||||
SitePrefixLength uint32
|
||||
Metric uint32
|
||||
NlMtu uint32
|
||||
Connected uint8
|
||||
SupportsWakeUpPatterns uint8
|
||||
SupportsNeighborDiscovery uint8
|
||||
SupportsRouterDiscovery uint8
|
||||
ReachableTime uint32
|
||||
TransmitOffload uint32
|
||||
ReceiveOffload uint32
|
||||
DisableDefaultRoutes uint8
|
||||
}
|
||||
|
||||
// Console related constants used for the mode parameter to SetConsoleMode. See
|
||||
// https://docs.microsoft.com/en-us/windows/console/setconsolemode for details.
|
||||
|
||||
@@ -3354,3 +3566,38 @@ type BLOB struct {
|
||||
Size uint32
|
||||
BlobData *byte
|
||||
}
|
||||
|
||||
type ComStat struct {
|
||||
Flags uint32
|
||||
CBInQue uint32
|
||||
CBOutQue uint32
|
||||
}
|
||||
|
||||
type DCB struct {
|
||||
DCBlength uint32
|
||||
BaudRate uint32
|
||||
Flags uint32
|
||||
wReserved uint16
|
||||
XonLim uint16
|
||||
XoffLim uint16
|
||||
ByteSize uint8
|
||||
Parity uint8
|
||||
StopBits uint8
|
||||
XonChar byte
|
||||
XoffChar byte
|
||||
ErrorChar byte
|
||||
EofChar byte
|
||||
EvtChar byte
|
||||
wReserved1 uint16
|
||||
}
|
||||
|
||||
// Keyboard Layout Flags.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadkeyboardlayoutw
|
||||
const (
|
||||
KLF_ACTIVATE = 0x00000001
|
||||
KLF_SUBSTITUTE_OK = 0x00000002
|
||||
KLF_REORDER = 0x00000008
|
||||
KLF_REPLACELANG = 0x00000010
|
||||
KLF_NOTELLSHELL = 0x00000080
|
||||
KLF_SETFORPROCESS = 0x00000100
|
||||
)
|
||||
|
||||
305
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
305
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
@@ -91,6 +91,7 @@ var (
|
||||
procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW")
|
||||
procEqualSid = modadvapi32.NewProc("EqualSid")
|
||||
procFreeSid = modadvapi32.NewProc("FreeSid")
|
||||
procGetAce = modadvapi32.NewProc("GetAce")
|
||||
procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
|
||||
procGetNamedSecurityInfoW = modadvapi32.NewProc("GetNamedSecurityInfoW")
|
||||
procGetSecurityDescriptorControl = modadvapi32.NewProc("GetSecurityDescriptorControl")
|
||||
@@ -180,13 +181,21 @@ var (
|
||||
procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
|
||||
procDwmGetWindowAttribute = moddwmapi.NewProc("DwmGetWindowAttribute")
|
||||
procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute")
|
||||
procCancelMibChangeNotify2 = modiphlpapi.NewProc("CancelMibChangeNotify2")
|
||||
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
|
||||
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
|
||||
procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx")
|
||||
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
|
||||
procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
|
||||
procGetUnicastIpAddressEntry = modiphlpapi.NewProc("GetUnicastIpAddressEntry")
|
||||
procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange")
|
||||
procNotifyUnicastIpAddressChange = modiphlpapi.NewProc("NotifyUnicastIpAddressChange")
|
||||
procAddDllDirectory = modkernel32.NewProc("AddDllDirectory")
|
||||
procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject")
|
||||
procCancelIo = modkernel32.NewProc("CancelIo")
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procClearCommBreak = modkernel32.NewProc("ClearCommBreak")
|
||||
procClearCommError = modkernel32.NewProc("ClearCommError")
|
||||
procCloseHandle = modkernel32.NewProc("CloseHandle")
|
||||
procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
@@ -211,7 +220,9 @@ var (
|
||||
procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList")
|
||||
procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW")
|
||||
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
||||
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
|
||||
procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
|
||||
procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction")
|
||||
procExitProcess = modkernel32.NewProc("ExitProcess")
|
||||
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
||||
procFindClose = modkernel32.NewProc("FindClose")
|
||||
@@ -235,11 +246,15 @@ var (
|
||||
procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
|
||||
procGetACP = modkernel32.NewProc("GetACP")
|
||||
procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount")
|
||||
procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus")
|
||||
procGetCommState = modkernel32.NewProc("GetCommState")
|
||||
procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts")
|
||||
procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
|
||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||
procGetComputerNameW = modkernel32.NewProc("GetComputerNameW")
|
||||
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
||||
procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
|
||||
procGetConsoleOutputCP = modkernel32.NewProc("GetConsoleOutputCP")
|
||||
procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW")
|
||||
procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
|
||||
@@ -253,6 +268,7 @@ var (
|
||||
procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
|
||||
procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
|
||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procGetFileTime = modkernel32.NewProc("GetFileTime")
|
||||
procGetFileType = modkernel32.NewProc("GetFileType")
|
||||
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
||||
procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
|
||||
@@ -264,8 +280,10 @@ var (
|
||||
procGetMaximumProcessorCount = modkernel32.NewProc("GetMaximumProcessorCount")
|
||||
procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
|
||||
procGetModuleHandleExW = modkernel32.NewProc("GetModuleHandleExW")
|
||||
procGetNamedPipeClientProcessId = modkernel32.NewProc("GetNamedPipeClientProcessId")
|
||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||
procGetNamedPipeServerProcessId = modkernel32.NewProc("GetNamedPipeServerProcessId")
|
||||
procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult")
|
||||
procGetPriorityClass = modkernel32.NewProc("GetPriorityClass")
|
||||
procGetProcAddress = modkernel32.NewProc("GetProcAddress")
|
||||
@@ -320,6 +338,7 @@ var (
|
||||
procProcess32NextW = modkernel32.NewProc("Process32NextW")
|
||||
procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
|
||||
procPulseEvent = modkernel32.NewProc("PulseEvent")
|
||||
procPurgeComm = modkernel32.NewProc("PurgeComm")
|
||||
procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW")
|
||||
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
||||
procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject")
|
||||
@@ -329,12 +348,18 @@ var (
|
||||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||
procReleaseMutex = modkernel32.NewProc("ReleaseMutex")
|
||||
procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
|
||||
procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory")
|
||||
procResetEvent = modkernel32.NewProc("ResetEvent")
|
||||
procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole")
|
||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||
procSetCommBreak = modkernel32.NewProc("SetCommBreak")
|
||||
procSetCommMask = modkernel32.NewProc("SetCommMask")
|
||||
procSetCommState = modkernel32.NewProc("SetCommState")
|
||||
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
|
||||
procSetConsoleCP = modkernel32.NewProc("SetConsoleCP")
|
||||
procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition")
|
||||
procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
|
||||
procSetConsoleOutputCP = modkernel32.NewProc("SetConsoleOutputCP")
|
||||
procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
|
||||
procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories")
|
||||
procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW")
|
||||
@@ -347,6 +372,7 @@ var (
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
procSetFilePointer = modkernel32.NewProc("SetFilePointer")
|
||||
procSetFileTime = modkernel32.NewProc("SetFileTime")
|
||||
procSetFileValidData = modkernel32.NewProc("SetFileValidData")
|
||||
procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
|
||||
procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject")
|
||||
procSetNamedPipeHandleState = modkernel32.NewProc("SetNamedPipeHandleState")
|
||||
@@ -357,6 +383,7 @@ var (
|
||||
procSetStdHandle = modkernel32.NewProc("SetStdHandle")
|
||||
procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW")
|
||||
procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW")
|
||||
procSetupComm = modkernel32.NewProc("SetupComm")
|
||||
procSizeofResource = modkernel32.NewProc("SizeofResource")
|
||||
procSleepEx = modkernel32.NewProc("SleepEx")
|
||||
procTerminateJobObject = modkernel32.NewProc("TerminateJobObject")
|
||||
@@ -375,6 +402,7 @@ var (
|
||||
procVirtualQueryEx = modkernel32.NewProc("VirtualQueryEx")
|
||||
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
||||
procWTSGetActiveConsoleSessionId = modkernel32.NewProc("WTSGetActiveConsoleSessionId")
|
||||
procWaitCommEvent = modkernel32.NewProc("WaitCommEvent")
|
||||
procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects")
|
||||
procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
|
||||
procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
|
||||
@@ -385,6 +413,7 @@ var (
|
||||
procTransmitFile = modmswsock.NewProc("TransmitFile")
|
||||
procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
|
||||
procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
|
||||
procNetUserEnum = modnetapi32.NewProc("NetUserEnum")
|
||||
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
|
||||
procNtCreateFile = modntdll.NewProc("NtCreateFile")
|
||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||
@@ -460,12 +489,16 @@ var (
|
||||
procGetDesktopWindow = moduser32.NewProc("GetDesktopWindow")
|
||||
procGetForegroundWindow = moduser32.NewProc("GetForegroundWindow")
|
||||
procGetGUIThreadInfo = moduser32.NewProc("GetGUIThreadInfo")
|
||||
procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout")
|
||||
procGetShellWindow = moduser32.NewProc("GetShellWindow")
|
||||
procGetWindowThreadProcessId = moduser32.NewProc("GetWindowThreadProcessId")
|
||||
procIsWindow = moduser32.NewProc("IsWindow")
|
||||
procIsWindowUnicode = moduser32.NewProc("IsWindowUnicode")
|
||||
procIsWindowVisible = moduser32.NewProc("IsWindowVisible")
|
||||
procLoadKeyboardLayoutW = moduser32.NewProc("LoadKeyboardLayoutW")
|
||||
procMessageBoxW = moduser32.NewProc("MessageBoxW")
|
||||
procToUnicodeEx = moduser32.NewProc("ToUnicodeEx")
|
||||
procUnloadKeyboardLayout = moduser32.NewProc("UnloadKeyboardLayout")
|
||||
procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock")
|
||||
procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock")
|
||||
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
|
||||
@@ -771,6 +804,14 @@ func FreeSid(sid *SID) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetAce.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetLengthSid(sid *SID) (len uint32) {
|
||||
r0, _, _ := syscall.Syscall(procGetLengthSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0)
|
||||
len = uint32(r0)
|
||||
@@ -1572,6 +1613,14 @@ func DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, si
|
||||
return
|
||||
}
|
||||
|
||||
func CancelMibChangeNotify2(notificationHandle Handle) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall(procCancelMibChangeNotify2.Addr(), 1, uintptr(notificationHandle), 0, 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0)
|
||||
if r0 != 0 {
|
||||
@@ -1604,6 +1653,55 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall(procGetIfEntry2Ex.Addr(), 2, uintptr(level), uintptr(unsafe.Pointer(row)), 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall(procGetUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) {
|
||||
var _p0 uint32
|
||||
if initialNotification {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall6(procNotifyIpInterfaceChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) {
|
||||
var _p0 uint32
|
||||
if initialNotification {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall6(procNotifyUnicastIpAddressChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func AddDllDirectory(path *uint16) (cookie uintptr, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
|
||||
cookie = uintptr(r0)
|
||||
if cookie == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func AssignProcessToJobObject(job Handle, process Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0)
|
||||
if r1 == 0 {
|
||||
@@ -1628,6 +1726,22 @@ func CancelIoEx(s Handle, o *Overlapped) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func ClearCommBreak(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procClearCommError.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CloseHandle(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
@@ -1832,6 +1946,14 @@ func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBuff
|
||||
return
|
||||
}
|
||||
|
||||
func DisconnectNamedPipe(pipe Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(pipe), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) {
|
||||
var _p0 uint32
|
||||
if bInheritHandle {
|
||||
@@ -1844,6 +1966,14 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP
|
||||
return
|
||||
}
|
||||
|
||||
func EscapeCommFunction(handle Handle, dwFunc uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(dwFunc), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExitProcess(exitcode uint32) {
|
||||
syscall.Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0)
|
||||
return
|
||||
@@ -2045,6 +2175,22 @@ func GetActiveProcessorCount(groupNumber uint16) (ret uint32) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpModemStat)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommState(handle Handle, lpDCB *DCB) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2075,6 +2221,15 @@ func GetComputerName(buf *uint16, n *uint32) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetConsoleCP() (cp uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0)
|
||||
cp = uint32(r0)
|
||||
if cp == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetConsoleMode(console Handle, mode *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2083,6 +2238,15 @@ func GetConsoleMode(console Handle, mode *uint32) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetConsoleOutputCP() (cp uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetConsoleOutputCP.Addr(), 0, 0, 0, 0)
|
||||
cp = uint32(r0)
|
||||
if cp == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(info)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2185,6 +2349,14 @@ func GetFileInformationByHandleEx(handle Handle, class uint32, outBuffer *byte,
|
||||
return
|
||||
}
|
||||
|
||||
func GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetFileType(filehandle Handle) (n uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0)
|
||||
n = uint32(r0)
|
||||
@@ -2276,6 +2448,14 @@ func GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err er
|
||||
return
|
||||
}
|
||||
|
||||
func GetNamedPipeClientProcessId(pipe Handle, clientProcessID *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetNamedPipeClientProcessId.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(clientProcessID)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
@@ -2292,6 +2472,14 @@ func GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint3
|
||||
return
|
||||
}
|
||||
|
||||
func GetNamedPipeServerProcessId(pipe Handle, serverProcessID *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetNamedPipeServerProcessId.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(serverProcessID)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetOverlappedResult(handle Handle, overlapped *Overlapped, done *uint32, wait bool) (err error) {
|
||||
var _p0 uint32
|
||||
if wait {
|
||||
@@ -2789,6 +2977,14 @@ func PulseEvent(event Handle) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func PurgeComm(handle Handle, dwFlags uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(dwFlags), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procQueryDosDeviceW.Addr(), 3, uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max))
|
||||
n = uint32(r0)
|
||||
@@ -2870,6 +3066,14 @@ func RemoveDirectory(path *uint16) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func RemoveDllDirectory(cookie uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ResetEvent(event Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0)
|
||||
if r1 == 0 {
|
||||
@@ -2895,6 +3099,30 @@ func ResumeThread(thread Handle) (ret uint32, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommBreak(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommMask(handle Handle, dwEvtMask uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(dwEvtMask), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommState(handle Handle, lpDCB *DCB) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2903,6 +3131,14 @@ func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func SetConsoleCP(cp uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetConsoleCP.Addr(), 1, uintptr(cp), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setConsoleCursorPosition(console Handle, position uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetConsoleCursorPosition.Addr(), 2, uintptr(console), uintptr(position), 0)
|
||||
if r1 == 0 {
|
||||
@@ -2919,6 +3155,14 @@ func SetConsoleMode(console Handle, mode uint32) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func SetConsoleOutputCP(cp uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetConsoleOutputCP.Addr(), 1, uintptr(cp), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCurrentDirectory(path *uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
|
||||
if r1 == 0 {
|
||||
@@ -3023,6 +3267,14 @@ func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim
|
||||
return
|
||||
}
|
||||
|
||||
func SetFileValidData(handle Handle, validDataLength int64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
|
||||
if r1 == 0 {
|
||||
@@ -3108,6 +3360,14 @@ func SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err erro
|
||||
return
|
||||
}
|
||||
|
||||
func SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupComm.Addr(), 3, uintptr(handle), uintptr(dwInQueue), uintptr(dwOutQueue))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0)
|
||||
size = uint32(r0)
|
||||
@@ -3254,6 +3514,14 @@ func WTSGetActiveConsoleSessionId() (sessionID uint32) {
|
||||
return
|
||||
}
|
||||
|
||||
func WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWaitCommEvent.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpEvtMask)), uintptr(unsafe.Pointer(lpOverlapped)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMilliseconds uint32) (event uint32, err error) {
|
||||
var _p0 uint32
|
||||
if waitAll {
|
||||
@@ -3341,6 +3609,14 @@ func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (nete
|
||||
return
|
||||
}
|
||||
|
||||
func NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) {
|
||||
r0, _, _ := syscall.Syscall9(procNetUserEnum.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), uintptr(unsafe.Pointer(resumeHandle)), 0)
|
||||
if r0 != 0 {
|
||||
neterr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) {
|
||||
r0, _, _ := syscall.Syscall6(procNetUserGetInfo.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), 0, 0)
|
||||
if r0 != 0 {
|
||||
@@ -3919,6 +4195,12 @@ func GetGUIThreadInfo(thread uint32, info *GUIThreadInfo) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetKeyboardLayout(tid uint32) (hkl Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(tid), 0, 0)
|
||||
hkl = Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func GetShellWindow() (shellWindow HWND) {
|
||||
r0, _, _ := syscall.Syscall(procGetShellWindow.Addr(), 0, 0, 0, 0)
|
||||
shellWindow = HWND(r0)
|
||||
@@ -3952,6 +4234,15 @@ func IsWindowVisible(hwnd HWND) (isVisible bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procLoadKeyboardLayoutW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(flags), 0)
|
||||
hkl = Handle(r0)
|
||||
if hkl == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procMessageBoxW.Addr(), 4, uintptr(hwnd), uintptr(unsafe.Pointer(text)), uintptr(unsafe.Pointer(caption)), uintptr(boxtype), 0, 0)
|
||||
ret = int32(r0)
|
||||
@@ -3961,6 +4252,20 @@ func MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret i
|
||||
return
|
||||
}
|
||||
|
||||
func ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32) {
|
||||
r0, _, _ := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(vkey), uintptr(scancode), uintptr(unsafe.Pointer(keystate)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(flags), uintptr(hkl), 0, 0)
|
||||
ret = int32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func UnloadKeyboardLayout(hkl Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procUnloadKeyboardLayout.Addr(), 1, uintptr(hkl), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) {
|
||||
var _p0 uint32
|
||||
if inheritExisting {
|
||||
|
||||
4
vendor/golang.org/x/text/LICENSE
generated
vendored
4
vendor/golang.org/x/text/LICENSE
generated
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
12
vendor/modules.txt
vendored
12
vendor/modules.txt
vendored
@@ -1,16 +1,16 @@
|
||||
# github.com/mattn/go-sqlite3 v1.14.7
|
||||
## explicit; go 1.12
|
||||
github.com/mattn/go-sqlite3
|
||||
# golang.org/x/net v0.17.0
|
||||
## explicit; go 1.17
|
||||
# golang.org/x/net v0.33.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/net/html
|
||||
golang.org/x/net/html/atom
|
||||
golang.org/x/net/html/charset
|
||||
# golang.org/x/sys v0.13.0
|
||||
## explicit; go 1.17
|
||||
# golang.org/x/sys v0.28.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/sys/windows
|
||||
# golang.org/x/text v0.13.0
|
||||
## explicit; go 1.17
|
||||
# golang.org/x/text v0.21.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/text/encoding
|
||||
golang.org/x/text/encoding/charmap
|
||||
golang.org/x/text/encoding/htmlindex
|
||||
|
||||
Reference in New Issue
Block a user